You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Today @relayburn/mcp exposes only two hand-authored tools (burn__sessionCost, burn__currentBlock) — a tiny subset of the CLI's read surface. As the CLI grows (#152 hotspots/overhead, #156 summary --by-tool, #164 budget, #165 state, etc.), MCP falls further behind. Refactor the MCP package so each tool is a thin wrapper that shells out to burn <verb> --json internally. New CLI verbs become MCP tools "for free" — drift becomes structurally impossible.
burn__sessionCost — total USD / tokens / turns / models for a session
burn__currentBlock — 5-hour OAuth window % + local burn-rate forecast
668 LOC across packages/mcp/src/ (config, server, tools, types)
Each tool is hand-authored — query logic duplicated from CLI commands
Today's CLI surface (post-cleanup epic): 10+ verbs, every read verb supports --json, output schemas are stable.
The drift: every new CLI verb requires a hand-authored MCP tool to stay relevant. That has not happened. The two MCP tools are subsets of burn summary --session $X --json and burn budget --json — already replicable through the CLI alone if the agent has shell access.
Why MCP still earns its keep: in-session agent self-query has real value over Bash invocation —
Tool-schema discoverability (agent sees the tools listed, doesn't need to know burn exists)
Session id baked into the server invocation (no env-var dance)
Lower per-query overhead in tokens (structured tool definitions vs. shell command + JSON parse)
Works in restricted-shell environments
Works across all major harnesses today (Claude Code, Codex, OpenCode all support MCP)
The right fix isn't to drop MCP — it's to make MCP's surface a derivative of the CLI's surface so they stay in lockstep without coordination.
Proposed shape
Each MCP tool becomes a thin registration that:
Declares its input schema (the subset of CLI flags it accepts)
Maps tool input to CLI args
Spawns burn <verb> --json [flags] as a subprocess
Parses stdout, returns the JSON as the MCP tool result
The two existing tools (burn__sessionCost, burn__currentBlock) are subsets of the new generic tools. Two paths:
Drop them — agents use burn__summary --session $defaultSessionId and burn__budget --no-forecast. Cleaner long-term.
Keep as named aliases — preserves the convenience surface for existing config consumers. One-line registrations that internally call the same CLI subprocess.
Recommend drop since this is internal-API churn and there are no users yet.
Default session id flow
The --session-id <uuid> passed to burn mcp-server (today's behavior — commands/mcp-server.ts:30-31) flows into ToolDefaults.sessionId. Tools that scope by session (burn__summary, burn__hotspots) inject it as a default --session flag if the tool input doesn't override.
Implementation Steps
No users yet — clean refactor, no compatibility shims for the existing tool names.
Add the cli-derive helper (packages/mcp/src/tools/cli-derive.ts):
execFile-based subprocess wrapper. Use node:child_process's execFile (not exec) to avoid shell injection.
Resolve the burn binary path: prefer process.execPath-relative resolution if running from the same monorepo install; otherwise rely on $PATH. Document the assumption.
JSON parse + error handling. If stdout isn't valid JSON (e.g. command error), bubble stderr as the tool error.
Build the tool catalog (packages/mcp/src/tools/):
One file per tool: summary.ts, hotspots.ts, compare.ts, overhead.ts, overhead-trim.ts, budget.ts. Each exports a CliDerivedToolDef with the tool's input schema and arg-builder.
Input schemas use Zod (already a dependency of @relayburn/mcp). Cover the read flags: since, project, session, workflow, agent, provider, plus mode flags for summary (byProvider, byTool, bySubagentType, subagentTree) and the patterns flag for hotspots.
Update @relayburn/mcp README and packages/mcp/CHANGELOG.md to reflect the new shape and tool catalog. Bump major version (1.0.0 → 2.0.0) since the tool names change.
Tests:
packages/mcp/src/tools/cli-derive.test.ts: stub execFile, assert correct args are passed for various tool inputs, assert JSON parse round-trip, assert error-path behavior.
packages/mcp/src/end-to-end.test.ts: spawn the real burn binary via cli-derive, exercise each tool against a fixture ledger, assert the structured output matches the CLI's --json shape.
Docs sweep: README mention of MCP tools, AGENTS.md if present, CHANGELOG [Unreleased] on @relayburn/mcp and @relayburn/cli.
Files touched (scope)
packages/mcp/src/tools/cli-derive.ts — new helper
packages/mcp/src/tools/{summary,hotspots,compare,overhead,overhead-trim,budget}.ts — new (one per tool)
packages/cli/src/commands/mcp-server.ts — uses the new catalog
packages/mcp/{README.md,CHANGELOG.md} — version bump + tool catalog
packages/mcp/src/end-to-end.test.ts and new test file
Root README, AGENTS.md if needed
No CLI surface changes. No @relayburn/analyze / @relayburn/ledger API changes.
Risks / questions
Subprocess overhead. Per-call cost is ~ms (Node spawn + JSON parse). Negligible against typical model latency. Document the assumption — if it ever becomes a hot path, the same tools can grow an in-process implementation later.
burn on PATH. The MCP server must resolve the burn binary. Today's MCP setup already implicitly requires this (the harness's MCP config invokes burn mcp-server by name). Document the requirement; consider BURN_BIN env override for non-standard installs.
Error-shape parity. CLI commands exit non-zero with stderr text on error. MCP tools must surface those as structured errors ({ error: string }). Define a uniform error-translation policy in cli-derive.
Tool input schema vs CLI flag drift. Adding a new flag to a CLI verb won't automatically appear in the MCP tool's input schema. Schemas are still hand-authored, but they're per-tool and small. Worth tracking — if drift becomes a problem, the long-term play is to have the CLI export its flag schemas in a structured format MCP can import.
Tool naming.burn__summary vs summary (no namespace). Today's tools use the burn__ prefix; preserve it. Some harnesses present tools without namespacing, so the prefix gives users a "where is this from" hint.
Major bump. Renaming burn__sessionCost → drop and replace with burn__summary is a breaking API change for any consumer importing @relayburn/mcp directly. No users yet, so the major bump is documentation, not a migration.
Out of scope
Changing the CLI's --json schemas. Stable contract — MCP tools depend on it.
Wrapping burn run to auto-wire MCP into the harness config. Considered and rejected — configuration overhead is a one-time cost users accept; auto-wiring would add hidden behavior to burn run that may not be wanted.
Summary
Today
@relayburn/mcpexposes only two hand-authored tools (burn__sessionCost,burn__currentBlock) — a tiny subset of the CLI's read surface. As the CLI grows (#152 hotspots/overhead, #156 summary --by-tool, #164 budget, #165 state, etc.), MCP falls further behind. Refactor the MCP package so each tool is a thin wrapper that shells out toburn <verb> --jsoninternally. New CLI verbs become MCP tools "for free" — drift becomes structurally impossible.Background
Today's MCP surface (
packages/cli/src/commands/mcp-server.ts:64-67,@relayburn/mcppackage):burn__sessionCost— total USD / tokens / turns / models for a sessionburn__currentBlock— 5-hour OAuth window % + local burn-rate forecastpackages/mcp/src/(config, server, tools, types)Today's CLI surface (post-cleanup epic): 10+ verbs, every read verb supports
--json, output schemas are stable.The drift: every new CLI verb requires a hand-authored MCP tool to stay relevant. That has not happened. The two MCP tools are subsets of
burn summary --session $X --jsonandburn budget --json— already replicable through the CLI alone if the agent has shell access.Why MCP still earns its keep: in-session agent self-query has real value over Bash invocation —
The right fix isn't to drop MCP — it's to make MCP's surface a derivative of the CLI's surface so they stay in lockstep without coordination.
Proposed shape
Each MCP tool becomes a thin registration that:
burn <verb> --json [flags]as a subprocessTool catalog (initial)
burn__summaryburn summary --json [flags]burn__hotspotsburn hotspots --json [flags]burn__compareburn compare <models> --json [flags]burn__overheadburn overhead --json [flags]burn__overheadTrimburn overhead trim --json [flags](after #204)burn__budgetburn budget --json [flags]The two existing tools (
burn__sessionCost,burn__currentBlock) are subsets of the new generic tools. Two paths:burn__summary --session $defaultSessionIdandburn__budget --no-forecast. Cleaner long-term.Recommend drop since this is internal-API churn and there are no users yet.
Default session id flow
The
--session-id <uuid>passed toburn mcp-server(today's behavior —commands/mcp-server.ts:30-31) flows intoToolDefaults.sessionId. Tools that scope by session (burn__summary,burn__hotspots) inject it as a default--sessionflag if the tool input doesn't override.Implementation Steps
No users yet — clean refactor, no compatibility shims for the existing tool names.
cli-derivehelper (packages/mcp/src/tools/cli-derive.ts):execFile-based subprocess wrapper. Usenode:child_process'sexecFile(notexec) to avoid shell injection.burnbinary path: preferprocess.execPath-relative resolution if running from the same monorepo install; otherwise rely on$PATH. Document the assumption.packages/mcp/src/tools/):summary.ts,hotspots.ts,compare.ts,overhead.ts,overhead-trim.ts,budget.ts. Each exports aCliDerivedToolDefwith the tool's input schema and arg-builder.@relayburn/mcp). Cover the read flags:since,project,session,workflow,agent,provider, plus mode flags forsummary(byProvider,byTool,bySubagentType,subagentTree) and the patterns flag forhotspots.comparerequires the model-list positional (per cli:burn compareshould take models as a required positional, not a--modelsflag #159). Schema:{ models: string[] }(length ≥ 2).mcp-server.ts(packages/cli/src/commands/mcp-server.ts:64-67):createSessionCostTool/createCurrentBlockToolwith the new tool catalog.defaults: { sessionId: defaultSessionId }through to every tool that scopes by session.MCP_HELPconstant atmcp-server.ts:11-23) to list the new tool catalog.packages/mcp/src/tools/sessionCost.ts(and equivalents) — gone.packages/mcp/src/index.tsexports updated.@relayburn/mcpREADME andpackages/mcp/CHANGELOG.mdto reflect the new shape and tool catalog. Bump major version (1.0.0 → 2.0.0) since the tool names change.packages/mcp/src/tools/cli-derive.test.ts: stubexecFile, assert correct args are passed for various tool inputs, assert JSON parse round-trip, assert error-path behavior.packages/mcp/src/end-to-end.test.ts: spawn the realburnbinary viacli-derive, exercise each tool against a fixture ledger, assert the structured output matches the CLI's--jsonshape.[Unreleased]on@relayburn/mcpand@relayburn/cli.Files touched (scope)
packages/mcp/src/tools/cli-derive.ts— new helperpackages/mcp/src/tools/{summary,hotspots,compare,overhead,overhead-trim,budget}.ts— new (one per tool)packages/mcp/src/tools/{sessionCost,currentBlock}.ts— deletedpackages/mcp/src/index.ts— export updatespackages/cli/src/commands/mcp-server.ts— uses the new catalogpackages/mcp/{README.md,CHANGELOG.md}— version bump + tool catalogpackages/mcp/src/end-to-end.test.tsand new test fileNo CLI surface changes. No
@relayburn/analyze/@relayburn/ledgerAPI changes.Risks / questions
burnon PATH. The MCP server must resolve theburnbinary. Today's MCP setup already implicitly requires this (the harness's MCP config invokesburn mcp-serverby name). Document the requirement; considerBURN_BINenv override for non-standard installs.{ error: string }). Define a uniform error-translation policy incli-derive.burn__summaryvssummary(no namespace). Today's tools use theburn__prefix; preserve it. Some harnesses present tools without namespacing, so the prefix gives users a "where is this from" hint.burn__sessionCost→ drop and replace withburn__summaryis a breaking API change for any consumer importing@relayburn/mcpdirectly. No users yet, so the major bump is documentation, not a migration.Out of scope
--jsonschemas. Stable contract — MCP tools depend on it.burn runto auto-wire MCP into the harness config. Considered and rejected — configuration overhead is a one-time cost users accept; auto-wiring would add hidden behavior toburn runthat may not be wanted.waste/diagnose→hotspots,context→overhead#152 (waste/context→hotspots/overhead), cli: unifyburn rebuildandburn archiveunder one "rebuild derived state" verb #153 (rebuild+archive), cli: foldburn by-toolintoburn summary --by-tool#156 (summary --by-tool), cli:burn compareshould take models as a required positional, not a--modelsflag #159 (comparepositional models), cli: collapseburn watchandburn ingestunder oneburn ingestverb with mode flags #161 (burn ingest), cli: foldlimitsandplansunder oneburn budgetverb with nestedplanssubcommand #164 (burn budget), cli: grouprebuildandcontent pruneunder oneburn stateverb #165 (burn state), cli: add--jsonsupport toburn context advise(burn overhead trimpost-#152) #204 (overhead trim --json).