Skip to content

Traces and spans

A span is one unit of work with a start, a duration and a status. A trace is a set of spans linked by a shared trace id, forming a tree.

For MCP, the tree usually looks like this:

graph TD
    A["POST /mcp — transport"] --> B["tools/call your_tool"]
    B --> C["SELECT — postgres"]
    B --> D["GET — partner API"]
    B --> E["SETEX — redis"]

Which span is the root

The root is the span whose parent is absent from the trace — not simply the one with no parent id. An instrumented MCP client makes your server's span a legitimate child of a span you never received, and treating "has a parent id" as "is not a root" would leave such traces with no root at all.

Ordering

Spans are returned parent-before-child, siblings in start order. This matters more than it sounds: children frequently reach storage before their parents, because a parent span cannot end until its children have. Ordering by arrival would show a child above its own parent.

Self-time

self_ms is a span's duration minus the sum of its children. A tool with 500ms duration and 10ms self-time is not slow — it is waiting on something that is.

Latency eligibility

Not every span belongs in a latency percentile. A subscriptions/listen span measures a stream's lifetime, which may be hours; averaged with tool calls it destroys a p95. Such spans are marked ineligible at write time, so a reader cannot forget to exclude them. See Latency and clock accuracy.