Skip to main content
Glama
ktsenapathy

gmail-mcp-server

by ktsenapathy
README.md
# Gmail MCP Server

A [Model Context Protocol](https://modelcontextprotocol.io) server that lets
an AI assistant (Claude Desktop, Claude API, etc.) read unread Gmail messages
and create threaded draft replies — without ever sending email automatically.

---

## What this server does

| Tool | Description |
|------|-------------|
| `get_unread_emails` | Returns up to 50 unread inbox messages: sender, subject, date, body, and IDs |
| `create_draft_reply` | Saves a threaded draft reply in Gmail Drafts (user must send manually) |
| `get_reply_context` | *(Stretch goal)* Returns a style guide, templates, and knowledge-base snippets to guide better replies |

The server communicates with Claude Desktop over **stdio** using the MCP
JSON-RPC protocol.  It never sends email — only creates drafts.

---

## Prerequisites

- Python 3.10+
- A Google account
- [Claude Desktop](https://claude.ai/download) installed

---

## Quick start

### 1. Clone and install

```bash
git clone <this-repo>
cd gmail-mcp-server
bash scripts/setup.sh
source .venv/bin/activate
```

### 2. Set up Gmail API credentials

Follow the detailed guide in [`docs/gmail_api_setup.md`](docs/gmail_api_setup.md).

**Summary:**
1. Create a Google Cloud project.
2. Enable the **Gmail API**.
3. Configure the **OAuth consent screen** (External, test mode is fine).
4. Add scopes: `gmail.readonly` + `gmail.compose`.
5. Create an **OAuth 2.0 Desktop App** credential.
6. Download `credentials.json` → place in project root.

### 3. Authenticate

```bash
python scripts/auth_check.py
```

A browser window opens. Sign in and approve access. `token.pickle` is saved.

### 4. Test the server manually

```bash
# Start the server (press Ctrl-C to stop)
gmail-mcp
```

The server waits for JSON-RPC on stdin.  You can send a test message:

```bash
echo '{"jsonrpc":"2.0","method":"tools/list","id":1}' | gmail-mcp
```

### 5. Configure Claude Desktop

Edit `claude_desktop_config.json` (see [`docs/claude_desktop_config.md`](docs/claude_desktop_config.md)):

```json
{
  "mcpServers": {
    "gmail": {
      "command": "/absolute/path/to/.venv/bin/python",
      "args": ["-m", "gmail_mcp_server.server"],
      "cwd": "/absolute/path/to/gmail-mcp-server",
      "env": {
        "GMAIL_CREDENTIALS_PATH": "/absolute/path/to/credentials.json",
        "GMAIL_TOKEN_PATH": "/absolute/path/to/token.pickle"
      }
    }
  }
}
```

Restart Claude Desktop.  The gmail tools will appear automatically.

---

## Example Claude prompts

```
Check my unread emails and tell me which ones need urgent attention.
```

```
I have an unread email about a refund request. Draft a polite reply
using our style guide, then save it as a draft.
```

```
Fetch my 5 newest unread emails. For each one, tell me the sender,
subject, and a one-sentence summary.
```

```
Use get_reply_context to see our templates, then create a draft reply
to the meeting invite email.
```

---

## Project structure

```
gmail-mcp-server/
├── src/
│   └── gmail_mcp_server/
│       ├── __init__.py
│       ├── server.py          # MCP server entry point & tool definitions
│       ├── auth.py            # OAuth 2.0 credential management
│       ├── gmail_tools.py     # Gmail API logic (read emails, create drafts)
│       └── context.py         # Stretch-goal: style guide, templates, KB search
├── context/
│   ├── style_guide.md         # Writing style guide (edit to match your voice)
│   ├── templates.json         # Reply templates
│   └── knowledge_base/        # .md/.txt files Claude can search
├── scripts/
│   ├── setup.sh               # One-time environment setup
│   └── auth_check.py          # OAuth flow + credential verification
├── docs/
│   ├── mcp_concepts.md        # How MCP tools work (detailed explanation)
│   ├── gmail_api_setup.md     # Step-by-step Google Cloud setup
│   └── claude_desktop_config.md  # Claude Desktop config + example prompts
├── pyproject.toml
├── .env.example
└── .gitignore
```

---

## How MCP tools work (summary)

MCP tools are **callable functions** that an LLM can invoke via JSON-RPC:

1. **Discovery** — Claude calls `tools/list`; the server returns tool names,
   descriptions, and JSON Schemas for their arguments.
2. **Decision** — The LLM decides which tool to call based on the description
   and the user's request.
3. **Invocation** — Claude calls `tools/call` with the tool name and arguments.
4. **Result** — The server returns a `TextContent` block (JSON string) that
   Claude reads and incorporates into its response.

See [`docs/mcp_concepts.md`](docs/mcp_concepts.md) for a full explanation with
sequence diagrams and annotated code.

---

## Stretch goal: external context

The `get_reply_context` tool pulls in:
- **Style guide** (`context/style_guide.md`) – tone, structure, phrases to use/avoid.
- **Templates** (`context/templates.json`) – starting points for common reply types.
- **Knowledge base** (`context/knowledge_base/`) – searched by keyword.

To connect real cloud sources:
- **Google Docs** – enable the Docs API, add `documents.readonly` scope,
  fetch document content via `docs.documents().get()`.
- **Notion** – install `notion-client`, query a database with your integration token.
- **Vector search** – replace `search_knowledge_base()` in `context.py` with
  `chromadb` or `hnswlib` for semantic search over larger document sets.

---

## Security notes

- `credentials.json` and `token.pickle` are in `.gitignore` — never commit them.
- The server only requests `gmail.readonly` and `gmail.compose` scopes.
- No email is ever sent automatically; `create_draft_reply` only calls
  `drafts.create`, not `messages.send`.
- The OAuth consent screen is in "Testing" mode by default — only you can authenticate.

---

## Troubleshooting

| Problem | Fix |
|---------|-----|
| Tools don't appear in Claude Desktop | Check `command` path uses `.venv/bin/python`; restart Claude Desktop |
| `credentials.json not found` | Download from Google Cloud Console and place in project root |
| `Access blocked` in browser | Add your email to Test Users in OAuth consent screen |
| `Token revoked` error | Delete `token.pickle` and re-run `auth_check.py` |
| Empty email body | Email may be HTML-only; `gmail_tools.py` extracts `text/plain` parts |

For more, see [`docs/gmail_api_setup.md`](docs/gmail_api_setup.md).

---

## License

MIT