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.
- Group
server- Since
0.1.0- Mutates
- yes
- Identity required
- no
- MCP tool
- none
- Envelope op
mcp- Exit codes
- 0
- Errors
- none
Syntax#
swarmops mcp [OPTIONS]Required arguments#
None.
Optional arguments#
The command also takes the global options.
Examples#
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 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 andref_delete'sexpected_old, acommit'sparentwithadvance, andland'sresolutions.againstmust be the full 64-hex changeset id read before. A ref name always matches, a relative revision such asmain~1never does, and an id prefix stops resolving once another changeset shares it. - Absolute, regular files. A
{"file": <path>}content incommit,blob_putorland'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:
claude mcp add swarmops -e SWARMOPS_AGENT=agent/worker-1 -- swarmops mcp --repo ~/.local/state/swarmops/repos/acmeGive 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 and
git export 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).
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 measures a
snapshot, and names swarmops mcp as the long-lived process where caching would pay
(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 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 runs a session message by message.
- Use SwarmOps from Claude Code over MCP registers the server and publishes a change.
- Give agents the store covers registration per agent and per project.
- 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, E_IO or E_STORE_CORRUPT; a command line the grammar refuses is E_INVALID_ARG (exit 2).