Read, search, send, draft, label, filter, and thread Gmail from any MCP-enabled AI assistant. Wraps the Gmail API with scope-gated tools and in-process safeguards.
Note
Hardened + enhanced fork of GongRzhe/Gmail-MCP-Server (archived 2026-03-03), via ArtyMcLabin/Gmail-MCP-Server. Since the divergence point: 180+ commits and an extensive rewrite — security hardening, Gmail-surface improvements (reply-all, send-as alias, thread-level tools, download-to-disk, recipient pairing, batch ops with retry…), supply-chain hygiene, and CI gating. Every PR goes through CodeRabbit + dual-model Qodo Merge before merge. See SECURITY.md for the controls and threat model, and the comparison table below for the parent-forks delta.
A Model Context Protocol (MCP) server that lets AI assistants (Claude Desktop, Claude Code, Cursor, Continue, OpenClaw…) read and manage a Gmail account through scope-gated tools. Exposes the Gmail v1 API surface you actually need (messages, threads, labels, filters, attachments, drafts, reply-all) behind a single npx install.
Important
Positioning. Upstream calls this fork the maximalist one. The label is accurate from the outside, but it misframes the intent. klodr/gmail-mcp is built around four properties — and the code, tests and CI/CD all exist to pay rent on these and only these:
- Secure — input sanitization, recipient pairing, rate limiting, sender resolution, and audit logging are enforced in the server (not in the operator's attention). The Gmail surface is mediated, not exposed raw.
- Autonomous — designed to stay safe when no human is watching every action. Different threat model from upstream's "I use it daily in my own workflow"; the in-process middleware is what bridges that gap.
- Tested — 781 vitest cases across 39 files, 99.25% statement coverage (89% branch), plus a fast-check property-based fuzz suite and a dedicated hardening test file.
- Resilient under acceleration — 17 CI/CD workflows (CodeQL, OSV, Gitleaks, leak-detect, Scorecard, Socket, SLSA, lockfile lint, signed releases…) track the security ecosystem at the pace it actually changes — days, sometimes hours. Recent concrete instance: qs CVE-2026-8723, surfaced and shipped within hours of the public advisory across the klodr/* MCP family.
Full rationale and the per-tool design trade-offs live in docs/DESIGN_DECISIONS.md.
Comparison of the three maintained forks of the original Gmail MCP server, focusing on what an agent platform actually needs — prompt-injection safety, supply-chain integrity, and operational hygiene:
| Capability | GongRzhe/Gmail-MCP-Server (original, unmaintained) | ArtyMcLabin/Gmail-MCP-Server (intermediate fork) | klodr/gmail-mcp (this repo) |
|---|---|---|---|
| Core Gmail surface | |||
| Send / draft / read / search messages | ✅ | ✅ | ✅ |
| Label CRUD | ✅ | ✅ | ✅ |
| Filter CRUD | list_filters broken |
✅ fixed | ✅ |
| Batch modify / delete | ✅ | ✅ | ✅ |
Reply threading (In-Reply-To / References) |
❌ orphaned replies | ✅ | ✅ |
| Reply-all tool | ❌ | ✅ | ✅ |
Send-as alias (from parameter) |
❌ | ✅ | ✅ |
Thread-level tools (get_thread, list_inbox_threads, get_inbox_with_threads) |
❌ | ✅ | ✅ |
Download email to disk (json/eml/txt/html) |
❌ | ✅ | ✅ |
| Download attachment | ✅ | ✅ | ✅ |
| OAuth / authorization | |||
--scopes flag for least-privilege auth |
❌ | ✅ | ✅ |
| Tool list filtered by granted scopes | ❌ | ✅ | ✅ |
OAuth credentials file mode 0o600 |
❌ | ✅ | ✅ |
| Security — input handling | |||
CRLF header injection sanitization (\r\n\0) |
❌ | ✅ | |
Path traversal in download_attachment |
❌ | ✅ fixed | ✅ |
| Path jails (attachment source + download destination) blocking prompt-injected exfiltration | ❌ | ❌ | ✅ |
Symlink-safe writes (O_NOFOLLOW + post-mkdir realpath re-verification, TOCTOU defense) |
❌ | ❌ | ✅ |
Zod bounds on maxResults / batchSize / messageIds length |
❌ | ❌ | ✅ |
Cryptographic MIME boundary (crypto.randomBytes, not Math.random) |
❌ | ❌ | ✅ |
| MCP protocol & tool surface | |||
| MCP SDK version | v0.4.x | v1.27.x | v1.29.x |
Tool annotations (readOnlyHint / destructiveHint / idempotentHint) |
❌ | ✅ | ✅ |
llms-install.md (LLM-readable install guide) |
❌ | ❌ | ✅ |
| Publishing / discoverability | |||
| Published on npm | ❌ stale — no future releases (repo archived) | ❌ (consumed as a GitHub install from the intermediate fork) | ✅ dedicated scoped package, signed releases |
| Active maintenance (last 30 d) | ❌ (archived 2026-03-03) | ✅ daily review cycle (CodeRabbit + human) | |
| Supply-chain integrity | |||
| Node.js floor | ❌ >=14 (EOL April 2023) |
❌ >=14 (EOL April 2023) |
✅ >=22 (Active LTS, maintenance until 2027-04-30) |
CI: CodeQL Advanced (javascript-typescript + actions) |
❌ | ❌ | ✅ |
| CI: OpenSSF Scorecard (weekly scan + badge) | ❌ | ❌ | ✅ |
| CI: Socket Security supply-chain alerts | ❌ | ❌ | ✅ |
| CI: CodeRabbit assertive reviews on every PR | ❌ | ❌ | ✅ |
| Release: signed builds (Sigstore + SLSA in-toto attestation + npm provenance) | ❌ | ❌ | ✅ |
| Release: single-file ESM bundle | ❌ | ❌ | ✅ |
| Testing | |||
| Unit/property tests | ❌ (0 tests) | ✅ (781 tests, 39 files) | |
Statement coverage across src/** |
0% | 16.14% | 99.25% (89% branch) |
| Fast-check property-based fuzz suite | ❌ | ❌ | ✅ |
| Hardening-specific test file (jails, CRLF, O_EXCL) | ❌ | ❌ | ✅ |
| CI/CD hardening | |||
| Shell-injection-safe GitHub Actions workflows | ❌ | ✅ | ✅ |
Workflows use least-privilege permissions: scopes |
❌ | ✅ | ✅ |
| All GitHub Actions pinned by full commit SHA | ❌ | ❌ | ✅ |
| Operational | |||
CHANGELOG.md (Keep-a-Changelog) |
❌ | ❌ | ✅ |
SECURITY.md (vulnerability reporting) |
❌ | ❌ | ✅ |
klodr/gmail-mcp is the only one of the three with (a) source-path jails that make prompt-injection attachment exfiltration inert, (b) a modern supply chain (Scorecard, Socket, Sigstore), and (c) an in-repo review policy (.coderabbit.yaml) that every PR must pass before merge.
npm install -g @klodr/gmail-mcpOr directly via npx:
npx -y @klodr/gmail-mcpRequires Node.js 22+.
- Open the Google Cloud Console.
- Create a project and enable the Gmail API.
- Under APIs & Services → Credentials, create an OAuth 2.0 Client ID (Desktop or Web). For Web, add
http://localhost:3000/oauth2callbackto the authorized redirect URIs. - Download the JSON, rename it to
gcp-oauth.keys.json, place it at~/.gmail-mcp/gcp-oauth.keys.json(or override withGMAIL_OAUTH_PATH=/abs/path/gcp-oauth.keys.json).
npx -y @klodr/gmail-mcp auth --scopes=gmail.readonlyAlways pass --scopes with the minimum you actually need — the MCP filters the tool list at startup based on the granted scopes, so a read-only token doesn't expose write tools to the LLM. A browser opens for Google's consent flow; tokens are written to ~/.gmail-mcp/credentials.json (mode 0o600).
{
"mcpServers": {
"gmail": {
"command": "npx",
"args": ["-y", "@klodr/gmail-mcp"]
}
}
}Client-specific config file:
- Claude Code:
~/.claude.json - Claude Desktop:
~/Library/Application Support/Claude/claude_desktop_config.json(macOS) /%APPDATA%\Claude\claude_desktop_config.json(Windows) - Cursor:
~/.cursor/mcp.json - OpenClaw:
~/.openclaw/openclaw.json
See llms-install.md for an LLM-readable install guide.
| Scope shorthand | Full Gmail scope | What it grants |
|---|---|---|
gmail.readonly |
…/auth/gmail.readonly |
Read messages, threads, labels (filter tools require gmail.settings.basic) |
gmail.modify |
…/auth/gmail.modify |
Readonly + apply/remove labels, delete messages |
gmail.compose |
…/auth/gmail.compose |
Create drafts |
gmail.send |
…/auth/gmail.send |
Send messages |
gmail.labels |
…/auth/gmail.labels |
Manage labels only |
gmail.settings.basic |
…/auth/gmail.settings.basic |
Manage filters |
Recipes:
# Read-only browsing
npx @klodr/gmail-mcp auth --scopes=gmail.readonly
# Read + send (mailing-list bot)
npx @klodr/gmail-mcp auth --scopes=gmail.readonly,gmail.send
# Everything (default; explicit)
npx @klodr/gmail-mcp auth --scopes=gmail.modify,gmail.settings.basic
# Default + permanent delete (delete_email / batch_delete_emails)
# gmail.modify authorizes trash; mail.google.com is the only scope
# that authorizes purging from Trash. Both are listed because the
# tool gate does exact scope-name matching — a token holding only
# mail.google.com would not enable the gmail.modify-gated tools,
# even though Google's scope hierarchy would technically accept the
# same calls.
npx @klodr/gmail-mcp auth --scopes=gmail.modify,mail.google.com,gmail.settings.basicThe runtime constraints that an autonomous LLM client cannot violate are enforced in the server, not in the operator's attention span. Seven in-process modules sit on the tool entry path; the ones that are opt-in only activate when the matching env var is set.
Always-on (active for every call out of the box):
sanitize.ts— strips/escapes input the LLM provides (CRLF injection, control characters, NUL bytes) before it reaches Gmail headers, filenames, or filesystem paths.sender-resolver.ts— resolves thefromparameter against the account's authorized send-as aliases only. Spoofing the sender field is rejected before the Gmail API send call.rate-limit.ts— bounds Gmail API call volume per tool family (send,delete,modify,drafts,labels,filters) over rolling daily/monthly windows. The state is persisted to disk so it survives restarts. Disable withGMAIL_MCP_RATE_LIMIT_DISABLE=truefor test runs only.gmail-errors.ts— normalizes Gmail API failure modes into typed errors so retries and surfacing to the LLM are deterministic.middleware.ts— composes the always-on chain and binds it to every tool entry point. New tools inherit the pipeline by construction.
Opt-in (off by default, set the env var to engage):
recipient-pairing.ts— refuses to send mail to addresses that are not on the operator-controlled allow-list (~/.gmail-mcp/paired.json). No fresh outbound destinations from a hallucinated address. Engages whenGMAIL_MCP_RECIPIENT_PAIRING=true; gates thesend_email/reply_*/forward_email/draft_email/update_draftfamily only.audit-log.ts— append-only JSONL trail of every tool call (name, redacted args, outcome) with structural-key-preserving redaction so operators can replay decisions post-hoc. Engages whenGMAIL_MCP_AUDIT_LOG=/abs/path/audit.jsonlis set.
Configure each module via the env vars below:
| Knob | Env var | Default | Notes |
|---|---|---|---|
| Attachment jail | GMAIL_MCP_ATTACHMENT_DIR=/abs/path |
~/GmailAttachments/ (auto-created mode 0o700) |
Every attachment path (send_email, draft_email, update_draft, reply_all, reply_to_email, forward_email) must live inside this directory after realpath canonicalization. Symlinks pointing outside are rejected. Blocks prompt-injected exfiltration of ~/.ssh/id_rsa, ~/.gmail-mcp/credentials.json, ~/.claude.json, etc. |
| Download jail | GMAIL_MCP_DOWNLOAD_DIR=/abs/path |
~/GmailDownloads/ (auto-created mode 0o700) |
download_email and download_attachment write exclusively here. The leaf is opened with O_NOFOLLOW; post-mkdir the resolved path is re-verified against the jail root (TOCTOU defense). |
| OAuth keys path | GMAIL_OAUTH_PATH=/abs/path/gcp-oauth.keys.json |
~/.gmail-mcp/gcp-oauth.keys.json |
Google Desktop/Web OAuth client credentials. |
| Credentials path | GMAIL_CREDENTIALS_PATH=/abs/path/credentials.json |
~/.gmail-mcp/credentials.json |
Access/refresh tokens. File mode 0o600. |
| Rate limit state dir | GMAIL_MCP_STATE_DIR=/abs/path |
~/.gmail-mcp/ |
Where the rolling call-history for rate limiting is persisted (ratelimit.json, mode 0o600). Same directory is reused for any future state files. |
| Rate limit overrides | GMAIL_MCP_RATE_LIMIT_<bucket>=D/day,M/month |
see below | Override the per-bucket daily/monthly caps. Buckets: send (100/2000), delete (200/2000), modify (500/5000), drafts (300/3000), labels (50/500), filters (20/200). The send cap is sized at the upper end of a human professional workload (~40 emails/day with a 2.5× cushion); raise it via GMAIL_MCP_RATE_LIMIT_send=400/day,6000/month if you need the pre-v0.30.2 default. The bucket name is lowercase and matches the tool family. |
| Rate limit disable | GMAIL_MCP_RATE_LIMIT_DISABLE=true |
unset (limiter active) | Kill-switch for the entire limiter. Use only for test suites or controlled batch operations. |
| Audit log | GMAIL_MCP_AUDIT_LOG=/abs/path/audit.jsonl |
unset (no audit trail) | Opt-in append-only JSONL log of every tool call (name, redacted args, outcome). File mode 0o600. Must be an absolute path; relative paths are rejected at startup. Redaction keeps structural keys and drops values under an allowlist. |
| Dry-run | GMAIL_MCP_DRY_RUN=true |
unset (real calls) | When "true" (strict match), every write tool (send_email, reply_all, reply_to_email, forward_email, draft_email, update_draft, delete_draft, send_draft, delete_email, modify_email, batch_modify_emails, batch_delete_emails, create_label, update_label, delete_label, get_or_create_label, create_filter, delete_filter, create_filter_from_template, modify_thread) short-circuits before reaching Gmail and returns the redacted payload it would have sent. Useful for CI smoke tests, agent debugging, and human-in-the-loop approval flows. Read tools ignore the flag (nothing to preview). Matches MERCURY_MCP_DRY_RUN / FAXDROP_MCP_DRY_RUN on the sibling servers. |
The exact set depends on the OAuth scopes granted at auth time. Full catalog:
- Messages —
send_email,draft_email,read_email,search_emails,modify_email,delete_email,download_email,download_attachment,batch_modify_emails,batch_delete_emails,reply_all,reply_to_email,forward_email - Drafts —
list_drafts,get_draft,update_draft,delete_draft,send_draft(fullusers.drafts.*surface;draft_emailabove creates the initial draft) - Threads —
get_thread,list_inbox_threads,get_inbox_with_threads,modify_thread - Labels —
list_email_labels,create_label,update_label,delete_label,get_or_create_label - Filters —
list_filters,get_filter,create_filter,delete_filter,create_filter_from_template - Recipient pairing —
pair_recipient(manage the~/.gmail-mcp/paired.jsonallowlist whenGMAIL_MCP_RECIPIENT_PAIRING=true)
Every write tool is annotated with destructiveHint / readOnlyHint / idempotentHint per the MCP spec so policy-aware clients can gate on HITL confirmation.
search_emails accepts Gmail's native search operators — from:, to:, subject:, has:attachment, after:YYYY/MM/DD, before:YYYY/MM/DD, is:unread, label:<name>, etc. They combine freely: from:alice@example.com after:2026/01/01 has:attachment. Full reference: Google's Gmail search operators cheat sheet.
See ROADMAP.md.
- 📧 klodr/gmail-mcp — Gmail (you are here)
- 📠 klodr/faxdrop-mcp — Send real faxes via FaxDrop
- 🏦 klodr/mercury-invoicing-mcp — Mercury banking + invoicing
29 standalone repositories and 349 forks of the original GongRzhe server are reviewed in docs/COMPETITORS.md — which ideas we borrowed, which we chose not to, and where klodr/gmail-mcp sits on the maturity axes.
See CONTRIBUTING.md for the test / build / lint checklist and release process.
See SECURITY.md for the vulnerability-reporting process and the current security model, and ASSURANCE_CASE.md for the threat model, trust boundaries, and CWE/OWASP mitigation table.
See CONTINUITY.md for the handover plan if the maintainer becomes unavailable.
MIT — see LICENSE.
klodr/gmail-mcp is the maintenance fork of a two-step upstream chain:
- GongRzhe/Gmail-MCP-Server — the original server. Unmaintained since August 2025 (7+ months with zero maintainer activity and 72+ unmerged pull requests).
- ArtyMcLabin/Gmail-MCP-Server — Arty MacKiewicz's active fork. Since divergence from Gong, it merged community contributions including: reply-all (#3 by @MaxGhenis),
list_filtersfix (#4 by @nicholas-anthony-ai), CI/CD shell-injection hardening (#9 by @JF10R),download_email(#13 by @icanhasjonas), tool annotations (#14 by @bryankthompson), CC/BCC fields inread_email(#21 by @panghy), and draft lifecycle toolssend_draft/delete_draft/update_draft(#30 by @thisisambros). Reply-threading auto-resolution and the--scopesflag were folded in directly by the maintainer.
klodr/gmail-mcp carries all of the above forward and adds the supply-chain / path-jail / review-policy layer (see comparison table above). Credit to every PR author along the chain.