# Bash execution tools Background shell execution via `hive-bash-mcp`. Tools land as `mcp__bash__` (the MCP server name is `bash`, not `hyperhive`). Available on every agent unconditionally — `nix/agent-modules/mcp.nix` always injects bash into `hyperhive.extraMcpServers` (with `allowedTools = ["*"]`), so `mcp__bash__*` is in `--allowedTools` for every claude invocation regardless of tool groups. ## Tools ### `run(cmd, timeout_secs?, wait_seconds?, name?)` Submit a shell command for background execution (runs via `bash`). Stdout and stderr stream to `harness/bash-tasks/.{out,err}`. When the task completes (or times out, or the process errors), it surfaces as a todo in the agent's loose-ends (via `get_loose_ends`), carrying the exit code and a `Read()` pointer to the captured output. Handle it on a future turn — unless `wait_seconds` already delivered the terminal result inline, in which case no todo is created (see `status` below). * `timeout_secs` — kill the task after N seconds and mark it `timed_out`. Omit for no timeout (runs until natural exit). * `wait_seconds` — inline poll before returning (capped at 30). When the task finishes within the window the full status is returned immediately and no todo is created; when the window expires the task keeps running and the normal `task started: id=` response is returned. **Defaults to 3** — pass `wait_seconds: 0` to disable inline waiting and always get the immediate response. * `name` — optional caller-chosen task id. When set it replaces the auto-generated hex id, so it surfaces in `status()` lookups and the loose-ends list — a memorable label instead of an opaque id. A name is **reusable once its previous task has finished**; submitting a name whose task is still `pending`/`running` is rejected. Allowed characters: `[a-z0-9-]` (a valid identifier — lowercase, digits, hyphen; max 63). Omit for the auto-generated id. Exposed as `mcp__bash__run`. ### `status(id, wait_seconds?)` Poll the status of a task submitted with `run`. Returns: - `status` — `pending` / `running` / `done` / `timed_out` / `interrupted` / `killed` - `exit_code` — set when done - run duration - last 4 KiB of stdout and stderr (full output in the `.out` / `.err` files) `wait_seconds` — optional inline poll (capped at 30): when the task finishes within the window the full status is returned immediately. Useful to avoid a separate round-trip when the task is expected to finish soon. Any `status` call (waited or not) that observes a terminal task clears that task's completion todo — you already have the result in this response, so no redundant loose-end follows. Narrow best-effort race: a `status`/`run` inline wait that resolves in the same instant the task actually finishes can still occasionally get both. Tasks marked `interrupted` had their process killed by a harness restart; a best-effort todo is still surfaced so the agent is not silently blocked. Exposed as `mcp__bash__status`. ### `kill(id, force?)` Stop a running or pending task by its ID (from `run`). Fire-and-forget: sends the signal and returns without waiting — the completion surfaces in the agent's loose-ends; handle it on a future turn. - `force: false` (default) — SIGINT to the task's **process group** (graceful; lets the process clean up). The whole process group is signalled, so children spawned by the shell (cargo, nix, etc.) are also stopped. - `force: true` — SIGKILL. If a SIGINT'd task doesn't exit, call `kill` again with `force: true`. A still-pending task is cancelled before it starts. The task ends as `killed` and surfaces in the loose-ends like any completion. Exposed as `mcp__bash__kill`. ## Namespace note `run` and `status` live in the `bash` MCP server, not `hyperhive`. So the tool names in claude are `mcp__bash__run` and `mcp__bash__status`. The `Bash` built-in tool is blocked — all shell execution goes through this structured path so tasks get task-id tracking and structured output. ## Architecture The bash tooling follows the same daemon + stdio-bridge pattern as the matrix MCP: - **`hive-bash-daemon`** — long-running process (one per agent container, systemd service in `nix/agent-modules/mcp.nix`). Owns subprocess management, output file writing, and todo delivery on the harness's in-agent socket. Listens on `/run/hive-bash/socket` inside the container. - **`hive-bash-mcp`** — stdio bridge spawned by claude per turn (declared in `hyperhive.extraMcpServers.bash`). Connects to the daemon socket and forwards `run` / `status` tool calls. Has no subprocess management logic of its own. This split keeps claude's turn-local MCP bridge lightweight while the daemon tracks long-running tasks that outlive a single turn. ### Completion as a todo (loose-ends v2) When a bash task changes state, `hive-bash-daemon` upserts a single keyed todo (`key = task id`) on the harness's in-agent socket (`HIVE_AGENT_SOCKET`) — "running" at start, then the completion summary when it finishes. The summary change signals the harness turn loop directly (in-process, no broker round-trip), so the agent is driven a turn to handle it via `get_loose_ends`, then clears the todo with `cancel_loose_end(kind: "todo", id: N)` (dials the in-container socket directly — no bash task involved, so clearing doesn't spawn another todo; see #2639). Same mechanism the matrix daemon uses for unread rooms. An inline `wait_seconds` / `status` observation that already delivered the result instead clears the keyed todo, so no redundant loose-end follows. ## Relationship to the `execution` tool group `ToolGroup::Execution` exists and appears in `AGENT_DEFAULT`, but its `tools()` returns `["run", "status"]` which the harness expands to `mcp__hyperhive__run` / `mcp__hyperhive__status` — tools that don't exist in the hyperhive MCP server (dead entries). Removing `execution` from an agent's groups has no effect on bash availability. Bash is registered separately via the `extraMcpServers` path described above.