Skip to main content
Glama
Arshdeep54

gmail-mcp-server

by Arshdeep54
README.md
<p align="center">
  <img alt="gmail-mcp-server" src="https://img.shields.io/badge/MCP-Gmail-informational">
  <img alt="license" src="https://img.shields.io/badge/license-MIT-blue">
</p>

# gmail-mcp-server

A self-hosted [MCP](https://modelcontextprotocol.io) server for Gmail, built on the official
Gmail API. No third-party relay, no data leaving your own infrastructure, your Google OAuth
token and email data stay on whatever machine you run this on.

Works with any MCP-compatible client: [Claude Code](https://claude.com/claude-code) over local
stdio, or a hosted client like ChatGPT's custom connectors over HTTP with a built-in OAuth 2.1
authorization server.

## Why

Most "Gmail for AI" integrations are either a paid SaaS connector or require handing a third
party your inbox. This is neither: it's a small server you run yourself, talking directly to
Google's API with your own OAuth client.

## Tools

| Tool | Description |
|---|---|
| `gmail_search` | Search messages with Gmail's query syntax (`from:`, `is:unread`, `after:`, ...) |
| `gmail_get_message` | Fetch one message in full: headers, plain-text and HTML body, attachment metadata |
| `gmail_get_thread` | Fetch every message in a thread |
| `gmail_create_draft` | Create a draft (optionally as a threaded reply). Never sends anything |
| `gmail_list_labels` | List labels, with per-label message/thread counts |

Every response includes `to`/`cc`/`bcc`, `labelIds`, and (for `get_message`/`get_thread`)
attachment filenames and sizes. This server requests `gmail.readonly` and `gmail.compose`,
enough to read mail and create drafts, and never anything more: there is no send, modify, or
delete tool, and it never returns the contents of `credentials.json` or any token through a
tool. `gmail_create_draft` only ever leaves a draft sitting in Drafts, a human has to open Gmail
and hit send themselves.

## Quickstart: local use with Claude Code

Requirements: Python 3.10+, a Google Cloud project with the Gmail API enabled.

```bash
git clone https://github.com/<you>/gmail-mcp-server.git
cd gmail-mcp-server
python -m venv .venv && source .venv/bin/activate
pip install -e .
```

1. In the [Google Cloud Console](https://console.cloud.google.com/), enable the **Gmail API**,
   then go to **APIs & Services → Credentials → Create Credentials → OAuth client ID**, choose
   **Desktop app**, and download the JSON as `secrets/credentials.json`.
2. Add yourself as a test user under **OAuth consent screen → Test users** if the app is in
   Testing mode.
3. Authorize once:

   ```bash
   python authorize.py
   ```

   This opens a browser, and writes `secrets/token.json`.
4. Add it to Claude Code's MCP config:

   ```json
   {
     "mcpServers": {
       "gmail": {
         "command": "python",
         "args": ["-m", "gmail_mcp.server"],
         "cwd": "/path/to/gmail-mcp-server"
       }
     }
   }
   ```

That's it for local, stdio-based use, no auth server, no network exposure.

## Deploying for a hosted client (e.g. ChatGPT connectors)

Hosted MCP clients need a real HTTPS endpoint and typically support only **None** or **OAuth**
authentication, not a plain bearer header. This project ships a minimal OAuth 2.1 authorization
server for exactly that case: a standard authorization-code + PKCE flow, gated by a single
consent screen that asks for a passphrase you set yourself, so a token is only ever issued after
you personally approve it.

1. Copy `.env.example` to `.env` and fill it in (see the comments in that file).
2. Put this behind a reverse proxy that terminates HTTPS (Caddy, Nginx, Traefik, your load
   balancer of choice) and point a real domain at it.
3. `docker compose up -d --build`
4. In your MCP client, add a custom connector:
   - Server URL: `https://your-domain/mcp`
   - Authentication: OAuth, with your own client ID (from `.env`), no client secret, token
     endpoint auth method `none`
   - It should auto-discover the authorization/token endpoints from
     `https://your-domain/.well-known/oauth-authorization-server`
5. On first connect you'll land on a small consent page on your own server, enter the
   passphrase from `.env` to approve.

Access tokens are short-lived (1 hour) with long-lived refresh tokens, both persisted to
`secrets/oauth_state.json` so a container restart doesn't force re-authorization.

## Security notes

- Only `gmail.readonly` and `gmail.compose` are ever requested, nothing broader.
- `credentials.json`, `token.json`, `.env`, and `secrets/` are git-ignored, never commit them.
- The OAuth flow's consent step requires a passphrase only you know, so a leaked connector URL
  alone can't silently mint a token.
- DNS-rebinding protection (`GMAIL_MCP_ALLOWED_HOSTS`) locks the server to the hostname(s) you
  configure.
- There is no `send`, `modify`, or `delete` tool, by construction: the only write path is
  `gmail_create_draft`, and a draft is inert until a human opens it in Gmail and sends it.
- This is single-tenant: it authenticates as one Gmail account (yours), not a per-user login.
  Anyone who reaches your deployed instance and gets past the consent passphrase reads and
  drafts as *you*, not as themselves, don't share the deployed URL and passphrase together.

## Project layout

```
gmail_mcp/
  auth.py            Google OAuth (installed-app flow) for the Gmail API itself
  gmail_client.py     Thin wrapper around the Gmail API for the four tools
  server.py           MCP tool definitions
  http_app.py          HTTP transport entrypoint (used for Docker/hosted deployments)
  oauth_provider.py   Minimal OAuth 2.1 authorization server (auth code + PKCE + refresh)
  consent.py           The passphrase-gated consent page
authorize.py           One-time local script to obtain secrets/token.json
```

## License

MIT, see [LICENSE](LICENSE).

TDQS

A3.8/5.0

Scored across 5 tools

Disambiguation5/5

Each tool targets a distinct Gmail resource and action: searching, fetching a message, fetching a thread, creating a draft, and listing labels. There is no overlap or ambiguity between them.

Naming Consistency5/5

All tool names follow a uniform `gmail_<verb>_<noun>` pattern (search, get_message, get_thread, create_draft, list_labels). The naming is fully consistent and predictable.

Tool Count5/5

Five tools is a well-scoped size for a focused Gmail integration covering search, read, drafts, and labels. The count feels intentional rather than sparse or bloated.

Completeness3/5

The set covers search, retrieval, thread viewing, draft creation, and label listing, but lacks common Gmail operations like sending, updating/deleting drafts, modifying labels on messages, or marking read/unread. The read-and-draft focus is coherent, but the domain is only partially covered.

Maintenance

ActivityMaintained
ResponsivenessNo issues