Gmail Manager

by arthjhon

Not rated
GitHub

About

Gmail MCP server (33 tools) with optional recipient allowlist and local audit log

Details

Author
arthjhon
Categories
Productivity, Other, Automation, Communication

Google Cloud setup (one-time, ~10 minutes)

- Go to](https://peps.python.org/pep-0668/)Google Cloud Consoleand create a new project (or pick an existing one). - Enable theGmail API(not "Gmail MCP API" — that's Google's own remote MCP; not what we want). - Configure theOAuth consent screen:

- User type:Internalif your account is part of a Google Workspace (no token expiration); otherwiseExternalin Testing mode (up to 100 users, refresh tokensexpire every 7 days— seeToken expirationbelow).
- Scopes: addhttps://www.googleapis.com/auth/gmail.modifyandhttps://www.googleapis.com/auth/gmail.settings.basic.Do notadd anything else.
- Test users (External only): add the Gmail address you'll authenticate with.

- Application type:Desktop app
- Download the JSON. Save it ascredentials.json.

Move your credentials into the config directory (default~/.config/mcp-gmail-manager/):

mkdir -p ~/.config/mcp-gmail-manager mv ~/Downloads/client_secret_.json ~/.config/mcp-gmail-manager/credentials.json chmod 600 ~/.config/mcp-gmail-manager/credentials.json

This binds tolocalhost:8765and prints a Google authorisation URL. Open it in a browseron a machine that can reachlocalhost:8765on the auth host:

- Local desktop: the printed URL works directly.
- Remote / headless server: forward the port from your laptop first:

ssh -L 8765:localhost:8765 user@your-server

Authorise with the Google account that will own outbound mail. On success the script writestoken.jsonand exits.

The refresh token's lifetime depends on how the OAuth consent screen is configured:

When the refresh token expires in Testing mode you'll seeinvalid_grantorToken has been expired or revokederrors. To recover:

rm ~/.config/mcp-gmail-manager/token.json mcp-gmail-manager-auth

Takes ~30 seconds. Yourcredentials.jsonisnotaffected — only the user's token.

- Workspace users: configure the consent screen asInternalinstead of External. Token never expires.
- Personal Gmail users: weekly re-auth is the only practical option today. Production verification forgmail.modifyrequires a Google security assessment (paid, weeks of process) — not feasible for most personal projects.
- Set a calendar reminderor a cron job to nudge you weekly. A future release may add proactive in-tool warnings before expiry.

claude mcp add gmail-manager -- mcp-gmail-manager

If installed in a manual venv that isn't on$PATH:

claude mcp add gmail-manager -- ~/.venv-mcp-gmail/bin/mcp-gmail-manager

Restart your Claude Code session so the new tool schemas load.

Each MCP instance handlesoneGmail account. To use several accounts from the same Claude Code session (e.g. personal + work), register the MCPonce per accountwith a distinctGMAIL_MCP_CONFIG_DIR. Each instance gets its own credentials, token, audit log, and config — fully isolated.

# 1. Dedicated config directory mkdir -p ~/.config/mcp-gmail-<name> && chmod 700 ~/.config/mcp-gmail-<name> # 2. Reuse the same OAuth client (one credentials.json works for any user in the same GCP project) cp ~/.config/mcp-gmail-<other>/credentials.json ~/.config/mcp-gmail-<name>/ chmod 600 ~/.config/mcp-gmail-<name>/credentials.json # 3. Authenticate with the target Gmail account GMAIL_MCP_CONFIG_DIR=$HOME/.config/mcp-gmail-<name> mcp-gmail-manager-auth # 4. Register with the env override claude mcp add gmail-<name> -s user \ -e GMAIL_MCP_CONFIG_DIR=$HOME/.config/mcp-gmail-<name> \ -- mcp-gmail-manager

Restart Claude Code. The tools appear under separate namespaces:

- mcp__gmail-personal__send_email→ sends from the personal account
- mcp__gmail-work__send_email→ sends from the work account

You can prompt Claude with "send via gmail-work" and it picks the right namespace.

Each<config_dir>/config.jsonis independent. Useful patterns:

// ~/.config/mcp-gmail-work/config.json — strict allowlist { "allowlist": { "enabled": true, "domains": ["yourcompany.com"] } }
// ~/.config/mcp-gmail-personal/config.json — silence the audit log { "audit_log": { "enabled": false } }

Compromise of one account's token does not leak the other — each lives in a separate directory withchmod 600.

~/.config/mcp-gmail-manager/config.jsonis optional — if it doesn't exist, sensible defaults apply (no allowlist, audit log enabled). Two ready-to-copy examples are provided:

- examples/config.example.jsonhardened defaults(recommended starting point). Every guardrail on; allowlist enabled but empty, so the startup warning will point you at what to configure first.
-
examples/config.with-allowlist.json— fully-populated institutional example with placeholder domains.
-
examples/config.permissive.jsonexplicit opt-outfor users who want no guardrails (allowlist off, content scan off, rate limit off, no send confirmation). Consider this only if you understand the blast radius.

{ "allowlist": { "enabled": false, "domains": [], "emails": [] }, "audit_log": { "enabled": true, "include_reads": false, "path": null }, "attachments": { "max_total_bytes": 20971520, "allowed_paths": [], "deny_patterns": [], "use_default_deny_patterns": true } }

Runmcp-gmail-manager-verify-logto walk the hash chain and confirm no entry has been edited or removed:

mcp-gmail-manager-verify-log # verify the active log mcp-gmail-manager-verify-log ~/.config/.../audit.jsonl.1 # verify a rotated backup

Exit codes:0OK,1log not found,2malformed JSON,3chain broken.

- Threat model: this MCP is primarily hardened against amisbehaving LLM— prompt injection, hallucinated recipients, tricked-into-exfil scenarios. It is NOT a substitute for host security; an attacker with local access can readtoken.jsonand call Gmail directly, bypassing every guardrail here.
- Token storage:token.jsonis writtenchmod 600. Treat it as a password.
- No remote attestation: this server runs entirely on your machine. No telemetry, no third-party calls beyondgoogleapis.com.
- OAuth scope is deliberately narrow-ish:gmail.modifycovers send/read/label/trash/drafts. It does NOT requestmail.google.com, so permanent delete is unavailable — deletes go to Trash and can be undone withuntrash_
. If you only need to send, fork and replace the scope withgmail.send.
- Recipient guardrails cover forward-in-filters:create_filterwith anaction.forwardtargeting a non-allowlisted address is rejected. Filters were a common bypass of send-only allowlists.
- Read tools mark content as untrusted: bodies and snippets are wrapped in<untrusted-email-content>...</untrusted-email-content>. Tool descriptions instruct downstream LLMs to treat wrapped content as data. Any occurrence of the closing tag inside a message body is escaped to prevent break-out.
- Default attachment deny set(source and destination) covers common credential / secret paths:~/.ssh/,~/.aws/,~/.gnupg/,~/.docker/config.json,~/.kube/,.env,.env.,credentials.json,token.json,id_rsa/id_ed25519/id_ecdsa/id_dsa,.git-credentials,.netrc,wallet.dat,.bash_history,.zsh_history,~/.mozilla//logins.json,authorized_keys,known_hosts. Extend viaattachments.deny_patternsor narrow further viaattachments.allowed_paths.
- Audit log is tamper-evident, not tamper-proof: each entry includesprev_hash = sha256(previous line). Partial modification breaks the chain and is detectable. A full log rewrite by an attacker with file-write is NOT prevented — pair with off-host log shipping (roadmap) for stronger guarantees.
- What is NOT mitigated: rate limiting (a compromised agent can burn Gmail quota fast), outbound content pattern scanning (no secret regex on bodies), signature/vacation phishing (allowlist doesn't cover their content), full log rewrite by a local attacker. SeeSECURITY.mdfor the current threat model and roadmap.

- OAuth "Production" verification forgmail.modifyrequires a paid Google security assessment. Stay in "Internal" (Workspace, no expiration) or "Testing" (≤ 100 users,7-day refresh token rotation— seeToken expiration) to avoid this.
- HTML email body composition is not exposed as a first-class field. Send viacreate_draft+ manual HTML editing in the Gmail UI, or extend_build_mimein a fork.
- Push notifications (Pub/Subwatch/stop) not implemented — out of scope.

Issues and PRs welcome. Keep changes scoped, document any new tool with a schema example, and add an audit-log entry for anything that mutates state.

Query live Gmail data using LLMs via CData's read-only MCP server.

Search and delete emails in your Gmail account.

Enables AI assistants to manage Gmail through natural language interactions.

A standardized interface for managing, sending, and retrieving emails through the Gmail API.

Manage your Gmail account, including sending, reading, and organizing emails.

A super simple and tiny MCP server for gmail in python

Allows AI agents to search Gmail threads, learn your writing style, and draft emails.

An MCP server that enables AI models to interact directly with the Gmail API to manage emails.

An MCP server for integrating with the Gmail API to manage emails.

Most email MCP servers only read from IMAP. mail-mcp does everything: 30 tools for reading, searching, sending, replying, forwarding, and bulk operations across IMAP, SMTP, Microsoft Graph API, and Exchange Web Services. Multi-account, native OAuth2, built in Rust. Works with Gmail, Microsoft 365, Hotmail/Outlook.com, Zoho, and any standard IMAP/SMTP server.

A comprehensive GmailModel Context Protocolserver:35 toolscovering send/preview/confirm, reply, forward, drafts, search, read, attachments, trash, labels, filters, signature, and vacation responder.

Defence-in-depth features that distinguish it from other Gmail MCPs:

- Tamper-evident audit log(on by default) — every write/send/modify/download appends a JSON line toaudit.jsonl, chained by SHA-256 so partial tampering is detectable. Includes log rotation, startup chain verification, optional read auditing, and amcp-gmail-manager-verify-logCLI.
- Recipient allowlist(off by default) — when enabled, every outbound operation (send_email,create_draft,reply_to_message,forward_message,create_filterwith aforwardaction, plus embedded addresses in signature and vacation body) checks recipients against configured domains and explicit addresses.
- Attachment path allowlist + denylist(denylist on by default) — the MCP refuses to attach or overwrite obvious credential files (~/.ssh/,~/.aws/,id_rsa,.env,token.json, etc.), closing the "LLM exfils SSH key as attachment" attack. See
Security notesfor the full default deny set.
- Prompt-injection tainted-content markers— read tools (get_message,get_thread,search_threads,list_drafts) wrap message bodies and snippets in<untrusted-email-content>...</untrusted-email-content>tags. Tool descriptions instruct the LLM to treat wrapped content as data, not instructions.
- Outbound content scanning(off by default) — regex-based detection of secrets in the subject/body/signature/vacation content (AWS access keys, Stripe/OpenAI/Anthropic/GitHub/GitLab/Google/Twilio tokens, PEM private keys, JWTs, credentials embedded in URLs). Blocks the send before it hits Gmail if a pattern matches.
- Preview + confirm send flow(off by default) —preview_send_emailruns every guardrail and stores the payload;confirm_send_email(preview_id)delivers it. Whensend_confirmation.required=true, directsend_emailis disabled so a compromised LLM cannot "preview X, then send Y".
- Rate limiting(off by default) — cap outbound sends per hour to stop runaway agent loops from burning Gmail quota.
- Least-privilege OAuth scopes— requestsgmail.modify+gmail.settings.basiconly. Does NOT requestmail.google.com, so permanent delete is intentionally unavailable.

Seeexamples/config.with-allowlist.jsonfor an institutional-mode configuration.

OAuth scopes requested:gmail.modify+gmail.settings.basic. Doesnotrequest thehttps://mail.google.com/superuser scope — permanent delete is intentionally unsupported.

- Python ≥ 3.10
- A Google Cloud project with the Gmail API enabled and an OAuth 2.0 client (Desktop type)
- A way to forwardlocalhost:8765to your auth host (typicallyssh -L 8765:localhost:8765 user@host)

📖Prefer a step-by-step tutorial with screenshots for every stage of the Google Cloud setup?See theInstallation Guide. The section below covers only the package install; the full guide walks through GCP, credentials, OAuth, VM setup, and Claude Code registration.

Supported on Linux, macOS, and Windows. Recommended path ispipx, which installs the CLI into an isolated venv and exposes the entry points on$PATH.

Linux (Debian / Ubuntu / Mint / Fedora / Arch)

sudo apt install pipx # Debian / Ubuntu / Mint sudo dnf install pipx # Fedora sudo pacman -S python-pipx # Arch pipx ensurepath # adds ~/.local/bin to PATH # reopen shell or: source ~/.bashrc pipx install mcp-gmail-manager
brew install pipx # or: python3 -m pip install --user pipx pipx ensurepath # adds ~/.local/bin to PATH # reopen shell or: source ~/.zshrc pipx install mcp-gmail-manager
# If you don't have Python yet: winget install --id Python.Python.3.12 python -m pip install --user pipx python -m pipx ensurepath # close and reopen PowerShell pipx install mcp-gmail-manager

Windows caveats — everything works, with three notes:

- Token file permissions.On Linux/macOS the MCP writestoken.jsonwithchmod 0o600. On Windows there is no POSIX chmod, so the file inherits your%USERPROFILE%ACL — protected against other user accounts, but any process running asyouruser can read it. Same effective posture as most Windows CLI tools that store OAuth tokens.
- Attachment path deny list works.As of v0.3.2 the deny/allow-list matching normalises paths to forward-slash form viaPath.as_posix(), so a Windows path likeC:\Users\me\.ssh\id_rsais correctly caught by the default~/.ssh/deny pattern. Confirmed by the smoke suite on both platforms.
- Port 8765 may be reserved by Windows.Hyper-V, WSL2, and Docker Desktop reserve dynamic port ranges that sometimes include 8765, givingbind
[127.0.0.1]:8765: Permission deniedon the local end of an SSH-Lforward. Check withnetsh interface ipv4 show excludedportrange protocol=tcp. If 8765 is reserved, setGMAIL_MCP_AUTH_PORTto a free port on both ends (v0.3.3+):set GMAIL_MCP_AUTH_PORT=18765on the server before runningmcp-gmail-manager-auth, and forward that same port:ssh -L 18765:localhost:18765 user@server.

python3 -m venv ~/.venv-mcp-gmail ~/.venv-mcp-gmail/bin/pip install mcp-gmail-manager # Windows: python -m venv %USERPROFILE%\.venv-mcp-gmail # Use the absolute path when registering with Claude Code (see below)

Why not plainpip installsystem-wide?On modern Debian-based distros and Homebrew Python it fails witherror: externally-managed-environment(PEP 668) — the OS protects its Python. The pipx and venv methods above are the canonical workarounds.

git clone https://github.com/arthjhon/mcp-gmail-manager.git cd mcp-gmail-manager pipx install .

Google Cloud setup (one-time, ~10 minutes)

- Go to
Google Cloud Consoleand create a new project (or pick an existing one). - Enable theGmail API(not "Gmail MCP API" — that's Google's own remote MCP; not what we want). - Configure theOAuth consent screen:

- User type:Internalif your account is part of a Google Workspace (no token expiration); otherwiseExternalin Testing mode (up to 100 users, refresh tokensexpire every 7 days— seeToken expirationbelow).
- Scopes: addhttps://www.googleapis.com/auth/gmail.modifyandhttps://www.googleapis.com/auth/gmail.settings.basic.Do notadd anything else.
- Test users (External only): add the Gmail address you'll authenticate with.

- Application type:Desktop app
- Download the JSON. Save it ascredentials.json.

Move your credentials into the config directory (default~/.config/mcp-gmail-manager/):

mkdir -p ~/.config/mcp-gmail-manager mv ~/Downloads/client_secret_.json ~/.config/mcp-gmail-manager/credentials.json chmod 600 ~/.config/mcp-gmail-manager/credentials.json

This binds tolocalhost:8765and prints a Google authorisation URL. Open it in a browseron a machine that can reachlocalhost:8765on the auth host:

- Local desktop: the printed URL works directly.
- Remote / headless server: forward the port from your laptop first:

ssh -L 8765:localhost:8765 user@your-server

Authorise with the Google account that will own outbound mail. On success the script writestoken.jsonand exits.

The refresh token's lifetime depends on how the OAuth consent screen is configured:

When the refresh token expires in Testing mode you'll seeinvalid_grantorToken has been expired or revokederrors. To recover:

rm ~/.config/mcp-gmail-manager/token.json mcp-gmail-manager-auth

Takes ~30 seconds. Yourcredentials.jsonisnotaffected — only the user's token.

- Workspace users: configure the consent screen asInternalinstead of External. Token never expires.
- Personal Gmail users: weekly re-auth is the only practical option today. Production verification forgmail.modifyrequires a Google security assessment (paid, weeks of process) — not feasible for most personal projects.
- Set a calendar reminderor a cron job to nudge you weekly. A future release may add proactive in-tool warnings before expiry.

claude mcp add gmail-manager -- mcp-gmail-manager

If installed in a manual venv that isn't on$PATH:

claude mcp add gmail-manager -- ~/.venv-mcp-gmail/bin/mcp-gmail-manager

Restart your Claude Code session so the new tool schemas load.

Each MCP instance handlesoneGmail account. To use several accounts from the same Claude Code session (e.g. personal + work), register the MCPonce per accountwith a distinctGMAIL_MCP_CONFIG_DIR. Each instance gets its own credentials, token, audit log, and config — fully isolated.

# 1. Dedicated config directory mkdir -p ~/.config/mcp-gmail-<name> && chmod 700 ~/.config/mcp-gmail-<name> # 2. Reuse the same OAuth client (one credentials.json works for any user in the same GCP project) cp ~/.config/mcp-gmail-<other>/credentials.json ~/.config/mcp-gmail-<name>/ chmod 600 ~/.config/mcp-gmail-<name>/credentials.json # 3. Authenticate with the target Gmail account GMAIL_MCP_CONFIG_DIR=$HOME/.config/mcp-gmail-<name> mcp-gmail-manager-auth # 4. Register with the env override claude mcp add gmail-<name> -s user \ -e GMAIL_MCP_CONFIG_DIR=$HOME/.config/mcp-gmail-<name> \ -- mcp-gmail-manager

Restart Claude Code. The tools appear under separate namespaces:

- mcp__gmail-personal__send_email→ sends from the personal account
- mcp__gmail-work__send_email→ sends from the work account

You can prompt Claude with "send via gmail-work" and it picks the right namespace.

Each<config_dir>/config.jsonis independent. Useful patterns:

// ~/.config/mcp-gmail-work/config.json — strict allowlist { "allowlist": { "enabled": true, "domains": ["yourcompany.com"] } }
// ~/.config/mcp-gmail-personal/config.json — silence the audit log { "audit_log": { "enabled": false } }

Compromise of one account's token does not leak the other — each lives in a separate directory withchmod 600.

~/.config/mcp-gmail-manager/config.jsonis optional — if it doesn't exist, sensible defaults apply (no allowlist, audit log enabled). Two ready-to-copy examples are provided:

- examples/config.example.jsonhardened defaults(recommended starting point). Every guardrail on; allowlist enabled but empty, so the startup warning will point you at what to configure first.
-
examples/config.with-allowlist.json— fully-populated institutional example with placeholder domains.
-
examples/config.permissive.jsonexplicit opt-outfor users who want no guardrails (allowlist off, content scan off, rate limit off, no send confirmation). Consider this only if you understand the blast radius.

{ "allowlist": { "enabled": false, "domains": [], "emails": [] }, "audit_log": { "enabled": true, "include_reads": false, "path": null }, "attachments": { "max_total_bytes": 20971520, "allowed_paths": [], "deny_patterns": [], "use_default_deny_patterns": true } }

Runmcp-gmail-manager-verify-logto walk the hash chain and confirm no entry has been edited or removed:

mcp-gmail-manager-verify-log # verify the active log mcp-gmail-manager-verify-log ~/.config/.../audit.jsonl.1 # verify a rotated backup

Exit codes:0OK,1log not found,2malformed JSON,3chain broken.

- Threat model: this MCP is primarily hardened against amisbehaving LLM— prompt injection, hallucinated recipients, tricked-into-exfil scenarios. It is NOT a substitute for host security; an attacker with local access can readtoken.jsonand call Gmail directly, bypassing every guardrail here.
- Token storage:token.jsonis writtenchmod 600. Treat it as a password.
- No remote attestation: this server runs entirely on your machine. No telemetry, no third-party calls beyondgoogleapis.com.
- OAuth scope is deliberately narrow-ish:gmail.modifycovers send/read/label/trash/drafts. It does NOT requestmail.google.com, so permanent delete is unavailable — deletes go to Trash and can be undone withuntrash_
. If you only need to send, fork and replace the scope withgmail.send.
- Recipient guardrails cover forward-in-filters:create_filterwith anaction.forwardtargeting a non-allowlisted address is rejected. Filters were a common bypass of send-only allowlists.
- Read tools mark content as untrusted: bodies and snippets are wrapped in<untrusted-email-content>...</untrusted-email-content>. Tool descriptions instruct downstream LLMs to treat wrapped content as data. Any occurrence of the closing tag inside a message body is escaped to prevent break-out.
- Default attachment deny set(source and destination) covers common credential / secret paths:~/.ssh/,~/.aws/,~/.gnupg/,~/.docker/config.json,~/.kube/,.env,.env.,credentials.json,token.json,id_rsa/id_ed25519/id_ecdsa/id_dsa,.git-credentials,.netrc,wallet.dat,.bash_history,.zsh_history,~/.mozilla//logins.json,authorized_keys,known_hosts. Extend viaattachments.deny_patternsor narrow further viaattachments.allowed_paths.
- Audit log is tamper-evident, not tamper-proof: each entry includesprev_hash = sha256(previous line). Partial modification breaks the chain and is detectable. A full log rewrite by an attacker with file-write is NOT prevented — pair with off-host log shipping (roadmap) for stronger guarantees.
- What is NOT mitigated: rate limiting (a compromised agent can burn Gmail quota fast), outbound content pattern scanning (no secret regex on bodies), signature/vacation phishing (allowlist doesn't cover their content), full log rewrite by a local attacker. SeeSECURITY.mdfor the current threat model and roadmap.

- OAuth "Production" verification forgmail.modifyrequires a paid Google security assessment. Stay in "Internal" (Workspace, no expiration) or "Testing" (≤ 100 users,7-day refresh token rotation— seeToken expiration) to avoid this.
- HTML email body composition is not exposed as a first-class field. Send viacreate_draft+ manual HTML editing in the Gmail UI, or extend_build_mimein a fork.
- Push notifications (Pub/Subwatch/stop) not implemented — out of scope.

Issues and PRs welcome. Keep changes scoped, document any new tool with a schema example, and add an audit-log entry for anything that mutates state.

Query live Gmail data using LLMs via CData's read-only MCP server.

Search and delete emails in your Gmail account.

Enables AI assistants to manage Gmail through natural language interactions.

A standardized interface for managing, sending, and retrieving emails through the Gmail API.

Manage your Gmail account, including sending, reading, and organizing emails.

A super simple and tiny MCP server for gmail in python

Allows AI agents to search Gmail threads, learn your writing style, and draft emails.

An MCP server that enables AI models to interact directly with the Gmail API to manage emails.

An MCP server for integrating with the Gmail API to manage emails.

Most email MCP servers only read from IMAP. mail-mcp does everything: 30 tools for reading, searching, sending, replying, forwarding, and bulk operations across IMAP, SMTP, Microsoft Graph API, and Exchange Web Services. Multi-account, native OAuth2, built in Rust. Works with Gmail, Microsoft 365, Hotmail/Outlook.com, Zoho, and any standard IMAP/SMTP server.

No reviews yet — be the first

Sign in to leave a review

Use Google, GitHub, or an email account so ratings stay tied to real people.

Email sign in

No reviews posted yet.