hyperhive/docs/turn-loop/mcp.md
iris 78021ce982 docs: fix 329 Microsoft.Contractions hits with proper safety checks
Apply contraction fixes across ~40 doc files (setup, integrations, lifecycle,
networking, scheduler, swarm, tools, trust-boundary, UI, etc.). Skipped 14 hits:
- 10 where words appear in ALL CAPS for deliberate emphasis (is NOT, do NOT, etc.)
- 4 where text could not be safely located due to markdown formatting or column position

Applied via systematic scan with checks for fenced code blocks, inline code spans,
and intentional caps. Preserves sentence-initial capitalization throughout.
2026-09-07 16:28:06 +02:00

231 lines
12 KiB
Markdown

# MCP surface
The harness ships an embedded MCP server (rmcp 2). The built-in
`hyperhive` surface is served over streamable HTTP by a persistent
`hive-mcp-http` daemon (loopback, `127.0.0.1:<hyperhive.mcp.httpPort>`,
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 (`hyperhive.extraMcpServers`) pick their own transport per
entry (`type = "stdio" | "http"`, default `"stdio"`): `matrix` stays a
stdio bridge, `bash` runs its own persistent streamable-http listener
(`hive-bash-daemon`) — same reasoning as the built-in surface. The
server name is `hyperhive`, so the tools land in claude as
`mcp__hyperhive__<tool>`.
Tool access is gated by tool groups (`HIVE_TOOL_GROUPS`). The default
preset (`AGENT_DEFAULT`) includes `messaging`, `meta`, `inbox`, and
`execution`. Privileged groups (`lifecycle`, `approvals`, `scheduling`,
`diagnostics`) are opt-in via the P3RM1SS10NS tab.
## Core tools (always available)
**Messaging** (`messaging` group): `send(to, body, in_reply_to?)`,
`recv(max?)`, `ack_until(up_to)`.
- `send` — message a peer (logical name) or the operator
(`to: "operator"`). Use `to: "<parent>"` to address the topology
parent without hardcoding the label; the broker resolves the
sentinel at delivery time. Optional `in_reply_to: i64` links the
message to a prior id for thread rendering. Per-agent
`hyperhive.allowedRecipients` (default: empty = unrestricted) limits
which names `send` accepts — useful for sandboxing: set
`[ "operator" ]` to restrict a sub-agent to operator messages only
(the topology parent is always reachable regardless of this list —
that carve-out is structural, keyed on parent relationship, not name).
- `recv` — drain inbox. Always an immediate peek, never blocks. `max`
(default 1, cap 5) drains up to N rows. Each returned row is prefixed with
`[msg #<id>]` (broker row id; note the highest id seen, then pass
it to `ack_until` to bulk-triage the batch). **Graceful shutdown**: when the harness
receives a stop signal, the inbox becomes fenced and `recv` returns an
explicit `from: "graceful-stop"` message instead of an empty inbox.
This unmissably 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 huge transcript.
- `ack_until(up_to)` — bulk-mark inbox rows handled: every row with
broker id `<= up_to` is stamped as acked in a single UPDATE.
Recipient-scoped (agents can only ack their own rows). Use when a
restart redelivers a large backlog of already-handled messages: read
the highest `[msg #N]` from the set you've actually processed, then
`ack_until(N)` to prevent re-pop. Acked rows never redeliver.
Transient pings (sentinel id 0) have nothing to ack and show no marker.
**System messages** (from sender `system`): the higher-urgency
lifecycle events (`hive_sh4re::manager::HelperEvent`) are
delivered as regular inbox messages (same `recv` path; body is a JSON
object with an `event` discriminant field). The **submitting agent**
(the root agent for top-level containers; an agent with the `approvals`
tool group for its own subtree) receives `container_crash`,
`needs_update`, and `approval_resolved` this way. The remaining, lower-urgency lifecycle
notices — `spawned`, `rebuilt`, `killed`, `destroyed`, `needs_login`,
`logged_in`, `config_ready` — skip the inbox entirely: they land as
todos on the submitting agent's in-container todo socket instead
(`Coordinator::push_todo`/`push_todo_submitter`, `subsystem = "core"`),
which still wakes a turn (the todo-wake path — see [Turn
outcomes](README.md#turn-outcomes)) but via a generic "call
`get_loose_ends`" prompt rather than the event body itself. Full
payload shapes and routing logic in
[`docs/agent-lifecycle/approvals.md` § Helper events](../agent-lifecycle/approvals.md#helper-events-to-the-submitting-agent).
**Inbox** (`inbox` group): `get_loose_ends(agent?)`,
`cancel_loose_end(kind, id)`, `remind(message, delay_seconds? |
at_unix_timestamp?)`.
- `get_loose_ends(agent?)` — list scheduled reminders, pending
approvals you submitted, and active local tasks published by
external MCP daemons (e.g. running bash tasks from
`hive-bash-daemon`). Each row carries an id + kind for
`cancel_loose_end`. Omit `agent` to list your own threads. Pass
`agent: "<name>"` to inspect a direct child agent (always accessible
per topology enforcement); non-children require the
`query_agent_state` capability. The `"*"` hive-wide query isn't
available on the agent socket.
- `cancel_loose_end` — hard-delete a `reminder`, cancel a pending
`approval` row, or clear a `todo` row (loose-ends-v2). Agents may
only cancel rows they own; the `approval` kind is further restricted
to the root agent (`ruth`) server-side.
- `remind` — schedule a reminder in this agent's own inbox. Large
payloads spill to `/agents/<self>/state/reminders/`. Pending count
capped at 50 per agent (`HIVE_REMIND_MAX_PENDING_PER_AGENT`).
There is no same-turn self-continue tool — 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): `set_status(text)`, `get_agent_meta(name?)`.
- `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.
- `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 tool group** (like `set_status`): `compact()`.
- `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`.
## Privileged tools (by tool group)
- **Bash execution** (`execution`) — background shell tasks. See
[`docs/tools/bash.md`](../tools/bash.md).
- **Lifecycle + config** (`lifecycle`, `approvals`) — manage child
agents, spawn new ones, apply config commits. See
[`docs/tools/lifecycle.md`](../tools/lifecycle.md).
- **Scheduling + diagnostics** (`scheduling`, `diagnostics`) —
scheduled prompts, `get_logs`. See
[`docs/tools/scheduling.md`](../tools/scheduling.md).
- **Forge repos** (`forge`) — `create_repo` — the only agent path to
create a repo under the `agents/` org (direct forge token creation is
disabled for agents). The repo is created in the c0re-owned `agents`
org; the calling agent gets write collaborator access; the default
branch is branch-protected (operator-team must approve merges, so the
agent can't self-merge). Opt-in; not in any default preset.
See [`docs/tools/forge.md — Repo management`](../tools/forge.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; add the group in the
P3RM1SS10NS tab and rebuild to enable.
- **Capability-gated** — `get_host_journal` (requires
`read_host_journal` capability set via the P3RM1SS10NS tab;
orthogonal to tool groups). Full list of capabilities and their
effects in [`docs/process/conventions.md#capabilities`](../process/conventions.md).
Also documented in [`docs/tools/scheduling.md`](../tools/scheduling.md).
- **Matrix MCP + extra servers** — `mcp__matrix__*` tools and
per-agent extra MCP config. See
[`docs/tools/matrix.md`](../tools/matrix.md).
## Waking the agent from inside the container
External MCP servers (and any other in-container process) can
inject a wake-up event into the agent's inbox via the per-agent
socket at `/run/hive/mcp.sock`. Speak the wire protocol directly —
JSON-line over the unix socket: `{"cmd":"wake","from":"matrix","body":
"new dm from @alice"}\n`. Same shape as any other request on this
socket; see `hive_core_agent_sock::Request::Wake`. Every built-in producer that wakes
the harness (matrix, bash) dials the socket directly — there is no
CLI wrapper, just the raw protocol.
The wake event lands in the broker as `{from:<label>,
to:<agent>, body}`, waking whatever `recv` call the harness
is currently blocked on. The next turn fires with the wake
prompt formed from that message.
Identity = socket: anything that can connect to
`/run/hive/mcp.sock` is implicitly trusted to inject these —
the bind-mount is the agent's own container only.
## Authoritative state
`hive-agent`'s `events::Bus` carries the current turn-loop state in
addition to the broadcast channel and the events history. Variants:
- `Idle` — sitting on `Recv` waiting for mail.
- `Thinking``claude --print` is running for a turn.
- `Compacting` — operator-triggered `/compact` is in flight.
The harness flips state at the relevant transitions
(`set_state(Thinking)` before `drive_turn`, `set_state(Idle)`
after; `set_state(Compacting)` around an idle operator compact in
`turn::run_pending_compact`). Exposed via `/api/state.turn_state` +
`turn_state_since` (unix seconds); the agent page renders this rather
than deriving from SSE events.
## Tool envelope
`mcp::run_tool_envelope`: every MCP tool handler logs the request,
runs the body, logs the result. Pre-/post-log only — the inbox
status hint lives in the wake prompt + UI header, not here.
## Tool allowlist (`mcp_config::ALLOWED_BUILTIN_TOOLS`)
- Allowed built-ins: `Edit`, `Glob`, `Grep`, `Read`, `Skill`, `Write`.
`Skill` is what makes an installed plugin's `SKILL.md` invokable —
without it, a skill's `description` frontmatter never gets seen by
the model no matter how well it matches the task.
- Tool-group-gated built-ins: `WebFetch`, `WebSearch` (added when the
`web_tools` tool group is enabled — see P3RM1SS10NS tab).
- Denied by omission (absent from the harness `--tools` /
`--allowedTools`, so they "literally don't exist" in a harness turn):
`Bash`, `Task`, `NotebookEdit`, `TodoWrite`.
- Additionally in the managed-settings deny list
(`/etc/claude-code/managed-settings.json`, un-overridable): `Task`,
`TodoWrite`. `Bash` is **not** in the managed deny — see below.
- Allowed MCP tools: as listed above (by tool group).
`Bash` is disallowed for the autonomous harness — shell execution goes
through `mcp__bash__run` (background tasks with structured output +
task-id tracking) instead of an interactive shell. The harness gate is
`--tools` / `--allowedTools` (Bash absent from `ALLOWED_BUILTIN_TOOLS`),
so Bash never exists in a harness turn regardless of managed settings.
`Bash` is deliberately **not** in the managed-settings deny so that the
operator-driven `hivectl choom` session — which passes neither `--tools`
nor `--allowedTools` — gets claude's built-in synchronous `Bash` tool
(inline, human-approved). That sidesteps the async `mcp__bash__run`
completion wake landing in the wrong session (the harness inbox) for a
choom-started task (#2356); `choom` is an operator (root) action, so
built-in shell there stays within the existing trust boundary. The bash MCP server
(`run` / `status` / `kill`) uses `allowedTools = ["*"]` so all
`mcp__bash__*` tools are always available regardless of tool groups.
`WebFetch` / `WebSearch` are off by default; enable the `web_tools`
tool group in the P3RM1SS10NS tab and rebuild the agent to enable them.