Skip to content

Commit c4271fc

Browse files
logaretmclaude
andcommitted
fix(core): handle stateless MCP wrapper transport correlation
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>
1 parent 3572788 commit c4271fc

File tree

2 files changed

+38
-6
lines changed

2 files changed

+38
-6
lines changed

packages/core/src/integrations/mcp-server/transport.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ export function wrapTransportOnMessage(transport: MCPTransport, options: Resolve
4343
if (isInitialize) {
4444
try {
4545
initSessionData = extractSessionDataFromInitializeRequest(message);
46-
storeSessionDataForTransport(this, initSessionData);
46+
storeSessionDataForTransport(transport, initSessionData);
4747
} catch {
4848
// noop
4949
}
@@ -65,7 +65,7 @@ export function wrapTransportOnMessage(transport: MCPTransport, options: Resolve
6565
});
6666
}
6767

68-
storeSpanForRequest(this, message.id, span, message.method);
68+
storeSpanForRequest(transport, message.id, span, message.method);
6969

7070
return withActiveSpan(span, () => {
7171
return (originalOnMessage as (...args: unknown[]) => unknown).call(this, message, extra);
@@ -114,14 +114,14 @@ export function wrapTransportSend(transport: MCPTransport, options: ResolvedMcpO
114114
if (message.result.protocolVersion || message.result.serverInfo) {
115115
try {
116116
const serverData = extractSessionDataFromInitializeResponse(message.result);
117-
updateSessionDataForTransport(this, serverData);
117+
updateSessionDataForTransport(transport, serverData);
118118
} catch {
119119
// noop
120120
}
121121
}
122122
}
123123

124-
completeSpanWithResults(this, message.id, message.result, options, !!message.error);
124+
completeSpanWithResults(transport, message.id, message.result, options, !!message.error);
125125
}
126126
}
127127

@@ -139,8 +139,8 @@ export function wrapTransportOnClose(transport: MCPTransport): void {
139139
if (transport.onclose) {
140140
fill(transport, 'onclose', originalOnClose => {
141141
return function (this: MCPTransport, ...args: unknown[]) {
142-
cleanupPendingSpansForTransport(this);
143-
cleanupSessionDataForTransport(this);
142+
cleanupPendingSpansForTransport(transport);
143+
cleanupSessionDataForTransport(transport);
144144
return (originalOnClose as (...args: unknown[]) => unknown).call(this, ...args);
145145
};
146146
});

packages/core/test/lib/integrations/mcp-server/transportInstrumentation.test.ts

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -880,6 +880,38 @@ describe('MCP Server Transport Instrumentation', () => {
880880
expect(mockSpan.end).toHaveBeenCalled();
881881
});
882882

883+
it('should correlate spans correctly for stateless wrapper transports', async () => {
884+
const { wrapper, inner } = createMockWrapperTransport('stateless-wrapper-session');
885+
inner.sessionId = undefined;
886+
887+
const mockMcpServer = createMockMcpServer();
888+
const wrappedMcpServer = wrapMcpServerWithSentry(mockMcpServer);
889+
890+
await wrappedMcpServer.connect(wrapper);
891+
892+
const mockSpan = { setAttributes: vi.fn(), end: vi.fn() };
893+
startInactiveSpanSpy.mockReturnValue(mockSpan as any);
894+
895+
inner.onmessage?.call(
896+
inner,
897+
{
898+
jsonrpc: '2.0',
899+
method: 'tools/call',
900+
id: 'stateless-wrapper-req-1',
901+
params: { name: 'test-tool' },
902+
},
903+
{},
904+
);
905+
906+
await wrapper.send({
907+
jsonrpc: '2.0',
908+
id: 'stateless-wrapper-req-1',
909+
result: { content: [{ type: 'text', text: 'success' }] },
910+
});
911+
912+
expect(mockSpan.end).toHaveBeenCalled();
913+
});
914+
883915
it('should handle initialize request/response with wrapper transport', async () => {
884916
const { wrapper } = createMockWrapperTransport('init-wrapper-session');
885917
const mockMcpServer = createMockMcpServer();

0 commit comments

Comments
 (0)