![]() |
![]() |
![]() |
![]() |
A keyboard-driven terminal email client built in Rust. Reads and sends mail over standard IMAP/SMTP with real push delivery via RFC 2177 IDLE, stores everything locally in SQLite, and never opens a browser - HTML renders inline, OAuth2 flows run inside the TUI itself.
- Real RFC 2177 IDLE push - new mail appears instantly without polling
- IMAP implicit TLS, STARTTLS, and plain; SMTP with the same three modes
- Compose, reply, reply-all, and forward with quoted bodies and correct threading headers (
In-Reply-To,References) - Full-text search across all cached messages via SQLite FTS5
- Move to folder, delete to Trash, flag, mark read/unread
- AI reply drafting in compose (
Ctrl-G) via a local Claude / Gemini / Codex CLI - generates from the thread, or expands the notes you typed;Ctrl-Tadds an instruction/context prompt first. Files the model generates (txt/csv/xlsx/pdf/images/zip, ...) are auto-attached to the draft
Reading
- Three-pane layout: account/folder sidebar, message list, reader
- Attachment indicator: messages with attachments are marked π in the message list (every folder, including Sent), detected at sync time without fetching the body
- Conversation thread view (
t): see the whole conversation - including replies in other folders (e.g. Sent) - grouped strictly by RFC 822 references (Message-ID / In-Reply-To / References), never by subject, so unrelated same-subject mail is not lumped together; messages with attachments are marked π and you can open the attachment viewer for the selected one witha - HTML bodies rendered inline via
html2text- no browser needed - Attachment viewer (centered modal): images rendered inline as truecolor half-blocks (works over SSH, no graphics protocol needed), PDF and Word (.docx) shown as extracted text, text/code/markdown inline; other binaries identified by MIME type and saved to disk
- Resizable, draggable UI: drag pane dividers to resize the account/inbox/reading panes; drag the compose window to move/resize it - all remembered across restarts
- Auto mark-read after a configurable dwell time (default 3 seconds)
- Preview snippets in the message list (optional)
Account setup
- Interactive onboarding modal with provider auto-fill for Gmail, Outlook, Fastmail, Yahoo, and iCloud
- Password auth or full OAuth2 (PKCE) - switch auth type inside the modal with
β/β - OAuth2 for Gmail, Microsoft 365, Yahoo Mail, and any custom provider; auth URL opens automatically, token exchange and refresh happen in the background
- "Keep copy on server" per-account toggle (default on). Turn it off for POP3-style behavior: a message is removed from the server once its body is downloaded, while the local copy stays in the client
- Multi-account support with an account manager (
m)
AI agent integration
- MCP server (
imt mcp) - expose your inbox as tools to any MCP-compatible AI agent - 11 tools covering the full read/write lifecycle: list accounts/folders/messages, search, read, send, reply, mark read, flag, move, delete (send/reply accept file attachments)
- One-line Claude Desktop config; works with any MCP client
Other
- SQLite local cache with WAL mode - fast reads, no corruption on crash
- Configurable auto-refresh interval; IDLE push always active
- Toast notifications for async events (sync errors, account added, mail moved)
- File picker for attaching files in compose (
Ctrl-A) - Theming support
- Single static binary, no runtime dependencies
Requires Rust 1.80+.
git clone https://github.com/a2-stuff/Indicium-Mail-TUI.git
cd Indicium-Mail-TUI
cargo install --path crates/imtThe imt binary lands at ~/.cargo/bin/imt.
# Add your first account interactively
imt add-account
# Or non-interactively (password from $IMT_PASSWORD)
IMT_PASSWORD='secret' imt add-account \
--email you@example.com \
--imap-host imap.example.com --imap-port 993 --imap-tls implicit \
--smtp-host smtp.example.com --smtp-port 465 --smtp-tls implicit
# Launch the TUI
imt
# Mock mode (no network, sample inbox)
imt run --mockimt run TUI (default)
imt run [--mock] run TUI explicitly
imt add-account [flags] add account; password from $IMT_PASSWORD or prompt
imt list-accounts show configured accounts
imt delete-account <uuid> remove an account
imt --help full flag reference
Global flags: --db <path>, --config <path>, --log-file <path>.
| Key | Action |
|---|---|
Tab / Shift-Tab |
cycle focus |
j k / arrows |
move within focused pane |
Enter |
open message |
Esc |
back |
c |
compose |
r / R |
reply / reply-all |
f |
forward |
t |
view conversation thread |
s |
toggle flag |
u |
toggle read/unread |
m |
account manager |
F10 |
open the menu bar (arrows/Tab to move, Enter to run, Esc to exit) |
d |
delete (moves to Trash) |
E |
empty Trash (only in Trash folder) |
v |
move to folder |
o |
view HTML body inline (reader pane) |
a |
attachment viewer (reader pane) |
A |
add account (onboarding modal) |
F5 / Ctrl-R |
refresh / resync current folder |
/ |
search |
i |
info |
? |
help overlay |
q |
quit |
In compose: Tab next field, Ctrl-G AI reply, Ctrl-T AI reply with an instruction/context prompt, Ctrl-A file picker, Ctrl-S send, Ctrl-D save draft, Esc cancel. Drag the title bar to move the window and the bottom-right corner to resize it; the body word-wraps to the window width.
Mouse support is enabled, and the whole interface is clickable:
- Menu bar: click a top menu to open its dropdown (or run it directly); click a dropdown item to run it; click away to close.
- Sidebar: click an account to expand/collapse it, click a folder to switch to it.
- Message list: click an email to open it in the reading pane.
- Scroll wheel scrolls whichever pane is under the cursor.
- Resize/move: drag the dividers between the account, inbox, and reading panes to resize them; drag the compose window's title bar to move it and its bottom-right corner to resize it. Pane sizes and the compose window's position/size persist across restarts.
In most terminals, hold Shift while dragging to do native text selection / copy-paste.
In the compose window, press Ctrl-G to draft a reply with a local AI CLI:
- With an empty body it generates a reply from the email/thread.
- If you have typed a few notes (e.g. a time and place), it expands and polishes them into a full reply using the thread context - your notes are woven in, not duplicated.
Pick the provider and model in Settings (, β AI reply provider / AI model):
- Claude (
claude), Gemini (gemini), or Codex (codex). The chosen provider's CLI must be installed and onPATH. - Model is provider-specific; leave it empty for the CLI's default. The default is Claude with the
sonnetalias, which always tracks the latest Sonnet.
Generation runs in the background and is inserted at the cursor when ready.
Press Ctrl-T instead to open an Instruction or Context dialog: type an
extra instruction (e.g. "keep it short and decline politely") and press Enter -
the reply uses the email/thread plus your typed notes plus that instruction.
(Ctrl-Shift-G does the same on terminals with the enhanced keyboard protocol -
kitty, foot, WezTerm, ghostty, recent xterm - but Ctrl-T works everywhere.)
If the instruction asks the AI to create a file (a text/CSV/Excel/PDF/image/ ZIP, etc.), it is generated in the background and attached to the draft automatically - e.g. "draft a summary and attach it as a CSV", or "generate a chart of these numbers as a PNG and attach it". The status line shows how many files were attached. (Requires the Claude provider, which is run with its file tools enabled in an isolated working directory.)
In attachment viewer: j/k or arrows scroll, Enter or v view inline, s save to disk, Esc/q close.
In HTML viewer: j/k or arrows scroll, o/Esc/q close.
When adding an account via the onboarding modal (A), switch Auth type to < OAuth2 > using the left/right arrow keys. Enter your Client ID (and optionally Client Secret). Tab to the Auth Code field - the browser opens automatically with the authorization URL. Approve access, copy the ?code=... value from the redirect URL, paste it, then Ctrl-S to save.
The app handles token refresh automatically before each IMAP connection.
Enable IMAP in Gmail settings and create an OAuth2 credential in the Google Cloud Console (type: Desktop App). Use https://mail.google.com/ as the scope.
Register an app in Azure Active Directory with https://outlook.office365.com/IMAP.AccessAsUser.All and https://outlook.office365.com/SMTP.Send permissions.
Create an app at the Yahoo Developer Console. Use mail-w as the scope.
| What | Where |
|---|---|
| Database | ~/.local/share/indicium-mail-tui/imt.sqlite3 |
| Secrets (file fallback) | ~/.local/share/indicium-mail-tui/secrets/ (0600) |
| Config | ~/.config/indicium-mail-tui/config.toml |
| Logs | ~/.local/share/indicium-mail-tui/imt.log |
Set RUST_LOG=imt_sync=debug,imt_net=debug for protocol-level traces.
imt mcp starts the MCP server on stdin/stdout so any MCP-compatible AI agent can read and manage your email via tool calls.
Add to claude_desktop_config.json (macOS: ~/Library/Application Support/Claude/claude_desktop_config.json):
{
"mcpServers": {
"indicium-mail": {
"command": "imt",
"args": ["mcp"]
}
}
}Restart Claude Desktop - the mail tools appear automatically.
| Tool | What it does |
|---|---|
list_accounts |
List configured accounts |
list_folders |
List folders for an account |
list_messages |
List messages in a folder (paginated) |
read_message |
Fetch full body, auto-downloads from IMAP if not cached |
search |
Full-text search via SQLite FTS5 |
send |
Send a new email (supports an attachments list of file paths) |
reply |
Reply or reply-all to a message (supports an attachments list of file paths) |
mark_read |
Mark read or unread |
toggle_flag |
Star / unstar a message |
move_message |
Move to another folder |
delete_message |
Move to Trash |
See MCP_DOCUMENTATION.md for the full protocol reference, parameter schemas, and example agent workflows.
See DOCUMENTATION.md for architecture and the CHANGELOG for release notes.
MIT - see LICENSE.




