Ask Claude "which organic keywords actually generate revenue, and which pages should I rank up first?" and get a real answer — Search Console rankings, Analytics 4 conversions, and the cross-platform tools that connect them, all in one MCP. Not a CSV dump. Not a hallucination. A diagnosis.
This project means different things to different people. Skip ahead to whichever description sounds like you:
- 👋 "I just want my website to do better in Google. Should I care about this?" — 90 seconds, no jargon.
- 🛠️ "I run an agency / I do SEO for a living. What does this give me?" — 2 minutes, the value prop.
- ⚡ "I'm an engineer and I know what an MCP is. Just show me the install." — 30 seconds.
- 🧠 "I want to see all 100 tools and the cross-platform tricks." — full technical surface below.
Imagine you have a website and you ask Google two questions every month:
- "Search Console, which keywords brought me visitors?" → Google answers with one report.
- "Analytics, what did those visitors do once they arrived?" → Google answers with another report, in a totally different place.
To get the picture that actually matters — "which keywords bring visitors who buy / sign up / contact me?" — you have to put both reports together by hand in a spreadsheet. Every single time. And if you're not a SEO person, the reports are confusing on their own.
This project lets you skip the spreadsheet. You install it once on your computer, and after that you can just talk to Claude in plain language:
"Why did my traffic drop last week?" "Which pages on my site are wasting their potential — they could rank higher AND people would actually convert?" "Show me the keywords that bring revenue, not just clicks."
Claude pulls the data from Google directly, cross-checks Search Console against Analytics, and answers in plain language with the exact numbers (not made-up numbers — every figure comes with a "where I got this from" stamp). You don't need to know what GSC, GA4 or "cross-platform attribution" mean. Claude does.
One important catch: your website needs to already have Google Search Console and Google Analytics 4 set up (most websites do — these are Google's free tools). If they aren't, this project can't help you yet. Set them up first via search.google.com/search-console and analytics.google.com.
If that sounds useful, jump to ⚡ the 30-second install and ask a developer friend to set it up — it takes 5 minutes and is a one-time thing.
If you spend your week pasting GSC + GA4 + Lighthouse + DataForSEO output into ChatGPT and praying it doesn't hallucinate the numbers, this is for you. Specifically:
- Replaces Looker Studio dashboards for diagnostic questions ("why did this page drop?") because the answers come classified —
ranking_lossvsctr_collapsevsdemand_declinevsdisappeared— instead of as a chart you have to interpret. - Replaces Sistrix / Semrush light usage for keyword research and SERP feature detection (AI Overview, PAA, featured snippet) — DataForSEO is pay-as-you-go, ~$0.20/year for monthly audits of one client.
- Replaces a chunk of Botify / OnCrawl for migration audits: redirect chains, sitemap diff, hreflang cluster check, schema parity, image alt coverage, server log analysis (Googlebot vs spoofers), and a full WP → JS-stack migration toolkit.
- Replaces the manual "paste into ChatGPT and hope" workflow for client reports. Every number comes with provenance: which API, which date range, which property. The agent literally cannot make figures up.
- Adds AEO 2026 tooling that doesn't exist anywhere else:
llms.txtvalidation, AI bot policy audit (GPTBot, ClaudeBot, PerplexityBot, CCBot…), AI Overview presence detection per query.
It does not replace a real link audit (no Ahrefs / Majestic backlink data), competitor share-of-voice tracking at scale, or an enterprise crawl on 5M+ URLs. For everything else — the 95% case of SMB to mid-market SEO — it does the job in one binary.
Jump to the install ↓ · or browse all 100 tools ↓ · or read the agent's playbook (AGENTS.md).
🎯 The headline (for everyone, technical or not): Claude becomes able to answer "which organic keywords actually generate revenue, and which pages should I rank up first?" — Search Console rankings, Analytics 4 conversions, and the cross-platform tools that connect them, all in one MCP. Not a CSV dump. Not a hallucination. A diagnosis.
Latest release — v0.8.3. 100 tools across 14 categories, 103 regression tests, multi-tenant safe, Service Account first-class. v0.8.x added four new modules (history persistence, SERP intelligence via DataForSEO, server log analysis, advanced technical crawl) plus polish from real-world feedback (dynamic
get_capabilities, structuredprerender_mode_viability, batch parallel audits, hardened JSON-LD detector, GA4property_timezone). SeeCHANGELOG.mdfor line-by-line history.
# 1. Install (Python 3.11+)
pipx install git+https://github.com/mario-hernandez/google-seo-mcp-claude-code
# 2. Add to Claude Code
claude mcp add google-seo-mcp -- $(which google-seo-mcp)
# 3. Authenticate — pick ONE method (next section)That's it. Now ask Claude:
"List all my GSC sites and GA4 properties, then run the opportunity matrix for example.com to surface pages where ranking up would also convert."
Works with Claude Code, Claude Desktop, Cursor, Windsurf, and any other MCP-compatible client.
There are three ways to give the MCP permission to read your Google data. Picking the right one for your situation is the single biggest source of confusion on this project — read this carefully.
🟢 Service Account (recommended for most people)
Use this if you're using Claude Code, running on a server, automating with agents, working with multiple clients, or your Google account is part of a Workspace where IT controls app approvals.
Create a Service Account JSON file in Google Cloud Console → grant its email read access to each Search Console site and Analytics property you want to use → set one environment variable:
export GOOGLE_SEO_SERVICE_ACCOUNT_FILE=/path/to/sa.jsonWhy this is the default: no browser needed, no token expirations, no "App is blocked" surprises, no reconsenting every few weeks. It just keeps working. Full step-by-step in Service Account setup ↓.
🔵 ADC (good for solo developers on a desktop)
Use this if you're a single developer, on your own laptop, with a browser open right now, and you don't mind reauthenticating every couple of months.
gcloud auth application-default login \
--scopes=https://www.googleapis.com/auth/webmasters.readonly,https://www.googleapis.com/auth/analytics.readonlyThis is what Google's own docs recommend, but in practice the OAuth client gets revoked silently after periods of inactivity and Workspace-managed accounts often see "This app is blocked". If either happens, switch to Service Account.
🟡 OAuth Desktop flow (you already have a verified OAuth client JSON)
export GOOGLE_SEO_OAUTH_CLIENT_FILE=/path/to/client.jsonNiche use case. Most people don't need this.
Stuck on auth? If you see
invalid_grant,App is blocked, empty results, orpermission deniedon your first call, jump to Troubleshooting ↓ — every common error has a documented fix.
Real prompts that fire real tool sequences (full playbook list in AGENTS.md):
| You ask | The agent runs |
|---|---|
| "What's our SEO ROI this month?" | cross_seo_to_revenue_attribution (90-day window) → returns top queries with attributed revenue |
| "Why did organic traffic drop?" | cross_traffic_health_check (rule out tracking) → gsc_traffic_drops (classify as ranking_loss / ctr_collapse / demand_decline) → resource://algorithm-updates for the drop date |
| "Where should I invest SEO budget this quarter?" | cross_opportunity_matrix → 4 quadrants (high_impact / worth_optimizing / good_but_capped / low_priority) auto-calibrated to your traffic |
| "Are old blog posts decaying?" | gsc_content_decay AND ga4_content_decay — pages flagged in BOTH = refresh priority |
| "Compare my 12 store locations by organic traffic" | cross_multi_property_comparison (parallel fan-out, up to 50 properties) |
100 tools + 1 reference resource across fourteen categories. Six of them are impossible without unified GSC+GA4 auth; the rest stitch in Lighthouse, CrUX, Schema validation, IndexNow, Google Trends/Suggest/Alerts, full WordPress→JS migration tooling, AEO (llms.txt + AI bot policy), and crawl-budget audits — the SEO swiss knife in one binary, no SaaS subscription needed for the 95% case.
- 🔄 Cross-platform tools that nobody else has — the whole reason this exists.
gsc_to_ga4_journeytraces an organic click to its conversion.opportunity_matrixscores pages by both "could rank higher" AND "would convert if it did".traffic_health_checkdetects when GSC and GA4 disagree (broken tracking).seo_to_revenue_attributiontells you which queries actually pay (with a 50% confidence band, not as a measurement). - 🩺 Diagnoses, not data dumps —
gsc_traffic_dropsclassifies pages asranking_loss/ctr_collapse/demand_decline/disappeared.ga4_traffic_drops_by_channelclassifies channels asvolume_loss/engagement_decay/conversion_decay/bounce_surge. Multi-axis taxonomy improved over the existing OSS competitors. - 🔍 Anti-hallucination guardrails — every response is wrapped with
_metaprovenance (source, site_url, property, period, fetched_at) and passed through a_json_safecoercer that handles datetime / Decimal / set / Path / numpy / pandas scalars before the JSON-RPC transport sees them. Your agent literally cannot make up the numbers when reporting to clients.
- 📐 Rigorous statistics —
ga4_anomaliesruns STL deseasonalisation (period=7) before a leave-one-out rolling Z-score, then applies Benjamini–Hochberg FDR correction when a dimension is set.gsc_content_decayrequires 3 monotonic 30-day windows before flagging. Real funnels via sequential filters. Pearson correlation in pagespeed analyses. - 🛡️ Read-only by default — destructive operations (sitemap submission, Google Indexing API publish/delete) require an explicit
GSC_ALLOW_DESTRUCTIVE=trueflag. - 🔐 Hardened for multi-client work (v0.7.1) — SSRF guard rejects RFC1918 / loopback / cloud-metadata IPs (override with
GOOGLE_SEO_ALLOW_PRIVATE_FETCH=trueif needed). XML parsing viadefusedxml(XXE-safe). Scraped HTML / OG / meta returned to the LLM is wrapped in<untrusted-third-party-content>markers. Auth singletons are thread-safe with double-checked locking, and a credentials fingerprint detects account rotation between clients so tools never silently keep returning data from the previous tenant.
These tools require both GSC and GA4 auth in the same process — which is exactly why they only exist here.
| Tool | What it does | Why it matters |
|---|---|---|
cross_gsc_to_ga4_journey |
Given an organic landing path, returns top GSC queries that drove clicks AND the GA4 behavior on that page (sessions, engagement, bounce, conversions, revenue, secondary pages). | Closes the loop from "what they searched for" to "what they did". |
cross_opportunity_matrix |
Identifies GSC quick-win pages (ranking 4-15) AND fetches their GA4 conversion rate. Classifies into 4 quadrants: high_impact / worth_optimizing / good_but_capped / low_priority. |
Prioritization that nobody can do with single-source MCPs. Tells you WHICH pages to rank up first, not just which COULD rank up. |
cross_traffic_health_check |
Compares GSC organic clicks vs GA4 organic sessions. Diagnoses tracking_gap / filter_issue / healthy based on the ratio. |
Detects broken tracking, consent banner problems, bot traffic, channel mis-classification — invisible to either MCP alone. |
cross_seo_to_revenue_attribution |
For each top organic query, attributes GA4 revenue proportionally by GSC click-share on the landing page. | Approximate but powerful: "which queries actually pay" — a question pure-GSC cannot answer and pure-GA4 cannot connect to keywords. |
cross_landing_page_full_diagnosis |
One call → GSC ranking signals + cannibalization check + GA4 behavior + composite health score (0-100, red/amber/green) + specific issue flags. | End-to-end triage of one page. The single tool you'd use during a client review. |
cross_multi_property_comparison |
Fans out to N GA4 properties (up to 50) in parallel, comparing a single metric. Returns sorted totals with optional dimension breakdown. | For agencies / multi-site owners: one call, all properties, one shared scope of auth. |
The cross_opportunity_matrix quadrant model: pages in the green high_impact corner are where SEO effort pays off twice — they're close enough to top positions that a small ranking push will move them, AND once there they convert. The amber worth_optimizing zone needs both a ranking push AND on-page work. The cyan good_but_capped is already converting; leave them alone. The slate low_priority is where most pages live; ignore them.
Ask Claude: "Is the tracking healthy on example.com?"
{
"data": {
"diagnosis": "healthy",
"ratio_ga4_to_gsc": 0.608,
"gsc_organic_clicks": 477,
"ga4_organic_sessions": 290,
"interpretation": "GA4 reports 61% of GSC organic clicks — within the expected 0.6-1.4 range. Tracking is consistent."
},
"_meta": {
"source": "crossplatform.traffic_health_check",
"site_url": "https://www.example.com/",
"property": "properties/123456789",
"period": {
"gsc": { "start": "2026-03-27", "end": "2026-04-23" },
"ga4": { "start": "2026-03-27", "end": "2026-04-23" }
},
"fetched_at": "2026-04-26T22:00:00Z"
}
}Claude can now explain the health: GA4 sees 61% of GSC clicks, which is normal because GSC counts every search-result-click while GA4 counts unique sessions (some users navigate away before analytics fires). Both periods are aligned to GSC's 3-day reporting lag so the comparison is apples-to-apples — the LLM can quote exact dates without making them up.
14 categories. Counts verified against mcp._tool_manager.list_tools() on the v0.8.3 release. The list below shows the original 11 categories that existed up to v0.7.1; v0.8.0 added aeo, history, serp, logs modules — call get_capabilities for the live grouping (it derives categories dynamically and never drifts from reality).
🔄 Cross-platform (6 — the unique selling proposition)
| Tool | What it does |
|---|---|
cross_gsc_to_ga4_journey |
Given a landing path, returns GSC top queries + GA4 behavior on that page |
cross_opportunity_matrix |
GSC quick-win pages ranked by GA4 conversion potential, in 4 quadrants |
cross_traffic_health_check |
GSC clicks vs GA4 sessions ratio with tracking_gap / filter_issue / healthy diagnosis |
cross_seo_to_revenue_attribution |
Top organic queries attributed to GA4 revenue by click-share (with 50% confidence band) |
cross_landing_page_full_diagnosis |
End-to-end page diagnosis: GSC + GA4 + cannibalization + score + issues |
cross_multi_property_comparison |
Parallel fan-out for ≤50 GA4 properties (multi-location dashboards) |
🔎 Google Search Console (12)
| Tool | What it does |
|---|---|
gsc_list_sites |
Verified properties + permission level + property type |
gsc_inspect_url |
URL Inspection API — index status, canonical, mobile, rich results |
gsc_list_sitemaps |
Sitemaps with errors / warnings / last-submitted |
gsc_submit_sitemap |
Submit a sitemap (gated by GSC_ALLOW_DESTRUCTIVE=true) |
gsc_search_analytics |
Custom Search Analytics query — supports country (ISO-3166 alpha-3) and device filters |
gsc_site_snapshot |
Aggregated totals last N days vs prior period |
gsc_quick_wins |
Queries in positions 4-15, scored by impressions × CTR-gap-to-pos-3 |
gsc_traffic_drops |
Pages losing traffic, classified ranking_loss / ctr_collapse / demand_decline / disappeared |
gsc_content_decay |
Pages with monotonic decline across 3 consecutive 30-day windows |
gsc_cannibalization |
Queries where ≥2 pages on your site compete |
gsc_ctr_opportunities |
Pages with CTR far below the expected for their position |
gsc_alerts |
Position drops / CTR collapses / click drops / disappeared queries with severity dedup |
📈 Google Analytics 4 (14)
| Tool | What it does |
|---|---|
ga4_list_properties |
Every GA4 property the auth account can access |
ga4_get_property_details |
Timezone, currency, industry, service level for one property |
ga4_search_schema |
TF-IDF keyword search over GA4 dimensions/metrics (top-N, avoids 10k token dump) |
ga4_list_schema_categories |
Cheap discovery of available dim/metric categories |
ga4_estimate_query_size |
Anti-context-blowup probe (limit=1 → reads row_count for free) |
ga4_query |
Full Data API report with filters, order_bys, aggregations |
ga4_anomalies |
STL-deseasonalised + leave-one-out Z-score + Benjamini–Hochberg FDR correction |
ga4_traffic_drops_by_channel |
Channels in decline, multi-axis: volume / engagement / conversion / bounce |
ga4_landing_page_health |
Health score (red/amber/green) for top landing pages |
ga4_event_volume_comparison |
Per-event unique-user counts (was conversion_funnel; renamed for honesty — GA4 Data API can't enforce sequence) |
ga4_conversion_funnel |
Deprecated alias of event_volume_comparison (kept for back-compat) |
ga4_cohort_retention |
New vs returning visitor metrics |
ga4_channel_attribution |
First-touch vs last-touch comparison; classifies as assister / closer / balanced |
ga4_content_decay |
GA4 metric decline across 3 consecutive 30-day windows (any metric) |
🚚 Migration (21 — WordPress → JS stack equity preservation)
| Tool | What it does |
|---|---|
migration_seo_equity_report |
Composer: WP REST + crawl + GSC + internal-link graph → equity score per URL with MUST_PRESERVE / WORTH_PRESERVING / LOW_VALUE / DEPRECATE tags |
migration_wp_audit_site |
WordPress REST inventory: post types, taxonomies, plugin probes |
migration_wp_extract_redirects |
Enumerate redirects from Redirection / RankMath / Yoast Premium |
migration_wp_internal_links_graph |
advertools crawl → in/out degree, orphans, top hubs |
migration_sitemap_diff |
Old vs new sitemap with xhtml:link hreflang alternate parsing |
migration_sitemap_validate |
Concurrent GET (Range: bytes=0-0) + retry on 503/504 |
migration_redirects_plan |
Hybrid exact + rapidfuzz match with collision + self-redirect detection |
migration_export_redirects_nginx / _apache / _cloudflare |
301 rules in three formats (CF schema follows official Lists API) |
migration_googlebot_diff |
Mobile-first Googlebot UA vs user UA with 5 anti-FP guards (cf-mitigated, Vary, html.unescape, A/B threshold 30%, cache-bust double-fetch) |
migration_multi_bot_diff |
Same diff across Googlebot + Bingbot + user UAs |
migration_verify_googlebot_ip |
rDNS forward+backward validation of a server-log IP |
migration_prerender_check |
Pre-JS HTML SEO signals + shell-only soft-404 detection |
migration_prerender_vs_hydrated |
Playwright DOM diff with optional wrs_realistic=True (5s budget, console errors, hydration mismatches) |
migration_schema_parity_check |
JSON-LD types + critical props old vs new with parity_score |
migration_hreflang_cluster_audit |
Reciprocity + region-aware (es-ES ≠ es-MX) cross-domain support |
migration_indexation_recovery_monitor |
Post-launch GSC URL Inspection batch with INDEXED / DISCOVERED / SOFT_404 / BLOCKED classification |
migration_wayback_baseline |
Internet Archive CDX snapshot inventory as historical anchor |
migration_robots_audit |
robots.txt sitemaps declared, crawl-delay, disallow counts, sample-path verdicts |
migration_robots_diff |
Old vs new robots.txt × GSC ranked paths → newly_blocked flag |
⚡ Lighthouse / PageSpeed Insights (5)
| Tool | What it does |
|---|---|
lighthouse_audit |
Full PSI audit; surfaces lab + CrUX field data (loadingExperience) |
lighthouse_core_web_vitals |
LCP + CLS lab + INP/LCP/CLS field + TBT/FCP/SI/TTI under lab_metrics |
lighthouse_lcp_opportunities |
Filtered to LCP-relevant audits via auditRefs.relevantAudits |
lighthouse_compare_mobile_desktop |
Side-by-side perf score delta |
lighthouse_seo_score |
Lighthouse SEO category breakdown |
📊 CrUX — Chrome User Experience Report (3)
| Tool | What it does |
|---|---|
crux_current |
Latest 28-day p75 with auto-fallback URL→origin and scope tag |
crux_history |
Up to 25 weekly snapshots (~6 months) for one metric |
crux_compare_origins |
Side-by-side with winner / metric_unit / interpretation |
🏷️ Schema.org / JSON-LD (3)
| Tool | What it does |
|---|---|
schema_extract_url |
Extract JSON-LD + microdata + RDFa via extruct |
schema_validate_url |
Lightweight validation against 40+ types (incl. MedicalWebPage, ClaimReview, DefinedTerm, JobPosting) |
schema_suggest_for_page |
Heuristic schema suggestions by content_type |
📝 Indexing (5)
| Tool | What it does |
|---|---|
indexnow_generate_key |
32-char key for Bing/Yandex IndexNow |
indexnow_submit |
Submit a list of URLs |
indexnow_submit_sitemap |
Parse sitemap and submit (chunked at 10k URLs) |
google_indexing_publish |
URL_UPDATED notification (gated by GSC_ALLOW_DESTRUCTIVE=true) |
google_indexing_delete |
URL_DELETED notification (gated) |
📈 Trends / Suggest / Alerts (5)
| Tool | What it does |
|---|---|
google_suggest |
Google autocomplete for a seed query |
google_suggest_alphabet |
Seed × A-Z fan-out (~110 long-tails per seed) |
google_trends_keyword |
pytrends interest-over-time + region |
google_trends_related |
Top + rising related queries |
alerts_rss_parse |
Parse a Google Alerts RSS feed for brand mentions |
🤖 AEO — Answer Engine Optimisation (2)
| Tool | What it does |
|---|---|
aeo_llms_txt_check |
Verify /llms.txt + /llms-full.txt against the llmstxt.org spec |
aeo_ai_bots_robots_audit |
Per-bot allow/block for 16 AI/LLM crawlers (GPTBot, ClaudeBot, PerplexityBot, Google-Extended, CCBot, Bytespider, etc.) with vendor + purpose + docs |
🛠️ Meta (2)
| Tool | What it does |
|---|---|
get_capabilities |
Tool catalog + auth status for both GSC and GA4 (call this first) |
reauthenticate |
Reset in-process auth clients for both APIs |
The server exposes one read-only resource — accessible to the LLM without issuing a tool call:
google-seo://algorithm-updates— reference list of confirmed Google Search algorithm updates from 2023 to today (core updates, spam updates, helpful content, AI Overviews, etc.) with start/end dates and notes. Use this to correlategsc_traffic_dropsorga4_anomaliesfindings with industry-wide events. A drop on a core-update rollout date is much more likely Google-driven than site-specific.
The OSS landscape has split GSC and GA4 into separate MCPs. This one unifies them — and adds the cross-platform tools none of the others can deliver.
| You should use… | If you want… |
|---|---|
googleanalytics/google-analytics-mcp (Google official) |
The most polished raw GA4 API bridge maintained by Google. No GSC, no diagnostic logic, no provenance. |
AminForou/mcp-gsc (Python) |
The popular general-purpose GSC bridge. No GA4, no cross-platform. |
Suganthan-Mohanadasan/Suganthans-GSC-MCP (TypeScript) |
Maximum tool surface for GSC alone. No GA4 integration. |
saurabhsharma2u/search-console-mcp (TypeScript) |
The closest competitor: GSC + GA4 + Bing in 90+ tools. Pick if you live in TypeScript or need Bing. Z-score baseline contamination, fake funnel, no _meta provenance. |
surendranb/google-analytics-mcp (Python) |
Schema-discovery + row-count-probe for raw GA4. Single-property only, no diagnostic logic, no GSC. |
| This MCP | The unified Python SEO swiss-knife: GSC + GA4 + 6 unique cross-platform tools + Lighthouse/PSI + CrUX + Schema validation + IndexNow/Google Indexing + Google Trends/Suggest/Alerts + DataForSEO SERP + server log analysis + persistence. 100 tools, anti-hallucination provenance on every response, leave-one-out Z-score, real funnels, read-only-by-default. Pick if you want one binary to cover the whole audit-to-action loop without paying SaaS for what's just a free Google API away. |
This MCP started as a security-audited synthesis of seven open-source projects — credits at the bottom.
Why use this over the official Google MCP? The official MCP is a transport layer — wrappers around runReport and searchanalytics.query. Useful if you're building your own logic. This MCP adds 25+ tools with diagnostic logic baked in (classifications, scoring, anti-hallucination provenance) plus the 6 cross-platform tools that no single-source MCP can provide.
My site has very little traffic. Will this work? Yes, but lower the thresholds. For sites under 500 clicks/mo, set min_impressions=25-50 on gsc_quick_wins and min_clicks_prior=5-10 on gsc_traffic_drops. Below ~100 clicks/mo, the issue is usually content/indexation rather than optimization — pivot to gsc_inspect_url and gsc_list_sitemaps. Don't fabricate recommendations from empty data; the agent should explicitly say "insufficient sample for trend detection" when thresholds aren't met. See AGENTS.md §9 for full edge-case handling.
Does this measure AI Overview impact? Not directly — it's GSC + GA4 only, no SERP scraping. But the inference is reliable: pages ranking 1-3 with CTR collapse vs historical baseline are high-confidence AIO hits, especially when correlated with rollout dates from resource://algorithm-updates. See AGENTS.md §7 for the full inference protocol.
My GA4 has no ecommerce — will revenue tools still work? They run, but attributed_revenue will be 0 across the board. Don't conclude SEO has no ROI — the agent should flag this as a tracking-config gap and suggest ga4_search_schema(keyword='revenue') to confirm what fields are available.
What about ad-blocker traffic? Common in DACH/tech audiences. cross_traffic_health_check will flag tracking_gap when GA4 systematically under-reports vs GSC. Apply +15-30% upward correction on GA4 organic and disclose to the user.
Does it work with sc-domain: properties? Yes — but the site_url string must match exactly what gsc_list_sites returns. Don't manually compose https://example.com/ if the registered property is sc-domain:example.com — cross-platform path normalization handles both internally, but the join key requires the canonical form.
The MCP supports three credential methods. The cascade order in auth.py is:
ADC (GOOGLE_APPLICATION_CREDENTIALS) → Service Account (GOOGLE_SEO_SERVICE_ACCOUNT_FILE) → OAuth Desktop flow. Set the env var corresponding to the method you choose; leave the others unset so the cascade picks the right one.
Important about the cascade order: ADC is checked first because it's gcloud's default and exists implicitly on most developer machines. If you have BOTH
GOOGLE_APPLICATION_CREDENTIALS(or a stale~/.config/gcloud/application_default_credentials.json) AND a working SA, the cascade tries ADC first. Since v0.7.3 it auto-validates the ADC refresh token and silently falls through to the SA if revoked — but it's cleaner to unsetGOOGLE_APPLICATION_CREDENTIALSand remove the stale ADC file when running on Service Account, so you skip a network round-trip on every cold start.
Best for: anything that isn't an interactive desktop session. No browser, no token expiration, no consent screen issues, no reconsent every few weeks. This is the production-grade option.
Step 1 — Create the Service Account in Google Cloud Console
- Open
https://console.cloud.google.com/iam-admin/serviceaccounts?project=YOUR_PROJECT_ID(use any GCP project where you have Owner/Editor access — APIs are billed to this project). - Create Service Account:
- Name:
google-seo-mcp-readonly - Description: "Read-only access to GSC + GA4 for the SEO MCP"
- Grant no IAM roles (GSC and GA4 don't use IAM, they use their own ACLs).
- Name:
- Click the SA → Keys tab → Add Key → Create new key → JSON → Download.
- Move and lock down the file:
mkdir -p ~/.config/gcloud mv ~/Downloads/<your-project>-*.json ~/.config/gcloud/google-seo-mcp-sa.json chmod 600 ~/.config/gcloud/google-seo-mcp-sa.json
- Note the SA email — it has the format
google-seo-mcp-readonly@YOUR_PROJECT_ID.iam.gserviceaccount.com.
Step 2 — Enable the required APIs in your project (one-time)
gcloud services enable \
searchconsole.googleapis.com \
analyticsadmin.googleapis.com \
analyticsdata.googleapis.com \
--project=YOUR_PROJECT_ID(Or enable from the web: https://console.cloud.google.com/apis/library/searchconsole.googleapis.com?project=YOUR_PROJECT_ID.)
Step 3 — Grant the SA access to each GSC site and GA4 property
The SA email behaves like a user. Add it explicitly to each property you want to access:
-
GA4 (per property) —
https://analytics.google.com→ Admin → Property → Property Access Management → + (top right) → Add user → SA email → role Viewer. Works straight from the UI. -
Search Console (per site) —
https://search.google.com/search-console→ Settings (gear icon) → Users and permissions → Add user → paste the SA email → permission Restricted (read-only) or Full.
⚠️ Search Console + Service Account: Google's UI may reject the SA email with "user not found" or similar. This is a known Google quirk — GSC's user picker validates that the email belongs to a Google identity that has logged into Google products, which a service account never does. Three workarounds, in order of preference:
- Use a Domain-wide Delegation SA (Google Workspace customers only) — set up DwD in Cloud Console + Workspace admin and the SA can impersonate a real user.
- Add the SA as a verified property owner via DNS or HTML file verification — works for any account, but you need DNS/file access. Add a TXT record
google-site-verification=...keyed to the SA email. Once verified the SA gets owner-level access.- Use a different SA that is already verified for the property (common in agencies that have a "house" SA per client).
If you hit the
gsc_list_sitesempty-list problem after granting permissions and it persists for >5 minutes (GSC user grants take time to propagate), this is the most likely root cause. Check the Google Cloud documentation on SA + GSC for the exact verification flow.
For multi-client agencies, this is one-time per client. Subsequent property additions just need this single email added.
Step 4 — Configure the env var
In ~/.claude.json (or your MCP client config), set:
{
"mcpServers": {
"google-seo-mcp": {
"type": "stdio",
"command": "google-seo-mcp",
"env": {
"GOOGLE_SEO_SERVICE_ACCOUNT_FILE": "/Users/YOU/.config/gcloud/google-seo-mcp-sa.json"
}
}
}
}Or via shell:
export GOOGLE_SEO_SERVICE_ACCOUNT_FILE=~/.config/gcloud/google-seo-mcp-sa.jsonStep 5 — Restart your MCP client and verify
Quit the client fully (Cmd+Q in Claude Code, not just close window) and reopen. Then:
Run gsc_list_sites and ga4_list_properties.
You should see all properties where the SA email was granted access.
Important: if you also have
GOOGLE_APPLICATION_CREDENTIALSset or a stale~/.config/gcloud/application_default_credentials.jsonfile, the cascade tries ADC first and tries to refresh it on every cold start. Since v0.7.3 it auto-falls-through to the SA if the ADC refresh token is revoked, but the cleaner setup is to unsetGOOGLE_APPLICATION_CREDENTIALSand rename the stale ADC file:mv ~/.config/gcloud/application_default_credentials.json{,.bak}. That skips the network round-trip entirely.
Best for: a single developer on a desktop with a working browser and an OAuth client that's either fully verified by Google or whose consent screen lists your email as test user.
gcloud auth application-default login \
--scopes=https://www.googleapis.com/auth/webmasters.readonly,https://www.googleapis.com/auth/analytics.readonlyThe authenticated Google account must be a verified user in both:
- Search Console for each property (Property → Settings → Users and permissions)
- Analytics 4 for each property (Admin → Property → Property Access Management)
Known limitations:
- Requires interactive browser (won't work in Claude Code shells, agents, CI).
- OAuth client must be either fully App-Verified by Google or include your email as test user (max 100 lifetime test users in External + Testing mode).
- Refresh tokens get silently revoked by Google after periods of inactivity, security policy changes, or cross-machine reuse.
- Workspace-managed accounts often have admin policy that blocks unverified apps entirely with "This app is blocked".
If any of those bite you, switch to a Service Account — that's why it's the recommended default.
Best for: developers running the MCP from a desktop terminal with a Google Cloud project where they don't want to provision a SA.
export GOOGLE_SEO_OAUTH_CLIENT_FILE=/path/to/client_secret.jsonThe first call opens a browser; the token is cached at ~/Library/Application Support/google-seo-mcp/token.json (macOS) or the equivalent XDG_CONFIG_HOME location.
Note: requires the OAuth Desktop client to either be App-Verified by Google or to have your email registered as a test user in the OAuth consent screen. Otherwise you'll see "This app is blocked" — see Troubleshooting.
Your ADC refresh token has been revoked by Google (common after periods of inactivity, security policy changes, or if you authenticated with the same client across multiple machines).
Since v0.7.3 this should auto-recover if you have a Service Account configured: the cascade detects the dead refresh token and silently falls through to GOOGLE_SEO_SERVICE_ACCOUNT_FILE. You'll see a warning in the logs. If you still see the raw invalid_grant error, it means there's no SA fallback configured — set one up.
Fix: switch to a Service Account (Service Account setup). It eliminates token expiration entirely. Don't waste time re-running gcloud auth application-default login — the underlying OAuth client may be flagged for blocking.
If you still want to retry ADC: gcloud auth application-default revoke && gcloud auth application-default login --scopes=...
Cleanest setup once you've migrated to SA: unset GOOGLE_APPLICATION_CREDENTIALS and rename the stale ADC file:
unset GOOGLE_APPLICATION_CREDENTIALS # in your shell config
mv ~/.config/gcloud/application_default_credentials.json{,.bak}This skips the ADC validation round-trip on every cold start.
Google blocks OAuth flows for clients that request sensitive scopes (webmasters, analytics.readonly) and are either unverified or whose verification has lapsed. Common with the default gcloud client and with custom OAuth Desktop clients in External + Testing consent mode.
Fix options (best to worst):
- Switch to Service Account — bypasses OAuth entirely. This is the recommended path for any production / agent / multi-client workflow.
- Add your email as test user — Cloud Console → APIs & Services → OAuth consent screen → Audience → Test users → add your email. Limited to 100 test users lifecycle, only works in External + Testing.
- Submit OAuth consent screen for verification — Google's official path, takes weeks, not worth it for self-hosted SEO tooling.
The gcloud auth application-default login --no-browser flow needs an interactive shell to paste the verification code. Claude Code, Cursor, agents, and CI shells are non-interactive — the prompt fails immediately.
Fix: do not use OAuth flow in non-interactive environments. Use a Service Account.
The authenticated identity (your Google account or the SA email) doesn't have access to any GA4 property.
Fix: open https://analytics.google.com → Admin → Property Access Management for each property, and explicitly grant the auth identity at least the Viewer role.
The auth identity has no access to any GSC site, OR you tried to add a Service Account email and Google Search Console rejected it.
Common case A — using ADC / OAuth: each site has its own ACL.
Fix: https://search.google.com/search-console → site → Settings → Users and permissions → Add user → grant Restricted or Full to the auth identity.
Common case B — using Service Account: GSC's "Add user" UI may reject the SA email with "user not found". This is a Google quirk: SA emails can't be added through the user picker because they've never logged into Google products. Three workarounds:
- Domain-wide Delegation (Google Workspace customers only) — set up DwD in Cloud Console + Workspace admin so the SA can impersonate a real user.
- Verify the SA as a property owner via DNS or HTML file:
- Add a
TXTrecordgoogle-site-verification=<token>to your domain. - The token is generated when you start the verification flow with the SA credentials, NOT through the GSC UI. Use the GSC Site Verification API.
- Once verified, the SA gets owner-level access automatically.
- Add a
- Reuse a SA already verified for the property (common in agencies — one "house" SA per client domain).
After granting permissions, GSC user grants can take 5-15 minutes to propagate. If gsc_list_sites is still empty after that, double-check using the GSC UI — log into https://search.google.com/search-console as the SA email (you can't, that's the point) OR check the property's "Users and permissions" list to see whether the SA email appears there.
Reference: Google docs on SA + GSC authorization.
The GCP project hosting the auth identity (the project where the SA was created, or the project tied to your ADC) doesn't have the required APIs enabled.
Fix:
gcloud services enable searchconsole.googleapis.com analyticsadmin.googleapis.com analyticsdata.googleapis.com --project=YOUR_PROJECT_IDOr enable each from the web console.
You rotated gcloud accounts or swapped the SA file but the MCP keeps returning data from the previous identity.
Fix: this should NOT happen since v0.7.1 — the auth singletons detect a credentials fingerprint change and rebuild the clients automatically. If you observe this, file an issue. As a workaround, call the reauthenticate tool from your agent, or restart the MCP client.
If you completed step "add test user" but Google still blocks the flow, your client may be in Production mode without verification. Check Cloud Console → APIs & Services → OAuth consent screen → Publishing status. Either:
- Switch to Testing + ensure test user is added, or
- Use a Service Account (no consent screen involved).
Claude Code
claude mcp add google-seo-mcp -- $(which google-seo-mcp)Or manually in ~/.claude.json:
{
"mcpServers": {
"google-seo-mcp": {
"type": "stdio",
"command": "google-seo-mcp"
}
}
}Claude Desktop
Add to ~/Library/Application Support/Claude/claude_desktop_config.json:
{
"mcpServers": {
"google-seo-mcp": {
"command": "google-seo-mcp"
}
}
}Cursor / Windsurf / Zed
{
"google-seo-mcp": { "command": "google-seo-mcp" }
}| Var | Default | Purpose |
|---|---|---|
GOOGLE_APPLICATION_CREDENTIALS |
gcloud ADC default | ADC file path. Set this only if you're explicitly using ADC. The cascade tries it first, so leaving a stale value here makes every cold start slower (one extra refresh round-trip) and triggers a warning log. |
GOOGLE_SEO_OAUTH_CLIENT_FILE |
— | Desktop OAuth client JSON. Triggers interactive browser flow on first call. |
GOOGLE_SEO_SERVICE_ACCOUNT_FILE |
— | Service account key path. Recommended default. Self-issued JWTs, no expiration, works headless. |
GOOGLE_PROJECT_ID |
— | Optional, fingerprint-only. Used exclusively as part of the credentials fingerprint that detects multi-tenant rotation between calls. It does NOT route requests to a specific GCP project — the actual project is inferred from the active credential (the SA's own JSON, or the ADC's quota_project_id). Leaving it empty is fine. Setting it wrong does NOT break anything (the credential still works); it just slightly degrades the fingerprint's ability to detect account rotation. If you swap your SA file to one in a different GCP project, the fingerprint changes anyway via the file's mtime. |
GOOGLE_SEO_ALLOW_PRIVATE_FETCH |
false |
If true, the SSRF guard accepts RFC1918 / loopback / cloud-metadata URLs in fetch_url family tools. Use only on trusted intranets. |
GSC_ALLOW_DESTRUCTIVE |
false |
Enables sitemap submission and write-scope OAuth. |
GSC_CTR_BENCHMARKS |
conservative defaults | Comma-separated 10 floats overriding per-position expected CTR. |
PAGESPEED_API_KEY |
— | API key for Lighthouse/PSI and CrUX. Without it, the anonymous quota is shared and frequently 429-throttled. Create one at console.cloud.google.com — enable PageSpeed Insights API + Chrome UX Report API, then create an API key restricted to those two services. The same key works for both. |
CRUX_API_KEY |
(falls back to PAGESPEED_API_KEY) |
Alternative if you prefer separate keys. |
DATAFORSEO_LOGIN |
— | Required only by the serp_* tools. Sign up at dataforseo.com (pay-as-you-go, ~$0.0006 per Live SERP call). Tools fail gracefully (no exception) when credentials absent — they return {"error": "credentials_missing", "fix": ...}. |
DATAFORSEO_PASSWORD |
— | Pair with DATAFORSEO_LOGIN. Store in macOS Keychain or your secrets manager rather than .env files. |
GOOGLE_SEO_HISTORY_DIR |
~/.google-seo-mcp/history |
Filesystem location where history_save_snapshot writes per-client snapshots. Override if you want them on a synced drive (iCloud, Dropbox) for cross-device continuity. |
GOOGLE_SEO_LOG_LEVEL |
INFO |
Python log level. |
- One install, one auth, both APIs. Single OAuth flow requests both
webmasters.readonlyandanalytics.readonly. No double setup. - Read-only. Destructive operations behind explicit
GSC_ALLOW_DESTRUCTIVEflag. - Provenance always included. Every response is wrapped in
{"data": ..., "_meta": {source, site_url|property, period, fetched_at}}. Cross-platform tools include both site_url AND property AND separate periods (GSC and GA4 have different lags). - Diagnoses, not data dumps. All intelligence tools classify findings rather than handing the LLM thousands of rows.
- Anti-context-blowup. GA4 has
estimate_query_sizeandsearch_ga4_schemaso the LLM can size queries cheaply before fetching. - Multi-property by parameter, not env. Pass any
property_idto any tool — no need to restart with a different property. - Statistical rigor. Leave-one-out Z baselines, monotonic-window decay detection, real funnels via sequential filters.
If you're using this MCP through Claude Code / Cursor / Windsurf and want to get the most out of it, drop AGENTS.md into your agent's context. It's a synthesis of three expert perspectives (technical SEO, local SEO + SERP intent, marketing/growth analytics) into a single operating manual:
- 25+ playbooks mapping common user questions to exact tool sequences
- 3 decision trees (drop diagnosis, ROI/prioritization, local-specific)
- 12 anti-patterns to avoid hallucinations and context blowup
- Argument cookbook tuned by site profile (small/medium/enterprise/local)
- Inference workarounds for what the MCP doesn't measure directly (AI Overview impact, brand vs non-brand, featured snippets)
- 3 ready-to-paste reporting templates (weekly digest, monthly CMO report, agency multi-client dashboard)
- Edge cases for small sites, multi-language, ad-blocker-heavy traffic, GA4 misconfig, holidays
The file is designed to be pasted directly into the system prompt or attached as a context document.
Three layers of tests, run them in order on any new client setup:
.venv/bin/pytest tests/ -q103 tests covering auth fingerprint detection, atomic token writes, _json_safe JSON-RPC coercion (datetime / Decimal / set / Path / numpy / bytes / weird types), float("") regression on empty GA4 metrics, KeyError regression on raw GSC subscripts, SSRF guard against RFC1918/loopback/cloud-metadata, XXE protection in sitemap parsing, equity URL normalisation, rapidfuzz no-match graceful path, JSON-LD detector edge cases (whitespace, extra attrs, multiline), etc.
.venv/bin/python scripts/smoke_test.pyProbes all six layers of the MCP (auth, admin discovery, reporting, intelligence, cross-platform, resource) using the first GSC site / GA4 property your account can see. It's the first thing to run after any code change or before promoting to a new client.
When onboarding a new client, run the full client probe to verify every tool category responds against their data:
.venv/bin/python scripts/client_probe.py \
--gsc-site "https://example.com/" \
--ga4-property "properties/123456789"Outputs a JSON report with one row per tool, status OK / SKIP (gated/optional) / FAIL (real bug to investigate), timing, and a summary of any actionable findings against the client's data (CTR opportunities, traffic drops, indexation gaps, AEO gaps, multi-tenant safety check).
This unified suite supersedes two previous repos that were split by API:
→ archived; install this instead.google-search-console-mcp-claude-code→ archived; install this instead.google-analytics-mcp-claude-code
If you're already using either of them, migrate by:
pipx uninstall gsc-seo-mcp(orga4-seo-mcp)pipx install git+https://github.com/mario-hernandez/google-seo-mcp-claude-code- Update your Claude config to point to the new
google-seo-mcpbinary.
Tool names are similar but prefixed now (gsc_*, ga4_*, cross_*) — your prompts may need light adjustments.
Independent implementation that synthesizes the strongest ideas from seven open-source projects, all of which were security-audited and found clean before being studied:
GSC sources:
AminForou/mcp-gsc— LLM-friendly errors, destructive-flag gating, capability discoverySuganthan-Mohanadasan/Suganthans-GSC-MCP— diagnostic SEO logic (quick-wins scoring, traffic-drop classification, content-decay 3-window)acamolese/google-search-console-mcp— three-tier credential cascadesurendranb/google-search-console-mcp— minimal FastMCP boilerplate
GA4 sources:
googleanalytics/google-analytics-mcp— proto-serialized examples in docstrings, inputSchema sanitizationsurendranb/google-analytics-mcp— schema cache + TF-IDF search, row-count probesaurabhsharma2u/search-console-mcp— diagnostic philosophy (improved here with statistical rigor)
If those projects fit your workflow better, use them — they're great in their own right.
- No telemetry. Zero outbound traffic to anything other than
googleapis.com/accounts.google.com/oauth2.googleapis.com. - No credentials in the repo.
.gitignoreexcludes*.jsonby default. - Read-only OAuth scopes unless
GSC_ALLOW_DESTRUCTIVE=true.
MIT — see LICENSE.



