Static analyzer for MCP servers. Spec: ../docs/static-rules.md.
Implemented (Layer 1 — Python AST + captured JSON):
@something.tool / @something.tool()) over .py filestools/list JSON (mcp-witness-analyze captured.json)mcp-server-fetch)$comment; catches real mcp-server-time pattern)readOnlyHint: true on a tool named delete_record, etc.)overbroad_combinations)shell=True, os.system, os.popen)mcp-server-fetch + mcp-server-http-request SSRF surface from captured tools/list alone)mcp-witness-analyze <path> CLI with text and JSON output, severity filtering, and CI-friendly exit codes (--fail-on)mcp-witness-lint-scenarios scenarios/ — catches the null-byte-smuggling class of bug + parse errors + schema violationsNot yet implemented:
Server.list_tools() discovery pattern (only FastMCP decorators today)| File | Purpose |
|---|---|
discover.py |
Walks a path, parses Python files with ast, finds @.tool decorated functions |
rules.py |
The three detection rules, plus the rule registry |
analyze.py |
analyze_path() — orchestrator: discover then run all rules |
types.py |
Finding, DiscoveredTool dataclasses |
__main__.py |
CLI |
lint_scenarios.py |
YAML lint tool (separate CLI: mcp-witness-lint-scenarios) |
tests/ |
Rule fixtures (vulnerable + safe examples) + tests; scenario-lint tests |
# Analyze an MCP server source tree:
mcp-witness-analyze /path/to/some-mcp-server
# JSON output, gate at high severity:
mcp-witness-analyze --format json --fail-on critical ./src
# Lint scenario files:
mcp-witness-lint-scenarios scenarios/
pip install -e ".[dev]"
pytest analyzer/tests/
Layer 1 is heuristic. Expected v0.1 precision (vibes-tuned, not corpus-tuned):
medium for that reason._PATH_GUARD_ATTRS list captures the common patterns (is_relative_to, resolve, realpath, commonpath, startswith). Helpers in separate functions are not yet followed (inter-procedural is v0.2).shell=True / os.system / os.popen usage. Does not yet trace taint from tool params; flags any usage in a tool function body.