An MCP server for the Gmail Postmaster Tools API (v2) — query your domains, sender compliance status, and Gmail traffic metrics from Claude, Cursor, or any MCP client.
This is a bring-your-own-credentials tool: you supply your own Google OAuth client, and the server runs the sign-in locally. Nothing is hosted. The server talks only to Google; what it returns goes to your MCP client and its model.
GravityKit's fork of optipub/gmail-postmaster-tools-mcp, hardened for security and with a test suite. See What this fork changes.
list_domains/get_domain— your registered domains and their verification state (off by default, see Scopes)get_compliance_status— SPF, DKIM, DMARC, alignment, message formatting, DNS records, TLS encryption, user-reported spam rate, and one-click / honored unsubscribe verdictsquery_domain_stats— spam rate, auth success (SPF/DKIM/DMARC), TLS encryption rate, delivery errors, and feedback-loop metrics over any date rangegpt_authenticate/gpt_auth_status/gpt_sign_out
Note: Postmaster Tools v2 covers traffic metrics + compliance. Domain and IP reputation are not part of v2.
You need a Google Cloud OAuth client because Google doesn't offer a shared public one for this API.
- Go to the Google Cloud Console and create (or pick) a project.
- APIs & Services → Library → search "Gmail Postmaster Tools API" → Enable.
- APIs & Services → OAuth consent screen (redirects to Google Auth Platform). If the project has never been configured, click Get started and set an App name + support email, Audience = External, and a contact email. If it's already configured, you'll just see the tabs — skip to the next step.
- Open the Audience tab. If User type = External, add your Google address under Test users → + Add users and Save (leave status as Testing — works without app verification, up to 100 users). If User type = Internal (a Workspace org project), no test users are needed — org users can sign in directly.
- Open the Clients tab (or APIs & Services → Credentials) → Create client → application type Desktop app → Create.
- Copy the Client ID and Client secret.
You'll paste those two values into this extension's configuration at install time. (Make sure the Google account you sign in with is one that has access in Gmail Postmaster Tools.)
The .mcpb is a packaging convenience for Claude Desktop / Cowork only.
- Download
gmail-postmaster-tools.mcpbfrom the latest release. - Settings → Capabilities → install extension, pick the file.
- When prompted, paste your Google OAuth Client ID and Client Secret.
- Run
gpt_authenticateonce — your browser opens for the Google sign-in.
Underneath, this is a standard stdio MCP server — any client that runs local
MCP servers can use it directly, no .mcpb required. Clone or download the repo,
then point the client at server/index.js and pass your Google credentials as
env vars.
Cursor — edit ~/.cursor/mcp.json (global) or .cursor/mcp.json (per project):
{
"mcpServers": {
"gmail-postmaster-tools": {
"command": "node",
"args": ["/absolute/path/to/gmail-postmaster-tools-mcp/server/index.js"],
"env": {
"GPT_CLIENT_ID": "xxxxx.apps.googleusercontent.com",
"GPT_CLIENT_SECRET": "xxxxx"
}
}
}
}VS Code (.vscode/mcp.json), Windsurf, Claude Desktop (manual config),
and most other clients use the same command + args + env shape — only the
config file location differs. Other env overrides are listed under
Configuration.
The gpt_authenticate tool opens your browser for the Google sign-in; the
127.0.0.1 loopback works on any local desktop client. Other tools never open a
browser: they tell you to run gpt_authenticate when sign-in is needed.
Remote-only clients (e.g. Perplexity, ChatGPT connectors): these accept only a remote HTTPS MCP server URL, not a local command, so this stdio build can't be added directly.
- "Sign me in to Gmail Postmaster Tools" →
gpt_authenticate - "List my Postmaster domains" →
list_domains - "Is example.com compliant with Gmail's sender rules?" →
get_compliance_status { domain: "example.com" } - "Spam rate for example.com over the last 30 days" →
query_domain_stats { domain: "example.com", start_date: "...", end_date: "...", metrics: ["SPAM_RATE"] } - "DKIM auth success for example.com" →
query_domain_stats { domain: "example.com", metrics: [{ "standardMetric": "AUTH_SUCCESS_RATE", "filter": "auth_type = \"dkim\"" }] }
Filters, from Google's API definition:
| Metric | Filter |
|---|---|
SPAM_RATE |
none |
AUTH_SUCCESS_RATE |
required: auth_type = "spf", "dkim" or "dmarc" |
TLS_ENCRYPTION_RATE, TLS_ENCRYPTION_MESSAGE_COUNT |
required: traffic_direction = "inbound" or "outbound" |
DELIVERY_ERROR_RATE, DELIVERY_ERROR_COUNT |
optional: error_type = "reject" or "temp_fail" |
FEEDBACK_LOOP_SPAM_RATE |
required: feedback_loop_id = "<id>" |
query_domain_stats reads every page of results, up to 25 pages of 200 rows.
OAuth 2.0 authorization-code + PKCE, over a http://127.0.0.1:<dynamic-port>
loopback redirect (Google's recommended flow for desktop apps). On sign-in the
server starts a short-lived listener on 127.0.0.1, opens your browser, receives
the code, and exchanges it (with your client secret + PKCE verifier) for tokens.
Google requires a client secret even for desktop apps, which is why both values
are needed. Tokens are stored only on your machine at
~/.gmail-postmaster-mcp/tokens.json (0600, in a 0700 folder).
gpt_sign_out revokes the access at Google and deletes that file.
Scopes: by default the server requests only postmaster.traffic.readonly,
which covers metrics and compliance. list_domains and get_domain need
postmaster.domain, which Google describes as "View and manage" and which also
allows creating, verifying and deleting domains. This server never does those
things, but the token could, so those two tools stay hidden unless you set:
GPT_SCOPE="https://www.googleapis.com/auth/postmaster.traffic.readonly https://www.googleapis.com/auth/postmaster.domain"
In a .mcpb install, put the same value in the extension's Google permissions
setting. The server refuses to start with any other scope. Changing the scope
requires running gpt_authenticate again.
Google revokes refresh tokens after 7 days for OAuth apps whose publishing
status is Testing — the mode the setup steps above use to skip verification.
In practice that means you'll re-run gpt_authenticate about once a week. The
server handles this gracefully: when the refresh fails, tool calls tell you to
re-authenticate.
To stop the weekly re-auth, set your OAuth app's publishing status to In
production (Google Auth Platform → Audience → Publish app). Notes for the
postmaster (sensitive) scopes:
- Production, unverified — refresh tokens no longer expire at 7 days. You'll still click through the "Google hasn't verified this app" screen, and the app is capped at 100 users until verified. For your own single-user use, this is the simple fix.
- Verified — submit the app for Google's OAuth verification to remove the warning and the user cap. Only worth it if you're distributing the app widely.
| Variable | Purpose | Default |
|---|---|---|
GPT_CLIENT_ID |
Google OAuth client ID (required) | — |
GPT_CLIENT_SECRET |
Google OAuth client secret (required) | — |
GPT_SCOPE |
Space-separated OAuth scopes (allowed: traffic.readonly, domain) | traffic.readonly |
GPT_TOKEN_DIR |
Token cache directory | ~/.gmail-postmaster-mcp |
GPT_LOGIN_TIMEOUT_MS |
Sign-in wait | 180000 |
GPT_REQUEST_TIMEOUT_MS |
Per-request timeout | 60000 |
npx @anthropic-ai/mcpb validate manifest.json
npx @anthropic-ai/mcpb pack . gmail-postmaster-tools.mcpbPushing a vX.Y.Z tag that matches manifest.json makes CI run the tests, pack,
and attach the .mcpb to a GitHub Release. This fork does not publish to the MCP
Registry.
npm test # local, against a fake Google server
npm run test:live # Google's real endpoints with made-up credentials; needs no accountSecurity:
- Removed the
GPT_API_BASE,GPT_TOKEN_URLandGPT_AUTHORIZE_URLsettings, which could send tokens and the client secret to another host. - Default permission is read-only traffic data; the "View and manage" domain scope is opt-in, and wider scopes are refused.
- The sign-in callback checks
statefirst, so a stray request can't end a sign-in, and it escapes what it shows. - Token files are written atomically in a
0700folder. Sign-out revokes at Google. - Tokens saved with different permissions are not reused.
- Time and size limits on every Google request, and token-endpoint errors show only Google's error fields.
- Release workflow: actions pinned to commits, least permissions, no registry publishing.
Bugs:
query_domain_statsreturned only the first 10 rows with no way to get the rest. It now reads every page.- A malformed request to the sign-in callback, or a missing browser command, crashed the server.
- HTTP errors from Google weren't marked as errors, and a hung token request never ended.
- February 31 was accepted as a date, and start dates after end dates reached Google.
- An
end_datealone could produce a backwards range. - A 401 no longer forces a new sign-in when a refresh would work.
- Characters split across network chunks were corrupted.
- JSON-RPC: parse errors, unknown tools, notifications and protocol version negotiation now follow the spec, and answers in progress are sent when input closes.
Only Node.js built-ins (http, https, crypto, readline) — tiny bundle, no
supply-chain risk, nothing to npm install.
See CONTRIBUTING.md.
MIT © OptiPub. Original work by Kyle Renfrow at OptiPub; fork changes by GravityKit.
Not affiliated with or endorsed by Google. "Gmail", "Google", and "Postmaster Tools" are trademarks of Google LLC.