usewebmcp 5.1.0 → 6.0.0-beta.20261001010549

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/README.md CHANGED
@@ -1,119 +1,104 @@
1
1
  # usewebmcp
2
2
 
3
- React hook for registering strict-core WebMCP tools with `document.modelContext`.
4
-
5
- Use `usewebmcp` when an app only needs standard tool registration. Use
6
- `@mcp-b/react-webmcp` for prompts, resources, or MCP client/provider flows.
7
-
8
- ## Install
9
-
10
- ```bash
11
- pnpm add usewebmcp react
12
- ```
13
-
14
- The hook expects `document.modelContext` to exist before it is enabled. Provide it through
15
- native browser support, `@mcp-b/webmcp-polyfill`, or `@mcp-b/global`. Older
16
- `navigator.modelContext` runtimes remain a fallback.
17
-
18
- ## Quick start
3
+ Expose React state and actions as WebMCP tools, with automatic registration and cleanup.
19
4
 
20
5
  ```tsx
21
- import { initializeWebMCPPolyfill } from '@mcp-b/webmcp-polyfill';
6
+ 'use client';
7
+
8
+ import { useState } from 'react';
22
9
  import { useWebMCP } from 'usewebmcp';
23
10
 
24
- initializeWebMCPPolyfill();
25
-
26
- const INPUT_SCHEMA = {
27
- type: 'object',
28
- properties: { query: { type: 'string' } },
29
- required: ['query'],
30
- } as const;
31
-
32
- const OUTPUT_SCHEMA = {
33
- type: 'object',
34
- properties: { total: { type: 'integer' } },
35
- required: ['total'],
36
- } as const;
37
-
38
- export function SearchTool() {
39
- const search = useWebMCP({
40
- name: 'search',
41
- description: 'Search indexed documents',
42
- inputSchema: INPUT_SCHEMA,
43
- outputSchema: OUTPUT_SCHEMA,
44
- execute: async ({ query }) => ({ total: await countMatches(query) }),
11
+ export function Counter() {
12
+ const [count, setCount] = useState(0);
13
+
14
+ useWebMCP({
15
+ name: 'get_count',
16
+ description: 'Get the current counter value',
17
+ annotations: { readOnlyHint: true },
18
+ execute: () => ({ count }),
45
19
  });
46
20
 
47
21
  return (
48
- <button disabled={search.state.isExecuting} onClick={() => search.execute({ query: 'webmcp' })}>
49
- Run search ({search.state.executionCount})
22
+ <button type="button" onClick={() => setCount((value) => value + 1)}>
23
+ Count: {count}
50
24
  </button>
51
25
  );
52
26
  }
53
27
  ```
54
28
 
55
- JSON Schema literals infer the `execute` input, output, returned `execute(input)`, and
56
- `state.lastResult` types. Standard JSON Schema v1 inputs such as Zod 4.2+ are also supported.
29
+ Each agent call reads the latest committed `count`.
57
30
 
58
- ## API
31
+ [API reference](https://docs.mcp-b.ai/packages/usewebmcp/reference) · [Framework setup](https://docs.mcp-b.ai/how-to/frameworks)
59
32
 
60
- ```ts
61
- const { state, execute, reset } = useWebMCP(config, deps?);
33
+ ## Install and provide a runtime
34
+
35
+ In a React 18 or 19 application:
36
+
37
+ ```bash
38
+ pnpm add usewebmcp
39
+ ```
40
+
41
+ The hook reads `document.modelContext` when it registers and does not wait for a runtime installed
42
+ later. For browsers without it, install and initialize the polyfill before rendering tools:
43
+
44
+ ```bash
45
+ pnpm add @mcp-b/webmcp-polyfill
62
46
  ```
63
47
 
64
- `config` contains:
48
+ ```ts
49
+ import { installWebMCP } from '@mcp-b/webmcp-polyfill';
65
50
 
66
- - `name` and `description`
67
- - optional `enabled`, which defaults to `true`
68
- - optional `inputSchema`, `outputSchema`, and `annotations`
69
- - `execute(input)`, which may return synchronously or asynchronously
51
+ installWebMCP();
52
+ ```
70
53
 
71
- `state` contains `isExecuting`, `lastResult`, `error`, and `executionCount`. The returned
72
- `execute(input)` invokes the same implementation locally. `reset()` clears observed state; it does
73
- not cancel work already running.
54
+ Use [`@mcp-b/global`](../global/README.md) for MCP server features. Browser types come from the Community Group's [`webmcp-types`](https://github.com/webmachinelearning/webmcp-types).
74
55
 
75
- ## Conditional registration
56
+ ## Input schemas
76
57
 
77
- Pass `enabled: false` to keep the tool unregistered. Changing it to `true` registers the latest
78
- committed configuration; changing it back to `false` unregisters the tool. Call the hook
79
- unconditionally and pass your condition as `enabled`.
58
+ `usewebmcp` accepts the JSON Schema object from the WebMCP API and infers TypeScript input types
59
+ from it. The schema is metadata; the hook does not validate arguments. Validate in your handler
60
+ when needed. Local calls to `execute()` are typed but do not run a validator.
80
61
 
81
- Disabling preserves execution state and the local `execute` and `reset` controls. It does not
82
- cancel your `execute` implementation. An in-flight MCP request may still be rejected by the
83
- runtime when its registration is removed. `enabled` controls exposure, not authorization.
62
+ Zod and other Standard Schema validators are rejected. Passing one is a type error, and at runtime
63
+ the hook sets `registrationError` without registering the tool. For Standard Schema validation,
64
+ output schemas, MCP annotations, MCP result formatting, or prompt and resource hooks, use
65
+ [`@mcp-b/react-webmcp`](../react-webmcp/README.md).
84
66
 
85
- ## Tool responses
67
+ ## State and lifecycle
86
68
 
87
- Raw return values use the same normalization as the MCP-B runtime:
69
+ - `state` exposes `isExecuting`, `lastResult`, `error`, and `executionCount`.
70
+ - `execute(input, options?)` calls the handler locally. `reset()` clears state without cancelling work.
71
+ - `isSupported` reports API availability; `registrationError` reports setup failures separately from `state.error`.
72
+ - A rejected registration, such as a duplicate tool name, also logs one `console.warn` naming the tool.
73
+ - `enabled: false` unregisters the tool while keeping local execution available.
74
+ - Local and agent failures reject. Agents receive `null` when the handler returns `undefined`; any
75
+ other result must be JSON-serializable, or the agent call fails and `state.error` records why.
76
+ - Handlers receive `(input, { signal })` for cancellation, which always rejects.
77
+ - The hook commits once after mount when `isSupported` becomes true. Later successful registrations
78
+ and unrelated renders add no commits.
79
+ - The package preserves `'use client'` and supports React 18/19, SSR, and StrictMode.
88
80
 
89
- - Existing MCP responses with a `content` array pass through unchanged.
90
- - Strings become text content.
91
- - JSON values become text content plus `structuredContent`.
92
- - Other values receive a safe text representation.
81
+ See the [reference](https://docs.mcp-b.ai/packages/usewebmcp/reference) for metadata updates,
82
+ cancellation, and raw result handling. The `usewebmcp/internal` entry exists for
83
+ `@mcp-b/react-webmcp` and can change in any release.
93
84
 
94
- When `outputSchema` is present, a non-JSON-serializable result rejects local execution and becomes
95
- an MCP error response. Native Chrome WebMCP does not advertise `outputSchema`; it is MCP-B metadata
96
- for output inference and compatible runtimes.
85
+ ## Upgrading from 5.x
97
86
 
98
- ## Re-registration
87
+ To keep Standard Schema validation, `outputSchema`, MCP annotations, `InferOutput`, or automatic
88
+ MCP responses, import `useWebMCP` from `@mcp-b/react-webmcp` instead. The
89
+ [changelog](./CHANGELOG.md) lists every breaking change.
99
90
 
100
- While enabled, the tool re-registers when `name`, `description`, or a value in `deps` changes. Implementations,
101
- schemas, and annotations use their latest committed values without reference-driven churn. Include
102
- a primitive schema or annotation revision in `deps` when registered metadata must change.
91
+ ## Development
103
92
 
104
- Registration is aborted on unmount or when disabled. If the runtime is missing while enabled, the hook warns and skips
105
- registration; initialize the runtime first rather than polling once per hook.
93
+ From the repository root after `pnpm build`:
106
94
 
107
- ## Exports
95
+ ```bash
96
+ pnpm test:hooks
97
+ CHROME_BIN=/path/to/chrome-canary pnpm --filter usewebmcp test:native
98
+ ```
108
99
 
109
- - `useWebMCP`
110
- - `WebMCPConfig`
111
- - `WebMCPReturn`
112
- - `ToolExecutionState`
113
- - `ToolExecuteFunction`
114
- - `InferToolInput`
115
- - `InferOutput`
100
+ [Harness details](../../docs/TESTING.md#react-hook-harness). Prior art: [GoogleChromeLabs/use-webmcp-tool](https://github.com/GoogleChromeLabs/use-webmcp-tool).
116
101
 
117
102
  ## License
118
103
 
119
- MIT
104
+ [MIT](../../LICENSE)
package/dist/index.d.ts CHANGED
@@ -1,58 +1,3 @@
1
- import { ToolInputSchema } from "@mcp-b/webmcp-polyfill/schema";
2
- import { DependencyList } from "react";
3
- import { InferArgsFromInputSchema, InferJsonSchema, InputSchema, JsonSchemaForInference, ToolAnnotations } from "@mcp-b/webmcp-types";
4
-
5
- //#region src/types.d.ts
6
- /** Infers tool input from a JSON Schema literal or Standard JSON Schema. */
7
- type InferToolInput<T> = T extends {
8
- readonly '~standard': {
9
- readonly types?: infer Types;
10
- };
11
- } ? NonNullable<Types> extends {
12
- readonly input: infer Input;
13
- } ? Input : Record<string, unknown> : T extends InputSchema ? InferArgsFromInputSchema<T> : Record<string, unknown>;
14
- /** Infers tool output from a JSON Schema literal. */
15
- type InferOutput<TOutputSchema extends JsonSchemaForInference | undefined = undefined> = TOutputSchema extends undefined ? unknown : TOutputSchema extends JsonSchemaForInference ? InferJsonSchema<TOutputSchema> : unknown;
16
- /** Current state for local and MCP-triggered tool executions. */
17
- interface ToolExecutionState<TOutput = unknown> {
18
- /** Whether at least one execution is pending. */
19
- isExecuting: boolean;
20
- /** Most recent successful result, or `null` before one exists. */
21
- lastResult: TOutput | null;
22
- /** Most recent execution error. */
23
- error: Error | null;
24
- /** Number of successful executions since the last reset. */
25
- executionCount: number;
26
- }
27
- /** Synchronous or asynchronous tool implementation. */
28
- type ToolExecuteFunction<TInputSchema extends ToolInputSchema = InputSchema, TOutputSchema extends JsonSchemaForInference | undefined = undefined> = (input: InferToolInput<TInputSchema>) => Promise<InferOutput<TOutputSchema>> | InferOutput<TOutputSchema>;
29
- /** Configuration for a tool registered by `useWebMCP`. */
30
- interface WebMCPConfig<TInputSchema extends ToolInputSchema = InputSchema, TOutputSchema extends JsonSchemaForInference | undefined = undefined> {
31
- /** Unique WebMCP tool name. */
32
- name: string;
33
- /** Description shown to MCP clients. */
34
- description: string;
35
- /** Whether to register with the runtime. Defaults to `true`. */
36
- enabled?: boolean;
37
- /** JSON Schema literal or Standard JSON Schema for the tool input. */
38
- inputSchema?: TInputSchema;
39
- /** JSON Schema used for output inference and structured content. */
40
- outputSchema?: TOutputSchema;
41
- /** Optional WebMCP behavior hints. */
42
- annotations?: ToolAnnotations;
43
- /** Tool implementation. */
44
- execute: ToolExecuteFunction<TInputSchema, TOutputSchema>;
45
- }
46
- /** State and controls returned by `useWebMCP`. */
47
- interface WebMCPReturn<TOutputSchema extends JsonSchemaForInference | undefined = undefined, TInputSchema extends ToolInputSchema = InputSchema> {
48
- state: ToolExecutionState<InferOutput<TOutputSchema>>;
49
- execute: (input: InferToolInput<TInputSchema>) => Promise<InferOutput<TOutputSchema>>;
50
- reset: () => void;
51
- }
52
- //#endregion
53
- //#region src/useWebMCP.d.ts
54
- /** Registers a React-owned tool with the current WebMCP model context. */
55
- declare function useWebMCP<TInputSchema extends ToolInputSchema = InputSchema, TOutputSchema extends JsonSchemaForInference | undefined = undefined>(config: WebMCPConfig<TInputSchema, TOutputSchema>, deps?: DependencyList): WebMCPReturn<TOutputSchema, TInputSchema>;
56
- //#endregion
57
- export { type InferOutput, type InferToolInput, type ToolExecuteFunction, type ToolExecutionState, type WebMCPConfig, type WebMCPReturn, useWebMCP };
58
- //# sourceMappingURL=index.d.ts.map
1
+ import { a as ToolExecuteFunction, c as WebMCPConfig, i as InferToolInput, l as WebMCPReturn, n as useWebMCP, o as ToolExecutionState, s as ToolInputSchema } from "./useWebMCP-CD4hFHVo.js";
2
+ import { WebMCP } from "webmcp-types";
3
+ export { type InferToolInput, type ToolExecuteFunction, type ToolExecutionState, type ToolInputSchema, type WebMCP, type WebMCPConfig, type WebMCPReturn, useWebMCP };
package/dist/index.js CHANGED
@@ -1,2 +1 @@
1
- "use client";import{normalizeInputSchema as e,normalizeToolResponse as t}from"@mcp-b/webmcp-polyfill/schema";import{useCallback as n,useEffect as r,useLayoutEffect as i,useRef as a,useState as o}from"react";const s={isExecuting:!1,lastResult:null,error:null,executionCount:0},c=typeof window>`u`?r:i;function l(i,l){let{name:u,description:d,enabled:f=!0}=i,[p,m]=o(s),h=a(i),g=a(0);c(()=>{h.current=i});let _=n(async e=>{g.current+=1,m(e=>e.isExecuting&&e.error===null?e:{...e,isExecuting:!0,error:null});let n,r=h.current;try{if(n=await r.execute(e),r.outputSchema&&t(n).structuredContent===void 0)throw Error(`Tool "${r.name}" outputSchema requires execute to return a JSON-serializable result`)}catch(e){--g.current;let t=e instanceof Error?e:Error(String(e));throw m(e=>({...e,isExecuting:g.current>0,error:t})),t}return--g.current,m(e=>({isExecuting:g.current>0,lastResult:n,error:null,executionCount:e.executionCount+1})),n},[]),v=n(()=>{let e=g.current>0;m(t=>t.isExecuting===e&&t.lastResult===null&&t.error===null&&t.executionCount===0?t:{...s,isExecuting:e})},[]);return r(()=>{if(!f)return;let n=typeof document>`u`?void 0:document.modelContext??(typeof navigator>`u`?void 0:navigator.modelContext);if(!n){console.warn(`[useWebMCP] document.modelContext is not available. Tool "${u}" will not be registered.`);return}let r=h.current,i=r.outputSchema,a=new AbortController,o={name:u,description:d,inputSchema:e(r.inputSchema).inputSchema,...i&&{outputSchema:i},...r.annotations&&{annotations:r.annotations},execute:async e=>{try{let n=t(await _(e));if(i&&n.structuredContent===void 0)throw Error(`Tool "${u}" outputSchema requires the tool implementation to return a JSON-serializable result`);return n}catch(e){return{content:[{type:`text`,text:`Error: ${e instanceof Error?e.message:String(e)}`}],isError:!0}}}};try{let e=n.registerTool(o,{signal:a.signal});Promise.resolve(e).catch(e=>{a.signal.aborted||(a.abort(),console.warn(`[useWebMCP] registerTool("${u}") rejected:`,e))})}catch(e){a.abort(),console.warn(`[useWebMCP] registerTool("${u}") rejected:`,e);return}return()=>a.abort()},[u,d,f,...l??[]]),{state:p,execute:_,reset:v}}export{l as useWebMCP};
2
- //# sourceMappingURL=index.js.map
1
+ "use client";import{t as e}from"./useWebMCP-CQZUa6bR.js";export{e as useWebMCP};
@@ -0,0 +1,2 @@
1
+ import { r as useWebMCPWithAdapter, t as WebMCPAdapter } from "./useWebMCP-CD4hFHVo.js";
2
+ export { type WebMCPAdapter, useWebMCPWithAdapter };
@@ -0,0 +1 @@
1
+ import{n as e}from"./useWebMCP-CQZUa6bR.js";export{e as useWebMCPWithAdapter};
@@ -0,0 +1,58 @@
1
+ import { DependencyList } from "react";
2
+ import { WebMCP } from "webmcp-types";
3
+
4
+ //#region src/types.d.ts
5
+ /**
6
+ * The JSON Schema object accepted by the WebMCP tool contract. Standard Schema
7
+ * validators such as Zod belong to `@mcp-b/react-webmcp`.
8
+ */
9
+ type ToolInputSchema = NonNullable<WebMCP.ModelContextTool['inputSchema']> & {
10
+ readonly '~standard'?: never;
11
+ };
12
+ /** Input inferred from the WebMCP JSON Schema. */
13
+ type InferToolInput<T extends object> = Parameters<WebMCP.ModelContextToolFromSchema<T>['execute']>[0];
14
+ /** Current state for local and agent-triggered tool executions. */
15
+ interface ToolExecutionState<TResult = unknown> {
16
+ /** Whether at least one execution is pending. */
17
+ isExecuting: boolean;
18
+ /** Most recent successful result, or null before one exists. */
19
+ lastResult: TResult | null;
20
+ /** Most recent execution error. */
21
+ error: Error | null;
22
+ /** Number of successful executions since the last reset. */
23
+ executionCount: number;
24
+ }
25
+ /** Synchronous or asynchronous tool implementation. */
26
+ type ToolExecuteFunction<TInputSchema extends ToolInputSchema = object, TResult = unknown> = (input: InferToolInput<TInputSchema>, options: WebMCP.ToolExecuteCallbackOptions) => WebMCP.MaybePromise<TResult>;
27
+ /** Standard tool metadata plus React lifecycle and execution options. */
28
+ interface WebMCPConfig<TInputSchema extends ToolInputSchema = object, TResult = unknown> extends Omit<WebMCP.ModelContextTool, 'inputSchema' | 'execute'> {
29
+ inputSchema?: TInputSchema;
30
+ execute: ToolExecuteFunction<TInputSchema, TResult>;
31
+ /** Register while true. Local execution remains available when false. */
32
+ enabled?: boolean;
33
+ /** Origins allowed to discover/call the tool, enforced by the browser. */
34
+ exposedTo?: WebMCP.ModelContextRegisterToolOptions['exposedTo'];
35
+ }
36
+ /** State and controls returned by useWebMCP. */
37
+ interface WebMCPReturn<TInputSchema extends ToolInputSchema = object, TResult = unknown> {
38
+ state: ToolExecutionState<TResult>;
39
+ isSupported: boolean;
40
+ registrationError: Error | null;
41
+ execute: (input: InferToolInput<TInputSchema>, options?: WebMCP.ToolExecuteCallbackOptions) => Promise<TResult>;
42
+ /** Clears observed execution state without cancelling pending work. */
43
+ reset: () => void;
44
+ }
45
+ //#endregion
46
+ //#region src/useWebMCP.d.ts
47
+ interface WebMCPAdapter<TResult> {
48
+ descriptor?: object;
49
+ preparationError?: Error;
50
+ formatOutput?: (result: TResult) => WebMCP.MaybePromise<unknown>;
51
+ formatError?: (error: Error) => WebMCP.MaybePromise<unknown>;
52
+ }
53
+ declare function useWebMCPWithAdapter<const TInputSchema extends ToolInputSchema = object, TResult = unknown>(config: WebMCPConfig<TInputSchema, TResult>, deps: DependencyList | undefined, adapter: WebMCPAdapter<TResult>): WebMCPReturn<TInputSchema, TResult>;
54
+ /** Registers a React-owned tool using the upstream WebMCP contract. */
55
+ declare function useWebMCP<const TInputSchema extends ToolInputSchema = object, TResult = unknown>(config: WebMCPConfig<TInputSchema, TResult>, deps?: DependencyList): WebMCPReturn<TInputSchema, TResult>;
56
+ //#endregion
57
+ export { ToolExecuteFunction as a, WebMCPConfig as c, InferToolInput as i, WebMCPReturn as l, useWebMCP as n, ToolExecutionState as o, useWebMCPWithAdapter as r, ToolInputSchema as s, WebMCPAdapter as t };
58
+ //# sourceMappingURL=useWebMCP-CD4hFHVo.d.ts.map
@@ -0,0 +1,2 @@
1
+ import{useCallback as e,useEffect as t,useLayoutEffect as n,useMemo as r,useRef as i,useState as a}from"react";const o={isExecuting:!1,lastResult:null,error:null,executionCount:0},s={isSupported:!1,registrationError:null},c=globalThis.window===void 0?t:n;function l(e){return e instanceof Error?e:Error(String(e))}function u(n,u,d){let[f,p]=a(o),[m,h]=a(s),g=i(0),_=r(()=>{try{let e=n.inputSchema;if(e!==void 0&&`~standard`in e)throw TypeError(`inputSchema must be JSON Schema. Use @mcp-b/react-webmcp for Zod and other Standard Schema validators.`);let t=JSON.stringify(e);if(e!==void 0&&t===void 0)throw TypeError(`inputSchema must serialize to JSON`);return{value:e,key:t}}catch(e){return{error:l(e)}}},[n.inputSchema]),{name:v,title:y,description:b,annotations:x,enabled:S=!0,exposedTo:C}=n,w={name:v,...y!==void 0&&{title:y},description:b,...x!==void 0&&{annotations:x},...d.descriptor},T={...w,..._.value!==void 0&&{inputSchema:_.value}},E=_.error??d.preparationError,D;try{D=JSON.stringify([w,_.key,C])}catch(e){E=l(e),D=E.message}let O=i({config:n,descriptor:T,preparationError:E,adapter:d});c(()=>{O.current={config:n,descriptor:T,preparationError:E,adapter:d}});let k=e(async(e,t={signal:new AbortController().signal},n=!1)=>{let{config:r,adapter:i}=O.current,{signal:a}=t;g.current+=1,p(e=>e.isExecuting&&e.error===null?e:{...e,isExecuting:!0,error:null});let o,s;try{a.throwIfAborted(),s=await Promise.race([new Promise((e,t)=>{o=()=>t(a.reason),a.addEventListener(`abort`,o,{once:!0})}),(async()=>{try{let o=await r.execute(e,t);if(a.throwIfAborted(),o instanceof Error)throw o;let s=n&&i.formatOutput?await i.formatOutput(o):o;if(a.throwIfAborted(),n&&s!==void 0)try{if(JSON.stringify(s)===void 0)throw TypeError(`JSON.stringify returned undefined`)}catch(e){throw TypeError(`Tool "${r.name}" returned a result that is not JSON-serializable`,{cause:e})}return{result:o,output:s??null}}catch(e){a.throwIfAborted();let t=l(e),r=i.formatError;if(!n||!r)return{error:t};let o=await r(t);return a.throwIfAborted(),{error:t,output:o}}})()])}catch(e){s={error:l(e)}}finally{o&&a.removeEventListener(`abort`,o),--g.current}let c=g.current>0;return p(e=>`error`in s?{...e,isExecuting:c,error:s.error}:{isExecuting:c,lastResult:s.result,error:null,executionCount:e.executionCount+1}),s},[]),A=e(async(e,t)=>{let n=await k(e,t);if(`error`in n)throw n.error;return n.result},[k]),j=e(()=>{let e=g.current>0;p(t=>t.isExecuting===e&&t.lastResult===null&&t.error===null&&t.executionCount===0?t:{...o,isExecuting:e})},[]);return t(()=>{let e=new AbortController,t=globalThis.document?.modelContext,n=typeof t?.registerTool==`function`,{config:r,descriptor:i,preparationError:a}=O.current;h(e=>e.isSupported===n&&e.registrationError===(a??null)?e:{isSupported:n,registrationError:a??null});let o=t=>{if(e.signal.aborted)return;e.abort();let n=l(t);console.warn(`[useWebMCP] registerTool("${r.name}") rejected:`,n),h({isSupported:!0,registrationError:n})};if(!a&&S&&n&&t)try{t.registerTool({...i,execute:async(e,t)=>{let n=await k(e,t,!0);if(`output`in n)return n.output;throw n.error}},{signal:e.signal,...r.exposedTo&&{exposedTo:r.exposedTo}}).catch(o)}catch(e){o(e)}return()=>e.abort()},[D,E?.message,S,...u??[]]),{state:f,...m,execute:A,reset:j}}function d(e,t){return u(e,t,{})}export{u as n,d as t};
2
+ //# sourceMappingURL=useWebMCP-CQZUa6bR.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useWebMCP-CQZUa6bR.js","names":[],"sources":["../src/useWebMCP.ts"],"sourcesContent":["'use client';\n\nimport type { DependencyList } from 'react';\nimport { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react';\nimport type { WebMCP } from 'webmcp-types';\nimport type {\n InferToolInput,\n ToolExecutionState,\n ToolInputSchema,\n WebMCPConfig,\n WebMCPReturn,\n} from './types.js';\n\nconst INITIAL_STATE = { isExecuting: false, lastResult: null, error: null, executionCount: 0 };\nconst INITIAL_REGISTRATION = { isSupported: false, registrationError: null };\nconst useIsomorphicLayoutEffect = globalThis.window === undefined ? useEffect : useLayoutEffect;\n\ntype ExecutionOutcome<T> = { result: T; output: unknown } | { error: Error; output?: unknown };\ntype RegisteredToolInput = Parameters<WebMCP.ModelContextTool['execute']>[0];\n\nexport interface WebMCPAdapter<TResult> {\n descriptor?: object;\n preparationError?: Error;\n formatOutput?: (result: TResult) => WebMCP.MaybePromise<unknown>;\n formatError?: (error: Error) => WebMCP.MaybePromise<unknown>;\n}\n\nfunction toError(cause: unknown): Error {\n return cause instanceof Error ? cause : new Error(String(cause));\n}\n\nexport function useWebMCPWithAdapter<\n const TInputSchema extends ToolInputSchema = object,\n TResult = unknown,\n>(\n config: WebMCPConfig<TInputSchema, TResult>,\n deps: DependencyList | undefined,\n adapter: WebMCPAdapter<TResult>\n): WebMCPReturn<TInputSchema, TResult> {\n const [state, setState] = useState<ToolExecutionState<TResult>>(INITIAL_STATE);\n const [registration, setRegistration] =\n useState<Pick<WebMCPReturn, 'isSupported' | 'registrationError'>>(INITIAL_REGISTRATION);\n const pendingExecutions = useRef(0);\n const schema = useMemo(() => {\n try {\n const value = config.inputSchema;\n if (value !== undefined && '~standard' in value) {\n throw new TypeError(\n 'inputSchema must be JSON Schema. Use @mcp-b/react-webmcp for Zod and other Standard Schema validators.'\n );\n }\n const key = JSON.stringify(value);\n if (value !== undefined && key === undefined) {\n throw new TypeError('inputSchema must serialize to JSON');\n }\n return { value, key };\n } catch (error) {\n return { error: toError(error) };\n }\n }, [config.inputSchema]);\n const { name, title, description, annotations, enabled = true, exposedTo } = config;\n const metadata = {\n name,\n ...(title !== undefined && { title }),\n description,\n ...(annotations !== undefined && { annotations }),\n ...adapter.descriptor,\n };\n const descriptor = {\n ...metadata,\n ...(schema.value !== undefined && { inputSchema: schema.value }),\n };\n let preparationError = schema.error ?? adapter.preparationError;\n let descriptorKey: string;\n try {\n descriptorKey = JSON.stringify([metadata, schema.key, exposedTo]);\n } catch (error) {\n preparationError = toError(error);\n descriptorKey = preparationError.message;\n }\n const committed = useRef({ config, descriptor, preparationError, adapter });\n\n // Publish only committed renders, before external calls from later layout effects.\n useIsomorphicLayoutEffect(() => {\n committed.current = { config, descriptor, preparationError, adapter };\n });\n\n const run = useCallback(\n async (\n input: InferToolInput<TInputSchema> | RegisteredToolInput,\n options: WebMCP.ToolExecuteCallbackOptions = { signal: new AbortController().signal },\n forAgent = false\n ): Promise<ExecutionOutcome<TResult>> => {\n const { config: executionConfig, adapter: executionAdapter } = committed.current;\n const { signal } = options;\n pendingExecutions.current += 1;\n setState((previous) =>\n previous.isExecuting && previous.error === null\n ? previous\n : { ...previous, isExecuting: true, error: null }\n );\n let onAbort: (() => void) | undefined;\n let outcome: ExecutionOutcome<TResult>;\n try {\n signal.throwIfAborted();\n const operation = async (): Promise<ExecutionOutcome<TResult>> => {\n try {\n // SAFETY: registered calls are validated by inputSchema; local calls are typed.\n const result = await executionConfig.execute(\n input as InferToolInput<TInputSchema>,\n options\n );\n signal.throwIfAborted();\n if (result instanceof Error) throw result;\n const output =\n forAgent && executionAdapter.formatOutput\n ? await executionAdapter.formatOutput(result)\n : result;\n signal.throwIfAborted();\n if (forAgent && output !== undefined) {\n try {\n if (JSON.stringify(output) === undefined) {\n throw new TypeError('JSON.stringify returned undefined');\n }\n } catch (cause) {\n throw new TypeError(\n `Tool \"${executionConfig.name}\" returned a result that is not JSON-serializable`,\n { cause }\n );\n }\n }\n return { result, output: output ?? null };\n } catch (cause) {\n signal.throwIfAborted();\n const error = toError(cause);\n const formatError = executionAdapter.formatError;\n if (!forAgent || !formatError) return { error };\n const output = await formatError(error);\n signal.throwIfAborted();\n return { error, output };\n }\n };\n outcome = await Promise.race([\n new Promise<never>((_, reject) => {\n onAbort = () => reject(signal.reason);\n signal.addEventListener('abort', onAbort, { once: true });\n }),\n operation(),\n ]);\n } catch (error) {\n outcome = { error: toError(error) };\n } finally {\n if (onAbort) signal.removeEventListener('abort', onAbort);\n pendingExecutions.current -= 1;\n }\n const isExecuting = pendingExecutions.current > 0;\n setState((previous) =>\n 'error' in outcome\n ? { ...previous, isExecuting, error: outcome.error }\n : {\n isExecuting,\n lastResult: outcome.result,\n error: null,\n executionCount: previous.executionCount + 1,\n }\n );\n return outcome;\n },\n []\n );\n\n const execute = useCallback<WebMCPReturn<TInputSchema, TResult>['execute']>(\n async (input, options) => {\n const outcome = await run(input, options);\n if ('error' in outcome) throw outcome.error;\n return outcome.result;\n },\n [run]\n );\n\n const reset = useCallback(() => {\n const isExecuting = pendingExecutions.current > 0;\n setState((previous) =>\n previous.isExecuting === isExecuting &&\n previous.lastResult === null &&\n previous.error === null &&\n previous.executionCount === 0\n ? previous\n : { ...INITIAL_STATE, isExecuting }\n );\n }, []);\n\n useEffect(() => {\n const controller = new AbortController();\n const context = globalThis.document?.modelContext;\n const isSupported = typeof context?.registerTool === 'function';\n const { config: current, descriptor: tool, preparationError: error } = committed.current;\n setRegistration((previous) =>\n previous.isSupported === isSupported && previous.registrationError === (error ?? null)\n ? previous\n : { isSupported, registrationError: error ?? null }\n );\n const failed = (cause: unknown) => {\n if (controller.signal.aborted) return;\n controller.abort();\n const registrationError = toError(cause);\n console.warn(`[useWebMCP] registerTool(\"${current.name}\") rejected:`, registrationError);\n setRegistration({ isSupported: true, registrationError });\n };\n if (!error && enabled && isSupported && context) {\n try {\n const registered = context.registerTool(\n {\n ...tool,\n execute: async (input, options) => {\n const outcome = await run(input, options, true);\n if ('output' in outcome) return outcome.output;\n throw outcome.error;\n },\n },\n { signal: controller.signal, ...(current.exposedTo && { exposedTo: current.exposedTo }) }\n );\n void registered.catch(failed);\n } catch (cause) {\n failed(cause);\n }\n }\n return () => controller.abort();\n // Descriptor contents avoid churn from inline schemas; deps can explicitly refresh registration.\n // oxlint-disable-next-line react-doctor/exhaustive-deps -- Metadata is compared by value and callbacks are read after commit.\n }, [descriptorKey, preparationError?.message, enabled, ...(deps ?? [])]);\n\n return { state, ...registration, execute, reset };\n}\n\n/** Registers a React-owned tool using the upstream WebMCP contract. */\nexport function useWebMCP<const TInputSchema extends ToolInputSchema = object, TResult = unknown>(\n config: WebMCPConfig<TInputSchema, TResult>,\n deps?: DependencyList\n): WebMCPReturn<TInputSchema, TResult> {\n return useWebMCPWithAdapter(config, deps, {});\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "usewebmcp",
3
- "version": "5.1.0",
3
+ "version": "6.0.0-beta.20261001010549",
4
4
  "description": "Standalone React hooks for strict core WebMCP tool registration with document.modelContext",
5
5
  "keywords": [
6
6
  "ai",
@@ -36,6 +36,10 @@
36
36
  ".": {
37
37
  "types": "./dist/index.d.ts",
38
38
  "import": "./dist/index.js"
39
+ },
40
+ "./internal": {
41
+ "types": "./dist/internal.d.ts",
42
+ "import": "./dist/internal.js"
39
43
  }
40
44
  },
41
45
  "publishConfig": {
@@ -43,8 +47,7 @@
43
47
  "registry": "https://registry.npmjs.org/"
44
48
  },
45
49
  "dependencies": {
46
- "@mcp-b/webmcp-polyfill": "5.1.0",
47
- "@mcp-b/webmcp-types": "5.1.0"
50
+ "webmcp-types": "0.1.9"
48
51
  },
49
52
  "devDependencies": {
50
53
  "@types/node": "22.20.1",
@@ -57,8 +60,7 @@
57
60
  "vite-plus": "0.1.24",
58
61
  "vitest": "npm:@voidzero-dev/vite-plus-test@0.1.24",
59
62
  "vitest-browser-react": "^2.2.0",
60
- "zod": "4.4.3",
61
- "@mcp-b/global": "5.1.0"
63
+ "@mcp-b/webmcp-polyfill": "6.0.0-beta.20261001010549"
62
64
  },
63
65
  "peerDependencies": {
64
66
  "react": "^18.0.0 || ^19.0.0"
@@ -76,6 +78,7 @@
76
78
  "publish:dry": "pnpm publish --access public --dry-run",
77
79
  "publish:npm": "pnpm publish --access public",
78
80
  "test": "vp test run",
81
+ "test:native": "WEBMCP_NATIVE=1 vp test run",
79
82
  "test:watch": "vp test",
80
83
  "typecheck": "tsc --noEmit && tsc -p tsconfig.strict-null-checks-false.json --noEmit"
81
84
  }
package/dist/index.js.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../src/useWebMCP.ts"],"sourcesContent":["import {\n normalizeInputSchema,\n normalizeToolResponse,\n type ToolInputSchema,\n} from '@mcp-b/webmcp-polyfill/schema';\nimport type { InputSchema, JsonSchemaForInference } from '@mcp-b/webmcp-types';\nimport type { DependencyList } from 'react';\nimport { useCallback, useEffect, useLayoutEffect, useRef, useState } from 'react';\nimport type {\n InferOutput,\n InferToolInput,\n ToolExecutionState,\n WebMCPConfig,\n WebMCPReturn,\n} from './types.js';\n\nconst INITIAL_STATE = {\n isExecuting: false,\n lastResult: null,\n error: null,\n executionCount: 0,\n};\n\nconst useIsomorphicLayoutEffect = typeof window === 'undefined' ? useEffect : useLayoutEffect;\n\n/** Registers a React-owned tool with the current WebMCP model context. */\nexport function useWebMCP<\n TInputSchema extends ToolInputSchema = InputSchema,\n TOutputSchema extends JsonSchemaForInference | undefined = undefined,\n>(\n config: WebMCPConfig<TInputSchema, TOutputSchema>,\n deps?: DependencyList\n): WebMCPReturn<TOutputSchema, TInputSchema> {\n type TOutput = InferOutput<TOutputSchema>;\n type TInput = InferToolInput<TInputSchema>;\n const { name, description, enabled = true } = config;\n const [state, setState] = useState<ToolExecutionState<TOutput>>(INITIAL_STATE);\n const committedConfigRef = useRef(config);\n const pendingExecutionsRef = useRef(0);\n\n // MCP calls can arrive after commit but before passive effects. A layout effect\n // publishes only committed renders; the server fallback avoids the SSR warning.\n useIsomorphicLayoutEffect(() => {\n committedConfigRef.current = config;\n });\n\n const run = useCallback(async (input: TInput): Promise<TOutput> => {\n pendingExecutionsRef.current += 1;\n setState((previous) =>\n previous.isExecuting && previous.error === null\n ? previous\n : { ...previous, isExecuting: true, error: null }\n );\n\n let result: TOutput;\n const executionConfig = committedConfigRef.current;\n try {\n result = await executionConfig.execute(input);\n if (\n executionConfig.outputSchema &&\n normalizeToolResponse(result).structuredContent === undefined\n ) {\n throw new Error(\n `Tool \"${executionConfig.name}\" outputSchema requires execute to return a JSON-serializable result`\n );\n }\n } catch (error) {\n pendingExecutionsRef.current -= 1;\n const normalizedError = error instanceof Error ? error : new Error(String(error));\n setState((previous) => ({\n ...previous,\n isExecuting: pendingExecutionsRef.current > 0,\n error: normalizedError,\n }));\n throw normalizedError;\n }\n\n pendingExecutionsRef.current -= 1;\n setState((previous) => ({\n isExecuting: pendingExecutionsRef.current > 0,\n lastResult: result,\n error: null,\n executionCount: previous.executionCount + 1,\n }));\n return result;\n }, []);\n\n const reset = useCallback(() => {\n const isExecuting = pendingExecutionsRef.current > 0;\n setState((previous) =>\n previous.isExecuting === isExecuting &&\n previous.lastResult === null &&\n previous.error === null &&\n previous.executionCount === 0\n ? previous\n : { ...INITIAL_STATE, isExecuting }\n );\n }, []);\n\n useEffect(() => {\n if (!enabled) return;\n\n const modelContext =\n typeof document === 'undefined'\n ? undefined\n : (document.modelContext ??\n (typeof navigator === 'undefined' ? undefined : navigator.modelContext));\n if (!modelContext) {\n console.warn(\n `[useWebMCP] document.modelContext is not available. Tool \"${name}\" will not be registered.`\n );\n return;\n }\n\n const registeredConfig = committedConfigRef.current;\n const registeredOutputSchema = registeredConfig.outputSchema;\n const controller = new AbortController();\n const tool = {\n name,\n description,\n inputSchema: normalizeInputSchema(registeredConfig.inputSchema).inputSchema,\n ...(registeredOutputSchema && { outputSchema: registeredOutputSchema }),\n ...(registeredConfig.annotations && { annotations: registeredConfig.annotations }),\n execute: async (input: TInput) => {\n try {\n const response = normalizeToolResponse(await run(input));\n if (registeredOutputSchema && response.structuredContent === undefined) {\n throw new Error(\n `Tool \"${name}\" outputSchema requires the tool implementation to return a JSON-serializable result`\n );\n }\n return response;\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n return {\n content: [{ type: 'text' as const, text: `Error: ${message}` }],\n isError: true,\n };\n }\n },\n };\n\n try {\n const registration = modelContext.registerTool(tool, { signal: controller.signal });\n void Promise.resolve(registration).catch((error: unknown) => {\n if (!controller.signal.aborted) {\n controller.abort();\n console.warn(`[useWebMCP] registerTool(\"${name}\") rejected:`, error);\n }\n });\n } catch (error) {\n controller.abort();\n console.warn(`[useWebMCP] registerTool(\"${name}\") rejected:`, error);\n return;\n }\n\n return () => controller.abort();\n // `deps` lets callers explicitly opt descriptor values into re-registration.\n // oxlint-disable-next-line react-doctor/exhaustive-deps -- Public API deliberately forwards caller deps.\n }, [name, description, enabled, ...(deps ?? [])]);\n\n return { state, execute: run, reset };\n}\n"],"mappings":"+MAgBA,MAAM,EAAgB,CACpB,YAAa,GACb,WAAY,KACZ,MAAO,KACP,eAAgB,CAClB,EAEM,EAA4B,OAAO,OAAW,IAAc,EAAY,EAG9E,SAAgB,EAId,EACA,EAC2C,CAG3C,GAAM,CAAE,OAAM,cAAa,UAAU,IAAS,EACxC,CAAC,EAAO,GAAY,EAAsC,CAAa,EACvE,EAAqB,EAAO,CAAM,EAClC,EAAuB,EAAO,CAAC,EAIrC,MAAgC,CAC9B,EAAmB,QAAU,CAC/B,CAAC,EAED,IAAM,EAAM,EAAY,KAAO,IAAoC,CACjE,EAAqB,SAAW,EAChC,EAAU,GACR,EAAS,aAAe,EAAS,QAAU,KACvC,EACA,CAAE,GAAG,EAAU,YAAa,GAAM,MAAO,IAAK,CACpD,EAEA,IAAI,EACE,EAAkB,EAAmB,QAC3C,GAAI,CAEF,GADA,EAAS,MAAM,EAAgB,QAAQ,CAAK,EAE1C,EAAgB,cAChB,EAAsB,CAAM,EAAE,oBAAsB,IAAA,GAEpD,MAAU,MACR,SAAS,EAAgB,KAAK,qEAChC,CAEJ,OAAS,EAAO,CACd,IAAqB,QACrB,IAAM,EAAkB,aAAiB,MAAQ,EAAY,MAAM,OAAO,CAAK,CAAC,EAMhF,MALA,EAAU,IAAc,CACtB,GAAG,EACH,YAAa,EAAqB,QAAU,EAC5C,MAAO,CACT,EAAE,EACI,CACR,CASA,MAPA,IAAqB,QACrB,EAAU,IAAc,CACtB,YAAa,EAAqB,QAAU,EAC5C,WAAY,EACZ,MAAO,KACP,eAAgB,EAAS,eAAiB,CAC5C,EAAE,EACK,CACT,EAAG,CAAC,CAAC,EAEC,EAAQ,MAAkB,CAC9B,IAAM,EAAc,EAAqB,QAAU,EACnD,EAAU,GACR,EAAS,cAAgB,GACzB,EAAS,aAAe,MACxB,EAAS,QAAU,MACnB,EAAS,iBAAmB,EACxB,EACA,CAAE,GAAG,EAAe,aAAY,CACtC,CACF,EAAG,CAAC,CAAC,EAgEL,OA9DA,MAAgB,CACd,GAAI,CAAC,EAAS,OAEd,IAAM,EACJ,OAAO,SAAa,IAChB,IAAA,GACC,SAAS,eACT,OAAO,UAAc,IAAc,IAAA,GAAY,UAAU,cAChE,GAAI,CAAC,EAAc,CACjB,QAAQ,KACN,6DAA6D,EAAK,0BACpE,EACA,MACF,CAEA,IAAM,EAAmB,EAAmB,QACtC,EAAyB,EAAiB,aAC1C,EAAa,IAAI,gBACjB,EAAO,CACX,OACA,cACA,YAAa,EAAqB,EAAiB,WAAW,EAAE,YAChE,GAAI,GAA0B,CAAE,aAAc,CAAuB,EACrE,GAAI,EAAiB,aAAe,CAAE,YAAa,EAAiB,WAAY,EAChF,QAAS,KAAO,IAAkB,CAChC,GAAI,CACF,IAAM,EAAW,EAAsB,MAAM,EAAI,CAAK,CAAC,EACvD,GAAI,GAA0B,EAAS,oBAAsB,IAAA,GAC3D,MAAU,MACR,SAAS,EAAK,qFAChB,EAEF,OAAO,CACT,OAAS,EAAO,CAEd,MAAO,CACL,QAAS,CAAC,CAAE,KAAM,OAAiB,KAAM,UAF3B,aAAiB,MAAQ,EAAM,QAAU,OAAO,CAAK,GAEN,CAAC,EAC9D,QAAS,EACX,CACF,CACF,CACF,EAEA,GAAI,CACF,IAAM,EAAe,EAAa,aAAa,EAAM,CAAE,OAAQ,EAAW,MAAO,CAAC,EAClF,QAAa,QAAQ,CAAY,EAAE,MAAO,GAAmB,CACtD,EAAW,OAAO,UACrB,EAAW,MAAM,EACjB,QAAQ,KAAK,6BAA6B,EAAK,cAAe,CAAK,EAEvE,CAAC,CACH,OAAS,EAAO,CACd,EAAW,MAAM,EACjB,QAAQ,KAAK,6BAA6B,EAAK,cAAe,CAAK,EACnE,MACF,CAEA,UAAa,EAAW,MAAM,CAGhC,EAAG,CAAC,EAAM,EAAa,EAAS,GAAI,GAAQ,CAAC,CAAE,CAAC,EAEzC,CAAE,QAAO,QAAS,EAAK,OAAM,CACtC"}