read-email-mcp
v1.0.0
Published
Read-only MCP server that mirrors any IMAP mailbox into a local SQLite copy for browsing and searching email.
Maintainers
Readme
Read Email MCP
A read-only Model Context Protocol server that mirrors any IMAP mailbox into a local SQLite database, then lets AI clients browse, search, and read email without reconnecting to the server on every request.
It works with Gmail, Outlook, Yahoo, Fastmail, iCloud, Proton Mail Bridge, self-hosted mail, and any other provider that exposes standard IMAP.
Version 1.0.0-rc.1 release candidate. The tool surface and configuration are stable for review. Please report issues.
Why use it
- Fast browsing and search. Email is synced once into a local SQLite mirror with full-text search. Every browse, search, and read call hits the local copy, so it is quick and never re-logs-in to IMAP per request.
- Read-only and safe. It never sends, deletes, moves, flags, or marks messages. IMAP mailboxes are opened with read-only locks. Your mailbox cannot be mutated by this server.
- Provider agnostic. Point it at any IMAP server with host, port, and TLS settings. No vendor lock-in.
- Private by design. Credentials stay in the process environment. Passwords are never written to disk. Raw
.emlfiles live under your home directory and never leave your machine. - MCP native. Exposes a clean set of tools over stdio so any MCP-compatible client can read email on your behalf.
Requirements
- Node.js 20 or newer
- A mailbox with IMAP enabled
- The mailbox email address and password. Most consumer providers (Gmail, Yahoo, iCloud, Outlook with 2FA) require an app-specific password instead of your normal sign-in password.
Quick start
git clone <this-repo>
cd read-email-mcp
npm install
npm run buildRun the server with credentials and an IMAP host:
node dist/index.js `
--email "[email protected]" `
--password "your-app-password" `
--imap-host "imap.gmail.com"Or set environment variables and omit the flags:
$env:EMAIL = "[email protected]"
$env:PASSWORD = "your-app-password"
$env:IMAP_HOST = "imap.gmail.com"
node dist/index.jsConfiguration
All settings can be passed as CLI args or environment variables. CLI args take precedence over environment variables.
| Setting | Required | Env var | CLI arg | Default |
| --- | --- | --- | --- | --- |
| Email address | yes | EMAIL | --email | none |
| Password | yes | PASSWORD | --password | none |
| IMAP host | yes | IMAP_HOST | --imap-host | none |
| IMAP port | no | IMAP_PORT | --imap-port | 993 |
| IMAP TLS | no | IMAP_SECURE | --imap-secure | true |
| IMAP login user | no | IMAP_USER | --imap-user | same as email |
IMAP_SECURE accepts true or false (also 1/0, yes/no, on/off). Use false with port 143 for STARTTLS or plain IMAP.
IMAP_USER is optional. Set it only when the IMAP login name differs from the mailbox email address. When omitted, the email address is used as the IMAP username.
Common provider settings
| Provider | IMAP host | Port | TLS | Notes |
| --- | --- | --- | --- | --- |
| Gmail | imap.gmail.com | 993 | true | Needs an app password with 2FA enabled |
| Outlook / Office 365 | outlook.office365.com | 993 | true | Use your Microsoft account password or app password |
| Yahoo | imap.mail.yahoo.com | 993 | true | Needs an app password |
| iCloud | imap.mail.me.com | 993 | true | Needs an app password |
| Fastmail | imap.fastmail.com | 993 | true | Use your Fastmail app password |
| Proton Mail | 127.0.0.1 | 1143 | false | Requires the Proton Mail Bridge app running locally |
| Self-hosted | your mail server | 993 or 143 | true or false | Depends on your server config |
MCP client configuration
The server speaks MCP over stdio. Add it to any MCP-compatible client.
Claude Desktop
{
"mcpServers": {
"read-email": {
"command": "npx",
"args": [
"-y",
"[email protected]",
"--email",
"[email protected]",
"--password",
"your-app-password",
"--imap-host",
"imap.gmail.com"
]
}
}
}Codex CLI and other env-based clients
Set credentials in the server environment and pass only the binary:
{
"mcpServers": {
"read-email": {
"command": "npx",
"args": ["-y", "[email protected]"],
"env": {
"EMAIL": "[email protected]",
"PASSWORD": "your-app-password",
"IMAP_HOST": "imap.gmail.com"
}
}
}
}On npm, pre-release versions like
1.0.0-rc.1are not selected by@latest. Pin the exact version as shown above, or install from source after building.
Tools
The server exposes 12 tools. All read from the local mirror except sync_email, which pulls from IMAP.
sync_email: pull new messages from IMAP into the local mirror. Call this first, or after a gap, before browsing or searching. Start with a small limit on one mailbox to verify credentials, then sync more.get_sync_status: show account, storage path, total messages, latest message date, per-mailbox counts, and the last sync result. Good first check to see whether a sync is needed.list_mailboxes: list locally known mailboxes (INBOX, Sent, Drafts, and so on) with message counts and last-sync time.get_recent_emails: show the latest emails, newest first, with optionalunreadOnlyandflaggedOnly. This is the everyday tool for checking what is new.browse_email_dates: summarize message volume as counts per day, week, or month. Useful for finding a busy period before drilling in with a date range.search_emails: full-text search across subject, sender and recipient addresses, preview, and body, with filters and keyset pagination.list_emails: list email summaries in chronological order with filters and pagination, without full-text search. Prefer this when browsing by sender, date, or mailbox.get_email: return full metadata and body (text and HTML) for a single message, by id, Message-ID, or mailbox plus UID.get_thread: return all locally known messages in one conversation, matched by Message-ID, In-Reply-To, and References headers.get_raw_email: return raw RFC822/MIME source for one message, byte-limited. Use this only when you need original headers or MIME structure.get_storage_stats: report SQLite database, WAL, and page statistics for the local mirror.optimize_email_store: run lightweight SQLite and FTS maintenance to keep queries fast. Safe to run after large syncs.
search_emails, list_emails, and get_recent_emails accept unread and flagged filters. search_emails and list_emails use limit, cursor, and nextCursor for keyset pagination.
Typical workflow
- Run
sync_emailwith a small limit on one mailbox to verify credentials. - Run
get_sync_statusto confirm the mirror is fresh. - Run
get_recent_emailswithunreadOnly: trueto see what is new. - Run
get_emailto read a message, orget_threadto follow a conversation. - Run
search_emailsorlist_emailsto find older messages by sender, subject, date range, or unread and flagged state.
How sync works
Sync connects to IMAP, lists selectable mailboxes, and fetches new messages by tracking the highest UID already seen per mailbox. If a mailbox UIDVALIDITY changes, the local copy for that mailbox is reset and refetched. Raw RFC822 source is written to disk, parsed metadata and body text go into SQLite, and a full-text search index is maintained alongside.
Sync is incremental. Subsequent calls only fetch messages with UIDs higher than the stored high-water mark. Mailboxes are opened read-only, so flags and read state on the server are never changed.
Local storage
The mirror is stored under:
~/.read-email-mcp/<account-hash>/
emails.sqlite
raw/The account hash is derived from the email address, so multiple accounts coexist without exposing the address in folder names. Passwords are never written to disk.
Raw .eml files contain full email content. Treat the storage directory as sensitive and protect it the same way you protect your mailbox.
Safety
- The server is read-only. It does not send, delete, move, archive, mark read, or mutate flags.
- IMAP mailboxes are opened with read-only locks during sync.
- Credentials live only in the process environment and are never persisted by this server.
- The SQLite mirror uses WAL mode, a busy timeout, keyset pagination, and periodic FTS optimization for local performance and concurrency.
Development
npm test # unit tests (no real mailbox needed)
npm run typecheck # TypeScript checks
npm run build # compile to dist/
npm run smoke:mcp # start the built server over stdio and verify all tools
npm run verify # runs all of the aboveTests use fixtures and fake IMAP clients, so they do not require real credentials or a live mail server.
Limitations
- No sending, replying, or composing email.
- No deleting, moving, flagging, or marking messages read.
- No OAuth. App passwords or direct passwords only.
- No background daemon. Sync runs when the client calls
sync_email. - Attachment bytes are summarized but not extracted to separate files.
License
MIT
