@@ -15,7 +15,14 @@ import {
15
15
NETWORK_PROTOCOL_VERSION_ATTRIBUTE ,
16
16
NETWORK_TRANSPORT_ATTRIBUTE ,
17
17
} from './attributes' ;
18
- import type { ExtraHandlerData , JsonRpcNotification , JsonRpcRequest , McpSpanType , MCPTransport , MethodConfig } from './types' ;
18
+ import type {
19
+ ExtraHandlerData ,
20
+ JsonRpcNotification ,
21
+ JsonRpcRequest ,
22
+ McpSpanType ,
23
+ MCPTransport ,
24
+ MethodConfig ,
25
+ } from './types' ;
19
26
20
27
/** Configuration for MCP methods to extract targets and arguments */
21
28
const METHOD_CONFIGS : Record < string , MethodConfig > = {
@@ -48,22 +55,26 @@ const METHOD_CONFIGS: Record<string, MethodConfig> = {
48
55
} ;
49
56
50
57
/** Extracts target info from method and params based on method type */
51
- export function extractTargetInfo ( method : string , params : Record < string , unknown > ) : {
58
+ export function extractTargetInfo (
59
+ method : string ,
60
+ params : Record < string , unknown > ,
61
+ ) : {
52
62
target ?: string ;
53
- attributes : Record < string , string >
63
+ attributes : Record < string , string > ;
54
64
} {
55
65
const config = METHOD_CONFIGS [ method as keyof typeof METHOD_CONFIGS ] ;
56
66
if ( ! config ) {
57
67
return { attributes : { } } ;
58
68
}
59
69
60
- const target = config . targetField && typeof params ?. [ config . targetField ] === 'string'
61
- ? params [ config . targetField ] as string
62
- : undefined ;
70
+ const target =
71
+ config . targetField && typeof params ?. [ config . targetField ] === 'string'
72
+ ? ( params [ config . targetField ] as string )
73
+ : undefined ;
63
74
64
75
return {
65
76
target,
66
- attributes : target && config . targetAttribute ? { [ config . targetAttribute ] : target } : { }
77
+ attributes : target && config . targetAttribute ? { [ config . targetAttribute ] : target } : { } ,
67
78
} ;
68
79
}
69
80
@@ -197,16 +208,15 @@ export function getNotificationAttributes(
197
208
/** Extracts client connection info from extra handler data */
198
209
export function extractClientInfo ( extra : ExtraHandlerData ) : {
199
210
address ?: string ;
200
- port ?: number
211
+ port ?: number ;
201
212
} {
202
213
return {
203
- address : extra ?. requestInfo ?. remoteAddress ||
204
- extra ?. clientAddress ||
205
- extra ?. request ?. ip ||
206
- extra ?. request ?. connection ?. remoteAddress ,
207
- port : extra ?. requestInfo ?. remotePort ||
208
- extra ?. clientPort ||
209
- extra ?. request ?. connection ?. remotePort
214
+ address :
215
+ extra ?. requestInfo ?. remoteAddress ||
216
+ extra ?. clientAddress ||
217
+ extra ?. request ?. ip ||
218
+ extra ?. request ?. connection ?. remoteAddress ,
219
+ port : extra ?. requestInfo ?. remotePort || extra ?. clientPort || extra ?. request ?. connection ?. remotePort ,
210
220
} ;
211
221
}
212
222
@@ -250,70 +260,106 @@ export function buildTypeSpecificAttributes(
250
260
return getNotificationAttributes ( message . method , params || { } ) ;
251
261
}
252
262
253
- /** Simplified tool result attribute extraction */
254
- export function extractSimpleToolAttributes ( result : unknown ) : Record < string , string | number | boolean > {
255
- const attributes : Record < string , string | number | boolean > = { } ;
256
-
257
- if ( typeof result === 'object' && result !== null ) {
258
- const resultObj = result as Record < string , unknown > ;
259
-
260
- // Check if this is an error result
261
- if ( typeof resultObj . isError === 'boolean' ) {
262
- attributes [ 'mcp.tool.result.is_error' ] = resultObj . isError ;
263
- }
264
-
265
- // Extract basic content info
266
- if ( Array . isArray ( resultObj . content ) ) {
267
- attributes [ 'mcp.tool.result.content_count' ] = resultObj . content . length ;
268
-
269
- // Extract info from all content items
270
- for ( let i = 0 ; i < resultObj . content . length ; i ++ ) {
271
- const item = resultObj . content [ i ] ;
272
- if ( item && typeof item === 'object' && item !== null ) {
273
- const contentItem = item as Record < string , unknown > ;
274
- const prefix = resultObj . content . length === 1 ? 'mcp.tool.result' : `mcp.tool.result.${ i } ` ;
275
-
276
- // Always capture the content type
277
- if ( typeof contentItem . type === 'string' ) {
278
- attributes [ `${ prefix } .content_type` ] = contentItem . type ;
279
- }
280
-
281
- // Extract common fields generically
282
- if ( typeof contentItem . text === 'string' ) {
283
- const text = contentItem . text ;
284
- attributes [ `${ prefix } .content` ] = text . length > 500 ? `${ text . substring ( 0 , 497 ) } ...` : text ;
285
- }
286
-
287
- if ( typeof contentItem . mimeType === 'string' ) {
288
- attributes [ `${ prefix } .mime_type` ] = contentItem . mimeType ;
289
- }
290
-
291
- if ( typeof contentItem . uri === 'string' ) {
292
- attributes [ `${ prefix } .uri` ] = contentItem . uri ;
293
- }
294
-
295
- if ( typeof contentItem . name === 'string' ) {
296
- attributes [ `${ prefix } .name` ] = contentItem . name ;
297
- }
298
-
299
- if ( typeof contentItem . data === 'string' ) {
300
- attributes [ `${ prefix } .data_size` ] = contentItem . data . length ;
301
- }
302
-
303
- // For embedded resources, check the nested resource object
304
- if ( contentItem . resource && typeof contentItem . resource === 'object' ) {
305
- const resource = contentItem . resource as Record < string , unknown > ;
306
- if ( typeof resource . uri === 'string' ) {
307
- attributes [ `${ prefix } .resource_uri` ] = resource . uri ;
308
- }
309
- if ( typeof resource . mimeType === 'string' ) {
310
- attributes [ `${ prefix } .resource_mime_type` ] = resource . mimeType ;
311
- }
312
- }
313
- }
263
+ /** Get metadata about tool result content array */
264
+ function getContentMetadata ( content : unknown [ ] ) : Record < string , string | number > {
265
+ return {
266
+ 'mcp.tool.result.content_count' : content . length ,
267
+ } ;
268
+ }
269
+
270
+ /** Build attributes from a single content item */
271
+ function buildContentItemAttributes (
272
+ contentItem : Record < string , unknown > ,
273
+ prefix : string ,
274
+ ) : Record < string , string | number > {
275
+ const attributes : Record < string , string | number > = { } ;
276
+
277
+ if ( typeof contentItem . type === 'string' ) {
278
+ attributes [ `${ prefix } .content_type` ] = contentItem . type ;
279
+ }
280
+
281
+ if ( typeof contentItem . text === 'string' ) {
282
+ const text = contentItem . text ;
283
+ attributes [ `${ prefix } .content` ] = text . length > 500 ? `${ text . substring ( 0 , 497 ) } ...` : text ;
284
+ }
285
+
286
+ if ( typeof contentItem . mimeType === 'string' ) {
287
+ attributes [ `${ prefix } .mime_type` ] = contentItem . mimeType ;
288
+ }
289
+
290
+ if ( typeof contentItem . uri === 'string' ) {
291
+ attributes [ `${ prefix } .uri` ] = contentItem . uri ;
292
+ }
293
+
294
+ if ( typeof contentItem . name === 'string' ) {
295
+ attributes [ `${ prefix } .name` ] = contentItem . name ;
296
+ }
297
+
298
+ if ( typeof contentItem . data === 'string' ) {
299
+ attributes [ `${ prefix } .data_size` ] = contentItem . data . length ;
300
+ }
301
+
302
+ return attributes ;
303
+ }
304
+
305
+ /** Build attributes from embedded resource object */
306
+ function buildEmbeddedResourceAttributes ( resource : Record < string , unknown > , prefix : string ) : Record < string , string > {
307
+ const attributes : Record < string , string > = { } ;
308
+
309
+ if ( typeof resource . uri === 'string' ) {
310
+ attributes [ `${ prefix } .resource_uri` ] = resource . uri ;
311
+ }
312
+
313
+ if ( typeof resource . mimeType === 'string' ) {
314
+ attributes [ `${ prefix } .resource_mime_type` ] = resource . mimeType ;
315
+ }
316
+
317
+ return attributes ;
318
+ }
319
+
320
+ /** Build attributes for all content items in the result */
321
+ function buildAllContentItemAttributes ( content : unknown [ ] ) : Record < string , string | number > {
322
+ const attributes : Record < string , string | number > = { } ;
323
+
324
+ for ( let i = 0 ; i < content . length ; i ++ ) {
325
+ const item = content [ i ] ;
326
+ if ( item && typeof item === 'object' && item !== null ) {
327
+ const contentItem = item as Record < string , unknown > ;
328
+ const prefix = content . length === 1 ? 'mcp.tool.result' : `mcp.tool.result.${ i } ` ;
329
+
330
+ Object . assign ( attributes , buildContentItemAttributes ( contentItem , prefix ) ) ;
331
+
332
+ if ( contentItem . resource && typeof contentItem . resource === 'object' ) {
333
+ const resourceAttrs = buildEmbeddedResourceAttributes ( contentItem . resource as Record < string , unknown > , prefix ) ;
334
+ Object . assign ( attributes , resourceAttrs ) ;
314
335
}
315
336
}
316
337
}
317
-
338
+
339
+ return attributes ;
340
+ }
341
+
342
+ /** Extract tool result attributes for span instrumentation */
343
+ export function extractToolResultAttributes ( result : unknown ) : Record < string , string | number | boolean > {
344
+ let attributes : Record < string , string | number | boolean > = { } ;
345
+
346
+ if ( typeof result !== 'object' || result === null ) {
347
+ return attributes ;
348
+ }
349
+
350
+ const resultObj = result as Record < string , unknown > ;
351
+
352
+ if ( typeof resultObj . isError === 'boolean' ) {
353
+ attributes [ 'mcp.tool.result.is_error' ] = resultObj . isError ;
354
+ }
355
+
356
+ if ( Array . isArray ( resultObj . content ) ) {
357
+ attributes = {
358
+ ...attributes ,
359
+ ...getContentMetadata ( resultObj . content ) ,
360
+ ...buildAllContentItemAttributes ( resultObj . content ) ,
361
+ } ;
362
+ }
363
+
318
364
return attributes ;
319
365
}
0 commit comments