Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 53 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,59 @@ The in-app browser has a scriptable API ported from [agent-browser](https://gith

Everything is scriptable through the CLI and socket API — create workspaces/tabs, split panes, send keystrokes, open URLs in the browser.

## MCP server

cmux now includes an initial local MCP server for external agents that want to steer cmux programmatically instead of shelling out directly.

What it wraps today:
- `system.identify` and `tree` for discovery
- window, workspace, pane, and surface control through the existing `cmux` CLI
- terminal interaction via `read-screen`, `send`, and `send-key`
- a first-pass browser wrapper for common `cmux browser ...` actions

The server intentionally reuses the existing CLI and socket surface instead of introducing a second control plane, so MCP clients get the same handle model (`window:N`, `workspace:N`, `pane:N`, `surface:N`) and routing behavior that cmux already documents.

Install the Node dependencies once:

```bash
npm install
```

Run over stdio:

```bash
npm run cmux:mcp
```

Run over HTTP:

```bash
npm run cmux:mcp -- --transport http --host 127.0.0.1 --port 8765
```

Useful environment variables:

```bash
export CMUX_MCP_CMUX_BIN=/path/to/cmux
export CMUX_MCP_SOCKET_PATH=/tmp/cmux.sock
export CMUX_MCP_SOCKET_PASSWORD=...
export CMUX_MCP_ID_FORMAT=refs
```

Available MCP tools:
- `cmux_identify`
- `cmux_tree`
- `cmux_list`
- `cmux_control`
- `cmux_terminal`
- `cmux_browser`

Term mapping for MCP clients:
- `window` is a native macOS cmux window
- `workspace` is the sidebar tab-like container
- `pane` is a split region inside a workspace
- `surface` is a tab inside a pane, usually the most stable automation target
Comment on lines +110 to +161

Copilot AI Mar 8, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The PR description mentions the Python server (python scripts/cmux_mcp_server.py) as a runnable implementation, but the README (lines 110–161) only documents the JavaScript MCP server. The Python server has a different tool set (8 tools using the v2 socket API directly) compared to the JavaScript server (6 tools using the cmux CLI). Neither the README nor the docs explain when to use which server, or that both exist. This creates confusion for users. The README should either document the Python server as an alternative, or explain the relationship/difference between the two implementations.

Copilot uses AI. Check for mistakes.

## The Zen of cmux

cmux is not prescriptive about how developers hold their tools. It's a terminal and browser with a CLI, and the rest is up to you.
Expand Down
111 changes: 111 additions & 0 deletions docs/cmux-mcp-server.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
# cmux MCP Server Recommendation

Last updated: March 7, 2026

## Recommendation

Build a local MCP server for cmux, but keep it as a thin adapter over the existing v2 Unix socket API instead of adding a second control plane inside the app.

This is justified because cmux already exposes most of the primitives an MCP host would need:

1. `TerminalController` already implements a JSON v2 socket protocol with stable methods for `window.*`, `workspace.*`, `pane.*`, `surface.*`, and `browser.*` in [TerminalController.swift](../Sources/TerminalController.swift).
2. `system.tree` and `system.identify` already provide the discovery and self-location data an agent needs to reason about windows, workspaces, panes, and surfaces.
3. `TabManager`, `Workspace`, and `AppDelegate` already own the real mutations for selection, creation, moving, focusing, and cross-window routing, so an MCP server does not need to replicate that logic.

## Codebase Findings

### Existing control plane

1. `TerminalController` is already the automation boundary:
- capability advertisement via `v2Capabilities()`
- hierarchy discovery via `v2SystemTree()`
- window/workspace/surface/pane/browser dispatch in the `window.*`, `workspace.*`, `surface.*`, `pane.*`, and `browser.*` handlers
2. `AppDelegate` already exposes window-level lookup and mutation:
- `listMainWindowSummaries()`
- `tabManagerFor(windowId:)`
- `focusMainWindow(windowId:)`
- `createMainWindow()`
- `closeMainWindow(windowId:)`
3. `TabManager` already owns workspace lifecycle and selection:
- `addWorkspace()`
- `selectWorkspace(_:)`
- `reorderWorkspace(...)`
- workspace title/color/pin operations
4. `Workspace` already owns pane and surface topology:
- bonsplit pane state
- panel registry
- focused surface tracking
- terminal/browser surface metadata

### Design implication

The app already has a useful "cmux control kernel". The missing piece is a host-friendly MCP facade, not more app-side orchestration code.

## Proposed Architecture

Run the MCP server as an external local stdio process:

1. MCP host starts `scripts/cmux_mcp_server.py`.
2. The server connects to the configured cmux socket (`CMUX_SOCKET_PATH` or `--socket`).
3. MCP tools translate directly to v2 socket calls.
4. `TerminalController` remains the source of truth for capability routing and object identity.

Why external is better than embedding inside cmux:

1. MCP is a transport/protocol concern; cmux already has the stateful control API.
2. External stdio avoids mixing MCP lifecycle with AppKit lifecycle.
3. The same adapter can target different cmux sockets without changing the app.
4. The adapter can stay intentionally small and experimental while the socket API evolves.

## Initial Tool Set

Recommend exposing a narrow first pass instead of mirroring every socket method one-for-one.

### Discovery

1. `cmux_socket_discover`
- Find candidate local cmux sockets under `/tmp`.
2. `cmux_system_tree`
- Return windows, workspaces, panes, surfaces, and the active path.

### Workspace steering

1. `cmux_list_workspaces`
2. `cmux_create_workspace`
3. `cmux_select_workspace`

### Surface steering

1. `cmux_list_surfaces`
2. `cmux_send_text`
3. `cmux_read_text`

### Escape hatch

1. `cmux_socket_call`
- Raw pass-through to an existing v2 socket method.
- Useful while validating which abstractions should become first-class MCP tools.

## What Not To Build Yet

1. Do not duplicate the entire `window.*` / `workspace.*` / `surface.*` / `browser.*` matrix as separate MCP tools yet.
2. Do not add a second IPC path inside cmux just for MCP.
3. Do not invent new object IDs; reuse cmux UUID/ref handles from the socket API.
4. Do not let the MCP layer bypass `TerminalController` focus rules or socket auth/access mode.

## Risks and Guardrails

1. Focus-stealing remains an app concern. The MCP adapter should respect the focus policies already enforced by `TerminalController`.
2. Socket auth mode matters. If the socket is password-protected or disabled, MCP should fail clearly instead of trying to bypass it.
3. Tool count can explode quickly. Keep MCP tools task-shaped and leave uncommon operations behind `cmux_socket_call` until repeated usage proves they deserve dedicated schemas.

## Recommendation Summary

Yes, build the MCP server.

But build it as:

1. an external stdio adapter
2. backed by the existing v2 socket API
3. with a small, high-value tool surface first
4. and a raw-call escape hatch while the tool taxonomy settles
Loading