All posts
Engineering

How TracePath's MCP Server Works

TracePath speaks MCP: a remote server with OAuth built in and a local stdio one on the CLI. The tools, the three credentials, and when to skip it.

TracePath exposes its production telemetry over the Model Context Protocol. Point Claude Code, Claude Desktop or Cursor at your instance and the agent reads the same data your dashboard shows — exceptions, logs, endpoint latency, traces, metrics — so it can find the deploy where p95 doubled, pull the stack trace, and walk the span waterfall without anyone pasting a screenshot into a chat window.

The interesting part is that the MCP server itself was the quick piece. It is sixteen tools, and every one is a thin wrapper around an API call that already existed. The real work was the authentication around it.

claude mcp add --transport http tracepath https://app.tracepath.dev/mcp

Key takeaways

  • Sixteen tools, fourteen of them read-only and annotated as such; the two writes archive and unarchive exceptions.
  • The remote server is not a sidecar. It runs inside the existing backend at /mcp and reaches the API through an in-process loopback transport.
  • Every tool call goes through the same bearer auth and project-access middleware as a browser request, so the MCP layer holds no extra powers.
  • Three credentials work: OAuth with dynamic client registration, a personal access token, or the CLI's device-flow token.
  • The CLI, which is not published yet, carries the same server over stdio, and the CLI skill and the MCP prompts are generated from one set of knowledge documents with a CI gate against drift.

Sixteen tools over an existing client

The server lives in one Go package and it is deliberately boring. Each tool wraps one call on the same API client the tracepath CLI commands use, declares a JSON schema for its output, and returns validated structured content next to the plain text.

Fourteen of the sixteen are read-only and carry the read-only annotation, so a client can present them as safe: listing projects, exceptions and endpoints, fetching a single exception or occurrence, querying logs and metrics, pulling an endpoint chart, a task, an AI trace, a session, a distributed trace. The other two archive and unarchive exceptions. They undo each other, and their descriptions tell the agent to touch them only when a human asks by name — "look at this error" means read it, not archive it.

The debugging knowledge ships inside the server too. Four prompts (debug_issue, investigate_performance, whats_broken, resolve_notification) and nine short knowledge documents, exposed as MCP resources, cover things a model reliably gets wrong on its own: always bound queries in time, recover a record's timestamp from the dashboard URL before guessing, which tool replaces which CLI command, how to read a latency shape. Where those documents come from matters, and it is the last section of this post.

One process, no second service

The remote server is not a separate deployment. Your TracePath backend serves it at /mcp, and internally the tool layer talks to the API through a loopback transport: a custom RoundTripper that hands the request straight to the same Gin engine, in process. No socket, no internal port, no network hop.

How a tool call travels through one process

The reason this shape is worth the small amount of plumbing is that every tool call still enters the API through the front door. The caller's bearer token rides along on each loopback request, the same auth middleware validates it, and the same project-access checks run. The MCP layer holds no special powers: if your token cannot see a project in the dashboard, it cannot see it through a tool either. Multi-user isolation comes for free — two people hitting /mcp at once are two bearer tokens flowing through the middleware that already handles two browser tabs.

One binary, three surfaces. The CLI, the stdio MCP server and the remote MCP server are the same code reading the same API.

Three ways in

An MCP endpoint that can read production errors needs real authentication, and MCP clients expect to configure it themselves. The spec leans on OAuth, and OAuth is where the time went.

Three credentials, one door

OAuth, where the client does the work. Add the server and watch what happens. The first request has no token, so the backend answers 401 with a WWW-Authenticate header pointing at a discovery document. From there the client finds the authorization server metadata, registers itself as an OAuth client — RFC 7591 dynamic client registration, no admin screen, no client secret to paste — and opens your browser on a consent page served by your own instance. You click Approve. The client exchanges the resulting code for tokens and starts calling tools. Nothing was installed and nobody configured anything.

Under the hood that exchange is the authorization-code grant with PKCE, S256 only. PKCE is a small trick: the client invents a secret, sends only its hash with the authorization request, then must present the original secret at the token endpoint. Whoever steals the code mid-flight cannot redeem it without the secret that never left the client. The codes themselves are single-use, expire in five minutes and are stored as SHA-256 hashes; a failed exchange burns the code anyway. What comes back is a 15-minute access token plus a refresh token that rotates on every use and lives at most 90 days. Replay an already-spent refresh token and the server revokes the whole token family, on the theory that someone else is holding your credentials.

Personal access tokens, for everything headless. CI jobs and clients that do not do OAuth send a tpp_ token as a plain bearer header. You mint them on your account page, give them an optional expiry, and revoke them from the same screen. They are stored hashed, because a token table is a target.

The device flow, for terminals. tracepath login shows a short code, you approve it in the browser, and the CLI holds the same rotating refresh token an OAuth client would. This one predates MCP, but /mcp accepts its tokens too, because all three credentials go through one bearer-auth path.

The local server

The tracepath CLI is not published yet, so the remote server above is the way in today. When the CLI does ship there is a second way to run the whole thing, with no HTTP transport involved:

claude mcp add tracepath -- tracepath mcp

The CLI doubles as a stdio MCP server. It reuses your CLI session, refreshes tokens the same way, and inherits your current project, so tools do not need a project_id on every call. In a container, skip the session and set TRACEPATH_URL and TRACEPATH_TOKEN (a personal access token) in the environment instead. If you are not logged in it exits immediately and says how to fix it, rather than leaving the agent talking to a server that cannot answer.

Until it lands, the MCP documentation tracks availability, and [email protected] can tell you where it stands.

So when would we not use MCP?

Honest answer: whenever the agent has a shell.

One knowledge source, two paths, same instance

In a terminal agent, the TracePath skill plus the CLI is the better tool, for two reasons.

Context. Sixteen tool definitions with their schemas ride along in every conversation, used or not. That is a real tax on the context window. A skill is one paragraph until the agent actually needs it, and then it loads exactly the playbook it wants. Same knowledge, paid for only when it is used.

Composition. A shell agent can pipe tracepath exceptions list --output json through jq, cross-reference the result with git log, and grep the codebase for the function in the stack trace, all in one breath. Tools do not compose like that: a tool result lands in the conversation and the model has to carry it by hand to the next call.

So the rule is one question — can the agent run commands? Yes: skill plus CLI, lighter and composable. No — Claude Desktop, a browser session, an IDE chat panel, or a teammate who will never install a binary — the remote MCP server, because OAuth makes the setup self-service and there is nothing to install.

One knowledge source, rendered twice

The trap with shipping both surfaces would be maintaining two brains that drift apart, and that is the one thing worth refusing outright.

The skill files, the MCP prompts and the MCP resources are all generated from the same knowledge documents in the repository. The build regenerates the skill into a temporary directory and diffs it against the committed copy, and CI fails if they differ. The CLI skill and the MCP server cannot disagree, because there is nothing for them to disagree about. One source, rendered twice.

FAQ

Which clients can connect? Anything that speaks MCP over streamable HTTP or stdio. Claude Code, Claude Desktop and Cursor are the ones with copy-paste setup in the docs.

Can an agent change anything in my account through MCP? Only archive and unarchive on exceptions. The other fourteen tools are read-only and annotated so clients can show that, and every call is still bound by the project access your token already has.

Do I need to set up an OAuth client first? No. The client registers itself on first connect via RFC 7591, then sends you to a consent page on your own instance. There is no client secret to create or store.

What if my client cannot do OAuth? Mint a personal access token on your account page and send it as a bearer header. That is also the right choice for CI and containers, together with TRACEPATH_URL and TRACEPATH_TOKEN on the local stdio server.

Does the MCP server see data my dashboard user cannot? No. Tool calls re-enter the API through the same middleware as a browser request, so project access and read/write roles apply identically.

Further reading

Subscribe

Get new engineering posts in your inbox

This opens your mail client with a short message to [email protected]. Prefer no email at all? Use the RSS feed.