gmail-accounts
# gmail-mcp
A multi-account Gmail [MCP](https://modelcontextprotocol.io) server for AI agents
(Claude Code and anything else that speaks MCP), with one design goal above all:
**the agent can read and draft freely, but a human must approve every send.**
Built because the hosted Gmail connectors bind one account at a time and send
without friction. This server binds any number of Gmail accounts behind short
aliases (`work`, `school`, ...) and splits capabilities by risk:
| Capability | Who can do it |
|---|---|
| Search, read threads, list drafts | Agent, freely |
| Create and update drafts | Agent, freely |
| **Send** | Only with a single-use approval token a human mints in a terminal |
The server is registered as `gmail-accounts` (not `gmail`) on purpose: if you also
run a hosted Gmail connector, near-identical tool names make an agent read the
wrong mailbox and report a confident false negative.
## How the send gate works
1. The agent stages a draft and asks you to approve it.
2. You run `gmail-approve` in your own terminal, review the exact RFC822 payload,
and get a one-time token bound to that draft's content hash.
3. The agent calls the send tool with the token. Wrong draft, edited draft, reused
token, expired token: all refused.
The send tool also carries `anthropic/requiresUserInteraction`, so Claude Code
prompts a human even in `bypassPermissions` mode. The token CLI is a mistake
interlock; the harness prompt is the enforced boundary.
## Setup
Requires Python 3.12+ and [uv](https://docs.astral.sh/uv/).
### 1. Google Cloud OAuth client
Create a project at console.cloud.google.com, enable the Gmail API, create an
**OAuth client ID of type Desktop app**, and download the JSON to:
```
~/.config/gmail-mcp/client_secret.json
```
### 2. Declare your accounts
`~/.config/gmail-mcp/accounts.json`:
```json
{
"accounts": [
{ "alias": "personal", "email": "alice.personal@gmail.com" },
{ "alias": "work", "email": "alice.work@gmail.com" }
]
}
```
Adding an account later is a config edit plus one auth run; the file is re-read on
every call, so no restart is needed.
### 3. Authorize each account
```bash
uv sync
scripts/auth-all.sh # runs the OAuth flow once per alias, skips done ones
```
Each grant is verified against its alias: authorizing the wrong Google account in
the browser is detected and refused, not silently stored.
### 4. Register with Claude Code
```bash
claude mcp add gmail-accounts -- uv run --directory /path/to/gmail-mcp --frozen --no-sync gmail-mcp
```
## Everyday use
Every tool takes an `account` alias. Ask the agent things like "search work for
the invoice thread" or "draft a reply on personal". When it is time to send:
```bash
gmail-approve # lists pending sends, shows the payload, mints a token
```
Health check:
```bash
uv run gmail-mcp-health
```
## Security notes
- Secrets never enter the repo: `client_secret.json` and per-account tokens live
in `~/.config/gmail-mcp/` (override with `GMAIL_MCP_CONFIG`).
- All config writes are atomic (tmp + fsync + rename); a crash mid-write never
leaves a torn file.
- stdout is the JSON-RPC channel; nothing in the package may `print()` (enforced
by ruff T201), so diagnostics cannot corrupt the protocol.
## Tests
```bash
uv run pytest
```
TDQS
Scored across 12 tools
Each tool targets a distinct operation: search, thread/message retrieval, attachment download, threading labels, and a clearly separated draft/stage/send pipeline. Even adjacent tools like get_thread and get_message are delineated by thread-level vs message-level detail.
Tool names mostly follow a predictable verb_noun snake_case pattern with list_*, get_*, and create_/update_ prefixes. The bare verbs search and send, plus stage_send, are minor deviations but still readable and consistent in style.
Twelve tools is well-scoped for a Gmail account server covering search, reading, attachments, labels, drafts, and sending. Each tool earns its place with no obvious redundancy.
Core email workflows are covered: search, read, download attachments, label threads, create/update drafts, and send through an approval flow. Minor gaps like creating labels, adding attachments to drafts, or managing read state are plausible but not critical to the server's apparent purpose.