Self-hosted MCP server for unified IMAP/SMTP mailbox access: read, list, and send email.
Copy the AI prompt to install this server into Claude Code, Cursor, or another agent β or use 1-click editor setup below.
π‘ Paste the JSON block into your client's configuration file under mcpServers, then restart the application.
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.
| 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. |
config.yaml next to you (steal config.example.yaml if you're feeling lazy β that's what it's there for).One YAML file. Lives at MAILBOXD_CONFIG, or --config, or /etc/mailboxd/config.yaml if you can't be bothered.
The fine print:
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.993/ssl, SMTP 587/starttls. Override if your provider is weird.chmod 600, mount read-only, don't paste it in Slack.If auth.tokens is set, every request except GET /health has to carry a bearer:
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.
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 /healthAlways open, no bearer required. Point your liveness probe at this and forget about it.
GET /mailboxesGET /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.
No reviews yet β be the first to share how this listing worked for you.
Showcase your server listing on GitHub or your project documentation. Embed this dynamic SVG badge to highlight official listing status and live engagement.
[](https://allmcps.com/mcp/mailbox-2)<a href="https://allmcps.com/mcp/mailbox-2"><img src="https://allmcps.com/api/badge/mailbox-2?style=directory" alt="Mailbox on AllMCPs" /></a>