seo-console-mcp
Server Configuration
Describes the environment variables required to run the server.
| Name | Required | Description | Default |
|---|---|---|---|
| CF_API_TOKEN | No | Alternative Cloudflare API token environment variable. | |
| SEO_MCP_CRUX_KEY | No | API key for Chrome UX Report tools, falling back to SEO_MCP_PAGESPEED_KEY if allowed. | |
| SEO_MCP_ASC_KEY_ID | No | App Store Connect API key ID. | |
| GOOGLE_ADS_ENV_FILE | No | Path to an existing .env file containing any of the GOOGLE_ADS_* variables. | |
| SEO_MCP_CREDENTIALS | No | Path to service account key for Search Console tools, checked before GOOGLE_APPLICATION_CREDENTIALS. | |
| SEO_MCP_PLAY_BUCKET | No | Google Play reporting bucket, e.g. gs://pubsite_prod_... or bare name. | |
| CLOUDFLARE_API_TOKEN | No | Cloudflare API token for domain verification, scoped to Zone.DNS:Edit. | |
| GOOGLE_ADS_CLIENT_ID | No | Google Ads OAuth client ID. | |
| SEO_MCP_ASC_KEY_PATH | No | Path to the App Store Connect .p8 private key. | |
| SEO_MCP_INDEXNOW_KEY | No | IndexNow key for submitting URLs, also can be passed per tool call. | |
| SEO_MCP_SNAPSHOT_DIR | No | Directory where snapshot documents are stored, defaulting to ~/.config/seo-mcp/snapshots. | ~/.config/seo-mcp/snapshots |
| SEO_MCP_ASC_ISSUER_ID | No | App Store Connect issuer ID (required for team keys). | |
| SEO_MCP_PAGESPEED_KEY | No | Optional API key for higher PageSpeed Insights quota, passed via setup wizard --pagespeed-key or set manually. | |
| GOOGLE_ADS_API_VERSION | No | Overrides the default Google Ads API version. | |
| GOOGLE_ADS_CUSTOMER_ID | No | Google Ads customer ID (dashes optional). | |
| GOOGLE_ADS_CLIENT_SECRET | No | Google Ads OAuth client secret. | |
| GOOGLE_ADS_REFRESH_TOKEN | No | Google Ads OAuth refresh token. | |
| SEO_MCP_PLAY_CREDENTIALS | No | Path to a service account key with access to Play reporting, falling back to GOOGLE_APPLICATION_CREDENTIALS. | |
| SEO_MCP_ASC_VENDOR_NUMBER | No | App Store Connect vendor number for Sales and Trends reports. | |
| GOOGLE_ADS_DEVELOPER_TOKEN | No | Google Ads developer token. | |
| SEO_MCP_ALLOW_PRIVATE_HOSTS | No | Set to '1' to allow auditing private or internal hosts; otherwise such hosts are refused. | |
| GOOGLE_ADS_CLIENT_SECRET_PATH | No | Path to the OAuth client JSON from Google Cloud, which supplies the client ID and secret. | |
| GOOGLE_APPLICATION_CREDENTIALS | No | Standard Google Application Credentials path, used for Search Console and Play tools. |
Instructions
Guidance the server publishes about itself, which clients place ahead of the tool catalog so the model reads it before choosing anything.
This server publishes no instructions, or was last inspected before Glama recorded them.
Capabilities
Features and capabilities supported by this server
Protocol revision2025-11-25
| Capability | Details |
|---|---|
| tools | {
"listChanged": true
} |
| prompts | {
"listChanged": true
} |
| resources | {
"listChanged": true
} |
Tools
Functions exposed to the LLM to take actions
| Name | Description |
|---|---|
| search_analyticsB | Query Google Search Console search analytics and return ranked clicks, impressions, CTR, and position |
| keyword_ideasA | Expand a seed with free Google Autocomplete suggestions and optionally cross-reference Search Console rankings; no extra API key needed |
| search_opportunitiesA | Find queries ranking just off page 1 with high impressions, the highest-ROI keywords to improve |
| compare_search_periodsB | Compare an analysis window with the preceding equal period to identify search gainers and losers |
| ctr_gapsA | Find high-impression queries or pages whose CTR trails peers at the same position for snippet rewrite prioritization |
| query_cannibalizationA | Find queries served by multiple pages to prioritize consolidation and internal-linking decisions |
| list_sitemapsA | List sitemaps submitted for a Google Search Console property |
| list_propertiesA | List Google Search Console properties the service account can access, with permission levels |
| submit_sitemapA | Submit a sitemap to Google Search Console and return its current state |
| delete_sitemapA | Remove a submitted sitemap from a Search Console property (write; supports dryRun) |
| inspect_urlA | Inspect a URL's Google index status, canonical selection, mobile usability, and rich results |
| index_coverageA | Check how many of a sitemap's pages are indexed by Google (bounded; respects URL Inspection quota) |
| request_recrawlC | Inspect URLs' Google index status and resubmit the covering sitemap for the ones not indexed, the supported bulk recrawl nudge (write; supports dryRun) |
| indexnow_submitA | Submit changed URLs in bulk to IndexNow search engines: Bing, Yandex, Naver, Seznam, Yep; not Google. Needs an IndexNow key hosted on the site at https:///.txt (write; supports dryRun) |
| pagespeedB | Run PageSpeed Insights for field Core Web Vitals, Lighthouse scores, and top opportunities |
| seo_auditA | Fetch and audit a web page's on-page SEO without Google credentials |
| audit_siteA | Audit the on-page SEO of up to N pages from a sitemap and roll up the most common issues across the site. Takes a sitemap URL rather than a Search Console property, and needs no Google credentials |
| server_versionA | Report which build of this server is answering, where it is running from, and whether it came out of an npx cache. Four values look like this one and are not: what npm calls latest, what the version range resolves to, what the plugin manifest declares, and what is actually running. Checking the command-line tool is not a substitute, since it is a separate process resolved separately. No credentials needed; read-only |
| wporg_pluginA | Look up a WordPress.org plugin's install base, downloads, ratings, and support stats by slug; public API, no credentials or API key needed |
| play_store_statsA | Read Google Play bulk reports for an app: active device installs and store-listing visitors and acquisitions by traffic source. installsDimension picks which installs breakdown is read (overview, country, language, device, os_version, carrier or app_version), include adds the ratings, crashes and reviews report families, and startDate with endDate reads every month the window touches instead of the single month in month. Reads the reporting bucket named by SEO_MCP_PLAY_BUCKET; read-only |
| app_store_listingA | Read an App Store listing's indexed fields per locale (name, subtitle, keywords) against Apple's character limits, plus promotional text, version state and star ratings. The ratings come from the public storefront lookup because App Store Connect exposes no aggregate rating; each entry names its source. Needs SEO_MCP_ASC_KEY_PATH, SEO_MCP_ASC_KEY_ID and SEO_MCP_ASC_ISSUER_ID; read-only |
| app_store_salesA | Read App Store Sales and Trends: units downloaded per day per territory per app, summarized by SKU. Needs SEO_MCP_ASC_VENDOR_NUMBER and a team key with Admin, Finance or Sales and Reports. A period with no sales is reported as an absence rather than an error. Read-only |
| play_vitalsA | Read Android vitals from the Play Developer Reporting API: crash rate, ANR rate, error counts and startup metrics, daily or hourly, with optional breakdowns. Reports how fresh the data actually is. Carries no acquisition or conversion data; use play_store_stats for that. Read-only |
| app_store_discoveryA | Read the App Store surfaces beyond the listing text: search keywords, app tags, product page optimization experiments, custom product pages, in-app events, territory availability and review summarizations. A resource this key cannot read is reported as unavailable rather than as empty. Read-only |
| app_store_reviewsA | Read App Store customer reviews and your responses, filtered by rating or storefront. Reports the mean and star split of the reviews actually fetched, which is not the app's lifetime rating; App Store Connect exposes no aggregate rating resource. Read-only |
| crux_field_dataA | Read real-user Core Web Vitals for an origin or URL from the Chrome UX Report: the current 28-day field record with p75s and histograms. Field data, not a lab test; PageSpeed's own field block is being discontinued. Needs SEO_MCP_CRUX_KEY or a PageSpeed key allowed to call the Chrome UX Report API; read-only |
| crux_historyA | Read the Chrome UX Report weekly history for an origin or URL, roughly six months of 28-day rolling windows, so a field metric can be seen trending rather than as one point. Read-only |
| ads_campaignsA | Read Google Ads campaigns: status, daily budget, impressions, clicks, cost and conversions over a window. Needs GOOGLE_ADS_DEVELOPER_TOKEN, an OAuth client and a refresh token; read-only |
| ads_keywordsA | Read every Google Ads keyword with its effective CPC bid, approval and serving status, and metrics. Returns every row rather than a first page, which is how a count taken from the console goes wrong; read-only |
| ads_adsA | Read Google Ads ads with ad strength, policy approval status, serving status and metrics; read-only |
| ads_ad_copyA | Read what a Google Ads ad actually says: every headline and description with its pinning and Google's performance label, the display path, the final URLs, and the policy topics behind a limited or disapproved status rather than only the status word. Also reports headline text shared by more than one ad, since two ads in an ad group with the same headlines are not testing anything against each other. Assets such as sitelinks and promotions are not read here; read-only |
| ads_assetsA | Read the sitelinks, callouts, structured snippets, promotions, prices, call and image assets attached to the account, its campaigns and its ad groups, with what each one actually says rather than only its type and id. An account-level asset applies to every campaign, so it is listed even when one campaign is named: an ad that looks bare in ads_ad_copy may be serving with these beside it. Attached is not shown, and a level that cannot be read is reported as an error in place rather than as nothing attached; read-only |
| ads_queryA | Run an arbitrary GAQL SELECT against the Google Ads account for a question the shaped reads do not cover. GAQL has no statement other than SELECT, so this cannot change anything; read-only |
| ads_search_termsA | Read the queries that actually triggered an ad, with the keyword each one matched and its metrics. This is the paid equivalent of the Search Console query dimension. Google withholds terms too few people searched, so an absent term is unknown rather than absent; read-only |
| ads_negativesA | Read the negative keywords already in place, at campaign, ad group or shared-set level. A negative blocks traffic without leaving any record that it did, so this is what to check when a keyword stops serving and nothing looks wrong, and what to check before adding a term twice; read-only |
| ads_changesA | Read the Google Ads change history: what changed, when, which fields, by whom, and whether it came from a tool or from someone in the browser. Google keeps 30 days and at most 10,000 rows, so an empty result over a longer window is a limit rather than a finding. Filter on resourceType rather than on changed field names: a budget change reports amountMicros and says neither budget nor status. This is the audit trail for anything ads_update writes; read-only |
| ads_negatives_updateA | Add or remove negative keywords in a batch, enumerated one by one with no pattern form. Before adding, every proposed negative is checked against the campaign's own live keywords and the batch is refused if one would block traffic, because a wrong negative leaves no evidence anywhere: the traffic just stops. Dry run unless dryRun is false, and the terms are read back afterwards |
| ads_updateA | Change one Google Ads keyword bid, campaign daily budget, campaign status, ad status or keyword status. Pausing one keyword is its own kind because dropping a bid is not the same thing: the keyword stays eligible and goes on competing for the same budget. Spends money, so it is a dry run unless dryRun is false, it refuses a change that trips a guard unless confirm is true, and it re-reads the value after writing because an accepted request is not a stored value. Guards: more than three times the current amount, more than $25, or pausing something that is serving |
| ads_keyword_createA | Add one keyword to an ad group. This is the only tool here that creates rather than changes, and it is guarded differently for that reason: there is no current value to compare against, so it is a duplicate check instead. It refuses a keyword that already exists in the target ad group, including a removed one, since a removed criterion still holds the text and Google rejects the create with an error naming a resource the interface does not show. A copy elsewhere in the account trips a guard rather than refusing, because two copies compete for the same budget. EXACT by default; PHRASE and BROAD buy more than the text written and each trips a guard. Dry run unless dryRun is false, and the keyword is read back afterwards |
| ads_update_batchA | Change several Google Ads keyword bids, or several campaign daily budgets, in one call. It is a named list of pairs, not a rule applied to many things: each entry names one target and the value it should end at, and an entry that matches no row or more than one refuses the whole batch before anything is written. The sum is guarded as well as each entry, because separately reasonable raises are one large spend change together. Dry run unless dryRun is false, and every value is read back afterwards |
| list_snapshotsA | List the snapshot documents already in the snapshot directory, newest first: when each was taken, the window it covers, and how many properties, apps, packages and plugins it holds. This is what says whether there is an earlier snapshot to compare against and what to name as from and to; a file that does not parse is listed with its error rather than hidden; read-only |
| snapshotA | Capture four surfaces in one timestamped document: Search Console totals and top rows per property, App Store listings, Google Play installs and traffic, and WordPress.org stats. Core Web Vitals field data, Android vitals, App Store sales and App Store reviews are not captured. A surface that cannot be read is recorded as an error in place rather than omitted; list_snapshots names the documents already on disk to compare an earlier one against; read-only |
| compare_snapshotsA | Compare two snapshot documents and return the differences between them: clicks, impressions, positions, installs, ratings and locale counts. Reports arithmetic only, never whether a change was good or what caused it; read-only |
Prompts
Interactive templates invoked by user choice
| Name | Description |
|---|---|
| seo_triage | Triage an SEO property and produce an impact-ranked action plan |
| content_opportunities | Find content to create or improve from Search Console evidence |
| launch_seo_check | Assess technical and indexing readiness before a launch |
Resources
Contextual data attached and managed by the client
| Name | Description |
|---|---|
| properties | Live list of Google Search Console properties available to the service account |
TDQS
Scored across 43 tools
Most tools target distinct surfaces (Search Console, App Store, Play, Ads, CRUX), and the detailed descriptions separate near neighbors like ctr_gaps and search_opportunities. However, clusters such as seo_audit/audit_site, ads_ads/ads_ad_copy, and the six search-analytics tools still create real selection risk, so the set is not fully unambiguous.
The dominant pattern is snake_case verb_noun (list_sitemaps, submit_sitemap, ads_update, crux_field_data), and write tools consistently signal side effects. A notable minority uses noun-only or phrase names (keyword_ideas, search_opportunities, ctr_gaps, query_cannibalization, compare_search_periods), breaking the pattern enough to lower consistency.
43 tools is far beyond the 3–15 sweet spot for a single server. The count is inflated by parallel surfaces (Search Console, App Store, Play, Ads, snapshots) and numerous analytics slice tools, which makes the server feel like a bundled suite rather than a focused console.
The set covers an unusually wide lifecycle: sitemap management, index inspection, search analytics, page audits, app-store/Play reads, ads reads and guarded writes, plus snapshot comparison. Gaps are minor—no Search Console property add/remove and no ad campaign or ad-group creation—but most workflows reach a terminal action.