gsc-mcp
by RobertDWhite
README.md
# gsc-mcp
MCP server for **Google Search Console** and **IndexNow** — manage search
indexing programmatically instead of driving a browser.
- **Image:** `ghcr.io/robertdwhite/gsc-mcp`, built by CI in this repo.
- **Transport:** MCP streamable HTTP at `/mcp`, bearer-gated by `MCP_TOKEN`.
`/healthz` is open.
## Tools
| Tool | Does |
| --- | --- |
| `gsc_status()` | Credential check; reports the service-account email |
| `list_sites()` / `add_site(site_url)` | Search Console properties |
| `verification_token(site_url, method)` | Get FILE/META ownership token |
| `verify_site(site_url, method)` | Complete verification — the service account becomes an owner |
| `add_owner(site_url, email)` | Grant a human Google account access (UI + API) |
| `submit_sitemap(site_url, sitemap_url)` / `list_sitemaps(site_url)` | Sitemap management with indexed-count stats |
| `search_analytics(site_url, days, dimensions, row_limit)` | Clicks/impressions/CTR/position by query, page, country, device, date |
| `inspect_url(site_url, url)` | Per-URL index status, last crawl, canonical |
| `submit_indexnow(urls, host, key)` | Push URLs to Bing/DuckDuckGo/Yandex — **works with no Google setup** |
## One-time Google setup (human, ~5 minutes)
1. In [Google Cloud Console](https://console.cloud.google.com), create/reuse a
project and enable **Google Search Console API** and **Site Verification API**.
2. Create a **service account** (no roles needed) and a **JSON key**.
3. Put the key JSON in the `GSC_SA_JSON` env var (in-cluster: the
`gsc-mcp-env` secret).
Then, via tools: `add_site` → `verification_token` (place the token on the
site) → `verify_site` → the service account is a verified owner and every
other tool works. `add_owner` grants your personal account the same property.
## Config (env)
| Var | Meaning |
| --- | --- |
| `MCP_TOKEN` | Bearer token required on `/mcp`. Unset = unauthenticated (dev only). |
| `GSC_SA_JSON` | Service-account key JSON (the raw JSON string). |
| `INDEXNOW_HOST` / `INDEXNOW_KEY` | Optional defaults for `submit_indexnow`. |
| `PORT` | Listen port (default 8080). |
## Deploy
Manifests live in
[whitehouse-rke2](https://github.com/RobertDWhite/whitehouse-rke2) under
`apps/ai/gsc-mcp` (endpoint `https://gsc-mcp.internal.white.fm/mcp`, tailnet
only).
## Connecting clients
The server speaks **MCP streamable HTTP** at `/mcp` and expects
`Authorization: Bearer <MCP_TOKEN>`. Anything below works from any machine
that can reach the endpoint (for the house deploy: tailnet/LAN only).
### Claude Code
```sh
claude mcp add --transport http gsc https://gsc-mcp.internal.white.fm/mcp \
--header "Authorization: Bearer <MCP_TOKEN>"
```
Or directly in `~/.claude.json`:
```json
"mcpServers": {
"gsc": {
"type": "http",
"url": "https://gsc-mcp.internal.white.fm/mcp",
"headers": { "Authorization": "Bearer <MCP_TOKEN>" }
}
}
```
### Claude Desktop
The custom-connector UI expects OAuth; this server uses a static bearer, so
bridge it with [`mcp-remote`](https://www.npmjs.com/package/mcp-remote) in
`claude_desktop_config.json` (needs Node):
```json
"mcpServers": {
"gsc": {
"command": "npx",
"args": ["-y", "mcp-remote", "https://gsc-mcp.internal.white.fm/mcp",
"--transport", "http-only",
"--header", "Authorization: Bearer <MCP_TOKEN>"]
}
}
```
### Codex (ChatGPT app / Codex CLI)
Codex configures MCP servers as stdio commands — use the same bridge in
`~/.codex/config.toml`:
```toml
[mcp_servers.gsc]
command = "npx"
args = ["-y", "mcp-remote", "https://gsc-mcp.internal.white.fm/mcp",
"--transport", "http-only",
"--header", "Authorization: Bearer <MCP_TOKEN>"]
startup_timeout_sec = 60
```
Restart the app after editing.
### Hermes (or any YAML-config MCP client)
```yaml
mcp_servers:
gsc:
url: https://gsc-mcp.internal.white.fm/mcp
headers:
Authorization: Bearer ${GSC_MCP_TOKEN}
```
### Sanity check without a client
```sh
curl -s https://gsc-mcp.internal.white.fm/healthz # -> ok (no auth)
curl -s -o /dev/null -w '%{http_code}\n' -X POST \
https://gsc-mcp.internal.white.fm/mcp # -> 401 (auth gate works)
```
Note: `claude.ai` web connectors cannot reach a tailnet-only endpoint; expose
an external route first if you need it there.
This server cannot be deployed
Maintenance
ActivityStale
ResponsivenessNo issues