Skip to main content
Glama
ganeshhgupta

gmail-mcp-server

by ganeshhgupta
README.md
# gmail-mcp-server

MCP server exposing one tool, `send_email`, that sends Gmail messages
(with optional file attachments) via the Gmail API. Built for use as a
local stdio MCP server inside Claude Code.

Scope is deliberately narrow: `gmail.send` only (no read/modify access to
the mailbox) — least privilege for a tool whose only job is sending mail.

This README is written from an actual first-time setup, including every
error that came up along the way. Follow it top to bottom and you should
not hit any of them.

> **PowerShell note:** `&&` does not work as a command separator in
> Windows PowerShell (that's bash/cmd syntax). Every multi-command line
> below is written as separate lines, or use `;` if you want one line:
> `cd C:\Users\GaneshGupta\gmail-mcp-server; python auth_setup.py`

---

## 1. Google Cloud — enable the API and create OAuth credentials

Google's console UI for this was recently renamed from "OAuth consent
screen" to **Google Auth Platform**, with the settings split across
separate left-nav pages (Branding / Audience / Clients / Data Access /
Verification Center). The steps below use the current names.

1. Go to [console.cloud.google.com](https://console.cloud.google.com/).
   Create a new project or reuse an existing one.
2. **APIs & Services → Library** → search "Gmail API" → **Enable**.
3. **APIs & Services → OAuth consent screen** (this lands you in the new
   Google Auth Platform section) → fill in the **Branding** page:
   - App name: anything, e.g. `gmail-mcp-tool`
   - User support email: your Gmail address
   - Developer contact email: your Gmail address
   - Leave logo/App domain fields blank for now — **you'll come back to
     App domain in step 4 if you hit the "incomplete configuration"
     error.** Don't fill them speculatively; only do it if you actually
     see that error.
4. **Audience** page (left nav) → under **Test users** → **+ Add users**
   → enter your own Gmail address (the one that will send mail) → Save.

   **If "Add users" is blocked** by a yellow banner reading *"Your app's
   OAuth configuration is incomplete... Please visit the Branding page"*
   — this happens because `gmail.send` is a sensitive scope, which
   requires the App domain fields to be non-empty even for a
   Testing-only app. Fix:
   - Go to **Branding → App domain**, fill in:
     - Application home page: `https://example.com`
     - Application privacy policy link: `https://example.com/privacy`
     - Application terms of service link: `https://example.com/terms`
   - This will surface an **Authorized domains** field. Enter the **bare
     domain only — no `https://` prefix**:
     - Correct: `example.com`
     - Wrong (rejected with "Invalid domain: must not specify the
       scheme"): `https://example.com`
   - Save on Branding (you should see a "Branding changes saved!" toast).
   - Go back to **Audience → Add users** and try again — it will go
     through this time.

   These URLs don't need to be real/functional — Google doesn't verify
   them while the app stays in Testing status, it just requires the
   fields to be filled.

5. **Clients** page (left nav) → **+ Create client**:
   - Application type: **Desktop app**
   - Name: anything, e.g. `gmail-mcp-desktop`
   - Create → **Download JSON** (button appears right after creation)

6. The downloaded file will be named something like
   `client_secret_<long-id>.apps.googleusercontent.com.json`. **Rename
   it exactly to `client_secret.json`** and move it into:
   ```
   C:\Users\GaneshGupta\gmail-mcp-server\credentials\client_secret.json
   ```
   The exact filename matters — `gmail_auth.py` looks for it by that
   name and raises `FileNotFoundError` if it doesn't match (this is the
   most common thing to get wrong here).

## 2. Install dependencies

```
cd C:\Users\GaneshGupta\gmail-mcp-server
pip install -r requirements.txt
```

## 3. One-time login

```
python auth_setup.py
```

What happens:

1. A browser window opens to a normal Google sign-in.
2. You'll land on **"Google hasn't verified this app"**. This is
   expected — it's your own OAuth client, in Testing mode, requesting a
   sensitive scope. Click **Continue** (older UI: **Advanced → Go to
   [app name] (unsafe)**).
3. Grant the "Send email on your behalf" permission.
4. Browser shows *"The authentication flow has completed. You may close
   this window."*
5. Terminal prints `Authorized. Token saved to ...credentials\token.json`.

If step 2 instead shows **`Error 403: access_denied`** / *"has not
completed the Google verification process... can only be accessed by
developer-approved testers"* — your Gmail address isn't in the Audience
→ Test users list yet. Go back to step 1.4 above.

Do this once. After this, `server.py` only ever silently refreshes the
saved token — it never opens a browser again on its own.

## 4. Sanity-check the token (optional but recommended)

```
python -c "from gmail_auth import load_credentials; c = load_credentials(); print('valid:', c.valid); print('scopes:', c.scopes); print('has refresh token:', bool(c.refresh_token))"
```

Expect `valid: True`, `scopes: ['https://www.googleapis.com/auth/gmail.send']`,
`has refresh token: True`.

## 5. Register with Claude Code

```
claude mcp add gmail-sender --scope user -- python C:\Users\GaneshGupta\gmail-mcp-server\server.py
```

`--scope user` registers it globally — it becomes available in every
Claude Code session on this machine from then on, not just the one
where you ran the command. **It will not appear retroactively in a
session that's already running** — MCP servers load at session start,
so use a new terminal / new `claude` session to see it.

Verify:

```
claude mcp list
```

You should see `gmail-sender` in the list.

## 6. Using it

In any Claude Code session (after the registration above), just ask in
natural language:

> Send an email to jane@example.com with subject "Following up" and body
> "..." — attach C:\path\to\file.pdf

Claude Code will call the `send_email` tool directly. No further setup
needed per-session.

## Tool reference

`send_email(to, subject, body, attachments=None, cc=None, bcc=None, html=False)`

- `to` / `cc` / `bcc`: comma-separated addresses
- `attachments`: list of **absolute** local file paths, 15MB combined
  limit (Gmail's raw-send cap is 25MB; 15MB of raw files leaves headroom
  for base64 inflation + headers)
- `html`: set true to send an HTML body instead of plain text
- Returns `{status, message_id, thread_id, to, subject, attachment_count}`

## Troubleshooting index

| Symptom | Cause | Fix |
|---|---|---|
| `FileNotFoundError: Missing OAuth client secret at ...` | Downloaded JSON kept Google's default long filename | Rename to exactly `credentials\client_secret.json` |
| `Error 403: access_denied` — "has not completed Google verification" | Your account isn't a Test user yet | Audience → Test users → Add your Gmail address |
| Audience page: "Your app's OAuth configuration is incomplete" banner blocking Add users | Sensitive scope (`gmail.send`) requires App domain fields | Fill Branding → App domain (home page/privacy/terms) with any `https://` URL, save |
| "Invalid domain: must not specify the scheme" on Authorized domain field | Entered `https://example.com` instead of bare domain | Enter `example.com` only, no `http(s)://` |
| "Google hasn't verified this app" warning during login | Expected — your own OAuth client in Testing mode | Click Continue (or Advanced → Go to app), this is normal, not an error |
| `&&` gives `The token '&&' is not a valid statement separator` | That's bash syntax, not PowerShell | Use `;` or put commands on separate lines |
| `gmail-sender` not showing up in Claude Code | Registered after the current session started, or wrong scope | Open a new `claude` session; check `claude mcp list`; re-run `claude mcp add` with `--scope user` |
| Need to send from a different Gmail account | Token is tied to whichever account you logged in as | Delete `credentials\token.json` and re-run `python auth_setup.py` |
| Attachment rejected / send fails on large files | Combined attachments over 15MB | Split into multiple emails or compress |

## Notes

- `credentials/client_secret.json` and `credentials/token.json` are
  gitignored — never commit them.
- To send from a different Gmail account, delete `credentials/token.json`
  and re-run `auth_setup.py`.