Reused MCP server instances leak tool results across clients
A design flaw in the official Model Context Protocol TypeScript SDK let a shared server or transport route one client's tool results, notifications, and sampling requests to another. Fixed in 1.26.0.
What is this?
On February 4, 2026, a maintainer of the official Model Context Protocol (MCP) TypeScript SDK published a security advisory for a cross-client data leak in @modelcontextprotocol/sdk. The flaw is not a payload or an injection — it is a concurrency bug in how the SDK routes responses. When a single server or a single HTTP transport instance is shared across multiple connected clients, one client can receive data that was meant for another: tool results, resource contents, progress notifications, and sampling or elicitation requests can all be delivered to the wrong connection.
The advisory affects SDK versions 1.10.0 through 1.25.3 and is fixed in 1.26.0. It is scored 7.1 (high) and classified as a race condition (CWE-362). We flag it here because the vulnerable pattern — one server object handling every request — is exactly how many developers first wire up an MCP server, and because MCP is now the connective tissue between LLM agents and enterprise tools. This write-up is a defensive summary; it reproduces no exploit, because none is needed to understand the failure.
How it works
The advisory describes two distinct but related issues, and a deployment can suffer from either or both.
The first is transport reuse. The SDK’s streamable HTTP transport keeps an internal map from a JSON-RPC request ID to the HTTP stream waiting for that response. MCP clients generate request IDs with a counter that starts at zero and increments. So two clients that connect independently both send request ID 0, then 1, then 2. If a single transport instance serves both, the second client’s entry overwrites the first in the map, and the response is written to the wrong stream. Every request type is exposed — tool calls, resource reads, prompt fetches — and no server-initiated feature is required to trigger it.
The second is server (protocol) reuse. When one server object is connected to several transports, one per client, the protocol layer keeps a single internal reference to “the current transport,” and each new connection silently overwrites it. Final responses are usually routed correctly because the reference is captured when the request arrives, but anything sent during handling — progress notifications, sampling calls back to the model, elicitation prompts, or spontaneous server notifications — travels through the shared reference and can land on a different client’s connection.
The unifying cause is shared mutable state with no per-client isolation, surfacing only under concurrency. The most exposed configuration is stateless mode, where a server is spun up without a session generator and reused across requests to save allocation — a common performance shortcut that turns into a data-boundary failure the moment two users are active at once.
Why it matters
MCP servers increasingly sit in front of real data: file stores, databases, ticketing systems, internal APIs, and the credentials those tools carry. A misrouted response is therefore not a cosmetic glitch — it can hand one tenant another tenant’s query results, document contents, or error messages containing sensitive context. In a multi-user deployment, that crosses a security boundary that the operator assumed the framework was enforcing.
The exposure is broad precisely because the bug lives in the reference SDK rather than in one product. Any hosted MCP server built on the affected versions and serving concurrent clients could be affected, and the trigger is ordinary load, not a crafted attack. The low measured exploitation probability reflects that an attacker cannot reliably choose whose data they receive — but accidental leakage across tenants under normal traffic is its own compliance and confidentiality problem, independent of whether anyone is actively trying to exploit it.
Defenses
Upgrade the SDK to 1.26.0 or later. The fix does not paper over the race; it converts silent misrouting into loud, immediate errors — connecting an already-connected protocol now throws, and a stateless transport refuses to handle more than one request — so misconfigurations fail closed instead of leaking.
If you cannot upgrade at once, create a fresh server and transport instance per connection. In stateless mode, instantiate both inside the request handler and tear them down after; in stateful mode, key a new server and transport off the session ID and store them per session. Never share one server object across sessions if your tools send progress notifications, sampling, or elicitation mid-execution.
Audit your deployment topology. Ask specifically: is a single server or transport object reused across requests, and can two clients ever be in flight simultaneously? If yes, you match the vulnerable pattern regardless of SDK version and should confirm the runtime guards are present.
Treat cross-tenant isolation as something you verify, not assume. Add integration tests that drive two concurrent clients through the same server and assert that each receives only its own responses, and add telemetry that can detect a response arriving on an unexpected session. The general lesson outlasts this one CVE: in agent infrastructure, per-client state must be per-client by construction, and “reuse the object to go faster” is a data-boundary decision, not just a performance one.
Status
| Item | Detail |
|---|---|
| Component | @modelcontextprotocol/sdk (MCP TypeScript SDK) |
| Advisory | GHSA-345p-7cg4-v4c7 / CVE-2026-25536, published Feb 4, 2026 |
| Affected | 1.10.0 through 1.25.3 |
| Fixed | 1.26.0 |
| Class | Race condition (CWE-362), cross-client response misrouting |
| Severity | 7.1 (high); confidentiality impact high, exploitation probability low |
| In the wild | No public evidence of exploitation |