Files
awesome-copilot/skills/webmcpify/references/verify.md
T
Jonas Tüchler 03fb5fc96e Add the WebMCPify agent skill 🤖🤖🤖 (#2400)
* feat(skills): add webmcpify skill

* style(skills): quote webmcpify description
2026-07-23 10:50:25 -07:00

133 lines
6.9 KiB
Markdown

# Verify — proving every tool works in a real browser
## Environment
- **Current Chrome** (the API moved during the trial — the old
`navigator.modelContextTesting` surface was removed 2026-07 in favor of
production `document.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:
```js
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 `inputSchema` is a **stringified** JSON Schema — `JSON.parse` before
comparing against the manifest entry.
- `executeTool(...)` resolves to a **string result, or `null` when 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 tool
`kind`.
- **Registration is asynchronous** — `registerTool()` returns a promise, so a tool
is not enumerable the instant the page loads. Poll for it (`waitForTool` in the
template) or await a `toolchange` event; never assert presence immediately
after `goto`.
- **Mutating declarative forms pause mid-execution**: Chrome fills the form, then
waits for a real submit interaction before letting `executeTool` settle —
awaiting it alone deadlocks into a timeout. Use the concurrent pattern: start
`executeTool` unawaited → 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
1. Registered (poll — registration is async) with the expected name, the (parsed)
schema, **and** the manifest `annotations` on the enumerated tool. The legacy
`modelContextTesting` fallback cannot enumerate annotations — skip that
assertion there and note the gap in the report.
2. Valid example executes: assert the result per `expect``expect.result` as a
substring of the resolved string, or `expect.navigation` as the destination
when `executeTool` resolves `null` (it navigated) — **and** the `expect.ui`
state 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 manifest `route` in `beforeEach`, not
`beforeAll`.
3. 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 with `examples.invalid: null` get the dual-outcome assertion
instead: `{"unexpected": true}` may be rejected with a validation reason OR
resolve benignly — both pass; a missing tool/surface fails.
4. Mutating tools: run against disposable data, verify the mutation through the
same read path the UI uses, then execute the manifest `cleanup` — a
`mutating: "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:
```sh
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]`/`name` on declarative forms.