Scry MCP Server
Delivers alert notifications to a Discord channel through a channel webhook URL for the same set of monitoring rules.
Reads Search Console data via the Google Search Console API using a service account: pulls search analytics by page, query, device, country and search type, backfills 16 months of history, runs URL Inspection to flag non-indexed pages, canonical mismatches and coverage changes, tracks sitemap health and submits sitemaps.
Sends alert notifications to a Slack channel via an incoming webhook URL when rules fire (click drops, page traffic losses, keyword group slips, indexing and sitemap issues).
A companion WordPress plugin that surfaces Search Console numbers from Scry directly in the posts list and post editor.
This server does not currently have a deployable release on Glama. Follow the repository's setup instructions to install and configure it for your MCP client, including any required credentials and local dependencies.
Scry
Your Google Search Console data on your own Cloudflare account, with a dashboard, insights and an MCP server your AI tools can query.
Search Console keeps 16 months of data, then forgets it. Its interface answers one question at a time, and nothing you build on top of it can see last year next to last week without exports and spreadsheets.
Scry is one Cloudflare Worker that copies your Search Console data into D1 every six hours and keeps it. It gives you fast reports, analyses Search Console doesn't offer, and an API that Claude, ChatGPT, Codex or Cursor can call directly. It runs on your Cloudflare account, and no third party sees your data.
What you get
Dashboard. All your properties on one page, then per property: trend chart, pages, queries, devices, countries, and a detail page for any URL or query. Every report compares against the previous period or the same period last year. Filters cover search type, device, country and branded vs non-branded queries, and every table exports to CSV. Built on Tabler, with dark mode.
Insights. Eleven ready-made analyses:
Insight
What it finds
Striking distance
Queries at positions 4 to 20, ranked by the extra clicks position 3 would bring
CTR gaps
Top-10 queries whose CTR is under half of what your site usually gets at that position
Page and query drops
The biggest click losses vs the comparison period
Page and query gains
The biggest click gains
New and lost queries
Queries that appeared or disappeared
Cannibalization
Queries where two or more of your pages split the impressions
Content decay
Pages that lost clicks in four 28-day blocks in a row
Zero clicks
Pages with many impressions and no clicks
Alerts. Rules that watch for site-wide click drops, pages losing traffic, keyword groups slipping, pages dropping out of the index, sitemap errors and canonical mismatches. They're checked daily, each problem is reported once, and alerts go to email, Slack, Discord, Microsoft Teams or a signed webhook.
Annotations. Mark releases, migrations and incidents on the charts. Confirmed Google ranking updates are imported automatically from Google's Search Status Dashboard, and AI tools get the annotations along with the time series.
Content groups. Define site sections with URL patterns (
/blog/,/docs/*/api) and filter any report or insight to one section.Indexing. A daily URL Inspection budget works through your most valuable pages. Scry flags pages that aren't indexed, canonical mismatches and coverage changes, tracks sitemap health, and lets you submit sitemaps.
Keyword groups. Rank tracking from your own Search Console data, with exact or contains matching and no keyword limit.
AI access. An MCP server at
/mcp, a REST API at/apiand an OpenAPI 3.1 spec at/api/openapi.json. All three come from one tool registry.Multiple Google accounts. Upload as many service account keys as you need. They're stored encrypted, and each property uses the account that can read it.
White-label branding. Your own name, logo, dark-mode logo, favicon, accent colour and login message. Text colours adjust themselves to keep 7:1 contrast in both themes.
Weekly digest by email, plus a WordPress plugin that adds Search Console numbers to your posts list and editor.
Related MCP server: Google Search Console MCP Server
Deploy
Option A: Deploy to Cloudflare button (about 5 minutes)
Click the button above. Cloudflare copies this repository to your GitHub account, creates the D1 database, KV namespace and queue, and deploys the Worker.
Enter the two secrets when it asks:
ADMIN_PASSWORD: your dashboard password, 12 characters or more.SA_ENCRYPTION_KEY: any long random string.openssl rand -base64 48makes a good one. It encrypts the Google keys you upload, so keep it: if you change it, you'll have to upload the keys again.
Open the Worker URL (
https://scry.<your-subdomain>.workers.dev) and sign in with the password.Connect Google. Follow Create a Google service account below, then go to Settings → Service accounts and upload the JSON key.
Add your sites. Settings lists every property the service account can read. Add one and its 16-month backfill starts right away.
The database schema sets itself up on the first request, so there are no migrations to run.
Option B: Command line
git clone https://github.com/wpgaurav/scry.git && cd scry
npm install
npx wrangler login
npm run deploy # creates D1, KV and the queue on first deploy
npx wrangler secret put ADMIN_PASSWORD
openssl rand -base64 48 | npx wrangler secret put SA_ENCRYPTION_KEYThen carry on from step 3 above.
Create a Google service account
Scry reads Search Console as a Google service account. It doesn't use your personal Google login, so there's nothing to refresh or re-authorize.
In Google Cloud Console, create a project (or pick one).
Go to APIs & Services → Library, search for Google Search Console API and click Enable.
Go to IAM & Admin → Service accounts → Create service account. Any name works. Skip the optional role and access steps.
Open the new account, go to Keys → Add key → Create new key → JSON, and save the file.
In Search Console, open each property, go to Settings → Users and permissions → Add user and paste the service account's email (it ends in
iam.gserviceaccount.com). Choose Full to submit sitemaps from Scry, or Restricted if read-only is enough.
With the gcloud CLI, steps 1 to 4 look like this:
gcloud projects create my-scry-project
gcloud services enable searchconsole.googleapis.com --project my-scry-project
gcloud iam service-accounts create scry --project my-scry-project
gcloud iam service-accounts keys create scry-key.json \
--iam-account scry@my-scry-project.iam.gserviceaccount.comUpload scry-key.json in Settings, then delete the file.
Can I reuse Rank Math's or another plugin's Google connection? No. Plugins connect through their own OAuth app, and its client secret lives on the vendor's servers, so another app can't refresh those tokens. A service account is the supported way for server software to read Search Console.
Optional setup
Cloudflare Access instead of a password
Access gives you Google or one-time PIN sign-in with an email allow list.
In Zero Trust, go to Access → Applications and add a self-hosted application for your Scry hostname with an Allow policy for your email.
Add three more applications with the action Bypass and the include rule Everyone, for
<host>/api,<host>/mcpand<host>/healthz. The Worker still checks an API key on every request to those paths. The bypass only stops Access from blocking AI tools, which can't complete a browser login.Set the
ACCESS_TEAM_DOMAINvariable (https://yourteam.cloudflareaccess.com) and theACCESS_AUDsecret (the application's audience tag). You can also limit sign-ins withALLOWED_EMAILS.
Access and the password can run side by side. Remove ADMIN_PASSWORD to switch to Access only.
Alerts
Go to Alerts → Channels and add a destination:
Type | What to paste |
Slack | An incoming webhook URL ( |
Discord | A channel webhook URL ( |
Microsoft Teams | A Workflows URL from "Post to a channel when a webhook request is received" |
Webhook | Any https URL. Scry shows a signing secret once, when you add it |
One or more addresses. Uses the email settings from the weekly digest |
Then go to Alerts → Rules and pick what to watch:
Rule | Fires when | Default |
Site clicks dropped | Clicks in the last 7 days fell against the 7 days before | 20% |
A page lost clicks | A page with real traffic lost a share of its weekly clicks | 50%, at least 30 clicks |
Keyword group slipped | A keyword group's average position got worse | 2 positions |
Page dropped out of the index | An inspected page went from indexed to not indexed | – |
Sitemap has errors | Google reports errors on a submitted sitemap | – |
Google picked another canonical | Google ignored the canonical you declared on a page with traffic | 20 clicks |
Checks run daily at 05:30 UTC on final data, or on demand with Run checks now. A rule can watch one property or all of them. Each problem gets a fingerprint, so it alerts once rather than every day. Webhook requests carry X-Scry-Timestamp and X-Scry-Signature: sha256=<HMAC-SHA256 of "timestamp.body">, so you can verify they came from your Scry. Channel URLs are stored encrypted, and only a signed-in person can add or remove them.
Annotations
Add a note under any chart, or use the add_annotation tool or REST endpoint. A note can be one day or a date range, for one property or all of them. Confirmed Google ranking updates are imported daily from Google's Search Status Dashboard and shaded on the charts. Older updates are included so the full 16 months is covered. You can hide them for a property in Settings → Properties → Edit.
Content groups
Go to Content groups for a property and define a section with patterns, one per line:
/blog/matches paths that start with/blog/./docs/*/apiuses*as a wildcard.pricingmatches anywhere in the path.Exclude patterns remove pages, for example
/blog/tag/.
Preview shows the matching pages before you save. Once saved, pick the group in the filter bar, or pass group= to the API or MCP, to limit any report or insight to that section. The patterns also become regex filters for live queries, so country and device filters work too.
Branding
Go to Settings → Branding to set:
Name: shown in the sidebar, browser tab, emails and MCP clients.
Logo: plus an optional dark-mode version and a favicon. PNG, SVG, WebP or JPEG up to 512 KB, with ICO also accepted for the favicon. Turn off "Show the name next to the logo" if the name is already in the logo.
Accent colour: pick any colour. Scry derives the text, button and link shades, darkening or lightening them until they clear 7:1 contrast on every surface in light and dark mode. Error and decline colours stay red, whatever the brand colour.
Login page message: for example, "Search reporting for the Acme team."
Files are checked by their actual bytes, not their extension. SVGs with scripts, event handlers or external links are rejected, and every logo is served with a sandboxing content security policy. Images live in KV, and the text settings live in D1.
Custom domain
Uncomment routes in wrangler.jsonc and set your hostname, or add it in the dashboard under the Worker's Settings → Domains & Routes.
Weekly digest
Set EMAIL_API_URL, DIGEST_TO and DIGEST_FROM, and add the EMAIL_API_KEY secret. Any provider that accepts {from, to, subject, html} JSON with a Bearer key works, including Emailit and Resend. The sender must be on a domain you've verified with that provider. Settings → Preview weekly digest shows what will be sent.
More search types
Web is tracked by default. You can turn on Image, Video, News, Discover or Google News for each property in Settings → Properties → Edit.
Connect AI tools
Create a key in Settings → API keys. A read key can see all your data. A write key can also change settings, keyword groups and sitemaps, and start a sync. Only a signed-in person can manage keys or service accounts.
Claude Code
claude mcp add --transport http scry https://YOUR-HOST/mcp --header "Authorization: Bearer gsk_..."Claude Desktop, Cursor and other mcp.json clients
{
"mcpServers": {
"scry": { "type": "http", "url": "https://YOUR-HOST/mcp", "headers": { "Authorization": "Bearer gsk_..." } }
}
}Codex (~/.codex/config.toml)
[mcp_servers.scry]
url = "https://YOUR-HOST/mcp"
bearer_token_env_var = "SCRY_API_KEY"ChatGPT custom GPTs and other HTTP tool runners: import https://YOUR-HOST/api/openapi.json and set Bearer authentication.
Then ask questions like "Which example.com pages lost the most clicks this month, and what queries did they lose?" or "Find cannibalized queries on example.com and tell me which page should win."
Tools
Tool | What it does |
| Tracked properties, stored date coverage, and totals across all of them |
| Totals or the daily trend, with page, query, country, device and brand filters |
| Sortable tables with deltas. Pages for one query, or queries for one page |
| Everything about one URL or one query |
| Split by country, device or search appearance |
| Any of the 11 insights |
| A raw live Search Analytics query with any dimensions and filters |
| Index status for one URL or the whole property |
| Sitemap health and submission |
| Rank tracking |
| Property management |
| Site sections by URL pattern. Pass |
| Dated notes and Google ranking updates |
| Fired alerts |
| What to watch and where it goes |
| Sync coverage and a manual run |
Every tool is also a REST endpoint:
curl -H "Authorization: Bearer gsk_..." "https://YOUR-HOST/api/p/example.com/queries?days=28&sort=delta_clicks&order=asc"
curl -H "Authorization: Bearer gsk_..." "https://YOUR-HOST/api/p/example.com/insights/striking_distance?format=csv"A property can be given as its id, its exact Search Console URL (sc-domain:example.com) or a bare domain. CTR is a percentage. Position is the impression-weighted average, so a negative delta_position is an improvement.
How it works
Cron, every 6h ──► Queue ──► Search Console API ──► D1 (daily rows, 16 months)
│
Browser (password or Access) ──┐ ▼
AI tools (gsk_ keys) ──────────┴──► Worker ──► reports · insights · MCP · RESTFinal data is fetched for every day from 16 months ago up to 3 days ago. The last few days are stored as provisional and replaced once Google finalizes them.
Each day stores totals plus page, query, query×page, country, device and search appearance rows. Position is stored as
position × impressions, so averages stay correct across any date range.Country and device filters, and anything the stored tables can't answer, go to Google live and are cached for 6 hours. Stored reports are cached for 15 minutes.
The first backfill for a large site takes a while. Expect roughly an hour per busy property. The Settings page shows progress.
Limits worth knowing
Totals can run higher than query-level numbers. Google hides rare queries (anonymized queries) from query-level data, and Scry's query views say so where it matters. Property totals match Search Console.
The API returns the top rows for each day, not every long-tail pair. On very large sites, query×page detail stops at what Google returns. Only Search Console's BigQuery bulk export has every row.
URL Inspection is limited to 2,000 checks per property per day. Scry uses up to the daily budget you set (500 by default) and keeps headroom for manual checks.
Plan. Scry runs within Workers limits, but 16 months of data for several busy sites is most comfortable on Workers Paid ($5/month), which raises D1 storage and CPU time.
Security
Every request, including static files, goes through the Worker's sign-in check. The only exceptions are the login page, its stylesheet,
/healthzand/api/openapi.json, and none of them contain data.With no password, no Access and no API key, Scry refuses every request.
Password sessions are HMAC-signed, HttpOnly, Secure, SameSite=Lax cookies. Logins are limited to 10 attempts per IP every 15 minutes, and changing the password signs everyone out.
API keys are stored as SHA-256 hashes and can be revoked at any time.
Service account keys are checked against Google, then stored AES-GCM encrypted with
SA_ENCRYPTION_KEY. No endpoint ever returns them.Requests that change data and rely on a browser cookie must come from the same origin.
Report vulnerabilities through GitHub's private vulnerability reporting. See SECURITY.md.
Configuration reference
Name | Type | Required | Purpose |
| secret | unless using Access | Dashboard password, 12+ characters |
| secret | yes | Encrypts uploaded service account keys |
| var | for Access |
|
| secret | for Access | Access application audience tag |
| var | no | Comma-separated Access allow list |
| secret | no | One service account JSON key, as an alternative to uploading |
| var | no | Weekly digest |
| secret | no | Email provider key |
| var | no | Name shown in the dashboard and MCP clients |
WordPress plugin
wordpress-plugin/scry-insights adds a Search (28d) column to the posts and pages lists, plus a Search Console box in the editor with clicks, impressions, CTR, position, index status and top queries. Zip the folder, upload it in WordPress, then go to Settings → Scry Insights and enter your Scry URL, a read API key and the property.
Development
npm install
cp .dev.vars.example .dev.vars # add AUTH_MODE=none to skip sign-in on localhost
npm run seed:demo # synthetic demo data, no Google account needed
npm run dev # http://localhost:8787
npm testNew tools go in src/tools.ts. That one registry feeds the REST API, the MCP server and the OpenAPI spec. Schema changes go in a new numbered file in migrations/, followed by npm run migrations. See CONTRIBUTING.md.
Roadmap
Client portals with share links and scheduled PDF reports are next, then anomaly detection, keyword position history and MCP over OAuth. See ROADMAP.md and tell us what you'd use.
License
MIT © Gaurav Tiwari
This server cannot be deployed
Maintenance
Related MCP Connectors
Google Search Console, GA4 and Bing Webmaster data for SEO and AI-search work. 20 read-only tools.
Google Search Console for AI assistants: traffic, rankings, indexing and page checks. Read-only.
Google Analytics, Search Console, Ads, CRM, Cloudflare and more for your sites, from your own AI.
Read-only Google Ads, GA4 and Search Console data for Claude, with no Google Cloud setup.
Related MCP Servers
- AlicenseBqualityDmaintenanceConnects Google Search Console to AI assistants, enabling SEO data analysis through natural language conversations.32MIT
- AlicenseNot gradedqualityDmaintenanceConnects Google Search Console to AI assistants, enabling SEO data analysis via natural language conversations.MIT
- AlicenseAqualityDmaintenanceEnables AI assistants to analyze Google Search Console SEO data through natural language, including search analytics, URL inspection, sitemap management, and property management.21MIT
- AlicenseNot gradedqualityNot gradedmaintenanceEnables AI assistants such as ChatGPT and Claude to query Google Search Console data in natural language via four tools covering property listing, search analytics (clicks, impressions, CTR, position with filters), URL inspection, and sitemap status. It is self-hosted on Cloudflare Workers with OAuth-based Google authentication, requiring no service account or paid Cloudflare plan.MIT