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
14 changes: 14 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,18 @@ reconstruct them from git history.

## [Unreleased]

## [0.5.0] — 2026-09-09

- Added an agent-launched, dependency-free visual WebMCP Workbench with the
WebMCPify palette, responsive desktop/phone layouts, manifest comparison,
schema-driven arguments, structured results, mutation confirmation, and
explicit `Native` versus `Simulated` evidence. The portable simulation is
development-only and never counts as native verification.
- Added the CG draft's `consequentialHint` across ambient types, inventory,
integration, security, manifest examples, and verification so significant
real-world or non-reversible effects are signaled without weakening the
application's actual safety boundaries. Verification records the dated Chrome
150 omission instead of falsely claiming native enumeration support.
- Added native/stub I/O compat: the harness uses an explicit adapter mode —
stub `tool.execute(object)` or spec-shaped `mc.executeTool(object)` when
`mc.__webmcpStubObjectMode` is set, native `mc.executeTool(JSON string)` otherwise
Expand Down Expand Up @@ -45,3 +57,5 @@ reconstruct them from git history.
agent skill rather than an MCP server.

[0.4.0]: https://github.com/TueJon/webmcpify/releases/tag/v0.4.0
[0.5.0]: https://github.com/TueJon/webmcpify/compare/v0.4.0...v0.5.0
[Unreleased]: https://github.com/TueJon/webmcpify/compare/v0.5.0...HEAD
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ Open your agent in the target repo and pick your scope:
/webmcpify # full pipeline
/webmcpify inventory # just investigate + propose the tool manifest (zero code changes)
/webmcpify integrate # integrate the approved manifest
/webmcpify workbench # agent launches the temporary visual tool inspector
/webmcpify verify # verify + heal what's integrated
/webmcpify status # where are we? what's next?
/webmcpify full parity # census every interactive element on every authenticated route
Expand Down Expand Up @@ -145,8 +146,8 @@ Every phase is a **loop over persistent state**, not a one-shot pass:
| Path | Purpose |
|---|---|
| [`skills/webmcpify/SKILL.md`](skills/webmcpify/SKILL.md) | The pipeline (what your agent follows) |
| [`skills/webmcpify/references/`](skills/webmcpify/references/) | Phase guides: inventory, integrate, runtime, verify, heal, security, discovery |
| [`skills/webmcpify/templates/`](skills/webmcpify/templates/) | Vendorable runtime (TS + JS), ambient types, Playwright verification template, discovery manifest |
| [`skills/webmcpify/references/`](skills/webmcpify/references/) | Phase guides: inventory, integrate, Workbench, runtime, verify, heal, security, discovery |
| [`skills/webmcpify/templates/`](skills/webmcpify/templates/) | Vendorable runtime (TS + JS), temporary visual Workbench, ambient types, Playwright verification template, discovery manifest |

## Status

Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,10 @@
},
"scripts": {
"typecheck": "tsc -p tsconfig.templates.json && tsc -p tsconfig.spec.json && tsc -p tsconfig.jsx.json",
"syntaxcheck": "node --check skills/webmcpify/templates/webmcpify.js",
"syntaxcheck": "node --check skills/webmcpify/templates/webmcpify.js && node --check skills/webmcpify/templates/webmcp-workbench.js && node --check skills/webmcpify/scripts/workbench.mjs",
"test": "node --test \"tests/*.test.mjs\"",
"check": "npm run typecheck && npm run syntaxcheck && npm test",
"workbench:verify": "node tests/workbench-browser.mjs",
"proof:verify": "xvfb-run -a node proof/demo/run.mjs --verify",
"proof:record": "xvfb-run -a node proof/demo/run.mjs --record"
},
Expand Down
7 changes: 6 additions & 1 deletion proof/demo/run.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,11 @@ try {
assert(tool);
assert.equal(tool.annotations.readOnlyHint, false);
assert.equal(tool.annotations.untrustedContentHint, false);
assert.equal(
tool.annotations.consequentialHint,
undefined,
'Chrome compatibility changed: update the dated consequentialHint evidence and harness expectation',
);
assert.deepEqual(JSON.parse(tool.inputSchema), {
type: 'object',
properties: { category: { type: 'string', enum: ['all', 'feature', 'fix'] } },
Expand Down Expand Up @@ -172,7 +177,7 @@ try {
await rename(generated, sourceVideo);
console.log(`recorded ${sourceVideo}`);
}
console.log(`proof verified in Chrome ${chromeVersion}: native getTools/executeTool, schema, annotations, UI delta, bounded invalid input, cleanup`);
console.log(`proof verified in Chrome ${chromeVersion}: native getTools/executeTool, schema, annotations (consequentialHint omitted by this build), UI delta, bounded invalid input, cleanup`);
} finally {
await browser?.close().catch(() => {});
await new Promise((resolve) => server.close(resolve));
Expand Down
2 changes: 1 addition & 1 deletion proof/demo/webmcp-tools.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ window.addEventListener('webmcpify:integrate', () => {
name: 'set_release_filter',
description: 'Filters the visible synthetic release notes by category using the page existing filter path.',
inputSchema: schema,
annotations: { readOnlyHint: false, untrustedContentHint: false },
annotations: { readOnlyHint: false, untrustedContentHint: false, consequentialHint: false },
execute: ({ category }) => {
if (!schema.properties.category.enum.includes(category)) {
return 'ERROR: category must be one of all, feature, or fix.';
Expand Down
2 changes: 1 addition & 1 deletion proof/manifest.after.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
"required": ["category"],
"additionalProperties": false
},
"annotations": { "readOnlyHint": false, "untrustedContentHint": false },
"annotations": { "readOnlyHint": false, "untrustedContentHint": false, "consequentialHint": false },
"source": ["proof/demo/app.js:applyFilter"],
"route": "/",
"auth": ["none"],
Expand Down
2 changes: 1 addition & 1 deletion proof/manifest.before.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
"required": ["category"],
"additionalProperties": false
},
"annotations": { "readOnlyHint": false, "untrustedContentHint": false },
"annotations": { "readOnlyHint": false, "untrustedContentHint": false, "consequentialHint": false },
"source": ["proof/demo/app.js:applyFilter"],
"route": "/",
"auth": ["none"],
Expand Down
3 changes: 2 additions & 1 deletion release/v0.5.0-directory-update.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ core coverage or route-by-route parity; it inventories a reviewable tool manifes
integrates only approved tools through the app's existing UI paths, and verifies and
heals them in real headed Chrome. v0.5.0 adds secure-origin/CORS gates,
policy-backed coverage evidence, structured results for route-changing tools and
failure-aware retries.
failure-aware retries. It also adds a temporary visual Workbench, explicit
native/stub I/O compatibility, and the CG draft's consequential-action hint.

Canonical source:
<https://github.com/TueJon/webmcpify/tree/main/skills/webmcpify>
Expand Down
20 changes: 16 additions & 4 deletions release/v0.5.0.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,4 @@
# webmcpify v0.5.0 — draft release notes

Status: prepared, not published. Re-check the dated client claims and run the
release checklist before creating a GitHub release or directory update.
# webmcpify v0.5.0

webmcpify now asks for a coverage target up front:

Expand All @@ -26,6 +23,18 @@ The rest of the pipeline is stricter where real integrations failed:
counter after an approved contract revision.
- The verification template requires a secure env-provided origin, a dedicated
Chrome profile and a real headed browser environment.
- A temporary, agent-launched WebMCP Workbench now compares approved manifest
tools with live registrations, builds schema-driven examples, and labels every
result as Native or Simulated. Simulated evidence never counts as verification.
- The current CG draft's `consequentialHint` is carried through inventory,
ambient types, integration guidance and verification. It signals significant
real-world or non-reversible effects without replacing application-enforced
authorization, confirmation, idempotency or replay protection. Chrome's current
documentation includes the hint, but the local Chrome 150 proof accepted it at
registration and still omitted it from `getTools()`; native propagation is not
claimed until the target browser actually returns the field.
- Native Chrome's stringified I/O and spec-shaped object stubs now use an explicit
adapter mode, avoiding heuristic retries that could execute a mutation twice.

The skill also documents ChatGPT's **Site tools** surface separately from Chrome
developer verification. Availability details are dated 2026-08-31 and link to the
Expand All @@ -38,3 +47,6 @@ Compatibility note: no public source or measured client run establishes a univer
safe tool count per page. Parity is an auditable coverage target, not a promise that
every client will accept an arbitrarily large route toolset; client-capacity gaps are
reported honestly.

Thanks to [@arnabwithab](https://github.com/arnabwithab) for the native/stub I/O
compatibility work in [#13](https://github.com/TueJon/webmcpify/pull/13).
29 changes: 26 additions & 3 deletions skills/webmcpify/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
---
name: webmcpify
description: WebMCP agent skill for curated core coverage or route-by-route parity — inventory an existing web app, integrate approved tools, then verify and heal them in a real browser. Use for "webmcpify", "add WebMCP", or "expose app actions to AI agents".
argument-hint: "[inventory|integrate|verify|status|full] [scope notes]"
description: WebMCP agent skill for curated core coverage or route-by-route parity — inventory an existing web app, integrate approved tools, then inspect, verify and heal them in a real browser. Use for "webmcpify", "add WebMCP", or "expose app actions to AI agents".
argument-hint: "[inventory|integrate|workbench|verify|status|full] [scope notes]"
license: MIT
tags:
- webmcp
Expand Down Expand Up @@ -43,6 +43,7 @@ The user may pass an argument (`/webmcpify <mode>` or plain words):
| *(none)* or `full` | all phases, resuming from current manifest state | done |
| `inventory` / `map` | DETECT + INVENTORY loops only — **zero code changes** | present the manifest table for review |
| `integrate` | INTEGRATE loop only (requires approved tools in the manifest) | integrated + built |
| `workbench` / `inspect` | launch the temporary visual inspector for approved/integrated tools | user closes the session |
| `verify` | VERIFY + HEAL loops on integrated/verified tools | green/skipped report |
| `status` | read `.webmcpify/manifest.json` — **read-only** | report phase, per-status tool counts, and the recommended next command |

Expand All @@ -64,6 +65,9 @@ Any other text is scoping guidance (e.g. "only the checkout area", "read-only to
a credential, token, key, JWT, signed URL, or cookie; and no irreversible delete
except by opening the app's own confirmation UI. Creating or changing ordinary
product objects is in scope and must not be mistaken for account creation.
Mark a tool with `consequentialHint: true` when execution has a significant
real-world or non-reversible effect. This is agent metadata, never a replacement
for the application's authorization, confirmation, idempotency, or replay guards.
3. **The server is the only trust boundary.** A tool's `execute()` may only call code
paths the UI already uses (same endpoints, same validation, same auth). Never
create new endpoints, never bypass existing checks, never put secrets in tools.
Expand All @@ -79,6 +83,9 @@ Any other text is scoping guidance (e.g. "only the checkout area", "read-only to
7. **Commits are opt-in.** Never commit unless the human chose a commit policy at
the gate (see below). Without git or without permission, leave changes in the
working tree and record progress in the manifest only.
8. **Workbench evidence is explicit.** The optional visual Workbench is development-
only and agent-launched (`references/workbench.md`). It must always label evidence
`Native` or `Simulated`; simulated calls never satisfy native verification.

## Fresh, authoritative guidance

Expand Down Expand Up @@ -205,7 +212,7 @@ Manifest schema (Webmcpify Manifest v4):
"priority": 1, // 1 = expose first; 2/3 = later waves
"description": "Creates a new ticket in the currently open project.",
"inputSchema": { /* JSON Schema */ },
"annotations": { "readOnlyHint": false, "untrustedContentHint": false }, // verify asserts these on the enumerated tool
"annotations": { "readOnlyHint": false, "untrustedContentHint": false, "consequentialHint": false }, // verify asserts all recorded hints
"source": ["src/features/tickets/NewTicket.tsx:42"], // the UI code path it wraps
"route": "/projects/demo/tickets", // where verify navigates
"auth": ["role:member"], // "none" | "session" | ["role:<name>", ...] — keys into app.authFixtures; verify runs once per listed role
Expand Down Expand Up @@ -367,6 +374,21 @@ a published manifest, and flag one **the pipeline created or modified** since
`baselineSha` as an unmapped hunk (a pre-existing, untouched manifest is not your
hunk — leave it alone).

## Optional — WORKBENCH (visual inspection)

When the user asks to inspect or try tools visually, read
`references/workbench.md` and launch the temporary Workbench yourself. Prefer its
Playwright runner: it injects before application code, requires no extension,
flag, command, or project edit from the user, and cleans up when the browser
closes. The panel must visibly say `Native` or `Simulated`.

Use the approved manifest as Expected evidence and the live page as Observed
evidence. Simulated mode is useful for portable browser/device and responsive
checks, but never changes a tool to `verified`; the normal headed native-browser
loop below remains authoritative. Never ship Workbench in a production entry or
bundle. If a physical device requires temporary dev-entry wiring, the agent adds
and removes it within the same inspection session.

## Phase 3 — VERIFY (loop)

Set up once from `templates/webmcp.spec.ts` per `references/verify.md` (real headed
Expand Down Expand Up @@ -439,6 +461,7 @@ scope collisions).
- `references/inventory.md` — area mapping, naming/schema conventions, budgets/overlap
- `references/integrate.md` — declarative + imperative patterns per stack
- `references/runtime.md` — vendoring + wiring the `templates/` runtime
- `references/workbench.md` — zero-setup visual inspection and evidence modes
- `references/verify.md` — harness setup: flags, surfaces, Playwright/Puppeteer, evals
- `references/heal.md` — failure taxonomy → fixes
- `references/discovery.md` — optional off-page discovery (manifest, `rel="webmcp"`,
Expand Down
6 changes: 5 additions & 1 deletion skills/webmcpify/references/integrate.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ export const searchTicketsTool = {
},
required: ['query'],
},
annotations: { readOnlyHint: true, untrustedContentHint: true },
annotations: { readOnlyHint: true, untrustedContentHint: true, consequentialHint: false },
async execute(input: Record<string, unknown>) {
const q = String(input.query ?? '').trim();
if (!q) return 'ERROR: `query` must be a non-empty string.';
Expand All @@ -101,6 +101,10 @@ export const searchTicketsTool = {
> **Native I/O compat** — `getTools()` returns `inputSchema` stringified on native Chrome but as object in stubs — handle both (`typeof === 'string' ? JSON.parse : id`). `executeTool` needs `JSON.stringify(args)` on native lag, object per spec. For `validate:true`, register with `inputSchema` only. Runner LLM envelope: `const raw=t.inputSchema; const schema=typeof raw==='string'?JSON.parse(raw):raw??{type:'object',properties:{}}; const llmTool={function:{parameters:schema}}` — never pass `parameters` through WebMCP.

Key rules:
- **Annotations describe risk; they do not enforce it.** Use
`consequentialHint: true` only for significant real-world or non-reversible
effects, and retain the app's real confirmation, authorization, idempotency,
and replay boundaries.
- **`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.
Expand Down
28 changes: 18 additions & 10 deletions skills/webmcpify/references/inventory.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,15 +44,15 @@ too big; merge trivial ones.

Walk each area's UI code and list **user actions**, not functions:

| UI pattern | Candidate tool | `mutating` | `readOnlyHint` |
|---|---|---|---|
| Search/filter form or input | `search_<noun>` | false | true |
| Data list/detail currently rendered | `list_<noun>` / `get_<noun>` | false | true |
| Create/edit form with submit → API call | `create_<noun>` / `update_<noun>` | "server" | — |
| Button triggering a server state change | `<verb>_<noun>` | "server" | — |
| Preference/theme/localStorage toggle | `<verb>_<noun>` | "client" | — |
| Multi-step flow (wizard, checkout) | `start_<noun>_flow` (initiation) | false* | **never** |
| Contact/booking form (static sites) | declarative form annotation | "server" | — |
| UI pattern | Candidate tool | `mutating` | `readOnlyHint` | `consequentialHint` |
|---|---|---|---|---|
| Search/filter form or input | `search_<noun>` | false | true | false |
| Data list/detail currently rendered | `list_<noun>` / `get_<noun>` | false | true | false |
| Create/edit form with submit → API call | `create_<noun>` / `update_<noun>` | "server" | — | classify by effect |
| Button triggering a server state change | `<verb>_<noun>` | "server" | — | classify by effect |
| Preference/theme/localStorage toggle | `<verb>_<noun>` | "client" | — | false |
| Multi-step flow (wizard, checkout) | `start_<noun>_flow` (initiation) | false* | **never** | false* |
| Contact/booking form (static sites) | declarative form annotation | "server" | — | true |

*Initiation tools only navigate/open the flow — the human completes it. They are
classified non-mutating (no data changes) **but must NOT carry `readOnlyHint`**:
Expand All @@ -66,6 +66,12 @@ dev/test-data-only verification; `"client"` may be batch-approved at the gate
(`cleanup` recommended). `toolautosubmit` is banned for **both** mutation classes
(ground rule 5).

`consequentialHint` is a separate risk dimension, not a synonym for `mutating`.
Set it only when execution has a significant real-world or non-reversible effect
(for example sending, publishing, booking, payment, permission change, or deletion).
An initiation tool that only opens the application's real confirmation UI remains
`false`; the final action stays outside the tool. The hint does not enforce safety.

**Policy gates — use these exact classes.** Exclude auth/login/session/password/
MFA/SSO; signup/registration/payment/billing/subscription; any tool that returns a
credential, token, key, JWT, signed URL or cookie; and irreversible deletion except
Expand Down Expand Up @@ -121,6 +127,8 @@ Agents degrade when many similar tools compete. Enforce while drafting:
(`"High"`, not `priority_id: 3`).
- Tools returning user-generated or external content get
`untrustedContentHint: true`.
- Consequential tools get `consequentialHint: true` and retain every existing
application-side confirmation, authorization, idempotency, and replay guard.

## Choosing `kind`

Expand All @@ -138,7 +146,7 @@ Fill EVERY field of the v4 schema:

- `route` + `auth` (array of roles keying into `app.authFixtures`; verify runs
once per role).
- `annotations` — `readOnlyHint`/`untrustedContentHint` per the candidate table;
- `annotations` — `readOnlyHint`/`untrustedContentHint`/`consequentialHint` per the candidate table;
verify asserts them on the enumerated tool.
- `examples` — one valid + one invalid. `invalid: null` is allowed ONLY for
readOnly tools with no/empty params (verify then asserts dual-outcome); the
Expand Down
3 changes: 3 additions & 0 deletions skills/webmcpify/references/security.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,9 @@ both directions. Design every tool as if it were a public, authenticated API end
confirmation based on it; mislabeling is the worst single mistake).
- [ ] `untrustedContentHint: true` on every tool returning user-generated or
external content.
- [ ] `consequentialHint: true` on every tool whose execution has a significant
real-world or non-reversible effect. It is a client signal, not enforcement:
preserve application authorization, confirmation, idempotency and replay guards.
- [ ] Outputs capped (~1.5k chars) and free of instruction-like content where
possible.

Expand Down
Loading
Loading