1
1
/**
2
- * Attribute extraction and building functions for MCP server instrumentation
2
+ * Core attribute extraction and building functions for MCP server instrumentation
3
3
*/
4
4
5
5
import { isURLObjectRelative , parseStringToURLObject } from '../../utils/url' ;
6
6
import {
7
- CLIENT_ADDRESS_ATTRIBUTE ,
8
- CLIENT_PORT_ATTRIBUTE ,
9
7
MCP_LOGGING_DATA_TYPE_ATTRIBUTE ,
10
8
MCP_LOGGING_LEVEL_ATTRIBUTE ,
11
9
MCP_LOGGING_LOGGER_ATTRIBUTE ,
12
10
MCP_LOGGING_MESSAGE_ATTRIBUTE ,
13
- MCP_PROTOCOL_VERSION_ATTRIBUTE ,
14
11
MCP_REQUEST_ID_ATTRIBUTE ,
15
12
MCP_RESOURCE_URI_ATTRIBUTE ,
16
- MCP_SERVER_NAME_ATTRIBUTE ,
17
- MCP_SERVER_TITLE_ATTRIBUTE ,
18
- MCP_SERVER_VERSION_ATTRIBUTE ,
19
- MCP_SESSION_ID_ATTRIBUTE ,
20
- MCP_TOOL_RESULT_CONTENT_COUNT_ATTRIBUTE ,
21
- MCP_TOOL_RESULT_IS_ERROR_ATTRIBUTE ,
22
- MCP_TRANSPORT_ATTRIBUTE ,
23
- NETWORK_PROTOCOL_VERSION_ATTRIBUTE ,
24
- NETWORK_TRANSPORT_ATTRIBUTE ,
25
13
} from './attributes' ;
26
14
import { extractTargetInfo , getRequestArguments } from './methodConfig' ;
27
- import {
28
- getClientInfoForTransport ,
29
- getProtocolVersionForTransport ,
30
- getSessionDataForTransport ,
31
- } from './sessionManagement' ;
32
- import type {
33
- ExtraHandlerData ,
34
- JsonRpcNotification ,
35
- JsonRpcRequest ,
36
- McpSpanType ,
37
- MCPTransport ,
38
- PartyInfo ,
39
- SessionData ,
40
- } from './types' ;
41
-
42
- /**
43
- * Extracts transport types based on transport constructor name
44
- * @param transport - MCP transport instance
45
- * @returns Transport type mapping for span attributes
46
- */
47
- export function getTransportTypes ( transport : MCPTransport ) : { mcpTransport : string ; networkTransport : string } {
48
- const transportName = transport . constructor ?. name ?. toLowerCase ( ) || '' ;
49
-
50
- if ( transportName . includes ( 'stdio' ) ) {
51
- return { mcpTransport : 'stdio' , networkTransport : 'pipe' } ;
52
- }
53
-
54
- if ( transportName . includes ( 'streamablehttp' ) || transportName . includes ( 'streamable' ) ) {
55
- return { mcpTransport : 'http' , networkTransport : 'tcp' } ;
56
- }
57
-
58
- if ( transportName . includes ( 'sse' ) ) {
59
- return { mcpTransport : 'sse' , networkTransport : 'tcp' } ;
60
- }
61
-
62
- return { mcpTransport : 'unknown' , networkTransport : 'unknown' } ;
63
- }
15
+ import type { JsonRpcNotification , JsonRpcRequest , McpSpanType } from './types' ;
64
16
65
17
/**
66
18
* Extracts additional attributes for specific notification types
@@ -138,155 +90,6 @@ export function getNotificationAttributes(
138
90
return attributes ;
139
91
}
140
92
141
- /**
142
- * Extracts and validates PartyInfo from an unknown object
143
- * @param obj - Unknown object that might contain party info
144
- * @returns Validated PartyInfo object with only string properties
145
- */
146
- function extractPartyInfo ( obj : unknown ) : PartyInfo {
147
- const partyInfo : PartyInfo = { } ;
148
-
149
- if ( obj && typeof obj === 'object' && obj !== null ) {
150
- const source = obj as Record < string , unknown > ;
151
- if ( typeof source . name === 'string' ) partyInfo . name = source . name ;
152
- if ( typeof source . title === 'string' ) partyInfo . title = source . title ;
153
- if ( typeof source . version === 'string' ) partyInfo . version = source . version ;
154
- }
155
-
156
- return partyInfo ;
157
- }
158
-
159
- /**
160
- * Extracts session data from "initialize" requests
161
- * @param request - JSON-RPC "initialize" request containing client info and protocol version
162
- * @returns Session data extracted from request parameters including protocol version and client info
163
- */
164
- export function extractSessionDataFromInitializeRequest ( request : JsonRpcRequest ) : SessionData {
165
- const sessionData : SessionData = { } ;
166
- if ( request . params && typeof request . params === 'object' && request . params !== null ) {
167
- const params = request . params as Record < string , unknown > ;
168
- if ( typeof params . protocolVersion === 'string' ) {
169
- sessionData . protocolVersion = params . protocolVersion ;
170
- }
171
- if ( params . clientInfo ) {
172
- sessionData . clientInfo = extractPartyInfo ( params . clientInfo ) ;
173
- }
174
- }
175
- return sessionData ;
176
- }
177
-
178
- /**
179
- * Extracts session data from "initialize" response
180
- * @param result - "initialize" response result containing server info and protocol version
181
- * @returns Partial session data extracted from response including protocol version and server info
182
- */
183
- export function extractSessionDataFromInitializeResponse ( result : unknown ) : Partial < SessionData > {
184
- const sessionData : Partial < SessionData > = { } ;
185
- if ( result && typeof result === 'object' ) {
186
- const resultObj = result as Record < string , unknown > ;
187
- if ( typeof resultObj . protocolVersion === 'string' ) sessionData . protocolVersion = resultObj . protocolVersion ;
188
- if ( resultObj . serverInfo ) {
189
- sessionData . serverInfo = extractPartyInfo ( resultObj . serverInfo ) ;
190
- }
191
- }
192
- return sessionData ;
193
- }
194
-
195
- /**
196
- * Build client attributes from stored client info
197
- * @param transport - MCP transport instance
198
- * @returns Client attributes for span instrumentation
199
- */
200
- export function getClientAttributes ( transport : MCPTransport ) : Record < string , string > {
201
- const clientInfo = getClientInfoForTransport ( transport ) ;
202
- const attributes : Record < string , string > = { } ;
203
-
204
- if ( clientInfo ?. name ) {
205
- attributes [ 'mcp.client.name' ] = clientInfo . name ;
206
- }
207
- if ( clientInfo ?. title ) {
208
- attributes [ 'mcp.client.title' ] = clientInfo . title ;
209
- }
210
- if ( clientInfo ?. version ) {
211
- attributes [ 'mcp.client.version' ] = clientInfo . version ;
212
- }
213
-
214
- return attributes ;
215
- }
216
-
217
- /**
218
- * Build server attributes from stored server info
219
- * @param transport - MCP transport instance
220
- * @returns Server attributes for span instrumentation
221
- */
222
- export function getServerAttributes ( transport : MCPTransport ) : Record < string , string > {
223
- const serverInfo = getSessionDataForTransport ( transport ) ?. serverInfo ;
224
- const attributes : Record < string , string > = { } ;
225
-
226
- if ( serverInfo ?. name ) {
227
- attributes [ MCP_SERVER_NAME_ATTRIBUTE ] = serverInfo . name ;
228
- }
229
- if ( serverInfo ?. title ) {
230
- attributes [ MCP_SERVER_TITLE_ATTRIBUTE ] = serverInfo . title ;
231
- }
232
- if ( serverInfo ?. version ) {
233
- attributes [ MCP_SERVER_VERSION_ATTRIBUTE ] = serverInfo . version ;
234
- }
235
-
236
- return attributes ;
237
- }
238
-
239
- /**
240
- * Extracts client connection info from extra handler data
241
- * @param extra - Extra handler data containing connection info
242
- * @returns Client address and port information
243
- */
244
- export function extractClientInfo ( extra : ExtraHandlerData ) : {
245
- address ?: string ;
246
- port ?: number ;
247
- } {
248
- return {
249
- address :
250
- extra ?. requestInfo ?. remoteAddress ||
251
- extra ?. clientAddress ||
252
- extra ?. request ?. ip ||
253
- extra ?. request ?. connection ?. remoteAddress ,
254
- port : extra ?. requestInfo ?. remotePort || extra ?. clientPort || extra ?. request ?. connection ?. remotePort ,
255
- } ;
256
- }
257
-
258
- /**
259
- * Build transport and network attributes
260
- * @param transport - MCP transport instance
261
- * @param extra - Optional extra handler data
262
- * @returns Transport attributes for span instrumentation
263
- */
264
- export function buildTransportAttributes (
265
- transport : MCPTransport ,
266
- extra ?: ExtraHandlerData ,
267
- ) : Record < string , string | number > {
268
- const sessionId = transport . sessionId ;
269
- const clientInfo = extra ? extractClientInfo ( extra ) : { } ;
270
- const { mcpTransport, networkTransport } = getTransportTypes ( transport ) ;
271
- const clientAttributes = getClientAttributes ( transport ) ;
272
- const serverAttributes = getServerAttributes ( transport ) ;
273
- const protocolVersion = getProtocolVersionForTransport ( transport ) ;
274
-
275
- const attributes = {
276
- ...( sessionId && { [ MCP_SESSION_ID_ATTRIBUTE ] : sessionId } ) ,
277
- ...( clientInfo . address && { [ CLIENT_ADDRESS_ATTRIBUTE ] : clientInfo . address } ) ,
278
- ...( clientInfo . port && { [ CLIENT_PORT_ATTRIBUTE ] : clientInfo . port } ) ,
279
- [ MCP_TRANSPORT_ATTRIBUTE ] : mcpTransport ,
280
- [ NETWORK_TRANSPORT_ATTRIBUTE ] : networkTransport ,
281
- [ NETWORK_PROTOCOL_VERSION_ATTRIBUTE ] : '2.0' ,
282
- ...( protocolVersion && { [ MCP_PROTOCOL_VERSION_ATTRIBUTE ] : protocolVersion } ) ,
283
- ...clientAttributes ,
284
- ...serverAttributes ,
285
- } ;
286
-
287
- return attributes ;
288
- }
289
-
290
93
/**
291
94
* Build type-specific attributes based on message type
292
95
* @param type - Span type (request or notification)
@@ -313,67 +116,5 @@ export function buildTypeSpecificAttributes(
313
116
return getNotificationAttributes ( message . method , params || { } ) ;
314
117
}
315
118
316
- /**
317
- * Build attributes for tool result content items
318
- * @param content - Array of content items from tool result
319
- * @returns Attributes extracted from each content item including type, text, mime type, URI, and resource info
320
- */
321
- function buildAllContentItemAttributes ( content : unknown [ ] ) : Record < string , string | number > {
322
- const attributes : Record < string , string | number > = {
323
- [ MCP_TOOL_RESULT_CONTENT_COUNT_ATTRIBUTE ] : content . length ,
324
- } ;
325
-
326
- for ( const [ i , item ] of content . entries ( ) ) {
327
- if ( typeof item !== 'object' || item === null ) continue ;
328
-
329
- const contentItem = item as Record < string , unknown > ;
330
- const prefix = content . length === 1 ? 'mcp.tool.result' : `mcp.tool.result.${ i } ` ;
331
-
332
- const safeSet = ( key : string , value : unknown ) : void => {
333
- if ( typeof value === 'string' ) attributes [ `${ prefix } .${ key } ` ] = value ;
334
- } ;
335
-
336
- safeSet ( 'content_type' , contentItem . type ) ;
337
- safeSet ( 'mime_type' , contentItem . mimeType ) ;
338
- safeSet ( 'uri' , contentItem . uri ) ;
339
- safeSet ( 'name' , contentItem . name ) ;
340
-
341
- if ( typeof contentItem . text === 'string' ) {
342
- const text = contentItem . text ;
343
- const maxLength = 500 ;
344
- attributes [ `${ prefix } .content` ] = text . length > maxLength ? `${ text . slice ( 0 , maxLength - 3 ) } ...` : text ;
345
- }
346
-
347
- if ( typeof contentItem . data === 'string' ) {
348
- attributes [ `${ prefix } .data_size` ] = contentItem . data . length ;
349
- }
350
-
351
- const resource = contentItem . resource ;
352
- if ( typeof resource === 'object' && resource !== null ) {
353
- const res = resource as Record < string , unknown > ;
354
- safeSet ( 'resource_uri' , res . uri ) ;
355
- safeSet ( 'resource_mime_type' , res . mimeType ) ;
356
- }
357
- }
358
-
359
- return attributes ;
360
- }
361
-
362
- /**
363
- * Extract tool result attributes for span instrumentation
364
- * @param result - Tool execution result
365
- * @returns Attributes extracted from tool result content
366
- */
367
- export function extractToolResultAttributes ( result : unknown ) : Record < string , string | number | boolean > {
368
- let attributes : Record < string , string | number | boolean > = { } ;
369
- if ( typeof result !== 'object' || result === null ) return attributes ;
370
-
371
- const resultObj = result as Record < string , unknown > ;
372
- if ( typeof resultObj . isError === 'boolean' ) {
373
- attributes [ MCP_TOOL_RESULT_IS_ERROR_ATTRIBUTE ] = resultObj . isError ;
374
- }
375
- if ( Array . isArray ( resultObj . content ) ) {
376
- attributes = { ...attributes , ...buildAllContentItemAttributes ( resultObj . content ) } ;
377
- }
378
- return attributes ;
379
- }
119
+ // Re-export buildTransportAttributes for spans.ts
120
+ export { buildTransportAttributes } from './sessionExtraction' ;
0 commit comments