webmcp

Your React app,
callable.

React hooks for WebMCP. Expose app functionality as tools for in-browser AI agents — one hook, zero dependencies, SSR-safe.

GitHub
gemini-in-chrome → your-app.example
useradd oat milk to my shopping list
One hook
useWebMCPTool registers on mount, unregisters on unmount. execute always sees fresh state — no memoization, no stale closures.
SSR-safe, zero deps
No-ops on the server and in unsupported browsers. Nothing in your bundle but the package itself; react is a peer dep.
Spec-current
document.modelContext (Chrome 150+) with navigator fallback, AbortSignal unregistration, exposedTo, annotations, outputSchema.
Any UI library
useFormTool derives the schema from the rendered DOM form — MUI, AntD, shadcn/ui, portals. No per-library adapters.
Safe by default
Errors become readable isError responses. Outputs are length-capped. Passwords never enter a schema. Human-in-the-loop forms.
Declarative too
ToolForm renders toolname/tooldescription attributes and answers agent submissions via respondWith — no navigation.

This page is the demo.

Three tools are registered right now — add-item, clear-list, set-accent-color. Call them from the DevTools WebMCP panel or a browser agent. No flag enabled? The buttons hit the same code paths.

demo list

empty — ask an agent to add something

set-accent-color
invocation log
modelContext: unavailable

no calls yet

Four ways in.

import { useWebMCPTool } from "@cr4yfish/react-web-mcp";

function TodoList() {
  const [todos, setTodos] = useState<string[]>([]);

  useWebMCPTool({
    name: "add-todo",
    description: "Add an item to the user's todo list",
    inputSchema: {
      type: "object",
      properties: { text: { type: "string" } },
      required: ["text"],
    },
    execute: ({ text }) => {
      setTodos((prev) => [...prev, text]); // fresh state, no memoization
      return `Added "${text}"`;
    },
  });

  return <ul>{todos.map((t) => <li key={t}>{t}</li>)}</ul>;
}
Not on React?

WebMCP for every framework — install the skill.

web-mcp-skill turns your coding agent into a WebMCP expert. The whole standard — imperative + declarative APIs, schema synthesis, security model, evals — distilled into one agent skill. It works anywhere your app does, not just React.

Vanilla JSReactVueSvelteAngular
Explore the skill

Roadmap

Each release automates more of the work, until v1.0 converts an existing React app into a full WebMCP tool surface automatically.

v0.4.0

Zero-boilerplate toolsin-progress
  • Auto form tools: one page-level hook discovers every rendered form and registers a tool per form (schema, name, and description derived from labels, headings, and aria attributes)
  • Action tools from interactive elements: derive simple no-input tools from buttons and links with accessible names
  • Dev overlay listing the page's registered tools, their schemas, and recent invocations live

v0.5.0

App-aware automationplanned
  • Router adapters (Next.js App Router, React Router): navigation tools synthesized from the route table
  • Automatic page context: provideContext fed from headings, landmarks, and visible state so agents know where they are
  • Per-route tool scoping: tools register and unregister automatically as the user navigates

v0.6.0

Whole-page discoveryplanned
  • Runtime scanner that finds interactive surfaces beyond forms — search inputs, filters, pagination, dialogs, tables — and synthesizes tools with confidence scores
  • Allow/deny configuration and sensitive-surface heuristics so discovery stays opt-out granular and safe by default

v0.7.0

Codegen CLIplanned
  • npx react-web-mcp generate: statically analyze a React codebase and emit explicit useWebMCPTool/useFormTool registrations as reviewable code (codemods, not runtime magic)
  • Generated tools carry descriptions inferred from component names, props, and JSDoc, editable before commit

v0.8.0

Eval-driven qualityplanned
  • First-class integration with the webmcp-tools evals CLI: score every auto-generated tool against agent task completion
  • Regeneration loop: low-scoring tools get better names, descriptions, and schemas suggested automatically

v0.9.0

Hardening & API freezeplanned
  • Security pass over all automation: sensitive-field redaction, destructive-action annotations, human-in-the-loop confirmation everywhere
  • Performance budget for scanners and registration churn; public API frozen as a 1.0 release candidate

v1.0.0

Fully automatic conversionplanned
  • One drop-in — a single provider component or a single CLI command — converts an existing React app into a complete WebMCP tool surface with no per-tool code
  • Runtime discovery, codegen, and eval-driven refinement combined: every meaningful user action exposed as a well-described, validated, safe tool
  • Stable semver, published on npm with provenance

Changelog

v0.3.5

2026-06-12
  • changedReview mode (autoSubmit={false}) is channel-safe by default: the new reviewResponse option ('immediate' by default) answers every invocation right away with a staged 'Form filled out. The user must review and submit it manually.' response (useFormTool semantics), so nothing ever stays pending browser-side and a double invocation simply answers twice; the user's review submit then completes as a normal form submission (onSubmit, agentInvoked=false). The platform-native pending-until-submit flow is opt-in via reviewResponse='on-submit' when the agent needs the final submitted data
  • changedRe-invoke guard is now scoped to reviewResponse='on-submit' forms (the other modes are channel-safe by construction) and treats any input event on an unfocused control as a fill regardless of event class (user interactions always target the focused control); its hard limit is documented — a re-invoke whose fill changes no control values dispatches no events and cannot be caught, which is why 'immediate' is the review default

v0.3.4

2026-06-12
  • addedRe-invoke guard on ToolForm (reinvokeGuard, default true): when the agent re-invokes a review-mode tool while an invocation is still pending, the guard intercepts the new fill's input events — the one window before Chromium drops the old reply — snapshots all control values, cancels the old invocation cleanly via form.reset(), restores the values, and lets the new invocation proceed; the channel-killing callback drop can no longer happen, and an invocation-reinvoked warning diagnostic is emitted

v0.3.3

2026-06-12
  • changedToolForm autoSubmit now defaults to true (renders toolautosubmit), deliberately flipping the platform's human-in-the-loop default: review mode keeps the invocation pending until the user submits, Chromium tracks only one pending invocation per form, and a re-invoke drops the previous reply and closes the page's WebMCP channel — confirmed in practice to silently kill every tool until reload; opt back into review mode per form with autoSubmit={false} (keep pendingTimeoutMs and indicators on)

v0.3.2

2026-06-12
  • addedROADMAP.json at the repo root, rendered automatically on the site like the changelog: milestone plan up to v1.0 (fully automatic conversion of React apps into WebMCP tool surfaces), statuses updated as releases land
  • fixedToolForm guards the page's WebMCP channel against stale declarative invocations: Chromium keeps one pending invocation per form and a re-invoke silently drops the older reply callback (closing the channel and disabling every tool until reload) — a pendingTimeoutMs watchdog (default 2 min, 0 disables) now auto-cancels stale invocations via form.reset(), the sanctioned page-side cancel that answers the agent with a proper 'cancelled' error, and overlapping invocations are reported as a loud invocation-overlap error diagnostic
  • fixeduseWebMCPEvent (and the new addWebMCPEventListener) actually fire in Chrome now: Chromium dispatches toolactivated and the cancel event at the window (not the ModelContext) and names the cancel event toolcancel — listeners attach to both targets and both spellings, deduped per event, and expose the event's toolName
  • addedVerbose mode and a diagnostics stream so nothing fails silently: setWebMCPVerbose(true) logs the full lifecycle ([webmcp] console prefix) and onWebMCPDiagnostic(listener) delivers every diagnostic (registrations, invocations, responses, validation rejections, truncations, cancellations, overlaps) to page code regardless of verbose mode
  • addedOpt-in visual indicators for agent-filled forms: ToolForm's indicators prop injects a shared stylesheet keyed on the native :tool-form-active/:tool-submit-active pseudo-classes with a data-webmcp-active attribute fallback, customizable via --webmcp-indicator-color; WEBMCP_INDICATOR_CSS and injectWebMCPIndicatorStyles() are exported for custom styling
  • addedToolForm lifecycle hooks: onPendingChange(pending) observes the agent-filled/awaiting-review state and resetAfterAgentSubmit resets the form one tick after an agent submission was answered
  • addedToolForm answers agent submits with loud diagnostics for the previously silent paths: respondwith-missing (error) when SubmitEvent.respondWith is unavailable and agent-submit-navigation (warn) when no onAgentSubmit handler is set
  • addedtooltitle added to the JSX attribute augmentation (Chromium supports a tool title on declarative forms)

v0.3.1

2026-06-12
  • fixedToolForm renders the form noValidate so an agent-filled control that fails native HTML validation (e.g. an empty required field) can no longer silently block submission — previously the submit event never fired, respondWith was never called, and the unanswered invocation hung and silenced every later tool call on the page; human submits are re-validated via reportValidity() so their inline-error UX is unchanged
  • fixedToolForm now always answers an agent-invoked submit when onAgentSubmit is set, even if a consumer's onSubmit calls preventDefault — so a stray preventDefault can no longer strand the invocation

v0.3.0

2026-06-12
  • addedAutomatic input validation: registerTool / useWebMCPTool / useWebMCPTools / provideContext now validate incoming arguments against inputSchema before execute runs and answer schema-violating calls with a readable isError response (browsers don't enforce the schema); opt out per tool with validateInput: false
  • addedvalidateToolInput(args, schema) exported from both entry points: the standalone conservative JSON-Schema-subset validator returning a human-readable problem list
  • changedextractFormSchema (and thus useFormTool) marks derived schemas additionalProperties: false, so unknown-field calls are rejected at validation time with the offending field named
  • fixedToolForm: a throwing or rejecting onAgentSubmit now reaches the agent as an isError response — previously a synchronous throw escaped before respondWith was called, leaving the prevented invocation unanswered and silencing every later tool call on the page, and async rejections became unhandled rejections

v0.2.0

2026-06-12
  • addeddoc/ folder bundled into the published package (index, API reference, WebMCP standard reference, llms.txt) so coding agents in consuming repos can read the docs offline from node_modules
  • addedDiscovery hints for the bundled docs: an AGENTS.md pointer at the package root (also bundled) and a callout near the top of the README — script-free, so the zero-deps/auditable install surface is unchanged
  • addeduseFormTool: register a tool whose input schema is derived from the rendered DOM form — works with MUI, AntD, shadcn/ui, portals, any library that renders native controls
  • addedextractFormSchema / applyArgsToForm DOM primitives (agent-driven form filling with native setters + input/change events)
  • addeduseWebMCPTools: composable batch registration (individual registrations, not provideContext)
  • addedisWebMCPTestingSupported(): detects the #enable-webmcp-testing flag / Model Context Tool Inspector API
  • addedDev-mode validation in registerTool/provideContext: empty names/descriptions and non-serializable schemas throw in development, degrade to console + no-op in production
  • addedDocumentation site in site/ (Next.js + shadcn + motion), deployable on Vercel; dogfoods the package with live tools and renders this changelog
  • addedCHANGELOG.json policy: every change must ship with an entry (enforced via CLAUDE.md), consumed by the site automatically
  • changedGitHub Actions bumped to v6 (Node 24 runners)
  • fixedTypeScript 6 dts build (ignoreDeprecations for tsup-injected baseUrl)
  • fixedSite: readable hero subtitle (higher-contrast secondary text) and pinned Turbopack workspace root to silence the multi-lockfile warning
  • fixedSite: hero text no longer fades out — the grid backdrop's radial mask moved to its own layer instead of masking the whole section
  • addedSite: llms.txt at the site root describing the package API for LLM consumers
  • fixedSite: pin the Vercel framework preset via site/vercel.json and document that Root Directory must be set to site/ (root builds fail with 'No Output Directory named public')
  • addedSite: dedicated /skill page and homepage promo for the framework-agnostic web-mcp-skill (npx skills add cr4yfish/web-mcp-skill), with a shared nav, dual-product footer, and updated metadata

v0.1.0

2026-06-12
  • addedInitial release as @cr4yfish/react-web-mcp (renamed from react-web-mcp: npm typosquat protection vs. existing react-webmcp)
  • addeduseWebMCPTool, useWebMCP, useWebMCPEvent hooks with SSR safety, ref-fresh execute, and definition-keyed re-registration
  • addedToolForm declarative component (toolname/tooldescription attributes, respondWith handling) + toolFormAttrs/toolParamAttrs helpers and JSX typings
  • addedFramework-agnostic core (registerTool, provideContext, textResult, jsonResult with 50k truncation, error-to-isError normalization) via @cr4yfish/react-web-mcp/vanilla
  • addeddocument.modelContext (Chrome 150+ spec surface) with navigator.modelContext fallback
  • addedCI (audit, type-check, tests, build, stale-dist check) and npm publish workflow