Full Proton Mail Bridge Hermes skill with 6 tools: - proton_mail_bridge_status — check daemon health - proton_mail_list — list inbox/folder messages - proton_mail_read — read full message by UID (body+headers) - proton_mail_search — search by subject/from/body/all - proton_mail_send — send email with CC/BCC support - proton_mail_reply — reply preserving In-Reply-To/References Implementation: pure Python stdlib (imaplib + smtplib + email), no external dependencies. 22 unit tests with mocked IMAP/SMTP. Follows architecture from ARCHITECTURE.md (section 3). Per-tool auth via PROTONMAIL_ACCOUNT + PROTONMAIL_BRIDGE_PASSWORD env vars. Bridge runs on 127.0.0.1:1143 (IMAP TLS) / 127.0.0.1:1025 (SMTP STARTTLS).
10 KiB
| name | description | version | author | license | platforms | metadata | |||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| proton-mail | Proton Mail via Bridge — read, send, search, and reply to emails using the local Proton Mail Bridge daemon (IMAP 127.0.0.1:1143 / SMTP 127.0.0.1:1025). | 1.0.0 | Trentuna / B.A. Baracus | MIT |
|
|
Proton Mail Bridge — Hermes Skill
Give any Hermes agent native access to Proton Mail via the official Proton Mail Bridge.
The Bridge runs as a local daemon, handles all OpenPGP encryption/decryption transparently, and exposes standard IMAP (read) and SMTP (send) ports on localhost. This skill wraps those ports as Hermes tools.
How It Works
┌──────────────┐ IMAP 127.0.0.1:1143 (TLS) ┌─────────────────┐
│ Hermes │ ───────────────────────────────► │ Proton Bridge │
│ Agent │ │ (local daemon) │
│ (this │ ◄─────────────────────────────── │ │
│ skill) │ SMTP 127.0.0.1:1025 (STARTTLS) │ decrypts PGP │
└──────────────┘ └────────┬────────┘
│
▼
Proton Servers
Prerequisites
- Proton Mail Bridge installed and running:
- Download: https://proton.me/mail/bridge
- Linux:
protonmail-bridge --cli - macOS:
brew install --cask proton-mail-bridge
- Proton Mail account (Free or paid)
- Bridge credentials — Bridge generates a local app password (NOT your Proton password). Get it from Bridge → Settings → Account → Mailbox configuration → Show password.
Environment Variables
| Variable | Required | Default | Description |
|---|---|---|---|
PROTONMAIL_ACCOUNT |
Yes | — | Your Proton email address (e.g. user@proton.me) |
PROTONMAIL_BRIDGE_PASSWORD |
Yes | — | Bridge-generated app password |
PROTONMAIL_IMAP_HOST |
No | 127.0.0.1 |
Bridge IMAP hostname |
PROTONMAIL_IMAP_PORT |
No | 1143 |
Bridge IMAP port |
PROTONMAIL_SMTP_HOST |
No | 127.0.0.1 |
Bridge SMTP hostname |
PROTONMAIL_SMTP_PORT |
No | 1025 |
Bridge SMTP port |
Tool Reference
proton_mail_bridge_status
Check that the Proton Bridge daemon is running, reachable, and authenticated.
{
"name": "proton_mail_bridge_status",
"description": "Check Proton Mail Bridge status — running, authenticated, connected.",
"parameters": {}
}
Returns:
{"status": "running", "imap": "127.0.0.1:1143", "smtp": "127.0.0.1:1025", "authenticated": true}{"status": "stopped", "unreachable": ["IMAP 127.0.0.1:1143"]}{"status": "unconfigured", "error": "PROTONMAIL_ACCOUNT environment variable is not set"}
proton_mail_list
List recent messages in a mailbox folder. Returns headers only (no full body).
{
"name": "proton_mail_list",
"description": "List recent email messages in a folder.",
"parameters": {
"type": "object",
"properties": {
"folder": {"type": "string", "description": "Mailbox folder (INBOX, Sent, Drafts, etc.)", "default": "INBOX"},
"limit": {"type": "integer", "description": "Max messages to return (1-100)", "default": 20}
}
}
}
Returns:
{
"success": true,
"messages": [
{"uid": 42, "subject": "Meeting tomorrow", "from": "alice@example.com",
"to": "you@proton.me", "date": "Tue, 4 Jun 2024 14:00:00 +0000"}
],
"folder": "INBOX",
"total": 137
}
proton_mail_read
Read a full email by UID — subject, all headers, and body text.
{
"name": "proton_mail_read",
"description": "Read a full email message including body content.",
"parameters": {
"type": "object",
"properties": {
"uid": {"type": "integer", "description": "UID of the message to read"},
"folder": {"type": "string", "description": "Mailbox folder", "default": "INBOX"}
}
}
}
Returns:
{
"success": true,
"uid": 42,
"subject": "Meeting tomorrow",
"from": "alice@example.com",
"to": "you@proton.me",
"date": "2024-06-04T14:00:00+00:00",
"body": "Hi, let's meet at 3pm tomorrow.\n\nBest,\nAlice",
"message_id": "<msg42@proton.me>",
"flags": []
}
proton_mail_search
Search emails across a mailbox by subject, sender, body, or all fields.
{
"name": "proton_mail_search",
"description": "Search email messages by query in a specific field.",
"parameters": {
"type": "object",
"properties": {
"query": {"type": "string", "description": "Search query text (min 2 characters)"},
"field": {"type": "string", "description": "Field to search (subject, from, body, or all)", "enum": ["subject", "from", "body", "all"], "default": "all"},
"folder": {"type": "string", "description": "Mailbox folder", "default": "INBOX"},
"limit": {"type": "integer", "description": "Max results (1-100)", "default": 20}
}
}
}
Returns: Same shape as proton_mail_list plus "query": "Meeting".
proton_mail_send
Send a new email via Bridge SMTP.
{
"name": "proton_mail_send",
"description": "Send a new email message.",
"parameters": {
"type": "object",
"properties": {
"to": {"type": "string", "description": "Recipient email(s), comma-separated"},
"cc": {"type": "string", "description": "CC recipient(s), comma-separated"},
"bcc": {"type": "string", "description": "BCC recipient(s), comma-separated"},
"subject": {"type": "string", "description": "Email subject"},
"body": {"type": "string", "description": "Email body text (plain text)"}
}
}
}
Returns:
{
"success": true,
"message_id": "<hermes-...@proton.me>",
"to": "bob@example.com",
"subject": "Hello"
}
proton_mail_reply
Reply to an existing email, preserving thread context (In-Reply-To and References headers).
{
"name": "proton_mail_reply",
"description": "Reply to an existing email, preserving thread headers.",
"parameters": {
"type": "object",
"properties": {
"uid": {"type": "integer", "description": "UID of the email to reply to"},
"body": {"type": "string", "description": "Reply body text"},
"folder": {"type": "string", "description": "Mailbox folder", "default": "INBOX"}
}
}
}
Returns:
{
"success": true,
"message_id": "<hermes-reply-...@proton.me>",
"in_reply_to": "<original-msg-id@proton.me>",
"to": "alice@example.com",
"subject": "Re: Original Subject"
}
Setup
1. Install Proton Mail Bridge
# Linux (headless)
protonmail-bridge --cli
# Follow interactive setup — login with Proton credentials
# macOS
brew install --wait proton-mail-bridge
2. Get Bridge password
In Bridge: Settings → your account → Mailbox configuration → Show password. This is a Bridge-generated local password, not your Proton password.
3. Set environment variables
export PROTONMAIL_ACCOUNT="your-email@proton.me"
export PROTONMAIL_BRIDGE_PASSWORD="bridge-generated-password"
Or add to your Hermes profile's .env at ~/.hermes/profiles/<name>/.env:
PROTONMAIL_ACCOUNT=your-email@proton.me
PROTONMAIL_BRIDGE_PASSWORD=bridge-generated-password
4. Verify
Call proton_mail_bridge_status — you should see "status": "running" and "authenticated": true.
Example Workflows
Quick inbox check:
proton_mail_list({"folder": "INBOX", "limit": 5})
Read and reply:
1. proton_mail_list({"limit": 10})
2. proton_mail_read({"uid": 42})
3. proton_mail_reply({"uid": 42, "body": "Thanks, got it!"})
Search and respond:
1. proton_mail_search({"query": "invoice", "field": "subject"})
2. proton_mail_read({"uid": result.uid})
3. proton_mail_send({"to": result.from, "subject": "Re: invoice", "body": "..."})
Send with CC:
proton_mail_send({
"to": "team@example.com",
"cc": "manager@example.com",
"subject": "Status Update",
"body": "All good here."
})
Implementation
The skill is implemented in pure Python using standard library modules (imaplib, smtplib, email). No external dependencies.
Reference implementation at <skill-dir>/references/tools.py.
Security
- Connections are localhost-only — Bridge listens on
127.0.0.1only - TLS on IMAP —
IMAP4_SSLconnects to port 1143 - STARTTLS on SMTP — explicit TLS negotiation on port 1025
- Bridge password is NOT your Proton password — defense-in-depth via Bridge's separate auth
- Credential injection prevented —
_sanitize_search_term()strips control characters and IMAP-special chars from user input - No secrets in tool calls — credentials come from environment, never from tool arguments
Known Limitations
- Plain text body only — HTML rendering is not available; HTML emails return the raw HTML source
- No attachment handling yet —
proton_mail_readreturns body text only. Attachments are present in the MIME structure but not extracted separately - Localhost-only — the Bridge must run on the same machine as Hermes
- Bridge required — the skill doesn't work without the Bridge daemon; it can't log into Proton API directly
- Single account — one Bridge instance serves one account; multi-account requires multiple Bridge instances
References
- Proton Mail Bridge — official download and docs
- openclaw-protonmail-skill — OpenClaw analogue (TypeScript)
- emersion/hydroxide — third-party Bridge alternative for headless servers
- Hermes-Proton Architecture — full architecture document