Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/README.skills.md
Original file line number Diff line number Diff line change
Expand Up @@ -406,6 +406,7 @@ See [CONTRIBUTING.md](../CONTRIBUTING.md#adding-skills) for guidelines on how to
| [vscode-ext-localization](../skills/vscode-ext-localization/SKILL.md)<br />`gh skills install github/awesome-copilot vscode-ext-localization` | Guidelines for proper localization of VS Code extensions, following VS Code extension development guidelines, libraries and good practices | None |
| [web-design-reviewer](../skills/web-design-reviewer/SKILL.md)<br />`gh skills install github/awesome-copilot web-design-reviewer` | This skill enables visual inspection of websites running locally or remotely to identify and fix design issues. Triggers on requests like "review website design", "check the UI", "fix the layout", "find design problems". Detects issues with responsive design, accessibility, visual consistency, and layout breakage, then performs fixes at the source code level. | `references/framework-fixes.md`<br />`references/visual-checklist.md` |
| [webapp-testing](../skills/webapp-testing/SKILL.md)<br />`gh skills install github/awesome-copilot webapp-testing` | Toolkit for interacting with and testing local web applications using Playwright. Supports verifying frontend functionality, debugging UI behavior, capturing browser screenshots, and viewing browser logs. | `assets/test-helper.js` |
| [webmcpify](../skills/webmcpify/SKILL.md)<br />`gh skills install github/awesome-copilot webmcpify` | Make a web app agent-ready — propose a WebMCP tool manifest, integrate, verify in a real browser, heal; unrelated code stays untouched. Use for "webmcpify", "add WebMCP", or "expose app actions to AI agents". | `references/heal.md`<br />`references/integrate.md`<br />`references/inventory.md`<br />`references/runtime.md`<br />`references/security.md`<br />`references/verify.md`<br />`templates` |
| [what-context-needed](../skills/what-context-needed/SKILL.md)<br />`gh skills install github/awesome-copilot what-context-needed` | Ask Copilot what files it needs to see before answering a question | None |
| [winmd-api-search](../skills/winmd-api-search/SKILL.md)<br />`gh skills install github/awesome-copilot winmd-api-search` | Find and explore Windows desktop APIs. Use when building features that need platform capabilities — camera, file access, notifications, UI controls, AI/ML, sensors, networking, etc. Discovers the right API for a task and retrieves full type details (methods, properties, events, enumeration values). | `LICENSE.txt`<br />`scripts/Invoke-WinMdQuery.ps1`<br />`scripts/Update-WinMdCache.ps1`<br />`scripts/cache-generator` |
| [winui3-migration-guide](../skills/winui3-migration-guide/SKILL.md)<br />`gh skills install github/awesome-copilot winui3-migration-guide` | UWP-to-WinUI 3 migration reference. Maps legacy UWP APIs to correct Windows App SDK equivalents with before/after code snippets. Covers namespace changes, threading (CoreDispatcher to DispatcherQueue), windowing (CoreWindow to AppWindow), dialogs, pickers, sharing, printing, background tasks, and the most common Copilot code generation mistakes. | None |
Expand Down
331 changes: 331 additions & 0 deletions skills/webmcpify/SKILL.md

Large diffs are not rendered by default.

69 changes: 69 additions & 0 deletions skills/webmcpify/references/heal.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
# Heal — failure taxonomy → fixes

Work one failed tool at a time. Re-verify after each fix. The triggering verify
failure counts as attempt 0; each fix cycle increments `attempts`. At `attempts`
= 3 → mark `skipped` with a blocker note (this is an explicit escalation to the
human in the final report, not a silent drop) and move on. **Never** widen the
diff, disable a check, or fake a return value to force a pass. **Mutating
tools:** run the manifest `cleanup` between attempts — retrying a mutation
without cleanup duplicates data.

**Heal fixes implementations, not contracts.** The manifest is the
human-approved contract: if the correct fix would change a tool's `inputSchema`,
`description`, `mutating` class, `annotations`, or `expect`, take it back to the
gate as a mini re-approval — never silently edit the manifest to match the code.

## Taxonomy

| Symptom | Likely cause | Fix |
|---|---|---|
| Tool absent from enumeration | **Registration is async** — the test asserted before `registerTool()` settled; or registration never ran (bootstrap not reached, view not mounted) or wrong Chrome build/flags | FIRST make the test poll (`waitForTool`) or await `toolchange` — only if it still fails, trace the registration call; confirm `isWebMCPAvailable()` in the test env; current Chrome + `--enable-features=WebMCP,WebMCPTesting` |
| Whole scope absent | A registration in the batch rejected (duplicate name, invalid schema, policy) — the runtime rolls back the entire scope | Check console for the `onError` report; fix the offending tool contract |
| Tool absent after route change | Scope disposed by navigation (over-scoping) | Move to static app-level registration unless genuinely view-bound |
| Declarative tool missing | `toolname` typo, frame without `allow="tools"`, or page sends `Origin-Agent-Cluster: ?0` | Fix attribute; check Permissions-Policy `tools` and origin-keying headers |
| Schema mismatch (declarative) | Control lacks `name`, description not resolvable, unsupported control type in this build | Add `name`/`toolparamdescription`/`label[for]`; unsupported controls → switch that form to imperative |
| Schema mismatch (imperative) | Manifest and code drifted | Make code match the approved manifest; if the manifest was wrong, that's a contract change — take it back to the gate for re-approval (see above), never silently update it |
| Assertion compares object to string | Enumerated `inputSchema` is a stringified JSON Schema | `JSON.parse` before comparing (see `verify.md`) |
| `executeTool` returns `null` unexpectedly | The execution navigated (normal for submit-navigating declarative forms) | Assert on the post-navigation page instead of the return value |
| `executeTool` rejects | Schema violation or declarative-validation failure — rejection IS the failure signal for these | For invalid-input tests on declarative tools, assert rejection, not an `"ERROR:"` string |
| Mutating declarative execution hangs until timeout | Chrome fills the form, then **pauses the execution awaiting a real submit interaction** — awaiting `executeTool` alone deadlocks | Use the concurrent pattern in the spec template: start `executeTool` unawaited → wait for the agent-filled value → click submit → await. **NEVER heal by adding `toolautosubmit`** (ground rule 5) |
| Backend rejects the harness with 403/CORS despite correct auth | The endpoint **allow-lists the production `Origin`** (mailers, form gateways) — the localhost harness origin is refused before the tool logic runs, and no local fix exists | Verify the live path with the env-gated server-side replay (§Origin-allow-listed endpoints below), only with the production side-effect approval recorded in `approval.productionSideEffect` (see §Origin-allow-listed endpoints below); without it, mark the live path `skipped` with a blocker note |
| Execution times out / canned success while UI still loading | Completion event fired before the async work finished, or listener missing/wrong event name | Fire `tool-completion-<requestId>` with `{ ok, message/error }` AFTER awaiting the real work (`runtime.md` contract) |
| Returns success but UI unchanged | `execute()` bypassed the real UI path (parallel implementation) | Rewrite to call the same handler/store action/endpoint the UI uses |
| Invalid input resolves successfully (imperative) | Missing in-code validation | Validate strictly in code; return `"ERROR: <what/how to fix>"` |
| Fetch-submitted form: agent gets nothing | `preventDefault()` without `respondWith()` | Add the `e.agentInvoked → e.respondWith(promise)` bridge |
| Works manually, fails in Playwright | Headless, missing flags, or profile without the flag | Headed + flags; persistent context; `xvfb-run` in CI |
| 401/403 from `execute()` in test | Tool registered outside the authenticated scope, or test session lacks the role in the manifest `auth` field | Role-scope the registration; sign in with the recorded fixture |
| Flaky: passes alone, fails in suite | Shared state between tool executions | Isolate test data per tool run (use `cleanup`); don't reorder tests to hide it |

## Origin-allow-listed endpoints — the replay pattern

Some production backends (mailers, form gateways) allow-list the production
`Origin` header and refuse everything else — the localhost harness can never
exercise the live path directly. When (and only when) the gate approved the real
production side effect (`approval.productionSideEffect`), verify the live path
with an env-gated replay: intercept the app's own request in Playwright and
re-issue it server-side (Node context — not subject to browser CORS) with the
production `Origin`:

```ts
// Env-gated: runs only with WEBMCP_LIVE_MUTATIONS=1 — never default-on in CI.
if (process.env.WEBMCP_LIVE_MUTATIONS === '1') {
await page.route('**/api/contact', async (route) => {
const response = await context.request.fetch(route.request(), {
headers: { ...route.request().headers(), origin: 'https://example.com' }, // the prod Origin
});
await route.fulfill({ response });
});
}
```

This causes a REAL production side effect. Mark every payload
`[webmcpify verification]`, run the manifest `cleanup`, list the effect in
`report.md`, and never enable the gate by default in CI.

## After healing

Re-run verification once for **all** tools with status `integrated` or `verified`
(not only the healed ones) — healing one tool can unregister or break another;
scope collisions are the classic case. Only then evaluate the exit condition.
141 changes: 141 additions & 0 deletions skills/webmcpify/references/integrate.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
# Integrate — patterns per stack

> Prefer the live official guides when online:
> `npx -y modern-web-guidance@latest retrieve "webmcp,agentic-forms,agentic-javascript-tools"`.
> The patterns below follow Google's reference implementations
> (GoogleChromeLabs/webmcp-tools) and the W3C CG draft.

## Declarative — standard HTML forms

Applies to plain HTML, SSG-emitted, server-rendered, and framework-rendered
(uncontrolled) forms — anywhere a real `<form>` with named controls exists.
Annotate the existing form; do not restructure it.

```html
<form toolname="request_quote"
tooldescription="Requests a project quote. A team member replies within one business day."
action="/contact" method="post">
<label for="email">Email</label>
<input type="email" id="email" name="email" required
toolparamdescription="Email address for the reply">
<!-- …existing fields, each with label[for] + name + toolparamdescription… -->
<button type="submit">Request quote</button>
</form>
```

Rules:
- The browser derives the JSON Schema from the controls — every control needs
`name`, a resolvable description (`toolparamdescription` → `label[for]` text →
`aria-description`), and correct HTML constraints. Radio groups: description on
the enclosing `<fieldset>`.
- `toolautosubmit` **only** on pure read forms (search/filter/availability).
Never on contact/checkout/settings/messaging forms.
- Fetch-submitted forms (`preventDefault()`) MUST route the result back to the
agent — the most common integration bug is a swallowed submit:

```js
form.addEventListener('submit', (e) => {
e.preventDefault();
const result = doSubmit(new FormData(e.target))
.then(() => 'Request received. Reply within one business day.');
if (e.agentInvoked) e.respondWith(result); // pass the PROMISE, not a value
});
```

- Optional UX (verbatim from Chrome docs): style agent activity with
`form:tool-form-active` / `:tool-submit-active` CSS pseudo-classes.
- Forms that navigate to a thank-you page: `executeTool` returns `null` on
navigation (expected). A JSON-LD `{"@type":"Message","text":"…"}` block on the
target page is best-effort garnish — the mechanism is still under spec debate;
never make behavior depend on it.

### Framework notes — React

- Vendor `templates/webmcp-jsx.d.ts` alongside the ambient types so strict TSX
accepts `toolname`/`tooldescription`/`toolparamdescription` (it augments the
React attribute interfaces; it is a MODULE file — keep it separate from
`webmcp.d.ts`).
- The typings are string-valued (boolean-attribute style): write
`toolautosubmit=""` — and only on pure read forms (ground rule 5).
- In `onSubmit`, the WebMCP fields live on the NATIVE event:

```tsx
const native = e.nativeEvent as SubmitEvent;
if (native.agentInvoked) native.respondWith?.(doSubmit(new FormData(e.currentTarget))
.then(() => 'Request received. Reply within one business day.'));
```

Pass the PROMISE of the result string, not an already-resolved value.

## Imperative — SPAs and dynamic apps

Use the vendored runtime (`runtime.md`). Tools live in a dedicated module per app
(e.g. `src/webmcp/tools.ts`), decoupled from components:

```ts
import { createToolScope, dispatchAndWait } from './webmcpify';

export const searchTicketsTool = {
name: 'search_tickets',
description: 'Searches tickets in the currently open project and shows results on screen.',
inputSchema: {
type: 'object',
properties: {
query: { type: 'string', description: 'Search terms, exactly as the user phrased them.' },
},
required: ['query'],
},
annotations: { readOnlyHint: true, untrustedContentHint: true },
async execute(input: Record<string, unknown>) {
const q = String(input.query ?? '').trim();
if (!q) return 'ERROR: `query` must be a non-empty string.';
return dispatchAndWait('webmcp:search_tickets', { query: q });
},
};
```

Key rules:
- **`execute()` wraps the existing UI code path** — dispatch the same event / call
the same store action / hit the same API the button does. Never a parallel
implementation.
- **Return only after the interface state is settled**: the component listener
awaits the real work, then fires the completion event with the outcome payload
(`{ ok, message | error }`) — full contract and component example in
`runtime.md`. A canned success before the work finishes is a false green.
- Return short strings; errors as `"ERROR: <what and how to fix>"` so the model can
self-correct. Cap outputs ~1.5k chars.
- Validate strictly in code, loosely in schema — and keep **parity with the
form's native HTML constraints**: when a tool wraps a form, probe the real
constraints on a detached clone instead of re-implementing them —
`const probe = emailInput.cloneNode() as HTMLInputElement; probe.value = value;`
then reject when `!probe.checkValidity()`. `execute()` must refuse exactly what
the form itself would refuse.

### Registration & lifecycle

- **Static registration is the default**: register app-wide tools once at bootstrap.
- **Per-view registration only** for tools meaningless outside their view — via
`createToolScope` in the view's mount/unmount (React `useEffect` cleanup, Vue
`onUnmounted`, Angular `DestroyRef`). Over-scoping makes the toolset flicker and
strands agents mid-plan.
- Registration failures roll back the scope and surface via `onError` — check the
console during integration; a silently missing toolset usually means a duplicate
name or invalid schema rejected the batch.

### Auth / roles (SaaS)

Never register a tool the current session couldn't use through the UI. On
login/logout/role change/tenant switch: dispose the scope and re-register the
correct set (`runtime.md` §Wiring). The server still re-checks everything (ground
rule 3) — role-scoped registration is UX hygiene, not security.

## Origin trial / flags note

WebMCP is a Chrome origin trial (149→, stable milestone still an estimate). For
production exposure the origin needs a token:
`<meta http-equiv="origin-trial" content="TOKEN">` or an `Origin-Trial` response
header — registered at the Chrome Origin Trials console. For local work,
`chrome://flags/#enable-webmcp-testing`. Chrome **silently ignores** expired
tokens, so nothing may depend on WebMCP being present (ground rule 4). Add a short
note about this to the target repo's README as part of setup, and record the
touched file path in `pipeline.setup.originTrialNoted` (e.g. `["README.md"]`).
Loading
Loading