useWebMCP
React adapter for @web-ai-sdk/webmcp. Registers WebMCP tools on mount and unregisters them on unmount via an AbortSignal. For the conceptual overview see WebMCP.
Live demo
Section titled “Live demo”Toggle the tools on or off, then click “Run agent”. On browsers with WebMCP enabled the demo asks Chrome’s testing surface (navigator.modelContextTesting.executeTool) to dispatch one of the live tools.
import { useWebMCP } from "@web-ai-sdk/webmcp/react";import { useMemo } from "react";
export function AgentTools() { const tools = useMemo( () => [ { name: "add_to_cart", description: "Add a SKU to the user's cart", execute: async (input) => ({ ok: true, ...input }), }, ], [], );
useWebMCP(tools); return null; // pure logic; render whatever UI you want elsewhere}The hook re-registers whenever the tools array reference changes. Pass a stable reference (memoized or module-level) so the hook doesn’t tear down and rebuild on every render.
State + reactivity
Section titled “State + reactivity”Tool execute closures capture whatever they read at definition time. If your tool needs to read live React state, use a useRef:
const stateRef = useRef(state);stateRef.current = state;
const tools = useMemo( () => [ { name: "open_settings", description: "...", execute: async () => ({ pane: stateRef.current.pane }), }, ], [],);
useWebMCP(tools);This is necessary because Chrome’s AbortSignal-driven unregister is asynchronous; a same-tick re-register can be silently skipped on a duplicate-name race, leaving the old closure in place. Reading from a ref guarantees the latest state regardless of which closure Chrome holds.
Cleanup
Section titled “Cleanup”The hook returns nothing; cleanup is automatic on unmount via useEffect’s return value. If you need imperative cleanup (e.g. log out flow), call the vanilla registerTool directly per tool and combine the cleanups: const cleanups = tools.map(registerTool).
Errors and unavailability
Section titled “Errors and unavailability”On browsers without document.modelContext (or the legacy navigator.modelContext), the hook is a no-op. Render whatever fallback UI you want; nothing breaks.
Reference
Section titled “Reference”import type { Tool, ToolAnnotations, DefineToolOptions } from "@web-ai-sdk/webmcp/react";
interface Tool { name: string; description: string; execute: (input: unknown) => Promise<unknown> | unknown; readOnly?: boolean; // → annotations.readOnlyHint destructive?: boolean; // → annotations.destructiveHint annotations?: ToolAnnotations; // raw passthrough; merges on top of shorthand inputSchema?: object; // JSON Schema or Standard Schema v1}
interface ToolAnnotations { readOnlyHint?: boolean; destructiveHint?: boolean; idempotentHint?: boolean; openWorldHint?: boolean;}
declare const useWebMCP: (tools: readonly Tool[]) => void;
// Helper for type-safe tool definitions with a Standard Schema.declare function defineTool<T extends StandardSchemaV1>(options: DefineToolOptions<T>): Tool;Source: packages/webmcp/src/react/index.ts.