usewebmcp 2.1.0 → 2.3.0

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
@@ -17,12 +17,12 @@ Standalone React hooks for strict core WebMCP tool registration via `navigator.m
17
17
 
18
18
  ## Package Selection
19
19
 
20
- | Package | Use When |
21
- | --- | --- |
22
- | `usewebmcp` | React hooks for strict core `navigator.modelContext` tools |
23
- | `@mcp-b/react-webmcp` | React hooks for full MCP-B runtime surface |
24
- | `@mcp-b/webmcp-polyfill` | You need a strict core runtime polyfill |
25
- | `@mcp-b/global` | You need full MCP-B runtime (core + extensions) |
20
+ | Package | Use When |
21
+ | ------------------------ | ---------------------------------------------------------- |
22
+ | `usewebmcp` | React hooks for strict core `navigator.modelContext` tools |
23
+ | `@mcp-b/react-webmcp` | React hooks for full MCP-B runtime surface |
24
+ | `@mcp-b/webmcp-polyfill` | You need a strict core runtime polyfill |
25
+ | `@mcp-b/global` | You need full MCP-B runtime (core + extensions) |
26
26
 
27
27
  ## Install
28
28
 
@@ -100,8 +100,8 @@ export function CounterTool() {
100
100
 
101
101
  ## How `useWebMCP` Works
102
102
 
103
- - Registers a tool on mount with `navigator.modelContext.registerTool(...)`.
104
- - Unregisters on unmount with `navigator.modelContext.unregisterTool(name)`.
103
+ - Registers a tool on mount with `navigator.modelContext.registerTool(tool, { signal })` and aborts the controller on unmount.
104
+ - On Chrome Beta 147 native (which ignores the second arg) cleanup cannot remove the tool. Install `@mcp-b/global` or `@mcp-b/webmcp-polyfill` for spec-aligned behavior.
105
105
  - Exposes local execution state:
106
106
  - `state.isExecuting`
107
107
  - `state.lastResult`
package/dist/index.d.ts CHANGED
@@ -1,9 +1,8 @@
1
1
  import { DependencyList } from "react";
2
2
  import { ToolInputSchema } from "@mcp-b/webmcp-polyfill";
3
- import { InferArgsFromInputSchema, InferJsonSchema, InputSchema, JsonSchemaObject, ToolAnnotations } from "@mcp-b/webmcp-types";
3
+ import { InferArgsFromInputSchema, InferJsonSchema, InputSchema, JsonSchemaForInference, ToolAnnotations } from "@mcp-b/webmcp-types";
4
4
 
5
5
  //#region src/types.d.ts
6
-
7
6
  /**
8
7
  * Infers tool input type from either a Standard Schema or JSON Schema.
9
8
  *
@@ -22,17 +21,17 @@ type InferToolInput<T> = T extends {
22
21
  readonly input: infer I;
23
22
  } ? I : Record<string, unknown> : T extends InputSchema ? InferArgsFromInputSchema<T> : Record<string, unknown>;
24
23
  /**
25
- * Utility type to infer the output type from a JSON Schema object.
24
+ * Utility type to infer the output type from a JSON Schema.
26
25
  *
27
- * When `TOutputSchema` is a literal `JsonSchemaObject`, this resolves to
26
+ * When `TOutputSchema` is a literal inferable JSON schema, this resolves to
28
27
  * `InferJsonSchema<TOutputSchema>`. When it's `undefined`,
29
28
  * it falls back to the provided `TFallback` type.
30
29
  *
31
- * @template TOutputSchema - JSON Schema object for output inference
30
+ * @template TOutputSchema - JSON Schema for output inference
32
31
  * @template TFallback - Fallback type when no schema is provided
33
32
  * @internal
34
33
  */
35
- type InferOutput<TOutputSchema extends JsonSchemaObject | undefined = undefined, TFallback = unknown> = TOutputSchema extends undefined ? TFallback : TOutputSchema extends JsonSchemaObject ? InferJsonSchema<TOutputSchema> : TFallback;
34
+ type InferOutput<TOutputSchema extends JsonSchemaForInference | undefined = undefined, TFallback = unknown> = TOutputSchema extends undefined ? TFallback : TOutputSchema extends JsonSchemaForInference ? InferJsonSchema<TOutputSchema> : TFallback;
36
35
  /**
37
36
  * Represents the current execution state of a tool, including loading status,
38
37
  * results, errors, and execution history.
@@ -68,11 +67,11 @@ interface ToolExecutionState<TOutput = unknown> {
68
67
  * Supports sync or async implementations.
69
68
  *
70
69
  * @template TInputSchema - Schema defining input parameters
71
- * @template TOutputSchema - Optional JSON Schema object defining output structure
70
+ * @template TOutputSchema - Optional JSON Schema defining output structure
72
71
  *
73
72
  * @public
74
73
  */
75
- type ToolExecuteFunction<TInputSchema extends ToolInputSchema = InputSchema, TOutputSchema extends JsonSchemaObject | undefined = undefined> = (input: InferToolInput<TInputSchema>) => Promise<InferOutput<TOutputSchema>> | InferOutput<TOutputSchema>;
74
+ type ToolExecuteFunction<TInputSchema extends ToolInputSchema = InputSchema, TOutputSchema extends JsonSchemaForInference | undefined = undefined> = (input: InferToolInput<TInputSchema>) => Promise<InferOutput<TOutputSchema>> | InferOutput<TOutputSchema>;
76
75
  /**
77
76
  * Shared configuration fields for the `useWebMCP` hook.
78
77
  *
@@ -80,7 +79,7 @@ type ToolExecuteFunction<TInputSchema extends ToolInputSchema = InputSchema, TOu
80
79
  * Uses JSON Schema for type inference via `as const`.
81
80
  *
82
81
  * @template TInputSchema - JSON Schema defining input parameters
83
- * @template TOutputSchema - JSON Schema object defining output structure (enables structuredContent)
82
+ * @template TOutputSchema - JSON Schema defining output structure (object schemas enable structuredContent)
84
83
  *
85
84
  * @public
86
85
  *
@@ -125,7 +124,7 @@ type ToolExecuteFunction<TInputSchema extends ToolInputSchema = InputSchema, TOu
125
124
  * });
126
125
  * ```
127
126
  */
128
- interface WebMCPConfigBase<TInputSchema extends ToolInputSchema = InputSchema, TOutputSchema extends JsonSchemaObject | undefined = undefined> {
127
+ interface WebMCPConfigBase<TInputSchema extends ToolInputSchema = InputSchema, TOutputSchema extends JsonSchemaForInference | undefined = undefined> {
129
128
  /**
130
129
  * Unique identifier for the tool (e.g., 'posts_like', 'graph_navigate').
131
130
  * Must follow naming conventions: lowercase with underscores.
@@ -159,7 +158,7 @@ interface WebMCPConfigBase<TInputSchema extends ToolInputSchema = InputSchema, T
159
158
  */
160
159
  inputSchema?: TInputSchema;
161
160
  /**
162
- * **Recommended:** JSON Schema object defining the expected output structure.
161
+ * **Recommended:** JSON Schema defining the expected output structure.
163
162
  *
164
163
  * When provided, this enables three key features:
165
164
  * 1. **Type Safety**: The implementation return type is inferred from this schema
@@ -214,7 +213,7 @@ interface WebMCPConfigBase<TInputSchema extends ToolInputSchema = InputSchema, T
214
213
  */
215
214
  onError?: (error: Error, input: unknown) => void;
216
215
  }
217
- type WebMCPConfigImplementation<TInputSchema extends ToolInputSchema = InputSchema, TOutputSchema extends JsonSchemaObject | undefined = undefined> = {
216
+ type WebMCPConfigImplementation<TInputSchema extends ToolInputSchema = InputSchema, TOutputSchema extends JsonSchemaForInference | undefined = undefined> = {
218
217
  /**
219
218
  * Preferred tool implementation function.
220
219
  */
@@ -243,19 +242,19 @@ type WebMCPConfigImplementation<TInputSchema extends ToolInputSchema = InputSche
243
242
  * If both are provided, `execute` is used.
244
243
  *
245
244
  * @template TInputSchema - JSON Schema defining input parameters
246
- * @template TOutputSchema - JSON Schema object defining output structure (enables structuredContent)
245
+ * @template TOutputSchema - JSON Schema defining output structure (object schemas enable structuredContent)
247
246
  *
248
247
  * @public
249
248
  */
250
- type WebMCPConfig<TInputSchema extends ToolInputSchema = InputSchema, TOutputSchema extends JsonSchemaObject | undefined = undefined> = WebMCPConfigBase<TInputSchema, TOutputSchema> & WebMCPConfigImplementation<TInputSchema, TOutputSchema>;
249
+ type WebMCPConfig<TInputSchema extends ToolInputSchema = InputSchema, TOutputSchema extends JsonSchemaForInference | undefined = undefined> = WebMCPConfigBase<TInputSchema, TOutputSchema> & WebMCPConfigImplementation<TInputSchema, TOutputSchema>;
251
250
  /**
252
251
  * Return value from the `useWebMCP` hook.
253
252
  * Provides access to execution state and methods for manual tool control.
254
253
  *
255
- * @template TOutputSchema - JSON Schema object defining output structure
254
+ * @template TOutputSchema - JSON Schema defining output structure
256
255
  * @public
257
256
  */
258
- interface WebMCPReturn<TOutputSchema extends JsonSchemaObject | undefined = undefined> {
257
+ interface WebMCPReturn<TOutputSchema extends JsonSchemaForInference | undefined = undefined, TInputSchema extends ToolInputSchema = InputSchema> {
259
258
  /**
260
259
  * Current execution state including loading status, results, and errors.
261
260
  * See {@link ToolExecutionState} for details.
@@ -269,7 +268,7 @@ interface WebMCPReturn<TOutputSchema extends JsonSchemaObject | undefined = unde
269
268
  * @returns Promise resolving to the tool's output
270
269
  * @throws Error if validation fails or tool implementation throws
271
270
  */
272
- execute: (input: unknown) => Promise<InferOutput<TOutputSchema>>;
271
+ execute: (input: InferToolInput<TInputSchema>) => Promise<InferOutput<TOutputSchema>>;
273
272
  /**
274
273
  * Reset the execution state to its initial values.
275
274
  * Clears results, errors, and resets the execution count.
@@ -344,7 +343,7 @@ interface WebMCPReturn<TOutputSchema extends JsonSchemaObject | undefined = unde
344
343
  * ```
345
344
  *
346
345
  * @template TInputSchema - JSON Schema defining input parameter types (use `as const` for inference)
347
- * @template TOutputSchema - JSON Schema object defining output structure (enables structuredContent)
346
+ * @template TOutputSchema - JSON Schema defining output structure (object schemas enable structuredContent)
348
347
  *
349
348
  * @param config - Configuration object for the tool
350
349
  * @param deps - Optional dependency array that triggers tool re-registration when values change.
@@ -382,7 +381,7 @@ interface WebMCPReturn<TOutputSchema extends JsonSchemaObject | undefined = unde
382
381
  * }
383
382
  * ```
384
383
  */
385
- declare function useWebMCP<TInputSchema extends ToolInputSchema = InputSchema, TOutputSchema extends JsonSchemaObject | undefined = undefined>(config: WebMCPConfig<TInputSchema, TOutputSchema>, deps?: DependencyList): WebMCPReturn<TOutputSchema>;
384
+ declare function useWebMCP<TInputSchema extends ToolInputSchema = InputSchema, TOutputSchema extends JsonSchemaForInference | undefined = undefined>(config: WebMCPConfig<TInputSchema, TOutputSchema>, deps?: DependencyList): WebMCPReturn<TOutputSchema, TInputSchema>;
386
385
  //#endregion
387
386
  export { type InferOutput, type InferToolInput, type ToolExecuteFunction, type ToolExecutionState, type WebMCPConfig, type WebMCPReturn, useWebMCP };
388
387
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","names":[],"sources":["../src/types.ts","../src/useWebMCP.ts"],"sourcesContent":[],"mappings":";;;;;;;;;AAmBA;;;;;;;AAK+B,KALnB,cAKmB,CAAA,CAAA,CAAA,GALC,CAKD,SAAA;EAAzB,SAAA,WAAA,EAAA;IACA,SAAA,KAAA,CAAA,EAAA,KAAA,MAAA;EAAM,CAAA;AAaZ,CAAA,GAlBI,KAkBQ,SAAW;EACC,SAAA,KAAA,EAAA,KAAA,EAAA;CAEpB,GApBE,CAoBF,GAnBE,MAmBF,CAAA,MAAA,EAAA,OAAA,CAAA,GAlBA,CAkBA,SAlBU,WAkBV,GAjBE,wBAiBF,CAjB2B,CAiB3B,CAAA,GAhBE,MAgBF,CAAA,MAAA,EAAA,OAAA,CAAA;;;;;;;;AAaJ;AAoCA;;;AAEwB,KAtDZ,WAsDY,CAAA,sBArDA,gBAqDA,GAAA,SAAA,GAAA,SAAA,EAAA,YAAA,OAAA,CAAA,GAnDpB,aAmDoB,SAAA,SAAA,GAlDpB,SAkDoB,GAjDpB,aAiDoB,SAjDE,gBAiDF,GAhDlB,eAgDkB,CAhDF,aAgDE,CAAA,GA/ClB,SA+CkB;;;;;;;;AAG8B,UAzCrC,kBAyCqC,CAAA,UAAA,OAAA,CAAA,CAAA;EAsD5C;;;;EAqCM,WAAA,EAAA,OAAA;EAwBC;;;;EA2BkB,UAAA,EA5KrB,OA4KqB,GAAA,IAAA;EAAZ;;;AASE;EAIF,KAAA,EAnLd,KAmLc,GAAA,IAAA;EAAkB;;;;EAO1B,cAAA,EAAA,MAAA;;;;;;;;;;;AA+Bf;AACuB,KAvMX,mBAuMW,CAAA,qBAtMA,eAsMA,GAtMkB,WAsMlB,EAAA,sBArMC,gBAqMD,GAAA,SAAA,GAAA,SAAA,CAAA,GAAA,CAAA,KAAA,EAnMd,cAmMc,CAnMC,YAmMD,CAAA,EAAA,GAlMlB,OAkMkB,CAlMV,WAkMU,CAlME,aAkMF,CAAA,CAAA,GAlMoB,WAkMpB,CAlMgC,aAkMhC,CAAA;;;;;;;;;;AAYvB;;;;;;;;;;;;AC3IA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;UDbU,sCACa,kBAAkB,mCACjB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;gBAmCR;;;;;;;;;;;;;;;;;;;;;;;iBAwBC;;;;;gBAMD;;;;;;;;;;;0BAYU,YAAY;;;;;;;;uBASf,YAAY;;;;;;;;oBASf;;KAGf,gDACkB,kBAAkB,mCACjB;;;;WAMT,oBAAoB,cAAc;;;;YAIjC,oBAAoB,cAAc;;;;;WAMnC,oBAAoB,cAAc;;;;YAIjC,oBAAoB,cAAc;;;;;;;;;;;;;;;;KAiBtC,kCACW,kBAAkB,mCACjB,4CACpB,iBAAiB,cAAc,iBACjC,2BAA2B,cAAc;;;;;;;;UAS1B,mCAAmC;;;;;SAK3C,mBAAmB,YAAY;;;;;;;;;+BAUT,QAAQ,YAAY;;;;;;;;;;AAzSnD;;;;;;;;;;;AAmBA;;;;;;;;;;AAgBA;AAoCA;;;;;;;;;;;;AAKsE;;;;;;;;;;;;;AAuJ7C;;;;;;;;;;;;;;;;;AA0CzB;;;;;;;;;;;AAaA;;;;;;;;;;;;AC3IA;;;;;;;;;;;;;;;;iBAAgB,+BACO,kBAAkB,mCACjB,kDAEd,aAAa,cAAc,uBAC5B,iBACN,aAAa"}
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../src/types.ts","../src/useWebMCP.ts"],"mappings":";;;;;;;;AAmBA;;;;;;;KAAY,cAAA,MAAoB,CAAA;EAAA,SAAqB,WAAA;IAAA,SAAwB,KAAA;EAAA;AAAA,IACzE,KAAA;EAAA,SAAyB,KAAA;AAAA,IACvB,CAAA,GACA,MAAA,oBACF,CAAA,SAAU,WAAA,GACR,wBAAA,CAAyB,CAAA,IACzB,MAAA;;;;;;;;;;;;KAaM,WAAA,uBACY,sBAAA,iDAEpB,aAAA,qBACA,SAAA,GACA,aAAA,SAAsB,sBAAA,GACpB,eAAA,CAAgB,aAAA,IAChB,SAAA;;;;AAPN;;;;UAgBiB,kBAAA;EAZb;;;;EAiBF,WAAA;EAdI;;;;EAoBJ,UAAA,EAAY,OAAA;EAzBZ;;;;EA+BA,KAAA,EAAO,KAAA;EA3BH;;;;EAiCJ,cAAA;AAAA;;;;;;;;;;;KAaU,mBAAA,sBACW,eAAA,GAAkB,WAAA,wBACjB,sBAAA,6BAEtB,KAAA,EAAO,cAAA,CAAe,YAAA,MACnB,OAAA,CAAQ,WAAA,CAAY,aAAA,KAAkB,WAAA,CAAY,aAAA;;AALvD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAKsE;;;;;;;;;;;;;;;;;;;;;UAsD5D,gBAAA,sBACa,eAAA,GAAkB,WAAA,wBACjB,sBAAA;EAYtB;;;;EANA,IAAA;EA2DA;;;;EArDA,WAAA;EAiEgB;;;;;;;;;;;;AAkBuB;;;;;;;;;EA5DvC,WAAA,GAAc,YAAA;EA2EkC;;;;;;;;;;;;;;;;;;;;;;EAnDhD,YAAA,GAAe,aAAA;EAyDX;;;;EAnDJ,WAAA,GAAc,eAAA;EAuDA;;;;;AAiBhB;;;;;EA5DE,YAAA,IAAgB,MAAA,EAAQ,WAAA,CAAY,aAAA;EA+DjB;;;;;;;EAtDnB,SAAA,IAAa,MAAA,EAAQ,WAAA,CAAY,aAAA,GAAgB,KAAA;EAoDjD;;;;;;;EA3CA,OAAA,IAAW,KAAA,EAAO,KAAA,EAAO,KAAA;AAAA;AAAA,KAGtB,0BAAA,sBACkB,eAAA,GAAkB,WAAA,wBACjB,sBAAA;EAyCmB;;;EAnCrC,OAAA,EAAS,mBAAA,CAAoB,YAAA,EAAc,aAAA;EA4CpB;;;EAxCvB,OAAA,GAAU,mBAAA,CAAoB,YAAA,EAAc,aAAA;AAAA;EAgDV;;;EA1ClC,OAAA,EAAS,mBAAA,CAAoB,YAAA,EAAc,aAAA;EAoD9B;;;EAhDb,OAAA,GAAU,mBAAA,CAAoB,YAAA,EAAc,aAAA;AAAA;;;;;;;;;;;;;;;KAiBtC,YAAA,sBACW,eAAA,GAAkB,WAAA,wBACjB,sBAAA,4BACpB,gBAAA,CAAiB,YAAA,EAAc,aAAA,IACjC,0BAAA,CAA2B,YAAA,EAAc,aAAA;;;;;;;;UAS1B,YAAA,uBACO,sBAAA,+CACD,eAAA,GAAkB,WAAA;ECxBzB;;;;ED8Bd,KAAA,EAAO,kBAAA,CAAmB,WAAA,CAAY,aAAA;EC5BhB;;;;;;;;EDsCtB,OAAA,GAAU,KAAA,EAAO,cAAA,CAAe,YAAA,MAAkB,OAAA,CAAQ,WAAA,CAAY,aAAA;EClCzD;;;;EDwCb,KAAA;AAAA;;;;AAlTF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAmBA;;;;;;;;;;;;;;;;;;;;;;;;;AAgBA;;;;;;;;;;;;;AAoCA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAKsE;;;;;;iBCwLtD,SAAA,sBACO,eAAA,GAAkB,WAAA,wBACjB,sBAAA,yBAAA,CAEtB,MAAA,EAAQ,YAAA,CAAa,YAAA,EAAc,aAAA,GACnC,IAAA,GAAO,cAAA,GACN,YAAA,CAAa,aAAA,EAAe,YAAA"}
package/dist/index.js CHANGED
@@ -1,2 +1,2 @@
1
- "use client";import{useCallback as e,useEffect as t,useLayoutEffect as n,useRef as r,useState as i}from"react";function a(e){return typeof e==`string`?e:JSON.stringify(e,null,2)}const o=new Map;function s(e){if(!e||typeof e!=`object`||Array.isArray(e))return null;try{let t=JSON.parse(JSON.stringify(e));return!t||typeof t!=`object`||Array.isArray(t)?null:t}catch{return null}}const c=typeof window<`u`?n:t;function l(){let e=globalThis.process?.env?.NODE_ENV;return e===void 0?!1:e!==`production`}function u(n,u){let{name:d,description:f,inputSchema:p,outputSchema:m,annotations:h,execute:g,handler:_,formatOutput:v=a,onSuccess:y,onError:b}=n,x=g??_;if(!x)throw TypeError(`[useWebMCP] Tool "${d}" must provide an implementation via config.execute or config.handler`);let[S,C]=i({isExecuting:!1,lastResult:null,error:null,executionCount:0}),w=r(x),T=r(y),E=r(b),D=r(v),O=r(!0),k=r(new Set),A=r({inputSchema:p,outputSchema:m,annotations:h,description:f,deps:u});c(()=>{w.current=x,T.current=y,E.current=b,D.current=v},[x,y,b,v]),t(()=>(O.current=!0,()=>{O.current=!1}),[]),t(()=>{if(!l()){A.current={inputSchema:p,outputSchema:m,annotations:h,description:f,deps:u};return}let e=(e,t)=>{k.current.has(e)||(console.warn(`[useWebMCP] ${t}`),k.current.add(e))},t=A.current;p&&t.inputSchema&&t.inputSchema!==p&&e(`inputSchema`,`Tool "${d}" inputSchema reference changed; memoize or define it outside the component to avoid re-registration.`),m&&t.outputSchema&&t.outputSchema!==m&&e(`outputSchema`,`Tool "${d}" outputSchema reference changed; memoize or define it outside the component to avoid re-registration.`),h&&t.annotations&&t.annotations!==h&&e(`annotations`,`Tool "${d}" annotations reference changed; memoize or define it outside the component to avoid re-registration.`),f!==t.description&&e(`description`,`Tool "${d}" description changed; this re-registers the tool. Memoize the description if it does not need to update.`),u?.some(e=>typeof e==`object`&&!!e||typeof e==`function`)&&e(`deps`,`Tool "${d}" deps contains non-primitive values; prefer primitives or memoize objects/functions to reduce re-registration.`),A.current={inputSchema:p,outputSchema:m,annotations:h,description:f,deps:u}},[h,u,f,p,d,m]);let j=e(async e=>{C(e=>({...e,isExecuting:!0,error:null}));try{let t=await w.current(e);return O.current&&C(e=>({isExecuting:!1,lastResult:t,error:null,executionCount:e.executionCount+1})),T.current&&T.current(t,e),t}catch(t){let n=t instanceof Error?t:Error(String(t));throw O.current&&C(e=>({...e,isExecuting:!1,error:n})),E.current&&E.current(n,e),n}},[]),M=r(j);t(()=>{M.current=j},[j]);let N=e(e=>M.current(e),[]),P=e(()=>{C({isExecuting:!1,lastResult:null,error:null,executionCount:0})},[]);return t(()=>{if(typeof window>`u`||!window.navigator?.modelContext){console.warn(`[useWebMCP] window.navigator.modelContext is not available. Tool "${d}" will not be registered.`);return}let e=async e=>{try{let t=await M.current(e),n={content:[{type:`text`,text:D.current(t)}]};if(m){let e=s(t);if(!e)throw Error(`Tool "${d}" outputSchema requires the tool implementation to return a JSON object result`);n.structuredContent=e}return n}catch(e){return{content:[{type:`text`,text:`Error: ${e instanceof Error?e.message:String(e)}`}],isError:!0}}},t=Symbol(d),n=window.navigator.modelContext;return n.registerTool({name:d,description:f,...p&&{inputSchema:p},...m&&{outputSchema:m},...h&&{annotations:h},execute:e}),o.set(d,t),()=>{if(o.get(d)===t){o.delete(d);try{n.unregisterTool(d)}catch(e){l()&&console.warn(`[useWebMCP] Failed to unregister tool "${d}" during cleanup:`,e)}}}},[d,f,p,m,h,...u??[]]),{state:S,execute:N,reset:P}}export{u as useWebMCP};
1
+ "use client";import{useCallback as e,useEffect as t,useLayoutEffect as n,useRef as r,useState as i}from"react";function a(e){return typeof e==`string`?e:JSON.stringify(e,null,2)}const o=new Map,s={type:`object`,properties:{}},c=[`draft-2020-12`,`draft-07`];function l(e){return e?.type===`object`}function u(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function d(e){return!(!u(e)||`type`in e&&e.type!==void 0&&typeof e.type!=`string`||`properties`in e&&e.properties!==void 0&&!u(e.properties)||`required`in e&&e.required!==void 0&&(!Array.isArray(e.required)||e.required.some(e=>typeof e!=`string`)))}function f(e){return e===null||typeof e==`string`||typeof e==`number`||typeof e==`boolean`?!0:Array.isArray(e)?e.every(f):typeof e==`object`?Object.values(e).every(f):!1}function p(e){return typeof e==`object`&&!!e&&!Array.isArray(e)&&f(e)}function m(e){if(!e||typeof e!=`object`||Array.isArray(e))return null;try{let t=JSON.parse(JSON.stringify(e));return p(t)?t:null}catch{return null}}const h=typeof window<`u`?n:t;function g(){let e=typeof process<`u`?`production`:void 0;return e===void 0?!1:e!==`production`}function _(e,t){let n=new AbortController;return e.registerTool.call(e,t,{signal:n.signal}),n}function v(e){if(e===void 0)return;if(!u(e)||!(`~standard`in e))return d(e)?e:s;let t=e[`~standard`];if(!u(t))return s;let n=t.jsonSchema;if(!u(n)||typeof n.input!=`function`)return s;for(let e of c)try{let t=n.input({target:e});if(d(t))return t}catch{}return s}function y(e){if(e!==void 0)return e}function b(n,s){let{name:c,description:u,inputSchema:d,outputSchema:f,annotations:p,execute:b,handler:x,formatOutput:S=a,onSuccess:C,onError:w}=n,T=b??x;if(!T)throw TypeError(`[useWebMCP] Tool "${c}" must provide an implementation via config.execute or config.handler`);let[E,D]=i({isExecuting:!1,lastResult:null,error:null,executionCount:0}),O=r(T),k=r(C),A=r(w),j=r(S),M=r(!0),N=r(new Set),P=r({inputSchema:d,outputSchema:f,annotations:p,description:u,deps:s});h(()=>{O.current=T,k.current=C,A.current=w,j.current=S},[T,C,w,S]),t(()=>(M.current=!0,()=>{M.current=!1}),[]),t(()=>{if(!g()){P.current={inputSchema:d,outputSchema:f,annotations:p,description:u,deps:s};return}let e=(e,t)=>{N.current.has(e)||(console.warn(`[useWebMCP] ${t}`),N.current.add(e))},t=P.current;d&&t.inputSchema&&t.inputSchema!==d&&e(`inputSchema`,`Tool "${c}" inputSchema reference changed; memoize or define it outside the component to avoid re-registration.`),f&&t.outputSchema&&t.outputSchema!==f&&e(`outputSchema`,`Tool "${c}" outputSchema reference changed; memoize or define it outside the component to avoid re-registration.`),p&&t.annotations&&t.annotations!==p&&e(`annotations`,`Tool "${c}" annotations reference changed; memoize or define it outside the component to avoid re-registration.`),u!==t.description&&e(`description`,`Tool "${c}" description changed; this re-registers the tool. Memoize the description if it does not need to update.`),s?.some(e=>typeof e==`object`&&!!e||typeof e==`function`)&&e(`deps`,`Tool "${c}" deps contains non-primitive values; prefer primitives or memoize objects/functions to reduce re-registration.`),P.current={inputSchema:d,outputSchema:f,annotations:p,description:u,deps:s}},[p,s,u,d,c,f]);let F=e(async e=>{D(e=>({...e,isExecuting:!0,error:null}));try{let t=await O.current(e);return M.current&&D(e=>({isExecuting:!1,lastResult:t,error:null,executionCount:e.executionCount+1})),k.current&&k.current(t,e),t}catch(t){let n=t instanceof Error?t:Error(String(t));throw M.current&&D(e=>({...e,isExecuting:!1,error:n})),A.current&&A.current(n,e),n}},[]),I=r(F);t(()=>{I.current=F},[F]);let L=e(e=>I.current(e),[]),R=e(()=>{D({isExecuting:!1,lastResult:null,error:null,executionCount:0})},[]);return t(()=>{if(typeof window>`u`||!window.navigator?.modelContext){console.warn(`[useWebMCP] window.navigator.modelContext is not available. Tool "${c}" will not be registered.`);return}let e=async e=>{try{let t=await Reflect.apply(I.current,void 0,[e]),n={content:[{type:`text`,text:j.current(t)}]};if(l(f)){let e=m(t);if(!e)throw Error(`Tool "${c}" outputSchema requires the tool implementation to return a JSON object result`);n.structuredContent=e}return n}catch(e){return{content:[{type:`text`,text:`Error: ${e instanceof Error?e.message:String(e)}`}],isError:!0}}},t=Symbol(c),n=window.navigator.modelContext,r=v(d),i=y(f),a=_(n,{name:c,description:u,...r&&{inputSchema:r},...i&&{outputSchema:i},...p&&{annotations:p},execute:e});return o.set(c,t),()=>{o.get(c)===t&&(o.delete(c),a.abort())}},[c,u,d,f,p,...s??[]]),{state:E,execute:L,reset:R}}export{b as useWebMCP};
2
2
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["response: CallToolResult"],"sources":["../src/useWebMCP.ts"],"sourcesContent":["import type { ToolInputSchema } from '@mcp-b/webmcp-polyfill';\nimport type {\n CallToolResult,\n InputSchema,\n JsonSchemaObject,\n ToolDescriptor,\n} 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\n/**\n * Default output formatter that converts values to formatted JSON strings.\n *\n * String values are returned as-is; all other types are serialized to\n * indented JSON for readability.\n *\n * @internal\n */\nfunction defaultFormatOutput(output: unknown): string {\n if (typeof output === 'string') {\n return output;\n }\n return JSON.stringify(output, null, 2);\n}\n\nconst TOOL_OWNER_BY_NAME = new Map<string, symbol>();\ntype StructuredContent = Exclude<CallToolResult['structuredContent'], undefined>;\n\nfunction toStructuredContent(value: unknown): StructuredContent | null {\n if (!value || typeof value !== 'object' || Array.isArray(value)) {\n return null;\n }\n\n try {\n const normalized = JSON.parse(JSON.stringify(value)) as unknown;\n if (!normalized || typeof normalized !== 'object' || Array.isArray(normalized)) {\n return null;\n }\n return normalized as StructuredContent;\n } catch {\n return null;\n }\n}\n\nconst useIsomorphicLayoutEffect = typeof window !== 'undefined' ? useLayoutEffect : useEffect;\n\nfunction isDev(): boolean {\n const env = (globalThis as { process?: { env?: { NODE_ENV?: string } } }).process?.env?.NODE_ENV;\n return env !== undefined ? env !== 'production' : false;\n}\n\n/**\n * React hook for registering and managing Model Context Protocol (MCP) tools.\n *\n * This hook handles the complete lifecycle of an MCP tool:\n * - Registers the tool with `window.navigator.modelContext`\n * - Manages execution state (loading, results, errors)\n * - Handles tool execution and lifecycle callbacks\n * - Automatically unregisters on component unmount\n * - Returns `structuredContent` when `outputSchema` is defined\n *\n * ## Output Schema (Recommended)\n *\n * Always define an `outputSchema` for your tools. This provides:\n * - **Type Safety**: Handler return type is inferred from the schema\n * - **MCP structuredContent**: AI models receive structured, typed data\n * - **Better AI Understanding**: Models can reason about your tool's output format\n *\n * ```tsx\n * useWebMCP({\n * name: 'get_user',\n * description: 'Get user by ID',\n * inputSchema: {\n * type: 'object',\n * properties: { userId: { type: 'string' } },\n * required: ['userId'],\n * } as const,\n * outputSchema: {\n * type: 'object',\n * properties: {\n * id: { type: 'string' },\n * name: { type: 'string' },\n * email: { type: 'string' },\n * },\n * } as const,\n * execute: async ({ userId }) => {\n * const user = await fetchUser(userId);\n * return { id: user.id, name: user.name, email: user.email };\n * },\n * });\n * ```\n *\n * ## Re-render Optimization\n *\n * This hook is optimized to minimize unnecessary tool re-registrations:\n *\n * - **Ref-based callbacks**: `execute`/`handler`, `onSuccess`, `onError`, and `formatOutput`\n * are stored in refs, so changing these functions won't trigger re-registration.\n *\n * **IMPORTANT**: If `inputSchema`, `outputSchema`, or `annotations` are defined inline\n * or change on every render, the tool will re-register unnecessarily. To avoid this,\n * define them outside your component with `as const`:\n *\n * ```tsx\n * // Good: Static schema defined outside component\n * const OUTPUT_SCHEMA = {\n * type: 'object',\n * properties: { count: { type: 'number' } },\n * } as const;\n *\n * // Bad: Inline schema (creates new object every render)\n * useWebMCP({\n * outputSchema: { type: 'object', properties: { count: { type: 'number' } } } as const,\n * });\n * ```\n *\n * @template TInputSchema - JSON Schema defining input parameter types (use `as const` for inference)\n * @template TOutputSchema - JSON Schema object defining output structure (enables structuredContent)\n *\n * @param config - Configuration object for the tool\n * @param deps - Optional dependency array that triggers tool re-registration when values change.\n *\n * @returns Object containing execution state and control methods\n *\n * @public\n *\n * @example\n * Basic tool with outputSchema (recommended):\n * ```tsx\n * function PostActions() {\n * const likeTool = useWebMCP({\n * name: 'posts_like',\n * description: 'Like a post by ID',\n * inputSchema: {\n * type: 'object',\n * properties: { postId: { type: 'string', description: 'The post ID' } },\n * required: ['postId'],\n * } as const,\n * outputSchema: {\n * type: 'object',\n * properties: {\n * success: { type: 'boolean' },\n * likeCount: { type: 'number' },\n * },\n * } as const,\n * execute: async ({ postId }) => {\n * const result = await api.posts.like(postId);\n * return { success: true, likeCount: result.likes };\n * },\n * });\n *\n * return <div>Likes: {likeTool.state.lastResult?.likeCount ?? 0}</div>;\n * }\n * ```\n */\nexport function useWebMCP<\n TInputSchema extends ToolInputSchema = InputSchema,\n TOutputSchema extends JsonSchemaObject | undefined = undefined,\n>(\n config: WebMCPConfig<TInputSchema, TOutputSchema>,\n deps?: DependencyList\n): WebMCPReturn<TOutputSchema> {\n type TOutput = InferOutput<TOutputSchema>;\n type TInput = InferToolInput<TInputSchema>;\n const {\n name,\n description,\n inputSchema,\n outputSchema,\n annotations,\n execute: configExecute,\n handler: legacyHandler,\n formatOutput = defaultFormatOutput,\n onSuccess,\n onError,\n } = config;\n const toolExecute = configExecute ?? legacyHandler;\n\n if (!toolExecute) {\n throw new TypeError(\n `[useWebMCP] Tool \"${name}\" must provide an implementation via config.execute or config.handler`\n );\n }\n\n const [state, setState] = useState<ToolExecutionState<TOutput>>({\n isExecuting: false,\n lastResult: null,\n error: null,\n executionCount: 0,\n });\n\n const toolExecuteRef = useRef(toolExecute);\n const onSuccessRef = useRef(onSuccess);\n const onErrorRef = useRef(onError);\n const formatOutputRef = useRef(formatOutput);\n const isMountedRef = useRef(true);\n const warnedRef = useRef(new Set<string>());\n const prevConfigRef = useRef({\n inputSchema,\n outputSchema,\n annotations,\n description,\n deps,\n });\n // Update refs when callbacks change (doesn't trigger re-registration)\n useIsomorphicLayoutEffect(() => {\n toolExecuteRef.current = toolExecute;\n onSuccessRef.current = onSuccess;\n onErrorRef.current = onError;\n formatOutputRef.current = formatOutput;\n }, [toolExecute, onSuccess, onError, formatOutput]);\n\n // Cleanup: mark component as unmounted\n useEffect(() => {\n isMountedRef.current = true;\n return () => {\n isMountedRef.current = false;\n };\n }, []);\n\n useEffect(() => {\n if (!isDev()) {\n prevConfigRef.current = { inputSchema, outputSchema, annotations, description, deps };\n return;\n }\n\n const warnOnce = (key: string, message: string) => {\n if (warnedRef.current.has(key)) {\n return;\n }\n console.warn(`[useWebMCP] ${message}`);\n warnedRef.current.add(key);\n };\n\n const prev = prevConfigRef.current;\n\n if (inputSchema && prev.inputSchema && prev.inputSchema !== inputSchema) {\n warnOnce(\n 'inputSchema',\n `Tool \"${name}\" inputSchema reference changed; memoize or define it outside the component to avoid re-registration.`\n );\n }\n\n if (outputSchema && prev.outputSchema && prev.outputSchema !== outputSchema) {\n warnOnce(\n 'outputSchema',\n `Tool \"${name}\" outputSchema reference changed; memoize or define it outside the component to avoid re-registration.`\n );\n }\n\n if (annotations && prev.annotations && prev.annotations !== annotations) {\n warnOnce(\n 'annotations',\n `Tool \"${name}\" annotations reference changed; memoize or define it outside the component to avoid re-registration.`\n );\n }\n\n if (description !== prev.description) {\n warnOnce(\n 'description',\n `Tool \"${name}\" description changed; this re-registers the tool. Memoize the description if it does not need to update.`\n );\n }\n\n if (\n deps?.some(\n (value) => (typeof value === 'object' && value !== null) || typeof value === 'function'\n )\n ) {\n warnOnce(\n 'deps',\n `Tool \"${name}\" deps contains non-primitive values; prefer primitives or memoize objects/functions to reduce re-registration.`\n );\n }\n\n prevConfigRef.current = { inputSchema, outputSchema, annotations, description, deps };\n }, [annotations, deps, description, inputSchema, name, outputSchema]);\n\n /**\n * Executes the configured tool implementation with input validation and state management.\n *\n * @param input - The input parameters to validate and pass to the tool implementation\n * @returns Promise resolving to the tool output\n * @throws Error if validation fails or the tool implementation throws\n */\n const execute = useCallback(async (input: unknown): Promise<TOutput> => {\n setState((prev) => ({\n ...prev,\n isExecuting: true,\n error: null,\n }));\n\n try {\n const result = await toolExecuteRef.current(input as TInput);\n\n // Only update state if component is still mounted\n if (isMountedRef.current) {\n setState((prev) => ({\n isExecuting: false,\n lastResult: result,\n error: null,\n executionCount: prev.executionCount + 1,\n }));\n }\n\n if (onSuccessRef.current) {\n onSuccessRef.current(result, input);\n }\n\n return result;\n } catch (error) {\n const err = error instanceof Error ? error : new Error(String(error));\n\n // Only update state if component is still mounted\n if (isMountedRef.current) {\n setState((prev) => ({\n ...prev,\n isExecuting: false,\n error: err,\n }));\n }\n\n if (onErrorRef.current) {\n onErrorRef.current(err, input);\n }\n\n throw err;\n }\n }, []);\n const executeRef = useRef(execute);\n\n useEffect(() => {\n executeRef.current = execute;\n }, [execute]);\n\n const stableExecute = useCallback(\n (input: unknown): Promise<TOutput> => executeRef.current(input),\n []\n );\n\n /**\n * Resets the execution state to initial values.\n */\n const reset = useCallback(() => {\n setState({\n isExecuting: false,\n lastResult: null,\n error: null,\n executionCount: 0,\n });\n }, []);\n\n useEffect(() => {\n if (typeof window === 'undefined' || !window.navigator?.modelContext) {\n console.warn(\n `[useWebMCP] window.navigator.modelContext is not available. Tool \"${name}\" will not be registered.`\n );\n return;\n }\n\n /**\n * Handles MCP tool execution by running the tool implementation and formatting the response.\n *\n * @param input - The input parameters from the MCP client\n * @returns CallToolResult with text content and optional structuredContent\n */\n const mcpHandler = async (input: unknown): Promise<CallToolResult> => {\n try {\n const result = await executeRef.current(input);\n const formattedOutput = formatOutputRef.current(result);\n\n const response: CallToolResult = {\n content: [\n {\n type: 'text',\n text: formattedOutput,\n },\n ],\n };\n\n if (outputSchema) {\n const structuredContent = toStructuredContent(result);\n if (!structuredContent) {\n throw new Error(\n `Tool \"${name}\" outputSchema requires the tool implementation to return a JSON object result`\n );\n }\n response.structuredContent = structuredContent;\n }\n\n return response;\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n\n return {\n content: [\n {\n type: 'text',\n text: `Error: ${errorMessage}`,\n },\n ],\n isError: true,\n };\n }\n };\n\n const ownerToken = Symbol(name);\n const modelContext = window.navigator.modelContext;\n\n (modelContext.registerTool as (tool: ToolDescriptor) => void)({\n name,\n description,\n ...(inputSchema && { inputSchema: inputSchema as InputSchema }),\n ...(outputSchema && { outputSchema: outputSchema as InputSchema }),\n ...(annotations && { annotations }),\n execute: mcpHandler,\n });\n TOOL_OWNER_BY_NAME.set(name, ownerToken);\n\n return () => {\n const currentOwner = TOOL_OWNER_BY_NAME.get(name);\n if (currentOwner !== ownerToken) {\n return;\n }\n\n TOOL_OWNER_BY_NAME.delete(name);\n try {\n modelContext.unregisterTool(name);\n } catch (error) {\n if (isDev()) {\n console.warn(`[useWebMCP] Failed to unregister tool \"${name}\" during cleanup:`, error);\n }\n }\n };\n // Spread operator in dependencies: Allows users to provide additional dependencies\n // via the `deps` parameter. While unconventional, this pattern is intentional to support\n // dynamic dependency injection. The spread is safe because deps is validated and warned\n // about non-primitive values earlier in this hook.\n }, [name, description, inputSchema, outputSchema, annotations, ...(deps ?? [])]);\n\n return {\n state,\n execute: stableExecute,\n reset,\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../src/useWebMCP.ts"],"sourcesContent":["import type { ToolInputSchema } from '@mcp-b/webmcp-polyfill';\nimport type {\n CallToolResult,\n InputSchema,\n JsonObject,\n JsonSchemaForInference,\n ToolDescriptor,\n} 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\n/**\n * Default output formatter that converts values to formatted JSON strings.\n *\n * String values are returned as-is; all other types are serialized to\n * indented JSON for readability.\n *\n * @internal\n */\nfunction defaultFormatOutput(output: unknown): string {\n if (typeof output === 'string') {\n return output;\n }\n return JSON.stringify(output, null, 2);\n}\n\nconst TOOL_OWNER_BY_NAME = new Map<string, symbol>();\nconst DEFAULT_REGISTERED_INPUT_SCHEMA: InputSchema = { type: 'object', properties: {} };\nconst STANDARD_JSON_SCHEMA_TARGETS = ['draft-2020-12', 'draft-07'] as const;\ntype StructuredContent = Exclude<CallToolResult['structuredContent'], undefined>;\n\nfunction isObjectOutputSchema(schema: JsonSchemaForInference | undefined): boolean {\n return schema?.type === 'object';\n}\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nfunction isInputSchema(value: unknown): value is InputSchema {\n if (!isPlainObject(value)) {\n return false;\n }\n\n if ('type' in value && value.type !== undefined && typeof value.type !== 'string') {\n return false;\n }\n\n if ('properties' in value && value.properties !== undefined && !isPlainObject(value.properties)) {\n return false;\n }\n\n if (\n 'required' in value &&\n value.required !== undefined &&\n (!Array.isArray(value.required) || value.required.some((entry) => typeof entry !== 'string'))\n ) {\n return false;\n }\n\n return true;\n}\n\nfunction isJsonValue(value: unknown): boolean {\n if (value === null) {\n return true;\n }\n\n if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') {\n return true;\n }\n\n if (Array.isArray(value)) {\n return value.every(isJsonValue);\n }\n\n if (typeof value !== 'object') {\n return false;\n }\n\n return Object.values(value).every(isJsonValue);\n}\n\nfunction isJsonObject(value: unknown): value is JsonObject {\n return typeof value === 'object' && value !== null && !Array.isArray(value) && isJsonValue(value);\n}\n\nfunction toStructuredContent(value: unknown): StructuredContent | null {\n if (!value || typeof value !== 'object' || Array.isArray(value)) {\n return null;\n }\n\n try {\n const normalized = JSON.parse(JSON.stringify(value));\n return isJsonObject(normalized) ? normalized : null;\n } catch {\n return null;\n }\n}\n\nconst useIsomorphicLayoutEffect = typeof window !== 'undefined' ? useLayoutEffect : useEffect;\n\nfunction isDev(): boolean {\n const env = typeof process !== 'undefined' ? process.env?.NODE_ENV : undefined;\n return env !== undefined ? env !== 'production' : false;\n}\n\n/**\n * On Chrome Beta 147 native (which ignores the second arg), aborting\n * the controller cannot remove the tool. Install `@mcp-b/global`\n * or `@mcp-b/webmcp-polyfill` there.\n */\nfunction registerToolWithCleanup(\n modelContext: Navigator['modelContext'],\n toolDescriptor: ToolDescriptor\n): AbortController {\n const controller = new AbortController();\n (\n modelContext.registerTool as (tool: ToolDescriptor, options?: { signal?: AbortSignal }) => void\n ).call(modelContext, toolDescriptor, { signal: controller.signal });\n return controller;\n}\n\nfunction toRegisteredInputSchema(\n inputSchema: ToolInputSchema | undefined\n): InputSchema | undefined {\n if (inputSchema === undefined) {\n return undefined;\n }\n\n if (!isPlainObject(inputSchema) || !('~standard' in inputSchema)) {\n return isInputSchema(inputSchema) ? inputSchema : DEFAULT_REGISTERED_INPUT_SCHEMA;\n }\n\n const standard = inputSchema['~standard'];\n if (!isPlainObject(standard)) {\n return DEFAULT_REGISTERED_INPUT_SCHEMA;\n }\n\n const jsonSchema = standard.jsonSchema;\n if (!isPlainObject(jsonSchema) || typeof jsonSchema.input !== 'function') {\n return DEFAULT_REGISTERED_INPUT_SCHEMA;\n }\n\n for (const target of STANDARD_JSON_SCHEMA_TARGETS) {\n try {\n const converted = jsonSchema.input({ target });\n if (isInputSchema(converted)) {\n return converted;\n }\n } catch {\n // Try the next target before falling back to the default registration schema.\n }\n }\n\n return DEFAULT_REGISTERED_INPUT_SCHEMA;\n}\n\nfunction toRegisteredOutputSchema(\n outputSchema: JsonSchemaForInference | undefined\n): JsonSchemaForInference | undefined {\n if (outputSchema === undefined) {\n return undefined;\n }\n\n return outputSchema;\n}\n\n/**\n * React hook for registering and managing Model Context Protocol (MCP) tools.\n *\n * This hook handles the complete lifecycle of an MCP tool:\n * - Registers the tool with `window.navigator.modelContext`\n * - Manages execution state (loading, results, errors)\n * - Handles tool execution and lifecycle callbacks\n * - Automatically unregisters on component unmount\n * - Returns `structuredContent` when `outputSchema` is defined\n *\n * ## Output Schema (Recommended)\n *\n * Always define an `outputSchema` for your tools. This provides:\n * - **Type Safety**: Handler return type is inferred from the schema\n * - **MCP structuredContent**: AI models receive structured, typed data\n * - **Better AI Understanding**: Models can reason about your tool's output format\n *\n * ```tsx\n * useWebMCP({\n * name: 'get_user',\n * description: 'Get user by ID',\n * inputSchema: {\n * type: 'object',\n * properties: { userId: { type: 'string' } },\n * required: ['userId'],\n * } as const,\n * outputSchema: {\n * type: 'object',\n * properties: {\n * id: { type: 'string' },\n * name: { type: 'string' },\n * email: { type: 'string' },\n * },\n * } as const,\n * execute: async ({ userId }) => {\n * const user = await fetchUser(userId);\n * return { id: user.id, name: user.name, email: user.email };\n * },\n * });\n * ```\n *\n * ## Re-render Optimization\n *\n * This hook is optimized to minimize unnecessary tool re-registrations:\n *\n * - **Ref-based callbacks**: `execute`/`handler`, `onSuccess`, `onError`, and `formatOutput`\n * are stored in refs, so changing these functions won't trigger re-registration.\n *\n * **IMPORTANT**: If `inputSchema`, `outputSchema`, or `annotations` are defined inline\n * or change on every render, the tool will re-register unnecessarily. To avoid this,\n * define them outside your component with `as const`:\n *\n * ```tsx\n * // Good: Static schema defined outside component\n * const OUTPUT_SCHEMA = {\n * type: 'object',\n * properties: { count: { type: 'number' } },\n * } as const;\n *\n * // Bad: Inline schema (creates new object every render)\n * useWebMCP({\n * outputSchema: { type: 'object', properties: { count: { type: 'number' } } } as const,\n * });\n * ```\n *\n * @template TInputSchema - JSON Schema defining input parameter types (use `as const` for inference)\n * @template TOutputSchema - JSON Schema defining output structure (object schemas enable structuredContent)\n *\n * @param config - Configuration object for the tool\n * @param deps - Optional dependency array that triggers tool re-registration when values change.\n *\n * @returns Object containing execution state and control methods\n *\n * @public\n *\n * @example\n * Basic tool with outputSchema (recommended):\n * ```tsx\n * function PostActions() {\n * const likeTool = useWebMCP({\n * name: 'posts_like',\n * description: 'Like a post by ID',\n * inputSchema: {\n * type: 'object',\n * properties: { postId: { type: 'string', description: 'The post ID' } },\n * required: ['postId'],\n * } as const,\n * outputSchema: {\n * type: 'object',\n * properties: {\n * success: { type: 'boolean' },\n * likeCount: { type: 'number' },\n * },\n * } as const,\n * execute: async ({ postId }) => {\n * const result = await api.posts.like(postId);\n * return { success: true, likeCount: result.likes };\n * },\n * });\n *\n * return <div>Likes: {likeTool.state.lastResult?.likeCount ?? 0}</div>;\n * }\n * ```\n */\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 {\n name,\n description,\n inputSchema,\n outputSchema,\n annotations,\n execute: configExecute,\n handler: legacyHandler,\n formatOutput = defaultFormatOutput,\n onSuccess,\n onError,\n } = config;\n const toolExecute = configExecute ?? legacyHandler;\n\n if (!toolExecute) {\n throw new TypeError(\n `[useWebMCP] Tool \"${name}\" must provide an implementation via config.execute or config.handler`\n );\n }\n\n const [state, setState] = useState<ToolExecutionState<TOutput>>({\n isExecuting: false,\n lastResult: null,\n error: null,\n executionCount: 0,\n });\n\n const toolExecuteRef = useRef(toolExecute);\n const onSuccessRef = useRef(onSuccess);\n const onErrorRef = useRef(onError);\n const formatOutputRef = useRef(formatOutput);\n const isMountedRef = useRef(true);\n const warnedRef = useRef(new Set<string>());\n const prevConfigRef = useRef({\n inputSchema,\n outputSchema,\n annotations,\n description,\n deps,\n });\n // Update refs when callbacks change (doesn't trigger re-registration)\n useIsomorphicLayoutEffect(() => {\n toolExecuteRef.current = toolExecute;\n onSuccessRef.current = onSuccess;\n onErrorRef.current = onError;\n formatOutputRef.current = formatOutput;\n }, [toolExecute, onSuccess, onError, formatOutput]);\n\n // Cleanup: mark component as unmounted\n useEffect(() => {\n isMountedRef.current = true;\n return () => {\n isMountedRef.current = false;\n };\n }, []);\n\n useEffect(() => {\n if (!isDev()) {\n prevConfigRef.current = { inputSchema, outputSchema, annotations, description, deps };\n return;\n }\n\n const warnOnce = (key: string, message: string) => {\n if (warnedRef.current.has(key)) {\n return;\n }\n console.warn(`[useWebMCP] ${message}`);\n warnedRef.current.add(key);\n };\n\n const prev = prevConfigRef.current;\n\n if (inputSchema && prev.inputSchema && prev.inputSchema !== inputSchema) {\n warnOnce(\n 'inputSchema',\n `Tool \"${name}\" inputSchema reference changed; memoize or define it outside the component to avoid re-registration.`\n );\n }\n\n if (outputSchema && prev.outputSchema && prev.outputSchema !== outputSchema) {\n warnOnce(\n 'outputSchema',\n `Tool \"${name}\" outputSchema reference changed; memoize or define it outside the component to avoid re-registration.`\n );\n }\n\n if (annotations && prev.annotations && prev.annotations !== annotations) {\n warnOnce(\n 'annotations',\n `Tool \"${name}\" annotations reference changed; memoize or define it outside the component to avoid re-registration.`\n );\n }\n\n if (description !== prev.description) {\n warnOnce(\n 'description',\n `Tool \"${name}\" description changed; this re-registers the tool. Memoize the description if it does not need to update.`\n );\n }\n\n if (\n deps?.some(\n (value) => (typeof value === 'object' && value !== null) || typeof value === 'function'\n )\n ) {\n warnOnce(\n 'deps',\n `Tool \"${name}\" deps contains non-primitive values; prefer primitives or memoize objects/functions to reduce re-registration.`\n );\n }\n\n prevConfigRef.current = { inputSchema, outputSchema, annotations, description, deps };\n }, [annotations, deps, description, inputSchema, name, outputSchema]);\n\n /**\n * Executes the configured tool implementation with input validation and state management.\n *\n * @param input - The input parameters to validate and pass to the tool implementation\n * @returns Promise resolving to the tool output\n * @throws Error if validation fails or the tool implementation throws\n */\n const execute = useCallback(async (input: TInput): Promise<TOutput> => {\n setState((prev) => ({\n ...prev,\n isExecuting: true,\n error: null,\n }));\n\n try {\n const result = await toolExecuteRef.current(input);\n\n // Only update state if component is still mounted\n if (isMountedRef.current) {\n setState((prev) => ({\n isExecuting: false,\n lastResult: result,\n error: null,\n executionCount: prev.executionCount + 1,\n }));\n }\n\n if (onSuccessRef.current) {\n onSuccessRef.current(result, input);\n }\n\n return result;\n } catch (error) {\n const err = error instanceof Error ? error : new Error(String(error));\n\n // Only update state if component is still mounted\n if (isMountedRef.current) {\n setState((prev) => ({\n ...prev,\n isExecuting: false,\n error: err,\n }));\n }\n\n if (onErrorRef.current) {\n onErrorRef.current(err, input);\n }\n\n throw err;\n }\n }, []);\n const executeRef = useRef(execute);\n\n useEffect(() => {\n executeRef.current = execute;\n }, [execute]);\n\n const stableExecute = useCallback(\n (input: TInput): Promise<TOutput> => executeRef.current(input),\n []\n );\n\n /**\n * Resets the execution state to initial values.\n */\n const reset = useCallback(() => {\n setState({\n isExecuting: false,\n lastResult: null,\n error: null,\n executionCount: 0,\n });\n }, []);\n\n useEffect(() => {\n if (typeof window === 'undefined' || !window.navigator?.modelContext) {\n console.warn(\n `[useWebMCP] window.navigator.modelContext is not available. Tool \"${name}\" will not be registered.`\n );\n return;\n }\n\n /**\n * Handles MCP tool execution by running the tool implementation and formatting the response.\n *\n * @param input - The input parameters from the MCP client\n * @returns CallToolResult with text content and optional structuredContent\n */\n const mcpHandler = async (input: unknown): Promise<CallToolResult> => {\n try {\n const result = await Reflect.apply(executeRef.current, undefined, [input]);\n const formattedOutput = formatOutputRef.current(result);\n\n const response: CallToolResult = {\n content: [\n {\n type: 'text',\n text: formattedOutput,\n },\n ],\n };\n\n if (isObjectOutputSchema(outputSchema)) {\n const structuredContent = toStructuredContent(result);\n if (!structuredContent) {\n throw new Error(\n `Tool \"${name}\" outputSchema requires the tool implementation to return a JSON object result`\n );\n }\n response.structuredContent = structuredContent;\n }\n\n return response;\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n\n return {\n content: [\n {\n type: 'text',\n text: `Error: ${errorMessage}`,\n },\n ],\n isError: true,\n };\n }\n };\n\n const ownerToken = Symbol(name);\n const modelContext = window.navigator.modelContext;\n const resolvedInputSchema = toRegisteredInputSchema(inputSchema);\n const resolvedOutputSchema = toRegisteredOutputSchema(outputSchema);\n const toolDescriptor: ToolDescriptor = {\n name,\n description,\n ...(resolvedInputSchema && { inputSchema: resolvedInputSchema }),\n ...(resolvedOutputSchema && { outputSchema: resolvedOutputSchema }),\n ...(annotations && { annotations }),\n execute: mcpHandler,\n };\n\n const controller = registerToolWithCleanup(modelContext, toolDescriptor);\n TOOL_OWNER_BY_NAME.set(name, ownerToken);\n\n return () => {\n const currentOwner = TOOL_OWNER_BY_NAME.get(name);\n if (currentOwner !== ownerToken) {\n return;\n }\n\n TOOL_OWNER_BY_NAME.delete(name);\n controller.abort();\n };\n // Spread operator in dependencies: Allows users to provide additional dependencies\n // via the `deps` parameter. While unconventional, this pattern is intentional to support\n // dynamic dependency injection. The spread is safe because deps is validated and warned\n // about non-primitive values earlier in this hook.\n }, [name, description, inputSchema, outputSchema, annotations, ...(deps ?? [])]);\n\n return {\n state,\n execute: stableExecute,\n reset,\n };\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,16 +1,16 @@
1
1
  {
2
2
  "name": "usewebmcp",
3
- "version": "2.1.0",
3
+ "version": "2.3.0",
4
4
  "description": "Standalone React hooks for strict core WebMCP tool registration with navigator.modelContext",
5
5
  "keywords": [
6
+ "ai",
7
+ "assistant",
8
+ "browser",
9
+ "hooks",
6
10
  "mcp",
7
11
  "model-context-protocol",
8
12
  "react",
9
- "hooks",
10
13
  "react-hooks",
11
- "browser",
12
- "ai",
13
- "assistant",
14
14
  "tools",
15
15
  "usewebmcp"
16
16
  ],
@@ -18,61 +18,59 @@
18
18
  "bugs": {
19
19
  "url": "https://github.com/WebMCP-org/npm-packages/issues"
20
20
  },
21
+ "license": "MIT",
22
+ "author": "WebMCP Team",
21
23
  "repository": {
22
24
  "type": "git",
23
25
  "url": "git+https://github.com/WebMCP-org/npm-packages.git",
24
26
  "directory": "packages/usewebmcp"
25
27
  },
26
- "license": "MIT",
27
- "author": "WebMCP Team",
28
+ "files": [
29
+ "dist"
30
+ ],
28
31
  "type": "module",
32
+ "main": "./dist/index.js",
29
33
  "exports": {
30
34
  ".": {
31
35
  "types": "./dist/index.d.ts",
32
36
  "import": "./dist/index.js"
33
37
  }
34
38
  },
35
- "main": "./dist/index.js",
36
- "files": [
37
- "dist"
38
- ],
39
+ "publishConfig": {
40
+ "access": "public",
41
+ "registry": "https://registry.npmjs.org/"
42
+ },
39
43
  "dependencies": {
40
- "@mcp-b/webmcp-polyfill": "2.1.0",
41
- "@mcp-b/webmcp-types": "2.1.0"
44
+ "@mcp-b/webmcp-polyfill": "2.3.0",
45
+ "@mcp-b/webmcp-types": "2.3.0"
42
46
  },
43
47
  "devDependencies": {
44
48
  "@types/node": "22.17.2",
45
49
  "@types/react": "^19.2.9",
46
- "@vitest/browser": "^4.0.18",
47
- "@vitest/browser-playwright": "^4.0.18",
48
- "@vitest/coverage-v8": "^4.0.18",
50
+ "@vitest/coverage-v8": "^4.1.0",
49
51
  "playwright": "^1.58.0",
50
52
  "react": "^19.1.0",
51
53
  "react-dom": "^19.1.0",
52
- "tsdown": "^0.15.10",
53
54
  "typescript": "^5.8.3",
54
- "vitest": "^4.0.18",
55
+ "vite-plus": "latest",
56
+ "vitest": "npm:@voidzero-dev/vite-plus-test@latest",
55
57
  "vitest-browser-react": "^2.0.4",
56
- "@mcp-b/global": "2.1.0"
58
+ "@mcp-b/global": "2.3.0"
57
59
  },
58
60
  "peerDependencies": {
59
61
  "react": "^17.0.0 || ^18.0.0 || ^19.0.0"
60
62
  },
61
- "publishConfig": {
62
- "access": "public",
63
- "registry": "https://registry.npmjs.org/"
64
- },
65
63
  "scripts": {
66
- "build": "tsdown",
67
- "build:prod": "NODE_ENV=prod tsdown",
68
- "check": "biome check --write .",
69
- "clean": "rm -rf dist .turbo",
70
- "format": "biome format --write .",
71
- "lint": "biome lint --write .",
64
+ "build": "vp pack",
65
+ "build:prod": "NODE_ENV=prod vp pack",
66
+ "check": "vp check --fix",
67
+ "clean": "rm -rf dist",
68
+ "format": "vp fmt --write",
69
+ "lint": "vp lint --fix",
72
70
  "publish:dry": "pnpm publish --access public --dry-run",
73
71
  "publish:npm": "pnpm publish --access public",
74
- "test": "vitest run",
75
- "test:watch": "vitest",
76
- "typecheck": "tsc --noEmit && tsc -p tsconfig.strict-null-checks-false.json --noEmit && vitest run --typecheck --silent"
72
+ "test": "vp test run",
73
+ "test:watch": "vp test",
74
+ "typecheck": "tsc --noEmit && tsc -p tsconfig.strict-null-checks-false.json --noEmit && vp test run --typecheck --silent"
77
75
  }
78
76
  }