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.
The four primitives
Section titled “The four primitives”--format <FORMAT>—table(default) for humans,json|jsonl|csv|idsfor machines. The generated CLI reference lists the exact values per command.--dry-run— preview affected ids/labels/threads without mutating provider state. Implemented by core mail mutations and selected lifecycle commands.--yes— skip confirmation prompts on commands that ask before mutating. Required when stdin is not a TTY.- stdin IDs — pass message IDs on stdin, one per line. Equivalent to listing them as positional args. Available on most mutations; not all.
Reads (commands that return data)
Section titled “Reads (commands that return data)”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.
| Command | Returns | Pipeable formats |
|---|---|---|
mxr search | envelopes (matching messages) | json, jsonl, csv, ids, table |
mxr count | scalar count | json, jsonl, table/text |
mxr cat | full message body | json, jsonl, table (and the --view modes for body rendering) |
mxr thread | thread + messages | json, jsonl, table |
mxr headers | RFC 822 headers | json, jsonl, table |
mxr labels | labels with counts | json, jsonl, csv, ids, table |
mxr saved list / mxr saved run <name> | saved searches / matches | json, jsonl, csv, ids, table |
mxr drafts list | drafts | json, jsonl, csv, ids, table |
mxr replies list | reply-later queue | json, jsonl, table |
mxr snippets list | snippets | json, jsonl, table |
mxr storage / mxr stale / mxr response-time / mxr contacts / mxr subscriptions / mxr wrapped | analytics summaries | json, jsonl, csv, table |
mxr status | daemon health | json, jsonl, table |
mxr sync --status | sync state per account | json, jsonl, table |
mxr events / mxr history / mxr logs | streaming event/log records | json, jsonl, csv, table |
mxr notify | unread summary | json, jsonl, text |
mxr accounts | runtime account inventory | json, jsonl, csv, ids, table |
mxr config show | resolved config | json, jsonl, csv, ids, table |
mxr config get | one config value | text |
mxr attachments list | attachments for a message | table/text |
mxr export | thread export | markdown, json, mbox, llm |
Mutations (destructive or stateful)
Section titled “Mutations (destructive or stateful)”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.
| Command | Targets | --dry-run | --search | stdin IDs |
|---|---|---|---|---|
mxr archive | message(s) | ✓ | ✓ | ✓ |
mxr read-archive | message(s) | ✓ | ✓ | ✓ |
mxr trash | message(s) | ✓ | ✓ | ✓ |
mxr spam | message(s) | ✓ | ✓ | ✓ |
mxr star / mxr unstar | message(s) | ✓ | ✓ | ✓ |
mxr read / mxr unread | message(s) | ✓ | ✓ | ✓ |
mxr label NAME / mxr unlabel NAME | message(s) | ✓ | ✓ | ✓ |
mxr move LABEL | message(s) | ✓ | ✓ | ✓ |
mxr snooze | message(s) | ✓ | ✓ | ✓ |
mxr unsnooze | message(s) or --all | ✓ | — | ✓ |
mxr unsubscribe | message(s) | ✓ | ✓ | ✓ |
mxr undo MUTATION_ID | one mutation | ✓ | — | — |
mxr send DRAFT_ID | a draft | ✓ (--at conflicts) | — | — |
mxr unsend DRAFT_ID | a scheduled send | — | — | — |
mxr drafts discard | draft(s) | — | — | — |
mxr rules dry-run | a rule | n/a (always dry-run) | — | — |
What’s not scriptable
Section titled “What’s not scriptable”mxr(no args) — launches the TUI. There is no--format jsonfor “the TUI.”mxr daemon— is a long-running process; structured output is onmxr status/mxr events/mxr logs.mxr compose/mxr reply/mxr reply-all/mxr forward— open$EDITORby default. For scripts, use--body,--body-stdin,--yes, and--dry-run, or use the HTTP bridge’s compose endpoints.mxr setup— interactive (with--demofor unattended fake-provider).mxr accounts add— interactive wizard by default, but goes non-interactive when you pass enough flags AND setMXR_IMAP_PASSWORD/MXR_SMTP_PASSWORD/MXR_GMAIL_CLIENT_SECRETenv vars.
Safe agent loop
Section titled “Safe agent loop”For agents driving mutations, follow this pattern:
1. SEARCH — mxr search '<query>' --format json2. CONFIRM — surface the candidates to the user3. DRY-RUN — mxr <verb> --search '<query>' --dry-run4. APPROVE — user signs off on the diff5. MUTATE — mxr <verb> --search '<query>' --yes6. RECORD — capture the printed mutation_id; offer mxr undo within ~60s7. VERIFY — mxr history --category mutation --limit 1 --format jsonThe 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.
Idempotency
Section titled “Idempotency”mxr archive/read-archive/trash/spam/star/unstar/read/unread/label/unlabel/move/snooze/unsnoozeare idempotent — re-running with the same target IDs leaves state unchanged after the first call.mxr send DRAFT_IDis not idempotent — calling twice will send twice (the daemon schedules the second send). Always checkmxr drafts listfirst.mxr unsubscribemay hit a provider URL once; re-running on an already-unsubscribed message is harmless but emits no useful new state.mxr undo MUTATION_IDworks within a 60-second window; after that it returns an error.
See also
Section titled “See also”- CLI reference — every command, every flag, auto-generated
- JSON output schemas — field names you can pipe into
jq - Recipes — copy-pasteable pipelines using these primitives
- For agents — patterns and safe defaults when an LLM is driving