Boogie
jmapcli · JMAP from the terminal
01.
v1.7.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 install boogie-digital/boogie/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 install boogie-digital/boogie/jmapcli — a single signed binary, no dependencies to compile. Upgrades via brew upgrade jmapcli.
— 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
# OAuth — no app password to create, copy, or rotate. # Your browser opens once; the token lands in the Keychain under jmap-oauth. $ jmapcli login --url https://api.fastmail.com --username you@fastmail.com --oauth # Stalwart — or any server that speaks RFC 8252 — same flag. $ jmapcli login --url https://mail.example.com --username you@example.com --oauth
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
identity
List every sendable identity across your accounts, and set the display name the server shows for one.
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)
attachments
List an email's attachments, or save them to a folder
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.7.0
Reply-To and display names: --reply-to sets a message's Reply-To on send, draft, reply and forward, staged drafts included, and replaces the sending identity's own Reply-To for that message. --to, --cc and --bcc now take Name <addr>, so "Jane Doe <jane@example.com>" goes out as a name and an address; a bare address is unchanged. — 2026-10-15
v1.6.0
Attachments: jmapcli attachments lists an email's attachments (index, name, type, size) and --save writes one or all of them to a folder, refusing to overwrite an existing file unless --force. The MCP server gains the same surface: mail.read now reports attachment metadata and the new mail.save_attachment tool writes one attachment to a local directory. — 2026-09-28
v1.5.0
OAuth login: jmapcli login --oauth is a first-class alternative to --password and --token, using loopback PKCE (RFC 8252) and dynamic client registration (RFC 7591). The token is held in the macOS Keychain under jmap-oauth. Verified against Fastmail and Stalwart; app-password and token login are unchanged. — 2026-09-01
v1.4.6
identity list (id, display name, email) and identity set --name to write the server Identity display name. Role aliases no longer have to be renamed via raw Identity/set. — 2026-08-24
v1.4.5
JMAP core from Boogie 1.4.5: Stalwart aliases now appear as send-as identities (accounts identities and send --from); send, contacts, and calendar library fixes from the 1.4.5 cycle. — 2026-08-24
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