Published Sep 20, 20265 min read
MCP Fusion 5.1.0: correlating MCP tool calls into the distributed trace
Released September 20, 2026. MCP tool spans used to be islands: every call started a fresh root, and backends that don't speak mcp.* couldn't query them at all. 5.1.0 adds W3C Trace Context parent correlation and dual GenAI/OpenInference emission to every tool span, without a single OpenTelemetry dependency in core.

By Renato Marinho
Founder · Vinkius
The release notes for 5.1.0, published September 20, 2026, list two work items: G1, W3C Trace Context parent correlation, and G2, dual-convention emission, and both of them land in a single package (release notes · MCP Fusion on GitHub). The fifteen other packages ride along to 5.1.0 in lockstep without a line of code: their ^5.0.0 range on core is already satisfied, so for them the release is one line of semver bookkeeping. What moved is narrow, and it moves the one thing that decides whether you can even see your agent's tool calls in your backend.
The waterfall ended at the gateway
Before this release, MCPFusionTracer.startSpan took two arguments, and the spans it produced were always fresh roots. In practice that meant the agent's distributed trace stopped at the MCP gateway: each tool call surfaced in your backend as its own orphan trace, and following agent → tool in one waterfall was not possible. It also meant that backends without knowledge of the private mcp.* namespace were blind; "all tool calls in the last hour" was a query you could only run where you had taught your vendor that namespace.
A parent handle the framework will not touch
The new exported type MCPFusionSpanContext is a structurally opaque parent handle: traceId (32 lowercase hex characters), spanId (16), flags (the W3C trace-flags byte, 0x01 when sampled), plus optional remote, traceState, baggage and a raw slot for the host's own tracer context. The host builds one from an incoming traceparent / tracestate / baggage pair, HTTP headers, or MCP params._meta, and the framework does exactly one thing with it: it forwards it. It never inspects it. That opacity is the design, not an accident: because a host adapter maps traceId / spanId / raw onto OpenTelemetry's SpanContext + Context, @mcpfusion/core keeps zero @opentelemetry/* dependencies. A raw OTel Tracer is not assignable to MCPFusionTracer (immutable attribute arrays, plus the Context split), so the boundary stays a thin adapter in your code rather than a package dependency.
Parsing is strict by choice. The zero-dependency helpers sit on crypto.randomUUID(): newTraceId(), newSpanId(), generateTraceparent(sampled = true), and parseTraceparent accepts only version 00, 32-character lowercase non-zero trace IDs, 16-character lowercase non-zero span IDs, and two-hex-digit flags; anything else returns undefined rather than a fabricated parent. tracestate and baggage pass through verbatim under their W3C limits (512 characters, 8192 bytes), and extractW3CContext makes a valid traceparent mandatory: no valid header, no context, and the span is a fresh root. The byte format matches what SwarmGateway already emits, so traces across the monorepo interoperate out of the box.
The pickup is per-request. GroupedToolBuilder and ToolRegistry read a conventional duck-typed key, ctx.mcpTraceContext (the same pattern as ctx.handoffTraceparent), and thread it into the tool span, and the unknown-tool 404 span that ToolRegistry.routeCall emits gets the same treatment, which matters precisely because a 404 is usually the moment an agent has lost track of which tools exist. One subtlety worth knowing: without a contextFactory, attachToServer() installs a guard proxy that throws on any property access; the new readMcpTraceContext helper performs its single read inside a try/catch and swallows the throw, so enabling tracing alone never forces you to write a factory. Absence of context reads as "fresh root", never as an error. There is one documented limit: because the framework does not hold an OTel Context at runtime, auto-instrumented downstream calls inside a tool handler (Prisma, HTTP clients, Redis) surface as siblings of the MCP span, not children. The incoming parent is honored; the tool span does not become the active context for everything below it.
One span, three dialects
Every tool span now carries, next to the native mcp.* attributes, openinference.span.kind = "TOOL", gen_ai.operation.name = "tools/call" and tool.name, the 404 span included. The same span is therefore portable across Datadog, Arize and Phoenix without any of them needing to understand mcp.*; filter on openinference.span.kind = TOOL anywhere. What is not emitted matters as much: no token or cost attributes at the tool boundary, because those belong on the agent-side LLM span, and 5.1.0 refuses to invent them at the tool layer. Status semantics stay consistent with the pipeline: UNSET for AI-side failures (validation errors, unknown actions, unknown tools; the 404 span is explicitly UNSET with a message, not ERROR), OK on success, and ERROR only when a handler throws a system failure. An LLM calling the wrong tool does not page your on-call.
If you already run an OpenTelemetry tracer
The entire adoption is two snippets. First, wrap the tracer so the optional context argument lands where it belongs:
import { trace, type SpanOptions, type Context } from '@opentelemetry/api';
const otel = trace.getTracer('mcpfusion');
registry.attachToServer(server, {
contextFactory: createContext,
tracing: {
startSpan: (name, options, context) =>
otel.startSpan(name, options as SpanOptions, context?.raw as Context | undefined),
},
});
Then, in your per-request context factory, extract the W3C context under the conventional key:
const ctx = {
...baseContext,
mcpTraceContext: extractW3CContext(request.headers),
};
From that point on, every tool span, 404 routing included, hangs off the caller's trace, and every backend that speaks the GenAI/OpenInference conventions can see it. The release's verification block backs it up: core builds with zero TypeScript errors, all 16 satellite packages clean, the core suite green at 272 files / 5,263 tests with zero failures, and @mcpfusion/swarm at 158/158. The new Tracing.test.ts pins dual-emit parity between the tool span and the 404 span, W3C round-trips, malformed-input rejection, guard-proxy tolerance, and parent-context propagation on both paths.
