4
4
*/
5
5
6
6
import { getClient } from '../../currentScopes' ;
7
- import { SPAN_STATUS_ERROR , withActiveSpan } from '../../tracing' ;
7
+ import { SPAN_STATUS_ERROR } from '../../tracing' ;
8
8
import type { Span } from '../../types-hoist/span' ;
9
9
import { extractToolResultAttributes } from './attributeExtraction' ;
10
10
import { filterMcpPiiFromSpanData } from './piiFiltering' ;
11
- import type { RequestId , RequestSpanMapValue } from './types' ;
11
+ import type { MCPTransport , RequestId , RequestSpanMapValue } from './types' ;
12
12
13
- // Simplified correlation system that works with or without sessionId
14
- // Maps requestId directly to span data for stateless operation
15
- const requestIdToSpanMap = new Map < RequestId , RequestSpanMapValue > ( ) ;
13
+ // Transport-scoped correlation system that prevents collisions between different MCP sessions
14
+ // Each transport instance gets its own correlation map, eliminating request ID conflicts
15
+ const transportToSpanMap = new WeakMap < MCPTransport , Map < RequestId , RequestSpanMapValue > > ( ) ;
16
+
17
+ /**
18
+ * Gets or creates the span map for a specific transport instance
19
+ */
20
+ function getOrCreateSpanMap ( transport : MCPTransport ) : Map < RequestId , RequestSpanMapValue > {
21
+ let spanMap = transportToSpanMap . get ( transport ) ;
22
+ if ( ! spanMap ) {
23
+ spanMap = new Map ( ) ;
24
+ transportToSpanMap . set ( transport , spanMap ) ;
25
+ }
26
+ return spanMap ;
27
+ }
16
28
17
29
/**
18
30
* Stores span context for later correlation with handler execution
19
31
*/
20
- export function storeSpanForRequest ( requestId : RequestId , span : Span , method : string ) : void {
21
- requestIdToSpanMap . set ( requestId , {
32
+ export function storeSpanForRequest ( transport : MCPTransport , requestId : RequestId , span : Span , method : string ) : void {
33
+ const spanMap = getOrCreateSpanMap ( transport ) ;
34
+ spanMap . set ( requestId , {
22
35
span,
23
36
method,
24
37
startTime : Date . now ( ) ,
25
38
} ) ;
26
39
}
27
40
28
- /**
29
- * Associates handler execution with the corresponding request span
30
- */
31
- export function associateContextWithRequestSpan < T > (
32
- extraHandlerData : { requestId : RequestId } | undefined ,
33
- cb : ( ) => T ,
34
- ) : T {
35
- if ( extraHandlerData ) {
36
- const { requestId } = extraHandlerData ;
37
-
38
- const spanData = requestIdToSpanMap . get ( requestId ) ;
39
- if ( ! spanData ) {
40
- return cb ( ) ;
41
- }
42
-
43
- // Keep span in map for response enrichment (don't delete yet)
44
- return withActiveSpan ( spanData . span , ( ) => {
45
- return cb ( ) ;
46
- } ) ;
47
- }
48
-
49
- return cb ( ) ;
50
- }
51
-
52
41
/**
53
42
* Completes span with tool results and cleans up correlation
54
43
*/
55
- export function completeSpanWithResults ( requestId : RequestId , result : unknown ) : void {
56
- const spanData = requestIdToSpanMap . get ( requestId ) ;
44
+ export function completeSpanWithResults ( transport : MCPTransport , requestId : RequestId , result : unknown ) : void {
45
+ const spanMap = getOrCreateSpanMap ( transport ) ;
46
+ const spanData = spanMap . get ( requestId ) ;
57
47
if ( spanData ) {
58
48
const { span, method } = spanData ;
59
49
@@ -68,24 +58,27 @@ export function completeSpanWithResults(requestId: RequestId, result: unknown):
68
58
}
69
59
70
60
span . end ( ) ;
71
- requestIdToSpanMap . delete ( requestId ) ;
61
+ spanMap . delete ( requestId ) ;
72
62
}
73
63
}
74
64
75
65
/**
76
- * Cleans up all pending spans ( for transport close )
66
+ * Cleans up pending spans for a specific transport (when that transport closes )
77
67
*/
78
- export function cleanupAllPendingSpans ( ) : number {
79
- const pendingCount = requestIdToSpanMap . size ;
68
+ export function cleanupPendingSpansForTransport ( transport : MCPTransport ) : number {
69
+ const spanMap = transportToSpanMap . get ( transport ) ;
70
+ if ( ! spanMap ) return 0 ;
71
+
72
+ const pendingCount = spanMap . size ;
80
73
81
- for ( const [ , spanData ] of requestIdToSpanMap ) {
74
+ for ( const [ , spanData ] of spanMap ) {
82
75
spanData . span . setStatus ( {
83
76
code : SPAN_STATUS_ERROR ,
84
77
message : 'cancelled' ,
85
78
} ) ;
86
79
spanData . span . end ( ) ;
87
80
}
88
81
89
- requestIdToSpanMap . clear ( ) ;
82
+ spanMap . clear ( ) ;
90
83
return pendingCount ;
91
84
}
0 commit comments