# MCP surface The harness ships an embedded MCP server (rmcp 2). A persistent `hive-mcp-http` daemon serves the built-in `hyperhive` surface over streamable HTTP (loopback, `127.0.0.1:`, per-container private netns). Claude connects to its stable URL via `--mcp-config` rather than respawning a stdio child each turn, so the URL survives the per-turn claude re-spawn (and a host-side hive-c0re restart) — there is no per-turn MCP re-registration race for the built-in surface. HTTP is the sole transport for it (no stdio fallback). Extra servers (`services.hyperhive.agent.extraMcpServers`) pick their own transport per entry (`type = "stdio" | "http"`, default `"stdio"`): `matrix` and `subagent` each run their own persistent streamable-http listener (`hive-matrix-daemon`, `hive-subagent-daemon`), same reasoning as the built-in surface and `bash`'s `hive-bash-daemon`. The GUI bridge (`screen`, gated on `services.hyperhive.agent.gui.enable`) is the one entry on the schema's stdio default — it spawns `hive-screen-mcp` fresh each turn. `rea` (gated on `services.hyperhive.agent.rea.enable`) is a second stdio entry, spawning `rea mcp`. The server name is `hyperhive`, so the tools land in claude as `mcp__hyperhive__`. Each entry also has an `availableToSubagents` toggle (default `false`) — see [`docs/4-implementation/agent-tools/subagent.md`](../agent-tools/subagent.md#mcp-servers-available-to-a-subagent) for what reaches a subagent's own `--mcp-config` and what doesn't. An `"http"` entry that needs request headers (a bearer-authenticated remote server) sets `headersFile` to an absolute path: a file holding a JSON object of header name to value. It renders as claude's `headersHelper` (`cat` of the file), so the secret never enters the nix store, and claude re-reads a rotated token on its next reconnect. ACP agents don't read it. The agent's permissions gate tool access ([`docs/5-project/process/conventions.md#permissions`](../../5-project/process/conventions.md)). The default (`Permission::DEFAULT`) is `messaging`, `meta`, `inbox` and `execution`; `swarmctl agent permissions set` changes it, and the next turn runs on the change. ## Core tools (always available) **Messaging.** There is no hyperhive messaging tool. Agents talk to peers and the operator over matrix ([matrix](../agent-tools/matrix.md)); `send_dm` takes a peer's agent name (`iris`, meaning that agent on the same homeserver) or a full user id. An agent without matrix tools is cut off from peers on purpose, and the operator reads its `set_status` line, its turn output on the dashboard, and its logs. The `messaging` permission remains an accepted value for stored permission sets, but grants no tool. The inbox itself is the harness's: it pops one message per turn and acks it when the turn ends, so there is no agent-facing `recv` or `ack_until`. **Graceful shutdown**: when the harness receives a stop signal, the inbox becomes fenced and the harness delivers an explicit `from: "graceful-stop"` message as the turn's prompt. It directs the agent to flush durable state (`/state` files) and end the turn; the container exits when the turn completes. The graceful-stop turn takes the same post-turn compaction path as any other turn: if the context crossed the watermark the harness runs a notes-checkpoint turn and then `/compact`. Compacting before shutdown keeps a later cold start cheap instead of re-uploading a large transcript. **System messages** (from sender `system`): the broker delivers them as regular inbox messages (the same path as every other inbox message). A container crash never reaches an agent — it only produces a log line + swarm-level grafana alert, no agent turn at all (`docs/4-implementation/scheduler/observability.md`). Lifecycle transitions the job-queue scheduler or crash watcher drive directly — stop/kill, destroy, a flake-rev login or logout state change — reach no individual agent: they publish onto a swarm-wide NATS JetStream stream (`swarm_notices::notify`, `hive-c0re/src/swarm_notices.rs`); every hive is swarm-controlled. **Inbox** (`inbox` group): `get_loose_ends()`, `cancel_loose_end(kind, id)`, `remind(message, delay_seconds? | at_unix_timestamp?)`. - `get_loose_ends()` — list this agent's own open threads: pending reminders, open todos pushed by an in-container subsystem (matrix, bash, forge, or any user-configured MCP server), an undelivered-inbox count, and unread matrix notifications. Always the caller's own rows. Full per-variant wire shape in [`docs/5-project/process/conventions.md` § Loose-ends wire shape](../../5-project/process/conventions.md#loose-ends-wire-shape). - `cancel_loose_end` — hard-delete a `reminder` or clear a `todo` row (loose-ends-v2). Agents may only cancel rows they own. - `remind` — schedule a reminder in this agent's own inbox. Large payloads spill to `/agents//state/reminders/`. Pending count capped at 50 per agent (`HIVE_REMIND_MAX_PENDING_PER_AGENT`). No same-turn self-continue tool exists — see [Turn outcomes](README.md#turn-outcomes) for why. Multi-step work rides `remind` for a durable self-wake, or an in-container todo wake (bash-task completion, forge notification, matrix activity) for work already in flight. **Meta** (`meta` group): `get_agent_meta(name?)`. - `get_agent_meta` — fetch identity + status metadata for an agent: `{ name, hyperhive_rev, running, status_text, status_set_at, hive_name?, swarm_name?, matrix_accounts? }`. `matrix_accounts` is a list of matrix identities the agent can act as (`name`, `user_id?`, `homeserver`); omitted for agents with no matrix provisioning. Omit `name` to query self. **Always-on, no permission needed**: `set_status(text)`, `compact()`, `mark_todos_done(ids)`. - `set_status` — set a free-text status string visible on the dashboard. Single line, ≤ 200 chars. Persisted to `{state_dir}/hyperhive-status`. Pass `""` to clear. Not gated by the `meta` group — every agent needs to keep its dashboard status chip current regardless of which optional groups it holds. - `compact` — agent self-service equivalent of the operator dashboard's `/compact` button. No args. Gated server-side on the last completed turn's context usage: refused (with an explanation, no side effect) unless usage is above 66% of the effective context window. On a pass, queues the same deferred `compact_pending` flag the dashboard button sets — consumed at the end of the current turn, so it never races a live claude process, and the usual pre-compaction notes-checkpoint turn still fires first. Dispatched through the in-agent socket (`hive-agent-sock::Request::Compact`), not the broker — see `hive-agent/src/todo_server.rs`. - `mark_todos_done(ids)` — bulk-clear specific loose-ends-v2 todo rows by id in one call, instead of `cancel_loose_end`ing each one. List-based: pass the ids `get_loose_ends` actually showed you reviewed; unknown or already-done ids are silently skipped. Dials the same in-agent todo socket as `cancel_loose_end`'s `todo` kind. ## Privileged tools (by permission) - **Bash execution** (`execution`) — background shell tasks. See [`docs/4-implementation/agent-tools/bash.md`](../agent-tools/bash.md). - **Subagent spawning** — nested sessions on the agent's own runtime (claude or ACP, via `hive-runtime`) as background tasks, shipped default-on like bash execution; no permission gates it. See [`docs/4-implementation/agent-tools/subagent.md`](../agent-tools/subagent.md). - **Config changes** — no permission: they go through a forge PR on `agent-configs/` — see [`docs/2-handbook/agents/config-changes.md`](../../2-handbook/agents/config-changes.md). - **Web egress** (`web_tools`) — enables Claude's built-in `WebFetch` and `WebSearch` tools (not MCP tools; added directly to the `--allowedTools` list). Off by default. - **Recovery mounts** (`manage_root_agent`) — no tool: every agent's state and config mounted into this agent's container. See [`docs/5-project/process/conventions.md#permissions`](../../5-project/process/conventions.md). - **Matrix MCP + extra servers** — `mcp__matrix__*` tools and per-agent extra MCP config. See [`docs/4-implementation/agent-tools/matrix.md`](../agent-tools/matrix.md). ## Waking the agent from inside the container The built-in producers (matrix, bash, forge) upsert a todo on the harness's in-agent socket (`HIVE_AGENT_SOCKET`, `UpsertTodo` — see [`docs/4-implementation/agent-tools/bash.md` § Completion as a todo (loose-ends v2)](../agent-tools/bash.md#completion-as-a-todo-loose-ends-v2) for the full upsert/signal/clear mechanism): a new or changed summary makes the harness signal its own turn loop, with no hive-c0re round-trip. An external MCP server (or any other in-container process) can push its own todo the same way — `subsystem` is any string. The host-served per-agent socket at `/run/hive/mcp.sock` carries a lower-level `Wake` request (`hive_core_agent_sock::Request::Wake { from, body }` — JSON-line, `{"cmd":"wake","from":"matrix","body":"new dm from @alice"}\n`) that drops the body straight into the broker inbox as `{from: