MCP servers are the new attack surface. Every agent that mounts a GitHub MCP server, a filesystem MCP server, or a custom tool server is trusting that server's tool descriptions, input schemas, and handler code. Most of that trust is misplaced.
We built mcp-scan — an open-source CLI that connects to an MCP server via stdio, introspects its tool manifest, and runs deterministic security checks against both the protocol metadata and the Python source code.
Install: pip install velox-mcp-scan
Repo: github.com/veloxlabsio/mcp-scan
Demo page: veloxlabs.dev/mcp-scan
What it catches
6 checks ship today. Two work against any MCP server (no source access needed). Four require pointing at the server's Python source with --source.
Protocol-level:
-
MCPA-001 (Critical) — Prompt-injection markers in tool descriptions. Imperative verbs,
<system>tags, exfiltration phrases. This is the Trail of Bits "line jumping" attack — the payload fires when the agent connects, before any tool call. - MCPA-002 (High) — ANSI escapes, C0 control chars, zero-width characters hiding payloads in descriptions. The terminal renders them invisible; the LLM reads them as instructions.
Source-code AST:
-
MCPA-010 (Critical) — Path traversal in file handlers. Flags
open()/read_text()on user-derived paths withoutis_relative_to()containment.resolve()alone is not sufficient — that's the EscapeRoute bypass (CVE-2025-53109/53110). -
MCPA-012 (Critical) — Shell injection.
subprocesswithshell=Trueand dynamic command strings. Catches the pattern from CVE-2025-68144 (Anthropic Git MCP). -
MCPA-060 (High) — SSRF sinks. HTTP client calls (
httpx,requests,urllib) with variable URLs. The guard detection does lightweight dataflow tracking —urlparse(url)alone doesn't count as validation. It traces the URL variable through parse results to hostname membership checks against trusted collections. -
MCPA-070 (High) — Hardcoded secrets. Known prefixes (
sk-,ghp_,AKIA,xoxb-) and high-entropy strings in secret-named variables.
19 more checks are planned for v0.1 — see docs/checks.md for the full roadmap.
Design decisions that matter
Fail-closed. If introspection fails — server hangs, tools/list errors, timeout — the scanner produces a CRITICAL finding, not an empty clean report. A security tool that silently passes on errors is worse than no tool.
No LLM required. Every check is deterministic AST analysis and pattern matching. No API keys, no cloud calls, no probabilistic scoring. Runs fully offline in ~2 seconds.
Capability-aware introspection. If a server only advertises tools (not resources or prompts), a failed resources/list call is informational, not critical. The scanner respects what the server actually claims to support.
Dataflow-tracked SSRF guards. This was the hardest check to get right. Early versions counted urlparse() anywhere in the function as "guarded." Six review rounds later, the detection traces url → urlparse(url) → parsed → parsed.hostname in ALLOWED_HOSTS and validates that the membership target is a trusted collection (literal set, all-literal local variable, or module-scope constant). Equality comparisons (==) are rejected — only in / not in count. Local aliases of function parameters or attributes are rejected. Any non-literal assignment in any branch poisons a local name.
Four limitations are honestly documented: single-hop tracking, trust-based helper names (validate_url(url)), module-scope over-trust, and no DNS resolution as a guard pattern.
Try it in 30 seconds
The repo ships with vulnerable-mcp — a deliberately broken MCP server with 5 planted vulnerabilities. The scanner catches all 5 (7 findings total, zero false positives).
pip install velox-mcp-scan
# Protocol-level only (2 findings)
mcp-scan scan --stdio "python3 -m vulnerable_mcp.server"
# Protocol + source (7 findings, all 5 vulns caught)
mcp-scan scan --stdio "python3 -m vulnerable_mcp.server" --source ./vulnerable_mcp
Output formats: terminal (default), JSON (-f json), Markdown (-f markdown). Non-zero exit on findings — drop it into CI as a gate.
What's next
- MCPA-020 — Curated MCP dependency CVE matching
- MCPA-061 — Markdown image / auto-link exfiltration vector detection
-
HTTP/SSE transport —
--urlfor remote servers - OAuth 2.1 DCR flow auditing
Why we built this
Security tools that ship without a reference vulnerable target are hard to evaluate. We wanted something you could install, point at a deliberately broken server, and see exactly what it catches — on your own laptop, in 30 seconds, without connecting to anything real.
MCP is early. The default configs are copy-pasted from examples. There's no equivalent of SELinux for tool-use permissions yet. The window to harden these before they become the 2027 supply-chain story is open right now.
Built by Velox Labs — AI Security & Platform Engineering.
Links:
- PyPI: velox-mcp-scan
- GitHub: veloxlabsio/mcp-scan
- Demo: veloxlabs.dev/mcp-scan
- Check catalog: docs/checks.md
Top comments (0)