Inject W3C Trace Context into vMCP backend HTTP requests#3814
Open
ChrisJBurns wants to merge 2 commits intomainfrom
Open
Inject W3C Trace Context into vMCP backend HTTP requests#3814ChrisJBurns wants to merge 2 commits intomainfrom
ChrisJBurns wants to merge 2 commits intomainfrom
Conversation
c1b9f09 to
048a9e4
Compare
When vMCP calls backend MCP servers, traces appeared as separate, unlinked traces in Tempo instead of being part of the same distributed trace. Add a tracePropagatingRoundTripper to the HTTP transport chain that injects traceparent/tracestate headers into outgoing requests, linking vMCP client spans with backend server spans. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
048a9e4 to
90ef6c4
Compare
jerm-dro
approved these changes
Feb 12, 2026
11 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
tracePropagatingRoundTripperto the vMCP HTTP transport chain that injectstraceparent/tracestateheaders into outgoing backend requests, linking vMCP and backend spans in the same distributed tracetelemetryBackendClient's tracer injection patternotel.GetTextMapPropagator().Inject()instead ofotelhttp.NewTransport()to avoid creating duplicate client spans (telemetry.go already creates them)authRoundTripperand thehttp.RoundTrippercontractContext
When vMCP calls backend MCP servers, traces appeared as separate, unlinked traces in Tempo. The vMCP telemetry middleware correctly creates client spans, but outgoing HTTP requests never received W3C Trace Context headers, so backends created new root spans instead of continuing the trace.
Confirmed via Tempo:
tools/call yardstick-traced_echoandtools/call echohad different trace IDs despite being the same request flow.How trace context flows
This PR closes the gap on the outgoing side of vMCP → backend communication. The full trace chain now works:
Incoming (already implemented): The telemetry middleware (
pkg/telemetry/middleware.go:167-177) extracts trace context from both HTTP headers and the MCP_metafield, with_metataking priority per the MCP OTEL spec.Outgoing (this PR): The
tracePropagatingRoundTripperinjects trace context into HTTP headers on all outgoing requests to backends. This works for every MCP method (tools/call,tools/list,resources/read,prompts/get,initialize, etc.), unlike_metainjection which would only cover methods that carry_metain their params.Callers providing
_metaIf a caller includes
traceparentin the MCP_metafield:{ "jsonrpc": "2.0", "method": "tools/call", "params": { "_meta": { "traceparent": "00-abcdef1234567890abcdef1234567890-1234567890abcdef-01" }, "name": "yardstick-traced_echo", "arguments": {"input": "hello"} }, "id": 2 }The vMCP spans will appear as children within that trace, and the backend spans will chain from there — giving a complete distributed trace from client through vMCP to backend.
Design decisions
_metainjection: HTTP header propagation is the primary mechanism because it works universally for all MCP methods. TheInjectMetaTraceContextinfrastructure exists inpkg/telemetry/propagation.gobut is not used for outgoing requests — this could be a follow-up for methods that support_meta.otelhttp.NewTransport(): That would create duplicate client spans sincetelemetryBackendClientinpkg/vmcp/server/telemetry.goalready createsSpanKindClientspans for each backend operation.otel.GetTextMapPropagator()on every request, the propagator is captured at client creation time. This makes tests parallel-safe (no global state mutation) and is consistent with how the tracer is injected intelemetryBackendClient.Test plan
go test ./pkg/vmcp/client/... -racepasses (all tests run in parallel)task lint-fixreports 0 issues🤖 Generated with Claude Code