Documentation

The owner's manual

Everything mrc does, how to configure it, and what to do when the radar goes quiet. New here? Start with Installation, then skim the CLI reference.

Installation

Mister Claude is a Node.js launcher (mrc.js) that drives Docker. You need a Docker engine, and on macOS the lightweight choice is Colima — no Docker Desktop, no GUI, no license fees.

1 · Prerequisites (macOS)

prerequisites
# Homebrew, then the toolchain $ brew install node docker docker-buildx colima terminal-notifier # Tell Docker where Homebrew put the buildx plugin $ mkdir -p ~/.docker $ echo '{"cliPluginsExtraDirs": ["/opt/homebrew/lib/docker/cli-plugins"]}' > ~/.docker/config.json
  • node — runtime for the mrc launcher
  • docker + docker-buildx — the CLI client and build plugin
  • colima — the Linux VM that runs the Docker daemon
  • terminal-notifier — native macOS desktop notifications
Merging config.json

If you already have a ~/.docker/config.json, merge the cliPluginsExtraDirs key in by hand rather than overwriting the file.

2 · Install mrc

The install script drops a launcher into ~/.local/bin/mrc and the Docker build context into ~/.local/share/mrc/:

install — one liner
$ curl -fsSL https://aisaacs.github.io/mrc/install.sh | bash

Or from source (requires Node.js 22+):

install — from source
$ git clone https://github.com/aisaacs/mrc.git $ sudo ln -s "$(pwd)/mrc/mrc.js" /usr/local/bin/mrc
Linux

On Linux with a native Docker daemon, skip Colima entirely — mrc talks to Docker directly. Swap terminal-notifier for notify-send (usually in libnotify-bin).

Configuration

Three sources, merged in order — global config, per-repo config, then CLI flags. CLI flags always win.

.mrcrc files

One CLI flag per line; # for comments. Global defaults live in ~/.mrcrc; per-repo overrides in <repo>/.mrcrc.

~/.mrcrc
# Global defaults — apply to every repo --no-sound --web --colima-memory 32

API keys & .env

mrc loads a .env from next to the launcher, or from ~/.config/mrc/.env. Both Anthropic and OpenAI keys are picked up.

.env
ANTHROPIC_API_KEY="sk-ant-..." OPENAI_API_KEY="sk-..." # only needed for --agent codex
1Password native

Use op:// references and mrc resolves them through the 1Password CLI at launch — keys never touch disk in plaintext:

ANTHROPIC_API_KEY="op://Engineering/MRC Claude API key/credential" OPENAI_API_KEY="op://Engineering/OpenAI/credential"
Why a separate Anthropic key?

This key is not your Claude Code subscription. It powers small Haiku calls for session naming and summaries. A dedicated key lets you revoke it independently. Claude Code itself authenticates inside the container as usual.

Environment variables

VariablePurpose
ANTHROPIC_API_KEYAnthropic key — from env, .env, or ~/.config/mrc/.env
OPENAI_API_KEYOpenAI key — required for --agent codex
MRC_PORT_BASEStarting port for proxy allocation (default 7722)
MRC_HOMEOverride the Docker build-context directory (advanced)

Usage

Point mrc at a repo. Pass anything after -- straight through to Claude Code.

usage
# Open a project (resumes the last conversation) $ mrc ~/projects/my-app # Current directory $ mrc . # Pass args through to Claude Code after -- $ mrc . -- -p "refactor the auth module" $ mrc . -- --model claude-sonnet-4-5-20250929 # Start a fresh, named conversation $ mrc --new fix-bug-42 ~/projects/my-app # Open the web (HTTPS to any host) for search & fetch $ mrc --web ~/projects/my-app

When you quit, the container disappears — your files are safe on the host, only the container is ephemeral. Reopening the same repo auto-resumes via --continue.

Agent teams

Mister Claude can run a whole team of agents on one problem — an architect, engineers, a critic, an adversary — each in its own sandboxed container behind the same firewall, addressing each other by @mention. You steer from a web dashboard or your phone: they propose; you steer; you commit.

Define a roster

A team is a team.json file of members, each with a role and a backend. Write the file, start from a preset, or build one in the dashboard:

team.json
{ "org": "shop", "teams": [ { "name": "client", "territory": "client", "members": [ { "role": "architect", "backend": "claude", "lead": true }, { "role": "engineer", "backend": "claude", "territory": "client/src" }, { "role": "critic", "backend": "claude" } ] }, { "name": "api", "territory": "api", "members": [ { "role": "architect", "backend": "claude", "lead": true }, { "role": "engineer", "backend": "claude", "territory": "api/src" }, { "role": "adversary", "backend": "codex" } ] } ] }

Or start from a ready-made preset — game, web, mobile, backend (mrc team presets lists them):

shell
# write a team.json from a preset mrc team new --preset web
  • Roles — architect, engineer, critic, adversary, ultracritical, user-defender, researcher, tester (plus media makers). One lead per team bridges to the shared leads room.
  • Write territory — each member's territory is mounted read-write and everything else read-only, so two engineers never clobber each other's files and a critic is read-only by capability.
  • Live vs worker — claude members are live participants; codex / qwen members are task-workers (a directed @mention runs their CLI for one turn).

Launch & attach

shell
# push the roster + launch the live members (each in its own ttyd terminal) mrc team up # org, rooms, and your @you inbox mrc team status # drop into a member's terminal mrc team console roland # close the org's rooms mrc team down
One accept per member

Each live member boots in its own terminal — you accept its one-time Channels prompt to bring it online. A team isn't hands-free, by design: that per-member accept, plus you commit every change, is the control, not a caveat.

Steer from the dashboard

mrc rooms dashboard opens a local web app for the whole org — every team room, live transcripts, and your inbox. Each project is a tab:

  • Close = suspend — stops the members but keeps the team, transcripts, and history; Resume relaunches it and each member picks up its own conversation.
  • Delete project — forgets the org from Mister Claude (stops sessions, drops the tab) but deletes nothing on disk: your repo, team.json, and transcripts stay, so you can re-add it with mrc team up.

The @you inbox

When a member needs you, it lands in your inbox as one of two kinds:

  • Question (from ask_user) — badged; framed "reply to answer".
  • Notification / FYI (a plain @user) — reply optional; visible everywhere, nags nowhere.

Both are replyable (your reply routes back to the member as a directive). Dismiss what you don't need — it's recoverable. Each item carries a stable #N id, and the inbox persists across daemon restarts.

Answer from your phone (Telegram)

Optional: link a Telegram bot and the team's questions and FYIs push to your phone — reply there and it routes back to the right agent. Each project uses its own bot, and linking is human-gated (nothing auto-binds):

1

Create a bot

DM @BotFather → /newbot → copy the token.

2

Add the token to the project

Put it in that repo's own .env (read strictly per-project — no global fallback): MRC_TELEGRAM_BOT_TOKEN=123456:ABC-…

3

Bring the project up

mrc team up — the daemon starts the bridge for any project that has a token.

4

DM the bot /start

from the phone or account you want to link.

5

Confirm in the dashboard

the chat appears as pending on that project's tab → click Confirm (Reject an unexpected one). Linked.

Questions push as ❓ "reply to answer", FYIs as 🔔 "reply optional", both with the same #N. Reply on either surface and it syncs to the other; Unpair on the tab anytime.

Trust & control

A teammate's message — even the architect's — is untrusted peer data; only your own directives (a steer, or an inbox/Telegram reply) carry authority, and forged "human" markers in agent text are defanged. Members can't overwrite each other's files or reach the open internet, a critic reviews risky work when called, and you commit every change. See the Security model for the layered boundary.

Go deeper

The full architecture — topology, the trust model, and what's tested — lives in docs/agent-teams.md in the repo.

CLI reference

mrc [options] [path-to-repo] [-- claude-code-args...]

Options

FlagDescription
-r, --rebuildForce a full image rebuild (no cache)
-v, --verboseShow Colima and Docker output
-n, --new [name]Start a new conversation (optionally named)
-w, --webAllow outbound HTTPS to any host
--agent <name>Agent to launch: claude (default) or codex
--daemonStart the container detached and print its ID
-j, --jsonStream stream-json instead of an interactive TTY
--no-summarySkip the AI session summary on exit
--no-notifyDisable desktop notifications entirely
--no-soundNotify silently (no sound)
--colima-cpu NCPUs for the Colima VM (default: all host cores)
--colima-memory NMemory in GB (default: half host RAM, min 8)

Commands

CommandDescription
mrc statusShow active containers across all repos (no key needed)
mrc pick [path]Interactive session picker (arrow keys)
mrc sessions ls [path]List saved sessions
mrc sessions name <name> [#] [path]Name a session (defaults to most recent)
mrc sessions resume <name-or-#> [path]Resume a specific session
mrc sessions pick [path]Alias for mrc pick

Sessions

Every conversation is a .jsonl file in .mrc/. They auto-resume, auto-name, and auto-summarize.

  • Auto-resume — reopening a repo continues the last conversation (--continue).
  • Named sessions — mrc --new bugfix starts fresh with a name; names live in .mrc/session-names.
  • AI names & summaries — once enough conversation builds up, a Haiku call names and summarizes it (skip with --no-summary).
  • Tool-miss detection — on exit, mrc reports any tools Claude reached for but didn't have, with a one-click GitHub issue link.
sessions
$ mrc pick # arrow-key picker $ mrc sessions ls # list them $ mrc sessions name "auth-refactor" # name the latest $ mrc sessions name "clipboard-fix" 2 # name session #2 $ mrc sessions resume auth-refactor # by name $ mrc sessions resume 2 # or by number

.sandboxignore

Hide secrets and infrastructure from the container. Works like .gitignore and is recursive — drop a .sandboxignore anywhere in the tree and its entries resolve relative to that directory.

.sandboxignore
# Secrets .env .env.local .env.production # Infrastructure terraform/ k8s/ secrets/
  • Files are masked with /dev/null — they appear empty inside the container.
  • Directories get an anonymous volume overlay — they appear as empty dirs.
  • Comments (#) and blank lines are supported.

He doesn't know what he's missing. Even if Claude is convinced your AWS keys must be somewhere, the bytes simply aren't in the container.

Colima resources

By default the Colima VM gets all your CPU cores and half your RAM (with an 8 GB floor). Override per-run or in .mrcrc:

colima sizing
$ mrc --colima-cpu 8 --colima-memory 32 ~/projects/my-app # or pin it in ~/.mrcrc --colima-memory 48
Only applies on a fresh start

Colima is a single shared VM. These settings take effect when mrc starts Colima — if it's already running, it keeps its current size. To resize: colima stop then run mrc again.

Claude & Codex

Claude Code is the default. Pass --agent codex to launch OpenAI's Codex CLI in the same sandbox instead.

agents
$ mrc ~/projects/my-app # Claude (default) $ mrc --agent codex ~/projects/my-app # Codex
  • Codex needs OPENAI_API_KEY in your environment or .env.
  • Claude config persists in mrc-config-<hash>; Codex config in a separate mrc-codex-<hash> volume.
  • Session naming & summaries are Claude-only — they're skipped for Codex.
  • The firewall already whitelists api.openai.com, so Codex works out of the box.

Clipboard (image paste)

Text paste works through the terminal already. Images need a bridge — mrc ships one as an in-process TCP proxy (no socat, no external dependency).

  1. mrc starts a clipboard server on a dynamic port (from MRC_PORT_BASE, default 7722).
  2. Inside the container, a shim at /usr/local/bin/xclip intercepts Claude's clipboard reads.
  3. The shim fetches clipboard data over host.docker.internal.

The banner shows Clipboard: the Schwartz can see your clipboard when it's live. Just copy an image and press Ctrl+V inside Claude Code.

Debugging

Check /tmp/mrc-xclip-shim.log inside the container. A "No route to host" there usually means a stale firewall — rebuild with docker rmi mister-claude.

Notifications

A Claude Code Stop hook fires when he finishes responding. The container-side hook sends the repo name and a one-line summary to a host-side proxy, which fires a native notification (terminal-notifier on macOS, notify-send on Linux).

notification flags
$ mrc --no-notify ~/projects/my-app # off entirely $ mrc --no-sound ~/projects/my-app # show it, silently
macOS not showing banners?

System Settings → Notifications → terminal-notifier → set the style to Banners or Alerts and toggle Allow Notifications on. To let them through Do Not Disturb, add terminal-notifier under Focus → Allowed Notifications.

Status line

A live bar at the bottom of every session: context-window usage, plus 5-hour and 7-day rate-limit gauges, plus the session name.

statusline
[█████████░░░░░░░░░░░] 49% context · 5h 12% │ 7d 7% · add-context-progress-bar
RangeColorMeaning
0–59%GreenNormal usage
60–76%YellowGetting full
77–100%RedCompaction imminent (Claude reserves ~15%)

It's installed automatically on container start — but only if you haven't set your own. A custom /statusline always wins.

Data persistence

Two places, two jobs:

LocationHoldsLifecycle
mrc-config-<hash>Global Claude config: auth, settings, pluginsDocker volume, per repo
mrc-codex-<hash>Codex configDocker volume, per repo
.mrc/ in your repoMemory, conversation history, project settingsLives in the repo; survives volume resets

Each repo gets its own volumes (keyed by an MD5 hash of the repo path) so projects never cross-contaminate. .mrc/ is auto-created and auto-added to .gitignore, and is symlinked from ~/.claude/projects/-workspace/ inside the container.

resets
# Fresh project memory $ rm -rf .mrc/ # Nuke a repo's global config volume $ docker volume ls | grep mrc-config $ docker volume rm mrc-config-<hash>

Security model

The whole design rests on one idea: the container is the security boundary, not Claude's own permission prompts. That's what makes --dangerously-skip-permissions safe here.

What the firewall does

  • Resolves the allowlist to IPs at boot and pins them in an ipset.
  • Sets the default iptables policy to DROP on INPUT, OUTPUT, and FORWARD.
  • Adds an explicit REJECT for non-allowed outbound traffic, so blocked calls fail instantly instead of hanging.
  • Blocks all IPv6 — otherwise an IPv6-capable service could sidestep the IPv4 rules.
  • Allows the host only on the dynamic proxy ports — your local Postgres, Redis, and everything else stay unreachable.
  • Verifies the lock by confirming example.com is unreachable before handing control to Claude.

The allowlist

api.anthropic.com api.openai.com registry.npmjs.org sentry.io statsig.com

Add your own (a private registry, an internal API) by editing the for domain in ... loop in init-firewall.sh, then rebuild. With --web, outbound HTTPS (port 443) opens to any host while every other rule stays in force.

Other guarantees

  • UID/GID matching — the image is built with your host UID/GID, so bind-mounted files keep correct ownership and Claude runs as a non-root coder user.
  • Scoped sudo — coder may run exactly one command with sudo: the firewall script. Nothing else.
  • Ephemeral containers — every container runs with --rm; quit and it's gone.
  • NET_ADMIN / NET_RAW only — the container gets just the capabilities needed to firewall itself.
Add domains, responsibly

The allowlist is short on purpose. Every domain you add is a door. Prefer .sandboxignore for secrets and --web only when you actually need the open web.

Updating

Claude Code's version is baked into the image at build time, and auto-update is disabled inside the container (the firewall blocks the npm CDN hosts it would need). To get a newer Claude Code, rebuild:

rebuild
$ docker rmi mister-claude $ mrc ~/projects/my-app # or, equivalently $ mrc --rebuild ~/projects/my-app

mrc nudges you when the image is more than four days old. Rebuilding also picks up changes to the Dockerfile, entrypoint, firewall rules, and container scripts.

Troubleshooting

SymptomFix
docker: command not foundbrew install docker docker-buildx colima and ensure Homebrew is on your PATH.
Cannot connect to the Docker daemonColima isn't running. Just run mrc — it auto-starts Colima — or colima start --vm-type vz --mount-type virtiofs.
Network not ready after 30 attemptsDNS didn't come up in the VM. colima stop && colima start --vm-type vz --mount-type virtiofs.
Firewall verification failediptables rules didn't apply. Confirm the container has NET_ADMIN/NET_RAW (mrc adds these automatically).
Permission errors on mounted filesThe image is built with your UID/GID. Rebuild: docker rmi mister-claude, then mrc.
Auto-update failed (inside container)Expected — the image version is pinned. Rebuild to update Claude Code.
Slow file accessMake sure Colima runs with --mount-type virtiofs (mrc does this when it starts the VM).
Codex: Permission denied loading configRebuild the image so ~/.codex is created with the right owner: docker rmi mister-claude.

Still stuck? Open an issue at github.com/aisaacs/mrc — and run with -v to capture the Colima/Docker output.

"May the Schwartz be with you."
— Yogurt