imap-mcp
Read-only MCP server for IMAP email access, enabling AI agents to read, search, and monitor email without sending or deleting messages.
README
imap-mcp
An MCP server that gives AI agents access to email over IMAP, designed for agents that need to read and reason about email without making changes to it.
Philosophy
Read-first, intentionally constrained. imap-mcp is built for agentic workflows where the agent needs to understand a user's email — triage, summarise, search, monitor — but should not be able to send, delete, or move messages. The server is currently read-only, with one deliberate exception: agents can set and clear custom IMAP keywords (flags) on messages. This lets an agent mark messages it has processed, acted on, or flagged for follow-up, without touching the messages themselves.
Standard IMAP first. The server targets standard IMAP servers. Gmail is well-supported for reading, but keyword flagging does not yet work on Gmail — Gmail maps IMAP keywords to labels, which requires special handling. Gmail flagging support is next on the roadmap. Outlook/Microsoft 365 requires OAuth2, which is not currently implemented, so Microsoft accounts are not supported.
Multi-inbox, unified view. Connect as many accounts as you need. All tools accept an optional account parameter — omit it to query across all configured accounts and get a combined, sorted result.
Connect one or more email accounts and agents can read mailboxes, search messages by sender, subject, date, or body text, download attachments, and monitor for new mail — all via the Model Context Protocol. Designed to work with Claude Desktop and any MCP-compatible client.
Quick Start
-
Clone and install:
git clone https://github.com/jonathanhowell/imap-mcp.git cd imap-mcp npm install && npm run build -
Copy the example config:
mkdir -p ~/.config/imap-mcp cp config.example.yaml ~/.config/imap-mcp/config.yaml -
Edit the config — fill in your account details and set environment variables for passwords:
accounts: - name: personal host: imap.gmail.com port: 993 username: you@gmail.com password: $GMAIL_PASSWORDThen export the variable:
export GMAIL_PASSWORD="your-app-password" -
Run the server:
node build/index.js
The server reads the config from ~/.config/imap-mcp/config.yaml by default. Override with IMAP_MCP_CONFIG=/path/to/config.yaml.
Configuration Reference
The config file is YAML. Default location: ~/.config/imap-mcp/config.yaml. Override with the IMAP_MCP_CONFIG environment variable.
accounts[]
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
name |
string | yes | — | Short identifier used by tools (e.g. personal, work) |
host |
string | yes | — | IMAP hostname (e.g. imap.gmail.com) |
port |
number | yes | — | Must be 993. TLS is enforced; port 143 and 587 are rejected |
username |
string | yes | — | IMAP login username (usually your email address) |
password |
string | yes | — | Use $ENV_VAR_NAME to read from environment variable |
display_name |
string | no | — | Human-readable label shown in list_accounts responses |
polling (optional)
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
interval_seconds |
number | no | 300 |
How often to poll for new mail (minimum: 60s) |
Setting environment variables for passwords:
export GMAIL_PASSWORD="your-app-password"
export WORK_PASSWORD="your-work-password"
In config.yaml, reference them as $GMAIL_PASSWORD and $WORK_PASSWORD.
Performance note: To verify performance on large mailboxes, connect to a mailbox with 10,000+ messages, run list_messages with default parameters, and confirm the response arrives within 5 seconds and contains only message headers (no bodies). The 200-result cap ensures response size is bounded.
Claude Desktop Setup
Add this entry to your claude_desktop_config.json (usually at ~/Library/Application Support/Claude/claude_desktop_config.json on macOS):
{
"mcpServers": {
"imap-mcp": {
"command": "node",
"args": ["/absolute/path/to/imap-mcp/build/index.js"],
"env": {
"IMAP_MCP_CONFIG": "/absolute/path/to/config.yaml",
"GMAIL_PASSWORD": "your-app-password"
}
}
}
}
Replace /absolute/path/to/imap-mcp with the directory where you cloned the repo. The IMAP_MCP_CONFIG key is optional — omit it to use the default ~/.config/imap-mcp/config.yaml. Any env vars referenced in your config file (e.g. $GMAIL_PASSWORD) must appear in the env block so Claude Desktop passes them to the server process.
Provider Compatibility
Gmail
Gmail requires an App Password — your regular Google account password will not work with IMAP.
- Enable IMAP in Gmail settings: Settings → See all settings → Forwarding and POP/IMAP → Enable IMAP
- Generate an App Password: myaccount.google.com/apppasswords
- Use the generated 16-character password as
$GMAIL_PASSWORDin your config
Host: imap.gmail.com, Port: 993
Generic IMAP
Any IMAP server that supports TLS on port 993 and Basic Auth should work. Check your provider's documentation for the correct hostname.
Outlook / Hotmail / Microsoft 365
Outlook/Hotmail/Microsoft 365: Microsoft deprecated Basic Auth for IMAP in October 2022. Modern Microsoft accounts require OAuth2, which this server does not currently support. If you are using an Outlook, Hotmail, or Microsoft 365 account, it will not work with this server.
Tool Reference
All tools are available via MCP. account parameters accept the name field from your config. Results are capped at 200 items for list_messages and search_messages.
list_accounts
Returns all configured accounts. No parameters.
Response: Array of { account, email, display_name? } objects.
list_folders
| Parameter | Type | Required | Description |
|---|---|---|---|
account |
string | no | Account name. Omit to list folders for all accounts. |
Response (single account): Array of { name, delimiter, flags, messages, unseen }.
Response (all accounts): Same array, sorted alphabetically by folder name.
list_messages
| Parameter | Type | Required | Description |
|---|---|---|---|
account |
string | no | Account name. Omit to query all accounts. |
folder |
string | no | Folder name (default: INBOX) |
limit |
number | no | Max messages to return (server cap: 200) |
offset |
number | no | Pagination offset |
sort |
newest|oldest |
no | Sort order (default: newest) |
unread_only |
boolean | no | Return only unread messages |
Response (single account): Flat array of MessageHeader[] — each item includes uid, from, to[], cc[], subject, date, flags, and folder.
Response (all accounts): { results: MultiAccountMessageHeader[], errors?: Record<string, string> }. The errors key is present only when one or more accounts failed.
read_message
| Parameter | Type | Required | Description |
|---|---|---|---|
account |
string | yes | Account name |
uid |
number | yes | Message UID |
format |
clean|full |
no | clean strips reply chains (default). full returns raw body. |
Response: Full message with headers, body, and attachments[] metadata. Inline text/calendar parts (calendar invites) are automatically surfaced as attachments with a default filename of invite.ics, even when they lack an explicit attachment disposition.
read_messages
Fetch multiple messages in a single call. All UIDs must belong to the same account and folder.
| Parameter | Type | Required | Description |
|---|---|---|---|
account |
string | yes | Account name |
uids |
number[] | yes | List of message UIDs to fetch (max 50) |
folder |
string | no | Folder containing the messages (default: INBOX) |
format |
clean|full|truncated |
no | Body format (default: clean) |
max_chars |
number | no | Max body characters when format is truncated (default: 2000) |
Response: Array of message objects in the same order as uids. Each item includes headers, body, attachments[] metadata, and a uid field.
search_messages
| Parameter | Type | Required | Description |
|---|---|---|---|
account |
string | no | Account name. Omit to search all accounts. |
from |
string | no | Filter by sender address or name |
subject |
string | no | Filter by subject text |
body |
string | no | Filter by body text content (case-insensitive, server-side IMAP SEARCH BODY) |
since |
string | no | ISO date string — messages on or after this date |
before |
string | no | ISO date string — messages before this date |
unread |
boolean | no | true = unread only, false = read only |
folder |
string | no | Folder to search (default: INBOX). Use all to search every folder sequentially. |
max_results |
number | no | Max results to return (server cap: 200) |
exclude_keywords |
string[] | no | Exclude messages that have any of these custom IMAP keywords set (e.g. ["ClaudeProcessed", "ClaudeReplied"]). First keyword filtered server-side; additional keywords filtered in memory. |
include_keywords |
string[] | no | Return only messages that have at least one of these custom IMAP keywords set (OR semantics). Single keyword uses server-side KEYWORD filter; multiple uses IMAP OR. |
Response (single account): Flat array of SearchResultItem[] — each item includes uid, from, to[], cc[], subject, date, unread, folder, and keywords[].
Response (all accounts): { results: MultiAccountSearchResultItem[], errors?: Record<string, string> }.
Note: folder='all' searches every folder sequentially and can be slow on large mailboxes.
download_attachment
| Parameter | Type | Required | Description |
|---|---|---|---|
account |
string | yes | Account name |
uid |
number | yes | Message UID |
part_id |
string | no | MIME part identifier (e.g. 2, 2.1). Faster when known. |
filename |
string | no | Attachment filename. Used to look up the part ID when part_id is not known. |
folder |
string | no | Folder containing the message (default: INBOX) |
At least one of part_id or filename must be provided. When both are given, part_id takes precedence. Filename matching is case-insensitive.
Response: { filename, mimeType, size, data } where data is base64-encoded.
Calendar invites: Inline text/calendar parts are surfaced as attachments (typically named invite.ics). Use download_attachment with the part_id from the message's attachments[] to retrieve the ICS data.
get_new_mail
| Parameter | Type | Required | Description |
|---|---|---|---|
since |
string | yes | ISO 8601 timestamp — return messages with internalDate after this time |
account |
string | no | Account name. Omit to query all accounts. |
exclude_keywords |
string[] | no | Exclude messages that have any of these custom IMAP keywords set (e.g. ["ClaudeProcessed"]). Filters cached results in memory. |
Response: { results: MultiAccountMessageHeader[], errors?: Record<string, string> }. The server polls in the background at the configured interval; this tool reads from the cache, not live IMAP. Each result includes uid, from, subject, date, unread, folder, account, and keywords[].
Example Agent Prompts
Once connected to Claude Desktop, you can ask things like:
- "Show me all unread emails from the last 24 hours across all my accounts"
- "Search for emails from GitHub about pull requests in my work account from this week"
- "Find any emails mentioning the invoice number INV-2024-042"
- "Read the most recent email from alice@example.com and summarize it"
- "Read all three emails in that thread and give me a summary"
- "Download the PDF attachment from that email"
- "Check if that email has a calendar invite and tell me the event details"
Troubleshooting
Connection refused / timeout
Check that port in your config is 993. This server enforces TLS — port 143 (plaintext) and port 587 (SMTP submission) will not work.
Authentication failed (Gmail)
Use an App Password, not your regular Google account password. Generate one at myaccount.google.com/apppasswords. Also verify IMAP is enabled in Gmail: Settings → See all settings → Forwarding and POP/IMAP → Enable IMAP.
Authentication failed (generic IMAP)
Verify the username and password are correct. Some providers require app-specific passwords even when not using Gmail — check your provider's security settings.
Outlook / Microsoft accounts not working
Microsoft deprecated Basic Auth for IMAP in October 2022. Microsoft 365, Outlook.com, and Hotmail accounts require OAuth2, which this server does not support. This is a Microsoft platform limitation that cannot be worked around with configuration changes.
Server crashes on startup
Check that the config file exists at the expected path (~/.config/imap-mcp/config.yaml by default, or the path in IMAP_MCP_CONFIG). Verify that all environment variables referenced in the config (e.g. $GMAIL_PASSWORD) are set before launching the server.
Performance: slow on large mailboxes
Using folder='all' in search_messages triggers a sequential search across every folder. On accounts with many folders or large mailboxes this can take several seconds. For faster results, specify a folder (e.g. folder='INBOX').
Contributing
Dev setup
npm install
npm run build
npm test
Guidelines
- TypeScript strict mode throughout — no
anytypes without explicit justification - All log output goes to stderr via
logger.ts— never useconsole.log(enforced by ESLint) - Pre-commit hooks run lint + tests automatically via Husky
- Install gitleaks for secret scanning (required for pre-commit hook):
brew install gitleaks
PR flow
Branch from main, open a PR, CI must pass before merging.
Recommended Servers
playwright-mcp
A Model Context Protocol server that enables LLMs to interact with web pages through structured accessibility snapshots without requiring vision models or screenshots.
Magic Component Platform (MCP)
An AI-powered tool that generates modern UI components from natural language descriptions, integrating with popular IDEs to streamline UI development workflow.
Audiense Insights MCP Server
Enables interaction with Audiense Insights accounts via the Model Context Protocol, facilitating the extraction and analysis of marketing insights and audience data including demographics, behavior, and influencer engagement.
VeyraX MCP
Single MCP tool to connect all your favorite tools: Gmail, Calendar and 40 more.
graphlit-mcp-server
The Model Context Protocol (MCP) Server enables integration between MCP clients and the Graphlit service. Ingest anything from Slack to Gmail to podcast feeds, in addition to web crawling, into a Graphlit project - and then retrieve relevant contents from the MCP client.
Kagi MCP Server
An MCP server that integrates Kagi search capabilities with Claude AI, enabling Claude to perform real-time web searches when answering questions that require up-to-date information.
E2B
Using MCP to run code via e2b.
Neon Database
MCP server for interacting with Neon Management API and databases
Exa Search
A Model Context Protocol (MCP) server lets AI assistants like Claude use the Exa AI Search API for web searches. This setup allows AI models to get real-time web information in a safe and controlled way.
Qdrant Server
This repository is an example of how to create a MCP server for Qdrant, a vector search engine.