mcp-gmail
MCP server that connects Claude with Gmail to read, send, delete, and manage messages and labels via the Google Gmail API.
README
mcp-gmail
An MCP (Model Context Protocol) server that connects Claude with Gmail through the Google Gmail API.
Quick Start
- Install dependencies:
npm install. - Set up Google Cloud credentials — see Google Cloud Console Setup.
- Configure environment — copy
.env.exampleto.env.developmentand add your Google OAuth credentials. - Build:
npm run build. - Configure Claude Desktop with
dist/mcp-server/index.jsand yourGMAIL_CLIENT_ID/GMAIL_CLIENT_SECRET(see Configuration). - Start the auth server:
npm run dev:auth(separate process; handles OAuth onlocalhost:3334). - Authenticate — call the
authenticatetool in your MCP client, follow the URL, sign in. Tokens land at~/.mcp-gmail-tokens.json(mode0600).
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
- Open the Google Cloud Console.
- Project dropdown → New Project.
- Name it (e.g.
mcp-gmail) → Create.
2. Enable the Gmail API
- APIs & Services → Library.
- Search for Gmail API → Enable.
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
- APIs & Services → OAuth consent screen → Get Started.
- App Information: app name, your support email → Next.
- Audience: External → Next.
- Contact Information: your email → Next.
- Agree to the user-data policy → Continue → Create.
3b. Publish the app
- OAuth consent screen → Audience.
- Publishing status → Publish App → Confirm. (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.
- OAuth consent screen → Data Access → Add or remove scopes.
- Tick
https://www.googleapis.com/auth/gmail.modify→ Update → Save.
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
- APIs & Services → Credentials → Create Credentials → OAuth 2.0 Client ID.
- Application type: Web application.
- Name: anything (e.g.
mcp-gmail). - Authorized redirect URIs → add
http://localhost:3334/auth/callback(must matchGMAIL_REDIRECT_URI). - 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:
- Start
npm run dev:auth(listens onhttp://localhost:3334). - In your MCP client, call the
authenticatetool — it returns a sign-in URL. - Open the URL, sign in with the Google account you want to access, grant the requested scope.
- Tokens (including refresh token) are persisted to
~/.mcp-gmail-tokens.json(override withGMAIL_TOKEN_PATH). - 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*.exampletemplates. - OAuth tokens live at
GMAIL_TOKEN_PATH(default~/.mcp-gmail-tokens.json), mode0600. - 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-statustool exposes presence flags and metadata only. - The auth server binds to
localhost:3334only 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
A Model Context Protocol server that enables LLMs to interact with web pages through structured accessibility snapshots without requiring vision models or screenshots.
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.
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.
VeyraX MCP
Single MCP tool to connect all your favorite tools: Gmail, Calendar and 40 more.
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.
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.
E2B
Using MCP to run code via e2b.
Neon Database
MCP server for interacting with Neon Management API and databases
Qdrant Server
This repository is an example of how to create a MCP server for Qdrant, a vector search engine.
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.