owlpost
A high-fidelity IMAP/SMTP MCP server for iCloud, Gmail, and other mail
providers. Built because every other email MCP server I tried got tripped up
by real-world IMAP quirks (CRLF line endings, RFC822 vs BODY.PEEK[],
provider-specific Sent folders, multi-word search values).
owlpost is the one I actually use day-to-day from Claude Code to read, search, follow conversations, and send mail.
Features
- Multi-account — configure as many mailboxes as you want, switch by name
- iCloud and Gmail tested end-to-end, with provider-specific quirks handled:
- iCloud's
RFC822fetch returns empty bodies → usesBODY.PEEK[] - iCloud's IMAP
APPENDrequires CRLF line endings → all outbound mail usespolicy.SMTP - iCloud's SMTP doesn't auto-save sent mail → owlpost APPENDs to Sent for you
- Gmail's SMTP does auto-save → owlpost skips the duplicate APPEND
- Folder names auto-discovered via RFC 6154 SPECIAL-USE (no hardcoding
Sent Messagesvs[Gmail]/Sent Mail)
- iCloud's
- Conversation following — Gmail's
X-GM-THRIDextension where available, otherwise a generic Message-ID/References BFS across folders - Reliable connections — short-lived per-operation IMAP sessions instead of long-lived ones that drop randomly
- MIME-aware — parses multipart messages, decodes headers, lists attachments, downloads them to disk
- Threaded reply/forward — preserves
In-Reply-To/References, quotes the original
Tools
| Tool | What it does |
|---|---|
list_accounts |
List configured mail accounts |
list_folders |
List folders for an account, with detected special-use roles |
resolve_folder |
Resolve a role (sent, trash, drafts, inbox, all, archive) to a folder name |
search_messages |
Structured IMAP search (from/to/cc/subject/body/since/before/unseen/flagged/has_attachment), newest first |
read_email |
Read one message: headers, plaintext/HTML body, attachment list. Truncates by default to fit MCP result limits |
save_attachment |
Download an attachment to disk by part index |
get_conversation |
Return all messages in the same thread (Gmail X-GM-THRID or generic reference walking) |
send_email |
Send mail. Auto-saves to Sent on providers that don't (e.g. iCloud). Supports attachments, threading headers |
save_draft_email |
Build a message and APPEND it to Drafts without sending — same parameters as send_email |
reply_email |
Reply (with optional reply-all), preserving threading and quoting |
forward_email |
Forward, re-attaching original attachments by default |
mark_read |
Set/unset \Seen |
flag_message |
Set/unset \Flagged (star) |
move_email |
Move to another folder |
delete_email |
Move to Trash |
Install
The recommended way is to not install at all — let
uvx fetch and cache it on first
use. This keeps the package always up-to-date and avoids polluting your
global Python environment:
# Run directly — uvx fetches from PyPI and caches.
uvx --from owlpost owlpost
If you'd rather have a stable binary on your PATH:
# Persistent install with uv (recommended)
uv tool install owlpost
# Or plain pip
pip install owlpost
Either way you'll end up with an owlpost command that speaks MCP over stdio.
Configure
Copy accounts.example.toml to
~/.config/owlpost/accounts.toml and fill in your credentials. Use
app-specific passwords, not your real password:
- iCloud: account.apple.com → Sign-In and Security → App-Specific Passwords
- Gmail: myaccount.google.com/apppasswords (2FA must be enabled)
[accounts.icloud]
email = "you@icloud.com"
password = "xxxx-xxxx-xxxx-xxxx"
provider = "icloud"
imap_host = "imap.mail.me.com"
imap_port = 993
smtp_host = "smtp.mail.me.com"
smtp_port = 587
auto_save_sent = true # iCloud SMTP doesn't auto-save sent
[accounts.gmail]
email = "you@gmail.com"
password = "xxxx xxxx xxxx xxxx"
provider = "gmail"
imap_host = "imap.gmail.com"
imap_port = 993
smtp_host = "smtp.gmail.com"
smtp_port = 587
auto_save_sent = false # Gmail SMTP auto-saves
You can override the config path with the OWLPOST_CONFIG environment
variable.
Use with Claude Code
Add to ~/.claude.json under mcpServers. The uvx form is recommended —
it fetches owlpost from PyPI on first launch, caches it, and keeps you on
the latest version with no manual install step:
{
"mcpServers": {
"owlpost": {
"type": "stdio",
"command": "uvx",
"args": ["--from", "owlpost", "owlpost"],
"env": {}
}
}
}
If uvx isn't on Claude Code's PATH you may need to use the absolute
path (typically ~/.local/bin/uvx).
Alternatively, if you installed with uv tool install owlpost or
pip install owlpost, point command directly at the resulting binary:
{
"mcpServers": {
"owlpost": {
"type": "stdio",
"command": "owlpost",
"args": [],
"env": {}
}
}
}
Restart Claude Code and the tools will appear under the owlpost namespace.
Use with Claude Desktop
Add to ~/Library/Application Support/Claude/claude_desktop_config.json:
{
"mcpServers": {
"owlpost": {
"command": "uvx",
"args": ["--from", "owlpost", "owlpost"]
}
}
}
Use with other MCP clients
owlpost speaks the standard MCP stdio transport — any MCP-compatible client
can talk to it by spawning either uvx --from owlpost owlpost or, if
installed, the bare owlpost binary.
Examples
Once registered, you can ask Claude things like:
- "Find emails from priya about the kids' school in the last month"
- "Show me the full thread for the latest mortgage email"
- "Reply to the most recent message from the landlord saying I'll be in touch tomorrow"
- "Forward the Manulife policy PDFs to Mom and Dad with Priyanka in CC"
- "Save the attachment from the Toronto Hydro bill to ~/Downloads"
Provider notes
iCloud
- Use an app-specific password, not your Apple ID password.
- iCloud throttles aggressive reconnection. owlpost uses short-lived per-op sessions but doesn't pool — if you get
SSLEOFError: EOF, back off for ~30 seconds. - Sent folder is
Sent Messages, Trash isDeleted Messages. Both are auto-detected.
Gmail
- Requires 2FA and an app password.
- IMAP must be enabled in Gmail settings → Forwarding and POP/IMAP.
- owlpost uses Gmail's
X-GM-THRIDextension for reliable thread detection.
Proton Mail (via Proton Bridge)
Proton only exposes IMAP/SMTP through the Proton Bridge app, which runs on localhost with a self-signed certificate and speaks STARTTLS on non-standard ports. Configure with:
provider = "generic"imap_host = "127.0.0.1",imap_port = 1143,imap_security = "starttls"smtp_host = "127.0.0.1",smtp_port = 1025,smtp_security = "starttls"tls_verify = false— required because Bridge's cert is self-signed- Use the Bridge-generated mailbox password (not your Proton account password)
Other providers
Set provider = "generic" and configure imap_host, smtp_host, ports,
imap_security / smtp_security (starttls or ssl), and tls_verify
(set false for self-signed certs). Folder roles will still be
auto-detected if your provider supports SPECIAL-USE (most modern ones do).
Development
git clone https://github.com/smankoo/owlpost
cd owlpost
uv venv
uv pip install -e .
Run the server directly to verify it starts:
.venv/bin/owlpost
# (waits for MCP stdio input)
Publishing
Releases go to PyPI. From a clean tree
on main:
# 1. Bump version in pyproject.toml
# 2. Commit the bump
# 3. Build the sdist + wheel
uv build
# 4. Upload to PyPI (requires ~/.pypirc with an API token)
uv publish
# 5. Tag and push
git tag v0.1.x
git push && git push --tags
uv build writes to dist/. uv publish reads credentials from
~/.pypirc (expects [pypi] with username = __token__ and
password = pypi-...). uvx users pick up the new version automatically
on next launch.
License
MIT — see LICENSE.
Metadata
Release files for owlpost 0.1.4
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| owlpost-0.1.4.tar.gz | 18.0 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| owlpost-0.1.4-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 39.9 kB
Release files / owlpost-0.1.4.tar.gz
| Download URL | owlpost-0.1.4.tar.gz |
|---|---|
| Size | 18.0 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
cb7ce1474736520d9f6c36c8d8d2bacf402facd01e1776c53d6389e1f292ce88
|
|
BLAKE2b-256 checksum How to use checksums |
e2341508d65269a9cfb71e6d3c7083ef843fbc809aecedc189573c9dcac69ca1
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.6.14
|
Release files / owlpost-0.1.4-py3-none-any.whl
| Download URL | owlpost-0.1.4-py3-none-any.whl |
|---|---|
| Size | 21.9 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
a580936d8f814b2dd4884d9c1b5beeb1ea6b9a8cbac14db2ec3332b354a9a4fd
|
|
BLAKE2b-256 checksum How to use checksums |
4afdb5f3887c97d14b76f4e541658e3e3df5e7f12c08aa8a028be8eb7a168a66
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.6.14
|