Email toolkit for AI assistants and command-line scripting
Project description
Give your script or AI assistant access to your email.
Contents
- What your AI can do with it
- Installation
- Configuration
- Quick test
- CLI usage
- Claude Code (terminal-based Claude)
- MCP server
- Scripting and automation
- Faster searches
- Connection handling
- Security
- License
Courier connects to your existing mailbox on Gmail, Outlook, Fastmail, or any IMAP provider. It does not create new email addresses or route mail through a third-party service.
Commandline users, script authors, and AI assistants can search, read, download, reply, send, and organize email. Two interfaces serve different environments: a CLI that outputs JSON (for terminal-based agents, scripts, and automation) and an MCP server (for web-based AI chats and MCP clients). Both expose the same operations.
What your AI can do with it
- Courier runs on your machine, with no service of ours between you and your mailbox.
- Keep banking, OTPs, and sensitive senders out of the LLM's view, using per-mailbox Sieve rules.
- Reply from the right alias, using our identity feature.
- Gmail-style queries (
from:alice newer:3d is:unread) work on any IMAP provider. - An optional local
muindex makes archive-grade searches near-instant. - Search every mailbox in one call with
-A. - Drafts on your server by default;
--sendis opt-in. - Handle a meeting invite (parse the
.ics, draft an RSVP). - Chain searches and reads in one call:
courier search foo search bar read -f INBOX -u 42. - Move, flag, or archive messages.
- Download attachments, export messages as HTML, extract links.
Installation
See INSTALLATION.md for Homebrew, Debian/Ubuntu (.deb), Fedora/RHEL (.rpm), and source installs.
Configuration
Copy the sample and fill in your credentials:
cp examples/config.sample.toml ~/.config/courier/config.toml
A small config has three top-level named-entity tables: an [imap.NAME] mailbox, an [smtp.NAME] outgoing endpoint, and an [identity.NAME] describing one sendable address pointing at the IMAP block:
[smtp.gmail]
host = "smtp.gmail.com"
port = 587
[imap.personal]
host = "imap.gmail.com"
port = 993
username = "you@gmail.com"
# For Gmail, generate this at https://myaccount.google.com/apppasswords
password = "abcdefghijklmnop"
default_smtp = "gmail"
[identity.personal]
imap = "personal"
address = "you@gmail.com"
(Smaller is also valid: [imap.*] alone reads but cannot send; [identity.*] with bcc plus [smtp.*] sends but cannot read.)
For Gmail, the simpler path is the app-password example above. The alternative is OAuth2, which needs a Google Cloud project set up through Google's developer console (a much messier path); if you have already done that, the same [imap.NAME] block carries the OAuth2 keys instead of password:
[imap.personal]
host = "imap.gmail.com"
port = 993
username = "you@gmail.com"
client_id = "YOUR_CLIENT_ID"
client_secret = "YOUR_CLIENT_SECRET"
refresh_token = "YOUR_REFRESH_TOKEN"
default_smtp = "gmail"
[smtp.NAME] blocks declare named SMTP endpoints. When the block omits credentials, courier inherits them from the [imap.NAME] block in scope at send time, the right shape for Gmail and Fastmail where IMAP and SMTP share one credential. When the block carries its own username and password, courier uses those, the right shape for AWS SES and similar smarthosts where one IAM SMTP user serves many From addresses.
Sending requires at least one [identity.NAME] block pointing at the [imap.NAME]. A block with no identities is read-only for sending; drafting and reading still work. This is a valid state, not an error. Declaring identities explicitly avoids the registration-handle hazard, where an IMAP login (e.g. a Gmail handle that is not an intended sender) could otherwise become a sendable identity by accident.
courier config-check validates the config (cross-references, identity addresses, send-route resolution) without performing any IMAP or SMTP traffic. The same warnings surface on courier, courier --help, courier status, and courier list.
Gmail OAuth2 setup requires a Google Cloud project with the Gmail API enabled. See GMAIL_SETUP.md for the full walkthrough.
For multi-account configs (multiple [imap.*] blocks, send-as through several identities, SES smarthost routing), see docs/CONFIGURATION.md.
Quick test
With uv (any platform):
uvx courier search "subject:invoice"
No installation step; uvx runs it directly. To install permanently:
uv tool install courier
On Debian and Ubuntu, the default install path is the .deb package from the GitHub release (see INSTALLATION.md). As an alternative for running from a clone without installing, on Ubuntu 25.04 or later the CLI dependencies are all in the standard repositories:
sudo apt-get install python3-typer python3-dotenv python3-imapclient python3-requests
python3 -m courier search "subject:invoice"
Courier looks for a config file at ~/.config/courier/config.toml. Use --config /path/to/config.toml to point to a different location.
The MCP server (courier mcp) requires the mcp Python package, which is not in apt. Use uv or pip for that. Many users prefer the CLI to MCP because the latter loads 80+ tools into every conversation; the CLI is the lighter footprint.
CLI usage
Every command outputs JSON to stdout. Errors go to stderr. This makes Courier composable with jq, shell scripts, and AI agent skill definitions.
# Look up several keywords in one invocation; each gets its own outer key
# in the result, so hits stay attributed to the keyword that matched them
courier search "from:alice" search 'subject:"hotel booking"' search "is:unread"
# What's unread in INBOX?
courier search "is:unread" --folder INBOX --limit 10
# Read an email
courier read -f INBOX -u 4523
# List and download attachments
courier attachments -f INBOX -u 4523
courier save -f INBOX -u 4523 --attachment itinerary.pdf -o /tmp/itinerary.pdf
# Export an HTML email as a standalone file (images embedded)
courier export -f INBOX -u 4523 -o /tmp/email.html
courier export -f INBOX -u 4523 -o /tmp/email.eml --raw
# Extract all links from several emails
courier links -f INBOX -u 4523 -u 4524 -u 4525
# Reply, saved to drafts by default; --send transmits via SMTP
courier reply -f INBOX -u 4523 -b "Thanks, confirmed."
courier reply -f INBOX -u 4523 -b "Invoice attached." --attach /tmp/invoice.pdf
courier reply -f INBOX -u 4523 -b "Thanks, confirmed." --send
# Compose a new message (--send requires --identity NAME, or
# --smtp NAME --from EMAIL; see docs/CONFIGURATION.md)
courier compose --to alice@example.com --subject "Meeting" \
-b "See attached." --send --identity work
# Send a draft
courier send-draft -f Drafts -u 4530
# Organize
courier move -f INBOX -u 4523 -t Archive
courier mark-read -f INBOX -u 4524
courier flag -f INBOX -u 4525
Run courier --help for the full command list.
Claude Code (terminal-based Claude)
Run courier install-claude-command once after installation. This writes ~/.claude/commands/courier.md, which tells Claude Code how to use the courier CLI for email tasks. After that, prompts like "find the booking confirmation from last week" or "reply to Alice's message" route through courier automatically.
MCP server
For AI environments that cannot run shell commands (Claude web, Cursor, or any MCP client):
courier mcp
This starts an MCP server exposing the same operations as tools. The MCP package is only imported when this subcommand runs, so the CLI stays lightweight.
Scripting and automation
Because every command returns JSON and uses non-zero exit codes on failure, Courier works as a building block in pipelines and cron jobs.
# Forward all emails from a sender to another folder
courier search "from:sender@example.com" --folder INBOX \
| jq -r '.[].uid' \
| xargs -I{} courier move -f INBOX -u {} -t Forwarded
# Daily digest: save today's unread subjects to a file
courier search "is:unread" --folder INBOX \
| jq -r '.[].subject' > ~/daily-digest.txt
# Auto-acknowledge incoming invoices
courier search "is:unread subject:invoice" --folder INBOX \
| jq -r '.[].uid' \
| xargs -I{} courier reply -f INBOX -u {} -b "Received, processing." \
--send --identity work
AI agents with skill/hook systems call Courier the same way: define a skill that runs a shell command and parses the JSON output.
Faster searches
Courier can answer search from a local Xapian index instead of IMAP, orders of magnitude faster, with transparent fallback to IMAP when the index can't serve the query. See docs/LOCAL_CACHE.md for the offlineimap+mu setup.
Connection handling
IMAP servers drop idle connections after 10-30 minutes. AI assistants work in bursts: a flurry of operations, then thinking time. Courier tracks connection age and reconnects transparently before operations fail. The default idle timeout is 300 seconds; set idle_timeout in the config to adjust.
Security
Courier accesses your email account. Store credentials outside your repository (environment variables, a secrets manager, or a config file in .gitignore). Use app-specific passwords or OAuth2 rather than your main account password. Restrict allowed_folders in the config to limit what the tool can see.
For per-message control over what reaches the LLM, point an [imap.NAME] block's redact field at a Sieve script. Matching messages have subject, body, and party addresses blanked before courier returns them, so banking notices, OTPs, and other sensitive content stay out of the model's context window. See examples/work-only.sieve for a starting policy.
License
MIT. See LICENSE.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file courier-1.1.15.tar.gz.
File metadata
- Download URL: courier-1.1.15.tar.gz
- Upload date:
- Size: 214.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1b5a97de5dc9195325f048306b9a96134db99c0cd7ee53859be18eed7e9e26e3
|
|
| MD5 |
57ec14f2912f73fa6278bcd4ad4264aa
|
|
| BLAKE2b-256 |
88699c0174e4d185279dac7962ef7a3b54832f1a3a007a965c8cbb3f0127c8fb
|
File details
Details for the file courier-1.1.15-py3-none-any.whl.
File metadata
- Download URL: courier-1.1.15-py3-none-any.whl
- Upload date:
- Size: 132.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
894ab9b1c4cce119c42feb600e5a27c4789f195c7206333937982b9428185b8f
|
|
| MD5 |
1672c34c1c507b8030ff191894cb5110
|
|
| BLAKE2b-256 |
5684b8bb9047236a3d1e8e3de00e922b3637c1db42f88361fda0e145842eb60f
|