agentmail
IMAP email client exposed as both a CLI and an MCP (Model Context Protocol) server, built with Rust.
One binary: agentmail serve starts the MCP stdio server, all other subcommands are a direct CLI.
See also: DESIGN.md for architecture diagrams and design decisions, MCP.md for the full MCP tool & prompt reference with output schemas.
Requirements
- Rust toolchain (edition 2024)
- An IMAP-enabled email account (Gmail, Outlook, Fastmail, self-hosted, etc.)
Build
Output binary: target/release/agentmail
Configuration
agentmail reads its config from a single TOML file:
| Location | Path |
|---|---|
| Default | ~/.config/agentmail/config.toml |
| Override | Set the AGENTMAIL_CONFIG environment variable to any path |
On macOS the default expands to ~/Library/Application Support/agentmail/config.toml if dirs::config_dir() returns Library/Application Support, but ~/.config/agentmail/config.toml is more conventional and works fine — just pick one.
Quick start
The fastest way to add an account is the interactive configure command:
# With a provider preset (gmail, icloud, outlook, fastmail, yahoo)
# Or fully custom
This prompts for your username, password method, writes the config file, and tests the connection.
Single account
[]
= "imap.gmail.com"
= "you@gmail.com"
= "you@gmail.com"
Multiple accounts
Add as many [accounts.<name>] sections as you like. Each is a fully independent IMAP connection with its own credentials and settings. You do not need separate config files or multiple server instances.
[]
= "imap.gmail.com"
= "you@gmail.com"
= "you@gmail.com"
[]
= "imap.mail.me.com"
= "johnappleseed"
= "security find-internet-password -s imap.mail.me.com -a johnappleseed -w"
[]
= "imap.company.com"
= "you@company.com"
= "op read op://Work/Email/password"
= "Trash"
= "Drafts"
All accounts are available simultaneously — the MCP tools and CLI commands accept an account parameter to select which one to operate on.
Account config reference
| Field | Type | Default | Description |
|---|---|---|---|
host |
string | required | IMAP server hostname |
port |
u16 | 993 |
IMAP port |
username |
string | required | Login username / email |
password |
Secret | — | Password source (see Passwords below) |
tls |
bool | true |
Use TLS |
trash_mailbox |
string | auto-detect / "Trash" |
Trash folder name override |
drafts_mailbox |
string | auto-detect / "Drafts" |
Drafts folder name override |
Passwords
agentmail uses secret-lib for flexible credential management. The password field supports three sources:
Shell command (recommended for reusing existing credentials):
# Read from Apple Mail / macOS Keychain internet passwords
= "security find-internet-password -s imap.mail.me.com -a johnappleseed -w"
# Read from pass (Unix password manager)
= "pass show email/gmail"
# Read from 1Password CLI
= "op read op://Personal/Gmail/password"
# Read from Bitwarden CLI
= "bw get password gmail-imap"
The command is executed at connection time and the first line of stdout is used as the password. This is the most flexible option — it works with any password manager that has a CLI.
System keyring (recommended for standalone use):
= "you@gmail.com"
Stores and retrieves from the system credential store (macOS Keychain, Windows Credential Manager, Linux Secret Service). The value is the keyring entry key; the service name is "agentmail". Store a password with:
Raw string (not recommended — plaintext in config file):
= "hunter2"
Using Apple Mail / iCloud passwords
macOS Mail stores IMAP passwords as internet password items in the Keychain. You can read them directly using password.cmd:
[]
= "imap.mail.me.com"
= "johnappleseed"
= "security find-internet-password -s imap.mail.me.com -a johnappleseed -w"
This shells out to security at connection time, which reads Apple Mail's stored password. The first time you run this, macOS may prompt you to allow keychain access.
To find the correct server and account values for your setup:
# List all internet passwords for iCloud Mail
# List for Gmail
Password resolution order
When connecting, agentmail tries these sources in order and uses the first one found:
AGENTMAIL_PASSWORD_<ACCOUNT>environment variable (override for CI/Docker)passwordfield in config (command, keyring, or raw)- Default keyring lookup under
"agentmail"service with username as key (backward compat forset-passwordusers with nopasswordfield)
Environment variable override
For CI, Docker, or headless servers, passwords can be passed via environment variables regardless of what's in the config file:
The variable name is AGENTMAIL_PASSWORD_ followed by the account name uppercased, with dashes and spaces replaced by underscores.
Testing your setup
# 1. Check that the account appears in the config
# 2. Test IMAP connectivity and authentication
# 3. List mailboxes to confirm full access
Gmail setup
Gmail requires an App Password (not your regular Google account password). Generate one, then:
[]
= "imap.gmail.com"
= "you@gmail.com"
= "you@gmail.com"
# paste the 16-character app password
iCloud Mail setup
iCloud uses your Apple ID with an app-specific password. The IMAP login is your iCloud username (not full email):
[]
= "imap.mail.me.com"
= "johnappleseed"
= "johnappleseed"
Or reuse the password Apple Mail already stored in the Keychain:
[]
= "imap.mail.me.com"
= "johnappleseed"
= "security find-internet-password -s imap.mail.me.com -a johnappleseed -w"
Migration from previous versions
If you're upgrading from a version that used keychain_service or password = "...":
keychain_servicehas been removed. Usepassword.keyring = "your-username"instead. Passwords previously stored viaset-passwordare still found automatically (backward compat fallback).password = "plaintext"still works but is treated aspassword.raw = "plaintext"internally.
Usage
MCP Server
Starts an MCP stdio server. Logs go to stderr; JSON-RPC on stdin/stdout.
CLI
Full subcommand list: agentmail --help
MCP Client Configuration
Add to your MCP client config (Claude Desktop, Claude Code, etc.):
To pass passwords via environment variables instead of keychain:
MCP Tools
21 tools covering account discovery, mailbox management, message reading, search, bulk operations, flag management, and composition.
| Tool | Description |
|---|---|
list_accounts |
Return configured account names (use this first) |
list_mailboxes |
List mailboxes with message counts (total, unseen, recent) |
create_mailbox |
Create a new mailbox (folder) on the server |
check_connection |
Test IMAP connectivity for an account |
list_capabilities |
List IMAP server capabilities (IDLE, MOVE, etc.) |
get_messages |
Paginated message fetch, newest-first by UID |
search_messages |
IMAP SEARCH with text, header, sender, subject, and status filters |
list_flags |
List all flags in use with counts; resolves Apple Mail color flags |
rank_senders |
Rank senders by message count across one or all mailboxes |
rank_unsubscribe |
Rank bulk-mail senders by List-Unsubscribe presence, sorted by one-click support |
rank_list_id |
Rank mailing lists by List-Id header (RFC 2919), groups regardless of sender |
find_attachments |
Scan for messages with attachments (multipart/mixed or multipart/related) |
download_attachments |
Download attachments from a message to disk |
delete_messages |
Delete messages by UID (up to 500 per call, moves to Trash or expunges) |
delete_by_sender |
Delete all messages from a sender identified by UID, optionally across all mailboxes |
delete_list_id |
Delete all messages with a specific List-Id across all mailboxes |
move_message |
Move a message between mailboxes via IMAP MOVE |
create_draft |
Compose RFC822 draft and append to Drafts folder |
unsubscribe_message |
RFC 8058 one-click unsubscribe, optionally delete matching bulk mail across all boxes |
add_flags |
Add flags and/or set Apple Mail color on a message (union semantics) |
remove_flags |
Remove flags and/or clear Apple Mail color from a message |
Key parameters
accountis required for most tools. Uselist_accountsto discover valid names.mailboxdefaults toINBOXwhen omitted. Omit it onrank_senders,rank_unsubscribe,rank_list_id,list_flags, andfind_attachmentsto scan the entire account (auto-skips Trash, Junk, Spam, Drafts).limitdefaults to 25, clamped to 1..50.includeContent(default false) returns normalized markdown body text, trimmed for context window safety.- All reads use
BODY.PEEKto avoid marking messages as\Seen. - Long-running operations (
rank_senders,rank_unsubscribe,rank_list_id,find_attachments,list_flags,delete_messages) support MCP progress notifications.
MCP Prompts
6 prompts provide guided conversation starters for common email workflows:
| Prompt | Description |
|---|---|
inbox-summary |
Get a comprehensive inbox overview: folder structure, top senders, unread messages |
cleanup-sender |
Find and bulk-delete all emails from a specific sender (with preview) |
find-attachments |
Scan a mailbox for messages with attachments and list for download |
compose-email |
Guided email draft composition |
unsubscribe-cleanup |
Identify high-volume mailing lists, unsubscribe and bulk-delete |
list-id-cleanup |
Identify mailing lists by List-Id and bulk-delete entire lists |
Architecture
agentmail (binary crate: agentmail-mcp)
├── serve → MCP stdio server (tokio + rmcp)
│ 21 tools + 6 prompts, progress notifications
├── list-accounts → CLI
├── list-mailboxes → CLI
├── create-mailbox → CLI
├── check-connection → CLI
├── list-capabilities → CLI
├── get-messages → CLI
├── get-messages-by-uid → CLI
├── rank-senders → CLI
├── rank-unsubscribe → CLI
├── find-attachments → CLI
├── download-attachments → CLI
├── list-flags → CLI
├── add-flags → CLI (flags + Apple Mail colors)
├── create-draft → CLI
├── set-password → CLI (keychain store)
└── configure → CLI (interactive account setup)
crates/agentmail (library)
├── config.rs → TOML config loading, default account resolution
├── credentials.rs → Password resolution (env → secret-lib → keyring fallback)
├── connection.rs → IMAP connection pool (3 idle sessions/account)
├── imap_client.rs → IMAP operations (fetch, search, delete, move, create, sync)
├── parser.rs → RFC822 → MessageInfo (via mail-parser), attachment extraction
├── draft.rs → RFC822 composition (via lettre)
├── content.rs → HTML→markdown conversion, context window trimming
├── provider.rs → Email provider presets (Gmail, iCloud, Outlook, Fastmail, Yahoo)
├── types.rs → Shared data structures (MessageInfo, MailboxInfo, etc.)
├── error.rs → Error types
└── lib.rs → Public API facade
Connection pooling: Each account maintains up to 3 idle IMAP sessions. Sessions are validated with NOOP before reuse and replaced when stale. Credentials are resolved on-demand when a new connection is needed.
Post-mutation sync: All mutating operations (delete, move, create draft, create mailbox) issue a NOOP after the operation to flush pending server-side state before releasing the session back to the pool.
Troubleshooting
- Run
agentmail check-connection --account <name>to test connectivity. - Verify your password:
agentmail set-password --account <name>to re-store it. - Gmail users: ensure you're using an App Password, not your Google account password.
- Check that your IMAP server allows external clients (some providers disable IMAP by default).
- If the MCP server appears empty in Inspector, call
initializefirst, thentools/list.