react-webmcp 0.2.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/utils/modelContext.ts","../src/hooks/useWebMCPTool.ts","../src/hooks/useWebMCPContext.ts","../src/hooks/useToolEvent.ts","../src/components/WebMCPForm.tsx","../src/components/WebMCPInput.tsx","../src/components/WebMCPSelect.tsx","../src/components/WebMCPTextarea.tsx","../src/context.tsx"],"names":["useRef","useEffect","useCallback","jsx","React","createContext","useMemo","useContext"],"mappings":"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","file":"index.js","sourcesContent":["import type { ModelContext } from \"../types\";\n\n/**\n * Returns the navigator.modelContext API if available, or null.\n */\nexport function getModelContext(): ModelContext | null {\n if (\n typeof window !== \"undefined\" &&\n typeof window.navigator !== \"undefined\" &&\n window.navigator.modelContext\n ) {\n return window.navigator.modelContext;\n }\n return null;\n}\n\n/**\n * Returns true if the WebMCP API (navigator.modelContext) is available\n * in the current browsing context.\n */\nexport function isWebMCPAvailable(): boolean {\n return getModelContext() !== null;\n}\n\n/**\n * Returns true if the WebMCP testing API (navigator.modelContextTesting)\n * is available. This is the API used by the Model Context Tool Inspector\n * extension and requires the \"WebMCP for testing\" Chrome flag.\n */\nexport function isWebMCPTestingAvailable(): boolean {\n return (\n typeof window !== \"undefined\" &&\n typeof window.navigator !== \"undefined\" &&\n !!window.navigator.modelContextTesting\n );\n}\n\n/**\n * Logs a warning when WebMCP is not available. Only fires in development\n * builds so the warning is stripped from production bundles.\n */\nexport function warnIfUnavailable(hookName: string): void {\n if (process.env.NODE_ENV !== \"production\") {\n if (!isWebMCPAvailable()) {\n console.warn(\n `[react-webmcp] ${hookName}: navigator.modelContext is not available. ` +\n `Ensure you are running Chrome 146+ with the \"WebMCP for testing\" flag enabled.`,\n );\n }\n }\n}\n","import { useEffect, useRef } from \"react\";\nimport type { UseWebMCPToolConfig } from \"../types\";\nimport { getModelContext, warnIfUnavailable } from \"../utils/modelContext\";\n\n/**\n * Produces a stable fingerprint for a single tool definition so we can\n * detect meaningful changes without being tricked by new object references\n * created on every render (e.g. inline schema literals).\n */\nfunction toolFingerprint(config: UseWebMCPToolConfig): string {\n return `${config.name}::${config.description}::${JSON.stringify(config.inputSchema)}::${JSON.stringify(config.outputSchema ?? {})}::${JSON.stringify(config.annotations ?? {})}`;\n}\n\n/**\n * Register a single WebMCP tool via the imperative API.\n *\n * The tool is registered with `navigator.modelContext.registerTool()` when\n * the component mounts and unregistered with `unregisterTool()` on unmount.\n * If the tool definition changes (name, description, schemas, or\n * annotations), the previous tool is unregistered and the new one is\n * registered.\n *\n * Object/array props like `inputSchema` and `annotations` are compared by\n * value (serialised fingerprint), so passing inline literals on every render\n * will **not** cause unnecessary re-registration.\n *\n * The `execute` callback is always called through a ref, so it does not\n * need to be memoised by the consumer.\n *\n * @example\n * ```tsx\n * useWebMCPTool({\n * name: \"searchFlights\",\n * description: \"Search for flights with the given parameters.\",\n * inputSchema: {\n * type: \"object\",\n * properties: {\n * origin: { type: \"string\", description: \"Origin IATA code\" },\n * destination: { type: \"string\", description: \"Destination IATA code\" },\n * },\n * required: [\"origin\", \"destination\"],\n * },\n * execute: async ({ origin, destination }) => {\n * const results = await api.searchFlights(origin, destination);\n * return { content: [{ type: \"text\", text: JSON.stringify(results) }] };\n * },\n * });\n * ```\n */\nexport function useWebMCPTool(config: UseWebMCPToolConfig): void {\n const registeredNameRef = useRef<string | null>(null);\n const configRef = useRef(config);\n configRef.current = config;\n\n // Derive a stable fingerprint from the definition values.\n const fingerprint = toolFingerprint(config);\n\n useEffect(() => {\n const mc = getModelContext();\n if (!mc) {\n warnIfUnavailable(\"useWebMCPTool\");\n return;\n }\n\n // Unregister the previous tool if the name changed\n if (registeredNameRef.current && registeredNameRef.current !== config.name) {\n try {\n mc.unregisterTool(registeredNameRef.current);\n } catch {\n // Tool may have already been unregistered\n }\n }\n\n // Build the tool definition matching the navigator.modelContext shape.\n // The execute function is always routed through configRef so callers\n // never need to memoise their handler.\n const toolDef: Record<string, unknown> = {\n name: config.name,\n description: config.description,\n inputSchema: config.inputSchema,\n execute: (input: Record<string, unknown>) => {\n return configRef.current.execute(input);\n },\n };\n if (config.outputSchema) {\n toolDef.outputSchema = config.outputSchema;\n }\n if (config.annotations) {\n toolDef.annotations = config.annotations;\n }\n\n try {\n mc.registerTool(toolDef as unknown as Parameters<typeof mc.registerTool>[0]);\n registeredNameRef.current = config.name;\n } catch (err) {\n if (process.env.NODE_ENV !== \"production\") {\n console.error(\n `[react-webmcp] Failed to register tool \"${config.name}\":`,\n err,\n );\n }\n }\n\n return () => {\n try {\n mc.unregisterTool(config.name);\n } catch {\n // Tool may have already been unregistered externally\n }\n registeredNameRef.current = null;\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps -- fingerprint\n // captures the serialised value of all definition fields; config.name\n // is included so the cleanup closure captures the correct name.\n }, [fingerprint, config.name]);\n}\n","import { useEffect, useRef } from \"react\";\nimport type { WebMCPToolDefinition } from \"../types\";\nimport { getModelContext, warnIfUnavailable } from \"../utils/modelContext\";\n\n/**\n * Produces a stable fingerprint string from a tools array so we can detect\n * meaningful changes without being tricked by new array references.\n * Compares tool names, descriptions, and serialised input schemas.\n */\nfunction toolsFingerprint(tools: WebMCPToolDefinition[]): string {\n return tools\n .map(\n (t) =>\n `${t.name}::${t.description}::${JSON.stringify(t.inputSchema)}::${JSON.stringify(t.outputSchema ?? {})}::${JSON.stringify(t.annotations ?? {})}`,\n )\n .join(\"|\");\n}\n\n/**\n * Register multiple WebMCP tools at once using `provideContext()`.\n *\n * Unlike `useWebMCPTool` which manages a single tool, `useWebMCPContext`\n * replaces the entire set of registered tools. This is useful when the\n * application state changes significantly and you want to expose a\n * completely different set of tools.\n *\n * On unmount, all tools are cleared via `clearContext()`.\n *\n * The hook performs a deep comparison of tool definitions (name, description,\n * inputSchema, annotations) so that passing a new array reference on every\n * render does **not** cause unnecessary re-registration.\n *\n * @example\n * ```tsx\n * useWebMCPContext({\n * tools: [\n * {\n * name: \"addTodo\",\n * description: \"Add a new item to the todo list\",\n * inputSchema: { type: \"object\", properties: { text: { type: \"string\" } } },\n * execute: ({ text }) => ({ content: [{ type: \"text\", text: `Added: ${text}` }] }),\n * },\n * {\n * name: \"markComplete\",\n * description: \"Mark a todo item as complete\",\n * inputSchema: { type: \"object\", properties: { id: { type: \"string\" } } },\n * execute: ({ id }) => ({ content: [{ type: \"text\", text: `Completed: ${id}` }] }),\n * },\n * ],\n * });\n * ```\n */\nexport function useWebMCPContext(config: {\n tools: WebMCPToolDefinition[];\n}): void {\n // Keep a ref to the latest tools so the execute callbacks always close\n // over current handlers without triggering the effect.\n const toolsRef = useRef(config.tools);\n toolsRef.current = config.tools;\n\n const fingerprint = toolsFingerprint(config.tools);\n\n useEffect(() => {\n const mc = getModelContext();\n if (!mc) {\n warnIfUnavailable(\"useWebMCPContext\");\n return;\n }\n\n // Wrap execute functions so they always call through the latest ref,\n // allowing callers to pass inline arrow functions without triggering\n // the effect.\n const stableTools = toolsRef.current.map((tool, idx) => {\n const def: Record<string, unknown> = {\n name: tool.name,\n description: tool.description,\n inputSchema: tool.inputSchema,\n execute: (input: Record<string, unknown>) => {\n return toolsRef.current[idx].execute(input);\n },\n };\n if (tool.annotations) {\n def.annotations = tool.annotations;\n }\n if (tool.outputSchema) {\n def.outputSchema = tool.outputSchema;\n }\n return def;\n });\n\n try {\n mc.provideContext({\n tools: stableTools as unknown as Parameters<typeof mc.provideContext>[0][\"tools\"],\n });\n } catch (err) {\n if (process.env.NODE_ENV !== \"production\") {\n console.error(\"[react-webmcp] Failed to provide context:\", err);\n }\n }\n\n return () => {\n try {\n mc.clearContext();\n } catch {\n // Context may have already been cleared\n }\n };\n }, [fingerprint]);\n}\n","import { useEffect } from \"react\";\n\n/**\n * Listen for WebMCP tool lifecycle events on the window.\n *\n * The browser fires `toolactivated` when an AI agent invokes a declarative\n * tool (form fields are pre-filled) and `toolcancel` when the agent or\n * user cancels the operation.\n *\n * @param event - The event name: \"toolactivated\" or \"toolcancel\"\n * @param callback - Called with the tool name when the event fires\n * @param toolNameFilter - Optional: only fire for a specific tool name\n *\n * @example\n * ```tsx\n * useToolEvent(\"toolactivated\", (toolName) => {\n * console.log(`Agent activated tool: ${toolName}`);\n * validateForm();\n * }, \"book_table\");\n * ```\n */\nexport function useToolEvent(\n event: \"toolactivated\" | \"toolcancel\",\n callback: (toolName: string) => void,\n toolNameFilter?: string,\n): void {\n useEffect(() => {\n const handler = (e: Event) => {\n const toolName = (e as CustomEvent & { toolName?: string }).toolName ??\n (e as CustomEvent).detail?.toolName;\n if (!toolName) return;\n if (toolNameFilter && toolName !== toolNameFilter) return;\n callback(toolName);\n };\n\n window.addEventListener(event, handler);\n return () => {\n window.removeEventListener(event, handler);\n };\n }, [event, callback, toolNameFilter]);\n}\n","import React, { useCallback, useEffect, useRef } from \"react\";\nimport type { WebMCPFormSubmitEvent } from \"../types\";\n\nexport interface WebMCPFormProps\n extends Omit<React.FormHTMLAttributes<HTMLFormElement>, \"onSubmit\"> {\n /** The tool name exposed to AI agents. Maps to the `toolname` HTML attribute. */\n toolName: string;\n /** Description of what this tool does. Maps to `tooldescription`. */\n toolDescription: string;\n /** If true, the form auto-submits when filled by an agent. Maps to `toolautosubmit`. */\n toolAutoSubmit?: boolean;\n /**\n * Submit handler that receives the enhanced SubmitEvent with\n * `agentInvoked` and `respondWith` properties.\n */\n onSubmit?: (event: WebMCPFormSubmitEvent) => void;\n /** Called when a tool activation event fires for this form's tool. */\n onToolActivated?: (toolName: string) => void;\n /** Called when a tool cancel event fires for this form's tool. */\n onToolCancel?: (toolName: string) => void;\n children: React.ReactNode;\n}\n\n/**\n * A React wrapper for the WebMCP declarative API.\n *\n * Renders a `<form>` element with the appropriate WebMCP HTML attributes\n * (`toolname`, `tooldescription`, `toolautosubmit`) so the browser\n * automatically registers it as a WebMCP tool.\n *\n * @example\n * ```tsx\n * <WebMCPForm\n * toolName=\"book_table\"\n * toolDescription=\"Book a table at the restaurant\"\n * onSubmit={(e) => {\n * e.preventDefault();\n * if (e.agentInvoked) {\n * e.respondWith(Promise.resolve(\"Booking confirmed!\"));\n * }\n * }}\n * >\n * <WebMCPInput name=\"name\" label=\"Full Name\" />\n * <button type=\"submit\">Book</button>\n * </WebMCPForm>\n * ```\n */\nexport function WebMCPForm({\n toolName,\n toolDescription,\n toolAutoSubmit,\n onSubmit,\n onToolActivated,\n onToolCancel,\n children,\n ...rest\n}: WebMCPFormProps) {\n const formRef = useRef<HTMLFormElement>(null);\n\n // Listen for toolactivated and toolcancel events\n useEffect(() => {\n const handleActivated = (e: Event) => {\n const name =\n (e as CustomEvent & { toolName?: string }).toolName ??\n (e as CustomEvent).detail?.toolName;\n if (name === toolName && onToolActivated) {\n onToolActivated(name);\n }\n };\n\n const handleCancel = (e: Event) => {\n const name =\n (e as CustomEvent & { toolName?: string }).toolName ??\n (e as CustomEvent).detail?.toolName;\n if (name === toolName && onToolCancel) {\n onToolCancel(name);\n }\n };\n\n window.addEventListener(\"toolactivated\", handleActivated);\n window.addEventListener(\"toolcancel\", handleCancel);\n\n return () => {\n window.removeEventListener(\"toolactivated\", handleActivated);\n window.removeEventListener(\"toolcancel\", handleCancel);\n };\n }, [toolName, onToolActivated, onToolCancel]);\n\n const handleSubmit = useCallback(\n (e: React.FormEvent<HTMLFormElement>) => {\n if (onSubmit) {\n onSubmit(e.nativeEvent as unknown as WebMCPFormSubmitEvent);\n }\n },\n [onSubmit],\n );\n\n // Build the HTML attributes. React doesn't recognize toolname etc.,\n // so we spread them via a plain object cast.\n const webmcpAttrs: Record<string, string | boolean> = {\n toolname: toolName,\n tooldescription: toolDescription,\n };\n if (toolAutoSubmit) {\n webmcpAttrs.toolautosubmit = \"\";\n }\n\n return (\n <form\n ref={formRef}\n onSubmit={handleSubmit}\n {...webmcpAttrs}\n {...rest}\n >\n {children}\n </form>\n );\n}\n","import React from \"react\";\n\nexport interface WebMCPInputProps\n extends React.InputHTMLAttributes<HTMLInputElement> {\n /** Maps to the `toolparamtitle` attribute (overrides the JSON Schema property key). */\n toolParamTitle?: string;\n /** Maps to the `toolparamdescription` attribute (describes this parameter to agents). */\n toolParamDescription?: string;\n}\n\n/**\n * An `<input>` element enhanced with WebMCP declarative attributes.\n *\n * Use inside a `<WebMCPForm>` to annotate individual form fields\n * for AI agents.\n *\n * @example\n * ```tsx\n * <WebMCPInput\n * type=\"text\"\n * name=\"name\"\n * toolParamDescription=\"Customer's full name (min 2 chars)\"\n * required\n * minLength={2}\n * />\n * ```\n */\nexport const WebMCPInput = React.forwardRef<HTMLInputElement, WebMCPInputProps>(\n ({ toolParamTitle, toolParamDescription, ...rest }, ref) => {\n const webmcpAttrs: Record<string, string> = {};\n if (toolParamTitle) {\n webmcpAttrs.toolparamtitle = toolParamTitle;\n }\n if (toolParamDescription) {\n webmcpAttrs.toolparamdescription = toolParamDescription;\n }\n\n return <input ref={ref} {...webmcpAttrs} {...rest} />;\n },\n);\n\nWebMCPInput.displayName = \"WebMCPInput\";\n","import React from \"react\";\n\nexport interface WebMCPSelectProps\n extends React.SelectHTMLAttributes<HTMLSelectElement> {\n /** Maps to the `toolparamtitle` attribute (overrides the JSON Schema property key). */\n toolParamTitle?: string;\n /** Maps to the `toolparamdescription` attribute (describes this parameter to agents). */\n toolParamDescription?: string;\n children: React.ReactNode;\n}\n\n/**\n * A `<select>` element enhanced with WebMCP declarative attributes.\n *\n * Use inside a `<WebMCPForm>` to annotate select inputs for AI agents.\n * The `<option>` values and text are automatically mapped to the tool's\n * JSON Schema `enum` / `oneOf` definitions by the browser.\n *\n * @example\n * ```tsx\n * <WebMCPSelect\n * name=\"seating\"\n * toolParamDescription=\"Preferred seating area\"\n * >\n * <option value=\"Main Dining\">Main Dining Room</option>\n * <option value=\"Terrace\">Terrace (Outdoor)</option>\n * </WebMCPSelect>\n * ```\n */\nexport const WebMCPSelect = React.forwardRef<\n HTMLSelectElement,\n WebMCPSelectProps\n>(({ toolParamTitle, toolParamDescription, children, ...rest }, ref) => {\n const webmcpAttrs: Record<string, string> = {};\n if (toolParamTitle) {\n webmcpAttrs.toolparamtitle = toolParamTitle;\n }\n if (toolParamDescription) {\n webmcpAttrs.toolparamdescription = toolParamDescription;\n }\n\n return (\n <select ref={ref} {...webmcpAttrs} {...rest}>\n {children}\n </select>\n );\n});\n\nWebMCPSelect.displayName = \"WebMCPSelect\";\n","import React from \"react\";\n\nexport interface WebMCPTextareaProps\n extends React.TextareaHTMLAttributes<HTMLTextAreaElement> {\n /** Maps to the `toolparamtitle` attribute (overrides the JSON Schema property key). */\n toolParamTitle?: string;\n /** Maps to the `toolparamdescription` attribute (describes this parameter to agents). */\n toolParamDescription?: string;\n}\n\n/**\n * A `<textarea>` element enhanced with WebMCP declarative attributes.\n *\n * Use inside a `<WebMCPForm>` to annotate textarea inputs for AI agents.\n *\n * @example\n * ```tsx\n * <WebMCPTextarea\n * name=\"requests\"\n * rows={3}\n * toolParamDescription=\"Special requests (allergies, occasions, etc.)\"\n * />\n * ```\n */\nexport const WebMCPTextarea = React.forwardRef<\n HTMLTextAreaElement,\n WebMCPTextareaProps\n>(({ toolParamTitle, toolParamDescription, ...rest }, ref) => {\n const webmcpAttrs: Record<string, string> = {};\n if (toolParamTitle) {\n webmcpAttrs.toolparamtitle = toolParamTitle;\n }\n if (toolParamDescription) {\n webmcpAttrs.toolparamdescription = toolParamDescription;\n }\n\n return <textarea ref={ref} {...webmcpAttrs} {...rest} />;\n});\n\nWebMCPTextarea.displayName = \"WebMCPTextarea\";\n","import React, { createContext, useContext, useMemo } from \"react\";\nimport { isWebMCPAvailable, isWebMCPTestingAvailable } from \"./utils/modelContext\";\n\ninterface WebMCPContextValue {\n /** Whether navigator.modelContext is available in this browser. */\n available: boolean;\n /** Whether navigator.modelContextTesting is available (inspector API). */\n testingAvailable: boolean;\n}\n\nconst WebMCPReactContext = createContext<WebMCPContextValue>({\n available: false,\n testingAvailable: false,\n});\n\n/**\n * Provides WebMCP availability information to the component tree.\n *\n * Wrap your application (or a subtree) with `<WebMCPProvider>` to let\n * child components check WebMCP availability via the `useWebMCPStatus` hook.\n *\n * @example\n * ```tsx\n * function App() {\n * return (\n * <WebMCPProvider>\n * <MyComponent />\n * </WebMCPProvider>\n * );\n * }\n * ```\n */\nexport function WebMCPProvider({ children }: { children: React.ReactNode }) {\n const value = useMemo<WebMCPContextValue>(\n () => ({\n available: isWebMCPAvailable(),\n testingAvailable: isWebMCPTestingAvailable(),\n }),\n [],\n );\n\n return (\n <WebMCPReactContext.Provider value={value}>\n {children}\n </WebMCPReactContext.Provider>\n );\n}\n\n/**\n * Returns the current WebMCP availability status.\n *\n * Must be used within a `<WebMCPProvider>`.\n *\n * @example\n * ```tsx\n * function StatusBadge() {\n * const { available } = useWebMCPStatus();\n * return <span>{available ? \"WebMCP Ready\" : \"WebMCP Not Available\"}</span>;\n * }\n * ```\n */\nexport function useWebMCPStatus(): WebMCPContextValue {\n return useContext(WebMCPReactContext);\n}\n"]}
1
+ {"version":3,"sources":["../src/utils/modelContext.ts","../src/hooks/useWebMCPTool.ts","../src/hooks/useWebMCPContext.ts","../src/hooks/useToolEvent.ts","../src/components/WebMCPForm.tsx","../src/components/WebMCPInput.tsx","../src/components/WebMCPSelect.tsx","../src/components/WebMCPTextarea.tsx","../src/context.tsx","../src/adapters/extractFields.ts","../src/adapters/buildSchema.ts","../src/adapters/validateSchema.ts","../src/adapters/useSchemaCollector.ts","../src/adapters/WebMCPTool.tsx","../src/adapters/useRegisterField.ts","../src/adapters/WebMCPField.tsx","../src/adapters/index.ts"],"names":["useRef","useEffect","useCallback","jsx","React","createContext","useMemo","useContext","useState","Fragment"],"mappings":"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","file":"index.js","sourcesContent":["import type { ModelContext } from \"../types\";\n\n/**\n * Returns the navigator.modelContext API if available, or null.\n */\nexport function getModelContext(): ModelContext | null {\n if (\n typeof window !== \"undefined\" &&\n typeof window.navigator !== \"undefined\" &&\n window.navigator.modelContext\n ) {\n return window.navigator.modelContext;\n }\n return null;\n}\n\n/**\n * Returns true if the WebMCP API (navigator.modelContext) is available\n * in the current browsing context.\n */\nexport function isWebMCPAvailable(): boolean {\n return getModelContext() !== null;\n}\n\n/**\n * Returns true if the WebMCP testing API (navigator.modelContextTesting)\n * is available. This is the API used by the Model Context Tool Inspector\n * extension and requires the \"WebMCP for testing\" Chrome flag.\n */\nexport function isWebMCPTestingAvailable(): boolean {\n return (\n typeof window !== \"undefined\" &&\n typeof window.navigator !== \"undefined\" &&\n !!window.navigator.modelContextTesting\n );\n}\n\n/**\n * Logs a warning when WebMCP is not available. Only fires in development\n * builds so the warning is stripped from production bundles.\n */\nexport function warnIfUnavailable(hookName: string): void {\n if (process.env.NODE_ENV !== \"production\") {\n if (!isWebMCPAvailable()) {\n console.warn(\n `[react-webmcp] ${hookName}: navigator.modelContext is not available. ` +\n `Ensure you are running Chrome 146+ with the \"WebMCP for testing\" flag enabled.`,\n );\n }\n }\n}\n","import { useEffect, useRef } from \"react\";\nimport type { UseWebMCPToolConfig } from \"../types\";\nimport { getModelContext, warnIfUnavailable } from \"../utils/modelContext\";\n\n/**\n * Produces a stable fingerprint for a single tool definition so we can\n * detect meaningful changes without being tricked by new object references\n * created on every render (e.g. inline schema literals).\n */\nfunction toolFingerprint(config: UseWebMCPToolConfig): string {\n return `${config.name}::${config.description}::${JSON.stringify(config.inputSchema)}::${JSON.stringify(config.outputSchema ?? {})}::${JSON.stringify(config.annotations ?? {})}`;\n}\n\n/**\n * Register a single WebMCP tool via the imperative API.\n *\n * The tool is registered with `navigator.modelContext.registerTool()` when\n * the component mounts and unregistered with `unregisterTool()` on unmount.\n * If the tool definition changes (name, description, schemas, or\n * annotations), the previous tool is unregistered and the new one is\n * registered.\n *\n * Object/array props like `inputSchema` and `annotations` are compared by\n * value (serialised fingerprint), so passing inline literals on every render\n * will **not** cause unnecessary re-registration.\n *\n * The `execute` callback is always called through a ref, so it does not\n * need to be memoised by the consumer.\n *\n * @example\n * ```tsx\n * useWebMCPTool({\n * name: \"searchFlights\",\n * description: \"Search for flights with the given parameters.\",\n * inputSchema: {\n * type: \"object\",\n * properties: {\n * origin: { type: \"string\", description: \"Origin IATA code\" },\n * destination: { type: \"string\", description: \"Destination IATA code\" },\n * },\n * required: [\"origin\", \"destination\"],\n * },\n * execute: async ({ origin, destination }) => {\n * const results = await api.searchFlights(origin, destination);\n * return { content: [{ type: \"text\", text: JSON.stringify(results) }] };\n * },\n * });\n * ```\n */\nexport function useWebMCPTool(config: UseWebMCPToolConfig): void {\n const registeredNameRef = useRef<string | null>(null);\n const configRef = useRef(config);\n configRef.current = config;\n\n // Derive a stable fingerprint from the definition values.\n const fingerprint = toolFingerprint(config);\n\n useEffect(() => {\n const mc = getModelContext();\n if (!mc) {\n warnIfUnavailable(\"useWebMCPTool\");\n return;\n }\n\n // Unregister the previous tool if the name changed\n if (registeredNameRef.current && registeredNameRef.current !== config.name) {\n try {\n mc.unregisterTool(registeredNameRef.current);\n } catch {\n // Tool may have already been unregistered\n }\n }\n\n // Build the tool definition matching the navigator.modelContext shape.\n // The execute function is always routed through configRef so callers\n // never need to memoise their handler.\n const toolDef: Record<string, unknown> = {\n name: config.name,\n description: config.description,\n inputSchema: config.inputSchema,\n execute: (input: Record<string, unknown>) => {\n return configRef.current.execute(input);\n },\n };\n if (config.outputSchema) {\n toolDef.outputSchema = config.outputSchema;\n }\n if (config.annotations) {\n toolDef.annotations = config.annotations;\n }\n\n try {\n mc.registerTool(toolDef as unknown as Parameters<typeof mc.registerTool>[0]);\n registeredNameRef.current = config.name;\n } catch (err) {\n if (process.env.NODE_ENV !== \"production\") {\n console.error(\n `[react-webmcp] Failed to register tool \"${config.name}\":`,\n err,\n );\n }\n }\n\n return () => {\n try {\n mc.unregisterTool(config.name);\n } catch {\n // Tool may have already been unregistered externally\n }\n registeredNameRef.current = null;\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps -- fingerprint\n // captures the serialised value of all definition fields; config.name\n // is included so the cleanup closure captures the correct name.\n }, [fingerprint, config.name]);\n}\n","import { useEffect, useRef } from \"react\";\nimport type { WebMCPToolDefinition } from \"../types\";\nimport { getModelContext, warnIfUnavailable } from \"../utils/modelContext\";\n\n/**\n * Produces a stable fingerprint string from a tools array so we can detect\n * meaningful changes without being tricked by new array references.\n * Compares tool names, descriptions, and serialised input schemas.\n */\nfunction toolsFingerprint(tools: WebMCPToolDefinition[]): string {\n return tools\n .map(\n (t) =>\n `${t.name}::${t.description}::${JSON.stringify(t.inputSchema)}::${JSON.stringify(t.outputSchema ?? {})}::${JSON.stringify(t.annotations ?? {})}`,\n )\n .join(\"|\");\n}\n\n/**\n * Register multiple WebMCP tools at once using `provideContext()`.\n *\n * Unlike `useWebMCPTool` which manages a single tool, `useWebMCPContext`\n * replaces the entire set of registered tools. This is useful when the\n * application state changes significantly and you want to expose a\n * completely different set of tools.\n *\n * On unmount, all tools are cleared via `clearContext()`.\n *\n * The hook performs a deep comparison of tool definitions (name, description,\n * inputSchema, annotations) so that passing a new array reference on every\n * render does **not** cause unnecessary re-registration.\n *\n * @example\n * ```tsx\n * useWebMCPContext({\n * tools: [\n * {\n * name: \"addTodo\",\n * description: \"Add a new item to the todo list\",\n * inputSchema: { type: \"object\", properties: { text: { type: \"string\" } } },\n * execute: ({ text }) => ({ content: [{ type: \"text\", text: `Added: ${text}` }] }),\n * },\n * {\n * name: \"markComplete\",\n * description: \"Mark a todo item as complete\",\n * inputSchema: { type: \"object\", properties: { id: { type: \"string\" } } },\n * execute: ({ id }) => ({ content: [{ type: \"text\", text: `Completed: ${id}` }] }),\n * },\n * ],\n * });\n * ```\n */\nexport function useWebMCPContext(config: {\n tools: WebMCPToolDefinition[];\n}): void {\n // Keep a ref to the latest tools so the execute callbacks always close\n // over current handlers without triggering the effect.\n const toolsRef = useRef(config.tools);\n toolsRef.current = config.tools;\n\n const fingerprint = toolsFingerprint(config.tools);\n\n useEffect(() => {\n const mc = getModelContext();\n if (!mc) {\n warnIfUnavailable(\"useWebMCPContext\");\n return;\n }\n\n // Wrap execute functions so they always call through the latest ref,\n // allowing callers to pass inline arrow functions without triggering\n // the effect.\n const stableTools = toolsRef.current.map((tool, idx) => {\n const def: Record<string, unknown> = {\n name: tool.name,\n description: tool.description,\n inputSchema: tool.inputSchema,\n execute: (input: Record<string, unknown>) => {\n return toolsRef.current[idx].execute(input);\n },\n };\n if (tool.annotations) {\n def.annotations = tool.annotations;\n }\n if (tool.outputSchema) {\n def.outputSchema = tool.outputSchema;\n }\n return def;\n });\n\n try {\n mc.provideContext({\n tools: stableTools as unknown as Parameters<typeof mc.provideContext>[0][\"tools\"],\n });\n } catch (err) {\n if (process.env.NODE_ENV !== \"production\") {\n console.error(\"[react-webmcp] Failed to provide context:\", err);\n }\n }\n\n return () => {\n try {\n mc.clearContext();\n } catch {\n // Context may have already been cleared\n }\n };\n }, [fingerprint]);\n}\n","import { useEffect } from \"react\";\n\n/**\n * Listen for WebMCP tool lifecycle events on the window.\n *\n * The browser fires `toolactivated` when an AI agent invokes a declarative\n * tool (form fields are pre-filled) and `toolcancel` when the agent or\n * user cancels the operation.\n *\n * @param event - The event name: \"toolactivated\" or \"toolcancel\"\n * @param callback - Called with the tool name when the event fires\n * @param toolNameFilter - Optional: only fire for a specific tool name\n *\n * @example\n * ```tsx\n * useToolEvent(\"toolactivated\", (toolName) => {\n * console.log(`Agent activated tool: ${toolName}`);\n * validateForm();\n * }, \"book_table\");\n * ```\n */\nexport function useToolEvent(\n event: \"toolactivated\" | \"toolcancel\",\n callback: (toolName: string) => void,\n toolNameFilter?: string,\n): void {\n useEffect(() => {\n const handler = (e: Event) => {\n const toolName = (e as CustomEvent & { toolName?: string }).toolName ??\n (e as CustomEvent).detail?.toolName;\n if (!toolName) return;\n if (toolNameFilter && toolName !== toolNameFilter) return;\n callback(toolName);\n };\n\n window.addEventListener(event, handler);\n return () => {\n window.removeEventListener(event, handler);\n };\n }, [event, callback, toolNameFilter]);\n}\n","import React, { useCallback, useEffect, useRef } from \"react\";\nimport type { WebMCPFormSubmitEvent } from \"../types\";\n\nexport interface WebMCPFormProps\n extends Omit<React.FormHTMLAttributes<HTMLFormElement>, \"onSubmit\"> {\n /** The tool name exposed to AI agents. Maps to the `toolname` HTML attribute. */\n toolName: string;\n /** Description of what this tool does. Maps to `tooldescription`. */\n toolDescription: string;\n /** If true, the form auto-submits when filled by an agent. Maps to `toolautosubmit`. */\n toolAutoSubmit?: boolean;\n /**\n * Submit handler that receives the enhanced SubmitEvent with\n * `agentInvoked` and `respondWith` properties.\n */\n onSubmit?: (event: WebMCPFormSubmitEvent) => void;\n /** Called when a tool activation event fires for this form's tool. */\n onToolActivated?: (toolName: string) => void;\n /** Called when a tool cancel event fires for this form's tool. */\n onToolCancel?: (toolName: string) => void;\n children: React.ReactNode;\n}\n\n/**\n * A React wrapper for the WebMCP declarative API.\n *\n * Renders a `<form>` element with the appropriate WebMCP HTML attributes\n * (`toolname`, `tooldescription`, `toolautosubmit`) so the browser\n * automatically registers it as a WebMCP tool.\n *\n * @example\n * ```tsx\n * <WebMCPForm\n * toolName=\"book_table\"\n * toolDescription=\"Book a table at the restaurant\"\n * onSubmit={(e) => {\n * e.preventDefault();\n * if (e.agentInvoked) {\n * e.respondWith(Promise.resolve(\"Booking confirmed!\"));\n * }\n * }}\n * >\n * <WebMCPInput name=\"name\" label=\"Full Name\" />\n * <button type=\"submit\">Book</button>\n * </WebMCPForm>\n * ```\n */\nexport function WebMCPForm({\n toolName,\n toolDescription,\n toolAutoSubmit,\n onSubmit,\n onToolActivated,\n onToolCancel,\n children,\n ...rest\n}: WebMCPFormProps) {\n const formRef = useRef<HTMLFormElement>(null);\n\n // Listen for toolactivated and toolcancel events\n useEffect(() => {\n const handleActivated = (e: Event) => {\n const name =\n (e as CustomEvent & { toolName?: string }).toolName ??\n (e as CustomEvent).detail?.toolName;\n if (name === toolName && onToolActivated) {\n onToolActivated(name);\n }\n };\n\n const handleCancel = (e: Event) => {\n const name =\n (e as CustomEvent & { toolName?: string }).toolName ??\n (e as CustomEvent).detail?.toolName;\n if (name === toolName && onToolCancel) {\n onToolCancel(name);\n }\n };\n\n window.addEventListener(\"toolactivated\", handleActivated);\n window.addEventListener(\"toolcancel\", handleCancel);\n\n return () => {\n window.removeEventListener(\"toolactivated\", handleActivated);\n window.removeEventListener(\"toolcancel\", handleCancel);\n };\n }, [toolName, onToolActivated, onToolCancel]);\n\n const handleSubmit = useCallback(\n (e: React.FormEvent<HTMLFormElement>) => {\n if (onSubmit) {\n onSubmit(e.nativeEvent as unknown as WebMCPFormSubmitEvent);\n }\n },\n [onSubmit],\n );\n\n // Build the HTML attributes. React doesn't recognize toolname etc.,\n // so we spread them via a plain object cast.\n const webmcpAttrs: Record<string, string | boolean> = {\n toolname: toolName,\n tooldescription: toolDescription,\n };\n if (toolAutoSubmit) {\n webmcpAttrs.toolautosubmit = \"\";\n }\n\n return (\n <form\n ref={formRef}\n onSubmit={handleSubmit}\n {...webmcpAttrs}\n {...rest}\n >\n {children}\n </form>\n );\n}\n","import React from \"react\";\n\nexport interface WebMCPInputProps\n extends React.InputHTMLAttributes<HTMLInputElement> {\n /** Maps to the `toolparamtitle` attribute (overrides the JSON Schema property key). */\n toolParamTitle?: string;\n /** Maps to the `toolparamdescription` attribute (describes this parameter to agents). */\n toolParamDescription?: string;\n}\n\n/**\n * An `<input>` element enhanced with WebMCP declarative attributes.\n *\n * Use inside a `<WebMCPForm>` to annotate individual form fields\n * for AI agents.\n *\n * @example\n * ```tsx\n * <WebMCPInput\n * type=\"text\"\n * name=\"name\"\n * toolParamDescription=\"Customer's full name (min 2 chars)\"\n * required\n * minLength={2}\n * />\n * ```\n */\nexport const WebMCPInput = React.forwardRef<HTMLInputElement, WebMCPInputProps>(\n ({ toolParamTitle, toolParamDescription, ...rest }, ref) => {\n const webmcpAttrs: Record<string, string> = {};\n if (toolParamTitle) {\n webmcpAttrs.toolparamtitle = toolParamTitle;\n }\n if (toolParamDescription) {\n webmcpAttrs.toolparamdescription = toolParamDescription;\n }\n\n return <input ref={ref} {...webmcpAttrs} {...rest} />;\n },\n);\n\nWebMCPInput.displayName = \"WebMCPInput\";\n","import React from \"react\";\n\nexport interface WebMCPSelectProps\n extends React.SelectHTMLAttributes<HTMLSelectElement> {\n /** Maps to the `toolparamtitle` attribute (overrides the JSON Schema property key). */\n toolParamTitle?: string;\n /** Maps to the `toolparamdescription` attribute (describes this parameter to agents). */\n toolParamDescription?: string;\n children: React.ReactNode;\n}\n\n/**\n * A `<select>` element enhanced with WebMCP declarative attributes.\n *\n * Use inside a `<WebMCPForm>` to annotate select inputs for AI agents.\n * The `<option>` values and text are automatically mapped to the tool's\n * JSON Schema `enum` / `oneOf` definitions by the browser.\n *\n * @example\n * ```tsx\n * <WebMCPSelect\n * name=\"seating\"\n * toolParamDescription=\"Preferred seating area\"\n * >\n * <option value=\"Main Dining\">Main Dining Room</option>\n * <option value=\"Terrace\">Terrace (Outdoor)</option>\n * </WebMCPSelect>\n * ```\n */\nexport const WebMCPSelect = React.forwardRef<\n HTMLSelectElement,\n WebMCPSelectProps\n>(({ toolParamTitle, toolParamDescription, children, ...rest }, ref) => {\n const webmcpAttrs: Record<string, string> = {};\n if (toolParamTitle) {\n webmcpAttrs.toolparamtitle = toolParamTitle;\n }\n if (toolParamDescription) {\n webmcpAttrs.toolparamdescription = toolParamDescription;\n }\n\n return (\n <select ref={ref} {...webmcpAttrs} {...rest}>\n {children}\n </select>\n );\n});\n\nWebMCPSelect.displayName = \"WebMCPSelect\";\n","import React from \"react\";\n\nexport interface WebMCPTextareaProps\n extends React.TextareaHTMLAttributes<HTMLTextAreaElement> {\n /** Maps to the `toolparamtitle` attribute (overrides the JSON Schema property key). */\n toolParamTitle?: string;\n /** Maps to the `toolparamdescription` attribute (describes this parameter to agents). */\n toolParamDescription?: string;\n}\n\n/**\n * A `<textarea>` element enhanced with WebMCP declarative attributes.\n *\n * Use inside a `<WebMCPForm>` to annotate textarea inputs for AI agents.\n *\n * @example\n * ```tsx\n * <WebMCPTextarea\n * name=\"requests\"\n * rows={3}\n * toolParamDescription=\"Special requests (allergies, occasions, etc.)\"\n * />\n * ```\n */\nexport const WebMCPTextarea = React.forwardRef<\n HTMLTextAreaElement,\n WebMCPTextareaProps\n>(({ toolParamTitle, toolParamDescription, ...rest }, ref) => {\n const webmcpAttrs: Record<string, string> = {};\n if (toolParamTitle) {\n webmcpAttrs.toolparamtitle = toolParamTitle;\n }\n if (toolParamDescription) {\n webmcpAttrs.toolparamdescription = toolParamDescription;\n }\n\n return <textarea ref={ref} {...webmcpAttrs} {...rest} />;\n});\n\nWebMCPTextarea.displayName = \"WebMCPTextarea\";\n","import React, { createContext, useContext, useMemo } from \"react\";\nimport { isWebMCPAvailable, isWebMCPTestingAvailable } from \"./utils/modelContext\";\n\ninterface WebMCPContextValue {\n /** Whether navigator.modelContext is available in this browser. */\n available: boolean;\n /** Whether navigator.modelContextTesting is available (inspector API). */\n testingAvailable: boolean;\n}\n\nconst WebMCPReactContext = createContext<WebMCPContextValue>({\n available: false,\n testingAvailable: false,\n});\n\n/**\n * Provides WebMCP availability information to the component tree.\n *\n * Wrap your application (or a subtree) with `<WebMCPProvider>` to let\n * child components check WebMCP availability via the `useWebMCPStatus` hook.\n *\n * @example\n * ```tsx\n * function App() {\n * return (\n * <WebMCPProvider>\n * <MyComponent />\n * </WebMCPProvider>\n * );\n * }\n * ```\n */\nexport function WebMCPProvider({ children }: { children: React.ReactNode }) {\n const value = useMemo<WebMCPContextValue>(\n () => ({\n available: isWebMCPAvailable(),\n testingAvailable: isWebMCPTestingAvailable(),\n }),\n [],\n );\n\n return (\n <WebMCPReactContext.Provider value={value}>\n {children}\n </WebMCPReactContext.Provider>\n );\n}\n\n/**\n * Returns the current WebMCP availability status.\n *\n * Must be used within a `<WebMCPProvider>`.\n *\n * @example\n * ```tsx\n * function StatusBadge() {\n * const { available } = useWebMCPStatus();\n * return <span>{available ? \"WebMCP Ready\" : \"WebMCP Not Available\"}</span>;\n * }\n * ```\n */\nexport function useWebMCPStatus(): WebMCPContextValue {\n return useContext(WebMCPReactContext);\n}\n","import React from \"react\";\nimport type { FieldDefinition } from \"./types\";\n\n/**\n * Recursively extract option values from React children.\n *\n * Detects elements that have a `value` prop (e.g. `<MenuItem value=\"low\">Low</MenuItem>`)\n * and collects `{ value, label }` pairs. The label is derived from string children\n * or falls back to `String(value)`.\n *\n * @example\n * ```tsx\n * const options = extractOptions(\n * <>\n * <MenuItem value=\"low\">Low</MenuItem>\n * <MenuItem value=\"high\">High</MenuItem>\n * </>\n * );\n * // [{ value: \"low\", label: \"Low\" }, { value: \"high\", label: \"High\" }]\n * ```\n */\nexport function extractOptions(\n children: React.ReactNode,\n): { value: string | number | boolean; label: string }[] {\n const results: { value: string | number | boolean; label: string }[] = [];\n\n React.Children.toArray(children).forEach((child) => {\n if (!React.isValidElement(child)) return;\n\n const props = child.props as Record<string, unknown>;\n\n if (props.value !== undefined && props.value !== null) {\n const value = props.value as string | number | boolean;\n let label: string;\n\n if (typeof props.children === \"string\") {\n label = props.children;\n } else {\n label = String(value);\n }\n\n results.push({ value, label });\n }\n\n if (props.children) {\n results.push(...extractOptions(props.children as React.ReactNode));\n }\n });\n\n return results;\n}\n\n/**\n * Recursively extract field definitions from a React children tree.\n *\n * Walks the tree using `React.Children.toArray` (safe, pure traversal).\n * Detects field names from `props.name`, `props.inputProps.name`, or\n * `props.slotProps.input.name`. When a named element is found, it builds\n * a `FieldDefinition` from its props and auto-detects enum values from\n * its children. Elements without a name are recursed into.\n *\n * @example\n * ```tsx\n * const fields = extractFields(\n * <>\n * <Input name=\"email\" type=\"email\" required />\n * <Select name=\"priority\">\n * <MenuItem value=\"low\">Low</MenuItem>\n * <MenuItem value=\"high\">High</MenuItem>\n * </Select>\n * </>\n * );\n * ```\n */\nexport function extractFields(children: React.ReactNode): FieldDefinition[] {\n const fields: FieldDefinition[] = [];\n\n React.Children.toArray(children).forEach((child) => {\n if (!React.isValidElement(child)) return;\n\n const props = child.props as Record<string, unknown>;\n\n const inputProps = props.inputProps as Record<string, unknown> | undefined;\n const slotInput = (props.slotProps as Record<string, unknown> | undefined)\n ?.input as Record<string, unknown> | undefined;\n\n const name =\n (props.name as string | undefined) ??\n (inputProps?.name as string | undefined) ??\n (slotInput?.name as string | undefined);\n\n if (name) {\n const field: FieldDefinition = { name };\n\n if (props.type !== undefined) field.type = props.type as string;\n if (props.required !== undefined) field.required = Boolean(props.required);\n if (props.min !== undefined) field.min = Number(props.min);\n if (props.max !== undefined) field.max = Number(props.max);\n if (props.minLength !== undefined) field.minLength = Number(props.minLength);\n if (props.maxLength !== undefined) field.maxLength = Number(props.maxLength);\n if (props.pattern !== undefined) field.pattern = props.pattern as string;\n\n if (props.children) {\n const options = extractOptions(props.children as React.ReactNode);\n if (options.length > 0) {\n field.enumValues = options.map((o) => o.value);\n field.oneOf = options;\n }\n }\n\n fields.push(field);\n } else if (props.children) {\n fields.push(...extractFields(props.children as React.ReactNode));\n }\n });\n\n return fields;\n}\n","import type { JSONSchema, JSONSchemaProperty } from \"../types\";\nimport type { FieldDefinition } from \"./types\";\n\n/**\n * Map an HTML input type to the corresponding JSON Schema type.\n *\n * @example\n * ```ts\n * mapHtmlTypeToSchemaType(\"number\"); // \"number\"\n * mapHtmlTypeToSchemaType(\"checkbox\"); // \"boolean\"\n * mapHtmlTypeToSchemaType(\"email\"); // \"string\"\n * ```\n */\nexport function mapHtmlTypeToSchemaType(\n htmlType?: string,\n): \"string\" | \"number\" | \"boolean\" {\n switch (htmlType) {\n case \"number\":\n case \"range\":\n return \"number\";\n case \"checkbox\":\n return \"boolean\";\n default:\n return \"string\";\n }\n}\n\n/**\n * Build a deterministic JSON Schema from an array of field definitions.\n *\n * Property names are sorted alphabetically and the `required` array is\n * also sorted, ensuring identical output regardless of field insertion\n * order.\n *\n * @example\n * ```ts\n * const schema = buildInputSchema([\n * { name: \"email\", type: \"email\", required: true },\n * { name: \"age\", type: \"number\", min: 0, max: 120 },\n * ]);\n * ```\n */\nexport function buildInputSchema(fields: FieldDefinition[]): JSONSchema {\n const properties: Record<string, JSONSchemaProperty> = {};\n const required: string[] = [];\n\n const sortedFields = [...fields].sort((a, b) => a.name.localeCompare(b.name));\n\n for (const field of sortedFields) {\n const prop: JSONSchemaProperty = {\n type: mapHtmlTypeToSchemaType(field.type),\n };\n\n if (field.title) prop.title = field.title;\n if (field.description) prop.description = field.description;\n if (field.min !== undefined) prop.minimum = field.min;\n if (field.max !== undefined) prop.maximum = field.max;\n if (field.minLength !== undefined) prop.minLength = field.minLength;\n if (field.maxLength !== undefined) prop.maxLength = field.maxLength;\n if (field.pattern) prop.pattern = field.pattern;\n\n if (field.enumValues && field.enumValues.length > 0) {\n prop.enum = field.enumValues;\n }\n\n if (field.oneOf && field.oneOf.length > 0) {\n prop.oneOf = field.oneOf.map((opt) => ({\n const: opt.value,\n title: opt.label,\n }));\n }\n\n properties[field.name] = prop;\n\n if (field.required) {\n required.push(field.name);\n }\n }\n\n const schema: JSONSchema = {\n type: \"object\",\n properties,\n };\n\n if (required.length > 0) {\n schema.required = required.sort();\n }\n\n return schema;\n}\n","import type { FieldDefinition } from \"./types\";\nimport { mapHtmlTypeToSchemaType } from \"./buildSchema\";\n\n/**\n * Validate an array of field definitions for common schema issues.\n *\n * In production this is a no-op. In development it checks for:\n * - Duplicate field names\n * - `pattern` used on non-string types\n * - `min`/`max` used on non-number types\n * - `minLength`/`maxLength` used on non-string types\n * - Enum values whose types don't match the declared field type\n *\n * By default warnings are logged via `console.warn` with the\n * `[react-webmcp]` prefix. When `strict` is `true`, an `Error` is\n * thrown instead.\n *\n * @example\n * ```ts\n * validateSchema(fields); // warns in dev\n * validateSchema(fields, { strict: true }); // throws in dev\n * ```\n */\nexport function validateSchema(\n fields: FieldDefinition[],\n options?: { strict?: boolean },\n): void {\n if (process.env.NODE_ENV === \"production\") return;\n\n const strict = options?.strict ?? false;\n const issues: string[] = [];\n\n const seen = new Set<string>();\n for (const field of fields) {\n if (seen.has(field.name)) {\n issues.push(`Duplicate field name \"${field.name}\".`);\n }\n seen.add(field.name);\n\n const schemaType = mapHtmlTypeToSchemaType(field.type);\n\n if (field.pattern !== undefined && schemaType !== \"string\") {\n issues.push(\n `Field \"${field.name}\": pattern is only valid for string types, but type is \"${schemaType}\".`,\n );\n }\n\n if ((field.min !== undefined || field.max !== undefined) && schemaType !== \"number\") {\n issues.push(\n `Field \"${field.name}\": min/max are only valid for number types, but type is \"${schemaType}\".`,\n );\n }\n\n if (\n (field.minLength !== undefined || field.maxLength !== undefined) &&\n schemaType !== \"string\"\n ) {\n issues.push(\n `Field \"${field.name}\": minLength/maxLength are only valid for string types, but type is \"${schemaType}\".`,\n );\n }\n\n if (field.enumValues && field.enumValues.length > 0) {\n for (const val of field.enumValues) {\n const valType = typeof val;\n if (schemaType === \"string\" && valType !== \"string\") {\n issues.push(\n `Field \"${field.name}\": enum value ${JSON.stringify(val)} is not a string.`,\n );\n }\n if (schemaType === \"number\" && valType !== \"number\") {\n issues.push(\n `Field \"${field.name}\": enum value ${JSON.stringify(val)} is not a number.`,\n );\n }\n if (schemaType === \"boolean\" && valType !== \"boolean\") {\n issues.push(\n `Field \"${field.name}\": enum value ${JSON.stringify(val)} is not a boolean.`,\n );\n }\n }\n }\n }\n\n for (const issue of issues) {\n if (strict) {\n throw new Error(`[react-webmcp] ${issue}`);\n }\n console.warn(`[react-webmcp] ${issue}`);\n }\n}\n","import { createContext, useCallback, useMemo, useRef, useState } from \"react\";\nimport type { JSONSchema } from \"../types\";\nimport type { FieldDefinition, ToolContextValue } from \"./types\";\nimport { extractFields } from \"./extractFields\";\nimport { buildInputSchema } from \"./buildSchema\";\nimport { validateSchema } from \"./validateSchema\";\n\n// ---------------------------------------------------------------------------\n// Tool context — consumed by useRegisterField\n// ---------------------------------------------------------------------------\n\n/**\n * React context used by `WebMCP.Tool` to expose field registration to\n * descendant components. Consumers should use `useRegisterField` rather\n * than accessing this context directly.\n */\nexport const ToolContext = createContext<ToolContextValue | null>(null);\n\n// ---------------------------------------------------------------------------\n// Private helpers (not exported — same pattern as toolFingerprint)\n// ---------------------------------------------------------------------------\n\n/**\n * Produce a stable string representation of a field array for change\n * detection in dependency arrays.\n */\nfunction fieldsFingerprint(fields: FieldDefinition[]): string {\n return fields\n .map(\n (f) =>\n `${f.name}::${f.type ?? \"\"}::${f.required ?? \"\"}::${f.title ?? \"\"}::${f.description ?? \"\"}::${JSON.stringify(f.enumValues ?? [])}::${JSON.stringify(f.oneOf ?? [])}::${f.min ?? \"\"}::${f.max ?? \"\"}::${f.minLength ?? \"\"}::${f.maxLength ?? \"\"}::${f.pattern ?? \"\"}`,\n )\n .join(\"|\");\n}\n\n/**\n * Merge a base field definition with an override. Skips `undefined`\n * values in the override so they don't clobber defined base values.\n * Arrays (`enumValues`, `oneOf`) are replaced wholesale, not concatenated.\n */\nfunction mergeField(\n base: FieldDefinition,\n override: Partial<FieldDefinition>,\n): FieldDefinition {\n const result = { ...base };\n for (const key of Object.keys(override) as Array<keyof FieldDefinition>) {\n if (override[key] !== undefined) {\n (result as Record<string, unknown>)[key] = override[key];\n }\n }\n return result;\n}\n\n// ---------------------------------------------------------------------------\n// Hook\n// ---------------------------------------------------------------------------\n\nexport interface UseSchemaCollectorOptions {\n /** React children to traverse for auto-detected fields. */\n children: React.ReactNode;\n /** Optional field overrides keyed by field name. */\n fields?: Record<string, Partial<FieldDefinition>>;\n /** When true, validation issues throw instead of warn. */\n strict?: boolean;\n}\n\n/**\n * Core engine hook that collects field definitions from three sources,\n * merges them, validates in dev, and builds a deterministic JSON Schema.\n *\n * **Sources** (lowest to highest priority):\n * 1. Children traversal (auto-detected from React tree)\n * 2. `fields` prop (enrichment / override)\n * 3. Context-registered fields (via `useRegisterField`)\n *\n * @example\n * ```tsx\n * const { schema, registerField, unregisterField } = useSchemaCollector({\n * children,\n * fields: { email: { description: \"Recipient\" } },\n * });\n * ```\n */\nexport function useSchemaCollector({\n children,\n fields: fieldsProp,\n strict,\n}: UseSchemaCollectorOptions): {\n schema: JSONSchema;\n registerField: (field: FieldDefinition) => void;\n unregisterField: (name: string) => void;\n} {\n // Source 3: context-registered fields (useRegisterField)\n const contextFieldsRef = useRef<Map<string, FieldDefinition>>(new Map());\n const [version, setVersion] = useState(0);\n\n const registerField = useCallback((field: FieldDefinition) => {\n contextFieldsRef.current.set(field.name, field);\n setVersion((v) => v + 1);\n }, []);\n\n const unregisterField = useCallback((name: string) => {\n contextFieldsRef.current.delete(name);\n setVersion((v) => v + 1);\n }, []);\n\n // Source 1: children traversal (cheap O(n), runs every render)\n const childrenFields = extractFields(children);\n const childrenFP = fieldsFingerprint(childrenFields);\n\n // Merge all sources: children < fields prop < context\n const merged = useMemo(() => {\n const fieldMap = new Map<string, FieldDefinition>();\n\n // 1. Children-detected fields (lowest priority)\n for (const field of childrenFields) {\n fieldMap.set(field.name, field);\n }\n\n // 2. Fields prop (enrichment)\n if (fieldsProp) {\n for (const [name, overrides] of Object.entries(fieldsProp)) {\n const existing = fieldMap.get(name);\n if (existing) {\n fieldMap.set(name, mergeField(existing, overrides));\n } else {\n fieldMap.set(name, { name, ...overrides } as FieldDefinition);\n }\n }\n }\n\n // 3. Context-registered fields (highest priority)\n for (const [name, field] of contextFieldsRef.current) {\n const existing = fieldMap.get(name);\n if (existing) {\n fieldMap.set(name, mergeField(existing, field));\n } else {\n fieldMap.set(name, field);\n }\n }\n\n const result = Array.from(fieldMap.values());\n\n if (process.env.NODE_ENV !== \"production\") {\n validateSchema(result, { strict });\n }\n\n return result;\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [childrenFP, fieldsProp, version, strict]);\n\n const mergedFP = fieldsFingerprint(merged);\n\n // Build deterministic JSON Schema\n const schema = useMemo(\n () => buildInputSchema(merged),\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [mergedFP],\n );\n\n return { schema, registerField, unregisterField };\n}\n","import React, { useEffect, useRef } from \"react\";\nimport type { ToolAnnotations } from \"../types\";\nimport type { FieldDefinition } from \"./types\";\nimport { useSchemaCollector, ToolContext } from \"./useSchemaCollector\";\nimport { useWebMCPTool } from \"../hooks/useWebMCPTool\";\n\n/**\n * Props for the `WebMCP.Tool` component.\n */\nexport interface WebMCPToolProps {\n /** Unique tool name exposed to AI agents. */\n name: string;\n /** Human-readable description of what this tool does. */\n description: string;\n /** Handler called when an AI agent invokes this tool. */\n onExecute: (input: Record<string, unknown>) => unknown | Promise<unknown>;\n /** Optional field overrides / enrichment keyed by field name. */\n fields?: Record<string, Partial<FieldDefinition>>;\n /** When true, schema validation issues throw instead of warn. */\n strict?: boolean;\n /** If true, the tool auto-submits when filled by an agent. */\n autoSubmit?: boolean;\n /** Optional metadata hints for agents. */\n annotations?: ToolAnnotations;\n /** Called when a `toolactivated` event fires for this tool. */\n onToolActivated?: (toolName: string) => void;\n /** Called when a `toolcancel` event fires for this tool. */\n onToolCancel?: (toolName: string) => void;\n children: React.ReactNode;\n}\n\n/**\n * Framework-agnostic tool wrapper that collects a JSON Schema from its\n * React children and registers it as a WebMCP tool.\n *\n * Fields are auto-detected from child components (e.g. `<Input name=\"email\" />`),\n * enriched via the `fields` prop, and can be overridden by descendant\n * components using `useRegisterField`.\n *\n * @example\n * ```tsx\n * <WebMCPTool\n * name=\"send_email\"\n * description=\"Send an email to a user\"\n * onExecute={async ({ email, priority }) => {\n * await sendEmail(email, priority);\n * return { content: [{ type: \"text\", text: \"Sent!\" }] };\n * }}\n * fields={{ email: { description: \"Recipient's email\" } }}\n * >\n * <FormControl>\n * <Input name=\"email\" type=\"email\" required />\n * </FormControl>\n * </WebMCPTool>\n * ```\n */\nexport function WebMCPTool({\n name,\n description,\n onExecute,\n fields: fieldsProp,\n strict,\n autoSubmit,\n annotations,\n onToolActivated,\n onToolCancel,\n children,\n}: WebMCPToolProps) {\n const { schema, registerField, unregisterField } = useSchemaCollector({\n children,\n fields: fieldsProp,\n strict,\n });\n\n const executeRef = useRef(onExecute);\n executeRef.current = onExecute;\n\n useWebMCPTool({\n name,\n description,\n inputSchema: schema,\n annotations,\n execute: (input) => executeRef.current(input),\n });\n\n // Listen for toolactivated / toolcancel events (same pattern as WebMCPForm)\n useEffect(() => {\n if (typeof window === \"undefined\") return;\n if (!onToolActivated && !onToolCancel) return;\n\n const handleActivated = (e: Event) => {\n const toolName =\n (e as CustomEvent & { toolName?: string }).toolName ??\n (e as CustomEvent).detail?.toolName;\n if (toolName === name && onToolActivated) {\n onToolActivated(toolName);\n }\n };\n\n const handleCancel = (e: Event) => {\n const toolName =\n (e as CustomEvent & { toolName?: string }).toolName ??\n (e as CustomEvent).detail?.toolName;\n if (toolName === name && onToolCancel) {\n onToolCancel(toolName);\n }\n };\n\n window.addEventListener(\"toolactivated\", handleActivated);\n window.addEventListener(\"toolcancel\", handleCancel);\n\n return () => {\n window.removeEventListener(\"toolactivated\", handleActivated);\n window.removeEventListener(\"toolcancel\", handleCancel);\n };\n }, [name, onToolActivated, onToolCancel]);\n\n return (\n <ToolContext.Provider value={{ registerField, unregisterField }}>\n {children}\n </ToolContext.Provider>\n );\n}\n\nWebMCPTool.displayName = \"WebMCP.Tool\";\n","import { useContext, useEffect } from \"react\";\nimport type { FieldDefinition } from \"./types\";\nimport { ToolContext } from \"./useSchemaCollector\";\n\n/**\n * Produce a stable fingerprint for a field definition so dependency\n * arrays only trigger when meaningful values change.\n */\nfunction fieldFingerprint(field: FieldDefinition): string {\n return `${field.name}::${field.type ?? \"\"}::${field.required ?? \"\"}::${field.title ?? \"\"}::${field.description ?? \"\"}::${JSON.stringify(field.enumValues ?? [])}::${JSON.stringify(field.oneOf ?? [])}::${field.min ?? \"\"}::${field.max ?? \"\"}::${field.minLength ?? \"\"}::${field.maxLength ?? \"\"}::${field.pattern ?? \"\"}`;\n}\n\n/**\n * Register a field definition with the nearest `WebMCP.Tool` ancestor.\n *\n * Uses `useEffect` (SSR-safe) so registration only happens on the client.\n * The field is automatically unregistered on unmount. Re-registration\n * only occurs when the field definition changes (compared by fingerprint).\n *\n * If no `WebMCP.Tool` ancestor exists, a dev-mode warning is logged.\n *\n * @example\n * ```tsx\n * function MyField() {\n * useRegisterField({ name: \"email\", type: \"email\", required: true });\n * return <input name=\"email\" type=\"email\" required />;\n * }\n * ```\n */\nexport function useRegisterField(field: FieldDefinition): void {\n const ctx = useContext(ToolContext);\n const fp = fieldFingerprint(field);\n\n useEffect(() => {\n if (!ctx) {\n if (process.env.NODE_ENV !== \"production\") {\n console.warn(\n `[react-webmcp] useRegisterField: no WebMCP.Tool context found for field \"${field.name}\". ` +\n `Wrap this component in a <WebMCP.Tool> to register fields.`,\n );\n }\n return;\n }\n\n ctx.registerField(field);\n\n return () => {\n try {\n ctx.unregisterField(field.name);\n } catch {\n // Field may have already been unregistered\n }\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [fp]);\n}\n","import React from \"react\";\nimport type { FieldDefinition } from \"./types\";\nimport { extractOptions } from \"./extractFields\";\nimport { useRegisterField } from \"./useRegisterField\";\n\n/**\n * Props for the `WebMCP.Field` component.\n */\nexport interface WebMCPFieldProps extends Omit<FieldDefinition, \"name\"> {\n /** Field name — must be unique within the parent `WebMCP.Tool`. */\n name: string;\n children: React.ReactNode;\n}\n\n/**\n * Zero-UI wrapper that registers a field with the nearest `WebMCP.Tool`.\n *\n * Use this as an escape hatch for custom components that cannot be\n * auto-detected by the children traversal engine. Enum values are\n * automatically detected from children that have a `value` prop\n * (e.g. `<MenuItem value=\"low\">Low</MenuItem>`).\n *\n * Renders a React Fragment — no extra DOM elements are introduced.\n *\n * @example\n * ```tsx\n * <WebMCP.Field name=\"priority\" description=\"Email priority\">\n * <Select>\n * <MenuItem value=\"low\">Low</MenuItem>\n * <MenuItem value=\"high\">High</MenuItem>\n * </Select>\n * </WebMCP.Field>\n * ```\n */\nexport function WebMCPField({\n children,\n name,\n ...rest\n}: WebMCPFieldProps) {\n const field: FieldDefinition = { name, ...rest };\n\n // Auto-detect enum values from children if not explicitly provided\n if (!field.enumValues && !field.oneOf) {\n const options = extractOptions(children);\n if (options.length > 0) {\n field.enumValues = options.map((o) => o.value);\n field.oneOf = options;\n }\n }\n\n useRegisterField(field);\n\n return <>{children}</>;\n}\n\nWebMCPField.displayName = \"WebMCP.Field\";\n","import { WebMCPTool } from \"./WebMCPTool\";\nimport { WebMCPField } from \"./WebMCPField\";\n\nexport const WebMCP = { Tool: WebMCPTool, Field: WebMCPField } as const;\n\nexport { WebMCPTool, WebMCPField };\nexport type { WebMCPToolProps } from \"./WebMCPTool\";\nexport type { WebMCPFieldProps } from \"./WebMCPField\";\nexport { useRegisterField } from \"./useRegisterField\";\nexport { useSchemaCollector } from \"./useSchemaCollector\";\nexport { extractFields, extractOptions } from \"./extractFields\";\nexport { buildInputSchema } from \"./buildSchema\";\nexport { validateSchema } from \"./validateSchema\";\nexport type { FieldDefinition, ToolContextValue } from \"./types\";\n"]}
package/dist/index.mjs CHANGED
@@ -1,5 +1,5 @@
1
- import React2, { createContext, useRef, useEffect, useCallback, useMemo, useContext } from 'react';
2
- import { jsx } from 'react/jsx-runtime';
1
+ import React6, { createContext, useRef, useEffect, useCallback, useMemo, useContext, useState } from 'react';
2
+ import { jsx, Fragment } from 'react/jsx-runtime';
3
3
 
4
4
  // src/hooks/useWebMCPTool.ts
5
5
 
@@ -201,7 +201,7 @@ function WebMCPForm({
201
201
  }
202
202
  );
203
203
  }
204
- var WebMCPInput = React2.forwardRef(
204
+ var WebMCPInput = React6.forwardRef(
205
205
  ({ toolParamTitle, toolParamDescription, ...rest }, ref) => {
206
206
  const webmcpAttrs = {};
207
207
  if (toolParamTitle) {
@@ -214,7 +214,7 @@ var WebMCPInput = React2.forwardRef(
214
214
  }
215
215
  );
216
216
  WebMCPInput.displayName = "WebMCPInput";
217
- var WebMCPSelect = React2.forwardRef(({ toolParamTitle, toolParamDescription, children, ...rest }, ref) => {
217
+ var WebMCPSelect = React6.forwardRef(({ toolParamTitle, toolParamDescription, children, ...rest }, ref) => {
218
218
  const webmcpAttrs = {};
219
219
  if (toolParamTitle) {
220
220
  webmcpAttrs.toolparamtitle = toolParamTitle;
@@ -225,7 +225,7 @@ var WebMCPSelect = React2.forwardRef(({ toolParamTitle, toolParamDescription, ch
225
225
  return /* @__PURE__ */ jsx("select", { ref, ...webmcpAttrs, ...rest, children });
226
226
  });
227
227
  WebMCPSelect.displayName = "WebMCPSelect";
228
- var WebMCPTextarea = React2.forwardRef(({ toolParamTitle, toolParamDescription, ...rest }, ref) => {
228
+ var WebMCPTextarea = React6.forwardRef(({ toolParamTitle, toolParamDescription, ...rest }, ref) => {
229
229
  const webmcpAttrs = {};
230
230
  if (toolParamTitle) {
231
231
  webmcpAttrs.toolparamtitle = toolParamTitle;
@@ -253,7 +253,332 @@ function WebMCPProvider({ children }) {
253
253
  function useWebMCPStatus() {
254
254
  return useContext(WebMCPReactContext);
255
255
  }
256
+ function extractOptions(children) {
257
+ const results = [];
258
+ React6.Children.toArray(children).forEach((child) => {
259
+ if (!React6.isValidElement(child)) return;
260
+ const props = child.props;
261
+ if (props.value !== void 0 && props.value !== null) {
262
+ const value = props.value;
263
+ let label;
264
+ if (typeof props.children === "string") {
265
+ label = props.children;
266
+ } else {
267
+ label = String(value);
268
+ }
269
+ results.push({ value, label });
270
+ }
271
+ if (props.children) {
272
+ results.push(...extractOptions(props.children));
273
+ }
274
+ });
275
+ return results;
276
+ }
277
+ function extractFields(children) {
278
+ const fields = [];
279
+ React6.Children.toArray(children).forEach((child) => {
280
+ if (!React6.isValidElement(child)) return;
281
+ const props = child.props;
282
+ const inputProps = props.inputProps;
283
+ const slotInput = props.slotProps?.input;
284
+ const name = props.name ?? inputProps?.name ?? slotInput?.name;
285
+ if (name) {
286
+ const field = { name };
287
+ if (props.type !== void 0) field.type = props.type;
288
+ if (props.required !== void 0) field.required = Boolean(props.required);
289
+ if (props.min !== void 0) field.min = Number(props.min);
290
+ if (props.max !== void 0) field.max = Number(props.max);
291
+ if (props.minLength !== void 0) field.minLength = Number(props.minLength);
292
+ if (props.maxLength !== void 0) field.maxLength = Number(props.maxLength);
293
+ if (props.pattern !== void 0) field.pattern = props.pattern;
294
+ if (props.children) {
295
+ const options = extractOptions(props.children);
296
+ if (options.length > 0) {
297
+ field.enumValues = options.map((o) => o.value);
298
+ field.oneOf = options;
299
+ }
300
+ }
301
+ fields.push(field);
302
+ } else if (props.children) {
303
+ fields.push(...extractFields(props.children));
304
+ }
305
+ });
306
+ return fields;
307
+ }
308
+
309
+ // src/adapters/buildSchema.ts
310
+ function mapHtmlTypeToSchemaType(htmlType) {
311
+ switch (htmlType) {
312
+ case "number":
313
+ case "range":
314
+ return "number";
315
+ case "checkbox":
316
+ return "boolean";
317
+ default:
318
+ return "string";
319
+ }
320
+ }
321
+ function buildInputSchema(fields) {
322
+ const properties = {};
323
+ const required = [];
324
+ const sortedFields = [...fields].sort((a, b) => a.name.localeCompare(b.name));
325
+ for (const field of sortedFields) {
326
+ const prop = {
327
+ type: mapHtmlTypeToSchemaType(field.type)
328
+ };
329
+ if (field.title) prop.title = field.title;
330
+ if (field.description) prop.description = field.description;
331
+ if (field.min !== void 0) prop.minimum = field.min;
332
+ if (field.max !== void 0) prop.maximum = field.max;
333
+ if (field.minLength !== void 0) prop.minLength = field.minLength;
334
+ if (field.maxLength !== void 0) prop.maxLength = field.maxLength;
335
+ if (field.pattern) prop.pattern = field.pattern;
336
+ if (field.enumValues && field.enumValues.length > 0) {
337
+ prop.enum = field.enumValues;
338
+ }
339
+ if (field.oneOf && field.oneOf.length > 0) {
340
+ prop.oneOf = field.oneOf.map((opt) => ({
341
+ const: opt.value,
342
+ title: opt.label
343
+ }));
344
+ }
345
+ properties[field.name] = prop;
346
+ if (field.required) {
347
+ required.push(field.name);
348
+ }
349
+ }
350
+ const schema = {
351
+ type: "object",
352
+ properties
353
+ };
354
+ if (required.length > 0) {
355
+ schema.required = required.sort();
356
+ }
357
+ return schema;
358
+ }
359
+
360
+ // src/adapters/validateSchema.ts
361
+ function validateSchema(fields, options) {
362
+ if (process.env.NODE_ENV === "production") return;
363
+ const strict = options?.strict ?? false;
364
+ const issues = [];
365
+ const seen = /* @__PURE__ */ new Set();
366
+ for (const field of fields) {
367
+ if (seen.has(field.name)) {
368
+ issues.push(`Duplicate field name "${field.name}".`);
369
+ }
370
+ seen.add(field.name);
371
+ const schemaType = mapHtmlTypeToSchemaType(field.type);
372
+ if (field.pattern !== void 0 && schemaType !== "string") {
373
+ issues.push(
374
+ `Field "${field.name}": pattern is only valid for string types, but type is "${schemaType}".`
375
+ );
376
+ }
377
+ if ((field.min !== void 0 || field.max !== void 0) && schemaType !== "number") {
378
+ issues.push(
379
+ `Field "${field.name}": min/max are only valid for number types, but type is "${schemaType}".`
380
+ );
381
+ }
382
+ if ((field.minLength !== void 0 || field.maxLength !== void 0) && schemaType !== "string") {
383
+ issues.push(
384
+ `Field "${field.name}": minLength/maxLength are only valid for string types, but type is "${schemaType}".`
385
+ );
386
+ }
387
+ if (field.enumValues && field.enumValues.length > 0) {
388
+ for (const val of field.enumValues) {
389
+ const valType = typeof val;
390
+ if (schemaType === "string" && valType !== "string") {
391
+ issues.push(
392
+ `Field "${field.name}": enum value ${JSON.stringify(val)} is not a string.`
393
+ );
394
+ }
395
+ if (schemaType === "number" && valType !== "number") {
396
+ issues.push(
397
+ `Field "${field.name}": enum value ${JSON.stringify(val)} is not a number.`
398
+ );
399
+ }
400
+ if (schemaType === "boolean" && valType !== "boolean") {
401
+ issues.push(
402
+ `Field "${field.name}": enum value ${JSON.stringify(val)} is not a boolean.`
403
+ );
404
+ }
405
+ }
406
+ }
407
+ }
408
+ for (const issue of issues) {
409
+ if (strict) {
410
+ throw new Error(`[react-webmcp] ${issue}`);
411
+ }
412
+ console.warn(`[react-webmcp] ${issue}`);
413
+ }
414
+ }
415
+
416
+ // src/adapters/useSchemaCollector.ts
417
+ var ToolContext = createContext(null);
418
+ function fieldsFingerprint(fields) {
419
+ return fields.map(
420
+ (f) => `${f.name}::${f.type ?? ""}::${f.required ?? ""}::${f.title ?? ""}::${f.description ?? ""}::${JSON.stringify(f.enumValues ?? [])}::${JSON.stringify(f.oneOf ?? [])}::${f.min ?? ""}::${f.max ?? ""}::${f.minLength ?? ""}::${f.maxLength ?? ""}::${f.pattern ?? ""}`
421
+ ).join("|");
422
+ }
423
+ function mergeField(base, override) {
424
+ const result = { ...base };
425
+ for (const key of Object.keys(override)) {
426
+ if (override[key] !== void 0) {
427
+ result[key] = override[key];
428
+ }
429
+ }
430
+ return result;
431
+ }
432
+ function useSchemaCollector({
433
+ children,
434
+ fields: fieldsProp,
435
+ strict
436
+ }) {
437
+ const contextFieldsRef = useRef(/* @__PURE__ */ new Map());
438
+ const [version, setVersion] = useState(0);
439
+ const registerField = useCallback((field) => {
440
+ contextFieldsRef.current.set(field.name, field);
441
+ setVersion((v) => v + 1);
442
+ }, []);
443
+ const unregisterField = useCallback((name) => {
444
+ contextFieldsRef.current.delete(name);
445
+ setVersion((v) => v + 1);
446
+ }, []);
447
+ const childrenFields = extractFields(children);
448
+ const childrenFP = fieldsFingerprint(childrenFields);
449
+ const merged = useMemo(() => {
450
+ const fieldMap = /* @__PURE__ */ new Map();
451
+ for (const field of childrenFields) {
452
+ fieldMap.set(field.name, field);
453
+ }
454
+ if (fieldsProp) {
455
+ for (const [name, overrides] of Object.entries(fieldsProp)) {
456
+ const existing = fieldMap.get(name);
457
+ if (existing) {
458
+ fieldMap.set(name, mergeField(existing, overrides));
459
+ } else {
460
+ fieldMap.set(name, { name, ...overrides });
461
+ }
462
+ }
463
+ }
464
+ for (const [name, field] of contextFieldsRef.current) {
465
+ const existing = fieldMap.get(name);
466
+ if (existing) {
467
+ fieldMap.set(name, mergeField(existing, field));
468
+ } else {
469
+ fieldMap.set(name, field);
470
+ }
471
+ }
472
+ const result = Array.from(fieldMap.values());
473
+ if (process.env.NODE_ENV !== "production") {
474
+ validateSchema(result, { strict });
475
+ }
476
+ return result;
477
+ }, [childrenFP, fieldsProp, version, strict]);
478
+ const mergedFP = fieldsFingerprint(merged);
479
+ const schema = useMemo(
480
+ () => buildInputSchema(merged),
481
+ // eslint-disable-next-line react-hooks/exhaustive-deps
482
+ [mergedFP]
483
+ );
484
+ return { schema, registerField, unregisterField };
485
+ }
486
+ function WebMCPTool({
487
+ name,
488
+ description,
489
+ onExecute,
490
+ fields: fieldsProp,
491
+ strict,
492
+ autoSubmit,
493
+ annotations,
494
+ onToolActivated,
495
+ onToolCancel,
496
+ children
497
+ }) {
498
+ const { schema, registerField, unregisterField } = useSchemaCollector({
499
+ children,
500
+ fields: fieldsProp,
501
+ strict
502
+ });
503
+ const executeRef = useRef(onExecute);
504
+ executeRef.current = onExecute;
505
+ useWebMCPTool({
506
+ name,
507
+ description,
508
+ inputSchema: schema,
509
+ annotations,
510
+ execute: (input) => executeRef.current(input)
511
+ });
512
+ useEffect(() => {
513
+ if (typeof window === "undefined") return;
514
+ if (!onToolActivated && !onToolCancel) return;
515
+ const handleActivated = (e) => {
516
+ const toolName = e.toolName ?? e.detail?.toolName;
517
+ if (toolName === name && onToolActivated) {
518
+ onToolActivated(toolName);
519
+ }
520
+ };
521
+ const handleCancel = (e) => {
522
+ const toolName = e.toolName ?? e.detail?.toolName;
523
+ if (toolName === name && onToolCancel) {
524
+ onToolCancel(toolName);
525
+ }
526
+ };
527
+ window.addEventListener("toolactivated", handleActivated);
528
+ window.addEventListener("toolcancel", handleCancel);
529
+ return () => {
530
+ window.removeEventListener("toolactivated", handleActivated);
531
+ window.removeEventListener("toolcancel", handleCancel);
532
+ };
533
+ }, [name, onToolActivated, onToolCancel]);
534
+ return /* @__PURE__ */ jsx(ToolContext.Provider, { value: { registerField, unregisterField }, children });
535
+ }
536
+ WebMCPTool.displayName = "WebMCP.Tool";
537
+ function fieldFingerprint(field) {
538
+ return `${field.name}::${field.type ?? ""}::${field.required ?? ""}::${field.title ?? ""}::${field.description ?? ""}::${JSON.stringify(field.enumValues ?? [])}::${JSON.stringify(field.oneOf ?? [])}::${field.min ?? ""}::${field.max ?? ""}::${field.minLength ?? ""}::${field.maxLength ?? ""}::${field.pattern ?? ""}`;
539
+ }
540
+ function useRegisterField(field) {
541
+ const ctx = useContext(ToolContext);
542
+ const fp = fieldFingerprint(field);
543
+ useEffect(() => {
544
+ if (!ctx) {
545
+ if (process.env.NODE_ENV !== "production") {
546
+ console.warn(
547
+ `[react-webmcp] useRegisterField: no WebMCP.Tool context found for field "${field.name}". Wrap this component in a <WebMCP.Tool> to register fields.`
548
+ );
549
+ }
550
+ return;
551
+ }
552
+ ctx.registerField(field);
553
+ return () => {
554
+ try {
555
+ ctx.unregisterField(field.name);
556
+ } catch {
557
+ }
558
+ };
559
+ }, [fp]);
560
+ }
561
+ function WebMCPField({
562
+ children,
563
+ name,
564
+ ...rest
565
+ }) {
566
+ const field = { name, ...rest };
567
+ if (!field.enumValues && !field.oneOf) {
568
+ const options = extractOptions(children);
569
+ if (options.length > 0) {
570
+ field.enumValues = options.map((o) => o.value);
571
+ field.oneOf = options;
572
+ }
573
+ }
574
+ useRegisterField(field);
575
+ return /* @__PURE__ */ jsx(Fragment, { children });
576
+ }
577
+ WebMCPField.displayName = "WebMCP.Field";
578
+
579
+ // src/adapters/index.ts
580
+ var WebMCP = { Tool: WebMCPTool, Field: WebMCPField };
256
581
 
257
- export { WebMCPForm, WebMCPInput, WebMCPProvider, WebMCPSelect, WebMCPTextarea, getModelContext, isWebMCPAvailable, isWebMCPTestingAvailable, useToolEvent, useWebMCPContext, useWebMCPStatus, useWebMCPTool };
582
+ export { WebMCP, WebMCPField, WebMCPForm, WebMCPInput, WebMCPProvider, WebMCPSelect, WebMCPTextarea, WebMCPTool, buildInputSchema, extractFields, extractOptions, getModelContext, isWebMCPAvailable, isWebMCPTestingAvailable, useRegisterField, useSchemaCollector, useToolEvent, useWebMCPContext, useWebMCPStatus, useWebMCPTool, validateSchema };
258
583
  //# sourceMappingURL=index.mjs.map
259
584
  //# sourceMappingURL=index.mjs.map