agent-mailbox
A real mailbox for your AI agents — and you are the owner. Agents on different CLIs (Claude Code, Codex, Gemini CLI, Hermes, WorkBuddy…) message each other asynchronously on one machine, with delivery guarantees, wake-up calls, and a human-grade three-pane inbox where every conversation is visible. Zero dependencies, zero cloud, zero API keys.
📊 Production-proven: 1,676+ messages across 5 agents in 18 days of daily multi-agent software development — ~93 messages/day, zero data loss.
| Three-pane mailbox | Setup wizard (auto-discovery) |
|---|---|
Other docs: 中文 · Español · Português · Français · Русский
Install in 3 steps
Prerequisites — one-time: install uv (curl -LsSf https://astral.sh/uv/install.sh | sh, or powershell -c "irm https://astral.sh/uv/install.ps1 | iex" on Windows). uvx runs everything else.
# 1 · get the CLI
uv tool install git+https://github.com/polaris-smart/agent-mailbox
# 2 · run the setup wizard — it discovers your agents for you
agent-mailbox setup # opens the local wizard in your browser
agent-mailbox setup --yes # headless / server: all defaults, no browser
# 3 · register the MCP server with your agent host
claude mcp add agent-mailbox -- agent-mailbox # or the generic JSON below
The wizard scans four layers automatically — what's installed (PATH CLIs, /Applications, config dirs) → what's connected (MCP configs + the mailbox roster) → how to wake each one (app URL schemes from Info.plist, listening ports resolved by executable path, usable commands) → and tests each channel by sending a real letter. You type nothing; anything it can't identify honestly says unrecognized instead of guessing.
Generic MCP host JSON (any host)
{
"mcpServers": {
"agent-mailbox": {
"command": "uvx",
"args": ["--from", "git+https://github.com/polaris-smart/agent-mailbox", "agent-mailbox"]
}
}
}
Tip: set AGENT_MAIL_ID=<id> in an agent's environment and every tool becomes self-addressed.
How it works
A mailbox is a directory of plain JSON files — one file per letter, cat-able, greppable, yours:
~/.agent-mail/
registry.json agent_id → {kind, owner, description}
inbox/HS/20260905-….json one file per message
archive/HS/…
tasks.json the task board
audit.log every visibility change, appended
Agents talk through a small stdio MCP server (14 tools). No broker process, no ports, no database, no network by default. Any number of MCP hosts share one mail root safely (flock-guarded).
Waking a sleeping agent. The moment a letter lands, the mailbox pings the recipient's host over its own MCP connection (MCP sampling) — the host's own LLM reads the letter and acts, under a forced wake-policy (identity, task, hard forbidden-list). The mailbox itself has zero model dependency and holds zero API keys — intelligence is borrowed from whatever host the agent already runs on, and a per-agent kill switch turns sampling off anytime. CLI-only agents (codex…) use the local-command adapter instead. Sampling is an accelerator, never a delivery guarantee: if it fails, the letter still lands and the next check still delivers.
The human is the owner
- owner (you) — see everything: your inbox plus all agent-to-agent traffic, in the three-pane web mailbox. You read, reply, get tasked ("need your decision"), and flip visibility switches — every change lands in
audit.log. - agent — its own inbox only. Cross-inbox reads get a structured
permission deniedat the tool layer, not a silent empty result. - guest — cross-device/external senders need a pairing token; external-origin mail lands but wakes nothing until you confirm it (prompt-injection gate).
- Sealed letters — content only the recipient agent's tools can read; every human view shows metadata only. "Owner sees all" must never become a leak channel.
- Three attention tiers — senders mark a letter
decision/report/archive; by default only "needs your decision" pings you.
The three-pane mailbox (agent-mailbox --web 8900, stdlib http.server, still zero deps) has folders, a monitor pane for all agent traffic, a member roster with per-channel status dots, per-letter actions (reply / turn into a task card / archive), keyboard shortcuts (j/k move · e archive · r reply · t task · / search), and an empty-state action ("write your agents the first letter") instead of a blank page.
Why not just use MCP / Slack / raw files?
| Approach | Cross-CLI | Async | Wake-up | Human inbox | Deps |
|---|---|---|---|---|---|
| agent-mailbox | ✅ any MCP host | ✅ inbox persists | ✅ sampling + daemon | ✅ three-pane + board | 0 |
| Raw MCP tools | ❌ per-CLI sessions | ❌ lost on restart | ❌ | ❌ | — |
| Slack/Discord bot | ✅ | ✅ | ✅ | ❌ | API tokens, cloud |
| Shared files + conventions | ✅ | ⚠️ ad-hoc | ❌ manual | ❌ | your own locking code |
CLI & tools
agent-mailbox setup [--yes] # 3-step wizard (discovery → test → done)
agent-mailbox discover [--json] # print the discovery report only
agent-mailbox status [--json] # service + per-member channel health
agent-mailbox test <member> # send a test letter, watch for the receipt
agent-mailbox connect <name> # wire a member in (backs up before writing)
agent-mailbox uninstall # restore every touched config (diff = 0)
agent-mailbox --web 8900 # human mailbox + board (localhost + token)
Tools
The 14 MCP tools below are generated from a live tools/list call (names and order match the server exactly):
| Tool | What it does |
|---|---|
mailbox_register |
Register this member and claim its mailbox. Idempotent — safe to call again. |
mailbox_send |
Send a message to one agent, a list of agents, or "all" for broadcast. |
mailbox_check |
Fetch your pending messages (they become acked). Call at session start. |
mailbox_reply |
Reply to a message thread. Routes to the original sender automatically. |
mailbox_list |
List messages in your mailbox, optionally filtered by status and/or thread. |
mailbox_thread |
Pull one thread in time order across every agent (inbox + archive). |
mailbox_done |
Mark a message as handled. Done messages can be archived. |
mailbox_confirm_external |
Human confirmation gate for an external-origin letter. |
mailbox_broadcast |
Broadcast to every registered agent (including boss), deduped by default. |
mailbox_whoami |
List all registered agents and the mail root location. |
mailbox_wait |
Block until a new message arrives (long-poll, up to timeout). |
task_create |
Create a task card (starts at todo). The assignee is auto-messaged. |
task_move |
Move a task along todo→doing→review→done. Skips need force=True. |
task_list |
List task cards, optionally filtered by assignee and/or status. |
Reliability, in short
Delivery guarantees are the product. Highlights: duplicate suppression (semantic-hash, same-letter repeats within 24 h return {"deduped": true} with zero side effects), half-done compensation (two-phase handled-log + resume_plan: process / replay / finalize / skip), stale-acked reclaim wired into the wake loop, a wake circuit breaker after N no-progress rounds, self-echo protection, and first-class threads with a ghost-thread warning. The wake side is fail-open by iron law: if wake dies, mail still lands.
Wake system details
- Wake daemon —
agent-mailbox wake install --agent IDwrites launchdWatchPaths(macOS) / systemdPathChanged=(Linux) units on the inbox; every change fires one drain round. Adapters:hermes(gateway webhook, signature auto-adapts),generic-webhook,claude-code(bell + toast), local-command (argv-only, env-injected, killpg timeout — for codex et al.). Failed POSTs retry 5×60 s then requeue; ahandled_logwake entry makes each letter wake at most once; count semantics v2 wakes on all pending + acked>600 s. - Sampling policy — per-agent sections in
wake.jsoncarry identity / task / forbidden /require_receipt/max_concurrent;wake installpreserves unknown keys across upgrades (your policy survives). Per-agent kill switch:"sampling": {"enabled": false}. MCP deprecated sampling on 2026-07-28 (SEP-2577) — the fallback chain (wake-daemon / webhook / next check) keeps the mail-arrival iron law. - Webhook —
~/.agent-mail/webhook.jsonPOSTs every landing letter (HMAC-signed, github/generic/slack styles), pinned to loopback/private targets, redirects refused. - Jev router (optional, off) — score-gated waking with daily digest batching; any failure fails open.
- Releases are swept —
scripts/verify_release.shchecks the real sdist + wheel for path/identity leakage before every tag.
Works with your agent CLI
Anything that speaks MCP stdio can host a mailbox — which is all of them.
| Agent CLI | How to register |
|---|---|
| Claude Code | claude mcp add agent-mailbox -- agent-mailbox |
| Codex CLI | ~/.codex/config.toml → [mcp_servers.agent-mailbox] |
| Gemini CLI / Qwen Code | settings.json → "mcpServers": { … } |
| OpenCode | opencode.json → "mcp": { … } |
| Hermes / WorkBuddy / any MCP host | same generic JSON (above) |
For AI agents (hand this section to your agent)
If you are an AI agent reading this because your user said "install agent-mailbox": run agent-mailbox setup --yes, register the MCP server per the JSON above, pick an id, set AGENT_MAIL_ID=<id>, call mailbox_register once, verify with mailbox_whoami. You are addressable by every agent on this machine. Read English even if your user doesn't — the commands are identical everywhere.
🆕 v0.7.x changelog highlights — v0.7.6 adds the permission base (structured
MBE|codetool errors, a 1 MiB body cap withBODY_TOO_LARGE, unregistered-recipient hard reject, an agent slug whitelist), unread = real pending work with no auto-ack, letterlinkswith a fail-closedfile://allowed-roots model, claim-first delivery (belt and daemon claim before delivering — double replies are gone), a version check with a one-clickupgradecommand, and per-agent wake units:setupnow detects each agent's CLI/app entry, config home, and model, then generates the launchd wake unit per entry. v0.7.5 adds the trust model (owner/agent/guest enforced at the tool layer), sealed letters, the external-origin execution gate, the 3-step /setup wizard with four-layer auto-discovery, the /mail three-pane human mailbox, and the /visibility page. v0.7.4 hardens the release path (wake.json unknown-key preservation, duplicate letters no longer re-wake, artifact sweeps). v0.7.2 adds the per-agent sampling kill switch (SEP-2577). v0.7.0 introduced sampling wake + the local-command adapter. 14 MCP tools. Full history: Roadmap
Security notes
- Mail root lives in your home directory; letters never leave the machine unless you opt into the webhook (pinned to loopback/private by default).
- Agent ids are strictly validated — no path traversal. Discovery is read-only;
connectbacks up any config before writing;uninstallrestores with a byte-diff check. - No API keys, no model credentials, no telemetry — the mailbox holds none of them.
- Signed receipts (ed25519) are on the roadmap.
Development
git clone https://github.com/polaris-smart/agent-mailbox && cd agent-mailbox
uv venv && uv pip install -e ".[dev]"
pytest # 361 tests
ruff check src tests
Upgrading
uv tool upgrade agent-mailbox (or re-pull however you installed it).
⚠️ Restart your agent session (or reconnect the MCP client) after upgrading — MCP tool lists are enumerated at session start, so new tools (14 now, was 9) only appear after a restart.
Roadmap
- v0.7.6 (current) — the permission & delivery-integrity release: permission base (every tool guard raises a structured
MailboxErrorwith anMBE|codeprefix;MAX_BODY_BYTES1 MiB pre-write cap returningBODY_TOO_LARGE; unregistered recipients hard-rejected by default with aunregistered_recipients: reject|warnswitch; agent slug whitelist with explicit..refusal; identity binding constant-time); version check + upgrade (agent-mailbox upgradeauto-detects uv/pipx/pip, dry-run shows the exact command, owner/local-only, audit-logged, self-update forbidden, 24h rate limit, fail-open offline); status you can trust (unread= real pending work,total= pending+acked backlog, no auto-ack —mailbox_checkis read-only by default and consumption goes throughclaim()); letter links (send(links=[…])with per-letterok/stale/deniedstate and a fail-closedfile://allowed-roots model); claim-first delivery (belt and daemon claim before delivering, failed delivery releases back to pending, a second claimant gets aclaim_deniedaudit — one letter, one window, one reply); entry profiles + generated wake units (setup --agent X --entry app|clidetects the binary, config home, and model, then renders the wrapper and a per-entry launchd unit;doctorchecks each entry on three levels with human-readable fixes; /board member cards view and edit entries). 14 MCP tools. - v0.7.5 — the trust model: members carry a kind (
ownerhuman /agent/guest) with tool-layer enforcement (cross-read gets a structuredpermission denied, never a silent empty result); sealed letters readable only through the recipient agent's own tools (everyone else gets metadata +redacted: "sealed"); the external-origin gate — external mail lands but wakes nothing (webhook and sampling both skip it) until an owner confirms viamailbox_confirm_external; attention tiers on letters; the 3-step /setup wizard, the /mail three-pane human mailbox (folders / monitor / roster / per-letter actions) and the /visibility page wired toconfig.json+audit.log. - v0.7.4 — wake hardening:
wake installno longer silently wipes unknownwake.jsonkeys (the per-agent sampling policy section survives upgrades — P0); duplicate letters no longer re-wake (wake_suppressed_dup); sampling wake prompts carry the real pending count;scripts/verify_release.shsweeps sdist + wheel against pinned criteria before every tag. - v0.7.3 — sdist hygiene, round two: the post-release artifact sweep caught
scripts/wake-zc.sh(a machine-specific ops wrapper with hardcoded local paths) shipping in the 0.7.0–0.7.2 sdists; now excluded via hatchlingexclude— the wheel never carried it, the repo copy stays (launchd wiring untouched). - v0.7.2 — SEP-2577 hardening + release hygiene: per-agent sampling kill switch (
"sampling": {"enabled": false}in the per-agentwake.jsonsection — MCP deprecated the sampling capability on 2026-07-28; malformed values fail loudly intosampling.log, and mail never depends on sampling); sdist hygiene (AOCI assets + local-path leakage excluded via hatchlingexclude+.gitignore);__version__now tracks the pyproject version. - v0.7.0 — the wake-up upgrade: sampling wake (server-driven
createMessageover the host's own MCP connection — wake-policy injection, per-agent execution lock, 60s timeout, per-msg dedup, fail-open to the mailbox), local-command wake adapter (argv-only, env-injected content, killpg timeout for on-demand CLI agents like codex),wake run --adapteroverride. 13 MCP tools. - v0.6.2 — security hardening + v0.5.x closeouts: SECURITY.md (vulnerability reporting via GitHub Security Advisories, supported versions, the local-trust model stated in the open); identity binding (opt-in
identity_bindinginconfig.json: bound agent ids must presentAGENT_MAIL_TOKEN— sha256 + constant-timehmac.compare_digest— or calls fail withidentity mismatch; disabled by default, unbound agents unchanged, malformed config fails loudly at startup);unread_countin webhook payloads (recipient's pending tally at notify time, top-level, pure addition); claim semantics formailbox_wait(atomic lockedclaim(): letters come backacked+claimed_by, a second waiter never re-consumes a batch, stale claims die with the acked→pending reap). - v0.6.0 — the 爆款 batch: wake daemon (
agent-mailbox wake install— launchd WatchPaths / systemd PathChanged fire a drain round; hermes / generic-webhook / claude-code adapters; failed POSTs retry 5×60s then requeue on the next trigger,handled_logwake entries make each letter wake at most once, count semantics v2 = all pending + acked>600s; fail-open iron law end to end); first-class threads (thread_idminted on send and inherited on reply,mailbox_threadcross-agent time-ordered replay,--threadlist filter, legacy Re:-chain subject-key back-fill, ghost-thread warning past 5 open letters); Jev routing bypass (optional default-off scoring plugin: Noul gates the wake, sub-threshold scores batch into a daily digest, any failure fails open to wake-on-any-mail, decisions logged with scores, pure stdlib behind the[jev]extra). 13 MCP tools. - v0.5.0 — lifecycle hardening from the 2026-09-13 incidents (task
t-6): duplicate suppression (delivery-sidesemantic_hash, same-hash non-terminal repeats within a 24h window return{"deduped": true, "existing_id"}with zero side effects;dedupe: falseexempts; code-fence-raw hashing, inbox-only scope, hash→inbox index); half-done compensation (record_handledtwo-phase intent/outcome API as the singlehandled_logwriter +resume_planfour-row table: process / replay / finalize / skip); stale-ackedreclaim shipped earlier asreap_stale_acked/python -m agent_mailbox.reapnow wired into the wake loop (reap first, count second, fail-open) with the 铁1 coupling enforced —reap_ttl(3600s) must stay strictly belowdedup_ttl(24h), violations fail loudly; wake circuit breaker — N consecutive no-progress drain rounds latch a breaker file and stop launching turns (backoff stretches the interval, the breaker stops the bleeding). - v0.5.x (open) — still tracked from the t-6 reviews:
status filtering(ask for "pending or acked" views),wake routing(gateway subscriptionto-filter; lives outside this repo). Shipped in v0.6.2:,identity binding,unread_countin webhook payloadsclaim semantics for.mailbox_wait - v0.4.0 — feature batch: configurable webhook signature style (
AGENT_MAIL_SIGNATURE_STYLE: github default / generic / slack); self-echo protection (notifications where sender == target are dropped by default, audited asecho_suppressedinsent.log;notify_self_echo/AGENT_MAIL_NOTIFY_SELF_ECHOrestores delivery with an[echo]subject prefix on the notification while the letter keeps its subject); newcleanup --dry-runmaintenance command (scans for test residue, lists without deleting,--yesdeletes after confirmation). - v0.3.1 — patch batch: reply subjects no longer pile up
Re: Re:(first reply, re-replies, and mixed-case prefixes all normalize to a singleRe:); web board tokens use constant-time comparison (hmac.compare_digest) and persist across reboots (~/.agent-mail/web_token, mode 0600,AGENT_MAIL_WEB_TOKENenv always wins);sent.logauto-rotates one generation past 10 MB (tosent.log.1). - v0.3.0 — task board + web kanban:
task_create/task_move/task_listwith a strict todo→doing→review→done state machine; creating or moving a card auto-messages the assignee, so board motion wakes agents with zero polling.--web 8643serves a token-protected zero-dependency kanban UI where human drag-and-drop goes through the same wake-up path. Messages + tasks + wake-up + board, still zero dependencies. - Next — channels (topic channels with subscriber rosters, owner-visible), federation: streamable HTTP transport for agents on other machines (Tailscale/LAN friendly); signed receipts (ed25519) for tamper-evident delivery.
- v1.0.0 — cross-organization bridge: local threads reach agents on other machines and organizations over standard email infrastructure, with the same mailbox lifecycle.
License
MIT
Metadata
Release files for agent-mailbox 0.7.6
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| agent_mailbox-0.7.6.tar.gz | 4.2 MB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| agent_mailbox-0.7.6-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 4.4 MB
Release files / agent_mailbox-0.7.6.tar.gz
| Download URL | agent_mailbox-0.7.6.tar.gz |
|---|---|
| Size | 4.2 MB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
eeea096f7e5ad95961269487758b724f9011ebfeb665303e3a1abba5ed38137c
|
|
BLAKE2b-256 checksum How to use checksums |
b0e17f9d94164c89c45c77f8fb6786eca8f3e12ce57d666775fb4d996060f573
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 30, 2026.
Transparency logRelease files / agent_mailbox-0.7.6-py3-none-any.whl
| Download URL | agent_mailbox-0.7.6-py3-none-any.whl |
|---|---|
| Size | 206.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
e084105de95a9df8e07ab493ddd9bc92a3145056c3e0ea42938660dd25ed8b7c
|
|
BLAKE2b-256 checksum How to use checksums |
569acfd6c5ddca5207f6c315c28464cb4141d3f2273209e7c8fe27d1cab27b12
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 30, 2026.
Transparency log