claw-mail
// Multi-account email management skill for IMAP/SMTP. Fetches, reads, searches, composes, sends, replies, forwards, and organizes emails across multiple accounts. Features IMAP Outbox for reliable delivery, secure credential storage via 1Password and macOS Keychain, TLS 1.2+ with hardened ciphers, OAu
clawMail Skill
You are an email management agent with multi-account IMAP/SMTP support. You can fetch, read, search, process, compose, send, reply, forward, move, and manage emails, drafts, and folders across multiple email accounts.
Multi-Account Model
- Account profiles: Each account has its own IMAP/SMTP credentials, mailboxes, fetch limits, archival settings, and processing rules.
- Default account: One account is designated as the default. Any script invoked
without
--accountuses the default automatically. - SMTP fallback: If an account's SMTP server fails, the system automatically retries via a configured fallback relay.
- IMAP Outbox: Messages are staged in a temporary Outbox folder before SMTP delivery. If SMTP fails, the message stays in Outbox for retry by the heartbeat.
- Per-account + global rules: Each account has its own rules, plus global rules that apply to all accounts.
- OAuth2: Accounts can use OAuth2 (XOAUTH2) authentication instead of passwords.
- Dated-folder archival:
archive_mail.pyand the heartbeat honor per-accountarchive_root/archive_frequencydefaults so messages routed to thearchiveaction land in folders such asArchive-202603,Archive-W09, orArchive-20260315.
Security
- TLS 1.2+: All IMAP and SMTP connections enforce TLS 1.2 or higher.
- Hardened ciphers: Only ECDHE+AESGCM, ECDHE+CHACHA20, DHE+AESGCM, and DHE+CHACHA20 cipher suites are allowed. Weak ciphers (MD5, RC4, 3DES, DSS) are explicitly blocked.
- Certificate verification: Hostname checking and certificate validation are always enabled.
- RFC 5322 compliance: All outgoing emails include required Date, Message-ID, and MIME-Version headers automatically.
- Secure credential storage: Passwords in config support 1Password CLI
(
op://vault/item/field), macOS Keychain (keychain://service/account), and environment variables (env://VAR_NAME).
Available Scripts
All scripts are in the scripts/ directory. Run with
python3 scripts/<name>.py from the skill root. Every script accepts
--account <name> to target a specific account.
Core Scripts
| Script | Purpose |
|---|---|
scripts/fetch_mail.py | Fetch emails from an IMAP folder |
scripts/read_mail.py | Read/render an email by Message-ID; save attachments to disk |
scripts/search_mail.py | Search emails by subject, sender, body, date, flags |
scripts/send_mail.py | Send rich HTML emails via SMTP (Outbox + fallback); attach files |
scripts/compose_mail.py | Compose rich HTML emails from templates; attach files |
scripts/reply_mail.py | Reply to an email with original-message quoting |
scripts/forward_mail.py | Forward an email inline-quoted or with attachments |
scripts/draft_mail.py | Save, list, resume, or send drafts via IMAP Drafts folder |
scripts/process_mail.py | Run emails through the rule-based processing pipeline |
scripts/manage_folders.py | List, create, delete, rename, and move IMAP folders |
scripts/move_mail.py | Move emails between IMAP folders (batch support) |
scripts/heartbeat.py | Run a full heartbeat cycle (drains Outbox, fetches, processes) |
scripts/idle_monitor.py | Monitor a mailbox via IMAP IDLE (push notifications) |
scripts/retry_send.py | Retry sending messages stuck in the IMAP Outbox |
scripts/calendar_invite.py | Compose and send iCalendar meeting invitations |
scripts/mail_merge.py | Batch personalised sends from template + CSV/JSON data |
scripts/thread_mail.py | Group messages into conversation threads |
scripts/archive_mail.py | Auto-archive old messages into dated folders (daily/weekly/monthly/yearly) |
Library Modules
| Module | Purpose |
|---|---|
scripts/lib/imap_client.py | IMAP client with IDLE, search, folder management, TLS 1.2+ |
scripts/lib/smtp_client.py | SMTP client with TLS 1.2+, RFC 5322, OAuth2, MIME building |
scripts/lib/composer.py | Rich HTML email composer with templates, reply, forward |
scripts/lib/processor.py | Rule-based processing pipeline with webhook actions |
scripts/lib/account_manager.py | Multi-account manager with SMTP fallback and Outbox |
scripts/lib/outbox.py | IMAP Outbox — temporary folder for reliable delivery |
scripts/lib/credential_store.py | Secure credential storage (1Password, Keychain, env) |
scripts/lib/pool.py | Connection pool for IMAP/SMTP reuse |
scripts/lib/send_queue.py | Legacy file-backed send queue (superseded by Outbox) |
scripts/lib/smime.py | S/MIME signing and encryption |
scripts/lib/oauth2.py | OAuth2 (XOAUTH2) token management |
scripts/lib/models.py | Data models (EmailMessage, EmailAddress, etc.) |
Reference Documents
| Reference | When to read |
|---|---|
references/REFERENCE.md | API overview, all script arguments and output formats |
references/TEMPLATES.md | Available email templates and template variables |
references/RULES.md | How to configure processing rules |
ROADMAP.md | Feature roadmap and progress tracker |
Quick Start
Fetching Mail
python3 scripts/fetch_mail.py --config config.yaml
python3 scripts/fetch_mail.py --account personal --unread-only --format cli --config config.yaml
Sending Rich Emails
Messages are staged in a temporary IMAP Outbox folder, sent via SMTP (with automatic fallback), then removed from Outbox on success.
python3 scripts/send_mail.py \
--to "recipient@example.com" \
--subject "Weekly Report" \
--body "<p>Here are this week's results.</p>" \
--template default \
--attach report.pdf \
--config config.yaml
Replying and Forwarding
python3 scripts/reply_mail.py --message-id "<id@example.com>" --body "Thanks!" --config config.yaml
python3 scripts/forward_mail.py --message-id "<id@example.com>" --to "colleague@x.com" --config config.yaml
Searching Emails
python3 scripts/search_mail.py --subject "invoice" --unseen --config config.yaml
python3 scripts/search_mail.py --criteria '(FROM "alice@x.com" SINCE 01-Jan-2026)' --config config.yaml
Working with Drafts
python3 scripts/draft_mail.py --action save --to "user@x.com" --subject "WIP" --body "..." --config config.yaml
python3 scripts/draft_mail.py --action list --format cli --config config.yaml
python3 scripts/draft_mail.py --action send --message-id "<draft@x.com>" --config config.yaml
Outbox & Send Retry
python3 scripts/retry_send.py --config config.yaml
python3 scripts/retry_send.py --config config.yaml --list
Heartbeat Cycle
The heartbeat drains each account's Outbox, then fetches and processes mail:
python3 scripts/heartbeat.py --config config.yaml
python3 scripts/heartbeat.py --config config.yaml --account work
Archiving Old Messages
python3 scripts/archive_mail.py --config config.yaml --days 90 --frequency monthly
python3 scripts/archive_mail.py --config config.yaml --days 30 --frequency daily --archive-root "Old Mail" --dry-run --format cli
Archiving honors archive_root / archive_frequency settings (defaults: Archive, monthly). The heartbeat and any rule with the archive action move the message into folders named Archive-202603, Archive-W09, or Archive-20260315 based on the configured cadence.
Calendar Invitations
python3 scripts/calendar_invite.py \
--to "bob@example.com" --subject "Standup" \
--start "2026-03-01T09:00:00" --end "2026-03-01T09:30:00" \
--location "Zoom" --config config.yaml
Mail Merge
python3 scripts/mail_merge.py \
--data contacts.csv --subject "Hello {{name}}" \
--body "<p>Dear {{name}}, your code is {{code}}.</p>" \
--to-field email --config config.yaml
Configuration
Create a config.yaml from assets/config.example.yaml:
default_account: work
accounts:
work:
label: "Work"
sender_address: "alice@company.com"
sender_name: "Alice Smith"
imap:
host: imap.company.com
port: 993
username: "alice@company.com"
password: "op://Work/IMAP/password" # 1Password CLI
ssl: true
smtp:
host: smtp.company.com
port: 587
username: "alice@company.com"
password: "op://Work/SMTP/password" # 1Password CLI
tls: true
mailboxes: [INBOX, Projects]
fetch_limit: 50
rules:
- name: flag_urgent
sender_pattern: "boss@company\\.com"
actions: [flag, tag]
tag: urgent
personal:
label: "Personal"
sender_address: "alice@gmail.com"
imap:
host: imap.gmail.com
password: "keychain://imap.gmail.com/alice@gmail.com" # macOS Keychain
smtp:
host: smtp.gmail.com
password: "keychain://smtp.gmail.com/alice@gmail.com" # macOS Keychain
You can also define archive_root (e.g., Archive) and archive_frequency (daily, weekly, monthly, yearly) either globally or per- account. These defaults drive both the archive_mail.py script and the heartbeat's handling of the archive rule action so that archived messages consistently live under folders like Archive-202603, Archive-W09, or Archive-20260315.
Secure Credential Storage
Passwords in config support four backends:
| Scheme | Backend | Example |
|---|---|---|
op:// | 1Password CLI | "op://Work/IMAP/password" |
keychain:// | macOS Keychain | "keychain://imap.gmail.com/alice" |
env:// | Environment variable | "env://GMAIL_APP_PASSWORD" |
| (plain text) | Literal value | "my-password" (logs a warning) |
OAuth2 Authentication (Gmail, Outlook 365)
For providers that require OAuth2, set auth: oauth2 on the IMAP/SMTP block:
imap:
host: imap.gmail.com
username: "user@gmail.com"
auth: oauth2
oauth2:
client_id: "your-client-id"
client_secret: "your-client-secret"
refresh_token: "your-refresh-token"
token_uri: "https://oauth2.googleapis.com/token"
Legacy Single-Account Config
Flat imap: / smtp: at root is automatically treated as a single account
named "default".