Skip to main content
Glama
brentdemark

Gmail MCP Server

by brentdemark
README.md
# Gmail MCP Server

A self-hostable [Model Context Protocol](https://modelcontextprotocol.io) server that exposes Gmail read and write tools. Connect it to Claude Code or Claude.ai to let Claude manage your email.

## Prerequisites

- Node.js 20+
- A Google Cloud project with Gmail API enabled
- OAuth 2.0 credentials (Desktop app type)

## Setup

### 1. Create Google Cloud Credentials

1. Go to the [Google Cloud Console](https://console.cloud.google.com/)
2. Create a new project (or select an existing one)
3. Enable the **Gmail API**:
   - Navigate to **APIs & Services > Library**
   - Search for "Gmail API" and click **Enable**
4. Create OAuth credentials:
   - Navigate to **APIs & Services > Credentials**
   - Click **Create Credentials > OAuth client ID**
   - Select **Desktop app** as the application type
   - Name it (e.g., "Gmail MCP Server")
   - Click **Create**
5. Copy the **Client ID** and **Client Secret**

### 2. Configure Environment

```bash
cd gmail-mcp
cp .env.example .env
```

Edit `.env` and fill in your credentials:

```
GOOGLE_CLIENT_ID=your-client-id.apps.googleusercontent.com
GOOGLE_CLIENT_SECRET=your-client-secret
```

### 3. Install Dependencies

```bash
npm install
```

### 4. Authenticate with Gmail

```bash
npm run auth
```

This opens your browser for Google's OAuth consent screen. After granting access, the refresh token is automatically saved to `.env`.

> **Note:** If you've previously authorized and need to re-authenticate, revoke access at [Google Account Permissions](https://myaccount.google.com/permissions) first, then run `npm run auth` again.

### 5. Start the Server

**For Claude Code (stdio transport):**
```bash
npm start
```

**For Claude.ai (HTTP/SSE transport):**
```bash
npm run start:http
```

## Connecting to Claude Code

Add to your Claude Code MCP config (`~/.claude.json` or project `.mcp.json`):

```json
{
  "mcpServers": {
    "gmail": {
      "command": "npx",
      "args": ["tsx", "/absolute/path/to/gmail-mcp/src/index.ts"],
      "env": {
        "GOOGLE_CLIENT_ID": "your-client-id",
        "GOOGLE_CLIENT_SECRET": "your-client-secret",
        "GOOGLE_REFRESH_TOKEN": "your-refresh-token"
      }
    }
  }
}
```

Or if you prefer it to read from `.env` automatically, use a wrapper:

```json
{
  "mcpServers": {
    "gmail": {
      "command": "npm",
      "args": ["start", "--prefix", "/absolute/path/to/gmail-mcp"]
    }
  }
}
```

## Connecting to Claude.ai

1. Start the HTTP server: `npm run start:http`
2. The server listens on `http://127.0.0.1:3000` by default (change `PORT` in `.env`)
3. If you need remote access, use a tunnel (e.g., `ngrok http 3000` or Cloudflare Tunnel)
4. In Claude.ai, add the SSE endpoint URL as a custom MCP server: `http://localhost:3000/sse`

## Available Tools

### Read Tools

| Tool | Description |
|------|-------------|
| `search_messages` | Search Gmail using Gmail search syntax. Params: `q` (required), `maxResults`, `pageToken` |
| `read_message` | Read a full message with decoded body. Params: `messageId` |
| `read_thread` | Read all messages in a thread. Params: `threadId` |
| `list_labels` | List all Gmail labels (system + user) with IDs |
| `get_profile` | Get email address and message/thread counts |

### Write Tools (all marked MUTATING)

| Tool | Description |
|------|-------------|
| `label_message` | Add/remove labels on a message. Params: `messageId`, `addLabelIds[]`, `removeLabelIds[]` |
| `label_thread` | Add/remove labels on a thread. Params: `threadId`, `addLabelIds[]`, `removeLabelIds[]` |
| `archive_message` | Archive a message (removes INBOX label). Params: `messageId` |
| `archive_thread` | Archive a thread. Params: `threadId` |
| `trash_message` | Move to Trash (auto-deleted after 30 days). Params: `messageId` |
| `create_label` | Create a new label. Params: `name` |
| `send_message` | Send an email (or reply). Params: `to`, `subject`, `body`, `replyToMessageId?` |
| `create_draft` | Create a draft (or reply draft). Params: `to`, `subject`, `body`, `replyToMessageId?` |

## Gmail Search Syntax Examples

```
from:alice@example.com          # Messages from a specific sender
to:bob@example.com              # Messages to a specific recipient
subject:meeting                 # Subject contains "meeting"
has:attachment                  # Messages with attachments
is:unread                       # Unread messages
is:starred                      # Starred messages
label:INBOX                     # Messages in Inbox
label:Subscriptions             # Messages with a user label
after:2025/01/01                # Messages after a date
before:2025/06/01               # Messages before a date
newer_than:7d                   # Messages from the last 7 days
from:alice subject:project      # Combine multiple criteria
```

## Your Labels

System labels: INBOX, SENT, DRAFT, TRASH, SPAM, STARRED, IMPORTANT, CATEGORY_PERSONAL, CATEGORY_SOCIAL, CATEGORY_PROMOTIONS, CATEGORY_UPDATES, CATEGORY_FORUMS

User labels: Subscriptions, My Finance, Receipts, My Travel, Taxes, DFL, Integral Function, PV 2025, Scotland, mini-split, eclips

> **Tip:** Use `list_labels` to get the exact label IDs needed for `label_message` and `label_thread` operations.

## Security Notes

- The refresh token in `.env` grants access to your Gmail. **Never commit `.env` to version control.**
- The HTTP server binds to `127.0.0.1` only (localhost). Use a tunnel for remote access.
- The `gmail.modify` scope is used — this allows reading, labeling, archiving, trashing, and sending, but does **not** allow permanent message deletion.