* feat(skills): add webmcpify skill * style(skills): quote webmcpify description
6.9 KiB
Verify — proving every tool works in a real browser
Environment
- Current Chrome (the API moved during the trial — the old
navigator.modelContextTestingsurface was removed 2026-07 in favor of productiondocument.modelContext.getTools()/executeTool()). - Enable:
chrome://flags/#enable-webmcp-testing, or launch with--enable-features=WebMCP,WebMCPTesting(covers both current and older builds). - Headed only — WebMCP requires a visible tab by design. In CI, run under
xvfb-run. Headless will never work; don't heal toward it. - App running locally via
app.startCommand, against dev/test data only. - Each tool's manifest entry tells you where and how:
route(navigate there),auth(sign in with the recorded test fixture; verify under EACH role for role-scoped tools),examples(what to execute),expect(what to assert),cleanup(how to undo a mutating tool's effect after the test).
The enumeration/execution surface (probe, don't assume)
In the page context, prefer the production surface and fall back for older builds:
const mc = document.modelContext ?? navigator.modelContext;
const tools = mc?.getTools
? await mc.getTools()
: await navigator.modelContextTesting?.listTools(); // removed 2026-07; legacy only
Contract facts that generated assertions MUST respect:
- Enumerated
inputSchemais a stringified JSON Schema —JSON.parsebefore comparing against the manifest entry. executeTool(...)resolves to a string result, ornullwhen the execution navigated (normal for declarative forms that submit-navigate).- Execution and declarative-validation failures reject the promise — they do
not resolve to
"ERROR: ...". Only imperative tools following the runtime's convention resolve with"ERROR: ..."strings. Assert accordingly per toolkind. - Registration is asynchronous —
registerTool()returns a promise, so a tool is not enumerable the instant the page loads. Poll for it (waitForToolin the template) or await atoolchangeevent; never assert presence immediately aftergoto. - Mutating declarative forms pause mid-execution: Chrome fills the form, then
waits for a real submit interaction before letting
executeToolsettle — awaiting it alone deadlocks into a timeout. Use the concurrent pattern: startexecuteToolunawaited → wait for an agent-filled value to appear → click submit → await the result (full example in the template). - These surfaces are for agents/harnesses only — they must never appear in shipped application code.
For declarative tools also verify the synthesized schema: the form-control → schema mapping is only partially specified, so check each annotated control appears as the expected property in the actual target Chrome build.
Per-tool checks
- Registered (poll — registration is async) with the expected name, the (parsed)
schema, and the manifest
annotationson the enumerated tool. The legacymodelContextTestingfallback cannot enumerate annotations — skip that assertion there and note the gap in the report. - Valid example executes: assert the result per
expect—expect.resultas a substring of the resolved string, orexpect.navigationas the destination whenexecuteToolresolvesnull(it navigated) — and theexpect.uistate as a delta (capture the relevant state before executing; mere visibility of something already on screen proves nothing). A tool that reports success without the UI changing is a fail (UI-settled rule). Because executions can navigate, restore the manifestrouteinbeforeEach, notbeforeAll. - Invalid example: prove the tool is present first (a rejection from a
never-registered tool is not a validation rejection). Then: imperative →
resolves
"ERROR: ..."; declarative/schema violation → rejects. Zero-param read tools withexamples.invalid: nullget the dual-outcome assertion instead:{"unexpected": true}may be rejected with a validation reason OR resolve benignly — both pass; a missing tool/surface fails. - Mutating tools: run against disposable data, verify the mutation through the
same read path the UI uses, then execute the manifest
cleanup— amutating: "server"tool without working cleanup blocks at the gate, and heal-loop retries of mutating tools must clean up between attempts.
Harness
Instantiate templates/webmcp.spec.ts (bundled with this skill) — Playwright,
headed persistent Chrome, one describe-block per tool generated from the manifest,
with real assertions (never commented-out placeholders). Put the generated spec
next to the repo's existing e2e tests.
Repos without a test setup — the standalone-harness recipe. The spec stays in
.webmcpify/webmcp.spec.ts (single source of truth, committed per the gate's
commitWebmcpifyDir choice); the Playwright installation lives in a scratch
harness OUTSIDE the repo so the target gains no dependencies:
mkdir -p /tmp/webmcpify-harness && cd /tmp/webmcpify-harness
npm init -y && npm i -D @playwright/test typescript @types/node
cat > playwright.config.ts <<'EOF'
import { defineConfig } from '@playwright/test';
export default defineConfig({
testDir: process.env.WEBMCP_SPEC_DIR, // → <target-repo>/.webmcpify
workers: 1, // one shared headed Chrome — never parallelize
});
EOF
WEBMCP_SPEC_DIR=<target-repo>/.webmcpify WEBMCP_BASE_URL=http://localhost:5173 \
NODE_PATH=/tmp/webmcpify-harness/node_modules npx playwright test
NODE_PATH lets the out-of-repo spec resolve @playwright/test; if the target's
tooling ignores NODE_PATH, symlink instead:
ln -s /tmp/webmcpify-harness/node_modules <target-repo>/.webmcpify/node_modules
(and make sure it isn't committed). Note in the report that verification ran from
a standalone harness.
Alternative: Puppeteer ships a first-class experimental WebMCP API (https://pptr.dev/guides/webmcp) — prefer it when the target repo already uses Puppeteer.
Tool-selection evals (recommended; mandatory for SaaS-scale toolsets)
Schema-level verification proves tools work, not that an LLM picks them.
For apps exposing more than a handful of tools, run Google's WebMCP Evals CLI
(GoogleChromeLabs/webmcp-tools, evals-cli): write one eval case per tool from the
manifest examples ("user says X → expect tool Y with args Z") and run them —
this catches ambiguous names/descriptions and overlapping tools that Playwright
cannot.
Manual QA (tell the human in the report)
- DevTools → Application → WebMCP pane: live tool list, invocation log, "Run tool" with editable params.
- Model Context Tool Inspector Chrome extension (by Google's François Beaufort): natural-language smoke tests of tool selection.
- Chrome's WebMCP audits flag missing
toolname/toolparamdescription/label[for]/nameon declarative forms.