Skip to main content

Static security + correctness audit for MCP server repos. Ships four checks. v0.5 fixes a v0.4 false-positive family where list-concatenation (e.g. `["kubectl"] + args + ["get"]`) was misclassified as string-interpolation in command_injection.

Project description

mcp-audit

Static security + correctness audit for MCP server repos.

pip install mcp-audit
mcp-audit                     # scan the current directory
mcp-audit /path/to/repo       # scan a specific repo
mcp-audit --json              # machine-readable output
mcp-audit --check fastmcp_wrapper_layer  # one check only
mcp-audit --list-checks       # list available checks

Exit codes: 0 clean, 1 at least one finding, 2 usage error.

What it checks

Check ID Severity What it finds
starlette_badhost HIGH / MED Starlette < 1.0.1 in pyproject.toml, requirements*.txt, uv.lock, poetry.lock, pdm.lock. BadHost (CVE-2026-48710) lets a crafted HTTP Host header bypass path-based authorization. Affects any HTTP/SSE-transport MCP server. Stdio servers are unaffected.
fastmcp_wrapper_layer HIGH Sync @mcp.tool() functions that call asyncio.run(...) inside their body. FastMCP invokes tools inside an already-running event loop; asyncio.run() raises RuntimeError. Looks fine in unit tests, dies on the first real protocol call.
tool_input_validation LOW @mcp.tool() parameters typed as bare str / bytes / Any / list[Any] / dict[..., Any] or with no annotation at all. The schema FastMCP exposes to the LLM is the substrate prompt-injection-via-tool-description attacks rely on; constraining it (Annotated[str, Field(max_length=N)], Literal[...], Pydantic models) closes the window without losing expressiveness. Hygiene check, not a CVE — expect findings even on well-written servers. Added in v0.2.
command_injection HIGH @mcp.tool() functions where a tool parameter (or a local tainted via assignment / .format() / string concat) flows into os.system, os.popen, or subprocess.* with shell=True or a tainted-interpolated command string. v0.4 added same-file cross-function taint propagation: the analyzer now follows local helper calls (positional + keyword binding, recursion-visited guard), so tool -> helper -> sink flows are caught. Cross-file taint remains out of scope. The list-of-args / no-shell pattern is correctly NOT flagged. Added in v0.3, cross-function in v0.4.

More checks are landing — hard-coded secrets, write-API tools missing a FORBIDDEN_NAMES-style guardrail, read-only-by-default violations, path traversal in filesystem-touching servers.

Output format

$ mcp-audit examples/bad/
[HIGH  ] starlette_badhost @ uv.lock
           uv lockfile pins starlette==0.36.3 — vulnerable to BadHost (CVE-2026-48710). Patched in 1.0.1.
           -> Upgrade Starlette to >=1.0.1 (the BadHost patch). If FastAPI pulls Starlette transitively, pin it explicitly. ...

[HIGH  ] fastmcp_wrapper_layer @ server.py:18
           tool 'fetch_url' (def) calls asyncio.run() inside its body. FastMCP invokes tools inside an already-running event loop, and asyncio.run() raises RuntimeError when nested. This will fail at the first real MCP protocol call even if every unit test passes.
           -> Convert the tool to `async def` and replace `asyncio.run(...)` with `await`. ...

mcp-audit: 2 finding(s) — 2 high

--json emits one object: {"root": "...", "finding_count": N, "findings": [...]}. Each finding has check, severity, path, line, message, remediation.

What this is not

  • It is not a runtime sandbox. Static analysis only.
  • It does not install your venv to introspect it. It reads what's declared (manifests + lockfiles + source).
  • It will not detect every vulnerability — only the classes its checks know about. Treat zero findings as "no known issues from this tool," not as a clean bill.

Background

Development

git clone https://github.com/Alienbushman/mcpdone-samples
cd mcpdone-samples/mcp-audit
pip install -e ".[dev]"
pytest
python smoke_test.py

To add a check: drop src/mcp_audit/checks/<name>.py exposing a module-level CHECK_ID and a check(root: Path) -> list[Finding] callable. Register it in src/mcp_audit/checks/__init__.py. Add fixtures + tests under tests/.

License

MIT.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

mcpdone_audit-0.5.0.tar.gz (25.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

mcpdone_audit-0.5.0-py3-none-any.whl (22.2 kB view details)

Uploaded Python 3

File details

Details for the file mcpdone_audit-0.5.0.tar.gz.

File metadata

  • Download URL: mcpdone_audit-0.5.0.tar.gz
  • Upload date:
  • Size: 25.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for mcpdone_audit-0.5.0.tar.gz
Algorithm Hash digest
SHA256 1a4089fa30a7dc5af9a5aca98f3ede3139d59fb1e03883674482881625849408
MD5 9efc56ba057ee5092116e05fd4707d1f
BLAKE2b-256 80c585867f8a61fe15499c256d481490deb4c65dff447a52ce3592d9f9246dcd

See more details on using hashes here.

Provenance

The following attestation bundles were made for mcpdone_audit-0.5.0.tar.gz:

Publisher: release.yml on Alienbushman/mcpdone-samples

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file mcpdone_audit-0.5.0-py3-none-any.whl.

File metadata

  • Download URL: mcpdone_audit-0.5.0-py3-none-any.whl
  • Upload date:
  • Size: 22.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for mcpdone_audit-0.5.0-py3-none-any.whl
Algorithm Hash digest
SHA256 ec1d41d5a34be868d2c6026b8b3297e8e334e8a5f27f9a2083ac350db3d1d9f8
MD5 27f63682b0a3f9f1156c6c7728dd34f9
BLAKE2b-256 ec30c0ea5c43bfecf91a3df0d342d00bdc661e20ecc266e0bb284e85244bab8c

See more details on using hashes here.

Provenance

The following attestation bundles were made for mcpdone_audit-0.5.0-py3-none-any.whl:

Publisher: release.yml on Alienbushman/mcpdone-samples

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page