Why MCP has become a target
The Model Context Protocol (MCP) lets an AI assistant (Claude, Cursor, Windsurf, VS Code…) call tools exposed by servers: read files, query a database, drive software like Blender. It is powerful, but it also means a malicious MCP server, or poisoned content read by the model, can act with your privileges.
In 2026, three families of flaws keep coming back:
| Family | Recent example | Impact |
|---|---|---|
| OAuth trust | CVE-2026-104850 (MCP TypeScript SDK) | Theft of refresh_token and client secrets |
| STDIO command injection | A series of critical CVEs in AI tools (LangFlow, LiteLLM, Agent Zero…) | Execution of system commands |
| Prompt injection | CVE-2026-75130 (Context7), CVE-2026-30615 (Windsurf) | Credential theft, config tampering, RCE |
1. CVE-2026-104850: when the server decides where your secrets go
Severity: high (CVSS 7.5), published October 6, 2026 (GHSA-6qxp-vccf-f47h).
Affected packages:
@modelcontextprotocol/sdk1.12.0 to 1.30.1 → fixed in 1.31.0@modelcontextprotocol/client2.0.0 to 2.1.0 (in some cases) → fixed in 2.2.0
How it works: the SDK's OAuth client let the MCP server choose the authorization server that receives the credentials. A malicious or compromised server could name its own, and the client would send it the refresh_token and client_secret stored from a previous sign-in, with no user interaction.
Not affected: MCP servers built with the SDK, and STDIO clients.
The official Rust SDK (rmcp) received related advisories (OAuth resource field validation, SSRF, header leaks on redirects): version 2.1.0 fixes all of them.
Fixing it
# TypeScript SDK v1
npm install @modelcontextprotocol/sdk@^1.31.0
# SDK v2
npm install @modelcontextprotocol/client@^2.2.0 @modelcontextprotocol/core@^2.2.0
# Rust
cargo update -p rmcp --precise 2.1.0
Then:
- Set
expectedIssueron your providers (ClientCredentialsProvider,PrivateKeyJwtProvider…). Without it, they still follow the server's choice. - Handle already-stored credentials: those saved before the upgrade have no
issuerrecorded. Add one, or clear them to force users to sign in again. - Rotate secrets if a vulnerable client may have connected to an untrusted MCP server: new
client_secretor signing key, and revoke the tokens.
2. Command injection through the STDIO transport
An MCP server over STDIO is just a program launched as a subprocess with a command and arguments. OX Security researchers showed that many AI applications let users, or even unauthenticated visitors, set that command from a web UI. The result: arbitrary system command execution.
Observed variants:
- Direct configuration: an "Add MCP server" screen that accepts any command.
- Allowlist bypass: only
npx,npmorpythonare allowed… butnpx -c "<command>"orpython -crun anything. - Hidden transport: the UI only offers HTTP/SSE, but the backend accepts
transport_type: "stdio"if you tamper with the request.
❌ Vulnerable code
// Configuration comes straight from the HTTP request
app.post("/api/mcp/servers", async (req, res) => {
const { command, args } = req.body;
const transport = new StdioClientTransport({ command, args });
await client.connect(transport);
res.json({ ok: true });
});
✅ Hardened code
// Only known servers, with arguments fixed on the server side
const ALLOWED_SERVERS = {
filesystem: { command: "/usr/local/bin/mcp-filesystem", args: ["--root", "/srv/data"] },
github: { command: "/usr/local/bin/mcp-github", args: [] },
} as const;
app.post("/api/mcp/servers", requireAdmin, async (req, res) => {
const name = req.body.server as keyof typeof ALLOWED_SERVERS;
const config = ALLOWED_SERVERS[name];
if (!config) return res.status(400).json({ error: "Server not allowed" });
// Transport is enforced server-side, never read from the request
const transport = new StdioClientTransport(config);
await client.connect(transport);
res.json({ ok: true });
});
Rules to remember:
- Never pass a user-supplied command or arguments to a STDIO transport.
- An allowlist of binaries is not enough: fix the arguments too.
- Authenticate and authorize MCP configuration endpoints, and keep them off the Internet.
- Validate the transport type server-side, without trusting the client.
3. Prompt injection: when content becomes a command
The model can't tell your instructions apart from the text it reads. A web page, a README or documentation fetched by an MCP tool can carry hidden instructions.
- Context7 (CVE-2026-75130): poisoned custom instructions served through the MCP server could push the agent to steal credentials and delete files (versions up to 2.1.2).
- Windsurf (CVE-2026-30615): attacker-controlled HTML could rewrite the local MCP configuration and register a malicious STDIO server, with zero user interaction.
Protecting yourself
- Mandatory human approval for any change to the MCP configuration (
mcp.json,settings.json) and for sensitive actions (writing files, running commands, network requests). - Least privilege: an MCP server that reads documentation doesn't need disk write access or your cloud keys.
- Isolation: run MCP servers in a container or VM with a restricted filesystem and no secrets in the environment.
- Treat all external content as hostile: web pages, GitHub issues, emails, downloaded files.
4. Good practices for a local MCP server
A local MCP server (such as Blender's) often opens a TCP port on the machine. A few simple rules:
- Listen on
127.0.0.1only, never on0.0.0.0. - Connect trusted clients only, and stop the server when you don't need it.
- Check whether the server executes arbitrary code (many do by design) and don't expose it to untrusted content.
- Read the code or diff of a third-party MCP server before installing it, as with any dependency.
Checklist
- [ ]
@modelcontextprotocol/sdk1.31.0+ /@modelcontextprotocol/client2.2.0+ / rmcp 2.1.0+ - [ ]
expectedIssuerset, stored credentials cleaned up, exposed secrets rotated - [ ] No STDIO command built from user input
- [ ] MCP configuration endpoints authenticated and not publicly exposed
- [ ] Human approval for configuration changes and sensitive actions
- [ ] MCP servers isolated, without unnecessary secrets, listening on
127.0.0.1
Sources: GitHub Advisory GHSA-6qxp-vccf-f47h · OX Security — MCP STDIO Command Injection