posts_verdict is documented as reporting only, posts_snapshot as read-only with no third-party writes, and posts_refresh_brief exists precisely to hand the decision to a writer or a rewrite tool, which makes this an analysis layer rather than an editing one. The design consequence worth knowing before you wire it up is that every signal is optional and silently absent: a snapshot taken with only Search Console configured looks exactly like a complete answer, because missing sources are omitted rather than reported, so a verdict is only as broad as the variables you set.
A content-decay analysis server for a publishing site, with 9 tools that discover posts from a sitemap, pull a 30, 60 or 90-day snapshot across Search Console, Matomo, GA4, Clarity and AI-citation counts, classify the traffic curve, and emit a rule-based verdict with reason codes and a confidence score.
- Post discovery that does not assume a CMS: posts_list reads an XML sitemap, a JSON override list or the Ghost Admin API, and takes an explicit urls array to skip discovery altogether, returning url, title, published_at, age_days and tags filtered by min_age_days or since.
- One snapshot spanning whatever you have wired up: posts_snapshot pulls GSC clicks, impressions, CTR, position and top queries, Matomo visits and dwell, GA4 pageviews, Clarity scroll and rage clicks, and AI-citation counts for a single url, omitting any source whose variables are absent instead of failing the call.
- A trend classified rather than eyeballed: posts_decay_curve buckets GSC clicks, impressions and average position into roughly weekly windows over the last N weeks, 12 by default, and labels the result decay, plateau or growth.
- A verdict you can argue with: posts_verdict combines the snapshot and the decay curve into a verdict carrying deterministic reason codes and a 0-1 confidence score, and names src/verdict/rules.ts as the file where the reasons-to-verdict mapping can be inspected.
- The week's shortlist instead of one post at a time: cohort_report runs the same engine across a cohort filtered by tag or minimum age and returns a table sorted by verdict priority, then by confidence.
- A hand-off artefact rather than another dashboard: posts_refresh_brief emits a markdown brief with the verdict, its reasons, the raw numbers, top queries and suggested actions, for the refresh, expand, merge and double_down verdicts.
- Rank-gap scans on both engines, with the difference stated rather than glossed: gsc_quick_wins returns (page, query) pairs in positions 5-15 whose CTR sits below the curve expected at that position, while bing_quick_wins covers the same band query-level only, because Bing exposes no single page-and-query API.
- Citations tracked as losses, not just totals: posts_cite_loss returns the LLMs that previously cited a URL and no longer do, each with the prior query and the last-seen date, and optionally the URL that displaced it.
Nothing is required across the board — each signal source lights up only when its own variables are present, and posts_snapshot omits the rest. Discovery reads POSTS_SITEMAP_URL; Search Console needs GSC_SERVICE_ACCOUNT_JSON with GSC_SITE_URL; Matomo needs MATOMO_URL, MATOMO_TOKEN and MATOMO_SITE_ID; GA4 needs GA4_PROPERTY_ID with GA4_SERVICE_ACCOUNT_JSON; Clarity needs CLARITY_PROJECT_ID and CLARITY_API_TOKEN; posts_cite_loss returns an empty list until CITATION_INTELLIGENCE_URL points at a citation-intelligence endpoint; and bing_quick_wins wants a Bing Webmaster Tools API credential of its own.
One command — npx -y @automatelab/seo-performance-mcp
