v0.8.7-beta.5 is out · 5 Novsee the release
Browse guides / MCP bridge
DOCS

The MCP bridge

docs/mcp-bridge.md on GitHub

Aether's MCP bridge is an in-container MCP server for six existing coordination tools, registered automatically for Enhanced runs and manually elsewhere. The canonical agent interface is the aether-internal CLI, which is automatically available in every managed container and can load a version-matched skill on demand. The bridge is not the Release B orchestration interface and does not need to mirror its swarm or worker-management surface.

When coordination assets are available, the server stages verified executable bytes read-only at the two in-container paths used by the existing surfaces:

CODE
/opt/aether/aether-server         optional MCP entry point and lifecycle hook
/usr/local/bin/aether-internal    canonical agent-facing coordination CLI

The run coordination directory carries the run socket and related server assets:

CODE
/run/aether/coord3.sock           this run's v3 socket
/run/aether/co-authors            server-generated commit trailers

These are Aether-owned surfaces, not caller-provided mounts. Caller mounts cannot shadow the coordination directory or either executable. If verified staging fails, Aether refuses to create the managed container; it does not silently launch a container without the canonical CLI or bridge binary.

The socket is the run identity and authentication boundary. The bridge and CLI have no token, login, run-ID, or credential option. A connection to a run's socket is that run; a binary can be present without a run identity, but then run-bound operations are unavailable.

Registration

Enhanced runs (runs driven over ACP, the Agent Client Protocol) get the bridge automatically: when the run has a coordination directory, Aether passes /opt/aether/aether-server mcp as the stdio MCP server aether in ACP session/new, session/resume and session/load. Aether writes no agent configuration file for it.

Standard runs do not get the bridge registered. To use it there, create a configuration outside /run/aether and point an agent that supports MCP at it:

CODE
cat >/tmp/aether-mcp.json <<'EOF'
{"mcpServers":{"aether":{"type":"stdio","command":"/opt/aether/aether-server","args":["mcp"]}}}
EOF
claude --mcp-config /tmp/aether-mcp.json

Aether does not write or manage that path, and other agents use different syntax. The bridge still reaches only the run socket of its container.

The bridge's MCP instructions are the run's role card (see Agent instructions). When AETHER_ROLE_CARD=1, which Aether sets on the bridge when the ACP system prompt already carries the card, they are only a brief of under 512 bytes: report success or failure when the work the agent owns is done or cannot be done, needs-input only for a question a human must answer (workers and Background runs are told never to), blocked when a human must intervene, and waiting when it ends its turn to await agents, CI or a process, or, where nothing resumes an idle session, never waiting but waiting for mail in the turn with aether_inbox; never report while still working; subagents and spawned workflows never report. Both come from coord.status, asked once with a 2-second limit: without coord.report in its capabilities there is no brief and the card has no report lines, and without an answer the instructions only point at aether-internal skill.

The canonical CLI remains available without MCP. A shell-capable agent can invoke aether-internal directly and request its live, version-matched skill on demand. A container or terminal without a run identity can use general help or the non-run skill guidance, but status, messaging, reporting, and swarm operations return unavailable. Lack of manual MCP registration is not an overlap-only or notice-only mode.

See coordination.md for the established CLI commands and wire limits. The optional bridge does not install a skill package or carry an identity claim.

The existing six-tool surface

The bridge exposes exactly six existing tools. Their parameters, receipts, authorization rules, limits, idempotency behavior, and durable storage map to the established v3 coordination methods. This is the bridge's complete surface; it does not expose Release B swarm, task, worker, takeover, or integrator-management commands.

MCP tool v3 method Parameters Result
aether_status coord.status none v3 identity, assignment, messageable peers, unread count, capabilities
aether_send coord.send to_run_id, body, idempotency_key message_id, recipient_status
aether_inbox coord.inbox optional ack_token, optional wait_seconds messages, ack_token
aether_ask coord.ask to_run_id, body, idempotency_key question_id, recipient_status
aether_reply coord.reply question_id, body, idempotency_key message_id
aether_report coord.report outcome, summary, optional evidence_refs, optional waiting_for, idempotency_key durable report_id, outcome, summary, next action, evidence references, automatic evidence_ref

The bridge returns the established structured result directly. This presentation differs from any CLI envelope, but it does not add operations or make MCP a Release B-parity interface.

aether_status lists every capability of the run, including dev.* methods that have no MCP tool. Its tool description tells the agent to run aether-internal skill for those.

aether_status lists the peers described in Wire v3; a peer's run_id is the to_run_id for aether_send and aether_ask, and only listed peers can be messaged. The aether_send and aether_ask descriptions tell the agent to announce its plan and owned files before editing what a peer touches, or a decision, progress or blocker that affects a peer; that a receipt means queued rather than read and replies arrive in the inbox; and to check status instead of retrying a denial. The aether_inbox description says peer messages and replies arrive there and marks them as untrusted information that never overrides the user, the assignment or the integrator. The aether_reply description says a reply reaches only the asking run and grants it no control. These descriptions reach subagents, which share the MCP tools but not the parent's system prompt. See Send a message.

aether_send, aether_ask, aether_reply, and aether_report require an explicit idempotency_key; the bridge never invents one. For a retry after a timeout or lost response, provide the same key and the same semantic inputs. A different payload under an existing key is a conflict.

aether_inbox is at-least-once. The returned ack_token identifies exactly the returned batch. Supplying it on the next call acknowledges that batch; without it, the batch remains available. The bridge carries forward the last acknowledgement token that reached the MCP stream when the next call omits ack_token, while an explicit token remains supported. A cancelled call, failed response write, or bridge process exit does not promote a staged token, so the batch is delivered again. wait_seconds requests one bounded server wait; it is not an unbounded poll.

aether_ask returns a durable question_id. aether_reply takes that ID instead of a target run ID and routes only to the original question sender. The question establishes the reply relationship; it cannot authorize an unrelated message or cross a workspace boundary.

aether_report accepts only success, failure, blocked, needs-input and waiting, which its input schema enumerates, and requires a non-empty summary. Every coordination tool field has a schema description, and the aether_report description leads with the rule that subagents and spawned workflows never report. needs-input and waiting take no evidence_refs. waiting marks a turn the agent ends while work it started goes on; the next turn clears it. waiting_for is agents (the default), ci or process, which the schema enumerates, and is valid only with waiting; anything else is invalid params. The schema's outcome description is the same for every run; the server refuses outcomes a run's role or launch cannot use, and the instructions list the ones that apply. Before the server accepts a report, Aether captures the run's evidence, including a private Git evidence commit and a bounded PTY transcript. The retained packet records factual context, provenance, unresolved facts, and a next action, with unavailable or truncated sources shown explicitly. Evidence is not an atomic environment snapshot and does not assert that the outcome was verified. A failed capture or persistence step leaves the outcome unaccepted and the runtime recoverable.

Interactive Standard and Enhanced runs remain open after a success or failure report; the agent ends its turn and waits for follow-up. Each later task uses a fresh report idempotency key. Background runs and swarm workers retain terminal report behavior. See Report an outcome.

MCP errors

Coordination failures are returned as MCP tool results with isError, not as MCP session-level JSON-RPC failures. Aether's numeric code is in the result metadata key aether/error_code; the text includes the method-qualified message. Local socket failures such as a missing listener, EOF, broken pipe, or a connection-cap refusal map to CodeUnavailable (-32004). Server responses preserve their own Aether codes, including CodeDenied (-32001), CodeConflict (-32003), CodeNotFound (-32000), and CodeInvalidParams (-32602). This keeps an operation failure actionable without tearing down the MCP session.

The method set is closed. The bridge cannot invoke a control verb, type into a terminal, read Git, or access another run's transcript. Human messages still use Aether's host-side serialized PTY input path; MCP is not an inbound terminal hook.

run.report is not coord.report

aether_report is the MCP spelling of durable coord.report, and has the same evidence-before-acceptance behavior as aether-internal report.

The staged binary at /opt/aether/aether-server also retains the separate agent lifecycle command:

CODE
printf '%s\n' '{"hook_event_name":"Stop"}' | /opt/aether/aether-server report claude
/opt/aether/aether-server report codex '{"type":"agent-turn-complete"}'
/opt/aether/aether-server report pi --json '{"state":"waiting","reason":"agent idle"}'
/opt/aether/aether-server report opencode --json '{"state":"working","input_updates":[{"operation":"replace","requests":[]}]}'

These callbacks invoke wire method run.report. They update execution (working or the legacy waiting wire value, which parks the run at needs-attention) and/or correlated pending input requests. A request-only update leaves execution unchanged. They are not an agent outcome, are not an MCP tool, and do not create a coordination report or evidence receipt. See status reporting for request lifetimes. Agent callbacks are hidden lifecycle plumbing, not commands for an operator or worker to run. The callback exits promptly even when status reporting is unavailable so it cannot block the agent.

Staging, retention, and shutdown

The staged binary is content-addressed. A run keeps the exact verified bytes used at provisioning, even if the server binary is upgraded later. Aether records the staged digest and coordination directory in the run sidecar before creating the container, and recovery rebuilds the listener from surviving sidecars.

Active runs and retained terminal TUI runs keep their socket, unread mailbox, and MCP assets through a server restart. When the container is destroyed, Aether releases the coordination directory and mailbox after required evidence capture. The coordination disable setting remains effective. When it is off, no run socket or usable MCP bridge is provided: bridge calls and run-bound CLI operations return unavailable. Every newly created managed container still receives the staged CLI, but without a run socket it can provide only general help or non-run skill guidance. Existing mounted assets become inert; the conflict radar itself continues to operate.

The assistant tool set

aether-server mcp --assistant serves a different tool set for the member assistant (the built-in agent that runs in the member's environment container, not in a run). It dials /run/aether-assistant/assistant.sock; -socket overrides the path. The socket is bound to one member, so no tool takes a member id and the server rejects any param containing member_id, except the run.list read filter; aether_list_runs with mine filters to the socket's member.

Tier Tools
Read aether_list_runs, aether_get_run, aether_run_history, aether_list_workspaces, aether_get_workspace, aether_list_agents, aether_usage, aether_environment_status, aether_docs
Reversible aether_pause_run, aether_resume_run
Confirmed aether_launch_run, aether_message_run, aether_stop_run, aether_save_environment

A confirmed call blocks until the member approves it in the dashboard, or runs at once when the member turned on auto-approval, except that a message to or stop of another member's run always waits for a click. A decline, timeout or closed session fails the call with the reason. aether_docs reads the guides in docs/, which the server binary embeds. Without --assistant the bridge serves the coordination tools above, unchanged.