# ClaudeMailbox A standalone MCP mail server that lets parallel Claude sessions coordinate with each other. Any Claude session (plain terminal, ClaudeDo worktree, anything that consumes `.mcp.json`) can send messages to a peer session's inbox, check for pending messages, and discover other active mailboxes. Not a substitute for `run_in_background: true` — that handles single-session responsiveness. This handles **session-to-session** coordination. ## Architecture One long-running daemon binds HTTP on loopback, hosts the MCP server at `/mcp` and a small REST API at `/v1/*`, and persists state in a single SQLite file. Sessions declare themselves via an `X-Mailbox` header in their `.mcp.json`. ``` session-backend session-frontend external sender (X-Mailbox: backend) (X-Mailbox: frontend) (CLI / UI / hook) | | | | HTTP | | +--------------+-----------------+--------------------------+ v claude-mailbox serve (ASP.NET Core + Kestrel) /mcp MCP tools /v1/* REST for non-MCP senders /health v ~/.claude-mailbox/mailbox.db (SQLite WAL) ``` ## Install The recommended path is the npm package — it works on Windows, macOS, and Linux. ```sh # one-time per machine: point the @kuns scope at the public Gitea npm registry npm config set @kuns:registry=https://git.kuns.dev/api/packages/releases/npm/ # install npm install -g @kuns/claude-mailbox ``` Or use the bootstrap one-liner: ```powershell # Windows irm https://git.kuns.dev/releases/ClaudeMailbox/raw/branch/main/install.ps1 | iex ``` ```sh # macOS / Linux curl -fsSL https://git.kuns.dev/releases/ClaudeMailbox/raw/branch/main/install.sh | sh ``` macOS users can also install via Homebrew once the tap is published: ```sh brew install kuns/tap/claude-mailbox ``` ### Autostart ```sh claude-mailbox install-autostart # per-user, no admin claude-mailbox install-autostart --service # Windows only: register as a Windows Service (admin) claude-mailbox status # Running | Stopped | NotInstalled claude-mailbox uninstall-autostart [--purge] ``` | Platform | Default mechanism | `--service` mechanism | |---|---|---| | Windows | Scheduled Task at logon (no admin) | Windows Service (admin, via `node-windows`) | | macOS | launchd LaunchAgent in `~/Library/LaunchAgents/` | n/a | | Linux | systemd `--user` unit in `~/.config/systemd/user/` | n/a | ### Config precedence ``` CLI flag > mailbox.json > built-in defaults ``` `mailbox.json` is searched at `~/.claude-mailbox/mailbox.json` (per-user), and on Windows additionally at `%ProgramData%\ClaudeMailbox\mailbox.json` (machine-wide, written by `--service` install). Pass `--config ` to override. Defaults: port `47822`, bind `127.0.0.1`, database at `~/.claude-mailbox/mailbox.db`. ### Smoke test ```sh claude-mailbox install-autostart claude-mailbox status curl http://127.0.0.1:47822/health claude-mailbox uninstall-autostart --purge ``` ### Build the .NET binary (alternative) The original .NET 8 implementation still lives in `src/ClaudeMailbox/`. Build a self-contained Windows exe with: ```powershell dotnet publish src/ClaudeMailbox -c Release -r win-x64 --self-contained -p:PublishSingleFile=true ``` Put the resulting `claude-mailbox.exe` on your `PATH` and use the legacy `install-service` verbs (Windows-only, admin shell): ``` claude-mailbox install-service [--port 47822] [--bind 127.0.0.1] [--db-path ] claude-mailbox uninstall-service [--purge] ``` The .NET and Node builds are wire-compatible (same port, same `X-Mailbox` header, same MCP tool names, same SQLite schema), so a `.mcp.json` configured against one works against the other. ## Use from Claude Code (plugin) Easiest path — three prompts, all inside Claude Code: ``` /plugin marketplace add https://git.kuns.dev/releases/ClaudeMailbox /plugin install claude-mailbox@claude-mailbox /claude-mailbox:mailbox-doctor ``` The doctor command auto-installs the daemon binary via npm (asks first), registers autostart, optionally takes a base prefix (e.g. `backend`), and runs a smoke test. Subsequent slash commands: - `/claude-mailbox:mailbox-status` — read-only health check - `/claude-mailbox:mailbox-update` — pull the latest daemon version and restart **Each Claude session gets its own mailbox identity** derived from the session's UUID — `claude-a8b3c1d2` by default, or `-a8b3c1d2` if you set a prefix. Parallel sessions in the same project automatically get distinct names. The `SessionStart` hook announces the current session's name in context so Claude knows its own identity and which args to pass to MCP tools. The plugin auto-wires the MCP server too. Because two parallel sessions share one `.mcp.json`, the MCP tools take the caller's mailbox name as an **explicit argument** (`from` / `name`) instead of relying on a shared HTTP header — so multi-session coordination just works: ``` You: "I started a second session, work together on this." Claude (session A): looks up peers via mcp__mailbox__list_mailboxes(name=""), sends via mcp__mailbox__send(from="", to="", body="...") Claude (session B): receives the message on the next prompt via the UserPromptSubmit hook. ``` If the daemon goes down later, the hook emits a one-line setup hint instead of staying silent. See [`plugin/README.md`](./plugin/README.md) for the full walkthrough. ## Use from a Claude session (without the plugin) If you're not using the Claude Code plugin, drop this into your project's `.mcp.json`: ```json { "mcpServers": { "mailbox": { "type": "http", "url": "http://127.0.0.1:47822/mcp" } } } ``` The MCP tools take the caller's identity as an argument. If you want the legacy single-session style where every call uses the same mailbox name without specifying it, add a header (Claude then doesn't need to pass `from` / `name`): ```json "headers": { "X-Mailbox": "backend" } ``` Four MCP tools are exposed: | Tool | Purpose | |---|---| | `mcp__mailbox__send(from, to, body)` | Send a message. `from` is your mailbox; falls back to X-Mailbox header. | | `mcp__mailbox__check_inbox(name)` | Pull all pending messages for `name` (marks delivered). Falls back to X-Mailbox header. | | `mcp__mailbox__peek_inbox(name)` | Non-consuming check — returns `{ pending, oldestAt }`. Falls back to X-Mailbox header. | | `mcp__mailbox__list_mailboxes(name)` | Discover known mailboxes; `name` is needed for accurate `pendingForYou`. Falls back to X-Mailbox header. | ### Suggested CLAUDE.md snippet for poll discipline ``` When coordinating with a peer session, call mcp__mailbox__peek_inbox after each subagent completes. If pending > 0, call mcp__mailbox__check_inbox and treat the messages as input with priority over the current plan. ``` ## CLI client mode Any external process (scripts, UIs, hooks) can talk to a running daemon without needing MCP: ``` claude-mailbox send --to --from --body [--url http://127.0.0.1:47822] claude-mailbox peek --name [--url ...] claude-mailbox check --name [--url ...] claude-mailbox list [--url ...] ``` The CLI subcommands are thin HTTP clients against the `/v1/*` endpoints. ## REST surface | Method | Path | Requires `X-Mailbox` | Purpose | |---|---|---|---| | `GET` | `/health` | no | `{ status, version, dbPath }` | | `POST` | `/v1/send` | yes (sender) | `{ to, body }` | | `GET` | `/v1/peek?name=` | no | read-only status | | `POST` | `/v1/check-inbox?name=` | yes (must match `name`) | consume inbox | | `GET` | `/v1/list` | no | list all mailboxes | ## Development ``` dotnet build dotnet test tests/ClaudeMailbox.Tests/ClaudeMailbox.Tests.csproj dotnet run --project src/ClaudeMailbox -- serve ``` Test suite covers end-to-end coordination, concurrent `check_inbox` race safety, and schema idempotency. ## Scope - Loopback bind only (v1). Cross-machine coordination is a future extension — swap the middleware for token auth and change the bind address. - No auth on loopback. Local filesystem permissions are the trust boundary. - No message expiry or cleanup. Delivered messages stay as a timeline/audit log.