MCP (Model Context Protocol) lets AI agents call external tools — file systems, databases, browsers, smart homes, even reverse engineering tools. We ran automated static analysis on 106 MCP servers: Anthropic's official implementations plus the most popular community servers. Combined: 300K+ GitHub stars.
Tool: mcp-redteam (pip install redteam-mcp)
Mode: 25 Semgrep rules + embedding-based tool poisoning detection.
4 servers have confirmed Remote Code Execution. These aren't obscure repos — they have 10K-25K stars and real users.
| Server | Stars | Language | Vulnerability | Verified |
|---|---|---|---|---|
| serena | 25.2K | Python | subprocess.Popen(command, shell=True) — LLM-controlled input |
Disclosed, maintainer considers intentional |
| mcp-chrome | 11.8K | TypeScript | new Function(code)() in browser MAIN world — 7 injection points in 5 files |
Confirmed |
| mcp-use | 10K | Python | exec() — "restricted namespace" bypassed via asyncio.create_subprocess_shell() |
Confirmed, disclosed |
| ida-pro-mcp | 9.3K | Python | exec() + eval() with full __builtins__ inside IDA Pro |
Confirmed (has @unsafe opt-in flag) |
Two additional servers flagged by the scanner turned out to be less severe after manual review:
- DesktopCommanderMCP (6.1K) — terminal tool by design,
spawn()is the intended behavior. Has blocklist butnode:localbypasses it completely. By-design risk, not a vulnerability. - klavis (5.7K) — shell=True exists but has multi-layer validation. 3 of 4 original claims (credential leaks, hardcoded secrets, eval injection) were false positives after deeper review.
ast.literal_eval≠eval().
We initially also reported ha-mcp (3.3K) but closed the issue after finding their python_sandbox.py has proper AST validation, dunder-attribute blocking, and method whitelisting. Not every exec() is a vulnerability.
Three independent servers use exec()/eval()/new Function() and call it "sandboxed":
mcp-use (10K stars):
exec(compiled_wrapped, namespace) # "restricted namespace"We verified: asyncio is passed into the namespace. asyncio.create_subprocess_shell() gives full RCE without any __subclasses__ tricks. The "safety tests" in the repo only check three trivial cases (import os, open(), eval()) — none of the real bypass vectors.
ida-pro-mcp (9.3K stars):
exec(code) # inside IDA Pro process — access to binary analysismcp-chrome (11.8K stars):
func: (code) => new Function(code)() // browser MAIN worldIf you maintain an MCP server: grep -r "exec(\|eval(\|new Function(" your-server/. If any of these execute agent-controlled input — you have RCE.
We reported two findings before publishing. Both were closed as not_planned:
| Server | Issue | Reported | Outcome |
|---|---|---|---|
| serena #1569 | shell=True with unsanitized LLM input | 2026-06-11 | Closed same day as not_planned — "shell=True is necessary; this is intentional. The arguments for the executions do not come from user inputs." |
| mcp-use #1718 | exec() sandbox bypass via asyncio | 2026-06-11 | Closed 2026-08-23 as not_planned, without a reply |
Neither is fixed. Both servers are still shipping the behavior described above, so treat the table at the top of this post as current, not historical.
We also reported ha-mcp (3.3K stars) but after deeper review found their python_sandbox.py has proper AST validation, dunder-attribute blocking, and method whitelisting. We closed the issue with an apology — that's what honest scanning looks like. Not every exec() is a vulnerability.
klavis leaks credentials across 10+ sub-servers (Confluence, Freshdesk, Slack) — hardcoded secrets and tokens in responses.
pipeboard-co/meta-ads-mcp serializes Facebook access_token in tool response.
Combined with prompt injection: steal tokens, access external services.
Most security scanners only check local servers — code you can read. But many MCP servers run remotely, connected via URL. You can't read their source code, but you can read their tool descriptions.
We built scan-remote to check remote servers via MCP protocol:
mcp-redteam scan-remote https://your-server.com/mcp --token <bearer>What it does:
- Connects via MCP Streamable HTTP
- Fetches all tool descriptions (tools/list)
- Runs embedding-based poisoning detection (MiniLM-L6-v2 cosine similarity against known malicious patterns)
- Checks for dangerous parameter names, excessive tool count, missing TLS
We tested on a production Google Workspace MCP server with 246 tools — clean, no poisoning detected. The scan took 3 seconds.
Static regex can't catch rephrased attacks. "Ignore previous instructions" has a hundred variants. So we added an ML layer:
- Encode known malicious patterns into vectors (MiniLM-L6-v2, 22M params, runs locally)
- Encode each tool description
- Cosine similarity — if description is semantically close to a malicious pattern, alert
This catches attacks that regex misses, without sending any data to external APIs. Fully local, fully private.
serena (25.2K stars) — the most starred server with shell=True:
subprocess.Popen(command, shell=True)The maintainer responded that this is intentional: "arguments do not come from user inputs." We disagree — in MCP context, arguments come from the LLM, and LLM output can be manipulated via prompt injection in analyzed files. A docstring telling the LLM "Never execute unsafe commands" is not a security boundary.
DesktopCommanderMCP (6.1K stars) — writes agent-supplied code to temp file and executes:
const proc = spawn(process.execPath, [tempFile]);Anthropic's official servers are clean. The filesystem server has proper validatePath(). Sequential thinking and time servers: zero findings.
GitHub's official MCP server (30.6K stars): zero findings. Written in Go with proper input handling.
Clean community servers exist: apple-mcp, chart-server, perplexity MCP — minimal or zero findings.
| Metric | Value |
|---|---|
| Servers scanned (local) | 106 |
| Servers scanned (remote) | 1 (246 tools) |
| Combined GitHub stars | 300K+ |
| Confirmed RCE | 4 (3.8%) |
| By-design risks | 2 (terminal tools with bypass vectors) |
| False positives caught by manual review | 3 (ha-mcp, klavis creds, klavis eval) |
| Servers with zero findings | 12 (11.3%) |
| Responsible disclosures sent | 3 (2 confirmed, 1 closed as FP) |
This scan is pattern-matching on source code + embedding similarity on descriptions. It catches exec() and shell=True reliably, but misses:
- Rug-pull attacks — server changes behavior after approval
- Behavioral mismatches — description says "reads files", code also writes to network
- Validated-but-flagged — server validates input, but scanner can't trace through validation (this caused our ha-mcp false positive)
- Multi-server attack chains — credential from server A used to access server B
False positive rate after test exclusion: ~40%. For comparison: Cisco's MCP scanner has 78% FP rate on YARA rules.
pip install redteam-mcp
# Scan local MCP server source code
mcp-redteam scan ./your-mcp-server --no-llm
# Scan remote MCP server via URL
mcp-redteam scan-remote https://your-server.com/mcp --token <bearer>25 Semgrep rules. Embedding-based poisoning detection. Remote server scanning. Config health checks. SARIF output for CI/CD.
Source: github.com/m0rvayne/mcp-redteam
I build MCP connectors and AI automation for businesses. 70+ connectors deployed across client projects. Some started breaking silently — dead servers in configs, scope conflicts, unpinned packages. I looked for a scanner and found nothing that reads source code.
Built this in a week by orchestrating AI agents — same approach Karpathy described: understand the problem deeply, specify precisely, let agents implement under strict quality gates (75+ tests, self-security audit, 3 independent architecture reviews).
Scan performed with redteam-mcp v0.2.0 in June 2026. 106 local servers + 1 remote (246 tools). Static analysis + embedding detection. Responsible disclosure sent to affected maintainers before publication; disclosure outcomes above were re-checked before this post went out.
The ~40% false positive rate above is what v0.2.0 produced on this corpus. Rule precision has since been tuned over three rounds — on a separate set of 15 production servers, 15,248 initial findings came down to 90, all manually confirmed. That is a different measurement on a different corpus, not a restatement of the 40% figure, and this scan has not been re-run on the current rules.