Mail Shadow MCP
MCP server for structured, read-only email access. Exposes a minimal, auditable API surface β AI agents can search and read emails, but cannot send, delete, or modify your mailbox.
Ask AI about Mail Shadow MCP
Powered by Claude Β· Grounded in docs
I know everything about Mail Shadow MCP. Ask me about installation, configuration, usage, or troubleshooting.
0/500
Reviews
Documentation
Structured, read-only email access for AI agents.
mail-shadow-mcp is a Model Context Protocol (MCP) server that creates a local shadow copy of your IMAP mailboxes in a SQLite database. AI agents query the local database through well-defined MCP tools instead of connecting directly to your IMAP server.
[Remote IMAP Server] ββIMAPβββΆ [Sync Engine] βββΆ [SQLite FTS5] ββββΆ [MCP Server] ββββΆ [AI Agent]
Features
- Local shadow database β emails are synced into a local SQLite database; the AI agent never connects to your IMAP server directly
- Read-only β no
STORE,APPEND, orEXPUNGEcommands; your mailbox is never modified - Incremental sync β only fetches messages newer than the last known UID
- Full-text search β SQLite FTS5 index for fast body-text queries
- Multi-account β sync any number of IMAP accounts simultaneously
- IMAP IDLE β optional real-time push notifications; new mail detected within seconds instead of waiting for the next poll interval
- Read/replied status β
is_readandis_repliedflags synced from IMAP and exposed as filters - Thread view β
get_threadwalks full email conversations viaMessage-ID/In-Reply-Toheaders - Paginated results β all list tools return
total_countso agents can page through large result sets - On-demand attachments β attachment files are fetched from IMAP only when explicitly requested
MCP Tools
| Tool | Description |
|---|---|
list_accounts_and_folders | List all synced accounts and their folders |
get_recent_activity | N most recent emails with optional filters (is_read, has_attachments, pagination) |
get_email_content | Full body text, read/replied status, and attachment list for a single email |
search_emails | FTS5 full-text search with subject/sender/date/folder/is_read/sent_by filters |
get_thread | All emails in the same thread as a given email, sorted by date ascending |
download_attachments | Fetch attachment files from IMAP and save them to disk |
get_download_link | Generate a temporary HTTP download URL for attachments (optional fallback) |
Quick Start
1. Build
make build # current platform
make release # cross-compile for all platforms into dist/
Requires Go 1.25+.
2. Configure
Copy the example config and fill in your IMAP credentials:
cp config.example.yaml config.yaml
sync_interval_min: 15
database:
path: "data/mail.db"
attachment_dir: "data/attachments"
# Optional: structured JSON logs for Loki / Elasticsearch pipelines.
# log_format: json # json | text (default: text)
# log_level: info # debug | info | warn | error (default: info)
# log_file: "/var/log/mail-shadow-mcp.log" # omit to use stderr
# Optional: lightweight HTTP server for temporary attachment download links.
# Only enable this if you need the get_download_link MCP tool (e.g. as a
# fallback when the AI agent cannot transfer files via its normal channels).
fileserver_port: 8787 # TCP port to listen on (disabled if omitted)
fileserver_ttl_min: 15 # minutes before a link expires (default: 15)
fileserver_host: "localhost" # hostname/IP shown in generated URLs
accounts:
- id: "work@example.com"
host: "imap.example.com"
port: 993
username: "work@example.com"
password: "$WORK_IMAP_PASS" # or plain text
folders: ["INBOX", "Archive"] # only sync the mentioned folders
# idle_folders: ["INBOX"] # optional: IMAP IDLE for real-time push on these folders
- id: "private@example.com"
host: "imap.example.com"
port: 993
username: "private@example.com"
password: "$PRIVATE_IMAP_PASS" # or plain text
Credentials can be stored as plain text or as $ENV_VAR references that are resolved at runtime.
3. Run
# Start the MCP server (syncs on startup, then every sync_interval_min minutes)
./mail-shadow-mcp serve
# One-shot sync without starting the server
./mail-shadow-mcp sync
# Query from the command line (output is JSON)
./mail-shadow-mcp query --subject "invoice" --body "Q1"
./mail-shadow-mcp query -q "budget" --attachments only # only emails with attachments
./mail-shadow-mcp query --recent --attachments none # recent emails without attachments
./mail-shadow-mcp query --recent --limit 10 --offset 10 # page 2
# Download attachments for a specific email
./mail-shadow-mcp attachments --id "work@example.com:INBOX:42"
Integrating with an AI Agent
Configure your MCP client to launch the server via stdio.
Example for Claude Desktop (claude_desktop_config.json):
{
"mcpServers": {
"mail_shadow": {
"command": "/path/to/mail-shadow-mcp",
"args": ["serve", "--config", "/path/to/config.yaml"]
}
}
}
Example for Hermes Agent (config.yaml):
mcp_servers:
mail_shadow:
command: "/path/to/mail-shadow-mcp"
args: ["serve", "--config", "/path/to/config.yaml"]
Example for OpenClaw (~/.openclaw/openclaw.json):
{
"mcpServers": {
"mail_shadow": {
"command": "/path/to/mail-shadow-mcp",
"args": ["serve", "--config", "/path/to/config.yaml"],
"transport": "stdio"
}
}
}
TLS Modes
tls_mode | Port | Description |
|---|---|---|
tls | 993 | Implicit TLS (default) |
starttls | 143 | STARTTLS upgrade |
none | 143 | No encryption β localhost/testing only |
Set tls_skip_verify: true to accept self-signed certificates.
IMAP IDLE (Real-time Push)
By default, mail-shadow-mcp polls for new messages every sync_interval_min minutes. For folders where you want near-instant notifications, enable IMAP IDLE:
accounts:
- id: "work@example.com"
# ...
idle_folders: ["INBOX"] # IDLE runs on top of regular polling
- One dedicated IMAP connection is opened per entry in
idle_folders - When the server sends an
EXISTSnotification, a sync is triggered immediately - Regular polling continues unchanged for all other folders
- Falls back to polling automatically if the server does not support IDLE
- Exponential backoff (30 s β 5 min) on persistent connection errors
Attachment Download Server
The optional built-in HTTP server lets the AI agent generate temporary, single-use download links for attachment files β useful as a fallback when the agent cannot transfer files through its normal communication channels (e.g. WhatsApp, email).
Enable it in config.yaml:
fileserver_port: 8787 # TCP port to listen on
fileserver_ttl_min: 15 # minutes before a link expires (default: 15)
fileserver_host: "localhost" # hostname/IP shown in generated URLs
When enabled, the get_download_link MCP tool becomes available. It downloads the attachments, saves them to attachment_dir, and returns one temporary URL per file:
[
{
"file": "data/attachments/work@example.com/INBOX/42/invoice.pdf",
"url": "http://localhost:8787/dl/3f8a1c.../invoice.pdf"
}
]
Each link is single-use and expires after fileserver_ttl_min minutes. The tool description instructs the AI agent to prefer direct file transfer and only fall back to this mechanism when necessary.
License
Apache 2.0 β see LICENSE for details.
Copyright (c) 2026 Benjamin Kaiser.
