mcp-patch
Static security scanner for Python MCP server code.
43% of popular MCP servers have shell injection vulnerabilities. No existing tool does AST-level scanning with MCP context awareness. This one does.
Real CVEs this would have caught
- CVE-2025-53967 (Framelink Figma MCP) — shell injection via unsanitized tool parameters
- CVE-2025-6514 (mcp-remote, 437K downloads) — arbitrary command execution via unsanitized tool params
Install
pip install mcp-patch
mcp-patch scan my_server.py
Usage
# Scan a single file
mcp-patch scan server.py
# Scan a directory
mcp-patch scan ./servers/
Example output
Scanning server.py...
CRITICAL shell_injection line 14
subprocess.run(f"ls {path}", shell=True)
subprocess.run(shell=True) — tool param 'path' flows to shell
Fix: Use subprocess.run([cmd, shlex.quote(arg)]) without shell=True
HIGH path_traversal line 28
open(filename)
open(filename) — tool param 'filename' used as file path without validation
Fix: Use (base_dir / Path(filename).name).resolve() and verify result starts with base_dir
Found 2 issues (1 CRITICAL, 1 HIGH) in 1 file.
Checks
| Check | Severity | What it detects |
|---|---|---|
shell_injection |
CRITICAL | subprocess.run/Popen/call(f"...{param}", shell=True), os.system(), os.popen() with tool params |
path_traversal |
HIGH | open(param), Path(param) with a tool param passed directly as a path |
ssrf |
HIGH | requests.get/post(url), httpx.get(url), urllib.request.urlopen(url) where url is a tool param |
Only functions decorated with @tool or @mcp.tool() are scanned. Plain helper functions are ignored.
How it works
Pure stdlib. No network calls. No LLM. Parses your Python source with the ast module, finds @tool decorated functions, collects their parameter names, then walks each function body looking for dangerous call patterns where user-controlled params flow into dangerous sinks.
False positives
This is an MVP scanner — it prefers to over-report rather than miss real vulnerabilities. A path_traversal finding on open(filename) is real even if you have runtime validation elsewhere; the fix is to move validation into the same function so the scanner (and reviewers) can see it.
Development
python -m pytest tests/
No external dependencies. Python 3.9+.
Metadata
Release files for mcp-patch 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| mcp_patch-0.1.0.tar.gz | 10.0 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| mcp_patch-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 18.4 kB
Release files / mcp_patch-0.1.0.tar.gz
| Download URL | mcp_patch-0.1.0.tar.gz |
|---|---|
| Size | 10.0 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
1a8b62020365f4c1396ac973ac1697e1f9ee1ad37acbc8589e04f08612410b25
|
|
BLAKE2b-256 checksum How to use checksums |
d4cae3ca49760bb7e47ca1e4e43054335decf5b96da46103957709953e79ab0e
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.13.12
|
Release files / mcp_patch-0.1.0-py3-none-any.whl
| Download URL | mcp_patch-0.1.0-py3-none-any.whl |
|---|---|
| Size | 8.4 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
67d0fb560721e1d077eb881ba49d0d899042d85614fbf31d74dfb74975dc3396
|
|
BLAKE2b-256 checksum How to use checksums |
87f722449b6075345eeefe74ff608ad8ac0ca0f4b51f3d8815f20c1620a80998
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.13.12
|