Know which blog posts to refresh, expand, merge, or kill - without guessing.
A Model Context Protocol (MCP) server that turns your scattered SEO and analytics data into one clear verdict per URL. Plug it into Claude, Cursor, or any MCP-aware client and ask: "Which three posts should I update this week?" - and get an answer backed by hard numbers.
seo-performance-mcp unifies post-publish signals from every channel you already pay for:
- Google Search Console - clicks, impressions, CTR, position, top queries
- Matomo or GA4 - visits, dwell time, bounce rate
- Microsoft Clarity - scroll depth, rage clicks, dead clicks
- AI citation tracking - which LLMs cite your URL today vs. last month
- Sitemap / CMS - publish dates, tags, word counts (any platform via XML sitemap; optional Ghost integration for richer metadata)
It then runs a deterministic rule engine over those signals and emits a verdict per URL:
refresh/expand/merge/kill/double_down/hold
with reason codes, evidence, and a 0-1 confidence score. Reporting only - the server never mutates your posts.
Most content teams have analytics in five tabs and a gut feeling. That's how good posts rot quietly, mediocre posts get over-promoted, and the obvious "rewrite this one" is invisible until traffic has already cratered.
This MCP closes the loop:
- One question, one URL in, one verdict out.
- Same logic across the whole cohort, so the ranking is comparable.
- All decisions traceable to numeric thresholds you can pin in
src/verdict/rules.ts. - AI clients (Claude, Cursor, MCP hosts) can drive the entire content audit in plain English.
- Content marketers running a blog of 50+ posts and tired of guessing what to refresh.
- SEO consultants doing audits who want a portable, deterministic scoring layer instead of bespoke spreadsheets.
- AI-first content teams wiring up rewrite agents - this MCP is the upstream signal layer.
- Indie publishers on Ghost, WordPress, Hugo, Astro, Next, Webflow, or any CMS that exposes a sitemap.
After one cohort run you have:
- A ranked table of every post with a verdict and confidence score.
- A markdown brief per "refresh" URL: numbers + top queries + suggested actions an editor (or a writing agent) can act on immediately.
- A list of "quick wins": queries sitting at positions 5-15 with below-expected CTR - the fastest title-rewrite wins on the property.
- A historical AI-citation diff: which LLMs cited you and stopped.
npx -y @automatelab/seo-performance-mcpIn a Claude, Claude Code, or Cursor MCP config:
{
"mcpServers": {
"seo-performance": {
"command": "npx",
"args": ["-y", "@automatelab/seo-performance-mcp"],
"env": {
"POSTS_SITEMAP_URL": "https://example.com/sitemap.xml",
"GSC_SERVICE_ACCOUNT_JSON": "<base64-encoded service-account JSON>",
"GSC_SITE_URL": "sc-domain:example.com",
"MATOMO_URL": "https://example.com/analytics",
"MATOMO_TOKEN": "...",
"MATOMO_SITE_ID": "1",
"GA4_PROPERTY_ID": "123456789",
"GA4_SERVICE_ACCOUNT_JSON": "<base64-encoded service-account JSON>",
"CLARITY_PROJECT_ID": "...",
"CLARITY_API_TOKEN": "...",
"CITATION_INTELLIGENCE_URL": "https://citation.example.com"
}
}
}
}Every env var is optional. Adapters that lack their env config skip their slice of the snapshot; the server still boots. The verdict engine works on whatever slices are present.
Point it at any site, no CMS plugin required. The post-discovery layer resolves in priority order:
POSTS_LIST- JSON array of{url, title?, published_at?, tags?, word_count?}. Use this when you already have a content index and want exact control.- Ghost Admin API - if both
GHOST_ADMIN_API_URLandGHOST_ADMIN_API_KEYare set, Ghost is used as a richer metadata source. Optional. - HTML extraction - per-URL
og:title,article:published_time, and JSON-LDdatePublishedare read live from the URL. - XML sitemap - set
POSTS_SITEMAP_URLto your sitemap (or sitemap index) and the server enumerates posts from<loc>+<lastmod>.
Most users only need POSTS_SITEMAP_URL. WordPress, Hugo, Astro, Next.js, Webflow, Framer, Wix, Squarespace, Notion-as-a-site, Substack-mirror sites all expose a sitemap by default.
To add a brand-new platform: nothing to build - just point POSTS_SITEMAP_URL at it.
| Tool | What it returns |
|---|---|
posts.list |
Posts with {url, title, age_days, tags} from sitemap, Ghost, or your POSTS_LIST. |
posts.snapshot |
Per-URL unified rollup for a 30/60/90-day window: GSC + Matomo + GA4 + Clarity + citations + meta. |
posts.decay_curve |
Weekly GSC clicks/impressions/position buckets + a decay/plateau/growth trend label. |
posts.verdict |
Verdict (refresh/expand/merge/kill/double_down/hold) + reason codes + 0-1 confidence. |
posts.refresh_brief |
Markdown brief for a human or downstream LLM editor: numbers, top queries, suggested actions. |
cohort.report |
Cohort verdict table sorted by priority + confidence. "Which three posts should I refresh this week?" |
posts.cite_loss |
LLM citations that dropped off for a given URL. Needs CITATION_INTELLIGENCE_URL. |
gsc.quick_wins |
(page, query) pairs at positions 5-15 with low CTR - fastest title-rewrite wins. |
Three thin routing files ship in the repo so the LLM in your client knows when to reach for these tools:
skills/seo-performance/SKILL.md- tool-routing skill. Drop into~/.claude/skills/seo-performance/(or.claude/skills/per project) to auto-load in Claude Code. Routes a single question to the right tool.skills/weekly-audit/SKILL.md- one-shot weekly audit playbook. Composesgsc.quick_wins+cohort.report+posts.cite_lossinto a deduped, cross-signal ranked digest with proposed edits per URL. Drop in alongside the routing skill.cursor/rules/seo-performance.mdc- copy to.cursor/rules/seo-performance.mdcin any Cursor workspace.
All optional. The MCP server works without them; they just shorten the "which tool do I call" round-trip.
The server exposes three prompts that bundle the playbook. Any MCP client (Claude Desktop, Claude Code, Cursor, Continue) can list and invoke them:
| Prompt | What it runs |
|---|---|
audit_cohort |
cohort.report on posts >=90d, then posts.refresh_brief per refresh/expand/merge row. The weekly audit. |
find_quick_wins |
gsc.quick_wins (positions 5-15) + per-URL posts.snapshot, then proposes verbatim-query meta_title rewrites. |
citation_loss_sweep |
posts.cite_loss per URL, refresh_brief for any with losses, targeted H1/lead phrasing recommendations. |
Deterministic, rule-based, traceable. Reason codes:
ctr_below_position_expectedposition_driftdecay_30d_over_30pct/decay_60d_over_50pctstagnant_no_clicksthin_content_low_dwellrising_impressions_low_ctr/rising_clicks_continue_investmentcitation_loss/citation_growthduplicate_or_cannibalizinghigh_bounce_low_scrollfresh_post_too_young
The mapping (reasons → verdict) and every threshold lives in src/verdict/rules.ts. Edit it, pin it in tests, ship your own rule book.
npm install
npm run dev # tsx src/index.ts
npm run build # tsc
npm test # vitestMIT