Command Palette

Search for a command to run...

UnylyUnyly
Browse all

Postfach

FreeNot checked

Read-focused remote MCP server for IMAP mailboxes: search, read, create drafts - no send, no delete

GitHubEmbed

About

Read-focused remote MCP server for IMAP mailboxes: search, read, create drafts - no send, no delete

README

A self-hosted remote MCP server that gives an AI assistant read-focused access to any IMAP mailbox: search, read, create drafts, light triage. Postfach is German for mailbox.

Deliberately no send, no delete — by design, not by configuration. E-mail is untrusted third-party input; an assistant that reads it can be manipulated by it. This server keeps the blast radius small: the worst a hijacked session can do is file a draft, create a folder or move a message — all of it sits in your mailbox, in plain sight, reversible. A send tool may appear in a later version, but only behind an explicit opt-in flag, unregistered by default. Feature requests to weaken this stance will be declined.

Status

v0.2.0 — in production use by the author against a real IMAP mailbox, with three client paths verified: Claude Code, claude.ai on the web, and the Claude Android app. Still a 0.x: the tool surface and configuration may change between releases. Changes are tracked in CHANGELOG.md.

Tools

Tool Purpose
list_folders List all folders, optionally with message and unseen counts
folder_status Message and unseen counts for a folder
list_messages Newest messages in a folder
list_headers Page through the header data of a whole folder, oldest first
search_messages Server-side IMAP search, including arbitrary header matches
get_message Full message: headers, body, attachment metadata
create_draft Build an RFC-822 message and file it in the drafts folder — never sends
create_folder Create a folder — creating only, no deleting, no renaming
mark_read Set or clear the seen flag
mark_flagged Set or clear the flagged star
move_messages Move messages to another folder, reporting the new uids when the server supports UIDPLUS

Messages are addressed by folder + uid; UIDs are per-folder. Reading never sets the seen flag — only mark_read does, when asked.

Installation

Requires Python 3.11+.

pip install git+https://github.com/unstko/postfach-mcp

Or from a clone: pip install . — both install the postfach-mcp command.

Configuration

Everything is environment variables prefixed POSTFACH_MCP_; a commented template is in .env.example. Missing or invalid variables are reported together, each by name.

Variable Default Purpose
IMAP_HOST (required) IMAP server to connect to
IMAP_USER (required) Login name
IMAP_PASSWORD (required) Password — use an app password if your provider offers them
IMAP_PORT 993 IMAP over TLS port
DRAFTS_FOLDER Drafts Folder that receives created drafts; postfach-mcp check verifies it exists
FROM_ADDRESS IMAP_USER From header for drafts, e.g. Your Name <[email protected]>
FROM_ADDRESSES Comma-separated additional sender identities create_draft may select via its from_address argument; anything not listed here or in FROM_ADDRESS is rejected
DRAFT_FORMAT text text writes plain-text drafts; html adds an HTML rendering of the same text as a multipart/alternative part — for clients whose HTML-based composer collapses plain-text line breaks (Spark, for example)
TOKEN (required for serve) Bearer token, at least 32 characters (openssl rand -hex 32)
EXTRA_TOKENS Comma-separated additional bearer tokens, same length rule — give each client its own so one can be revoked without touching the others
HOST 127.0.0.1 Bind address of the HTTP server
PORT 8000 Port of the HTTP server
ALLOWED_HOSTS 127.0.0.1,localhost Comma-separated Host header allowlist — add the public name your proxy or tunnel uses
ENABLE_SEND Reserved for a future explicit opt-in; not implemented in v0.1

Running

postfach-mcp check   # probe the IMAP login, list folders, verify the drafts folder
postfach-mcp serve   # run the HTTP server (--host/--port override the environment)

check is the deploy diagnosis: it answers "are the credentials right, and what is the drafts folder actually called on this server?" without starting anything. serve exposes the MCP endpoint at /mcp and an unauthenticated health probe at /api/health.

Connecting clients

Any Streamable-HTTP MCP client that can send an Authorization header works. The two most common ones:

Claude Code

claude mcp add --transport http postfach https://mail.example.org/mcp \
  -H "Authorization: Bearer <token>" --scope user

claude.ai custom connector (web and mobile apps)

claude.ai can talk to this server as a custom connector, which also makes it available in the Claude mobile apps — the connector is configured once and appears there automatically.

Custom connectors normally require OAuth, which this server does not offer. What it relies on instead is the request-header option in the add-connector dialog (authentication "None" plus a static header). At the time of writing Anthropic describes that option as available to a limited set of organizations, so it may not appear for your account — check the dialog before planning around it.

Configuration, and the three pitfalls that cost the author an afternoon:

  • URL: your public https://…/mcp endpoint. Authentication: None. Add a request header Authorization with the value Bearer <token>including the scheme and the space; the value is sent verbatim, so a bare token produces nothing but 401s.
  • A connector's authentication settings cannot be changed later. To rotate a token, delete the connector and create it again. This pairs well with EXTRA_TOKENS: give the connector its own token and it can be revoked without touching your other clients.
  • The connection test in the dialog can fail even when everything is configured correctly (some of its probes are sent without the header). If the server logs show 401s from the dialog but your header is right, deleting and re-creating the connector is faster than debugging.

One consequence for your network setup: connector traffic originates from Anthropic's backend, not from your browser — the server must be reachable from the public internet, a VPN or tailnet is not enough for this path. To keep the exposed surface small you can restrict it to Anthropic's published egress IP range at your proxy or firewall; the bearer token remains the actual authentication either way.

Security model

  • No send, no delete. The server cannot transmit mail or destroy it; those tools do not exist at runtime. Drafts are filed via IMAP APPEND into your drafts folder and stay there until you act on them.
  • Mail content is untrusted. Bodies and headers are returned in structured fields, never interpreted; tool descriptions warn the model that message content is third-party input. Header fields of drafts are validated against CRLF injection.
  • Bearer token on every MCP request (constant-time comparison), minimum 32 characters. The health endpoint is the only unauthenticated route.
  • Host header allowlist (ALLOWED_HOSTS) rejects requests addressed under any other name — DNS-rebinding protection. Behind a proxy or tunnel you must add the public host name, or every request fails with 421.
  • Transport security is your job. The server speaks plain HTTP and binds to localhost by default; put a TLS-terminating reverse proxy, tunnel, or VPN in front of it. Do not expose the port directly.
  • Errors are terse. IMAP failures reach the client as one English sentence; credentials and tracebacks never do.

Limitations

  • One account per server instance.
  • Drafts carry no formatting beyond line and paragraph breaks, and IMAP cannot edit them in place — a changed draft means a new one.
  • Some clients render plain-text drafts through an HTML composer and lose all line breaks (observed in Spark on macOS and Android; webmail shows the same draft correctly). DRAFT_FORMAT=html works around this by adding an HTML alternative part.
  • Attachments are reported as metadata only (name, type, size); their content is not retrievable.
  • Message bodies are capped at 50,000 characters, list/search results at 100 messages per call, list_headers at 500 headers per page; drafts at 500,000 characters.
  • Lossless export of raw messages is out of scope — for backups, use a dedicated tool such as mbsync or offlineimap.
  • HTML-only messages are converted to text with a deliberately simple converter — layout is lost, links are kept visible.
  • mark_read, mark_flagged and move_messages trigger an expunge in the source folder (imap-tools behavior; a move is IMAP-internally copy + delete + expunge). Harmless for this server, which never sets the deleted flag itself, but it also purges messages other clients have marked deleted in that folder.

Development

python -m venv .venv && .venv/bin/pip install -e ".[dev]"
.venv/bin/python -m pytest
.venv/bin/python -m ruff check . && .venv/bin/python -m mypy

Tests run entirely without network access — enforced by the test suite itself, which fails any accidental socket connect.

License

MIT


Built with assistance from Claude Code.

from github.com/unstko/postfach-mcp

Installing Postfach

This server has no published package — it is built from source. Open the repository and follow its README.

▸ github.com/unstko/postfach-mcp

FAQ

Is Postfach MCP free?

Yes, Postfach MCP is free — one-click install via Unyly at no cost.

Does Postfach need an API key?

No, Postfach runs without API keys or environment variables.

Is Postfach hosted or self-hosted?

Self-hosted: the server runs locally on your machine via the install command above.

How do I install Postfach in Claude Desktop, Claude Code or Cursor?

Open Postfach on unyly.org, pick your client tab (Claude Desktop, Claude Code, Cursor) and press Install — the config is generated automatically, no JSON editing.

Related MCPs

Compare Postfach with

Not sure what to pick?

Find your stack in 60 seconds

Author?

Embed badge for your README

Browse similar

All communication MCPs