mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-15 23:31:29 +00:00
Under otel_v2 a single MCP tool call surfaced in APM as two disconnected
traces joined only by a span link: the HTTP transport transaction
POST /{mcp_server_name}/mcp and the tools/call span carrying
error.type=MCPToolResultError. resolve_mcp_span_context parented the MCP
span to the W3C trace context the client propagates in params._meta
(SEP-414) and recorded the transport as a link, so with no traceparent
propagated (the common case today, including MCP Inspector) the span
started its own root trace.
Nest the MCP span under the transport span when nothing is propagated, so
the call stays in one trace; the propagated-context path is unchanged and
still parents to the remote context and links the transport per the OTel
GenAI MCP semconv.
The transport has to be resolved per message rather than read from the
request-root ContextVar. A stateful streamable-HTTP session runs every
message on the single task the session's initialize POST spawned, so that
ContextVar is frozen at initialize inside the handler: live capture on
staging showed the tools/call span linking the initialize POST rather than
the POST that carried it, and nesting on that anchor would hang every tool
call of a session off the first request's already-ended span. The gateway
now resolves the current request's span on the ASGI task and carries it to
the handler on the authenticated-user object, the same way per-request auth
already crosses that boundary.
|
||
|---|---|---|
| .. | ||
| test_otel_v2_baggage.py | ||
| test_otel_v2_components.py | ||
| test_otel_v2_config_baggage_parenting_guardrails.py | ||
| test_otel_v2_dynamic.py | ||
| test_otel_v2_emitter.py | ||
| test_otel_v2_logger.py | ||
| test_otel_v2_metrics.py | ||
| test_otel_v2_mount.py | ||
| test_otel_v2_multibackend.py | ||
| test_otel_v2_presets.py | ||
| test_otel_v2_sources_of_truth.py | ||
| test_otel_v2_vendor_mappers.py | ||
| test_runtime.py | ||