# Harness as MCP

`forge mcp serve` speaks MCP over stdio, exposing the harness as tools any
MCP-capable agent can call directly — so the agent can *ask* the harness what it
knows, instead of only being fed by [hooks](/harness/live-agent-hooks).

```sh
forge mcp serve            # stdio JSON-RPC; wire it into your host as a server
```

## Tools

| Tool | Arguments | Returns |
|---|---|---|
| `forge_metrics` | — | [harness rollup](/harness/metrics): verify pass rate, breaker, budget, secscan, timeline |
| `forge_profile` | — | repo-adaptive profile: stack, provider, size, relevant skills |
| `forge_provider` | — | resolved git host + related repos |
| `forge_trace_show` | `change` | that change's [session timeline](/harness/timeline) |
| `forge_budget_check` | `change`, optional `max_tokens` | [budget](/harness/budget) status; reports a crossed ceiling |
| `forge_verify` | optional `change` | tests/lint + scenario coverage; with `change`, the [circuit-breaker](/harness/circuit-breaker) verdict |

Each tool delegates to the same `forge` command you'd run in a terminal, so the
CLI and the MCP surface cannot drift apart.

## A refusal is data, not an error

When a gate refuses — an open breaker, a crossed budget ceiling — the tool
returns a **result** with `isError: true` and the message as text, not a
JSON-RPC transport error. That distinction matters: the agent receives the
verdict as something it can read and reason about, rather than a broken call it
might retry blindly.

## Queryable, not enforcing

::: warning This is the boundary
MCP makes the harness **legible**. It does not make it **binding**. An agent
that can call a tool can also ignore what the tool says.
:::

Enforcement stays where it has teeth: the non-zero exits used by
[live-agent hooks](/harness/live-agent-hooks), pre-commit, and CI — paths the
agent does not control. Use MCP so the agent can steer itself with real
evidence; keep the [enforced gates](/harness/overview) for the cases where being
ignored is not an acceptable outcome.

## Implementation note

The server is hand-rolled on stdlib JSON-RPC — no MCP SDK dependency — so forge
remains a single static binary with no runtime requirements. It implements the
minimal useful subset: `initialize`, `tools/list`, `tools/call`. Notifications
produce no response, and one malformed line yields a parse error without killing
the session.
