Skip to main content
Glama
Mrshahidali420

Google Search Console MCP Server

Server Configuration

Describes the environment variables required to run the server.

NameRequiredDescriptionDefault

No arguments

Capabilities

Features and capabilities supported by this server

CapabilityDetails
tools
{
  "listChanged": false
}
prompts
{
  "listChanged": false
}
resources
{
  "subscribe": false,
  "listChanged": false
}
experimental
{}

Tools

Functions exposed to the LLM to take actions

NameDescription
gsc_list_sitesA

List every Search Console property this account can see.

Costs one Search Console API call (sites.list) — no per-property quota is spent. Persists each property into the local store (upserted by property URL) so other tools can route a page URL to its property without another network round trip; an existing row's sitemaps are left untouched here, since this call does not fetch or change them.

Returns [{"property", "host", "permission"}, ...] sorted by property. On a missing, expired, or rejected token, returns {"ok": False, "error": "auth_required", "fix": ...} instead of raising; if no OAuth client is configured at all, returns {"ok": False, "error": "not_configured", "fix": ...} instead — in either case the caller can surface fix directly rather than parsing an exception. A Search Console error returns {"ok": False, "error": "api_error", "status": <http status>, "fix": ...}, and anything else {"ok": False, "error": "unexpected", "detail": <exception type>, "fix": ...}. An EMPTY LIST therefore means what it says — this account really has no properties — and never a call that was refused.

Does not fetch sitemaps, index status, or search analytics; see gsc_doctor, gsc_check_status, and gsc_performance for those. A property already known to the store keeps whatever sitemaps a prior gsc_submit_sitemaps() call recorded against it — this call never fetches or clears that list, so a routine refresh cannot erase it.

gsc_doctorA

Diagnose whether gsc-mcp is set up to talk to Search Console.

Runs five checks in order — oauth_client, token, config, store, properties — and reports all of them even if one raises. A check that raises is recorded as ok: False with the exception's TYPE NAME only in detail; the message is never included, because it can carry a bearer token, a credentialed URL, or a raw response body. Every failing check carries a non-empty fix string with a concrete next step; this tool diagnoses, it does not repair anything itself.

Costs at most one Search Console API call (sites.list, for the properties check) — zero network calls if an earlier check already shows the client or token is unusable and a caller stops before reaching it, though this implementation always runs all five.

Returns {"ok": bool, "checks": [{"name", "ok", "detail", "fix"}, ...]}; ok is true only when every check passed.

gsc_check_statusA

Check whether each URL is indexed by Google Search, via the URL Inspection API.

READ-ONLY: this tool inspects current index status and submits NOTHING. It never requests indexing and never spends a Request-Indexing slot. No tool here does yet — requesting indexing is not built, so an assistant asked to get a URL indexed should say so rather than reaching for this one, which answers a different question and spends a different budget doing it. This tool DOES spend URL Inspection quota, a separate per-property budget of 2,000 calls a day and roughly 600 a minute (see gsc_quota) — one call per URL inspected.

concurrency defaults to the configured inspect_concurrency (config.load()["inspect_concurrency"]) when omitted.

Properties come from the local store, the same one gsc_list_sites populates. If the store has never been synced — e.g. this is the very first call this install has ever made — it is synced automatically first, so a first-ever gsc_check_status call does not fail with "no property matches" purely because nothing has been synced yet.

Returns {"rows": [...], "checked": int, "skipped_quota": [...], "quota": {...}}. "quota" is per property, and its daily_free_at_gate/minute_free_at_gate are the headroom measured BEFORE this call reserved its own budget — they are a record of what the gate saw, not current headroom, and are already stale by the size of this batch by the time you read them. gsc_quota's similarly-shaped daily_free/minute_free are the ones measured now; do not compare the two pairs or plan a second batch against these. binding_at_gate (here and on each "skipped_quota" entry) carries the suffix for the same reason: it names only the exhausted INSPECTION window as seen at the gate, where gsc_quota's binding covers the submission budget too and is read now. Each row is {"url", "status", "detail", "unverified"}. status is one of: indexed, crawled_not_indexed, discovered_not_indexed, unknown_to_google, redirect, noindex, duplicate, alternate_canonical, not_found, soft_404, blocked_robots, no_property, error. no_property means no Search Console property in this account covers that URL's host. unverified is True when a concurrent burst produced a suspect result (unknown_to_google or error) that a sequential re-check could not confirm before quota or time ran out — treat such a row as UNKNOWN, not as a confirmed "not indexed".

On a missing, expired, or rejected token, returns {"ok": False, "error": "auth_required", "fix": ...} instead of raising; if no OAuth client is configured at all, returns {"ok": False, "error": "not_configured", "fix": ...}.

gsc_quotaA

Report Request-Indexing and URL Inspection budget for every property the store currently knows about.

Local-only: reads the store and the config file, makes no Search Console API call, and needs no OAuth token — safe to call at any time, including before signing in. An empty store (nothing synced yet via gsc_list_sites or gsc_check_status) returns [].

One entry per property: {"property", "submission", "inspection", "binding"}.

"submission" is the Request-Indexing slot budget: {"free", "spendable_free", "used", "slots", "daily_reserve", "next_free_at"}. free is the RAW free-slot count and ignores daily_reserve. spendable_free is computed against the RESERVE-ADJUSTED ceiling — max(0, (slots - daily_reserve) - used) — NOT simply free minus daily_reserve: that arithmetic breaks at the clamp (slots=11, daily_reserve=2, used=10 gives free=1, but spendable_free is 0, not -1). ACT ON spendable_free, NOT free: daily_reserve exists to hold slots back from every tool, and a caller that submits up to free instead will be refused once spendable_free runs out. next_free_at is an ISO-8601 string, or None when a slot is free right now — it is already computed against the reserve-adjusted ceiling too, so it can report a wait even while free (the raw count) is nonzero.

"inspection" is the URL Inspection API budget: {"daily_free", "minute_free", "daily_limit", "minute_limit"} (2000/day, 600/minute, per property — the same quota gsc_check_status spends).

"binding" names whichever budget is exhausted for that property right now — "submission" (the Request-Indexing ceiling, reserve applied), "inspection_daily", or "inspection_minute" — or None when every budget has headroom.

gsc_performanceA

Search Analytics performance: clicks, impressions, ctr, position.

Three shapes, chosen by what is passed:

  • No site -> one row per property the store knows about (scope: "portfolio", sites, totals aggregated across all of them).

  • site, no dim -> a single aggregate for that site (scope: "site", plus clicks/impressions/ctr/position).

  • site and dim -> per-dim rows for that site, sorted by clicks descending (scope: <dim>, rows, totals aggregated across rows). dim must be one of perf.VALID_DIMENSIONS ("query", "page", "country", "device", "date", "searchAppearance").

Date window: pass start_date AND end_date (both "YYYY-MM-DD") for an explicit range, or leave both unset and get the trailing days calendar days ending yesterday. start_date without end_date is refused outright -- {"ok": False, "note": "start_date needs end_date (both YYYY-MM-DD)"} -- rather than guessing an end.

IMPORTANT -- data_state defaults to "all", not Google's own API default of "final". Passing data_state="final" attaches a warning string explaining why: data_state='final' silently omits the most recent ~perf.FINAL_LAG_DAYS (3) day(s) of data -- Google has not finished processing them yet, and nothing in the response marks the gap. The Search Console web UI has no such restriction and shows those days immediately, so a 'final' query compared against what a human sees in the UI right now will look like a discrepancy or a missed traffic change when both are simply answering different questions. Prefer data_state='all' (the default here) unless you specifically need finalized-only rows; its trade-off is that the last day or two may still revise upward on a later query.

On a missing, expired, or rejected token, returns {"ok": False, "error": "auth_required", "fix": ...}; if no OAuth client is configured at all, {"ok": False, "error": "not_configured", "fix": ...}. Any other failure -- a bad dimension, an unroutable site, a Search Console API error -- comes back as {"ok": False, "start", "end", "note": str(exc)} rather than raising.

gsc_submit_sitemapsA

Submit one or more sitemaps to Search Console (PUT, idempotent -- safe to resubmit an already-known sitemap).

sitemaps is an optional list of sitemap URLs; each is routed to its covering property via the same host-matching gsc_check_status uses. Omit it to resubmit every sitemap already on record for every property (the same list gsc_list_sites carries forward on refresh).

REFUSES TO GUESS: when sitemaps is omitted and the store has no sitemap recorded for any property, this returns {"ok": False, "note": "No sitemaps known. Pass sitemaps=[...] explicitly."} rather than trying /sitemap.xml -- submitting a URL nobody named is an outward-facing action against the caller's Search Console property, and a wrong guess leaves a permanent failed submission in their sitemap list for no reason.

Returns a list of {"site", "sitemap", "http_status", "ok", "note"} (api.submit_sitemap's shape) -- one entry per sitemap attempted, plus one {"site": None, "sitemap", "http_status": None, "ok": False, "note"} entry for any URL that matched no known property. Every successful submission is recorded back onto its property's row (merged with, never replacing, whatever sitemaps were already there), so a later bare call resubmits it too.

On a missing, expired, or rejected token, returns {"ok": False, "error": "auth_required", "fix": ...} instead of raising, once real work is about to start (nothing has been submitted yet at that point); if no OAuth client is configured at all, {"ok": False, "error": "not_configured", "fix": ...}. Any other failure comes back as {"ok": False, "error": "unexpected", "detail": <exception type>, "fix": ...}.

Prompts

Interactive templates invoked by user choice

NameDescription

No prompts

Resources

Contextual data attached and managed by the client

NameDescription

No resources

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/Mrshahidali420/google-search-console-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server