CVE-2026-111770
High npm @modelcontextprotocol/server

CVE-2026-111770: MCP TypeScript Server SDK Delivered One Client’s Responses to Another Through Shared Transports

CVE ID
CVE-2026-111770
Severity
HIGH
CVSS
7.7
Vendor
npm
Product
@modelcontextprotocol/server
Published
October 20, 2026

Summary

CVE-2026-111770 is a high-severity vulnerability in the MCP TypeScript server SDK, published on npm as @modelcontextprotocol/server. In versions 2.0.0 through 2.2.0, an MCP server reached over HTTP could deliver one client’s responses and notifications to a different client. The flaw appears when multiple clients share a single stateless transport, or a single McpServer or Server instance, because the SDK matched messages to connections by JSON-RPC id alone — and SDK clients number their requests from 0, so identifiers collide in normal use. Version 2.3.0 fixes the issue, but the fix deliberately refuses shared instances, so affected applications need code changes as well as an upgrade.

Technical Analysis

The Model Context Protocol (MCP) TypeScript SDK allows developers to expose MCP servers over HTTP. This vulnerability sits in the server-side routing logic: deciding which connection a JSON-RPC message belongs to.

In a correctly isolated deployment, every client gets its own transport or its own server instance, so the runtime always knows where a response should go. In the vulnerable pattern, the transport or the McpServer/Server object is created once at startup and shared by every client that reaches the HTTP endpoint. According to the advisory, messages were then matched to a connection by JSON-RPC id alone.

That matching strategy collapses under concurrency because MCP SDK clients number their requests starting from 0. Two independent clients will both emit requests with ids 0, 1, 2, and so on. When both clients have requests in flight against a shared transport, the id by itself cannot identify which connection a response belongs to, and one client’s responses and notifications can be handed to the other. The advisory stresses that this collision happens in normal use — no unusual id values or specially crafted requests are needed for two clients to overlap, since both simply start counting at zero.

Two factors widen the blast radius. First, until this release, the README examples of @modelcontextprotocol/hono, express, fastify and node showed exactly the vulnerable pattern: creating the transport or the server once at startup, instead of once per request or per session. Developers who copied the official examples inherited the flaw directly from the documentation. Second, the failure is silent. Nothing crashes when a response crosses wires; the wrong client simply receives it, and the server continues operating normally, which makes the bug easy to miss in production logs.

The CVSS 3.1 vector is AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:L/A:H, producing a base score of 7.7 (HIGH). The network attack vector (AV:N) reflects that any client able to reach the HTTP endpoint participates in the shared id space. Attack complexity is high (AC:H) because an attacker must align with another client’s in-flight traffic for a cross-delivery to be observed, although the from-zero id numbering makes such overlaps routine. No privileges (PR:N) and no user interaction (UI:N) are required, and the scope remains unchanged (S:U). Confidentiality impact is rated high (C:H) because responses — the protocol’s primary data channel — can be delivered to the wrong party. Integrity is rated low (I:L), reflecting that a client may act on messages not intended for it. Availability is rated high (A:H) in the vector; the public description focuses on cross-delivery of messages and does not explain the availability mechanism, so those details are not fully disclosed.

No CWE classification is provided in the available data for this CVE.

The fix, tracked in pull request #2918 and commit 84804c22e45a662675a198f853b7f00063838a8d of the modelcontextprotocol/typescript-sdk repository, changes the SDK so that sharing is refused outright. A second connect() on the same instance now rejects with ALREADY_CONNECTED, and a stateless transport rejects its second request. This is a deliberate breaking change: it converts a silent cross-delivery bug into a loud, immediate failure, forcing applications built on the shared pattern to restructure rather than continue delivering data to the wrong clients. The rejection behavior also doubles as a diagnostic — if your application starts failing with ALREADY_CONNECTED after upgrading, it was relying on the vulnerable shared-instance pattern.

Affected Software and Versions

The affected package is the npm package @modelcontextprotocol/server, the TypeScript server SDK for MCP.

Version boundaries from the advisory and the OSV semver range:

  • Affected versions: 2.0.0 through 2.2.0. The OSV range lists 2.0.0 as introduced and 2.3.0 as fixed.
  • Fixed in: 2.3.0 or later.
  • Exploitation precondition: more than one client can reach the HTTP endpoint, and the code creates the transport or the server once at startup, instead of once per request or per session.

The advisory also lists configurations that are not affected:

  • createMcpHandler() used with a factory that builds a new server on every call.
  • stdio servers.
  • @modelcontextprotocol/sdk 1.26.0 and later.

Note the two distinct package names. The advisory names @modelcontextprotocol/server as the affected package and separately lists @modelcontextprotocol/sdk 1.26.0 and later as not affected. When triaging your dependency tree, check exactly which of these packages you import and which version you resolve, rather than assuming the names are interchangeable. The not-affected list also doubles as a checklist of safe patterns: a per-call server factory under createMcpHandler(), stdio-based servers, and current sdk versions all fall outside the vulnerable population.

Impact

The dominant impact is confidentiality. In MCP, responses carry the outputs of client requests, and notifications carry server-initiated messages. Under this vulnerability, responses and notifications that belong to one client can be delivered to a different client sharing the same transport or server instance. A second client therefore receives data it was never meant to see, with no error raised on either side. The CVSS vector rates this confidentiality impact as High (C:H).

Integrity impact is rated Low (I:L). A client that receives responses or notifications intended for another session may act on data that was not meant for it, but the advisory does not describe any mechanism by which an attacker modifies another client’s state.

Availability impact is rated High (A:H) in the CVSS vector. The public description centers on cross-delivery of messages and does not explain the availability mechanism, so those details are not fully disclosed.

Practically, the impact is silent data exposure between concurrent sessions. The server does not crash or log errors when a response crosses wires, so operators receive no signal that one client’s data is flowing to another. Combined with the fact that the vulnerable pattern appeared in official README examples, the realistic exposure is broad: any HTTP-deployed MCP server on the affected versions that instantiates its transport or server once at startup is a candidate.

Exploitation Scenario

The following scenario is illustrative and derived strictly from the mechanics described in the advisory. It is not a documented attack in the wild.

  1. An operator deploys an MCP server over HTTP following a README example for hono, express, fastify or node. The transport or McpServer is created once at startup and shared by all requests, on @modelcontextprotocol/server 2.0.0 through 2.2.0.
  2. Client A connects and begins issuing requests. Its client numbers them from 0: id 0, id 1, id 2.
  3. Client B connects to the same endpoint. Its requests are also numbered from 0, so both clients now hold in-flight requests carrying the same ids.
  4. While the overlapping requests are in flight, the server matches responses to connections by JSON-RPC id alone. A response belonging to Client A’s request is delivered to Client B, and vice versa.
  5. Server-initiated notifications can cross in the same way, since they are matched by the same id-based logic.
  6. Neither client sees an error. Client B simply receives a response payload that was generated for Client A.

The outcome is that Client B obtains Client A’s response data without credentials, privileges, or user interaction. The only requirements are the shared singleton transport and colliding request ids — which the advisory notes happen in normal use. The high attack complexity rating reflects the need to align with another client’s in-flight traffic, not any need for special access or a foothold on the server.

Detection and Mitigation

Detection

  • Version check: determine whether @modelcontextprotocol/server is in your dependency tree and whether the resolved version falls in 2.0.0 through 2.2.0.
  • Code audit: look for the transport or the McpServer/Server being constructed at module scope, at startup, or in a singleton — anywhere it is created once rather than per request or per session.
  • Exposure check: confirm whether more than one client can reach the HTTP endpoint. A single-client endpoint cannot produce the collision condition.
  • Documentation-derived patterns: integrations copied from the README examples of @modelcontextprotocol/hono, express, fastify and node prior to this release used the vulnerable pattern.
  • Post-upgrade signal: after moving to 2.3.0 or later, a second connect() rejecting with ALREADY_CONNECTED, or a stateless transport rejecting its second request, is direct evidence that your application relied on the shared pattern.

Mitigation

Upgrade to @modelcontextprotocol/server 2.3.0 or later. You also need to change your code, because the SDK now refuses to be shared and an affected app fails requests after upgrading. The advisory recommends three patterns; choose whichever fits your deployment:

  • createMcpHandler(): pass a factory that builds a new McpServer on every call.
  • Per request: build a new McpServer and a new transport inside the request handler.
  • Per session: set sessionIdGenerator and keep one McpServer and one transport per session.

If you cannot upgrade yet, the advisory states that the same code changes fix affected versions. The restructuring is the actual remedy; the upgrade adds guardrails that turn future sharing mistakes into loud failures instead of silent cross-delivery.

For prioritization context, the available data marks this CVE as not listed in CISA’s Known Exploited Vulnerabilities catalog (KEV: false).

Bug Bounty Angle

For bug bounty hunters, the target profile is specific: an HTTP-exposed MCP server built on the TypeScript SDK, running @modelcontextprotocol/server 2.0.0 through 2.2.0, with the transport or server instantiated once at startup. Because the official README examples for hono, express, fastify and node demonstrated exactly that pattern until this release, integrations copied from documentation are the highest-yield candidates.

The test, on systems you are authorized to test, follows directly from the described mechanism. Run two clients against the same endpoint. Both number requests from 0, so issue overlapping requests with low ids and keep several in flight at once to widen the collision window. If the server matches messages by id alone, one client’s responses or notifications arrive at the other. The advisory’s statement that ids collide in normal use means you do not need crafted id values — ordinary numbering is enough. The high attack complexity score (AC:H) translates into a timing requirement, not an access requirement: no privileges and no user interaction are needed.

When reporting, include the affected version, evidence of cross-client delivery — the wrong client receiving a response or notification — and the shared-instance code pattern that caused it. Referencing the published advisory GHSA-jm63-cq47-j5jp helps triagers connect your finding to the known issue. Two scope caveats apply: many programs only reward library-level flaws when a specific in-scope deployment is demonstrably affected, so anchor your report to a target within scope; and since version 2.3.0 is available, findings against patched deployments carry far less value, so verify the resolved version before writing anything up.

Frequently Asked Questions

Which versions of the MCP TypeScript server SDK are affected by CVE-2026-111770?

@modelcontextprotocol/server versions 2.0.0 through 2.2.0 are affected; the fix is in 2.3.0 and later. stdio servers, createMcpHandler() with a per-call server factory, and @modelcontextprotocol/sdk 1.26.0 and later are not affected.

Why do JSON-RPC ids collide between MCP clients?

SDK clients number their requests from 0. Two clients sharing one transport both send ids 0, 1, 2 and onward, and the server matches messages to connections by id alone, so responses and notifications can reach the wrong client.

Does upgrading to 2.3.0 fix CVE-2026-111770 without code changes?

No. The SDK now refuses shared instances: a second connect() rejects with ALREADY_CONNECTED and a stateless transport rejects its second request. Applications must adopt per-request, per-session, or createMcpHandler() factory patterns to keep working after the upgrade.

How severe is CVE-2026-111770?

CVSS 3.1 base score 7.7, HIGH (AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:L/A:H). It is network reachable, requires no privileges or user interaction, has high attack complexity, and a high confidentiality impact because one client’s responses can be delivered to another.

References

The following sources were used for this article:

Responsible use: this analysis is published for security research, authorized testing, defensive security and education. Only test or attempt exploitation on systems you are explicitly authorized to assess. Unauthorized access to computer systems is illegal.

Leave a Reply

Your email address will not be published. Required fields are marked *