11/**
2- * Attribute extraction and building functions for MCP server instrumentation
2+ * Core attribute extraction and building functions for MCP server instrumentation
33 */
44
55import { isURLObjectRelative , parseStringToURLObject } from '../../utils/url' ;
66import {
7- CLIENT_ADDRESS_ATTRIBUTE ,
8- CLIENT_PORT_ATTRIBUTE ,
97 MCP_LOGGING_DATA_TYPE_ATTRIBUTE ,
108 MCP_LOGGING_LEVEL_ATTRIBUTE ,
119 MCP_LOGGING_LOGGER_ATTRIBUTE ,
1210 MCP_LOGGING_MESSAGE_ATTRIBUTE ,
13- MCP_PROTOCOL_VERSION_ATTRIBUTE ,
1411 MCP_REQUEST_ID_ATTRIBUTE ,
1512 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 ,
2513} from './attributes' ;
2614import { 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' ;
6416
6517/**
6618 * Extracts additional attributes for specific notification types
@@ -138,155 +90,6 @@ export function getNotificationAttributes(
13890 return attributes ;
13991}
14092
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-
29093/**
29194 * Build type-specific attributes based on message type
29295 * @param type - Span type (request or notification)
@@ -313,67 +116,5 @@ export function buildTypeSpecificAttributes(
313116 return getNotificationAttributes ( message . method , params || { } ) ;
314117}
315118
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