Skip to main content
Glama

mail-mcp

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.

Repos: GitHub · GitLab


Architecture

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

Secret resolution (3 tiers)

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)

Transports

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


Supported accounts

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.


Quick start

# 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

Admin surfaces

1. CLI — mail-admin

mail-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

2. HTTP routes

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

3. Telegram bot

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

4. SSH exec

docker compose exec -T mail-mcp mail-admin status
docker compose logs --tail=100 mail-mcp

MCP agent registration

Claude Code (~/.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 (~/.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/Mistral (~/.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 (~/.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 reference

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


Security

  • Credentials are never stored in config.yaml — only env var names.

  • Secrets live in Bitwarden (GLOBAL_ENV_VARS) and are injected via bw-env / login shell.

  • Admin credential overrides persist in /data/mail-admin.env (Docker volume) — never committed.

  • No OAuth2, no refresh token storage — IMAP password auth via TLS only.


Deployment (Docker / homelab)

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.

-
security - not tested
A
license - permissive license
-
quality - not tested

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/KpihX/mail-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server