A drop-in MCP server that wraps the Google Gmail API for any MCP-compatible client. Production-grade, not a demo. FastAPI, Postgres, Railway-ready, multi-tenant by design.
gmail-mcp-oauth exposes the Google Gmail API as 32 tools any MCP-compatible client can call (Claude.ai, IDE assistants, internal agents). It handles the full upstream and downstream auth chain in one service.
The MCP client authenticates with Auth0. Every request to the server validates issuer, audience, scope, signature, and expiry on the JWT before any tool dispatch happens.
Each Auth0 user can link multiple Gmail accounts. Refresh tokens are encrypted at rest with Fernet and keyed on (auth0_sub, account_email), with MultiFernet support for key rotation.
13 read tools, 14 write tools, 4 cleanup tools, 1 bootstrap tool. Every authenticated route is gated by an explicit allowlist; failure modes default to closed.
Most public Gmail-MCP projects are toy single-user demos. They are useful for a weekend, not for shipping. The harder integration concerns only show up once a service is real.
This project closes them, then went through a 15-finding static security review (all closed), then was extracted from an internal monorepo to a standalone public repo through a systematic, agent-driven process.
Specifically:
13 read + 14 write + 4 cleanup + 1 bootstrap. Tools are dispatched only after JWT validation, scope check, and allowlist match.
One Auth0 sub can manage multiple Gmail accounts. Every tool call takes an account_email parameter that resolves to the correct encrypted refresh token.
Explicit allowlist gates which Auth0 subs can link accounts at all. A post-callback confirmation page (auto-activates in multi-user mode) verifies the user completing the Google consent is the same user who initiated it. This is the consent-phishing defense.
Fernet symmetric encryption with MultiFernet key rotation support. Tokens are decrypted only at the moment of API call, never logged, never returned in a response.
Issuer, audience, scope, signature, and expiry all validated on every authenticated request. JWKS calls are throttled to prevent upstream abuse.
JSON Schema patterns catch CRLF injection, oversized inputs, Unicode lookalikes, and null-byte attacks at the schema boundary. Handler-entry checks back that up. 9 adversarial probe categories live in the test suite.
Every dependency pinned by hash. Container builds are reproducible. The base image is digest-pinned, not tag-pinned.
Runtime drops privileges. Filesystem layout assumes a non-root user. No chown dance at startup.
FastAPI + httpx + asyncio. Concurrent Gmail API calls inside a single tool dispatch where the workload allows it.
Explicit replica-count acknowledgment is required before single-replica deployments. Allowlist enforcement at every authenticated route. The default for any ambiguous configuration is "do not run."
Documented procedures for key rotation, DB compromise response, and account re-linking. Not just code; the operational playbook ships with the repo.
The two paths that matter: the steady-state tool-call path, and the one-time account-linking path.
MCP Client (Claude.ai)
-> Auth0 (issues JWT to client)
-> gmail-mcp-oauth /mcp endpoint (validates JWT, allowlist)
-> tool dispatch
-> Gmail API (with per-user encrypted refresh token,
fetched + decrypted from Postgres)
Account linking flow:
MCP Client invokes connect_gmail_account(account_email)
-> gmail-mcp-oauth returns Google OAuth authorization URL
-> User opens URL, signs in to Google, consents to scopes
-> Google redirects to /oauth2callback
-> Server validates state, checks email_verified
-> (multi-user mode) renders confirmation page;
user confirms identity binding
-> Refresh token encrypted with Fernet, stored under
(auth0_sub, account_email)
-> Tool calls now work for that account
The account_email parameter is the multi-account model in one line. Real Gmail query syntax inside.
{
"method": "tools/call",
"params": {
"name": "search_emails",
"arguments": {
"account_email": "[email protected]",
"query": "from:[email protected] newer_than:30d",
"max_results": 25
}
}
}
The codebase originated as part of an internal Amazon FBA operations platform. Over a six-week period it went through five distinct phases.
OAuth flow, 14 read tools, basic tool dispatch. Enough to prove the model worked end to end against a real Gmail account.
15 findings across HIGH, MEDIUM, and LOW severity. The review was done before the repo went public, not after.
12 PRs systematically closing every finding: consent-phishing identity binding, refresh-token wipe, JWKS throttling, body-size limits, scope hierarchy, declarative schema patterns, and the rest. Each PR scoped tight enough to review without skimming.
Test count grew from roughly 200 to 651 with adversarial probe coverage across 9 categories. Coverage settled at 91.35%.
Multi-pass scrub for PII, brand context, and internal process tags before extraction to a standalone public repo. CI workflow, branch protection ruleset, Railway redeploy from public source. Multi-tenant validation exercised end to end in production with multiple accounts under a single Auth0 sub.
This is what production-grade open source looks like end to end, not just the code.
The repo is the source of truth. The walkthrough and tool reference go deeper than this page.
How the service accesses, uses, stores, and deletes your Google user data, and the terms that govern its use: Privacy Policy and Terms of Service.
Welch Commerce Systems builds production-grade automation systems for e-commerce brands doing $2M to $10M. If this looks like the discipline you'd want on your stack, schedule a call.
Schedule a Discovery Call →