# react-web-mcp

> React hooks and components for WebMCP (Web Model Context Protocol) — the proposed W3C web standard that lets a web page expose its client-side functionality as structured tools that in-browser AI agents (Gemini in Chrome, Copilot in Edge) can discover and invoke. Published on npm as `@cr4yfish/react-web-mcp`. Zero runtime dependencies, React 18+ peer dependency, SSR-safe, MIT-licensed.

This file ships inside the npm package (`node_modules/@cr4yfish/react-web-mcp/doc/llms.txt`) so coding agents can read it offline.

Install: `npm install @cr4yfish/react-web-mcp`

Key facts for code generation:

- Import hooks/components from `@cr4yfish/react-web-mcp` (ships a "use client" directive); import the React-free core from `@cr4yfish/react-web-mcp/vanilla` (safe in server components and plain scripts).
- `useWebMCPTool({ name, description, inputSchema?, outputSchema?, annotations?, exposedTo?, enabled?, validateInput?, execute })` registers a tool while the component is mounted. `execute` may close over props/state freely — it always sees the latest values and changing it never re-registers (definition is keyed by JSON.stringify; execute stays fresh via a ref). Returns `{ isRegistered }`.
- `useWebMCPTools(tools, { enabled? })` registers a batch individually (composable across components, unlike provideContext which replaces the page toolset).
- `useFormTool({ formRef, name, description, autoSubmit?, onToolCall?, annotations?, enabled? })` derives the tool's input schema from a rendered `<form>` element's controls — works with any UI library (MUI, AntD, shadcn/ui, portals). Agent calls fill the form via native setters + input/change events; without `autoSubmit` the user reviews and submits (human-in-the-loop). Password, hidden, and file inputs are never exposed. Returns `{ isRegistered, refresh }` — call refresh() after dynamic field changes.
- `useWebMCP()` returns `{ isSupported, modelContext }` (false during SSR/hydration). `useWebMCPEvent("toolchange" | "toolactivated" | "toolcanceled", handler)` subscribes to WebMCP lifecycle events on every implementation surface (Chromium fires toolactivated/toolcancel at the window with a `toolName` property and only toolchange at the ModelContext; the hook handles both targets and the toolcancel/toolcanceled naming, deduped). Framework-agnostic: `addWebMCPEventListener(name, handler)`.
- `<ToolForm name description autoSubmit? onAgentSubmit? indicators? pendingTimeoutMs? onPendingChange? resetAfterAgentSubmit?>` renders a declarative WebMCP form (`toolname`/`tooldescription` attributes); `onAgentSubmit(formData, event)` answers agent submissions via `SubmitEvent.respondWith()` without navigation. `indicators` opts into visual highlighting of the agent-filled/awaiting-review state (native `:tool-form-active` pseudo-class + `data-webmcp-active` fallback; customize via `--webmcp-indicator-color` or the exported `WEBMCP_INDICATOR_CSS`/`injectWebMCPIndicatorStyles()`). `autoSubmit` defaults to true (toolautosubmit rendered) — flipped from the platform default because in current Chromium a pending review-mode invocation that gets re-invoked drops its reply and closes the page WebMCP channel, silently killing every tool until reload; pass autoSubmit={false} to opt into review mode for consequential actions. `pendingTimeoutMs` (default 120000, 0 disables) is a stale-invocation watchdog: Chromium keeps ONE pending invocation per form and a re-invoke silently drops the older reply callback, which can kill every WebMCP tool on the page until reload — the watchdog cancels stale invocations via form.reset(), the sanctioned page-side cancel that answers the agent with a proper "cancelled" error. A built-in re-invoke guard (`reinvokeGuard`, default true) defuses the re-invoke case: it intercepts the new fill's input events (before the browser drops the old reply), cancels the old invocation cleanly via form.reset() with full value snapshot/restore, and emits an `invocation-reinvoked` warning; overlap that escapes the guard is reported as an `invocation-overlap` error diagnostic. Spread `toolParamAttrs("description")` onto controls; `toolFormAttrs({ name, description, autoSubmit? })` onto your own `<form>`.
- Verbose mode & diagnostics: `setWebMCPVerbose(true)` logs all lifecycle events to the console (`[webmcp]` prefix); `onWebMCPDiagnostic(listener)` streams every diagnostic `{ level, code, message, toolName?, detail? }` regardless of verbose mode (codes: register, unregister, register-failed, execute, execute-result, execute-error, invalid-arguments, result-truncated, agent-submit, agent-response, agent-response-error, respondwith-missing, agent-submit-navigation, invocation-pending, invocation-overlap, invocation-timeout, invocation-canceled, unsupported). The package never fails silently.
- Core functions: `registerTool(tool, { signal?, exposedTo? })` returns an `unregister()` function; `provideContext(tools)` replaces the page toolset; `textResult(text, isError?)` and `jsonResult(value, maxLength?)` build MCP-shaped responses; `isWebMCPSupported()`, `isWebMCPTestingSupported()`, `getModelContext()`, `extractFormSchema(form)`, `applyArgsToForm(form, args)`.
- Behavior guarantees: every primitive is a no-op without browser support; incoming arguments are validated against `inputSchema` before `execute` runs (invalid calls get a readable `{ isError: true }` response; conservative JSON-Schema subset; opt out per tool with `validateInput: false`; standalone validator exported as `validateToolInput(args, schema)`); tool results are normalized to MCP `CallToolResult` content (strings → text, objects → JSON, truncated at 50,000 chars) unless the tool declares an `outputSchema`; thrown errors become `{ isError: true }` responses; the context resolves `document.modelContext` first (Chrome 150+ spec surface) with deprecated `navigator.modelContext` fallback.
- WebMCP availability: Chrome 146+ behind chrome://flags/#enable-webmcp-testing, Chrome 149+ via origin trial, Edge 147+ natively. No support in other browsers — feature-detect and degrade.

## Bundled docs (offline)

- doc/index.md: overview, quick start, invariants
- doc/api-reference.md: full API reference for every export
- doc/webmcp-standard.md: the WebMCP standard (imperative + declarative APIs, security, best practices, browser support)

## Canonical / online docs

- README: https://github.com/cr4yfish/react-web-mcp#readme — full usage guide, API reference, best practices, security notes
- npm package: https://www.npmjs.com/package/@cr4yfish/react-web-mcp
- CHANGELOG.json: https://github.com/cr4yfish/react-web-mcp/blob/main/CHANGELOG.json — machine-readable changelog

## WebMCP standard

- Chrome WebMCP docs: https://developer.chrome.com/docs/ai/webmcp — imperative API, declarative API, best practices, security, evals
- Spec & explainer: https://github.com/webmachinelearning/webmcp — W3C Web Machine Learning CG proposal
- Official demos & tools: https://github.com/GoogleChromeLabs/webmcp-tools — reference demos, evals CLI, tool inspector
