Skip to main content
Glama
Helmo21

gmail-mcp

by Helmo21

gmail-mcp

A local MCP server that gives Claude access to several Gmail accounts at once — search, read, draft, send, label, and a scheduled cross-inbox digest — without switching mailboxes.

Runs entirely on your machine. Nothing is hosted; mail goes only to Google.

The problem

Running several mailboxes — personal, sales, support, billing — means switching between them all day. AI assistants don't fix it: they connect to one account, or take broad access with no way to say this one may send, that one may not. Unrestricted send access to a client-facing address is why most people never connect one at all.

Related MCP server: Gmail & Calendar Multi-Account MCP Server

Who it's for

Freelancers, agencies and small teams with several Gmail or Workspace mailboxes who want an assistant across all of them, and a hard guarantee about what it can send.

What it does

Ten tools, each taking an explicit account alias so the mailbox is never ambiguous.

Tool

Purpose

list_accounts

Aliases, addresses, send policies

search_mail

Gmail query syntax against one account

read_message / read_thread

Full body; threads with quotes collapsed

create_draft / send_message / send_draft

Compose, gated by policy

list_labels / modify_labels

Labels — also marks read and archives

check_inboxes

New unread across every account, for scheduled use

Send safety is the core design decision. Each mailbox declares send, confirm, or draft_only in config, enforced in code rather than in a prompt — so neither the model nor anything written into the mail it reads can talk it out of it. check_inboxes reads untrusted email on a schedule, which is exactly why that control can't live in an instruction.

Architecture

flowchart TD
    H["Claude Desktop · Claude Code"] -->|stdio| S["server.py<br/>10 tools — validate, delegate, shape"]
    S --> P{"send policy gate<br/>send · confirm · draft_only"}
    P -->|blocked| D["draft"]
    P -->|allowed| T["transmit"]
    S --> G["gmail/<br/>search · messages · compose · labels"]
    S --> C["check.py<br/>parallel watermarked sweep"]
    G --> CL["client.py<br/>retry · error mapping · per-account cache"]
    C --> CL
    T --> CL
    CL --> A["auth.py — OAuth refresh"]
    A --> ST["storage.py<br/>OS keyring, or 0600 file"]
    CL --> API[("Gmail API — gmail.modify")]

Every Gmail call passes through one choke point owning retries and error mapping, and each account gets its own service object — which is what makes the parallel sweep safe. check_inboxes keeps a per-account watermark, so a scheduled run reports only genuinely new mail.

Setup

Full guide: docs/SETUP.md.

1. Google Cloud — enable the Gmail API, configure an OAuth consent screen (Internal for a Workspace domain, External for consumer Gmail), add the single scope gmail.modify, create a Desktop app OAuth client, and save the JSON as client_secret.json in your config directory.

2. Install

git clone <this repo> && cd gmail-mcp
uv sync
cp config.example.toml <config-dir>/config.toml   # then edit it
uv run gmail-mcp auth add <alias>                 # once per account
uv run gmail-mcp doctor                           # verify every account

OS

Config directory

Linux

~/.config/gmail-mcp/

macOS

~/Library/Application Support/gmail-mcp/

Windows

%LOCALAPPDATA%\gmail-mcp\

3a. Claude Code — any OS:

claude mcp add gmail --scope user -- \
  /absolute/path/to/uv --directory /absolute/path/to/gmail-mcp run gmail-mcp serve

3b. Claude Desktop — add to claude_desktop_config.json:

{
  "mcpServers": {
    "gmail": {
      "command": "/absolute/path/to/uv",
      "args": ["--directory", "/absolute/path/to/gmail-mcp",
               "run", "gmail-mcp", "serve"]
    }
  }
}

OS

Config file

macOS

~/Library/Application Support/Claude/claude_desktop_config.json

Windows

%APPDATA%\Claude\claude_desktop_config.json

Linux

~/.config/Claude/claude_desktop_config.json

Absolute paths in both: neither host inherits a shell PATH, and a bare command name is the usual reason a server silently fails to start.

Demo

📹 Recording: add link here

Local server, so no hosted instance. Two moves:

  1. "Check all my inboxes and tell me what needs my attention." Ask again — nothing new, because each account remembers where it got to.

  2. "Send this from my personal account." It returns a draft and names the policy that stopped it.

Design notes

  • Least privilege. One scope, gmail.modify — which cannot permanently delete mail. Chosen, not inherited.

  • Credentials live in the OS keyring (Keychain, Credential Manager, Secret Service), falling back to a 0600 file. Deliberately not encrypted: the server starts unattended, so any key would sit beside the ciphertext.

  • Failure isolation. A revoked token fails one mailbox and names the fix; the others keep working.

  • Correct replies. In-Reply-To and References are set, so replies thread in Outlook and Apple Mail — not only Gmail.

  • Cross-platform via platformdirs and keyring.

Development

uv sync && uv run pytest

193 tests, written test-first, running offline against a fake Gmail API — no network, credentials, or live mailbox needed. One opt-in integration test covers the real OAuth round trip.

License

MIT

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    C
    maintenance
    A local MCP server that provides tool-level access to Gmail, allowing Claude to search, read, send, and manage emails using the Gmail API.
    1
    MIT
  • F
    license
    Not graded
    quality
    C
    maintenance
    A local MCP server that connects multiple Gmail accounts and Google Calendars to Claude Desktop, enabling email and calendar operations via natural language.
    -
  • A
    license
    Not graded
    quality
    B
    maintenance
    MCP server exposing multiple Google Workspace Gmail accounts to Claude, with support for verified send-as aliases and opt-in sending.
    6 npm
    MIT