The problem
The built-in Gmail connector handles one Google account. Running a studio means a dozen inboxes across several Workspace domains, and checking them means a dozen browser tabs. An assistant that could triage all of them at once would save real time, but only if it could never send an email you didn't write, and never touch the wrong mailbox.
What we built
A Python Model Context Protocol (MCP) server that Claude Code runs locally over stdio.
- Seven tools.
list_accounts,search_mail,read_thread, andtriagefor reading;apply_labels,archive, andcreate_draftfor changes. Reads can fan out across every inbox or a named group of them in parallel, so "what came in overnight across outreach" is one call, and one failing account doesn't break the rest. - Sending is impossible by construction. The server can only reach an allowlist of eight Gmail API endpoints, and none of them send. Tests enforce that three ways: the allowlist is pinned, the source is scanned for any send call, and a fake Gmail service checks the behavior. Drafts get written for a person to review and send.
- Writes touch one mailbox at a time. Every write tool rejects "all" and group names, and each inbox has a read or read-write tier checked before any network call.
- No credentials on disk. The server signs in as a Workspace service account with domain-wide delegation, and every secret in the config is a 1Password reference resolved at runtime in one batched call. A test checks that nothing secret gets written to disk.
- Replies that thread correctly. Drafts carry the right reply headers, reply-all leaves out your own addresses and aliases, and the server refuses to draft if the thread changed underneath it.
- Built to change. Config is re-read on every call, so adding an inbox is four lines of TOML and no restart. 171 tests.
Status
In daily private use across a dozen mailboxes. It's the same rule as the WorkMail server and our client automations: give the assistant real reach, then make the dangerous actions unreachable rather than just discouraged.
