google-search-console-mcp
MCP server for Google Search Console API, enabling search performance analysis, URL inspection, sitemap management, and property listing. Supports local, self-hosted, and Cloudflare Workers deployments.
README
Google Search Console MCP
An MCP server for the Google Search Console API: search performance data, URL index status, sitemap management, and property listing.
One codebase runs three ways — stdio (local, through npx), Streamable HTTP (self-hosted), and Cloudflare Workers (hosted at a URL). It implements MCP 2026-07-28 and falls back automatically to 2025-11-25, 2025-06-18, and 2025-03-26, so it works with clients on either side of the protocol change.
Zero runtime dependencies.
Quick start
You need Google credentials either way, so start there:
npx google-search-console-mcp auth
That command walks you through creating a Google OAuth client, runs the consent flow, verifies the credentials against the live API, and prints a ready-to-paste config block for your MCP client.
Then choose how to run the server:
- Locally, by pasting the printed stdio config into your client. See Run the server.
- Against the hosted endpoint, with no install at all. See Hosted endpoint.
Hosted endpoint
A public deployment runs at:
https://search-console-mcp.russjeffery.com/mcp
It runs in bring-your-own-credential mode. You send your own credential blob as the bearer token, the server uses it for that one request, and it stores nothing. Two people pointing the same client at this URL see only their own Search Console properties.
Add it to a client like this, replacing CREDENTIAL_BLOB with the blob that auth printed:
{
"mcpServers": {
"google-search-console": {
"type": "http",
"url": "https://search-console-mcp.russjeffery.com/mcp",
"headers": { "Authorization": "Bearer CREDENTIAL_BLOB" }
}
}
}
In the Claude web or desktop UI, add the same URL and header under Settings → Connectors → Add custom connector.
The deployment answers on these routes:
| Route | Behavior |
|---|---|
POST /mcp |
The MCP endpoint. Data tools require an Authorization: Bearer header carrying your blob. |
GET /health, GET / |
Server name, version, endpoint path, and supported protocol versions. No authentication, no credentialed data. |
GET /mcp, DELETE /mcp |
405, as the 2026-07-28 revision prescribes. There is no GET stream and no session to delete. |
A request that arrives with no bearer token isn't refused outright. Discovery — initialize, tools/list, resources/read, and the check_setup tool — answers normally and returns setup instructions in place of the usual guidance, so a client that isn't configured yet still connects and its agent can explain what to do. Only tools/call for a data tool returns 401. A deployment with MCP_SHARED_SECRET set is the exception: it requires the secret on every request, including discovery.
Two things to know before you point production work at it:
- You are sending your credentials to someone else's host. The blob grants Search Console access to your account until you revoke it. The code that receives it is in this repository, and it stores nothing. If that tradeoff doesn't suit you, run the server yourself. The stdio and self-hosted paths are equivalent in every other respect.
- It's a personal deployment with no uptime commitment. For anything you depend on, deploy your own copy. See Cloudflare Workers.
The hosted deployment allows no browser origins, so browser-based MCP clients are refused. Ordinary MCP clients send no Origin header and are unaffected.
Tools
The server exposes every method in the Search Console API v1, plus two composites and a setup check:
| Tool | Does | API method |
|---|---|---|
list_sites |
All properties you can access, with permission levels | sites.list |
get_site |
One property and your permission on it | sites.get |
query_search_analytics |
Clicks, impressions, CTR, and position — grouped, filtered, paged | searchanalytics.query |
compare_search_analytics |
Two periods with per-row and total deltas | composite |
list_sitemaps |
Submitted sitemaps, or the children of a sitemap index | sitemaps.list |
get_sitemap |
One sitemap's status and submitted and indexed counts | sitemaps.get |
submit_sitemap |
Submit or resubmit a sitemap | sitemaps.submit |
delete_sitemap |
Unsubmit a sitemap | sitemaps.delete |
inspect_url |
Full index status for one URL | urlInspection.index.inspect |
inspect_urls |
Up to 25 URLs concurrently, with a coverage-state summary | composite |
check_setup |
Whether credentials are present, still valid, and which properties they reach | diagnostic |
Site verification and the sites.add and sites.delete methods are deliberately not exposed. Adding and verifying a property is a browser flow that doesn't belong in an agent tool.
The server also serves prompts (performance_review, indexing_audit, query_opportunities, and sitemap_health) and resources (gsc://guide/search-analytics, gsc://guide/url-inspection, and gsc://guide/sitemaps) that agents can read on demand.
Authentication
Create a Google OAuth client
You do this once. The server can't do it for you, because Google requires a human in their console.
- Open the Google Cloud Console and select or create a project.
- Enable the Search Console API for that project.
- Configure the OAuth consent screen. External is fine for personal use. Add your own Google account under Test users.
- Go to Credentials → Create credentials → OAuth client ID and choose the application type Desktop app.
- Copy the Client ID and the Client secret.
While the consent screen is in Testing, Google expires refresh tokens after seven days, and you have to re-run auth weekly. Publishing the app, under OAuth consent screen → Publish app, makes them durable. For a single-user internal tool, publishing is safe and doesn't require Google's verification review, as long as you keep to the webmasters scopes.
Run the setup flow
npx google-search-console-mcp auth
This opens a setup page served from 127.0.0.1. Paste in the client ID and secret, then choose full or read-only access. The flow runs Google's consent screen and exchanges the code for a refresh token with PKCE. It then calls list_sites to prove the credentials work, showing you the exact properties they can reach.
The final page gives you the credential blob and ready-to-paste config for Claude Desktop, Claude Code, and remote deployments, each with a copy button. The same values are printed to your terminal as a fallback.
On a headless machine or over SSH, run auth --terminal for the prompt-driven version instead.
The credential blob
What you get back is a credential blob: base64url-encoded JSON holding your client ID, client secret, and refresh token.
eyJ2IjoxLCJjcmVkZW50aWFscyI6eyJ0eXBlIjoib2F1dGhfcmVmcmVzaF90b2tlbiIsImNsaWVu…
Treat the blob as a password. Anyone holding it has your Search Console access until you revoke it at Google Account permissions.
It's a single opaque string so that one value carries everything the server needs. That way it goes straight into an environment variable or an Authorization header, with no credentials file on disk.
Alternatives to the OAuth flow
Service account. This suits CI and team-owned properties. Create a service account in Google Cloud, then add its client_email address as a user on the property in Search Console, under Settings → Users and permissions. Encode the downloaded key file directly:
base64 -i service-account.json | tr -d '\n'
The server accepts a raw service-account key as the blob, with no envelope needed.
Existing access token. Set the blob to {"type":"access_token","access_token":"ya29..."}. No refresh is possible, so this suits only short-lived scripts.
Scopes
The flow requests one of two scopes:
| Scope | Grants |
|---|---|
https://www.googleapis.com/auth/webmasters.readonly |
Everything except sitemap submit and delete |
https://www.googleapis.com/auth/webmasters |
Full access (default) |
Choosing read-only during auth requests the narrower scope. The --read-only flag on the server is a separate, independent block that rejects mutating tools before they reach the API.
Run the server
Local (stdio)
Paste the config that auth printed into your client, replacing CREDENTIAL_BLOB with your own blob:
{
"mcpServers": {
"google-search-console": {
"command": "npx",
"args": ["-y", "google-search-console-mcp"],
"env": { "GSC_CREDENTIALS": "CREDENTIAL_BLOB" }
}
}
}
Config file locations differ by client:
| Client | Path |
|---|---|
| Claude Desktop (macOS) | ~/Library/Application Support/Claude/claude_desktop_config.json |
| Claude Desktop (Windows) | %APPDATA%\Claude\claude_desktop_config.json |
| Claude Code | Run claude mcp add google-search-console --env GSC_CREDENTIALS=CREDENTIAL_BLOB -- npx -y google-search-console-mcp |
| Cursor | ~/.cursor/mcp.json |
| VS Code | .vscode/mcp.json |
To skip the npx download on every launch, install the package globally:
npm install -g google-search-console-mcp
Self-hosted HTTP
Start a local Streamable HTTP server with your own credentials:
GSC_CREDENTIALS=CREDENTIAL_BLOB npx google-search-console-mcp http --port 8787
That serves POST http://127.0.0.1:8787/mcp. It binds to loopback by default. Pass --host 0.0.0.0 only if you mean to expose it, and put TLS in front of it when you do.
Browser-based clients are refused unless you name them, because a server holding its own credentials would otherwise be drivable by any page you visit. Ordinary MCP clients send no Origin header and are unaffected. A browser client needs its origin listed:
npx google-search-console-mcp http --allowed-origins http://localhost:6274
A rejected origin gets a 403 that the browser can't read, because a refusal carries no CORS headers by design. It surfaces as a generic CORS failure, so check the server's Origins: startup line when a browser client can't connect. To turn the check off, pass --allowed-origins '*'.
Cloudflare Workers
Deploy your own copy:
git clone https://github.com/russjeffery/google-search-console-mcp.git
cd google-search-console-mcp
npm install
npx wrangler deploy
Your endpoint is https://google-search-console-mcp.SUBDOMAIN.workers.dev/mcp, where SUBDOMAIN is your workers.dev subdomain.
By default the Worker stores no secrets. Each client sends its own credential blob as the bearer token, so a shared deployment never holds anyone's Google credentials. Different users of the same URL see only their own properties.
For a private, single-tenant deployment, set both secrets instead:
npx wrangler secret put GSC_CREDENTIALS # your blob
npx wrangler secret put MCP_SHARED_SECRET # token clients must present
Clients then send the shared secret rather than a blob.
The Worker reads these optional vars from wrangler.jsonc:
| Variable | Effect |
|---|---|
MCP_ENDPOINT |
Path to serve on. Default /mcp |
GSC_READ_ONLY |
"1" disables sitemap submit and delete |
ALLOWED_ORIGINS |
Comma-separated browser origins. Unset means non-browser clients only; * allows any |
MCP_STRICT_HEADERS |
"0" relaxes 2026-07-28 header-mirroring validation |
Serve it from your own domain
To use a custom hostname, add a named environment to wrangler.jsonc with the hostname as a custom-domain route. The prod environment already there is the one behind the hosted endpoint, so copy its shape and change the pattern:
"env": {
"prod": {
"name": "google-search-console-mcp",
"routes": [
{ "pattern": "mcp.example.com", "custom_domain": true }
],
"vars": { "MCP_ENDPOINT": "/mcp" }
}
}
Then deploy that environment:
npx wrangler deploy --env prod
Wrangler creates the DNS record and the edge certificate itself. The zone has to already be on the same Cloudflare account. Keeping the route out of the top level means a plain wrangler deploy still works for anyone else who clones this repository. Environments don't inherit vars, so repeat any you need. Secrets are per-environment too, so pass --env prod to wrangler secret put as well.
Connect a client to a remote server
Point the client at your deployment's URL and send the blob as a bearer token:
{
"mcpServers": {
"google-search-console": {
"type": "http",
"url": "https://mcp.example.com/mcp",
"headers": { "Authorization": "Bearer CREDENTIAL_BLOB" }
}
}
}
In the Claude web or desktop UI, add it under Settings → Connectors → Add custom connector.
To print that config filled in for your own deployment, run:
npx google-search-console-mcp config --url https://mcp.example.com/mcp
CLI
google-search-console-mcp [command] [options]
stdio Run as a stdio MCP server (default)
http Run a local Streamable HTTP MCP server
auth Guided setup in your browser: OAuth flow, blob, client config
config Print client config for existing credentials
doctor Verify credentials by calling the API
help Show usage
When something isn't working, run doctor first. It separates "the credentials are wrong" from "the client can't launch the server." doctor exits non-zero on a credential problem, so it also works in a health check.
The stdio command behaves differently: missing or unreadable credentials don't stop it. It starts anyway, lists its tools, and replaces its usual instructions with setup guidance, so the agent connected to it can explain the problem and walk you through the fix. Data tools return an error naming the remedy; check_setup returns the full diagnosis. A line on stderr says NOT CONFIGURED when the server comes up this way.
The commands accept these options: --credentials, --site, --read-only, --port, --host, --endpoint, --secret, --allowed-origins, --loose-headers, --url, --terminal, and --no-browser.
Three of them are worth a note:
--allowed-originstakes a comma-separated list, and unset means non-browser clients only. Entries are matched case-insensitively, and a trailing slash is ignored.--sitesets a default property so tools can omitsiteUrl. That's convenient when a deployment only ever covers one site.--loose-headersrelaxes the 2026-07-28 header-mirroring checks, the same as settingMCP_STRICT_HEADERS=0.
Protocol support
The 2026-07-28 revision changed Streamable HTTP substantially: no initialize handshake, no sessions, no Mcp-Session-Id header, no GET stream, and per-request metadata in params._meta mirrored into HTTP headers. The official TypeScript SDK doesn't implement it yet, so the protocol layer here is hand-written and dual-era.
| Client speaks | Server behavior |
|---|---|
2026-07-28 |
Stateless. Validates _meta, MCP-Protocol-Version, Mcp-Method, and Mcp-Name. Answers server/discover. Results carry resultType and serverInfo. |
2025-11-25 and earlier |
Standard initialize handshake. No session ID is issued, because the server is stateless either way. |
The era is detected per request. A request carrying modern _meta is served as modern, and an initialize selects legacy. GET and DELETE on the endpoint return 405, as the revision prescribes.
Header validation is strict by default, per the specification. When a client sends modern _meta without mirroring the headers, set MCP_STRICT_HEADERS=0 or pass --loose-headers rather than downgrading the protocol version.
On authorization. The specification's OAuth 2.1 flow assumes the server is a resource server with its own authorization server. This server instead uses the bearer token to carry your Google credentials directly. The specification permits custom strategies, and it means a hosted deployment holds no secrets and needs no user database. The tradeoff is that clients expecting automatic OAuth discovery need the header configured manually, as shown earlier.
Data caveats
Four properties of Search Console data cause most wrong conclusions. The tool descriptions and the bundled skill cover these in depth. In brief:
- Data lags about three days. Use
lastDaysand the tools pick a safe window. A range ending today shows a decline that isn't real. - Query data is privacy-filtered. Grouping by
querysilently drops rare queries, so query-level clicks never sum to the property total. That gap isn't lost traffic. - Position is inverted. Position 3 beats position 8, so a negative change is an improvement. The
compare_search_analyticstool returns an explicitimprovedflag. - Averages cancel out. Flat headline numbers routinely hide large offsetting movements. Group by page or query before concluding that nothing changed.
Quotas
Google enforces two limits that shape how you query:
- Search analytics: about 1,200 queries per minute per property.
- URL inspection: about 2,000 per day per property. This is the tighter of the two, so sample deliberately.
Not available through the API
Google exposes no API for the aggregate Index Coverage report, live URL testing, requesting indexing, Core Web Vitals, manual actions, security issues, links reports, or removals, so none of them are here. Per-URL inspect_url calls are the closest substitute for coverage questions.
Agent skill
The skills/google-search-console/ directory holds a ready-to-install skill that teaches an agent how to use these tools well: the preceding caveats, a diagnostic ladder for traffic changes, opportunity-finding heuristics, and a coverage-state lookup table.
cp -r skills/google-search-console ~/.claude/skills/
The same reference material is available at runtime through the server's gsc://guide/* resources, so agents without the skill installed can still read it.
Development
npm install
npm run build # compile to dist/
npm run typecheck
npm test
npm run cf:dev # Worker locally through wrangler
To check the HTTP transport by hand, start the server and send it a tools/list call:
GSC_CREDENTIALS=CREDENTIAL_BLOB npm run build && node dist/bin/cli.js http &
curl -s http://127.0.0.1:8787/mcp \
-H 'content-type: application/json' \
-H 'MCP-Protocol-Version: 2026-07-28' \
-H 'Mcp-Method: tools/list' \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{"_meta":{"io.modelcontextprotocol/protocolVersion":"2026-07-28","io.modelcontextprotocol/clientCapabilities":{}}}}' | jq '.result.tools[].name'
Troubleshooting
| Symptom | Cause and fix |
|---|---|
invalid_grant |
The refresh token was revoked, or the consent screen is in Testing mode, which expires tokens after seven days. Re-run auth, and publish the app to stop it recurring. |
403 insufficient permission on one property |
The siteUrl value doesn't match exactly. Run list_sites and copy the string verbatim. https://example.com/ and sc-domain:example.com are different properties. |
403 mentioning the API being disabled |
Enable the Search Console API in the Google Cloud project that issued the credentials. |
Empty list_sites result |
You authenticated successfully as a Google account with no properties. You most likely picked the wrong account at the consent screen. |
| Traffic appears to drop sharply in the last few days | The data isn't final yet. Use lastDays. |
| The server connects but every data tool fails | It started without usable credentials. Ask the agent to call check_setup, or run npx google-search-console-mcp doctor in a terminal for the same diagnosis. |
| The agent says the server isn't configured | check_setup reports which of the three cases it is: credentials missing, unreadable, or rejected by Google. Its howToFix list is the remedy for that case. |
-32020 HeaderMismatch |
The client sends modern _meta without mirroring the headers. Set MCP_STRICT_HEADERS=0. |
Security
- The credential blob is your Google access. Don't commit it, and don't paste it into shared documents. Revoke it at Google Account permissions.
- HTTP mode binds to
127.0.0.1by default and validatesOriginagainstALLOWED_ORIGINS, which helps prevent DNS rebinding. Unset means no browser origin is allowed, so list them explicitly or use*to opt out of the check. The/healthand/routes are exempt, because they expose no credentialed capability. - Shared-secret comparison is length-checked and constant-time.
- The default Worker deployment stores no credentials at all.
- The
--read-onlyflag andGSC_READ_ONLY=1block sitemap mutation independently of the granted OAuth scope.
License
MIT
推荐服务器
Baidu Map
百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。
Playwright MCP Server
一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。
Magic Component Platform (MCP)
一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。
Audiense Insights MCP Server
通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。
VeyraX
一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。
graphlit-mcp-server
模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。
Kagi MCP Server
一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。
e2b-mcp-server
使用 MCP 通过 e2b 运行代码。
Neon MCP Server
用于与 Neon 管理 API 和数据库交互的 MCP 服务器
Exa MCP Server
模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。