Skip to content

feat: add OpenTelemetry tracing for client and server requests#2025

Open
Kludex wants to merge 4 commits intomainfrom
feat/opentelemetry-tracing
Open

feat: add OpenTelemetry tracing for client and server requests#2025
Kludex wants to merge 4 commits intomainfrom
feat/opentelemetry-tracing

Conversation

@Kludex
Copy link
Member

@Kludex Kludex commented Feb 10, 2026

Summary

  • Adds opentelemetry-api as a runtime dependency and opentelemetry-sdk as a dev dependency
  • Creates src/mcp/shared/tracing.py with a module-level tracer, attribute constants, and helpers for span naming (including target extraction for tools/call, prompts/get, resources/read)
  • Instruments BaseSession.send_request() with a single CLIENT span per outgoing request — records method name, target, OK/ERROR status, and error type
  • Excluded methods (e.g. notifications/message) produce no span

This is the minimal foundation for OpenTelemetry support. No context propagation, metrics, or server-side spans yet — just one client-side span per request to establish the pattern.

Test plan

  • Unit tests for _extract_target() and _build_span_name() helpers (parametrized)
  • Integration test: span created with correct name, kind, and OK status on send_ping()
  • Integration test: span name includes tool name for tools/call requests
  • Integration test: ERROR status and error.type attribute on JSON-RPC error response
  • Integration test: no span created for excluded methods
  • ruff format + ruff check pass
  • pyright strict passes (0 errors)
  • Full test suite passes (1157 passed, 0 failed)

Add opentelemetry-api as a dependency and instrument
BaseSession.send_request() with a CLIENT span per outgoing request.

This establishes the tracing foundation for the SDK. Each request
gets a span with the MCP method name and, where applicable, the
target name (tool name, prompt name, resource URI). Spans record
OK/ERROR status and error type attributes. Excluded methods
(e.g. notifications/message) are skipped.
Comment on lines 1 to 4
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems it does. I can see the py.typed.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a case that we don't have a method?

Comment on lines 35 to 39
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need for this utility.

Comment on lines 134 to 136
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it possible to use mcp.client.Client instead?

Comment on lines 66 to 74
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We shouldn't test private functions! Please document that in the CLAUDE.md.

Comment on lines 51 to 63
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We shouldn't test private functions! Please document that in the CLAUDE.md.

- Remove pyright suppressions (opentelemetry-sdk ships py.typed)
- Use `request_data["method"]` instead of `.get("method", "")`
- Inline `_build_span_name` into `start_client_span`
- Remove unit tests for private functions, test through E2E instead
- Rewrite error test to use `Client` (timeout-based)
- Add "do not test private functions" rule to CLAUDE.md
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is the cast needed?

@claude
Copy link

claude bot commented Feb 10, 2026

Code review

No issues found. Checked for bugs and CLAUDE.md compliance.

Instrument the server side of MCP request handling with OpenTelemetry
SERVER spans. The span starts after request validation in _receive_loop
and ends when RequestResponder.respond() or cancel() is called, covering
the full server-side request lifecycle.
@Kludex Kludex changed the title feat: add OpenTelemetry tracing for client requests feat: add OpenTelemetry tracing for client and server requests Feb 10, 2026
Comment on lines 368 to 371
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Single line please. Is the method not always available in request_data?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

Comments