# Turn loop + MCP How the harness wakes up, what it asks claude to do, and what tools claude has access to in return. ## The loop Each agent harness (`hive-ag3nt serve` or `hive-m1nd serve`) runs: 1. Long-poll `Recv` on its socket. The host-side broker (`broker.rs::recv_blocking`) returns immediately if there's a pending message, otherwise waits up to 30 s for a broker `Sent` event for this recipient. 2. Pop one message. Peek the remaining inbox depth with `Status`. 3. Emit `LiveEvent::TurnStart { from, body, unread }` onto the SSE bus. 4. Spawn claude (one process per turn) and pipe the wake prompt over stdin. 5. Stream stdout (JSON lines) into the bus as `LiveEvent::Stream(value)`. Pump stderr as `Note`. 6. Wait for claude to exit. On `Prompt is too long`, run `/compact` on the session once and retry the turn. 7. Emit `LiveEvent::TurnEnd { ok, note }`. Sleep `poll_ms` to avoid tight loops on transient failures. ## The claude invocation ``` claude --print --verbose --output-format stream-json --model haiku \ --continue --settings /run/hive/claude-settings.json \ --system-prompt-file /run/hive/claude-system-prompt.md \ --mcp-config /run/hive/claude-mcp-config.json --strict-mcp-config \ --tools --allowedTools # wake prompt piped over stdin ``` `--continue` keeps a persistent session per agent (claude stores sessions in `~/.claude/projects/`, which is bind-mounted persistently). Auto-compact and auto-memory are disabled via `--settings` because hyperhive owns compaction (`/compact` on overflow, retry once; operator can also force one via `/api/compact`). The wake prompt is intentionally minimal: just the popped message's `from`/`body`, plus an inline `({unread} more pending — drain via …)` hint when `unread > 0`. Claude drives any further `recv`/`send` itself via the embedded MCP server. ### On-boot files `hive_ag3nt::turn::write_*` writes three files next to the per-agent socket at `/run/hive/` once at startup: - `claude-mcp-config.json` — re-invokes the running binary as `mcp` child (so the same binary serves as harness + as claude's MCP child process). - `claude-settings.json` — the `--settings` blob (auto-compact and auto-memory off, effortLevel medium). - `claude-system-prompt.md` — rendered from `hive-ag3nt/prompts/{agent,manager}.md` with `{label}` substituted. Passed via `--system-prompt-file`. The shared per-turn plumbing lives in `hive_ag3nt::turn::{write_mcp_config, write_settings, write_system_prompt, run_turn, drive_turn, emit_turn_end, wait_for_login, compact_session}` so the two binaries can't drift. ## MCP surface The harness ships an embedded MCP server (rmcp 1.7). Claude launches it as a stdio child via `--mcp-config`. The hyperhive socket name is `hyperhive`, so the tools land in claude as `mcp__hyperhive__`. ### Sub-agent tools - `send(to, body)` — message a peer (logical agent name), another agent, or the operator (recipient `operator`, surfaces in the dashboard inbox). - `recv()` — drain one inbox message. ### Manager tools (in addition to send/recv) - `request_spawn(name)` — queue a Spawn approval for a brand-new sub-agent (≤9 char name). Operator approves on the dashboard. - `kill(name)` — graceful stop. No approval required. - `start(name)` — start a stopped sub-agent. No approval. - `restart(name)` — stop + start. No approval. - `request_apply_commit(agent, commit_ref)` — submit a config change for any agent (`hm1nd` for the manager's own config) for operator approval. - `ask_operator(question, options?, multi?)` — surface a question on the dashboard. Non-blocking — returns the queued question id; the operator's answer arrives later as `HelperEvent::OperatorAnswered` in the manager inbox. Options always render alongside a free-text fallback; `multi=true` renders options as checkboxes. The boundary: lifecycle ops on *existing* sub-agents (`kill`/`start`/`restart`) are at the manager's discretion — no operator approval. Creating a new agent (`request_spawn`) and changing any agent's config (`request_apply_commit`) still go through the approval queue. ### 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 moved to the wake prompt + UI header. ### Tool whitelist (`mcp::ALLOWED_BUILTIN_TOOLS`) - Allowed built-ins: `Bash`, `Edit`, `Glob`, `Grep`, `Read`, `TodoWrite`, `Write`. - Denied by omission: `WebFetch`, `WebSearch`, `Task`, `NotebookEdit`. - Allowed MCP tools: as listed above per flavor. `Bash` is on the allow-list pending a finer-grained pattern allow-list (`Bash(git *)`-style) — see [TODO](../TODO.md).