Inspiration
We already run an index of 14,351 sites whose agent-facing capabilities are probe-verified and dated. When we turned that machinery on WebMCP, the finding was awkward: registered tools round to zero outside demos and the checker sites themselves. There were more tools for checking WebMCP than there were sites using it.
So the missing thing was never another checker. It was a denominator. Nobody could tell a site what its result meant, because nobody had a population to compare it against.
Then a bug decided the rest of the product. Our first detector reported a clean no tools found for a page whose script robots.txt had disallowed us from reading. It wasn't a no. It was a question we weren't allowed to ask, and reporting it as a no is the same error as counting a locked door as an empty room. Across the index that turned out to be 1,224 hosts. Every checker we could find reports those as failures. That distinction became the product.
What it does
Give it a hostname. It fetches the page, then opens it in a real browser, and reports three things a checker normally collapses into one: whether the site declares WebMCP in its source, whether it actually registers tools when the page executes, and — the state that matters — whether we were allowed to ask at all.
Then it does the thing no other checker can: it places the answer. 647 sites in our index carry the code, 580 register something at runtime, and 997 distinct tool names have been observed across them. If you're deciding which tools to expose, the distribution is the answer — search_site on 122 sites, fetch_as_markdown on 105, search_content on 28, get_contact_info on 18, get_pricing and search_products on 17 each. Measured and dated, not guessed.
Why WebMCP is the right shape for this. What anyone wants from this page is one action with one parameter: check this host. That's a tool call, not a document. An agent forced to fetch our HTML, locate the form, synthesise a POST and parse a verdict out of the markup is doing four fragile things to accomplish one simple one — and all four break the next time we change the layout. So the page declares its own WebMCP tools. An agent calls it rather than reading it. The checker is an instance of the thing it checks.
What that unlocks. An agent can audit fifty hosts in a loop with no HTML parsing and no brittle selectors. More interesting is the reverse direction: "does this host expose tools I can call, and were they observed running or merely declared?" is a question an agent should ask before it interacts with an unfamiliar site. Declared-versus-verified is exactly the distinction an autonomous agent needs and rarely has. This turns it into one call, with a date attached.
And a boundary that matters more as agents get more capable: we list tools, we never invoke them. Enumerating a stranger's tools is safe. Executing one could place an order, send a message, or spend someone's money.
How we built it
Two layers, mapping onto the verification tiers we already run.
Declared — a static fetch matching navigator.modelContext, document.modelContext, provideContext and registerTool. It matches both namespaces, because the spec renamed one mid-origin-trial and a site written against the earlier draft still counts. And it matches a call, not a mention — an article explaining WebMCP doesn't get scored as a site using it.
Registered — headless enumeration in a real browser via Cloudflare Browser Rendering, the tools/list equivalent. This is what separates a site that ships the code from a site where the code actually runs. 647 carry it; 580 register something.
It runs on Cloudflare Workers with D1 behind it. The page's own tools are declared with the declarative form attributes — toolname, tooldescription, toolparamdescription — so this is a WebMCP surface, not a page about WebMCP.
Consent is enforced before any fetch, because the tool probes third-party hosts on demand: robots.txt read before the resource request; ai-input=no treated as an absolute stop, with the host reported as excluded rather than as failing; the user-agent KnownGood-Verifier never disguised; Retry-After honoured. And running a check has zero effect on ranking in our index — listings there are earned by probe and never sold.
Challenges we ran into
Cloudflare Browser Rendering caps at two concurrent browsers and two new per minute, with a 60-second inactivity kill — a different problem at index scale than at demo scale.
The detector took two rewrites. The first version matched modelContext inside comments, and fixing that surfaced the worse case: WebMCP discussed in HTML body prose. Separating a call from a mention is most of the work in a detector like this, and neither failure was obvious until we looked at what had matched.
And the spec renamed navigator.modelContext to document.modelContext mid-origin-trial, so anything matching a single namespace silently under-counts. We match both.
Accomplishments that we're proud of
We shipped the state that makes our own number smaller. Adding "could not ask" means 1,224 hosts don't count toward anything, where every checker we found reports them as failures. It's the right number and it cost us the bigger one.
The checker passes its own check. Run it against https://knowngood.sh/verify/webmcp and it comes back registered, with its tools enumerated. We didn't want to ship a tool telling sites to do something we hadn't done.
The population produced a finding nobody else could. Treating each site's tool signature as a fingerprint puts 39.5% of the 580 registering sites in a cluster with at least one other site, and the largest single cluster is 18.1%. Most WebMCP "adoption" is one plugin — and you can only see that from a denominator.
Every figure carries the population and date it was measured against. No number on the page floats free of what it was measured over.
What we learned
A checker that cannot distinguish "no" from "couldn't ask" is worse than no checker at all for the sites it mislabels — it manufactures a false negative and presents it with the same confidence as a real one.
And adoption numbers in a young standard are mostly plugin numbers. Without the tool-signature clustering we would have reported 580 independent implementations. The truth is closer to a handful of implementations installed many times, and only a population makes that visible.
What's next for Known Good WebMCP Verifier
Declarative detection. The checker sees the imperative API but not toolname / tooldescription on form controls — the half of the standard a browser-side audit is most likely to score, and a real gap.
When it lands, the attribute route will report as declared and never as registered. Attributes prove intent; only execution proves registration. Conflating the two is exactly the error this tool exists to avoid, and we'd rather ship the gap honestly than close it dishonestly.
Built With
- cloudflare
- css
- html
- javascript
- mcp
- node.js
- webmcp
Log in or sign up for Devpost to join the conversation.