Skip to main content
Glama
MinKyawNyunt

IMAP MCP Connector

by MinKyawNyunt

IMAP MCP Connector

Use your own mailbox from Claude or ChatGPT. This is a small self-hosted server: people sign in with their email address and (app) password, choose what the AI may do, and paste one connector URL into their AI app.

  • Works with any IMAP mailbox that accepts a password or app password: Gmail, iCloud, Yahoo, Fastmail, Zoho, most hosting providers and self-run servers.

  • One deployment serves many users, for example a company or a family.

  • Per-user permission levels: Read only, Read and draft (default), Full access (send and delete-to-Trash).

  • Remote MCP server with OAuth 2.1 (dynamic client registration, PKCE), so it works as a custom connector in Claude and ChatGPT.

Not supported yet: Outlook.com and Microsoft 365, which no longer allow password sign-in over IMAP.

Quickstart (Docker Compose, ~5 minutes)

  1. Point a DNS name (e.g. mail.example.com) at your server, with ports 80 and 443 open.

  2. Clone this repository and create your config:

    cp .env.example .env
    # fill in DOMAIN, PUBLIC_URL, ENCRYPTION_KEY (openssl rand -base64 32),
    # SESSION_SECRET (openssl rand -hex 32), ALLOWED_EMAIL_DOMAINS or ALLOWED_IMAP_HOSTS
  3. Start it:

    docker compose up -d
  4. Open https://mail.example.com, sign in with your email and app password, and follow the connect instructions shown on the settings page.

Caddy obtains HTTPS certificates automatically.

Related MCP server: IMAP MCP Server

Connect to Claude

Settings → Connectors → Add custom connector → paste https://<your-domain>/mcp → Connect. Sign in and approve access when prompted.

Connect to ChatGPT

Settings → Connectors (turn on developer mode if your plan requires it) → Create → paste https://<your-domain>/mcp → authentication OAuth. Sign in and approve access when prompted.

App passwords

Most big providers refuse your normal password over IMAP once two-factor sign-in is on. Create an app password and use it to sign in:

Lost it? Create a new one and sign in again. The stored password is replaced.

Configuration

Variable

Required

Meaning

PUBLIC_URL

yes

External origin, https:// only (no path).

ENCRYPTION_KEY

yes

32 random bytes, base64. Encrypts stored mail passwords. Back it up: if it is lost, users must sign in again.

SESSION_SECRET

yes

≥ 32 characters, signs session cookies.

ALLOWED_EMAIL_DOMAINS

at least one of these

Comma-separated email domains allowed to sign in.

ALLOWED_IMAP_HOSTS

at least one of these

Comma-separated IMAP hosts allowed; * allows any public server.

ALLOW_PRIVATE_HOSTS

no (false)

Allow mail servers on private networks and unencrypted connections.

DEFAULT_SEND_LIMIT_PER_HOUR

no (20)

Emails each user's AI may send per hour.

DATA_DIR

no (/data)

Where the SQLite database lives.

PORT

no (3000)

Listen port.

When both allowlists are set, a sign-in must pass both: the email domain must be in ALLOWED_EMAIL_DOMAINS and the IMAP host must be in ALLOWED_IMAP_HOSTS (unless it is *). A list that is not set does not restrict, so ALLOWED_EMAIL_DOMAINS alone allows any public mail server for those domains. The check runs at sign-in, when SMTP settings change, and again before each new mail connection.

Server settings are auto-discovered on first sign-in; settings typed into the sign-in form are only used when discovery finds nothing, and the first successful sign-in pins the server for that address. If the automatic lookup fails temporarily (for example the settings database or DNS is unreachable), sign-in is refused with a request to try again rather than falling back to typed settings, so manually entered settings are only accepted for domains that publish no settings at all. For domains that cannot be auto-discovered, whoever first signs in with working settings decides the server, so with ALLOWED_IMAP_HOSTS=* anyone could claim such an address on a server they control. In shared deployments, set ALLOWED_IMAP_HOSTS to the servers your users actually use.

To change one user's send limit (run in the deployment directory):

docker compose exec app node -e "const db=require('better-sqlite3')('/data/imap-connector.db'); db.prepare('UPDATE users SET send_limit_per_hour = ? WHERE email = ?').run(50, 'someone@example.com')"

Tools the AI gets

Level

Tools

Read only

list_folders, search_messages, get_message, get_thread, get_attachment

Read and draft

+ set_flags, move_messages, create_draft

Full access

+ send_email, delete_messages (moves to Trash, never permanent)

Security model

  • What is stored: each user's email address, server settings, and mail password encrypted with AES-256-GCM using ENCRYPTION_KEY. OAuth codes and tokens are stored only as SHA-256 hashes.

  • What is never logged: passwords, tokens, or message content.

  • Who can sign in: only addresses or servers on your allowlist. Mail hosts resolving to private addresses are rejected unless ALLOW_PRIVATE_HOSTS=true.

  • Prompt injection: an email can contain text written to manipulate an AI. Email content is marked as untrusted for the model, tools carry read-only/destructive hints so AI apps ask before sending, sending is rate-limited, and sending is off by default. These measures reduce the risk but cannot eliminate it. Only enable Full access if you accept that risk.

  • Users can revoke any connected AI app, or delete their account, from the settings page.

Deployment notes and known limitations

  • Plain-http PUBLIC_URL: it works only for localhost / 127.0.0.1, even with NODE_ENV=development. The MCP SDK rejects any other non-https issuer URL.

  • Run behind exactly one reverse proxy: the app sets Express trust proxy to 1, as in the provided compose file with Caddy. If you expose the app directly without a proxy, clients can spoof X-Forwarded-For and weaken the per-IP sign-in rate limit.

  • Folder roles on servers without SPECIAL-USE: roles such as sent or trash are resolved from the server's SPECIAL-USE flags, then from common English top-level folder names, then from localized top-level names (for example "Gesendet" or "Papierkorb"). If a role still does not resolve, tools that take a folder accept the exact folder path returned by list_folders; create_draft, saving to Sent after send_email, and delete_messages need the Drafts, Sent and Trash roles to resolve.

Development

npm install
npm test                 # unit tests
npm run test:integration # needs Docker (GreenMail test server)
npm run dev              # needs the env vars above; NODE_ENV=development allows http://localhost

Gmail's native thread search (X-GM-EXT-1) is not covered by the automated tests. Check it manually against a Gmail account before releases.

License

MIT. Mail provider presets are adapted from nikolausm/imap-mcp-server (MIT); see THIRD_PARTY_NOTICES.md.

Related MCP Connectors

Related MCP Servers

  • A
    license
    A
    quality
    A
    maintenance
    Enables Claude to interact with email accounts via IMAP and SMTP, providing tools for searching, reading, sending, and managing emails across multiple providers.
    40
    1,877 npm
    98
    MIT
  • F
    license
    Not graded
    quality
    D
    maintenance
    Enables Claude to read, search, send, and manage emails across multiple IMAP/SMTP accounts via a single deployment.
    -
  • A
    license
    Not graded
    quality
    D
    maintenance
    Connects Claude to Microsoft 365 Outlook and Google Gmail APIs for email search, invoice detection, attachment handling, folder/label management, and draft creation. Uses Anthropic OAuth for secure authentication.
    25 npm
    ISC