MCP Servers

A collection of Model Context Protocol servers, templates, tools and more.

Read-only Gmail investigation tool with MCP server support. Secure OAuth-based email access for Claude with test-enforced security guarantees.

Created 2/17/2026
Updated about 21 hours ago
Repository documentation and setup instructions

Gmail Reader - Read-Only Gmail Investigation Tool

CRITICAL SECURITY NOTICE: This tool is READ-ONLY ONLY. It cannot send, modify, or delete emails.

A secure, Python-based CLI tool and MCP server for investigating Gmail accounts with guaranteed read-only access. Provides seamless Gmail integration for Claude desktop/Code via Model Context Protocol (MCP). Uses the same battle-tested security architecture as google_ads_controller.

Features

  • Dual Interface: CLI tool + MCP server for Claude integration
  • Read-Only: Gmail API with gmail.readonly scope only
  • Secure: Test-enforced security guardrails (13 tests, all passing)
  • Full Access: Search, read, list, export emails and threads
  • Rate Limited: 10 req/sec (configurable Gmail API quota management)
  • OAuth 2.0: Secure authentication with credentials in ~/.env

Security Guarantees

Three-Layer Security Architecture

  1. OAuth Scope Restriction (Layer 1)

    • Only gmail.readonly scope is granted during OAuth flow
    • Cannot send, modify, or delete emails at the OAuth level
    • Google enforces this - impossible to bypass
  2. Test-Enforced Guardrails (Layer 2)

    • test_read_only_guardrail.py scans all source files on every commit
    • CI fails if mutating methods found (.send(), .modify(), .trash(), .delete())
    • CI fails if non-readonly scopes found in auth.py
  3. Secret Leak Prevention (Layer 3)

    • test_secret_leak.py scans for hardcoded credentials
    • .gitignore blocks .env and *.json credential files
    • CI fails if secrets found in code

What Gets Committed vs. What Stays Local

Committed (safe):

  • Source code (no secrets)
  • Security tests (scanners)
  • Documentation

NEVER Committed (blocked by .gitignore + tests):

  • ~/.env (OAuth credentials)
  • *.json files (client_secret.json, token.json)
  • Hardcoded secrets

Installation

Prerequisites

  • Python 3.9+ (CLI only) or Python 3.11+ (for MCP server)
  • Google Cloud Project with Gmail API enabled
  • OAuth 2.0 credentials (Desktop app)

Setup Steps

  1. Clone or create project directory:

    cd ~/Documents/gmail_reader
    
  2. Create virtual environment:

    python3.9 -m venv .venv
    source .venv/bin/activate
    
  3. Install package:

    pip install -e .
    
  4. Obtain OAuth credentials (one-time):

    • Go to Google Cloud Console
    • Create a new project or select existing
    • Enable Gmail API (APIs & Services → Library → Gmail API → Enable)
    • Create credentials (APIs & Services → Credentials → Create Credentials → OAuth client ID)
    • Application type: Desktop app
    • Download JSON file
  5. Add credentials to ~/.env:

    • Open downloaded JSON file
    • Add to ~/.env:
      GMAIL_CLIENT_ID=YOUR_CLIENT_ID.apps.googleusercontent.com
      GMAIL_CLIENT_SECRET=GOCSPX-YOUR_CLIENT_SECRET
      
    • Delete the downloaded JSON file after copying values
  6. Run OAuth authentication (one-time):

    gmail-reader auth
    
    • Opens browser to Google consent screen
    • Grant permission for read-only Gmail access
    • Refresh token automatically saved to ~/.env
  7. Test:

    gmail-reader list --max 10
    

MCP Server Setup (Claude Integration)

The MCP server allows Claude to directly access your Gmail without manual CLI commands.

Quick Setup

  1. Ensure Python 3.11+ is installed (MCP requires Python 3.10+):

    python3.11 --version  # Should be 3.11 or higher
    
  2. Install gmail-reader (if not already):

    cd ~/Documents/gmail_reader
    python3.11 -m venv .venv
    source .venv/bin/activate
    pip install -e .
    
  3. Configure MCP server in Claude desktop config:

    For Claude Code (VSCode extension), add to your MCP settings:

    {
      "mcpServers": {
        "gmail": {
          "command": "/Users/YOUR_USERNAME/Documents/gmail_reader/.venv/bin/python",
          "args": ["-m", "gmail_reader.mcp_server"],
          "env": {}
        }
      }
    }
    

    For Claude Desktop app, add to ~/Library/Application Support/Claude/claude_desktop_config.json:

    {
      "mcpServers": {
        "gmail": {
          "command": "/Users/YOUR_USERNAME/Documents/gmail_reader/.venv/bin/python",
          "args": ["-m", "gmail_reader.mcp_server"]
        }
      }
    }
    
  4. Restart Claude to load the MCP server

MCP Tools Available

Once configured, Claude can use these tools:

| Tool | Description | |------|-------------| | gmail_list | List recent emails with sender, subject, date, snippet | | gmail_search | Search with Gmail query operators (from:, subject:, after:, etc.) | | gmail_read | Read full email content (headers + body) | | gmail_labels | List all Gmail labels (INBOX, SENT, user-created) | | gmail_thread | View all messages in a thread/conversation | | gmail_export | Export emails in date range to JSON |

Example Claude Interactions

User: "Show me emails from boss@company.com in the last week"
Claude: [Uses gmail_search with query "from:boss@company.com after:2026/02/10"]

User: "Read the most recent email from Google"
Claude: [Uses gmail_list to find ID, then gmail_read to show full content]

User: "How many unread emails do I have?"
Claude: [Uses gmail_search with query "is:unread"]

Troubleshooting MCP Server

Error: "Authentication error"

  • Run gmail-reader auth in terminal first
  • Ensure ~/.env has GMAIL_REFRESH_TOKEN

MCP server not loading

  • Check Python path is correct (use absolute path)
  • Restart Claude desktop/Code
  • Check Claude logs for errors

Usage

Authentication

# One-time OAuth setup (opens browser)
gmail-reader auth

List Recent Emails

# List 50 most recent emails (default)
gmail-reader list

# List 100 most recent emails
gmail-reader list --max 100

# Output as JSON
gmail-reader list --max 10 --output json

Search Emails

Gmail supports powerful search operators:

# Search by sender
gmail-reader search "from:boss@company.com"

# Search by date range
gmail-reader search "after:2026/02/01 before:2026/02/17"

# Search by subject
gmail-reader search "subject:invoice"

# Search unread emails
gmail-reader search "is:unread"

# Combine multiple criteria
gmail-reader search "from:google.com is:unread after:2026/02/01" --max 100

# Search with label
gmail-reader search "label:important"

# Output as JSON
gmail-reader search "from:noreply" --max 5 --output json

Search Operators:

  • from: - Sender email address
  • to: - Recipient email address
  • subject: - Subject line keywords
  • after: - Date range start (YYYY/MM/DD)
  • before: - Date range end (YYYY/MM/DD)
  • is:unread / is:read - Read status
  • label: - Gmail label
  • has:attachment - Has attachments

Read Full Email

# Read email (snippet view)
gmail-reader read <message-id>

# Read email (full body)
gmail-reader read <message-id> --format full

# Output as JSON
gmail-reader read <message-id> --output json

Export Emails

# Export all emails in date range to JSON
gmail-reader export --start-date 2026-01-01 --end-date 2026-02-17

# Custom output file
gmail-reader export --start-date 2026-01-01 --end-date 2026-02-17 --file my_emails.json

Note: Large exports stream to file to avoid memory overflow.

List Gmail Labels

# List all labels
gmail-reader labels

# Output as JSON
gmail-reader labels --output json

View Email Thread

# View all messages in a thread
gmail-reader threads <thread-id>

# Output as JSON
gmail-reader threads <thread-id> --output json

Rate Limiting

  • Gmail API quota: 250 units/user/second
  • Default: 10 requests/second (50 units/sec, 20% of limit)
  • Automatic retry: On 429 (rate limit exceeded) errors

Rate limiting is configured in src/gmail_reader/client.py:

_RATE_LIMIT_RPS = 10  # Requests per second

Project Structure

gmail_reader/
├── src/gmail_reader/
│   ├── __init__.py          - Package metadata
│   ├── config.py            - Load ~/.env credentials
│   ├── auth.py              - OAuth 2.0 flow
│   ├── client.py            - Gmail API client + rate limiting
│   ├── queries.py           - Query constants & helpers
│   ├── __main__.py          - CLI entry point
│   ├── mcp_server.py        - MCP server for Claude integration
│   └── reports.py           - Email parsing & formatting
├── tests/
│   ├── test_config.py                - Config loading tests
│   ├── test_read_only_guardrail.py   - Security scanner (mutations)
│   └── test_secret_leak.py           - Security scanner (credentials)
├── pyproject.toml           - Package configuration
├── .gitignore               - Blocks .env and *.json
└── README.md                - This file

Security Best Practices

Credentials Management

  1. All credentials in ~/.env - Never commit to git
  2. Delete OAuth JSON after adding to ~/.env
  3. Refresh token rotation - If token expires, re-run gmail-reader auth

Verifying Read-Only Status

Run security tests to verify no mutations:

pytest tests/test_read_only_guardrail.py -v
pytest tests/test_secret_leak.py -v

OAuth Scope Verification

Check src/gmail_reader/auth.py:

SCOPES = ["https://www.googleapis.com/auth/gmail.readonly"]

Only gmail.readonly should be present. Security test will fail if modified.

Troubleshooting

"Error: GMAIL_REFRESH_TOKEN not found"

Solution: Run gmail-reader auth to authenticate.

"Refresh token expired"

Solution: Re-run gmail-reader auth to obtain new refresh token.

"Rate limit exceeded"

Solution: Wait 10 seconds. Tool automatically retries once. If persistent, increase delay in client.py.

"No module named 'gmail_reader'"

Solution: Install package with pip install -e . from project root.

OAuth JSON downloaded but can't find it

Solution: Check ~/Downloads/client_secret_*.json. Open file and copy client_id and client_secret to ~/.env.

Development

Running Tests

# All tests
pytest tests/ -v

# Security tests only
pytest tests/test_read_only_guardrail.py tests/test_secret_leak.py -v

# Config tests only
pytest tests/test_config.py -v

Adding New Features

CRITICAL: Never add write operations. Security tests will fail.

If you need to add a new read operation:

  1. Update ALLOWED_GMAIL_METHODS in tests/test_read_only_guardrail.py
  2. Implement using only .list() or .get() methods
  3. Run pytest tests/test_read_only_guardrail.py to verify

License

MIT License - See LICENSE file for details.

Related Projects

Support

For issues or questions, create an issue in the GitHub repository.


Last Updated: 2026-02-17 Version: 0.1.0

Quick Setup
Installation guide for this server

Install Package (if required)

uvx gmail-mcp

Cursor configuration (mcp.json)

{ "mcpServers": { "adamferguson06-gmail-mcp": { "command": "uvx", "args": [ "gmail-mcp" ] } } }