Zero-config observability for MCP servers. Instrument at import, ship OTel
GenAI-compliant spans, replay offline. Core package is stdlib-only against
the grain of the ecosystem — httpx is an optional extra.
mcp SDK server ──patch──▶ monkey.wrap_tool_call
│
fastmcp server ─patch─▶ fastmcp.patch_fastmcp
│
manual / agent code ──▶ api.session() + api.span()
│
▼
store (TraceStore, in-memory + JSONL)
│ on_record
▼
exporters fanout: JsonlExporter · TextExporter · OtlpExporter(httpx)
│
▼
replay offline: mcp-trace --replay store.jsonl --console → OTLP-shaped
| File | Job |
|---|---|
store.py |
TraceStore — start/record/close traces; default store w/ JSONL append. |
api.py |
Public surface: auto, instrument, session, span. |
monkey.py |
Monkeypatch for the official mcp SDK — wrap_tool_call, patch_mcp_sdk, idempotent. |
redact.py |
SHA-256 input fingerprints + secret-key scrubbing (token → [REDACTED]). |
exporters.py |
Exporter protocol, JSONL / Text(panel) / OTLP(HTTP) + Fanout. |
model.py |
Span dataclass — includes parent_span_id for continued traces. |
propagator.py |
W3C traceparent / tracestate — generate_traceparent, parse_traceparent. |
sampler.py |
parent_based, ratio, rate_limited sampling policies. |
metrics.py |
Registry, Histogram w/ explicit buckets, metrics_from_store. |
otel_provider.py |
OtelProvider — builds OTLP-shaped span bundles (export_built), telemetry objects. |
replay.py |
Offline re-delivery of a recorded JSONL store through the exporter stack. |
fastmcp.py |
Opt-in shim for fastmcp — patch_fastmcp, make_server. |
cli.py |
mcp-trace — table, --tail, --json, --clear, --replay [--console] [--max N]. |
- Hidden from your code —
mt.auto()patches, scrubs, and exports; the only visible change is spans you never wrote a line for. - Never store secrets — inputs are fingerprinted, sensitive keys scrubbed, all before anything touches disk.
- Zero-config default — no SDK, no env vars, no account: install and
auto(). - Continued traces cross servers — a
traceparentheader on an inbound MCP request startsstore.start(parent.trace_id)and stampsparent_span_idon the span; the sibling repomcp-hubrelays that header on every upstream hop. One trace, end to end, replayable offline.
Instrumented tool call:
mcp SDK / fastmcp / manual span → store.start → span(fingerprint, redaction, parent_span_id) → store.record → exporters fanout (JSONL + panel + OTLP)
Replay:
mcp-trace --replay FILE [--console] [--max N] → rehydrate traces → OtelProvider.export_built → console / exporter
- Stdlib-first: the core import graph uses zero third-party imports.
- Everything pure where possible; side effects live in
store,exporters. - Spans model OTel GenAI semantic conventions
(
gen_ai.client.tool_call,gen_ai.agent.invoke) so no transform is needed in your observability stack.