Skip to main content
Glama
README.md
# Gmail MCP Server

Full Gmail control for any MCP-compatible AI agent. Exposes ~40 tools covering reading, composing, labeling, filtering, threading, and account management — works with Claude, Cursor, Copilot, Goose, OpenCode, and any agent that speaks the MCP stdio protocol.

---

## Requirements

- Python 3.10+
- A Google account
- Any MCP-compatible AI agent (Claude Code, Cursor, Copilot, Goose, OpenCode, etc.)

---

## Quick Install (Linux)

Detects Arch or Ubuntu/Debian automatically, creates a virtualenv, installs deps, sets credential permissions, and prints the MCP config snippet ready to paste:

```bash
bash install.sh
```

Manual steps follow below if you prefer full control.

---

## 1. Python Dependencies

```bash
pip install -r requirements.txt
```

`requirements.txt`:
```
mcp
google-api-python-client
google-auth-oauthlib
google-auth-httplib2
```

---

## 2. Google Cloud Console Setup

### 2.1 Create a Project

1. Go to [https://console.cloud.google.com/](https://console.cloud.google.com/)
2. Click the project dropdown (top-left) → **New Project**
3. Name it (e.g. `gmail-mcp`) → **Create**
4. Make sure the new project is selected in the dropdown

### 2.2 Enable the Gmail API

1. Go to **APIs & Services** → **Library**
2. Search for `Gmail API` → click it → **Enable**

### 2.3 Configure the OAuth Consent Screen

1. Go to **APIs & Services** → **OAuth consent screen**
2. Choose **External** → **Create**
3. Fill in:
   - **App name**: anything (e.g. `Gmail MCP`)
   - **User support email**: your Gmail address
   - **Developer contact email**: your Gmail address
4. Click **Save and Continue** through Scopes (skip adding scopes here)
5. On the **Test users** page → **Add users** → add your Gmail address
6. Click **Save and Continue** → **Back to Dashboard**

> **Important:** While the app is in *Testing* mode only the users listed here can authenticate. The app does not need to be published.

### 2.4 Add Required OAuth Scopes

1. Go to **APIs & Services** → **OAuth consent screen** → **Edit App**
2. On the **Scopes** step → **Add or Remove Scopes**
3. Search for and select each of the following:

   | Scope | Why |
   |---|---|
   | `https://mail.google.com/` | Full read/write/delete/send access |
   | `https://www.googleapis.com/auth/gmail.settings.basic` | Create/manage filters and labels |

4. Click **Update** → **Save and Continue**

> `gmail.settings.basic` is required specifically for `filters.create` / `filters.delete`. `mail.google.com` alone is **not** sufficient for filter management despite appearing to be a superset scope.

### 2.5 Create OAuth Credentials

1. Go to **APIs & Services** → **Credentials**
2. **+ Create Credentials** → **OAuth client ID**
3. Application type: **Desktop app**
4. Name: anything → **Create**
5. Click **Download JSON** on the resulting dialog
6. Save the downloaded file as `credentials.json` in this project directory

---

## 3. First-Run Authentication

Run the server once manually to trigger the OAuth browser flow:

```bash
python server.py
```

A browser window opens. Log in with the Google account added as a test user and grant both requested permissions.

This writes `token.pickle` to the project directory. Subsequent runs use this token silently (auto-refreshed by Google's OAuth library).

**If you change the requested scopes** (e.g. add `gmail.settings.basic` later), the old token must be deleted:

```bash
rm token.pickle
python server.py
```

---

## 4. Register with AI Coding Assistants

All tools below use the same MCP stdio transport. The only difference between them is where the config file lives.

**Common values to substitute throughout:**
- `PYTHON` → full path to the Python binary with dependencies (e.g. `/home/user/.local/share/mamba/bin/python`)
- `SERVER` → full path to `server.py` (e.g. `/home/user/gmail/server.py`)

---

### Claude Code

`~/.claude.json`:
```json
{
  "mcpServers": {
    "gmail": {
      "type": "stdio",
      "command": "PYTHON",
      "args": ["SERVER"],
      "env": {}
    }
  }
}
```

`~/.claude/settings.json`:
```json
{
  "mcpServers": {
    "gmail": {
      "command": "PYTHON",
      "args": ["SERVER"]
    }
  }
}
```

Restart Claude Code after editing.

---

### Cursor

Global config `~/.cursor/mcp.json` (or per-project `.cursor/mcp.json`):
```json
{
  "mcpServers": {
    "gmail": {
      "command": "PYTHON",
      "args": ["SERVER"]
    }
  }
}
```

Reload via **Cursor Settings → MCP** or restart Cursor.

---

### Windsurf (Codeium)

`~/.codeium/windsurf/mcp_config.json`:
```json
{
  "mcpServers": {
    "gmail": {
      "command": "PYTHON",
      "args": ["SERVER"]
    }
  }
}
```

Reload via **Windsurf Settings → MCP Servers → Refresh**.

---

### GitHub Copilot (VS Code)

VS Code 1.99+ supports MCP natively. Add to your **User `settings.json`** (`Ctrl+Shift+P` → *Open User Settings JSON*):
```json
{
  "mcp": {
    "servers": {
      "gmail": {
        "type": "stdio",
        "command": "PYTHON",
        "args": ["SERVER"]
      }
    }
  }
}
```

Or create a workspace-scoped `.vscode/mcp.json`:
```json
{
  "servers": {
    "gmail": {
      "type": "stdio",
      "command": "PYTHON",
      "args": ["SERVER"]
    }
  }
}
```

Copilot picks it up automatically. No restart needed.

---

### Cline (VS Code extension)

Open the Cline sidebar → **MCP Servers** tab → **Edit MCP Settings**.

This opens `cline_mcp_settings.json`. Add:
```json
{
  "mcpServers": {
    "gmail": {
      "command": "PYTHON",
      "args": ["SERVER"],
      "disabled": false,
      "autoApprove": []
    }
  }
}
```

Linux path: `~/.config/Code/User/globalStorage/saoudrizwan.claude-dev/settings/cline_mcp_settings.json`

---

### Continue.dev

`~/.continue/config.json`:
```json
{
  "mcpServers": [
    {
      "name": "gmail",
      "command": "PYTHON",
      "args": ["SERVER"]
    }
  ]
}
```

Or `~/.continue/config.yaml`:
```yaml
mcpServers:
  - name: gmail
    command: PYTHON
    args:
      - SERVER
```

Reload with `Ctrl+Shift+P` → *Continue: Reload Config*.

---

### Zed

`~/.config/zed/settings.json`:
```json
{
  "context_servers": {
    "gmail": {
      "command": {
        "path": "PYTHON",
        "args": ["SERVER"],
        "env": {}
      },
      "settings": {}
    }
  }
}
```

Zed reloads context servers automatically on save.

---

### Amazon Q Developer

`~/.aws/amazonq/mcp.json`:
```json
{
  "mcpServers": {
    "gmail": {
      "command": "PYTHON",
      "args": ["SERVER"]
    }
  }
}
```

Restart the Q Developer extension or CLI after editing.

---

### Goose (Block)

`~/.config/goose/config.yaml`:
```yaml
extensions:
  gmail:
    name: gmail
    type: stdio
    cmd: PYTHON
    args:
      - SERVER
    enabled: true
```

Run `goose session` — Goose loads extensions at startup.

---

### OpenCode (SST)

`~/.config/opencode/config.json`:
```json
{
  "mcp": {
    "gmail": {
      "command": "PYTHON",
      "args": ["SERVER"]
    }
  }
}
```

---

## 5. Using Programmatically

### Anthropic Python SDK

```bash
pip install anthropic
```

```python
import anthropic

client = anthropic.Anthropic()

with client.beta.messages.stream(
    model="claude-opus-4-8",
    max_tokens=4096,
    mcp_servers=[{
        "type": "stdio",
        "command": "PYTHON",
        "args": ["SERVER"],
    }],
    messages=[{"role": "user", "content": "List my last 5 unread emails."}],
    betas=["mcp-client-2025-04-04"],
) as stream:
    for text in stream.text_stream:
        print(text, end="", flush=True)
```

---

### OpenAI Agents SDK

```bash
pip install openai-agents
```

```python
import asyncio
from agents import Agent, Runner
from agents.mcp import MCPServerStdio

async def main():
    async with MCPServerStdio(
        params={"command": "PYTHON", "args": ["SERVER"]}
    ) as mcp:
        agent = Agent(
            name="gmail-agent",
            instructions="You manage Gmail for the user.",
            mcp_servers=[mcp],
        )
        result = await Runner.run(agent, "List my last 5 unread emails.")
        print(result.final_output)

asyncio.run(main())
```

---

### Claude Code CLI (subprocess / headless)

```bash
claude --mcp-config /path/to/mcp-config.json \
       -p "Summarize my unread emails from today."
```

`mcp-config.json`:
```json
{
  "mcpServers": {
    "gmail": {
      "type": "stdio",
      "command": "PYTHON",
      "args": ["SERVER"]
    }
  }
}
```

---

### Token handling in headless / CI environments

The OAuth flow requires a browser the first time. For agents running headlessly:

1. Run the browser flow once on a machine with a display to produce `token.pickle`
2. Copy `token.pickle` and `credentials.json` to the headless machine
3. The server auto-refreshes the token via the stored refresh token — no browser needed after that

```bash
chmod 600 credentials.json token.pickle
```

Do **not** pass secrets via environment variables.

---

## 6. Available Tools

### Reading
| Tool | Description |
|---|---|
| `list_messages` | List/search messages (Gmail search syntax) |
| `get_message` | Full plain-text content of a message |
| `get_message_html` | HTML body of a message |
| `list_threads` | List conversation threads |
| `get_thread` | Full thread with all messages |
| `list_attachments` | List attachments on a message |
| `get_attachment` | Download attachment to local path |

### Composing & Sending
| Tool | Description |
|---|---|
| `send_email` | Send a new email |
| `reply_email` | Reply to a thread |
| `forward_email` | Forward a message |
| `save_draft` | Save a draft |
| `list_drafts` | List drafts |
| `delete_draft` | Delete a draft |

### Organization
| Tool | Description |
|---|---|
| `trash_message` | Move to trash |
| `untrash_message` | Restore from trash |
| `delete_permanently` | Delete bypassing trash |
| `archive_message` | Archive (remove from inbox) |
| `move_to_spam` | Mark as spam |
| `mark_read` | Mark as read |
| `mark_unread` | Mark as unread |
| `star_message` | Star a message |
| `unstar_message` | Unstar a message |
| `apply_label` | Apply label by ID |
| `remove_label` | Remove label by ID |
| `batch_trash` | Trash multiple messages |
| `batch_delete_permanently` | Permanently delete multiple messages |
| `batch_archive` | Archive multiple messages |
| `batch_mark_read` | Mark multiple messages read |
| `trash_by_query` | Trash all messages matching a search |
| `archive_by_query` | Archive all messages matching a search |

### Labels
| Tool | Description |
|---|---|
| `list_labels` | List all labels (system + user) |
| `create_label` | Create a label (supports `/` for hierarchy) |
| `delete_label` | Delete a label by ID |
| `rename_label` | Rename a label |

### Filters
| Tool | Description |
|---|---|
| `list_filters` | List all Gmail filters |
| `create_filter` | Create a filter (from/to/subject/query + label actions) |
| `delete_filter` | Delete a filter by ID |

### Account & Export
| Tool | Description |
|---|---|
| `get_profile` | Email address, message count, storage quota |
| `download_all_messages` | Export all messages to JSON file (paginated) |
| `get_download_status` | Check progress of an ongoing export |

---

## 7. Troubleshooting

### `403 insufficientPermissions` on filter operations

The token was created without `gmail.settings.basic`. Delete it and re-authenticate:

```bash
rm token.pickle
python server.py
```

Verify `SCOPES` in `server.py` contains both scopes:

```python
SCOPES = [
    "https://mail.google.com/",
    "https://www.googleapis.com/auth/gmail.settings.basic",
]
```

### `Token has been expired or revoked`

Delete `token.pickle` and re-authenticate. This happens if access is revoked in Google account settings or if the app has been inactive for 6+ months in testing mode.

### `redirect_uri_mismatch`

The OAuth client must be type **Desktop app**, not Web application. Delete the existing client ID and create a new one with the correct type.

### MCP tools not appearing in Claude Code

- Confirm `credentials.json` and `token.pickle` exist in the project directory
- Run `python server.py` manually to check for import errors
- Restart Claude Code after editing `~/.claude.json`
- Check that the Python path in the MCP config points to the environment where dependencies are installed