@mcp-b/react-webmcp 0.3.0-beta.0 → 1.0.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
@@ -194,11 +194,16 @@ Main hook for registering MCP tools with full control over behavior and state.
194
194
 
195
195
  ```tsx
196
196
  function useWebMCP<
197
- TInputSchema extends Record<string, z.ZodTypeAny>,
198
- TOutput = string
199
- >(config: WebMCPConfig<TInputSchema, TOutput>): WebMCPReturn<TOutput>
197
+ TInputSchema extends Record<string, z.ZodTypeAny> = Record<string, never>,
198
+ TOutputSchema extends Record<string, z.ZodTypeAny> = Record<string, never>
199
+ >(
200
+ config: WebMCPConfig<TInputSchema, TOutputSchema>,
201
+ deps?: DependencyList
202
+ ): WebMCPReturn<TOutputSchema>
200
203
  ```
201
204
 
205
+ `InferOutput<TOutputSchema>` is the output type inferred from `outputSchema`.
206
+
202
207
  #### Configuration Options
203
208
 
204
209
  | Option | Type | Required | Description |
@@ -208,22 +213,59 @@ function useWebMCP<
208
213
  | `inputSchema` | `Record<string, ZodType>` | - | Input validation using Zod schemas |
209
214
  | `outputSchema` | `Record<string, ZodType>` | - | Output schema for structured responses (recommended) |
210
215
  | `annotations` | `ToolAnnotations` | - | Metadata hints for the AI |
211
- | `elicitation` | `ElicitationConfig` | - | User confirmation settings |
212
216
  | `handler` | `(input) => Promise<TOutput>` | ✓ | Function that executes the tool |
213
217
  | `formatOutput` | `(output) => string` | - | Custom output formatter |
218
+ | `onSuccess` | `(result, input) => void` | - | Success callback |
214
219
  | `onError` | `(error, input) => void` | - | Error handler callback |
215
220
 
221
+ #### Memoization and `deps` (important)
222
+
223
+ `useWebMCP` uses reference equality to decide when to re-register a tool. Inline
224
+ objects/arrays/functions can cause constant re-registration.
225
+
226
+ Bad:
227
+ ```tsx
228
+ useWebMCP({
229
+ name: 'counter',
230
+ description: `Count: ${count}`,
231
+ outputSchema: { count: z.number() },
232
+ handler: async () => ({ count }),
233
+ });
234
+ ```
235
+
236
+ Good:
237
+ ```tsx
238
+ const OUTPUT_SCHEMA = { count: z.number() };
239
+ const description = useMemo(() => `Count: ${count}`, [count]);
240
+
241
+ useWebMCP(
242
+ {
243
+ name: 'counter',
244
+ description,
245
+ outputSchema: OUTPUT_SCHEMA,
246
+ handler: async () => ({ count }),
247
+ },
248
+ [count]
249
+ );
250
+ ```
251
+
252
+ `deps` behaves like `useEffect` dependencies (reference comparison). Prefer
253
+ primitive values or memoized objects to avoid unnecessary re-registrations.
254
+
255
+ `handler` is stored in a ref to avoid re-registration when it changes. If you
256
+ memoize `handler` with stale dependencies, you'll still capture stale values.
257
+
216
258
  #### Return Value
217
259
 
218
260
  ```tsx
219
- interface WebMCPReturn<TOutput> {
261
+ interface WebMCPReturn<TOutputSchema> {
220
262
  state: {
221
263
  isExecuting: boolean; // Currently running
222
- lastResult: TOutput | null; // Last successful result
264
+ lastResult: InferOutput<TOutputSchema> | null; // Last successful result
223
265
  error: Error | null; // Last error
224
266
  executionCount: number; // Total executions
225
267
  };
226
- execute: (input: unknown) => Promise<TOutput>; // Manual execution
268
+ execute: (input: unknown) => Promise<InferOutput<TOutputSchema>>; // Manual execution
227
269
  reset: () => void; // Reset state
228
270
  }
229
271
  ```
@@ -237,7 +279,7 @@ function useWebMCPContext<T>(
237
279
  name: string,
238
280
  description: string,
239
281
  getValue: () => T
240
- ): WebMCPReturn<T>
282
+ ): WebMCPReturn
241
283
  ```
242
284
 
243
285
  ---
package/dist/index.d.ts CHANGED
@@ -1,11 +1,7 @@
1
1
  import { ElicitationFormParams, ElicitationParams, ElicitationParams as ElicitationParams$1, ElicitationResult, ElicitationResult as ElicitationResult$1, ElicitationUrlParams, ModelContext as ModelContextProtocol, PromptDescriptor, ResourceDescriptor, SamplingRequestParams, SamplingRequestParams as SamplingRequestParams$1, SamplingResult, SamplingResult as SamplingResult$1, ToolDescriptor } from "@mcp-b/global";
2
2
  import { DependencyList, ReactElement, ReactNode } from "react";
3
- import { z } from "zod";
4
- import { CallToolResult, Resource, Resource as Resource$1, ServerCapabilities, ServerCapabilities as ServerCapabilities$1, Tool, Tool as Tool$1 } from "@modelcontextprotocol/sdk/types.js";
5
- import { PromptMessage, ResourceContents, ToolAnnotations } from "@mcp-b/webmcp-ts-sdk";
6
- import { Client } from "@modelcontextprotocol/sdk/client/index.js";
7
- import { RequestOptions } from "@modelcontextprotocol/sdk/shared/protocol.js";
8
- import { Transport } from "@modelcontextprotocol/sdk/shared/transport.js";
3
+ import { z } from "zod/v4";
4
+ import { CallToolResult, Client, PromptMessage, RequestOptions, Resource, Resource as Resource$1, ResourceContents, ServerCapabilities, ServerCapabilities as ServerCapabilities$1, Tool, Tool as Tool$1, ToolAnnotations, Transport } from "@mcp-b/webmcp-ts-sdk";
9
5
 
10
6
  //#region src/types.d.ts
11
7
 
@@ -401,12 +397,7 @@ interface WebMCPResourceReturn {
401
397
  *
402
398
  * ## Re-render Optimization
403
399
  *
404
- * This hook is optimized to minimize unnecessary tool re-registrations, which
405
- * trigger JSON-RPC updates. Key optimizations include:
406
- *
407
- * - **Stable schema comparison**: `inputSchema`, `outputSchema`, and `annotations`
408
- * are compared by content (JSON serialization), not reference. This means passing
409
- * a new object with the same content won't trigger re-registration.
400
+ * This hook is optimized to minimize unnecessary tool re-registrations:
410
401
  *
411
402
  * - **Memoized JSON conversion**: Zod-to-JSON schema conversions are memoized to
412
403
  * avoid recomputation on every render.
@@ -414,13 +405,35 @@ interface WebMCPResourceReturn {
414
405
  * - **Ref-based callbacks**: `handler`, `onSuccess`, `onError`, and `formatOutput`
415
406
  * are stored in refs, so changing these functions won't trigger re-registration.
416
407
  *
408
+ * **IMPORTANT**: If `inputSchema`, `outputSchema`, or `annotations` are defined inline
409
+ * or change on every render, the tool will re-register unnecessarily. To avoid this,
410
+ * memoize these values using `useMemo` or define them outside your component:
411
+ *
412
+ * ```tsx
413
+ * // Good: Memoized schema (won't change unless deps change)
414
+ * const outputSchema = useMemo(() => ({
415
+ * count: z.number(),
416
+ * items: z.array(z.string()),
417
+ * }), []);
418
+ *
419
+ * // Good: Static schema defined outside component
420
+ * const OUTPUT_SCHEMA = {
421
+ * count: z.number(),
422
+ * items: z.array(z.string()),
423
+ * };
424
+ *
425
+ * // Bad: Inline schema (creates new object every render)
426
+ * useWebMCP({
427
+ * outputSchema: { count: z.number() }, // Re-registers every render!
428
+ * });
429
+ * ```
430
+ *
417
431
  * **What triggers re-registration:**
418
- * - Changes to `name` or `description` (string comparison)
419
- * - Changes to schema/annotation content (deep comparison via JSON serialization)
432
+ * - Changes to `name` or `description`
433
+ * - Changes to `inputSchema`, `outputSchema`, or `annotations` (reference comparison)
420
434
  * - Changes to any value in the `deps` argument (reference comparison)
421
435
  *
422
436
  * **What does NOT trigger re-registration:**
423
- * - New object references with identical content for schemas/annotations
424
437
  * - Changes to `handler`, `onSuccess`, `onError`, or `formatOutput` functions
425
438
  *
426
439
  * @template TInputSchema - Zod schema object defining input parameter types
@@ -431,8 +444,18 @@ interface WebMCPResourceReturn {
431
444
  * Similar to React's `useEffect` dependencies. When any value changes (by reference),
432
445
  * the tool will be unregistered and re-registered. Prefer primitive values over
433
446
  * objects/arrays to minimize re-registrations.
447
+ *
434
448
  * @returns Object containing execution state and control methods
435
449
  *
450
+ * @remarks
451
+ * The hook uses React refs to store callbacks (`handler`, `onSuccess`, `onError`, `formatOutput`)
452
+ * which prevents re-registration when these functions change. This is a performance optimization
453
+ * that follows the "latest ref" pattern.
454
+ *
455
+ * When `outputSchema` is provided, the MCP response includes both text content and
456
+ * `structuredContent` per the MCP specification. The type system ensures that the handler's
457
+ * return type matches the output schema through Zod's type inference.
458
+ *
436
459
  * @public
437
460
  *
438
461
  * @example
@@ -522,14 +545,15 @@ interface WebMCPResourceReturn {
522
545
  * ```
523
546
  *
524
547
  * @example
525
- * Optimizing deps to minimize re-registrations:
548
+ * Optimizing with memoization and deps:
526
549
  * ```tsx
527
550
  * function OptimizedSites({ sites }: { sites: Site[] }) {
528
- * // BAD: Using the whole array causes re-registration on every render
529
- * // if the array reference changes (e.g., from API response)
530
- * // useWebMCP(config, [sites])
551
+ * // Memoize schema to prevent re-registration on every render
552
+ * const outputSchema = useMemo(() => ({
553
+ * sites: z.array(z.object({ id: z.string(), name: z.string() })),
554
+ * }), []);
531
555
  *
532
- * // GOOD: Use primitive values that only change when content changes
556
+ * // Use primitive values in deps for better control
533
557
  * const siteIds = sites.map(s => s.id).join(',');
534
558
  * const siteCount = sites.length;
535
559
  *
@@ -537,9 +561,7 @@ interface WebMCPResourceReturn {
537
561
  * {
538
562
  * name: 'sites_query',
539
563
  * description: `Query ${siteCount} available sites`,
540
- * outputSchema: {
541
- * sites: z.array(z.object({ id: z.string(), name: z.string() })),
542
- * },
564
+ * outputSchema,
543
565
  * handler: async () => ({
544
566
  * sites: sites.map(s => ({ id: s.id, name: s.name })),
545
567
  * }),
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","names":[],"sources":["../src/types.ts","../src/useWebMCP.ts","../src/useWebMCPContext.ts","../src/useWebMCPPrompt.ts","../src/useWebMCPResource.ts","../src/useElicitationHandler.ts","../src/useSamplingHandler.ts","../src/client/McpClientProvider.tsx"],"sourcesContent":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../src/types.ts","../src/useWebMCP.ts","../src/useWebMCPContext.ts","../src/useWebMCPPrompt.ts","../src/useWebMCPResource.ts","../src/useElicitationHandler.ts","../src/useSamplingHandler.ts","../src/client/McpClientProvider.tsx"],"sourcesContent":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -1,2 +1,2 @@
1
- import{zodToJsonSchema as e}from"@mcp-b/global";import{createContext as t,useCallback as n,useContext as r,useEffect as i,useMemo as a,useRef as o,useState as s}from"react";import{z as c}from"zod";import{ResourceListChangedNotificationSchema as l,ToolListChangedNotificationSchema as u}from"@modelcontextprotocol/sdk/types.js";import{jsx as d}from"react/jsx-runtime";function f(e){return typeof e==`string`?e:JSON.stringify(e,null,2)}function p(e){if(e!==void 0){if(e===null)return`null`;try{return JSON.stringify(e,(e,t)=>typeof t==`object`&&t&&!Array.isArray(t)?Object.keys(t).sort().reduce((e,n)=>(e[n]=t[n],e),{}):t)}catch{return}}}function m(t,r){let{name:l,description:u,inputSchema:d,outputSchema:m,annotations:h,handler:g,formatOutput:_=f,onSuccess:v,onError:y}=t,[b,x]=s({isExecuting:!1,lastResult:null,error:null,executionCount:0}),S=o(g),C=o(v),w=o(y),T=o(_);i(()=>{S.current=g},[g]),i(()=>{C.current=v},[v]),i(()=>{w.current=y},[y]),i(()=>{T.current=_},[_]);let E=a(()=>d?e(d):void 0,[d]),D=a(()=>m?e(m):void 0,[m]),O=o(E),k=o(D),A=o(h);i(()=>{O.current=E},[E]),i(()=>{k.current=D},[D]),i(()=>{A.current=h},[h]);let j=a(()=>p(E),[E]),M=a(()=>p(D),[D]),N=a(()=>p(h),[h]),P=a(()=>d?c.object(d):null,[d]),F=o(P);i(()=>{F.current=P},[P]);let I=n(async e=>{x(e=>({...e,isExecuting:!0,error:null}));try{let t=F.current,n=t?t.parse(e):e,r=await S.current(n);return x(e=>({isExecuting:!1,lastResult:r,error:null,executionCount:e.executionCount+1})),C.current&&C.current(r,e),r}catch(t){let n=t instanceof Error?t:Error(String(t));throw x(e=>({...e,isExecuting:!1,error:n})),w.current&&w.current(n,e),n}},[]),L=n(()=>{x({isExecuting:!1,lastResult:null,error:null,executionCount:0})},[]);return i(()=>{if(typeof window>`u`||!window.navigator?.modelContext){console.warn(`[useWebMCP] window.navigator.modelContext is not available. Tool "${l}" will not be registered.`);return}let e=O.current,t=k.current,n=A.current,r=async(e,n)=>{try{let n=await I(e),r={content:[{type:`text`,text:T.current(n)}]};return t&&(r.structuredContent=n),r}catch(e){return{content:[{type:`text`,text:`Error: ${e instanceof Error?e.message:String(e)}`}],isError:!0}}},i=window.navigator.modelContext.registerTool({name:l,description:u,inputSchema:e||{type:`object`,properties:{}},...t&&{outputSchema:t},...n&&{annotations:n},execute:async e=>await r(e,{})});return()=>{i&&i.unregister()}},[l,u,j,M,N,...r??[]]),{state:b,execute:I,reset:L}}function h(e,t,n){let r=o(n);return r.current=n,m({name:e,description:t,annotations:{title:`Context: ${e}`,readOnlyHint:!0,idempotentHint:!0,destructiveHint:!1,openWorldHint:!1},handler:async()=>r.current(),formatOutput:e=>typeof e==`string`?e:JSON.stringify(e,null,2)})}function g(t){let{name:n,description:r,argsSchema:a,get:c}=t,[l,u]=s(!1),d=o(c);return i(()=>{d.current=c},[c]),i(()=>{if(typeof window>`u`||!window.navigator?.modelContext){console.warn(`[useWebMCPPrompt] window.navigator.modelContext is not available. Prompt "${n}" will not be registered.`);return}let t=a?e(a):void 0,i=async e=>d.current(e),o=window.navigator.modelContext.registerPrompt({name:n,...r!==void 0&&{description:r},...t&&{argsSchema:t},get:i});return console.log(`[useWebMCPPrompt] Registered prompt: ${n}`),u(!0),()=>{o&&(o.unregister(),console.log(`[useWebMCPPrompt] Unregistered prompt: ${n}`),u(!1))}},[n,r,a]),{isRegistered:l}}function _(e){let{uri:t,name:n,description:r,mimeType:a,read:c}=e,[l,u]=s(!1),d=o(c);return i(()=>{d.current=c},[c]),i(()=>{if(typeof window>`u`||!window.navigator?.modelContext){console.warn(`[useWebMCPResource] window.navigator.modelContext is not available. Resource "${t}" will not be registered.`);return}let e=async(e,t)=>d.current(e,t),i=window.navigator.modelContext.registerResource({uri:t,name:n,...r!==void 0&&{description:r},...a!==void 0&&{mimeType:a},read:e});return console.log(`[useWebMCPResource] Registered resource: ${t}`),u(!0),()=>{i&&(i.unregister(),console.log(`[useWebMCPResource] Unregistered resource: ${t}`),u(!1))}},[t,n,r,a]),{isRegistered:l}}function v(e={}){let{onSuccess:t,onError:r}=e,[i,a]=s({isLoading:!1,result:null,error:null,requestCount:0}),o=n(()=>{a({isLoading:!1,result:null,error:null,requestCount:0})},[]);return{state:i,elicitInput:n(async e=>{if(typeof window>`u`||!window.navigator?.modelContext)throw Error(`navigator.modelContext is not available`);a(e=>({...e,isLoading:!0,error:null}));try{let n=await window.navigator.modelContext.elicitInput(e);return a(e=>({isLoading:!1,result:n,error:null,requestCount:e.requestCount+1})),t?.(n),n}catch(e){let t=e instanceof Error?e:Error(String(e));throw a(e=>({...e,isLoading:!1,error:t})),r?.(t),t}},[t,r]),reset:o}}function y(e={}){let{onSuccess:t,onError:r}=e,[i,a]=s({isLoading:!1,result:null,error:null,requestCount:0}),o=n(()=>{a({isLoading:!1,result:null,error:null,requestCount:0})},[]);return{state:i,createMessage:n(async e=>{if(typeof window>`u`||!window.navigator?.modelContext)throw Error(`navigator.modelContext is not available`);a(e=>({...e,isLoading:!0,error:null}));try{let n=await window.navigator.modelContext.createMessage(e);return a(e=>({isLoading:!1,result:n,error:null,requestCount:e.requestCount+1})),t?.(n),n}catch(e){let t=e instanceof Error?e:Error(String(e));throw a(e=>({...e,isLoading:!1,error:t})),r?.(t),t}},[t,r]),reset:o}}const b=t(null);function x({children:e,client:t,transport:r,opts:a={}}){let[c,f]=s([]),[p,m]=s([]),[h,g]=s(!1),[_,v]=s(null),[y,x]=s(!1),[S,C]=s(null),w=o(`disconnected`),T=n(async()=>{if(t){if(!t.getServerCapabilities()?.resources){f([]);return}try{f((await t.listResources()).resources)}catch(e){throw console.error(`Error fetching resources:`,e),e}}},[t]),E=n(async()=>{if(t){if(!t.getServerCapabilities()?.tools){m([]);return}try{m((await t.listTools()).tools)}catch(e){throw console.error(`Error fetching tools:`,e),e}}},[t]),D=n(async()=>{if(!t||!r)throw Error(`Client or transport not available`);if(w.current===`disconnected`){w.current=`connecting`,g(!0),v(null);try{await t.connect(r,a);let e=t.getServerCapabilities();x(!0),C(e||null),w.current=`connected`,await Promise.all([T(),E()])}catch(e){let t=e instanceof Error?e:Error(String(e));throw w.current=`disconnected`,v(t),t}finally{g(!1)}}},[t,r,a,T,E]);return i(()=>{if(!y||!t)return;let e=t.getServerCapabilities();return e?.resources?.listChanged&&t.setNotificationHandler(l,()=>{T().catch(console.error)}),e?.tools?.listChanged&&t.setNotificationHandler(u,()=>{E().catch(console.error)}),Promise.all([T(),E()]).catch(console.error),()=>{e?.resources?.listChanged&&t.removeNotificationHandler(`notifications/resources/list_changed`),e?.tools?.listChanged&&t.removeNotificationHandler(`notifications/tools/list_changed`)}},[t,y,T,E]),i(()=>(D().catch(e=>{console.error(`Failed to connect MCP client:`,e)}),()=>{w.current=`disconnected`,x(!1)}),[t,r]),d(b.Provider,{value:{client:t,tools:p,resources:c,isConnected:y,isLoading:h,error:_,capabilities:S,reconnect:D},children:e})}function S(){let e=r(b);if(!e)throw Error(`useMcpClient must be used within an McpClientProvider`);return e}export{x as McpClientProvider,v as useElicitation,v as useElicitationHandler,S as useMcpClient,y as useSampling,y as useSamplingHandler,m as useWebMCP,h as useWebMCPContext,g as useWebMCPPrompt,_ as useWebMCPResource};
1
+ import{zodToJsonSchema as e}from"@mcp-b/global";import{createContext as t,useCallback as n,useContext as r,useEffect as i,useMemo as a,useRef as o,useState as s}from"react";import{z as c}from"zod/v4";import{ResourceListChangedNotificationSchema as l,ToolListChangedNotificationSchema as u}from"@mcp-b/webmcp-ts-sdk";import{jsx as d}from"react/jsx-runtime";function f(e){return typeof e==`string`?e:JSON.stringify(e,null,2)}function p(t,r){let{name:l,description:u,inputSchema:d,outputSchema:p,annotations:m,handler:h,formatOutput:g=f,onSuccess:_,onError:v}=t,[y,b]=s({isExecuting:!1,lastResult:null,error:null,executionCount:0}),x=o(h),S=o(_),C=o(v),w=o(g),T=o(!0),E=o(new Set),D=o({inputSchema:d,outputSchema:p,annotations:m,description:u,deps:r}),O=(()=>{let e=globalThis.process?.env?.NODE_ENV;return e===void 0?!1:e!==`production`})();i(()=>{x.current=h,S.current=_,C.current=v,w.current=g},[h,_,v,g]),i(()=>(T.current=!0,()=>{T.current=!1}),[]),i(()=>{if(!O){D.current={inputSchema:d,outputSchema:p,annotations:m,description:u,deps:r};return}let e=(e,t)=>{E.current.has(e)||(console.warn(`[useWebMCP] ${t}`),E.current.add(e))},t=D.current;d&&t.inputSchema&&t.inputSchema!==d&&e(`inputSchema`,`Tool "${l}" inputSchema reference changed; memoize or define it outside the component to avoid re-registration.`),p&&t.outputSchema&&t.outputSchema!==p&&e(`outputSchema`,`Tool "${l}" outputSchema reference changed; memoize or define it outside the component to avoid re-registration.`),m&&t.annotations&&t.annotations!==m&&e(`annotations`,`Tool "${l}" annotations reference changed; memoize or define it outside the component to avoid re-registration.`),u!==t.description&&e(`description`,`Tool "${l}" description changed; this re-registers the tool. Memoize the description if it does not need to update.`),r?.some(e=>typeof e==`object`&&!!e||typeof e==`function`)&&e(`deps`,`Tool "${l}" deps contains non-primitive values; prefer primitives or memoize objects/functions to reduce re-registration.`),D.current={inputSchema:d,outputSchema:p,annotations:m,description:u,deps:r}},[m,r,u,d,O,l,p]);let k=d?JSON.stringify(e(d)):null,A=p?JSON.stringify(e(p)):null,j=m?JSON.stringify(m):null,M=a(()=>k?JSON.parse(k):void 0,[k]),N=a(()=>A?JSON.parse(A):void 0,[A]),P=a(()=>j?JSON.parse(j):void 0,[j]),F=a(()=>d?c.object(d):null,[d]),I=n(async e=>{b(e=>({...e,isExecuting:!0,error:null}));try{let t=F?F.parse(e):e,n=await x.current(t);return T.current&&b(e=>({isExecuting:!1,lastResult:n,error:null,executionCount:e.executionCount+1})),S.current&&S.current(n,e),n}catch(t){let n=t instanceof Error?t:Error(String(t));throw T.current&&b(e=>({...e,isExecuting:!1,error:n})),C.current&&C.current(n,e),n}},[F]),L=n(()=>{b({isExecuting:!1,lastResult:null,error:null,executionCount:0})},[]);return i(()=>{if(typeof window>`u`||!window.navigator?.modelContext){console.warn(`[useWebMCP] window.navigator.modelContext is not available. Tool "${l}" will not be registered.`);return}let e=async e=>{try{let t=await I(e),n={content:[{type:`text`,text:w.current(t)}]};return N&&(n.structuredContent=t),n}catch(e){return{content:[{type:`text`,text:`Error: ${e instanceof Error?e.message:String(e)}`}],isError:!0}}},t=window.navigator.modelContext?.registerTool({name:l,description:u,inputSchema:M||{type:`object`,properties:{}},...N&&{outputSchema:N},...P&&{annotations:P},execute:e});return()=>{t?.unregister()}},[l,u,M,N,P,...r??[],I]),{state:y,execute:I,reset:L}}function m(e,t,n){let r=o(n);return r.current=n,p({name:e,description:t,annotations:{title:`Context: ${e}`,readOnlyHint:!0,idempotentHint:!0,destructiveHint:!1,openWorldHint:!1},handler:async()=>r.current(),formatOutput:e=>typeof e==`string`?e:JSON.stringify(e,null,2)})}function h(t){let{name:n,description:r,argsSchema:c,get:l}=t,[u,d]=s(!1),f=o(l),p=(()=>{let e=globalThis.process?.env?.NODE_ENV;return e===void 0?!1:e!==`production`})(),m=a(()=>c?e(c):void 0,[c]);return i(()=>{f.current=l},[l]),i(()=>{if(typeof window>`u`||!window.navigator?.modelContext){p&&console.warn(`[useWebMCPPrompt] window.navigator.modelContext is not available. Prompt "${n}" will not be registered.`);return}let e=async e=>f.current(e),t=window.navigator.modelContext?.registerPrompt({name:n,...r!==void 0&&{description:r},...m&&{argsSchema:m},get:e});return p&&console.log(`[useWebMCPPrompt] Registered prompt: ${n}`),d(!0),()=>{t&&(t.unregister(),p&&console.log(`[useWebMCPPrompt] Unregistered prompt: ${n}`),d(!1))}},[n,r,m,p]),{isRegistered:u}}function g(e){let{uri:t,name:n,description:r,mimeType:a,read:c}=e,[l,u]=s(!1),d=o(c),f=(()=>{let e=globalThis.process?.env?.NODE_ENV;return e===void 0?!1:e!==`production`})();return i(()=>{d.current=c},[c]),i(()=>{if(typeof window>`u`||!window.navigator?.modelContext){f&&console.warn(`[useWebMCPResource] window.navigator.modelContext is not available. Resource "${t}" will not be registered.`);return}let e=async(e,t)=>d.current(e,t),i=window.navigator.modelContext?.registerResource({uri:t,name:n,...r!==void 0&&{description:r},...a!==void 0&&{mimeType:a},read:e});return f&&console.log(`[useWebMCPResource] Registered resource: ${t}`),u(!0),()=>{i&&(i.unregister(),f&&console.log(`[useWebMCPResource] Unregistered resource: ${t}`),u(!1))}},[t,n,r,a,f]),{isRegistered:l}}function _(e={}){let{onSuccess:t,onError:r}=e,[i,a]=s({isLoading:!1,result:null,error:null,requestCount:0}),o=n(()=>{a({isLoading:!1,result:null,error:null,requestCount:0})},[]);return{state:i,elicitInput:n(async e=>{if(typeof window>`u`||!window.navigator?.modelContext)throw Error(`navigator.modelContext is not available`);a(e=>({...e,isLoading:!0,error:null}));try{let n=await window.navigator.modelContext.elicitInput(e);return a(e=>({isLoading:!1,result:n,error:null,requestCount:e.requestCount+1})),t?.(n),n}catch(e){let t=e instanceof Error?e:Error(String(e));throw a(e=>({...e,isLoading:!1,error:t})),r?.(t),t}},[t,r]),reset:o}}function v(e={}){let{onSuccess:t,onError:r}=e,[i,a]=s({isLoading:!1,result:null,error:null,requestCount:0}),o=n(()=>{a({isLoading:!1,result:null,error:null,requestCount:0})},[]);return{state:i,createMessage:n(async e=>{if(typeof window>`u`||!window.navigator?.modelContext)throw Error(`navigator.modelContext is not available`);a(e=>({...e,isLoading:!0,error:null}));try{let n=await window.navigator.modelContext.createMessage(e);return a(e=>({isLoading:!1,result:n,error:null,requestCount:e.requestCount+1})),t?.(n),n}catch(e){let t=e instanceof Error?e:Error(String(e));throw a(e=>({...e,isLoading:!1,error:t})),r?.(t),t}},[t,r]),reset:o}}const y=t(null);function b({children:e,client:t,transport:r,opts:a={}}){let[c,f]=s([]),[p,m]=s([]),[h,g]=s(!1),[_,v]=s(null),[b,x]=s(!1),[S,C]=s(null),w=o(`disconnected`),T=n(async()=>{if(t){if(!t.getServerCapabilities()?.resources){f([]);return}try{f((await t.listResources()).resources)}catch(e){throw console.error(`Error fetching resources:`,e),e}}},[t]),E=n(async()=>{if(t){if(!t.getServerCapabilities()?.tools){m([]);return}try{m((await t.listTools()).tools)}catch(e){throw console.error(`Error fetching tools:`,e),e}}},[t]),D=n(async()=>{if(!t||!r)throw Error(`Client or transport not available`);if(w.current===`disconnected`){w.current=`connecting`,g(!0),v(null);try{await t.connect(r,a);let e=t.getServerCapabilities();x(!0),C(e||null),w.current=`connected`,await Promise.all([T(),E()])}catch(e){let t=e instanceof Error?e:Error(String(e));throw w.current=`disconnected`,v(t),t}finally{g(!1)}}},[t,r,a,T,E]);return i(()=>{if(!b||!t)return;let e=t.getServerCapabilities();return e?.resources?.listChanged&&t.setNotificationHandler(l,()=>{T().catch(console.error)}),e?.tools?.listChanged&&t.setNotificationHandler(u,()=>{E().catch(console.error)}),Promise.all([T(),E()]).catch(console.error),()=>{e?.resources?.listChanged&&t.removeNotificationHandler(`notifications/resources/list_changed`),e?.tools?.listChanged&&t.removeNotificationHandler(`notifications/tools/list_changed`)}},[t,b,T,E]),i(()=>(D().catch(e=>{console.error(`Failed to connect MCP client:`,e)}),()=>{w.current=`disconnected`,x(!1)}),[t,r]),d(y.Provider,{value:{client:t,tools:p,resources:c,isConnected:b,isLoading:h,error:_,capabilities:S,reconnect:D},children:e})}function x(){let e=r(y);if(!e)throw Error(`useMcpClient must be used within an McpClientProvider`);return e}export{b as McpClientProvider,_ as useElicitation,_ as useElicitationHandler,x as useMcpClient,v as useSampling,v as useSamplingHandler,p as useWebMCP,m as useWebMCPContext,h as useWebMCPPrompt,g as useWebMCPResource};
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","../src/useWebMCPContext.ts","../src/useWebMCPPrompt.ts","../src/useWebMCPResource.ts","../src/useElicitationHandler.ts","../src/useSamplingHandler.ts","../src/client/McpClientProvider.tsx"],"sourcesContent":["import type { InputSchema } from '@mcp-b/global';\nimport { zodToJsonSchema } from '@mcp-b/global';\nimport type { CallToolResult } from '@modelcontextprotocol/sdk/types.js';\nimport type { DependencyList } from 'react';\nimport { useCallback, useEffect, useMemo, useRef, useState } from 'react';\nimport { z } from 'zod';\nimport type { InferOutput, ToolExecutionState, WebMCPConfig, WebMCPReturn } from './types.js';\n\n/**\n * Default output formatter that converts values to formatted JSON strings.\n *\n * @internal\n * @param output - The value to format\n * @returns Formatted string representation\n */\nfunction defaultFormatOutput(output: unknown): string {\n if (typeof output === 'string') {\n return output;\n }\n return JSON.stringify(output, null, 2);\n}\n\n/**\n * Converts a value to a stable JSON string for dependency comparison.\n * Returns undefined for undefined values to avoid unnecessary re-registrations.\n *\n * @internal\n * @param value - The value to serialize\n * @returns Stable JSON string or undefined\n */\nfunction stableStringify(value: unknown): string | undefined {\n if (value === undefined) {\n return undefined;\n }\n if (value === null) {\n return 'null';\n }\n try {\n // Sort object keys for consistent serialization\n return JSON.stringify(value, (_, v) => {\n if (v !== null && typeof v === 'object' && !Array.isArray(v)) {\n return Object.keys(v)\n .sort()\n .reduce(\n (sorted, key) => {\n sorted[key] = v[key];\n return sorted;\n },\n {} as Record<string, unknown>\n );\n }\n return v;\n });\n } catch {\n // Non-serializable values (functions, circular refs, symbols) cannot be\n // reliably compared. Return undefined to force re-registration on every\n // render when such values are present. This is a safe fallback since\n // JSON schemas from Zod should always be serializable.\n return undefined;\n }\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 * - Validates input using Zod schemas\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: { userId: z.string() },\n * outputSchema: {\n * id: z.string(),\n * name: z.string(),\n * email: z.string().email(),\n * },\n * handler: 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, which\n * trigger JSON-RPC updates. Key optimizations include:\n *\n * - **Stable schema comparison**: `inputSchema`, `outputSchema`, and `annotations`\n * are compared by content (JSON serialization), not reference. This means passing\n * a new object with the same content won't trigger re-registration.\n *\n * - **Memoized JSON conversion**: Zod-to-JSON schema conversions are memoized to\n * avoid recomputation on every render.\n *\n * - **Ref-based callbacks**: `handler`, `onSuccess`, `onError`, and `formatOutput`\n * are stored in refs, so changing these functions won't trigger re-registration.\n *\n * **What triggers re-registration:**\n * - Changes to `name` or `description` (string comparison)\n * - Changes to schema/annotation content (deep comparison via JSON serialization)\n * - Changes to any value in the `deps` argument (reference comparison)\n *\n * **What does NOT trigger re-registration:**\n * - New object references with identical content for schemas/annotations\n * - Changes to `handler`, `onSuccess`, `onError`, or `formatOutput` functions\n *\n * @template TInputSchema - Zod schema object defining input parameter types\n * @template TOutputSchema - Zod 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 * Similar to React's `useEffect` dependencies. When any value changes (by reference),\n * the tool will be unregistered and re-registered. Prefer primitive values over\n * objects/arrays to minimize re-registrations.\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 * postId: z.string().uuid().describe('The ID of the post to like'),\n * },\n * outputSchema: {\n * success: z.boolean().describe('Whether the like was successful'),\n * likeCount: z.number().describe('Updated like count'),\n * },\n * handler: async ({ postId }) => {\n * const result = await api.posts.like(postId);\n * return { success: true, likeCount: result.likes };\n * },\n * });\n *\n * // likeTool.state.lastResult is typed as { success: boolean; likeCount: number } | null\n * if (likeTool.state.isExecuting) {\n * return <Spinner />;\n * }\n *\n * return <div>Likes: {likeTool.state.lastResult?.likeCount ?? 0}</div>;\n * }\n * ```\n *\n * @example\n * Tool with annotations and callbacks:\n * ```tsx\n * const deleteTool = useWebMCP({\n * name: 'posts_delete',\n * description: 'Delete a post permanently',\n * inputSchema: {\n * postId: z.string().uuid(),\n * },\n * outputSchema: {\n * deleted: z.boolean(),\n * deletedAt: z.string().describe('ISO timestamp of deletion'),\n * },\n * annotations: {\n * destructiveHint: true,\n * idempotentHint: false,\n * },\n * handler: async ({ postId }) => {\n * await api.posts.delete(postId);\n * return { deleted: true, deletedAt: new Date().toISOString() };\n * },\n * onSuccess: () => {\n * navigate('/posts');\n * toast.success('Post deleted');\n * },\n * onError: (error) => {\n * toast.error(`Failed to delete: ${error.message}`);\n * },\n * });\n * ```\n *\n * @example\n * Tool with deps for automatic re-registration:\n * ```tsx\n * function SitesManager({ sites }: { sites: Site[] }) {\n * // Without deps, you'd need getter functions like: getSiteCount: () => sites.length\n * // With deps, values can be used directly in description and handler\n * const sitesTool = useWebMCP(\n * {\n * name: 'sites_query',\n * description: `Query available sites. Current count: ${sites.length}`,\n * outputSchema: {\n * count: z.number(),\n * sites: z.array(z.object({ id: z.string(), name: z.string() })),\n * },\n * handler: async () => ({\n * count: sites.length,\n * sites: sites.map(s => ({ id: s.id, name: s.name })),\n * }),\n * },\n * [sites] // Re-register tool when sites array changes (by reference)\n * );\n *\n * return <SitesList sites={sites} />;\n * }\n * ```\n *\n * @example\n * Optimizing deps to minimize re-registrations:\n * ```tsx\n * function OptimizedSites({ sites }: { sites: Site[] }) {\n * // BAD: Using the whole array causes re-registration on every render\n * // if the array reference changes (e.g., from API response)\n * // useWebMCP(config, [sites])\n *\n * // GOOD: Use primitive values that only change when content changes\n * const siteIds = sites.map(s => s.id).join(',');\n * const siteCount = sites.length;\n *\n * const sitesTool = useWebMCP(\n * {\n * name: 'sites_query',\n * description: `Query ${siteCount} available sites`,\n * outputSchema: {\n * sites: z.array(z.object({ id: z.string(), name: z.string() })),\n * },\n * handler: async () => ({\n * sites: sites.map(s => ({ id: s.id, name: s.name })),\n * }),\n * },\n * [siteIds, siteCount] // Only re-register when IDs or count actually change\n * );\n *\n * return <SitesList sites={sites} />;\n * }\n * ```\n */\nexport function useWebMCP<\n TInputSchema extends Record<string, z.ZodTypeAny> = Record<string, never>,\n TOutputSchema extends Record<string, z.ZodTypeAny> = Record<string, never>,\n>(\n config: WebMCPConfig<TInputSchema, TOutputSchema>,\n deps?: DependencyList\n): WebMCPReturn<TOutputSchema> {\n /** Inferred output type from the schema */\n type TOutput = InferOutput<TOutputSchema>;\n const {\n name,\n description,\n inputSchema,\n outputSchema,\n annotations,\n handler,\n formatOutput = defaultFormatOutput,\n onSuccess,\n onError,\n } = config;\n\n const [state, setState] = useState<ToolExecutionState<TOutput>>({\n isExecuting: false,\n lastResult: null,\n error: null,\n executionCount: 0,\n });\n\n const handlerRef = useRef(handler);\n const onSuccessRef = useRef(onSuccess);\n const onErrorRef = useRef(onError);\n const formatOutputRef = useRef(formatOutput);\n\n useEffect(() => {\n handlerRef.current = handler;\n }, [handler]);\n\n useEffect(() => {\n onSuccessRef.current = onSuccess;\n }, [onSuccess]);\n\n useEffect(() => {\n onErrorRef.current = onError;\n }, [onError]);\n\n useEffect(() => {\n formatOutputRef.current = formatOutput;\n }, [formatOutput]);\n\n // Memoize JSON schemas to avoid recomputation on every render\n // These are derived from Zod schemas and used for MCP registration\n const inputJsonSchema = useMemo(\n () => (inputSchema ? zodToJsonSchema(inputSchema) : undefined),\n [inputSchema]\n );\n const outputJsonSchema = useMemo(\n () => (outputSchema ? zodToJsonSchema(outputSchema) : undefined),\n [outputSchema]\n );\n\n // Store schemas in refs so the effect can access them without direct dependencies\n // This allows us to use stable serialized strings for dependency comparison\n const inputJsonSchemaRef = useRef(inputJsonSchema);\n const outputJsonSchemaRef = useRef(outputJsonSchema);\n const annotationsRef = useRef(annotations);\n\n useEffect(() => {\n inputJsonSchemaRef.current = inputJsonSchema;\n }, [inputJsonSchema]);\n\n useEffect(() => {\n outputJsonSchemaRef.current = outputJsonSchema;\n }, [outputJsonSchema]);\n\n useEffect(() => {\n annotationsRef.current = annotations;\n }, [annotations]);\n\n // Create stable string representations for dependency comparison\n // This prevents unnecessary re-registrations when object references change\n // but content remains the same\n const inputSchemaKey = useMemo(() => stableStringify(inputJsonSchema), [inputJsonSchema]);\n const outputSchemaKey = useMemo(() => stableStringify(outputJsonSchema), [outputJsonSchema]);\n const annotationsKey = useMemo(() => stableStringify(annotations), [annotations]);\n\n // Memoize validator to prevent recreation on every render\n // This ensures execute callback and registration effect have stable dependencies\n const validator = useMemo(() => (inputSchema ? z.object(inputSchema) : null), [inputSchema]);\n\n // Store validator in ref to avoid execute callback dependency\n const validatorRef = useRef(validator);\n useEffect(() => {\n validatorRef.current = validator;\n }, [validator]);\n\n /**\n * Executes the tool handler with input validation and state management.\n *\n * @param input - The input parameters to validate and pass to the handler\n * @returns Promise resolving to the handler's output\n * @throws Error if validation fails or the handler 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 currentValidator = validatorRef.current;\n const validatedInput = currentValidator ? currentValidator.parse(input) : input;\n const result = await handlerRef.current(validatedInput as never);\n\n setState((prev) => ({\n isExecuting: false,\n lastResult: result,\n error: null,\n executionCount: prev.executionCount + 1,\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 setState((prev) => ({\n ...prev,\n isExecuting: false,\n error: err,\n }));\n\n if (onErrorRef.current) {\n onErrorRef.current(err, input);\n }\n\n throw err;\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 // biome-ignore lint/correctness/useExhaustiveDependencies: Uses refs for latest values while stable keys trigger re-registration; execute is stable; deps is user-controlled\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 // Use refs to get the latest schema values - the stable keys ensure\n // we only re-register when content actually changes\n const currentInputSchema = inputJsonSchemaRef.current;\n const currentOutputSchema = outputJsonSchemaRef.current;\n const currentAnnotations = annotationsRef.current;\n\n const mcpHandler = async (input: unknown, _extra: unknown): Promise<CallToolResult> => {\n try {\n const result = await execute(input);\n const formattedOutput = formatOutputRef.current(result);\n\n // Build the MCP response with text content\n const response: CallToolResult = {\n content: [\n {\n type: 'text',\n text: formattedOutput,\n },\n ],\n };\n\n // When outputSchema is defined, include structuredContent per MCP specification.\n // The type assertion is safe because:\n // 1. outputSchema uses Zod schema, which always produces object types\n // 2. WebMCPConfig constrains handler return type to match outputSchema via InferOutput\n // 3. The MCP SDK's structuredContent type is Record<string, unknown>\n // Therefore, result is always assignable to Record<string, unknown> when outputSchema exists.\n if (currentOutputSchema) {\n response.structuredContent = result as Record<string, unknown>;\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 fallbackInputSchema: InputSchema = {\n type: 'object',\n properties: {},\n };\n\n const registration = window.navigator.modelContext.registerTool({\n name,\n description,\n inputSchema: (currentInputSchema || fallbackInputSchema) as InputSchema,\n ...(currentOutputSchema && { outputSchema: currentOutputSchema as InputSchema }),\n ...(currentAnnotations && { annotations: currentAnnotations }),\n execute: async (args: Record<string, unknown>) => {\n const result = await mcpHandler(args, {});\n return result;\n },\n });\n\n return () => {\n if (registration) {\n registration.unregister();\n }\n };\n // Dependencies use stable string keys for object comparison to prevent\n // unnecessary re-registrations when object references change but content is the same.\n // execute is intentionally omitted - it's stable (empty deps) and uses refs internally.\n // deps is spread to allow user-controlled re-registration triggers.\n }, [name, description, inputSchemaKey, outputSchemaKey, annotationsKey, ...(deps ?? [])]);\n\n return {\n state,\n execute,\n reset,\n };\n}\n","import { useRef } from 'react';\nimport type { WebMCPReturn } from './types.js';\nimport { useWebMCP } from './useWebMCP.js';\n\n/**\n * Convenience hook for exposing read-only context data to AI assistants.\n *\n * This is a simplified wrapper around {@link useWebMCP} specifically designed for\n * context tools that expose data without performing actions. The hook automatically\n * configures appropriate annotations (read-only, idempotent) and handles value\n * serialization.\n *\n * Note: This hook does not use an output schema, so the result will not include\n * `structuredContent` in the MCP response. Use {@link useWebMCP} directly with\n * `outputSchema` if you need structured output for MCP compliance.\n *\n * @template T - The type of context data to expose\n *\n * @param name - Unique identifier for the context tool (e.g., 'context_current_post')\n * @param description - Human-readable description of the context for AI assistants\n * @param getValue - Function that returns the current context value\n * @returns Tool execution state and control methods\n *\n * @public\n *\n * @example\n * Expose current post context:\n * ```tsx\n * function PostDetailPage() {\n * const { postId } = useParams();\n * const { data: post } = useQuery(['post', postId], () => fetchPost(postId));\n *\n * useWebMCPContext(\n * 'context_current_post',\n * 'Get the currently viewed post ID and metadata',\n * () => ({\n * postId,\n * title: post?.title,\n * author: post?.author,\n * tags: post?.tags,\n * createdAt: post?.createdAt,\n * })\n * );\n *\n * return <PostContent post={post} />;\n * }\n * ```\n *\n * @example\n * Expose user session context:\n * ```tsx\n * function AppRoot() {\n * const { user, isAuthenticated } = useAuth();\n *\n * useWebMCPContext(\n * 'context_user_session',\n * 'Get the current user session information',\n * () => ({\n * isAuthenticated,\n * userId: user?.id,\n * email: user?.email,\n * permissions: user?.permissions,\n * })\n * );\n *\n * return <App />;\n * }\n * ```\n */\nexport function useWebMCPContext<T>(\n name: string,\n description: string,\n getValue: () => T\n): WebMCPReturn {\n const getValueRef = useRef(getValue);\n getValueRef.current = getValue;\n\n // Use default generics (no input/output schema) since context tools\n // don't define structured schemas. The handler returns T but it's\n // treated as `unknown` in the return type since no outputSchema is defined.\n return useWebMCP({\n name,\n description,\n annotations: {\n title: `Context: ${name}`,\n readOnlyHint: true,\n idempotentHint: true,\n destructiveHint: false,\n openWorldHint: false,\n },\n handler: async () => {\n return getValueRef.current();\n },\n formatOutput: (output) => {\n if (typeof output === 'string') {\n return output as string;\n }\n return JSON.stringify(output, null, 2);\n },\n });\n}\n","import type { InputSchema } from '@mcp-b/global';\nimport { zodToJsonSchema } from '@mcp-b/global';\nimport { useEffect, useRef, useState } from 'react';\nimport type { z } from 'zod';\nimport type { PromptMessage, WebMCPPromptConfig, WebMCPPromptReturn } from './types.js';\n\n/**\n * React hook for registering Model Context Protocol (MCP) prompts.\n *\n * This hook handles the complete lifecycle of an MCP prompt:\n * - Registers the prompt with `window.navigator.modelContext`\n * - Converts Zod schemas to JSON Schema for argument validation\n * - Automatically unregisters on component unmount\n *\n * @template TArgsSchema - Zod schema object defining argument types\n *\n * @param config - Configuration object for the prompt\n * @returns Object indicating registration status\n *\n * @public\n *\n * @example\n * Simple prompt without arguments:\n * ```tsx\n * function HelpPrompt() {\n * const { isRegistered } = useWebMCPPrompt({\n * name: 'help',\n * description: 'Get help with using the application',\n * get: async () => ({\n * messages: [{\n * role: 'user',\n * content: { type: 'text', text: 'How do I use this application?' }\n * }]\n * }),\n * });\n *\n * return <div>Help prompt {isRegistered ? 'ready' : 'loading'}</div>;\n * }\n * ```\n *\n * @example\n * Prompt with typed arguments:\n * ```tsx\n * function CodeReviewPrompt() {\n * const { isRegistered } = useWebMCPPrompt({\n * name: 'review_code',\n * description: 'Review code for best practices',\n * argsSchema: {\n * code: z.string().describe('The code to review'),\n * language: z.string().optional().describe('Programming language'),\n * },\n * get: async ({ code, language }) => ({\n * messages: [{\n * role: 'user',\n * content: {\n * type: 'text',\n * text: `Please review this ${language ?? ''} code:\\n\\n${code}`\n * }\n * }]\n * }),\n * });\n *\n * return <div>Code review prompt {isRegistered ? 'ready' : 'loading'}</div>;\n * }\n * ```\n */\nexport function useWebMCPPrompt<\n TArgsSchema extends Record<string, z.ZodTypeAny> = Record<string, never>,\n>(config: WebMCPPromptConfig<TArgsSchema>): WebMCPPromptReturn {\n const { name, description, argsSchema, get } = config;\n\n const [isRegistered, setIsRegistered] = useState(false);\n\n const getRef = useRef(get);\n\n useEffect(() => {\n getRef.current = get;\n }, [get]);\n\n useEffect(() => {\n if (typeof window === 'undefined' || !window.navigator?.modelContext) {\n console.warn(\n `[useWebMCPPrompt] window.navigator.modelContext is not available. Prompt \"${name}\" will not be registered.`\n );\n return;\n }\n\n const argsJsonSchema = argsSchema ? zodToJsonSchema(argsSchema) : undefined;\n\n const promptHandler = async (\n args: Record<string, unknown>\n ): Promise<{ messages: PromptMessage[] }> => {\n return getRef.current(args as never);\n };\n\n const registration = window.navigator.modelContext.registerPrompt({\n name,\n ...(description !== undefined && { description }),\n ...(argsJsonSchema && { argsSchema: argsJsonSchema as InputSchema }),\n get: promptHandler,\n });\n\n console.log(`[useWebMCPPrompt] Registered prompt: ${name}`);\n setIsRegistered(true);\n\n return () => {\n if (registration) {\n registration.unregister();\n console.log(`[useWebMCPPrompt] Unregistered prompt: ${name}`);\n setIsRegistered(false);\n }\n };\n }, [name, description, argsSchema]);\n\n return {\n isRegistered,\n };\n}\n","import { useEffect, useRef, useState } from 'react';\nimport type { ResourceContents, WebMCPResourceConfig, WebMCPResourceReturn } from './types.js';\n\n/**\n * React hook for registering Model Context Protocol (MCP) resources.\n *\n * This hook handles the complete lifecycle of an MCP resource:\n * - Registers the resource with `window.navigator.modelContext`\n * - Supports both static URIs and URI templates with parameters\n * - Automatically unregisters on component unmount\n *\n * @param config - Configuration object for the resource\n * @returns Object indicating registration status\n *\n * @public\n *\n * @example\n * Static resource:\n * ```tsx\n * function AppSettingsResource() {\n * const { isRegistered } = useWebMCPResource({\n * uri: 'config://app-settings',\n * name: 'App Settings',\n * description: 'Application configuration',\n * mimeType: 'application/json',\n * read: async (uri) => ({\n * contents: [{\n * uri: uri.href,\n * text: JSON.stringify({ theme: 'dark', language: 'en' })\n * }]\n * }),\n * });\n *\n * return <div>Settings resource {isRegistered ? 'ready' : 'loading'}</div>;\n * }\n * ```\n *\n * @example\n * Dynamic resource with URI template:\n * ```tsx\n * function UserProfileResource() {\n * const { isRegistered } = useWebMCPResource({\n * uri: 'user://{userId}/profile',\n * name: 'User Profile',\n * description: 'User profile data by ID',\n * mimeType: 'application/json',\n * read: async (uri, params) => {\n * const userId = params?.userId ?? '';\n * const profile = await fetchUserProfile(userId);\n * return {\n * contents: [{\n * uri: uri.href,\n * text: JSON.stringify(profile)\n * }]\n * };\n * },\n * });\n *\n * return <div>User profile resource {isRegistered ? 'ready' : 'loading'}</div>;\n * }\n * ```\n */\nexport function useWebMCPResource(config: WebMCPResourceConfig): WebMCPResourceReturn {\n const { uri, name, description, mimeType, read } = config;\n\n const [isRegistered, setIsRegistered] = useState(false);\n\n const readRef = useRef(read);\n\n useEffect(() => {\n readRef.current = read;\n }, [read]);\n\n useEffect(() => {\n if (typeof window === 'undefined' || !window.navigator?.modelContext) {\n console.warn(\n `[useWebMCPResource] window.navigator.modelContext is not available. Resource \"${uri}\" will not be registered.`\n );\n return;\n }\n\n const resourceHandler = async (\n resolvedUri: URL,\n params?: Record<string, string>\n ): Promise<{ contents: ResourceContents[] }> => {\n return readRef.current(resolvedUri, params);\n };\n\n const registration = window.navigator.modelContext.registerResource({\n uri,\n name,\n ...(description !== undefined && { description }),\n ...(mimeType !== undefined && { mimeType }),\n read: resourceHandler,\n });\n\n console.log(`[useWebMCPResource] Registered resource: ${uri}`);\n setIsRegistered(true);\n\n return () => {\n if (registration) {\n registration.unregister();\n console.log(`[useWebMCPResource] Unregistered resource: ${uri}`);\n setIsRegistered(false);\n }\n };\n }, [uri, name, description, mimeType]);\n\n return {\n isRegistered,\n };\n}\n","import type { ElicitationParams, ElicitationResult } from '@mcp-b/global';\nimport { useCallback, useState } from 'react';\n\n/**\n * State for elicitation requests, tracking the current request and results.\n */\nexport interface ElicitationState {\n /** Whether an elicitation request is currently in progress */\n isLoading: boolean;\n /** The last elicitation result received */\n result: ElicitationResult | null;\n /** Any error that occurred during the last request */\n error: Error | null;\n /** Total number of requests made */\n requestCount: number;\n}\n\n/**\n * Configuration options for the useElicitation hook.\n */\nexport interface UseElicitationConfig {\n /**\n * Optional callback invoked when an elicitation request completes successfully.\n */\n onSuccess?: (result: ElicitationResult) => void;\n\n /**\n * Optional callback invoked when an elicitation request fails.\n */\n onError?: (error: Error) => void;\n}\n\n/**\n * Return value from the useElicitation hook.\n */\nexport interface UseElicitationReturn {\n /** Current state of elicitation */\n state: ElicitationState;\n /** Function to request user input from the connected client */\n elicitInput: (params: ElicitationParams) => Promise<ElicitationResult>;\n /** Reset the state */\n reset: () => void;\n}\n\n/**\n * React hook for requesting user input from the connected MCP client.\n *\n * Elicitation allows the server (webpage) to request user input from the\n * connected client. This is useful when the page needs additional information\n * from the user, such as API keys, configuration options, or confirmations.\n *\n * There are two modes:\n * 1. **Form mode**: For non-sensitive data collection using a schema-driven form.\n * 2. **URL mode**: For sensitive data collection via a web URL (API keys, OAuth, etc.).\n *\n * @param config - Optional configuration including callbacks\n * @returns Object containing state and the elicitInput function\n *\n * @example Form elicitation:\n * ```tsx\n * function ConfigForm() {\n * const { state, elicitInput } = useElicitation({\n * onSuccess: (result) => console.log('Got input:', result),\n * onError: (error) => console.error('Elicitation failed:', error),\n * });\n *\n * const handleConfigure = async () => {\n * const result = await elicitInput({\n * message: 'Please provide your configuration',\n * requestedSchema: {\n * type: 'object',\n * properties: {\n * apiKey: { type: 'string', title: 'API Key', description: 'Your API key' },\n * model: { type: 'string', enum: ['gpt-4', 'gpt-3.5'], title: 'Model' }\n * },\n * required: ['apiKey']\n * }\n * });\n *\n * if (result.action === 'accept') {\n * console.log('Config:', result.content);\n * }\n * };\n *\n * return (\n * <button onClick={handleConfigure} disabled={state.isLoading}>\n * Configure\n * </button>\n * );\n * }\n * ```\n *\n * @example URL elicitation (for sensitive data):\n * ```tsx\n * const { elicitInput } = useElicitation();\n *\n * const handleOAuth = async () => {\n * const result = await elicitInput({\n * mode: 'url',\n * message: 'Please authenticate with GitHub',\n * elicitationId: 'github-oauth-123',\n * url: 'https://github.com/login/oauth/authorize?client_id=...'\n * });\n *\n * if (result.action === 'accept') {\n * console.log('OAuth completed');\n * }\n * };\n * ```\n */\nexport function useElicitation(config: UseElicitationConfig = {}): UseElicitationReturn {\n const { onSuccess, onError } = config;\n\n const [state, setState] = useState<ElicitationState>({\n isLoading: false,\n result: null,\n error: null,\n requestCount: 0,\n });\n\n const reset = useCallback(() => {\n setState({\n isLoading: false,\n result: null,\n error: null,\n requestCount: 0,\n });\n }, []);\n\n const elicitInput = useCallback(\n async (params: ElicitationParams): Promise<ElicitationResult> => {\n if (typeof window === 'undefined' || !window.navigator?.modelContext) {\n throw new Error('navigator.modelContext is not available');\n }\n\n setState((prev) => ({\n ...prev,\n isLoading: true,\n error: null,\n }));\n\n try {\n const result = await window.navigator.modelContext.elicitInput(params);\n\n setState((prev) => ({\n isLoading: false,\n result,\n error: null,\n requestCount: prev.requestCount + 1,\n }));\n\n onSuccess?.(result);\n return result;\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n\n setState((prev) => ({\n ...prev,\n isLoading: false,\n error,\n }));\n\n onError?.(error);\n throw error;\n }\n },\n [onSuccess, onError]\n );\n\n return {\n state,\n elicitInput,\n reset,\n };\n}\n\n// Also export with the old name for backwards compatibility during migration\nexport { useElicitation as useElicitationHandler };\nexport type { ElicitationState as ElicitationHandlerState };\nexport type { UseElicitationConfig as UseElicitationHandlerConfig };\nexport type { UseElicitationReturn as UseElicitationHandlerReturn };\n","import type { SamplingRequestParams, SamplingResult } from '@mcp-b/global';\nimport { useCallback, useState } from 'react';\n\n/**\n * State for sampling requests, tracking the current request and results.\n */\nexport interface SamplingState {\n /** Whether a sampling request is currently in progress */\n isLoading: boolean;\n /** The last sampling result received */\n result: SamplingResult | null;\n /** Any error that occurred during the last request */\n error: Error | null;\n /** Total number of requests made */\n requestCount: number;\n}\n\n/**\n * Configuration options for the useSampling hook.\n */\nexport interface UseSamplingConfig {\n /**\n * Optional callback invoked when a sampling request completes successfully.\n */\n onSuccess?: (result: SamplingResult) => void;\n\n /**\n * Optional callback invoked when a sampling request fails.\n */\n onError?: (error: Error) => void;\n}\n\n/**\n * Return value from the useSampling hook.\n */\nexport interface UseSamplingReturn {\n /** Current state of sampling */\n state: SamplingState;\n /** Function to request LLM completion from the connected client */\n createMessage: (params: SamplingRequestParams) => Promise<SamplingResult>;\n /** Reset the state */\n reset: () => void;\n}\n\n/**\n * React hook for requesting LLM completions from the connected MCP client.\n *\n * Sampling allows the server (webpage) to request LLM completions from the\n * connected client. This is useful when the page needs AI capabilities like\n * summarization, generation, or analysis.\n *\n * @param config - Optional configuration including callbacks\n * @returns Object containing state and the createMessage function\n *\n * @example Basic usage:\n * ```tsx\n * function AIAssistant() {\n * const { state, createMessage } = useSampling({\n * onSuccess: (result) => console.log('Got response:', result),\n * onError: (error) => console.error('Sampling failed:', error),\n * });\n *\n * const handleAsk = async () => {\n * const result = await createMessage({\n * messages: [\n * { role: 'user', content: { type: 'text', text: 'What is 2+2?' } }\n * ],\n * maxTokens: 100,\n * });\n * console.log(result.content);\n * };\n *\n * return (\n * <div>\n * <button onClick={handleAsk} disabled={state.isLoading}>\n * Ask AI\n * </button>\n * {state.result && <p>{JSON.stringify(state.result.content)}</p>}\n * </div>\n * );\n * }\n * ```\n */\nexport function useSampling(config: UseSamplingConfig = {}): UseSamplingReturn {\n const { onSuccess, onError } = config;\n\n const [state, setState] = useState<SamplingState>({\n isLoading: false,\n result: null,\n error: null,\n requestCount: 0,\n });\n\n const reset = useCallback(() => {\n setState({\n isLoading: false,\n result: null,\n error: null,\n requestCount: 0,\n });\n }, []);\n\n const createMessage = useCallback(\n async (params: SamplingRequestParams): Promise<SamplingResult> => {\n if (typeof window === 'undefined' || !window.navigator?.modelContext) {\n throw new Error('navigator.modelContext is not available');\n }\n\n setState((prev) => ({\n ...prev,\n isLoading: true,\n error: null,\n }));\n\n try {\n const result = await window.navigator.modelContext.createMessage(params);\n\n setState((prev) => ({\n isLoading: false,\n result,\n error: null,\n requestCount: prev.requestCount + 1,\n }));\n\n onSuccess?.(result);\n return result;\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n\n setState((prev) => ({\n ...prev,\n isLoading: false,\n error,\n }));\n\n onError?.(error);\n throw error;\n }\n },\n [onSuccess, onError]\n );\n\n return {\n state,\n createMessage,\n reset,\n };\n}\n\n// Also export with the old name for backwards compatibility during migration\nexport { useSampling as useSamplingHandler };\nexport type { SamplingState as SamplingHandlerState };\nexport type { UseSamplingConfig as UseSamplingHandlerConfig };\nexport type { UseSamplingReturn as UseSamplingHandlerReturn };\n","import type { Client } from '@modelcontextprotocol/sdk/client/index.js';\nimport type { RequestOptions } from '@modelcontextprotocol/sdk/shared/protocol.js';\nimport type { Transport } from '@modelcontextprotocol/sdk/shared/transport.js';\nimport type {\n Tool as McpTool,\n Resource,\n ServerCapabilities,\n} from '@modelcontextprotocol/sdk/types.js';\nimport {\n ResourceListChangedNotificationSchema,\n ToolListChangedNotificationSchema,\n} from '@modelcontextprotocol/sdk/types.js';\nimport {\n createContext,\n type ReactElement,\n type ReactNode,\n useCallback,\n useContext,\n useEffect,\n useRef,\n useState,\n} from 'react';\n\n/**\n * Context value provided by McpClientProvider.\n *\n * @internal\n */\ninterface McpClientContextValue {\n client: Client;\n tools: McpTool[];\n resources: Resource[];\n isConnected: boolean;\n isLoading: boolean;\n error: Error | null;\n capabilities: ServerCapabilities | null;\n reconnect: () => Promise<void>;\n}\n\nconst McpClientContext = createContext<McpClientContextValue | null>(null);\n\n/**\n * Props for the McpClientProvider component.\n *\n * @public\n */\nexport interface McpClientProviderProps {\n /**\n * React children to render within the provider.\n */\n children: ReactNode;\n\n /**\n * MCP Client instance to use for communication.\n */\n client: Client;\n\n /**\n * Transport instance for the client to connect through.\n */\n transport: Transport;\n\n /**\n * Optional request options for the connection.\n */\n opts?: RequestOptions;\n}\n\n/**\n * Provider component that manages an MCP client connection and exposes\n * tools, resources, and connection state to child components.\n *\n * This provider handles:\n * - Establishing and maintaining the MCP client connection\n * - Fetching available tools and resources from the server\n * - Listening for server notifications about tool/resource changes\n * - Managing connection state and errors\n * - Automatic cleanup on unmount\n *\n * @param props - Component props\n * @returns Provider component wrapping children\n *\n * @public\n *\n * @example\n * Connect to an MCP server via tab transport:\n * ```tsx\n * import { Client } from '@modelcontextprotocol/sdk/client/index.js';\n * import { TabClientTransport } from '@mcp-b/transports';\n * import { McpClientProvider } from '@mcp-b/react-webmcp';\n *\n * const client = new Client(\n * { name: 'my-app', version: '1.0.0' },\n * { capabilities: {} }\n * );\n *\n * const transport = new TabClientTransport('mcp', {\n * clientInstanceId: 'my-app-instance',\n * });\n *\n * function App() {\n * return (\n * <McpClientProvider client={client} transport={transport}>\n * <MyAppContent />\n * </McpClientProvider>\n * );\n * }\n * ```\n *\n * @example\n * Access tools from child components:\n * ```tsx\n * function MyAppContent() {\n * const { tools, isConnected, isLoading } = useMcpClient();\n *\n * if (isLoading) {\n * return <div>Connecting to MCP server...</div>;\n * }\n *\n * if (!isConnected) {\n * return <div>Failed to connect to MCP server</div>;\n * }\n *\n * return (\n * <div>\n * <h2>Available Tools:</h2>\n * <ul>\n * {tools.map(tool => (\n * <li key={tool.name}>{tool.description}</li>\n * ))}\n * </ul>\n * </div>\n * );\n * }\n * ```\n */\nexport function McpClientProvider({\n children,\n client,\n transport,\n opts = {},\n}: McpClientProviderProps): ReactElement {\n const [resources, setResources] = useState<Resource[]>([]);\n const [tools, setTools] = useState<McpTool[]>([]);\n const [isLoading, setIsLoading] = useState<boolean>(false);\n const [error, setError] = useState<Error | null>(null);\n const [isConnected, setIsConnected] = useState<boolean>(false);\n const [capabilities, setCapabilities] = useState<ServerCapabilities | null>(null);\n\n const connectionStateRef = useRef<'disconnected' | 'connecting' | 'connected'>('disconnected');\n\n /**\n * Fetches available resources from the MCP server.\n * Only fetches if the server supports the resources capability.\n */\n const fetchResourcesInternal = useCallback(async () => {\n if (!client) return;\n\n const serverCapabilities = client.getServerCapabilities();\n if (!serverCapabilities?.resources) {\n setResources([]);\n return;\n }\n\n try {\n const response = await client.listResources();\n setResources(response.resources);\n } catch (e) {\n console.error('Error fetching resources:', e);\n throw e;\n }\n }, [client]);\n\n /**\n * Fetches available tools from the MCP server.\n * Only fetches if the server supports the tools capability.\n */\n const fetchToolsInternal = useCallback(async () => {\n if (!client) return;\n\n const serverCapabilities = client.getServerCapabilities();\n if (!serverCapabilities?.tools) {\n setTools([]);\n return;\n }\n\n try {\n const response = await client.listTools();\n setTools(response.tools);\n } catch (e) {\n console.error('Error fetching tools:', e);\n throw e;\n }\n }, [client]);\n\n /**\n * Establishes connection to the MCP server.\n * Safe to call multiple times - will no-op if already connected or connecting.\n */\n const reconnect = useCallback(async () => {\n if (!client || !transport) {\n throw new Error('Client or transport not available');\n }\n\n if (connectionStateRef.current !== 'disconnected') {\n return;\n }\n\n connectionStateRef.current = 'connecting';\n setIsLoading(true);\n setError(null);\n\n try {\n await client.connect(transport, opts);\n const caps = client.getServerCapabilities();\n setIsConnected(true);\n setCapabilities(caps || null);\n connectionStateRef.current = 'connected';\n\n await Promise.all([fetchResourcesInternal(), fetchToolsInternal()]);\n } catch (e) {\n const err = e instanceof Error ? e : new Error(String(e));\n connectionStateRef.current = 'disconnected';\n setError(err);\n throw err;\n } finally {\n setIsLoading(false);\n }\n }, [client, transport, opts, fetchResourcesInternal, fetchToolsInternal]);\n\n useEffect(() => {\n if (!isConnected || !client) {\n return;\n }\n\n const serverCapabilities = client.getServerCapabilities();\n\n const handleResourcesChanged = () => {\n fetchResourcesInternal().catch(console.error);\n };\n\n const handleToolsChanged = () => {\n fetchToolsInternal().catch(console.error);\n };\n\n if (serverCapabilities?.resources?.listChanged) {\n client.setNotificationHandler(ResourceListChangedNotificationSchema, handleResourcesChanged);\n }\n\n if (serverCapabilities?.tools?.listChanged) {\n client.setNotificationHandler(ToolListChangedNotificationSchema, handleToolsChanged);\n }\n\n // Re-fetch after setting up handlers to catch any changes that occurred\n // during the gap between initial fetch and handler setup\n Promise.all([fetchResourcesInternal(), fetchToolsInternal()]).catch(console.error);\n\n return () => {\n if (serverCapabilities?.resources?.listChanged) {\n client.removeNotificationHandler('notifications/resources/list_changed');\n }\n\n if (serverCapabilities?.tools?.listChanged) {\n client.removeNotificationHandler('notifications/tools/list_changed');\n }\n };\n }, [client, isConnected, fetchResourcesInternal, fetchToolsInternal]);\n\n // biome-ignore lint/correctness/useExhaustiveDependencies: intentional - reconnect when client/transport props change\n useEffect(() => {\n // Initial connection - reconnect() has its own guard to prevent concurrent connections\n reconnect().catch((err) => {\n console.error('Failed to connect MCP client:', err);\n });\n\n // Cleanup: mark as disconnected so next mount will reconnect\n return () => {\n connectionStateRef.current = 'disconnected';\n setIsConnected(false);\n };\n }, [client, transport]);\n\n return (\n <McpClientContext.Provider\n value={{\n client,\n tools,\n resources,\n isConnected,\n isLoading,\n error,\n capabilities,\n reconnect,\n }}\n >\n {children}\n </McpClientContext.Provider>\n );\n}\n\n/**\n * Hook to access the MCP client context.\n * Must be used within an {@link McpClientProvider}.\n *\n * @returns The MCP client context including client instance, tools, resources, and connection state\n * @throws Error if used outside of McpClientProvider\n *\n * @public\n *\n * @example\n * ```tsx\n * function ToolsList() {\n * const { tools, isConnected, error, reconnect } = useMcpClient();\n *\n * if (error) {\n * return (\n * <div>\n * Error: {error.message}\n * <button onClick={reconnect}>Retry</button>\n * </div>\n * );\n * }\n *\n * if (!isConnected) {\n * return <div>Not connected</div>;\n * }\n *\n * return (\n * <ul>\n * {tools.map(tool => (\n * <li key={tool.name}>{tool.description}</li>\n * ))}\n * </ul>\n * );\n * }\n * ```\n */\nexport function useMcpClient() {\n const context = useContext(McpClientContext);\n if (!context) {\n throw new Error('useMcpClient must be used within an McpClientProvider');\n }\n return context;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":["validatedInput: TInput","response: CallToolResult"],"sources":["../src/useWebMCP.ts","../src/useWebMCPContext.ts","../src/useWebMCPPrompt.ts","../src/useWebMCPResource.ts","../src/useElicitationHandler.ts","../src/useSamplingHandler.ts","../src/client/McpClientProvider.tsx"],"sourcesContent":["import type { InputSchema } from '@mcp-b/global';\nimport { zodToJsonSchema } from '@mcp-b/global';\nimport type { CallToolResult } from '@mcp-b/webmcp-ts-sdk';\nimport type { DependencyList } from 'react';\nimport { useCallback, useEffect, useMemo, useRef, useState } from 'react';\nimport { z } from 'zod/v4';\nimport type { InferOutput, ToolExecutionState, WebMCPConfig, WebMCPReturn } 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\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 * - Validates input using Zod schemas\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: { userId: z.string() },\n * outputSchema: {\n * id: z.string(),\n * name: z.string(),\n * email: z.string().email(),\n * },\n * handler: 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 * - **Memoized JSON conversion**: Zod-to-JSON schema conversions are memoized to\n * avoid recomputation on every render.\n *\n * - **Ref-based callbacks**: `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 * memoize these values using `useMemo` or define them outside your component:\n *\n * ```tsx\n * // Good: Memoized schema (won't change unless deps change)\n * const outputSchema = useMemo(() => ({\n * count: z.number(),\n * items: z.array(z.string()),\n * }), []);\n *\n * // Good: Static schema defined outside component\n * const OUTPUT_SCHEMA = {\n * count: z.number(),\n * items: z.array(z.string()),\n * };\n *\n * // Bad: Inline schema (creates new object every render)\n * useWebMCP({\n * outputSchema: { count: z.number() }, // Re-registers every render!\n * });\n * ```\n *\n * **What triggers re-registration:**\n * - Changes to `name` or `description`\n * - Changes to `inputSchema`, `outputSchema`, or `annotations` (reference comparison)\n * - Changes to any value in the `deps` argument (reference comparison)\n *\n * **What does NOT trigger re-registration:**\n * - Changes to `handler`, `onSuccess`, `onError`, or `formatOutput` functions\n *\n * @template TInputSchema - Zod schema object defining input parameter types\n * @template TOutputSchema - Zod 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 * Similar to React's `useEffect` dependencies. When any value changes (by reference),\n * the tool will be unregistered and re-registered. Prefer primitive values over\n * objects/arrays to minimize re-registrations.\n *\n * @returns Object containing execution state and control methods\n *\n * @remarks\n * The hook uses React refs to store callbacks (`handler`, `onSuccess`, `onError`, `formatOutput`)\n * which prevents re-registration when these functions change. This is a performance optimization\n * that follows the \"latest ref\" pattern.\n *\n * When `outputSchema` is provided, the MCP response includes both text content and\n * `structuredContent` per the MCP specification. The type system ensures that the handler's\n * return type matches the output schema through Zod's type inference.\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 * postId: z.string().uuid().describe('The ID of the post to like'),\n * },\n * outputSchema: {\n * success: z.boolean().describe('Whether the like was successful'),\n * likeCount: z.number().describe('Updated like count'),\n * },\n * handler: async ({ postId }) => {\n * const result = await api.posts.like(postId);\n * return { success: true, likeCount: result.likes };\n * },\n * });\n *\n * // likeTool.state.lastResult is typed as { success: boolean; likeCount: number } | null\n * if (likeTool.state.isExecuting) {\n * return <Spinner />;\n * }\n *\n * return <div>Likes: {likeTool.state.lastResult?.likeCount ?? 0}</div>;\n * }\n * ```\n *\n * @example\n * Tool with annotations and callbacks:\n * ```tsx\n * const deleteTool = useWebMCP({\n * name: 'posts_delete',\n * description: 'Delete a post permanently',\n * inputSchema: {\n * postId: z.string().uuid(),\n * },\n * outputSchema: {\n * deleted: z.boolean(),\n * deletedAt: z.string().describe('ISO timestamp of deletion'),\n * },\n * annotations: {\n * destructiveHint: true,\n * idempotentHint: false,\n * },\n * handler: async ({ postId }) => {\n * await api.posts.delete(postId);\n * return { deleted: true, deletedAt: new Date().toISOString() };\n * },\n * onSuccess: () => {\n * navigate('/posts');\n * toast.success('Post deleted');\n * },\n * onError: (error) => {\n * toast.error(`Failed to delete: ${error.message}`);\n * },\n * });\n * ```\n *\n * @example\n * Tool with deps for automatic re-registration:\n * ```tsx\n * function SitesManager({ sites }: { sites: Site[] }) {\n * // Without deps, you'd need getter functions like: getSiteCount: () => sites.length\n * // With deps, values can be used directly in description and handler\n * const sitesTool = useWebMCP(\n * {\n * name: 'sites_query',\n * description: `Query available sites. Current count: ${sites.length}`,\n * outputSchema: {\n * count: z.number(),\n * sites: z.array(z.object({ id: z.string(), name: z.string() })),\n * },\n * handler: async () => ({\n * count: sites.length,\n * sites: sites.map(s => ({ id: s.id, name: s.name })),\n * }),\n * },\n * [sites] // Re-register tool when sites array changes (by reference)\n * );\n *\n * return <SitesList sites={sites} />;\n * }\n * ```\n *\n * @example\n * Optimizing with memoization and deps:\n * ```tsx\n * function OptimizedSites({ sites }: { sites: Site[] }) {\n * // Memoize schema to prevent re-registration on every render\n * const outputSchema = useMemo(() => ({\n * sites: z.array(z.object({ id: z.string(), name: z.string() })),\n * }), []);\n *\n * // Use primitive values in deps for better control\n * const siteIds = sites.map(s => s.id).join(',');\n * const siteCount = sites.length;\n *\n * const sitesTool = useWebMCP(\n * {\n * name: 'sites_query',\n * description: `Query ${siteCount} available sites`,\n * outputSchema,\n * handler: async () => ({\n * sites: sites.map(s => ({ id: s.id, name: s.name })),\n * }),\n * },\n * [siteIds, siteCount] // Only re-register when IDs or count actually change\n * );\n *\n * return <SitesList sites={sites} />;\n * }\n * ```\n */\nexport function useWebMCP<\n TInputSchema extends Record<string, z.ZodTypeAny> = Record<string, never>,\n TOutputSchema extends Record<string, z.ZodTypeAny> = Record<string, never>,\n>(\n config: WebMCPConfig<TInputSchema, TOutputSchema>,\n deps?: DependencyList\n): WebMCPReturn<TOutputSchema> {\n type TOutput = InferOutput<TOutputSchema>;\n type TInput = z.infer<z.ZodObject<TInputSchema>>;\n const {\n name,\n description,\n inputSchema,\n outputSchema,\n annotations,\n handler,\n formatOutput = defaultFormatOutput,\n onSuccess,\n onError,\n } = config;\n\n const [state, setState] = useState<ToolExecutionState<TOutput>>({\n isExecuting: false,\n lastResult: null,\n error: null,\n executionCount: 0,\n });\n\n const handlerRef = useRef(handler);\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 const isDev = (() => {\n const env = (globalThis as { process?: { env?: { NODE_ENV?: string } } }).process?.env\n ?.NODE_ENV;\n return env !== undefined ? env !== 'production' : false;\n })();\n\n // Update refs when callbacks change (doesn't trigger re-registration)\n useEffect(() => {\n handlerRef.current = handler;\n onSuccessRef.current = onSuccess;\n onErrorRef.current = onError;\n formatOutputRef.current = formatOutput;\n }, [handler, 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, isDev, name, outputSchema]);\n\n // Stable schema memoization: Convert to JSON strings first, then memoize based on string value.\n // This prevents infinite re-registration when users pass inline schema objects, because\n // strings are compared by value (not reference) in React's dependency comparison.\n const inputSchemaStr = inputSchema ? JSON.stringify(zodToJsonSchema(inputSchema)) : null;\n const outputSchemaStr = outputSchema ? JSON.stringify(zodToJsonSchema(outputSchema)) : null;\n const annotationsStr = annotations ? JSON.stringify(annotations) : null;\n\n const inputJsonSchema = useMemo(\n () => (inputSchemaStr ? JSON.parse(inputSchemaStr) : undefined),\n [inputSchemaStr]\n );\n const outputJsonSchema = useMemo(\n () => (outputSchemaStr ? JSON.parse(outputSchemaStr) : undefined),\n [outputSchemaStr]\n );\n const stableAnnotations = useMemo(\n () => (annotationsStr ? JSON.parse(annotationsStr) : undefined),\n [annotationsStr]\n );\n\n const validator = useMemo(() => (inputSchema ? z.object(inputSchema) : null), [inputSchema]);\n\n /**\n * Executes the tool handler with input validation and state management.\n *\n * @param input - The input parameters to validate and pass to the handler\n * @returns Promise resolving to the handler's output\n * @throws Error if validation fails or the handler throws\n */\n const execute = useCallback(\n async (input: unknown): Promise<TOutput> => {\n setState((prev) => ({\n ...prev,\n isExecuting: true,\n error: null,\n }));\n\n try {\n const validatedInput: TInput = validator ? validator.parse(input) : (input as TInput);\n const result = await handlerRef.current(validatedInput);\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 [validator]\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 handler 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 execute(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 (outputJsonSchema) {\n response.structuredContent = result as Record<string, unknown>;\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 fallbackInputSchema: InputSchema = {\n type: 'object',\n properties: {},\n };\n\n const registration = window.navigator.modelContext?.registerTool({\n name,\n description,\n inputSchema: (inputJsonSchema || fallbackInputSchema) as InputSchema,\n ...(outputJsonSchema && { outputSchema: outputJsonSchema as InputSchema }),\n ...(stableAnnotations && { annotations: stableAnnotations }),\n execute: mcpHandler,\n });\n\n return () => {\n registration?.unregister();\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 }, [\n name,\n description,\n inputJsonSchema,\n outputJsonSchema,\n stableAnnotations,\n ...(deps ?? []),\n execute,\n ]);\n\n return {\n state,\n execute,\n reset,\n };\n}\n","import { useRef } from 'react';\nimport type { WebMCPReturn } from './types.js';\nimport { useWebMCP } from './useWebMCP.js';\n\n/**\n * Convenience hook for exposing read-only context data to AI assistants.\n *\n * This is a simplified wrapper around {@link useWebMCP} specifically designed for\n * context tools that expose data without performing actions. The hook automatically\n * configures appropriate annotations (read-only, idempotent) and handles value\n * serialization.\n *\n * Note: This hook does not use an output schema, so the result will not include\n * `structuredContent` in the MCP response. Use {@link useWebMCP} directly with\n * `outputSchema` if you need structured output for MCP compliance.\n *\n * @template T - The type of context data to expose\n *\n * @param name - Unique identifier for the context tool (e.g., 'context_current_post')\n * @param description - Human-readable description of the context for AI assistants\n * @param getValue - Function that returns the current context value\n * @returns Tool execution state and control methods\n *\n * @public\n *\n * @example\n * Expose current post context:\n * ```tsx\n * function PostDetailPage() {\n * const { postId } = useParams();\n * const { data: post } = useQuery(['post', postId], () => fetchPost(postId));\n *\n * useWebMCPContext(\n * 'context_current_post',\n * 'Get the currently viewed post ID and metadata',\n * () => ({\n * postId,\n * title: post?.title,\n * author: post?.author,\n * tags: post?.tags,\n * createdAt: post?.createdAt,\n * })\n * );\n *\n * return <PostContent post={post} />;\n * }\n * ```\n *\n * @example\n * Expose user session context:\n * ```tsx\n * function AppRoot() {\n * const { user, isAuthenticated } = useAuth();\n *\n * useWebMCPContext(\n * 'context_user_session',\n * 'Get the current user session information',\n * () => ({\n * isAuthenticated,\n * userId: user?.id,\n * email: user?.email,\n * permissions: user?.permissions,\n * })\n * );\n *\n * return <App />;\n * }\n * ```\n */\nexport function useWebMCPContext<T>(\n name: string,\n description: string,\n getValue: () => T\n): WebMCPReturn {\n const getValueRef = useRef(getValue);\n getValueRef.current = getValue;\n\n // Use default generics (no input/output schema) since context tools\n // don't define structured schemas. The handler returns T but it's\n // treated as `unknown` in the return type since no outputSchema is defined.\n return useWebMCP({\n name,\n description,\n annotations: {\n title: `Context: ${name}`,\n readOnlyHint: true,\n idempotentHint: true,\n destructiveHint: false,\n openWorldHint: false,\n },\n handler: async () => {\n return getValueRef.current();\n },\n formatOutput: (output) => {\n if (typeof output === 'string') {\n return output as string;\n }\n return JSON.stringify(output, null, 2);\n },\n });\n}\n","import type { InputSchema } from '@mcp-b/global';\nimport { zodToJsonSchema } from '@mcp-b/global';\nimport { useEffect, useMemo, useRef, useState } from 'react';\nimport type { z } from 'zod/v4';\nimport type { PromptMessage, WebMCPPromptConfig, WebMCPPromptReturn } from './types.js';\n\n/**\n * React hook for registering Model Context Protocol (MCP) prompts.\n *\n * This hook handles the complete lifecycle of an MCP prompt:\n * - Registers the prompt with `window.navigator.modelContext`\n * - Converts Zod schemas to JSON Schema for argument validation\n * - Automatically unregisters on component unmount\n *\n * @template TArgsSchema - Zod schema object defining argument types\n *\n * @param config - Configuration object for the prompt\n * @returns Object indicating registration status\n *\n * @public\n *\n * @example\n * Simple prompt without arguments:\n * ```tsx\n * function HelpPrompt() {\n * const { isRegistered } = useWebMCPPrompt({\n * name: 'help',\n * description: 'Get help with using the application',\n * get: async () => ({\n * messages: [{\n * role: 'user',\n * content: { type: 'text', text: 'How do I use this application?' }\n * }]\n * }),\n * });\n *\n * return <div>Help prompt {isRegistered ? 'ready' : 'loading'}</div>;\n * }\n * ```\n *\n * @example\n * Prompt with typed arguments:\n * ```tsx\n * function CodeReviewPrompt() {\n * const { isRegistered } = useWebMCPPrompt({\n * name: 'review_code',\n * description: 'Review code for best practices',\n * argsSchema: {\n * code: z.string().describe('The code to review'),\n * language: z.string().optional().describe('Programming language'),\n * },\n * get: async ({ code, language }) => ({\n * messages: [{\n * role: 'user',\n * content: {\n * type: 'text',\n * text: `Please review this ${language ?? ''} code:\\n\\n${code}`\n * }\n * }]\n * }),\n * });\n *\n * return <div>Code review prompt {isRegistered ? 'ready' : 'loading'}</div>;\n * }\n * ```\n */\nexport function useWebMCPPrompt<\n TArgsSchema extends Record<string, z.ZodTypeAny> = Record<string, never>,\n>(config: WebMCPPromptConfig<TArgsSchema>): WebMCPPromptReturn {\n const { name, description, argsSchema, get } = config;\n\n const [isRegistered, setIsRegistered] = useState(false);\n\n const getRef = useRef(get);\n\n const isDev = (() => {\n const env = (globalThis as { process?: { env?: { NODE_ENV?: string } } }).process?.env\n ?.NODE_ENV;\n return env !== undefined ? env !== 'production' : false;\n })();\n\n const argsJsonSchema = useMemo(\n () => (argsSchema ? zodToJsonSchema(argsSchema) : undefined),\n [argsSchema]\n );\n\n useEffect(() => {\n getRef.current = get;\n }, [get]);\n\n useEffect(() => {\n if (typeof window === 'undefined' || !window.navigator?.modelContext) {\n if (isDev) {\n console.warn(\n `[useWebMCPPrompt] window.navigator.modelContext is not available. Prompt \"${name}\" will not be registered.`\n );\n }\n return;\n }\n\n const promptHandler = async (\n args: Record<string, unknown>\n ): Promise<{ messages: PromptMessage[] }> => {\n return getRef.current(args as never);\n };\n\n const registration = window.navigator.modelContext?.registerPrompt({\n name,\n ...(description !== undefined && { description }),\n ...(argsJsonSchema && { argsSchema: argsJsonSchema as InputSchema }),\n get: promptHandler,\n });\n\n if (isDev) {\n console.log(`[useWebMCPPrompt] Registered prompt: ${name}`);\n }\n setIsRegistered(true);\n\n return () => {\n if (registration) {\n registration.unregister();\n if (isDev) {\n console.log(`[useWebMCPPrompt] Unregistered prompt: ${name}`);\n }\n setIsRegistered(false);\n }\n };\n }, [name, description, argsJsonSchema, isDev]);\n\n return {\n isRegistered,\n };\n}\n","import { useEffect, useRef, useState } from 'react';\nimport type { ResourceContents, WebMCPResourceConfig, WebMCPResourceReturn } from './types.js';\n\n/**\n * React hook for registering Model Context Protocol (MCP) resources.\n *\n * This hook handles the complete lifecycle of an MCP resource:\n * - Registers the resource with `window.navigator.modelContext`\n * - Supports both static URIs and URI templates with parameters\n * - Automatically unregisters on component unmount\n *\n * @param config - Configuration object for the resource\n * @returns Object indicating registration status\n *\n * @public\n *\n * @example\n * Static resource:\n * ```tsx\n * function AppSettingsResource() {\n * const { isRegistered } = useWebMCPResource({\n * uri: 'config://app-settings',\n * name: 'App Settings',\n * description: 'Application configuration',\n * mimeType: 'application/json',\n * read: async (uri) => ({\n * contents: [{\n * uri: uri.href,\n * text: JSON.stringify({ theme: 'dark', language: 'en' })\n * }]\n * }),\n * });\n *\n * return <div>Settings resource {isRegistered ? 'ready' : 'loading'}</div>;\n * }\n * ```\n *\n * @example\n * Dynamic resource with URI template:\n * ```tsx\n * function UserProfileResource() {\n * const { isRegistered } = useWebMCPResource({\n * uri: 'user://{userId}/profile',\n * name: 'User Profile',\n * description: 'User profile data by ID',\n * mimeType: 'application/json',\n * read: async (uri, params) => {\n * const userId = params?.userId ?? '';\n * const profile = await fetchUserProfile(userId);\n * return {\n * contents: [{\n * uri: uri.href,\n * text: JSON.stringify(profile)\n * }]\n * };\n * },\n * });\n *\n * return <div>User profile resource {isRegistered ? 'ready' : 'loading'}</div>;\n * }\n * ```\n */\nexport function useWebMCPResource(config: WebMCPResourceConfig): WebMCPResourceReturn {\n const { uri, name, description, mimeType, read } = config;\n\n const [isRegistered, setIsRegistered] = useState(false);\n\n const readRef = useRef(read);\n\n const isDev = (() => {\n const env = (globalThis as { process?: { env?: { NODE_ENV?: string } } }).process?.env\n ?.NODE_ENV;\n return env !== undefined ? env !== 'production' : false;\n })();\n\n useEffect(() => {\n readRef.current = read;\n }, [read]);\n\n useEffect(() => {\n if (typeof window === 'undefined' || !window.navigator?.modelContext) {\n if (isDev) {\n console.warn(\n `[useWebMCPResource] window.navigator.modelContext is not available. Resource \"${uri}\" will not be registered.`\n );\n }\n return;\n }\n\n const resourceHandler = async (\n resolvedUri: URL,\n params?: Record<string, string>\n ): Promise<{ contents: ResourceContents[] }> => {\n return readRef.current(resolvedUri, params);\n };\n\n const registration = window.navigator.modelContext?.registerResource({\n uri,\n name,\n ...(description !== undefined && { description }),\n ...(mimeType !== undefined && { mimeType }),\n read: resourceHandler,\n });\n\n if (isDev) {\n console.log(`[useWebMCPResource] Registered resource: ${uri}`);\n }\n setIsRegistered(true);\n\n return () => {\n if (registration) {\n registration.unregister();\n if (isDev) {\n console.log(`[useWebMCPResource] Unregistered resource: ${uri}`);\n }\n setIsRegistered(false);\n }\n };\n }, [uri, name, description, mimeType, isDev]);\n\n return {\n isRegistered,\n };\n}\n","import type { ElicitationParams, ElicitationResult } from '@mcp-b/global';\nimport { useCallback, useState } from 'react';\n\n/**\n * State for elicitation requests, tracking the current request and results.\n */\nexport interface ElicitationState {\n /** Whether an elicitation request is currently in progress */\n isLoading: boolean;\n /** The last elicitation result received */\n result: ElicitationResult | null;\n /** Any error that occurred during the last request */\n error: Error | null;\n /** Total number of requests made */\n requestCount: number;\n}\n\n/**\n * Configuration options for the useElicitation hook.\n */\nexport interface UseElicitationConfig {\n /**\n * Optional callback invoked when an elicitation request completes successfully.\n */\n onSuccess?: (result: ElicitationResult) => void;\n\n /**\n * Optional callback invoked when an elicitation request fails.\n */\n onError?: (error: Error) => void;\n}\n\n/**\n * Return value from the useElicitation hook.\n */\nexport interface UseElicitationReturn {\n /** Current state of elicitation */\n state: ElicitationState;\n /** Function to request user input from the connected client */\n elicitInput: (params: ElicitationParams) => Promise<ElicitationResult>;\n /** Reset the state */\n reset: () => void;\n}\n\n/**\n * React hook for requesting user input from the connected MCP client.\n *\n * Elicitation allows the server (webpage) to request user input from the\n * connected client. This is useful when the page needs additional information\n * from the user, such as API keys, configuration options, or confirmations.\n *\n * There are two modes:\n * 1. **Form mode**: For non-sensitive data collection using a schema-driven form.\n * 2. **URL mode**: For sensitive data collection via a web URL (API keys, OAuth, etc.).\n *\n * @param config - Optional configuration including callbacks\n * @returns Object containing state and the elicitInput function\n *\n * @example Form elicitation:\n * ```tsx\n * function ConfigForm() {\n * const { state, elicitInput } = useElicitation({\n * onSuccess: (result) => console.log('Got input:', result),\n * onError: (error) => console.error('Elicitation failed:', error),\n * });\n *\n * const handleConfigure = async () => {\n * const result = await elicitInput({\n * message: 'Please provide your configuration',\n * requestedSchema: {\n * type: 'object',\n * properties: {\n * apiKey: { type: 'string', title: 'API Key', description: 'Your API key' },\n * model: { type: 'string', enum: ['gpt-4', 'gpt-3.5'], title: 'Model' }\n * },\n * required: ['apiKey']\n * }\n * });\n *\n * if (result.action === 'accept') {\n * console.log('Config:', result.content);\n * }\n * };\n *\n * return (\n * <button onClick={handleConfigure} disabled={state.isLoading}>\n * Configure\n * </button>\n * );\n * }\n * ```\n *\n * @example URL elicitation (for sensitive data):\n * ```tsx\n * const { elicitInput } = useElicitation();\n *\n * const handleOAuth = async () => {\n * const result = await elicitInput({\n * mode: 'url',\n * message: 'Please authenticate with GitHub',\n * elicitationId: 'github-oauth-123',\n * url: 'https://github.com/login/oauth/authorize?client_id=...'\n * });\n *\n * if (result.action === 'accept') {\n * console.log('OAuth completed');\n * }\n * };\n * ```\n */\nexport function useElicitation(config: UseElicitationConfig = {}): UseElicitationReturn {\n const { onSuccess, onError } = config;\n\n const [state, setState] = useState<ElicitationState>({\n isLoading: false,\n result: null,\n error: null,\n requestCount: 0,\n });\n\n const reset = useCallback(() => {\n setState({\n isLoading: false,\n result: null,\n error: null,\n requestCount: 0,\n });\n }, []);\n\n const elicitInput = useCallback(\n async (params: ElicitationParams): Promise<ElicitationResult> => {\n if (typeof window === 'undefined' || !window.navigator?.modelContext) {\n throw new Error('navigator.modelContext is not available');\n }\n\n setState((prev) => ({\n ...prev,\n isLoading: true,\n error: null,\n }));\n\n try {\n const result = await window.navigator.modelContext.elicitInput(params);\n\n setState((prev) => ({\n isLoading: false,\n result,\n error: null,\n requestCount: prev.requestCount + 1,\n }));\n\n onSuccess?.(result);\n return result;\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n\n setState((prev) => ({\n ...prev,\n isLoading: false,\n error,\n }));\n\n onError?.(error);\n throw error;\n }\n },\n [onSuccess, onError]\n );\n\n return {\n state,\n elicitInput,\n reset,\n };\n}\n\n// Also export with the old name for backwards compatibility during migration\nexport { useElicitation as useElicitationHandler };\nexport type { ElicitationState as ElicitationHandlerState };\nexport type { UseElicitationConfig as UseElicitationHandlerConfig };\nexport type { UseElicitationReturn as UseElicitationHandlerReturn };\n","import type { SamplingRequestParams, SamplingResult } from '@mcp-b/global';\nimport { useCallback, useState } from 'react';\n\n/**\n * State for sampling requests, tracking the current request and results.\n */\nexport interface SamplingState {\n /** Whether a sampling request is currently in progress */\n isLoading: boolean;\n /** The last sampling result received */\n result: SamplingResult | null;\n /** Any error that occurred during the last request */\n error: Error | null;\n /** Total number of requests made */\n requestCount: number;\n}\n\n/**\n * Configuration options for the useSampling hook.\n */\nexport interface UseSamplingConfig {\n /**\n * Optional callback invoked when a sampling request completes successfully.\n */\n onSuccess?: (result: SamplingResult) => void;\n\n /**\n * Optional callback invoked when a sampling request fails.\n */\n onError?: (error: Error) => void;\n}\n\n/**\n * Return value from the useSampling hook.\n */\nexport interface UseSamplingReturn {\n /** Current state of sampling */\n state: SamplingState;\n /** Function to request LLM completion from the connected client */\n createMessage: (params: SamplingRequestParams) => Promise<SamplingResult>;\n /** Reset the state */\n reset: () => void;\n}\n\n/**\n * React hook for requesting LLM completions from the connected MCP client.\n *\n * Sampling allows the server (webpage) to request LLM completions from the\n * connected client. This is useful when the page needs AI capabilities like\n * summarization, generation, or analysis.\n *\n * @param config - Optional configuration including callbacks\n * @returns Object containing state and the createMessage function\n *\n * @example Basic usage:\n * ```tsx\n * function AIAssistant() {\n * const { state, createMessage } = useSampling({\n * onSuccess: (result) => console.log('Got response:', result),\n * onError: (error) => console.error('Sampling failed:', error),\n * });\n *\n * const handleAsk = async () => {\n * const result = await createMessage({\n * messages: [\n * { role: 'user', content: { type: 'text', text: 'What is 2+2?' } }\n * ],\n * maxTokens: 100,\n * });\n * console.log(result.content);\n * };\n *\n * return (\n * <div>\n * <button onClick={handleAsk} disabled={state.isLoading}>\n * Ask AI\n * </button>\n * {state.result && <p>{JSON.stringify(state.result.content)}</p>}\n * </div>\n * );\n * }\n * ```\n */\nexport function useSampling(config: UseSamplingConfig = {}): UseSamplingReturn {\n const { onSuccess, onError } = config;\n\n const [state, setState] = useState<SamplingState>({\n isLoading: false,\n result: null,\n error: null,\n requestCount: 0,\n });\n\n const reset = useCallback(() => {\n setState({\n isLoading: false,\n result: null,\n error: null,\n requestCount: 0,\n });\n }, []);\n\n const createMessage = useCallback(\n async (params: SamplingRequestParams): Promise<SamplingResult> => {\n if (typeof window === 'undefined' || !window.navigator?.modelContext) {\n throw new Error('navigator.modelContext is not available');\n }\n\n setState((prev) => ({\n ...prev,\n isLoading: true,\n error: null,\n }));\n\n try {\n const result = await window.navigator.modelContext.createMessage(params);\n\n setState((prev) => ({\n isLoading: false,\n result,\n error: null,\n requestCount: prev.requestCount + 1,\n }));\n\n onSuccess?.(result);\n return result;\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n\n setState((prev) => ({\n ...prev,\n isLoading: false,\n error,\n }));\n\n onError?.(error);\n throw error;\n }\n },\n [onSuccess, onError]\n );\n\n return {\n state,\n createMessage,\n reset,\n };\n}\n\n// Also export with the old name for backwards compatibility during migration\nexport { useSampling as useSamplingHandler };\nexport type { SamplingState as SamplingHandlerState };\nexport type { UseSamplingConfig as UseSamplingHandlerConfig };\nexport type { UseSamplingReturn as UseSamplingHandlerReturn };\n","import {\n type Client,\n type RequestOptions,\n ResourceListChangedNotificationSchema,\n type Resource,\n type ServerCapabilities,\n type Tool as McpTool,\n ToolListChangedNotificationSchema,\n type Transport,\n} from '@mcp-b/webmcp-ts-sdk';\nimport {\n createContext,\n type ReactElement,\n type ReactNode,\n useCallback,\n useContext,\n useEffect,\n useRef,\n useState,\n} from 'react';\n\n/**\n * Context value provided by McpClientProvider.\n *\n * @internal\n */\ninterface McpClientContextValue {\n client: Client;\n tools: McpTool[];\n resources: Resource[];\n isConnected: boolean;\n isLoading: boolean;\n error: Error | null;\n capabilities: ServerCapabilities | null;\n reconnect: () => Promise<void>;\n}\n\nconst McpClientContext = createContext<McpClientContextValue | null>(null);\n\n/**\n * Props for the McpClientProvider component.\n *\n * @public\n */\nexport interface McpClientProviderProps {\n /**\n * React children to render within the provider.\n */\n children: ReactNode;\n\n /**\n * MCP Client instance to use for communication.\n */\n client: Client;\n\n /**\n * Transport instance for the client to connect through.\n */\n transport: Transport;\n\n /**\n * Optional request options for the connection.\n */\n opts?: RequestOptions;\n}\n\n/**\n * Provider component that manages an MCP client connection and exposes\n * tools, resources, and connection state to child components.\n *\n * This provider handles:\n * - Establishing and maintaining the MCP client connection\n * - Fetching available tools and resources from the server\n * - Listening for server notifications about tool/resource changes\n * - Managing connection state and errors\n * - Automatic cleanup on unmount\n *\n * @param props - Component props\n * @returns Provider component wrapping children\n *\n * @public\n *\n * @example\n * Connect to an MCP server via tab transport:\n * ```tsx\n * import { Client } from '@modelcontextprotocol/sdk/client/index.js';\n * import { TabClientTransport } from '@mcp-b/transports';\n * import { McpClientProvider } from '@mcp-b/react-webmcp';\n *\n * const client = new Client(\n * { name: 'my-app', version: '1.0.0' },\n * { capabilities: {} }\n * );\n *\n * const transport = new TabClientTransport('mcp', {\n * clientInstanceId: 'my-app-instance',\n * });\n *\n * function App() {\n * return (\n * <McpClientProvider client={client} transport={transport}>\n * <MyAppContent />\n * </McpClientProvider>\n * );\n * }\n * ```\n *\n * @example\n * Access tools from child components:\n * ```tsx\n * function MyAppContent() {\n * const { tools, isConnected, isLoading } = useMcpClient();\n *\n * if (isLoading) {\n * return <div>Connecting to MCP server...</div>;\n * }\n *\n * if (!isConnected) {\n * return <div>Failed to connect to MCP server</div>;\n * }\n *\n * return (\n * <div>\n * <h2>Available Tools:</h2>\n * <ul>\n * {tools.map(tool => (\n * <li key={tool.name}>{tool.description}</li>\n * ))}\n * </ul>\n * </div>\n * );\n * }\n * ```\n */\nexport function McpClientProvider({\n children,\n client,\n transport,\n opts = {},\n}: McpClientProviderProps): ReactElement {\n const [resources, setResources] = useState<Resource[]>([]);\n const [tools, setTools] = useState<McpTool[]>([]);\n const [isLoading, setIsLoading] = useState<boolean>(false);\n const [error, setError] = useState<Error | null>(null);\n const [isConnected, setIsConnected] = useState<boolean>(false);\n const [capabilities, setCapabilities] = useState<ServerCapabilities | null>(null);\n\n const connectionStateRef = useRef<'disconnected' | 'connecting' | 'connected'>('disconnected');\n\n /**\n * Fetches available resources from the MCP server.\n * Only fetches if the server supports the resources capability.\n */\n const fetchResourcesInternal = useCallback(async () => {\n if (!client) return;\n\n const serverCapabilities = client.getServerCapabilities();\n if (!serverCapabilities?.resources) {\n setResources([]);\n return;\n }\n\n try {\n const response = await client.listResources();\n setResources(response.resources);\n } catch (e) {\n console.error('Error fetching resources:', e);\n throw e;\n }\n }, [client]);\n\n /**\n * Fetches available tools from the MCP server.\n * Only fetches if the server supports the tools capability.\n */\n const fetchToolsInternal = useCallback(async () => {\n if (!client) return;\n\n const serverCapabilities = client.getServerCapabilities();\n if (!serverCapabilities?.tools) {\n setTools([]);\n return;\n }\n\n try {\n const response = await client.listTools();\n setTools(response.tools);\n } catch (e) {\n console.error('Error fetching tools:', e);\n throw e;\n }\n }, [client]);\n\n /**\n * Establishes connection to the MCP server.\n * Safe to call multiple times - will no-op if already connected or connecting.\n */\n const reconnect = useCallback(async () => {\n if (!client || !transport) {\n throw new Error('Client or transport not available');\n }\n\n if (connectionStateRef.current !== 'disconnected') {\n return;\n }\n\n connectionStateRef.current = 'connecting';\n setIsLoading(true);\n setError(null);\n\n try {\n await client.connect(transport, opts);\n const caps = client.getServerCapabilities();\n setIsConnected(true);\n setCapabilities(caps || null);\n connectionStateRef.current = 'connected';\n\n await Promise.all([fetchResourcesInternal(), fetchToolsInternal()]);\n } catch (e) {\n const err = e instanceof Error ? e : new Error(String(e));\n connectionStateRef.current = 'disconnected';\n setError(err);\n throw err;\n } finally {\n setIsLoading(false);\n }\n }, [client, transport, opts, fetchResourcesInternal, fetchToolsInternal]);\n\n useEffect(() => {\n if (!isConnected || !client) {\n return;\n }\n\n const serverCapabilities = client.getServerCapabilities();\n\n const handleResourcesChanged = () => {\n fetchResourcesInternal().catch(console.error);\n };\n\n const handleToolsChanged = () => {\n fetchToolsInternal().catch(console.error);\n };\n\n if (serverCapabilities?.resources?.listChanged) {\n client.setNotificationHandler(ResourceListChangedNotificationSchema, handleResourcesChanged);\n }\n\n if (serverCapabilities?.tools?.listChanged) {\n client.setNotificationHandler(ToolListChangedNotificationSchema, handleToolsChanged);\n }\n\n // Re-fetch after setting up handlers to catch any changes that occurred\n // during the gap between initial fetch and handler setup\n Promise.all([fetchResourcesInternal(), fetchToolsInternal()]).catch(console.error);\n\n return () => {\n if (serverCapabilities?.resources?.listChanged) {\n client.removeNotificationHandler('notifications/resources/list_changed');\n }\n\n if (serverCapabilities?.tools?.listChanged) {\n client.removeNotificationHandler('notifications/tools/list_changed');\n }\n };\n }, [client, isConnected, fetchResourcesInternal, fetchToolsInternal]);\n\n // biome-ignore lint/correctness/useExhaustiveDependencies: intentional - reconnect when client/transport props change\n useEffect(() => {\n // Initial connection - reconnect() has its own guard to prevent concurrent connections\n reconnect().catch((err) => {\n console.error('Failed to connect MCP client:', err);\n });\n\n // Cleanup: mark as disconnected so next mount will reconnect\n return () => {\n connectionStateRef.current = 'disconnected';\n setIsConnected(false);\n };\n }, [client, transport]);\n\n return (\n <McpClientContext.Provider\n value={{\n client,\n tools,\n resources,\n isConnected,\n isLoading,\n error,\n capabilities,\n reconnect,\n }}\n >\n {children}\n </McpClientContext.Provider>\n );\n}\n\n/**\n * Hook to access the MCP client context.\n * Must be used within an {@link McpClientProvider}.\n *\n * @returns The MCP client context including client instance, tools, resources, and connection state\n * @throws Error if used outside of McpClientProvider\n *\n * @public\n *\n * @example\n * ```tsx\n * function ToolsList() {\n * const { tools, isConnected, error, reconnect } = useMcpClient();\n *\n * if (error) {\n * return (\n * <div>\n * Error: {error.message}\n * <button onClick={reconnect}>Retry</button>\n * </div>\n * );\n * }\n *\n * if (!isConnected) {\n * return <div>Not connected</div>;\n * }\n *\n * return (\n * <ul>\n * {tools.map(tool => (\n * <li key={tool.name}>{tool.description}</li>\n * ))}\n * </ul>\n * );\n * }\n * ```\n */\nexport function useMcpClient() {\n const context = useContext(McpClientContext);\n if (!context) {\n throw new Error('useMcpClient must be used within an McpClientProvider');\n }\n return context;\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mcp-b/react-webmcp",
3
- "version": "0.3.0-beta.0",
3
+ "version": "1.0.0",
4
4
  "description": "React hooks for Model Context Protocol (MCP) - expose React components as AI tools for Claude, ChatGPT, Cursor, and Copilot with Zod validation",
5
5
  "keywords": [
6
6
  "mcp",
@@ -55,9 +55,8 @@
55
55
  "dist"
56
56
  ],
57
57
  "dependencies": {
58
- "@modelcontextprotocol/sdk": "1.24.3",
59
- "@mcp-b/global": "1.2.1",
60
- "@mcp-b/transports": "1.2.0",
58
+ "@mcp-b/global": "2.0.0",
59
+ "@mcp-b/transports": "1.0.0",
61
60
  "@mcp-b/webmcp-ts-sdk": "1.1.0"
62
61
  },
63
62
  "devDependencies": {
@@ -65,11 +64,11 @@
65
64
  "@types/react": "^19.1.2",
66
65
  "tsdown": "^0.15.10",
67
66
  "typescript": "^5.8.3",
68
- "zod": "3.25.76"
67
+ "zod": "4.3.5"
69
68
  },
70
69
  "peerDependencies": {
71
70
  "react": "^17.0.0 || ^18.0.0 || ^19.0.0",
72
- "zod": "^3.25.0"
71
+ "zod": "4.3.5"
73
72
  },
74
73
  "publishConfig": {
75
74
  "access": "public",