# 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".

- **Type:** Skill
- **Install:** `agentstack add skill-tuejon-webmcpify-webmcpify`
- **Verified:** Yes — security-reviewed for prompt injection and unsafe behavior
- **Seller:** [TueJon](https://agentstack.voostack.com/s/tuejon)
- **Installs:** 0
- **Category:** [Web & Browser](https://agentstack.voostack.com/c/web-and-browser)
- **Latest version:** 0.1.0
- **License:** MIT
- **Upstream author:** [TueJon](https://github.com/TueJon)
- **Source:** https://github.com/TueJon/webmcpify/tree/main/skills/webmcpify

## Install

```sh
agentstack add skill-tuejon-webmcpify-webmcpify
```

Requires the [AgentStack CLI](https://agentstack.voostack.com/docs/cli). Works with Claude Code, Cursor, and any MCP-compatible agent.

## About

# webmcpify — make any web app agent-ready, verifiably

You are running the webmcpify pipeline. It takes an existing web application and
exposes its user-facing functionality as [WebMCP](https://webmachinelearning.github.io/webmcp/)
tools (`document.modelContext` — a proposed web standard incubated in the W3C Web
Machine Learning Community Group, currently a Chrome origin trial), so browser AI
agents can operate the app through structured tool calls instead of guessing at the DOM.

```
DETECT ──▶ INVENTORY ──▶ [HUMAN GATE: manifest approval] ──▶ INTEGRATE ──▶ VERIFY ──▶ HEAL ──▶ AUDIT
              ▲  loop            per-area batches on big apps    ▲  loop      ▲ loop    ▲ loop
              └── per area                                       └── per manifest entry ──┘
```

Everything you need ships inside this skill directory: phase guides in
`references/`, and vendorable code in `templates/` (runtime, ambient types,
JS variant, React JSX typings, verification spec). Never assume files exist
outside the skill dir.

**Out of scope** (stop and say so): backend-only MCP servers (that's classic MCP,
not WebMCP), automating third-party sites you don't control, and generic SEO work.

## Invocation modes

The user may pass an argument (`/webmcpify ` or plain words):

| Argument | Run | Stop at |
|---|---|---|
| *(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 |
| `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 |

Any other text is scoping guidance (e.g. "only the checkout area", "read-only tools only").

## Ground rules (non-negotiable, enforce in every phase)

1. **Zero unrelated changes.** Every diff hunk you produce must trace to a manifest
   entry or the recorded one-time setup. Never refactor, reformat, rename, or
   "improve" anything else — note problems in the report instead. Files that were
   already dirty at baseline (recorded in the manifest) are **untouchable**: never
   modify or revert them.
2. **Read-only tools first.** Mutations are tri-state: `mutating: false`,
   `"client"` (browser-local only: prefs, localStorage), or `"server"` (data
   leaves the browser). Server-mutating tools require explicit **per-tool** human
   approval recorded in the manifest; client-mutating tools may be approved as a
   batch at the gate. Never expose destructive, irreversible, or payment actions
   in a first integration.
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.
4. **Spec-shaped and dependency-free.** Register via `document.modelContext.registerTool()`
   with AbortSignal lifecycle (feature-detect the deprecated `navigator.modelContext`
   fallback). No third-party WebMCP runtime dependencies. Everything feature-detected:
   the app behaves identically in browsers without WebMCP.
5. **Never `toolautosubmit` on state-changing forms** — neither `mutating: "client"`
   nor `"server"`. Only on pure read forms (search, filter, availability).
6. **State lives in files, not in your context.** Read/write `.webmcpify/` constantly;
   assume your context can be wiped between any two steps. Write the manifest
   atomically (write `manifest.json.tmp`, then rename over `manifest.json`).
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.

## Fresh, authoritative guidance

WebMCP is an evolving origin-trial API — the surface has already changed during the
trial (testing API removed 2026-07; `navigator` → `document`). Before Phase 2, if
network is available, pull Google's current official guides rather than relying on
memory:

```sh
npx -y modern-web-guidance@latest retrieve "webmcp,agentic-forms,agentic-javascript-tools"
```

If offline, use `references/integrate.md` — but prefer the live guides when they conflict.

## The state protocol — `.webmcpify/` in the target repo

| File | Purpose |
|---|---|
| `manifest.json` | Single source of truth (schema below; atomic writes) |
| `areas/.tools.json` | Sub-agent shard output during inventory fan-out (merged, then deleted) |
| `report.md` | Human-facing running report; finalized at the end |

**Resume rule:** if `manifest.json` exists, resume — recompute nothing already
recorded. **Merge leftover shards FIRST**: any existing `areas/.tools.json`
files are merged into the manifest (mark those areas `inventoried`, delete the
shards) before redispatching any sub-agents. Then continue at `pipeline.phase`,
the first `pending` area, or the first tool whose status is not terminal.
Terminal statuses: `verified`, `skipped`, `rejected`.

**Phase transitions** (make the atomic manifest write the moment the condition holds):

- `detect → inventory`: `app` recorded, `baselineSha`/`baselineDirty` captured.
- `inventory → gate`: no area `pending`, completeness pass has run.
- `gate → integrate`: every `discovered` tool is `approved`/`rejected`, and
  `commitPolicy` + `commitWebmcpifyDir` are set.
- `integrate → verify`: no `approved` tools remain (each `integrated` or terminal),
  build green.
- `verify → heal`: verify loop visited every `integrated` tool and ≥1 is `failed`
  (none failed → straight to `audit`).
- `heal → audit`: no tool `failed` and post-heal full re-verify passed.
- `audit → done`: every hunk mapped-or-flagged, `report.md` finalized.

Manifest schema (Webmcpify Manifest v3):

```jsonc
{
  "webmcpify": 3,
  "app": { "stack": "react-vite", "typescript": true, "entry": "src/main.tsx",
           "baseUrl": "http://localhost:5173", "startCommand": "npm run dev",
           "authFixtures": {                    // how verify OBTAINS each session
             "member": { "obtain": "npm run seed:test-user, then sign in at /login",
                         "account": "member@example.test",
                         "env": ["TEST_MEMBER_PASSWORD"] }  // env var NAMES only — never secret values
           } },
  "pipeline": {
    "phase": "inventory",          // detect|inventory|gate|integrate|verify|heal|audit|done — transition rules above
    "setup": {                     // PATHS created/modified per one-time setup step ([] = not done yet)
      "runtimeVendored": ["src/webmcp/webmcpify.ts", "src/webmcp/webmcp.d.ts"],
      "harnessInstalled": [".webmcpify/webmcp.spec.ts"],
      "originTrialNoted": ["README.md"]
    },
    "baselineSha": "abc1234",      // HEAD at pipeline start; null if no git
    "baselineDirty": ["src/wip.ts"], // paths dirty at start — untouchable (ground rule 1)
    "commitPolicy": null,          // set at the gate: "commit-per-batch" | "no-commit"
    "commitWebmcpifyDir": null,    // set at the gate: commit .webmcpify/ itself? true | false
    "blockers": []                 // e.g. "app won't start locally: needs $API_KEY" — surfaced at the gate
  },
  "areas": [
    { "id": "checkout", "paths": ["src/features/checkout/"], "status": "pending" } // pending|inventoried
  ],
  "tools": [
    {
      "id": "create_ticket",
      "area": "tickets",
      "kind": "imperative",        // imperative | declarative
      "mutating": "server",        // false | "client" (browser-local only: prefs, localStorage) | "server" (data leaves the browser)
      "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
      "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:", ...] — keys into app.authFixtures; verify runs once per listed role
      "examples": { "valid": { "title": "Test ticket" }, "invalid": {} },
                                   // invalid: null ONLY for readOnlyHint tools with no/empty params —
                                   // verify then asserts dual-outcome: rejects OR resolves with no side effect
      "expect": { "result": "created", "navigation": null, "ui": "new row appears in the ticket list" },
                                   // exactly one of result|navigation: result = substring of the resolved string;
                                   // navigation = destination URL/pattern when executeTool resolves null (it navigated)
      "cleanup": "delete the created ticket via the UI's own delete path (test data only)", // required for mutating:"server", recommended for "client"
      "status": "discovered",      // discovered|approved|rejected*|integrated|verified*|failed|skipped*  (* = terminal)
      "approval": null,            // server-mutating tools, once approved: { "note": "...", "at": "2026-07-12",
                                   //   "productionSideEffect": null } — set only when verification unavoidably
                                   //   causes a real production effect (see VERIFY: production side-effect policy)
      "attempts": 0,               // heal-fix cycles; the triggering verify failure is attempt 0
      "batchCommit": null,         // sha under commit-per-batch — lands in the manifest one commit LATER
      "notes": ""
    }
  ],
  "log": [ "2026-07-12 inventory: area checkout done, 4 candidates" ]
}
```

**v2→v3 migration:** resuming a `"webmcpify": 2` manifest migrates in place on
first write — `auth` string → array; `setup` booleans → path arrays (`false` →
`[]`; `true` → recover paths from git/`log`, else `null` = done-but-unrecorded,
audit treats those files flag-only); `mutating: true` → `"server"`; add
`annotations` (defaults from the inventory table), `blockers: []`,
`commitWebmcpifyDir: null`, `expect.navigation: null`; then bump to 3.

## Phase 0 — DETECT

Identify stack, build + dev-server commands, TypeScript or not, auth model
(including how verify obtains each test session → `app.authFixtures`), test
setup, and how the app starts locally; record under `app`. Record the git baseline:
`pipeline.baselineSha` = current HEAD and `pipeline.baselineDirty` = `git status
--porcelain` paths (both `null`/`[]` without git). If the app cannot be started
locally, append the blocker to `pipeline.blockers` — integration may proceed, but
verification will be blocked and this must be surfaced at the gate. Details:
`references/inventory.md`.

## Phase 1 — INVENTORY (loop; scales to any size)

**Never map a large codebase in one pass.**

1. **Area map first (cheap, structural):** enumerate routes/views/feature modules
   from the router config, pages directory, or navigation — without reading
   implementation files. Write every area to `areas` with `"pending"`.
2. **Inventory loop — one area per iteration:** deep-read only that area's files;
   draft a candidate tool per user action (conventions, tool-count budget, and
   overlap rules: `references/inventory.md`) with ALL manifest fields filled,
   including `route`, `auth`, `annotations`, `examples`, `expect`, and `cleanup`
   (required for `mutating: "server"`, recommended for `"client"`) — the verify
   phase runs from these fields alone. Append as `"discovered"`, mark the area
   `"inventoried"`, write the manifest, repeat.
   - **Sub-agent fan-out:** sub-agents never write `manifest.json`. Each writes only
     its own `areas/.tools.json` shard — schema
     `{ "webmcpifyShard": 3, "area": "", "tools": [ /* full v3 tool entries */ ] }`,
     written atomically (tmp + rename). You (the coordinator) merge shards into
     the manifest sequentially, then delete them; on resume, merge existing
     shards FIRST before redispatching (Resume rule).
3. **Exit:** no `pending` areas remain, plus one completeness pass — walk the app's
   navigation and ask "is any visible user action missing?"

## GATE — manifest approval (the one main checkpoint)

Present the manifest compactly (id, area, kind, mutating, priority, one-line
description) — per-area batches on large apps. Ask the human to decide, in one
exchange where possible:

1. Which tools are `approved` vs `rejected` (**`rejected` is terminal** — rejected
   tools are excluded from every later phase and from exit conditions).
   `mutating: "server"` tools need individual acknowledgment → record in
   `approval`; `mutating: "client"` tools may be approved as a batch.
2. **Commit policy**: `commit-per-batch` (each integration batch committed,
   revertable — recommended on a clean baseline) or `no-commit` (leave changes
   uncommitted for the human to review/commit) → `pipeline.commitPolicy`. Also
   whether `.webmcpify/` itself should be committed (recommended: yes — it
   documents the integration) → `pipeline.commitWebmcpifyDir`.
3. Every entry in `pipeline.blockers` (e.g. app won't start). If verifying a tool
   will unavoidably cause a real production side effect (e.g. a mailer with an
   Origin-allow-listed endpoint), get that approved HERE and record it in the
   tool's `approval.productionSideEffect` — see VERIFY.

Apply `references/security.md` to every mutating tool **before** presenting.

## Phase 2 — INTEGRATE (loop)

One-time setup first — record the created/modified file **paths** in
`pipeline.setup` (e.g. `runtimeVendored: ["src/webmcp/webmcpify.ts", ...]`):
vendor the runtime from this skill's `templates/` (`webmcpify.ts`, or
`webmcpify.js` for non-TS projects, plus `webmcp.d.ts` for TS and
`webmcp-jsx.d.ts` for React TSX — keep the full MIT header; see
`references/runtime.md`) and note the origin-trial/flag requirement in the target
README (`originTrialNoted`). Then loop:

1. Pick the next batch of `approved` tools — one area or ≤5 tools.
2. Implement per `references/integrate.md`: declarative attributes for standard
   HTML forms (including framework-rendered and fetch-intercepted ones);
   imperative registration via the vendored runtime for non-form or
   controlled-state actions.
3. Build + typecheck; fix only what the batch broke.
4. Mark tools `"integrated"`, write the manifest. Under `commit-per-batch`:
   require a **clean index** before staging (unrelated staged changes → stop and
   surface); stage **only the batch's files by path** — never `git add -A`, `-u`,
   `.`, or `commit -a`; commit `feat(webmcp): expose  (webmcpify)`. The
   commit sha lands in `batchCommit` on the **next** manifest write — one commit
   later (the manifest can't contain its own commit's sha). Never amend a
   previous batch commit.
5. Repeat until no `approved` tools remain.

## Phase 3 — VERIFY (loop)

Set up once from `templates/webmcp.spec.ts` per `references/verify.md` (real headed
Chrome; production `getTools()`/`executeTool()` surface with legacy fallback probe).
Then loop over every `integrated` tool, using its manifest `route`, `auth`,
`examples`, `expect`, and `annotations` fields:

- assert the tool is registered with the expected schema (enumerated `inputSchema`
  is a *stringified* JSON Schema — parse before comparing) **and** the manifest
  `annotations`;
- execute the valid example (mutating tools: dev/test data only, then run
  `cleanup`) and one invalid example (`invalid: null` zero-param read tools:
  dual-outcome assertion — see `references/verify.md`);
- assert on the returned result **and** the resulting UI state per `expect`
  (a UI **delta**, or `expect.navigation` when execu

…

## Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

- **Author:** [TueJon](https://github.com/TueJon)
- **Source:** [TueJon/webmcpify](https://github.com/TueJon/webmcpify)
- **License:** MIT

Install and usage instructions live in the source repository linked above.

## Pricing

- **Free** — Free

## Security capabilities

Automated source analysis of v0.1.0 — what this tool can access:

- **Network access:** no
- **Filesystem access:** no
- **Shell / process execution:** no
- **Environment & secrets:** no
- **Dynamic code execution:** no

*"Yes" means the capability is present in the source — more access means more to trust, not that it is unsafe.*


## Versions

- **0.1.0** — security scan: passed — Imported from the upstream source.

## Links

- Listing page: https://agentstack.voostack.com/l/skill-tuejon-webmcpify-webmcpify
- Seller: https://agentstack.voostack.com/s/tuejon
- Browse the marketplace: https://agentstack.voostack.com/browse

---
Listed on AgentStack — the marketplace for AI agent skills and MCP servers. Every listing is security-reviewed. Creators keep 70%.
