@@ -26,15 +26,9 @@ import {
26
26
NETWORK_PROTOCOL_VERSION_ATTRIBUTE ,
27
27
NETWORK_TRANSPORT_ATTRIBUTE ,
28
28
} from './attributes' ;
29
- import type { ExtraHandlerData , JsonRpcNotification , JsonRpcRequest , MCPTransport } from './types' ;
29
+ import type { ExtraHandlerData , JsonRpcNotification , JsonRpcRequest , McpSpanConfig , MCPTransport } from './types' ;
30
30
31
- // =============================================================================
32
- // TYPE GUARDS
33
- // =============================================================================
34
-
35
- /**
36
- *
37
- */
31
+ /** Validates if a message is a JSON-RPC request */
38
32
export function isJsonRpcRequest ( message : unknown ) : message is JsonRpcRequest {
39
33
return (
40
34
typeof message === 'object' &&
@@ -46,9 +40,7 @@ export function isJsonRpcRequest(message: unknown): message is JsonRpcRequest {
46
40
) ;
47
41
}
48
42
49
- /**
50
- *
51
- */
43
+ /** Validates if a message is a JSON-RPC notification */
52
44
export function isJsonRpcNotification ( message : unknown ) : message is JsonRpcNotification {
53
45
return (
54
46
typeof message === 'object' &&
@@ -60,9 +52,7 @@ export function isJsonRpcNotification(message: unknown): message is JsonRpcNotif
60
52
) ;
61
53
}
62
54
63
- /**
64
- *
65
- */
55
+ /** Extracts target info from method and params based on method type */
66
56
export function validateMcpServerInstance ( instance : unknown ) : boolean {
67
57
if (
68
58
typeof instance === 'object' &&
@@ -78,50 +68,39 @@ export function validateMcpServerInstance(instance: unknown): boolean {
78
68
return false ;
79
69
}
80
70
81
- // =============================================================================
82
- // ATTRIBUTE EXTRACTION
83
- // =============================================================================
71
+ /** Extracts target info from method and params based on method type */
72
+ function extractTargetInfo ( method : string , params : Record < string , unknown > ) : {
73
+ target ?: string ;
74
+ attributes : Record < string , string >
75
+ } {
76
+ let target : string | undefined ;
77
+ let attributeKey : string | undefined ;
84
78
85
- /**
86
- *
87
- */
88
- export function extractTarget ( method : string , params : Record < string , unknown > ) : string | undefined {
89
79
switch ( method ) {
90
80
case 'tools/call' :
91
- return typeof params ?. name === 'string' ? params . name : undefined ;
81
+ target = typeof params ?. name === 'string' ? params . name : undefined ;
82
+ attributeKey = 'mcp.tool.name' ;
83
+ break ;
92
84
case 'resources/read' :
93
85
case 'resources/subscribe' :
94
86
case 'resources/unsubscribe' :
95
- return typeof params ?. uri === 'string' ? params . uri : undefined ;
87
+ target = typeof params ?. uri === 'string' ? params . uri : undefined ;
88
+ attributeKey = 'mcp.resource.uri' ;
89
+ break ;
96
90
case 'prompts/get' :
97
- return typeof params ?. name === 'string' ? params . name : undefined ;
98
- default :
99
- return undefined ;
91
+ target = typeof params ?. name === 'string' ? params . name : undefined ;
92
+ attributeKey = 'mcp.prompt.name' ;
93
+ break ;
100
94
}
101
- }
102
95
103
- /**
104
- *
105
- */
106
- export function getTargetAttributes ( method : string , target : string ) : Record < string , string > {
107
- switch ( method ) {
108
- case 'tools/call' :
109
- return { 'mcp.tool.name' : target } ;
110
- case 'resources/read' :
111
- case 'resources/subscribe' :
112
- case 'resources/unsubscribe' :
113
- return { 'mcp.resource.uri' : target } ;
114
- case 'prompts/get' :
115
- return { 'mcp.prompt.name' : target } ;
116
- default :
117
- return { } ;
118
- }
96
+ return {
97
+ target,
98
+ attributes : target && attributeKey ? { [ attributeKey ] : target } : { }
99
+ } ;
119
100
}
120
101
121
- /**
122
- *
123
- */
124
- export function getRequestArguments ( method : string , params : Record < string , unknown > ) : Record < string , string > {
102
+ /** Extracts request arguments based on method type */
103
+ function getRequestArguments ( method : string , params : Record < string , unknown > ) : Record < string , string > {
125
104
const args : Record < string , string > = { } ;
126
105
127
106
// Argument capture for different methods
@@ -153,14 +132,8 @@ export function getRequestArguments(method: string, params: Record<string, unkno
153
132
return args ;
154
133
}
155
134
156
- // =============================================================================
157
- // TRANSPORT DETECTION
158
- // =============================================================================
159
-
160
- /**
161
- *
162
- */
163
- export function getTransportTypes ( transport : MCPTransport ) : { mcpTransport : string ; networkTransport : string } {
135
+ /** Extracts transport types based on transport constructor name */
136
+ function getTransportTypes ( transport : MCPTransport ) : { mcpTransport : string ; networkTransport : string } {
164
137
const transportName = transport . constructor ?. name ?. toLowerCase ( ) || '' ;
165
138
166
139
if ( transportName . includes ( 'sse' ) ) return { mcpTransport : 'sse' , networkTransport : 'tcp' } ;
@@ -170,22 +143,8 @@ export function getTransportTypes(transport: MCPTransport): { mcpTransport: stri
170
143
return { mcpTransport : 'http' , networkTransport : 'tcp' } ;
171
144
}
172
145
173
- // =============================================================================
174
- // NOTIFICATION HANDLING
175
- // =============================================================================
176
-
177
- /**
178
- * Get notification span name following OpenTelemetry conventions
179
- * For notifications, we use the method name directly as per JSON-RPC conventions
180
- */
181
- export function getNotificationSpanName ( method : string ) : string {
182
- return method ;
183
- }
184
-
185
- /**
186
- * Extract additional attributes for specific notification types
187
- */
188
- export function getNotificationAttributes (
146
+ /** Extracts additional attributes for specific notification types */
147
+ function getNotificationAttributes (
189
148
method : string ,
190
149
params : Record < string , unknown > ,
191
150
) : Record < string , string | number > {
@@ -259,51 +218,14 @@ export function getNotificationAttributes(
259
218
return attributes ;
260
219
}
261
220
262
- // =============================================================================
263
- // CLIENT INFO EXTRACTION
264
- // =============================================================================
265
-
266
- /**
267
- *
268
- */
269
- export function extractClientAddress ( extra : ExtraHandlerData ) : string | undefined {
270
- return (
271
- extra ?. requestInfo ?. remoteAddress ||
272
- extra ?. clientAddress ||
273
- extra ?. request ?. ip ||
274
- extra ?. request ?. connection ?. remoteAddress
275
- ) ;
276
- }
277
-
278
- /**
279
- *
280
- */
281
- export function extractClientPort ( extra : ExtraHandlerData ) : number | undefined {
282
- return extra ?. requestInfo ?. remotePort || extra ?. clientPort || extra ?. request ?. connection ?. remotePort ;
283
- }
284
-
285
- // =============================================================================
286
- // SPAN NAMING
287
- // =============================================================================
288
221
289
222
/**
290
- *
223
+ * Creates a span name based on the method and target
291
224
*/
292
- export function createSpanName ( method : string , target ?: string ) : string {
225
+ function createSpanName ( method : string , target ?: string ) : string {
293
226
return target ? `${ method } ${ target } ` : method ;
294
227
}
295
228
296
- // =============================================================================
297
- // UNIFIED SPAN BUILDER
298
- // =============================================================================
299
-
300
- interface McpSpanConfig {
301
- type : 'request' | 'notification-incoming' | 'notification-outgoing' ;
302
- message : JsonRpcRequest | JsonRpcNotification ;
303
- transport : MCPTransport ;
304
- extra ?: ExtraHandlerData ;
305
- callback : ( ) => unknown ;
306
- }
307
229
308
230
/**
309
231
* Unified builder for creating MCP spans
@@ -317,11 +239,11 @@ function createMcpSpan(config: McpSpanConfig): unknown {
317
239
// Determine span name based on type and OTEL conventions
318
240
let spanName : string ;
319
241
if ( type === 'request' ) {
320
- const target = extractTarget ( method , params || { } ) ;
321
- spanName = createSpanName ( method , target ) ;
242
+ const targetInfo = extractTargetInfo ( method , params || { } ) ;
243
+ spanName = createSpanName ( method , targetInfo . target ) ;
322
244
} else {
323
- // For notifications, use method name directly (OTEL convention)
324
- spanName = getNotificationSpanName ( method ) ;
245
+ // For notifications, use method name directly per OpenTelemetry conventions
246
+ spanName = method ;
325
247
}
326
248
327
249
// Build attributes
@@ -354,14 +276,13 @@ function buildTransportAttributes(
354
276
extra ?: ExtraHandlerData ,
355
277
) : Record < string , string | number > {
356
278
const sessionId = transport . sessionId ;
357
- const clientAddress = extra ? extractClientAddress ( extra ) : undefined ;
358
- const clientPort = extra ? extractClientPort ( extra ) : undefined ;
279
+ const clientInfo = extra ? extractClientInfo ( extra ) : { } ;
359
280
const { mcpTransport, networkTransport } = getTransportTypes ( transport ) ;
360
281
361
282
return {
362
283
...( sessionId && { [ MCP_SESSION_ID_ATTRIBUTE ] : sessionId } ) ,
363
- ...( clientAddress && { [ CLIENT_ADDRESS_ATTRIBUTE ] : clientAddress } ) ,
364
- ...( clientPort && { [ CLIENT_PORT_ATTRIBUTE ] : clientPort } ) ,
284
+ ...( clientInfo . address && { [ CLIENT_ADDRESS_ATTRIBUTE ] : clientInfo . address } ) ,
285
+ ...( clientInfo . port && { [ CLIENT_PORT_ATTRIBUTE ] : clientInfo . port } ) ,
365
286
[ MCP_TRANSPORT_ATTRIBUTE ] : mcpTransport ,
366
287
[ NETWORK_TRANSPORT_ATTRIBUTE ] : networkTransport ,
367
288
[ NETWORK_PROTOCOL_VERSION_ATTRIBUTE ] : '2.0' ,
@@ -378,11 +299,11 @@ function buildTypeSpecificAttributes(
378
299
) : Record < string , string | number > {
379
300
if ( type === 'request' ) {
380
301
const request = message as JsonRpcRequest ;
381
- const target = extractTarget ( request . method , params || { } ) ;
302
+ const targetInfo = extractTargetInfo ( request . method , params || { } ) ;
382
303
383
304
return {
384
305
...( request . id !== undefined && { [ MCP_REQUEST_ID_ATTRIBUTE ] : String ( request . id ) } ) ,
385
- ...( target && getTargetAttributes ( request . method , target ) ) ,
306
+ ...targetInfo . attributes ,
386
307
...getRequestArguments ( request . method , params || { } ) ,
387
308
} ;
388
309
}
@@ -421,10 +342,6 @@ function buildSentryAttributes(type: McpSpanConfig['type']): Record<string, stri
421
342
} ;
422
343
}
423
344
424
- // =============================================================================
425
- // PUBLIC API - SIMPLIFIED SPAN CREATION FUNCTIONS
426
- // =============================================================================
427
-
428
345
/**
429
346
* Creates a span for MCP server request handling
430
347
*/
@@ -467,7 +384,6 @@ export function createMcpNotificationSpan(
467
384
export function createMcpOutgoingNotificationSpan (
468
385
jsonRpcMessage : JsonRpcNotification ,
469
386
transport : MCPTransport ,
470
- options : Record < string , unknown > ,
471
387
callback : ( ) => unknown ,
472
388
) : unknown {
473
389
return createMcpSpan ( {
@@ -477,3 +393,21 @@ export function createMcpOutgoingNotificationSpan(
477
393
callback,
478
394
} ) ;
479
395
}
396
+
397
+ /**
398
+ * Combine the two extraction functions into one
399
+ */
400
+ function extractClientInfo ( extra : ExtraHandlerData ) : {
401
+ address ?: string ;
402
+ port ?: number
403
+ } {
404
+ return {
405
+ address : extra ?. requestInfo ?. remoteAddress ||
406
+ extra ?. clientAddress ||
407
+ extra ?. request ?. ip ||
408
+ extra ?. request ?. connection ?. remoteAddress ,
409
+ port : extra ?. requestInfo ?. remotePort ||
410
+ extra ?. clientPort ||
411
+ extra ?. request ?. connection ?. remotePort
412
+ } ;
413
+ }
0 commit comments