Skip to content

A Model Context Protocol (MCP) server that provides read-only Gmail access to Claude CLI with label-based filtering and container deployment support.

License

Notifications You must be signed in to change notification settings

SynclyAI/gmail-mcp

Repository files navigation

Gmail MCP Server

CI codecov

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:

  1. Configure Claude CLI to trust your certificate (if self-signed):

    export NODE_EXTRA_CA_CERTS=/path/to/ca-cert.pem
  2. 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

  1. Ensure the service is running:

    curl http://localhost:3001/health

    Expected response: {"status":"ok"}

  2. Add the server (see above)

  3. Authenticate:

    • Start Claude Code: claude
    • Run /mcp command
    • Select the gmail server
    • Choose "Authenticate"
    • Complete Google OAuth in browser
  4. Verify tools are available: Ask Claude: "What Gmail tools do you have?"

    Claude should list three Gmail tools:

    • list_messages
    • read_message
    • search_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 ID
    • threadId: Thread ID
    • snippet: Message preview
    • labelIds: Array of label IDs
    • from: Sender email address
    • subject: Email subject
    • date: Send date

2. read_message

Reads the full content of a specific message.

Parameters:

  • messageId (string, required): The ID of the message to read
  • maxSize (number, optional): Maximum body size in characters. If exceeded, body is truncated.

Returns:

  • Full message object containing:
    • id: Message ID
    • threadId: Thread ID
    • labelIds: Array of label IDs
    • from: Sender email address
    • to: Recipient email address
    • subject: Email subject
    • date: Send date
    • body: Full email body (Markdown format)
    • bodySize: Original body size in characters
    • truncated: Whether body was truncated
    • snippet: 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 - Inbox
  • SENT - Sent mail
  • DRAFT - Drafts
  • TRASH - Trash
  • SPAM - Spam
  • IMPORTANT - Important
  • STARRED - Starred
  • UNREAD - Unread

Category Labels:

  • CATEGORY_PERSONAL
  • CATEGORY_SOCIAL
  • CATEGORY_PROMOTIONS
  • CATEGORY_UPDATES
  • CATEGORY_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:

  1. list_messages: Only returns messages that have at least one allowed label
  2. read_message: Validates the message has an allowed label before returning content
  3. 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:

  1. Run /mcp command in Claude Code
  2. Select the gmail server
  3. Choose "Authenticate"
  4. 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:

  1. Go to https://console.cloud.google.com
  2. Select your project
  3. Navigate to "APIs & Services" > "Dashboard"
  4. 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:

  1. Run /mcp command in Claude Code
  2. Select the gmail server
  3. Choose "Authenticate"
  4. Complete Google OAuth in browser

Tools not appearing in Claude CLI:

Solution:

  1. Verify the config.json format is correct
  2. Check that the URL is accessible: curl http://localhost:3000/health
  3. Restart Claude CLI
  4. 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 labels
  • read_message: Read full content of a specific message
  • search_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

  1. Navigate to https://console.cloud.google.com
  2. Click the project dropdown in the top navigation bar
  3. Click "New Project"
  4. Enter project name: "Gmail MCP Server" (or any name you prefer)
  5. Note the Project ID (must be globally unique)
  6. Click "Create"

Note: The free tier is sufficient for personal use.

Step 2: Enable Gmail API

  1. In Google Cloud Console, ensure your project is selected
  2. Navigate to "APIs & Services" > "Library" (left sidebar)
  3. Search for "Gmail API"
  4. Click on "Gmail API" from the results
  5. Click the "Enable" button
  6. 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

  1. Navigate to "APIs & Services" > "OAuth consent screen"
  2. Select user type:
    • Choose "External" if using a personal Gmail account
    • Choose "Internal" if using Google Workspace
  3. Click "Create"
  4. Fill in the App information:
    • App name: "Gmail MCP Server"
    • User support email: Your email address
    • Developer contact: Your email address
  5. Click "Save and Continue"
  6. Add scopes:
    • Click "Add or Remove Scopes"
    • Search for "gmail.readonly"
    • Check the box for: https://www.googleapis.com/auth/gmail.readonly
    • Click "Update"
  7. Click "Save and Continue"
  8. Add test users:
    • Click "Add Users"
    • Enter your Gmail address
    • Click "Add"
  9. Click "Save and Continue"
  10. 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

  1. Navigate to "APIs & Services" > "Credentials"
  2. Click "Create Credentials" > "OAuth client ID"
  3. If prompted to configure the consent screen, it should already be done
  4. Application type: Select "Web application"
  5. Name: "Gmail MCP Server"
  6. 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
  7. Click "Create"
  8. Click "Download JSON" to download credentials file
  9. Save the file as credentials.json in your credentials/ 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-server for OAuth metadata
  • Fetches /.well-known/oauth-protected-resource for 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 /authorize with code_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.readonly permission
  • 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_verifier using SHA-256 against stored code_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=... with Authorization: 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:

  1. Add the server to Claude CLI:

    claude mcp add --transport http gmail https://localhost:3000/mcp?allowed_labels=INBOX
  2. In Claude Code, run:

    /mcp
    
  3. Select the gmail server and choose "Authenticate"

  4. 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

  1. Never commit secrets: Ensure credentials/ is in .gitignore
  2. Use Docker secrets: Always use secrets for production deployments
  3. Rotate tokens regularly: Regenerate OAuth tokens periodically
  4. Limit network exposure: Restrict access to localhost or use a reverse proxy with HTTPS
  5. Monitor API quotas: Check Google Cloud Console for Gmail API usage
  6. Use TLS: Deploy behind a reverse proxy with HTTPS for remote access
  7. 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

  1. Install dependencies:

    npm install
  2. Run in development mode (with auto-reload):

    npm run dev
  3. 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

  1. Modify source files in src/
  2. Run tests with npm test
  3. Test locally with npm run dev
  4. Build with npm run build
  5. Rebuild Docker image and redeploy

License & Contributing

This project is licensed under the MIT License.

Contributing

Contributions are welcome! Please:

  1. Fork the repository
  2. Create a feature branch
  3. Make your changes
  4. Submit a pull request

Support

For issues or questions:

  1. Check service logs: docker service logs gmail-mcp
  2. Verify secrets: docker secret ls
  3. Test health endpoint: curl http://localhost:3000/health
  4. Check Gmail API quotas in Google Cloud Console
  5. Verify token validity by regenerating locally

About

A Model Context Protocol (MCP) server that provides read-only Gmail access to Claude CLI with label-based filtering and container deployment support.

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Contributors 3

  •  
  •  
  •  

Languages