<!-- Generated by crates/swarmops-cli/src/reference.rs from the code: do not edit. Change the code, then run SWARMOPS_REGEN_REFERENCE=1 with the regen_reference test. -->

# swarmops mcp

Serve the store's operations to an agent as an MCP server on stdin and stdout.

One tool per operation (repo\_info, resolve\_rev, read\_file, read\_tree, show, log, path\_history, diff, op\_log, ref\_list, ref\_set, ref\_delete, undo, commit, land, blob\_put, blob\_has, verify), each taking the operation's request as its arguments and returning the envelope the other commands print. The store and the identity are the server's, --repo and --as or their variables, never a call's. Speaks MCP 2026-07-28, and 2025-11-25 and 2025-06-18 to clients that open with initialize. Prints no envelope: stdout carries the protocol until stdin ends, and a command line it refuses has its envelope on stderr. The derive command has no tool: it is maintenance, no agent's work.

```yaml
title: "swarmops mcp"
description: "Serve the store's operations to an agent as an MCP server on stdin and stdout."
group: "server"
since: "0.1.0"
mutates: true
identity_required: false
mcp_tool: ""
envelope_op: "mcp"
exit_codes: [0]
errors: []
syntax: "swarmops mcp [OPTIONS]"
```

## Syntax

```text
swarmops mcp [OPTIONS]
```

## Required arguments

None.

## Optional arguments

The command also takes the [global options](../cli.md#global-options).

## Examples

### CLI

No recorded example yet.

## Details

### What the server does

`swarmops mcp` is the command an agent harness starts, not one a person types: it turns the store's
operations into MCP tools on stdin and stdout. The transport is JSON-RPC 2.0, one message per
line. The server reads a message, answers it on stdout if it is a request, and reads the next: one
at a time, in order, so a `notifications/cancelled` finds nothing to cancel. Notifications get no
answer.

- **stdout** carries nothing but answers.
- **stderr** gets a line, starting `swarmops mcp:`, for a message that is a response from the
  client, and for the I/O error that stops the server; a bug's panic report; and the reason before
  one of the store's four documented aborts.
- **The exit code** is 0 when stdin ends, 1 when reading stdin or writing stdout fails. On one of
  the store's aborts the process ends by SIGABRT, and the client has to start it again.

### Options

`mcp` takes no options of its own; the [global options](../cli.md#global-options) set the
server for its whole life:

| Option | Effect |
| --- | --- |
| `--repo`, else `SWARMOPS_REPO` | The store every call uses. It is opened anew for each call, so a call sees what other processes committed meanwhile. A store that is not there fails each call with `E_NOT_A_REPO`, and the server serves on. |
| `--as`, else `SWARMOPS_AGENT` | The identity every write is recorded for, with `SWARMOPS_RUN_ID` when it is set. It is checked when a call needs it: reads work without one, a write without one is `E_IDENTITY_REQUIRED`, and a write with a name that is no agent name is `E_INVALID_ARG`. |
| `--json`, `--human`, `--pretty` | No effect: a tool result always holds the JSON envelope. |

No tool argument names another store or identity. A command line that parses as `swarmops mcp` but
is refused (an unknown flag after `mcp`, a `SWARMOPS_FORMAT` that is neither `json` nor `human`)
starts no server: its envelope goes to stderr, stdout stays empty, and the process exits with the
envelope's code. A flag refused before the word `mcp` (`swarmops --frob mcp`) is an ordinary usage
error, printed on stdout with exit code 2.

### Protocol versions

| Version | How a client opens it |
| --- | --- |
| 2026-07-28 | No handshake: each request names the version in `params._meta` (`io.modelcontextprotocol/protocolVersion`) with the client's capabilities. Methods `server/discover`, `tools/list`, `tools/call`. |
| 2025-11-25 | `initialize`. Also the version a client gets that asks `initialize` for one the server does not serve. |
| 2025-06-18 | `initialize`. |

A handshake opens a session that lasts as long as the process; `ping` is answered in it, and also
before `initialize`. Both handshake versions get the same tools and results. 2025-03-26 and older
are not served. The results of `server/discover` and `tools/list` may be kept for an hour
(`ttlMs`), since they change only with the binary.

### Tools

`tools/list` gives 18 tools, one per operation, named as the envelope's `op` is: `repo_info`,
`resolve_rev`, `read_file`, `read_tree`, `show`, `log`, `path_history`, `diff`, `op_log`,
`ref_list`, `ref_set`, `ref_delete`, `undo`, `commit`, `land`, `blob_put`, `blob_has` and
`verify`. A tool's input schema is its request type's JSON Schema (`swarmops schema <Req>`), with
every reference inlined so that it stands alone. Its annotations say what it does to the store:

| Tools | `readOnlyHint` | `destructiveHint` | `idempotentHint` |
| --- | --- | --- | --- |
| The 12 reads, `blob_has` and `verify` among them | `true` | `false` | `true` |
| `commit`, `blob_put` | `false` | `false` | `false` |
| `ref_set`, `ref_delete`, `undo` | `false` | `true` | `true` |
| `land` | `false` | `false` | `true` |

None reaches beyond the store (`openWorldHint: false`). `init`, `whoami`, `schema`, `git import`,
`git export`, `derive` and `mcp` have no tool: a call to one is the protocol error `-32602`
(`Unknown tool`). The git commands and `init` read or write paths on the operator's host, and
`derive` is maintenance, no agent's work.

### Tool results

A call runs as one command of the CLI does, and its result holds the envelope the CLI prints for
the same request, twice: as `structuredContent` and as the text of its one content block.
`isError` is `true` exactly when the envelope's `ok` is `false`. An operation that fails is such a
result, not a protocol error: its `error` has the code, `retryable`, `exit`, `details` and a hint
that is often the next command, whose tool has the matching name (`ref list` is `ref_list`, `cat`
is `read_file`). A `verify` that finds problems is `E_STORE_CORRUPT`, as `swarmops verify` is.

The server adds its own rules to the request types, each refused with `E_INVALID_ARG`:

- **Full ids for compare-and-swap.** `ref_set`'s and `ref_delete`'s `expected_old`, a `commit`'s
  `parent` with `advance`, and `land`'s `resolutions.against` must be the full 64-hex changeset id
  read before. A ref name always matches, a relative revision such as `main~1` never does, and an
  id prefix stops resolving once another changeset shares it.
- **Absolute, regular files.** A `{"file": <path>}` content in `commit`, `blob_put` or `land`'s
  resolutions is read by the server, as the user it runs as. A relative path, the server's own
  stdin or stdout, and anything that is not a regular file are refused, since the server would
  read from a working directory the agent cannot see, or block.
- **Arguments that fit the type.** A field the type does not know is ignored; a field named twice,
  or a value of the wrong type, is refused.

### Sizes

A message line holds at most 64 MiB; a longer one is `-32600`, read no further, and the server
reads on from the next line. Send larger content as `{"file": <path>}`, or store it with
`blob_put` first and name it by id.

Clients limit long tool results, and each result holds its envelope twice. The tools' descriptions
and the server's instructions ask for small reads: `max_bytes` of 32 KiB or less, `limit` and
`max_changes` of 100 or less, an `op_log` `limit` of 20 or less, and paging on. `read_file` alone
declares a larger result for Claude Code (`_meta: {"anthropic/maxResultSizeChars": 200000}`).

### Patterns

**One server per agent identity.** A harness starts one server per session, and everything the
session's subagents write is recorded for that server's identity. An agent that needs an identity
of its own runs as its own process, with its own `SWARMOPS_AGENT`:

```sh
claude mcp add swarmops -e SWARMOPS_AGENT=agent/worker-1 -- swarmops mcp --repo ~/.local/state/swarmops/repos/acme
```

**Give the store by its absolute path.** The server runs in a working directory the agent cannot
see, so a relative `--repo` is read from wherever the harness started it.

**Let agents publish, keep git with the operator.** Agents read, `commit` and `land` through the
tools; the operator runs [`git import`](../commands/git-import.md) and
[`git export`](../commands/git-export.md) beside them as commands.

**Keep a copy of the answers.** The server keeps none. `swarmops op log` lists the writes that
succeeded; a wrapper that copies stdout keeps every envelope ([Keep a copy of every envelope](../../operate/agents.md#keep-a-copy-of-every-envelope)).

### Performance

The server opens the store for each call and takes its snapshots as a CLI command does, without a
process start per call. It keeps no cache between calls: the
[performance ledger](../../perf.md#read-snapshot-after-the-journal-hardening) measures a
snapshot, and names `swarmops mcp` as the long-lived process where caching would pay
([what would change the picture](../../perf.md#what-would-change-the-picture)).

### Failure cases

| Where | What | What to do |
| --- | --- | --- |
| Start | A refused command line: envelope on stderr, its exit code, no server. | Fix the flags or `SWARMOPS_FORMAT`. |
| A message | `-32700` (not JSON or not UTF-8), `-32600` (not one request object, or over 64 MiB), `-32601` (a method the version lacks), `-32602` (bad `params`, an unknown tool, no version before `initialize`), `-32022` (a version not served; `data.supported` lists the served ones), `-32603` (a bug). | Fix the message; the server reads on. |
| A call | `isError: true` and the envelope's `error`. | As at the CLI: [Scripting the CLI](../../develop/cli-scripting.md#retry-rules) gives the retry rules. |
| The process | Exit 1 on an I/O failure of stdin or stdout; SIGABRT on a store abort, with the reason on stderr. | Start the server again. |

### Go further

- [MCP integration](../../develop/mcp.md) runs a session message by message.
- [Use SwarmOps from Claude Code over MCP](../../guides/claude-code.md) registers the server and
  publishes a change.
- [Give agents the store](../../operate/agents.md) covers registration per agent and per
  project.
- [Serving requests](../../known-limitations.md#serving-requests) lists what is still open.

## Return information

`swarmops mcp` prints no envelope: stdout carries the MCP protocol until stdin ends, and a command line it refuses has its envelope on stderr.

Envelope `op`: `mcp`.

Exit codes:

- `0`: Success.

## Errors

No golden step shows this command failing yet.

Any command that opens a store may also fail with a store error, such as [`E_NOT_A_REPO`](../errors.md#e_not_a_repo), [`E_IO`](../errors.md#e_io) or [`E_STORE_CORRUPT`](../errors.md#e_store_corrupt); a command line the grammar refuses is [`E_INVALID_ARG`](../errors.md#e_invalid_arg) (exit 2).

## See also

- [Operating SwarmOps: Give agents the store](../../operations.md#give-agents-the-store)
- [CLI reference](../cli.md)
- [Error codes](../errors.md)
