Skip to main content
Glama
Gmail MCP Server

Gmail MCP Server

by arkaigrowth

Gmail MCP Server

Multi-account Gmail integration for AI assistants via the Model Context Protocol (MCP).

Give your AI assistant secure access to all your Gmail accounts -- search, read, triage, draft, label, archive, trash-stage, and send emails with per-account routing, macOS Keychain token storage, and two-step confirmation flows for sensitive actions.


The Problem

Built-in Gmail integrations for AI assistants have significant limitations:

  • Single account only -- no way to switch between personal, work, and business inboxes

  • Insecure token storage -- most open-source servers store OAuth tokens as plaintext JSON files

  • Overly broad scopes -- many request full mail.google.com access (read, write, delete, send)

  • No send protection -- an AI can compose and send an email in a single step with no human checkpoint

Related MCP server: Gmail MCP Server

What This Server Does Differently

Concern

This Server

Typical MCP Gmail Server

Accounts

Unlimited, with aliases (personal, work)

Single account

Token storage

macOS Keychain (encrypted at rest)

Plaintext JSON files

OAuth scopes

4 scoped Gmail scopes plus scoped Docs/Drive (9 total, no mail.google.com)

mail.google.com (full access)

Sending

Two-step: stage draft, then confirm with a 60-second code

Direct send, no confirmation

Trash/bulk changes

Two-step staged confirmation

Often direct mutation

Credentials

Bring your own Google Cloud OAuth client

Often bundled (security risk)

Shell injection

execFileSync with argument arrays

execSync with string interpolation

Features

  • 35 tools for serious Gmail and Workspace workflows: search/read, batch read, thread reads, draft create/update/list, forwarding, labels, filters, archive, staged trash, staged bulk labeling, profile, attachments, two-step send, Google Docs/Drive reads, and scoped Drive writes

  • Multi-account routing -- every tool accepts an optional account parameter (email or alias)

  • Active account -- set a default account that persists across restarts

  • Rich draft bodies -- create/update drafts can include raw html_body with a plain-text fallback for Gmail-safe formatted replies

  • Safe modify -- label/archive/trash operations require explicit message IDs or staged query preview/confirmation

  • Attachment handling -- downloads to a sandboxed quarantine directory with SHA-256 integrity hashes and read-only permissions

  • Minimal dependencies -- only the official Google APIs, MCP SDK, and Zod

Quick Start

Prerequisites

  • Node.js 18+ (tested on 22)

  • macOS (uses Keychain for token storage)

  • A Google Cloud project with OAuth credentials

Install & Build

git clone https://github.com/arkaigrowth/gmail-mcp-server.git
cd gmail-mcp-server
npm install
npm run build

Smoke Test

echo '{"jsonrpc":"2.0","method":"tools/list","id":1}' | node dist/index.js

You should see a JSON response listing all 35 tools.

Add to Your AI Assistant

Claude Code (~/.claude/settings.json under mcpServers):

{
  "gmail": {
    "command": "node",
    "args": ["/absolute/path/to/gmail-mcp-server/dist/index.js"]
  }
}

Claude Desktop (claude_desktop_config.json):

{
  "mcpServers": {
    "gmail": {
      "command": "node",
      "args": ["/absolute/path/to/gmail-mcp-server/dist/index.js"]
    }
  }
}

Authenticate Your First Account

Once the MCP server is connected, tell your AI assistant:

"Authenticate my Gmail account user@gmail.com with alias personal"

This will:

  1. Open your browser to Google's consent screen

  2. Store the OAuth refresh token in your macOS Keychain

  3. Register the account for future use

You can then immediately start using it:

"Search my personal inbox for emails from GitHub in the last week"


Google Cloud Setup

You need a Google Cloud OAuth client (type: "Desktop app") with the Gmail API enabled. Each Google account needs its own OAuth client from a project where that account has access.

Step-by-step

  1. Go to the Google Cloud Console

  2. Create a new project (or use an existing one)

  3. Enable the Gmail API: APIs & Services > Library > search "Gmail API" > Enable

  4. Configure the OAuth consent screen: APIs & Services > OAuth consent screen

    • User type: External (or Internal if using Google Workspace)

    • Add the Gmail scopes: gmail.readonly, gmail.compose, gmail.modify, gmail.settings.basic

    • Add the Workspace scopes used by the Docs/Drive tools: documents.readonly, documents, drive.metadata.readonly, drive.readonly, drive.file

    • Add your email as a test user (while in "Testing" status)

  5. Create OAuth credentials: APIs & Services > Credentials > Create Credentials > OAuth client ID

    • Application type: Desktop app

    • Download the JSON file

  6. Save the JSON file as ~/.gmail-mcp/client_secret.json

Tip: If you have multiple Google accounts, you can specify a per-account path via the client_secret_path parameter when authenticating.

Credential File Lookup Order

  1. client_secret_path parameter passed to gmail_authenticate

  2. Path stored in the account's config (accounts.json)

  3. GMAIL_MCP_CLIENT_SECRET_PATH environment variable

  4. ~/.gmail-mcp/client_secret.json (default)


Tool Reference

Account Management

Tool

Description

gmail_list_accounts

List all configured accounts and their auth status

gmail_switch_account

Set the active default account (persisted across restarts)

gmail_authenticate

OAuth flow -- opens browser, stores token in Keychain

Email Operations

Tool

Description

gmail_search_messages

Legacy search using Gmail query syntax (from:alice is:unread)

gmail_search_emails

Paginated rich search with messages, nextPageToken, and estimate

gmail_read_message

Read a specific message with full body content

gmail_batch_read_messages

Read explicit shortlisted message IDs, with partial failures

gmail_read_thread

Read a conversation thread, optionally capped with max_messages

gmail_list_labels

List all labels (system + custom) with message counts

gmail_get_profile

Get mailbox metadata (message/thread counts)

Labels, Archive, Trash

Tool

Description

gmail_create_label

Create a label, or return an existing label by name

gmail_apply_labels_to_messages

Add/remove labels on explicit message IDs only

gmail_archive_messages

Archive explicit message IDs by removing INBOX

gmail_stage_trash_messages

Stage explicit messages for Trash and return a confirmation code

gmail_confirm_trash_messages

Confirm staged Trash operation; account-bound, short expiry

gmail_prepare_bulk_label_query

Preview and stage capped query-based bulk labeling

gmail_confirm_bulk_label_operation

Confirm a staged bulk label operation

Gmail filters

Tool

Description

gmail_create_filter

Create a filter from criteria and label actions, with optional existing label-name resolution

gmail_list_filters

List filter IDs, criteria, and actions

gmail_delete_filter

Delete exactly one filter by ID

Filter label names must already exist. The server returns available label names when a requested name cannot be resolved, and does not auto-create labels.

Compose & Send

Tool

Description

gmail_create_draft

Create a draft email (does not send)

gmail_list_drafts

List draft metadata

gmail_update_draft

Update an existing draft; rejects drafts with attachments in v1

gmail_create_forward_draft

Create draft forwards; quotes original content inline

gmail_send_draft

Stage a draft for sending -- returns a confirmation code

gmail_confirm_send

Confirm and send with the code (60-second expiry)

Attachments

Tool

Description

gmail_download_attachment

Download to quarantine directory with SHA-256 hash

All email tools accept an optional account parameter (email or alias). If omitted, the active default account is used. If no default is set and only one account exists, it's auto-selected.

Draft bodies are plain text by default. For rich Gmail rendering, pass html_body with body as the plain-text fallback; the server emits multipart/alternative with text/plain first and text/html second. You can also set content_type: "text/html" to treat body as raw HTML when no separate fallback is available. Outbound attachments are not supported in draft create/update yet.


Security

Token Storage

OAuth tokens (refresh tokens, client credentials) are stored exclusively in the macOS Keychain using the security CLI. No plaintext token files are ever written to disk.

Keychain Service: gmail-mcp-server
Keychain Account: <your-email-address>

The ~/.gmail-mcp/accounts.json config file stores only metadata (email, alias, description, credential file path) -- never secrets.

OAuth Scopes

The consent flow requests one combined set of scopes (REQUIRED_AUTH_SCOPES in src/scopes.ts). It covers Gmail plus the Google Docs and Drive tools this server also exposes (reading shared docs and specs, and writing scoped Docs/Drive files), so the Workspace scopes are requested in the same flow.

Gmail scopes

Scope

Grants

gmail.readonly

Read messages, threads, labels, profile

gmail.compose

Create and send drafts

gmail.modify

Label, archive, trash, and other safe mailbox state changes

gmail.settings.basic

Create, list, and delete Gmail filters

Google Docs / Drive scopes

Scope

Grants

documents.readonly

Read the text of Google Docs the account can open

documents

Create and edit Google Docs the account can access

drive.metadata.readonly

Read Drive file metadata (names, types), no file content

drive.readonly

Read and download the content of any Drive file the account can access, including Office files shared by teammates

drive.file

Create and manage the Drive files this server creates or opens

The Gmail scopes are intentionally narrower than mail.google.com full access: the server does not request gmail.send; direct messages.send is blocked and all sends must go through draft staging plus confirmation. The Workspace scopes add Docs/Drive read and scoped-write access on top of that.

The scopes are defined as grouped constants in src/scopes.ts (REQUIRED_GMAIL_SCOPES, REQUIRED_WORKSPACE_READ_SCOPES, REQUIRED_WORKSPACE_WRITE_SCOPES). If you only need the Gmail tools, remove the Workspace groups from REQUIRED_AUTH_SCOPES and rebuild; the consent flow then requests only the Gmail scopes and the Docs/Drive tools stop working.

Tokens store the granted scope list in Keychain metadata. Tokens granted before a scope was added produce a clear reauth-required error for the tools that need it; rerun gmail_authenticate for that account.

Re-authentication required: Adding gmail.settings.basic means each account must rerun gmail_authenticate and complete the consent flow before filter tools work. Existing tokens keep working for old tools, but filter calls fail with insufficient scopes until re-authentication.

Two-Step Send Confirmation

Sending email requires two separate tool calls:

  1. gmail_send_draft -- stages the draft and returns a 16-character cryptographic confirmation code (valid for 60 seconds)

  2. gmail_confirm_send -- the AI must present the code back, and the account must match

Additionally, messages.send is explicitly blocked on the Gmail client -- all sends must go through the draft confirmation flow.

Two-Step Trash And Bulk Label Confirmation

Trash and query-based bulk label operations use short-lived, account-bound confirmation codes. Explicit message ID label/archive operations are direct because their targets are already selected; query-based bulk labeling always previews before mutation.

OAuth Callback Security

  • Callback server binds to 127.0.0.1 only (never 0.0.0.0)

  • Random state parameter validated to prevent CSRF

  • Callback server auto-terminates after 5 minutes by default (override via GMAIL_MCP_AUTH_TIMEOUT_MS)

  • Dynamic port allocation (no fixed ports)

Attachment Quarantine

Downloaded attachments are:

  • Saved to ~/.gmail-mcp/quarantine/<messageId>/ with 0o700 directory permissions

  • Written with 0o400 (read-only) file permissions

  • Filename sanitized to prevent path traversal

  • Accompanied by a SHA-256 hash for integrity verification

Shell Injection Prevention

All calls to the macOS security CLI use execFileSync with argument arrays, not execSync with string interpolation. This prevents shell injection via crafted email addresses or token data.


Multi-Account Examples

Setup two accounts

"Authenticate user@gmail.com with alias personal"
"Authenticate work@company.com with alias work, client_secret_path ~/.gmail-mcp/work_client_secret.json"

Switch between them

"Switch to my work account"
"Search work for emails from the finance team this month"
"Switch to personal"
"Read my latest emails"

Or specify per-call

"Search my personal inbox for order confirmations"
"Search my work inbox for meeting invites tomorrow"

File Structure

~/.gmail-mcp/
  accounts.json          # Account metadata (no secrets)
  defaults.json          # Active account preference
  client_secret.json     # Default OAuth client credentials (from Google)
  quarantine/            # Downloaded attachments (sandboxed)
    <messageId>/
      attachment.pdf     # Read-only (0o400)
macOS Keychain
  Service: gmail-mcp-server
  Account: user@gmail.com    # Token JSON, including granted OAuth scopes
  Account: work@company.com  # Token JSON, including granted OAuth scopes

Development

# Build
npm run build

# Run in development (with tsx)
npm run dev

# Run tests
npm test

# Smoke test (JSON-RPC)
echo '{"jsonrpc":"2.0","method":"tools/list","id":1}' | node dist/index.js

Project Structure

src/
  index.ts          # Entry point: MCP server + stdio transport
  tools.ts          # 35 tool registrations with Zod schemas
  gmail-client.ts   # Gmail API wrapper (search, read, draft, labels, filters, archive, trash, send, attachments)
  accounts.ts       # Account config management (load, resolve, persist)
  keychain.ts       # macOS Keychain integration via security CLI
  oauth.ts          # OAuth browser flow with localhost callback
  scopes.ts         # Gmail OAuth scope constants and metadata checks
  types.ts          # Shared TypeScript interfaces
skills/
  gmail-multi/      # Repo-local workflow guidance for agents using this server

Platform Support

Currently macOS only due to the Keychain dependency. Contributions for Linux (secret-tool / libsecret) and Windows (wincred) credential storage are welcome.


License

MIT

Available Tools

35 tools
docs_read_documentA

Read text and tables from a Google Doc visible to an account.

Inputs: document_id_or_url (required), tab_id, account.

drive_create_folderA

Create a Google Drive folder visible to an account. Uses the narrow drive.file scope.

Inputs: name (required), account, parent_folder_id_or_url.

drive_create_text_docA

Create a native Google Doc from plain text in Google Drive. Uses the narrow drive.file scope.

Inputs: text (required), title (required), account, parent_folder_id_or_url.

drive_get_file_metadataA

Get Google Drive file metadata visible to an account. Does not download file content.

Inputs: file_id_or_url (required), account.

drive_list_commentsA

List comments on a Google Drive file visible to an account. Uses existing narrow Drive access and does not broaden scopes.

Inputs: file_id_or_url (required), account, page_token, max_results, +3 more.

drive_list_revisionsB

List coarse Drive revisions for a file visible to an account. For native Docs, this is not the full editor version history.

Inputs: file_id_or_url (required), account, page_token, max_results.

drive_search_docsB

Search Google Docs visible to an account by title using Drive metadata only.

Inputs: query, account, page_token, max_results.

drive_upload_fileA

Upload an explicit local file to Google Drive for an account. Uses the narrow drive.file scope.

Inputs: local_file_path (required), name, account, mime_type, +1 more.

gmail_apply_labels_to_messagesB

Apply or remove Gmail labels on explicit message IDs. Does not accept queries.

Inputs: message_ids (required), account, add_label_names, remove_label_names, +1 more.

gmail_archive_messagesA

Archive explicit Gmail message IDs by removing INBOX. Does not accept queries.

Inputs: message_ids (required), account.

gmail_authenticateB

Authenticate a Gmail account via OAuth. Opens a browser for Google consent.

Inputs: email (required), alias, description, client_secret_path.

gmail_batch_read_messagesA

Read explicit shortlisted Gmail message IDs. Use after search when snippets are insufficient.

Inputs: message_ids (required), account.

gmail_confirm_bulk_label_operationB

Confirm a staged bulk label operation using the confirmation code.

Inputs: confirmation_code (required), account.

gmail_confirm_sendA

Confirm and send a previously staged draft. Code expires after 60 seconds.

Inputs: confirmation_code (required), account.

gmail_confirm_trash_messagesB

Confirm and move previously staged messages to Trash using the confirmation code.

Inputs: confirmation_code (required), account.

gmail_create_draftA

Create a draft email (does NOT send it)

Inputs: to (required), body (required), subject (required), cc, +7 more.

gmail_create_filterB

Create a Gmail filter. Label names must already exist and are resolved to label IDs.

Inputs: action (required), criteria (required), account.

gmail_create_forward_draftB

Create draft forwards for source messages. Source attachments are not preserved in v1.

Inputs: to (required), message_ids (required), cc, bcc, +2 more.

gmail_create_labelA

Create a Gmail label, or return the existing label if one already has this name.

Inputs: name (required), account, label_list_visibility, message_list_visibility.

gmail_delete_filterB

Delete one Gmail filter by its filter ID

Inputs: filter_id (required), account.

gmail_download_attachmentA

Download an email attachment to a sandboxed quarantine directory. Returns local path, SHA-256 hash, and image flag.

Inputs: filename (required), message_id (required), attachment_id (required), account, +1 more.

gmail_get_profileA

Get Gmail profile info for an account (message/thread counts)

Inputs: account.

gmail_list_accountsA

List all configured Gmail accounts and their authentication status

Inputs: no named parameters.

gmail_list_draftsC

List Gmail drafts with summarized metadata.

Inputs: account, page_token, max_results.

gmail_list_filtersA

List all Gmail filters for an account

Inputs: account.

gmail_list_labelsA

List all Gmail labels for an account

Inputs: account.

gmail_prepare_bulk_label_queryA

Preview and stage a capped query-based bulk label operation. Nothing changes until gmail_confirm_bulk_label_operation.

Inputs: query (required), label_name (required), account, archive, +2 more.

gmail_read_messageA

Read a specific Gmail message by ID, including full body content

Inputs: message_id (required), account.

gmail_read_threadB

Read a Gmail thread. Pass thread_id for legacy behavior, or id plus id_type=message/thread. max_messages returns the most recent N messages.

Inputs: id, account, id_type, thread_id, +1 more.

gmail_search_emailsA

Paginated rich Gmail search. Prefer this for triage because it returns messages, nextPageToken, and resultSizeEstimate.

Inputs: query, account, label_ids, page_token, +2 more.

gmail_search_messagesC

Legacy search returning an array of Gmail message metadata using Gmail query syntax.

Inputs: query (required), account, max_results.

gmail_send_draftA

Stage a draft for sending. Returns a preview and confirmation code. The draft is NOT sent until gmail_confirm_send.

Inputs: draft_id (required), account.

gmail_stage_trash_messagesA

Stage explicit messages for Trash. Must be confirmed with gmail_confirm_trash_messages.

Inputs: message_ids (required), account.

gmail_switch_accountA

Switch the active Gmail account for this session. All subsequent tool calls use this account by default unless overridden.

Inputs: account (required).

gmail_update_draftA

Update an existing Gmail draft. Omitted fields preserve current values. Drafts with attachments are rejected.

Inputs: draft_id (required), cc, to, bcc, +8 more.

Tool Schema Changelog

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

Loading tool schema changelog…

    TDQS

    B3.3/5.0

    Scored across 35 tools

    Disambiguation4/5

    Most tools have clearly distinct purposes, but there is some overlap between gmail_search_messages and gmail_search_emails, and between archive/trash workflows. Descriptions help differentiate, but ambiguity remains for agents.

    Naming Consistency5/5

    All tools follow a consistent verb_noun snake_case pattern with domain prefixes (gmail_, drive_, docs_). Naming is predictable and readable across the entire set.

    Tool Count3/5

    35 tools is high, but it combines Gmail and Google Drive/Docs functionality. The count could be reduced by merging similar operations (e.g., staging/confirmation pairs), but it remains within acceptable bounds for the combined scope.

    Completeness3/5

    Common Gmail operations are covered (search, read, send, labels, filters, archive, trash), but missing permanent deletion and direct marking of read/unread. Drive/Docs coverage is basic (search, metadata, read, create, upload) but lacks editing and broader file operations.

    Maintenance

    ActivityInactive
    ResponsivenessOutdated data

    Related MCP Connectors

    Related MCP Servers

    • A
      license
      Not graded
      quality
      D
      maintenance
      A Model Context Protocol server for Gmail that lets AI assistants read, send, search, label, and filter Gmail through natural language, with OAuth2 auto-authentication and full attachment support.
      128 npm
      3
      MIT
    • A
      license
      Not graded
      quality
      C
      maintenance
      Multi-account Gmail integration for AI agents, enabling read, write, archive, label, and auto-unsubscribe operations through the Model Context Protocol.
      128 npm
      16
      MIT
    • A
      license
      Not graded
      quality
      C
      maintenance
      Enables AI assistants to send, read, search, and manage Gmail emails, drafts, and labels via the Model Context Protocol.
      5
      MIT
    • A
      license
      Not graded
      quality
      C
      maintenance
      Connects Gmail to AI assistants via the MCP protocol, enabling search, read, send, and manage emails across multiple Google accounts simultaneously.
      MIT