Static security scanner for MCP servers — Python + TypeScript/JavaScript, zero dependencies, 36 rules
Project description
mcpaudit
Static security scanner for MCP servers -- zero dependencies, AST-based, 20 rules, <200ms.
python 3.11+ | zero dependencies | MIT license
Quick Start
# Install
pip install -e .
# Scan a file
mcpaudit /path/to/server.py
# Scan a directory
mcpaudit /path/to/mcp-servers/
# JSON output for CI/CD
mcpaudit /path/to/server.py --format json
# HTML report
mcpaudit /path/to/server.py --format html --output report.html
# SARIF output (GitHub Code Scanning, VS Code SARIF Viewer)
mcpaudit /path/to/server.py --format sarif -o results.sarif
# Generate default config file
mcpaudit --init
Why mcpaudit?
| AI-Infra-Guard (Tencent) | mcpaudit | |
|---|---|---|
| Requirements | Docker + 4GB RAM + LLM API key | Python 3.11+ (stdlib only) |
| Speed | Minutes (API calls) | <200ms (static analysis) |
| Method | ReAct agent with LLM | AST + regex pattern matching |
| Cost | LLM API calls | $0 (everything local) |
| Dependencies | Docker, LLM SDK | None (stdlib only) |
| CI/CD | Manual | Exit codes + JSON + HTML + SARIF |
Security Rules (20)
Every finding includes a CWE reference for standards traceability and a confidence level (HIGH, MEDIUM, or LOW) indicating detection accuracy.
| Rule ID | Severity | What it Detects | CWE |
|---|---|---|---|
CMD-001 |
CRITICAL | Command injection -- subprocess with shell=True |
CWE-78 |
CMD-002 |
HIGH | Command injection -- subprocess with f-strings or string concatenation | CWE-78 |
SQL-001 |
CRITICAL | SQL injection -- queries with string interpolation | CWE-89 |
SEC-001 |
CRITICAL | Hardcoded secrets -- API keys, tokens, passwords in source code | CWE-798 |
DESER-001 |
CRITICAL | Unsafe deserialization -- yaml.load(), arbitrary code execution |
CWE-502 |
PATH-001 |
HIGH | Path traversal -- file operations on user input without resolve()/is_relative_to() |
CWE-22 |
PATH-002 |
MEDIUM | Hardcoded absolute paths -- /Users/, /home/, C:\Users\ |
-- |
PERM-001 |
HIGH | Excessive permissions (chmod 777) |
CWE-250 |
AUTH-001 |
MEDIUM | Missing authentication in server code | CWE-306 |
CORS-001 |
MEDIUM | CORS wildcard (Access-Control-Allow-Origin: *) |
CWE-942 |
VAL-001 |
MEDIUM | Missing input validation in MCP handler functions | -- |
REDOS-001 |
MEDIUM | ReDoS -- regex with nested quantifiers | -- |
RATE-001 |
LOW | Missing rate limiting in server code | CWE-770 |
TEMP-001 |
LOW | Insecure temporary file creation | CWE-377 |
SSRF-001 |
HIGH | Server-Side Request Forgery -- HTTP requests with dynamic URLs | CWE-918 |
FILE-001 |
HIGH | Unsafe file write -- open() with dynamic path in write mode |
CWE-73 |
ERR-001 |
MEDIUM | Missing error handling in MCP tool handler functions | CWE-755 |
LOG-001 |
MEDIUM | Sensitive data in logs -- logging secrets, tokens, passwords | CWE-532 |
RES-001 |
MEDIUM | Resource exhaustion -- unbounded reads, missing timeouts | CWE-400 |
INFO-001 |
LOW | Information disclosure -- detailed exceptions returned to users | -- |
Configuration File
mcpaudit supports a .mcpaudit.yml configuration file for persistent project-level settings.
Generate default config
mcpaudit --init
This creates a .mcpaudit.yml in the current directory with all options commented out.
Use a custom config
mcpaudit /path/to/code --config .mcpaudit.yml
If no --config flag is passed, mcpaudit automatically loads .mcpaudit.yml from the current working directory when present.
Configuration options
# .mcpaudit.yml
# Disable specific rules by ID
exclude_rules:
- AUTH-001
- RATE-001
# Skip files/directories matching these glob patterns
exclude_paths:
- "tests/*"
- "vendor/*"
# Only show findings at or above this severity level
# Values: critical, high, medium, low, info
severity_threshold: medium
# Default output format: text, json, html, sarif
output_format: text
CLI flags (--format, --severity) override config file values when both are specified.
Output Formats
Terminal (default)
============================================================
MCP Security Audit -- Resultados
============================================================
Archivos escaneados: 1
Lineas escaneadas: 150
Hallazgos totales: 3
============================================================
[FILE] server.py
Grade: C | 3 hallazgos | 12.3ms
[!!] [CMD-001] Command Injection (shell=True)
Linea 42: subprocess.run() con shell=True.
Codigo: subprocess.run(cmd, shell=True)
Fix: Usar lista de argumentos sin shell=True.
[!] [PATH-001] Posible Path Traversal
Linea 18: Funcion open() sin validacion de path.
Fix: Usar path.resolve() y path.is_relative_to(base_dir).
[-] [AUTH-001] Sin autenticacion detectada
Linea 1: El servidor MCP no implementa autenticacion.
============================================================
JSON (for CI/CD)
{
"summary": {
"files_scanned": 1,
"total_findings": 3,
"overall_grade": "C",
"severity_counts": {
"CRITICAL": 1,
"HIGH": 1,
"MEDIUM": 1,
"LOW": 0,
"INFO": 0
},
"exit_code": 2
},
"results": [...]
}
Each finding in results includes cwe (e.g., "CWE-78") and confidence (e.g., "HIGH") fields.
HTML Report
Self-contained dark-themed HTML with dashboard, severity breakdown, and detailed findings. Generate with:
mcpaudit /path/to/code --format html --output report.html
SARIF (Static Analysis Results Interchange Format)
SARIF 2.1.0 output for integration with industry-standard tooling:
- GitHub Code Scanning -- upload via
github/codeql-action/upload-sarif - VS Code SARIF Viewer -- view findings inline in your editor
- Azure DevOps -- native SARIF support in pipelines
- Any SARIF-compatible viewer -- standard OASIS format
mcpaudit /path/to/code --format sarif -o results.sarif
Each SARIF result includes security-severity scores (0--10 scale), CWE tags, confidence-based precision levels, and inline fix suggestions.
Upload to GitHub Code Scanning
# In your GitHub Actions workflow
- run: mcpaudit . --format sarif -o results.sarif
- uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: results.sarif
Grading System
| Grade | Weighted Score | Meaning |
|---|---|---|
| A+ | 0 | No findings |
| A | 1-2 | Minor issues only |
| B | 3-5 | Some medium issues |
| C | 6-10 | High severity findings |
| D | 11-20 | Multiple high severity |
| F | 21+ | Critical issues present |
Exit Codes
| Code | Meaning |
|---|---|
0 |
No HIGH or CRITICAL findings |
1 |
HIGH findings present |
2 |
CRITICAL findings present |
CI/CD Integration
GitHub Actions
# .github/workflows/security.yml
name: Security Scan
on: [push, pull_request]
jobs:
scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.11'
- run: pip install -e path/to/mcpaudit
- run: mcpaudit . --format json --output results.json
- run: mcpaudit . --format html --output report.html
- run: mcpaudit . --format sarif --output results.sarif
- uses: actions/upload-artifact@v4
with:
name: security-report
path: |
report.html
results.json
- uses: github/codeql-action/upload-sarif@v3
if: always()
with:
sarif_file: results.sarif
A reusable workflow is included at .github/workflows/security-scan.yml.
Pre-commit Hook
cp hooks/pre-commit .git/hooks/pre-commit
chmod +x .git/hooks/pre-commit
Blocks commits with HIGH or CRITICAL findings.
Baseline (Suppress Known Findings)
Accept existing findings to only fail on new security issues:
# Step 1: Create baseline from current state
mcpaudit . --create-baseline .mcpaudit-baseline.json
# Step 2: In CI/CD, scan with baseline — only NEW findings trigger failure
mcpaudit . --baseline .mcpaudit-baseline.json
Fingerprints are stable across line number changes — a finding is only un-suppressed if the code itself changes.
Architecture
mcpaudit/
|
|-- __init__.py # Package exports (MCPSecurityScanner, models)
|-- __main__.py # CLI entry point (mcpaudit command)
|-- scanner.py # Core scanning engine
|-- models.py # Data classes (Finding, ScanResult, Severity, etc.)
|-- formatters.py # Text and JSON output formatters
|-- config.py # Configuration file loader (.mcpaudit.yml)
|
|-- rules/ # Modular security rules (20 rules)
| |-- __init__.py # Rule base class + get_all_rules()
| |-- path_traversal.py
| |-- command_injection.py
| |-- hardcoded_secrets.py
| |-- absolute_paths.py
| |-- input_validation.py
| |-- sql_injection.py
| |-- unsafe_deser.py
| |-- info_disclosure.py
| |-- missing_auth.py
| |-- cors_misconfig.py
| |-- unsafe_temp.py
| |-- excessive_perms.py
| |-- redos.py
| |-- rate_limiting.py
| |-- ssrf.py # SSRF detection
| |-- unsafe_file_write.py # Dynamic path write detection
| |-- error_handling.py # Missing try/except in tool handlers
| |-- sensitive_logging.py # Secrets in log statements
| |-- resource_exhaustion.py # Unbounded reads, missing timeouts
|
|-- reporters/ # Report generators
| |-- html_reporter.py # Self-contained HTML report (dark theme)
| |-- sarif_reporter.py # SARIF 2.1.0 output (GitHub, VS Code, etc.)
|
|
|-- examples/ # Demo files
| |-- vulnerable_server.py # Intentionally vulnerable MCP server (26 findings)
|
tests/
| |-- test_scanner.py # Core scanner tests
| |-- test_sarif.py # SARIF output validation
| |-- test_config.py # Config file tests
| |-- test_new_rules.py # Tests for SSRF, FILE, ERR, LOG, RES rules
|
hooks/
| |-- pre-commit # Git pre-commit hook
|
.github/workflows/
|-- security-scan.yml # Reusable GitHub Actions workflow
CWE Reference Table
All rule CWE mappings at a glance:
| Rule ID | CWE ID | CWE Name |
|---|---|---|
CMD-001, CMD-002 |
CWE-78 | OS Command Injection |
SQL-001 |
CWE-89 | SQL Injection |
AUTH-001 |
CWE-306 | Missing Authentication for Critical Function |
PATH-001 |
CWE-22 | Path Traversal |
DESER-001 |
CWE-502 | Deserialization of Untrusted Data |
CORS-001 |
CWE-942 | Overly Permissive Cross-domain Whitelist |
RATE-001 |
CWE-770 | Allocation of Resources Without Limits |
PERM-001 |
CWE-250 | Execution with Unnecessary Privileges |
TEMP-001 |
CWE-377 | Insecure Temporary File |
SEC-001 |
CWE-798 | Use of Hard-coded Credentials |
SSRF-001 |
CWE-918 | Server-Side Request Forgery |
FILE-001 |
CWE-73 | External Control of File Name or Path |
ERR-001 |
CWE-755 | Improper Handling of Exceptional Conditions |
LOG-001 |
CWE-532 | Insertion of Sensitive Information into Log File |
RES-001 |
CWE-400 | Uncontrolled Resource Consumption |
Adding Custom Rules
from mcpaudit.rules import Rule
from mcpaudit.models import Finding, Severity
class MyCustomRule(Rule):
@property
def rule_id(self) -> str:
return "CUSTOM-001"
@property
def name(self) -> str:
return "My Custom Check"
@property
def severity(self) -> Severity:
return Severity.HIGH
@property
def description(self) -> str:
return "Detects my custom pattern."
def check(self, tree, source) -> list[Finding]:
findings = []
# Your detection logic here
return findings
# Use it
from mcpaudit.scanner import MCPSecurityScanner
scanner = MCPSecurityScanner(rules=[MyCustomRule()])
License
MIT License. Created by Carlos Miret.
Project details
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 mcpaudit-0.5.0.tar.gz.
File metadata
- Download URL: mcpaudit-0.5.0.tar.gz
- Upload date:
- Size: 85.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.10.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b002fca6cecd2e6c2bb8269560df4ba3ff540bbc7efc7453edf81106334b259c
|
|
| MD5 |
fd550cad534d68e161539743934ca617
|
|
| BLAKE2b-256 |
8a1a7835996276916f0b9a279c734554b3f3b5c61969512b8fc6c8e9ee39aa1b
|
File details
Details for the file mcpaudit-0.5.0-py3-none-any.whl.
File metadata
- Download URL: mcpaudit-0.5.0-py3-none-any.whl
- Upload date:
- Size: 82.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.10.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
385bb7dad6df240c460b2ac652030ffdbdfcd7f46b01f7157880254bd59ebb93
|
|
| MD5 |
3c9e243eb980e4fd02fdc4c939741bba
|
|
| BLAKE2b-256 |
a846bdbf361a7e43c931ee5afe0488398a77c23f487dd9cbd70c3c947b997ac8
|