Skip to main content

vulkro mcp serve

Run Vulkro as a Model Context Protocol server. Once configured, Claude Code, Claude Desktop, Cursor, Windsurf, Continue, VS Code's MCP client, and any other JSON-RPC 2.0 MCP client can call the scanner directly from inside the editor: tell the model "scan this project" and it will invoke Vulkro, parse the findings JSON, and walk through them inline.

The sibling subcommand vulkro mcp-audit audits MCP host configs (kept at its original top-level path for backwards compatibility) and is unrelated to mcp serve.

Usage​

vulkro mcp serve # stdio transport (default)
vulkro mcp serve --port 9100 # SSE transport on http://127.0.0.1:9100/sse

Flags​

FlagDescriptionDefault
--port <PORT>When set, binds an HTTP listener on 127.0.0.1:<PORT> and exposes /sse (event stream) and /messages (POST endpoint for JSON-RPC requests). When omitted, the default stdio transport is used.(omitted, stdio)
--offlineHard-disable every outbound network call for this run (sets VULKRO_OFFLINE=1). The flag wins when both are set.off

Exit codes​

CodeMeaning
0Clean shutdown. The host closed stdin (stdio mode) or Ctrl-C was hit (SSE mode).
1The server started but encountered a feature refusal on a tool call that the caller treated as fatal.
2Operational error: bind failure, transport-level IO error, bad argument to mcp serve.

Tools exposed​

Sixteen tools are surfaced over JSON-RPC. Schemas are advertised via the standard tools/list method; the per-tool input shapes below match what hosts render in the tool picker. Every tool is deterministic and calls no model.

ToolWhat it doesTier
scan_projectFull project scan, returns a scan_idFree
scan_fileFindings for one source fileFree
get_findingsRe-filter a prior scan by severityFree
proveThe hop-by-hop proof chain for one findingFree
explainMarkdown explainer for a rule idFree
list_rulesThe rule catalogueFree
suggest_fixesFix suggestions and remedies for a prior scanFree
verify_fixCheck a proposed diff on a temporary copyFree
verify_sarifCheck another tool's SARIF results against Vulkro's proofFree
inspect_repoMalicious-capability review of a just-cloned repoFree
scan_diffFindings on the lines a git diff touchedPro (changes)
assess_changeBlast radius, tests and new findings for a changePro (changes)
code_graphScan-free ranked code map and blast radiusPro (code-structure)
from_traceMap a stack trace onto code and findingsPro (code-structure)
evidence_graphThe evidence-graph/1.0 document for a repoPro (evidence-formats)
aggregateLink evidence graphs across reposPro (portfolio)

Every language is Free on every tool. A Free-tier call to a Pro tool does not stop the server: the call fails with JSON-RPC error -32001 (License required) and a data object carrying reason: "pro_required", the capability id from the table above, and a message that names the capability and how to unlock it. The session stays up and every Free tool keeps working. See Accounts.

scan_project​

scan_project({path, format?, offset?, limit?})

Run a full Vulkro scan on the project root at path. The result carries a scan_id that get_findings, suggest_fixes, prove and verify_fix can use without re-scanning.

For an AI agent, pass format: "compact". It returns the findings as one compact table (TOON text: the field names once, then one row per finding), most severe first, each row carrying the flagged line of code, with as many rows as fit in about 10,000 tokens. page.next_offset says where the next page starts. The default json is the full ScanResult (the same shape vulkro scan -f json emits), which is often larger than an agent host accepts as one tool result.

FieldTypeRequiredDescription
pathstringyesAbsolute path to the project root.
format"compact" | "json" | "summary"nocompact returns the agent-sized findings table (recommended for agents); json (default) returns the full ScanResult; summary returns counts only.
offsetintegernocompact only: the first row to return, from the previous page's page.next_offset.
limitintegernocompact only: the most rows on a page. By default a page holds as many rows as fit in about 10,000 tokens.

A compact row has finding_id, severity, confidence, disposition, rule, cwe, file, line, message and code (the flagged line).

scan_file​

scan_file({path})

Detect the project containing path, run the scanner, and return only the findings whose source file matches. The response carries a gate block: gating is how many of these rows may fail a build, and withheld_not_examined is how many carry "disposition":"not-examined" (a class Vulkro flags structurally but cannot decide offline, such as object-level and function-level authorization). Withheld rows are reported, not cleared, and must not be used to fail a build.

FieldTypeRequiredDescription
pathstringyesAbsolute path to a source file.

get_findings​

get_findings({scan_id, severity?, finding_ids?, format?, offset?, limit?})

Re-read findings from a prior scan_project call using the returned scan_id. Filter by severity, or pass the finding_id values from compact rows to get the full record (evidence, code snippet, trace, remediation) of just those findings.

FieldTypeRequiredDescription
scan_idstringyesA scan_id returned by a prior scan_project call in the same MCP session.
severity"critical" | "high" | "medium" | "low" | "info"noWhen set, return only findings at this severity. Case-insensitive.
finding_idsstring arraynoWhen set, return only these findings.
format"json" | "compact"nojson (default) returns full records; compact returns the compact table.
offset, limitintegernocompact only: paging, as for scan_project.

prove​

prove({finding_id? | file?, line?, scan_id? | path?})

Return the deterministic, hop-by-hop proof chain Vulkro computed for one finding, so the agent can check a reported issue against evidence rather than a bare claim. The payload carries the ordered taint, sanitizer-gap and related-context hops (each with file, line, a phrase and the captured source line), the reachability verdict, the exploitability grade, the supporting evidence rows, whether the finding's class has a runnable proof shape, and the data flows that touch the finding's file. Same evidence as vulkro prove.

An empty proof chain means no proven data flow was found for this finding. It never means the code is safe: proven: false is "unproven", not "clean".

FieldTypeRequiredDescription
finding_idstringone of finding_id / fileThe finding's stable, content-addressed id (<rule>:<fingerprint>). Preferred: it is deterministic across runs.
filestringone of finding_id / fileLocate the finding by source file. Ignored when finding_id is set.
lineintegerno1-based line to disambiguate when several findings share file.
scan_idstringnoReuse a scan from a prior scan_project or scan_diff call in this session.
pathstringunless scan_id is setAbsolute path to the project root, to scan fresh.

explain​

explain({rule_id})

Return a markdown explainer for the named rule. Accepts:

  • OWASP-category slugs (api1-broken-object-level-auth, ...)
  • MCP-audit rules (MCP-001 ... MCP-006)
  • Extension-audit rules (EXT-001 ... EXT-003)
  • Supply-chain compromise families (SUP-COMPROMISE-001 ... SUP-COMPROMISE-005)

Same content the CLI vulkro explain <ID> produces, rendered as portable markdown (no ANSI).

list_rules​

list_rules()

Enumerate every rule Vulkro knows about. Returns { id, title, owasp_category, owasp_category_display, severity, description, rule_page } for each. The OWASP catalogue, MCP-audit rules, extension-audit rules, and supply-chain compromise families are all included in one call.

suggest_fixes​

suggest_fixes({scan_id, finding_id?, rule?, verify?})

Return git-apply-ready fix suggestions for a prior scan_project call. This closes the fix loop inside MCP: scan_project -> suggest_fixes -> the agent applies the diff -> scan_project again to confirm the finding is gone. Same envelope as vulkro fix --format json: each fix carries finding_id, rule, file/line, a prose explanation, and a unified diff (patch_format: unified-diff).

An additive remedies array covers the knowledge-base classes (SQL injection in the driver's bound-parameter form, command injection, path traversal, open redirect, XSS, weak random, cookies, CORS, TLS and JWT verification, missing authentication; SSRF and mass assignment as advice) for JavaScript, TypeScript, Python, Go and Java. Each remedy has steps, a before / after example and, when the code shape is recognised, a patch whose verdict comes from applying it to a temporary copy of the project and scanning again: fixed, not-fixed or regressed (unverified when verify is false). Vulkro never applies a diff to your working tree.

FieldTypeRequiredDescription
scan_idstringyesA scan_id returned by a prior scan_project call in the same MCP session.
finding_idstringnoOnly this finding (its id or stable finding_id).
rulestringnoOnly findings of this rule id (for example js-taint-sql-001).
verifybooleannoRe-scan a patched temporary copy to give each remedy patch a verdict. Default true.

verify_fix​

verify_fix({diff, path? | scan_id?, finding_id?})

Re-check a fix the agent proposes before it is applied. Vulkro applies the unified diff (as git diff writes it; one or more files; each hunk is located by its context, so positions may be approximate) to a temporary copy of the project, scans the copy and a fresh copy of the original, and returns a verdict:

  • fixed: the target finding is gone and no new Medium-or-higher finding appeared in the changed lines.
  • not-fixed: the re-scan still reports it.
  • regressed: the change raised a new Medium-or-higher finding in the lines it changed, or the patched file no longer parses.

It also returns the cleared and remaining finding ids and any new findings. The working tree is never modified. Without finding_id, every finding in the touched files is a target.

FieldTypeRequiredDescription
diffstringyesThe unified diff, with --- a/<file> / +++ b/<file> headers relative to the project root.
pathstringunless scan_id is setAbsolute path to the project root.
scan_idstringnoA scan_id from a prior scan_project call; its project root is used when path is absent.
finding_idstringnoThe finding the diff is meant to fix (its id or stable finding_id).

verify_sarif​

verify_sarif({sarif, path})

Check another scanner's SARIF 2.1.0 results (any SARIF-producing tool, including an AI security review that writes SARIF) against Vulkro's deterministic proof. Each result is mapped to a vulnerability family (CWE first, then rule id, then message), the repository is scanned once, and every result gets a verdict:

  • proven: a Vulkro finding of the same family at or within 3 lines of the result carries machine-checkable proof. The proof chain, entry point and the risk model's actor, asset and severity are attached.
  • not-provable: Vulkro analysed the location and holds no proof; reason says what it found instead (a heuristic-only finding, an adequate guard, a parameterised query, no recognised source, no flow).
  • not-examined: outside this build's coverage (unsupported language, skipped or missing file, unmapped family).

Returns the vulkro-verify/1 report. not-provable is not a false-positive verdict, and not-examined says nothing about the result.

FieldTypeRequiredDescription
sarifstringyesAbsolute path to the SARIF 2.1.0 file to verify.
pathstringyesAbsolute path to the repository the results were produced on. Relative result paths resolve against it.

inspect_repo​

inspect_repo({path})

Vulkro Inspect as a pre-run check for a just-cloned repository: call it before running any of the repo's code (install scripts, build steps, examples). It surfaces malicious-capability shapes (reverse shells and instance-metadata grabs, credential reads and exfiltration, install and build hooks, obfuscated or packed source, trojan-source text, logic-bomb environment gates, worm behaviour, wallet access, remote code load, persistence, committed opaque binaries, dependency-integrity anomalies, and prompt-injection payloads aimed at an AI agent), grouped by capability, with the file and line of each site.

It runs over the full unfiltered finding set (no confidence floor), so a low-confidence shape is never dropped. It is a human-review surface, not a gate: it never certifies code safe, clean, or trusted, returns no proceed / allow / approve verdict, and every response carries a top-level disclaimer.

FieldTypeRequiredDescription
pathstringyesAbsolute path to the just-cloned repository root.

scan_diff​

scan_diff({path, base_ref?}). Pro: changes.

Verify your own change: scan the project, then return only the findings on the lines the diff added or modified against a git ref (the same changed-line scoping vulkro scan --gate-vs <ref> uses). It does not compare two scans. The whole tree is analysed so cross-file taint stays coherent, then findings are narrowed to the changed lines, so the agent sees what its edit introduced rather than the existing backlog.

Returns the standard ScanResult shape (findings and stats recomputed for the diff scope), a scan_id for get_findings, suggest_fixes and prove, and a diff block with the base ref, the changed files, and how many findings fell inside and outside the diff. An empty result means no new issue was found on the changed lines, not that the change is safe: findings on unchanged lines are deliberately hidden here.

FieldTypeRequiredDescription
pathstringyesAbsolute path to the git project root.
base_refstringnoGit ref to diff against (main, origin/main, a SHA). The diff is <base_ref>...HEAD. Default HEAD~1, the change introduced by the most recent commit, so commit the edit first or pass a ref.

assess_change​

assess_change({path, symbol?, base?}). Pro: changes.

Assess whether a change is safe and non-breaking. It fuses the structural code graph with a full security scan, and is read-only.

  • Before the change: pass symbol to get its blast radius (the transitive set of symbols that depend on it), which tests to run, and whether it already carries findings or sits on a taint path.
  • After the change: omit symbol to assess the diff against base. It returns the dependents and tests the change touches plus the findings on the changed lines, with a "safe to proceed" or "review N" verdict.

Structural impact is a floor: unresolved calls are not counted.

FieldTypeRequiredDescription
pathstringyesAbsolute path to the project root.
symbolstringnoBefore-change mode: the symbol about to be edited, as file::name or a bare name. Omit for after-change (diff) mode.
basestringnoAfter-change mode: git ref to diff against. Default HEAD (the uncommitted working-tree change); for example HEAD~1 or origin/main.

code_graph​

code_graph({path, mode?, symbol?, query?, substring?, context?, offset?, max_symbols?, max_tokens?, format?}). Pro: code-structure.

A ranked, symbol-level code graph for a project, built without running a security scan and cached between calls, so it is fast. Use it to navigate code and to see the blast radius of a change before editing. The same modes are available from the command line as vulkro code-graph.

  • mode: "map" (default): the most important symbols, ranked. Use it to get oriented in an unfamiliar codebase.
  • mode: "search" with query: every place a name is defined, called, referenced (mocks, values) or imported, with the code lines and the enclosing function, production files before tests, paged with offset. It also finds calls the graph could not resolve. Use it instead of grep plus file reads.
  • mode: "body" with symbol: the source of that one function, with line numbers, its callees and its callers, instead of its whole file.
  • mode: "impact": the transitive set of symbols that reach symbol (its blast radius), split into tested and untested.
  • mode: "callers" / mode: "callees": the direct neighbours of symbol.

Pass format: "toon" to get the same content as compact text (lists as tables), which is smaller than JSON.

This is code structure only: it asserts nothing about vulnerabilities, and every reach or impact count is a floor (unresolved calls are disclosed in the honesty header). For the security view of a change, use scan_diff.

FieldTypeRequiredDescription
pathstringyesAbsolute path to the project root.
mode"map" | "impact" | "callers" | "callees" | "search" | "body"noQuery type. Default map.
symbolstringfor impact, callers, callees and bodyTarget symbol, as file::name or a bare name. A bare method name also finds Class.method.
querystringfor searchThe name to find (default: the name part of symbol). Matched as a whole identifier when it is one.
substringbooleannosearch: match the query anywhere, not only as a whole identifier.
contextintegernosearch: lines of context around each hit. Default 1, at most 5.
offsetintegernosearch: the first hit to return, from the previous page's page.next_offset.
max_symbolsintegernoMaximum rows to return (in search, hits per page). Default 40; the output reports the true total and how to get the rest.
max_tokensintegernoHard token ceiling; the lowest-ranked rows are trimmed to fit.
format"json" | "toon"nojson (default) or toon, the same content as compact text.

from_trace​

from_trace({path, trace}). Pro: code-structure.

Map a production stack trace onto the code graph for crash triage. Each file:line frame is resolved to its enclosing symbol, with that symbol's findings and blast radius, so the agent knows which frame to start at. Fuses the code graph with a scan; read-only.

FieldTypeRequiredDescription
pathstringyesAbsolute path to the project root.
tracestringyesThe stack trace text, in any common language format; file:line frames are extracted.

evidence_graph​

evidence_graph({path, repo_name?}). Pro: evidence-formats.

Scan a project root and return its evidence-graph/1.0 document: the same shape vulkro scan <repo> --format evidence-graph emits. This is a stable, versioned, model-free view of the repo's attack surface: endpoints with route shape and auth tier, outbound calls, taint flows, findings with reachability verdicts, and dependency evidence. Feed the output to aggregate for cross-repo linking.

FieldTypeRequiredDescription
pathstringyesAbsolute path to the project root to scan.
repo_namestringnoLabel for this repo in the graph. Defaults to the detected project name. Set it when you plan to aggregate several repos and want stable, human-readable node names.

aggregate​

aggregate({graphs}). Pro: portfolio.

Link two or more evidence-graph documents to surface cross-repo edges (a caller in one repo hitting an endpoint in another, especially unauthenticated targets). Same linker as vulkro aggregate. Pass the raw evidence-graph JSON objects inline (one per repo), each produced by the evidence_graph tool.

FieldTypeRequiredDescription
graphsarray of objectsyesTwo or more evidence-graph/1.0 JSON documents, one per repo, each as returned by the evidence_graph tool.

Resources: the local AI memory​

Besides tools, the server exposes the advisory AI memory store (the records under .vulkro/ai/ in the nearest ancestor of the server's working directory that has one) as read-only MCP resources, so a host can read prior triage, fix and hunt interactions. resources/list returns up to 200 records, newest first, as vulkro-ai-memory:///<id> URIs, and resources/read returns one record. Listing never creates the store, and nothing in it can change a finding, a severity or an exit code.

Workflows​

Verify a change before you commit it​

  1. scan_project({path, format: "compact"}) once, to get the findings and a scan_id.
  2. After the edit, scan_diff({path, base_ref}) (Pro) to see only what the change introduced.
  3. prove({scan_id, finding_id}) on anything reported, to read the evidence before acting on it.
  4. suggest_fixes({scan_id}), then verify_fix({diff, scan_id, finding_id}) on the proposed patch, before the agent applies it.

AI-usable evidence-graph workflow​

evidence_graph and aggregate (both Pro) compose into a deterministic, model-free pipeline an agent can drive end to end without leaving MCP:

  1. Call evidence_graph({path}) once per repo. Each call returns a versioned evidence-graph/1.0 document (endpoints, outbound calls, taint flows, findings, dependencies).
  2. Collect those documents and pass them to aggregate({graphs}).
  3. aggregate returns candidate cross-repo links (route-shape and method matches, deterministic) with the callee auth posture attached.

Vulkro supplies the evidence; the agent brings its own reasoning about whether a linked flow is a real risk. Nothing in this path calls a model, so the inputs an agent reasons over are reproducible.

Set up in Claude Code, Cursor and Codex​

Claude Code​

Register the server once:

claude mcp add vulkro -- vulkro mcp serve

Optionally add the Vulkro skill too, which teaches Claude Code when and how to call the CLI (see Claude Code skill):

curl -fsSL https://dist.vulkro.com/skill-install.sh | bash -s -- --agent claude-code

Cursor​

Add the server to ~/.cursor/mcp.json:

{
"mcpServers": {
"vulkro": {
"command": "vulkro",
"args": ["mcp", "serve"]
}
}
}

Restart Cursor. The skill installer's --agent cursor option also writes a Vulkro rules file to ~/.cursor/rules/vulkro.md.

Codex​

Codex uses the Vulkro skill rather than the MCP server: the installer appends the Vulkro instructions to ~/.codex/AGENTS.md (it never overwrites the file), and Codex then runs the vulkro CLI directly.

curl -fsSL https://dist.vulkro.com/skill-install.sh | bash -s -- --agent codex

VS Code agent mode​

The VS Code extension registers this server for the editor's agent automatically, launched with VULKRO_OFFLINE=1, and adds its own language-model tools. No manual config is needed.

Other MCP hosts​

Claude Desktop​

Edit ~/Library/Application Support/Claude/claude_desktop_config.json (macOS), ~/.config/Claude/claude_desktop_config.json (Linux), or %APPDATA%\Claude\claude_desktop_config.json (Windows) and add a mcpServers.vulkro entry:

{
"mcpServers": {
"vulkro": {
"command": "vulkro",
"args": ["mcp", "serve"]
}
}
}

Restart Claude Desktop. The tool picker should now list the Vulkro tools (scan_project, scan_file, prove, and the rest). Ask the assistant "scan this project" with a project directory open and the model will call scan_project with the path.

Windsurf​

Edit ~/.windsurf/mcp.json (or ~/.codeium/windsurf/mcp_config.json on older versions):

{
"mcpServers": {
"vulkro": {
"command": "vulkro",
"args": ["mcp", "serve"]
}
}
}

Restart Windsurf.

Continue (VS Code)​

Continue reads MCP server config from ~/.continue/mcp.json. The shape is identical:

{
"mcpServers": {
"vulkro": {
"command": "vulkro",
"args": ["mcp", "serve"]
}
}
}

Reload the Continue extension.

VS Code MCP client​

The VS Code MCP client reads from ~/.vscode/mcp_settings.json (or ~/.vscode-server/mcp_settings.json in Remote setups). Same shape.

Transport details​

Stdio is the default because every major MCP host launches its servers as child processes and talks JSON-RPC over the standard streams. The server reads one JSON-RPC request per line on stdin and writes one response per line on stdout; stderr is reserved for the optional VULKRO_MCP_LOG=1 trace stream.

The SSE transport (opt-in via --port) binds an HTTP listener on 127.0.0.1:<port> only (never 0.0.0.0). Clients POST JSON-RPC requests to /messages and read responses from a long-lived GET /sse stream. The server emits an event: endpoint SSE message on connection with the POST endpoint path; subsequent responses arrive as event: message payloads.

Environment variables​

NameEffect
VULKRO_MCP_LOG=1Emit per-request trace lines on stderr. Off by default so the stdio JSON-RPC stream stays clean of side-band output.
VULKRO_OFFLINE=1Disable any outbound HTTP call during scans triggered through the MCP surface. Same semantics as the top-level scan subcommand.

Other VULKRO_* variables honoured by vulkro scan (e.g. VULKRO_QUIET_PRESET_HINT, VULKRO_GIT_BLAME, VULKRO_AUTH_MIDDLEWARE_NAMES) apply transparently to scans triggered through the MCP surface.

Read-only by design​

There is no apply_fix, no write_file, no tool that mutates the user's repo. suggest_fixes and verify_fix work on temporary copies and hand the diff back; the agent or the user applies it. vulkro fix already prints diffs to stdout for the user to apply by hand, and the MCP surface holds that line.

  • vulkro mcp: the command group.
  • vulkro-sf mcp serve: the Salesforce MCP server, with the Vulkro Cloud workspace tools.
  • Desktop console: the same scanner exposed through a local web app instead.
  • vulkro mcp-audit: audit MCP host configs for supply-chain and credential-handling risks (the inverse direction: scan the configs that launch MCP servers, rather than be one).
  • vulkro explain: the per-rule explainer that backs the MCP explain tool.