MCP Server
Use the ccmux MCP server to let a coding agent inspect sessions, read project notes, and check usage. Session creation, keystrokes, and termination are available only when you enable mutation.
The server binary is included with ccmux v0.3.0 and later. Install ccmux and make sure its daemon is running before connecting a client.
Wire it up
For Claude Code, let ccmux register it for your user account:
ccmux mcp register # read-only tools
ccmux mcp register --allow-mutate # also spawn, send keys, kill
ccmux mcp status
ccmux mcp unregister
This runs claude mcp add-json --scope user when the claude CLI is on PATH. Without it, ccmux edits ~/.claude.json directly and writes a backup to ~/.claude/backups/ first. It won’t replace a ccmux entry that runs something other than ccmux-mcp unless you pass --force. ccmux setup offers the same step, and ccmux uninstall removes the entry.
To do it by hand instead:
claude mcp add --transport stdio --scope user ccmux -- ccmux-mcp
Run /mcp inside Claude Code to check the connection. See Claude Code’s MCP documentation for configuration scopes and troubleshooting.
For a project-scoped configuration, use .mcp.json at the project root:
{
"mcpServers": {
"ccmux": { "command": "ccmux-mcp" }
}
}
Other MCP clients use their own configuration format. Add a stdio server whose command is ccmux-mcp; use the binary’s absolute path if the client cannot find it on PATH.
The agent now has these tools:
| Tool | What it does |
|---|---|
list_sessions |
sessions visible to the connected daemon, with state and agent |
read_pane |
the last N lines of the pane a session’s agent runs in (default 24, max 500) |
list_projects |
every project under your projects root |
list_conversations |
past threads from Claude Code, Codex, Cursor and more, newest first |
get_usage |
tokens + cost per agent over the daemon’s 5-hour window, plus claude_block: Claude’s current 5-hour block as the dashboard’s quota bar counts it, with reset_at; takes no arguments |
list_machines |
tailnet peers, which ones run ccmuxd |
list_notes |
every markdown note in a project |
read_note |
one note’s contents |
search_notes |
search a project’s notes for text |
get_daemon_health |
is the daemon alive |
These tools are read-only. They expose information available to the connected daemon.
When you want it to do things
Add --allow-mutate to the server arguments. For a project-scoped Claude Code configuration:
{
"mcpServers": {
"ccmux": { "command": "ccmux-mcp", "args": ["--allow-mutate"] }
}
}
That unlocks:
| Tool | Effect |
|---|---|
spawn_session |
start a session in an existing project |
spawn_bare_session |
start a session not tied to a project |
send_keys |
type into the pane a session’s agent runs in |
kill_session |
terminate a session |
send_keys takes either literal text or exactly one tmux key name, such as Enter or C-c. To submit a reply, send the text, then Enter in a second call.
Off by default for a reason: an agent that can send_keys into your
work is an agent that can type anything you can type. The flag is the
opt-in. There’s no per-tool override — you turn the whole mutating set
on or you leave it off.
Without the flag, mutating tools are absent from tools/list. Restart the MCP server through your client after changing its arguments.
Drive a remote machine
CCMUX_HOST=mini.tail-xxxxx.ts.net:7474 ccmux-mcp
# or
ccmux-mcp --host mini.tail-xxxxx.ts.net:7474
The agent runs locally and queries the daemon on the Mac mini. Mutating tools still require --allow-mutate.
Same protocol, same tools — just over Tailscale HTTP instead of the
local Unix socket. Useful for “I’m coding on the laptop, but the
overnight Claude work is on the mini.”
--host takes host or host:port; the port defaults to 7474. An http:// prefix and a trailing / are stripped. https:// or a path is refused, because ccmuxd serves plain HTTP on the tailnet.
What this is actually for
A few concrete shapes:
Multi-session orchestration. An agent in one session spawns a
helper in another (spawn_session in a different project), reads its
output once it’s done (read_pane), pulls the result back, kills the
helper. No human in the loop.
Cross-machine work. An agent on your laptop checks how its sibling
on the Mac mini is doing (list_sessions, read_pane), without you
SSH-ing in. Or starts a long-running build on the mini (spawn_session
after configuring the MCP server with --host and --allow-mutate) and continues its local work.
Project memory. An agent reads the project’s notes at the start of
a session (list_notes + read_note) so it doesn’t repeat work the
previous agent did. The notes are plain markdown on disk; the agent
doesn’t need to know that.
Cost awareness. Before kicking off a big run, the agent checks
get_usage to see what the last five hours cost and when the Claude quota
resets (claude_block.reset_at), and get_daemon_health to see how many
sessions are already running.
None of this needs new infrastructure. ccmux already had all of it. MCP is just the protocol that makes it agent-accessible.
Security model
- Transport. stdio. The Unix socket the daemon listens on is user-scoped; tailnet HTTP requires being on your tailnet.
- Mutation. Off unless you flip the flag.
- Bounded reads.
read_panecaps at 500 lines so a buggy agent can’t drag the daemon down asking for the full scrollback every call. - Per-call deadline. Every handler runs under a 30-second context.
get_daemon_healthgives up after 5 seconds, so a hung daemon shows up fast. - Concurrency. Tool calls run concurrently, up to 16 at once; the rest wait in order. Read-only calls overlap. A mutating call waits for every call before it, and every later call waits for it, so pipelined mutations keep their order.
Protocol details
JSON-RPC 2.0 over stdio, one message per line.
- A line holding a JSON array is a batch. The reply is one array with a response per request, in order. Notifications get no response.
- Bad arguments are
-32602, and so is an unknown tool or one hidden behind--allow-mutate. A request without amethod, or with an object, array or booleanid, is-32600. - A tool that runs and fails returns
isError: truein its result, not a JSON-RPC error. notifications/cancelledcancels the named call, which then gets no response. A call cancelled while it waits never runs.argumentsis optional. Unknown fields are ignored, except awindowpassed toget_usage, which is-32602: the window is fixed.
Current limits
The server provides request/response tools. Live subscriptions, per-tool mutation permissions, and a prompt library are not currently available.
Related guides
- Configure a remote host before targeting its daemon.
- Read the HTTP API reference to build a client directly against ccmuxd.
Help improve ccmux
Found a bug, an unclear guide, or a translation that could read better? Contributions of any size are welcome. Open an issue or send a pull request.
Spotted an error or something out of date? Open an issue on GitHub.