cmuxlayer
# cmuxLayer
cmuxLayer exposes a 10-tool public MCP surface for controlling cmux terminal workspaces and managing CLI agents.
<p align="center">
<img src="./assets/cmuxlayer-logo-split-pane-grid.svg" alt="cmuxLayer" width="96" height="96" />
</p>
[](#quick-start)
[](LICENSE)
[](https://modelcontextprotocol.io)
[](#testing)
## Quick start
```bash
brew install etanhey/layers/cmuxlayer # stable, pinned release
brew install --HEAD etanhey/layers/cmuxlayer # or: dogfood the latest main
```
This installs the `cmuxlayer` command plus `cmuxlayer-app-server` and
`cmuxlayer-proxy`. [cmux](https://github.com/manaflow-ai/cmux) must be running.
For fleet wiring, versions, dogfooding, and the `CMUX_SOCKET_PATH` pin, see
[docs/guides/releases-and-brew.md](docs/guides/releases-and-brew.md).
Then set up this machine:
```bash
cmuxlayer init
```
The wizard selects spawnable repositories, per-repo launchers or direct CLI
launches, and approval behavior. It writes `~/.config/cmuxlayer/env.sh` and, in
launcher mode, a launcher registry. cmuxlayer reads both at startup, including
when an MCP client starts it from a GUI. The wizard asks before replacing a file
and creates a backup first.
For scripted installs, pass `--yes` with `--repo <name>=<path>`. cmuxlayer does
not assume a fixed repository layout. See
[docs/guides/fresh-install.md](docs/guides/fresh-install.md) for the walkthrough and
[docs/guides/registry-optional-spawn.md](docs/guides/registry-optional-spawn.md) for how each
lane behaves.
## Raise the open-files limit for agent CLIs
Agent CLIs open many files, while macOS login shells can start with a low soft
open-files limit. If you use repoGolem launchers, put this POSIX shell snippet
in a `global.prelaunch` entry so it runs before each agent CLI. You can also
put it in your shell rc file. It raises a low soft limit up to the hard limit,
capped at 65536, and never lowers an existing soft limit.
```sh
cmux_nf_s=$(ulimit -Sn); cmux_nf_h=$(ulimit -Hn); [ "$cmux_nf_s" = unlimited ] || [ "$cmux_nf_s" -ge 65536 ] || { [ "$cmux_nf_h" = unlimited ] && cmux_nf_h=65536; [ "$cmux_nf_h" -gt 65536 ] && cmux_nf_h=65536; ulimit -Sn "$cmux_nf_h"; }
```
Add to your MCP config:
**Codex CLI / T3 Code**
T3 Code inherits MCP servers from the Codex CLI config file at `~/.codex/config.toml` (or `$CODEX_HOME/config.toml`).
```toml
[mcp_servers.cmuxlayer]
command = "cmuxlayer"
env_vars = ["CMUX_SURFACE_ID", "CMUX_WORKSPACE_ID", "CMUX_TAB_ID", "CMUX_SOCKET_CAPABILITY", "CMUX_SOCKET_PATH"]
```
`env_vars` forwards the pane's existing values into Codex's MCP process. Do not paste a capability value into this file.
**Claude Code, Cursor, VS Code, Claude Desktop**
```json
{
"mcpServers": {
"cmuxlayer": {
"command": "cmuxlayer"
}
}
}
```
To keep only a per-session resident subset of tools, set
`CMUXLAYER_DEFAULT_PALETTE` to comma-separated bare tool names, for example
`list_surfaces,spawn_agent,send_to`. The server also exposes `expand_palette`,
which registers the rest of the 10 public tools for the rest of that MCP
session.
When unset or blank, the signed 10-tool thin-core default applies. When set, the
environment value overrides that default for the session. Unknown names are
warned and ignored while valid names still load.
cmuxlayer never answers a prompt chooser on an agent's behalf. It detects the
chooser, marks the agent `blocked_on_prompt`, and escalates without sending a
key.
`CMUXLAYER_FILE_DELIVERY_TICKETS=1` opts into public delivery-failure auto-filing with allowlisted bodies; full evidence stays in local tickets by default.
> **Config locations:** Codex CLI / T3 Code `~/.codex/config.toml` (or `$CODEX_HOME/config.toml`) | Claude Code `.mcp.json` or `claude mcp add cmuxlayer -s user -- cmuxlayer` | Cursor `.cursor/mcp.json` | VS Code `.vscode/mcp.json` | Claude Desktop — see [MCP docs](https://modelcontextprotocol.io/quickstart/user) for platform-specific paths
## Drive panes through the MCP, not the raw `cmux` CLI
Use cmuxlayer's MCP tools for pane operations. Calling the raw `cmux` CLI yourself bypasses stable-UUID guards, draft ownership, delivery receipts, tailer reaping, and placement. After a cmux restart, reconnect cmuxlayer before any pane operation: run `/mcp reconnect cmuxlayer` in Claude Code, restart Codex CLI or T3 Code, or use **MCP: List Servers → Restart Server** in VS Code. Use the equivalent MCP reconnect control in other clients.
Bare terminal creation through the production server requires an available observer. The handler checks the creation observer and any returned surface UUID before and after runtime initialization, at temporary focus and cleanup boundaries, and through its restoration lease. If those checks detect lost identity, cmuxlayer refuses stale refs and skips unsafe cleanup; the original initialization error remains visible. These handler checks do not provide atomic ownership across a connector's internal awaits, self-healing retries, or an in-flight RPC. Unsupported runtime metadata still permits temporary focus when ownership is stable. Explicit `focus:false` keeps initialization in the background, and a user focus change prevents restoration.
## What you can do
Tell your AI agent things like:
- *"Run my test suite in the pane to the right"*
- *"Spawn a Claude Code agent in a new pane to refactor auth.ts"*
- *"Read the screen of surface:2 and tell me if the build passed"*
- *"Wait for all agents to finish, then read their output"*
By default cmuxLayer registers exactly 10 tools, and all 10 are callable through MCP; there are no hidden internal tool definitions. `read_screen` parses agent metadata (status, model, tokens, context %) for Claude Code, Codex, Gemini, and Cursor.
## Agent routing workflow
For managed agents, use the agent-first path: `list_agents` to find the target, `send_to` to deliver work by `agent_id`, then `wait_for` when you need completion. `send_to` also preserves the registry-independent escape hatch: use `mode:"surface"`, `mode:"command"`, or `mode:"key"` with a raw surface ref for shells, launch/resume commands, and stuck-pane recovery.
Busy Codex messages use Return to steer at the next tool boundary. Pass `codex_busy_mode:"queue"` to `send_to` only for deliberate Tab delivery after the turn. `steer_pending` and `queued` both mean `delivered:false`; use `wait_for({delivery_id})` for confirmation. After 10 minutes, unresolved pending deliveries raise `needs_attention` and a parent alert without interruption or replay. Set `CMUXLAYER_DELIVERY_ATTENTION_MS` to a positive integer in milliseconds to change that threshold.
See [Agent Routing and Handling Workflow](docs/guides/agent-routing-and-handling.md) for the full operator playbook, including stuck surface recovery and safe `/mcp` menu reconnects.
## MCP tools (10 registered and callable)
All public tools include [ToolAnnotations](https://modelcontextprotocol.io/specification/2025-03-26/server/tools#annotations) that clients can use in safety policy.
**Public MCP surface** — `spawn_agent` `report_to_parent` `send_to` `read_screen` `list_agents` `wait_for` `control_health` `close_surface` `update_surface` `list_surfaces`
| Tool | What it does |
|------|-------------|
| `spawn_agent` | Spawn or resume; default receipt: `ok`, `agent_id`, `surface_id`, `state`, `delivered`, optional `warning` (at most six fields). Use `verbose:true` for contract and diagnostics; failures keep full detail |
| `report_to_parent` | Raise a short blocker to the managed agent's registry parent |
| `send_to` | Send by agent ID or raw surface using `mode:"agent"\|"surface"\|"command"\|"key"` |
| `read_screen` | Read terminal output with parsed agent status |
| `list_agents` | All agents, with optional filters |
| `wait_for` | Wait for one `agent_id` or several `ids` (defaults to `done`) |
| `control_health` | Report socket, binary, process, and job-control diagnostics |
| `close_surface` | Close one surface, managed agent, or workspace, with live-agent guards |
| `update_surface` | Move or rename a surface; explicitly adopt an existing managed session with pinned evidence |
| `list_surfaces` | List all surfaces across workspaces |
`update_surface` with `action: "adopt_session"` supports Claude and Codex after a restart, including
retained old bindings. Legacy rows require a known registry role and exact-session hook/process proof;
missing authority and placement use the same role aliases as spawn, and process cwd supplies missing
launch cwd. Legacy recovery records `surface_provenance: "session_registration"` and a one-line audit.
The old UUID must be absent and the new UUID uniquely live; titles never grant recovery authority.
Adoption requires the current stable surface and workspace UUIDs,
`managed_agent_id`, the same recorded `session_id`, `expected_agent_version`, an `observer_transition`
with explicit `historical_owner_id` and `current_owner_id`, and an absolute `binding_evidence_path`
plus its `binding_evidence_sha256`. The selected historical witness must establish the original
binding and explicit authority. Fresh complete topology, exact hook registration, qualified PID,
and direct process cwd must agree; ambiguous or competing known claimants refuse adoption.
Authority and coordination fields remain those of the original managed record. Adoption sends no
input and moves no panes; normal layout reconciliation follows separately. An interrupted write
returns `pending_verify`; an unchanged exact repeat revalidates binding, process, observer and routing
before completing persistence. A lifecycle-invalidated binding requires a fresh explicitly versioned
request. A public
`action: "move"` alone does not write a placement override or recover managed session identity.
`control_health` reports `cmux_fds` for detected cmux.app processes and warns when open descriptors reach 4096; set `CMUXLAYER_CMUX_FD_WARN` to a positive integer to change that threshold.
These 10 are the whole surface by default: setting `CMUXLAYER_DEFAULT_PALETTE` restricts the initially registered tools to the ones it names and adds `expand_palette`, which registers the rest on demand.
## Supported agents
| CLI | Command | Auto-detected |
|-----|---------|---------------|
| Claude Code | `claude` | status, model, tokens, context % |
| Codex | `codex` | status, model, context % |
| Gemini CLI | `gemini` | status, model, tokens, context % |
| Cursor | `cursor agent` | status, model, tokens, context % |
| Kiro CLI | `kiro-cli` | spawn and lifecycle only; no Kiro-specific screen parser |
`read_screen` auto-detects agent type and parses metadata from terminal output.
For launch and resume forms, input limits, and the ready/working/done markers per
CLI, see the [CLI reference](docs/reference/cli-reference.md).
## Architecture
```text
AI Agent ─── MCP ───> cmuxLayer ─── Unix socket ───> cmux
├── Agent engine (spawn → monitor → teardown)
├── Screen parser (Claude Code, Codex, Gemini, Cursor)
├── Mode policy (autonomous vs manual)
├── State manager + event log
├── Metacomm READ — harness JSONL (real tokens/context/model)
└── Metacomm WRITE — per-agent inbox file + Monitor dispatch
```
The socket client connects to cmux through a persistent Unix socket instead of starting a `cmux` CLI subprocess per call. It reconnects after a disconnect and falls back to the CLI subprocess when the socket is unavailable.
## Troubleshooting
**cmux is not running**
cmuxLayer requires a running [cmux](https://github.com/manaflow-ai/cmux) instance. Install it first, then start a cmux session before using cmuxLayer.
**Tools not appearing in Codex CLI or T3 Code**
Restart the client after adding `cmuxlayer` to `~/.codex/config.toml`. If you use a custom Codex home, verify `$CODEX_HOME/config.toml` contains the same `mcp_servers.cmuxlayer` entry.
**Tools not appearing in Claude Code**
Restart Claude Code after adding the MCP config. Run `claude mcp list` to verify cmuxlayer is connected.
**Socket connection failed**
cmuxLayer auto-discovers the cmux socket (macOS: `~/Library/Application Support/cmux/cmux.sock`). Override with `CMUX_SOCKET_PATH` if needed.
**"Cannot resolve a working directory for repo ..."**
cmuxLayer could not find that checkout. Run `cmuxlayer init` to register it, or
set `CMUXLAYER_REPO_HOME` to the colon-separated directories holding your
repositories. The error lists every path it searched.
## Testing
```bash
bun run test # vitest; 4452 tests collected by `vitest list`
bun run typecheck # Type checking
```
## Git hooks
Enable project hooks to run the regression gate automatically on `git push`:
```bash
git config core.hooksPath .githooks
```
This enables `.githooks/pre-push`, which runs `scripts/run_tests.sh` and blocks pushes on regression failures.
## Development
```bash
bun install
bun run dev # Run with tsx (hot reload)
bun run build # Compile TypeScript
bun run start # Run compiled output
```
## Contributing
See [CONTRIBUTING.md](CONTRIBUTING.md) for development setup and PR guidelines.
## License
Apache 2.0 — see [LICENSE](LICENSE).
---
Part of the [Golems](https://github.com/EtanHey/golems) AI agent ecosystem. [cmuxlayer.etanheyman.com](https://cmuxlayer.etanheyman.com) | Built by [@EtanHey](https://github.com/EtanHey).
TDQS
Scored across 10 tools
Each tool has a reasonably distinct purpose: spawn, send, list, close, read, wait, health, report. There is mild overlap between list_agents and list_surfaces (agents live on surfaces) and among the surface-touching tools (spawn_agent, update_surface, close_surface), but descriptions clarify the boundaries.
Nearly all names are snake_case with a verb_noun or verb_prep pattern (control_health, spawn_agent, list_agents, update_surface, close_surface, list_surfaces, read_screen). Minor deviations are the prepositional forms send_to, wait_for, and report_to_parent, which are still readable and consistent in style.
Ten tools is well within the ideal range and each covers a distinct facet of agent/surface orchestration. No redundant or filler tools appear; the set feels tightly scoped to the domain.
The surface covers the full lifecycle: spawn/resume, list, send, read, wait, update/adopt, close, report, and health. Coverage is strong, with only minor gaps such as no dedicated agent-state update beyond update_surface's adoption path.