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: email-mcp-server
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
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
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
