gmail-mcp
Provides tools for reading, searching, sending, drafting, labeling, filtering, and threading Gmail messages through the Gmail API, with scope-gated access and security safeguards.
π§ gmail-mcp
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.
Hardened + enhanced fork ofGongRzhe/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.
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 β 945 vitest cases across 43 files, 99.37% statement coverage (91% 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.
β¨ Why this MCP?
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 | β οΈ | β fixed | β |
Batch modify / delete | β | β | β |
Reply threading ( | β orphaned replies | β | β |
Reply-all tool | β | β | β |
Send-as alias ( | β | β | β |
Replies and forwards leave from the alias the conversation uses, like the Gmail web UI; | β | β | β |
Thread-level tools ( | β | β | β |
Download email to disk ( | β | β | β |
Download attachment | β | β | β |
Download all attachments of a message at once, optionally as one ZIP (original filenames kept) | β | β | β |
| β | β | β |
OAuth / authorization | |||
| β | β | β |
Tool list filtered by granted scopes | β | β | β |
OAuth credentials file mode | β | β | β |
OAuth loopback callback checked with | β | β | β |
A | β copied on every start | β copied on every start | β |
Security β input handling | |||
CRLF header injection sanitization ( | β | β οΈ partial | β |
Path traversal in | β | β fixed | β |
Path jails (attachment source + download destination) blocking prompt-injected exfiltration | β | β | β |
Symlink-safe writes ( | β | β | β |
Zod bounds on | β | β | β |
Cryptographic MIME boundary ( | β | β | β |
Security β outbound mail and prompt injection | |||
Optional recipient allowlist ( | β | β | β |
With the allowlist on, the model cannot extend it through gmail-mcp: the human confirms in the MCP client, or runs | β | β | β |
A | β | β | β |
| β (no | β | β |
Write caps per tool family over 24 h and 30 days; batch tools count every message, not every call | β | β | β |
Filters that forward mail are refused (a forwarding rule silently redirects all future matching mail) | β | β | β refused, with the reason |
Downloaded files marked as coming from the internet (macOS quarantine, Windows Mark of the Web), as Mail and Outlook do | β | β | β |
Exported HTML: scripts, frames and forms blocked; remote images, stylesheets, fonts and media (tracking pixels) blocked unless asked for | β (no HTML export) | β no CSP | β |
MCP protocol & tool surface | |||
MCP SDK version | v0.4.x | v1.27.x | v1.32.x |
Tool annotations ( | β | β | β |
| β | β | β |
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) | β οΈ sporadic | β daily review cycle (CodeRabbit + human) |
Supply-chain integrity | |||
Node.js floor | β | β | β
|
CI: CodeQL Advanced ( | β | β | β |
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) | β οΈ (97 tests) | β (945 tests, 43 files) |
Statement coverage across | 0% | 16.14% | 99.37% (91% 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 | β | β | β |
All GitHub Actions pinned by full commit SHA | β | β | β |
Operational | |||
| β | β | β |
| β | β | β |
klodr/gmail-mcp is the only one of the three with (a) source-path jails that make prompt-injection attachment exfiltration inert, (b) outbound guard rails a hostile email cannot talk the model past β an optional recipient allowlist the model cannot extend through gmail-mcp, no forwarding filters, write caps that count every message β (c) a modern supply chain (Scorecard, Socket, Sigstore), and (d) an in-repo review policy (.coderabbit.yaml) that every PR must pass before merge.
Fork columns checked against the code of GongRzhe a890d19 (2025-08-06, archived) and ArtyMcLabin 83e8daf (main, 2026-08-12).
Related MCP server: mercury-invoicing-mcp
π¦ Installation
npm install -g @klodr/gmail-mcpOr directly via npx:
npx -y @klodr/gmail-mcpRequires Node.js 24.18.1+ (tested on Node 24 and 26).
βοΈ Configuration
1οΈβ£ Google Cloud OAuth credentials
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).
2οΈβ£ Authenticate (once)
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).
3οΈβ£ Register the server with your MCP client
{
"mcpServers": {
"gmail": {
"command": "npx",
"args": ["-y", "@klodr/gmail-mcp"]
}
}
}Client-specific config file:
Claude Code:
~/.claude.jsonClaude Desktop:
~/Library/Application Support/Claude/claude_desktop_config.json(macOS) /%APPDATA%\Claude\claude_desktop_config.json(Windows)Cursor:
~/.cursor/mcp.jsonOpenClaw:
~/.openclaw/openclaw.json
See llms-install.md for an LLM-readable install guide.
π OAuth scopes
Scope shorthand | Full Gmail scope | What it grants |
|
| Read messages, threads, labels (filter tools require |
|
| Readonly + apply/remove labels, delete messages |
|
| Create drafts |
|
| Send messages |
|
| Manage labels only |
|
| 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.basicπ‘οΈ Safeguards
In-process middleware (autonomy enforcement)
The 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β when the send-as list is readable and non-empty, resolves thefromparameter against the account's authorized send-as aliases only, so spoofing the sender field is rejected before the Gmail API send call; when the list is unavailable or empty, an explicitfrompasses through unchanged. Withoutfrom, and when the send-as list is readable, a reply, a forward or a threadedsend_email/draft_emailleaves from the alias the conversation already uses (the one the source message came from or was sent to, else the last one used in the thread), as in the Gmail web UI, andreply_allnever copies any of the account's aliases. When the list is unavailable (e.g. a token limited togmail.sendorgmail.compose), the sender falls back toresolveDefaultSenderandreply_allleaves out only the account's own address.rate-limit.tsβ bounds write volume per tool family (send,delete,modify,drafts,labels,filters,pairing) over rolling daily/monthly windows. The batch tools (batch_modify_emails,batch_delete_emails,move_to_spam_batch) count each message they touch, so one call on 1 000 messages uses 1 000 units, not 1. The state is persisted to disk so it survives restarts. Disable withGMAIL_MCP_RATE_LIMIT_DISABLE=truefor test runs only.quarantine.tsβ marks every file written bydownload_attachment,download_all_attachmentsanddownload_emailas downloaded from the internet, as Mail and Outlook do: thecom.apple.quarantineattribute on macOS (Gatekeeper checks an executable or script when it is opened), the Mark of the Web on Windows; nothing on Linux. Never blocking: if the mark cannot be set, the file is still saved. Off withGMAIL_MCP_QUARANTINE=false.Forwarding filters refused (
filter-manager.ts) βcreate_filterand the filter templates never install a rule that forwards mail: such a rule silently redirects every future matching message, and Gmail forwards only to a verified forwarding address, whose registration needs thegmail.settings.sharingscope β not in the default scopes and used by no tool here; the refusal holds even if--scopesgrants it. Create those in the Gmail web interface.Exported HTML (
email-export.ts) β the filedownload_emailwrites inhtmlformat carries a Content-Security-Policy blocking scripts, plugins, frames,<base>rewrites and form submissions, and by default every remote fetch β images, stylesheets, fonts, media and the rest (default-src 'none') β so a tracking pixel or stylesheet cannot report when and where the copy is opened (inline styles and images still render); a sender's<meta http-equiv="refresh">is neutralized. Allow remote content per call withallowRemoteImages: trueor globally withGMAIL_MCP_EXPORT_REMOTE_IMAGES=true.send_draftrecipient check β the staged draft is read before sending, and a To / Cc / Bcc header that does not parse as a list of addresses is refused, so a draft's destination is always known.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_draft/send_draftfamily. Addresses are compared bare (Name <address>matches the pairedaddress). While the gate is on, the model cannot extend the list through gmail-mcp:pair_recipient addasks the human to confirm through the MCP client (elicitation) and, where the client cannot ask, refuses and names the terminal command βnpx @klodr/gmail-mcp pair add <address>(alsopair remove,pair list). Otherwise a hostile email could tell the agent to pair its own address, then write to it. The guarantee covers the gmail-mcp surface: it holds as long as the MCP client does not also let the model run terminal commands (such asnpx @klodr/gmail-mcp pair add) or writepaired.jsonwithout your approval.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 |
|
| Every attachment path ( |
Download jail |
|
|
|
OAuth keys path |
|
| Google Desktop/Web OAuth client credentials. |
Credentials path |
|
| Access/refresh tokens. File mode |
Rate limit state dir |
|
| Where the rolling call-history for rate limiting is persisted ( |
Rate limit overrides |
| see below | Override the per-bucket daily/monthly caps. Buckets: |
Rate limit disable |
| unset (limiter active) | Kill-switch for the entire limiter. Use only for test suites or controlled batch operations. |
Recipient pairing |
| unset (no allowlist) | Sends only to paired addresses ( |
Pairing confirmation wait |
| 10 minutes | How long |
Quarantine of downloads |
| on | macOS |
Remote content (images, stylesheets, fonts, media) in exported HTML |
| blocked | Default for |
Audit log |
| unset (no audit trail) | Opt-in append-only JSONL log of every tool call (name, redacted args, outcome). File mode |
Dry-run |
| unset (real calls) | When |
π οΈ Tools
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,download_all_attachments,batch_modify_emails,batch_delete_emails,reply_all,reply_to_email,forward_emailDrafts β
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_threadLabels β
list_email_labels,create_label,update_label,delete_label,get_or_create_labelFilters β
list_filters,get_filter,create_filter,delete_filter,create_filter_from_templateRecipient 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 query syntax
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 after:2026/01/01 has:attachment. Full reference: Google's Gmail search operators cheat sheet. A query that matches nothing returns No messages found for query: <query> rather than an empty result.
πΊοΈ Roadmap
See ROADMAP.md.
π Ecosystem
Other MCP servers in the klodr family
π§ klodr/gmail-mcp β Gmail (you are here)
π klodr/faxdrop-mcp β Send real faxes via FaxDrop
π¦ klodr/mercury-invoicing-mcp β Mercury banking + invoicing
Wider Gmail-MCP landscape
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.
π€ Contributing
See CONTRIBUTING.md for the test / build / lint checklist and release process.
π Security
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.
π Project continuity
See CONTINUITY.md for the handover plan if the maintainer becomes unavailable.
π License
MIT β see LICENSE.
π History
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.
Maintenance
Related MCP Connectors
Email safety MCP server. Detects phishing, prompt injection, CEO fraud for AI agents.
Programmable email inbox for AI agents β JMAP, PoW auth, stdio MCP server.
Email infrastructure for AI agents β send, receive, search, and reply to email over MCP.
Hosted MCP server for your Gmail, Outlook and IMAP mail, with mailboxes and scopes set per agent.
Related MCP Servers
- AlicenseAqualityAmaintenanceEnables AI assistants to send real faxes through the FaxDrop API. Provides safeguards including rate limiting, dry-run mode, and audit logging for secure fax operations.3255 npm5MIT
- AlicenseAqualityAmaintenanceMercury Banking MCP server with full Invoicing API support. Read accounts/transactions and create/manage recurring invoices via Model Context Protocol.39313 npm4MIT
- AlicenseAqualityBmaintenanceLocal-first Gmail MCP server using PKCE + loopback OAuth flow that stores refresh tokens in the OS keychain, enabling secure read/write Gmail access via MCP tools without tokens leaving the device.20MIT
- AlicenseNot gradedqualityDmaintenanceA self-hostable Gmail MCP server that enables Gmail search/read, sending, replies, drafts, labels, and attachment downloads via MCP tools with OAuth authorization. Supports stdio and streamable HTTP transports.MIT