← All posts
October 19, 2026 · IA & Cybersécurité

Securing Your MCP Servers: OAuth Token Theft, Command Injection and Prompt Injection

The Model Context Protocol (MCP) connects LLMs to your tools, files and APIs. In 2026 it has also become a target: OAuth tokens hijacked through the official SDK, command injection via the STDIO transport, and prompt injection leading to code execution. Here is how to fix and harden your integrations.

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/sdk 1.12.0 to 1.30.1 → fixed in 1.31.0
  • @modelcontextprotocol/client 2.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 expectedIssuer on your providers (ClientCredentialsProvider, PrivateKeyJwtProvider…). Without it, they still follow the server's choice.
  • Handle already-stored credentials: those saved before the upgrade have no issuer recorded. 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_secret or 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, npm or python are allowed… but npx -c "<command>" or python -c run 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.1 only, never on 0.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/sdk 1.31.0+ / @modelcontextprotocol/client 2.2.0+ / rmcp 2.1.0+
  • [ ] expectedIssuer set, 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