mcp-email-server
IMAP and SMTP via MCP Server
- Github repository: https://github.com/wh1isper/mcp-email-server/
- Documentation https://mcp-email-server.wh1isper.top/
Installation
Manual Installation
We recommend using uv to manage your environment.
Try uvx mcp-email-server@latest ui to config, and use following configuration for mcp client:
{
"mcpServers": {
"mcp-email-server": {
"command": "uvx",
"args": ["mcp-email-server@latest", "stdio"]
}
}
}
This package is available on PyPI, so you can install it using pip install mcp-email-server
After that, configure your email server using the ui: mcp-email-server ui
Credential Storage
Accounts added via the UI or the add_email_account tool are persisted to a TOMLfile at ~/.config/mcp-email-server/config.toml. On first use, an existingconfig from the previous ~/.config/zerolib/mcp_email_server/config.tomllocation is copied automatically when the new file does not exist. Where theactual passwords/API keys live depends on credential_storage (also settable viaMCP_EMAIL_SERVER_CREDENTIAL_STORAGE), one of:
auto(default): store credentials in the OS keyring — macOS Keychain,Linux Secret Service (GNOME Keyring / KWallet) — when a usable backend isdetected; otherwise fall back to the plaintext TOML file (0o600permissions, owner-only). Falls back automatically on headless Linux,containers, or any environment without a D-Bus session.keyring: require the OS keyring; fail loudly instead of silentlyfalling back if no backend is usable.plaintext: never touch the keyring. Useful for containers, CI, or ifyou simply prefer a portable config file.
When credentials are keyring-backed, the TOML file stores only a placeholder(__KEYRING__) and non-secret metadata — the real secret lives in the OSkeyring under service mcp-email-server, one entry per<account_name>:<incoming|outgoing|api_key> (viewable in Keychain Access onmacOS, or Seahorse on Linux).
Migrating an existing config between storage modes:
mcp-email-server migrate-credentials --to keyring # move plaintext secrets into the OS keyring
mcp-email-server migrate-credentials --to plaintext # move keyring secrets back into the TOML file
Migration also happens implicitly: any time you add/edit an account whilecredential_storage is auto or keyring with a usable backend, thataccount's secrets move into the keyring on the next save. IfMCP_EMAIL_SERVER_CREDENTIAL_STORAGE is active during a save, its effectivemode is persisted too, keeping the mode marker consistent with the credentialrepresentation written to the same file.
Failure modes & troubleshooting
- Server won't start / UI won't load accounts, keychain-related error: theOS keyring is locked or unreachable. This is expected if credentials arekeyring-backed — the secret simply isn't in the config file. Unlock yourkeychain, or run
mcp-email-server migrate-credentials --to plaintextifyou'd rather not depend on it. credential_storageis 'plaintext' but the config referenceskeyring-stored credentials: runmigrate-credentials --to plaintext, orunsetMCP_EMAIL_SERVER_CREDENTIAL_STORAGE/ thecredential_storagesetting so the config can resolve them from the keyring instead.- macOS Keychain access prompt, or the server can't read a secret it wroteearlier: Keychain ACLs are per-application. If the server is spawned via
uvx(as in the Claude Desktop config above), a freshuvxresolution canpresent a different binary path than the one that stored the secret,triggering a "Keychain wants to use a password" prompt. Choose "AlwaysAllow" the first time this happens. - A migration seems to have had no effect: if
MCP_EMAIL_SERVER_CREDENTIAL_STORAGEis set in your environment, it takesprecedence over whatevermigrate-credentials --to ...just wrote to thefile on every subsequent run. Unset it, or keep it in sync with yourintended mode.
Known limitations
- Non-POSIX (Windows) file permissions: the
0o600owner-only guarantee onthe plaintext TOML is enforced only on POSIX systems. On Windows the file iswritten without an owner-restricted ACL, so preferkeyringmode (WindowsCredential Locker) there when secrets must not be readable by other accounts. auto/keyringtrusts whateverkeyringbackend is active: usability isdecided by a live set/get round-trip, not by the backend's storage guarantees.A third-partykeyringplugin that persists secrets in plaintext would passthat probe. If you install customkeyringbackends, verify the active one(keyring --list-backends) stores secrets securely.- Keyring and TOML writes are not transactional: a save pushes secrets tothe keyring and then rewrites the TOML. The TOML rewrite is atomic on its own(temp file +
os.replace), but a crash between the two steps can leave akeyring entry with no matching config reference (an orphaned secret), or aconfig reference whose keyring write partly failed. A plaintext migrationreports keyring entries it could not remove so you can clean them up manually.
Environment Variable Configuration
You can also configure the email server using environment variables, which is particularly useful for CI/CD environments like Jenkins. mcp-email-server supports both UI configuration (via TOML file) and environment variables, with environment variables taking precedence.
{
"mcpServers": {
"mcp-email-server": {
"command": "uvx",
"args": ["mcp-email-server@latest", "stdio"],
"env": {
"MCP_EMAIL_SERVER_ACCOUNT_NAME": "work",
"MCP_EMAIL_SERVER_FULL_NAME": "John Doe",
"MCP_EMAIL_SERVER_EMAIL_ADDRESS": "[email protected]",
"MCP_EMAIL_SERVER_USER_NAME": "[email protected]",
"MCP_EMAIL_SERVER_PASSWORD": "your_password",
"MCP_EMAIL_SERVER_IMAP_HOST": "imap.gmail.com",
"MCP_EMAIL_SERVER_IMAP_PORT": "993",
"MCP_EMAIL_SERVER_SMTP_HOST": "smtp.gmail.com",
"MCP_EMAIL_SERVER_SMTP_PORT": "465"
}
}
}
}
Available Environment Variables
| Variable | Description | Default | Required |
|---|---|---|---|
MCP_EMAIL_SERVER_ACCOUNT_NAME |
Account identifier | "default" |
No |
MCP_EMAIL_SERVER_FULL_NAME |
Display name | Email prefix | No |
MCP_EMAIL_SERVER_EMAIL_ADDRESS |
Email address | - | Yes |
MCP_EMAIL_SERVER_USER_NAME |
Login username | Same as email | No |
MCP_EMAIL_SERVER_PASSWORD |
Email password | - | Yes |
MCP_EMAIL_SERVER_IMAP_HOST |
IMAP server host | - | Yes |
MCP_EMAIL_SERVER_IMAP_PORT |
IMAP server port | 993 |
No |
MCP_EMAIL_SERVER_IMAP_SSL |
Enable IMAP SSL | true |
No |
MCP_EMAIL_SERVER_IMAP_START_SSL |
Enable IMAP STARTTLS | false |
No |
MCP_EMAIL_SERVER_IMAP_VERIFY_SSL |
Verify IMAP SSL certificates (disable for self-signed) | true |
No |
MCP_EMAIL_SERVER_SMTP_HOST |
SMTP server host; omit for IMAP-only mode (no sending) | - | No |
MCP_EMAIL_SERVER_SMTP_PORT |
SMTP server port | 465 |
No |
MCP_EMAIL_SERVER_SMTP_SSL |
Enable SMTP SSL | true |
No |
MCP_EMAIL_SERVER_SMTP_START_SSL |
Enable STARTTLS | false |
No |
MCP_EMAIL_SERVER_SMTP_VERIFY_SSL |
Verify SSL certificates (disable for self-signed) | true |
No |
MCP_EMAIL_SERVER_ENABLE_ATTACHMENT_DOWNLOAD |
Enable attachment download | false |
No |
MCP_EMAIL_SERVER_SAVE_TO_SENT |
Save sent emails to IMAP Sent folder | true |
No |
MCP_EMAIL_SERVER_SENT_FOLDER_NAME |
Custom Sent folder name (auto-detect if not set) | - | No |
MCP_EMAIL_SERVER_ALLOWED_RECIPIENTS |
Recipient allowlist (comma-separated); empty = all | - | No |
MCP_EMAIL_SERVER_ALLOWED_SENDERS |
Sender allowlist (comma-separated globs); empty = all | - | No |
MCP_EMAIL_SERVER_REPORT_BLOCKED_MUTATIONS |
Report blocked mutations as failures (default: silent no-op) | false |
No |
MCP_EMAIL_SERVER_CREDENTIAL_STORAGE |
Credential storage mode: auto, keyring, or plaintext |
auto |
No |
IMAP-only mode (no SMTP)
SMTP configuration is optional. When MCP_EMAIL_SERVER_SMTP_HOST is omitted, the account runs in IMAP-only mode: send_email is hidden (when every configured email account lacks SMTP) and no mail can leave the server. Note that IMAP-only is not strictly read-only — IMAP-backed write tools such as save_to_mailbox (which composes a message and stores it in a folder via IMAP APPEND), delete_emails, move_emails, and archive_emails remain available.
{
"mcpServers": {
"mcp-email-server": {
"command": "uvx",
"args": ["mcp-email-server@latest", "stdio"],
"env": {
"MCP_EMAIL_SERVER_EMAIL_ADDRESS": "[email protected]",
"MCP_EMAIL_SERVER_PASSWORD": "your_password",
"MCP_EMAIL_SERVER_IMAP_HOST": "imap.gmail.com"
}
}
}
}
HTTP Transport Security
HTTP transports (sse and streamable-http) validate request Host and Origin headers to protect against DNS rebinding attacks. Localhost is allowed by default. For container networks or reverse proxies, configure the expected service names explicitly.
| Variable | Description | Default |
|---|---|---|
MCP_HOST |
HTTP bind host for streamable-http |
localhost |
MCP_PORT |
HTTP bind port for streamable-http |
9557 |
MCP_ALLOWED_HOSTS |
Comma-separated allowed Host values. Supports host:* ports |
Localhost hosts |
MCP_ALLOWED_ORIGINS |
Comma-separated allowed Origin values. Supports host:* ports |
Localhost origins |
MCP_ENABLE_DNS_REBINDING_PROTECTION |
Enable DNS rebinding protection | true |
Bare host entries such as MCP_ALLOWED_HOSTS=mcp-email-server also allow any port on that host. MCP_ENABLE_DNS_REBINDING_PROTECTION=false, MCP_ALLOWED_HOSTS=*, or MCP_ALLOWED_ORIGINS=* disables Host and Origin validation entirely. Use those options only in isolated local development environments.
IPv6 literals in allowlists should use bracketed notation, such as [::1]:* and http://[::1]:*.
Enabling Attachment Downloads
By default, downloading email attachments is disabled for security reasons. To enable this feature, you can either:
Option 1: Environment Variable
{
"mcpServers": {
"mcp-email-server": {
"command": "uvx",
"args": ["mcp-email-server@latest", "stdio"],
"env": {
"MCP_EMAIL_SERVER_ENABLE_ATTACHMENT_DOWNLOAD": "true"
}
}
}
}
Option 2: TOML Configuration
Add enable_attachment_download = true to your TOML configuration file (~/.config/mcp-email-server/config.toml):
enable_attachment_download = true
[[emails]]
# ... your email configuration
Once enabled, you can use the download_attachment tool to save email attachments to a specified path.
Saving Sent Emails to IMAP Sent Folder
By default, sent emails are automatically saved to your IMAP Sent folder. This ensures that emails sent via the MCP server appear in your email client (Thunderbird, webmail, etc.).
The server auto-detects common Sent folder names: Sent, INBOX.Sent, Sent Items, Sent Mail, [Gmail]/Sent Mail.
To specify a custom Sent folder name (useful for providers with non-standard folder names):
Option 1: Environment Variable
{
"mcpServers": {
"mcp-email-server": {
"command": "uvx",
"args": ["mcp-email-server@latest", "stdio"],
"env": {
"MCP_EMAIL_SERVER_SENT_FOLDER_NAME": "INBOX.Sent"
}
}
}
}
Option 2: TOML Configuration
[[emails]]
account_name = "work"
save_to_sent = true
sent_folder_name = "INBOX.Sent"
# ... rest of your email configuration
To disable saving to Sent folder, set MCP_EMAIL_SERVER_SAVE_TO_SENT=false or save_to_sent = false in your TOML config.
Restricting Recipients (Allowlist)
By default the server can send to any address. Set allowed_recipients to restrict bothsend_email and save_to_mailbox to a trusted set. Leave it empty (the default) to allow all.
allowed_recipients = ["[email protected]", "[email protected]"]
Or via environment variable (comma-separated):
MCP_EMAIL_SERVER_ALLOWED_RECIPIENTS="[email protected],[email protected]"
When configured, any To/CC/BCC address not on the list is rejected with a clear error. Matching iscase-insensitive and understands the Name <[email protected]> form. The list_allowed_recipientstool appears only when an allowlist is configured, so default installs keep a minimal tool surface.
Filtering Incoming Mail (Sender Allowlist)
By default all senders are visible. Set allowed_senders to show mail only from trusted senders.Patterns support globs (e.g. *@company.com) and exact addresses, matched case-insensitively. Leaveit empty (the default) to show everything.
allowed_senders = ["*@company.com", "[email protected]"]
Or via environment variable (comma-separated):
MCP_EMAIL_SERVER_ALLOWED_SENDERS="*@company.com,[email protected]"
When configured, filtering is applied to inbound read and mutation paths: list_emails_metadata excludesnon-allowed senders before pagination, so total and page sizes reflect only allowed mail;get_emails_content and download_attachment check the sender before reading a message, so a non-allowedmessage's body and attachments are never fetched or marked read, and it is reported as inaccessible —indistinguishable from a missing message. Mutation tools first check the sender and never delete, flag, ormove blocked mail. The list_allowed_senders tool appears only when an allowlist is configured.
Scope: the allowlist protects every inbound path — read (list_emails_metadata, get_emails_content,download_attachment) and mutation (delete_emails, mark_emails_as_read, move_emails,archive_emails). A blocked sender's mail is never read, deleted, flagged, or moved.
Blocked mutations (report_blocked_mutations, default false): when a mutation targets a blockedsender's message, it is never performed. By default the result is reported as a successful no-op —indistinguishable from acting on a non-existent message, so the allowlist does not reveal that a hiddenmessage exists. Set report_blocked_mutations = true (or MCP_EMAIL_SERVER_REPORT_BLOCKED_MUTATIONS=true)to instead report blocked UIDs as failures (explicit, but reveals a blocked-but-real message differs froma missing one).
Note: matching is against the message's From header — local filtering only, not senderauthentication. A spoofed From will pass the allowlist, so this is not a substitute for provider-sideSPF / DKIM / DMARC enforcement.
Self-Signed Certificates and IMAP STARTTLS (e.g., ProtonMail Bridge)
Local mail bridges such as ProtonMail Bridge commonly use STARTTLS with self-signed certificates. Configure IMAP with plaintext connect plus STARTTLS upgrade, and disable certificate verification for the local bridge certificate:
{
"mcpServers": {
"mcp-email-server": {
"command": "uvx",
"args": ["mcp-email-server@latest", "stdio"],
"env": {
"MCP_EMAIL_SERVER_IMAP_HOST": "127.0.0.1",
"MCP_EMAIL_SERVER_IMAP_PORT": "1143",
"MCP_EMAIL_SERVER_IMAP_SSL": "false",
"MCP_EMAIL_SERVER_IMAP_START_SSL": "true",
"MCP_EMAIL_SERVER_IMAP_VERIFY_SSL": "false",
"MCP_EMAIL_SERVER_SMTP_VERIFY_SSL": "false"
}
}
}
}
Or in TOML configuration:
[[emails]]
account_name = "protonmail"
# ... other settings ...
[emails.incoming]
host = "127.0.0.1"
port = 1143
use_ssl = false
start_ssl = true
verify_ssl = false
[emails.outgoing]
verify_ssl = false
For separate IMAP/SMTP credentials, you can also use:
MCP_EMAIL_SERVER_IMAP_USER_NAME/MCP_EMAIL_SERVER_IMAP_PASSWORDMCP_EMAIL_SERVER_SMTP_USER_NAME/MCP_EMAIL_SERVER_SMTP_PASSWORD
Then you can try it in Claude Desktop. If you want to intergrate it with other mcp client, run $which mcp-email-server for the path and configure it in your client like:
{
"mcpServers": {
"mcp-email-server": {
"command": "{{ ENTRYPOINT }}",
"args": ["stdio"]
}
}
}
Usage
Replying to Emails
To reply to an email with proper threading (so it appears in the same conversation in email clients):
- First, fetch the original email to get its
message_id:
emails = await get_emails_content(account_name="work", email_ids=["123"])
original = emails.emails[0]
- Send your reply using
in_reply_toandreferences:
await send_email(
account_name="work",
recipients=[original.sender],
subject=f"Re: {original.subject}",
body="Thank you for your email...",
in_reply_to=original.message_id,
references=original.message_id,
)
The in_reply_to parameter sets the In-Reply-To header, and references sets the References header. Both are used by email clients to thread conversations properly.
Development
This project is managed using uv.
Try make install to install the virtual environment and install the pre-commit hooks.
Use uv run mcp-email-server for local development.
Releasing a new version
- Create an API Token on PyPI.
- Add the API Token to your projects secrets with the name
PYPI_TOKENby visiting this page. - Create a new release on Github.
- Create a new tag in the form
*.*.*.
For more details, see here.