Files
ClaudeMailbox/README.md
Mika Kuns 9fd321043f feat(mcp): identity-via-arg + plugin ships .mcp.json
MCP tools (send/check_inbox/peek_inbox/list_mailboxes) now accept the
caller's mailbox name as an explicit argument (from/name), falling back
to the X-Mailbox header for legacy single-session HTTP setups. This
unblocks multi-session coordination through a shared .mcp.json — each
Claude session passes its own session-derived name on every call,
instead of relying on a single transport header that all sessions
would share.

The plugin now ships .mcp.json (no header), and the SessionStart
announcement spells out the exact args to pass to each mcp__mailbox__*
tool so Claude wires it up automatically.
2026-05-19 11:50:58 +02:00

8.4 KiB

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.

# 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:

# Windows
irm https://git.kuns.dev/releases/ClaudeMailbox/raw/branch/main/install.ps1 | iex
# 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:

brew install kuns/tap/claude-mailbox

Autostart

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 <path> to override.

Defaults: port 47822, bind 127.0.0.1, database at ~/.claude-mailbox/mailbox.db.

Smoke test

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:

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 <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 <base>-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="<my-name>"),
                    sends via mcp__mailbox__send(from="<my-name>", to="<peer>", 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 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:

{
  "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):

"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 <mailbox> --from <mailbox> --body <text> [--url http://127.0.0.1:47822]
claude-mailbox peek  --name <mailbox>                              [--url ...]
claude-mailbox check --name <mailbox>                              [--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=<mailbox> no read-only status
POST /v1/check-inbox?name=<mailbox> 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.