email-mcp
Click on "Install 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-mcpshow 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: mcp-email
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/pytestMaintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Latest Blog Posts
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/swapnilsurdi/email_mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server