Skip to main content

vulkro mcp-audit

Audit MCP host configs for supply-chain and credential-handling risks. MCP host configs (Claude Desktop, Cursor, Windsurf, VS Code, Cline, Continue, Gemini, project-local .mcp.json) describe which local processes the user's LLM client will launch on demand and what env / command-line arguments they receive. A bad entry here lands attacker code inside the shell the model already has reach into; this subcommand surfaces the six classes of risk that show up in real host configs.

Usage

vulkro mcp-audit [PATH] [FLAGS]

Arguments

ArgumentDescriptionDefault
PATHA specific MCP config file, or a project root. When omitted, scans project-local configs in the current directory plus the well-known host-config locations.(omitted)

Flags

FlagDescriptionDefault
--format, -f <FMT>table, json, ndjson, sarif, gh-pr, junit, csv.table
--require-mcpTreat "no MCP configs found" as an error (exit 2) instead of a clean exit. Useful in CI jobs that expect a config to be present.false
--include-hostAlso scan the well-known host-config locations when <path> is given. When <path> is omitted, host configs are already in scope; this flag only matters with an explicit path.false
--fail-on <SEVERITIES>Comma-separated severities that should produce a non-zero exit. Same shape as vulkro scan --fail-on.critical,high

Exit codes

CodeMeaning
0No findings, or --require-mcp not set and no configs found.
1Findings at or above the --fail-on threshold.
2Error: malformed JSON, IO failure, or --require-mcp set with no configs found.

Discovery paths

When <path> is omitted, the auditor walks the following well-known locations and reads whichever exist (missing files are skipped silently):

  • ~/Library/Application Support/Claude/claude_desktop_config.json (macOS)
  • ~/.config/Claude/claude_desktop_config.json (Linux)
  • %APPDATA%\Claude\claude_desktop_config.json (Windows, documented for completeness)
  • ~/.cursor/mcp.json, ~/.cursor/mcp_config.json
  • ~/.windsurf/mcp.json, ~/.codeium/windsurf/mcp_config.json
  • ~/.vscode/mcp_settings.json, ~/.vscode-server/mcp_settings.json
  • ~/.cline/cline_mcp_settings.json
  • ~/.gemini/settings.json
  • ~/.continue/mcp.json
  • Project-local under the current directory: ./.mcp.json and ./mcp.json

A missing path is normal: an absent ~/.cursor/mcp.json just means Cursor is not configured on this machine.

Rules

The auditor emits stable finding IDs in the MCP-NNN family. Every rule page links back here.

IDRuleSeverity
MCP-001Unpinned npx / uvx registry install. The package re-resolves at every host launch.Medium (High when filesystem-scope)
MCP-002Git install pointed at a mutable ref (HEAD, main, no ref). A force-push silently changes what the host runs.High
MCP-003Filesystem server mounted at /, $HOME, or a shallow home subdir. Broad model reach turns one prompt-injection into a wide blast radius.High (root) / Medium (shallow)
MCP-004Credential literal inlined in an env block instead of ${VAR}. Provider-format hit elevates to Critical.Critical / High / Medium
MCP-005Cleartext http:// endpoint, or HTTPS endpoint with no visible auth credential.High (http) / Medium (https without auth)
MCP-006Pinned MCP server version matches the Vulkro compromised-release catalog.Critical

Inventory envelope

--format json emits both the rule findings and a discovery inventory so the consumer can distinguish "nothing to scan" from "scanned N configs, all clean":

{
"findings": [ /* SecurityFinding records */ ],
"mcp_inventory": {
"paths_checked": [
"/Users/me/.cursor/mcp.json",
"/Users/me/.windsurf/mcp.json",
"/Users/me/work/project/.mcp.json"
],
"paths_with_configs": [
"/Users/me/.cursor/mcp.json",
"/Users/me/work/project/.mcp.json"
],
"servers": [
{
"name": "filesystem",
"source": "/Users/me/.cursor/mcp.json",
"host_kind": "cursor",
"command": "npx",
"transport": "stdio"
}
]
}
}

host_kind is the human-readable host classification (claude_desktop, cursor, windsurf, vscode, cline, gemini, continue, project-local, or unknown). transport is one of stdio, http, sse, or unknown, derived from the entry's type field or the presence of command vs. url.

Examples

# Default: walk every well-known host config plus project-local.
vulkro mcp-audit

# Audit a single host config explicitly.
vulkro mcp-audit ~/.cursor/mcp.json

# Project-local + host configs together, with explicit path.
vulkro mcp-audit . --include-host

# CI gate: require an MCP config to exist and fail on critical or high.
vulkro mcp-audit --require-mcp --fail-on critical,high

# Stream NDJSON into jq for ad-hoc filtering.
vulkro mcp-audit --format ndjson | jq 'select(.severity == "critical")'
  • vulkro scan - the broader pipeline; SUP-COMPROMISE-* findings on lockfiles live there.
  • vulkro extension-audit - same shape, but for installed editor and browser extensions.
  • vulkro respond - "is THIS advisory or package in my project?" in under a second.
  • vulkro explain MCP-001 (and the rest) renders per-rule rationale even when no live finding hits.
  • Confidence model - how High, Medium, and Low are calibrated for MCP findings.

Command reference

Generated from vulkro help mcp-audit on vulkro 0.26.0. This block is the authoritative flag, usage, and exit-code reference for this command; the prose above is the friendly explanation. Do not edit this block by hand; run npm run docs:cli after a release.

Check AI-assistant (MCP) configuration files for risky servers and leaked credentials.

Audit MCP host configs (Claude Desktop, Cursor, Windsurf, VS Code, Cline, Continue, Gemini) plus project-local `.mcp.json` for supply-chain and credential-handling risks.

Rules: MCP-001..006. See `vulkro explain MCP-001` (etc.) for the rationale and remediation per rule.

Path discovery: * If `<path>` is given and exists as a file, scan that file only. * If `<path>` is given and is a directory, scan its `.mcp.json` and `mcp.json` (project-local). With `--include-host` the well-known host-config locations are also scanned. * If `<path>` is omitted, scan project-local under the current directory AND the well-known host-config locations: - `~/Library/Application Support/Claude/claude_desktop_config.json` (macOS) - `~/.config/Claude/claude_desktop_config.json` (Linux) - `%APPDATA%\Claude\claude_desktop_config.json` (Windows - documented for completeness; this CLI is macOS/Linux focused) - `~/.cursor/mcp.json`, `~/.cursor/mcp_config.json` - `~/.windsurf/mcp.json`, `~/.codeium/windsurf/mcp_config.json` - `~/.vscode/mcp_settings.json`, `~/.vscode-server/mcp_settings.json` - `~/.cline/cline_mcp_settings.json` - `~/.gemini/settings.json` - `~/.continue/mcp.json`

Exit codes: `0` no findings (or `--require-mcp` not set and no configs found). `1` findings at or above `--fail-on` threshold. `2` error: malformed JSON, IO failure, or `--require-mcp` set with no configs found.

Usage: vulkro mcp-audit [OPTIONS] [PATH]

Arguments:
[PATH]
Path to a specific MCP config file or to a project root. When omitted, the project-local CWD configs are scanned alongside the well-known host-config locations

Options:
-f, --format <FORMAT>
Output format

Possible values:
- table
- json
- sarif
- gh-pr
- gh-pr-inline-comments: GitHub PR per-finding **inline review comments**. NDJSON, one `{path, line, side, severity, rule_id, fingerprint, body}` object per line. Designed to be piped straight to a `gh api` loop so `vulkro gate` can drop comments next to the offending line on the Files Changed tab without going through a GitHub App. See the GitHub CLI integration guide at vulkro.com/docs
- github-annotations: GitHub Actions / GitLab CI **PR annotations**. One workflow-command line per finding (`::error file=...,line=...,endLine=...,title=<rule id>::<message> (<helpUri>)`). Printed straight to a CI job's stdout, GitHub turns each line into an inline annotation pinned to `file:line` on the Files Changed tab; GitLab CI's annotation parser reads the same grammar. When run against a baseline (`--gate-vs` / a `gate` flow) only NEW findings are annotated so a first run does not paper the PR
- gitlab-mr: GitLab Merge-Request comment: GitLab-flavored Markdown with collapsible blocks
- bitbucket-pr: Bitbucket Pull-Request comment: flat Markdown (Bitbucket does not render `<details>`)
- azure-pr: Azure DevOps Pull-Request comment: flat Markdown (shared with Bitbucket)
- junit
- csv
- cyclonedx: CycloneDX 1.6 JSON SBOM (uses `ScanResult.packages`)
- cyclonedx-1.7: CycloneDX 1.7 JSON SBOM. Same component shape as `cyclonedx`; emits the newer `specVersion`. Offered alongside 1.6 (which stays default)
- spdx: SPDX 2.3 JSON SBOM
- spdx3: SPDX 3.0.1 JSON-LD SBOM (`@context` + `@graph` of typed elements). Offered alongside SPDX 2.3, which stays the default for `--format spdx`
- cbom: CycloneDX 1.6 CBOM (Cryptographic Bill of Materials): one `cryptographic-asset` component per detected weak algorithm (MD5, SHA-1, ECB, RC4, DES, static IV, insecure RNG), with file:line occurrences inlined under `evidence.occurrences`. Compliance buyers (FedRAMP, post-quantum readiness reviews) ask for this as a distinct artefact from the library SBOM
- openvex: OpenVEX 0.2.0 exploitability statements for each dependency CVE (`affected` / `not_affected` / `under_investigation`), with `not_affected` backed by reachability analysis. Pairs with an SBOM
- cyclonedx-vex: CycloneDX 1.6 VEX: the same exploitability verdicts as `openvex`, in a CycloneDX `vulnerabilities[].analysis` document
- cbom-1.7: CycloneDX 1.7 CBOM. Same crypto-asset grouping as `cbom` plus a richer post-quantum descriptor. Offered alongside 1.6 (default)
- pdf: PDF render of the executive HTML report (requires `wkhtmltopdf` on PATH)
- ropa-md: GDPR Article 30 Records-of-Processing template - Markdown
- ropa-html: GDPR Article 30 Records-of-Processing template - HTML
- ndjson: Newline-delimited JSON: one finding per line plus a trailing summary line. Designed for SIEM ingestion and `jq` filter pipelines
- evidence-graph: Evidence-graph JSON (`evidence-graph/1.0`): a stable, versioned, AI-consumable document that composes endpoints, taint source -> sink flows, reachability verdicts, findings, and the dependency SBOM into one graph. Meant to be handed to an external AI agent as deterministic ground truth (Vulkro embeds no model; the agent brings its own). Pairs with `vulkro aggregate` for cross-repo linking. See `docs/ai-tool/`

[default: table]

--offline
Hard-disable every outbound network call for this run (sets VULKRO_OFFLINE=1). Blocks the CVE feed, the license heartbeat, the update check, webhooks, and any cloud AI endpoint; a loopback model (http://127.0.0.1, http://localhost) is still allowed. Equivalent to exporting VULKRO_OFFLINE=1, and the flag wins when both are set

--require-mcp
Treat "no MCP configs found" as an error (exit 2) instead of a success (exit 0). Useful for CI jobs that should fail when the expected config isn't present. Off by default

--include-host
Also scan the well-known host-config locations when `<path>` is given. Default: off when `<path>` is explicit (keep the audit scoped); on when `<path>` is omitted

--fail-on <SEVERITIES>
Severities that should produce a non-zero exit (comma list). Default `critical,high`. Same shape as `vulkro scan --fail-on`

[default: critical,high]

-h, --help
Print help (see a summary with '-h')