Skip to main content
Glama

webaudit-mcp

MCP server that audits any public web page for tracking tags, duplicate pixels, page weight, header hygiene, and lab performance, from inside Claude Code or Claude Desktop.

Real smoke session: tracking_summary report for https://web.dev

Screenshot

What it shows

smoke-tracking-summary.png

Real npm run smoke -- https://web.dev session: the tools/list handshake and the full tracking_summary report (GTM container found, no consent management, no duplicates).

ssrf-refusal.png

The same smoke script pointed at http://localhost:8080: every tool returns a structured SSRF refusal instead of fetching, including the cloud metadata IP 169.254.169.254.

Both screenshots are captured from real sessions on 2026-07-19 against the current build. The second one is a selling point, not an error state: point any tool at a private, broken, or non-HTML URL and you get a structured, actionable error, never a crash or a hang.

Error: Refusing to fetch "localhost": local targets are blocked (SSRF protection).

Quickstart

Requires Node 20 or newer. From a clean clone:

git clone https://github.com/lpecom/webaudit-mcp.git webaudit-mcp
cd webaudit-mcp
npm install
npm run build   # compiles TypeScript into dist/
npm test        # 60 tests across 6 files, all offline, ~300 ms

Starting the server directly prints one line to stderr and then waits for an MCP client on stdio (stop it with Ctrl+C):

node dist/index.js
# webaudit-mcp running on stdio (7 tools registered)

Claude Code:

claude mcp add webaudit -- node /absolute/path/to/webaudit-mcp/dist/index.js

Claude Desktop (claude_desktop_config.json):

{
  "mcpServers": {
    "webaudit": {
      "command": "node",
      "args": ["/absolute/path/to/webaudit-mcp/dist/index.js"]
    }
  }
}

Then ask: "Audit https://example.com" or "What tracking is installed on web.dev?"

Optional, only needed for measure_performance (lab Core Web Vitals):

npx playwright install chromium

To see every tool fire against a live site without any MCP client:

npm run smoke -- https://web.dev

No API keys, no environment variables, no configuration files.

Related MCP server: mcp-uimax

Why I built this

I have spent years on both sides of this problem: running Google Ads, Meta, and Taboola campaigns, and building the tracking under them myself (GA4, GTM, server-side events). Broken tracking silently burns ad budgets: a duplicate GA4 config doubles every metric, a leftover GTM container re-fires conversions, and nobody notices until the numbers stop making sense. The tools to check for it are scattered across browser extensions, PageSpeed, and manual view-source archaeology.

This server puts the whole check behind one conversational interface, built the way I think an MCP server should be: strict input validation, SSRF-guarded fetching, structured errors instead of crashes, and honest labels on every number (lab vs field, measured vs estimated). The report is written for a developer and a media buyer: which analytics and pixel IDs are installed and where, whether anything double-fires, what the page weighs, and how the response headers look.

Tool reference

All 7 tools take a single url argument (absolute http/https URL) and return Markdown.

Tool

What it does

audit_page

One-shot report: tracking tags, duplicate risks, header hygiene, resource counts. Fast, no browser.

detect_tracking_tags

Static HTML scan for GA4, GTM, legacy UA, Google Ads, Meta Pixel, TikTok, LinkedIn Insight, Hotjar, Microsoft Clarity, server-side tagging, and consent platforms. Reports IDs and where each was found.

find_duplicate_tags

Flags double-initialized IDs, multiple GTM containers, GTM plus hardcoded GA4 double-fire patterns, duplicate pixel IDs, dead UA tags.

measure_performance

Headless-Chromium lab metrics: TTFB, FCP, LCP, CLS, load times, request count, transfer sizes. Clearly labeled lab data.

analyze_page_weight

Counts and byte sizes of scripts, styles, images, fonts, and iframes referenced in the HTML; largest third-party scripts.

check_headers

Caching, compression, HSTS, CSP, clickjacking, referrer policy. Informational grades with explanations, no synthetic scores.

tracking_summary

Plain-language briefing on the tracking setup, written for a media buyer: what is installed, what it means for campaigns, consent posture, issues to raise.

All example outputs below are real, captured 2026-07-19 against https://web.dev and trimmed for length.

audit_page

The one call to start with: combines the tracking scan, duplicate check, and header check in a single report.

# Audit of https://web.dev/

- HTTP status: 200
- HTML size: 130.7 KB
- Resources referenced in HTML: 14 (0 scripts, 4 stylesheets, 9 images, 0 fonts, 1 iframes; 5 third-party)

[tracking, duplicate, and header sections follow, same content as the
individual tools below, then pointers to the byte-level and lab tools]

detect_tracking_tags

Static scan with evidence: every finding names the vendor, the extracted ID, and the exact place in the HTML that triggered it.

# Tracking tags on https://web.dev/

| Vendor | ID | Found in | Evidence |
|---|---|---|---|
| Google Tag Manager | GTM-MZWCJPP | inline script | `[], "gtm": [{"id": "GTM-MZWCJPP", "purpose": 0}], ...` |

## Distinct IDs
- Google Tag Manager: GTM-MZWCJPP

## Consent signals
- None detected. If this site serves EU users with these tags firing pre-consent,
  that is a GDPR/ePrivacy problem.

_Static scan: 6 inline scripts and 0 external script tags inspected. Tags added
at runtime by GTM or a CMP are not visible to this scan._

find_duplicate_tags

Checks for the patterns that double-count: the same GA4 ID initialized twice, multiple GTM containers, a hardcoded GA4 tag next to a GTM container, multiple Meta pixel IDs, the gtag.js loader included more than once, and leftover Universal Analytics tags. web.dev is clean:

# Duplicate-tag check for https://web.dev/

No duplicate IDs or double-fire patterns found across 3 detected tag reference(s).

When something is found, each issue comes with a severity: HIGH (double-counting almost certain), MEDIUM (likely misconfiguration), INFO (verify manually).

measure_performance

Loads the page once in headless Chromium and reports what that load observed. Numbers vary run to run; the report says so.

# Lab performance metrics for https://web.dev/

**LAB DATA**: one headless-Chromium load from this machine. Not field data (no CrUX/RUM), no INP.

| Metric | Value |
|---|---|
| TTFB (time to first byte) | 495 ms |
| FCP (first contentful paint) | 764 ms |
| LCP (largest contentful paint) | 1.02 s |
| CLS (cumulative layout shift) | 0.088 |
| Load event | 2.34 s |
| Requests | 60 |
| Transferred (compressed) | 152.9 KB |

[transfer-by-initiator table and reference thresholds follow]

analyze_page_weight

Static resource inventory with real byte measurements (capped, guarded requests; up to 30 resources, HEAD first, GET fallback):

# Page weight for https://web.dev/

- HTML document: 130.7 KB
- Resources referenced: 14 (0 scripts, 4 stylesheets, 9 images, 0 fonts, 1 iframes)
- Measured 14 of 14 resources: 1.42 MB total

Note the honest gap between this and measure_performance above: the static scan sees 14 referenced resources, the browser load makes 60 requests. JavaScript loads the rest, and each report tells you which view you are looking at.

check_headers

# Header check for https://web.dev/ (HTTP 200)

| Check | Grade | Value | Note |
|---|---|---|---|
| Cache-Control | [OK] | `no-cache, must-revalidate` | Caching policy declared. |
| Compression (Content-Encoding) | [OK] | `gzip` | Response compressed with gzip. |
| HSTS (Strict-Transport-Security) | [OK] | `max-age=63072000; includeSubdomains; preload` | HSTS present with a solid max-age. |
| Referrer-Policy | [MISSING] | n/a | Default referrer behavior leaks full URLs to third parties on some browsers. |

_Grades are informational statements about header presence, not a security score._

tracking_summary

The same detection engine, rendered as a briefing for a non-developer. See the hero screenshot above for the full real output; the sections are: What is installed, What this means for media buying, Consent, Issues to raise.

How it works

src/
  index.ts          entry point: McpServer + stdio transport, registers the tools
  tools.ts          tool registration, zod input schemas, Markdown renderers,
                    error mapping (every failure becomes an isError result)
  lib/
    url-guard.ts    SSRF checks: scheme, hostname denylist, private IP ranges, DNS
    safe-fetch.ts   hardened fetch: timeout, byte cap, manual redirects, content type
    tracking.ts     static tag detection (regex + node-html-parser) and duplicate analysis
    page-weight.ts  resource extraction and capped byte measurement
    headers.ts      response-header checks
    performance.ts  Playwright Chromium lab metrics
    format.ts       byte/ms formatting and grade markers

SSRF protection is layered. url-guard runs two checks: a synchronous one (http/https only; localhost, .local, .internal, .home.arpa and similar hostnames refused; private, loopback, link-local, and CGNAT IP literals refused, for v4 and v6 including IPv4-mapped forms) and an asynchronous one that resolves the hostname with DNS and refuses if any returned address is private, so a public-looking hostname pointing at 192.168.x.x is also blocked. safe-fetch then follows redirects manually (max 5) and re-runs both checks on every hop, enforces a 15 s wall-clock timeout and a 5 MiB body cap, and rejects non-HTML content types for the HTML tools. Every outbound request in the codebase, including page-weight resource measurements, goes through this one client.

Static analysis vs browser analysis. Six of the seven tools parse the served HTML only: fast, no browser dependency, and honest about the boundary (every report footnotes that runtime-injected tags and JS-loaded resources are invisible to it). measure_performance is the one browser tool: it launches headless Chromium via Playwright, registers buffered PerformanceObservers before navigation, loads the page once, waits 3 s for LCP/CLS to settle, and reads the metrics out.

Graceful degradation without Chromium. Playwright is a devDependency and is imported dynamically only inside measure_performance. If the package or the browser binary is missing, the tool returns a structured error containing the exact fix (npx playwright install chromium) instead of crashing, and the other six tools are unaffected.

Tests

npm test runs 60 vitest tests in 6 files, entirely offline (network and DNS are mocked; nothing leaves the process):

  • url-guard.test.ts: scheme rejection, hostname denylist, private v4/v6 ranges including IPv4-mapped and CGNAT

  • safe-fetch.test.ts: timeouts, byte-cap truncation, manual redirect following and re-validation

  • tracking.test.ts: tag and consent detection plus duplicate analysis against synthetic fixtures (clean.html, duplicates.html, full-stack.html)

  • page-weight.test.ts: resource extraction, third-party heuristic (including co.uk-style domains), HEAD/GET measurement fallback

  • headers.test.ts: grades for present, missing, and weak headers

  • tools.test.ts: a real MCP client calling the real server over the SDK's in-memory transport, covering both success and error paths

npm run smoke -- <url> is the live counterpart: it drives the built stdio server with a real MCP client against a real site, on purpose.

Design decisions

  • Never throw from a handler. Every tool wraps its body in try/catch and converts UrlGuardError, FetchError, and PerformanceUnavailableError into MCP isError results whose message says what to do next.

  • Reports are Markdown a model can relay verbatim. The renderers produce tables and sections a client model can paste into an answer without rework, and each report ends with an honest footnote about what the scan could not see.

  • Bounded everything. 15 s request timeout, 5 MiB response cap, max 5 redirects, 30-resource measurement cap with concurrency 6, 10 s per-resource timeout, content-type enforcement for HTML tools. The numbers live as constants in safe-fetch.ts and page-weight.ts.

  • Honest labels on every number. Lab data is stamped LAB DATA, static scans say they are static, the third-party classifier is documented as a heuristic in the report itself.

  • Playwright stays a devDependency so the core install is three runtime dependencies (@modelcontextprotocol/sdk, node-html-parser, zod) and the browser tool degrades gracefully.

Built with the MCP TypeScript SDK (McpServer + registerTool, zod input schemas, stdio transport). Developed with Claude Code.

Limitations

  • Static scan. detect_tracking_tags, find_duplicate_tags, and tracking_summary read the served HTML only. Tags injected at runtime by GTM, a CMP after consent, or client-side routing are invisible to them. The reports say so explicitly.

  • Lab data, not field data. measure_performance is one headless load from your machine: no INP (needs real interaction), no CrUX percentiles, and numbers vary run to run. Treat it as a smoke check, not a Core Web Vitals verdict.

  • Single page scope. Every tool audits exactly the URL you give it. No crawling.

  • Third-party classification is a heuristic. Registrable-domain comparison without a full Public Suffix List; foo.co.uk style domains are handled, exotic suffixes may misclassify.

  • Transfer sizes can read 0 B. Cross-origin resources without Timing-Allow-Origin hide their size from the browser; page-weight measurements reflect what this client was served, which may differ from what your browser gets.

  • Bot protection. Sites behind aggressive bot walls (Cloudflare challenges and similar) may serve this tool different HTML than they serve a real browser.

License

MIT license, see LICENSE.

Install Server
A
license - permissive license
A
quality
B
maintenance

Maintenance

Maintainers
Response time
Release cycle
Releases (12mo)
Commit activity

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Servers

  • A
    license
    A
    quality
    D
    maintenance
    Core Web Vitals analysis powered by Lighthouse. Four tools: analyze a URL, compare two URLs, check against thresholds, or crawl an entire site. Works with Claude Code, Cursor, Windsurf, and any MCP-compatible AI tool.
    4
    11
    MIT
  • A
    license
    B
    quality
    C
    maintenance
    Frontend expert for Claude Code. Captures screenshots, runs Lighthouse + axe-core accessibility audits + code analysis, generates an expert review, and auto-fixes everything — 12 tools, completely free for Pro plan users.
    37
    55
    9
    MIT
  • F
    license
    A
    quality
    D
    maintenance
    Enables Claude Code to capture and analyze web page screenshots, responsive layouts, and page metadata using Puppeteer. It allows developers to perform visual UI inspections and compare designs across various viewports directly within the terminal.
    3
  • A
    license
    A
    quality
    A
    maintenance
    Enables auditing web pages for WCAG violations, applying deterministic fixes and PRs, all through MCP clients like Claude Desktop.
    7
    MIT

View all related MCP servers

Related MCP Connectors

  • Live SEO workflow tools for Claude Code, Codex, and AI agents.

  • Website QA for your coding agent: audit SEO, performance, security, accessibility over MCP.

  • Live tech-stack scan of any public site — vendors, account IDs, scripts, JSON-LD. 2 tools.

View all MCP Connectors

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/lpecom/webaudit-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server