Skip to content

cortexark/gmail-tools-mcp

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

3 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

gmail-tools-mcp

MCP server for Gmail — send, read, and manage emails from Claude Code (or any MCP client).

Prerequisites

  • Python 3.12+
  • uv package manager
  • Google Cloud project with Gmail API enabled
  • OAuth 2.0 client credentials (Desktop app type)

Setup

Step 1: Create a Google Cloud Project

  1. Go to Google Cloud Console
  2. Click Select a Project (top bar) → New Project
  3. Name it (e.g., gmail-mcp) and click Create

Step 2: Enable the Gmail API

  1. In your project, go to APIs & ServicesLibrary
  2. Search for Gmail API
  3. Click Enable

Step 3: Configure OAuth Consent Screen

  1. Go to APIs & ServicesOAuth consent screen
  2. Select External (or Internal if using Google Workspace)
  3. Fill in required fields:
    • App name: gmail-tools-mcp (or anything you like)
    • User support email: your email
    • Developer contact: your email
  4. Click Save and Continue
  5. On the Scopes page, click Add or Remove Scopes
  6. Find and add: https://www.googleapis.com/auth/gmail.modify
  7. Click Save and Continue
  8. On the Test users page, click Add Users
  9. Add your Gmail address (the account you want to manage)
  10. Click Save and ContinueBack to Dashboard

Step 4: Create OAuth Credentials

  1. Go to APIs & ServicesCredentials
  2. Click Create CredentialsOAuth client ID
  3. Application type: Desktop app
  4. Name: gmail-tools-mcp (or anything)
  5. Click Create
  6. Click Download JSON — save this file, you'll need it next

Step 5: Install the Server

git clone <repo-url> gmail-tools-mcp
cd gmail-tools-mcp
uv sync

Step 6: Place Your Credentials File

Create the credentials directory and copy your downloaded JSON file into it:

mkdir -p .credentials
cp ~/Downloads/client_secret_*.json .credentials/client_creds.json

Your .credentials/ directory should now look like:

.credentials/
└── client_creds.json    ← the file you just copied

The client_creds.json file contains your OAuth client ID and secret. It looks like this (do NOT share it):

{
  "installed": {
    "client_id": "123456789-abcdef.apps.googleusercontent.com",
    "project_id": "your-project-id",
    "auth_uri": "https://accounts.google.com/o/oauth2/auth",
    "token_uri": "https://oauth2.googleapis.com/token",
    "client_secret": "GOCSPX-...",
    "redirect_uris": ["http://localhost"]
  }
}

Step 7: First Run (OAuth Authentication)

Run the server for the first time to authenticate:

uv run gmail-tools \
  --creds-file-path .credentials/client_creds.json \
  --token-path .credentials/gmail_tokens.json

What happens:

  1. A browser window opens showing Google's OAuth consent screen
  2. Sign in with the Gmail account you added as a test user (Step 3)
  3. Click Allow to grant Gmail access
  4. The browser shows "The authentication flow has completed"
  5. Back in the terminal, the server starts and your tokens are encrypted and saved

After authentication, your .credentials/ directory will contain:

.credentials/
├── client_creds.json          ← your OAuth client credentials (from Google)
├── gmail_tokens.json          ← encrypted OAuth tokens (auto-generated)
└── gmail_tokens.json.key      ← encryption key for the tokens (auto-generated)

On subsequent runs, the server loads the encrypted tokens automatically — no browser needed unless tokens expire and can't be refreshed.

Step 8: Add to Claude Code

Add the server to your Claude Code MCP configuration. Edit ~/.claude.json and add under mcpServers:

{
  "mcpServers": {
    "gmail-tools": {
      "command": "uv",
      "args": [
        "--directory", "/absolute/path/to/gmail-tools-mcp",
        "run", "gmail-tools",
        "--creds-file-path", "/absolute/path/to/gmail-tools-mcp/.credentials/client_creds.json",
        "--token-path", "/absolute/path/to/gmail-tools-mcp/.credentials/gmail_tokens.json"
      ]
    }
  }
}

Replace /absolute/path/to/gmail-tools-mcp with the actual path where you cloned the repo.

For Claude Desktop, add the same config to ~/Library/Application Support/Claude/claude_desktop_config.json (macOS) or %APPDATA%\Claude\claude_desktop_config.json (Windows).


Tools

The server provides 6 tools:

Tool Description Required Inputs
send-email Send an email (confirms with user first) recipient, subject, message
get-unread-emails List unread emails in primary inbox none
read-email Read full content of an email by ID email_id
trash-email Move email to trash (confirms with user first) email_id
mark-as-read Mark an email as read email_id
open-email Open an email in the default browser email_id

Prompts

The server also provides 3 prompts for structured workflows:

Prompt Description Arguments
manage-email Email administrator assistant with all tools none
draft-email Compose an email draft for review content, recipient, recipient_email
edit-draft Revise an existing email draft changes, current_draft

CLI Reference

gmail-tools --creds-file-path <path> --token-path <path>
Argument Required Description
--creds-file-path Yes Path to OAuth 2.0 client credentials JSON (downloaded from Google Cloud Console)
--token-path Yes Path to store/load encrypted OAuth tokens (created automatically on first run)

Authentication Flow

┌─────────────────┐         ┌──────────────────┐         ┌─────────────┐
│  First Run      │         │  Cached Run      │         │  Expired    │
│                 │         │                  │         │             │
│  No tokens on   │         │  Tokens exist    │         │  Tokens     │
│  disk            │         │  and are valid   │         │  expired    │
│                 │         │                  │         │             │
│  → Opens browser │         │  → Loads from    │         │  → Auto     │
│  → OAuth consent │         │    encrypted     │         │    refresh  │
│  → Encrypts &   │         │    vault         │         │  → Re-seal  │
│    saves tokens  │         │  → Ready!        │         │  → Ready!   │
└─────────────────┘         └──────────────────┘         └─────────────┘

Token storage:

  • Tokens are encrypted with Fernet (AES-128-CBC + HMAC-SHA256) before writing to disk
  • The encryption key is stored in a separate .key file
  • Both files are created with 0600 permissions (owner read/write only)
  • The .credentials/ directory is excluded from git via .gitignore

If authentication fails:

  • Delete .credentials/gmail_tokens.json and .credentials/gmail_tokens.json.key
  • Re-run the server — it will trigger a fresh OAuth consent flow

Security

Feature Implementation
Token encryption Fernet (AES-128-CBC + HMAC-SHA256)
File permissions 0600 on vault + key files
Email validation RFC 5322 regex + CR/LF injection check + 254-char limit
Message ID validation Alphanumeric only, 64-char limit
Subject sanitization CR/LF replaced with spaces, 500-char limit
Body validation 100,000-char limit, non-blank check
Rate limiting 30 calls/min (general), 10 calls/min (send/trash)
Error handling No credentials or stack traces in error messages
Schema hardening additionalProperties: false on all tool schemas

Development

Install dev dependencies

uv sync --group dev

Run unit tests

uv run pytest

Unit tests use mocks — no Gmail credentials needed. They run by default and cover all code paths.

Run with coverage report

uv run pytest --cov=src/gmail_tools --cov-report=term-missing

Current coverage: 100% (322/322 statements)

Run live integration tests

Integration tests send real emails through the Gmail API. They require valid credentials at .credentials/.

uv run pytest tests/test_integration_live.py -m integration -v

Integration tests are skipped by default in normal pytest runs. They:

  • Send a test email to the authenticated account (self)
  • Send via the dispatch layer (same path MCP clients use)
  • Fetch unread emails from the real inbox
  • Full roundtrip: send → read back → verify content → trash cleanup

Test structure

tests/
├── test_guards.py              # Input validation (address, subject, body, msg_id)
├── test_vault.py               # Credential encryption (seal/unseal, permissions)
├── test_throttle.py            # Rate limiter (ceiling, window pruning)
├── test_dispatch.py            # Tool dispatch + prompt rendering
├── test_client.py              # GmailClient methods (auth, compose, fetch, etc.)
├── test_server_integration.py  # MCP server handlers, error paths
├── test_exploratory.py         # Boundary values, unicode, edge cases
├── test_bug_probes.py          # Targeted security probes (6 categories)
└── test_integration_live.py    # Live Gmail API tests (requires credentials)

Troubleshooting

Problem Solution
FileNotFoundError: Client secrets not found Check --creds-file-path points to your downloaded Google JSON file
RuntimeError: Gmail authentication failed Delete token files and re-authenticate (see Authentication Flow above)
Browser doesn't open for OAuth Ensure you're running on a machine with a browser; the server uses localhost redirect
Throttled error Rate limit hit — wait 60 seconds. Limits: 10/min for send/trash, 30/min for reads
Malformed email address Address must match RFC 5322 format, max 254 characters, no control characters
OAUTHLIB_INSECURE_TRANSPORT warning Only appears in dev; production uses HTTPS for token exchange
Tokens won't decrypt The .key file may be corrupted — delete both token files and re-authenticate

Project Structure

gmail-tools-mcp/
├── src/gmail_tools/
│   ├── __init__.py          # CLI entry point (argparse)
│   └── server.py            # MCP server (tools, prompts, client, vault, guards)
├── tests/                   # 167 unit tests + 4 integration tests
├── .credentials/            # OAuth credentials (gitignored)
├── pyproject.toml           # Project config, dependencies, test settings
└── README.md

License

MIT

About

MCP server for gmail access

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages