xSpeed Scan MCP: Speed-Test Any Site From Your AI Assistant
Connect one URL and your AI assistant can speed-test any public website, read every failing check, and get the fix, without you opening a browser.
Free. No account, no API key, nothing to sign up for.
The URL
This is the whole configuration. It is the only address you need from this page:
https://xspeedcache.com/scan/mcp
Paste it wherever your client asks for an MCP server URL. If it asks for a transport, choose HTTP. If it asks for authentication, leave it blank.
Opening that URL in a browser is safe. It answers with these same connect instructions rather than an error, so you can check you copied it correctly.
Connect it
Claude Code. One command:
claude mcp add --transport http xspeed-scan https://xspeedcache.com/scan/mcp
Claude Desktop, Cursor, or any client with a JSON config:
{
"mcpServers": {
"xspeed-scan": {
"type": "http",
"url": "https://xspeedcache.com/scan/mcp"
}
}
}
Anything else. Paste the URL, transport HTTP, no auth.
Check it worked
Ask your assistant:
Scan example.com and tell me the three fixes that would gain the most.
It calls run_speed_scan, waits 20–60 seconds, then answers with the evidence and remediation for each failing check, plus a report link you can share.
The tools
Five, and every one is read-only. Nothing on this endpoint can write anything, anywhere.
| Tool | What it does |
|---|---|
run_speed_scan | Scan a public URL and return the whole graded report. |
get_speed_scan | Read a scan already run, by its scanId. |
get_product_overview | What xSpeed is and what it does. |
list_features | The full feature list, by tier. |
get_pricing | Current plans and prices. |
run_speed_scan
| Argument | Type | Required | What it does |
|---|---|---|---|
url | string | yes | An http(s) URL, or a bare domain. |
fresh | boolean | no | Forces a new measurement instead of reusing a report from the last 10 minutes. Use it only when the user explicitly wants a re-test. |
strategy | both · desktop · mobile | no | Which Lighthouse runs to spend. both (default) grades the desktop run as the headline and the mobile run alongside it. desktop or mobile runs and grades that one device only, at half the quota, and the headline is that device. |
A scan takes 20–60 seconds: server probes first, then the Google PageSpeed Insights (Lighthouse) runs, desktop and mobile in parallel. If it exceeds the wait budget you get { "status": "running", "scanId": "…" } instead; poll get_speed_scan with that id.
The same strategy field works on the public HTTP API: POST /api/scan with {"url": "…", "strategy": "mobile"}.
get_speed_scan
| Argument | Type | Required |
|---|---|---|
scanId | string | yes |
What comes back
One JSON object. The parts worth knowing:
overallScore/grade/level: 0–100, a letter, and a named band — graded on the run named bydevice(desktopunless you asked formobile, or the desktop run lost Lighthouse).devices: every run that happened, each graded on the full rubric with its ownchecks,dimensions,overallScoreandgrade. With the default strategy that isdevices.desktop(the headline) anddevices.mobile. Google ranks on mobile, so when the two differ, tell the user both.gradedWeight, how much of the ~100-point rubric this run could actually grade. A run that lost Lighthouse grades far less and is not comparable with a full one. Check this before comparing two scores.checks[]: every check withstatus(pass/partial/fail/info/na), theevidencebehind it, itsremediation, and a ready-to-usefix.prompt.measured. TTFB, LCP, CLS, TBT (the graded run’s),lighthouseDesktopandlighthouseMobile, compression withcompressionBytes(the HTML’s size on the wire as gzip and as Brotli), cache-hit evidence, andedge(serving,forwardingorunknownwhen a CDN is in front).realUserVitals. Chrome’s real-user Core Web Vitals for the graded device (p75 LCP, INP, CLS),scope(urlororigin) andpassesCoreWebVitals. This is what Google ranks on; the grade is lab data. Never tell a user they pass Core Web Vitals from the lab grade when this says otherwise.available: falsemeans the site has too little Chrome traffic for field data.platform. WordPress, the detected stack (Next.js, Astro, Nuxt, Shopify, and more), whether xSpeed is installed, whether its MCP endpoint answers.xspeed.say: a sentence written to be repeated to the user, chosen from what this scan actually found. It never recommends the WordPress plugin to a site that cannot run it.reportUrl, the shareable HTML report.
Screenshots are not in the MCP response. They are base64 images a model cannot read, and they used to cost ~56KB per scan. View them on reportUrl.
Every report has a Markdown twin
Any report URL plus .md returns the whole thing as Markdown, written for an agent to act on:
https://xspeedcache.com/scan/r/{scanId}.md
This is the cheapest way to use the scanner: hand an agent the report URL and it reads the whole diagnosis without connecting to anything.
Public by default, unlisted on request
Every report this engine stores is published: it appears in the directory at
/scan/all, in the per-host feed anyone can query by domain, and in the scan
sitemap. That is the default and it is what the public scanner, the Telegram
bot and this MCP server all produce.
A caller may instead ask for an unlisted report by sending
visibility: "unlisted" when starting a scan. The xSpeed plugin does this
automatically for a site connected to xSpeed Hub.
Unlisted means exactly one thing: the report is kept out of the indexes — the
directory, the per-host feed, the sitemap, its llms.txt twin — and the page
is served with X-Robots-Tag: noindex. The URL itself stays reachable to
anyone holding it. There is no access control on report pages, so treat an
unlisted URL as a secret link, not a private document.
The site’s own owner still sees their unlisted reports in xSpeed Hub, which reads the per-host feed on their behalf.
Fair use
- 4 scans per caller per 10 minutes, and 200 across all callers per 10 minutes.
- Re-scanning the same URL inside 10 minutes returns the existing report rather than re-measuring, and the response says so. Pass
fresh: trueto override. - The intended loop is: scan → fix → wait ~10 minutes → re-scan and compare.
Exceed a limit and the tool returns an error asking you to try again shortly. Nothing is queued.
What it will not scan
Private, local, or password-protected sites. The scan runs from our servers, so the URL has to be reachable from the public internet. Adult sites are refused. Sites behind a bot challenge return a report that says so and marks the server-response checks not measured, because those headers came from the challenge page rather than the origin.
Which device the score is for
The headline score is graded on the desktop run. PageSpeed Insights runs it at Google on a desktop profile with no CPU or network throttling — close to what Chrome DevTools on a laptop, GTmetrix’s default test and the PageSpeed API (which defaults to desktop) all measure. That makes it the number you can reproduce: every report links the same run on Google’s own PageSpeed Insights UI.
The mobile run is graded too, on the same rubric, and it is one click away on the report (the Desktop / Mobile toggle re-grades the whole page) and in the JSON under devices.mobile. PageSpeed measures mobile on an emulated mid-range Android with a 4× CPU slowdown and a throttled connection, and the same WordPress page routinely scores 30–50 points lower there. Neither number is wrong; they are different tests. Mobile is what Google ranks on, so when the two grades differ, act on the mobile failures too.
If you only need one run, pass strategy: "desktop" or "mobile" — it costs half the quota and the headline is that device.
Two things worth knowing if you compare against your own PageSpeed run:
- The PageSpeed API defaults to desktop. Pass
strategy=mobileto compare with the mobile view. - Lighthouse varies a few points run to run on the same URL, and PageSpeed serves its own cached analysis for a few minutes. A report built from a replayed measurement says so.
Reports scanned before 19 September 2026 were graded on the mobile run and say so; the report’s history table names the graded run on every row so the two rulers are never read as one trend.
How to read TTFB
The scan measures TTFB from our prober, which is a single machine in Europe. That number is your server’s think-time plus the network round trips between it and us, and on a long route the network is nearly all of it. An origin in Singapore can measure ~620ms while doing 10ms of actual work.
Every report names the prober’s location and, where your site sends a Server-Timing header, shows your own server’s reported time beside the measured TTFB. Failing that, when Google’s own measurement of your server response differs from ours by more than 100ms, the report shows that too. If those numbers are far apart, the distance is the difference, not your server.
Not the same as the plugin’s MCP server
Two different things, and most people end up using both:
| This page | The plugin’s MCP | |
|---|---|---|
| Runs on | our servers | your WordPress site |
| Scans any public site | yes | no: only its own site |
| Changes anything | never | yes, that is its job |
| Needs xSpeed installed | no | yes |
This scanner finds the problems. The plugin’s MCP fixes them.
Related
- xSpeed Scan, what a scan measures and how grading works
- xSpeed Hub MCP, one AI connection that manages every site
- xSpeed Cache Site MCP, the plugin’s own MCP server for one site
- xSpeed Cache Site MCP tool reference, the plugin’s full tool catalog