hermes-proton/skills/proton-mail/SKILL.md
B.A. Baracus f8b9991207
feat(proton-mail): Hermes skill — IMAP/SMTP tools via Bridge
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).
2026-06-08 18:31:07 +02:00

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
linux
macos
hermes
tags category related_skills tools requires_env optional_env
proton
email
imap
smtp
bridge
productivity
productivity
hermes-agent
proton_mail_bridge_status
proton_mail_list
proton_mail_read
proton_mail_search
proton_mail_send
proton_mail_reply
PROTONMAIL_ACCOUNT
PROTONMAIL_BRIDGE_PASSWORD
PROTONMAIL_IMAP_HOST
PROTONMAIL_IMAP_PORT
PROTONMAIL_SMTP_HOST
PROTONMAIL_SMTP_PORT

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

  1. Proton Mail Bridge installed and running:
  2. Proton Mail account (Free or paid)
  3. 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": []
}

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.1 only
  • TLS on IMAPIMAP4_SSL connects 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 yetproton_mail_read returns 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