email-mcp
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@email-mcp show my unread emails from the last week"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
email-mcp
Local MCP server for multi-account IMAP/SMTP email (iCloud + Gmail via app-specific passwords). Never marks mail read. Cross-folder search, idempotent sends, TLS verified.
Two surfaces share the same core:
stdio MCP server (this README) — single user, runs per-client, config from an accounts file + Keychain/env.
HTTP service (
pip install "email-mcp[http]",email-mcp-http) — long-lived and multi-tenant: MCP over streamable-HTTP behind scoped per-agent keys, a web dashboard, Matrix-bot onboarding, and owner-approved sends (👍 OTP) for recipients outside the allowlist. Guide: src/email_mcp/http/HELP.md — also served live by the service atGET /help(JSON summary atGET /info).
Status
Tested against iCloud only. The design is provider-generic (Gmail config is included as an example), but only iCloud has been exercised end-to-end so far. Gmail/others are untested — use at your own risk and please report back.
macOS only (uses the macOS Keychain via
keyring).
Related MCP server: 163 Mail MCP
Requirements
An app-specific password for each mailbox, stored in your macOS Keychain (see Configure / Get an app-specific password). The password is read from the Keychain at runtime — it is never written to disk, logged, or returned by any tool.
You must grant Keychain permission. The first time the server reads the password, macOS shows a dialog: "… wants to use your confidential information stored in 'email-mcp' in your keychain." — click Allow (or Always Allow). This grants access to only that one
email-mcpitem, nothing else in your Keychain. Note the prompt is tied to the specific Python binary running the server, so it may re-ask if you switch interpreters.
Install
pip install email-mcp # or: pipx install email-mcpThis provides an email-mcp command (and python -m email_mcp.server).
Configure
Create your account registry at
~/.config/email-mcp/accounts.yml(seeconfig/accounts.example.ymlfor the format). Override the location with theEMAIL_MCP_ACCOUNTSenv var if you prefer.Store each account's app-specific password in the macOS Keychain:
python -m email_mcp.setup_cli icloud-personal(For local development you can instead set
EMAIL_MCP_PASSWORDin a.env.)
Register with Claude Code
claude mcp add email --scope user -- email-mcpTools
list_accounts, set_default_account, list_folders, get_emails (recency by default;
search via query/filters; per-message attachments metadata), download_attachment,
send_email (idempotent; optional attachments), mark_email, move_email.
Security policy
Optional security: section in the accounts file (all patterns are case-insensitive
full-match regexes; plain values work as-is — see config/accounts.example.yml):
allowed_recipients— when set, everysend_emailrecipient must match one or the send isBLOCKED (recipient_not_allowed)before SMTP and before the dedup ledger. Unset = allow all; an explicitly empty list blocks all sends. Each recipient is first canonicalized to its bare address(es) with the same parser SMTP uses, so a single entry bundling extra addresses behind a comma or display name ("ok@x.com, other@y.com") is checked address-by-address — and that same canonical set is what reaches SMTP and the dedup ledger.Trash is protected by default — the reserved trash names of the major providers (
Trash,Binfor Gmail UK,Deleted Messagesfor iCloud,Deleted Itemsfor Outlook, subfolders included) are read-only: nothing can be moved into or out of them and messages there can't be flagged or expunged. Since moving-to-trash is the only delete this server has, the MCP cannot delete mail at all under the default policy. Opt out withprotect_trash: false(applies on the next call). Note: a bareDeleted(some Exchange/O365/Dovecot setups) is not auto-matched — add it toprotected_folders.protected_folders— additional read-only folders (same semantics as trash).readable_folders/blocked_folders— gate reading (get_emails,download_attachment, folder listing, and the folder sets searched for mutations). A blocked folder is also refused as amove_emaildestination. Whenreadable_foldersis set only matching folders are readable;blocked_foldersalways wins. Blocked folders are also omitted fromlist_folders. Policy violations return structured results (folder_protected,folder_blocked,folders_blocked,recipient_not_allowed) — never exceptions. Config is re-read per call, so edits apply without restarting the server.
Searching & speed
get_emailshas two modes, reported bysearched_window_onlyin the result:No search terms → fast: the most-recent
page*page_sizeper folder, served from an in-memory cache when warm (a repeat read is ~instant).searched_window_only=truehere, so an empty result means "not in the recent window", not "doesn't exist". Passfresh=trueto force a live read.Search (
query,from_address,subject,since=YYYY-MM-DD,has_attachment, or rawfilters.criteria) → runs server-side over the whole mailbox —queryis a full-text IMAP search, so matches outside the recent window are found.searched_window_only=false.
body=falseomits message bodies (cheap headers + attachment metadata) — ideal for finding a message before opening it. Each message also carriesuid/uidvalidityfor robust follow-up actions.Background prefetch keeps the latest INBOX warm. Off by default; enable with
EMAIL_MCP_PREFETCH_INTERVAL=120(seconds; delta-fetches only new mail by UID, never marks read). Cache size knobs:EMAIL_MCP_CACHE_ENTRIES(256),EMAIL_MCP_CACHE_BYTES(32MiB),EMAIL_MCP_CACHE_BODY_MAX(64KiB),EMAIL_MCP_CACHE_RECENT_TTL(180s).send_emaildedup: by default a 2nd mail to the same recipients within 10 min is BLOCKED.allow_duplicate=truerelaxes this to block only a true repeat (same recipients AND subject/body);idempotency_keygives caller-controlled dedup.
Roadmap: running this as one shared HTTP server so a whole fleet of agents share a single warm cache + prefetch — see
docs/TODO-http-shared-server.md.
Attachments
Reading:
get_emailsreports anattachmentslist per message ({index, filename, mime_type, size, inline}) — metadata only, never the bytes.download_attachment(message_id, filename=… | index=…)writes one attachment to disk (read-only; never marks mail read) and returns the saved path.download_all=truesaves every attachment;return_base64=truereturns small files (≤256KB) inline instead of to disk;uid+folder(from get_emails) locate a message with no/duplicate Message-ID. Files land inEMAIL_MCP_DOWNLOAD_DIR(default~/.local/state/email-mcp/attachments) unless you passdest_dir. The email-supplied filename is sanitized and confined to that directory (path-traversal safe); existing files are not clobbered unlessoverwrite=true.Sending:
send_email's optionalattachmentsis a list where each item is either{"path": "/local/file"}(read from disk) or{"content": "<base64>", "filename": "name.ext"}, with an optional"mime_type". Combined size is capped at 25 MB.
Get an app-specific password
iCloud: appleid.apple.com -> Sign-In & Security -> App-Specific Passwords.
Gmail: myaccount.google.com -> Security -> App passwords.
Develop
python3.12 -m venv .venv && .venv/bin/pip install -e ".[dev]"
.venv/bin/pytestAvailable Tools
8 toolsdownload_attachmentA
Download a message's attachment(s) — read-only, never marks mail read.
Select one by filename or index (from get_emails' attachments list); a lone
attachment needs neither; download_all=true saves every attachment. Saved into the
server download dir (override env EMAIL_MCP_DOWNLOAD_DIR) unless dest_dir is given;
the email-supplied filename is sanitized and confined to that directory.
return_base64=true returns the bytes inline (small files only, ≤256KB) instead of
writing to disk. uid+folder (from get_emails) locate the message directly — use
them when a message has no/duplicate Message-ID. Returns {saved_path|content_base64,
filename, mime_type, size, ...}.
Inputs: message_id (required), filename, index, dest_dir, +7 more.
get_emailsA
Fetch or search emails. Never marks mail read. Defaults to INBOX + Junk.
Two modes, reported by searched_window_only in the result:
No search terms: fast — the most-recent page*page_size per folder, served from an in-memory cache when warm.
searched_window_only=truehere, so an empty result means 'not in the recent window', not 'doesn't exist'.fresh=trueforces a live read past the cache.Search (
query,from_address,subject,since=YYYY-MM-DD,has_attachment, or rawfilters.criteria): runs SERVER-SIDE over the whole mailbox —queryis a full-text IMAP search, so matches outside the recent window are found.searched_window_only=false.
body=false omits message bodies (cheap headers + attachment metadata — ideal for
finding a message before opening it). cached=true keeps the 60-min result-set
cache for stable pagination. Each message includes uid/uidvalidity for robust
follow-up actions.
Inputs: account, filters, query, folders, +11 more.
list_accountsA
List accounts and which is the default.
Inputs: no named parameters.
list_foldersB
List mail folders (folders the security policy blocks from reading are omitted).
Inputs: account.
mark_emailA
Mark a message read or unread. Refused (folder_protected) for messages in a protected folder — Trash/Bin/Deleted * by default, plus security.protected_folders.
Inputs: message_id (required), read (required), account, folders.
move_emailB
Move a message to another folder. Protected folders (Trash/Bin/Deleted * by default, plus security.protected_folders) are read-only: nothing can be moved into or out of them — i.e. with the default policy this server cannot delete mail.
Inputs: message_id (required), dest_folder (required), account, folders.
send_emailA
Send an email. Idempotent: by default a second mail to the same recipients
within 10 minutes is BLOCKED, not resent. allow_duplicate=true relaxes this to
block only a true repeat (same recipients AND subject/body), so distinct messages
to the same person go through. idempotency_key overrides entirely: blocks iff that
key was used in the window (caller-controlled dedup).
attachments is an optional list; each item is either {"path": "/local/file"}
(read from disk) or {"content": "", "filename": "name.ext"}, with an
optional "mime_type". Combined size must stay under 25MB. Note: a {"path"} item
reads any file this process can access and emails it — only attach paths you
intend to send; never a path derived from untrusted/email-supplied content.
If security.allowed_recipients is configured, every recipient must match it or
the send is BLOCKED with reason=recipient_not_allowed.
Inputs: to (required), subject (required), body (required), account, +4 more.
set_default_accountB
Set the default account (used when account is omitted).
Inputs: name (required).
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
TDQS
Scored across 8 tools
Each tool targets a distinct operation (downloading attachments, fetching emails, listing accounts/folders, marking/moving/sending emails, setting default account). There is no overlap or ambiguity.
All tool names follow a consistent verb_noun pattern with snake_case (e.g., download_attachment, get_emails, list_folders). No mixing of conventions.
With 8 tools, the server covers core email operations without being too sparse or overly heavy. Each tool serves a clear purpose.
Covers essential email operations (send, receive, list, mark, move, attachments) but lacks a direct delete capability; the move_email tool cannot move to protected folders like Trash, limiting deletion.
Maintenance
Related MCP Connectors
Your IMAP mailbox as an MCP server: read, search and (if you allow it) organize mail. Open source.
Hosted email MCP for your own Gmail, Outlook.com, Microsoft 365, iCloud or IMAP inbox: read, search, draft, reply in thread, forward and file mail. It moves or flags up to 500 messages in one call, and a send leaves exactly one copy in Sent. A calendar is a separate connection, and connecting one adds diary and scheduling tools.
Programmable email inbox for AI agents — JMAP, PoW auth, stdio MCP server.
Email infrastructure for AI agents — send, receive, search, and reply to email over MCP.
Related MCP Servers
- FlicenseNot gradedqualityDmaintenanceA minimal MCP server for reading and sending emails via IMAP/SMTP, supporting multiple accounts in a single instance with zero external dependencies.1-
- AlicenseAqualityDmaintenancePrivacy-first local MCP server for personal @163.com mailboxes, connecting via IMAP/SMTP with read-only search and write operations guarded by confirmation tokens.12MIT
- AlicenseNot gradedqualityDmaintenanceA minimal MCP server for reading, searching, managing, and sending email over IMAP and SMTP.MIT
- AlicenseNot gradedqualityBmaintenanceAn MCP server that drives macOS Mail: read, search, send, organise.MIT