Skip to main content
Glama
README.md
# seo-audit-mcp

An MCP server that gives Claude (or any MCP client) the ability to audit a live
website's technical SEO: sitemap coverage, per-page issues, and redirect chains.

Ask in plain language — *"audit mortgagecalculatortools.com and tell me which
pages Google is never told about"* — and the model calls the tools, crawls the
site, and answers with specifics.

---

## The problem it solves

A site's `sitemap.xml` is how you tell Google which pages exist. When a page is
missing from it, nothing errors and nothing warns — the page simply never
accumulates impressions. Checking it by hand means diffing a filesystem
listing against an XML file, so in practice nobody does it.

### Case study: a 25-page gap that turned out to be correct

The first site this was pointed at had **125 HTML files on disk and 100 URLs in
its sitemap**. A 25-page gap — the kind of finding that gets written up as a
bug and assigned to someone.

One `sitemap_coverage` call surfaced the gap, and one `audit_urls` call on a
sample explained it: every one of the 25 carried `<meta name="robots"
content="noindex, follow">`. They were two deliberately de-indexed content
clusters, and the sitemap was **exactly right** to omit them. Verified against
the filesystem afterwards: 25 `noindex` pages on disk, the same 25 absent from
the sitemap, zero `noindex` pages wrongly included. Perfect consistency.

That is the useful result. A coverage number alone ("125 vs 100") reads as a
defect and buys a day of somebody's time; coverage plus per-page `noindex`
status closes the question in a minute. **This tool is as valuable for the
false alarms it kills as for the real gaps it finds** — which is why
`audit_urls` reports `noindex` per page rather than only counting URLs.

---

## Tools

| Tool | What it does |
|---|---|
| `fetch_sitemap` | Fetches `sitemap.xml`, follows sitemap-index nesting (max depth 3), returns every declared URL, deduped |
| `audit_urls` | Crawls URLs concurrently and reports per-page issues: broken status, redirect chains, missing/over-length `<title>` and meta description, missing or duplicate `<h1>`, missing canonical, `noindex`, thin content |
| `sitemap_coverage` | Diffs a sitemap against a list of URLs you know exist → what's **missing from the sitemap**, what's declared but **dead** |
| `check_redirects` | Traces redirect chains, flags multi-hop chains and chains ending in 4xx/5xx — use after a URL-structure change |

Every tool returns structured JSON with an `issues` list per page and an
aggregated `issue_summary`, so the model can reason over counts instead of
re-reading raw HTML.

---

## Install

```bash
pip install -e .
```

Requires Python 3.10+. Dependencies: `mcp>=2.0.0`, `httpx`.

## Connect it to Claude Code

Add to `.mcp.json` in your project (or `~/.claude.json` for global use):

```json
{
  "mcpServers": {
    "seo-audit": {
      "command": "python",
      "args": ["-m", "seo_audit_mcp"]
    }
  }
}
```

For Claude Desktop, the same block goes in `claude_desktop_config.json`.

Then just ask:

> Fetch the sitemap for https://example.com/sitemap.xml, audit the first 20
> URLs, and summarise the issues by frequency.

## Run it directly

```bash
python -m seo_audit_mcp        # stdio transport
```

---

## Design notes

Three decisions worth calling out, because they are the difference between a
demo and something you can point at a client's production site:

**Crawling is rate-limited by construction.** `fetch_many` runs behind an
`asyncio.Semaphore` capped at 16 concurrent requests, and every tool clamps
its input. A 500-URL sitemap without that ceiling would open 500 sockets at
once and read as an attack to the target host. The crawl is a cost the *target*
site pays, so the ceiling is not configurable upward from the tool surface.

**No fetch failure aborts a run.** `fetch_one` catches `httpx.HTTPError` and
records it on the returned `PageAudit` rather than raising. One dead host in a
200-URL crawl degrades one row instead of losing 199 good results.

**Parsing is deliberately lenient.** Real-world HTML is malformed often enough
that a strict parser raising mid-crawl is a liability. The extractors are
permissive regexes that return `None` rather than throw — but with the traps
handled: `<script>` and `<style>` bodies are stripped before word-counting and
heading extraction, so a `<h1>` inside a JS string literal is not counted as a
heading, and relative canonicals are resolved against the page URL.

`normalize_url` deliberately does **not** strip trailing slashes: `/a` and `/a/`
can be genuinely different pages, and collapsing them would hide duplicate-content
problems this tool exists to surface.

---

## Tests

```bash
pip install -e ".[dev]"
pytest
```

The suite is network-free — HTTP is exercised through `httpx.MockTransport`,
so it runs in CI and on a plane. It covers the parsing edge cases that bite in
production: script-embedded headings, namespace-less sitemaps, relative
canonicals, sitemap URLs that return a styled HTML 404 with a 200 status, and
non-HTML content types being wrongly reported as pages "missing a title".

---

## License

MIT

TDQS

A3.8/5.0

Scored across 4 tools

Disambiguation4/5

Each tool targets a distinct phase of an SEO audit: sitemap fetching, page-level auditing, sitemap coverage comparison, and redirect tracing. The main overlap is that audit_urls already reports redirect chains and broken status codes, which overlaps with check_redirects.

Naming Consistency4/5

Three tools follow a clear verb_noun pattern (fetch_sitemap, audit_urls, check_redirects), but sitemap_coverage is a noun_noun exception. The inconsistent name is still readable and does not create real confusion.

Tool Count5/5

Four tools is a well-scoped size for a focused SEO audit server. Each tool has a clear job, and there is no redundant filler or overwhelming number of endpoints.

Completeness3/5

The server covers sitemap parsing, on-page/technical issue auditing, sitemap coverage, and redirects, but it lacks a site-crawling or internal-link-discovery tool, which is needed to find URLs not listed in a sitemap. This is a notable gap for a full audit, though the core workflow is usable with an existing URL list.

Maintenance

ActivitySlowing
ResponsivenessNo issues