Skip to main content
Glama
enoch-sit

gmail-mcp-server

by enoch-sit
README.md
# Gmail MCP Server

A comprehensive **Model Context Protocol (MCP) server** for Gmail, with 17 tools, an interactive CLI setup wizard, a browser-based Web UI wizard, and full Docker support.

---

## Features

| Tool | Description |
|---|---|
| `list_emails` | List emails from inbox or any label with optional query filter |
| `read_email` | Read full email content (headers + decoded body) |
| `search_emails` | Search using Gmail query syntax |
| `list_attachments_with_safety` | List message attachments with policy-based safety checks |
| `validate_attachment` | Validate one attachment against local file safety policy |
| `download_attachment_safe` | Download attachment only if it passes safety policy checks |
| `redact_text_local` | Redact sensitive text through local privacy pipeline |
| `read_email_with_privacy` | Read one email with privacy-redacted subject/snippet/body |
| `send_email` | Send plain-text or HTML emails |
| `create_draft` | Create email drafts without sending |
| `reply_to_email` | Reply to an email thread (preserves threading headers) |
| `delete_email` | Trash or permanently delete an email |
| `mark_as_read` | Mark email as read |
| `mark_as_unread` | Mark email as unread |
| `set_labels` | Add/remove labels on an email |
| `get_labels` | List all Gmail labels with message counts |
| `get_profile` | Get authenticated user profile info |

Attachment safety policy defaults are defined in `src/config/attachment-policy.json`.
Privacy policy defaults are defined in `src/config/privacy-policy.json`.

Optional local privacy stack (vLLM + privacy orchestrator) can be started with Docker profiles:

```bash
docker compose --profile privacy up --build
```

The privacy profile also enables optional persistent services:
- **Redis** (`redis:6379`) — token store for session-aware PII re-identification (future feature)
- **SQLite** (`audit.db`) — audit log of all PII detections and redactions for compliance

See [ARCHITECTURE.md](ARCHITECTURE.md#persistent-data-layer-optional) for database design details.

---

## Prerequisites

- **Node.js 20+** (for local setup)
- **Docker** (for containerised setup)
- A **Google Cloud project** with Gmail API enabled and an OAuth2 Client ID (Desktop app type)

### Google Cloud Setup (one-time)

1. Go to [https://console.cloud.google.com/](https://console.cloud.google.com/)
2. Create or select a project
3. **APIs & Services → Library** → search "Gmail API" → **Enable**
4. **APIs & Services → Credentials → Create Credentials → OAuth 2.0 Client ID**
   - Application type: **Desktop app**
   - Add Authorized redirect URIs:
     - `http://localhost:3333/oauth/callback` ← for CLI wizard
     - `http://localhost:3000/oauth/callback` ← for Web UI wizard
5. Note your **Client ID** and **Client Secret** (or download `credentials.json`)

---

## Quick Start

### Option A — CLI Wizard (recommended for developers)

```bash
# 1. Install dependencies and build
npm install
npm run build

# 2. Run the interactive CLI setup wizard
npm run setup:cli

# 3. Start the MCP server
npm start
```

The CLI wizard will:
- Guide you through credential input (file / paste JSON / manual entry)
- Let you choose Gmail permission scopes
- Open your browser for Google sign-in
- Automatically capture the OAuth callback and save tokens
- Validate the connection and print your email + next steps

---

### Option B — Web UI Wizard (browser-based)

```bash
# 1. Install dependencies and build
npm install
npm run build

# 2. Start the setup wizard UI
npm run setup:ui

# 3. Open http://localhost:3000 in your browser
#    Complete the 4-step wizard, then start the server:
npm start
```

The Web UI provides a guided 4-step flow:
1. Enter OAuth2 credentials
2. Select permission scopes
3. Click **Authorize Gmail Access** → completes Google sign-in flow
4. Success page with connection stats and next steps

---

### Option C — Manual `.env` (CI / Docker production)

```bash
# Copy and fill in the template
cp .env.example .env
# Edit .env: set GMAIL_CLIENT_ID, GMAIL_CLIENT_SECRET, GMAIL_REFRESH_TOKEN

npm install
npm run build
npm start
```

---

## Docker

### Build the image

```bash
docker build -t gmail-mcp-server .
```

### Run the MCP server (with env vars)

```bash
# Copy .env.example → .env and fill in credentials
cp .env.example .env

docker compose up
```

Or pass credentials inline:

```bash
docker run -i \
  -e GMAIL_CLIENT_ID=your-client-id \
  -e GMAIL_CLIENT_SECRET=your-secret \
  -e GMAIL_REFRESH_TOKEN=your-refresh-token \
  gmail-mcp-server
```

### Run the Web UI Setup Wizard via Docker

```bash
docker compose -f docker-compose.setup.yml up
```

Then open [http://localhost:3000](http://localhost:3000) — credentials are written back to `./credentials/` on the host via volume mount.

### Mount credentials files instead of env vars

```bash
docker run -i \
  -v ./credentials:/app/credentials:ro \
  gmail-mcp-server
```

---

## Environment Variables

| Variable | Required | Description |
|---|---|---|
| `GMAIL_CLIENT_ID` | Yes* | Google OAuth2 Client ID |
| `GMAIL_CLIENT_SECRET` | Yes* | Google OAuth2 Client Secret |
| `GMAIL_REFRESH_TOKEN` | Yes* | Long-lived refresh token (from setup wizard) |
| `GMAIL_REDIRECT_URI` | No | Override redirect URI (default: `http://localhost:3000/oauth/callback`) |
| `PORT` | No | Web UI setup server port (default: `3000`) |

\* Required when using env vars. Alternatively, use `credentials/credentials.json` + `credentials/token.json` (written by the setup wizards).

---

## Add to MCP Client (e.g. Claude Desktop)

Edit your MCP client config:

```json
{
  "mcpServers": {
    "gmail": {
      "command": "node",
      "args": ["/absolute/path/to/gmail-mcp-server/dist/index.js"]
    }
  }
}
```

Or use Docker:

```json
{
  "mcpServers": {
    "gmail": {
      "command": "docker",
      "args": [
        "run", "-i", "--rm",
        "-e", "GMAIL_CLIENT_ID=...",
        "-e", "GMAIL_CLIENT_SECRET=...",
        "-e", "GMAIL_REFRESH_TOKEN=...",
        "gmail-mcp-server"
      ]
    }
  }
}
```

---

## Project Structure

```
├── src/
│   ├── index.ts                  # MCP server entry point
│   ├── gmail/
│   │   ├── auth.ts               # OAuth2 credential + token management
│   │   └── client.ts             # Gmail API client factory (singleton)
│   ├── tools/
│   │   ├── emails.ts             # list_emails, read_email, search_emails
│   │   ├── compose.ts            # send_email, create_draft, reply_to_email
│   │   ├── manage.ts             # delete_email, mark_as_read/unread, set_labels
│   │   └── meta.ts               # get_labels, get_profile
│   └── setup/
│       ├── oauth.ts              # Shared OAuth2 URL generation + token exchange
│       ├── cli.ts                # Interactive CLI setup wizard
│       └── ui-server.ts          # Express Web UI setup server
├── public/
│   └── setup/
│       └── index.html            # 4-step Web UI wizard (vanilla HTML/CSS/JS)
├── credentials/                  # Runtime secrets — gitignored
│   └── .gitkeep
├── Dockerfile                    # Multi-stage build (build + runtime)
├── docker-compose.yml            # MCP server runtime
├── docker-compose.setup.yml      # Web UI setup wizard runtime
├── .env.example                  # Environment variable template
└── tsconfig.json
```

---

## Security Notes

- `credentials/` is gitignored — **never** commit `credentials.json` or `token.json`
- Credentials are **not baked into the Docker image** — always injected via env vars or volume mounts
- The Web UI server and CLI callback server bind to `127.0.0.1` only (not publicly accessible)
- The runtime Docker container runs as a non-root user (`mcp`)

---

## Troubleshooting

| Problem | Solution |
|---|---|
| `No Gmail credentials found` | Run `npm run setup:cli` or `npm run setup:ui` |
| `No Gmail token found` | Re-run the setup wizard to re-authorize |
| `invalid_client` from Google | Check that Client ID and Secret are correct |
| `redirect_uri_mismatch` | Ensure the redirect URI is added in Google Cloud Console exactly as shown in the wizard |
| `Port 3333 in use` | Kill the process using port 3333, or temporarily change `CLI_PORT` in `src/setup/cli.ts` |
| Token expired / `invalid_grant` | Re-run the setup wizard to get a fresh refresh token |
| Docker: `credentials not found` | Mount `./credentials:/app/credentials` or set env vars |

TDQS

B3.3/5.0

Scored across 18 tools

Disambiguation2/5

Multiple tools have overlapping purposes: list_emails and search_emails both return message lists with similar fields, read_email and read_email_with_privacy both read a single email, and download_attachment/download_attachment_safe/validate_attachment all handle attachment retrieval with safety. The descriptions help distinguish them, but the boundaries are still unclear and could cause misselection.

Naming Consistency4/5

Most tools follow a consistent verb_noun snake_case pattern (e.g., read_email, list_emails, send_email, get_profile). However, a few tools have irregular modifiers like '_with_safety', '_safe', '_local', and '_with_privacy', which breaks the uniformity slightly but does not create chaos.

Tool Count3/5

18 tools falls in the heavy range and feels slightly bloated due to redundancy. For instance, list_emails and search_emails overlap, and the attachment safety pipeline has three distinct tools that could be consolidated. Each tool has a purpose, but the count could be trimmed without losing capability.

Completeness3/5

Core Gmail workflows (read, search, send, reply, delete, labels) are covered, but there are significant gaps: create_draft has no way to send or update drafts, and get_labels exists but there are no tools to create or delete labels. This leaves workflows with dead ends, such as creating a draft that cannot be sent via the same server.

Maintenance

ActivityInactive
ResponsivenessNo issues