Skip to main content
Glama
EdmundLimBoEn

imap-mcp

imap-mcp

A small MCP server that bridges one or more IMAP mailboxes over Streamable HTTP, so AI agents can read, search and triage mail, and optionally send it.

Enabled by default: reading, searching and read-status changes. No tool can move or delete mail; mark_read / mark_unread only touch the \Seen flag. Sending is off unless you set SMTP_ENABLED=true; when it is off, the send_message tool is not registered at all.

Tools

Tool

What it does

list_accounts

Configured account names and addresses (no hosts or passwords)

list_folders

IMAP folders for an account

list_messages

Newest messages in a folder (folder, limit, unread_only)

search

from, subject, text, since, before (YYYY-MM-DD)

read_message

Headers, plain-text body (capped at MAX_BODY_CHARS), attachment names. Does not mark the message read

mark_read / mark_unread

Set or clear \Seen on UIDs

send_message

Only when SMTP_ENABLED=true. account, to[], cc[]?, subject, body, in_reply_to?. Plain text. in_reply_to sets both In-Reply-To and References. Addresses are validated and CR/LF in any header is rejected. After sending, a copy is APPENDed to the Sent folder (\Sent special-use, else Sent, or IMAP_<NAME>_SENT)

Message results include uidValidity as a decimal string. Pass it with the same account, folder and UID(s) to read_message, mark_read, and mark_unread. If the mailbox UIDVALIDITY changes, these operations reject the stale reference; list or search again before retrying.

If HTML-to-text conversion fails, read_message includes bodyUnavailable: "conversion_failed" while preserving available headers, attachment metadata, and any available text. The body may be incomplete even when truncated is false; truncated only indicates the character cap.

Related MCP server: Email MCP Server

Configuration (env vars only)

Var

Required

Default

Notes

MCP_BEARER_TOKEN

yes

≥16 chars. The server refuses to start without it

IMAP_ACCOUNTS

yes

Comma-separated names, e.g. work,personal

IMAP_<NAME>_HOST

yes

e.g. imap.example.com

IMAP_<NAME>_USER

yes

e.g. user@example.com

IMAP_<NAME>_PASS

yes

Use an app password

IMAP_<NAME>_PORT

no

993 (TLS) / 143

IMAP_<NAME>_TLS

no

true

Implicit TLS; false requires STARTTLS

IMAP_<NAME>_SMTP_HOST

for sending

e.g. smtp.example.com. Reuses IMAP_<NAME>_USER / _PASS

IMAP_<NAME>_SMTP_PORT

no

465

IMAP_<NAME>_SMTP_TLS

no

true

Implicit TLS; false requires STARTTLS (set the SMTP port accordingly)

IMAP_<NAME>_SENT

no

auto

Sent folder override

SMTP_ENABLED

no

false

Global gate; send_message exists only when true

PORT

no

8787

HOST

no

0.0.0.0

Listen address inside the container

MAX_BODY_CHARS

no

20000

Body cap for read_message

BIND_IP

compose only

Host IP to publish on (e.g. your Tailscale IP)

IMAP_TLS_INSECURE

no

false

Skip TLS cert verification (testing only)

<NAME> is the account name upper-cased. See .env.example.

read_message rejects messages larger than 1 MiB of raw MIME data, including attachments, before parsing. It checks the reported size before downloading and enforces the byte cap during download, independently of MAX_BODY_CHARS.

Boolean settings accept true/false, 1/0, yes/no, and on/off (case-insensitive). Unset or empty values use the default; other values are rejected.

Numeric settings accept decimal digits only: ports must be 1–65535, and MAX_BODY_CHARS must be 1–1048576. Unset values use the defaults; empty or invalid values prevent startup.

Run

cp .env.example .env   # fill in real values; .env is git-ignored
docker run -d --name imap-mcp --restart unless-stopped \
  --env-file .env -p "$BIND_IP:8787:8787" \
  --read-only --cap-drop ALL --security-opt no-new-privileges \
  ghcr.io/edmundlimboen/imap-mcp:latest

Or docker compose up -d (binds to ${BIND_IP}:8787). Or locally: npm ci && npm run build && npm start.

Check: curl http://$BIND_IP:8787/health → {"ok":true}.

Add as an MCP connector

  • URL: http://<BIND_IP>:8787/mcp

  • Transport: Streamable HTTP

  • Header: Authorization: Bearer <MCP_BEARER_TOKEN>

Example client config:

{
  "mcpServers": {
    "imap": {
      "url": "http://<BIND_IP>:8787/mcp",
      "headers": { "Authorization": "Bearer <MCP_BEARER_TOKEN>" }
    }
  }
}

Security

  • Every request except GET /health needs the bearer token; compared in constant time (SHA-256 + timingSafeEqual).

  • /health returns only {"ok":true}.

  • The server is stateless: each POST to /mcp opens a fresh IMAP connection per tool call.

  • Expose it only on a private network (e.g. Tailscale). It has no TLS of its own.

  • Container runs as the non-root node user.

Development

The GreenMail smoke tests explicitly use new ImapBackend({ allowPlaintextForTests: true }) for their plaintext server. This option is rejected unless NODE_ENV=test; normal connections always require TLS.

npm ci
npm test                       # unit tests (mocked IMAP layer)
docker run -d -p 3025:3025 -p 3143:3143 \
  -e GREENMAIL_OPTS="-Dgreenmail.setup.test.all -Dgreenmail.hostname=0.0.0.0 -Dgreenmail.users=smoke:smoke@example.test -Dgreenmail.users.login=email" \
  greenmail/standalone:2.1.12
GREENMAIL_HOST=localhost npm test   # adds the end-to-end smoke test

CI runs both and publishes ghcr.io/edmundlimboen/imap-mcp:latest (and :<sha>) on pushes to main, and :X.Y.Z for vX.Y.Z tags.

SMTP results include status (submitted, partial_submission, or rejected), accepted, rejected, and rejectionDetails with recipient addresses and numeric SMTP rejection codes when available. sent is true only when all recipients were accepted for submission; this does not confirm delivery. A partial submission still saves a Sent copy. retryable lists only recipients with temporary (4xx) rejections; retry those later, never the accepted recipients. Permanent (5xx) or unknown rejections require review. Raw SMTP rejection text is omitted.

Request resource limits are shared by all stateless HTTP requests for the configured bearer token: 120 requests and 10 send attempts per rolling minute, at most 24 in-flight HTTP requests, 8 active tool operations globally, 2 per account, and 16 queued operations. HTTP admission failures return 429 with Retry-After; queue and send-quota failures return MCP tool errors. Requests and operations have a 30-second deadline (including queue time). Disconnects and MCP cancellation remove queued work and close active mail connections/downloads. A cancelled SMTP submission may already have been accepted by the mail server; verify delivery before retrying. Limits are process-local; replicas each have their own budget.

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    C
    maintenance
    Exposes standard IMAP email accounts as tools so a chat client can list folders and recent messages, search by text across one or all configured accounts, read full message bodies, and mark messages read or unread. Credentials are supplied securely through environment variables, and the server never deletes, moves, or sends mail.
    929 npm
    MIT
  • A
    license
    Not graded
    quality
    A
    maintenance
    Enables AI assistants to read, search, send, schedule, and organize email across multiple IMAP/SMTP accounts, including drafts, provider-aware labels, real-time inbox watching, verification-code capture, and analytics. Supports OAuth2, calendar extraction, and both local stdio and networked Streamable HTTP access.
    21 npm
    ISC
  • A
    license
    Not graded
    quality
    A
    maintenance
    Enables AI assistants to read, search, send, reply, forward, schedule, and analyze email across multiple IMAP/SMTP accounts, with drafts, templates, provider-aware labels, bulk operations, attachment handling, calendar extraction, and real-time IMAP IDLE watching. Connections are secured with TLS/STARTTLS, OAuth2 support, and rate limiting, and it can run over stdio locally or Streamable HTTP for networked access.
    21 npm
    ISC
  • F
    license
    Not graded
    quality
    A
    maintenance
    Enables AI assistants to read, search, send, schedule, manage, and analyze mail across multiple IMAP/SMTP accounts, including drafts, provider-aware labels, real-time IDLE triage, verification-code capture, and calendar extraction. Runs over stdio for local clients or Streamable HTTP for networked access, with a guided setup wizard and OAuth2 support.
    -