modelcontextprotocol / modelcontextprotocol/typescript-sdk
Public API for multi-node session rehydration (e.g. transport.adoptSession(id))
Nobody has claimed this yet.
- 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,_requestResponseMapare 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
- Synthetic initialize — drive a fake
initializerequest through the transport. Works but costs 5-10ms and triggersMcpServerside effects (capability negotiation, notifications). - EventStore-based resumption — the
eventStorehook doesn't help because it replays events, not session state. - 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
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- 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