Self-hosted HTTP API and MCP server that reads, organizes, drafts, replies to and sends mail acro...
8.3K
Read, organize, draft and send mail across multiple IMAP/SMTP accounts through one HTTP API and an MCP endpoint at /mcp, on the same port. Each bearer token controls which operations and mailboxes a client can access.
For agent access, configure tokens with explicit permissions:
read, draft and organize cannot send or permanently delete. Over MCP it does not even see the send tools or the permanent flag.delete scope and only works from the Trash folder. The service never issues a folder-wide EXPUNGE.send) sends it.Mail bodies and drafts stay on IMAP. A shared SQLite database stores audit records, send counters, checkpoints, draft send claims and webhook delivery metadata. API requests close their mail-server connections after use; the watcher keeps an IMAP connection open for notifications.
The service uses Python's imaplib and smtplib, FastAPI, the official MCP Python SDK and nh3 for HTML sanitizing. Dependency updates enforce a seven-day release-age cutoff through uv's exclude-newer setting.
config.example.yaml to config.yaml and .env.example to .env. Configure your accounts and replace every placeholder in .env with its password or a fresh token from openssl rand -hex 32. Restrict both files with chmod 600.docker run --rm \
-p 127.0.0.1:8000:8000 \
--env-file .env \
-v "$PWD/config.yaml:/etc/mailboxd/config.yaml:ro" \
-v mailboxd-state:/var/lib/mailboxd \
psyb0t/mailbox:latest
TOKEN="paste-the-token-here"
# Health is always open, no token needed.
curl -s http://localhost:8000/health | jq
curl -s \
-H "Authorization: Bearer $TOKEN" \
http://localhost:8000/mailboxes | jq
curl -s \
-H "Authorization: Bearer $TOKEN" \
'http://localhost:8000/inbox?limit=5' | jq
The checked-in docker-compose.yml publishes on 127.0.0.1:${PORT:-8000}, mounts ./config.yaml (or $MAILBOXD_CONFIG) read-only and creates the mailboxd-state volume:
services:
mailboxd:
image: psyb0t/mailbox:latest
ports: ["127.0.0.1:8000:8000"]
volumes:
# Keep config.yaml out of Git and restrict access to its credentials.
- ./config.yaml:/etc/mailboxd/config.yaml:ro
- mailboxd-state:/var/lib/mailboxd
volumes:
mailboxd-state:
The optional webhook watcher is a second service in the same file, off by default: docker compose --profile watch up -d.
| Area | Operations |
|---|---|
| Reading | Unified inbox, structured search, raw source, headers, attachments and threads |
| Organization | Seen flags, labels, move, mailbox creation and Trash |
| Sending | Configured identities, signatures, replies, IMAP drafts and Sent copies |
| Access | Per-token scopes and mailbox allowlists shared by REST and MCP |
| Operations | SQLite audit records, shared send limits, checkpoints and durable signed webhooks |
Mount /var/lib/mailboxd for persistent operational state. HTTP workers and the optional watcher share one SQLite database on a local filesystem. Read the upgrade notes before replacing a stateless deployment.
The full documentation lives at psyb0t.github.io/docker-mailbox.
| Page | What is in it |
|---|---|
| Installation | Docker, Compose, the webhook watcher, image tags, CLI and environment variables |
| Configuration | Every config field: mailboxes, tokens and scopes, identities, limits, labels |
| HTTP API | Routes, parameters, bodies, responses and error codes |
| MCP server | Tools, scopes, annotations and client setup |
| Safety and security | Deletion, drafts, dry runs, rate limits, sender checks, audit, TLS |
| Webhooks | Event payload, signature verification, retries and replay |
| Agent integrations | Claude Code, Codex and OpenClaw |
| Operations | State, backups, health checks and upgrade notes |
| Troubleshooting | Startup errors and common API failures |
make lint # flake8 and mypy
make test-coverage # full suite, production container and coverage floor
make audit # frozen runtime and dev dependency advisories
make build # production image
make help # all available targets
All project tools run inside the development image. Tests use socket-level IMAP/SMTP fixtures, a local HTTP receiver, real SQLite files and a production-container smoke test. See development for details.
WTFPL. See LICENSE and third-party components.
Content type
Image
Digest
sha256:eb1fdc397…
Size
63.1 MB
Last updated
about 2 hours ago
docker pull psyb0t/mailbox