loading…
Search for a command to run...
loading…
A generic IMAP and SMTP MCP server that enables AI agents to interact with email accounts for reading, searching, and sending messages. It provides high-level t
A generic IMAP and SMTP MCP server that enables AI agents to interact with email accounts for reading, searching, and sending messages. It provides high-level tools for managing email workflows like daily digests and folder organization across any standard email provider.
Generic IMAP+SMTP MCP server for AI agents — v0.2.4
Connect any email account to any AI agent (Claude, Gemini, Codex, Copilot, Vibe…). Intent-first design: high-level tools matching real user workflows, backed by a clean IMAP/SMTP core and a full triple admin surface (CLI + HTTP + Telegram).
Problem: AI agents have no native email access.
Why: IMAP+SMTP are universal, server-agnostic, no OAuth2 dance required.
How: FastMCP tools layer, pure stdlib SMTP, imapclient for IMAP, bw-env secrets.
Admin: mail-admin CLI + /admin/* HTTP routes + Telegram bot + SSH exec.
mail_mcp/
├── config.yaml # Non-sensitive settings (hosts, ports, env var names)
├── config.py # @lru_cache loader + 3-tier secret resolution + admin env
│
├── admin/
│ ├── service.py # Shared backend: status, credentials CRUD, logs, summaries
│ ├── cli.py # mail-admin — Typer+Rich admin CLI
│ └── telegram.py # Telegram long-poll bot (in-process thread)
│
├── core/
│ ├── models.py # Pydantic: Message, MessageSummary, Folder, Address…
│ ├── imap_client.py # IMAPClient context-manager — search, fetch, flags, move
│ └── smtp_client.py # SMTPClient — send, reply, forward, draft
│
├── tools/
│ ├── guide.py # mail_guide() — agent orientation entry point
│ ├── read.py # check_inbox, daily_digest, search_messages, get_thread…
│ ├── compose.py # send_message, reply_message, forward_message, save_draft
│ └── manage.py # list_folders, mark_messages, move/archive/delete/spam
│
├── server.py # FastMCP root — mounts all sub-MCPs
├── http_app.py # Starlette app: MCP + /health + /admin/* routes
├── daemon.py # PID file lifecycle
└── cli.py # Typer CLI: serve, serve-http, stop, status, inbox, folders
1. Admin env file → /data/mail-admin.env (Docker volume, persistent credential overrides)
2. Process env → fastest (shell injection or MCP host)
3. bw-env login → zsh -l -c 'printf "%s" "${VAR}"' (Bitwarden GLOBAL_ENV_VARS)
| Transport | URL / Command | Description |
|---|---|---|
| HTTP (homelab) | https://mail.kpihx-labs.com/mcp |
Streamable-HTTP — production, always-on |
| stdio (fallback) | mail-mcp serve |
Direct process — local dev or when HTTP down |
| Account | IMAP | SMTP | Server |
|---|---|---|---|
| Polytechnique (X) | webmail.polytechnique.fr:993 TLS |
:587 STARTTLS |
Zimbra |
More accounts: add an entry in config.yaml — no code change needed.
# Install (editable for live dev)
uv tool install --editable .
# Admin CLI
mail-admin status # credential status table
mail-admin logs 20 # last 20 log lines
mail-admin credentials set poly <login> <pass> # live update without restart
mail-admin help # full capability map
# MCP stdio server
mail-mcp serve
# MCP HTTP server (port 8094)
mail-mcp serve-http
mail-adminmail-admin status [--account <id>] # Rich table: env var, value (masked), source
mail-admin logs [N] # tail last N lines (default 40)
mail-admin credentials set <id> <login> <pass>
mail-admin credentials unset <id>
mail-admin help # full capability map
| Route | Method | Description |
|---|---|---|
/health |
GET | Readiness probe — auth presence per account |
/admin/status |
GET | Full status: pid, transport, Telegram runtime, credentials |
/admin/help |
GET | Full capability map (CLI / HTTP / Telegram / SSH) |
/admin/logs?lines=40 |
GET | Tail of the admin log |
/admin/credentials/set |
POST | Set {account_id, login, password} |
/admin/credentials/unset |
POST | Clear credentials for {account_id} |
/mcp |
GET/POST | Streamable-HTTP MCP transport |
Token env: TELEGRAM_MAIL_HOMELAB_TOKEN — auth gate: TELEGRAM_CHAT_IDS
| Command | Args | Effect |
|---|---|---|
/start /help |
— | Full capability map |
/status |
[account_id] |
Credential status |
/health |
— | Quick health summary |
/urls |
— | Transport URLs |
/logs |
[N] |
Last N log lines |
/credentials_set |
<id> <login> <pass> |
Live credential update |
/credentials_unset |
<id> |
Clear credentials |
/restart |
— | Graceful service restart |
docker compose exec -T mail-mcp mail-admin status
docker compose logs --tail=100 mail-mcp
~/.claude.json)"mail-mcp": {
"url": "https://mail.kpihx-labs.com/mcp"
},
"mail-mcp--fallback": {
"command": "zsh",
"args": ["-l", "-c", "/home/kpihx/.local/bin/mail-mcp serve"]
}
~/.codex/config.toml)[mcp_servers.mail_mcp]
url = "https://mail.kpihx-labs.com/mcp"
[mcp_servers.mail_mcp_fallback]
command = "zsh"
args = ["-l", "-c", "/home/kpihx/.local/bin/mail-mcp serve"]
~/.vibe/config.toml)[[mcp_servers]]
name = "mail"
transport = "http"
url = "https://mail.kpihx-labs.com/mcp"
[[mcp_servers]]
name = "mail_fallback"
transport = "stdio"
command = "zsh"
args = ["-l", "-c", "/home/kpihx/.local/bin/mail-mcp serve"]
~/.gemini/settings.json)"mcpServers": {
"mail-mcp": { "url": "https://mail.kpihx-labs.com/mcp" },
"mail-mcp--fallback": {
"command": "zsh",
"args": ["-l", "-c", "/home/kpihx/.local/bin/mail-mcp serve"]
}
}
| Tool | Intent |
|---|---|
mail_guide |
Agent orientation — start here |
check_inbox |
Unread count + last N summaries |
daily_digest |
Structured morning overview |
list_messages |
Browse a folder |
get_message |
Full body by UID |
search_messages |
Flexible search (query, sender, date, flags) |
find_unread |
Unread shortcut |
get_thread |
Full thread by Message-ID |
send_message |
New email (+ auto copy to Sent, optional bounce probe via verify_bounce_window_seconds) |
reply_message |
Reply by UID (+ auto copy to Sent, optional bounce probe via verify_bounce_window_seconds) |
forward_message |
Forward by UID (+ auto copy to Sent, optional bounce probe via verify_bounce_window_seconds) |
save_draft |
Draft to Drafts folder |
list_folders |
All IMAP folders |
list_labels |
Alias for list_folders |
create_folder |
Create IMAP folder |
rename_folder |
Rename IMAP folder |
delete_folder |
Delete IMAP folder |
mark_messages |
Seen / flagged / answered flags |
move_messages |
Move UIDs to folder |
archive_messages |
Move to Archive |
trash_messages |
Move to Trash |
delete_messages |
Permanent delete + expunge |
mark_as_spam |
Move to Spam/Junk |
download_attachment |
Download to file (default) or ingest as Base64 (ingest_base64=True) |
set_labels |
Alias for move_messages |
config.yaml — only env var names.GLOBAL_ENV_VARS) and are injected via bw-env / login shell./data/mail-admin.env (Docker volume) — never committed.See deploy/ for docker-compose.yml and .env.example.
# Required env vars
X_LOGIN=your_imap_username
X_PASS=your_imap_password
TELEGRAM_MAIL_HOMELAB_TOKEN=<bot_token> # optional
TELEGRAM_CHAT_IDS=<comma,separated,ids> # optional
MAIL_MCP_ADMIN_ENV_FILE=/data/mail-admin.env
GitLab CI auto-deploys master to the homelab runner via docker compose up -d --build.
Добавь это в claude_desktop_config.json и перезапусти Claude Desktop.
{
"mcpServers": {
"mail-mcp": {
"command": "npx",
"args": []
}
}
}