auto-webmcp
v0.3.24
Published
Automatically make any HTML form WebMCP-ready — zero coding required
Maintainers
Readme
auto-webmcp
Automatically make any HTML form WebMCP-ready — zero explicit coding required.
Read the article on dev.to · Live demo · Platform guides
Drop in one script tag (or one import) and every <form> on your page is
instantly registered as a structured tool that in-browser AI agents can
discover and use via Chrome's
WebMCP early preview.
Quick start
Script tag (CDN / zero-config)
<script src="https://cdn.jsdelivr.net/npm/auto-webmcp/dist/auto-webmcp.iife.js"></script>All forms on the page are discovered and registered automatically.
npm / ESM
npm install auto-webmcpimport { autoWebMCP } from 'auto-webmcp';
await autoWebMCP();What it does
- Scans the page for all
<form>elements (on load + dynamically viaMutationObserver) - Also discovers inputs outside
<form>tags (orphan input groups) and inputs inside Web Component shadow roots - Infers a meaningful tool name, description, and JSON Schema from the form's DOM
- Registers each form as a WebMCP tool via
navigator.modelContext.registerTool() - Intercepts submissions to return structured results back to the agent
- Degrades silently in browsers without WebMCP
Configuration
import { autoWebMCP } from 'auto-webmcp';
await autoWebMCP({
// Skip specific forms (CSS selectors)
exclude: ['#login-form', '[data-no-webmcp]'],
// Auto-submit when agent invokes (default: false — human must click submit)
autoSubmit: false,
// Handling for forms already using native declarative WebMCP attributes:
// 'skip' (default), 'augment' (currently same as skip), or 'force'
declarativeMode: 'skip',
// Parameter binding behavior for execute payload keys
paramBinding: {
enableAliasResolution: true, // default
strict: false, // if true, exact schema keys only
},
// Deterministic execute timeout state
execution: {
timeoutMs: 15000, // default
},
// Per-form name / description overrides
overrides: {
'#checkout-form': {
name: 'checkout',
description: 'Complete a purchase'
}
},
// Log registered tools to console
debug: true,
});Per-form HTML overrides
Native WebMCP spec attributes (highest priority):
<form
toolname="book_appointment"
tooldescription="Book a doctor appointment"
toolautosubmit
>
<input
name="date"
type="date"
toolparamdescription="Preferred appointment date"
>
</form>data-webmcp- attributes* (fallback, useful when you cannot edit the form element directly):
<form
data-webmcp-name="book_appointment"
data-webmcp-description="Book a doctor appointment"
data-webmcp-autosubmit
>
<input
name="date"
type="date"
data-webmcp-title="Appointment Date"
data-webmcp-description="Preferred appointment date"
>
</form>Skip a form entirely
<form data-no-webmcp>…</form>Tool name inference (priority order)
toolnamenative attribute on<form>(WebMCP spec)data-webmcp-nameattribute on<form>- Submit button text (e.g. "Search Flights" →
search_flights) - Nearest
<h1>–<h3>heading above the form - Form
idornameattribute - Last segment of form
actionURL - Fallback:
form_N
Tool description inference (priority order)
tooldescriptionnative attribute on<form>(WebMCP spec)data-webmcp-descriptionattribute on<form><legend>text inside the formaria-labelon the formaria-describedbytarget- Nearest heading + page
<title>
Field title inference (priority order)
data-webmcp-titleattribute on the field- Associated
<label>text nameattribute (humanized)idattribute (humanized)placeholdertext
Field description inference (priority order)
toolparamdescriptionnative attribute on the field (WebMCP spec)data-webmcp-descriptionattribute on the fieldaria-description/aria-describedbytargetplaceholdertext
HTML → JSON Schema mapping
| HTML input | JSON Schema |
|------------------------------|------------------------------------|
| text, search, tel | string |
| email | string + format: email |
| url | string + format: uri |
| number, range | number (+ min / max) |
| date | string + format: date |
| datetime-local | string + format: date-time |
| checkbox | boolean |
| checkbox group (same name) | array + items.enum |
| radio group | string + enum + oneOf |
| select | string + enum + oneOf |
| select[multiple] | array + items.enum |
| textarea | string |
| ARIA role inputs | mapped by role (textbox, checkbox…) |
| file, hidden, password | skipped |
Additional schema enrichment:
- Pre-filled field values are exposed as
schema.default <optgroup>labels and<datalist>suggestions are included as metadata- Disabled select options and placeholder options (
"Select...","---") are excluded
ToolAnnotations
auto-webmcp automatically infers WebMCP ToolAnnotations from your HTML:
| Annotation | Auto-inferred when |
|---|---|
| readOnlyHint | Form method is GET, or submit button says "Search", "Find", etc. |
| destructiveHint | Submit button says "Delete", "Remove", "Cancel", etc. |
| idempotentHint | Form is read-only or GET |
| openWorldHint | Form modifies data (default for POST forms) |
Override any annotation with data attributes:
<form
data-webmcp-destructive="true"
data-webmcp-openworld="true"
>
<!-- delete account form -->
</form>Advanced discovery
Shadow DOM (Web Components)
Inputs inside custom element shadow roots are automatically discovered and included in the schema. No extra configuration needed.
<form>
<custom-date-picker></custom-date-picker> <!-- shadow root inputs discovered -->
<button type="submit">Book</button>
</form>Orphan inputs (no <form> tag)
Input groups not wrapped in a <form> element (common in SPAs) are detected by finding the nearest ancestor that contains a visible submit button. Each group is registered as a separate tool.
React and framework-managed forms
auto-webmcp fills React-controlled inputs using native HTMLInputElement prototype setters and execCommand to trigger onChange. A post-fill snapshot preserves field values if the framework re-renders before submit.
Structured execute result
Every tool execution returns a two-item content array:
content[0].text— human-readable summary:"Form submitted. Fields: {...}"content[1].text— JSON-stringified structured data:
{
"status": "success",
"filled_fields": { "email": "[email protected]", "frequency": "weekly" },
"skipped_fields": [],
"missing_required": [],
"warnings": []
}status is "partial" when required fields were missing or a value was clamped. Each warning in the warnings array has a field, type (clamped, not_filled, missing_required, type_mismatch), message, and optionally original / actual values.
API
const handle = await autoWebMCP(config?);
handle.isSupported // boolean — true if navigator.modelContext exists
handle.getTools() // Array<{ form: HTMLFormElement; name: string }>
handle.destroy() // Promise<void> — unregister all tools & stop observingEvents
// auto-webmcp lifecycle events
window.addEventListener('form:registered', (e) => {
console.log('Registered:', e.detail.toolName, e.detail.form);
});
window.addEventListener('form:unregistered', (e) => {
console.log('Removed:', e.detail.toolName);
});
// WebMCP spec events
window.addEventListener('toolactivated', (e) => {
// Agent has filled the form fields, waiting for submit
console.log('Agent activated:', e.detail.toolName);
});
window.addEventListener('toolcancel', (e) => {
// User reset the form or agent cancelled
console.log('Cancelled:', e.detail.toolName);
});Prevent auto-init (IIFE build)
Set this before the script loads:
<script>window.__AUTO_WEBMCP_NO_AUTOINIT = true;</script>
<script src="auto-webmcp.iife.js"></script>
<script>
AutoWebMCP.autoWebMCP({ debug: true });
</script>Development
npm install
npm run build # compile to dist/
npm run build:watch # rebuild on change
npm run typecheck # TypeScript type check only
npm test # Playwright integration tests (Chromium)Browser support
- Chrome 146+ with
chrome://flags/#enable-webmcp-testingenabled for full functionality - All other browsers: the library loads, analyzes forms, and silently no-ops
navigator.modelContextcalls (progressive enhancement)
License
MIT
