gmail-mcp-server
# Gmail MCP Server
A working example of connecting Claude to a real business tool — Gmail —
the way it should be done: with proper login security, the AI only able to
do what it's explicitly allowed to do, and no way for it to take an
irreversible action (like sending an email) without a clear checkpoint.
## The problem this solves
AI assistants like Claude are increasingly expected to *do* things, not
just talk — search an inbox, draft a reply, flag something urgent — instead
of a person copy-pasting between a chat window and their actual tools.
That capability is genuinely useful, but connecting an AI assistant
directly to something as sensitive as an email account raises an obvious
question: **what happens if it gets it wrong, or is tricked into doing
something it shouldn't?**
Most quick "connect my AI to my inbox" setups skip past that question —
they hand over broad access and hope for the best. That's the gap this
project is built to close. It's a demonstration, using a real Google
account and the real Gmail API (not a mock or toy example), of what a
*properly scoped* AI-to-business-tool integration looks like: the AI gets
exactly the access it needs for the job, nothing more, and the riskiest
action (sending real email) requires an explicit extra step rather than
happening automatically.
## How it stays safe, in plain terms
- **A specific, limited key — not the master key.** Logging in grants
three narrow permissions (read mail, draft/send mail, apply labels).
There is no permission to delete anything, change account settings, or
touch anything outside Gmail.
- **The key is locked in a safe.** Once you log in, the credentials that
let Claude access your inbox are encrypted on your own computer. They're
never sent anywhere else, and the encryption key lives only in a local
file that's never shared or uploaded.
- **Drafting and sending are two separate, deliberate steps.** Claude can
prepare a reply as a draft at any time — that's harmless and reversible,
the same as you starting an email and not hitting send. Actually sending
it is a separate action, clearly flagged to whatever app is running
Claude as a sensitive one-way step, so it isn't something that happens
as a side effect of Claude just being "helpful."
- **Labels can't be used to hide or destroy mail.** Claude can tag messages
with custom labels you create (e.g. "Needs Reply"), but it's structurally
blocked from touching Gmail's built-in Trash/Spam/Archive controls
through that same feature — a subtle way "just add a label" could
otherwise be misused.
The full technical breakdown — exact permissions requested, what's
reversible vs. not, and what the honest residual risk is — is in
[`SECURITY.md`](SECURITY.md).
## Why build this instead of using Google's own Gmail MCP integration
Google has since released its own first-party remote MCP server for
Gmail. For someone who just wants Gmail-in-Claude working today, that's
the faster path. This project exists for a different reason: to show, on a
tool everyone recognizes, the underlying skill of building this kind of
integration from scratch — the login flow, the credential storage, the
tool design, the safety boundaries — for the many business tools (CRMs,
ad platforms, shipping, payments, internal systems) that don't have a
ready-made AI integration and need someone to build one deliberately.
## See it in action
1. One-time login: run the authorize command, sign in with Google in the
browser that opens, and approve access.
2. Ask Claude: *"Search my inbox for anything from [sender] this week"* —
it searches, nothing else.
3. Ask: *"Check my inbox and tell me if anything looks urgent"* — it pulls
a fast summary of recent mail and reasons over it.
4. Ask: *"Draft a reply to that saying I'll follow up tomorrow"* — a real
draft appears in Gmail. Nothing has been sent.
5. Only if you then say so, ask Claude to send it — that's the one
deliberate, irreversible step in the whole flow.
---
## Under the hood
### Tools
| Tool | Scope | Description |
|---|---|---|
| `gmail_search` | read | Search messages with Gmail search syntax |
| `gmail_read_message` | read | Fetch one message's full body |
| `gmail_list_recent` | read | Fast metadata/snippet fetch for periodic triage |
| `gmail_create_draft` | write | Create a draft (never sends) |
| `gmail_send_draft` | write | Send a previously created draft |
| `gmail_apply_label` | write | Attach a custom (non-system) label |
### Setup
1. **Install dependencies** (requires Python 3.10+; this repo uses
[uv](https://docs.astral.sh/uv/)):
```
uv sync
```
2. **Set up Google OAuth and authorize an account** — follow
[`workflows/setup_google_oauth.md`](workflows/setup_google_oauth.md).
Short version:
```
cp .env.example .env
python -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())"
# paste the output into .env as TOKEN_ENCRYPTION_KEY
# download credentials.json from Google Cloud Console into the project root
python -m tools.gmail_mcp.authorize personal
```
3. **Point Claude Desktop/Code at the server.** Add to your MCP client
config (e.g. `claude_desktop_config.json`):
```json
{
"mcpServers": {
"gmail": {
"command": "/absolute/path/to/mcp-business-tools/.venv/bin/python",
"args": ["-m", "tools.gmail_mcp.server"],
"cwd": "/absolute/path/to/mcp-business-tools"
}
}
}
```
4. **Restart Claude Desktop/Code** so it picks up the new server.
### Adding more accounts
See [`workflows/add_gmail_account.md`](workflows/add_gmail_account.md).
### Project layout
```
workflows/ SOPs: OAuth setup, adding accounts, inbox-summary pattern
tools/gmail_mcp/ the MCP server package
config.py env-driven settings, scopes
auth.py OAuth flow + encrypted token storage
gmail_client.py Gmail API wrapper functions
server.py MCP tool definitions
authorize.py one-time per-account OAuth CLI
tokens/ encrypted per-account tokens (gitignored)
```
TDQS
Scored across 6 tools
Tools are mostly distinct: read/list/search handle retrieval with clear differences in scope, create_draft/send_draft separate drafting from sending, and apply_label handles labeling. One slight overlap exists between gmail_list_recent and gmail_search (both return compact metadata + snippets), though their intended use cases differ enough to disambiguate.
All tools follow a consistent gmail_verb_noun pattern (gmail_read_message, gmail_list_recent, gmail_create_draft, gmail_send_draft, gmail_apply_label, gmail_search). Naming is uniform, clear, and predictable throughout.
Six tools are well-scoped for a Gmail integration server. Each tool covers a distinct core Gmail operation (read, list, draft, send, label, search) without bloat or overlap, falling comfortably within the ideal 3-15 range.
Core read, list, search, draft, send, and label workflows are covered, but there are notable gaps: no update/modify message tools (trash, archive, mark read/unread), no delete draft tool, and no task to list or manage threads as a whole. The label tool explicitly rejects system labels, meaning agents cannot archive or trash messages at all, which is a significant dead end for common email workflows.