Agents keep guessing at forms that were built for people. Every wrong guess costs somebody a failed checkout or a broken signup. Toolsmith closes that gap without asking the site owner to write code, and without ever letting an agent approve its own work.
Paste a URL. Toolsmith reads the page, proposes one WebMCP tool per form, drafts a webmcp.js file, and waits. Nothing activates until a person clicks Approve. A receipt comes back, and the file downloads.
There is no language model anywhere in the loop. Every generated line traces to a real field on a real page.
URL ──▶ profile_site ──▶ propose_tools ──▶ draft_manifest ──▶ [ Approve ] ──▶ verify_manifest_activation
read forms one tool/form webmcp.js + sha256 human click receipt + download
| Step | Tool | What it does |
|---|---|---|
| 1 | profile_site |
Reads the public structure of one page: title, headings, forms and their fields, top-level navigation. |
| 2 | propose_tools |
Turns the profile into candidate tools. GET forms become read-only tools. POST forms are marked consequential and get a confirm gate. |
| 3 | draft_manifest |
Emits the webmcp.js source for the chosen candidates, plus the SHA-256 of that exact text. |
| 4 | activate_manifest |
Shows the approval card and returns a pending request. This tool cannot approve anything on its own. |
| 5 | verify_manifest_activation |
Reads back the receipt and checks its digest against the manifest stored in the browser. |
All five are registered natively on document.modelContext with AbortSignal cleanup.
The only thing that can activate a manifest is a click on the Approve card. The page checks two properties of that event before it writes a receipt:
event.isTrustedmust be true, so a synthetic click from script does not count.navigator.userActivation.isActivemust be true, so the click happened inside a real user gesture.
The generated file carries the same discipline forward. Every consequential tool it registers wraps its submit in a browser confirm dialog, so an agent using the output still cannot post a form without a person in the loop.
-
Open chudi.dev/tools/toolsmith in Chrome 152 for Testing with the WebMCP flag enabled.
-
Ask the agent:
Make https://chudi.dev/toolsmith-fixtures/shop.html agent-ready. -
Watch the activity strip fill with
profile_site,propose_tools, anddraft_manifest. -
Click Approve.
-
Download the receipt-backed
webmcp.js.
Three fixtures are available for testing:
| Fixture | What it exercises |
|---|---|
| shop.html | A single POST add-to-cart form, so the tool gets a confirm gate |
| newsletter.html | A GET search form beside a POST subscribe form, one read-only tool and one gated tool |
| empty.html | No forms at all, so the proposal step returns nothing |
These files are extracted from the SvelteKit app that serves chudi.dev, at the commit deployed for the WebMCP Challenge (54bdf5ef0).
src/lib/toolsmith/
profile.ts read a page into a structured profile
propose.ts map forms to candidate tools
draft.ts deterministic webmcp.js template
register.ts register the five tools on document.modelContext
src/routes/tools/toolsmith/+page.svelte
the page, the activity strip, and the Approve card
src/routes/api/toolsmith/profile/+server.ts
server-side fetch and form profiling
static/toolsmith-fixtures/
shop.html, newsletter.html, empty.html
scripts/toolsmith-eval.mjs
zero-dependency eval runner (unit and end-to-end modes)
The eval runner has two modes:
node scripts/toolsmith-eval.mjs --unit
node scripts/toolsmith-eval.mjs --e2e --url <preview-base>
A chat window returns text. Toolsmith returns a file that runs on your own domain, and only you hold the click that activates it. Anyone can verify the result independently by calling verify_manifest_activation and reading the four fields of the receipt.