Skip to content

Propagate W3C trace context in outbound MCP _meta - #5964

Merged
JAORMX merged 1 commit into
mainfrom
propagate-trace-context-meta-5758
Jul 24, 2026
Merged

Propagate W3C trace context in outbound MCP _meta#5964
JAORMX merged 1 commit into
mainfrom
propagate-trace-context-meta-5758

Conversation

@JAORMX

@JAORMX JAORMX commented Jul 24, 2026

Copy link
Copy Markdown
Collaborator

Summary

vMCP terminates the MCP protocol and re-issues requests to backends as a client, but it only propagated W3C trace context via HTTP headers. A spec-compliant MCP 2026-07-28 backend reads trace context from params._meta (SEP-414), so backend spans reached through vMCP were not joined to the client → proxy → server trace. The inbound half (extracting _meta trace context and the InjectMetaTraceContext helper) already landed in #3682 — but the helper was left unwired (dead code). This completes the outbound half.

  • Add telemetry.MetaWithTraceContext(ctx, meta) — a copy-safe wrapper that returns a clone of the caller's _meta enriched with the active traceparent/tracestate/baggage, reusing the existing InjectMetaTraceContext/MetaCarrier. Returns nil when there is no active trace context, so no _meta is emitted for 2025-11-25 peers (backward compatible).
  • Wire it into vMCP CallTool / ReadResource / GetPrompt on both backend paths: the pooled httpBackendClient (pkg/vmcp/client) and the session-backed mcpSession (pkg/vmcp/session/internal/backend).
  • Keeps the existing outbound HTTP-header propagation (tracePropagatingRoundTripper) — the two are complementary and carry identical values from the same propagator; the receiver prioritizes _meta.

Fixes #5758

Type of change

  • New feature

Test plan

  • Unit tests (task test)
  • Linting (task lint-fix)

Ran go test -race (with the Taskfile's ldflags) on ./pkg/telemetry/..., ./pkg/vmcp/client/..., ./pkg/vmcp/session/... — all pass. task lint-fix reports 0 issues. New/updated coverage:

  • TestMetaWithTraceContext — injection, backward-compat nil, copy-before-mutate (input map unmutated), baggage, caller-meta coexistence.
  • TestOutboundMetaTraceContext (client) and mcp_session_meta_propagation_test.go (session) — assert traceparent lands in outbound _meta on the wire for CallTool, that the value matches the active span's TraceID, that _meta is omitted with no active span, and that a caller progressToken coexists with the injected traceparent.

Changes

File Change
pkg/telemetry/propagation.go Add MetaWithTraceContext copy-safe wrapper; doc/security notes
pkg/vmcp/client/client.go Inject _meta trace context in CallTool/ReadResource/GetPrompt
pkg/vmcp/session/internal/backend/mcp_session.go Same, on the session-backed backend path
pkg/telemetry/propagation_test.go TestMetaWithTraceContext
pkg/vmcp/client/meta_integration_test.go Outbound _meta assertions + tripwire
pkg/vmcp/session/internal/backend/mcp_session_capabilities_test.go metaByMethod wire-capture on the fake backend
pkg/vmcp/session/internal/backend/mcp_session_meta_propagation_test.go New: session outbound _meta + coexistence tests

Does this introduce a user-facing change?

Yes (observability): when ToolHive telemetry is enabled, distributed traces now join across client → vMCP → backend for tools/call via MCP _meta, so a single trace spans the whole call path. No configuration change; harmless for peers that don't emit trace context.

Special notes for reviewers

  • Scope: This is a Workstream C item of epic Epic: MCP protocol currency and 2026-07-28 stateless readiness #5743 (MCP 2026-07-28 readiness). The inbound extraction was already done in Add W3C Trace Context propagation via MCP _meta #3682; this PR only wires the outbound side.
  • Known SDK limitation (documented + tested): mcpcompat v0.0.32 does not forward Params.Meta on the non-resume ReadResource/GetPrompt paths, so those two assignments are currently a no-op on the wire. They are set for forward-compatibility and guarded by a tripwire assertion that fails loudly (with instructions) the moment the SDK starts forwarding them — at which point the forward-compat NOTE comments should be removed and positive traceparent assertions added. CallTool forwards correctly today. For all three, trace context also still reaches the backend via HTTP headers.
  • completion/complete has no Meta field in mcpcompat v0.0.32, so it propagates via HTTP headers only — an inherent SDK asymmetry, not a regression.
  • Security: baggage crosses the trust boundary into (possibly untrusted) backends. It is intentionally not stripped — SEP-414 explicitly reserves traceparent/tracestate/baggage as bare _meta keys. MetaWithTraceContext's doc warns callers never to put secrets/PII in baggage. Stripping/trust-gating baggage on the backend-bound path (both _meta and the pre-existing header path) is a reasonable follow-up if desired.

Implementation plan

Approved implementation plan

Architect (opus) gap analysis established that #3682 already implemented inbound _meta extraction, the MetaCarrier, the InjectMetaTraceContext helper (dead code), the composite TraceContext+Baggage propagator, and vMCP outbound header injection. Transparent-proxy passthrough forwards the body verbatim, so inbound _meta is preserved with no change. The remaining gap was outbound _meta injection at the vMCP protocol-terminating client.

Steps:

  1. Add MetaWithTraceContext(ctx, meta) to pkg/telemetry/propagation.go — clone caller map, inject via existing helper, return nil when empty.
  2. Wire it into pkg/vmcp/client/client.go CallTool/ReadResource/GetPrompt.
  3. Wire it into pkg/vmcp/session/internal/backend/mcp_session.go for the same three.
  4. Tests: unit (propagation_test.go) + wire-level integration on both paths.

Reviewed by a 5-axis opus panel (security, code quality, MCP correctness, observability, test coverage) — all APPROVE-WITH-NITS, no blockers; SEP-414 key naming verified against the live spec. Panel nits addressed before this PR.

🤖 Generated with Claude Code

https://claude.ai/code/session_01MRh394VZgnqbYjJwjrcJWm

@github-actions github-actions Bot added the size/M Medium PR: 300-599 lines changed label Jul 24, 2026
vMCP terminates the MCP protocol and re-issues requests to backends as a
client, but it only propagated W3C trace context via HTTP headers. A
spec-compliant MCP 2026-07-28 backend reads trace context from
params._meta (SEP-414), so backend spans reached through vMCP were not
joined to the client->proxy->server trace. The InjectMetaTraceContext
helper added for this in #3682 was left unwired (dead code).

Add MetaWithTraceContext, a copy-safe wrapper that enriches a _meta map
with the active traceparent/tracestate/baggage, and wire it into the vMCP
CallTool/ReadResource/GetPrompt calls on both the pooled-client and
session-backed backend paths. It returns nil when there is no active
trace context so no _meta is emitted for 2025-11-25 peers, keeping the
change backward compatible.

Note: mcpcompat v0.0.32 does not forward Params.Meta on the non-resume
ReadResource/GetPrompt paths yet, so those two are set for forward
compatibility and covered by a tripwire test that fails loudly once the
SDK starts forwarding them.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MRh394VZgnqbYjJwjrcJWm
@JAORMX
JAORMX force-pushed the propagate-trace-context-meta-5758 branch from b7dbee8 to baf668c Compare July 24, 2026 09:50
@github-actions github-actions Bot added size/M Medium PR: 300-599 lines changed and removed size/M Medium PR: 300-599 lines changed labels Jul 24, 2026
@codecov

codecov Bot commented Jul 24, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 71.84%. Comparing base (52bc063) to head (baf668c).

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #5964      +/-   ##
==========================================
- Coverage   71.86%   71.84%   -0.02%     
==========================================
  Files         713      713              
  Lines       73228    73242      +14     
==========================================
  Hits        52624    52624              
- Misses      16834    16848      +14     
  Partials     3770     3770              

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@JAORMX
JAORMX merged commit ff13bce into main Jul 24, 2026
114 of 116 checks passed
@JAORMX
JAORMX deleted the propagate-trace-context-meta-5758 branch July 24, 2026 10:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/M Medium PR: 300-599 lines changed

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Propagate W3C trace context from MCP _meta in telemetry

2 participants