Skip to content

OTEL: fix end-to-end MCP trace interop#3997

Closed
strawgate wants to merge 5 commits into
mainfrom
codex/otel-interop-mode
Closed

OTEL: fix end-to-end MCP trace interop#3997
strawgate wants to merge 5 commits into
mainfrom
codex/otel-interop-mode

Conversation

@strawgate
Copy link
Copy Markdown
Collaborator

@strawgate strawgate commented Apr 21, 2026

FastMCP's OpenTelemetry support worked best when FastMCP owned the entire MCP exchange. In mixed setups, native FastMCP spans could compete with outer instrumentation for ownership of the MCP trace, server spans could prefer ambient transport context over the propagated MCP parent, and the FastMCP client still sent initialize and discovery requests like an opaque client. The result was traces with the right raw events but the wrong hierarchy at the MCP boundary.

This makes FastMCP compose cleanly as both client and server. FastMCP now has a supported propagation_only mode plus suppress_fastmcp_telemetry() for cases where another instrumentation layer should own MCP spans, server spans parent from propagated MCP context while linking ambient transport spans, baggage stays current during execution, and the FastMCP client now propagates _meta on initialize, tools/list, resources/list, resources/templates/list, and prompts/list. The new interop tests cover mixed instrumentation, opaque third-party MCP clients, and the missing client handshake/discovery paths so this behavior is exercised end to end instead of inferred.

from fastmcp import Client
from fastmcp.telemetry import suppress_fastmcp_telemetry

with tracer.start_as_current_span("execute_tool weather"):
    async with Client(server, auto_initialize=False) as client:
        with suppress_fastmcp_telemetry():
            await client.initialize()
            await client.list_tools()
            result = await client.call_tool("weather", {"city": "Chicago"})

Refs #3451. Closes #3993. Closes #3998.

🤖 Generated with Codex

@marvin-context-protocol marvin-context-protocol Bot added bug Something isn't working. Reports of errors, unexpected behavior, or broken functionality. server Related to FastMCP server implementation or server-side functionality. client Related to the FastMCP client SDK or client-side functionality. labels Apr 21, 2026
@marvin-context-protocol
Copy link
Copy Markdown
Contributor

CI failed due to two issues in tests/telemetry/test_interop.py: an import ordering violation (auto-fixed by ruff check) and a line that needs formatting (auto-fixed by ruff format). Run uv run prek run --all-files locally and push the result.

@strawgate strawgate changed the title OTEL: add propagation-only interop mode OTEL: fix end-to-end MCP trace interop Apr 21, 2026
@strawgate strawgate force-pushed the codex/otel-interop-mode branch from 031971c to 409c971 Compare April 25, 2026 23:46
- Rename get_trace_context_carrier -> extract_propagation_keys_from_meta
  (carrier is OTEL jargon, new name is self-evident)
- Rename _get_ambient_span_context -> _get_ambient_or_current_span_context
  (makes fallback to current span explicit in name)
- Fix extract_trace_context docstring: merged -> propagated (precise)
- Trim duplicate wording in _initialize_session_with_meta docstring
@strawgate strawgate marked this pull request as ready for review April 26, 2026 00:16
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 9feca45fb1

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +128 to +130
if not native_telemetry_enabled():
yield get_noop_span()
return
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Preserve extracted request context when telemetry is suppressed

When FASTMCP_TELEMETRY_MODE=propagation_only (or suppress_fastmcp_telemetry() is active), this early return skips _get_parent_trace_context() and never attaches the propagated MCP _meta context to the current execution context. In practice, server handlers then lose incoming trace/baggage (for example baggage.get_baggage(...) returns None) and any downstream inject_trace_context() call propagates the wrong context, which breaks the commit’s stated “propagation-only” interoperability contract for mixed/third-party clients.

Useful? React with 👍 / 👎.

@strawgate strawgate added the DON'T MERGE PR is not ready for merging. Used by authors to prevent premature merging. label Apr 26, 2026
@strawgate
Copy link
Copy Markdown
Collaborator Author

Closing in favor of PR #4046 which has a clean single-commit history.

@strawgate strawgate closed this Apr 26, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working. Reports of errors, unexpected behavior, or broken functionality. client Related to the FastMCP client SDK or client-side functionality. DON'T MERGE PR is not ready for merging. Used by authors to prevent premature merging. server Related to FastMCP server implementation or server-side functionality.

Projects

None yet

1 participant