sandesh संदेश
mail & chat

sandesh

संदेश · “message”

Gmail and Google Chat without leaving the terminal, with an agent that can read, draft and send for you.

v0.1.0 latest1 releases4 decisions recordedMIT license

Sandesh (संदेश) is Sanskrit for "message": the thing carried from one person to another, whichever road it took.

Checking mail and Google Chat usually means a browser tab: a context switch, a mouse, and a page that pulls you into everything else. sandesh keeps a local copy of both, puts the unread counts in your tmux status line and opens the full screen with prefix e, laid out like sangam. It runs like lazygit: type sandesh in any terminal. And because it ships an MCP server, you can tell Claude "read today's mails" or "mail Priya that Thursday is fine".

#Features

#Install

Requirements: Linux, a Google account (Google Chat needs a Google Workspace account; a personal Gmail gets mail only). Optional: tmux for the popup and status line, secret-tool (libsecret) for the keyring, notify-send for notifications, fzf for the label picker, wl-copy or xclip for copying links, Claude Code for the AI keys.

With kosha:

kosha install sandesh

Or with the installer, which downloads the release binary into ~/.local/share/sandesh (or builds it with Go) and links ~/.local/bin/sandesh:

curl -fsSL https://raw.githubusercontent.com/Theyashsawarkar/sandesh/main/install.sh | bash

Or from source (Go 1.26+):

git clone https://github.com/Theyashsawarkar/sandesh && cd sandesh
make && sudo make install          # or: make install PREFIX=~/.local

Arch: cd packaging/arch && makepkg -si.

Update by running the installer again; install.sh --uninstall removes it.

#Getting started

Try it first on made-up mail, no Google needed:

sandesh demo

Then just run sandesh (or press prefix e in tmux). With no credentials yet it opens a setup screen in three steps. 1, the OAuth client: it asks where your client file is (Tab completes the path, Enter on a folder opens it), checks it, and copies it into ~/.config/sandesh/client.json, so the download can be deleted; a pasted JSON or a typed client ID and secret work too. 2, sign in: Enter opens Google's sign-in in your browser (Tab switches between a work account and a personal Gmail). 3, the first sync, with a progress bar, and then the inbox.

KeySetup screen
TabSwitch to the next path completion (Enter on a folder opens it)
EnterSave the OAuth client file at this path, or start the Google sign-in
Ctrl+tEnter the OAuth client ID and secret by hand instead
Ctrl+oOpen the Google Cloud console credentials page
Ctrl+gShow the steps for creating the OAuth client
Ctrl+dStart the demo on example mail instead
nRename the account before signing in
cPick a different OAuth client file
yCopy the sign-in link

To use your own mail, sandesh needs an OAuth client of your own (Google doesn't hand out one for everyone's terminal tools). This takes about five minutes, once:

  1. Open the Google Cloud console, create a project (any name), and enable the Gmail API, the Google Chat API and the People API (APIs & Services → Library).
  2. OAuth consent screen: for a Workspace account choose Internal (no Google review needed; your admin may have to allow it). For a personal Gmail choose External, add yourself as a test user, then Publish it, or Google signs you out every 7 days.
  3. Google Chat API → Configuration: fill in an app name, an avatar URL and a description, and turn off "interactive features". Chat's API wants this even though sandesh only acts as you.
  4. Credentials → Create credentials → OAuth client ID → Desktop app, and download the JSON.

Then either run sandesh and follow the setup screen, or do the same from the command line:

sandesh auth setup ~/Downloads/client_secret_*.json
sandesh auth add work                  # opens Google's sign-in page
sandesh auth add personal --no-chat    # a personal Gmail: mail only
sandesh service install                # background sync and notifications
claude mcp add --scope user sandesh -- sandesh mcp
sandesh doctor                         # checks all of it

sandesh opens the screen.

#The screen

Sections on top, the list on the left, the selected thread or space on the right, keys at the bottom.

#Keys

The screen opens in normal mode: letters are commands. /, s and @ switch to typing; Enter or Esc switches back. ? shows every key.

Move and find

KeyDoes
j / kMove to the next / previous thread or space
g / GJump to the first / last
h / lSwitch to the previous / next section
TabSwitch between mail and chat
1 / 2Focus the list / the preview
EnterOpen the thread or space (marks it read)
J / KScroll the preview down / up
EToggle every message in the thread open / folded
QToggle the quoted text in the thread
oOpen a link from the preview by its number
Ctrl+d / Ctrl+uScroll the preview half a page down / up
/Search the list as you type (local filter)
sSearch Gmail on the server (Gmail search syntax)
WSave the current search as a section
XRemove the current saved-search section
uSwitch account (all, then each one)

Act

KeyDoes
cCreate a new mail, or a new message in this space (compose)
rSend a reply to the thread (in chat: in the latest thread)
aSend a reply to everyone on the thread (reply all)
fSend the thread on to someone else (forward)
eMove the thread out of the inbox (archive; or every selected one)
dMove the thread to the trash (or every selected one)
mSet the thread as read (mark read; or every selected one)
MSet the thread as unread (mark unread; or every selected one)
*Toggle the star on the thread
LAdd or remove a label (fzf picker)
xSelect the thread for e / d / m / M
iStart typing a chat message right in the preview
vSave the attachments to ~/Downloads/sandesh (picker when several)
VOpen the attachments (saved to ~/Downloads/sandesh first)
USend a file into this chat space (upload; fzf picker)
zCancel the last change, or a send in its undo window (undo)
AShow the sends an agent is waiting on you to approve
RReload mail and chat from Google now (sync)
bOpen the thread or space in the browser
yCopy the link to the thread or space

Claude

KeyDoes
@Start a question to Claude, with this thread or space as context (ask)
SShow Claude's summary of the thread or the space's recent messages
DCreate a reply draft with Claude, then edit it in your editor
TShow today's digest: what came in and what needs you

Other

KeyDoes
?Show every key
q / EscGo back, then quit

Reviewing your message (after the editor) and approving an agent's (A, or the popup):

KeyReviewApprove
ySend the message you wrote (after the undo window)Send the message an agent wrote (after the undo window)
eEdit the message again in your editorEdit the agent's message in your editor first
+Add a file to the message (attach; fzf picker)
-Remove the last file added to the message
dSave the message as a Gmail draft instead of sending
nDelete the message you wrote (discard)Cancel the agent's message (decline; nothing is sent)
qLeave the agent's message waiting and go back

#Files

#Claude and other agents

sandesh doesn't embed a model. It offers tools over MCP, and whichever agent you use calls them. Add it once and every Claude Code session can use it:

claude mcp add --scope user sandesh -- sandesh mcp

Then ask in plain words:

you     read today's mails
claude  → digest → mail_read
        3 new. Priya needs a yes/no on Thursday's release by end of day.
        Arjun asked for a review on #412. Staging DB credentials rotate at 18:00.

you     tell priya we're good for thursday, cc arjun
claude  → contacts_find → mail_send
        (a popup shows the mail; you press y)
        Sent.
ToolDoes
mail_listThreads in a section (default Inbox), today, unreadreads
mail_searchGmail search syntax, or the local copy offlinereads
mail_readA whole thread as text; attachments listed, not openedreads
mail_modifyArchive, unarchive, trash, untrash, mark read/unread, starchanges, undoable
mail_draftSave a Gmail draft (replies fill in recipients and quoting)changes
mail_sendSend a mail or a replyasks you
chat_spacesSpaces and DMs with unread countsreads
chat_readA space's recent messagesreads
chat_sendPost in a space, DM or threadasks you
contacts_findA name to an address you've written toreads
digestToday's mail and unread chat, ready to summarisereads

The @, S, D and T keys in the screen run claude -p (your ai_command) with the same server, so they use your Claude login. No API key. Any other MCP client works the same way: the server is sandesh mcp on stdio.

#Sending safely

Mail is outward-facing and hard to take back, and the mail an agent reads is written by strangers. So:

#Command line

Every command has --help; the read commands take --json.

CommandDoes
sandeshThe screen
sandesh mail list [--today] [--unread] [--section S]Threads (ls)
sandesh mail read <thread> [--mark-read]A whole thread as text
sandesh mail search <query> [--local]Gmail search syntax; --local searches the local copy
sandesh mail send --to A --subject S [--reply THREAD [--all]]Send; the body from stdin or $EDITOR; waits the undo window (Ctrl+c cancels)
sandesh mail archive|unarchive|trash|untrash|mark-read|unread|star|unstar <thread>...Change threads
sandesh chat list [--unread]Spaces and DMs
sandesh chat read <space> [--mark-read]A space's recent messages (<space> is its id or part of its name)
sandesh chat send <space> <text> [--thread T]Post in a space or DM
sandesh status [--json|--tmux]Unread counts
sandesh syncSync every account now
sandesh outbox / outbox cancel <id>Sends of the last week; stop one in its undo window
sandesh approve [id]Answer an agent's send that waits for you
sandesh auth setup <client.json>Install your OAuth client
sandesh auth add <name> [--email E] [--no-chat]Sign in a Google account
sandesh auth list / auth remove <name>The accounts
sandesh mcpThe MCP server (stdio)
sandesh daemonThe background sync, in the foreground
sandesh service install|remove|statusThe background sync as a systemd user unit
sandesh configPrint an example config file
sandesh demoThe screen on example mail, with a fake Google inside the process
sandesh doctorCheck the setup

Options: --account NAME on most commands (default: the first account), -h, -V.

#Using it inside tmux

sandesh is a command first; the tmux plugin adds a key and a status segment. With TPM:

set -g @plugin 'Theyashsawarkar/sandesh'
set -g status-right '#{sandesh_status} %H:%M'   # 󰇮 3  󰭹 2

prefix e opens sandesh in a popup. If sandesh isn't on your PATH, the plugin builds its own copy with Go the first time.

OptionDefault
@sandesh-keyeKey after the prefix; none binds nothing
@sandesh-width / @sandesh-height90% / 88%Popup size
@sandesh-title sandesh Popup title
@sandesh-launcherYour own popup command, run with display-popup's arguments appended (e.g. "~/.tmux/scripts/popup.sh '#{client_name}'")

The key carries a bind -N note, and scripts/keybindings lists the keys inside the screen as # keybind: lines, so both show up in tmux's prefix ? and in Vayu's keybinding search.

#Configuration

~/.config/sandesh/config.toml; every line is optional. sandesh config prints this:

send_policy   = "confirm"   # confirm | known-contacts | allow (see Sending safely)
undo_seconds  = 10          # every send waits this long; z cancels
mail_interval = "60s"       # how often the background sync checks Gmail
chat_interval = "30s"       # and Google Chat
initial_days  = 30          # how far back the first sync goes
max_threads   = 150         # at most this many threads on the first sync (older mail: search)
notify        = true        # desktop notifications for new mail and DMs
ai_command    = ["claude", "-p"]   # what @, S, D and T run
# editor      = "nvim"             # default: $VISUAL, $EDITOR, vi
# include     = "~/.local/state/varna/sandesh.toml"   # a theme file; its [colors] win

# Sections are the tabs on top. label: a Gmail label id (INBOX, STARRED,
# UNREAD, or a label's id), query: a Gmail search run on the server,
# today: only threads with mail from today.
[[section]]
name  = "Inbox"
label = "INBOX"

[[section]]
name  = "GitHub"
query = "from:notifications@github.com newer_than:14d"

[colors]           # Catppuccin Mocha by default
accent  = "#74c7ec"
accent2 = "#cba6f7"

Colours follow varna's desktop theme (~/.local/state/varna/palette.json) and change live when it does; anything set under [colors] wins. Colour names: accent, accent2, text, subtle, muted, surface, border, base, mantle, green, yellow, red, blue, peach. With varna, point include at the file it writes and the screen follows your theme.

#Files and environment

Path
~/.config/sandesh/config.tomlSettings
~/.config/sandesh/client.jsonYour OAuth client (auth setup)
keyring, service sandeshSign-in tokens (secret-tool search service sandesh); without a keyring, ~/.config/sandesh/tokens/ (mode 600)
~/.local/share/sandesh/cache.dbThe local copy (SQLite)
~/.local/state/sandesh/agent.logWhat agents did
~/.local/state/sandesh/sections.jsonSearches saved with W
~/.config/systemd/user/sandesh.serviceThe background sync (service install)

SANDESH_HOME puts all of sandesh's files under one folder (tests and demo use it). SANDESH_GOOGLE_URL sends every API call to a fake Google (the test suite's). The usual XDG_* variables are respected.

#How it works

Design decisions are recorded in docs/adr.

#Troubleshooting

#Contributing

make check runs what CI runs: go vet, gofmt, ShellCheck, the tests (against a fake Google, internal/fake) and the website check. Keys live in internal/tui/keys.go; after changing one, run go test ./internal/tui -update (it rewrites scripts/keybindings) and update the tables above. Every change gets a CHANGELOG entry.

#Roadmap

#License

MIT