@@ -14,6 +14,7 @@ public struct MCPServerConfiguration: Sendable {
1414 public let instructions : String ?
1515 public let registerTypes : ( @MainActor ( MCPIntrospectionRegistry ) -> Void ) ?
1616 public let captureOverride : RenderCaptureService . CaptureOverride ?
17+ public let traceRecorder : AdaMCPTraceRecorder ?
1718
1819 public init (
1920 enableHTTP: Bool = true ,
@@ -25,7 +26,8 @@ public struct MCPServerConfiguration: Sendable {
2526 serverVersion: String = " 0.1.0 " ,
2627 instructions: String ? = " Inspect live AdaEngine worlds, entities, resources, assets, and render captures. " ,
2728 registerTypes: ( @MainActor ( MCPIntrospectionRegistry ) -> Void ) ? = nil ,
28- captureOverride: RenderCaptureService . CaptureOverride ? = nil
29+ captureOverride: RenderCaptureService . CaptureOverride ? = nil ,
30+ traceRecorder: AdaMCPTraceRecorder ? = AdaMCPTraceRecorder ( )
2931 ) {
3032 self . enableHTTP = enableHTTP
3133 self . enableStdio = enableStdio
@@ -37,6 +39,7 @@ public struct MCPServerConfiguration: Sendable {
3739 self . instructions = instructions
3840 self . registerTypes = registerTypes
3941 self . captureOverride = captureOverride
42+ self . traceRecorder = traceRecorder
4043 }
4144}
4245
@@ -46,18 +49,21 @@ public final class AdaMCPRuntime {
4649 private let registry : MCPIntrospectionRegistry
4750 private let renderCaptureService : RenderCaptureService
4851 private let uiInspectionService : AdaUIInspectionService
52+ private let traceRecorder : AdaMCPTraceRecorder ?
4953 private let logger : Logger
5054
5155 public init (
5256 appWorlds: AppWorlds ,
5357 registry: MCPIntrospectionRegistry ,
5458 renderCaptureService: RenderCaptureService ,
59+ traceRecorder: AdaMCPTraceRecorder ? = nil ,
5560 logger: Logger = Logger ( label: " org.adaengine.mcp.runtime " )
5661 ) {
5762 self . appWorlds = appWorlds
5863 self . registry = registry
5964 self . renderCaptureService = renderCaptureService
6065 self . uiInspectionService = AdaUIInspectionService ( appWorlds: appWorlds)
66+ self . traceRecorder = traceRecorder
6167 self . logger = logger
6268 }
6369
@@ -211,6 +217,34 @@ public final class AdaMCPRuntime {
211217 ] ) ,
212218 annotations: . init( readOnlyHint: false , destructiveHint: false , idempotentHint: false , openWorldHint: false )
213219 ) ,
220+ Tool (
221+ name: " trace.list_spans " ,
222+ description: " List finished AdaEngine tracing spans captured by AdaMCP. " ,
223+ inputSchema: Self . objectSchema ( properties: [
224+ " traceId " : . object( [ " type " : " string " ] ) ,
225+ " nameContains " : . object( [ " type " : " string " ] ) ,
226+ " limit " : . object( [ " type " : " integer " ] ) ,
227+ " clearAfterRead " : . object( [ " type " : " boolean " ] )
228+ ] ) ,
229+ annotations: . init( readOnlyHint: false , destructiveHint: true , idempotentHint: false , openWorldHint: false )
230+ ) ,
231+ Tool (
232+ name: " trace.export_otlp " ,
233+ description: " Export finished AdaEngine tracing spans as OpenTelemetry OTLP JSON. " ,
234+ inputSchema: Self . objectSchema ( properties: [
235+ " traceId " : . object( [ " type " : " string " ] ) ,
236+ " nameContains " : . object( [ " type " : " string " ] ) ,
237+ " limit " : . object( [ " type " : " integer " ] ) ,
238+ " clearAfterExport " : . object( [ " type " : " boolean " ] )
239+ ] ) ,
240+ annotations: . init( readOnlyHint: false , destructiveHint: true , idempotentHint: false , openWorldHint: false )
241+ ) ,
242+ Tool (
243+ name: " trace.clear " ,
244+ description: " Clear finished tracing spans captured by AdaMCP. " ,
245+ inputSchema: Self . objectSchema ( ) ,
246+ annotations: . init( readOnlyHint: false , destructiveHint: true , idempotentHint: true , openWorldHint: false )
247+ ) ,
214248 Tool (
215249 name: " ui.list_windows " ,
216250 description: " List live AdaUI windows. " ,
@@ -389,6 +423,18 @@ public final class AdaMCPRuntime {
389423 uri: " ada://ui/windows " ,
390424 description: " Live AdaUI window index. " ,
391425 mimeType: " application/json "
426+ ) ,
427+ MCP . Resource (
428+ name: " Trace Spans " ,
429+ uri: " ada://traces/spans " ,
430+ description: " Finished AdaEngine tracing spans captured by AdaMCP. " ,
431+ mimeType: " application/json "
432+ ) ,
433+ MCP . Resource (
434+ name: " Trace OTLP Export " ,
435+ uri: " ada://traces/otlp " ,
436+ description: " Finished AdaEngine tracing spans as OpenTelemetry OTLP JSON. " ,
437+ mimeType: " application/json "
392438 )
393439 ]
394440
@@ -454,6 +500,13 @@ public final class AdaMCPRuntime {
454500 title: " UI Node Snapshot " ,
455501 description: " AdaUI node snapshot by window identifier and node selector. " ,
456502 mimeType: " application/json "
503+ ) ,
504+ MCP . Resource. Template (
505+ uriTemplate: " ada://traces/{format} " ,
506+ name: " Trace Export " ,
507+ title: " Trace Export " ,
508+ description: " Trace export by format: spans or otlp. " ,
509+ mimeType: " application/json "
457510 )
458511 ]
459512 }
@@ -492,6 +545,12 @@ public final class AdaMCPRuntime {
492545 payload = try await self . stepFramePayload ( frames: arguments [ " frames " ] ? . intValue ?? 1 )
493546 case " render.capture_screenshot " :
494547 payload = try await self . captureScreenshotPayload ( arguments: arguments)
548+ case " trace.list_spans " :
549+ payload = try self . traceSpansPayload ( arguments: arguments)
550+ case " trace.export_otlp " :
551+ payload = try self . traceOTLPPayload ( arguments: arguments)
552+ case " trace.clear " :
553+ payload = try self . traceClearPayload ( )
495554 case " ui.list_windows " :
496555 payload = try self . uiListWindowsPayload ( )
497556 case " ui.get_window " :
@@ -556,6 +615,8 @@ public final class AdaMCPRuntime {
556615 payload = try self . typeListPayload ( kind: kind)
557616 case " ui " :
558617 payload = try self . uiResourcePayload ( url: url, uri: uri)
618+ case " traces " :
619+ payload = try self . traceResourcePayload ( url: url, uri: uri)
559620 default :
560621 throw AdaMCPError . invalidResourceURI ( uri)
561622 }
@@ -773,6 +834,32 @@ public final class AdaMCPRuntime {
773834 return try Value ( result)
774835 }
775836
837+ private func traceSpansPayload( arguments: [ String : Value ] ) throws -> Value {
838+ guard let traceRecorder else {
839+ throw AdaMCPError . tracingUnavailable
840+ }
841+ return traceRecorder. spansPayload ( arguments: arguments)
842+ }
843+
844+ private func traceOTLPPayload( arguments: [ String : Value ] ) throws -> Value {
845+ guard let traceRecorder else {
846+ throw AdaMCPError . tracingUnavailable
847+ }
848+ return traceRecorder. otlpPayload ( arguments: arguments)
849+ }
850+
851+ private func traceClearPayload( ) throws -> Value {
852+ guard let traceRecorder else {
853+ throw AdaMCPError . tracingUnavailable
854+ }
855+ let clearedCount = traceRecorder. finishedSpanCount
856+ traceRecorder. clearFinishedSpans ( )
857+ return . object( [
858+ " clearedSpanCount " : . int( clearedCount) ,
859+ " activeSpanCount " : . int( traceRecorder. activeSpanCount)
860+ ] )
861+ }
862+
776863 private func uiListWindowsPayload( ) throws -> Value {
777864 [ " windows " : try Value ( self . uiInspectionService. listWindows ( ) ) ]
778865 }
@@ -1057,6 +1144,22 @@ public final class AdaMCPRuntime {
10571144 }
10581145 }
10591146
1147+ private func traceResourcePayload( url: URL , uri: String ) throws -> Value {
1148+ let parts = url. path. split ( separator: " / " , omittingEmptySubsequences: true ) . map ( String . init)
1149+ guard let format = parts. first else {
1150+ throw AdaMCPError . invalidResourceURI ( uri)
1151+ }
1152+
1153+ switch format {
1154+ case " spans " :
1155+ return try self . traceSpansPayload ( arguments: [ : ] )
1156+ case " otlp " :
1157+ return try self . traceOTLPPayload ( arguments: [ : ] )
1158+ default :
1159+ throw AdaMCPError . invalidResourceURI ( uri)
1160+ }
1161+ }
1162+
10601163 private func resolveOptionalUINodeSelector( arguments: [ String : Value ] ) -> UINodeSelector ? {
10611164 if let accessibilityIdentifier = arguments [ " accessibilityIdentifier " ] ? . stringValue, !accessibilityIdentifier. isEmpty {
10621165 return . accessibilityIdentifier( accessibilityIdentifier)
0 commit comments