Skip to content

Fix: W3C trace context propagation on outbound MCP HTTP/SSE requests#28299

Open
HrdlickaJakub wants to merge 1 commit into
anomalyco:devfrom
HrdlickaJakub:fix/mcp-w3c-trace-propagation
Open

Fix: W3C trace context propagation on outbound MCP HTTP/SSE requests#28299
HrdlickaJakub wants to merge 1 commit into
anomalyco:devfrom
HrdlickaJakub:fix/mcp-w3c-trace-propagation

Conversation

@HrdlickaJakub

Copy link
Copy Markdown

Issue for this PR

Closes #28295

Type of change

  • Bug fix
  • New feature
  • Refactor / code improvement
  • Documentation

What does this PR do?

When opencode talks to a remote MCP server over HTTP or SSE, the outbound
requests do not carry the W3C traceparent header, so the MCP server's
spans land in a separate trace and you can't follow a tool call end-to-end.

Two things blocked it:

  1. The MCP SDK transports (StreamableHTTPClientTransport, SSEClientTransport)
    use the default global fetch with no hook to inject headers.
  2. @effect/opentelemetry builds a NodeTracerProvider but never calls
    register(), so the @opentelemetry/api global propagator and context
    manager stay no-op. Even if the transport had called propagation.inject(...)
    it would have done nothing.

This PR fixes both, gated on tracing being enabled so the default code path
is unchanged:

  • Adds setupOtelApiGlobals() in packages/core/src/effect/observability.ts
    that installs AsyncLocalStorageContextManager and W3CTraceContextPropagator
    on the @opentelemetry/api globals once, idempotently. Called from the
    existing traces() setup and from the MCP fetch wrapper.
  • Adds buildTracingFetch() in packages/opencode/src/mcp/index.ts and
    passes it as the fetch option to both MCP HTTP/SSE transports when
    Observability.enabled && experimental.openTelemetry === true. It merges
    any caller-supplied headers and injects traceparent / tracestate from
    the active context.

Scope is limited to the two MCP HTTP/SSE transports — no other outbound HTTP
traffic (provider calls, auth, plugins) is touched.

How did you verify your code works?

Unit tests — added 3 tests in packages/opencode/test/mcp/headers.test.ts
covering: traceparent is injected when tracing is enabled, no injection
when tracing is disabled, and caller-supplied headers are preserved alongside
the injected ones. The 3 pre-existing header tests still pass.

End-to-end check — ran opencode against a local Jaeger 1.60 + echo MCP
server, with OTEL_EXPORTER_OTLP_ENDPOINT set and experimental.openTelemetry: true.
The echo server logs whatever traceparent header it receives.

In Jaeger the opencode-side and mcp-echo-server-side spans now share one
trace ID (236 spans across both services) instead of being two unrelated
traces.

Screenshots / recordings

N/A — not a UI change.

Checklist

  • I have tested my changes locally
  • I have not included unrelated changes in this PR

@github-actions

Copy link
Copy Markdown
Contributor

Hey! Your PR title Fix/mcp w3c trace propagation doesn't follow conventional commit format.

Please update it to start with one of:

  • feat: or feat(scope): new feature
  • fix: or fix(scope): bug fix
  • docs: or docs(scope): documentation changes
  • chore: or chore(scope): maintenance tasks
  • refactor: or refactor(scope): code refactoring
  • test: or test(scope): adding or updating tests

Where scope is the package name (e.g., app, desktop, opencode).

See CONTRIBUTING.md for details.

@github-actions

Copy link
Copy Markdown
Contributor

The following comment was made by an LLM, it may be inaccurate:

Potential Related PR Found

PR #27085: feat(observability): propagate trace context to spawned subprocesses

Why related: This PR also addresses trace context propagation in the observability system. While it focuses on subprocess spawning rather than MCP HTTP/SSE transports, both PRs are working on the same foundational issue of propagating W3C trace context across different execution boundaries. They may share or inform similar implementation patterns for handling setupOtelApiGlobals() or trace context propagation.


No other duplicate PRs found for the MCP W3C trace propagation fix itself.

@HrdlickaJakub HrdlickaJakub force-pushed the fix/mcp-w3c-trace-propagation branch from f9d6229 to f91e618 Compare May 19, 2026 06:33
@HrdlickaJakub HrdlickaJakub changed the title Fix/mcp w3c trace propagation Fix(mcp): w3c trace propagation May 19, 2026
@HrdlickaJakub HrdlickaJakub changed the title Fix(mcp): w3c trace propagation Fix: W3C trace context propagation on outbound MCP HTTP/SSE requests May 19, 2026
@HrdlickaJakub HrdlickaJakub force-pushed the fix/mcp-w3c-trace-propagation branch from f91e618 to 46b57a4 Compare May 19, 2026 10:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[FEATURE]: propagate W3C trace context on outbound MCP HTTP/SSE requests

1 participant