# MCP server

`heliograph mcp` turns the CLI into a set of tools any MCP-capable agent can
call. Claude Code, Claude Desktop, Cursor, Windsurf, Cline, Zed - anything that
speaks the Model Context Protocol.

It is the same binary. There is nothing extra to install.

## Configure it

Claude Code, one command:

```bash
claude mcp add heliograph -- heliograph mcp
```

Anything that takes a JSON config:

```json
{
  "mcpServers": {
    "heliograph": {
      "command": "heliograph",
      "args": ["mcp"]
    }
  }
}
```

The server reads your existing estates. If `heliograph estates` lists it, the
tools can reach it.

## The tools

| tool | what it does |
|---|---|
| `heliograph_estates` | what is configured here, and which transport each uses |
| `heliograph_send` | publish a step, and return. It does **not** wait |
| `heliograph_status` | what the station is doing now |
| `heliograph_logs` | the captured logs, newest first |
| `heliograph_read_log` | one log, whole |
| `heliograph_gaps` | where a run stalled, longest interval first |
| `heliograph_doctor` | will this work from here, changing nothing |

## Why tools and not just the skill

The [skill](claude-code.md) teaches an agent to run the CLI, and that works.
Every answer arrives as text it has to parse back out of a terminal.

A tool returns a result the agent did not have to scrape, with an argument list
it cannot get subtly wrong. `heliograph_gaps` hands back the intervals already
measured and attributed. The agent does not have to notice a stall in a column
of timestamps; it is told.

## What it does not change

**Nothing about the gates.** A tool call publishes a request. The station still
decides whether to run it, still refuses a step that declares neither read-only
nor action, and still refuses an action unless it was started with
`--allow-actions` and the request carries `CONFIRM=yes`.

An MCP client asks. It does not get to answer.

```diagram gates
Three gates, all on the far side. The control side can ask for anything; the station decides.
```


**Nothing about the far side.** There is still no connection, no tunnel and
nothing held open. Somebody with legitimate access started the loop, and they
can stop it.

## Reading a result

`heliograph_send` returns as soon as the request is published, which is before
the run has started. An agent that treats the reply as the result will read the
*previous* run's log and report it as this one's. The tool description says so,
and the shape of the loop is:

1. `heliograph_send`
2. `heliograph_status` until the state is `idle`, `cancelled`, `refused` or `stopped`
3. `heliograph_gaps` to find where the time went
4. `heliograph_read_log` for the whole thing

`refused` is not a failure. It means the station would not run that step, and
the reason says which flag would have permitted it. An agent that reads a
refusal as a crash will retry the same step forever.
