gmail-mcp
by ensismoebius
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
This server cannot be deployed
Maintenance
ActivityInactive
ResponsivenessNo issues