MCP for coding agents

Your traces already live in your database. This connects your coding agent to them, so instead of pasting a stack trace into a chat you can ask “which model is burning the budget” or “what failed in the last hour” and have the agent query the data itself.

Breadcrumb serves this over MCP at basePath + /api/mcp, on the same handler you already mounted. There is no extra service to run and no database credentials to hand out.

TIP

The engine behind this works on the rest of your database too, not just traces. valv is open source: point it at your own tables, write policies as TypeScript functions, and hand the tools to your agent. It is deny-all by default, so the model can’t read a column you hid or a row outside the caller’s scope.

Connect an agent

Open the MCP tab in your dashboard and create a key. The full key is shown once, on the row you just created, along with a copy-paste command for Claude Code, Codex, and a generic JSON config, with your own URL already filled in.

claude mcp add --transport http breadcrumb https://yourapp.com/api/breadcrumb/api/mcp \
  --header "Authorization: Bearer bcmcp_…"

That’s the whole setup. Ask your agent something:

Group error spans from the last day by model and show the total cost.

What the agent can do

It gets four tools over a single resource, the breadcrumb_spans table:

ToolWhat it does
list_resourcesLists what’s queryable.
search_resourcesFinds a resource by keyword.
describe_resourceReturns columns, types, and what each one means.
queryRuns a structured, validated query.

The agent never writes SQL. It sends a structured query that is validated against the schema and compiled to SQL by valv, which is also where the access rules are enforced. Columns carry descriptions, so the agent knows that start_time is epoch milliseconds and that a NULL cost means no price is configured rather than free.

What it cannot do

This is the part worth understanding before you hand out a key.

  • Read-only. No inserts, updates, or deletes. There is no write path.
  • One table. The schema declares exactly one resource. Every other table in your database is unreachable by construction, including breadcrumb_mcp_keys — an agent cannot read the hashes of the keys that authenticate it.
  • Bounded. Queries are capped at 1000 rows, so a careless request can’t pull your whole span table.

Keys are stored as SHA-256 hashes. A database leak yields nothing replayable, and the raw key is unrecoverable after creation. Revoking one from the dashboard takes effect immediately.

NOTE

Minting a key is guarded by your authorize function, the same one that guards every other query route. Anyone who can already read traces can create a key that reads the same data. If your API is open, so is this.

Local and production

Connect both. They are separate MCP servers with separate keys, so your agent can debug against local traces and production traces in the same session.

Because MCP clients key servers by name, the two would collide. A Breadcrumb reached over a loopback address calls itself breadcrumb-local; anywhere else it is breadcrumb. The dashboard’s snippets always use the right one.

Running a third, like staging, needs an explicit name:

export const bc = breadcrumb({
  database: postgres(process.env.DATABASE_URL),
  mcp: { name: "breadcrumb-staging" },
});

Hiding payloads

Spans capture input and output, which hold raw prompts and completions. If that content shouldn’t reach an agent:

export const bc = breadcrumb({
  database: postgres(process.env.DATABASE_URL),
  mcp: { hidePayloads: true },
});

Timings, tokens, cost, model, status, and errors stay queryable, so an agent can still diagnose a failing run without reading user content. For scrubbing content everywhere rather than just here, use redact instead — it runs before storage, so the data never lands in your database at all.

Options

OptionTypeDescription
namestringWhat clients register this server as. Defaults to "breadcrumb-local" over loopback, "breadcrumb" otherwise.
hidePayloadsbooleanHides the input and output columns from the agent. Default false.

The endpoint is always mounted; mcp only tunes it. It stays unreachable until someone creates a key.

Next steps

  • Configuration: every breadcrumb() option.
  • Production: locking down the API, which also controls who can mint a key.
  • Migrations: the breadcrumb_mcp_keys table keys are stored in.