11using ModelContextProtocol . Protocol ;
22using System . Diagnostics ;
3+ using System . Diagnostics . CodeAnalysis ;
34using System . Diagnostics . Metrics ;
45using System . Text . Json ;
56using System . Text . Json . Nodes ;
@@ -12,22 +13,14 @@ internal static class Diagnostics
1213
1314 internal static Meter Meter { get ; } = new ( "Experimental.ModelContextProtocol" ) ;
1415
15- internal static Histogram < double > CreateDurationHistogram ( string name , string description , bool longBuckets ) =>
16- Meter . CreateHistogram ( name , "s" , description , advice : longBuckets ? LongSecondsBucketBoundaries : ShortSecondsBucketBoundaries ) ;
16+ internal static Histogram < double > CreateDurationHistogram ( string name , string description ) =>
17+ Meter . CreateHistogram ( name , "s" , description , advice : ExplicitBucketBoundaries ) ;
1718
1819 /// <summary>
19- /// Follows boundaries from http.server.request.duration/http.client.request.duration
20+ /// ExplicitBucketBoundaries specified in MCP semantic conventions for all MCP metrics.
21+ /// See https://github.com/open-telemetry/semantic-conventions/blob/main/docs/gen-ai/mcp.md#metrics
2022 /// </summary>
21- private static InstrumentAdvice < double > ShortSecondsBucketBoundaries { get ; } = new ( )
22- {
23- HistogramBucketBoundaries = [ 0.005 , 0.01 , 0.025 , 0.05 , 0.075 , 0.1 , 0.25 , 0.5 , 0.75 , 1 , 2.5 , 5 , 7.5 , 10 ] ,
24- } ;
25-
26- /// <summary>
27- /// Not based on a standard. Larger bucket sizes for longer lasting operations, e.g. HTTP connection duration.
28- /// See https://github.com/open-telemetry/semantic-conventions/issues/336
29- /// </summary>
30- private static InstrumentAdvice < double > LongSecondsBucketBoundaries { get ; } = new ( )
23+ private static InstrumentAdvice < double > ExplicitBucketBoundaries { get ; } = new ( )
3124 {
3225 HistogramBucketBoundaries = [ 0.01 , 0.02 , 0.05 , 0.1 , 0.2 , 0.5 , 1 , 2 , 5 , 10 , 30 , 60 , 120 , 300 ] ,
3326 } ;
@@ -103,5 +96,25 @@ internal static bool ShouldInstrumentMessage(JsonRpcMessage message) =>
10396 _ => false
10497 } ;
10598
99+ /// <summary>
100+ /// If outer GenAI instrumentation is already tracing the tool execution,
101+ /// MCP instrumentation SHOULD add MCP-specific attributes to the existing tool execution span instead
102+ /// of creating a new one.
103+ /// </summary>
104+ /// <param name="activity">The outer activity for tool execution, if found.</param>
105+ /// <returns>true if an outer tool execution activity was found and can be reused; false otherwise.</returns>
106+ internal static bool TryGetOuterToolExecutionActivity ( [ NotNullWhen ( true ) ] out Activity ? activity )
107+ {
108+ if ( Activity . Current is { } currentActivity &&
109+ currentActivity . OperationName . StartsWith ( "execute_tool " , StringComparison . Ordinal ) )
110+ {
111+ activity = currentActivity ;
112+ return true ;
113+ }
114+
115+ activity = null ;
116+ return false ;
117+ }
118+
106119 internal static ActivityLink [ ] ActivityLinkFromCurrent ( ) => Activity . Current is null ? [ ] : [ new ActivityLink ( Activity . Current . Context ) ] ;
107120}
0 commit comments