Skip to content
This repository was archived by the owner on Oct 28, 2025. It is now read-only.
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 45 additions & 6 deletions src/semgrep_mcp/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -276,7 +276,9 @@ def create_temp_files_from_code_content(code_files: list[CodeFile]) -> str:
) from e


def get_semgrep_scan_args(temp_dir: str, config: str | None = None) -> list[str]:
def get_semgrep_scan_args(
temp_dir: str, config: str | None = None, scan_type: str = "code"
) -> list[str]:
"""
Builds command arguments for semgrep scan

Expand All @@ -291,10 +293,38 @@ def get_semgrep_scan_args(temp_dir: str, config: str | None = None) -> list[str]
# Build command arguments and just run semgrep scan
# if no config is provided to allow for either the default "auto"
# or whatever the logged in config is
args = ["scan", "--json", "--experimental"] # avoid the extra exec
if config:
args.extend(["--config", config])
args.append(temp_dir)
args = ["ci", "--json", "--dry-run"] # avoid the extra exec
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you share why ci is preferred here over scan?

Also, why is --dry-run needed?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Currently, you can't run a supply chain scan with scan, so we have to use ci in this case. We add --dry-run in so that the scan completes and reports findings locally in a json blob, but does not upload the findings to the cloud platform.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A-ha, I see! Thanks. All clear.

The --dry-run name is a bit confusing, but it makes sense 🤣

api_token = os.environ.get("SEMGREP_API_TOKEN")
match scan_type:
case "code":
args.append("--code")
case "supply-chain":
if not api_token:
raise McpError(
ErrorData(
code=INVALID_PARAMS,
message="SEMGREP_API_TOKEN environment variable "
"must be set to use this tool. Create a "
"token at semgrep.dev to continue.",
)
)
args.append("--supply-chain")
case "secret":
if not api_token:
raise McpError(
ErrorData(
code=INVALID_PARAMS,
message="SEMGREP_API_TOKEN environment variable "
"must be set to use this tool. Create a "
"token at semgrep.dev to continue.",
)
)
args.append("--secrets")
case _:
raise McpError(
ErrorData(code=INVALID_PARAMS, message=f"Invalid scan type: {scan_type}")
)
args.extend(["--include", temp_dir])
return args


Expand Down Expand Up @@ -702,13 +732,22 @@ async def semgrep_scan_with_custom_rule(
async def semgrep_scan(
code_files: list[CodeFile] = CODE_FILES_FIELD,
config: str | None = CONFIG_FIELD,
scan_type: str = "code",
) -> SemgrepScanResult:
"""
Runs a Semgrep scan on provided code content and returns the findings in JSON format

Use this tool when you need to:
- scan code files for security vulnerabilities
- scan code files for other issues

Args:
code_files: The code files to scan.
config: The Semgrep config to use.
scan_type: The type of scan to run.
- "code": Scan code files.
- "supply-chain": Scan code files for CI/CD.
- "secrets": Scan code files for secrets.
"""
# Validate config
config = validate_config(config)
Expand All @@ -719,7 +758,7 @@ async def semgrep_scan(
try:
# Create temporary files from code content
temp_dir = create_temp_files_from_code_content(code_files)
args = get_semgrep_scan_args(temp_dir, config)
args = get_semgrep_scan_args(temp_dir, config, scan_type)
output = await run_semgrep(args)
results: SemgrepScanResult = SemgrepScanResult.model_validate_json(output)
remove_temp_dir_from_results(results, temp_dir)
Expand Down
Loading