Multi-mailbox IMAP/SMTP control plane. One YAML, N accounts, one bearer-auth gate. Unified inbox across Gmail/Outlook/iCloud/mailo/inbox.lv/whatever — search, read, mark, send. Reader mode strips HTML chrome for LLMs. Streamable-HTTP MCP server + REST API on one port. Ships as a Docker image.
claude mcp add docker-mailbox -- uvx docker-mailbox{
"mcpServers": {
"docker-mailbox": {
"command": "uvx",
"args": ["docker-mailbox"]
}
}
}MCP Servers overview
# mailbox
[](https://hub.docker.com/r/psyb0t/mailbox)
[](LICENSE)
[](https://www.python.org/downloads/)
[](https://fastapi.tiangolo.com/)
Your inboxes, on tap. Point this thing at as many email accounts as you want over IMAP + SMTP, and out the other end you get **one HTTP API and one MCP server, both on the same port** (MCP rides a streamable-HTTP channel at `/mcp`) so you can read mail, send mail, and nuke mail across every account from one place. No webmail. No database. No three-thousand-toggle desktop app. Just: "here's some email creds" → "now my agent / shell script / chaotic 3am curl pipeline can drive the inbox."
Every other "unified inbox" thing on the planet wants to *own* your mail — slurp it all into their cloud, charge you forever, lose it in a breach next quarter. This one stores **zero bytes**. Restart the container, nothing's lost, because there was never anything to lose. Connections come up per request, do their job, and die in a `finally`.
Stdlib `imaplib` + `smtplib` under the hood, FastAPI on top, official MCP Python SDK riding shotgun (streamable HTTP, no stdio nonsense), a supply-chain `exclude-newer` gate so a malicious pip release published at 3am can't sneak in, and a real-SMTP-server integration test that actually puts bytes on a socket.
## Table of Contents
- [What's Inside](#whats-inside)
- [Quick Start](#quick-start)
- [Configuration](#configuration)
- [HTTP API](#http-api)
- [Authentication](#authentication)
- [MCP server](#mcp-server)
- [Architecture](#architecture)
- [Development](#development)
- [License](#license)
## What's Inside
| Surface | The goods |
| --------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- |
| **HTTP API** | `GET /inbox` fans out across every account at once (filter by mailbox, sender, subject, date, flags…). Per-mailbox reads + deletes. SMTP send. |
| **MCP server** | Streamable-HTTP MCP at `/mcp` — same port, same bearer, same boss. A flat set of tools (`mailboxes`, `inbox`, `list_messages`, `send`, …) that take `mailbox` as a parameter. 100 accounts? Still one tool catalog. |
| **Bearer auth** | One token list in YAML guards both the API and `/mcp`. Empty list = wide open (your problem). Multiple tokens = zero-downtime rotation. |
| **Protocols** | IMAP (SSL / STARTTLS / plain), SMTP (SSL / STARTTLS / plain). Standards-boring on purpose. |
| **Config** | One YAML file. Add a mailbox, restart, done. Each one declares whichever subset of `{imap, smtp}` you actually care about. |
| **State** | None. Truly none. No DB, no queue, no cache, no "oh just this little Redis." A connection opens, does the work, closes. Next. |
## Quick Start
1. Drop a `config.yaml` next to you (steal `config.example.yaml` if you're feeling lazy — that's what it's there for).
2. Light it up:
```bash
docker run --rm \
-p 8000:8000 \
-v "$PWD/config.yaml:/etc/mailboxd/config.yaml:ro" \
psyb0t/mailbox:latest
```
3. Poke it:
```bash
# no auth? this works as-is. with auth.tokens set, add: -H "Authorization: Bearer YOUR_TOKEN"
TOKEN="paste-a-token-from-config-here"
curl -s http://localhost:8000/health | jq # health is always open
curl -s -H "Authorization: Bearer $TOKEN" http://localhost:8000/mailboxes | jq
curl -s -H "Authorization: Bearer $TOKEN" 'http://localhost:8000/inbox?limit=5' | jq
```
### docker compose
```yaml
services:
mailbox:
image: psyb0t/mailbox:latest
ports: ["8000:8000"]
volumes:
# config.yaml holds your IMAP/SMTP passwords AND your auth.tokens —
# gitignore it, lock down its filesystem perms, treat it like an SSH key.
- ./config.yaml:/etc/mailboxd/config.yaml:ro
```
## Configuration
One YAML file. Lives at `MAILBOXD_CONFIG`, or `--config`, or `/etc/mailboxd/config.yaml` if you can't be bothered.
```yaml
log_level: INFO
# Bearer-token gate. Guards the HTTP API AND /mcp. Empty / missing = no auth
# (good luck out there). Multi-token list = rotate without downtime: add a
# new one, swap clients over, retire the old one.
auth:
tokens:
- "long-random-token-1"
- "long-random-token-2"
mailboxes:
- name: personal # URL-safe handle; shows up in /mailboxes/<name>/... and as the MCP tool prefix
description: "Gmail"
imap:
host: imap.gmail.com
port: 993 # default 993
tls: ssl # ssl | starttls | none (default ssl)
username: me@gmail.com
password: "app-password" # Gmail/Yahoo/etc. need an app password, not your real one
default_folder: INBOX # default folder when callers don't specify one
smtp:
host: smtp.gmail.com
port: 465 # default 587
tls: ssl # default starttls
username: me@gmail.com
password: "app-password"
from_address: "Me <me@gmail.com>"
- name: work
imap: { host: mail.work.com, port: 143, tls: starttls, username: me, password: "...", default_folder: INBOX }
smtp: { host: mail.work.com, port: 587, tls: starttls, username: me, password: "...", from_address: me@work.com }
```
The fine print:
- **At least one mailbox.** Each one needs at least one of `imap` / `smtp`. Both is fine. Neither is a config error.
- **`name`** matches `[a-zA-Z0-9_-]+` and is unique — it's the URL path segment and the MCP tool prefix, so don't put spaces or emojis in it.
- **Defaults**: IMAP `993/ssl`, SMTP `587/starttls`. Override if your provider is weird.
- **The config file holds plaintext passwords and your bearer tokens.** Treat it like a credential vault: gitignore it, `chmod 600`, mount read-only, don't paste it in Slack.
## HTTP API
### Authentication
If `auth.tokens` is set, **every request except `GET /health`** has to carry a bearer:
```
Authorization: Bearer <one of auth.tokens>
```
No header, wrong shape, wrong value → `401` with `WWW-Authenticate: Bearer`. Tokens get a constant-time compare so you don't leak them through timing. The same gate covers `/mcp` — there's no second auth system to learn.
Leave `auth.tokens` empty (or skip the block) and everything's open. Fine for "this is bound to 127.0.0.1 and there's a reverse proxy in front." Catastrophic otherwise. Your call.
### Errors
Everything's JSON. Errors look like `{"detail": "..."}`:
| Status | When |
| ------ | ------------------------------------------------------------------------------------------ |
| `401` | Missing or invalid bearer (when auth is on). |
| `404` | Unknown mailbox name in the URL. |
| `409` | You're asking a mailbox for a protocol it doesn't have (IMAP endpoint on an SMTP-only one). |
| `502` | The IMAP / SMTP server upstream said no. |
### `GET /health`
```json
{ "ok": true, "version": "0.1.0" }
```
Always open, no bearer required. Point your liveness probe at this and forget about it.
### `GET /mailboxes`
```json
{
"mailboxes": [
{ "name": "personal", "description": "Gmail",
"imap": true, "smtp": true }
]
}
```
### Unified inbox (the main event)
`GET /inbox` is the read endpoint you actually want 90% of the time. It hits **every IMAP-configured mailbox in parallel**, runs the same structured search on each one, merges newest-first, and tags every result with which account it came from. "Show me everything from `boss@corp.com`," "what's unread right now," "what came in this morning" — all the same call, no fanout dance on the client side.
| Query param | What it does |
| ------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------- |
| `mailbox` | CSV filter by mailbox name (`personal`) **or** email address (`me@gmail.com`). Omit to search all of them. |
| `from`, `to`, `subject`, `body`, `text` | IMAP SEARCH predicates. `text` is full-text across headers + body. |
| `since`, `before` | IMAP dates, e.g. `1-Jan-2026`. |
| `unseen`, `seen`, `flagged`, `answered` | Flag filters. Set the ones you want to true. |
| `larger_than`, `smaller_than` | Bytes. |
| `folder` | IMAP folder (default `INBOX`). |
| `limit` | Hard-capped at 500. Default 50. What people ask about docker-mailbox
What is psyb0t/docker-mailbox?
+
psyb0t/docker-mailbox is mcp servers for the Claude AI ecosystem. Multi-mailbox IMAP/SMTP control plane. One YAML, N accounts, one bearer-auth gate. Unified inbox across Gmail/Outlook/iCloud/mailo/inbox.lv/whatever — search, read, mark, send. Reader mode strips HTML chrome for LLMs. Streamable-HTTP MCP server + REST API on one port. Ships as a Docker image. It has 0 GitHub stars and was last updated today.
How do I install docker-mailbox?
+
You can install docker-mailbox by cloning the repository (https://github.com/psyb0t/docker-mailbox) or following the README instructions on GitHub. ClaudeWave also provides quick install blocks on this page.
Is psyb0t/docker-mailbox safe to use?
+
psyb0t/docker-mailbox has not been audited yet by our security agent. Review the original repository on GitHub before using it in production.
Who maintains psyb0t/docker-mailbox?
+
psyb0t/docker-mailbox is maintained by psyb0t. The last recorded GitHub activity is from today, with 0 open issues.
Are there alternatives to docker-mailbox?
+
Yes. On ClaudeWave you can browse similar mcp servers at /categories/mcp, sorted by popularity or recent activity.
Deploy docker-mailbox to your cloud
Ship this repo to production in minutes. Each platform spins up its own environment with editable env vars.
Maintain this repo? Add a badge to your README
Drop the badge into your GitHub README to show it's tracked on ClaudeWave. Each badge links back to this page and reflects the live Trust Score.
[](https://claudewave.com/repo/psyb0t-docker-mailbox)<a href="https://claudewave.com/repo/psyb0t-docker-mailbox"><img src="https://claudewave.com/api/badge/psyb0t-docker-mailbox" alt="Featured on ClaudeWave: psyb0t/docker-mailbox" width="320" height="64" /></a>More MCP Servers
Fair-code workflow automation platform with native AI capabilities. Combine visual building with custom code, self-host or cloud, 400+ integrations.
User-friendly AI Interface (Supports Ollama, OpenAI API, ...)
An open-source AI agent that brings the power of Gemini directly into your terminal.
The fastest path to AI-powered full stack observability, even for lean teams.
Real-time global intelligence dashboard. AI-powered news aggregation, geopolitical monitoring, and infrastructure tracking in a unified situational awareness interface
🕷️ An adaptive Web Scraping framework that handles everything from a single request to a full-scale crawl!