npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

mcp-programmatic-seo

v0.1.0

Published

MCP server for auditing large programmatic SEO sites: near-duplicate clustering, keyword-phrasing detection, sitemap-vs-reality checks, and redirect tracing.

Readme

mcp-programmatic-seo

An MCP server for auditing large programmatic SEO sites — the kind with a page per keyword × location, where the question is not "is this page optimised" but "why are four thousand of these pages unindexed".

Existing SEO MCP servers audit one URL at a time: metas, schema, Core Web Vitals. This one works across a set of pages, because at scale the defects are relational — pages duplicating each other, sitemaps drifting from reality, redirect chains left behind by a consolidation.

Why

A site with 4,000 templated pages usually has a handful of problems that only show up when you compare pages to each other:

  • Phrasing duplicationbest-x-in-y, hire-x-in-y, x-services-in-y and need-x-in-y are one search with four URLs. Search engines resolve them to one intent, index one, and file the rest as duplicates.
  • Template thinness — a 1,700-word page that shares 90% of its text with its siblings carries less unique substance than a 600-word page that doesn't.
  • Sitemap drift — after any consolidation, the sitemap keeps advertising URLs that now redirect. Every one spends crawl budget to learn nothing.
  • Redirect chains — multi-hop chains, and temporary redirects (302/307) left where permanent ones (301/308) belong.

Install

npm install -g mcp-programmatic-seo

Claude Code

claude mcp add programmatic-seo -- npx -y mcp-programmatic-seo

Claude Desktop

{
  "mcpServers": {
    "programmatic-seo": {
      "command": "npx",
      "args": ["-y", "mcp-programmatic-seo"]
    }
  }
}

Tools

| Tool | Network | What it answers | |---|---|---| | analyze_url_patterns | none | What share of these URLs are phrasings of the same query? | | audit_sitemap | yes | Do the sitemap's URLs actually return 200? | | find_duplicate_content | yes | Which of these pages are near-duplicates, and how much unique text does each carry? | | compare_pages | yes | What exactly differs between these two pages? | | trace_redirects | yes | How many hops, what status codes, any loops? | | audit_canonicals | yes | Canonical present, self-referencing, cross-host? Any noindex? |

Start with analyze_url_patterns. It needs only a URL list — no fetching — and on a page-per-keyword site it is usually diagnostic on its own.

Worked example

Pointed at a real container-pool retailer running 6,209 sitemap URLs:

analyze_url_patterns  (90 URLs: 18 page types × 5 cities)

  distinct queries : 60
  clustered        : 45 of 90  (50%)

  worst cluster (5 URLs, one query):
    /pools/best-container-pools/dallas-tx
    /pools/container-pool/dallas-tx
    /pools/container-pools-near-me/dallas-tx
    /pools/container-pools/dallas-tx
    /pools/pool-containers/dallas-tx
audit_sitemap  (60 sampled from 6,209)

  healthy      : 31
  redirecting  : 29  (48.3%)

  308  /pools/container-swimming-pools/boca-raton-fl -> /pools/container-pools/boca-raton-fl
  308  /pools/container-pools-near-me/conroe-tx      -> /pools/container-pools/conroe-tx

The site had already consolidated roughly half its pages onto canonical URLs and left every retired URL in the sitemap. Three of the five URLs in that worst cluster are ones the site itself now redirects — the tool's read matched the owner's own judgement.

Interpreting the numbers

Similarity is Jaccard over 5-shingles (sliding windows of five consecutive words), not a word bag. Two templated pages share nearly every individual word; what separates them is word order.

| Similarity | Reading | |---|---| | < 0.35 | Genuinely different pages | | 0.35 – 0.50 | Shared template, meaningful unique content | | 0.50 – 0.70 | Thin — one page's worth of content across several URLs | | > 0.70 | Duplicates. Expect one to be indexed and the rest dropped |

Unique words per pagemeanWords × (1 - meanSimilarity) — is usually the more useful figure than raw word count. Doubling a page's length while holding its template constant raises word count and lowers unique share; only the latter tracks with whether the page gets kept.

Notes

  • Concurrency is capped (default 6, max 12). These tools point at whole sitemaps, and an unbounded fan-out is hard to distinguish from an attack. Keep it low against sites you do not own.
  • find_duplicate_content is pairwise, so it takes at most 60 URLs. Pass a targeted set of suspected siblings, not a whole site.
  • analyze_url_patterns will not guess that a domain word is noise. If your site treats "swimming" in container-swimming-pools as redundant, pass it in extraPrefixes — the tool declines to infer that on its own because on another site it could be the distinction that matters.

Development

npm install
npm test        # 33 unit tests, no network
npm run build

The pure logic (src/text.ts, src/slugs.ts) is tested offline; the network layer is thin by design so the parts worth testing don't need a server.

License

MIT