Changelog
[Unreleased]
Fixed
- A search query containing a backslash directly before a pipe no longer breaks the analytics table.
escapeCellescaped the pipe but not the backslash, soshoes \| nikecame out asshoes \\| nike— an escaped backslash followed by a live column separator, which is exactly the row split the escaping prevents.
[0.1.0] - 2026-08-29
First release.
Added
- MCP server for Google Search Console, covering three Google APIs in full: Search Console v1 (properties, sitemaps, search analytics, URL inspection), Site Verification v1 (proving and managing ownership) and the Indexing API v3.
- 21 tools. Eighteen map one-to-one onto an API method; the other three exist because the APIs do not have them and the work needs them:
setup_sitereports which of the four steps a property is missing and hands over the DNS record or meta tag to place. Nothing in either API enforces that order, andadd_siteon an unverified domain succeeds while producing a property that answers 403 for every piece of data.submit_sitemapsandinspect_urlsdo one property's worth of work in a single call, reporting per-entry results.
- Three ways to authenticate, tried in a fixed order: a service account key (raw JSON or base64), an OAuth2 refresh token, or application default credentials. A partial OAuth2 triple is refused rather than silently falling through to whichever account the machine is logged into.
- Relative date ranges —
period: "last28days"— resolved against today in Pacific Time, which is the time zone Search Console counts days in. A range computed in UTC asks for a day Google has no data for. - Search analytics come back as a compact table with totals, at roughly a third the tokens of the API's JSON. CTR is computed from the totals and average position is weighted by impressions, and the result says plainly that the rows do not sum to the property total when the query dimension is used.
GSC_SITE_URLdefaults the property for every tool that takes one, andGSC_ALLOWED_SITESrestricts which properties the server may touch at all.GSC_ALLOW_TOOLS/GSC_DENY_TOOLSnarrow the tool list by name orlist_*prefix;essentialselects the five read tools that cover looking at a property.GSC_READ_ONLY=trueregisters only the twelve read tools; the nine write tools never reach the client's tool list.
Security
- The requested OAuth scopes follow the registered tools. A server narrowed to the Search Console tools never asks for the Site Verification or Indexing scope — which is the difference between working and not for a service account under domain-wide delegation, where an undelegated scope fails the whole token request.
GSC_READ_ONLYadditionally swapswebmastersforwebmasters.readonly, so writes are impossible below the tool layer. - The four irreversible operations —
delete_site,delete_sitemap,unverify_siteandupdate_site_owners— are two-step: the first call returns a short-lived confirmation token bound to those exact arguments, so a confirmation for one property cannot be replayed against another, and one for a two-name owner list cannot execute a three-name one.update_site_ownersreads like an update and is a replacement: the list passed becomes the complete owner list, so a single well-formed call removes everyone else. GSC_ALLOWED_SITEShas no exemptions. Tools that name a property are checked inresolveSite; the Indexing API tools, which name a page instead, match the URL against the list the way Search Console scopes a property; and the verification tools, which take an opaque resource id, resolve it to a property before acting.list_sitesandlist_verified_sitesfilter to the allowlist and report how many entries they withheld — the ids the second one returns are whatunverify_siteacts on.- Search queries, page titles and crawl diagnostics are marked as untrusted content, on every result carrying an upstream payload. Search queries in particular are strings arbitrary members of the public typed into Google, and page titles come from whoever runs the crawled site.
- A confirmation prompt's two sentences are built only from values the server derived. Where an operation cannot be described without naming its subject — a sitemap URL, an owner list — the subject is quoted below them as data, flattened to a single line so it cannot open one of its own.
- Credentials are deleted from the environment after start-up (including the path in
GSC_SERVICE_ACCOUNT_KEY_FILE, which points at one), never sent to a redirect target, and never echoed into an error message — including when the rejected value is a key pasted into the wrong variable, and including the one error path whose text comes fromgoogle-auth-libraryrather than from here. - Results are budgeted: list results drop whole entries rather than overflowing the model's context and name the call that fetches the rest, and a single oversized object has its largest field shortened — at any depth, arrays as well as strings — rather than being truncated into unparseable JSON.
- Verification resource ids are rejected when they are a path of dots.
encodeURIComponentescapes a slash but not a dot, so..would otherwise resolve/webResource/..back to the collection endpoint.
Notes
- The npm package is
@ni-c/google-search-console-mcp, scoped, while the repository, the image and the docs domain are unscoped.npm viewreported the unscoped name as free, but npm's similarity check runs only at publish time and refused it as too close to the existinggoogle-searchconsole-mcp. The MCP registry name is unaffected — that isio.github.ni-c/google-search-console-mcpeither way. - There is no
hosts.tsSSRF guard here, unlike its sibling servers, and that is a decision rather than an omission: there is no configurable target host. Every request goes to one of three hard-coded Google endpoints. urlTestingTools.mobileFriendlyTestis deliberately not exposed. It is still in Google's discovery document and the service behind it was switched off in December 2023, so a tool for it could only ever return an error.