Repository navigation
Releases
v1.1.0
What's Changed
- Actually run the container image in CI by @kacperkwapisz in #21
- Mint a 15-minute download URL from get_attachment by @kacperkwapisz in #22
- Compare download errors with errors.Is by @kacperkwapisz in #23
Full Changelog: v1.0.0...v1.1.0
v1.0.0
First release of the Go rewrite. This replaces the Python poke-mail server entirely — different language, different architecture, and no Poke integration. Treat it as a new project that happens to share a repository.
What this is
An MCP server that gives AI agents controlled access to IMAP and SMTP mailboxes — read, search, organize, and send — without ever handing them your credentials.
Tools take an opaque account_id. The server resolves it against a config file that stays on the server and makes the connection itself, so a remote agent has no way to reconstruct how to reach your mailbox.
Breaking changes from poke-mail
Everything Poke-specific is gone: the inbound webhook, the IMAP IDLE watcher, the polling fallback, and the poke tunnel integration. The server no longer initiates anything on its own — it only answers tool calls.
POKE_WEBHOOK_URL,POKE_API_KEY,POKE_TUNNELremoved. No replacement; the forwarding feature does not exist.MCP_API_KEYis now mandatory on the HTTP transport. The server refuses to start without it — there is no unauthenticated mode, and no tunnel exemption.watch_foldersandmark_as_readremoved from account config. Both existed only to feed the watcher.- Every tool renamed or reshaped. Message-scoped tools no longer take
account_id, anduid+folderare replaced by a single opaquemessage_id. - Sending and deleting are disabled by default. Set
allow_send/allow_delete, globally or per account. - STARTTLS is mandatory, never opportunistic. A provider that was silently falling back to plaintext will now fail loudly rather than transmitting your password in the clear.
Existing config.yml files load unchanged — the flat imap_host / smtp_username style keys are folded into the nested form automatically.
Correctness fixes carried over from the Python version
- Message identity is now safe. Tools previously took a bare
(uid, folder)pair with no UIDVALIDITY check. If a server renumbered a mailbox, a cached uid silently pointed at a different message, so an archive or move could hit the wrong mail. Handles now carry account, folder, UIDVALIDITY, and uid together, verified on every operation, and a stale handle produces an explicit error instead of acting. send_emailreturned an emptymessage_id. It read a header thatsmtplibnever sets, so callers always got"". Message-IDs are now generated locally and returned.- No timeouts existed on SMTP.
smtplibwas called with no timeout, so a hung server blocked a worker indefinitely. Every network operation is now bounded. - Recipient lists were split naively on commas, breaking
"Doe, John" <j@x.com>. Addresses are parsed properly and header injection via embedded newlines is rejected.
New capabilities
- Attachments on send, from a file path or inline base64.
reply_emailwith reply-all, self-address exclusion, derivedRe:subject, and a correctReferenceschain.forward_emailcarrying the original's attachments and quoting it below your note.get_attachmentwrites a chosen file to disk and returns the path. Attachment bytes never enter a tool response, so a large PDF cannot flood the context window.- Sent-folder copies written from the exact delivered bytes, with a provider-aware default: Gmail and Zoho file their own copy on submission, while iCloud, Office 365, and generic relays do not.
verify_accounttests IMAP and SMTP without sending or reading anything.- stdio transport for local clients, alongside authenticated HTTP.
- Pooled IMAP connections, health-checked and reused, removing a TLS handshake and LOGIN from every tool call.
Install
docker run -d --name mail-mcp \
-p 3000:3000 \
-v "$PWD/config.yml:/config.yml:ro" \
-e MCP_API_KEY="$(openssl rand -hex 32)" \
ghcr.io/kacperkwapisz/mail-mcp:1.0.0Or grab a binary below. Multi-arch images are published for linux/amd64 and linux/arm64.
See the README for configuration, and config.example.yml for an annotated template.
Verified
gofmt, go vet, and golangci-lint clean; full suite green under -race. The binary is exercised end to end: it refuses to start without a token, rejects bad tokens with 401, returns an empty 404 off-path, and serves all 18 tools over both HTTP and stdio.
Not yet exercised against a live mail server — validation so far is unit tests plus protocol-level handshakes.