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`.
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessNo issues