Skip to main content
Glama
JACKWULA123

mail-mcp

by JACKWULA123

mail-mcp

A local MCP server that lets AI coding agents (Claude Code and OpenAI Codex CLI) access your mailboxes: search, read messages/threads, read & download attachments, and create drafts — across multiple Gmail and personal Outlook accounts, routed by nickname.

Current capability: read-only + drafts. It never sends, deletes, or relabels mail. Sending/organizing are declared in the provider contract but gated off; enabling them later is a config change, not a rewrite (see Extending).

How safety is enforced

  • Outlook (Graph): we request Mail.Read + Mail.ReadWrite but not Mail.Send, so sending is impossible at the token level.

  • Gmail: there is no scope that grants drafts without also granting send (gmail.compose covers both). So for Gmail, "no send" is enforced at the application layer — no send tool is exposed and drafts.send/messages.send are never called. Keep this in mind before granting the send capability.

Related MCP server: Gmail MCP Server

Install

Requires Python ≥ 3.11.

uv venv --python 3.12 .venv
uv pip install --python .venv/bin/python -e ".[dev]"

Configure

.venv/bin/mail-mcp init            # creates ~/.config/mail-mcp/{config.toml,secrets,tokens,downloads}

Edit ~/.config/mail-mcp/config.toml (see config.example.toml). Each mailbox is one [accounts.<nickname>] block. Everything lives under $MAIL_MCP_HOME (default ~/.config/mail-mcp); set MAIL_MCP_HOME to relocate it.

Gmail OAuth (per Google account)

  1. Google Cloud Console → new project → enable the Gmail API.

  2. OAuth consent screen → User type External → add each Gmail address as a Test user (or publish to Production to avoid the 7-day refresh-token expiry in Testing mode).

  3. Credentials → Create OAuth client ID → Desktop app → download the JSON → save as ~/.config/mail-mcp/secrets/gmail_client_secret.json (the same client can serve multiple Gmail accounts).

  4. .venv/bin/mail-mcp auth gmail-personal

University / Workspace accounts (e.g. @g.ecc.u-tokyo.ac.jp): the admin may restrict third-party API access. If login fails with Error 400: admin_policy_enforced, your self-made client is blocked at the org level — Testing mode does not bypass this; only the Workspace admin can allowlist the client ID. Test a personal @gmail.com account first to confirm your setup, then try the university one.

Outlook OAuth (personal account)

  1. Azure Portal → Microsoft Entra ID → App registrations → New. Supported account types: Personal Microsoft accounts only.

  2. Authentication → Add platform Mobile and desktop applications → redirect http://localhost → mark as a public client.

  3. API permissions → Microsoft Graph → Delegated → add Mail.Read, Mail.ReadWrite, offline_access. Do not add Mail.Send.

  4. Put the Application (client) ID into the outlook block's client_id.

  5. .venv/bin/mail-mcp auth outlook

Check status any time: .venv/bin/mail-mcp accounts

Register with your AI client

Both launch the server over stdio. Use an absolute interpreter path and set MAIL_MCP_HOME explicitly so token discovery never depends on cwd or env forwarding.

Claude Code (user scope — available in every project):

claude mcp add --scope user mail \
  --env MAIL_MCP_HOME=/Users/<you>/.config/mail-mcp \
  -- /path/to/mail_mcp/.venv/bin/python -m mail_mcp

Codex CLI — add to ~/.codex/config.toml (Codex does not forward parent env by default, hence env_vars):

[mcp_servers.mail]
command = "/path/to/mail_mcp/.venv/bin/python"
args = ["-m", "mail_mcp"]
env_vars = ["HOME", "PATH"]

[mcp_servers.mail.env]
MAIL_MCP_HOME = "/Users/<you>/.config/mail-mcp"

Verify: claude mcp list (shows mail ✓ Connected) / Codex /mcp.

Tools

Tool

Purpose

list_accounts

list mailboxes, provider, capabilities, auth status

search_messages(account, query, limit=20)

search (Gmail operators for Gmail; free-text $search for Outlook)

read_message(account, message_id, format="text")

headers + body (text/html/full) + attachment list

read_thread(account, thread_id, format="text")

all messages in a thread/conversation

list_attachments(account, message_id)

attachment id/name/mime/size

download_attachment(account, message_id, attachment_id, dest_dir=None, include_base64=False)

save to disk, return path

create_draft(account, to, subject, body, cc, bcc, reply_to_message_id, body_is_html, attachments)

create an unsent draft

Extending

  • A new mailbox of an existing type → add an [accounts.<nick>] block + mail-mcp auth <nick>. No code.

  • A new mailbox type (e.g. IMAP) → implement MailProvider (src/mail_mcp/providers/base.py) and register it in providers/registry.py. The tool surface is unchanged.

  • Enable sending → add "send" to an account's capabilities. The send_message tool then registers automatically. For Graph, also add Mail.Send and re-run mail-mcp auth; Gmail needs no scope change. (Re-read the Gmail safety note first.)

Development

.venv/bin/pytest        # unit tests (no network)
.venv/bin/ruff check .
.venv/bin/mypy src

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    B
    maintenance
    Provides OpenCode agents with controlled MCP tools to read, search, export attachments, and draft replies for Outlook emails, with approval workflows.
    MIT
  • F
    license
    B
    quality
    B
    maintenance
    Enables external AI agents to read, send, and manage email over IMAP/SMTP via MCP, including inbox listing, search, drafts, scheduled/batch sending, and operations like reply, archive, and labels.
    30
    3
    -