# MCP Server

23 tools for querying cost, alerts, traces, drift, policy, and prompt summarization from inside an agent's own loop.

---

The MCP server puts tj directly in an agent's request path. That's the right fit for SDK and API integrations, where tj already sits in the loop and a few extra tool definitions in context are the cost of doing business. **It's not what Claude Code or Codex onboarding installs by default** — see [Claude Code & Codex](/docs/claude-code) for why: a measured A/B showed an in-loop MCP server costs Claude Code subscription users +36% model-weighted quota against a no-tj control.

## Start it

```bash
tj mcp   # starts the stdio MCP server
```

If `tj serve` is already running, `tj mcp` proxies through its HTTP API. Otherwise it opens the DuckDB file directly, read-only when it can, so a running `tj serve` never hits a lock conflict.

Wire it into any MCP-capable client (Claude Desktop, an SDK-based agent, a custom harness) by pointing the client's MCP config at the `tj mcp` command.

## Tools

### Observability

| Tool | What it does |
|---|---|
| `get_status` | Current state for one or all agents: tokens, cost, active alerts. |
| `get_budget_headroom` | Daily and per-session budget limits vs current spend for one agent. |
| `list_agents` | Every agent tj has seen, with first/last-seen timestamps and lifetime cost. |
| `list_active_sessions` | Every session currently running, across all agents. |
| `get_cost_summary` | Cost breakdown by day, agent, or model. |
| `list_alerts` | Alert history, filterable by severity or unread-only. |
| `list_traces` | Recent traces with cost, duration, and span count. |
| `get_trace` | Full span waterfall for one trace. |
| `get_tool_stats` | Call counts and average duration per tool. |
| `get_drift_report` | Statistical baseline vs the latest session's actual behavior. |
| `acknowledge_alert` | Mark one alert as read. |

### Setup

| Tool | What it does |
|---|---|
| `setup_project` | Write `OTEL_RESOURCE_ATTRIBUTES` so the current project tags its spans with the right agent ID. |
| `setup_harness` | Wire a fan-out harness (a governor that spawns many worker sessions) into tj's run grouping. `mode='map'` only reports; `mode='instrument'` writes a drop-in helper. |
| `open_dashboard` | Open the web UI, starting `tj serve` first if it isn't already running. |

### Cost optimization

| Tool | What it does |
|---|---|
| `get_optimize_report` | Cost-saving candidates and budget projections, same analyzers as `tj optimize`. |
| `get_policy_status` | Defined enforcement-plane policies and what the proxy has been deciding. |
| `get_savings_summary` | Estimated-recoverable spend if the current policies were enforced. |
| `suggest_policies` | Recommend policies (e.g. a budget cap) from actual usage. |

The policy tools describe **suggest mode**: every figure is what a policy *would* do or *would have* saved, never a claim of realized savings or enforcement. Nothing in the open-source tree enforces a policy today.

### Prompt summarization

| Tool | What it does |
|---|---|
| `list_summarize_candidates` | Prose-heavy `CLAUDE.md` / `AGENTS.md` files worth summarizing, with an estimated per-call token reduction. |
| `summarize_prep` | Wrap a prompt's structured regions (code, tags, tables) so a rewrite can touch only the prose. |
| `summarize_check` | Verify a rewritten summary restored every protected block verbatim, then stage it for review. |
| `summarize_apply` | Write a staged summary to disk. Defaults to a dry run; pass `go=true` to write. |
| `summarize_undo` | Restore the most recent backup for a file `summarize_apply` wrote. |

The summarize tools never rewrite a file directly — the model does the rewrite, and `summarize_check` verifies structure survived before anything is staged. `summarize_apply` still defaults to dry-run on top of that.

## Read-only by default

`tj mcp` opens its DuckDB connection read-only whenever it can, so it can run alongside `tj serve` without fighting over the file lock. `acknowledge_alert` is the one tool that needs a write: if a read-only connection is already open in-process, it returns an actionable error instead of crashing — acknowledge from the dashboard, or stop `tj serve` and retry.