Skip to content

Commit 036ff29

Browse files
author
Vladislav Prusakov
committed
bug fixes
1 parent d890756 commit 036ff29

6 files changed

Lines changed: 566 additions & 3 deletions

File tree

Package.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ let package = Package(
1616
],
1717
dependencies: [
1818
.package(url: "https://github.com/AdaEngine/AdaEngine", branch: "main"),
19+
.package(url: "https://github.com/apple/swift-distributed-tracing", from: "1.0.0"),
1920
.package(url: "https://github.com/apple/swift-system.git", from: "1.0.0"),
2021
.package(url: "https://github.com/apple/swift-log.git", from: "1.6.0"),
2122
.package(url: "https://github.com/apple/swift-nio.git", from: "2.74.0"),
@@ -26,6 +27,8 @@ let package = Package(
2627
name: "AdaMCPCore",
2728
dependencies: [
2829
.product(name: "AdaEngine", package: "AdaEngine"),
30+
.product(name: "InMemoryTracing", package: "swift-distributed-tracing"),
31+
.product(name: "Tracing", package: "swift-distributed-tracing"),
2932
.product(name: "MCP", package: "swift-sdk"),
3033
.product(name: "Logging", package: "swift-log")
3134
]
@@ -57,6 +60,8 @@ let package = Package(
5760
"AdaMCPServer",
5861
"AdaMCPPlugin",
5962
.product(name: "AdaEngine", package: "AdaEngine"),
63+
.product(name: "InMemoryTracing", package: "swift-distributed-tracing"),
64+
.product(name: "Tracing", package: "swift-distributed-tracing"),
6065
.product(name: "MCP", package: "swift-sdk"),
6166
.product(name: "SystemPackage", package: "swift-system")
6267
],

Sources/AdaMCPCore/AdaMCPRuntime.swift

Lines changed: 104 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)