v0.1.2 · MIT · WebMCP Challenge 2026
Add one script tag.
Your website is agent-ready.
AgentReady.js reads the semantic HTML you already ship — forms, navigation, buttons, app state — and turns it into safe, structured WebMCP tools. Humans keep the same interface. Agents get a reliable one.
<script src="https://cdn.jsdelivr.net/npm/@willh/agentready@latest/dist/agentready.min.js" defer></script>
<form aria-label="Product search" method="get">
<label for="q">Search products</label>
<input id="q" name="q" type="search">
<label for="category">Category</label>
<select id="category" name="category">
<option>Keyboards</option>
<option>Mice</option>
</select>
<label for="max_price">Max price</label>
<input id="max_price" name="max_price"
type="number" min="0" max="2000">
<button>Search</button>
</form>
{
"name": "search_products",
"title": "Product search",
"inputSchema": {
"type": "object",
"properties": {
"q": { "type": "string" },
"category": { "type": "string",
"enum": ["Keyboards", "Mice"] },
"max_price": { "type": "number",
"minimum": 0, "maximum": 2000 }
}
},
"annotations": { "untrustedContentHint": true }
}
- 37 KB minified, 13 KB gzipped
- 0 runtime dependencies
- 7 core tools + synthesized form tools
- 108 automated tests, real Chrome E2E
- MIT licensed
The problem
Agents shouldn't have to guess.
Today an agent driving a website works from screenshots or a dumped DOM tree. It is slow, brittle, and expensive, and it still can't tell a search box from a newsletter signup.
WebMCP fixes this by letting a site declare its capabilities through
document.modelContext. But nobody is going to rewrite millions of existing
websites by hand.
AgentReady.js derives the tool layer from what is already there. Semantic HTML, labels, and ARIA attributes carry almost all the structure an agent needs. Ship them, and the tools follow.
How it works
Three passes, on every page load and every DOM change.
-
01
Discover
Walks the DOM for interactive elements, forms, headings and regions. Every target gets a stable ref held behind a
WeakRef, so SPA re-renders never leak or go stale. -
02
Synthesize
Each semantic form becomes one typed tool with a real JSON Schema, named from
aria-label, headings or the action URL. A search form becomessearch_products, not twentyset_input_17primitives. -
03
Expose
Tools are registered through native WebMCP when the browser has it, with an identical in-page
getTools()/executeTool()shim when it doesn't. Same page, any agent.
Tools
Seven core tools. Plus one per form.
Read tools are annotated readOnlyHint. Everything carries
untrustedContentHint. Output is clamped to about 1,500 characters per call so a
large table can never flood an agent's context.
| Tool | Type | What it does |
|---|---|---|
get_page_context | read | Semantic summary: title, headings, regions, forms, counts |
find_on_page | read | Natural-language search over interactive elements, returns stable refs |
read_target | read | Details of one ref: value, options, href, surrounding content |
activate_target | write | Click buttons, links, tabs. Destructive ones require human approval |
set_field | write | Set one input, select, checkbox or radio, firing real input and change events |
fill_form | write | Fill a whole form by label. Never submits |
submit_form | approval | Submit with explicit human approval in the on-page panel |
search_products, signup_form, … | auto | One synthesized tool per semantic form, with a real JSON Schema. Up to eight per page |
Progressive enhancement
Start with nothing. Add precision when you want it.
Do nothing else.
Load the script. AgentReady discovers the page and registers the seven core tools plus one synthesized tool per semantic form. Names, descriptions and schemas are derived from labels, headings and action URLs.
Works on the deployed page and on localhost. The inspector badge appears bottom-right.
<script
src="https://cdn.jsdelivr.net/npm/@willh/agentready@latest/dist/agentready.min.js"
defer></script>
Name it yourself.
A handful of data-agent-* attributes give you exact tool names, descriptions and submit policies without touching JavaScript.
data-agent-nametool namedata-agent-descriptiontool descriptiondata-agent-submitauto · confirm · neverdata-agent-hideexclude a field entirelydata-agent-prioritysynthesize this form first
<form
method="get"
aria-label="Product search"
data-agent-name="search_products"
data-agent-description="Search products in the catalog"
data-agent-submit="auto">
<input name="q" type="search" placeholder="Search…" />
<select name="category">…</select>
<button>Search</button>
</form>
Bring your own logic.
Register domain tools with AgentReady.register(). They ride the same runtime, the same safety policy and the same inspector as everything else.
window.AgentReady also exposes getTools(), executeTool() and inspect(), the same shapes as the WebMCP standard API, so in-page agents work on every browser.
// after DOMContentLoaded
window.AgentReady.register({
name: 'add_to_cart',
description: 'Add a product to the shopping cart',
inputSchema: {
type: 'object',
properties: {
productId: { type: 'string' },
quantity: { type: 'number' },
},
required: ['productId'],
},
execute: ({ productId, quantity }) =>
cart.add(productId, quantity ?? 1),
});
Safety model
Untrusted by default. Fail closed.
Everything on the page, and everything an agent might do, is treated as untrusted. Sensitive fields are excluded from schemas and discovery, so agents never learn they exist. Consequential actions pause for a human. Turn the inspector off, and gated actions are declined, not waved through.
-
Allowed
Reading page content, search, navigation. Filling ordinary form fields.
-
Human approval
Form submission, checkout, and delete- or purchase-style buttons. An Approve / Decline dialog opens on the page.
-
Never
Hidden inputs, tokens, passwords, card fields (
cc-*, CVV). Not exposed, not filled, values redacted. Arbitrary JavaScript is never offered.
The inspector shows the receipts.
A small badge shows the live tool count. Open it for every agent action with its arguments and timing. Targets highlight as the agent touches them, and consequential calls wait on the Approve / Decline dialog.
window.AgentReadyConfig = {
inspector: true, // badge, activity, approvals
siteName: 'My Store', // badge label
maxResults: 8, // find_on_page result cap
};Live on this page
This site runs AgentReady.js too.
The list below is not a mock. It is read from window.AgentReady.getTools() on
this very page: the seven core tools plus two Level 2 tools this site registers itself. Open the badge in the corner and run one.
- Waiting for agentready.min.js…
Pick a read-only tool above. Output is clamped to ~1,500 characters, exactly as an agent would see it.
Runs where agents run
Native WebMCP when it's there. An identical shim when it isn't.
- ChatGPT desktop appIn-app browser, register-only client. Detected automatically.
- Chrome 149+Enable
chrome://flags/#enable-webmcp-testing, restart, done. - FirefoxDetects
navigator.modelContext. - Everything elseIn-page shim with the same
getTools/executeToolshape. Handy onlocalhost.
Get started
Two ways in. Both take a minute.
CDN
Tracks the newest release. For production, pin a version such as @0.1.2.
<script
src="https://cdn.jsdelivr.net/npm/@willh/agentready@latest/dist/agentready.min.js"
defer></script>
npm
Serve dist/agentready.js from the package, or import the types for Level 2 tools.
npm install @willh/agentready