mcp-gmail

mcp-gmail

MCP server that connects Claude with Gmail to read, send, delete, and manage messages and labels via the Google Gmail API.

Category
Visit Server

README

mcp-gmail

An MCP (Model Context Protocol) server that connects Claude with Gmail through the Google Gmail API.

Quick Start

  1. Install dependencies: npm install.
  2. Set up Google Cloud credentials — see Google Cloud Console Setup.
  3. Configure environment — copy .env.example to .env.development and add your Google OAuth credentials.
  4. Build: npm run build.
  5. Configure Claude Desktop with dist/mcp-server/index.js and your GMAIL_CLIENT_ID/GMAIL_CLIENT_SECRET (see Configuration).
  6. Start the auth server: npm run dev:auth (separate process; handles OAuth on localhost:3334).
  7. Authenticate — call the authenticate tool in your MCP client, follow the URL, sign in. Tokens land at ~/.mcp-gmail-tokens.json (mode 0600).

Installation

Prerequisites

  • Node.js 22.0.0 or higher
  • npm
  • A Google account for Cloud Console access
npm install

Google Cloud Console Setup

1. Create a project

  1. Open the Google Cloud Console.
  2. Project dropdown → New Project.
  3. Name it (e.g. mcp-gmail) → Create.

2. Enable the Gmail API

  1. APIs & Services → Library.
  2. Search for Gmail APIEnable.

3. Configure the OAuth consent screen

For brand-new projects, Google gates this behind a one-time wizard. If you see "Google Auth Platform not configured yet" with a Get Started button, follow 3a. Otherwise jump to 3b.

3a. First-time setup

  1. APIs & Services → OAuth consent screenGet Started.
  2. App Information: app name, your support email → Next.
  3. Audience: ExternalNext.
  4. Contact Information: your email → Next.
  5. Agree to the user-data policy → ContinueCreate.

3b. Publish the app

  1. OAuth consent screen → Audience.
  2. Publishing statusPublish AppConfirm. (Avoids the 7-day refresh-token expiry of "Testing" mode. The app stays unverified — fine for personal use; you'll see a one-time "advanced → continue" warning during sign-in.)

3c. Configure data access (scopes)

This step is mandatory. If a scope isn't pre-declared here, Google silently drops it from consent, and Gmail API calls return 403 even after a "successful" sign-in.

  1. OAuth consent screen → Data AccessAdd or remove scopes.
  2. Tick https://www.googleapis.com/auth/gmail.modifyUpdateSave.

After changing scopes here, delete the token file (default ~/.mcp-gmail-tokens.json) and re-run the authenticate tool so the consent screen prompts again with the new scope set.

4. Create OAuth credentials

  1. APIs & Services → Credentials → Create Credentials → OAuth 2.0 Client ID.
  2. Application type: Web application.
  3. Name: anything (e.g. mcp-gmail).
  4. Authorized redirect URIs → add http://localhost:3334/auth/callback (must match GMAIL_REDIRECT_URI).
  5. Create, then copy the Client ID and Client Secret.

Configuration

Environment Variables

Name Required Default Purpose
GMAIL_CLIENT_ID yes OAuth 2.0 Client ID (xxxx.apps.googleusercontent.com).
GMAIL_CLIENT_SECRET yes OAuth 2.0 Client Secret.
GMAIL_REDIRECT_URI no http://localhost:3334/auth/callback Must match the URI registered in Google Cloud.
GMAIL_SCOPES no https://www.googleapis.com/auth/gmail.modify Space-separated OAuth scopes.
GMAIL_AUTH_PORT no 3334 Port the auth server listens on. Must match the redirect URI port.
GMAIL_TOKEN_PATH no ~/.mcp-gmail-tokens.json Token file location. Override to keep multiple accounts side-by-side.
NODE_ENV no dev:*/inspect scripts set development so .env.development loads.

Claude Desktop Configuration

Run npm run build first so dist/mcp-server/index.js exists, then add to your Claude Desktop config:

{
  "mcpServers": {
    "mcp-gmail": {
      "command": "node",
      "args": ["/path/to/mcp-gmail/dist/mcp-server/index.js"],
      "env": {
        "GMAIL_CLIENT_ID": "your-client-id",
        "GMAIL_CLIENT_SECRET": "your-client-secret"
      }
    }
  }
}

A starter is in claude-config-sample.json.

Running From Source (Dev)

cp .env.example .env.development
# edit .env.development with your Google OAuth credentials, then:
npm run dev:mcp        # MCP server
npm run dev:auth       # OAuth server on :3334

Authentication

OAuth runs out-of-band via the standalone auth server:

  1. Start npm run dev:auth (listens on http://localhost:3334).
  2. In your MCP client, call the authenticate tool — it returns a sign-in URL.
  3. Open the URL, sign in with the Google account you want to access, grant the requested scope.
  4. Tokens (including refresh token) are persisted to ~/.mcp-gmail-tokens.json (override with GMAIL_TOKEN_PATH).
  5. The MCP server reads that file and refreshes tokens transparently when they expire.

To force re-authentication (or if the refresh token is revoked), delete the token file and call authenticate again.

Scope troubleshooting. If Gmail API calls return 403 after a successful sign-in, inspect the scope field in the token file — Google only grants scopes that are pre-declared on the OAuth consent screen → Data Access tab (see step 3c). If gmail.modify is missing from scope, add it to Data Access, delete the token file, and re-authenticate.

Tools

31 tools across six areas. Resource-scoped names follow <resource>_<action>; auth tools are server-level and don't fit that shape. Default OAuth scope: https://www.googleapis.com/auth/gmail.modify.

auth

Tool Purpose
about Server version, scopes, token store path.
authenticate Returns the URL to start Google OAuth consent.[^auth-server]
check-auth-status Whether a token is persisted + scope/expiry metadata.[^no-token]

label

Tool Purpose
label_list List all system + user labels with id and name.
label_create Create a user label.
label_update Rename a user label.[^system-labels]
label_delete Delete a user label.[^system-labels] [^label-delete-effect]

message

Tool Purpose
message_search Gmail-query search at message granularity.[^paginated]
message_get Full message: headers, body, label ids, attachment refs.[^html-strip]
message_get_raw Write the raw RFC 2822 message to outputPath (e.g. .eml).[^raw-no-body]
message_label Add label ids to a message.
message_unlabel Remove label ids from a message.
message_mark_read Remove the UNREAD label.[^sugar]
message_mark_unread Add the UNREAD label.[^sugar]
message_archive Remove the INBOX label.[^sugar]
message_trash Move to Trash via messages.trash.[^trash]
message_batch_modify Add/remove labels on up to 1000 messages in one call.[^batch-modify]

attachment

Tool Purpose
attachment_get Download an attachment, to disk via outputPath or inline.[^attach-inline]

thread

Tool Purpose
thread_search Gmail-query search at thread granularity.[^paginated] [^thread-shape]
thread_get Full thread: every message with headers, body, label ids, attachments.
thread_label Add label ids to every message in a thread.
thread_unlabel Remove label ids from every message in a thread.
thread_mark_read Remove the UNREAD label from every message in the thread.[^sugar]
thread_mark_unread Add the UNREAD label to every message in the thread.[^sugar]
thread_archive Remove the INBOX label from every message in the thread.[^sugar]
thread_trash Move every message in the thread to Trash via threads.trash.[^trash]

draft

Tool Purpose
draft_create Create a Gmail draft (saved, never sent).[^draft-shape]
draft_update Replace an existing draft's contents (same fields as draft_create).
draft_list List drafts with headers + snippet; optional query filter.[^paginated]
draft_get Get a draft's full headers, body, label ids, and attachment refs.
draft_delete Permanently delete a draft (does not go to Trash).

This server deliberately exposes draft creation but no sending tool. The user reviews drafts in Gmail and clicks Send — Claude never directly delivers mail. The OAuth scope technically permits sending; the MCP surface does not.

[^auth-server]: The auth server must be running on :3334.

[^no-token]: Never returns access or refresh token values.

[^system-labels]: System labels (INBOX, SENT, etc.) cannot be renamed or deleted; Gmail rejects the request.

[^label-delete-effect]: Gmail removes the label from every message that had it; the messages themselves are untouched.

[^paginated]: Returns {<items>, nextPageToken?}. Pass nextPageToken back as pageToken to fetch the next page; it's omitted on the last page.

[^html-strip]: If the message has no text/plain part, the HTML body is stripped and returned instead.

[^raw-no-body]: Returns {messageId, path, sizeBytes}. The body never travels through the response, so this is safe for messages with large attachments. Subject/date aren't returned — with format=raw Gmail does not break out headers (use message_get).

[^attach-inline]: With outputPath, writes the decoded bytes and returns {messageId, path, sizeBytes}. Without it, returns {filename, mimeType, data} (base64url) — suitable for small attachments only.

[^thread-shape]: Each thread carries id, snippet, messageCount, latest-message headers, and the union of label ids across all messages.

[^draft-shape]: Plain-text body, attachments by file path. With replyToMessageId we wire In-Reply-To, extend References, prepend Re: to Subject, and tie the draft to the right thread. [^sugar]: Sugar over messages.modify / threads.modify so callers don't have to know the magic system-label id. [^trash]: Recoverable for ~30 days from Gmail's Trash UI. Permanent deletion (messages.delete / threads.delete) is intentionally not exposed. [^batch-modify]: Backed by Gmail messages.batchModify. At least one of addLabelIds or removeLabelIds is required. Returns {count, messageIds, addLabelIds, removeLabelIds} echoing the operation; Gmail returns 204 No Content on success.

Security Model

  • Secrets (GMAIL_CLIENT_SECRET) come from env vars only; never committed. .env* files are gitignored except .env*.example templates.
  • OAuth tokens live at GMAIL_TOKEN_PATH (default ~/.mcp-gmail-tokens.json), mode 0600.
  • Token writes are atomic — temp file + rename(). A crash mid-write cannot corrupt the token file.
  • Token values are never logged or returned by any MCP tool. The check-auth-status tool exposes presence flags and metadata only.
  • The auth server binds to localhost:3334 only and accepts a single OAuth callback at a time; CSRF state entries expire after 10 minutes.
  • If the token file is lost, revoked, or you want to switch Google accounts, delete the file and re-authenticate.

Development

npm run dev:mcp        # tsx watch, MCP server
npm run dev:auth       # tsx watch, OAuth server
npm run start:mcp      # build then run from dist/
npm run start:auth     # build then run auth server from dist/
npm run inspect        # MCP Inspector against TS source
npm test               # vitest
npm run typecheck      # tsc --noEmit
npm run lint:check     # Biome
npm run lint:fix       # Biome auto-fix (--unsafe)
npm run lint:md        # prettier + markdownlint for *.md

Recommended Servers

playwright-mcp

playwright-mcp

A Model Context Protocol server that enables LLMs to interact with web pages through structured accessibility snapshots without requiring vision models or screenshots.

Official
Featured
TypeScript
Magic Component Platform (MCP)

Magic Component Platform (MCP)

An AI-powered tool that generates modern UI components from natural language descriptions, integrating with popular IDEs to streamline UI development workflow.

Official
Featured
Local
TypeScript
Audiense Insights MCP Server

Audiense Insights MCP Server

Enables interaction with Audiense Insights accounts via the Model Context Protocol, facilitating the extraction and analysis of marketing insights and audience data including demographics, behavior, and influencer engagement.

Official
Featured
Local
TypeScript
VeyraX MCP

VeyraX MCP

Single MCP tool to connect all your favorite tools: Gmail, Calendar and 40 more.

Official
Featured
Local
graphlit-mcp-server

graphlit-mcp-server

The Model Context Protocol (MCP) Server enables integration between MCP clients and the Graphlit service. Ingest anything from Slack to Gmail to podcast feeds, in addition to web crawling, into a Graphlit project - and then retrieve relevant contents from the MCP client.

Official
Featured
TypeScript
Kagi MCP Server

Kagi MCP Server

An MCP server that integrates Kagi search capabilities with Claude AI, enabling Claude to perform real-time web searches when answering questions that require up-to-date information.

Official
Featured
Python
E2B

E2B

Using MCP to run code via e2b.

Official
Featured
Neon Database

Neon Database

MCP server for interacting with Neon Management API and databases

Official
Featured
Qdrant Server

Qdrant Server

This repository is an example of how to create a MCP server for Qdrant, a vector search engine.

Official
Featured
Exa Search

Exa Search

A Model Context Protocol (MCP) server lets AI assistants like Claude use the Exa AI Search API for web searches. This setup allows AI models to get real-time web information in a safe and controlled way.

Official
Featured