# WebMCP agent readiness playbook: survey, register, roll out and verify a portfolio of sites A plain Markdown playbook for anyone who looks after several websites (an agency, a franchise, a family of small businesses) and wants AI assistants to use them properly. It is the method I used on October 16, 2026 to take thirty sites from "WebMCP tools shipped but inert" to "tools on, verified, renewal dated". Hand it to an AI coding assistant or work through it by hand. Companion article (the why, in plain English): Per-finding fix guide: Facts checked October 16, 2026: - WebMCP is a draft from the W3C Web Machine Learning Community Group, not a W3C Standard. - Chrome's origin trial runs Chrome 149 to 162 and ends March 29, 2027. - Edge runs its own trial from Edge 150, also listed to March 29, 2027, but its tokens are shorter lived: those issued in October 2026 expire November 30, 2026. - Chrome tokens don't work in Edge. - Safari has no WebMCP: WebKit opposes it. - Firefox has an off-by-default prototype. --- ## 0. Ground rules - **The assistant fills, the person sends.** Read tools return what the site already publishes. Fill tools fill a form and stop. No tool submits, pays, books or deletes anything on its own, and no form carries `toolautosubmit` if it sends a request. - **Read before you write.** Survey every site first and save the output. You'll want it as the "before" evidence. - **One change per deploy where you can.** Token first, then form labels, then server tools. It makes a bad deploy obvious. - **Compare what you deploy with what's live.** If a site's working copy can be behind production (content published from another machine, a CMS edit), diff it against the live site or the repository's main branch before deploying, or you'll quietly roll back someone else's work. --- ## 1. Survey: which sites run WebMCP, and which send a token Save as `webmcp-survey.mjs`, edit the list, run `node webmcp-survey.mjs` (Node 20 or later). It only reads. ```js // Which sites run WebMCP tools, send an origin-trial token, and label their forms. Read-only. const sites = ['example.com', 'www.example.org']; const get = async (url) => { try { const r = await fetch(url, { redirect: 'follow', signal: AbortSignal.timeout(15000) }); return { status: r.status, url: r.url, headers: r.headers, text: await r.text() }; } catch (e) { return { status: String(e.cause?.code || e.name), headers: new Headers(), text: '' }; } }; const rows = []; for (const d of sites) { const h = await get('https://' + d + '/'); const srcs = [...h.text.matchAll(/]+src="([^"]+)"/g)].map((m) => m[1]).filter((s) => /webmcp|model-?context|agent/i.test(s)); let runs = /registerTool|modelContext/.test(h.text); for (const s of srcs.slice(0, 3)) { const js = await get(new URL(s, h.url || 'https://' + d + '/').href); if (/registerTool|modelContext/.test(js.text)) runs = true; } const card = await get('https://' + d + '/.well-known/mcp/server-card.json'); rows.push({ site: d, status: h.status, webmcp: runs ? 'yes' : 'no', headerTokens: (h.headers.get('origin-trial') || '').split(',').filter(Boolean).length, metaTokens: (h.text.match(/http-equiv=["']origin-trial/gi) || []).length, forms: (h.text.match(/]*\btoolname=/gi) || []).length, mcpCard: card.status === 200 ? 'yes' : 'no', }); } console.table(rows); ``` Only sites with `webmcp: yes` need a token. A site with forms but no WebMCP can still gain from form labels, which do nothing until WebMCP runs there. --- ## 2. Register every origin (Chrome, then Edge) **Chrome.** 1. Open and choose **WebMCP**, then **Register**. 2. For each origin: - Type it exactly as visitors see it. - Tick "match all subdomains" only where both `www` and the bare domain serve pages. - Pick the expected usage band. - Tick the four acknowledgements and register. **Edge.** Open , sign in with GitHub, choose **WebMCP** and register each origin the same way. **Capture the tokens into one file** (`tokens.json`, keyed by host), then verify each before anything ships. The script below checks Chrome tokens fully. Edge tokens decode the same way, but Microsoft doesn't publish its signing key, so check their origin, feature and expiry and record the signature as "not checked". ```js // verify-tokens.mjs: node verify-tokens.mjs tokens.json (tokens.json = { "example.com": { "chrome": "...", "edge": "..." } }) import { readFileSync } from 'node:fs'; import { webcrypto as crypto } from 'node:crypto'; const CHROME_KEY = 'fMS4mpO6buLQ/QMd+zJmxzty/VQ6B1EUZqoCU04zoRU='; const key = await crypto.subtle.importKey('raw', Buffer.from(CHROME_KEY, 'base64'), { name: 'Ed25519' }, false, ['verify']); const all = JSON.parse(readFileSync(process.argv[2], 'utf8')); for (const [host, t] of Object.entries(all)) { for (const [vendor, token] of Object.entries(t)) { const b = Buffer.from(token, 'base64'), len = b.readUInt32BE(65); const p = JSON.parse(b.subarray(69, 69 + len).toString('utf8')); const sig = vendor === 'chrome' ? await crypto.subtle.verify('Ed25519', key, b.subarray(1, 65), Buffer.concat([b.subarray(0, 1), b.subarray(65, 69 + len)])) : 'not checked'; const tokHost = new URL(p.origin).hostname; const originOk = tokHost === host || (p.isSubdomain && host.endsWith('.' + tokHost)) || tokHost === host.replace(/^www\./, ''); console.log(host.padEnd(28), vendor.padEnd(6), 'v' + b[0], p.feature, new Date(p.expiry * 1000).toISOString().slice(0, 10), 'origin ' + (originOk ? 'ok' : 'MISMATCH'), 'sig ' + sig); } } ``` Stop and fix any line that shows a mismatch, a feature other than `WebMCP`, or `sig false`. --- ## 3. Ship the tokens Put both tokens on every HTML page of the matching site. Prefer a response header; fall back to meta tags in `` where the host can't set headers. ```text # Netlify or Cloudflare Pages: _headers /* Origin-Trial: CHROME_TOKEN, EDGE_TOKEN ``` ```html ``` Things that went wrong for other people, so they don't for you: - A meta tag outside `` is ignored without any warning. - A site generator that bundles or minifies the head can drop unknown meta tags. Check the built output, not the template. - `www` and the bare domain are different origins. If one redirects to the other, only the final one needs the token. - Staging hosts need their own tokens. Don't paste production tokens there. Deploy, then confirm on the live site: ```sh curl -s -D - -o /dev/null https://example.com/ | grep -i '^origin-trial' ``` --- ## 4. Label the forms For every form a person fills in, add `toolname` and `tooldescription` to the `
`, and `toolparamdescription` to every visible named field. Leave out `toolautosubmit`. Skip search boxes, hidden spam-detection copies of forms and honeypot fields. The fix guide has a full example and writing tips. Also register the important actions in JavaScript (the ChatGPT desktop browser ignores form attributes), guarded so browsers without WebMCP never see an error: ```js const mc = document.modelContext || navigator.modelContext; if (mc && typeof mc.registerTool === 'function') { try { mc.registerTool({ name: 'get_hours', title: 'Get opening hours', description: 'Opening hours as published on this site.', inputSchema: { type: 'object', properties: {}, additionalProperties: false }, annotations: { readOnlyHint: true }, execute: () => ({ hours: 'Mon to Fri 8:00 to 17:00' }) }); } catch (e) { /* never break the page */ } } ``` --- ## 5. If the sites run MCP servers For each tool: - set `title` at the top level and in `annotations.title`; - set `readOnlyHint: true` on tools that only read, or `destructiveHint: true` on tools that change things; - keep names at 64 characters or fewer. Make `GET /mcp` answer `405` with an `Allow: POST, OPTIONS` header. That is what the Claude Connectors Directory review looks for, and it's good manners for every client. Submitting a listing also needs a paid Claude plan, a privacy policy URL, a docs URL, a support contact and an icon. --- ## 6. Verify: before and after, on the live sites - Re-run the survey. Every `webmcp: yes` site should now show `headerTokens` or `metaTokens` of at least 1 (2 with Edge). - Run the [Mega Analyzer](https://jwatte.com/tools/mega-analyzer/) on each home page. The rows to read: - "WebMCP origin-trial token" (decodes the token, checks signature, origin and expiry); - "Declarative WebMCP form labels"; - "WebMCP code works in every browser"; - "Agent paths by browser"; - and, for MCP servers, "MCP tools ready for the Claude Connectors Directory". - Save the before and after outputs side by side, with timestamps. - Open one site in Chrome 149 or later and check DevTools' WebMCP debugging or Lighthouse's Agentic browsing category to see the tools listed. --- ## 7. Keep it working - **Calendar the expiry.** Every token carries one; both consoles email reminders. When a trial is extended, register again and swap the tokens. - **Re-read the API names before each renewal.** In 2026 the API moved from `navigator.modelContext` to `document.modelContext`, `provideContext()` was removed, and the form attributes left the draft for rework. Guarded, document-first code with a navigator fallback survived all of it. - **Watch the browsers.** Edge and Chrome are in trials, ChatGPT's desktop browser reads imperative tools, Safari opposes WebMCP, and Firefox's prototype is off by default. The parts that help in every browser are clean HTML and labelled forms, plus a server-side MCP endpoint for assistants that aren't in a browser at all. --- *Informational, not legal or technical consulting advice. Product names belong to their owners and are used for identification only.*