Skip to main content

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 audit for 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

Metadata

Release files for mcp-bandit 0.1.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for mcp-bandit 0.1.1
File Size Uploaded
mcp_bandit-0.1.1.tar.gz 13.6 MB Details

Built distribution (wheel)

Table of built distributions (wheels) for mcp-bandit 0.1.1
File Interpreter ABI Platform
mcp_bandit-0.1.1-py3-none-any.whl Python 3 none any Details

Total release size: 13.6 MB

Release files / mcp_bandit-0.1.1.tar.gz

Download URL mcp_bandit-0.1.1.tar.gz
Size 13.6 MB
Tags Source
SHA-256 checksum
How to use checksums
6278b1c6b31343db7be6e5ead58383a613ff019ea1766e6ac5b6cae70d59dba4
BLAKE2b-256 checksum
How to use checksums
3653898ee52665e4bbfe37f012ddda97969dfd5695314e001719707e1f614f92
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.14.4

Release files / mcp_bandit-0.1.1-py3-none-any.whl

Download URL mcp_bandit-0.1.1-py3-none-any.whl
Size 39.6 kB
Tags Python 3
SHA-256 checksum
How to use checksums
156298291c45569d96820ec53758c9858c2db987a945933658dd4bfa501a4d51
BLAKE2b-256 checksum
How to use checksums
e3410015721c63958369f72e6e7cfedb74bbe2a9a7846b7c8e367386d69d8de3
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.14.4

Release history Release notifications | RSS feed

This release

0.1.1 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page