
Tracehub Mcp
io.github.mcpsmithsPublished by GitHub account mcpsmithsv0.13.1Updated Oct 31, 2026
Query OTel traces across Jaeger, Tempo, Traceloop, Datadog, Sentry, X-Ray, New Relic, Honeycomb.
Overview
Queries OpenTelemetry traces across Jaeger, Tempo, Traceloop, Datadog, Sentry, X-Ray, New Relic and Honeycomb so an assistant can search, inspect and diagnose…
- What you can do
- Search and inspect traces and spans, fetch a full trace by ID, and list instrumented services
- Diagnose a trace with a synthesized root-cause view, and correlate one trace across a second configured backend
- Aggregate LLM token usage, model stats, expensive and slow traces, and tool/function calls
- Group spans into conversations and get per-session statistics
- Compare usage between two time windows and investigate cost spikes
- When to use it
- Useful when you already run an observability backend and want an assistant to debug failures, trace latency, or track LLM token cost and model usage without hand-writing queries in each backend's own syntax.
- Requirements
- Local Python 3.11+ process, typically run via uvx or pipx from PyPI. Requires BACKEND_TYPE and BACKEND_URL; most backends also need BACKEND_API_KEY, Datadog additionally BACKEND_APP_KEY, Sentry an org slug, New Relic an account ID, Honeycomb a dataset, X-Ray an AWS region and standard AWS credentials. Network access to the backend.
Installation
In SourceWeft
- Open Tracehub Mcp in the dashboard and add it to a workspace.
- Enable the server for the chats that should use its tools.
Desktop only via STDIO. STDIO servers start a local process, so they need the SourceWeft desktop host.
Other MCP clients
Follow the launch instructions in the repository.
README
tracehub-mcp
[CI] [codecov] [PyPI] [Python 3.11+] [License] [mcpsmiths/tracehub-mcp MCP server] [M8ven Score]
Also listed on the official MCP registry as io.github.mcpsmiths/tracehub-mcp (the registry's own ?search= endpoint can surface an outdated version first; this exact-name endpoint always reflects the current isLatest release), and on mcprush.
[tracehub-mcp MCP server – quality and maintenance score on Glama]
Give your AI assistant a direct line into your observability backend. tracehub-mcp is an MCP (Model Context Protocol) server that lets Claude, Cursor, Windsurf, Gemini CLI, or any MCP client query OpenTelemetry traces from your LLM/GenAI application and reason about them — find expensive calls, debug errors, compare model performance, track token usage — without you copy-pasting trace JSON into a chat window.
It speaks OpenTelemetry's gen_ai.* semantic conventions natively, so it understands prompts, completions, token usage, and finish reasons as first-class concepts, not just generic span attributes.
tracehub-mcp started as a fork of traceloop/opentelemetry-mcp-server (Apache 2.0) — full attribution and fork history are in NOTICE. It's grown into an 8-backend, security-hardened server maintained independently under mcpsmiths; see What's Different From Upstream below for the parts that are new here.
Table of Contents
- Quick Start
- Supported Backends
- What's Different From Upstream
- Installation
- Configuration
- Diagnostics
- Security Considerations
- Privacy Policy
- MCP Client Setup
- Tools Reference
- Generic Filter System
- Example Queries
- Common Workflows
- Development
- Troubleshooting
- Roadmap
- License
- Support
Quick Start
tracehub-mcp is on PyPI. No install step needed — uvx fetches and runs it in one shot:
Or from Claude Code directly:
That's it. Ask your assistant: "Show me traces with errors from the last hour."
See MCP Client Setup for Cursor, Windsurf, VS Code, and Gemini CLI, and Installation for pip/pipx/from-source alternatives.
Supported Backends
- Jaeger — local/self-hosted, the most common open-source trace backend. No auth required (a Bearer token can be sent via
BACKEND_API_KEY; credentials embedded inBACKEND_URLare rejected). - Grafana Tempo — local or Grafana Cloud, TraceQL-native search.
- Traceloop — cloud LLM observability platform, API-key auth.
- Datadog — cloud APM, requires an API key and an Application key.
- Sentry — cloud or self-hosted, requires an auth token and an organization slug.
- AWS X-Ray — SigV4-signed via
boto3, requires an AWS region and standard AWS credentials (not an API key). Because OTel span attributes land in unindexed X-Ray segment metadata by default (only annotations are queryable), native server-side filtering is narrower here than for the other backends — see the Backend Support Matrix. - New Relic — NerdGraph GraphQL API, requires a User API key (not an Ingest/License key) and an account ID. Built without a live account to verify against — see the New Relic setup section below and the module docstring in backends/newrelic.py for exactly which assumptions are unverified.
- Honeycomb — Query Data API, requires a Configuration Key and a dataset slug. Built without a live account to verify against, and the research behind it found the programmatic Query Data API (the only way to actually run a query and get results back) is documented as Enterprise-plan exclusive — see the Honeycomb setup section below and the module docstring in backends/honeycomb.py.
All eight implement the same BaseBackend interface, so every MCP tool works identically regardless of which one you point the server at. See Configuration for per-backend setup.
What's Different From Upstream
Upstream opentelemetry-mcp-server shipped Jaeger, Tempo, and Traceloop. tracehub-mcp adds Datadog and Sentry as full backends — not thin wrappers, but complete implementations of every tool (search, span search, trace hydration, service discovery, health checks). Along the way, all five backends — including the three inherited from upstream — were hardened to a consistent bar:
- HTTPS-only enforcement on cloud backends. Datadog and Sentry both refuse to start against a plain
http://URL, because their auth is a bearer token / API+App key pair that has no business going out over plaintext. - Query-injection-safe escaping. Every value spliced into a Datadog span-search query or a Sentry Discover query is escaped and exact-quoted; field names (which are less obviously untrusted, since they come from the MCP tool's
filtersparameter) are validated against an allowlist pattern before being spliced into the query string, closing off structural injection through a crafted field name. - Bounded pagination on every backend that paginates via cursor (Datadog, Sentry) — a search stops at the requested
limitor when the backend stops returning a continuation cursor, whichever comes first, so a single tool call can't degrade into an unbounded crawl. - Exact-ID re-verification. Where a backend's search API can return neighbors instead of an exact match (notably Datadog's trace reconstruction from grouped spans), every result is re-checked against the exact ID that was asked for before being returned.
- No fabricated data on malformed responses, in the backends we built. Datadog and Sentry reject a span outright — rather than substituting a placeholder like
now()for a missing timestamp or a literal"unknown"for a missingservice_name/operation_name— since a fabricated value would silently corrupt trace ordering, duration aggregation, and any tool that groups by service or operation. (The three backends inherited from upstream — Jaeger, Tempo, Traceloop — predate this discipline and haven't been retrofitted; that's deliberate scope discipline, not an oversight, mirroring this project's own precedent of not reaching into shared/inherited code without full regression coverage for it.)
All of this is backed by 458 passing tests (2 skipped, zero regressions), a clean ruff check and mypy --strict run, and two rounds of adversarial CodeRabbit review on the new backends.
Installation
tracehub-mcp is on PyPI. Pick whichever of these your workflow already uses — they're equivalent.
Option 1: uvx (no install step)
This is what the Quick Start config above uses — uv fetches the package and runs the tracehub-mcp entry point in one shot, nothing left behind on disk between runs.
Option 2: pip / pipx
Option 3: Clone and run from source
Use this if you're developing locally, want to pin to a specific commit, or want the dev tooling installed (uv sync --group dev).
Option 4: Docker
Backend responses are capped at BACKEND_MAX_RESPONSE_BYTES (64 MiB decoded by default) before they are buffered, but a container that serves many sessions still benefits from Docker's own guard rails: add --memory 1g --restart unless-stopped (or the Compose equivalents) so a process that does exhaust memory is contained and comes back without an operator.
Runs HTTP transport by default (the image's CMD) and refuses to start without MCP_HTTP_BEARER_TOKEN - or the explicit -e MCP_ALLOW_UNAUTHENTICATED_HTTP=true opt-out, which hands every peer that can reach the published port your backend's full read scope. Clients connect to http://localhost:8000/mcp with Authorization: Bearer <token>; /health and /ready (the image's HEALTHCHECK) never need the token. Because the CMD binds 0.0.0.0, the container validates the Host header only when MCP_ALLOWED_HOSTS is set - always set it to the hostnames clients use (localhost in the example above, the public name in a real deployment); * turns Host validation off and may not be mixed with names (the server refuses to start). This is also the form to use for MCP clients whose config takes a command/args pair pointing at docker directly (Cursor, Windsurf) instead of a local binary - pass the same -e MCP_HTTP_BEARER_TOKEN=... in args.
Prerequisites: Python 3.11+, plus uv for Options 1 and 3; Docker for Option 4.
Configuration
Configuration comes from environment variables, CLI flags, or both. Precedence: CLI arguments > environment variables > defaults.
All Configuration Options
Every backend-related CLI flag has a matching env var (--backend/BACKEND_TYPE, --url/BACKEND_URL, --api-key/BACKEND_API_KEY, --app-key/BACKEND_APP_KEY, --tempo-instance-id/BACKEND_TEMPO_INSTANCE_ID, --sentry-org/BACKEND_SENTRY_ORG, --sentry-project/BACKEND_SENTRY_PROJECT, --aws-region/BACKEND_AWS_REGION, --environments/BACKEND_ENVIRONMENTS). --disable-tools <name1,name2,...> / --enabled-tools <name1,name2,...> (CLI-only, no env var) remove/allowlist tools for reduced-trust deployments - --enabled-tools is applied first, --disable-tools on top of whatever it kept. --allowed-hosts, --forwarded-allow-ips, --rate-limit-max-requests and --rate-limit-window-seconds (and their env vars) are validated when the server starts: a value that cannot mean what was written exits 2 with a usage error that names both the flag and the env var - for --allowed-hosts and --forwarded-allow-ips the offending entry is echoed as a bounded repr; the two rate-limit options are checked by click's own integer/float ranges, which echo the value as written - and their --help text shows the env var ([env var: ...). Run tracehub-mcp --help for the full list.
Known third-party egress dependency: the underlying FastMCP framework checks PyPI (https://pypi.org/pypi/fastmcp/json) for a newer FastMCP release once every 12 hours when it prints its startup banner - this is FastMCP's own behavior, not tracehub-mcp's, and unrelated to the OTel self-instrumentation above. It fails silently if there's no network access. For network-restricted/air-gapped deployments, disable it with FASTMCP_CHECK_FOR_UPDATES=off, or suppress the banner entirely with FASTMCP_SHOW_SERVER_BANNER=false.
Backend-Specific Setup
Jaeger
No API key required. search_traces and search_spans_tool both require a service_name parameter — Jaeger's API is optimized for per-service queries, so querying across all services isn't supported. Discover service names first with list_services.
Grafana Tempo
No API key required for a local/self-hosted install. Search uses TraceQL under the hood; service_name is optional.
For Grafana Cloud-hosted Tempo, also set BACKEND_TEMPO_INSTANCE_ID to the stack's instance ID and BACKEND_API_KEY to a Cloud Access Policy token scoped to traces:read — Grafana Cloud requires Basic Auth (instance ID as username, token as password) instead of self-hosted Tempo's Bearer-token auth:
Traceloop
The API key encodes project information — the backend always uses a project slug of "default", and Traceloop resolves the actual project/environment from the key itself.
Datadog
Datadog requires both an API key and an Application key — a single key is not enough for span/trace queries, even though ingestion only needs the API key. Trace search uses Datadog's span search query syntax rather than TraceQL or Jaeger-style tag params, and traces are reconstructed from grouped spans since Datadog has no trace-level lookup endpoint. The backend also refuses a plain http:// URL — see What's Different From Upstream.
Troubleshooting: a
403from the Datadog API almost always means the Application key (not the API key) is missing or invalid. If you're on the EU site, double checkBACKEND_URLishttps://api.datadoghq.eu, not the US default.
Sentry
Sentry requires both an auth token and an organization slug — every endpoint this backend calls is organization-scoped. Trace search uses Sentry's search syntax against the Discover/Explore Events API. Unlike Datadog, Sentry does have a native trace-lookup endpoint, so get_trace calls it directly instead of reconstructing a trace from spans — search_traces still discovers candidate trace IDs via a span search first, since Sentry's search surface is itself span-centric. Like Datadog, this backend refuses a plain http:// URL.
Troubleshooting: a
403/401from the Sentry API almost always means the auth token is missing, invalid, or lacks the necessary scopes. A404on an org-scoped endpoint usually means the organization slug is wrong. For a self-hosted install,BACKEND_URLshould be the install's own base URL, nothttps://sentry.io. Some of the tracing endpoints this backend depends on are newer/experimental on Sentry's side and may not be available on every plan or self-hosted version — see the module docstring in backends/sentry.py for specifics.
AWS X-Ray
Unlike every other backend here, X-Ray is queried via boto3/SigV4, not a bearer token — auth comes from boto3's standard credential chain (environment variables, ~/.aws/credentials, an assumed role, or an instance/task role). The running process needs xray:GetTraceSummaries, xray:BatchGetTraces, and (optionally, for faster list_services) xray:GetServiceGraph IAM permissions. Every boto3 call runs on a private 8-thread pool owned by the backend instance, so at most 8 X-Ray API calls are in flight at once however many sessions or readiness probes arrive — a fixed cap with no setting, since raising it would only raise the X-Ray spend per burst.
Filtering is more limited than the other backends. By default, OpenTelemetry span attributes are converted to X-Ray segment metadata, not annotations — only annotations are indexed and queryable via X-Ray's FilterExpression search syntax. This backend can only natively push down service name, duration, and error/fault/throttle-derived status; every other field (including all gen_ai.* attributes) is always applied client-side after hydrating full traces, unless your own OTel/ADOT collector config explicitly promotes those keys to indexed annotations.
Troubleshooting: an
AccessDeniedExceptionfromGetServiceGraphis non-fatal —list_servicesautomatically falls back to sampling recent traces. AnAccessDeniedExceptionfromGetTraceSummaries/BatchGetTracesis fatal for search/hydration and will surface as an unhealthyhealth_check/doctorresult; double check the IAM permissions above.
New Relic
New Relic requires a User API key (not an Ingest or License key, which cannot query) and an account ID — a User key is user-scoped, not account-scoped, so every NerdGraph query here states which account to query explicitly. Search uses NRQL (SELECT * FROM Span WHERE ...); get_trace uses NerdGraph's native distributedTracing.trace lookup, like Sentry, rather than reconstructing a trace from search results.
Built without a live New Relic account to verify against. Two things a real account is needed to confirm: the exact shape of OTel span attributes inside
distributedTracing.trace's GraphQL response (assumed to be a flat, directly-keyed object), and the account's actual Span-data retention window (a conservative 7-day default is used pending confirmation). See the module docstring in backends/newrelic.py for the complete list of unverified assumptions before relying on this backend in production.
Honeycomb
Honeycomb requires a Configuration Key (not an Ingest or Management key) with "Manage Queries and Columns" and "Run Queries" permissions, and a dataset slug — the Query Data API is dataset-scoped. Search runs Honeycomb's 3-step async query flow (create a query spec, run it, poll for the result) rather than a text query language like NRQL/TraceQL; get_trace searches by trace.trace_id since no dedicated single-trace lookup endpoint is documented. Honeycomb limits Create Query Result to 10 per minute per Configuration Key, so the server paces every query run through one shared per-credential budget — a SECONDARY_BACKEND_* Honeycomb instance built with the same key shares it, and so does GET /ready's list_services probe on the HTTP transport (memoized per READINESS_CACHE_SECONDS). A query whose slot would not free within BACKEND_TIMEOUT fails fast with HoneycombRateLimitedError instead of parking past the timeout; raise BACKEND_TIMEOUT to allow longer waits.
Go/no-go risk, not just an unverified detail. The research behind this backend found the programmatic Query Data API — create-query-result + get-query-result, the only way to actually run a query and get data back — is documented as Enterprise-plan exclusive; Free and Pro accounts can build a query specification via the API but never execute it. This backend raises a clear
HoneycombEnterpriseRequiredErrorif a 402/403 is returned, but the exact status code a non-Enterprise account gets back was never confirmed live, and this backend was built entirely without a live account (Enterprise or otherwise). See the module docstring in backends/honeycomb.py for the complete list of unverified assumptions - including how raw per-span rows are extracted from Honeycomb's aggregation-oriented Query API - before relying on this backend in production.
Transport Modes
With HTTP transport, clients connect to http://<host>:<port>/mcp (streamable-HTTP, for compatibility across MCP clients) and, whenever MCP_HTTP_BEARER_TOKEN is set, send Authorization: Bearer <token> on every request - a missing or wrong token answers 401 with a WWW-Authenticate: Bearer challenge and never opens a session. uvicorn's per-request access log is off by default (--access-log / MCP_ACCESS_LOG=true re-enables it with query strings, fragments, matrix parameters (;, which uvicorn logs as %3B) and percent-encoded ?/# stripped from every line and the logged path cut at 2 KiB; a token placed in a path segment is still logged): it would otherwise write the full request target - including a token a misconfigured client puts in the query string - to the server log, and nothing in it is needed; request-level diagnostics come from the server's own bounded Host/Origin rejection and rate-limit lines, the slow-request WARNING and, when enabled, OTel spans.
A loopback bind also validates the Host header automatically (loopback names and the bound address pass, anything else answers 421), closing the DNS-rebinding shape where a browser page's Origin-less GET arrives with the attacker's name in Host. If a same-host reverse proxy forwards to 127.0.0.1 and preserves the public Host (nginx's default proxy_set_header Host $proxy_host does not), list that name in --allowed-hosts/MCP_ALLOWED_HOSTS. Network binds get the same protection once --allowed-hosts names their public hostnames; --allowed-hosts '*' opts out and must stand alone (a list that mixes * with names is refused at startup).
Fly.io: the one-click
fly mcp launchcommand only supports stdio-transport servers — it can't deploy this server's--transport httpmode. Running tracehub-mcp on Fly.io with HTTP transport needs the manualfly.toml+fly deploypath instead.
Diagnostics
Validate a config before wiring it into a real MCP client:
doctor accepts the same --backend/--url/etc. flags as the main command and exits non-zero if any step fails - unlike normal server startup, which lazily initializes the backend and deliberately keeps running even if the initial health check fails.
To see the fully-resolved configuration (env vars + CLI overrides merged) without starting the server:
Secrets (--api-key/--app-key) are reported as api_key_set/app_key_set booleans, never their actual value.
Security Considerations
Trace and span data returned by this server — attribute values, error messages, operation names — comes from whatever application your observability backend is instrumenting, not from tracehub-mcp itself. That makes it fundamentally the same category of untrusted external content as a webpage or a file, even though it's a trusted server (this one) handing it back to your MCP client.
- The HTTP transport runs every tool under your backend credential, so authenticate its callers.
--transport httpbinds loopback (127.0.0.1) by default. Serving on a network interface (--host 0.0.0.0, the Docker image'sCMD) requiresMCP_HTTP_BEARER_TOKEN(clients sendAuthorization: Bearer <token>); the server refuses to start without it unless you pass--allow-unauthenticated-httpto accept that every peer able to reach the port gets your backend's full read scope. Alternatively keep the loopback bind and front it with an authenticating reverse proxy on a private network./healthand/readynever require the token. - Backend credentials never ride inside
BACKEND_URL. Auser:password@hostURL is refused at startup with a message naming the supported alternatives (BACKEND_API_KEY, orBACKEND_TEMPO_INSTANCE_ID+BACKEND_API_KEYfor Basic auth), because the URL is logged, printed by--print-config/doctor, and embedded verbatim in backend error text that reaches/readybodies and tool errors. As defence in depth every URL and backend error string that leaves the process is additionally stripped of any userinfo component, and the root log handler redacts it from any record - traceback included. - A backend response cannot steer the server to another host. Every request the shared HTTP client sends - including each redirect hop a backend's own
3xx Locationasks for - is refused unless its origin - scheme, host and port - is the configuredBACKEND_URLorigin (a redirect to another port or scheme on the backend's own host is refused too: configureBACKEND_URLwith the final scheme and port), and additionally refused when it resolves to a cloud instance-metadata address in any spelling (169.254.169.254, decimal/hex/octal IPv4 forms,168.63.129.16,metadata.google.internal). A backend or reverse proxy that redirects API calls to a different hostname is not supported: configure the final host asBACKEND_URLinstead. Across all hops of one request the client retries at most twice, honours at most 10 s ofRetry-Afterin total, follows at most 5 redirects and gives up atmax(30 s, 4 x BACKEND_TIMEOUT)wherever it is, so a backend cannot pace a tool call. - Treat backend data as untrusted input. An LLM client consuming trace/span data from tracehub-mcp should apply the same caution it would to any other external tool output — a span attribute or error message is application data to reason about, not an instruction to follow, no matter how it's phrased.
- This is a known MCP risk category, not a tracehub-mcp-specific one. OWASP's GenAI Security Project covers it in their Practical Guide for Secure MCP Server Development, and Anthropic's own engineering guidance, How We Contain Claude, states plainly that tool output is an attack surface even when the tool itself is trusted.
- Practical implication: if you're querying traces from an application that processes untrusted user input (e.g. a customer-facing chatbot), be aware that adversarial content a user fed into that application could end up in a span attribute this server returns — and from there, in your LLM client's context.
Privacy Policy
tracehub-mcp is self-hosted software with no maintainer-operated service or account — it collects no data of its own. See PRIVACY.md for the full policy: what data the software touches, where network calls go, and how secrets and self-instrumentation are handled.
MCP Client Setup
Every example below uses uvx tracehub-mcp (no install step). Swap in tracehub-mcp (pip/pipx install) or uv run tracehub-mcp (from-source, --directory /absolute/path/to/tracehub-mcp) if you installed it a different way — see Installation.
Claude Desktop
Config file location:
- macOS:
~/Library/Application Support/Claude/claude_desktop_config.json - Windows:
%APPDATA%\Claude\claude_desktop_config.json
Jaeger (no auth):
Datadog (API key + App key):
Sentry (auth token + org slug):
If you're running from a clone instead, the bundled wrapper script gives easy backend switching during local dev:
(the script ships Jaeger/Traceloop/Tempo blocks only, with Jaeger active by default — to switch, comment out the active block and uncomment the one you want; Datadog/Sentry aren't in the script, so add their export lines manually).
Claude Code
Datadog/Sentry work the same way — add more -e KEY=value flags for each backend's required env vars (see Backend-Specific Setup). Then:
Cursor
.cursor/mcp.json (project-level) or your global Cursor MCP config:
Windsurf
~/.codeium/windsurf/mcp_config.json:
VS Code (GitHub Copilot)
.vscode/mcp.json in your workspace — note the top-level key is servers, not mcpServers, and stdio servers need no "type" field:
Gemini CLI
Config file: ~/.gemini/config.json, same JSON shape as Claude Desktop above. Then:
Tools Reference
tracehub-mcp exposes 19 MCP tools:
Backend Support Matrix
* Jaeger requires the service_name parameter for span search.
† Datadog, AWS X-Ray, New Relic, and Honeycomb have no trace-level search API (X-Ray's BatchGetTraces fetches by exact ID); traces are reconstructed by searching (spans, X-Ray trace summaries, NRQL Span events, or Honeycomb breakdown groups) and hydrating, with every result re-verified against the exact ID requested.
‡ Sentry and New Relic both have a native trace-lookup endpoint (unlike Datadog and Honeycomb), so get_trace calls it directly; search_traces still discovers candidate trace IDs via a span/NRQL search first, since both backends' search surface is itself span-centric.
§ AWS X-Ray natively filters only service name, duration, and error/fault/throttle-derived status — OTel attributes (including all gen_ai.* fields) land in unindexed segment metadata by default, not indexed annotations, so they're always applied client-side rather than pushed down to FilterExpression.
¶ New Relic: search_spans reads gen_ai.* attributes directly off NRQL's well-documented flat row shape; search_traces/get_trace additionally rely on an unverified assumption about the shape of the attributes field inside NerdGraph's distributedTracing.trace response, mitigated by overlaying the original search row's attributes back onto the hydrated result. Honeycomb: gen_ai.* attributes are extracted via the same breakdown-column technique used for every other field (module docstring point 2), an equally unverified extraction method.
♦ Honeycomb's entire "Search"/"Get trace by ID" row of checkmarks above depends on the Query Data API, which the research behind this backend found is documented as Enterprise-plan exclusive - a Free/Pro account may not be able to exercise any of this at all. See the Honeycomb setup section above.
Key Tool Details
search_traces
Parameters: service_name, operation_name, start_time/end_time (ISO 8601), min_duration_ms/max_duration_ms, gen_ai_system, gen_ai_request_model, gen_ai_response_model, has_error, tags, filters (see Generic Filter System), limit (1-1000, default 100). Returns trace summaries with token counts.
get_trace
Returns the full trace tree: all spans with attributes, parsed OpenTelemetry gen_ai.* data for LLM spans, per-span token usage, error information, and each span's raw events (e.g. gen_ai.evaluation.result, or any other instrumentation-emitted event — not filtered to a fixed set of names).
triage_trace
Instead of returning raw trace data for an agent to re-derive a diagnosis from every time, this synthesizes one directly: a critical path (the "Last Finishing Child" chain actually responsible for the trace's total latency), the top spans ranked by self-time (latency contribution net of children), and — when the trace contains an error — the deepest error span in the trace's error chain as the likely root cause (confidence: "high", or "medium" when multiple equally-deep error chains make blame ambiguous). Falls back to the highest self-time span as a pure-latency diagnosis (confidence: "low") when no error is present. Deterministic — no LLM call — and works against any configured backend, since it operates on get_trace's already-fetched span data. detail_level: "full" additionally attaches the diagnosed root cause's raw error detail (message/type/stacktrace) when the verdict is error-driven. Results are bounded whatever the trace's depth: in summary mode error_chain and critical_path each list at most 25 spans - the first 5 and last 20 around one explicit [K intermediate span(s) elided] entry, with the real depth stated in the reasoning - and detail_level: "full" raises that to 1000; a trace with no root span (a parent cycle) is reported as such with confidence: "low" rather than as a depth.
correlate_trace
Tries to find the corresponding trace in a second, independently-configured backend (e.g. a Datadog trace and its downstream Sentry error, joined). Tries a direct trace_id match in the secondary backend first (confidence: "high"); if that fails, falls back to a time-window + service-name-overlap heuristic search (confidence: "low") - queried once per service name in the primary trace, so this works against backends like Jaeger that require service_name on search_traces; at most the 10 most-represented services (by span count) are searched, so one trace cannot fan out into hundreds of secondary-backend queries. Each match also reports root_cause_consistent: null when neither trace has an error to compare, otherwise true/false for whether both sides agree on the error and which service it traces back to (reusing the same error-chain logic triage_trace uses). This is a best-effort correlation, not a guaranteed join - the result always includes the fixed limitations (clock skew, sampling mismatches, partial trace visibility, and the fact that trace-id continuity across a vendor boundary isn't guaranteed even under normal W3C Trace Context propagation) plus any per-call truncation notes: the heuristic search window is capped at 24 h after the primary trace's start, however wide an extent the primary trace's spans claim, so one trace cannot make the server scan years of the secondary backend, and the result says when services beyond the 10-service cap were skipped. Requires a secondary backend configured via SECONDARY_BACKEND_TYPE/SECONDARY_BACKEND_URL (see configuration table); raises a clear error otherwise.
get_llm_usage
Returns aggregated prompt/completion/total tokens, broken down by model and by service, plus request counts.
list_services — no parameters. Returns the list of instrumented service names.
find_errors
Returns error messages, error types, truncated stack traces, and LLM-specific error info.
list_llm_models / get_llm_model_stats / get_llm_expensive_traces / get_llm_slow_traces / list_llm_tools_tool / search_spans_tool / list_sessions / get_session_stats / compare_time_windows / investigate_cost_spike / investigate_error_spike / get_prompt_version_stats are documented in detail, with worked examples, in CLAUDE.md — this README covers the shape every tool shares; CLAUDE.md is the fuller reference for exact parameters and response fields on the LLM-analysis tools.
Generic Filter System
search_traces and search_spans_tool both accept a filters list in addition to (or instead of) their simple named parameters, for advanced queries. Each filter is:
field— dotted attribute name, e.g.gen_ai.usage.prompt_tokens,traceloop.span.kind,service.name(1–256 characters)operator— see table belowvalue— single value (most operators) orvalues— list (forin,not_in,between; at most 100 values)value_type—"string","number", or"boolean"
Limits and validation. One call accepts at most 50 filters and 50 tags (two independent caps, not one shared budget), each in/not_in filter at most 100 values, and a field name is 1–256 characters, so a single query can only ever produce a bounded number of backend clauses. Operands are normalised to their declared value_type before anything is sent: a numeric string such as "5000" under "number" is accepted and coerced, while a non-numeric one ("abc"), a non-finite value or an integer too large for a double is rejected; "boolean" accepts a JSON boolean or the strings "true"/"false"; "string" accepts a number or boolean and stringifies it. A filter object with an unknown key is rejected rather than silently ignored. Exceeding a limit or failing validation returns a tool error before any backend request is made.
Multiple filters combine with AND logic. Legacy simple parameters (service_name, gen_ai_request_model, etc.) still work and are converted to filters internally — mix and match freely.
The server uses a hybrid filtering strategy: filters are pushed to the backend's native query language when supported (TraceQL for Tempo, span-search syntax for Datadog, Discover syntax for Sentry), and applied client-side afterward for anything the backend can't express natively.
Example — expensive OpenAI traces:
For the full semantic-convention attribute list (gen_ai.* vs legacy llm.*, token-naming variants across providers, finish-reason values, and the token-calculation fallback chain), see CLAUDE.md.
Example Queries
Find Expensive OpenAI Operations
Ask: "Show me OpenAI traces from the last hour that took longer than 5 seconds"
Tool call: search_traces
Response:
Analyze Token Usage by Model
Ask: "How many tokens did we use for each model today?"
Tool call: get_llm_usage
Response:
Find Traces with Errors
Ask: "Show me all errors from the last hour"
Tool call: find_errors
Response:
Compare Model Performance
Ask: "What's the performance difference between GPT-4 and Claude?"
Tool call 1: get_llm_model_stats for gpt-4
Tool call 2: get_llm_model_stats for claude-3-opus
Investigate High Token Usage
Ask: "Which requests used the most tokens today?"
Tool call: get_llm_expensive_traces
Common Workflows
Cost Optimization
get_llm_expensive_traces— find the highest-token requestsget_llm_usage— see which models are costing the mostget_traceon a specifictrace_id— inspect the exact prompt/response
Performance Debugging
get_llm_slow_traces— identify latency outliersfind_errors— check for failure patternsget_llm_model_stats— check finish-reason distribution for truncation
Model Adoption Tracking
list_llm_models— see every model actually being calledget_llm_model_statsper model — compare performance- Scan
list_llm_modelsresults for unexpected models/services (shadow AI)
Development
CI (.github/workflows/ci.yml) runs Ruff and mypy (strict) on every push, plus the full pytest suite.
mcpb pack mcpb/ builds the Claude Desktop bundle. mcpb/.mcpbignore is a deny-list (mcpb never consults .gitignore): every file under mcpb/ that it or mcpb's built-in patterns do not name is archived, tracked or not - pack from a clean tree (git status --short mcpb/ prints nothing) and check the printed file count before publishing a bundle.
Troubleshooting
Backend connection issues:
Authentication errors: confirm your key is set —
No traces found:
- Check the time range (use recent timestamps)
- Verify service names with
list_services - Try searching without filters first
Token usage shows zero:
- Confirm your traces have OpenTelemetry
gen_ai.*(or legacyllm.*) instrumentation - Inspect raw span attributes with
get_trace
Datadog/Sentry-specific issues: see the troubleshooting notes under each backend in Configuration.
Roadmap
Both ideas that were previously listed here as deferred are now shipped:
- Agent-native triage — tools that flag a likely root cause rather than just returning raw trace data: see
triage_tracein the Tools Reference above. - Cross-backend correlation — querying a second, independently-configured backend and correlating results against it: see
correlate_traceabove. This ships as a best-effort, heuristic correlation (directtrace_idmatch, or a time-window/service-overlap fallback) rather than a guaranteed schema-level join, per the research behind it — trace-id continuity across a vendor boundary is not guaranteed even under normal W3C Trace Context propagation.
New Relic and Honeycomb have since shipped as the 7th and 8th backends, both built entirely from research/documentation without a live account to verify against — see their own setup sections for exactly what's unverified before relying on either in production; Honeycomb in particular carries a real go/no-go risk (its programmatic Query Data API is documented as Enterprise-plan exclusive). There is no further backend currently under research. (Grafana Cloud was never on this list — it already ships today via Tempo's Basic Auth path; see Grafana Tempo above.)
Carried over from upstream's older roadmap, re-prioritized behind the above rather than dropped: a dedicated model-vs-model comparison tool (today's get_llm_model_stats and compare_time_windows cover per-model stats and time-window diffing separately, but not a single tool that diffs two specific models directly), broader prompt-pattern analysis across templates rather than just version-over-version for one prompt (get_prompt_version_stats already covers the latter), MCP resources for common queries, and SigNoz/ClickHouse backend support. Cost calculation with built-in pricing tables and a query-result caching layer are shipped, not pending: every usage-reporting tool (e.g. investigate_cost_spike in the Tools Reference) returns a cost_usd/cost_usd_is_partial pair backed by a vendored litellm pricing table (src/opentelemetry_mcp/pricing/), and query results are cached with in-flight request coalescing via the QUERY_CACHE_TTL_SECONDS env var (see the configuration table above).
Everything else documented elsewhere in this README is shipped.
Contributing
Contributions are welcome. Before opening a PR, make sure:
- All tests pass:
uv run pytest - Code is formatted:
uv run ruff format . - No linting errors:
uv run ruff check . - Type checking passes:
uv run mypy src/
License
Apache License 2.0 — see LICENSE. This project is a fork of traceloop/opentelemetry-mcp-server; full attribution and the fork relationship are documented in NOTICE.
Support
- Issues & feature requests: github.com/mcpsmiths/tracehub-mcp/issues
- Changelog: CHANGELOG.md
- Community: Traceloop Community Slack (upstream's community channel; general OpenTelemetry/GenAI-tracing questions welcome)
- Related: Model Context Protocol · OpenTelemetry GenAI semantic conventions · traceloop/opentelemetry-mcp-server (upstream)
Source: README.md at commit e158055
Tools
0Version history
11- v0.13.1LatestOct 31, 2026
- v0.13.0Oct 14, 2026
- v0.12.3Oct 10, 2026
- v0.12.2Sep 20, 2026
- v0.12.0Sep 20, 2026
- v0.11.0Sep 19, 2026
- v0.10.0Sep 18, 2026
- v0.9.0Sep 18, 2026
- v0.8.2Sep 17, 2026
- v0.8.1Sep 16, 2026
- v0.7.0Sep 16, 2026
Score
50% of the applicable checks met
How complete this listing is, computed from the registry entry, the endpoint and public sources. Not a rating of the server and not a statement that it is safe or working today: reachability is a dated check, and no installs or reviews go into the score.
- Reached in the last 30 days25 pts
Keep the endpoint answering the MCP handshake; SourceWeft probes it every 30 days.
- Auth declared10 pts
Declare the credential headers in the registry entry, or keep the endpoint answering the probe so its auth can be classified.
- Tools listed15 pts
Answer tools/list without a credential, or ship a package that starts in a sandbox without one.
- DescriptionRequired15 pts
- Logo10 pts
- Documentation15 pts
- License10 pts