Security scanner for Python MCP server code
Project description
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+.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file mcp_patch-0.1.0.tar.gz.
File metadata
- Download URL: mcp_patch-0.1.0.tar.gz
- Upload date:
- Size: 10.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1a8b62020365f4c1396ac973ac1697e1f9ee1ad37acbc8589e04f08612410b25
|
|
| MD5 |
46dc53015f18a4bfed9092c67c3faa36
|
|
| BLAKE2b-256 |
d4cae3ca49760bb7e47ca1e4e43054335decf5b96da46103957709953e79ab0e
|
File details
Details for the file mcp_patch-0.1.0-py3-none-any.whl.
File metadata
- Download URL: mcp_patch-0.1.0-py3-none-any.whl
- Upload date:
- Size: 8.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
67d0fb560721e1d077eb881ba49d0d899042d85614fbf31d74dfb74975dc3396
|
|
| MD5 |
e277fb3d71523b86328d2e54d84a4906
|
|
| BLAKE2b-256 |
87f722449b6075345eeefe74ff608ad8ac0ca0f4b51f3d8815f20c1620a80998
|