Boogie
jmapcli · JMAP from the terminal
01.
v1.4.0 · macOS
A native JMAP command-line client.

jmapcli — JMAP from the
command line.

jmapcli talks JMAP to Stalwart (and any JMAP server) straight from your terminal — send, read, search, sync, move and manage mail. Native Swift, credentials in the macOS Keychain, no telemetry.

$ brew tap jasonhollis/boogie && brew install jmapcli $ jmapcli login # stored in the macOS Keychain $ jmapcli send --to you@example.com --subject "Hi" --body "From the CLI"
02.
— Section 2
Install
Homebrew

One line. Signed.

2.1
Homebrew tap
brew tap jasonhollis/boogie && brew install jmapcli — a single signed binary, no dependencies to compile. Upgrades via brew upgrade.
— macOS
Apple silicon
2.2
Keychain auth
Credentials are read from the macOS login Keychain by JMAP identity. Nothing in dotfiles, nothing in env vars.
— security
find-generic
2.3
No telemetry
The tool phones home to nothing. The only network it touches is your JMAP server.
— zero
SDKs
03.
— Section 3
Credentials
macOS Keychain

Your password never touches disk.

jmapcli keeps no secret on disk. Your password (or bearer token) lives in the macOS login Keychain; the account file — ~/.config/jmapcli/config.json, mode 0600 — holds only the server URL, your username and a default flag. New to the Keychain from the terminal? The whole workflow is three built-in security commands.

1 · Store a secret — and read it back. macOS prompts for the value, so the secret is never typed on the command line, shown on screen, or written to your shell history.

# store it — you’re prompted for the secret, nothing echoes $ security add-generic-password -U -s jmap-password -a you@example.com -l "Boogie JMAP password" -w # read it back at any time $ security find-generic-password -s jmap-password -a you@example.com -w # remove it $ security delete-generic-password -s jmap-password -a you@example.com
-s
service — the name jmapcli looks up: jmap-password for a password, or jmap-token for a bearer token (e.g. Fastmail); set only the one you use.
-a
account — your email address (the same username you give jmapcli).
-l
label — what you’ll see in the Keychain Access app; -U updates an existing item in place.
-w
with no value — prompt for the secret. This is the part that keeps it out of ~/.zsh_history and the process list.

2 · Point jmapcli at your server. Register the account once; the secret is pulled straight from the Keychain item you just stored, so it never appears on the command line.

$ jmapcli login --url https://mail.example.com --username you@example.com \ --password "$(security find-generic-password -s jmap-password -a you@example.com -w)" # token auth: swap --password for --token (and store under jmap-token above) $ jmapcli list # from here on, every command reads the Keychain for you
3.1
Nothing in your history, logs — or an AI’s context
Once the secret is in the Keychain, everyday commands (list, send, draft, sync) never carry a password, so it can’t be captured in ~/.zsh_history, a process listing or CI logs. That is what makes jmapcli safe to drive from terminal-AI tools — an agent is handed a JMAP client, never your secret.
— zsh
CI · agents
3.2
Rotate or sign out
Changed your password? Re-run the security add-generic-password … -w line — -U overwrites it in place. To remove an account, jmapcli logout deletes both the config entry and the Keychain secret in one step (--all clears every account).
— rotate
logout
3.3
Per-Mac by design
The login Keychain isn’t synced through iCloud — a deliberate boundary, so your password never leaves the Mac you typed it on. Repeat the security step once per Mac; config.json can ride a synced home folder because it carries no secret.
— per
machine
04.
— Section 4
Commands
One verb each

Everything is a subcommand.

login
Store credentials for a JMAP server in the Keychain
discover
Discover the JMAP session from the server
accounts
List configured accounts and every sendable identity
mailboxes
List all mailboxes
list
List emails in a mailbox
search
Search mail by text, subject, from, to, or body (server-side)
read
Display full email content (Markdown-rendered)
send
Send an email via JMAP Submission, from any sendable identity (--from)
reply
Reply to a message by ID, threaded and quoting the original
forward
Forward a message by ID, re-attaching its attachments
draft
Stage an email in Drafts without sending
move · trash · delete
Move into a mailbox, move to Trash, or permanently destroy by ID
sync
Perform a delta sync and show what changed
contacts
JSContact contacts: list, add, edit, remove, import aliases
calendar
List calendars and events, write events, set calendar colours
sieve
List, fetch, validate and push server-side Sieve filters
alias
Manage recipient aliases
mcp
Run as a Model Context Protocol server for AI tooling
logout
Remove stored credentials
05.
— Section 5
Changelog
Every release

What’s new.

v1.4.0
Full-text search (text, subject, from, to, body); threaded reply and forward; draft — stage in Drafts without sending; multi-account: accounts and identities, and send/reply/forward as any sendable identity via --from; contacts import-aliases; faster multi-recipient sends. — 2026-06-24
v1.3.1
Calendar event write (add, edit, remove); offline-first hardening. — 2026-06-20
v1.3.0
JSContact contacts — read and write, with a lossless round-trip. — 2026-06-15
v1.2.0
Sieve filters — list, get, validate, and push server-side mail rules. — 2026-06-12
v1.1.0
Calendar — list calendars and events, set colours. — 2026-06-11
v1.0.0
Initial release — send, read, search, sync, and triage mail from the terminal, credentials in the macOS Keychain. — 2026-06-06
— Part of Boogie

Want the full app?

jmapcli is free
the native Mail + Calendar app is on the App Store
KTP Digital Pty Ltd · ABN 85 648 755 980