Mail Shadow MCP

by dryas

338 downloads
Not rated
GitHub

About

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.

Details

Author
dryas
Downloads
338
Categories
Communication, Automation, Other, Productivity, AI

- Local SQLite shadow database – no direct IMAP access for agents
- Read-only mailbox – no STORE, APPEND, or EXPUNGE commands
- Incremental sync – fetches only new messages since last sync
- Full-text search via SQLite FTS5 index
- Multi-account support – sync any number of IMAP accounts
- Optional IMAP IDLE for near‑instant new‑mail notifications
- Soft‑delete (IMAP MOVE to configurable trash folder)
- Flexible transport: stdio, HTTP (StreamableHTTP), or SSE

Setting up with Highlight

This MCP is not yet compatible with Highlight’s one-click setup. However, you can still use it with Highlight by following these steps:

  1. Download and install Highlight from highlightai.com/download
  2. Navigate to the plugins tab and select "Add Custom Plugin"
  3. Configure the plugin with the settings below
    Plugin Name Mail Shadow MCP
    Command (node, npx, python, etc.)

    Please refer to the README for specific instructions on how to obtain API keys or other required environment variables.

  4. Enable "Start Automatically" if you want the plugin to start when Highlight launches

From the repository

Configure a config.yaml file with your IMAP account credentials, then run ./mail-shadow-mcp serve (or docker run for containerized deployments). Integrate with MCP clients (Claude Desktop, Cursor, etc.) by pointing them to the server via stdio or StreamableHTTP transport. Use the provided MCP tools like search_emails, get_thread, and download_attachments to interact with your mail.

Claude Desktop / Cursor

Paste into your MCP client config file to install this server.

{
    "mcpServers": {
        "mail shadow mcp": {
            "mail_shadow": {
                "command": "/path/to/mail-shadow-mcp",
                "args": [
                    "serve",
                    "--config",
                    "/path/to/config.yaml"
                ]
            }
        }
    }
}

McpServers

{
    "mail_shadow": {
        "command": "/path/to/mail-shadow-mcp",
        "args": [
            "serve",
            "--config",
            "/path/to/config.yaml"
        ]
    }
}

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.

Your AI Agent's Private, Secure, and Intelligent Inbox

Stop giving your AI direct access to your email. Give it a safe, lightning-fast and feature rich "shadow" copy instead.

Imagine having a personal assistant who has read all your emails, knows exactly what's important, and can answer your questions in seconds — without ever risking your actual mailbox through a misbehaving or hallucinating AI.

Withmail-shadow-mcp, you can ask your AI (like OpenClaw, Hermes Agent, Claude, Cursor or any other custom agent):

- "Did I receive any invoices from Amazon in the last 3 days?"
- "Summarize the last email thread from my boss about the project status."
- "Please summarize all unread emails in my 'Project' folder."
- "Check if there are any flight confirmation emails in my inbox for next week."
- "Find all emails from 'newsletter@example.com' that have attachments."
- "Is there anything in my inbox that looks like spam or junk?"

Most AI agents require direct access to your email (IMAP) to "see" your messages. This is risky — because once an agent has live IMAP credentials, it has the same permissions as you: it can read, move, delete, or even send emails. A single hallucination, a misunderstood instruction, or a bug could lead to an AI accidentally deleting your entire inbox, sending a reply you never intended, or exposing your credentials to a third party.

mail-shadow-mcpsolves this by creating a "Safe Zone":
- The Shadow Copy:Instead of connecting to your real email server, we create a local, high-speed "shadow" database (SQLite) of your emails. This also unlocks capabilities that raw IMAP simply cannot offer: instant full-text search across all folders and accounts at once, complex filtering by read/replied status, attachments, date ranges, and sender — all without any round-trips to your mail server. And it works just as well with multiple mailboxes simultaneously — just add more accounts to the config.
- Total Privacy:Your AI agentonlyever talks to this local database. Your IMAP credentials are used exclusively by the sync engine — they are never exposed through any MCP tool call or returned to the agent in any response.
- The "Safety Net" (Soft-Delete):Even if you ask the AI to "delete" an email, it doesn't actually delete it. It simply moves it to a "Trash" folder you've designated. If something goes wrong, you can always review the folder, restore individual emails, or permanently delete them yourself — you remain in total control.

[Remote IMAP Server] ──IMAP──▶ [Sync Engine] ──▶ [SQLite FTS5] ◀──▶ [MCP Server] ◀──▶ [AI Agent]

The recommended way to run mail-shadow-mcp is viaDocker. Running it in a container keeps the sync engine, credentials, and database fully isolated from your AI agent, which connects over HTTP. The agent never has access to the host filesystem or your IMAP password — only to the MCP API.

If you prefer to run it locally without Docker, you can download a pre-compiled binary from theReleases pageand usestdiotransport instead. However, this means the agent process and mail-shadow-mcp share the same user context, which reduces the isolation benefits described above.

Step 1 — Start the container to generate the example config

Create local directories for config and data, then do a first run to generate the example config:

mkdir -p ./config ./data docker run --rm \ -v ./config:/config \ -v ./data:/data \ ghcr.io/dryas/mail-shadow-mcp:latest

The container will detect that noconfig.yamlexists, copy an annotated example config into./config/, print a message, and exit.

Open./config/config.yaml(or wherever your/configvolume is mounted) and fill in your IMAP details:

sync_interval_min: 15 database: path: "/data/mail.db" attachment_dir: "/data/attachments" transport: http http_addr: ":8080" http_bearer_token: "your-secret-token" # generate one: openssl rand -hex 32 accounts: - id: "work@example.com" host: "imap.example.com" port: 993 username: "work@example.com" password: "$WORK_IMAP_PASS" # resolved from environment variable at startup tls_mode: tls # tls (default) | starttls | none tls_skip_verify: false # set true for self-signed certificates folders: ["INBOX", "Archive"] # omit to sync all folders idle_folders: ["INBOX"] # optional: instant new-mail push via IMAP IDLE trash_folder: "llm_delete" # target folder for soft-deletes via delete_mail

Passwords as environment variables:Instead of writing your IMAP password directly into the config file, use a$VARIABLE_NAMEplaceholder — mail-shadow-mcp will resolve it from the container's environment at startup. In the example above,password: "$WORK_IMAP_PASS"means the container reads the value from theWORK_IMAP_PASSenvironment variable, which you pass via-e WORK_IMAP_PASS=your_password_herewhen starting it (see Step 1 or 3). This way no plaintext password ends up in the config file.

folders:The list of IMAP folders to sync. If omitted, all folders are synced. Restricting to the folders you actually care about (e.g.["INBOX", "Archive"]) keeps the database smaller and initial sync faster.

idle_folders:Optional list of folders for which mail-shadow-mcp opens a persistent IMAP IDLE connection. When the mail server pushes an "EXISTS" notification, a sync is triggered immediately instead of waiting for the next poll interval so you will get informed about new mails in seconds. Keep this list short — each entry holds one open IMAP connection for the lifetime of the container. Best practice is to only add"INBOX"here, or leave it out entirely and rely on regular polling.

trash_folder:The IMAP folder that mail-shadow-mcp moves emails to when the AI agent calls thedelete_mailtool. The folder must already exist on your mail server. If this is not set,delete_mailwill return an error and do nothing — a safe default. Emails in the trash folder are automatically excluded from all MCP query results (search, recent activity, threads), so the agent can never see them again — regardless of whether the folder is included in the sync configuration. Note: if you ever changetrash_folderto a different folder name, the old trash folder will no longer be excluded and its contents will become visible to the agent again on the next sync. Make sure to manually empty the old folder before switching.

http_bearer_token:A secret token that protects the MCP HTTP endpoint. Every request from the AI agent must include it asAuthorization: Bearer <token>. Without this, anyone who can reach the port can talk to your MCP server — so always set this when running withhttptransport. Generate a secure random token with:

# Linux / macOS / WSL openssl rand -hex 32
# Windows PowerShell [System.Convert]::ToBase64String((1..32 | ForEach-Object { byte }))

Copy the output into the config and pass the same value to your AI agent (see Step 4).

docker run -d \ --name mail-shadow-mcp \ --restart unless-stopped \ -v ./config:/config:ro \ -v ./data:/data \ -e WORK_IMAP_PASS=your_password_here \ -p 8080:8080 \ ghcr.io/dryas/mail-shadow-mcp:latest

The MCP server is now reachable athttp://localhost:8080/mcp.

Pre-built multi-architecture images (linux/amd64,linux/arm64) are published to the GitHub Container Registry on every release:

docker pull ghcr.io/dryas/mail-shadow-mcp:latest

Since we're running with Docker, the MCP server is reachable via HTTP — and that works with Claude Desktop too, not just remote agents. Add the following to your agent's config:

Claude Desktop(claude_desktop_config.json):

{ "mcpServers": { "mail_shadow": { "url": "http://localhost:8080/mcp", "headers": { "Authorization": "Bearer your-secret-token" } } } }

Replacelocalhostwith your server's IP or hostname if mail-shadow-mcp runs on a different machine.

mail-shadow: url: http://localhost:8080/mcp headers: Authorization: Bearer your-secret-token
{ "mcp": { "servers": { "mail_shadow": { "url": "http://localhost:8080/mcp", "transport": "streamable-http", "headers": { "Authorization": "Bearer your-secret-token" } } } } }

Alternative: local stdio (pre-compiled binary, no Docker)

If you chose to run the binary directly instead of Docker, use thecommandform:

{ "mcpServers": { "mail_shadow": { "command": "/path/to/mail-shadow-mcp", "args": ["serve", "--config", "/path/to/config.yaml"] } } }

That's it — your AI can now search and read your emails safely.

mail-shadow-mcp gives AI agents adelete_mailtool, but this toolnever issues a destructive IMAP command. Here is exactly what happens when an agent calls it:
- The MCP server looks up the email in the local database.
- It opens a short-lived IMAP connection and executesIMAP MOVE— moving the message to thetrash_folderyou specify inconfig.yaml(e.g."llm_delete").
- The local database entry is removed, and the trash folder is permanently excluded from all MCP query results — the agent can never see the moved email again, regardless of whether the folder is synced.
- The email remainsintact on the IMAP server, safely tucked away in the trash folder. You can inspect, restore, or permanently delete it yourself at any time.

The AI agent has no direct IMAP access. It cannot expunge messages, empty folders, or issue any write command other than this controlled move. Iftrash_folderis not configured for an account,delete_mailreturns an error and does nothing.

- Local shadow database— emails are synced into a local SQLite database; the AI agent never connects to your IMAP server directly
- Read-only sync— the sync engine only issues read commands (SELECT,UID FETCH); noSTORE,APPEND, orEXPUNGEis ever sent to your mail server
- 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 statusis_readandis_repliedflags synced from IMAP and exposed as filters
- Thread viewget_threadwalks full email conversations viaMessage-ID/In-Reply-Toheaders
- Paginated results— all list tools returntotal_countso agents can page through large result sets
- On-demand attachments— attachment files are fetched from IMAP only when explicitly requested
- Flexible transportstdiofor local tools (Claude Desktop),http(StreamableHTTP) orssefor remote and Docker deployments
- Docker-ready— official multi-arch image (linux/amd64,linux/arm64) published toghcr.ioon every release

sync_interval_min: 15 database: path: "data/mail.db" # path to the local SQLite shadow database attachment_dir: "data/attachments" # base directory for downloaded attachments # Optional: log file and level. Omit log_file to write to stderr (default). # log_file: "logs/mail-shadow-mcp.log" # append mode; directory is created automatically # log_level: info # debug | info (default) | warn | error # log_format: text # text (default) | json # MCP transport mode. # stdio (default) — stdin/stdout, used by Claude Desktop and most local tools. # http — StreamableHTTP, recommended for Docker and remote deployments. # sse — legacy SSE transport (prefer http unless your client requires SSE). # transport: stdio # http_addr: ":8080" # bind address for http/sse (default: :8080) # http_base_url: "http://localhost:8080" # sse only: externally reachable base URL # http_bearer_token: "" # recommended: set a secret token to protect the HTTP endpoint # generate one with: openssl rand -hex 32 # Optional: lightweight HTTP server for temporary attachment download links. # 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; prefix with $ to read from env var tls_mode: tls # tls (default, implicit TLS, port 993) # starttls (STARTTLS upgrade, port 143) # none (no encryption — localhost/testing only) tls_skip_verify: false # set true for self-signed certificates folders: ["INBOX", "Archive"] # optional: omit to sync all folders # idle_folders: ["INBOX"] # optional: folders watched via IMAP IDLE for instant new-mail notification # trash_folder: "llm_delete" # optional: target folder for delete_mail (soft-delete via IMAP MOVE)
services: mail-shadow-mcp: image: ghcr.io/dryas/mail-shadow-mcp:latest restart: unless-stopped ports: - "8080:8080" volumes: - ./config/config.yaml:/config/config.yaml:ro # your config — mount read-only - ./data:/data # persistent DB + attachments environment: - WORK_IMAP_PASS=your_password_here # referenced as $WORK_IMAP_PASS in config

Passwords as environment variables:Inconfig.yamlyou can reference passwords as$ENV_VAR— the server resolves them at startup. Pass them viaenvironment:in docker-compose or via-ewithdocker run. This way no plaintext password ends up in the config file.

Settls_skip_verify: trueto accept self-signed certificates.

When usinghttporssetransport,always sethttp_bearer_token— otherwise the MCP endpoint is reachable by anyone who can access the port.

Generate a cryptographically secure token:

# Linux / macOS / WSL openssl rand -hex 32 # PowerShell [System.Convert]::ToBase64String((1..32 | ForEach-Object { byte }))

By default, mail-shadow-mcp polls for new messages everysync_interval_minminutes. 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 inidle_folders
- When the server sends anEXISTSnotification, 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

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 channels.

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
make build # current platform make release # cross-compile for all platforms into dist/

Beyond running as an MCP server,mail-shadow-mcpexposes a few CLI commands that are useful for manual operations, scripting, or debugging — without needing an AI agent at all.

Trigger a one-shot sync(fetches new emails into the local database and exits):

Query the local database(output is newline-delimited JSON, suitable forjqpipelines):

# Search by subject and body keyword ./mail-shadow-mcp query --subject "invoice" --body "Q1" # Full-text search with attachment filter ./mail-shadow-mcp query -q "budget" --attachments only # Most recent emails, paginated ./mail-shadow-mcp query --recent --limit 10 --offset 10

Download attachmentsfor a specific email by its ID (formataccount:folder:uid):

./mail-shadow-mcp attachments --id "work@example.com:INBOX:42"

Apache 2.0 — seeLICENSEfor details.
Copyright (c) 2026 Benjamin Kaiser.

Query live Gmail data using LLMs via CData's read-only MCP server.

Search and delete emails in your Gmail account.

Enables AI assistants to manage Gmail through natural language interactions.

A standardized interface for managing, sending, and retrieving emails through the Gmail API.

Manage your Gmail account, including sending, reading, and organizing emails.

A super simple and tiny MCP server for gmail in python

Allows AI agents to search Gmail threads, learn your writing style, and draft emails.

An MCP server that enables AI models to interact directly with the Gmail API to manage emails.

An MCP server for integrating with the Gmail API to manage emails.

Most email MCP servers only read from IMAP. mail-mcp does everything: 30 tools for reading, searching, sending, replying, forwarding, and bulk operations across IMAP, SMTP, Microsoft Graph API, and Exchange Web Services. Multi-account, native OAuth2, built in Rust. Works with Gmail, Microsoft 365, Hotmail/Outlook.com, Zoho, and any standard IMAP/SMTP server.

No reviews yet — be the first

Sign in to leave a review

Use Google, GitHub, or an email account so ratings stay tied to real people.

Email sign in

No reviews posted yet.