Static attack surface analyzer for MCP servers and LLM tool definitions.
MCP servers run with the same OS permissions as the user who launched them. When you add a server to your Claude Desktop config, you are granting it the ability to read files, execute commands, call APIs, and send emails — silently, on every session start.
Most people install MCP servers the same way they install browser extensions: quickly, without reviewing what they do. The difference is that an MCP server operates inside your AI agent's context, meaning a single malicious or misconfigured server can:
- execute shell commands on your machine via prompt injection
- read SSH keys, tokens, and credentials from broad filesystem paths
- exfiltrate data through email or HTTP tools triggered by a poisoned prompt
- persist across sessions because the config is loaded automatically
mcpmap scans your configuration before any of that can happen. It takes under a second, requires no network access, and maps every finding to OWASP LLM Top 10 and MITRE ATLAS so you know exactly what class of attack each issue enables.
- 24 detection rules across CRITICAL / HIGH / MEDIUM / LOW severity
- Supports Claude Desktop
claude_desktop_config.json, MCP config format, OpenAI tool definitions, and remote HTTP/SSE MCP servers - Entropy-based secret detection: catches hardcoded secrets even when env var names don't look like keys
- Unpinned package detection: flags
npx -y @pkg/namewithout a version pin - Typosquatting detection: edit-distance comparison against trusted publishers
- Adversarial instruction detection: flags prompt-injection patterns embedded in tool descriptions
- Suppression / allow-list via
.mcpmap-ignore: silences known findings without losing other coverage - Baseline / diff mode:
--baselineshows only what changed since the last scan - Context-aware remediation: advice names the exact path, key, or package to fix
- Output formats: Markdown, JSON, HTML (dark-mode report), SARIF (for GitHub Code Scanning)
- REST API (
mcpmap serve) for integration with web tooling --fail-onflag for CI gate enforcement- Custom rules via
--rulesflag
pip install mcpmapOr from source:
git clone https://github.com/bogdanticu88/mcp-map
cd mcp-map
pip install -e ".[dev]"Works on Windows, macOS, and Linux. Requires Python 3.9+.
Set NO_COLOR=1 to disable all colour output.
# Find your Claude Desktop config (cross-platform)
mcpmap find
# Scan a config file
mcpmap scan ~/.config/Claude/claude_desktop_config.json
# Scan a directory for all JSON/YAML configs
mcpmap scan ./configs/
# Show all built-in detection rules
mcpmap rules
# Output an HTML report
mcpmap scan config.json --format html --output report.html
# Fail CI if any HIGH or CRITICAL finding is detected
mcpmap scan config.json --fail-on HIGH
# Show only new findings since the last scan
mcpmap scan config.json --save-baseline baseline.json
mcpmap scan config.json --baseline baseline.json| OS | Default path |
|---|---|
| macOS | ~/Library/Application Support/Claude/claude_desktop_config.json |
| Windows | %APPDATA%\Claude\claude_desktop_config.json |
| Linux | ~/.config/Claude/claude_desktop_config.json (or $XDG_CONFIG_HOME/Claude/) |
Run mcpmap find to check all known paths on your current system.
mcpmap [OPTIONS] COMMAND [ARGS]...
mcpmap scan [OPTIONS] TARGETS...
TARGETS can be individual files or directories. Directories are walked recursively for .json, .yaml, and .yml files.
| Option | Description |
|---|---|
--format, -f |
Output format: markdown (default), json, html, sarif |
--output, -o |
Write report to file instead of stdout |
--fail-on |
Exit with code 1 if any finding is at or above this severity (CRITICAL, HIGH, MEDIUM, LOW) |
--summary |
Print terminal summary table only |
--ascii |
ASCII-only output (no emoji/badges) |
--rules |
Path to custom rules YAML; replaces the built-in rule set |
--ignore-file |
Path to a suppression file (default: .mcpmap-ignore in target dir or CWD) |
--no-ignore |
Disable all suppression file loading |
--show-suppressed |
Include suppressed findings in the report |
--baseline |
Path to a previous JSON scan result; marks new findings as NEW |
--save-baseline |
Save current results as a baseline JSON file |
--version, -V |
Show version and exit |
| Code | Meaning |
|---|---|
0 |
No findings at or above the --fail-on threshold (or --fail-on not set) |
1 |
One or more findings at or above the --fail-on severity threshold |
2 |
Invalid --rules or --baseline path, target not found, or output file not writable |
mcpmap rules [--rules PATH]
Lists all detection rules in a table (ID, severity, category, name). Pass --rules to preview a custom rules file before using it in a scan.
mcpmap find
Detects your OS and lists known Claude Desktop config locations, indicating which exist. Prints a ready-to-paste mcpmap scan command for found files.
mcpmap serve [OPTIONS]
| Option | Description |
|---|---|
--host |
Bind host (default: 127.0.0.1) |
--port, -p |
Bind port, 1-65535 (default: 8000) |
--rules |
Path to custom rules YAML |
Create a .mcpmap-ignore file in your project root or next to the config file:
# Suppress this rule everywhere
MCM-014
# Suppress only for the named server
MCM-010:my-internal-server
# Suppress all findings for a server you've audited
*:legacy-mcp-server
Format per line: RULE_ID or RULE_ID:server_name or *:server_name. Lines starting with # are comments.
mcpmap looks for .mcpmap-ignore adjacent to the first scan target, then in the current working directory. Override with --ignore-file PATH or disable entirely with --no-ignore.
# Scan with a custom suppression file
mcpmap scan config.json --ignore-file security/approved.ignore
# Show suppressed findings alongside active ones
mcpmap scan config.json --show-suppressedTrack what changes between scans. Useful in CI to alert only on regressions.
# Step 1: save the current findings as a baseline
mcpmap scan config.json --save-baseline baseline.json
# Step 2 (later, in CI): compare against it
mcpmap scan config.json --baseline baseline.jsonEach finding in the output is labelled:
- NEW: finding did not exist in the baseline
- Resolved: finding was in the baseline but is no longer detected
- Unchanged: present in both (hidden from the diff summary)
Combine with --fail-on to fail CI only when new findings are introduced:
mcpmap scan config.json --baseline baseline.json --fail-on HIGHStart the server:
mcpmap serveAnalyze a config payload.
curl -s http://localhost:8000/analyze \
-H "Content-Type: application/json" \
-d '{
"content": {
"mcpServers": {
"bash": {
"command": "npx",
"args": ["-y", "@anthropic-ai/mcp-server-bash"],
"env": {"API_KEY": "sk-..."}
}
}
},
"filename": "config.json",
"format": "json"
}'Request fields:
| Field | Type | Description |
|---|---|---|
content |
object | The config to analyze |
filename |
string | Hint for format detection (default: config.json) |
format |
string | Report format in report field: json, markdown, html, sarif |
fail_on |
string | Optional severity threshold; sets failed: true in response |
Response:
{
"results": [...],
"summary": {"CRITICAL": 2, "HIGH": 1, "MEDIUM": 0, "LOW": 0, "TOTAL": 3},
"failed": true,
"report": "..."
}List all loaded rules with severity, category, and framework mappings.
Returns {"status": "ok", "version": "..."}.
Local process-based server:
{
"mcpServers": {
"filesystem": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-filesystem@1.2.3", "/home/user/projects"],
"env": {}
}
}
}Remote HTTP/SSE server (also scanned):
{
"mcpServers": {
"remote-assistant": {
"url": "https://mcp.example.com/sse",
"headers": {"Authorization": "Bearer sk-..."}
}
}
}{
"tools": [
{
"type": "function",
"function": {
"name": "run_bash",
"description": "Execute a bash command",
"parameters": {
"type": "object",
"properties": {"command": {"type": "string"}}
}
}
}
]
}| ID | Name | Category |
|---|---|---|
| MCM-001 | Shell / Command Execution Capability | Excessive Agency |
| MCM-002 | Arbitrary Code Execution Capability | Excessive Agency |
| MCM-003 | Credential and Secret Store Access | Sensitive Data Exposure |
| MCM-004 | Process Management Capability | Excessive Agency |
| ID | Name | Category |
|---|---|---|
| MCM-005 | Filesystem Write Access | Excessive Agency |
| MCM-006 | Unrestricted Network Fetch / HTTP Request | Data Exfiltration |
| MCM-007 | Email Send Capability | Data Exfiltration |
| MCM-008 | Git Write / Repository Push Access | Supply Chain |
| MCM-009 | Database Write / Delete Access | Excessive Agency |
| MCM-010 | API Key or Token Exposed in Server Environment | Sensitive Data Exposure |
| MCM-011 | Broad Filesystem Access (Root or Home Directory) | Sensitive Data Exposure |
| MCM-012 | Overprivileged MCP Server | Excessive Agency |
| MCM-020 | Unpinned Package Version in Auto-Accept Runner | Supply Chain |
| MCM-021 | Adversarial Instructions Embedded in Tool Description | Prompt Injection Risk |
| MCM-022 | High-Entropy Secret in Server Environment | Sensitive Data Exposure |
| MCM-024 | Package Name Resembles Trusted Publisher (Typosquatting) | Supply Chain |
| ID | Name | Category |
|---|---|---|
| MCM-013 | Unrestricted Web Browsing / Browser Automation | Prompt Injection Risk |
| MCM-014 | Unverified Third-Party MCP Server | Supply Chain |
| MCM-015 | Tool Missing Description (Tool Confusion Risk) | Tool Confusion |
| MCM-016 | Sensitive Directory Read Access | Sensitive Data Exposure |
| MCM-023 | Remote MCP Server over HTTP/SSE | Supply Chain |
| ID | Name | Category |
|---|---|---|
| MCM-017 | Calendar Write Access | Data Exfiltration |
| MCM-018 | Social Media Post Capability | Data Exfiltration |
| MCM-019 | Push Notification Send Capability | Data Exfiltration |
Each finding includes:
- OWASP LLM Top 10 reference(s)
- MITRE ATLAS technique reference(s)
- Context-aware remediation guidance (names the exact path, key, or package to fix)
- Evidence string (what matched and where)
| Rule | Detection method |
|---|---|
| MCM-001 to MCM-019 | Pattern matching on server names, package names, tool names, descriptions, env keys, and filesystem paths |
| MCM-020 | Detects npx/uvx/bunx with -y/--yes and no version-pinned package arg |
| MCM-021 | Matches 20+ known prompt-injection phrase patterns in tool descriptions |
| MCM-022 | Shannon entropy >= 4.5 bits/char on env variable values >= 20 chars (with URL/path exclusions) |
| MCM-023 | Detects servers configured with a url field instead of a local command |
| MCM-024 | Levenshtein distance <= 2 between package scope and trusted publisher names |
Extend or replace the built-in rules with your own YAML file:
rules:
- id: CUSTOM-001
name: My Custom Rule
description: Detects a dangerous pattern specific to our environment.
severity: HIGH
category: Excessive Agency
owasp_llm:
- id: LLM06
name: "Excessive Agency"
url: "https://genai.owasp.org/llmrisk/llm06-excessive-agency/"
mitre_atlas:
- id: AML.T0051.001
name: "LLM Prompt Injection: Indirect"
url: "https://atlas.mitre.org/techniques/AML.T0051.001"
remediation: Remove or gate this tool behind human confirmation.
detection:
tool_name_patterns:
- my_dangerous_tool
tool_description_keywords:
- dangerous operationmcpmap scan config.json --rules my_rules.yaml
# Preview which rules would be loaded
mcpmap rules --rules my_rules.yaml| Field | Type | Description |
|---|---|---|
server_name_patterns |
list[str] | Substring match on server name |
package_patterns |
list[str] | Substring match on package name |
broad_path_patterns |
list[str] | Exact or subdirectory-prefix match on args |
sensitive_path_patterns |
list[str] | Substring match on args |
env_key_patterns |
list[str] | Substring match on env variable keys |
env_value_entropy_threshold |
float | Flag env values with Shannon entropy above this level |
env_value_min_length |
int | Minimum value length for entropy check (default: 20) |
tool_name_patterns |
list[str] | Substring match on tool name |
tool_description_keywords |
list[str] | Substring match on tool description |
description_injection_patterns |
list[str] | Adversarial phrases to detect in tool descriptions |
parameter_names |
list[str] | Substring match on tool parameter names |
check_empty_description |
bool | Flag tools with no description |
check_unpinned_package |
bool | Flag auto-accept runners without a version pin |
check_remote_server |
bool | Flag servers configured with a url field |
check_typosquatting |
bool | Enable edit-distance check against trusted publishers |
typosquatting_distance |
int | Maximum edit distance to flag (default: 2) |
trusted_package_prefixes |
list[str] | Trusted publisher prefixes for supply chain checks |
high_risk_tool_threshold |
int | Number of high-risk findings to trigger MCM-012 |
- name: Scan MCP config
run: |
pip install mcpmap
mcpmap scan claude_desktop_config.json --fail-on HIGH --format sarif --output mcpmap.sarif
- name: Upload SARIF to Code Scanning
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: mcpmap.sarifWith baseline diff to fail only on new regressions:
- name: Restore baseline
uses: actions/cache@v4
with:
path: mcpmap-baseline.json
key: mcpmap-baseline-${{ github.base_ref }}
- name: Scan MCP config
run: |
pip install mcpmap
mcpmap scan config.json \
--baseline mcpmap-baseline.json \
--save-baseline mcpmap-baseline.json \
--fail-on HIGH
- name: Save updated baseline
uses: actions/cache@v4
with:
path: mcpmap-baseline.json
key: mcpmap-baseline-${{ github.ref_name }}repos:
- repo: local
hooks:
- id: mcpmap
name: mcpmap MCP security scan
entry: mcpmap scan
args: ["--fail-on", "CRITICAL"]
language: python
files: "claude_desktop_config\\.json|mcp.*\\.json"docker build -t mcpmap .
docker run --rm -v $(pwd):/work mcpmap scan /work/config.jsonpip install -e ".[dev]"
pytest
ruff check mcpmap/You can also invoke the CLI as a module:
python -m mcpmap scan config.json
python -m mcpmap find
python -m mcpmap rulesmcpmap findings map to:
- OWASP LLM Top 10: LLM01 Prompt Injection, LLM02 Sensitive Information Disclosure, LLM03 Supply Chain, LLM06 Excessive Agency
- MITRE ATLAS: AML.T0051.000 (Direct Prompt Injection), AML.T0051.001 (Indirect Prompt Injection), AML.T0054 (LLM Jailbreak), AML.T0048 (Societal Harm)
mcpmap is a static analyzer. It reads configuration files without executing any code or making network requests.
- It cannot detect runtime misconfigurations or vulnerabilities introduced after the agent starts.
- Entropy-based secret detection (MCM-022) may produce false positives on long random-looking values (e.g., base64-encoded certificates, concatenated UUIDs). Use
.mcpmap-ignoreto suppress confirmed non-secrets. - Typosquatting detection (MCM-024) compares the package scope against a built-in list of trusted publishers. Publishers not in that list are not checked.
- Pattern matching rules (MCM-001 to MCM-019) look for known-dangerous names and capabilities. Novel tools with custom names that wrap dangerous operations are not detected unless they match a known pattern or custom rule.
- mcpmap does not perform sandbox execution, import analysis, or dynamic taint analysis.
MIT. See LICENSE.
