anymail-mcp
anymail-mcp
Connect ANY mailbox to ANY AI agent. Expose Gmail, Outlook, Apple Mail (iCloud), Fastmail, ProtonMail, Yahoo, and every self-hosted server (Stalwart, Dovecot, Postfix, Mailcow) as Model Context Protocol tools, so any MCP-compatible agent (Claude Desktop, Hermes, your own scripts) can read, search, send, reply to, and manage email.
9 tools · 82 unit tests · HTTP/MCP daemon (no stdio zombies) · MIT
┌──────────────────┐ HTTP + SSE ┌──────────────────┐ IMAP ┌────────────┐
│ MCP client │ ────────────────► │ anymail-mcp │ ───────►│ Mail │
│ (Claude, │ │ (HTTP daemon) │ │ server │
│ Hermes, etc.) │ ◄──────────────── │ :3143/mcp │ SMTP │ (any) │
└──────────────────┘ Server-Sent Eve └──────────────────┘ ───────►└────────────┘Why httpStream (not stdio)?
This server defaults to httpStream — a persistent HTTP daemon that speaks
MCP over Server-Sent Events. Unlike stdio (one process per client session),
httpStream is:
A single daemon serving multiple clients — no zombie processes piling up
Always warm — IMAP/SMTP connections stay alive between requests
Network-accessible — any machine on your LAN (or via reverse proxy) can connect to the same instance
systemd-friendly — runs as a managed service, restarts on crash
Use stdio only for single-user desktop apps (Claude Desktop local integration).
📦 Install
Option A — npm (fastest)
npm install -g anymail-mcpThen create a config file and start:
# Create config
cat > ~/.anymail-mcp.env << 'EOF'
IMAP_HOST=imap.gmail.com
IMAP_USER=you@gmail.com
IMAP_PASS=your-app-password
SMTP_HOST=smtp.gmail.com
SMTP_USER=you@gmail.com
SMTP_PASS=your-app-password
EOF
# Start the daemon (reads .env from cwd or ~/.anymail-mcp.env)
anymail-mcp
# → [INFO] HTTP Stream listening on http://0.0.0.0:3143/mcpOption B — From source
git clone https://github.com/DeamonDev888/anymail-mcp.git
cd anymail-mcp
npm install
npm run build # TypeScript → dist/
cp .env.example .env
# Edit .env with your IMAP/SMTP credentials
npm start
# → [INFO] HTTP Stream listening on http://0.0.0.0:3143/mcpOption C — systemd daemon (production)
# Clone + build
git clone https://github.com/DeamonDev888/anymail-mcp.git /opt/anymail-mcp
cd /opt/anymail-mcp
npm install && npm run build
# Config
cp .env.example .env
nano .env # fill in credentials
# Create systemd service
cat > /etc/systemd/system/anymail-mcp.service << 'EOF'
[Unit]
Description=anymail-mcp — IMAP/SMTP MCP Server
After=network.target
[Service]
Type=simple
User=mail
WorkingDirectory=/opt/anymail-mcp
EnvironmentFile=/opt/anymail-mcp/.env
ExecStart=/usr/bin/node /opt/anymail-mcp/dist/index.js
Restart=always
RestartSec=10
[Install]
WantedBy=multi-user.target
EOF
sudo systemctl daemon-reload
sudo systemctl enable --now anymail-mcp
systemctl status anymail-mcpThe anymail-mcp bin helper
When installed via npm install -g, the package ships a CLI helper
(dist/index.js with #!/usr/bin/env node shebang) registered as:
$ anymail-mcpIt loads .env from the current working directory (or from
~/.anymail-mcp.env), validates config, and starts the HTTP daemon on
:3143/mcp. Use FASTMCP_TRANSPORT=stdio env var to switch to stdio mode
for desktop integrations.
🔌 Connect your MCP client
Once the daemon is running on http://localhost:3143/mcp, add it to any
MCP-compatible client config:
Claude Desktop / Claude Code
{
"mcpServers": {
"mail": {
"url": "http://localhost:3143/mcp"
}
}
}Hermes Agent
In config.yaml:
mcp:
mail:
url: "http://localhost:3143/mcp"Remote server
Replace localhost with your server hostname:
{
"mcpServers": {
"mail": {
"url": "http://mail.myserver.com:3143/mcp"
}
}
}⚙️ Configuration
All configuration is via environment variables (.env file or shell exports).
Required
Variable | Description |
| IMAP server hostname (e.g. |
| IMAP username (often your full email address) |
| IMAP password or app password |
| SMTP server hostname (often same as IMAP host) |
| SMTP username |
| SMTP password or app password |
Optional
Variable | Default | Description |
|
| IMAP port (993 = SSL, 143 = STARTTLS) |
|
| Use implicit TLS |
|
| Reject invalid TLS certs (set |
|
| SMTP port (465 = SSL, 587 = STARTTLS) |
|
| Use implicit TLS |
|
| Reject invalid TLS certs |
|
| Override "From" address (aliases) |
|
|
|
|
| HTTP port (httpStream mode) |
|
| Bind address ( |
|
|
|
|
| Log file directory |
Security (optional, opt-in)
Variable | Default | Description |
| (none) | Bearer token for HTTP transport auth |
| (all) | Comma-separated recipient domain allowlist |
|
| Mask emails + passwords in logs |
Example .env
IMAP_HOST=imap.gmail.com
IMAP_USER=you@gmail.com
IMAP_PASS=abcd efgh ijkl mnop
IMAP_PORT=993
IMAP_SECURE=true
SMTP_HOST=smtp.gmail.com
SMTP_USER=you@gmail.com
SMTP_PASS=abcd efgh ijkl mnop
SMTP_PORT=465
SMTP_SECURE=true
FASTMCP_TRANSPORT=httpStream
FASTMCP_PORT=3143
FASTMCP_HOST=0.0.0.0
LOG_LEVEL=info🧰 Tools (9)
Tool | Description |
| Server details + inbox message counts |
| List all mailboxes (INBOX, Sent, Drafts, Junk...) |
| List emails with subject, from, date, preview |
| Read full email body by UID |
| Search by keyword (subject/from/to/body) |
| Send plain-text or HTML email via SMTP |
| Reply to a thread (In-Reply-To + References) |
| Toggle \Seen flag (read/unread) |
| Delete email (flag + expunge) |
list_emails
Param | Type | Default | Description |
| string |
| Mailbox name |
| number |
| Max results (1–500) |
| boolean |
| Only unread |
Returns: [{ uid, subject, from, to, date, flags, preview }]
read_email
Param | Type | Default | Description |
| number | (required) | Email UID from |
| string |
| Mailbox name |
| boolean |
|
|
Returns: { uid, subject, from, to, date, flags, preview, body, truncated? }
search_emails
Param | Type | Default | Description |
| string | (required) | Keyword |
| string |
| Mailbox |
| number |
| Max results |
send_email
Param | Type | Default | Description |
| string | (required) | Recipient |
| string | (required) | Subject line |
| string | (required) | Email body (plain text or HTML) |
| boolean |
| Treat body as HTML |
| boolean |
| Validate payload without sending |
Returns: { status: "sent", to, subject, messageId, sizeBytes }
reply_email
Param | Type | Default | Description |
| number | (required) | UID of email to reply to |
| string | (required) | Reply body (plain text) |
| string |
| Mailbox containing original |
Reads the original email, extracts Message-ID + From + Subject, then sends a
reply with proper threading headers (In-Reply-To, References) and Re:
prefix.
Returns: { status: "sent", to, subject, messageId, inReplyTo }
mark_read / delete_email
Param | Type | Default | Description |
| number | (required) | Email UID |
| string |
| Mailbox |
| boolean |
| (mark_read only) |
📧 Provider quick-start
Enable 2FA → Google Account Security
Create an app password
Use the app password as
IMAP_PASS/SMTP_PASS
IMAP_HOST=imap.gmail.com
IMAP_PORT=993
IMAP_SECURE=true
SMTP_HOST=smtp.gmail.com
SMTP_PORT=465
SMTP_SECURE=trueEnable 2FA on your Microsoft account
Create an app password via Microsoft account security
IMAP_HOST=outlook.office365.com
IMAP_PORT=993
IMAP_SECURE=true
SMTP_HOST=smtp.office365.com
SMTP_PORT=587
SMTP_SECURE=falseEnable 2FA on your Apple Account
Generate an app-specific password at appleid.apple.com → Sign-In and Security → App-Specific Passwords
IMAP_HOST=imap.mail.me.com
IMAP_PORT=993
IMAP_SECURE=true
SMTP_HOST=smtp.mail.me.com
SMTP_PORT=587
SMTP_SECURE=falseIMAP_HOST=imap.fastmail.com
IMAP_PORT=993
IMAP_SECURE=true
SMTP_HOST=smtp.fastmail.com
SMTP_PORT=465
SMTP_SECURE=trueRequires ProtonMail Bridge running locally:
IMAP_HOST=127.0.0.1
IMAP_PORT=1143
IMAP_SECURE=false
SMTP_HOST=127.0.0.1
SMTP_PORT=1025
SMTP_SECURE=false
IMAP_USER=your-bridge-username
IMAP_PASS=your-bridge-passwordGenerate an app password in Yahoo Account Security.
IMAP_HOST=imap.mail.yahoo.com
SMTP_HOST=smtp.mail.yahoo.com
IMAP_PORT=993
IMAP_SECURE=true
SMTP_PORT=465
SMTP_SECURE=trueIMAP_HOST=imap.zoho.com
IMAP_PORT=993
IMAP_SECURE=true
SMTP_HOST=smtp.zoho.com
SMTP_PORT=465
SMTP_SECURE=trueFor self-signed certificates:
IMAP_REJECT_UNAUTHORIZED=false
SMTP_REJECT_UNAUTHORIZED=falseFor valid Let's Encrypt certs in production:
IMAP_REJECT_UNAUTHORIZED=true
SMTP_REJECT_UNAUTHORIZED=true🛠️ Development
npm run build # TypeScript → dist/
npm run dev # Watch mode (tsx watch)
npm run lint # ESLint
npm test # Unit tests (vitest)
npm run test:watch # Tests in watch mode
npm run format # PrettierProject layout
src/
index.ts # Entry point — FastMCP server setup
config.ts # Env loading + Zod validation
logger.ts # Pino logger
mail-service.ts # IMAP/SMTP operations (ImapFlow + nodemailer)
tools.ts # 9 MCP tool definitions (Zod schemas)
security.ts # Input sanitization + allowlist enforcement
tests/
*.test.ts # 82 unit tests
dist/ # Compiled output (gitignored)Adding a new tool
Add a method to
MailServiceinsrc/mail-service.tsRegister the tool in
registerMailTools()insrc/tools.tsAdd tests in
tests/
🔒 Security
No secrets in code — all credentials come from environment variables
Use app passwords for Gmail/Outlook/Yahoo (never your account password)
Set
IMAP_REJECT_UNAUTHORIZED=truein production with valid certsBind
FASTMCP_HOST=127.0.0.1if the client runs on the same hostFor remote access, use a reverse proxy (nginx) with TLS termination
AUTH_TOKENenables Bearer authentication for HTTP transportALLOWED_DOMAINSrestricts who can receive emails viasend_emailREDACT_LOGS=truemasks emails + passwords in structured logs
📜 License
MIT — see LICENSE.
🤝 Contributing
Issues and PRs welcome at github.com/DeamonDev888/anymail-mcp.
Please run npm run lint && npm test before submitting.
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- 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/DeamonDev888/anymail-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server