Data Refinery
Real performance data for UK small-business websites, queryable by an AI agent. Not a survey and not scraped from a directory β every figure comes from running 134 checks against thousands of independent businesses with one engine and one methodology.
Endpoint
https://mcp.xsoftlimited.com/ Streamable HTTP, POST only. Four of the five tools are free and need no key.
What it will not tell you
It will not return a stored score for a business you did not name. There is no search, no leaderboard, and no way to ask "show me the worst restaurant websites in Leeds".
That is deliberate. The corpus covers thousands of real trading companies, many of them sole traders. Publishing a searchable quality verdict on each would mean publishing a judgement about a named business, from a dataset that has been wrong before β dead-domain detection silently broke for six days and misfiled over a thousand rows; a rate limit we triggered ourselves was recorded as site failures; an earlier engine version reported roughly half the true server response time. Each of those was found by checking, not by being told. Aggregates survive that kind of error. Verdicts about somebody's livelihood do not.
Tools
corpus_stats free Β· no key Size of the corpus, coverage per vertical, methodology and stated limits.
Call this first. It tells you what the data can and cannot support before you build on it.
{} benchmark_sector free Β· no key Distribution of one metric for one vertical β median, p25/p75/p90/p99, mean, and the share meeting Google thresholds.
The core benchmark. Refuses any cell below 30 audits.
{ "vertical": "STAY", "metric": "ttfb" } sector_comparison free Β· no key One metric across several verticals at once, ranked.
Where the non-obvious findings are. Hotels and restaurants fail for opposite reasons.
{ "metric": "ttfb" } check_adoption free Β· no key Adoption rate of a practice, with the score gap between adopters and non-adopters.
Returns a circularity-adjusted gap alongside the raw one, because the practice is itself scored.
{ "check": "llms_txt" } compare_domain API key required Audits a domain you name, live, and returns its percentile against its sector.
Runs a fresh audit rather than reading a stored one, so the answer is current. ~30 seconds.
{ "domain": "example.co.uk", "vertical": "FOOD" } compare_domain needs a key because
each call runs a live audit β about 30 seconds of compute and one Google PageSpeed
quota unit. Everything else is a read over data already collected, so it is free.
The split is by cost, not by value.
Parameters
metric
ttfb β Time to first byte, cold β the first uncached request lcp β Largest Contentful Paint, via Google PageSpeed Insights fcp β First Contentful Paint page_weight β Transferred page size in KB score β Overall audit score out of 100, across 134 checks vertical
FOODSTAYRETAILPROFESSIONALHEALTHPERSONALMOTORLEISURETRADE Omit it to query the whole corpus. Cells below 30 audits are refused β smaller samples are meaningless, and with a narrow filter they risk identifying individual businesses.
Connecting
Add it to any MCP client that speaks Streamable HTTP:
{
"mcpServers": {
"data-refinery": {
"type": "streamable-http",
"url": "https://mcp.xsoftlimited.com/"
}
}
} Or call it directly:
curl -X POST https://mcp.xsoftlimited.com/ \
-H 'Content-Type: application/json' \
-H 'Accept: application/json, text/event-stream' \
-H 'MCP-Protocol-Version: 2026-07-28' \
-H 'Mcp-Method: tools/call' \
-H 'Mcp-Name: benchmark_sector' \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "benchmark_sector",
"arguments": { "vertical": "STAY", "metric": "ttfb" },
"_meta": {
"io.modelcontextprotocol/protocolVersion": "2026-07-28",
"io.modelcontextprotocol/clientInfo": { "name": "my-agent", "version": "1.0" },
"io.modelcontextprotocol/clientCapabilities": {}
}
}
}'
Protocol 2026-07-28 carries the protocol version, client info and capabilities in
params._meta on every request β there
is no initialize handshake and no
session. The Mcp-Method and
Mcp-Name headers must match the body,
or the request is rejected with error -32020.
Most MCP clients handle all of this for you.
Where the data comes from
Sample
UK independent businesses across nine verticals and 44 counties, sourced from OpenStreetMap. Chain domains are excluded by frequency, so one hotel group cannot skew a cohort.
Speed
Time to first byte is the COLD measurement β the first uncached request, which is what a first-time visitor and Googlebot actually experience. Warm figures are roughly half as large and flatter every site.
Rendering
Largest Contentful Paint and First Contentful Paint come from Google PageSpeed Insights, a lab measurement on a simulated connection. Directionally sound; not field data from real visitors.
Statistics
Percentiles are linearly interpolated. Audits that ran but could not score a site are counted separately and excluded from every distribution β never imputed.
The published study behind this data: the national audit of UK hospitality websites β
Limits worth knowing
Observational, not causal.
Sites publishing an llms.txt file score better. Nothing here shows that adding one would raise a score.
Businesses without a website are invisible.
The sample is drawn from businesses that listed one, which makes this a study of the industryβs web presence rather than of the industry.
Coverage is uneven across verticals.
Call corpus_stats for the current per-vertical counts rather than assuming they are comparable.
Rate limited.
20 requests per minute for anonymous callers. Enough to evaluate the data properly, not enough to mirror it.
Need a key?
Only compare_domain requires one.
Email info@xsoftlimited.com
with what you are building. Everything else works right now with no signup.