Skip to main content

Email toolkit for AI assistants and command-line scripting

Project description

Courier

CI Python 3.11+ License: MIT

Give your script or AI assistant access to your email.

Contents

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 mu index makes archive-grade searches near-instant.
  • Search every mailbox in one call with -A.
  • Drafts on your server by default; --send is 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

The simplest cross-platform install is from PyPI:

pip install courier          # CLI only
pip install "courier[mcp]"   # CLI plus the MCP server

With uv, uvx courier ... runs it without installing and uv tool install courier installs it permanently.

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


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

courier-1.1.16.tar.gz (214.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

courier-1.1.16-py3-none-any.whl (132.3 kB view details)

Uploaded Python 3

File details

Details for the file courier-1.1.16.tar.gz.

File metadata

  • Download URL: courier-1.1.16.tar.gz
  • Upload date:
  • Size: 214.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.11.14

File hashes

Hashes for courier-1.1.16.tar.gz
Algorithm Hash digest
SHA256 7b194fcd2cf53c25e374a33d3e999705e50f64e8f6192ec05bf08af8968931b2
MD5 aca8ad8e320ca4a00bda96055b760a37
BLAKE2b-256 3bcc0487f5c234a4c8bc80b80e7a58098ca6a248d263e97850e357fd5ae3578a

See more details on using hashes here.

File details

Details for the file courier-1.1.16-py3-none-any.whl.

File metadata

  • Download URL: courier-1.1.16-py3-none-any.whl
  • Upload date:
  • Size: 132.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.11.14

File hashes

Hashes for courier-1.1.16-py3-none-any.whl
Algorithm Hash digest
SHA256 38ad71152e7234e5263beafaf1aa6100b9d99730cd605de3f9c3b1c350a68c2d
MD5 2c0bb41ce7bd1aec6c8280a9779310c9
BLAKE2b-256 7780e09d52b7faad8e6b60f61ccbffdb38fe4f31074b1d05c4fbc1e26625b1c3

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page