Skip to content

Repository files navigation

Claude Code Studio

Multi-session collaboration studio for Claude Code. Run multiple CC instances as a coordinated team.

Status — experimental / personal project. A working technical demo of multi-session MCP coordination, not a hardened production tool. It runs, but it is lightly used; treat the roadmap below as exploration, not commitments.

Claude Code Studio turns multiple Claude Code CLI sessions into a collaborative team. One session dispatches tasks, others execute, and everyone communicates through a shared MCP server. SQLite is the default state backend; Redis adds pub/sub delivery.

The repository now contains two parallel experiments:

  • Classic Studio mode is the MCP message bus, task board, watcher, and Claude Code pane launcher described in the original sections below.
  • Mutual Review Room MVP is an owner-led Codex/Kimi/Claude review workflow with durable per-job state and read-only terminal projections. It does not use the Studio watcher or pane input as its transport.
┌─────────────────────────────────────────────┐
│              CLAUDE CODE STUDIO              │
│                                              │
│   [You + Commander]  ← strategy & dispatch   │
│       │                                      │
│       ├── Agent A (research)     [ONLINE]    │
│       ├── Agent B (backend)      [ONLINE]    │
│       ├── Agent C (frontend)     [BUSY]      │
│       └── Agent D (testing)      [OFFLINE]   │
│                                              │
│   Agent A → Agent B: "API spec changed"      │
│   Agent B → Agent A: "Got it, updating"      │
│                                              │
│            ┌──────────────┐                  │
│            │  State + MCP │ ← messages       │
│            │  Server      │    + task board   │
│            └──────────────┘                  │
└─────────────────────────────────────────────┘

Why?

You're researching 5 projects across 5 Claude Code sessions. Halfway through, you realize they depend on each other. Now you need them to talk.

This experiment combines four things in one small local tool:

  • Dispatch tasks from a commander to worker agents
  • Exchange messages between any agents (peer-to-peer, not just hub-and-spoke)
  • Auto-wake agents when they receive messages (no manual window switching)
  • See the big picture with a single command

All through MCP tools that every Claude Code session can call natively.

Features

  • One-click launch — one script starts the MCP server, watcher daemon, and tmux or Zellij panes with Claude Code auto-started in each
  • Optional real-time messaging — Redis pub/sub delivers notifications instantly; SQLite mode polls every five seconds
  • Peer-to-peer — any agent can message any other agent, not just commander → worker
  • Task dispatch & tracking — assign tasks with priority, track status, auto-notify on completion
  • Auto-registration — agents register themselves on startup via project CLAUDE.md
  • Experimental cross-machine state — multiple machines can share Redis, with the identity-collision limitations documented below
  • CLI status tool — check studio status from terminal without entering Claude Code
  • Cross-model compatibility — ordinary Claude Code panes can keep using compatible plugins such as codex-plugin-cc

Quick Start

Prerequisites

  • Python 3.10+
  • uv (recommended) or pip
  • tmux or zellij (either one works)
  • Redis — optional, only for real-time pub/sub delivery (local Docker: docker run -d -p 127.0.0.1:6379:6379 redis:7-alpine). The default SQLite backend needs no external datastore.
  • Claude Code CLI

Install

git clone https://github.com/AliceLJY/claude-code-studio.git
cd claude-code-studio
uv venv && uv pip install -e .

Launch

Configure the MCP connection once (see Connect Claude Code) before the first launch.

# Launch studio with 1 commander + 3 agent windows (default)
./scripts/launch.sh

# Specify agent count
./scripts/launch.sh 5

# Use zellij instead of tmux
STUDIO_MUX=zellij ./scripts/launch.sh

This will:

  1. Start the MCP server on localhost:3777
  2. Start the watcher daemon (auto-kicks agents on new messages)
  3. Create a tmux or Zellij session with separate panes
  4. Auto-start claude in every window
  5. Each Claude auto-registers itself on startup

Sit in the commander window and talk — each agent auto-registers via the project CLAUDE.md once its MCP connection is up.

Check Status (CLI)

# See who's online and task board without entering Claude Code
./scripts/status.sh

Mutual Review Room MVP

Mutual Review Room is a parallel mode for attributable, owner-led review. Alice talks only to the native owner session in the left tmux pane. The right half contains one to three read-only observer projections, one lane per selected reviewer. Those projections render durable events; they are not reviewer terminals and have no dispatch controls.

Reviewer selection and adapter growth are separate concerns. The person launching the room chooses the exact subset for that job by repeating --reviewer ID=PROVIDER; no reviewer is silently added. The current terminal layout deliberately shows one to three lanes so each transcript remains readable. The provider catalog is open-ended at source level, but an adapter is admitted only after it proves provider-native start/resume identity, a parseable visible final answer, cold-context and tool isolation, and lane-local failure handling. Unknown provider names fail closed; the room does not execute arbitrary shell templates as “extensions.”

┌────────────────────────────┬────────────────────────────┐
│ Native owner session       │ Reviewer A observer        │
│                            ├────────────────────────────┤
│ Only this side dispatches  │ Reviewer B observer        │
│ and receives user input    ├────────────────────────────┤
│                            │ Reviewer C observer        │
└────────────────────────────┴────────────────────────────┘

Both sides retain scrollback. Point at a pane and use the mouse wheel, or press Ctrl-b [ to enter tmux copy mode, browse with PgUp, PgDn, or the arrow keys, then press q to return to the live view. Reviewer panes remain read-only with tmux input-off; copy-mode navigation does not turn them into interactive provider terminals. Each observer prints the complete durable history once and then appends only new visible events, so reading an earlier answer is not disrupted by full-screen redraws.

Each job has its own directory and SQLite database under ~/.claude-code-studio/review-jobs by default. The ledger stores append-only, attributable events with job, request, reviewer, native session, round, parent, direction, type, status, content, and timestamp fields. A new job starts with empty session IDs and cannot inherit state from an earlier job. Once a provider-native session ID is bound to a reviewer, follow-ups must continue on that same identity.

Owner dispatch and recovery commands require the job-bound owner.token file path. The launcher gives that path only to the owner environment; provider children do not inherit review-control environment variables. This is a trusted-local ownership boundary, not multi-user authentication.

Check Provider Capabilities

./scripts/review-room providers

The built-in adapters are Codex, Kimi, and Claude Code. Availability in this output means the executable is discoverable; a real invocation can still fail authentication or quota checks.

Launch a Room

# Native Kimi owner, with Codex and Kimi reviewer lanes
./scripts/review-room launch \
  --owner kimi \
  --reviewer codex=codex \
  --reviewer kimi=kimi \
  --cwd "$PWD"

# Create the same job and tmux session without attaching immediately
./scripts/review-room launch \
  --owner kimi \
  --reviewer codex=codex \
  --reviewer kimi=kimi \
  --cwd "$PWD" \
  --no-attach

launch prints the generated job ID and owner token-file path before opening the room. Unless --reviewer-cwd is explicitly supplied, every reviewer receives a separate workspace under ~/.claude-code-studio/review-workspaces/<job>/<reviewer>, outside the control ledger in review-jobs. The owner uses the requested project directory. On macOS, every reviewer process is wrapped in a Seatbelt profile that denies reads and writes to the complete review-state root, the current owner cwd, and every other reviewer workspace registered in that state root when dispatch begins. Kimi reviewer processes also cannot read the global Kimi AGENTS.md or mcp.json when those files exist. If the isolation layer is unavailable, the affected reviewer lane is marked unavailable and later lanes can still continue.

Inspect, Reopen, and Recover

JOB_ID=job_replace_with_launch_output
TOKEN_FILE="$HOME/.claude-code-studio/review-jobs/$JOB_ID/owner.token"

# Durable state, native session IDs, requests, event count, and tmux state
./scripts/review-room status --job "$JOB_ID"

# Rebuild the tmux projection and leave it detached
./scripts/review-room room --job "$JOB_ID" --replace --no-attach

# After an interrupted process, mark orphaned running requests interrupted
# without replaying them
./scripts/review-room recover \
  --job "$JOB_ID" \
  --token-file "$TOKEN_FILE"

status is read-only and therefore does not accept an owner token. Recovery is deliberately conservative: it records interruption and never automatically resubmits a provider request.

Dispatch Review Rounds Directly

The native owner normally runs these commands. Round 1 is a cold read: prepare one task-envelope file and send the exact same content to every selected reviewer. dispatch-all invokes reviewer lanes serially, so one unavailable provider is recorded in its own lane while later reviewers still continue. Before the first provider starts, every eligible lane receives a durable queue-position event. When its turn begins, a second event says that the provider is answering and that the complete text will appear only after completion; the UI does not pretend to be a token stream.

# R1: identical cold-read envelope for every reviewer
./scripts/review-room dispatch-all \
  --job "$JOB_ID" \
  --round 1 \
  --prompt-file ./review-r1.md \
  --token-file "$TOKEN_FILE"

# R2: a reviewer-specific follow-up on that reviewer's existing native session
./scripts/review-room dispatch \
  --job "$JOB_ID" \
  --reviewer codex \
  --round 2 \
  --prompt-file ./review-r2-codex.md \
  --token-file "$TOKEN_FILE"

# R3 uses the same directed form with a new reviewer-specific prompt
./scripts/review-room dispatch \
  --job "$JOB_ID" \
  --reviewer codex \
  --round 3 \
  --prompt-file ./review-r3-codex.md \
  --token-file "$TOKEN_FILE"

# Persist the owner's convergence or unresolved-disagreement decision
./scripts/review-room complete \
  --job "$JOB_ID" \
  --verdict-file ./review-final.md \
  --token-file "$TOKEN_FILE"

For rounds 2 and 3, the ledger automatically links an omitted parent to that reviewer's previous request and rejects a parent belonging to another reviewer. This preserves cold-read isolation in R1 while allowing directed adjudication and verification later.

Runtime Boundaries

  • Codex and Kimi reviewer adapters are built in. Codex reviewer invocations ignore user configuration and project rules and also run under the CLI's read-only sandbox.
  • Every reviewer is structurally denied access to the complete review-state root, its owner cwd, and every other reviewer workspace registered when dispatch begins by a macOS Seatbelt wrapper. Live regression tests verify that a lane can read its own canary while same-job peers, other jobs' control data, and other jobs' lanes cannot be read or written. This is a scoped isolation boundary, not a global read-only filesystem.
  • The Kimi CLI currently requires the prompt in process arguments and has no single provider-native read-only switch. On the first reviewer turn, the room binds a self-contained custom agent whose body does not include the base prompt or instruction/skill placeholders, whose tool and subagent allowlists are empty, and whose denylist also blocks dynamic tool disclosure and every MCP tool. Resume restores that same bound profile. An empty skills directory plus Seatbelt denial of global Kimi AGENTS.md, global mcp.json, owner, control, and peer paths closes the remaining inherited-context routes while preserving the user's normal owner login.
  • The Claude adapter is built in, but the current host's real invocation is authentication-unavailable. Executable discovery alone is not proof that the adapter can answer.
  • An interactive Codex owner is trusted and may inherit the user's normal Codex configuration and MCP connections. It runs with --no-alt-screen so tmux can retain its scrollback. --approve-for-me selects Codex's automatic-review mode with the workspace-write sandbox instead of repeatedly asking the user. The current CLI rejects combining that flag with explicit -s or -a options, so the room does not duplicate them; it never uses danger-full-access. When Codex exposes its local shared app-server socket, the launcher reconnects through that socket so closing and rebuilding tmux can reattach the already loaded owner instead of colliding with its active writer. This is intentionally different from the isolated Codex reviewer path.
  • On the acceptance host, a Kimi owner completed the real two-reviewer, three-round run. After raising the long-lived owner process's soft file-descriptor limit, a fresh interactive Codex owner also completed its provider-native bootstrap, persisted its native session ID, and remained live and writable in the tmux owner pane. A full Codex-owner, multi-round human interaction is intentionally left to the user-visible acceptance run.
  • The room never uses tmux send-keys, pane scraping, a browser dashboard, or Electron as its reliable control or message transport. tmux is only the native layout and observer surface.
  • Room creation enables tmux mouse support and extended keys with extended-keys-format csi-u, preserving per-pane scrollback while native owner TUIs submit multiline prompts reliably.

The accepted end-to-end host path is Kimi owner with Codex and Kimi reviewer lanes. In the latest three-round run, both lanes were blocked from that job's control directory, owner cwd, and each other's workspace; six of six requests completed; each lane used exactly one native session across all three rounds; round 1 and round 3 used one identical prompt hash while round 2 used two distinct hashes; and both queue events preceded the first round-3 provider invocation. The Kimi provider-native wire confirmed an exact match to the self-contained reviewer system prompt, zero effective tools, no inherited AGENTS paths, no MCP discovery events, and three turns under one bound profile. Completion persisted the owner's non-empty final synthesis in the immutable job_completed event. A separate live two-job regression confirms the broader state-root and cross-job lane boundary. Rebuilding the observer room retained the stored identities. Codex-owner startup is host-verified as described above, while its full human-driven multi-round run and the Claude adapter remain explicit acceptance gaps.

Connect Claude Code

From the repository directory, add the Studio server to Claude Code's local project scope (the launcher does not write this configuration):

claude mcp add --transport sse --scope local claude-code-studio http://localhost:3777/sse

Claude Code still supports SSE, but its MCP documentation now marks SSE as deprecated in favor of Streamable HTTP. Studio retains SSE for v0.3 compatibility; a transport migration should be a separate release rather than an undocumented endpoint change.

How It Works

Redis mode example — you say: "Tell agent-1 to research MCP frameworks"

Commander CC                    Watcher Daemon              Agent-1 CC
     │                               │                          │
     ├─ send_message(agent-1) ──────►│                          │
     │       │                       │                          │
     │       └─► Redis PUBLISH ─────►│                          │
     │                               ├─ tmux send-keys ───────►│
     │                               │  "check inbox"           │
     │                               │                          ├─ check_inbox()
     │                               │                          ├─ (does research)
     │                               │                          ├─ send_message(commander)
     │                               │                          │       │
     │                          ◄────┤◄─ Redis PUBLISH ─────────┘       │
     │◄─ tmux send-keys ────────┤    │                                  │
     │   "check inbox"          │    │                                  │
     ├─ check_inbox() ──────────┘    │                                  │
     ├─ "agent-1 says: ..."         │                                  │
     │                               │                                  │

In SQLite mode the watcher polls instead of subscribing to Redis. Auto-wake is best-effort in both modes because terminal-idle detection is heuristic.

Architecture

┌────────────┐  ┌────────────┐  ┌────────────┐
│  CC Session │  │  CC Session │  │  CC Session │
│  (commander)│  │  (agent-1) │  │  (agent-2) │
└──────┬─────┘  └──────┬─────┘  └──────┬─────┘
       │               │               │
       └───────────┬───┴───────────────┘
                   │  SSE/HTTP
           ┌───────▼────────┐
           │   MCP Server   │
           │  (FastMCP/SSE) │
           └───────┬────────┘
                   │
           ┌───────▼────────┐     ┌──────────────┐
           │ SQLite / Redis │◄───►│   Watcher    │
           │  state backend │     │  (auto-kick) │
           └────────────────┘     └──────────────┘
  • MCP Server: FastMCP with SSE transport — multiple CC sessions connect to one server
  • Redis: Message storage + pub/sub for instant delivery. Messages TTL 24h, tasks TTL 72h.
  • Watcher: Subscribes to Redis pub/sub or polls SQLite, then auto-sends prompts to panes it believes are idle
  • Backend: SQLite by default (no external datastore). Set STUDIO_BACKEND=redis for real-time pub/sub delivery (requires a running Redis)

MCP Tools

Tool Description
register Join the studio with a unique ID and role
unregister Leave the studio
send_message Direct message another agent
broadcast Message all other registered agents
check_inbox Read your messages
dispatch_task Assign a task to an agent (auto-notifies)
update_task Update task status (auto-notifies dispatcher)
my_tasks List your assigned tasks
studio_status Current agents and task board
kick Wake up an agent remotely via tmux
heartbeat Signal you're still active

Configuration

Environment Variable Default Description
STUDIO_HOST localhost MCP server bind address
STUDIO_PORT 3777 MCP server port
STUDIO_BACKEND sqlite Storage backend: sqlite (no external service) or redis (real-time pub/sub)
STUDIO_REDIS_URL redis://localhost:6379 Redis connection URL
STUDIO_MUX tmux Terminal multiplexer: tmux or zellij
STUDIO_STATE_DIR private per-user runtime directory Runtime logs, Zellij layout, pane map, and pane wrappers
STUDIO_DB_PATH ~/.claude-code-studio/studio.db SQLite database path (sqlite mode)
STUDIO_AUTO_KICK 1 Set to 0 to stop the watcher from auto-kicking agents (the idle heuristic can misfire)
STUDIO_UNSAFE_REMOTE_MCP unset Explicit opt-in required before binding the unauthenticated MCP server to a non-loopback host

Security Model

Studio assumes one trusted user on one machine or trusted private network. The MCP endpoint has no authentication, tool-supplied agent identities are not cryptographically verified, and kick can type a prompt into a managed terminal pane. The server therefore refuses a non-loopback STUDIO_HOST unless STUDIO_UNSAFE_REMOTE_MCP=1 is explicitly set. Do not expose the MCP or Redis ports to the public internet. If an agent's CLI has exited, its pane falls back to a shell — without a check, kick's prompt would be typed in as a shell command. Under STUDIO_MUX=tmux (the default), kick checks the pane's foreground process first and refuses instead of injecting when it isn't the agent anymore; zellij exposes no equivalent query, so this check is tmux-only.

Cross-Machine Setup

Run agents on different machines by pointing to a shared Redis:

# Machine A (your Mac)
STUDIO_BACKEND=redis STUDIO_REDIS_URL=redis://192.168.1.100:6379 ./scripts/launch.sh

# Machine B (Mac Mini)
STUDIO_BACKEND=redis STUDIO_REDIS_URL=redis://192.168.1.100:6379 ./scripts/launch.sh 3

All agents across both machines share the same message bus and task board.

Known limitations (experimental). Agent IDs are fixed (commander, agent-1, …), so two machines launched this way collide on the same IDs in Redis and overwrite each other's state — there is no real per-machine isolation yet. For cross-machine use, configure Redis authentication, network filtering, and encryption appropriate to your network; the local Docker command above intentionally listens on loopback only.

Positioning

Studio is a small trusted-local coordination demo, not a secure multi-tenant agent platform. Its useful combination is MCP messaging, a task board, terminal-pane launch, and best-effort auto-wake. It does not provide shared model context, strong agent identity, authorization, file locks, or durable workflow orchestration.

Mutual Review Room has a narrower contract: durable per-job review state, stable provider-native reviewer identities, owner-only dispatch controls, and read-only observer projections. It is not a general replacement for Studio's peer messaging or task board.

Cross-Model Collaboration

Classic Studio mode does not integrate with Codex directly. Each of its panes is an ordinary Claude Code session, so compatible Claude Code plugins such as codex-plugin-cc can still be used for a cross-model review workflow. Follow the plugin's own README for its current installation and command names. Mutual Review Room is separate and has built-in Codex, Kimi, and Claude provider adapters as described above.

┌─────────────────────────────────────────────────┐
│              CLAUDE CODE STUDIO                  │
│                                                  │
│   Claude            writes code                  │
│       │                                          │
│       ▼                                          │
│   Codex             reviews code                │
│       │                                          │
│       ▼                                          │
│   Claude            verifies findings & fixes    │
│                                                  │
│   Review stays in the same workspace.             │
└─────────────────────────────────────────────────┘

In classic Studio mode this compatibility is inherited from Claude Code; its MCP message bus adds no Codex-specific transport or routing. Mutual Review Room uses its own provider-native invocation adapters and durable review ledger instead.

Ecosystem

Part of AliceLJY's open-source AI workflow:

Project Description
mutual-review-room Owner-led Codex/Kimi/Claude review room — the standalone, actively developed successor to this repo's "Mutual Review Room MVP" section
recallnest MCP memory workbench (LanceDB + Jina v5)
content-publisher (private) Image generation + layout + WeChat publishing
openclaw-tunnel Docker ↔ host CLI bridge (/cc /codex)
digital-clone-skill Build digital clones from corpus data
telegram-ai-bridge Telegram bots for Claude, Codex, Agy, and Kimi
cc-empire (private) Complete Claude Code workflow scaffold (rules + hooks + agents)

License

MIT

Development

python -m unittest discover -s tests -v
python -m compileall -q studio tests
bash -n scripts/launch.sh scripts/launch-zellij.sh scripts/status.sh

Contributing

Issues and PRs welcome. This project was born from a real need — if you're running multiple Claude Code sessions and wish they could talk to each other, this is for you.

Releases

Packages

Contributors

Languages