Guides · WebMCP · Worked Example

WebMCP field notes: a real session, bugs included

This is what actually happens when you call the Reg Z threshold calculator through WebMCP, the draft W3C API that lets a web page offer tools to AI agents. It does not work on the first try, and that is the point: this page is a complete session, every command, every error the browser throws, what each one means, and the cryptographic receipt at the end. Nothing cleaned up, because the debugging is the useful part.

What happened, in order
  1. Launching Chrome with WebMCP enabled
  2. The console refuses to let you paste (by design)
  3. Error one: the API is not there at all
  4. Error two: the API moved
  5. Proving the fix with a probe tool
  6. The full call, and the receipt
  7. What changed on this site because of it

1. Launching Chrome with WebMCP enabled

WebMCP is new, so Chrome keeps it behind a feature switch. Close every Chrome window, then start it from the Run box (Windows key + R):

"C:\Program Files\Google\Chrome\Application\chrome.exe" --enable-features=WebMCP

Then open the calculator this whole example runs against: Reg Z Threshold Lookup. It is an ordinary page with two dropdowns. Everything below happens in the browser DevTools console (press F12, pick the Console tab).

2. The console refuses to let you paste

The first paste does nothing except produce a yellow warning:

Warning: Don't paste code into the DevTools Console that you don't understand or haven't reviewed yourself. This could allow attackers to steal your identity or take control of your computer. Please type "allow pasting" below and press Enter to allow pasting.
What this is: Chrome's one-time protection against a real scam where strangers talk people into pasting hostile code. Type the two words allow pasting at the console prompt and press Enter. Chrome remembers permanently. Every snippet on this page is shown in full so you can read what you are about to run, which is exactly the habit the warning is asking for.

3. Error one: the API is not there at all

With pasting unlocked, ask the page what tools it offers:

const tools = await navigator.modelContext.getTools(); console.log(tools.length, tools[0].name);

Which gets you this:

Uncaught TypeError: Cannot read properties of undefined (reading 'getTools')
Diagnosis: navigator.modelContext did not exist, meaning the feature switch never took effect. The cause is sneaky: Chrome keeps background processes running even when all its windows are closed, so the flagged launch just opens a window inside the old, unflagged instance. Fix: kill every Chrome process, then relaunch:
taskkill /IM chrome.exe /F

After a genuinely fresh start, the check worth running first comes back healthy:

> "modelContext" in navigator true

4. Error two: the API moved

Same snippet after the restart. A different failure this time, with a very informative warning above it:

navigator.modelContext is deprecated. Please use document.modelContext instead. Uncaught TypeError: Cannot read properties of undefined (reading 'name')

Checking both surfaces shows the tool list empty everywhere:

> const mc = document.modelContext ?? navigator.modelContext; const t = await mc.getTools(); console.log(t); []
Diagnosis: this page registered its tool on navigator.modelContext, which was correct when the integration shipped and passed its test harness. Newer Chrome moved the API to document.modelContext and turned the old path into a deprecated alias whose registrations quietly go nowhere. The page worked on the browser it was tested on and broke on the browser you are probably running. Draft APIs do this, and a test that passed last month is not a test that passes today.

5. Proving the fix with a probe tool

Before changing anything on the site, confirm the mechanism with a throwaway tool registered the new way:

document.modelContext.registerTool({ name: "probe-tool", description: "probe", inputSchema: { type: "object", properties: {} }, async execute() { return "ok"; } }); console.log((await document.modelContext.getTools()).map(t => t.name));
['probe-tool']

One line of output, and the diagnosis is confirmed: registration through document.modelContext lands; registration through the deprecated alias does not.

6. The full call, and the receipt

With the mechanism proven, the real calculator tool gets registered the new way (the page itself now does this at load) and called exactly as an agent would:

const tools = await document.modelContext.getTools(); const tool = tools.find(t => t.name === "art-220-reg-z-threshold-lookup-compute"); const result = await document.modelContext.executeTool( tool, JSON.stringify({ tableType: "qm_points_fees", tableYear: 2026 }) ); console.log(JSON.parse(result));

One trap worth knowing: the second argument to executeTool is a JSON string, not an object. Passing an object fails with "Failed to parse input arguments."

Two things happened at once. The console received the structured answer, and the page itself rendered the human view of the same result, because the tool drives the same function as the submit button. The answer:

fr_citationFR 2025-22773, effective 2026-01-01
tier_1_min$137,958
tier_1_pct3%
tier_2_fixed$4,139
tier_3_min$27,592
tier_3_pct5%
tier_4_fixed$1,380
tier_5_pct8%

And beneath it, the execution hash:

sha256:43d43e1b5ac519566dd345496b655fe80a2f815428ab17703c092c15f490c8b6

Why that hash matters: it is SHA-256 over the canonical form (RFC 8785) of the inputs and outputs together. The continuous-integration harness, on a different Chrome build, on a different day, produced the same hash for the same inputs. Same question, same answer, same fingerprint, on any machine. That is the property that lets an agent verify a result instead of trusting the page that produced it. The kernel behind the page is additionally sealed with a zero-knowledge proof.

7. What changed on this site because of it

The session found a live defect: the page registered its tool only on the deprecated surface, so current Chrome visitors got no tool at all. The fix is the pattern this site now ships:

const mc = document.modelContext ?? (("modelContext" in navigator) ? navigator.modelContext : null); if (mc) { mc.registerTool({ /* same tool object */ }); }

Try it yourself

Everything above is reproducible in about two minutes: launch Chrome with the flag (step 1), open the calculator, and paste the snippet from section 6. The registration is already done for you by the page. Compare the tier values with the form's output and check the hash.