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.