Skip to content

Automation contract

mxr is built to be scripted, but not every command supports every automation primitive. This page is the contract — the things you can rely on when piping mxr into a shell pipeline or an LLM agent.

When in doubt, the auto-generated CLI reference is the source of truth. The tables below summarise the patterns.

  1. --format <FORMAT>table (default) for humans, json|jsonl|csv|ids for machines. The generated CLI reference lists the exact values per command.
  2. --dry-run — preview affected ids/labels/threads without mutating provider state. Implemented by core mail mutations and selected lifecycle commands.
  3. --yes — skip confirmation prompts on commands that ask before mutating. Required when stdin is not a TTY.
  4. stdin IDs — pass message IDs on stdin, one per line. Equivalent to listing them as positional args. Available on most mutations; not all.

These are the common automation-oriented read surfaces. Exact formats live in the generated CLI reference. JSON shapes per command live in JSON output schemas.

CommandReturnsPipeable formats
mxr searchenvelopes (matching messages)json, jsonl, csv, ids, table
mxr countscalar countjson, jsonl, table/text
mxr catfull message bodyjson, jsonl, table (and the --view modes for body rendering)
mxr threadthread + messagesjson, jsonl, table
mxr headersRFC 822 headersjson, jsonl, table
mxr labelslabels with countsjson, jsonl, csv, ids, table
mxr saved list / mxr saved run <name>saved searches / matchesjson, jsonl, csv, ids, table
mxr drafts listdraftsjson, jsonl, csv, ids, table
mxr replies listreply-later queuejson, jsonl, table
mxr snippets listsnippetsjson, jsonl, table
mxr storage / mxr stale / mxr response-time / mxr contacts / mxr subscriptions / mxr wrappedanalytics summariesjson, jsonl, csv, table
mxr statusdaemon healthjson, jsonl, table
mxr sync --statussync state per accountjson, jsonl, table
mxr events / mxr history / mxr logsstreaming event/log recordsjson, jsonl, csv, table
mxr notifyunread summaryjson, jsonl, text
mxr accountsruntime account inventoryjson, jsonl, csv, ids, table
mxr config showresolved configjson, jsonl, csv, ids, table
mxr config getone config valuetext
mxr attachments listattachments for a messagetable/text
mxr exportthread exportmarkdown, json, mbox, llm

Core mail mutations accept either explicit message IDs as positional args, --search QUERY for batch ops, or piped IDs on stdin. Use the generated CLI reference for non-mail lifecycle commands.

CommandTargets--dry-run--searchstdin IDs
mxr archivemessage(s)
mxr read-archivemessage(s)
mxr trashmessage(s)
mxr spammessage(s)
mxr star / mxr unstarmessage(s)
mxr read / mxr unreadmessage(s)
mxr label NAME / mxr unlabel NAMEmessage(s)
mxr move LABELmessage(s)
mxr snoozemessage(s)
mxr unsnoozemessage(s) or --all
mxr unsubscribemessage(s)
mxr undo MUTATION_IDone mutation
mxr send DRAFT_IDa draft✓ (--at conflicts)
mxr unsend DRAFT_IDa scheduled send
mxr drafts discarddraft(s)
mxr rules dry-runa rulen/a (always dry-run)
  • mxr (no args) — launches the TUI. There is no --format json for “the TUI.”
  • mxr daemon — is a long-running process; structured output is on mxr status / mxr events / mxr logs.
  • mxr compose / mxr reply / mxr reply-all / mxr forward — open $EDITOR by default. For scripts, use --body, --body-stdin, --yes, and --dry-run, or use the HTTP bridge’s compose endpoints.
  • mxr setup — interactive (with --demo for unattended fake-provider).
  • mxr accounts add — interactive wizard by default, but goes non-interactive when you pass enough flags AND set MXR_IMAP_PASSWORD / MXR_SMTP_PASSWORD / MXR_GMAIL_CLIENT_SECRET env vars.

For agents driving mutations, follow this pattern:

1. SEARCH — mxr search '<query>' --format json
2. CONFIRM — surface the candidates to the user
3. DRY-RUN — mxr <verb> --search '<query>' --dry-run
4. APPROVE — user signs off on the diff
5. MUTATE — mxr <verb> --search '<query>' --yes
6. RECORD — capture the printed mutation_id; offer mxr undo within ~60s
7. VERIFY — mxr history --category mutation --limit 1 --format json

The loop is the same whether the agent is claude, cursor, aider, or a hand-rolled curl-and-jq script. The contract above guarantees every step is composable.

  • mxr archive / read-archive / trash / spam / star / unstar / read / unread / label / unlabel / move / snooze / unsnooze are idempotent — re-running with the same target IDs leaves state unchanged after the first call.
  • mxr send DRAFT_ID is not idempotent — calling twice will send twice (the daemon schedules the second send). Always check mxr drafts list first.
  • mxr unsubscribe may hit a provider URL once; re-running on an already-unsubscribed message is harmless but emits no useful new state.
  • mxr undo MUTATION_ID works within a 60-second window; after that it returns an error.