MCP Security Scan
Security scanner for Model Context Protocol (MCP) servers. Finds hardcoded secrets, unsafe execution patterns, data exfiltration risks, filesystem access issues, code obfuscation, and missing authentication.
Available as a GitHub Action and a CLI tool.
Why?
We statically scanned 7,029 public MCP servers (part of a 35,689-endpoint agent-ecosystem corpus):
- 9% — nearly 1 in 11 — ship at least one high or critical issue detectable by static analysis alone
- Most common: hardcoded secrets, unsafe shell/exec with user-controllable input, credential-exfiltration paths, filesystem access far broader than advertised
Full prevalence data + methodology: Agent Tool Supply-Chain Security: Prevalence from 35,689 Scans.
MCP servers run on your machine with access to your files, shell, and API keys — and increasingly get called in a loop by autonomous agents with nobody watching. Recent incidents like GitLost (GitHub's AI agent tricked into leaking private repos) and MCPoison / CVE-2025-54136 (the Cursor mcp.json rug-pull) live on this exact attack surface. Most MCP servers have no security review process.
What it detects
Twelve finding categories, all open source:
| Category | Examples |
|---|---|
secret |
Hardcoded API keys, tokens, credentials |
unsafe_exec |
shell=True with user input, eval/exec on external data |
exfiltration |
Env vars / secrets piped into network calls |
fs_access |
"Read a file" servers that can walk the entire home directory |
obfuscation |
Base64-packed or dynamically-assembled code |
dynamic_remote_load |
Rug-pulls: fetch-then-exec, curl|sh, unpinned npx/uvx, manifest-exec (the MCPoison / CVE-2025-54136 class) |
hidden_unicode |
Invisible Unicode Tags, bidi controls (Trojan Source), zero-width chars smuggling instructions past review |
prompt_injection |
Instruction-override / hide-from-user / exfil directives in tool descriptions, mcp.json, SKILL.md |
insecure_deserialization |
pickle.loads, unsafe yaml.load, torch.load on untrusted data |
install_hook |
npm preinstall/postinstall scripts that fetch, eval, or pipe to shell |
toxic_flow |
The lethal trifecta (the GitLost class): private-data read + untrusted input + outbound send composing in one tool |
| Missing auth | Tool endpoints with no authentication boundary |
The hosted scanner at agentgraph.co/check adds what a local one-shot scan structurally can't: every verdict signed as an offline-verifiable attestation (Ed25519/JWS), tool-definition pinning with drift detection across re-scans (a tool that changes after you vetted it fails verification), and vulnerable-dependency intelligence from a 35,689-endpoint scan corpus.
Quick Start
GitHub Action (recommended)
Add this to .github/workflows/mcp-security.yml:
name: MCP Security Scan
on: [push, pull_request]
jobs:
security-scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: agentgraph-co/mcp-security-scan@v1
with:
fail-on: critical
That's it. The action will:
- Scan your repository for security issues
- Post a detailed comment on pull requests
- Fail the check if critical findings are detected
CLI
pip install git+https://github.com/agentgraph-co/mcp-security-scan.git
# Scan any MCP server repo on GitHub
mcp-security-scan owner/repo
# Scan the current repo (auto-detects from git remote)
mcp-security-scan
# JSON output for programmatic use
mcp-security-scan owner/repo --format json
# Fail CI on critical findings
mcp-security-scan owner/repo --fail-on critical
Advanced Usage
Action Inputs
| Input | Description | Default |
|---|---|---|
repo |
GitHub repo to scan (owner/repo). Defaults to the current repository. |
Current repo |
token |
GitHub token for API access | ${{ github.token }} |
fail-on |
Fail the check if findings at or above this severity: critical, high, or medium |
critical |
format |
Output format: text, json, github |
github |
Action Outputs
| Output | Description |
|---|---|
trust-score |
Trust score from 0-100 |
findings-count |
Total number of security findings |
critical-count |
Number of critical-severity findings |
report |
Full scan report as JSON |
Using Outputs in Your Workflow
jobs:
security-scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: agentgraph-co/mcp-security-scan@v1
id: scan
with:
fail-on: high
- name: Check trust score
if: steps.scan.outputs.trust-score < 50
run: echo "Trust score is below 50 — review findings"
Scanning a Different Repository
- uses: agentgraph-co/mcp-security-scan@v1
with:
repo: some-org/their-mcp-server
token: ${{ secrets.GITHUB_TOKEN }}
CLI Flags
| Flag | Description | Default |
|---|---|---|
repo |
GitHub repo (owner/repo) |
Auto-detect from git remote |
--token |
GitHub API token | $GITHUB_TOKEN env var |
--format |
Output format: text, json, github |
text |
--output, -o |
Write JSON report to file | -- |
--fail-on |
Exit code 1 if findings at this severity or above: critical, high, medium |
-- |
What It Scans
The scanner checks for 6 categories of security issues:
🔑 Hardcoded Secrets (critical/high)
- AWS access keys and secret keys
- OpenAI, Anthropic, Google API keys
- GitHub tokens, Slack tokens, Stripe keys
- Private key blocks (RSA, etc.)
- Generic API key and password assignments
⚠️ Unsafe Execution (critical/high)
subprocess.run(),os.system(),os.popen()(Python)shell=Truein subprocess calls (Python -- critical)eval(),exec()(Python/JS)child_process,execSync(Node.js)Command::new(Rust),exec.Command(Go)
📁 Filesystem Access (medium/high)
- Unrestricted file read/write operations
- Path traversal patterns (
../) - Recursive delete (
shutil.rmtree,rimraf)
📡 Data Exfiltration (high/critical)
- Outbound HTTP requests with sensitive data
- Encoded data transmission patterns
- Suspicious network calls in unexpected contexts
🕵️ Code Obfuscation (high)
- Base64-encoded code execution
- Dynamic code generation patterns
- Obfuscated variable names and control flow
✅ Positive Security Signals (reduce risk)
- Authentication and authorization checks
- Input validation (Zod, Pydantic, JSON Schema)
- Rate limiting
- CORS configuration
- Security headers (Helmet, CSP)
Example PR Comment
When the action runs on a pull request, it posts a comment like this:
MCP Security Scan Results
✅ Trust Score: 85/100 (Good)
- ✅ Credential Theft -- Clear
- ✅ Data Exfiltration -- Clear
- ⚠️ Unsafe Execution -- 2 findings
- ✅ Filesystem Access -- Clear
- ✅ Code Obfuscation -- Clear
Metric Value Files scanned 45 Language Python Positive signals Input validation, Rate limiting Findings (2)
Severity Category Name File Line high unsafe_exec subprocess.run src/tools.py42 medium unsafe_exec os.popen src/utils.py18
Trust Score
Each scanned repo receives a score from 0 to 100:
| Factor | Impact |
|---|---|
| Base score | 70 |
| Critical finding | -15 each |
| High finding | -8 each |
| Medium finding | -3 each |
| Positive security signal | +5 each |
| Has README | +5 |
| Has LICENSE | +5 |
| Has tests | +5 |
Contributing
Found a false positive? Missing a detection pattern? PRs welcome.
Good first issues:
- Add detection for more secret patterns (Twilio, SendGrid, etc.)
- Reduce false positives on path traversal in test files
- Add support for local directory scanning (not just GitHub repos)
- Add SARIF output format for GitHub Code Scanning integration
License
MIT -- see LICENSE.
Built by AgentGraph -- trust infrastructure for AI agents.
Metadata
Release files for mcp-security-scan 1.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| mcp_security_scan-1.1.0.tar.gz | 25.7 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| mcp_security_scan-1.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 48.4 kB
Release files / mcp_security_scan-1.1.0.tar.gz
| Download URL | mcp_security_scan-1.1.0.tar.gz |
|---|---|
| Size | 25.7 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
233c85299f675afbb188c7ca32cdb2a7863e101c0240fdf6ac589660692dca62
|
|
BLAKE2b-256 checksum How to use checksums |
5ffe4df5547db8148eb707e6fce9f42ae5fc33c4a0c9fed65668e71720f73e6f
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.9.6
|
Release files / mcp_security_scan-1.1.0-py3-none-any.whl
| Download URL | mcp_security_scan-1.1.0-py3-none-any.whl |
|---|---|
| Size | 22.7 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
a1ed471cf4c295dcffdd256a6a79cdda8f79d2f8b625f846fb30153d93b1157b
|
|
BLAKE2b-256 checksum How to use checksums |
c557ae8f29dd77898881ce9d9fdf0ddd95d57d4c854103f5f55af73336ce51f6
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.9.6
|