modelcontextprotocol / modelcontextprotocol/typescript-sdk

Public API for multi-node session rehydration (e.g. transport.adoptSession(id))

Open
#1,882 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

enhancement needs decision P3
Dominant language
TypeScript
Stars
13.4k
Forks
2.2k
Avg merge
3d 15h
Merged PRs (30d)
4

Description

Problem

The onsessionclosed docstring in webStandardStreamableHttp.d.ts (lines 57-67) explicitly describes a multi-node deployment pattern:

if you are handling HTTP requests from multiple nodes you might want to close each WebStandardStreamableHTTPServerTransport after a request is completed while still keeping the session open/running.

This is exactly the pattern we need — we run an MCP server on Azure App Service behind a Cloudflare Worker proxy, and every container restart (deploy) wipes in-process session state, forcing clients to re-handshake.

To rehydrate a session on a fresh transport instance, downstream servers currently have to reach through private fields:

const inner = (transport as any)._webStandardTransport;
inner._initialized = true;
inner.sessionId = existingSessionId;

This works today (SDK 1.29.0) because _initialized and sessionId are plain TS private fields (runtime-writable JS properties), but it's fragile — any move to ES # private fields or internal restructuring silently breaks it.

Proposed API

A public method on both WebStandardStreamableHTTPServerTransport and StreamableHTTPServerTransport:

/**
 * Adopt an existing session ID on this transport, bypassing the
 * initialize handshake. Use this for multi-node deployments where
 * session state is externalized (e.g. to a database) and transports
 * are constructed per-request.
 *
 * @param sessionId - A previously-initialized session ID
 */
adoptSession(sessionId: string): void {
  this._initialized = true;
  this.sessionId = sessionId;
}

For the Node wrapper (StreamableHTTPServerTransport), it would delegate to this._webStandardTransport.adoptSession(sessionId).

Evidence

We built a POC that exercises 4 scenarios (fresh transport rejection, direct monkey-patch, synthetic-init workaround, Node wrapper field-reach). All 4 pass on SDK 1.29.0.
Our production workaround includes a runtime writability assertion that crashes loudly if _initialized becomes non-writable, pinned SDK version, and the POC as a pre-upgrade regression gate.

Context

  • ADR documenting this decision: internal (ADR-017, MCP session externalization)
  • The session state that needs to persist is minimal: just sessionId + "initialized" flag (~100 bytes)
  • eventStore, _streamMapping, _requestResponseMap are all per-request transient and don't need rehydration
  • This pattern is relevant to any multi-node or serverless deployment of an MCP server

Alternatives considered

  1. Synthetic initialize — drive a fake initialize request through the transport. Works but costs 5-10ms and triggers McpServer side effects (capability negotiation, notifications).
  2. EventStore-based resumption — the eventStore hook doesn't help because it replays events, not session state.
  3. Direct private field mutation — what we're doing now, with the fragility noted above.

A public adoptSession() method would make option 3 safe and officially supported.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by reading the onsessionclosed documentation in webStandardStreamableHttp.d.ts at lines 57-67, then inspect WebStandardStreamableHTTPServerTransport and StreamableHTTPServerTransport. Use the four POC scenarios described in the issue as behavioral checks. Done means both transports expose the proposed session-adoption entry point and the Node wrapper forwards it without private-field access.

Written by the indexing model from the issue text.

Assessment

Tech stack
typescript
Domain
api, backend-api-design, distributed-systems
Issue type
Feature
Difficulty
3/5
Estimated time
1-2 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
48/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.