221 lines
12 KiB
Markdown
221 lines
12 KiB
Markdown
# MCP surface
|
|
|
|
The harness ships an embedded MCP server (rmcp 1.7). 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`, e.g. matrix/bash) stay stdio
|
|
bridges. 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(wait_seconds?, max?)`, `ask(question, options?, multi?,
|
|
ttl_seconds?, to?)`, `answer(id, answer)`, `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. Without `wait_seconds` (or `0`) returns
|
|
immediately. Positive value parks the turn up to that many seconds
|
|
(cap 180) — incoming messages wake instantly. `max` (default 1, cap
|
|
5) drains up to N rows; `wait_seconds` applies to the first, then
|
|
drains up to `max` total. 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.
|
|
- `ask` — surface a structured question to the operator (default) or
|
|
a peer agent (`to: "<agent>"`). Non-blocking — returns a question
|
|
id; the answer arrives as a `question_answered` system event in the
|
|
asker's inbox. `options` is advisory; `multi=true` renders as
|
|
checkboxes; `ttl_seconds` auto-cancels with answer `[expired]`.
|
|
- `answer` — respond to a `question_asked` event routed to this
|
|
agent. Strict authorisation: only the declared target can answer.
|
|
- `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`): lifecycle and Q&A events
|
|
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 lifecycle events (`spawned`,
|
|
`rebuilt`, `killed`, `destroyed`, `container_crash`, `needs_login`,
|
|
`logged_in`, `config_ready`, `needs_update`, `approval_resolved`). Any
|
|
agent receives Q&A events when it is the declared target
|
|
(`question_asked`) or the asker (`question_answered`). Full payload
|
|
shapes and routing logic in
|
|
[`docs/approvals.md` § Helper events](../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?)`, `request_next_turn()`.
|
|
|
|
- `get_loose_ends(agent?)` — list pending questions (asked/owed),
|
|
scheduled reminders, and active local tasks published by external MCP
|
|
daemons (e.g. running bash tasks from `hive-bash-mcp`). 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 is not available on the agent socket.
|
|
- `cancel_loose_end` — withdraw a `question` (posts `[cancelled by
|
|
<self>]`), hard-delete a `reminder`, or cancel a pending `approval`
|
|
row. 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`).
|
|
- `request_next_turn` — ask the harness to start another turn
|
|
immediately after this one ends, even if the inbox is empty.
|
|
Next turn fires with `from: "self"` and `body: "continue"`.
|
|
|
|
**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.
|
|
|
|
## 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 cannot 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/conventions.md#capabilities`](../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`. Two equivalent paths:
|
|
|
|
- **Shell out to `hive-agent-wake --from <label> --body <text>`**
|
|
(use `--body -` to read body from stdin). Already on the
|
|
container's `PATH` since the harness binary is in
|
|
`systemPackages`. Convenient for shell-script integrations and
|
|
co-process daemons (matrix bridge, webhook listeners, scrapers).
|
|
|
|
- **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 `AgentRequest`;
|
|
see `hive-sh4re::AgentRequest::Wake`.
|
|
|
|
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_ag3nt::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 moved to the wake prompt + UI header.
|
|
|
|
## Tool whitelist (`mcp_config::ALLOWED_BUILTIN_TOOLS`)
|
|
|
|
- Allowed built-ins: `Edit`, `Glob`, `Grep`, `Read`, `Write`.
|
|
- 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.
|
|
|