fix(core): handle stateless MCP wrapper transport correlation#20293
fix(core): handle stateless MCP wrapper transport correlation#20293
Conversation
Use the connected transport as the stable correlation key for MCP wrapper transports in stateless mode, and add a regression test covering the inner-vs-outer transport split. Co-Authored-By: GPT-5 <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
This PR fixes MCP server transport span correlation when using stateless wrapper transports (e.g. StreamableHTTPServerTransport with sessionIdGenerator: undefined) by using the connected transport instance as the correlation key across wrapper layers, and adds a regression test to cover this scenario.
Changes:
- Use the
transportpassed toconnect()(instead of handlerthis) as the key for span/session correlation in transport wrappers. - Add a regression test ensuring tool-call spans are properly completed for stateless wrapper transports.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| packages/core/src/integrations/mcp-server/transport.ts | Switch correlation/session operations to use the connected transport instance for wrapper-layer stability. |
| packages/core/test/lib/integrations/mcp-server/transportInstrumentation.test.ts | Add regression test reproducing stateless wrapper onmessage/send split and asserting span completion. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| if (isInitialize) { | ||
| try { | ||
| initSessionData = extractSessionDataFromInitializeRequest(message); | ||
| storeSessionDataForTransport(this, initSessionData); | ||
| storeSessionDataForTransport(transport, initSessionData); |
There was a problem hiding this comment.
For stateless wrapper transports (no sessionId), switching session-data storage to use the outer transport instance means session data is now keyed in the WeakMap by the wrapper. However, span attribute building still passes the handler this (which can be the inner transport) into buildMcpServerSpanConfig/buildTransportAttributes, so later spans won’t be able to read the stored client/protocol info in stateless wrapper scenarios. To keep enrichment working, consider ensuring reads/writes use the same correlation key (e.g., pass the connected transport into span config building), or store session data for both transport and this when they differ and sessionId is absent.
| try { | ||
| const serverData = extractSessionDataFromInitializeResponse(message.result); | ||
| updateSessionDataForTransport(this, serverData); | ||
| updateSessionDataForTransport(transport, serverData); |
There was a problem hiding this comment.
Same keying issue as above: for stateless wrapper transports, updating session data using the outer transport instance will store it under the wrapper in the WeakMap, but subsequent span attribute extraction may run against the inner transport (this) and miss these updates. Align the transport instance used for both session-data updates and attribute reads (or dual-write when this !== transport and there is no sessionId).
| updateSessionDataForTransport(transport, serverData); | |
| updateSessionDataForTransport(this, serverData); | |
| if (this !== transport) { | |
| updateSessionDataForTransport(transport, serverData); | |
| } |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit c4271fc. Configure here.
Use the connected transport as the stable key for MCP span attribute building as well as correlation/session writes, and add a regression test covering initialize metadata on later stateless wrapper spans. Co-Authored-By: GPT-5 <noreply@anthropic.com>

This PR handles stateless MCP wrappers by using the connected transport instance as the stable MCP correlation key across wrapper layers.
I verified the issue and the fix by adding a regression test for stateless wrapper transports where the request path runs on the inner transport and the response path runs on the wrapper.
Closes #20290