Table of Contents generated with DocToc

Gmail — MCP operation catalogue

Shared reference for the mcp__claude_ai_Gmail__* tool calls the skills make against the active user's Gmail account. The skills reference this file for the call shape and for the limitations that constrain their flow.

Placeholder convention used below:

  • <security-list> — the project‘s private security mailing list (the list the user’s Gmail account subscribes to). For Airflow, the value is <project manifest>.security_list = <security-list>; see ../../<project-config>/project.md.
  • <threadId> — an opaque Gmail thread identifier.

Pre-flight

Every skill that talks to Gmail does a one-call pre-flight in Step 0 to confirm the MCP is reachable and the user‘s account subscribes to the project’s security list:

mcp__claude_ai_Gmail__search_threads(
  query='list:<security-list-domain>',
  pageSize=1,
)

Substitute the <security-list-domain> with the domain suffix of the project manifest's security_list (for <security-list>, the value is <security-list-domain>).

A non-empty result means Gmail is connected and indexed; an empty result means either the account does not subscribe, or the MCP is misconfigured. In either case the skill stops and asks the user to fix the setup rather than guessing.

Read

Search threads

mcp__claude_ai_Gmail__search_threads(
  query='<gmail search expression>',
  pageSize=<N>,
)

Returns an array of {threadId, snippet, …} objects. Use pageSize deliberately — some skills (e.g. sync-security-issue) impose a hard Gmail-call budget per issue to avoid running up the MCP quota on many-tracker sweeps.

For the search expression syntax and the canonical query templates the skills use, see search-queries.md.

Get thread

mcp__claude_ai_Gmail__get_thread(
  threadId='<threadId>',
  messageFormat='FULL_CONTENT',   # or 'METADATA' when bodies are not needed
)

Returns the full message history of a thread. Body reads are expensive — most skills filter candidates down on metadata first and only fetch bodies for the narrow set that actually warrants it (import-security-issue does this explicitly at Step 3).

Write — drafts only, never send

Drafting backends

Draft creation runs through one of two backends, selected by the user in config/user.md under tools.gmail.draft_backend. The full comparison and rationale live in draft-backends.md; the call shape per backend is here.

BackendValuethreadId attach?
claude.ai Gmail MCPclaude_ai_mcp (default)no (see below)
OAuth + curloauth_curlyes

Create draft — claude_ai_mcp backend

The claude.ai Gmail MCP‘s create_draft tool does not accept a threadId parameter. The Gmail REST API supports it on drafts.create, but the MCP does not plumb it through. Drafts created via this backend always start a new conversation on the **sender’s** Gmail side; recipients' mail clients thread-attach via subject + In-Reply-To / References matching, which usually works but is not guaranteed.

mcp__claude_ai_Gmail__create_draft(
  subject='Re: <root subject of the inbound message>',
  to=['<primary>'],
  cc=['<security-list>', ...],
  body='<body>',
)
  • Subject is always Re: <root subject>, never fabricated. A drifted subject defeats subject-based threading on every client.
  • Never send. The skills only create drafts; a human review-and-send step is required before every outbound message.
  • Subject-fallback threading is the only path. See the fallback rule in threading.md for when this applies and when it does not.

When the user needs threadId-attached drafts (so the draft threads on their own Gmail view and the drafts queue stays in lock-step with the inbound thread), switch the backend to oauth_curl — see below.

Create draft — oauth_curl backend

The oauth-draft-create console script (in oauth-draft/) creates drafts by talking directly to the Gmail REST API with a user-provided OAuth refresh token. It sets threadId on the Gmail API call and populates In-Reply-To / References from the thread's last message, so every client threads consistently.

uv run --project <framework>/tools/gmail/oauth-draft oauth-draft-create \
  --thread-id <gmail-threadId> \
  --to reporter@example.com \
  --cc <security-list> \
  --subject "Re: <root subject>" \
  --body-file /tmp/body.txt

See oauth-draft/README.md for one-time setup (creating the Google Cloud OAuth client, obtaining a refresh token, and populating the credentials file) and for the full flag list.

Every bullet from the claude_ai_mcp section above applies to this backend too — drafts only, never send; subject is always Re: <root subject>; composition happens under user review.

Hard rules that apply to both backends

  • Never send.
  • Subject is always Re: <root subject>, never fabricated.
  • Surface which backend was used in the proposal / recap so the user can tell at a glance whether the draft threads on their own Gmail view (oauth_curl) or only on the recipient's (claude_ai_mcp subject fallback).
  • Record the backend + draft ID on the tracker's status rollup so subsequent sync passes can find and (optionally) re-verify the draft.

For the ASF-security-relay special case (different to / cc shape), see asf-relay.md.

List drafts

mcp__claude_ai_Gmail__list_drafts(
  query='<optional filter>',    # e.g. 'list:<security-list-domain>'
)

Used by sync-security-issue to verify that a draft flagged as stale in a previous status comment still exists before carrying the flag forward. See the “self-replicating stale-draft flag” paragraph in that skill.

Hard limitation — no update, no delete

The Gmail MCP exposes create, list, and read only for drafts. There is no update_draft and no delete_draft tool. The skills must treat every existing draft as immutable:

  • If a correction is needed, surface the existing draft's draftId to the user with an explicit “discard this one manually in Gmail” note, then create a fresh draft with the corrected content.
  • Do not silently create a second draft that shadows the first — that leaves two near-identical drafts in the user's Gmail and invariably one of them gets sent by accident.
  • On the sync skill's stale-draft-forward-flagging path: verify the draftId still exists via list_drafts before copying the flag into a new sync status comment. Without verification, a one-time flag self-replicates forever.

Confidentiality of drafts

Drafts land in the user‘s personal Gmail account and are visible only to that user until sent. Draft content may reference the private tracker’s URL (reporter is on the private thread and is expected to keep it confidential), but anything destined for a public list must obey the confidentiality rules in ../../AGENTS.md — no <tracker> URLs, no CVE IDs before publication, no “security fix” leakage.

Error handling

If any Gmail call fails (MCP unreachable, 429, transient 5xx), stop and report the failure. The skills explicitly budget Gmail calls; silently retrying turns one flaky call into a quota-exhaustion storm.