Gmail MCP Server
Provides secure, read-only access to Gmail for Claude CLI with label-based filtering for enhanced privacy and control. It enables users to search, list, and summarize email content while ensuring no modification or deletion of messages is possible.
README
Gmail MCP Server
A Model Context Protocol (MCP) server that provides read-only Gmail access to Claude CLI with label-based filtering and container deployment support.
Why Use This?
- Security first: Read-only access means Claude cannot send, delete, or modify your emails. Label filtering lets you restrict which emails are visible.
- Summarize important information: Let Claude read and summarize emails so you can quickly understand what matters.
- Never miss important emails: Avoid overlooks by having Claude search and highlight critical information buried in your inbox.
- Save time: Quickly find and understand email content without manual searching through hundreds of messages.
Quick start:
-
Configure Claude CLI to trust your certificate (if self-signed):
export NODE_EXTRA_CA_CERTS=/path/to/ca-cert.pem -
Add the server:
claude mcp add --transport http gmail https://localhost:3000/mcp?allowed_labels=INBOX
Then ask Claude: "Summarize my unread emails from today" or "Find all emails about the project deadline"
Pro tip: Create a custom slash command for frequent tasks. Add to .claude/commands/email-summary.md:
Summarize unread emails from last 24 hours. Group by sender and highlight action items.
Then use /email-summary anytime.
Claude CLI Integration
Certificate Configuration
For self-signed or internal CA certificates, configure Claude CLI to trust them:
export NODE_EXTRA_CA_CERTS=/path/to/ca-cert.pem
Alternatively, add to ~/.claude/settings.json:
{
"env": {
"NODE_EXTRA_CA_CERTS": "/path/to/ca-cert.pem"
}
}
Note: The settings.json env configuration may not be applied due to a potential Claude CLI bug. If you experience certificate errors, use the shell environment variable approach instead.
Add Server via Command Line
claude mcp add --transport http gmail https://localhost:3001/mcp?allowed_labels=INBOX,STARRED
Label filtering via URL: Configure which Gmail labels Claude can access using the allowed_labels query parameter. Use comma-separated label names (e.g., INBOX,STARRED,IMPORTANT). This parameter is required.
Manual Configuration
Claude CLI configuration is stored at ~/.claude.json:
{
"mcpServers": {
"gmail": {
"transport": "http",
"url": "https://localhost:3001/mcp?allowed_labels=INBOX,STARRED"
}
}
}
Integration Steps
-
Ensure the service is running:
curl http://localhost:3001/healthExpected response:
{"status":"ok"} -
Add the server (see above)
-
Authenticate:
- Start Claude Code:
claude - Run
/mcpcommand - Select the gmail server
- Choose "Authenticate"
- Complete Google OAuth in browser
- Start Claude Code:
-
Verify tools are available: Ask Claude: "What Gmail tools do you have?"
Claude should list three Gmail tools:
list_messagesread_messagesearch_messages
Available Tools
1. list_messages
Lists Gmail messages filtered by allowed labels.
Parameters:
maxResults(number, optional): Maximum number of messages to return (default: 10)pageToken(string, optional): Page token for pagination
Returns:
- Array of message summaries containing:
id: Message IDthreadId: Thread IDsnippet: Message previewlabelIds: Array of label IDsfrom: Sender email addresssubject: Email subjectdate: Send date
2. read_message
Reads the full content of a specific message.
Parameters:
messageId(string, required): The ID of the message to readmaxSize(number, optional): Maximum body size in characters. If exceeded, body is truncated.
Returns:
- Full message object containing:
id: Message IDthreadId: Thread IDlabelIds: Array of label IDsfrom: Sender email addressto: Recipient email addresssubject: Email subjectdate: Send datebody: Full email body (Markdown format)bodySize: Original body size in characterstruncated: Whether body was truncatedsnippet: Message preview
Note: This tool validates that the message has at least one allowed label before returning content. HTML-only emails are automatically converted to Markdown for better readability and token efficiency.
3. search_messages
Searches messages using Gmail query syntax.
Parameters:
query(string, required): Gmail search query (e.g., "from:example@gmail.com subject:invoice")maxResults(number, optional): Maximum number of messages to return (default: 10)pageToken(string, optional): Page token for pagination
Returns:
- Array of matching message summaries (same format as
list_messages)
Note: Label filtering is automatically applied to search results.
Usage Examples
Example 1: List Recent Emails
User: List my 5 most recent Gmail messages
Claude: [Uses list_messages tool with maxResults: 5]
Claude will return a list of 5 messages showing ID, subject, sender, and date.
Example 2: Read Specific Message
User: Read the full content of message ID 18c2f4a3b5e91234
Claude: [Uses read_message tool with messageId: "18c2f4a3b5e91234"]
Claude will return the full email content including headers and body.
Example 3: Search Emails
User: Search my Gmail for emails from support@example.com
Claude: [Uses search_messages tool with query: "from:support@example.com"]
Claude will return all matching messages from that sender (filtered by allowed labels).
Example 4: Complex Search
User: Find emails from last week with "invoice" in the subject
Claude: [Uses search_messages with query: "after:2025/11/23 subject:invoice"]
Claude will return matching messages using Gmail's search syntax.
Label Filtering
Label filtering allows you to control which emails Claude can access by specifying a whitelist of allowed labels.
Configuration
Label filtering is configured per-user via the MCP server URL query parameter allowed_labels:
claude mcp add --transport http gmail http://localhost:3001/mcp?allowed_labels=INBOX,STARRED
Or in Claude CLI config:
{
"mcpServers": {
"gmail": {
"transport": "http",
"url": "http://localhost:3001/mcp?allowed_labels=INBOX,STARRED"
}
}
}
Label ID Formats
System Labels (uppercase):
INBOX- InboxSENT- Sent mailDRAFT- DraftsTRASH- TrashSPAM- SpamIMPORTANT- ImportantSTARRED- StarredUNREAD- Unread
Category Labels:
CATEGORY_PERSONALCATEGORY_SOCIALCATEGORY_PROMOTIONSCATEGORY_UPDATESCATEGORY_FORUMS
Custom Labels:
Use your custom label names directly (e.g., Work, Personal). The server resolves names to IDs automatically.
How Filtering Works
Label filtering is applied at three levels:
- list_messages: Only returns messages that have at least one allowed label
- read_message: Validates the message has an allowed label before returning content
- search_messages: Automatically adds label filter to the search query
The allowed_labels parameter is required on /mcp endpoint. If empty or missing, returns 400 error.
Examples
Allow only inbox and starred:
http://localhost:3001/mcp?allowed_labels=INBOX,STARRED
Allow inbox and a custom label:
http://localhost:3001/mcp?allowed_labels=INBOX,Work
Allow work-related categories:
http://localhost:3001/mcp?allowed_labels=INBOX,IMPORTANT,CATEGORY_UPDATES
Updating Labels
To change allowed_labels, update the URL in Claude CLI config:
Option 1: Edit config file
Edit ~/.claude.json and modify the URL:
{
"mcpServers": {
"gmail": {
"url": "https://localhost:3001/mcp?allowed_labels=INBOX,NEW_LABEL"
}
}
}
Option 2: Remove and re-add server
claude mcp remove gmail
claude mcp add --transport http gmail https://localhost:3001/mcp?allowed_labels=INBOX,NEW_LABEL
Troubleshooting
Common Issues
"Not authenticated" error:
Solution: Re-authenticate via Claude CLI:
- Run
/mcpcommand in Claude Code - Select the gmail server
- Choose "Authenticate"
- Complete Google OAuth in browser
"403 Forbidden" error:
Solution: Check that you've enabled the Gmail API and configured the correct OAuth scope (gmail.readonly).
"Quota exceeded" error:
Solution: Check your Gmail API usage in Google Cloud Console:
- Go to https://console.cloud.google.com
- Select your project
- Navigate to "APIs & Services" > "Dashboard"
- Check Gmail API quotas
Default quota is 1 billion units per day, which should be sufficient for personal use.
"Connection refused" error:
Solution: Verify the service is running:
curl http://localhost:3000/health
"Timeout" error when token invalid:
Claude CLI may report a timeout instead of authentication error when the MCP server returns 401 (token decryption failed or refresh token revoked).
Solution: Re-authenticate via Claude CLI:
- Run
/mcpcommand in Claude Code - Select the gmail server
- Choose "Authenticate"
- Complete Google OAuth in browser
Tools not appearing in Claude CLI:
Solution:
- Verify the config.json format is correct
- Check that the URL is accessible:
curl http://localhost:3000/health - Restart Claude CLI
- Check Claude CLI logs for errors
Server Administration
The following sections are for administrators deploying and managing the MCP server.
Features
- Stateless architecture - Encrypted bearer tokens (JWE) with no server-side session storage
- Multi-tenant OAuth 2.0 - Multiple clients with separate Gmail accounts
- Server-side Google callback - Google redirects to server, not directly to client
- Per-user label filtering - Each user configures their own allowed labels
- Transparent token refresh - Automatic Google token refresh on each request
- Three MCP tools: list messages, read message content, search messages
- Structured logging - Pino logger with JSON output in production
- Unit tests - Vitest test suite for auth modules
- HTTPS transport for secure remote deployment
- Docker ready with health checks
Architecture
The server uses a stateless architecture where Google OAuth tokens are encrypted into a JWE (JSON Web Encryption) bearer token returned to Claude CLI. On each request, the server decrypts the token, refreshes the Google access token if expired, and executes Gmail API calls.
Three MCP tools are available:
list_messages: List Gmail messages filtered by allowed labelsread_message: Read full content of a specific messagesearch_messages: Search messages using Gmail query syntax
All communication happens over HTTPS. Credentials and encryption keys are loaded from files specified via environment variables.
Prerequisites
- Node.js >= 22.0.0
- Docker (for containerized deployment)
- Gmail account
- Google Cloud Console account (free tier sufficient)
- Claude CLI installed
Google Cloud Console Setup
Step 1: Create Google Cloud Project
- Navigate to https://console.cloud.google.com
- Click the project dropdown in the top navigation bar
- Click "New Project"
- Enter project name: "Gmail MCP Server" (or any name you prefer)
- Note the Project ID (must be globally unique)
- Click "Create"
Note: The free tier is sufficient for personal use.
Step 2: Enable Gmail API
- In Google Cloud Console, ensure your project is selected
- Navigate to "APIs & Services" > "Library" (left sidebar)
- Search for "Gmail API"
- Click on "Gmail API" from the results
- Click the "Enable" button
- Wait for the API to be enabled (~30 seconds)
Note: There's no cost for enabling the API; it uses pay-per-use pricing.
Step 3: Configure OAuth Consent Screen
- Navigate to "APIs & Services" > "OAuth consent screen"
- Select user type:
- Choose "External" if using a personal Gmail account
- Choose "Internal" if using Google Workspace
- Click "Create"
- Fill in the App information:
- App name: "Gmail MCP Server"
- User support email: Your email address
- Developer contact: Your email address
- Click "Save and Continue"
- Add scopes:
- Click "Add or Remove Scopes"
- Search for "gmail.readonly"
- Check the box for:
https://www.googleapis.com/auth/gmail.readonly - Click "Update"
- Click "Save and Continue"
- Add test users:
- Click "Add Users"
- Enter your Gmail address
- Click "Add"
- Click "Save and Continue"
- Review the summary and click "Back to Dashboard"
Important: The read-only scope (gmail.readonly) is the most secure option and is all that's needed.
Step 4: Create OAuth 2.0 Credentials
- Navigate to "APIs & Services" > "Credentials"
- Click "Create Credentials" > "OAuth client ID"
- If prompted to configure the consent screen, it should already be done
- Application type: Select "Web application"
- Name: "Gmail MCP Server"
- Add Authorized redirect URI: The URL where your MCP server's callback endpoint is accessible
- This must be the actual URL reachable from user's browser after Google login
- Production:
https://mcp.example.com/callback - Local testing:
http://localhost:3000/callback
- Click "Create"
- Click "Download JSON" to download credentials file
- Save the file as
credentials.jsonin yourcredentials/directory
Important:
- The redirect URI in credentials must exactly match what's configured in Google Cloud Console
- Keep credentials secure and never commit them to version control
The downloaded file format:
{
"web": {
"client_id": "xxx.apps.googleusercontent.com",
"client_secret": "xxx",
"redirect_uris": ["https://mcp.example.com/callback"]
}
}
For Docker deployment, mount this file as a volume or use container secrets.
Installation & Setup
Clone or Download
If you have this project locally, navigate to the project directory:
cd /path/to/email-mcp
Install Dependencies
npm install
Authentication Flow (MCP OAuth 2.1)
The server implements the MCP Authorization Specification using OAuth 2.1 with PKCE. This creates a two-tier OAuth flow: Claude CLI authenticates with the Gmail MCP Server, which in turn authenticates with Google.
┌─────────────┐ ┌─────────────────┐ ┌────────────────┐
│ Claude CLI │────▶│ Gmail MCP Server│────▶│ Google OAuth │
└─────────────┘ └─────────────────┘ └────────────────┘
MCP OAuth 2.1 Proxies to Google OAuth 2.0
with PKCE Google OAuth
Step-by-Step Flow
1. Discovery & Client Registration
Claude CLI discovers the server's OAuth capabilities:
- Fetches
/.well-known/oauth-authorization-serverfor OAuth metadata - Fetches
/.well-known/oauth-protected-resourcefor resource metadata - Performs Dynamic Client Registration at
/register(RFC 7591)
2. Authorization Request (Claude CLI → Server → Browser)
Claude CLI initiates OAuth 2.1 with PKCE:
- Claude CLI opens browser to
/authorizewithcode_challenge - Server stores pending request (client ID, redirect URI, code challenge)
- Server responds with HTTP 302 redirect to Google OAuth (browser follows automatically)
3. Google Authentication (Browser → Google → Server → Browser)
User authenticates with Google:
- User logs in and grants
gmail.readonlypermission - Google responds with HTTP 302 redirect to
/callback(browser follows) - Server exchanges Google authorization code for Google access/refresh tokens
- Server generates an MCP authorization code
- Server responds with HTTP 302 redirect to Claude CLI callback (browser follows)
4. Token Exchange (Claude CLI → Server)
Claude CLI completes the OAuth flow:
- Exchanges MCP authorization code for access token at
/token - Server validates PKCE
code_verifierusing SHA-256 against storedcode_challenge - Server encrypts Google tokens + user email into a JWE (JSON Web Encryption) token
- Server returns the encrypted JWE as the MCP bearer token
5. Authenticated MCP Requests (Stateless)
Claude CLI uses the bearer token:
- Sends requests to
/mcp?allowed_labels=...withAuthorization: Bearer <encrypted-jwe> - Server decrypts the JWE to extract Google tokens
- Server refreshes Google access token if expired (transparent to client)
- Server executes Gmail API calls and returns results
Claude CLI Authentication
The easiest way to authenticate is using Claude CLI's built-in OAuth support:
-
Add the server to Claude CLI:
claude mcp add --transport http gmail https://localhost:3000/mcp?allowed_labels=INBOX -
In Claude Code, run:
/mcp -
Select the gmail server and choose "Authenticate"
-
Complete the Google OAuth flow in your browser
Claude CLI automatically handles token storage and refresh.
Quick Start (Local Development)
For local development and testing:
1. Build the Project
npm run build
2. Start the Server
npm start
The server will start on port 3000 (or the port specified in the PORT environment variable).
3. Test the Server
Health check:
curl http://localhost:3000/health
Expected response:
{"status":"ok"}
Test MCP tools list:
curl -X POST http://localhost:3000/mcp \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}'
You should see four tools listed: list_messages, read_message, search_messages, logout.
4. Development Mode
For development with auto-reload:
npm run dev
Environment Variables
| Variable | Required | Default | Description |
|---|---|---|---|
OAUTH_CREDENTIALS_PATH |
Yes | - | Path to Google OAuth credentials JSON file |
TOKEN_ENCRYPTION_PATH |
Yes | - | Path to file containing 32-byte encryption key (hex or base64) |
TLS_KEY_PATH |
Yes | - | Path to TLS private key file (PEM) |
TLS_CERT_PATH |
Yes | - | Path to TLS certificate file (PEM) |
PORT |
No | 3000 | HTTPS server port |
HOST |
No | localhost | Server hostname |
SESSION_TTL |
No | 86400 | MCP session TTL in seconds (default: 24 hours) |
LOG_LEVEL |
No | info | Log level (trace, debug, info, warn, error, fatal) |
NODE_ENV |
No | - | Set to "production" for JSON log output |
Note: Label filtering is configured per-client via URL query parameter allowed_labels. This parameter is required and cannot be empty.
Security Considerations
OAuth Scope
This server uses the gmail.readonly scope, which provides:
- Read-only access to Gmail messages and metadata
- No ability to send, delete, or modify emails
- No access to Gmail settings or account information
This is the most secure scope for accessing Gmail data.
Credential Storage
Local Development:
- Credentials stored in
credentials/directory - Directory is excluded from git via
.gitignore - Never commit
credentials/to version control
Container Deployment:
- Use container secrets or mounted volumes for credentials
- Ensure credentials file is read-only
- Use environment variables for configuration
Token Management
- MCP bearer token is an encrypted JWE containing Google tokens and user email
- Stateless design: Server has no persistent storage for authenticated requests
- Google access tokens expire after 1 hour (auto-refreshed transparently on each request)
- Google refresh tokens are long-lived (until manually revoked by user)
- Encryption key is required (
TOKEN_ENCRYPTION_PATH) - key rotation invalidates all tokens - Re-authentication required when: refresh token revoked, encryption key rotated, or token corrupted
OAuth Security Measures
The server implements several OAuth 2.1 security measures:
- PKCE S256 verification: Code verifier is validated using SHA-256 hash comparison
- Redirect URI validation: Only registered redirect URIs are accepted during authorization
- Encrypted bearer tokens: JWE tokens use AES-256-GCM encryption (dir/A256GCM)
- State parameter validation: OAuth state uses UUID format with strict parsing
- Authorization code single-use: Codes are deleted immediately after exchange
Best Practices
- Never commit secrets: Ensure
credentials/is in.gitignore - Use Docker secrets: Always use secrets for production deployments
- Rotate tokens regularly: Regenerate OAuth tokens periodically
- Limit network exposure: Restrict access to localhost or use a reverse proxy with HTTPS
- Monitor API quotas: Check Google Cloud Console for Gmail API usage
- Use TLS: Deploy behind a reverse proxy with HTTPS for remote access
- Audit access: Review OAuth consent screen and authorized applications in your Google account
Gmail API Quotas
- Default quota: 1 billion units per day
- Personal use should stay well within limits
- Monitor usage in Google Cloud Console
- The server implements no additional rate limiting
Development
Local Development Setup
-
Install dependencies:
npm install -
Run in development mode (with auto-reload):
npm run dev -
Authenticate via Claude CLI when first accessing the server
Running Tests
Run unit tests:
npm test
Run tests in watch mode:
npm run test:watch
Run tests with coverage:
npm run test:coverage
Building
Build the project:
npm run build
Test the compiled output:
npm start
Making Changes
- Modify source files in
src/ - Run tests with
npm test - Test locally with
npm run dev - Build with
npm run build - Rebuild Docker image and redeploy
License & Contributing
This project is licensed under the MIT License.
Contributing
Contributions are welcome! Please:
- Fork the repository
- Create a feature branch
- Make your changes
- Submit a pull request
Support
For issues or questions:
- Check service logs:
docker service logs gmail-mcp - Verify secrets:
docker secret ls - Test health endpoint:
curl http://localhost:3000/health - Check Gmail API quotas in Google Cloud Console
- Verify token validity by regenerating locally
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.
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.
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.
Qdrant Server
This repository is an example of how to create a MCP server for Qdrant, a vector search engine.
Neon Database
MCP server for interacting with Neon Management API and databases
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.
E2B
Using MCP to run code via e2b.