Privacy-first security scanner for MCP server configurations
Project description
mcp-audit
Privacy-first security scanner for MCP server configurations.
Free & open source. Apache 2.0, all features included — no paid tier, no license keys required.
Privacy-first: mcp-audit collects no telemetry. Every scan runs locally. See docs/telemetry.md for the full policy.
MCP (Model Context Protocol) servers give AI agents access to your tools, files, APIs, and databases. Misconfigured or malicious servers can exfiltrate credentials, poison tool behavior, and compromise your development environment — without anything appearing in the UI.
mcp-audit scans your local MCP configurations across all major AI coding clients, connects to running servers to inspect what agents actually see, and flags security issues across individual servers and dangerous cross-server combinations.
Features
- Auto-discovers MCP configs across 8 clients (Claude Desktop, Cursor, VS Code, Windsurf, Claude Code user-level, Claude Code project-level, GitHub Copilot CLI, Augment Code)
- Tool poisoning detection — 11 regex patterns across 5 severity tiers, validated against 6 published exploit PoCs (Invariant Labs, CrowdStrike, CyberArk) with 0% false-positive rate across 22 real servers
- Credential exposure — 9 patterns covering AWS, GitHub, OpenAI, Anthropic, Stripe, Slack, and database URLs
- Transport security — unencrypted connections, elevated privileges (
sudo/doas/pkexec/su/run0), wildcard bindings (0.0.0.0,::), runtime package fetching - Supply chain — typosquatting detection via Levenshtein distance against 64 known-legitimate MCP servers; SHA-256 hash verification; Sigstore SLSA provenance verification; transitive-dependency CVE lookup via OSV.dev; CycloneDX SBOM generation
- Rug-pull detection — stateful SHA-256 hash comparison of tool descriptions across scans
- Cross-server toxic flows — capability tagging and 7 dangerous pair patterns detecting multi-server attack paths (file-read + network, secrets + network, shell-exec + network, etc.)
- Attack path engine — multi-hop path detection with greedy hitting set algorithm (minimum set of servers to remove to break all attack paths)
- Interactive attack graph dashboard —
mcp-audit dashboardopens a D3 force-directed graph in your browser with light/dark mode, click-to-highlight attack paths, and hitting set recommendations - Live server analysis — connects to running servers via MCP protocol to inspect actual tool definitions
- SAST rule pack — 37 Semgrep rules (28 Python, 9 TypeScript) for MCP server source code
- IDE extension scanner — known-vuln registry, dangerous capability combos, wildcard activation, unknown publisher, sideloaded VSIX, stale AI extensions
- Governance + policy-as-code — YAML governance policies (approved server lists, score thresholds, transport constraints) and custom detection rules; 12 community rules ship bundled and run for every user
- 5 output formats — terminal (Rich), JSON, SARIF (GitHub Security tab), Nucleus Security FlexConnect, self-contained HTML dashboard
- Continuous monitoring —
mcp-audit watchmonitors config files in real-time and re-scans on any change - Fleet deployment — machine-tagged output with
--asset-prefixfor enterprise-wide aggregation - Fully offline by default — no data leaves your machine
Enterprise vulnerability management
MCP security findings typically exist in isolation: a developer runs a scanner, sees terminal output, maybe fixes something. Enterprises deploying AI agents across hundreds of developers need the same workflow they use for every other vulnerability class — centralized ingestion, asset correlation, deduplication, ownership, remediation tracking, and SLA reporting.
mcp-audit's --format nucleus output and mcp-audit push-nucleus command align with the Nucleus Security FlexConnect schema — the same ingestion pipeline that normalizes data from Qualys, Tenable, CrowdStrike, and 200+ other security tools. Validated end-to-end against a live Nucleus instance on 2026-04-23; see docs/nucleus-integration.md.
Tenable WAS has added MCP server detection plugins that scan server-side code for web vulnerabilities, but no other standalone MCP configuration scanner bridges developer-side config analysis (tool poisoning, credential exposure, toxic flows, supply chain risks) with enterprise vulnerability management. Most output to terminal or JSON and stop there.
Free & open source
mcp-audit is released under the Apache License 2.0 and every feature is available to every user. There are no paid tiers, license keys, or gated commands — the full scanner, rule authoring, governance, SAST integration, extension scanning, dashboard, fleet merge, and Nucleus FlexConnect output all ship in the same binary.
Install
pip install mcp-audit-scanner
or with uv:
uv add mcp-audit-scanner
Note: The PyPI package name is
mcp-audit-scanner(themcp-auditname was already taken). The CLI command is stillmcp-audit. Standalone binaries are also available on GitHub Releases.
For live server connection support:
pip install 'mcp-audit-scanner[mcp]'
For Sigstore-based signature verification (scan --verify-signatures):
pip install 'mcp-audit-scanner[attestation]'
Note:
scan --verify-hashesuses stdlib SHA-256 and works without any extra. The[attestation]extra is only needed for Sigstore/Rekor signature verification.
macOS notes
If pip install fails with bad interpreter: /usr/bin/python: no such file or directory, invoke pip through Python directly:
python3 -m pip install mcp-audit-scanner
After installing, if mcp-audit is not found in your shell, add Python's bin directory to your PATH:
echo 'export PATH="/Library/Frameworks/Python.framework/Versions/3.12/bin:$PATH"' >> ~/.zshrc
source ~/.zshrc
If you installed via Homebrew Python, the path will be different — use python3 -c "import sys; print(sys.prefix)" to find it and append /bin.
Quick start
mcp-audit scan # Scan all detected MCP configs
mcp-audit scan --connect # Also connect to running servers
mcp-audit scan --format sarif -o results.sarif # SARIF for GitHub Security
mcp-audit scan --format nucleus -o results.json # Nucleus FlexConnect output
mcp-audit dashboard # Open interactive attack graph dashboard
mcp-audit dashboard --path demo/configs # Dashboard against demo data
mcp-audit discover # List detected clients and servers
mcp-audit pin # Lock current state as trusted baseline
mcp-audit diff # Show changes since last pin
mcp-audit watch # Monitor configs and re-scan on changes
mcp-audit push-nucleus --url ... --project-id ... # Scan and push to a Nucleus project
mcp-audit merge --dir ./scans # Merge multi-machine JSON outputs
Supported clients
| Client | Config location |
|---|---|
| Claude Desktop | ~/Library/Application Support/Claude/claude_desktop_config.json |
| Cursor | ~/.cursor/mcp.json |
| VS Code | .vscode/mcp.json (workspace) |
| Windsurf | ~/.codeium/windsurf/mcp_config.json |
| Claude Code (user) | ~/.claude.json |
| Claude Code (project) | .mcp.json (project root) |
| GitHub Copilot CLI | ~/.copilot/mcp-config.json |
| Augment Code | ~/.augment/settings.json |
What it detects
| Analyzer | Finding IDs | Examples |
|---|---|---|
| Tool poisoning | 11 patterns (POISON-001 – POISON-050) | SSH key exfiltration instructions, XML injection markers (<IMPORTANT>), behavioral overrides ("ignore previous instructions"), zero-width Unicode stealth characters |
| Credential exposure | CRED-001…009 | AWS access keys, GitHub tokens, OpenAI/Anthropic API keys, Stripe secrets, database connection strings with embedded passwords |
| Transport security | TRANSPORT-001…003 | Unencrypted remote SSE connections, elevated privilege execution, runtime package fetching via npx/uvx without version pinning |
| Supply chain | SC-001…003 | Typosquatted package names (@modelcontextprotocol/server-filesytem vs server-filesystem), distance-1 substitutions flagged CRITICAL |
| Rug-pull | RUGPULL-001…003 | Tool description changed since last scan (HIGH), new server appeared (INFO), previously tracked server removed (INFO) |
| Toxic flow | TOXIC-001…007 | File-read server + network server (exfiltration path), secret-access server + network server (credential theft), shell-exec server + network server (arbitrary command + exfiltration) |
Live server analysis
By default, mcp-audit performs static analysis — it reads config files and inspects the command, args, env vars, and any tool descriptions stored there.
The --connect flag goes further: it connects to each server using the MCP protocol, completes the initialization handshake, and calls list_tools(), list_resources(), and list_prompts() to retrieve the actual definitions the server exposes to the AI agent. Those live definitions are then run through the poisoning analyzer.
This matters because a config file can look completely clean while the server it points to is serving poisoned tool descriptions. Static analysis cannot catch this. Connection-based analysis can.
mcp-audit scan --connect
Requires the optional MCP SDK dependency:
pip install 'mcp-audit-scanner[mcp]'
Connection is best-effort: servers that do not respond within 10 seconds produce an error finding rather than crashing the scan. All static analysis still runs regardless.
Cross-server attack paths
Most MCP security analysis focuses on individual servers. That misses an entire category of risk.
Server A reads files. Server B makes HTTP requests. Neither is malicious alone — they each do exactly what the config says. Together, a prompt injection can instruct the agent to read your SSH keys with A and POST them to an attacker's endpoint with B. No single server ever looked dangerous.
mcp-audit detects 7 categories of these toxic combinations by tagging each server with capability labels (FILE_READ, NETWORK_OUT, SHELL_EXEC, DATABASE, SECRETS, etc.) and checking every server pair for known-dangerous combinations:
| ID | Combination | Severity |
|---|---|---|
| TOXIC-001 | File read + outbound network | HIGH |
| TOXIC-002 | File read + email | HIGH |
| TOXIC-003 | Secret store access + outbound network | CRITICAL |
| TOXIC-004 | File read + shell execution | HIGH |
| TOXIC-005 | Database access + outbound network | HIGH |
| TOXIC-006 | Shell execution + outbound network | CRITICAL |
| TOXIC-007 | Git repository access + outbound network | MEDIUM |
† A single server that provides both capabilities of a dangerous pair is also flagged — no second server required.
Attack graph dashboard
mcp-audit dashboard # Scan your real MCP environment and open browser
mcp-audit dashboard --path demo/configs # Use the bundled demo data
mcp-audit dashboard --port 9090 # Custom port
mcp-audit dashboard --connect # Include live-connection findings
One command runs a full scan, generates a self-contained HTML report, and opens it in your browser. No external dependencies — D3 v7, all scan data, and fonts are embedded inline. No CDN requests are made.
The dashboard shows:
- Force-directed attack graph — your MCP servers arranged around a central AI Agent node. Server nodes are colour-coded by max severity (green = clean, orange = high, red = critical). Toxic flow edges connect pairs with dangerous capability combinations.
- Attack path sidebar — every exploitable multi-hop path listed as a card with severity badge, hop chain, and description. Click a card to highlight the path on the graph with animated dashed lines.
- Hitting set recommendation — at the bottom of the sidebar, the minimum set of servers you can remove to break every attack path. Example: removing
fetchalone breaks three separate attack paths. - Findings table — full findings list with severity filter pills and sortable columns.
- Light/dark mode toggle — pill toggle in the top bar. Preference is applied instantly via CSS custom properties; no page reload required.
The dashboard works against your real MCP environment — whatever mcp-audit scan finds on your machine is what appears in the graph. It is not restricted to demo data.
Rug-pull detection
MCP servers can update their tool definitions at any time. A server can publish clean, trusted descriptions during initial review and silently swap them for malicious ones after developers have granted access.
mcp-audit pin records SHA-256 hashes of every tracked server's configuration as a trusted baseline. Subsequent mcp-audit scan runs compare against that baseline and flag any change as RUGPULL-001 (HIGH).
mcp-audit pin # Record current state as trusted
mcp-audit diff # Show what has changed since last pin
Rug-pull state is stored per-config-set at ~/.mcp-audit/state_<hash>.json. All other persistent state (baselines, registry cache, policy, rules, license) uses the platform user-config directory: ~/Library/Application Support/mcp-audit/ on macOS, ~/.config/mcp-audit/ on Linux, %APPDATA%\mcp-audit\ on Windows.
CI/CD usage
mcp-audit exits with code 1 when findings are detected, 0 when clean, and 2 on errors.
# .github/workflows/mcp-security.yml
- name: Scan MCP configs
run: mcp-audit scan --severity-threshold HIGH
- name: Export SARIF for GitHub Security tab
run: mcp-audit scan --format sarif -o mcp-audit.sarif
- name: Upload SARIF
uses: github/codeql-action/upload-sarif@v4
with:
sarif_file: mcp-audit.sarif
Where the detection logic comes from
All detection patterns are original implementations based on published security research — no code was copied from existing scanners. Sources include Invariant Labs' tool poisoning disclosure, CrowdStrike's MCP exfiltration research, CyberArk's agent attack demonstrations, the OWASP Agentic Top 10, and MITRE ATLAS agent-specific techniques. Supply chain patterns follow npm package naming conventions; credential patterns follow the publicly documented key formats from AWS, GitHub, OpenAI, Anthropic, Stripe, and others.
1,308 tests validate detection accuracy and guard against regressions.
See PROVENANCE.md for the full list of research sources, framework mappings, and contribution guidelines for new detection rules.
CLI reference
Every command is available to every user — no tier, no license required.
| Command | Key flags | Description |
|---|---|---|
mcp-audit scan |
--connect, --format, --output, --severity-threshold, --asset-prefix, --baseline, --policy, --verify-hashes, --no-score, --registry, --offline-registry, --rules-dir, --sast, --include-extensions |
Run all analyzers and report findings |
mcp-audit dashboard |
--path, --port, --connect, --no-open |
Generate and open the interactive attack graph dashboard |
mcp-audit watch |
--path, --format, --severity-threshold, --connect |
Monitor config files and re-scan on any change |
mcp-audit discover |
— | List all detected MCP clients and their configured servers |
mcp-audit pin |
— | Record current server state as a trusted baseline |
mcp-audit diff |
— | Show configuration changes since the last pin |
mcp-audit verify |
<package|config-path> |
Verify server hashes: pass a package name (@scope/pkg), a config file path, or --all |
mcp-audit version |
— | Print version string |
mcp-audit update-registry |
— | Fetch the latest known-server registry from upstream |
mcp-audit sast |
<path> |
Run MCP-aware Semgrep SAST rules on server source code |
mcp-audit push-nucleus |
--url, --project-id, --api-key, --asset-prefix |
Run a scan and push results to a Nucleus Security project via FlexConnect |
mcp-audit merge |
--dir, --format, --asset-prefix |
Merge JSON scan outputs from multiple machines into a fleet report |
mcp-audit baseline save [NAME] |
--path |
Capture a baseline snapshot; NAME is optional (auto-generated if omitted) |
mcp-audit baseline list |
— | List all saved baselines |
mcp-audit baseline compare [NAME] |
--path |
Compare current config against a saved baseline (defaults to latest) |
mcp-audit baseline delete NAME |
--yes |
Delete a saved baseline |
mcp-audit baseline export NAME |
--output-file |
Write a baseline as raw JSON to stdout or a file |
mcp-audit rule validate |
<file> |
Validate a rule file without running a scan |
mcp-audit rule test |
<rule> <config> |
Test a rule file against a specific MCP config file |
mcp-audit rule list |
— | List all currently loaded rules (bundled + user-local) |
mcp-audit policy validate |
<file> |
Validate a governance policy YAML file |
mcp-audit policy init |
— | Scaffold a new governance policy file |
mcp-audit policy check |
--policy, --result |
Check a scan result against a policy file |
mcp-audit extensions discover |
— | Inventory installed IDE extensions from VS Code/Cursor |
mcp-audit extensions scan |
— | Analyze installed IDE extensions for security risks |
mcp-audit scan flags
| Flag | Default | Description |
|---|---|---|
--format |
terminal |
Output format: terminal, json, sarif, nucleus |
--output / --output-file / -o |
stdout | File path for json/sarif/nucleus output; parent directories are created automatically |
--connect |
off | Connect to running servers via MCP protocol |
--severity-threshold |
INFO |
Filter findings and set exit code; exit 1 if any finding at or above this level |
--path |
auto-detect | Directory to search for MCP configs |
--asset-prefix |
hostname | Override machine identifier in Nucleus/SARIF output |
--no-score |
off | Suppress the score/grade panel in terminal output |
--registry |
bundled | Custom registry file path (overrides user cache and bundled registry) |
--baseline |
none | Compare scan results against a named baseline (latest selects most recent) |
--rules-dir |
none | Load additional detection rules from this directory (bundled community rules still apply) |
--offline-registry |
off | Use bundled registry only, skip user cache |
--policy |
auto-discover | Path to a governance policy file; auto-discovers .mcp-audit-policy.yml in cwd/repo root when omitted |
--verify-hashes |
off | Download and verify package hashes against registry (requires network) |
--sast |
none | Path to MCP server source code to scan with Semgrep SAST rules |
--include-extensions |
off | Also scan installed IDE extensions for security issues |
mcp-audit dashboard flags
| Flag | Default | Description |
|---|---|---|
--path |
auto-detect | Directory to search for MCP configs |
--port |
8088 |
HTTP port for the local dashboard server |
--connect |
off | Include live-connection findings in the dashboard |
--no-open |
off | Generate the report without opening a browser tab |
GitHub Action
mcp-audit ships as a composite GitHub Action that you can drop into any repository with a single workflow addition. It installs mcp-audit, runs a full scan against your MCP configs, uploads findings to the GitHub Security tab as SARIF, and writes a findings summary to the job summary page. The build fails only when findings at or above your chosen severity threshold exist — making it easy to adopt incrementally (start with severity-threshold: high, tighten to medium once you've cleared existing issues).
Minimal setup
Add this workflow to .github/workflows/mcp-audit.yml in your repo:
name: MCP Security Scan
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
mcp-audit:
runs-on: ubuntu-latest
permissions:
security-events: write
contents: read
steps:
- uses: actions/checkout@v4
- name: Run mcp-audit
uses: adudley78/mcp-audit@main
with:
severity-threshold: high
upload-sarif: 'true'
The permissions: security-events: write block is required for SARIF upload on public repositories. Without it the upload step will fail silently.
Action inputs
| Input | Default | Description |
|---|---|---|
severity-threshold |
high |
Fail the build if findings at or above this level exist (critical, high, medium, low, info) |
format |
sarif |
Output format (sarif, json, terminal) |
config-paths |
(auto-discover) | Single MCP config file path to scan |
baseline |
(none) | Baseline name for drift detection |
upload-sarif |
true |
Upload SARIF results to the GitHub Security tab |
Action outputs
| Output | Description |
|---|---|
finding-count |
Total number of findings |
grade |
Letter grade (A–F) |
sarif-path |
Path to generated SARIF file |
More examples
See examples/github-actions/ for:
basic.yml— visibility-only, never fails the buildstrict.yml— fail on any MEDIUM or higher findingwith-baseline.yml— drift detection against a committed baseline
Full reference, troubleshooting, and baseline setup instructions: docs/github-action.md.
Pre-Commit Hook
mcp-audit ships as a pre-commit hook, catching MCP misconfigurations before they land in the repository. The hook fires only when a JSON file is staged — no false triggers on Python-only or markdown-only commits — and exits 1 to block the commit when findings at or above your chosen severity threshold exist.
Minimal setup
Add this to your .pre-commit-config.yaml (replace rev with the latest release tag):
repos:
- repo: https://github.com/adudley78/mcp-audit
rev: v0.1.0 # Replace with the latest release tag
hooks:
- id: mcp-audit
Then install the hooks:
pip install pre-commit
pre-commit install
The hook uses --severity-threshold high by default. To lower the bar to MEDIUM, override args:
hooks:
- id: mcp-audit
args: [scan, --severity-threshold, medium]
Note: pass_filenames: false is set intentionally. pre-commit would otherwise pass individual staged JSON filenames to the command, but mcp-audit scan requires full config files discovered through its own client-aware logic. The hook re-scans all MCP configs (not just staged ones) each time it fires.
See examples/pre-commit/ for ready-to-copy config patterns and docs/pre-commit.md for the full reference.
Development
git clone https://github.com/adudley78/mcp-audit.git
cd mcp-audit
uv sync --all-extras
uv run pytest # Run all 1,308 tests
uv run ruff check src/ tests/ # Lint
uv run bandit -r src/ # Security audit of the scanner itself
Known limitations
This tool is in early development. See GAPS.md for known detection gaps, untested areas, and planned improvements.
License
Apache License 2.0 — see LICENSE.
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_audit_scanner-0.3.3.tar.gz.
File metadata
- Download URL: mcp_audit_scanner-0.3.3.tar.gz
- Upload date:
- Size: 649.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f270b8ad2516ddc12291219ba78936c3f1b15a864b5659ce59b047cb8c5d34d4
|
|
| MD5 |
f22c3fdaaf5b305bd665c3b20136c8a7
|
|
| BLAKE2b-256 |
1998267736718f1ae13eca4578b125849382940970a90473b7782165538b9b3a
|
Provenance
The following attestation bundles were made for mcp_audit_scanner-0.3.3.tar.gz:
Publisher:
release.yml on adudley78/mcp-audit
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
mcp_audit_scanner-0.3.3.tar.gz -
Subject digest:
f270b8ad2516ddc12291219ba78936c3f1b15a864b5659ce59b047cb8c5d34d4 - Sigstore transparency entry: 1374216892
- Sigstore integration time:
-
Permalink:
adudley78/mcp-audit@b07d76fa061a54c68e8e63b602802501c56c1bf6 -
Branch / Tag:
refs/tags/v0.3.3 - Owner: https://github.com/adudley78
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@b07d76fa061a54c68e8e63b602802501c56c1bf6 -
Trigger Event:
push
-
Statement type:
File details
Details for the file mcp_audit_scanner-0.3.3-py3-none-any.whl.
File metadata
- Download URL: mcp_audit_scanner-0.3.3-py3-none-any.whl
- Upload date:
- Size: 318.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5caeda543f1b0a4a516c57cd9e5f8ca0078cb4f569176da03d6fbdfb7d9c8145
|
|
| MD5 |
a6877d6b3786bad640d607313e718201
|
|
| BLAKE2b-256 |
60533b255d5f89b86fafee6eeb8624ed2195a67add9b9162120ea29744dfc150
|
Provenance
The following attestation bundles were made for mcp_audit_scanner-0.3.3-py3-none-any.whl:
Publisher:
release.yml on adudley78/mcp-audit
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
mcp_audit_scanner-0.3.3-py3-none-any.whl -
Subject digest:
5caeda543f1b0a4a516c57cd9e5f8ca0078cb4f569176da03d6fbdfb7d9c8145 - Sigstore transparency entry: 1374217003
- Sigstore integration time:
-
Permalink:
adudley78/mcp-audit@b07d76fa061a54c68e8e63b602802501c56c1bf6 -
Branch / Tag:
refs/tags/v0.3.3 - Owner: https://github.com/adudley78
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@b07d76fa061a54c68e8e63b602802501c56c1bf6 -
Trigger Event:
push
-
Statement type: