Skip to main content

Apple Mail from your terminal

Project description

mxctl

CI Python 3.10+ Coverage: 87% License: MIT

Apple Mail from your terminal.

49 commands. Triage with AI, batch-process newsletters, turn emails into Todoist tasks — all from the terminal. Every command supports --json for scripting and AI workflows. Zero external dependencies.

mxctl demo — inbox, triage, summary, and batch operations

Table of Contents

Key Features

  • 49 Commands - Everything from basic operations to advanced batch processing
  • Any Account, One Interface - iCloud, Gmail, Outlook, Exchange, IMAP -- whatever Mail.app has, this works with
  • Gmail Mailbox Translation - Automatically maps standard names (Trash, Spam, Sent) to Gmail's [Gmail]/... paths
  • Built for AI Workflows - Every command supports --json output designed for AI assistants to read and act on
  • Todoist Integration - Turn any email into a task with mxctl to-todoist (project, priority, due date)
  • Batch Operations with Undo - Process hundreds of emails safely with rollback support
  • Zero Dependencies - Pure Python stdlib, no external packages required
  • Works with Your Existing Setup - Doesn't replace Mail.app, extends it

Installation

# Requires Python 3.10+ and macOS
pip install git+https://github.com/Jscoats/mxctl

# Or with uv (faster)
uv tool install git+https://github.com/Jscoats/mxctl

Then run the setup wizard — it detects your Mail.app accounts, configures Gmail mailbox translation, and optionally connects Todoist:

mxctl init — setup wizard detecting accounts, configuring Gmail, and connecting Todoist

Quick Start

# First time? Set up your default account
mxctl init

# See what's in your inbox
mxctl inbox

# Smart summary of unread emails (concise, one-liner per email)
mxctl summary

# Triage unread emails by urgency
mxctl triage

# Search for messages
mxctl search "project update" --sender

# Mark all unread as read (with undo support!)
mxctl batch-read -m INBOX

# Oops, undo that
mxctl undo

# Create email from template
mxctl draft --to colleague@company.com --template "weekly-update"

# Send email to Todoist as a task
mxctl to-todoist 123 --project Work

Example Output

mxctl inbox

Inbox Overview
------------------------------------------
  iCloud             3 unread   (47 total)
  Work Email         12 unread  (203 total)
  Johnny.Coats84@gmail.com  0 unread  (18 total)
------------------------------------------
  Total              15 unread

mxctl triage

Triage -- 15 Unread Messages
==========================================

[URGENT -- 2]
  #4821  Sarah Johnson       Re: Contract review deadline TODAY
  #4819  boss@company.com    Q4 budget approval needed

[PEOPLE -- 5]
  #4820  mom@gmail.com       Thanksgiving plans?
  #4818  john.smith@work.com Project kickoff Thursday?
  #4817  recruiter@corp.com  Opportunity at TechCorp
  #4815  friend@gmail.com    Weekend hiking trip
  #4814  alice@work.com      Coffee catch-up?

[NOTIFICATIONS -- 8]
  #4816  GitHub              [mxctl] PR #12 merged
  #4813  noreply@bank.com    Statement available
  ... and 6 more

mxctl summary

15 unread -- iCloud + Work Email

* Contract review deadline TODAY -- Sarah Johnson (urgent, reply needed)
* Q4 budget approval -- boss@company.com (action required)
* Thanksgiving plans -- mom@gmail.com (personal, low urgency)
* Project kickoff Thursday -- john.smith@work.com (confirm attendance)
* PR #12 merged -- GitHub notification (no action needed)
* 10 more notifications and newsletters

Command Categories

Setup

  • init - First-time setup wizard (auto-detects Mail accounts, configures default account and optional Todoist token)

Account & Mailbox Management

  • inbox - Overview of all accounts with unread counts
  • accounts - List all mail accounts
  • mailboxes - List mailboxes in an account
  • create-mailbox, delete-mailbox - Manage folders
  • count - Unread message count (for scripting and status bars)
  • empty-trash - Empty trash with confirmation dialog

Message Operations

  • list - List messages with filters (unread, date range)
  • read - Display full message
  • search - Find messages by subject/sender
  • mark-read, mark-unread, flag, unflag - Message actions
  • move, delete - Organize messages
  • junk, not-junk - Mark as spam / restore from spam (moves to INBOX)
  • open - Open message in Mail.app GUI
  • unsubscribe - Unsubscribe from mailing lists via List-Unsubscribe header (supports one-click RFC 8058)
  • attachments, save-attachment - Handle attachments

AI-Ready Features

  • summary - Ultra-concise summaries optimized for AI assistants
  • triage - Smart categorization by urgency (flagged -> people -> notifications)
  • context - Thread messages with parent/child relationships
  • find-related - Discover similar messages
  • process-inbox - Diagnostic inbox categorization

Batch Operations

  • batch-read - Mark all unread as read
  • batch-flag - Flag all from sender
  • batch-move - Move messages by sender
  • batch-delete - Delete messages by sender and/or age
  • undo, undo --list - Rollback batch operations

Analytics & Tools

  • stats - Message statistics for timeframe
  • top-senders - Most frequent senders
  • digest - Grouped unread summary
  • show-flagged - List flagged messages
  • weekly-review - Past 7 days summary
  • clean-newsletters - Archive/delete newsletter subscriptions

System

  • check - Trigger Mail.app to fetch new mail
  • headers - Full email header analysis (SPF, DKIM, DMARC, hop count, return path)
  • rules - List, enable, or disable mail rules

Compose & Templates

  • draft - Create email draft (supports templates)
  • templates list/create/show/delete - Manage email templates
  • reply, forward - Create response drafts
  • thread - Show full conversation thread for a message

Integrations

  • to-todoist - Send email to Todoist as task
  • export - Export messages as markdown (use --to for destination path/directory)

Requirements

  • macOS 12 or later (uses AppleScript to communicate with Mail.app)
  • Python 3.10+
  • Mail.app with at least one configured account
  • Permissions: First run will prompt for Mail.app automation permission in System Settings
  • Note: Mail.app will be launched automatically if it is not already running -- this is normal macOS/AppleScript behavior

Usage Tips

Multi-Account Support

Works with any combination of iCloud, Gmail, Outlook, Exchange, or custom IMAP accounts -- whatever you have configured in Mail.app.

# Commands default to your primary account (set during init)
mxctl list

# Switch accounts with -a
mxctl list -a "Work Email"
mxctl list -a "Personal"

# Commands like inbox, summary, and triage scan ALL accounts automatically
mxctl inbox

Three-tier account resolution: Commands use the first available: (1) explicit -a flag, (2) default account from config, (3) last-used account from state.

Gmail Mailbox Translation

Gmail uses non-standard mailbox names ([Gmail]/Spam instead of Junk, [Gmail]/Sent Mail instead of Sent Messages, etc.). If you tag your Gmail accounts during mxctl init, the CLI auto-translates standard names so you don't have to remember Gmail's conventions.

# These just work -- no need to type [Gmail]/... paths
mxctl list -a "Work Gmail" -m Trash     # -> [Gmail]/Trash
mxctl list -a "Work Gmail" -m Spam      # -> [Gmail]/Spam
mxctl list -a "Work Gmail" -m Sent      # -> [Gmail]/Sent Mail
mxctl list -a "Work Gmail" -m Archive   # -> [Gmail]/All Mail

# iCloud and other accounts pass through unchanged
mxctl list -a "iCloud" -m Trash         # -> Trash (no translation)

Supported translations: Trash, Spam/Junk, Sent/Sent Messages, Archive/All Mail, Drafts, Starred, Important.

Todoist Integration

Turn any email into a Todoist task without leaving the terminal. The task includes the email subject, sender, and a link back to the message.

# Set up during init, or add manually to ~/.config/mxctl/config.json
mxctl init  # step 3 prompts for your Todoist API token

# Send an email to Todoist
mxctl to-todoist 123

# With project and priority
mxctl to-todoist 123 --project "Work" --priority 3

# With a due date (natural language)
mxctl to-todoist 123 --due "next Monday"

To get your token: Todoist Settings -> Integrations -> Developer

Short Message Aliases

Listing commands assign short numbers starting from [1] -- no more copying 5-digit IDs:

mxctl list                    # Shows [1], [2], [3]...
mxctl read 1                  # Read message [1]
mxctl flag 2                  # Flag message [2]
mxctl move 3 --to Archive     # Move message [3]

Aliases update each time you run a listing command (list, inbox, search, triage, summary, etc.). Full message IDs still work if you prefer them. JSON output includes both id (real) and alias (short number).

JSON Output for Automation

# Every command supports --json
mxctl inbox --json | jq '.accounts[0].unread_count'
mxctl search "invoice" --json | jq '.[].subject'

Export Messages

# Export a single message
mxctl export 123 --to ~/Documents/mail/ -a "iCloud"

# Bulk export all messages in a mailbox
mxctl export "Work" --to ~/Documents/mail/ -a "Work Email"

# Bulk export messages after a date
mxctl export "INBOX" --to ~/Documents/mail/ -a "iCloud" --after 2026-01-01

Note: The destination flag is --to (not --dest).

Email Templates

# Create a template
mxctl templates create "meeting-followup" \
  --subject "Re: {original_subject}" \
  --body "Thanks for the meeting today..."

# Use it
mxctl draft --to client@company.com --template "meeting-followup"

Built for AI Workflows

Every command supports --json, making your inbox data available to any AI assistant. Commands like summary, triage, and context are specifically designed to give AI a structured understanding of your inbox in seconds.

With Claude Code

# Just ask Claude to check your mail
"Run mxctl triage and tell me what's urgent"
"Summarize my unread mail and create Todoist tasks for anything that needs action"

With any AI tool

# Pipe structured data to any LLM CLI
mxctl summary --json | llm "What needs my attention?"

# Feed triage results to AI for prioritization
mxctl triage --json | llm "Draft responses for the urgent items"

For scripting and automation

# Unread count for your status bar
mxctl count

# Export to JSON for any workflow
mxctl inbox --json | jq '.accounts[].unread_count'

The CLI is the bridge between Mail.app and whatever tools you use -- AI, scripts, or both.

AI Demos

These demos show how an AI assistant (like Claude Code) uses mxctl to manage your inbox conversationally. You say what you want in plain English, and the AI picks the right commands, checks before acting, and reports back.

Inbox triage and drafting

The AI triages your inbox, marks newsletters as read, flags important messages for follow-up, and drafts a reply to your mom -- all from a single request.

AI assistant triaging inbox, flagging messages, and drafting a reply

Bulk sender cleanup

The AI finds your noisiest senders, dry-runs the deletes so you can see what would be removed, then cleans up 60 marketing emails in seconds. Everything is undoable with mxctl undo.

AI assistant finding top spammy senders and batch-deleting 60 messages

Newsletter unsubscribe

The AI analyzes which newsletters you actually read vs. ignore, then unsubscribes from the ones with 0% open rate while leaving the ones you engage with. One-click unsubscribe when the header supports it, browser fallback when it doesn't.

AI assistant analyzing newsletter read rates and unsubscribing from unread ones

Architecture

Built with modern Python patterns:

  • Zero runtime dependencies (stdlib only)
  • Comprehensive test suite (422 tests)
  • Modular command structure (16 focused modules)
  • AppleScript bridge for Mail.app communication
  • Three-tier account resolution (explicit flag -> config default -> last-used)

See ARCHITECTURE.md for detailed architecture documentation.

Why Not X?

Why not mutt or neomutt? Mutt replaces Mail.app -- you lose native macOS notifications, calendar event detection, FaceTime/iMessage continuity, and Rules. This CLI extends Mail.app rather than replacing it: your mail is still managed natively, but now also scriptable from the terminal.

Why not the Gmail API or Outlook API? Those are per-provider -- separate SDKs, separate auth flows, separate data models. mxctl works with any account configured in Mail.app (iCloud, Gmail, Outlook, Exchange, custom IMAP) through a single unified interface. Add a new account to Mail.app and it just works.

Why not raw AppleScript or Hammerspoon? You could wire this up yourself -- but this gives you 49 structured commands with --json output, batch operations with undo, template support, Todoist integration, and an AI-ready interface, all without writing a single line of AppleScript. The hard parts (field parsing, timeout handling, account resolution, error recovery) are already done.

Contributing

Contributions welcome! See CONTRIBUTING.md for guidelines.

License

MIT License - see LICENSE file for details.

Acknowledgments

Built to automate email workflows without leaving the terminal.

Contact


Like this project? Star it on GitHub and share it with fellow terminal enthusiasts!

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

mxctl-0.3.0.tar.gz (1.7 MB view details)

Uploaded Source

Built Distribution

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

mxctl-0.3.0-py3-none-any.whl (73.0 kB view details)

Uploaded Python 3

File details

Details for the file mxctl-0.3.0.tar.gz.

File metadata

  • Download URL: mxctl-0.3.0.tar.gz
  • Upload date:
  • Size: 1.7 MB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.9.25 {"installer":{"name":"uv","version":"0.9.25","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for mxctl-0.3.0.tar.gz
Algorithm Hash digest
SHA256 0f002129c63f98eef586542404ba61c6ece3b6690e7771e524925b0c05676cc8
MD5 87083b6f7b0abb31b40ea3758b681d58
BLAKE2b-256 61632fef70c4f1cf2f970398b08858c39a052eace73b5f9537577be41d786baf

See more details on using hashes here.

File details

Details for the file mxctl-0.3.0-py3-none-any.whl.

File metadata

  • Download URL: mxctl-0.3.0-py3-none-any.whl
  • Upload date:
  • Size: 73.0 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.9.25 {"installer":{"name":"uv","version":"0.9.25","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for mxctl-0.3.0-py3-none-any.whl
Algorithm Hash digest
SHA256 cf9b9e92465442ff21b529a4ea8cf1da9fb59ba9add513b31f9048beb6dc75fa
MD5 15c588ee4cdf67477f8676fa79dc7b51
BLAKE2b-256 86561bf5efb8596ae546d2f34582631c9940f50e36097191c243b545581935c7

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