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

12 KiB

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) 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.

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 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.
  • Lifecycle + config (lifecycle, approvals) — manage child agents, spawn new ones, apply config commits. See docs/tools/lifecycle.md.
  • Scheduling + diagnostics (scheduling, diagnostics) — scheduled prompts, get_logs. See docs/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.
  • 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-gatedget_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. Also documented in docs/tools/scheduling.md.
  • Matrix MCP + extra serversmcp__matrix__* tools and per-agent extra MCP config. See docs/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.
  • Thinkingclaude --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.