Security scanner for Model Context Protocol (MCP) servers
Project description
mcp-bandit
Beta — install via
pip install mcp-bandit. API and rule set may change before the 0.1.0 stable release.
Security scanner for Model Context Protocol (MCP) servers. Detects vulnerabilities via static AST analysis of Python source code and dynamic probing of live MCP servers.
37% of public MCP servers have SSRF vulnerabilities and there's no equivalent of
npm auditfor the MCP ecosystem. mcp-bandit fills that gap.
Installation
pip install mcp-bandit
# or
uv add mcp-bandit
Requires Python 3.11+.
Note: Until v0.1.0 stable ships to PyPI, install from source:
git clone https://github.com/giridharpandurangi/mcp-scan cd mcp-scan pip install -e .
Quickstart
# Scan a Python MCP server for vulnerabilities
mcp-bandit static --path my_server.py
# Scan a whole directory, fail on HIGH+ findings
mcp-bandit static --path src/ --severity HIGH --format sarif
Detection Rules
| Rule | Category | Severity | Description |
|---|---|---|---|
| MCP001 | SSRF | HIGH | Tainted URL passed to httpx/requests HTTP client |
| MCP002 | SSRF | LOW | HTTP client call missing timeout argument |
| MCP003 | SSRF | HIGH | Tainted URL passed to urllib.request.urlopen |
| MCP004 | Command Injection | CRITICAL | Tainted argument passed to subprocess with shell=True |
| MCP005 | Path Traversal | HIGH | Tainted path passed to open() |
| MCP010 | Secrets | CRITICAL | Hardcoded API key, token, or secret in source code |
| MCP011 | Secrets | CRITICAL | Hardcoded password in source code |
| MCP012 | Auth | MEDIUM | Insecure http:// URL used in auth/API context |
| MCP013 | Auth | HIGH | OAuth authorization URL missing PKCE code_challenge |
| MCP014 | Secrets | HIGH | Credential variable passed to print() or logging |
| MCP020 | Prompt Injection | HIGH | Tool description contains non-Latin-1 Unicode (> U+00FF) |
| MCP021 | Prompt Injection | HIGH | Tool description contains prompt injection phrases |
Usage
Static analysis
Scan a Python source file or directory:
mcp-bandit static --path src/
mcp-bandit static --path my_server.py
Dynamic probing
Connect to a running MCP server and send crafted probe inputs:
# stdio transport (command string)
mcp-bandit dynamic --target "python my_server.py"
# HTTP transport
mcp-bandit dynamic --target "http://localhost:8080"
Warning: Dynamic probing sends crafted inputs to live MCP tools. Always run against a development or staging server, never production.
Both modes combined
mcp-bandit all --path src/ --target "python my_server.py"
List all rules
mcp-bandit rules
CLI Options
Shared flags
| Flag | Description |
|---|---|
--format / -f |
Output format: rich (default), json, sarif, markdown |
--output / -o |
Write output to a file instead of stdout |
--severity / -s |
Minimum severity to report: CRITICAL, HIGH, MEDIUM, LOW, INFO |
--rule / -r |
Run only a specific rule (e.g. MCP001) |
--config / -c |
Path to a TOML or JSON configuration file |
Dynamic-only flags
| Flag | Description |
|---|---|
--allow-destructive |
Send probes to tools with destructive names (delete*, drop*, write*, etc.) |
--yes / -y |
Skip the --allow-destructive confirmation prompt |
--ssrf-listener |
Address of a controlled HTTP listener for confirmed SSRF detection |
Exit codes
| Code | Meaning |
|---|---|
0 |
Scan completed, no findings at or above HIGH severity |
1 |
Scan completed, one or more HIGH+ findings found |
2 |
Scan failed due to configuration or runtime error |
Output Formats
# Rich terminal table (default)
mcp-bandit static --path src/
# JSON (ScanResult schema)
mcp-bandit static --path src/ --format json
# SARIF 2.1.0 (for GitHub Code Scanning)
mcp-bandit static --path src/ --format sarif --output results.sarif
# Markdown report
mcp-bandit static --path src/ --format markdown --output report.md
Configuration File
Create a mcp-bandit.toml (or .json) file to set persistent options:
# mcp-bandit.toml
disabled_rules = ["MCP002"]
exclude_paths = ["tests/**", "docs/**"]
min_severity = "MEDIUM"
output_format = "rich"
trusted_validators = ["validate_url", "is_safe_path"]
Or embed it in pyproject.toml:
[tool.mcp-scan]
disabled_rules = ["MCP002"]
min_severity = "HIGH"
Load it with:
mcp-bandit static --path src/ --config mcp-bandit.toml
CLI flags always override config file values.
trusted_validators
The taint tracker is intra-procedural — it can't follow calls into helper functions. If you have a custom sanitizer (e.g. validate_url(x)), register its name to suppress false positives:
trusted_validators = ["validate_url", "is_safe_path", "check_host"]
GitHub Action
Add mcp-bandit to your CI pipeline:
# .github/workflows/security.yml
- name: Run mcp-bandit
uses: ./.github/actions/mcp-scan
with:
path: src/
severity: HIGH
format: sarif
The action uploads SARIF output as a workflow artifact and fails the step when HIGH+ findings are found.
Python API
from mcp_scan import Scanner, Finding, ScanResult, Severity
scanner = Scanner()
# Static analysis
result: ScanResult = scanner.scan_static("src/")
# Dynamic probing
import asyncio
result = asyncio.run(scanner.scan_dynamic("python my_server.py"))
# Filter findings
high_plus = [f for f in result.findings if f.severity >= Severity.HIGH]
Configuration via API
from mcp_scan.models import ScanConfig
config = ScanConfig(
disabled_rules=["MCP002"],
min_severity=Severity.MEDIUM,
exclude_paths=["tests/**"],
trusted_validators=["validate_url"],
)
scanner = Scanner(config=config)
Development
# Install with dev dependencies
uv sync --extra dev
# Run tests
pytest
# Run tests with coverage
pytest --cov=mcp_scan --cov-fail-under=80
# Run a specific test file
pytest tests/test_rules_ssrf.py -v
The test suite includes unit tests, snapshot tests for all formatters, and property-based tests (via Hypothesis) covering:
- ScanResult JSON round-trip fidelity
- Static analysis determinism
- No false positives on clean files (SSRF, secrets, injection rules)
- Severity filter monotonicity
- All rules applied to every analyzed file
- Taint sanitization suppresses findings
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_bandit-0.1.1.tar.gz.
File metadata
- Download URL: mcp_bandit-0.1.1.tar.gz
- Upload date:
- Size: 13.6 MB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.4
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
6278b1c6b31343db7be6e5ead58383a613ff019ea1766e6ac5b6cae70d59dba4
|
|
| MD5 |
15e5a99be559ce424fa5a9a6a9cf5349
|
|
| BLAKE2b-256 |
3653898ee52665e4bbfe37f012ddda97969dfd5695314e001719707e1f614f92
|
File details
Details for the file mcp_bandit-0.1.1-py3-none-any.whl.
File metadata
- Download URL: mcp_bandit-0.1.1-py3-none-any.whl
- Upload date:
- Size: 39.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.4
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
156298291c45569d96820ec53758c9858c2db987a945933658dd4bfa501a4d51
|
|
| MD5 |
66b1edd9b6a5b28b23620edbc56a7dbb
|
|
| BLAKE2b-256 |
e3410015721c63958369f72e6e7cfedb74bbe2a9a7846b7c8e367386d69d8de3
|