ProtonMail MCP Server
<div align="center">
# ProtonMail MCP Server
**Email management for AI agents through ProtonMail and Proton Bridge**
[](https://github.com/ronamosa/protonmail-pro-mcp/actions/workflows/ci.yml)
[](https://www.npmjs.com/package/@ronamosa/protonmail-pro-mcp)
[](https://modelcontextprotocol.io)
[](https://www.typescriptlang.org)
[](https://opensource.org/licenses/MIT)
[](https://nodejs.org)
Send, read, search, and organize emails from Claude Code, Claude Desktop, Cursor, or any MCP-compatible client.

</div>
---
## Quick start
### Install from npm (recommended)
```bash
npx @ronamosa/protonmail-pro-mcp
```
Or install globally:
```bash
npm install -g @ronamosa/protonmail-pro-mcp
protonmail-pro-mcp
```
### Install from source
```bash
git clone https://github.com/ronamosa/protonmail-pro-mcp.git
cd protonmail-pro-mcp
npm install
npm link
```
Verify the install:
```bash
which protonmail-pro-mcp
```
> **Prerequisites** -- [Node.js](https://nodejs.org) >= 18 and [Proton Bridge](https://proton.me/mail/bridge) running locally.
## Configuration
```bash
cp .env.example .env # then fill in your credentials
```
| Variable | Required | Default | Description |
|:---------|:--------:|:-------:|:------------|
| `PROTONMAIL_USERNAME` | Yes | -- | Your ProtonMail email address |
| `PROTONMAIL_PASSWORD` | Yes | -- | Proton Bridge password (not your login password) |
| `PROTONMAIL_SMTP_HOST` | | `smtp.protonmail.ch` | SMTP server host |
| `PROTONMAIL_SMTP_PORT` | | `587` | SMTP server port |
| `PROTONMAIL_IMAP_HOST` | | `127.0.0.1` | IMAP host (Proton Bridge) |
| `PROTONMAIL_IMAP_PORT` | | `1143` | IMAP port (Proton Bridge) |
| `PROTONMAIL_IMAP_TLS` | | `false` | Enable TLS for IMAP |
| `PORT` | | `3000` | HTTP transport port |
| `DEBUG` | | `false` | Enable debug logging |
> **Security** -- `PROTONMAIL_PASSWORD` is the bridge-generated password, not your ProtonMail login. Never commit `.env` files.
### Local development (Cursor)
To test this repo's built output instead of the published npm package:
```bash
npm run build
cp .cursor/mcp.json.example .cursor/mcp.json # add your Bridge credentials
```
Restart Cursor MCP (Settings → MCP → reload). The local server runs `node dist/index.js` from this workspace.
### Manual attachment test (Bridge required)
Sends a self-addressed email with two attachments named `dupe.txt`, then verifies ambiguous lookup and index-based retrieval:
```bash
cp .env.example .env # if you have not already
npm run build
npm run test:attachments:manual
```
The script leaves the test email in INBOX so you can also exercise `get_email_by_id` and `get_attachment` from Cursor.
## Usage
<details>
<summary><strong>Claude Code</strong></summary>
Add to `~/.claude.json` under `mcpServers`, or run `claude mcp add`:
```json
{
"mcpServers": {
"protonmail": {
"type": "stdio",
"command": "npx",
"args": ["@ronamosa/protonmail-pro-mcp"],
"env": {
"PROTONMAIL_USERNAME": "you@protonmail.com",
"PROTONMAIL_PASSWORD": "your-bridge-password"
}
}
}
}
```
</details>
<details>
<summary><strong>Claude Desktop</strong></summary>
Add to `~/.config/claude/claude_desktop_config.json`:
```json
{
"mcpServers": {
"protonmail": {
"command": "npx",
"args": ["@ronamosa/protonmail-pro-mcp"],
"env": {
"PROTONMAIL_USERNAME": "you@protonmail.com",
"PROTONMAIL_PASSWORD": "your-bridge-password"
}
}
}
}
```
</details>
<details>
<summary><strong>Cursor</strong></summary>
Add to `.cursor/mcp.json` in your project:
```json
{
"mcpServers": {
"protonmail": {
"command": "npx",
"args": ["@ronamosa/protonmail-pro-mcp"],
"env": {
"PROTONMAIL_USERNAME": "you@protonmail.com",
"PROTONMAIL_PASSWORD": "your-bridge-password"
}
}
}
}
```
</details>
<details>
<summary><strong>HTTP transport (remote deployment)</strong></summary>
```bash
protonmail-pro-mcp --transport http --port 3000
```
Endpoints: `POST /mcp`, `GET /mcp`, `DELETE /mcp` (Streamable HTTP). Health check at `GET /health`.
</details>
## Tools
| | Tool | Description |
|:--|:-----|:------------|
| **Send** | `send_email` | Send with to/cc/bcc, HTML, priority, reply-to, threading, attachments |
| | `send_test_email` | Quick test email to verify SMTP |
| **Read** | `get_emails` | Fetch from a folder with pagination |
| | `get_email_by_id` | Full email with body, headers, and attachment metadata (includes `index`) |
| | `get_attachment` | Download an attachment by `emailId` and `filename`; pass `index` when filenames duplicate |
| | `search_emails` | Filter by from, to, subject, date, flags, attachments |
| **Drafts** | `create_draft` | Create a new draft in the Drafts folder |
| | `create_reply_draft` | Draft a threaded reply — derives recipients, `Re:` subject and threading headers from the parent |
| | `update_draft` | Replace an existing draft with new content |
| | `delete_draft` | Delete a draft |
| | `send_draft` | Send a draft via SMTP and remove it from Drafts |
| **Act** | `mark_email_read` | Mark read or unread |
| | `star_email` | Star or unstar |
| | `move_email` | Move between folders |
| | `delete_email` | Soft-delete to Trash; permanent only if already in Trash |
| **Folders** | `get_folders` | List all folders with message counts |
| | `sync_folders` | Force-refresh folder list |
| **System** | `get_connection_status` | SMTP and IMAP connection health |
### Threading
Replies thread when the message carries `In-Reply-To` and `References` (RFC 5322 §3.6.4).
Subject-line matching alone is not threading — Gmail happens to group on it, most clients do not.
`create_reply_draft` is the tool to reach for: give it the parent's `emailId` and a body, and it
derives the recipients, the `Re:` subject and both headers itself. Assembling the chain by hand is
easy to get wrong, and a wrong chain threads one level deep and then breaks.
```
create_reply_draft({ emailId: "INBOX:4821", body: "Sounds good." })
```
`create_draft`, `update_draft` and `send_email` also accept `inReplyTo` and `references` directly
for cases the helper does not cover.
Every outgoing message now carries a generated `Message-ID`, so replies *to* your own sent mail
thread as well. `send_draft` forwards the draft's threading headers to SMTP — without that a
correctly threaded draft would send un-threaded.
A parent with no `Message-ID` cannot be threaded to. `create_reply_draft` returns
`threaded: false` in that case rather than pretending otherwise.
## Architecture

<details>
<summary><strong>Project structure</strong></summary>
```
src/
index.ts Entry point, transport selection, graceful shutdown
server.ts McpServer setup, tool registration
config.ts Zod-validated environment configuration
logger.ts Structured stderr logger with credential redaction
types.ts Shared TypeScript interfaces
services/
smtp.ts nodemailer wrapper (lazy connection)
imap.ts imapflow + mailparser wrapper (lazy connection, auto-reconnect)
tools/
sending.ts send_email, send_test_email
reading.ts get_emails, get_email_by_id, search_emails
drafts.ts create_draft, create_reply_draft, update_draft, delete_draft, send_draft
actions.ts mark_email_read, star_email, move_email, delete_email
folders.ts get_folders, sync_folders
system.ts get_connection_status
```
</details>
<details>
<summary><strong>Design decisions</strong></summary>
- **McpServer API** (SDK v1.29+) with Zod input validation on every tool
- **Tool annotations** (`readOnlyHint`, `destructiveHint`, `openWorldHint`) per MCP spec
- **Dual transport** -- stdio for local use, Streamable HTTP for remote deployment
- **Lazy connections** -- SMTP and IMAP connect on first use, not at startup
- **Credential redaction** -- passwords scrubbed from all log output
- **Soft delete** -- `delete_email` moves to Trash first; permanent delete only from Trash
</details>
## Development
```bash
npm run dev # Watch mode with tsx
npm run typecheck # Type checking without emit
npm run lint # ESLint
npm run format # Prettier
npm test # Run tests
npm run build # Rebuild (symlink picks up changes automatically)
```
## Credits
Originally scaffolded from [anyrxo/protonmail-pro-mcp](https://github.com/anyrxo/protonmail-pro-mcp). Completely rewritten with modern MCP SDK, Zod validation, dual transport, and full tool implementations.
## License
MIT
TDQS
Scored across 18 tools
Most tools target clearly distinct resources and actions (folders, emails, drafts, attachments, connection). Minor overlap between get_emails/get_email_by_id/search_emails and between sync_folders/get_folders, though descriptions differentiate list vs. fetch-by-id vs. search and refresh vs. list.
Every tool follows a consistent snake_case verb_noun pattern (get_emails, create_draft, send_email, mark_email_read, move_email). The convention is applied uniformly across all 18 tools with no deviations.
18 tools is slightly heavy but justified for a full email client spanning folders, reading, sending, drafts, attachments, search, and flags. Each tool earns its place with little redundancy.
Covers core lifecycle: list/read/search, send, draft create/update/send/delete, attachments, folders, move, delete, and flags. Gaps include direct reply/forward sending and thread/conversation views, but most workflows are workable.