Use this command to install Agent Mail CLI with WinGet:
winget install --id=JuanjoFuchs.agent-mail-cli -e
Agent Mail CLI is a local inbox tool designed to facilitate communication and coordination between coding agents. It operates using a SQLite-backed database, enabling developers to create, send, read, acknowledge, and inspect messages locally without requiring external servers or complex setups.
Key Features:
Runtime schema introspection: Agents can learn how to interact with the mailbox by running a single command, eliminating the need for manual documentation.
JSON output by default: The tool provides structured data formats for seamless integration into automated workflows.
Local durable storage: Messages are stored persistently in a SQLite database, ensuring reliability and availability across sessions.
No registration or daemon requirements: Agents can use Agent Mail CLI without additional overhead or setup.
Audience & Benefit:
Ideal for developers working on multi-agent systems to streamline communication and coordination. It enables agents to exchange information efficiently, reducing friction in coding workflows. By eliminating the need for MCP servers or external daemons, it simplifies development and testing environments.
Agent Mail CLI can be installed via winget, making it accessible across platforms with minimal effort.
README
Agent Mail CLI
A self-describing local inbox for coding agents.
npx -y @juanjofuchs/agent-mail describe
That command is the product wedge: an agent can run it, read the JSON schema,
and learn how to send, read, acknowledge, and inspect messages without MCP
setup, a daemon, or separate documentation.
Status
This repository is the open-source extraction of a working internal tool.
src/agent_mail/cli.py is the Python implementation and source of truth for
behavior. Spec 001 is the behavioral specification. Spec 002 covers Python
packaging, GitHub Release binaries, and WinGet. Spec 003 covers npm and npx.
Multi-agent coding workflows need coordination. Heavy systems already exist
for that: MCP servers, agent frameworks, workspace managers, and network
protocols.
Agent Mail CLI is aimed at the simpler moment:
> I am already inside Claude Code or Codex. I need this agent to send a
> handoff to that agent. I want one command that teaches both sides the
> mailbox.
Installation
npx
The primary experience is one command. The npm package name is scoped because
npm rejected the unscoped agent-mail and agent-mail-cli names; the installed
command remains agent-mail.
The npm package also exposes agent-mail-cli as an alias for compatibility:
agent-mail-cli describe
pipx
pipx install agent-mail-cli
agent-mail describe
For one-shot Python execution:
pipx run --spec agent-mail-cli agent-mail describe
From source:
python -m agent_mail describe
WinGet
WinGet support has been submitted and is waiting on Microsoft's package review.
After approval:
winget install JuanjoFuchs.agent-mail-cli
Intended Usage
Sender:
npx -y @juanjofuchs/agent-mail send --from research:lead --to docs:writer --subject "Review spec" --body "Please read the referenced spec and report risks."
Recipient:
npx -y @juanjofuchs/agent-mail read docs:writer
A message is coordination, not content: the body is capped at 2000 characters
and the substance goes in a file the message points at. Attach paths with
--ref, repeated once per path:
npx -y @juanjofuchs/agent-mail send --from analysis:researcher --to research:lead \
--subject "Benchmark rerun done: p95 +18%" \
--body "Regression is query-specific, not global. Numbers and method in the refs." \
--ref research/benchmarks/FINDINGS.md --ref research/benchmarks/method.md
Each path is resolved to an absolute path and must exist when you send, so the
recipient can open it from another project. --refs still takes a JSON array
for compatibility, but prefer --ref: JSON inside a single argument has to
survive the shell, and on Windows it cannot — Git Bash collapses the backslash
escaping a Windows path needs, so the JSON that arrives is invalid however you
quote it. One path per argument has no escaping layer to survive.
If a send has several problems, you get all of them at once rather than one per
attempt.
Recipient staying reachable without a timed poll loop. watch blocks until mail
arrives, so waiting costs nothing — the process sleeps, not the model:
# Blocks up to 55 minutes, or returns an empty result. Use --once to check
# without blocking; the wait is in MINUTES (--timeout-minutes, floor 5).
npx -y @juanjofuchs/agent-mail watch docs:writer
# watch reports envelopes and marks nothing read — always follow it with read.
npx -y @juanjofuchs/agent-mail read docs:writer
The default is 55 rather than a round hour on purpose: a blocked watch makes no
API calls, so nothing refreshes the caller's prompt cache while it waits. 55
minutes is the longest wait that still wakes inside a one-hour cache window,
where the context is re-read at cache-read price instead of rewritten.
Every result tells you what to do next
The failure that costs the most is an agent that stops watching: mail is still
accepted for it, the sender sees no error, and the only symptom is silence. So
watch, read, and ack each answer with reachable and a next list of
literal commands, computed from the state that call just observed:
next shrinks as steps get done — the ack line appears only when something is
genuinely outstanding, the watch line only when nothing is armed. Guidance keyed
to state beats guidance keyed to memory: describe is read once at orientation,
and by the time it matters an agent is holding fifty other instructions.
Acknowledging is one call for a whole batch, so it is not a per-message chore:
Replying acknowledges what it answers, since a reply is evidence the message was
acted on:
npx -y @juanjofuchs/agent-mail send --from docs:writer --to research:lead \
--subject "Reviewed" --body "Two risks, in the refs." --reply-to
Sender checking whether a recipient is actually listening, rather than guessing
from silence:
npx -y @juanjofuchs/agent-mail status --agent docs:writer --fields agent,watching,last_seen
Watching a fleet of agents
agent-mail view is a live dashboard for humans — the one command that answers
"who is stuck?" without reading a single message. It is read-only: it opens the
mailbox in read-only mode and never marks anything read, so watching cannot
consume mail addressed to an agent.
Agents are ranked by frecency, so the team currently working fills the window.
wait is how long the oldest unread message has sat there, which is the signal
that an inbox is piling up: above, docs:writer was asked three times, stopped
watching 26 minutes ago, and has acknowledged nothing. Live agents are always
shown, even when they are quiet.
Press tab for the flow panel — directed edges, so A ▸ B and B ▸ A are
separate facts, with !n counting what the recipient never acknowledged:
agent-mail view # 1s refresh, q to quit
agent-mail view --window 120 # widen the history window
agent-mail view --once # print one frame and exit
agent-mail view --ascii --no-color # plain terminals
view is deliberately absent from describe: it blocks by design, and an agent
that found it in its schema would hang a turn on a TUI. It is for you, not them.
Design Goals
Runtime schema introspection through describe
JSON output by default
JSON errors on stderr
Local durable mailbox state
No registration
No daemon
No MCP server required for v1
Stable storage outside npm cache (post-packaging)
One-command install for users without the source script
One rule the surface keeps re-learning
A surface that lets an agent choose wrongly will be chosen wrongly. Remove the
choice, or make the wrong value unreachable — do not document your way out of
it. The documentation had been correct and ignored for months in every case:
The wrong choice
What was tried first
What actually worked
--timeout 30 (seconds, off by 60×)
A correct 900-second default
Put the unit in the name: --timeout-minutes, floor 5
A 24-hour TTL nobody chose
Documenting the expiry
Delete the TTL; cleanup --older-than is now the only deletion
Pasting a document into the body
A content_routing rule since v1
Cap the body at 2000 chars and reject, naming the fix
Never acknowledging
An invariant explaining ack
One call for a batch, and replying acks for free
Never re-arming the watch
An invariant saying "re-arm"
Every result carries next, computed from current state