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

@soyrageagency/seo-mcp

v1.0.0

Published

Chat with your site's SEO. A Model Context Protocol (MCP) server that lets any LLM crawl a site, audit on-page factors, read Google Search Console, measure Core Web Vitals and compare against competitors — read-only by default.

Downloads

42

Readme

SEO MCP Server

Chat with your site's SEO — with real data, not guesses.

Crawl a site, audit its on-page factors, read your actual Google Search Console performance, measure Core Web Vitals and compare against a competitor — from inside Claude, Cursor or any MCP client.

npm npm downloads CI Node MCP License: MIT

Designed, built & maintained by SoyRage Agency

⚡ No site, no credentials, no setup → try it in 30 seconds.


📑 Table of contents


⚡ Try it in 30 seconds

Demo mode serves a small fabricated site and a fabricated Search Console history, so you can see exactly what every tool returns before pointing it at anything real. No credentials, no network access.

{
  "mcpServers": {
    "seo": {
      "command": "npx",
      "args": ["-y", "@soyrageagency/seo-mcp"],
      "env": { "SEO_MCP_DEMO": "true" }
    }
  }
}

Restart your client and ask: “Audit my site.”

The fake site is deliberately imperfect — a 404 linked from an article, a page missing its H1, a title 83 characters long, images with no alt text, a page that quietly says noindex. A demo where everything is fine would teach you nothing about what the tool actually finds.


📦 Install

Nothing to clone or build. Add this to your MCP client's config (%APPDATA%\Claude\claude_desktop_config.json on Windows, ~/Library/Application Support/Claude/claude_desktop_config.json on macOS):

{
  "mcpServers": {
    "seo": {
      "command": "npx",
      "args": ["-y", "@soyrageagency/seo-mcp"],
      "env": {
        "SEO_MCP_SITE": "https://yoursite.com",

        // Optional, but this is where the real data is — see below
        "GSC_SERVICE_ACCOUNT_FILE": "/absolute/path/to/service-account.json",
        "GSC_SITE_URL": "https://yoursite.com/",

        // Optional: a free key, avoids PageSpeed rate limits
        "PAGESPEED_API_KEY": "AIza…"
      }
    }
  }
}

npx fetches the package on first run and keeps it current. A ready-to-edit copy lives in examples/claude_desktop_config.json.

Everything works with no credentials at all — you get the crawler and the on-page auditor, just not the real search data.


🧭 What it does

Most SEO tooling hands you a dashboard and leaves you to interpret it. This hands an LLM the underlying data, so you can ask a question in your own words and get an answer about your site.

Every screenshot below is the real, unedited output of the tool named in its title bar, generated by npm run shots against the demo site. If the tool's output changes, the screenshots are regenerated, not retouched.

Crawl a whole site

crawl_site finds what only a crawl can see: broken URLs and the page that links to them, duplicate titles, missing basics as a proportion, and pages buried too deep to be crawled often. link_graph, find_orphan_pages and find_duplicate_content go further into the same crawl.

Audit one page

audit_page reports every on-page signal for a single URL, with findings ranked by impact instead of listed flat.

Read real Search Console data

search_console_queries returns what Google actually recorded — not an estimate from a third-party index.

Compare two periods

search_console_compare answers the question people actually ask: is this going up or down, and because of what?

Measure Core Web Vitals

pagespeed keeps field data — what real Chrome users experienced, the part tied to ranking — separate from the lab simulation. Conflating the two is the standard mistake.

Compare against a competitor

compare_competitor puts the two pages side by side and then says what they do that you do not.

Draft a title and description

generate_meta gathers the evidence — current tags, structure, and the queries that already bring you impressions — and hands it over with the limits stated. It does not fill in a template, because a templated Buy X | Brand is worse than what the model writes from the page's real content.


🧩 Chrome extension — zero setup

The MCP server is for asking questions. The extension is for landing on a page and immediately knowing what you are looking at — no client, no credentials, no config. Install it, click the button, done.

It scores three things separately, because they fail independently: a page can rank beautifully and convert nothing.

SEO

A search-result preview showing what Google will actually display, measured in pixels rather than characters — Google truncates by rendered width, so a title of capitals is cut far sooner than one of narrow letters. Plus findings ranked by severity, the full heading outline, images without alt, anchor-text problems, structured data, lang and hreflang.

Leads

The contact details, extracted. Emails, phone numbers, WhatsApp, postal address, tax id and social profiles — from mailto:/tel: links, JSON-LD and a deliberately strict text scan. On a prospect's site, this is the lead.

Then the conversion audit: are there calls to action, and are any visible without scrolling? Is there a contact form? Any trust signals — testimonials, reviews, guarantees, case studies? Is a live chat available? And is anything being measured at all?

Stack

What a competitor is running tells you more about their strategy than their copy does. CMS, framework, and every analytics and ad pixel on the page. A Meta pixel means they are buying traffic. A session-recording tool means somebody is watching behaviour. No pixels at all means nobody is measuring anything — which is a finding in itself.

Two things the crawler cannot see

The extension is not a smaller copy of the server. It runs in a real browser:

  • The rendered page. If a site builds its content with JavaScript, an HTTP fetch gets an empty shell. The extension reads the DOM you are looking at. Comparing the two is often the finding.
  • Real Core Web Vitals for that load — the Largest Contentful Paint, TTFB and bytes transferred your browser measured, not a simulation. Plus images served at more than twice their displayed size, which costs bandwidth on every single visit and is invisible unless you look for it.

Copy puts the whole report on your clipboard as text — contact details, stack, measurements and every finding — ready for a doc, a ticket, or straight into Claude.

Install

Not on the Chrome Web Store yet, so load it unpacked — about thirty seconds:

  1. Download or clone this repo.
  2. Open chrome://extensions.
  3. Turn on Developer mode, top right.
  4. Load unpacked → select the extension folder.
  5. Pin it, then click it on any page.

What it can and cannot do

Worth being precise, because this is where comparable tools overpromise:

| | | | --- | --- | | ✅ Audit any page — yours, a client's, a competitor's | It reads what your browser already loaded. | | ✅ See JavaScript-rendered content | It reads the live DOM. | | ✅ Pull contact details for prospecting | From links, schema and a strict text scan. Only what the page publishes. | | ❌ Show heatmaps of your visitors | An extension only runs for whoever installed it. Your visitors will not install yours. Visitor analytics needs a snippet on the site — that is why Hotjar and Clarity work the way they do, and it is on the roadmap. |

Privacy

It requests activeTab, not host permissions: it can read a page only after you click the button, and only that tab. No content script runs on pages you have not asked about, nothing is sent anywhere, and there is no account or analytics. The report exists in the popup until you close it.


🧪 Worked examples

Real questions, and what the assistant does with them.

Calls crawl_site. Comes back with the 404 and the article that links to it — which is the part that turns a finding into a fix, because you now know where to edit. Also surfaces the page quietly serving noindex, which is the single most expensive thing to have wrong and the least visible.

Calls search_console_queries with dimension: "page". High impressions with a low CTR means Google shows you and people choose someone else — usually a snippet problem rather than a ranking problem, and one of the cheapest things to fix. Follow it with generate_meta on the worst offender.

The why-did-traffic-drop prompt. It compares the window against the one before it, then breaks the loss down by page, query, device and country, because a drop concentrated in one page and a drop spread evenly have completely different causes.

The prompt explicitly tells the assistant not to reach for "a Google update" unless the pattern actually fits one — broad, sudden, sitewide. It is the explanation that requires no evidence, which is exactly why it gets used when there is none.

The beat-competitor prompt: compare_competitor for the on-page differences, audit_page on each for the detail, search_console_queries filtered to your URL for what you are already close on.

It is also told to say when the gap looks off-page. Sending you off to rewrite headings when the real difference is 400 referring domains wastes your week, and this server cannot see links pointing at you.

list_sitemap for what the site says it publishes, crawl_site for what it actually links to. The gap between the two is where stale URLs, orphan pages and forgotten sections live.

The audit-site prompt against a site you do not own. It obeys robots.txt, identifies itself honestly and paces itself, so you get the picture without being rude to their server. Everything is read-only.


🔑 Connect Google Search Console

This is the step worth doing. Without it, everything about search intent is inference; with it, the assistant is working from what Google actually recorded.

A service account is the easier path — no browser flow, no token to refresh.

  1. In Google Cloud Console, create a project and enable the Google Search Console API.
  2. Create a service account and download its JSON key.
  3. Open the JSON and copy the client_email — it looks like [email protected].
  4. In Search Console → your property → Settings → Users and permissions → Add user, paste that email and give it Full or Restricted access.
  5. Point the server at the key:
"env": {
  "GSC_SERVICE_ACCOUNT_FILE": "/absolute/path/to/key.json",
  "GSC_SITE_URL": "https://yoursite.com/"
}

Then ask: “What did we rank for last month, and what moved?”

"env": {
  "GSC_CLIENT_ID": "…apps.googleusercontent.com",
  "GSC_CLIENT_SECRET": "…",
  "GSC_REFRESH_TOKEN": "…",
  "GSC_SITE_URL": "https://yoursite.com/"
}

If Search Console is not configured, its tools do not register at all — and the server says why in its startup log. A tool that can only fail is worse than one that is not there.


🧰 Tool reference

| Tool | What it does | | --- | --- | | crawl_site | Crawl and report site-wide problems: broken URLs with their referrers, duplicate titles and descriptions, missing basics, crawl depth, then the worst individual findings. | | list_sitemap | The URLs a site declares, following the sitemap index. Often revealing on its own — what a site publishes is frequently not what it links to. | | audit_page | Every on-page signal for one URL, ranked by impact. showOutline adds the full heading structure. | | compare_competitor | Your page and a competitor's, field by field, ending in what they do that you do not. | | search_console_queries | Real performance by query, page, country, device or date. Filterable to a single URL. | | search_console_compare | A window against the one before it: what gained, what lost ground, what dropped out entirely. | | list_search_console_sites | Which properties your credentials can read, and at what permission level. | | pagespeed | Core Web Vitals, field data separated from the lab simulation. | | generate_meta | Assembles the evidence for a new title and description — current tags, structure, and the queries that already work. | | link_graph | PageRank over the internal links: where authority accumulates, which important pages are starved of it, which minor pages hoard it. | | find_orphan_pages | Indexable pages nothing links to — reachable only from the sitemap, crawled rarely, invisible page by page. | | find_duplicate_content | Pages saying the same thing in different words, by exact phrase overlap with site boilerplate excluded. | | validate_structured_data | Schema checked against what Google requires for the rich result each type is for, not merely listed. | | check_hreflang | Reciprocity, self-reference and valid codes across the crawl — where hreflang actually breaks. | | find_broken_links | Outbound links that no longer resolve, with the pages that link to them. | | serp_preview | The result as Google will render it, measured in pixels.Try a different title without editing the page. | | about | Version, configuration and which capabilities loaded. Start here when a tool is missing. |


💬 Guided workflows (prompts & resources)

Tools answer a question you already knew how to ask. SEO is a field where most people do not know which question to ask — so this server also ships MCP prompts, which your client lists for you.

| Prompt | What it does | | --- | --- | | audit-site | Full sweep, ending in the three things to fix first with what each is plausibly worth. Narrow it with technical, content or performance. | | why-did-traffic-drop | Works out what actually changed from real Search Console data. | | improve-page | One URL end to end: what it has, what it already ranks for, how fast it is, then a specific edit list. | | beat-competitor | Why their page outranks yours — and honest about what is not visible from here. |

Each prompt names the tools to call, the order, and what a good answer looks like: verdict first, findings ranked, no padding, and ground every claim in tool output. An SEO audit full of confidently invented numbers is worse than no audit, so the instruction is explicit rather than hoped for.

Two resources are exposed as attachable context:

  • seo://server/capabilities — what is loaded and how it is configured.
  • seo://reference/checklist — every check and the threshold behind it, so the numbers in an audit can be questioned rather than taken on faith.

🌐 Run it over the network (HTTP)

By default an MCP server talks over stdio: your client starts it as a child process on your machine. Set one variable and it serves Streamable HTTP instead, so one instance can serve a whole team:

SEO_MCP_HTTP=true \
SEO_MCP_HTTP_TOKEN="$(openssl rand -hex 32)" \
SEO_MCP_SITE=https://yoursite.com \
npx -y @soyrageagency/seo-mcp
{
  "mcpServers": {
    "seo": {
      "type": "http",
      "url": "http://10.0.0.5:8621/mcp",
      "headers": { "Authorization": "Bearer <the token you generated>" }
    }
  }
}

It binds loopback by default and warns loudly if you start it without a token. Anyone who can reach the port can crawl anything the server can reach and read your Search Console data, so keep it behind your VPN. GET /health needs no token, for container healthchecks.


🐳 Run it in Docker

docker run --rm -i \
  -e SEO_MCP_SITE=https://yoursite.com \
  -v /path/to/key.json:/keys/gsc.json:ro \
  -e GSC_SERVICE_ACCOUNT_FILE=/keys/gsc.json \
  ghcr.io/soyrageagency/seo-mcp

Keep -i and do not allocate a TTY: stdout is the JSON-RPC stream. To serve it over the network instead, add -p 8621:8621 -e SEO_MCP_HTTP=true -e SEO_MCP_HTTP_HOST=0.0.0.0 -e SEO_MCP_HTTP_TOKEN=….


⚙️ Configuration reference

Everything is settable by environment variable, by seo-mcp.config.json, or by a local .env — in that order of precedence.

| Variable | Default | What it does | | --- | --- | --- | | SEO_MCP_SITE | — | Default site, so tools do not need the URL every time. | | SEO_MCP_DEMO | false | Fabricated site and search data. No network access at all. | | SEO_MCP_MAX_PAGES | 200 | Hard cap on pages per crawl. | | SEO_MCP_MAX_DEPTH | 3 | How many links deep to follow. | | SEO_MCP_CONCURRENCY | 4 | Parallel requests. | | SEO_MCP_DELAY_MS | 200 | Pause between requests to the same host. | | SEO_MCP_TIMEOUT_MS | 15000 | Per-request timeout. | | SEO_MCP_RESPECT_ROBOTS | true | Obey robots.txt. Only turn this off for a site you control. | | SEO_MCP_USER_AGENT | SeoMcpServer/1.0 (+…) | How the crawler identifies itself. | | GSC_SITE_URL | SEO_MCP_SITE | Search Console property. Mind the trailing slash, or use sc-domain:. | | GSC_SERVICE_ACCOUNT_FILE | — | Path to a service-account JSON key. | | GSC_SERVICE_ACCOUNT_JSON | — | The key inline, for clients that only pass env vars. | | GSC_CLIENT_ID / GSC_CLIENT_SECRET / GSC_REFRESH_TOKEN | — | The OAuth alternative. | | PAGESPEED_API_KEY | — | Free key. Without it PageSpeed is rate-limited. | | SEO_MCP_HTTP | false | Serve over HTTP instead of stdio. | | SEO_MCP_HTTP_HOST / PORT / PATH / TOKEN | 127.0.0.1 / 8621 / /mcp / — | HTTP transport settings. | | SEO_MCP_PLUGINS | (all) | Allowlist: crawl,audit,searchconsole,pagespeed,meta,prompts. | | SEO_MCP_DISABLED_PLUGINS | — | Blocklist. Always wins over the allowlist. | | SEO_MCP_LOG_LEVEL | info | debug, info, warn, error. |


🛡️ How it behaves on other people's servers

Every crawl hits somebody's server, often one you do not own — a competitor, a client's staging box. So the crawler is deliberately conservative:

  • It identifies itself honestly. The default User-Agent names the tool and links to this repo, so a site owner can see who hit them and block it.
  • It obeys robots.txt, including wildcards, $ anchors, longest-match precedence and per-agent groups. There is an override, and it warns you every time you use it.
  • It honours Crawl-delay, and paces itself per host regardless.
  • It is bounded by page count and depth, and gives up rather than hanging.
  • It follows redirects by hand, up to five hops, because the chain itself is a finding worth reporting.

🚫 What it will not do

Saying this plainly is more useful than another feature list:

  • It will not change your site. No edits, no submissions, no settings.
  • It will not invent numbers. The server's instructions tell the assistant to ground every claim in tool output and to say when it is inferring.
  • It will not tell you a page is "optimised". It reports conventions and thresholds. Relevance and links matter more than any of them, and a page can pass every check here and rank nowhere.
  • It has no backlink data. Nothing here sees links pointing to you.
  • No telemetry. Nothing leaves your machine except the requests you asked for: the sites you crawl, and Google's APIs when you configure them.

🩺 Troubleshooting

It does not register without credentials. Run about — it reports whether Search Console is connected and lists which capabilities loaded. The server also logs the reason on startup.

Run list_search_console_sites to see exactly which properties your credentials can read. The usual cause is a mismatch in how the property is verified: https://example.com/ (with the trailing slash), https://www.example.com/ and sc-domain:example.com are three different properties to Google.

For a service account, also check you added its client_email as a user on the property itself — enabling the API is not enough.

Set PAGESPEED_API_KEY. The key is free and takes a minute to create; without one the quota is tiny.

Two common causes. Either robots.txt is blocking the crawler — the report lists those URLs under NOT CRAWLED with the reason — or the site renders its content with JavaScript, and a plain HTTP fetch sees an empty shell. Rendered crawling is on the roadmap.

SEO_MCP_DEMO is still true. Remove it, or set it to false.


🧪 Development

git clone https://github.com/soyrageagency/seo-mcp-server.git
cd seo-mcp-server
npm install
npm run build

npm test        # 25 unit tests: robots.txt parsing, extraction, audit rules
npm run smoke   # 42 checks end to end: boots the real server and drives it
npm run inspect # the MCP Inspector, against your local build

npm run shots   # regenerate the README screenshots from real output
npm run gif     # regenerate the demo GIF
npm run banner  # regenerate the sponsor banner and social card
npm run icons   # regenerate the extension icons

npm run extension:shots  # load the extension in a real Chrome and screenshot it
npm run extension:gif    # the extension demo GIF

npm run dev watches and rebuilds. TypeScript strict, with noUnusedLocals, noUnusedParameters, noImplicitReturns and noFallthroughCasesInSwitch. CI runs the whole suite on Linux and Windows.


🗺️ Roadmap

The full roadmap — shipped, next, and what is deliberately not planned — lives in ROADMAP.md.

The short version: self-hosted visitor analytics with click and scroll heatmaps (a cookieless snippet, since an extension can only ever see its own installer), JavaScript-rendered crawling, and audits that remember so the question becomes "what changed since last week". Backlink data and keyword-volume estimates are not planned, because doing either honestly needs an index this project will never have.


💙 Support the project


🖋️ Credits & License

Designed, built and maintained by SoyRage Agency — https://soyrage.es/

Part of a family of self-hosting tools you can talk to: Proxmox MCP · Docker MCP · RageVault

Released under the MIT License — use it, modify it, self-host it, ship it commercially.

If you build something on top of it, a link back to soyrage.es is appreciated but never required.

© 2026 SoyRage Agency — https://soyrage.es/ · Made with care in Valencia, Spain.