Skip to main content
Glama
gmail-ai-mcp

gmail-ai-mcp

gmail-ai-broker

Small, auditable CLI that fills the four gaps the official claude.ai Gmail connector leaves open, so Claude can act as a real email assistant:

Capability

Why the official connector can't

Command

Download attachments

connector exposes no attachment bytes

attachment download

Send raw MIME w/ attachments

connector can't attach files / send raw

draft create --attach then draft send

Filter CRUD

needs gmail.settings.basic scope

filter list/get/create/delete

Raw message fetch

connector renders, doesn't expose raw

message get-raw

Keep the official connector for reads/triage; this tool adds only the missing writes. It is intentionally small enough to read end-to-end -- that auditability is the trust model.

Security model (read this)

  • Read-safe by default. The only command that transmits mail is draft send <id> --yes. Everything outbound is staged as a draft you review. There are no delete/archive/bulk verbs in v1.

  • The token is the crown jewel. Auth is OAuth; the agent never holds a durable credential. A refresh token lives in a mode-600 file (~/.secrets/.gmail-refresh-token), and google-auth exchanges it for a short-lived access token per call. Back the token up in your password manager.

  • Scopes are mailbox-wide. This tool requests gmail.modify + gmail.settings.basic. There is no per-label OAuth scoping in Gmail -- gmail.modify legally grants whole-mailbox read/write. Any "only touch label X" behavior would be enforced by our code, not by Google. Treat the token accordingly.

  • Every mutation is audited. Draft create, draft send, and filter create/delete each append a line to an append-only JSONL log (~/.local/state/gmail-ai-broker/audit.jsonl). Call args are hashed, not stored verbatim, so recipient/subject content is not persisted.

  • Untrusted content stays data. The CLI emits attachment/raw bytes to files or stdout and never acts on email content as instructions. (Indirect prompt injection -- e.g. EchoLeak / CVE-2025-32711 -- is a real, in-the-wild threat for email agents. The defense lives at the agent layer; this CLI simply never auto-acts.)

Related MCP server: honest-gmail-mcp

Install (dev)

uv sync                      # runtime deps
uv sync --extra dev          # + pytest
uv run gmail-ai-broker --help
uv run --extra dev pytest -q # full suite

One-time setup runbook

You only do this once. Steps 2-3 are Console-manual -- Google exposes no API (and no Terraform resource) for creating a Gmail user-consent Desktop OAuth client.

1. Enable the Gmail API ($0)

In any Google Cloud project you own (create one free if needed):

gcloud services enable gmail.googleapis.com --project <your-project-id>

Google Cloud Console -> APIs & Services -> OAuth consent screen:

  • User type: External (required for a personal @gmail.com account).

  • Fill app name, your support email, developer email.

  • Add scopes: https://www.googleapis.com/auth/gmail.modify and https://www.googleapis.com/auth/gmail.settings.basic.

  • Publishing status: click "Publish app" -> move to "In production". Accept the "unverified app" warning. As the sole user you can run it unverified indefinitely. This step matters: an app left in "Testing" expires its refresh token every 7 days; "In production" makes the token persist.

3. Create a Desktop OAuth client

Console -> APIs & Services -> Credentials -> Create credentials -> OAuth client ID:

  • Application type: Desktop app. Name it (e.g. gmail-ai-broker).

  • Create, then Download JSON (contains client_id + client_secret). For a Desktop app the "secret" is not truly secret, but Google still needs it to refresh.

4. Write the config

~/.config/gmail-ai-broker/config.toml:

client_id = "XXXX.apps.googleusercontent.com"
client_secret = "YYYY"
# Optional overrides (defaults shown):
# refresh_token_path = "~/.secrets/.gmail-refresh-token"
# audit_log_path = "~/.local/state/gmail-ai-broker/audit.jsonl"

5. Authorize and capture the refresh token

uv run gmail-ai-broker auth login --client-secrets ~/Downloads/client_secret_XXXX.json

A browser opens; approve the two scopes. The refresh token is written to ~/.secrets/.gmail-refresh-token (mode 600). Back it up in your password manager.

Verify:

uv run gmail-ai-broker auth status
uv run gmail-ai-broker filter list      # Gate 1: proves the token + scopes work

Usage

# Read a message's full raw MIME (to a file or stdout)
gmail-ai-broker message get-raw <message-id> --out msg.eml

# Download an attachment
gmail-ai-broker attachment download <message-id> <attachment-id> --out form.pdf

# Stage a reply with an attachment (does NOT send)
gmail-ai-broker draft create \
  --to someone@example.com --subject "Re: your request" \
  --body-file reply.txt --attach completed-form.pdf \
  --in-reply-to "<original-message-id@mail.gmail.com>"

# Review the draft in Gmail, then -- and only then -- send it:
gmail-ai-broker draft send <draft-id> --yes

# Filters
gmail-ai-broker filter list
gmail-ai-broker filter create --spec my-filter.toml
gmail-ai-broker filter delete <filter-id>

Filter spec file

A reviewed declarative spec (.toml or .json). Example my-filter.toml:

[criteria]
from = "billing@example.com"
# to, subject, query, has_attachment also supported

[action]
add_label_ids = ["Label_42"]
# remove_label_ids, forward also supported

Label IDs (not names) -- list them with filter list or the official connector's list_labels.

Audit log

Append-only JSONL, one record per mutation:

{"timestamp":"2026-05-28T...","action":"draft.send","args_hash":"...","result":{"message_id":"...","label_ids":["SENT"]}}

Architecture

auth.py     token load/save (mode 600), credential build, loopback flow
config.py   TOML config + path defaults
mime.py     raw MIME build (attachments) + attachment decode  [pure]
models.py   declarative FilterSpec -> Gmail filter resource    [pure]
audit.py    append-only JSONL mutation log                     [pure]
client.py   GmailBroker: the four capabilities, wired together
service.py  googleapiclient discovery build                    [glue]
cli/main.py typer entrypoint

MCP server

A stdio FastMCP server (gmail-ai-mcp) exposes the broker's capabilities as native MCP tools alongside the official connector. It exposes seven tools -- download_attachment, get_raw_message, list_filters, get_filter, create_draft, create_filter, delete_filter -- and deliberately omits send_draft: transmitting mail stays a terminal-only draft send --yes act.

Binary reads (download_attachment, get_raw_message) write bytes to an agent-supplied out path and return {path, bytes_written}; raw email bytes never enter the model context.

Register it with your MCP host, e.g.:

claude mcp add --scope user gmail-ai-broker \
  uv run --directory /abs/path/to/gmail-ai-broker gmail-ai-mcp

Config is loaded exactly like the CLI (XDG default), or point at a specific config with the GMAIL_AI_BROKER_CONFIG environment variable in the launch command. Complete the one-time setup runbook above before registering.

Available Tools

7 tools
create_draftA

Stage a draft (optionally with local-file attachments). Does NOT send.

attachments are local file paths. Returns the draft resource. Sending is intentionally unavailable here; review and send the draft from the CLI.

Inputs: to (required), body (required), subject (required), cc, +2 more. Returns: object

create_filterA

Create a Gmail filter from a declarative spec object.

spec is {criteria: {...}, action: {...}} (snake_case keys, e.g. add_label_ids). Validated before submission. forward actions are rejected over MCP -- create forwarding filters with the CLI instead.

Inputs: spec (required). Returns: object

delete_filterA

Delete a Gmail filter by id. Returns {deleted: }.

Inputs: filter_id (required). Returns: object

download_attachmentA

Download a message attachment to a local file path.

Writes the attachment bytes to out and returns {path, bytes_written}. Bytes are never returned inline.

Inputs: out (required), message_id (required), attachment_id (required). Returns: object

get_filterA

Get a single Gmail filter resource by id.

Inputs: filter_id (required). Returns: object

get_raw_messageC

Fetch a message's full raw RFC822 MIME to a local file path.

Writes the raw bytes to out and returns {path, bytes_written}.

Inputs: out (required), message_id (required). Returns: object

list_filtersA

List all Gmail filters as JSON resources.

Inputs: no named parameters. Returns: result

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

Loading tool schema changelog…

    TDQS

    A3.6/5.0

    Scored across 7 tools

    Disambiguation4/5

    Most tools have clear boundaries: filter CRUD is distinct, and create_draft is separate. The only potential confusion is download_attachment vs get_raw_message, but their descriptions specify attachment vs full MIME bytes.

    Naming Consistency5/5

    All tools use a consistent verb_noun pattern in snake_case (download_, get_, list_, create_, delete_). There are no mixed casing styles or vague generic verbs.

    Tool Count5/5

    Seven tools is well within the ideal 3-15 range, and each tool serves a distinct function in the advertised workflow. The count feels neither bloated nor thin for the server's focused scope.

    Completeness2/5

    The set covers filter CRUD (minus update), draft creation, and message extraction, but omits core Gmail operations like listing/searching messages, getting a parsed message, updating/deleting drafts, and appending labels. This leaves significant gaps for a general Gmail MCP, especially since there is no way to move a conversation forward past draft creation.

    Maintenance

    ActivityInactive
    ResponsivenessOutdated data

    Related MCP Connectors

    Related MCP Servers

    • A
      license
      Not graded
      quality
      D
      maintenance
      An MCP server that provides read, label, and draft access to multiple Gmail accounts from a single server, never sending email.
      101 npm
      MIT
    • A
      license
      A
      quality
      B
      maintenance
      A local MCP server that provides Gmail tools (search, read, send, draft, label management) while keeping your emails only between your machine and Google, with no third-party access.
      6
      MIT
    • A
      license
      Not graded
      quality
      C
      maintenance
      MCP server for Gmail with full read/write coverage: search, send, reply, drafts, labels, filters, vacation responder, auto-forwarding, signature, and attachment handling (download and PDF export). Uses the Gmail API with the gmail.modify scope to prevent permanent deletion, and requires explicit confirmation for sensitive actions.
      MIT
    • F
      license
      Not graded
      quality
      D
      maintenance
      MCP server for email management that enables reading, searching, drafting, replying to, and sending emails with thread-aware replies and draft-first safety, supporting Gmail API and IMAP/SMTP backends.
      14 npm
      -