Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,7 @@ This will automatically configure the MCP server for use with Claude Code. Make
| `OTEL_SERVICE_NAME` | Optional | Service name reported to the OpenTelemetry collector (example: `appium-mcp`). |
| `OTEL_EXPORTER_OTLP_TRACES_ENDPOINT` | Optional | OTLP/HTTP traces endpoint (example: `http://127.0.0.1:4318/v1/traces`). |
| `OTEL_TRACES_SAMPLER` | Optional | Trace sampling strategy; `parentbased_always_on` samples new root traces and follows parent decisions. |
| `OTEL_RESOURCE_ATTRIBUTES` | Optional | Comma-separated `key=value` pairs attached as resource attributes to every span (example: `testcase.id=my-test-123,team=platform`). |

### OpenTelemetry tracing

Expand All @@ -174,6 +175,8 @@ OpenTelemetry tracing is disabled by default. Set `APPIUM_MCP_OTEL_ENABLED=true`
APPIUM_MCP_OTEL_ENABLED=true
# Optional: include sanitized non-sensitive argument values in spans.
# APPIUM_MCP_OTEL_INCLUDE_ARGUMENT_VALUES=true
# Optional: attach custom key=value pairs to every span (e.g. test case ID, team name).
# OTEL_RESOURCE_ATTRIBUTES=testcase.id=my-test-123,team=platform
OTEL_SERVICE_NAME=appium-mcp
OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=http://127.0.0.1:4318/v1/traces
OTEL_TRACES_SAMPLER=parentbased_always_on
Expand Down
20 changes: 20 additions & 0 deletions src/tests/telemetry.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,8 @@ describe('telemetry attributes', () => {
process.env.APPIUM_MCP_OTEL_INCLUDE_ARGUMENT_VALUES = 'true';
process.env.OTEL_SERVICE_NAME = 'appium-mcp-test';
process.env.OTEL_EXPORTER_OTLP_TRACES_ENDPOINT = receiver.endpoint;
process.env.OTEL_RESOURCE_ATTRIBUTES =
'testcase.id=my-test-123,team=platform';

const server = {
tools: [] as any[],
Expand Down Expand Up @@ -361,13 +363,31 @@ describe('telemetry attributes', () => {
}),
])
);

const resourceSpanAttrs = flattenOtlpResourceAttributes(
receiver.requests[0].body
);
expect(resourceSpanAttrs).toMatchObject({
'testcase.id': 'my-test-123',
team: 'platform',
});
} finally {
await shutdownOpenTelemetry();
await receiver.close();
}
});
});

function flattenOtlpResourceAttributes(body: unknown): Record<string, unknown> {
const resourceSpan = (body as any)?.resourceSpans?.[0];
return Object.fromEntries(
(resourceSpan?.resource?.attributes ?? []).map((attr: any) => [
attr.key,
otlpValue(attr.value),
])
);
}

function flattenOtlpSpans(bodies: unknown[]): any[] {
return bodies.flatMap((body: any) =>
(body?.resourceSpans ?? []).flatMap((resourceSpan: any) =>
Expand Down
Loading