high

CVE

CVE-2026-111767, CVE-2026-111770, CVE-2026-111768, CVE-2026-111771, CVE-2026-111769, CVE-2026-25536

CWE

CWE-639, CWE-862, CWE-362, CWE-770, CWE-863, CWE-200

Affected Surface

  • npm package `@modelcontextprotocol/sdk` versions `< 1.32.0` (`1.24.0` to `1.31.0` for `CVE-2026-111767`, `1.3.0` to `1.31.0` for `CVE-2026-111768`, `1.6.0` to `1.31.0` for `CVE-2026-111771`, and all `1.x <= 1.31.0` for `CVE-2026-111769`)
  • npm package `@modelcontextprotocol/server` versions `2.0.0` through `2.2.0`, plus `@modelcontextprotocol/client` (`2.0.0` to `2.2.0`), `@modelcontextprotocol/express` (`2.0.0` to `2.0.1`), `@modelcontextprotocol/server-legacy` (`2.0.0` to `2.3.0`), `@modelcontextprotocol/node` (`2.0.0`), and `@modelcontextprotocol/hono` (`2.0.0`)
  • HTTP-exposed Model Context Protocol servers built with Express, Fastify, Hono, `node:http`, or Cloudflare Workers that share a single `McpServer`, `Server`, `InMemoryTaskStore`, or stateless `StreamableHTTPServerTransport` across clients

TL;DR

On 20 October 2026, the GitHub Advisory Database and NVD published five CVE-assigned advisories (CVE-2026-111767 through CVE-2026-111771) alongside a sixth repository advisory (GHSA-f897-62hf-6gxq) for the official Model Context Protocol TypeScript SDK (@modelcontextprotocol/sdk and the v2 @modelcontextprotocol/server package family). While the fixes shipped quietly in @modelcontextprotocol/sdk@1.32.0 and @modelcontextprotocol/server@2.3.0 on 2 October, the public advisories reveal three operational traps that a version bump alone will not solve:

  1. Cross-client message delivery taught by the official READMEs (CVE-2026-111770, GHSA-jm63-cq47-j5jp, CVSS 7.7 High): In @modelcontextprotocol/server 2.0.0 through 2.2.0, sharing one McpServer, Server, or stateless StreamableHTTPServerTransport across HTTP requests matched replies by JSON-RPC id alone. Because MCP clients start request IDs at 0, concurrent clients received each other’s tool outputs and notifications. Until 2.3.0, the official README examples for @modelcontextprotocol/hono, express, fastify, and node instructed developers to write that exact shared-instance pattern. Upgrading to 2.3.0 makes the second request fail with ALREADY_CONNECTED or a 500 error unless you refactor your handler to construct McpServer and the transport per request.
  2. Un-scoped experimental tasks across HTTP sessions (CVE-2026-111767, GHSA-22jm-h49p-29qw, CVSS 8.6 High): In @modelcontextprotocol/sdk 1.24.0 through 1.31.0, InMemoryTaskStore accepted _sessionId and ignored it. Any HTTP client sharing the store could list, read results from, and cancel tasks created by other sessions.
  3. Two fixes that do nothing until you change code (CVE-2026-111768 / GHSA-46jr-4wx8-v453, CVSS 7.5 High, and CVE-2026-111771 / GHSA-rvq5-wwqv-78pq, CVSS 6.8 Medium): A single HTTP request inside the 4 MiB body limit can crash the Node.js process with JavaScript heap out of memory during tool argument validation (CVE-2026-111768), and requireBearerAuth() accepts OAuth bearer tokens issued for other services (CVE-2026-111771). Both advisories state that “Upgrading alone changes nothing”: you must explicitly set maxToolInputElements on McpServer and wire expectedResource plus AuthInfo.resource in requireBearerAuth().

Affected packages and projects

EcosystemPackageCVE / GHSACVSS v3.1Affected versionsFixed versionCode change required after upgrade?
npm@modelcontextprotocol/sdkCVE-2026-111767 (GHSA-22jm-h49p-29qw)8.6 High>= 1.24.0, < 1.32.01.32.0 (1.32.1 current)Custom TaskStore implementations must scope by sessionId
npm@modelcontextprotocol/serverCVE-2026-111770 (GHSA-jm63-cq47-j5jp)7.7 High>= 2.0.0, < 2.3.02.3.0 (2.3.1 current)Yes: construct McpServer and transport per request or session
npm@modelcontextprotocol/sdk, @modelcontextprotocol/serverCVE-2026-111768 (GHSA-46jr-4wx8-v453)7.5 Highsdk >= 1.3.0, < 1.32.0; server >= 2.0.0, < 2.3.0sdk 1.32.0, server 2.3.0Yes: pass { maxToolInputElements } and lower maxRequestBodySize
npm@modelcontextprotocol/sdk, server, node, honoGHSA-f897-62hf-6gxq7.5 Highsdk 1.10.0..1.30.0; server 2.0.0; node 2.0.0; hono 2.0.0sdk 1.30.1, server 2.1.0, node 2.1.0, hono 2.0.1Default 4 MiB body cap and 100-message batch cap enabled automatically
npm@modelcontextprotocol/sdk, server, express, server-legacyCVE-2026-111771 (GHSA-rvq5-wwqv-78pq)6.8 Mediumsdk 1.6.0..1.31.0; server 2.0.0..2.2.0; express 2.0.0..2.0.1; server-legacy 2.0.0..2.3.0sdk 1.32.0, server 2.3.0, express 2.0.2, server-legacy 2.3.1Yes: set expectedResource AND return AuthInfo.resource
npm@modelcontextprotocol/sdk, @modelcontextprotocol/clientCVE-2026-111769 (GHSA-6prh-2h8m-c8cw)6.5 Mediumsdk <= 1.31.0; client 2.0.0..2.2.0sdk 1.32.0, client 2.3.0Same-origin redirect policy enabled by default; rotate leaked secrets

Technical analysis

1. CVE-2026-111770: the shared-server regression and the quickstart pattern that caused it

In February 2026, @modelcontextprotocol/sdk 1.26.0 patched CVE-2026-25536 (GHSA-345p-7cg4-v4c7), a bug where sharing a single McpServer or transport instance across HTTP connections routed responses using an internal requestId -> stream map keyed only by the JSON-RPC id. Because MCP client SDKs initialize their request counter at 0, two clients talking to the same HTTP endpoint generated colliding request IDs (0, 1, 2), causing Client A’s tool responses and server notifications to be written to Client B’s HTTP stream.

When the 2.x rewrite (@modelcontextprotocol/server 2.0.0) shipped on 27 July 2026, that guard was omitted. Worse, the official READMEs for @modelcontextprotocol/hono, @modelcontextprotocol/express, @modelcontextprotocol/fastify, and @modelcontextprotocol/node taught developers to instantiate McpServer (and in Hono, WebStandardStreamableHTTPServerTransport) once at module scope and reuse it on every request.

Commit 84804c22e4 (PR #2918) fixes both the runtime enforcement and the documentation across all four middleware packages. In packages/server/src/server/server.ts and streamableHttp.ts, the SDK now throws immediately if a Server is connected twice or a stateless transport handles a second request:

--- a/packages/server/src/server/server.ts
+++ b/packages/server/src/server/server.ts
@@ -351,6 +352,17 @@ export class Server extends Protocol<ServerContext> {
+    /** Attaches to the given transport; rejects while this instance is connected to another one. */
+    override async connect(transport: Transport): Promise<void> {
+        if (this.transport !== undefined) {
+            throw new SdkError(
+                SdkErrorCode.AlreadyConnected,
+                'Use a separate Server instance per connection: this instance is already connected to a transport. To connect it to a new transport, call close() first.'
+            );
+        }
+        await super.connect(transport);
+    }
--- a/packages/server/src/server/streamableHttp.ts
+++ b/packages/server/src/server/streamableHttp.ts
@@ -415,6 +424,12 @@ export class WebStandardStreamableHTTPServerTransport implements Transport {
+        // A stateless transport (no sessionIdGenerator) serves exactly one request.
+        if (!this.sessionIdGenerator && this._hasHandledRequest) {
+            throw new Error('Stateless transport cannot be reused across requests. Create a new transport per request.');
+        }
+        this._hasHandledRequest = true;

And the adapter READMEs (such as packages/middleware/hono/README.md) show the required code change:

--- a/packages/middleware/hono/README.md
+++ b/packages/middleware/hono/README.md
@@ -30,12 +30,14 @@
-const server = new McpServer({ name: 'my-server', version: '1.0.0' });
-const transport = new WebStandardStreamableHTTPServerTransport({ sessionIdGenerator: undefined });
-await server.connect(transport);
-
 const app = createMcpHonoApp();
-app.all('/mcp', c => transport.handleRequest(c.req.raw, { parsedBody: c.get('parsedBody') }));
+app.all('/mcp', async c => {
+    // Stateless example: create a server and a transport per request.
+    const server = new McpServer({ name: 'my-server', version: '1.0.0' });
+    const transport = new WebStandardStreamableHTTPServerTransport({ sessionIdGenerator: undefined });
+    await server.connect(transport);
+    return transport.handleRequest(c.req.raw, { parsedBody: c.get('parsedBody') });
+});

2. CVE-2026-111767: InMemoryTaskStore ignored _sessionId

In @modelcontextprotocol/sdk 1.24.0 through 1.31.0, every method on InMemoryTaskStore (createTask, getTask, getTaskResult, updateTaskStatus, listTasks) received an optional _sessionId?: string parameter from the protocol layer and discarded it. When an HTTP MCP server passed a single InMemoryTaskStore to McpServer or Server, any client could call tasks/list to enumerate every active task on the server, tasks/result to read another client’s task output or queued messages, or tasks/cancel to abort another user’s job.

Commit 727075b5bb (PR #2925) stores sessionId on each StoredTask and gates every lookup and queue delivery on session equality:

--- a/src/experimental/tasks/stores/in-memory.ts
+++ b/src/experimental/tasks/stores/in-memory.ts
@@ -39,7 +45,18 @@ export class InMemoryTaskStore implements TaskStore {
-    async createTask(taskParams: CreateTaskOptions, requestId: RequestId, request: Request, _sessionId?: string): Promise<Task> {
+    private lookup(taskId: string, sessionId?: string): StoredTask | undefined {
+        const stored = this.tasks.get(taskId);
+        if (stored && sessionId !== undefined && stored.sessionId !== undefined && stored.sessionId !== sessionId) {
+            return undefined;
+        }
+        return stored;
+    }
+
+    async createTask(taskParams: CreateTaskOptions, requestId: RequestId, request: Request, sessionId?: string): Promise<Task> {
...
-    async getTask(taskId: string, _sessionId?: string): Promise<Task | null> {
-        const stored = this.tasks.get(taskId);
+    async getTask(taskId: string, sessionId?: string): Promise<Task | null> {
+        const stored = this.lookup(taskId, sessionId);
         return stored ? { ...stored.task } : null;
     }

3. CVE-2026-111768 and CVE-2026-111771: opt-in fixes for heap OOM and bearer audience checks

Two advisories in the batch require explicit configuration after upgrading.

For tool argument element amplification (CVE-2026-111768), even after GHSA-f897-62hf-6gxq capped HTTP request bodies at 4 MiB and JSON-RPC batches at 100 messages in 1.30.1 and 2.1.0, a single 4 MiB JSON body containing hundreds of thousands of tiny array elements ([0,0,0,...]) or empty object keys ({"a":0,"b":0,...}) causes Zod schema validation to allocate enough per-element error and parse state to crash Node.js with JavaScript heap out of memory. Commit 6d8dbc6cb5 adds toolInputElementCount() before tool.inputSchema runs, but leaves maxToolInputElements undefined by default:

if (
    this._maxToolInputElements !== undefined &&
    toolInputElementCount(args, this._maxToolInputElements) > this._maxToolInputElements
) {
    throw new ProtocolError(
        ProtocolErrorCode.InvalidParams,
        `Invalid arguments for tool ${toolName}: arguments contain more than the maximum of ${this._maxToolInputElements} elements`
    );
}

For bearer token audience confusion (CVE-2026-111771), when an MCP server shares an OAuth authorization server with other internal APIs, requireBearerAuth() did not check that the token’s audience (aud or RFC 8707 resource indicator) matched the MCP server. In 1.32.0 and 2.3.0, the SDK adds expectedResource to requireBearerAuth(), also off by default. Enabling expectedResource without updating your verifyAccessToken() callback to populate AuthInfo.resource causes every request to fail with 401.

The fifth CVE covers cross-origin redirect credential leaks (CVE-2026-111769). In @modelcontextprotocol/sdk <= 1.31.0 and @modelcontextprotocol/client 2.0.0..2.2.0, StreamableHTTPClientTransport, SSEClientTransport, and OAuth auth() followed 301, 302, 307, and 308 redirects across origins, forwarding custom headers (X-API-Key, mcp-session-id) and, on 307 and 308, the POST body containing refresh_token and client_secret. Versions 1.32.0 and 2.3.0 restrict redirects to the same origin by default.

IOCs and runtime signatures

No in-the-wild exploitation campaign is tied to these five CVEs, so there are no external C2 domains or malware hashes. When reviewing logs and codebases, look for these runtime and configuration signatures:

  • Process crashes with FATAL ERROR: Reached heap limit Allocation failed - JavaScript heap out of memory on HTTP requests under 4 MiB (CVE-2026-111768)
  • SdkError: Use a separate Server instance per connection: this instance is already connected to a transport (ALREADY_CONNECTED) or Stateless transport cannot be reused across requests after upgrading to @modelcontextprotocol/server >= 2.3.0 (CVE-2026-111770)
  • Outbound 301, 302, 307, or 308 redirect responses in MCP client logs pointing to unexpected third-party origins, or code setting redirectPolicy: 'follow' (CVE-2026-111769)

Detection and remediation

1. Find vulnerable package versions and shared-instance code patterns

Audit both your lockfiles and your server initialization code, because npm audit will not flag the shared-instance pattern or missing maxToolInputElements / expectedResource options:

# Check installed MCP TypeScript packages
npm ls @modelcontextprotocol/sdk @modelcontextprotocol/server @modelcontextprotocol/client @modelcontextprotocol/express @modelcontextprotocol/node @modelcontextprotocol/hono @modelcontextprotocol/server-legacy

# Check for experimental TaskStore usage (CVE-2026-111767)
rg -n -i 'taskstore|InMemoryTaskStore' --glob '!node_modules/**' .

# Find module-scoped McpServer or StreamableHTTPServerTransport instances (CVE-2026-111770)
rg -n 'new (McpServer|Server|WebStandardStreamableHTTPServerTransport|NodeStreamableHTTPServerTransport|StreamableHTTPServerTransport)\s*\(' \
  --glob '!node_modules/**' .

# Check whether maxToolInputElements and expectedResource are configured (CVE-2026-111768, CVE-2026-111771)
rg -n 'maxToolInputElements|maxRequestBodySize|expectedResource|requireBearerAuth' \
  --glob '!node_modules/**' .

2. Upgrade packages and update server construction and options

  1. Upgrade all MCP packages:
    # v1.x SDK users
    npm install @modelcontextprotocol/sdk@^1.32.1
    
    # v2.x modular package users
    npm install @modelcontextprotocol/server@^2.3.1 @modelcontextprotocol/client@^2.3.1 \
      @modelcontextprotocol/express@^2.0.2 @modelcontextprotocol/node@^2.1.1 @modelcontextprotocol/hono@^2.0.2
  2. Move McpServer and stateless transport creation inside the request handler (CVE-2026-111770) and set maxToolInputElements (CVE-2026-111768):
    import { McpServer, WebStandardStreamableHTTPServerTransport } from '@modelcontextprotocol/server';
    
    app.all('/mcp', async (c) => {
      const server = new McpServer(
        { name: 'my-server', version: '1.0.0' },
        { maxToolInputElements: 10_000 }
      );
      registerTools(server);
      const transport = new WebStandardStreamableHTTPServerTransport({
        sessionIdGenerator: undefined,
        maxRequestBodySize: 256 * 1024,
      });
      await server.connect(transport);
      return transport.handleRequest(c.req.raw);
    });
  3. Wire both halves of expectedResource in requireBearerAuth() (CVE-2026-111771):
    requireBearerAuth({
      expectedResource: new URL('https://mcp.example.com/mcp'),
      verifier: {
        async verifyAccessToken(token) {
          const claims = await verifyJwt(token);
          return {
            token,
            clientId: claims.client_id,
            scopes: claims.scope.split(' '),
            resource: new URL(claims.aud),
          };
        },
      },
    });
  4. Rotate credentials if an MCP client connected to untrusted servers (CVE-2026-111769): If an application using StreamableHTTPClientTransport, SSEClientTransport, or auth() connected to an external MCP server that could have redirected across origins before 1.32.0 / 2.3.0, rotate any custom header API keys, OAuth client_secret values, and refresh tokens that client held.

Timeline

  • 2026-02-04: @modelcontextprotocol/sdk 1.26.0 fixes the original shared-instance cross-client leak (CVE-2026-25536 / GHSA-345p-7cg4-v4c7) on the 1.x line.
  • 2026-07-27: @modelcontextprotocol/server 2.0.0 is published to npm without the shared-instance guard, and the v2 middleware READMEs document module-scoped server reuse.
  • 2026-09-23: @modelcontextprotocol/sdk 1.30.1 and @modelcontextprotocol/server 2.1.0 add the 4 MiB body limit and 100-message batch cap (GHSA-f897-62hf-6gxq).
  • 2026-10-02: @modelcontextprotocol/sdk 1.32.0 and @modelcontextprotocol/server 2.3.0 ship on npm with commits 84804c22e4, 727075b5bb, and 6d8dbc6cb5.
  • 2026-10-20 (18:26 to 20:07 UTC): Five advisories (CVE-2026-111767 through CVE-2026-111771) are published to the global GitHub Advisory Database and NVD.
  • 2026-10-21: Independent analysis documents the 18-day gap between the silent 2 October release and the 20 October CVE assignments, as well as the opt-in default posture of maxToolInputElements and expectedResource.

From research to remediation

Check whether this pattern exists in your codebase

Turn this research into a remediation workflow. Scan dependencies and package manifests for similar supply-chain risk, then prioritize fixes with reachability context.

References