Content
# 📧 gmail-mcp
> Read, search, send, draft, label, filter, and thread Gmail from any MCP-enabled AI assistant. Wraps the [Gmail API](https://developers.google.com/gmail/api) with scope-gated tools and in-process safeguards.
[](https://github.com/klodr/gmail-mcp/actions/workflows/ci.yml)
[](https://github.com/klodr/gmail-mcp/actions/workflows/codeql.yml)
[](https://vitest.dev)
[](https://codecov.io/gh/klodr/gmail-mcp)
[](https://scorecard.dev/viewer/?uri=github.com/klodr/gmail-mcp)
[](https://www.bestpractices.dev/projects/12613)
[](https://socket.dev/npm/package/@klodr/gmail-mcp)
[](https://coderabbit.ai)
[](https://www.npmjs.com/package/@klodr/gmail-mcp)
[](https://www.npmjs.com/package/@klodr/gmail-mcp)
[](https://nodejs.org)
[](https://modelcontextprotocol.io)
[](https://modelcontextprotocol.io)
[](https://github.com/klodr/gmail-mcp/pulls)
[](https://github.com/sponsors/klodr)
[](https://www.patreon.com/klodr)
[](https://ko-fi.com/klodr)
> [!NOTE]
> Hardened + enhanced fork of [GongRzhe/Gmail-MCP-Server](https://github.com/GongRzhe/Gmail-MCP-Server) (archived 2026-03-03), via [ArtyMcLabin/Gmail-MCP-Server](https://github.com/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](.github/SECURITY.md) for the controls and threat model, and the [comparison table](#-why-this-mcp) 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`](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](https://github.com/GongRzhe/Gmail-MCP-Server) (original, unmaintained) | [ArtyMcLabin/Gmail-MCP-Server](https://github.com/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`) | ❌ | ⚠️ partial | ✅ |
| 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) | ⚠️ sporadic | ✅ daily review cycle (CodeRabbit + human) |
| **Supply-chain integrity** | | | |
| Node.js floor | ❌ `>=14` ([EOL April 2023](https://nodejs.org/en/about/previous-releases)) | ❌ `>=14` ([EOL April 2023](https://nodejs.org/en/about/previous-releases)) | ✅ `>=22` (Maintenance LTS 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) | ⚠️ (97 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.
## 📦 Installation
```bash
npm install -g @klodr/gmail-mcp
```
Or directly via `npx`:
```bash
npx -y @klodr/gmail-mcp
```
Requires **Node.js 22+**.
## ⚙️ Configuration
### 1️⃣ Google Cloud OAuth credentials
1. Open the [Google Cloud Console](https://console.cloud.google.com/).
2. Create a project and enable the **Gmail API**.
3. Under **APIs & Services → Credentials**, create an **OAuth 2.0 Client ID** (Desktop or Web). For Web, add `http://localhost:3000/oauth2callback` to the authorized redirect URIs.
4. Download the JSON, rename it to `gcp-oauth.keys.json`, place it at `~/.gmail-mcp/gcp-oauth.keys.json` (or override with `GMAIL_OAUTH_PATH=/abs/path/gcp-oauth.keys.json`).
### 2️⃣ Authenticate (once)
```bash
npx -y @klodr/gmail-mcp auth --scopes=gmail.readonly
```
Always 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
```json
{
"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](./llms-install.md) for an LLM-readable install guide.
## 🔑 OAuth scopes
| 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:
```bash
# 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`** — resolves the `from` parameter 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 with `GMAIL_MCP_RATE_LIMIT_DISABLE=true` for 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 when `GMAIL_MCP_RECIPIENT_PAIRING=true`; gates the `send_email` / `reply_*` / `forward_email` / `draft_email` / `update_draft` family 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 when `GMAIL_MCP_AUDIT_LOG=/abs/path/audit.jsonl` is 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. |
## 🛠️ 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`, `batch_modify_emails`, `batch_delete_emails`, `reply_all`, `reply_to_email`, `forward_email`
- **Drafts** — `list_drafts`, `get_draft`, `update_draft`, `delete_draft`, `send_draft` (full `users.drafts.*` surface; `draft_email` above 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.json` allowlist when `GMAIL_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@example.com after:2026/01/01 has:attachment`. Full reference: [Google's Gmail search operators cheat sheet](https://support.google.com/mail/answer/7190).
## 🗺️ Roadmap
See [ROADMAP.md](docs/ROADMAP.md).
## 🌐 Ecosystem
### Other MCP servers in the klodr family
- 📧 [klodr/gmail-mcp](https://github.com/klodr/gmail-mcp) — Gmail (you are here)
- 📠 [klodr/faxdrop-mcp](https://github.com/klodr/faxdrop-mcp) — Send real faxes via FaxDrop
- 🏦 [klodr/mercury-invoicing-mcp](https://github.com/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](./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](.github/CONTRIBUTING.md) for the test / build / lint checklist and release process.
## 🔒 Security
See [SECURITY.md](.github/SECURITY.md) for the vulnerability-reporting process and the current security model, and [ASSURANCE_CASE.md](docs/ASSURANCE_CASE.md) for the threat model, trust boundaries, and CWE/OWASP mitigation table.
## 📋 Project continuity
See [CONTINUITY.md](docs/CONTINUITY.md) for the handover plan if the maintainer becomes unavailable.
## 📄 License
MIT — see [LICENSE](./LICENSE).
## 📜 History
`klodr/gmail-mcp` is the maintenance fork of a two-step upstream chain:
- **[GongRzhe/Gmail-MCP-Server](https://github.com/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](https://github.com/ArtyMcLabin/Gmail-MCP-Server)** — Arty MacKiewicz's active fork. Since divergence from Gong, it merged community contributions including: reply-all ([#3](https://github.com/ArtyMcLabin/Gmail-MCP-Server/pull/3) by @MaxGhenis), `list_filters` fix ([#4](https://github.com/ArtyMcLabin/Gmail-MCP-Server/pull/4) by @nicholas-anthony-ai), CI/CD shell-injection hardening ([#9](https://github.com/ArtyMcLabin/Gmail-MCP-Server/pull/9) by @JF10R), `download_email` ([#13](https://github.com/ArtyMcLabin/Gmail-MCP-Server/pull/13) by @icanhasjonas), tool annotations ([#14](https://github.com/ArtyMcLabin/Gmail-MCP-Server/pull/14) by @bryankthompson), CC/BCC fields in `read_email` ([#21](https://github.com/ArtyMcLabin/Gmail-MCP-Server/pull/21) by @panghy), and draft lifecycle tools `send_draft` / `delete_draft` / `update_draft` ([#30](https://github.com/ArtyMcLabin/Gmail-MCP-Server/pull/30) by @thisisambros). Reply-threading auto-resolution and the `--scopes` flag 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.
MCP Config
Below is the configuration for this MCP Server. You can copy it directly to Cursor or other MCP clients.
mcp.json
Connection Info
You Might Also Like
cc-switch
All-in-One Assistant for Claude Code, Codex & Gemini CLI across platforms.
awesome-mcp-servers
A collection of MCP servers.
git
A Model Context Protocol server for Git automation and interaction.
oh-my-opencode
Background agents · Curated agents like oracle, librarians, frontend...
TrendRadar
TrendRadar: Your hotspot assistant for real news in just 30 seconds.
Appwrite
Build like a team of hundreds