@mcp-b/react-webmcp 0.2.0 → 0.2.1-beta.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/dist/index.d.ts +103 -19
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.d.ts
CHANGED
|
@@ -9,6 +9,18 @@ import { Transport } from "@modelcontextprotocol/sdk/shared/transport.js";
|
|
|
9
9
|
|
|
10
10
|
//#region src/types.d.ts
|
|
11
11
|
|
|
12
|
+
/**
|
|
13
|
+
* Utility type to infer the output type from a Zod schema object.
|
|
14
|
+
*
|
|
15
|
+
* When `TOutputSchema` is a non-empty Zod schema object, this resolves to
|
|
16
|
+
* `z.infer<z.ZodObject<TOutputSchema>>`. When it's empty (`Record<string, never>`),
|
|
17
|
+
* it falls back to the provided `TFallback` type.
|
|
18
|
+
*
|
|
19
|
+
* @template TOutputSchema - Zod schema object for output validation
|
|
20
|
+
* @template TFallback - Fallback type when no schema is provided
|
|
21
|
+
* @internal
|
|
22
|
+
*/
|
|
23
|
+
type InferOutput<TOutputSchema extends Record<string, z.ZodTypeAny>, TFallback = unknown> = TOutputSchema extends Record<string, never> ? TFallback : z.infer<z.ZodObject<TOutputSchema>>;
|
|
12
24
|
/**
|
|
13
25
|
* Represents the current execution state of a tool, including loading status,
|
|
14
26
|
* results, errors, and execution history.
|
|
@@ -40,13 +52,16 @@ interface ToolExecutionState<TOutput = unknown> {
|
|
|
40
52
|
}
|
|
41
53
|
/**
|
|
42
54
|
* Configuration options for the `useWebMCP` hook.
|
|
55
|
+
*
|
|
43
56
|
* Defines a tool's metadata, schema, handler, and lifecycle callbacks.
|
|
57
|
+
* Supports full Zod type inference for both input and output schemas.
|
|
44
58
|
*
|
|
45
59
|
* @template TInputSchema - Zod schema object defining input parameters
|
|
46
|
-
* @template
|
|
60
|
+
* @template TOutputSchema - Zod schema object defining output structure (enables structuredContent)
|
|
61
|
+
*
|
|
47
62
|
* @public
|
|
48
63
|
*
|
|
49
|
-
* @example
|
|
64
|
+
* @example Basic tool without output schema:
|
|
50
65
|
* ```typescript
|
|
51
66
|
* const config: WebMCPConfig = {
|
|
52
67
|
* name: 'posts_like',
|
|
@@ -60,8 +75,31 @@ interface ToolExecutionState<TOutput = unknown> {
|
|
|
60
75
|
* },
|
|
61
76
|
* };
|
|
62
77
|
* ```
|
|
78
|
+
*
|
|
79
|
+
* @example Tool with output schema (enables MCP structuredContent):
|
|
80
|
+
* ```typescript
|
|
81
|
+
* const config: WebMCPConfig<
|
|
82
|
+
* { postId: z.ZodString },
|
|
83
|
+
* { likes: z.ZodNumber; likedAt: z.ZodString }
|
|
84
|
+
* > = {
|
|
85
|
+
* name: 'posts_like',
|
|
86
|
+
* description: 'Like a post and return updated like count',
|
|
87
|
+
* inputSchema: {
|
|
88
|
+
* postId: z.string().uuid(),
|
|
89
|
+
* },
|
|
90
|
+
* outputSchema: {
|
|
91
|
+
* likes: z.number().describe('Updated like count'),
|
|
92
|
+
* likedAt: z.string().describe('ISO timestamp of the like'),
|
|
93
|
+
* },
|
|
94
|
+
* // Handler return type is inferred from outputSchema
|
|
95
|
+
* handler: async ({ postId }) => {
|
|
96
|
+
* const result = await api.likePost(postId);
|
|
97
|
+
* return { likes: result.likes, likedAt: new Date().toISOString() };
|
|
98
|
+
* },
|
|
99
|
+
* };
|
|
100
|
+
* ```
|
|
63
101
|
*/
|
|
64
|
-
interface WebMCPConfig<TInputSchema extends Record<string, z.ZodTypeAny> = Record<string, never>,
|
|
102
|
+
interface WebMCPConfig<TInputSchema extends Record<string, z.ZodTypeAny> = Record<string, never>, TOutputSchema extends Record<string, z.ZodTypeAny> = Record<string, never>> {
|
|
65
103
|
/**
|
|
66
104
|
* Unique identifier for the tool (e.g., 'posts_like', 'graph_navigate').
|
|
67
105
|
* Must follow naming conventions: lowercase with underscores.
|
|
@@ -86,10 +124,25 @@ interface WebMCPConfig<TInputSchema extends Record<string, z.ZodTypeAny> = Recor
|
|
|
86
124
|
*/
|
|
87
125
|
inputSchema?: TInputSchema;
|
|
88
126
|
/**
|
|
89
|
-
*
|
|
90
|
-
*
|
|
127
|
+
* Zod schema object defining the expected output structure.
|
|
128
|
+
*
|
|
129
|
+
* When provided, this enables two key features:
|
|
130
|
+
* 1. **Type Safety**: The handler's return type is inferred from this schema
|
|
131
|
+
* 2. **MCP structuredContent**: The MCP response includes `structuredContent`
|
|
132
|
+
* containing the typed output, required when tools define outputSchema
|
|
133
|
+
* per the MCP specification
|
|
134
|
+
*
|
|
135
|
+
* @see {@link https://spec.modelcontextprotocol.io/specification/server/tools/#output-schemas}
|
|
136
|
+
*
|
|
137
|
+
* @example
|
|
138
|
+
* ```typescript
|
|
139
|
+
* outputSchema: {
|
|
140
|
+
* counter: z.number().describe('The current counter value'),
|
|
141
|
+
* timestamp: z.string().describe('ISO timestamp'),
|
|
142
|
+
* }
|
|
143
|
+
* ```
|
|
91
144
|
*/
|
|
92
|
-
outputSchema?:
|
|
145
|
+
outputSchema?: TOutputSchema;
|
|
93
146
|
/**
|
|
94
147
|
* Optional metadata annotations providing hints about tool behavior.
|
|
95
148
|
* See {@link ToolAnnotations} for available options.
|
|
@@ -99,18 +152,25 @@ interface WebMCPConfig<TInputSchema extends Record<string, z.ZodTypeAny> = Recor
|
|
|
99
152
|
* The function that executes when the tool is called.
|
|
100
153
|
* Can be synchronous or asynchronous.
|
|
101
154
|
*
|
|
155
|
+
* When `outputSchema` is provided, the return type is inferred from the schema.
|
|
156
|
+
* Otherwise, any return type is allowed.
|
|
157
|
+
*
|
|
102
158
|
* @param input - Validated input parameters matching the inputSchema
|
|
103
159
|
* @returns The result data or a Promise resolving to the result
|
|
104
160
|
*/
|
|
105
|
-
handler: (input: z.infer<z.ZodObject<TInputSchema>>) => Promise<
|
|
161
|
+
handler: (input: z.infer<z.ZodObject<TInputSchema>>) => Promise<InferOutput<TOutputSchema>> | InferOutput<TOutputSchema>;
|
|
106
162
|
/**
|
|
107
|
-
* Optional function to format the handler output for the MCP response.
|
|
163
|
+
* Optional function to format the handler output for the MCP text response.
|
|
108
164
|
* Defaults to JSON.stringify with indentation.
|
|
109
165
|
*
|
|
166
|
+
* This formats the `content[].text` field in the MCP response.
|
|
167
|
+
* When `outputSchema` is defined, `structuredContent` is also included
|
|
168
|
+
* alongside the formatted text.
|
|
169
|
+
*
|
|
110
170
|
* @param output - The raw output from the handler
|
|
111
|
-
* @returns Formatted string for the MCP response
|
|
171
|
+
* @returns Formatted string for the MCP response content
|
|
112
172
|
*/
|
|
113
|
-
formatOutput?: (output:
|
|
173
|
+
formatOutput?: (output: InferOutput<TOutputSchema>) => string;
|
|
114
174
|
/**
|
|
115
175
|
* Optional callback invoked when the tool execution succeeds.
|
|
116
176
|
* Useful for triggering side effects like navigation or analytics.
|
|
@@ -118,7 +178,7 @@ interface WebMCPConfig<TInputSchema extends Record<string, z.ZodTypeAny> = Recor
|
|
|
118
178
|
* @param result - The successful result from the handler
|
|
119
179
|
* @param input - The input that was passed to the handler
|
|
120
180
|
*/
|
|
121
|
-
onSuccess?: (result:
|
|
181
|
+
onSuccess?: (result: InferOutput<TOutputSchema>, input: unknown) => void;
|
|
122
182
|
/**
|
|
123
183
|
* Optional callback invoked when the tool execution fails.
|
|
124
184
|
* Useful for error handling, logging, or showing user notifications.
|
|
@@ -132,15 +192,15 @@ interface WebMCPConfig<TInputSchema extends Record<string, z.ZodTypeAny> = Recor
|
|
|
132
192
|
* Return value from the `useWebMCP` hook.
|
|
133
193
|
* Provides access to execution state and methods for manual tool control.
|
|
134
194
|
*
|
|
135
|
-
* @template
|
|
195
|
+
* @template TOutputSchema - Zod schema object defining output structure
|
|
136
196
|
* @public
|
|
137
197
|
*/
|
|
138
|
-
interface WebMCPReturn<
|
|
198
|
+
interface WebMCPReturn<TOutputSchema extends Record<string, z.ZodTypeAny> = Record<string, never>> {
|
|
139
199
|
/**
|
|
140
200
|
* Current execution state including loading status, results, and errors.
|
|
141
201
|
* See {@link ToolExecutionState} for details.
|
|
142
202
|
*/
|
|
143
|
-
state: ToolExecutionState<
|
|
203
|
+
state: ToolExecutionState<InferOutput<TOutputSchema>>;
|
|
144
204
|
/**
|
|
145
205
|
* Manually execute the tool with the provided input.
|
|
146
206
|
* Useful for testing, debugging, or triggering execution from your UI.
|
|
@@ -149,7 +209,7 @@ interface WebMCPReturn<TOutput = unknown> {
|
|
|
149
209
|
* @returns Promise resolving to the tool's output
|
|
150
210
|
* @throws Error if validation fails or handler throws
|
|
151
211
|
*/
|
|
152
|
-
execute: (input: unknown) => Promise<
|
|
212
|
+
execute: (input: unknown) => Promise<InferOutput<TOutputSchema>>;
|
|
153
213
|
/**
|
|
154
214
|
* Reset the execution state to its initial values.
|
|
155
215
|
* Clears results, errors, and resets the execution count.
|
|
@@ -308,9 +368,10 @@ interface WebMCPResourceReturn {
|
|
|
308
368
|
* - Validates input using Zod schemas
|
|
309
369
|
* - Handles tool execution and lifecycle callbacks
|
|
310
370
|
* - Automatically unregisters on component unmount
|
|
371
|
+
* - Properly returns `structuredContent` when `outputSchema` is defined
|
|
311
372
|
*
|
|
312
373
|
* @template TInputSchema - Zod schema object defining input parameter types
|
|
313
|
-
* @template
|
|
374
|
+
* @template TOutputSchema - Zod schema object defining output structure (enables structuredContent)
|
|
314
375
|
*
|
|
315
376
|
* @param config - Configuration object for the tool
|
|
316
377
|
* @returns Object containing execution state and control methods
|
|
@@ -342,6 +403,25 @@ interface WebMCPResourceReturn {
|
|
|
342
403
|
* ```
|
|
343
404
|
*
|
|
344
405
|
* @example
|
|
406
|
+
* Tool with outputSchema (enables MCP structuredContent):
|
|
407
|
+
* ```tsx
|
|
408
|
+
* const counterTool = useWebMCP({
|
|
409
|
+
* name: 'counter_get',
|
|
410
|
+
* description: 'Get the current counter value',
|
|
411
|
+
* outputSchema: {
|
|
412
|
+
* counter: z.number().describe('Current counter value'),
|
|
413
|
+
* timestamp: z.string().describe('ISO timestamp'),
|
|
414
|
+
* },
|
|
415
|
+
* handler: async () => {
|
|
416
|
+
* // Return type is inferred from outputSchema
|
|
417
|
+
* return { counter: getCounter(), timestamp: new Date().toISOString() };
|
|
418
|
+
* },
|
|
419
|
+
* });
|
|
420
|
+
*
|
|
421
|
+
* // counterTool.state.lastResult is typed as { counter: number; timestamp: string } | null
|
|
422
|
+
* ```
|
|
423
|
+
*
|
|
424
|
+
* @example
|
|
345
425
|
* Tool with annotations and callbacks:
|
|
346
426
|
* ```tsx
|
|
347
427
|
* const deleteTool = useWebMCP({
|
|
@@ -368,7 +448,7 @@ interface WebMCPResourceReturn {
|
|
|
368
448
|
* });
|
|
369
449
|
* ```
|
|
370
450
|
*/
|
|
371
|
-
declare function useWebMCP<TInputSchema extends Record<string, z.ZodTypeAny> = Record<string, never>,
|
|
451
|
+
declare function useWebMCP<TInputSchema extends Record<string, z.ZodTypeAny> = Record<string, never>, TOutputSchema extends Record<string, z.ZodTypeAny> = Record<string, never>>(config: WebMCPConfig<TInputSchema, TOutputSchema>): WebMCPReturn<TOutputSchema>;
|
|
372
452
|
//#endregion
|
|
373
453
|
//#region src/useWebMCPContext.d.ts
|
|
374
454
|
/**
|
|
@@ -379,6 +459,10 @@ declare function useWebMCP<TInputSchema extends Record<string, z.ZodTypeAny> = R
|
|
|
379
459
|
* configures appropriate annotations (read-only, idempotent) and handles value
|
|
380
460
|
* serialization.
|
|
381
461
|
*
|
|
462
|
+
* Note: This hook does not use an output schema, so the result will not include
|
|
463
|
+
* `structuredContent` in the MCP response. Use {@link useWebMCP} directly with
|
|
464
|
+
* `outputSchema` if you need structured output for MCP compliance.
|
|
465
|
+
*
|
|
382
466
|
* @template T - The type of context data to expose
|
|
383
467
|
*
|
|
384
468
|
* @param name - Unique identifier for the context tool (e.g., 'context_current_post')
|
|
@@ -432,7 +516,7 @@ declare function useWebMCP<TInputSchema extends Record<string, z.ZodTypeAny> = R
|
|
|
432
516
|
* }
|
|
433
517
|
* ```
|
|
434
518
|
*/
|
|
435
|
-
declare function useWebMCPContext<T>(name: string, description: string, getValue: () => T): WebMCPReturn
|
|
519
|
+
declare function useWebMCPContext<T>(name: string, description: string, getValue: () => T): WebMCPReturn;
|
|
436
520
|
//#endregion
|
|
437
521
|
//#region src/useWebMCPPrompt.d.ts
|
|
438
522
|
/**
|
|
@@ -896,5 +980,5 @@ declare function McpClientProvider({
|
|
|
896
980
|
*/
|
|
897
981
|
declare function useMcpClient(): McpClientContextValue;
|
|
898
982
|
//#endregion
|
|
899
|
-
export { type CallToolResult, type ElicitationFormParams, type ElicitationState as ElicitationHandlerState, type ElicitationState, type ElicitationParams, type ElicitationResult, type ElicitationUrlParams, McpClientProvider, type McpClientProviderProps, type ModelContextProtocol, type PromptDescriptor, type PromptMessage, type Resource, type ResourceContents, type ResourceDescriptor, type SamplingState as SamplingHandlerState, type SamplingState, type SamplingRequestParams, type SamplingResult, type ServerCapabilities, type Tool, type ToolAnnotations, type ToolDescriptor, type ToolExecutionState, type UseElicitationConfig, type UseElicitationConfig as UseElicitationHandlerConfig, type UseElicitationReturn as UseElicitationHandlerReturn, type UseElicitationReturn, type UseSamplingConfig, type UseSamplingConfig as UseSamplingHandlerConfig, type UseSamplingReturn as UseSamplingHandlerReturn, type UseSamplingReturn, type WebMCPConfig, type WebMCPPromptConfig, type WebMCPPromptReturn, type WebMCPResourceConfig, type WebMCPResourceReturn, type WebMCPReturn, useElicitation, useElicitation as useElicitationHandler, useMcpClient, useSampling, useSampling as useSamplingHandler, useWebMCP, useWebMCPContext, useWebMCPPrompt, useWebMCPResource };
|
|
983
|
+
export { type CallToolResult, type ElicitationFormParams, type ElicitationState as ElicitationHandlerState, type ElicitationState, type ElicitationParams, type ElicitationResult, type ElicitationUrlParams, type InferOutput, McpClientProvider, type McpClientProviderProps, type ModelContextProtocol, type PromptDescriptor, type PromptMessage, type Resource, type ResourceContents, type ResourceDescriptor, type SamplingState as SamplingHandlerState, type SamplingState, type SamplingRequestParams, type SamplingResult, type ServerCapabilities, type Tool, type ToolAnnotations, type ToolDescriptor, type ToolExecutionState, type UseElicitationConfig, type UseElicitationConfig as UseElicitationHandlerConfig, type UseElicitationReturn as UseElicitationHandlerReturn, type UseElicitationReturn, type UseSamplingConfig, type UseSamplingConfig as UseSamplingHandlerConfig, type UseSamplingReturn as UseSamplingHandlerReturn, type UseSamplingReturn, type WebMCPConfig, type WebMCPPromptConfig, type WebMCPPromptReturn, type WebMCPResourceConfig, type WebMCPResourceReturn, type WebMCPReturn, useElicitation, useElicitation as useElicitationHandler, useMcpClient, useSampling, useSampling as useSamplingHandler, useWebMCP, useWebMCPContext, useWebMCPPrompt, useWebMCPResource };
|
|
900
984
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -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":";;;;;;;;;;;;;;;;
|
|
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(t){let{name:r,description:l,inputSchema:u,outputSchema:d,annotations:p,handler:m,formatOutput:h=f,onSuccess:g,onError:_}=t,[v,y]=s({isExecuting:!1,lastResult:null,error:null,executionCount:0}),b=o(m),x=o(g),S=o(_),C=o(h);i(()=>{b.current=m},[m]),i(()=>{x.current=g},[g]),i(()=>{S.current=_},[_]),i(()=>{C.current=h},[h]);let w=a(()=>u?c.object(u):null,[u]),T=o(w);i(()=>{T.current=w},[w]);let E=n(async e=>{y(e=>({...e,isExecuting:!0,error:null}));try{let t=T.current,n=t?t.parse(e):e,r=await b.current(n);return y(e=>({isExecuting:!1,lastResult:r,error:null,executionCount:e.executionCount+1})),x.current&&x.current(r,e),r}catch(t){let n=t instanceof Error?t:Error(String(t));throw y(e=>({...e,isExecuting:!1,error:n})),S.current&&S.current(n,e),n}},[]),D=n(()=>{y({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 "${r}" will not be registered.`);return}let t=u?e(u):void 0,n=d?e(d):void 0,i=async(e,t)=>{try{let t=await E(e)
|
|
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(t){let{name:r,description:l,inputSchema:u,outputSchema:d,annotations:p,handler:m,formatOutput:h=f,onSuccess:g,onError:_}=t,[v,y]=s({isExecuting:!1,lastResult:null,error:null,executionCount:0}),b=o(m),x=o(g),S=o(_),C=o(h);i(()=>{b.current=m},[m]),i(()=>{x.current=g},[g]),i(()=>{S.current=_},[_]),i(()=>{C.current=h},[h]);let w=a(()=>u?c.object(u):null,[u]),T=o(w);i(()=>{T.current=w},[w]);let E=n(async e=>{y(e=>({...e,isExecuting:!0,error:null}));try{let t=T.current,n=t?t.parse(e):e,r=await b.current(n);return y(e=>({isExecuting:!1,lastResult:r,error:null,executionCount:e.executionCount+1})),x.current&&x.current(r,e),r}catch(t){let n=t instanceof Error?t:Error(String(t));throw y(e=>({...e,isExecuting:!1,error:n})),S.current&&S.current(n,e),n}},[]),D=n(()=>{y({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 "${r}" will not be registered.`);return}let t=u?e(u):void 0,n=d?e(d):void 0,i=async(e,t)=>{try{let t=await E(e),r={content:[{type:`text`,text:C.current(t)}]};return n&&(r.structuredContent=t),r}catch(e){return{content:[{type:`text`,text:`Error: ${e instanceof Error?e.message:String(e)}`}],isError:!0}}},a=window.navigator.modelContext.registerTool({name:r,description:l,inputSchema:t||{type:`object`,properties:{}},...n&&{outputSchema:n},...p&&{annotations:p},execute:async e=>await i(e,{})});return console.log(`[useWebMCP] Registered tool: ${r}`),()=>{a&&(a.unregister(),console.log(`[useWebMCP] Unregistered tool: ${r}`))}},[r,l,u,d,p]),{state:v,execute:E,reset:D}}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: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 g(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 _(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":[],"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 { useCallback, useEffect, useMemo, useRef, useState } from 'react';\nimport { z } from 'zod';\nimport type { 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 * 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 *\n * @template TInputSchema - Zod schema object defining input parameter types\n * @template TOutput - Type of data returned by the handler function\n *\n * @param config - Configuration object for the tool\n * @returns Object containing execution state and control methods\n *\n * @public\n *\n * @example\n * Basic tool registration:\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 * handler: async ({ postId }) => {\n * await api.posts.like(postId);\n * return { success: true, postId };\n * },\n * });\n *\n * if (likeTool.state.isExecuting) {\n * return <Spinner />;\n * }\n *\n * return <div>Post actions ready</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 * annotations: {\n * destructiveHint: true,\n * idempotentHint: false,\n * },\n * handler: async ({ postId }) => {\n * await api.posts.delete(postId);\n * return { deleted: true };\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 */\nexport function useWebMCP<\n TInputSchema extends Record<string, z.ZodTypeAny> = Record<string, never>,\n TOutput = string,\n>(config: WebMCPConfig<TInputSchema, TOutput>): WebMCPReturn<TOutput> {\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 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 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 const inputJsonSchema = inputSchema ? zodToJsonSchema(inputSchema) : undefined;\n const outputJsonSchema = outputSchema ? zodToJsonSchema(outputSchema) : undefined;\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 return {\n content: [\n {\n type: 'text',\n text: formattedOutput,\n },\n ],\n };\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 ...(annotations && { annotations }),\n execute: async (args: Record<string, unknown>) => {\n const result = await mcpHandler(args, {});\n return result;\n },\n });\n\n console.log(`[useWebMCP] Registered tool: ${name}`);\n\n return () => {\n if (registration) {\n registration.unregister();\n console.log(`[useWebMCP] Unregistered tool: ${name}`);\n }\n };\n // Note: execute is intentionally omitted - it's stable (empty deps) and uses refs internally\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [name, description, inputSchema, outputSchema, annotations]);\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 * @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<T> {\n const getValueRef = useRef(getValue);\n getValueRef.current = getValue;\n\n return useWebMCP<Record<string, never>, T>({\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;\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 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 // eslint-disable-next-line react-hooks/exhaustive-deps\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":["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 { 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 * 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 * - Properly returns `structuredContent` when `outputSchema` is defined\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 * @returns Object containing execution state and control methods\n *\n * @public\n *\n * @example\n * Basic tool registration:\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 * handler: async ({ postId }) => {\n * await api.posts.like(postId);\n * return { success: true, postId };\n * },\n * });\n *\n * if (likeTool.state.isExecuting) {\n * return <Spinner />;\n * }\n *\n * return <div>Post actions ready</div>;\n * }\n * ```\n *\n * @example\n * Tool with outputSchema (enables MCP structuredContent):\n * ```tsx\n * const counterTool = useWebMCP({\n * name: 'counter_get',\n * description: 'Get the current counter value',\n * outputSchema: {\n * counter: z.number().describe('Current counter value'),\n * timestamp: z.string().describe('ISO timestamp'),\n * },\n * handler: async () => {\n * // Return type is inferred from outputSchema\n * return { counter: getCounter(), timestamp: new Date().toISOString() };\n * },\n * });\n *\n * // counterTool.state.lastResult is typed as { counter: number; timestamp: string } | null\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 * annotations: {\n * destructiveHint: true,\n * idempotentHint: false,\n * },\n * handler: async ({ postId }) => {\n * await api.posts.delete(postId);\n * return { deleted: true };\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 */\nexport function useWebMCP<\n TInputSchema extends Record<string, z.ZodTypeAny> = Record<string, never>,\n TOutputSchema extends Record<string, z.ZodTypeAny> = Record<string, never>,\n>(config: WebMCPConfig<TInputSchema, TOutputSchema>): 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 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 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 const inputJsonSchema = inputSchema ? zodToJsonSchema(inputSchema) : undefined;\n const outputJsonSchema = outputSchema ? zodToJsonSchema(outputSchema) : undefined;\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 (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 ...(annotations && { annotations }),\n execute: async (args: Record<string, unknown>) => {\n const result = await mcpHandler(args, {});\n return result;\n },\n });\n\n console.log(`[useWebMCP] Registered tool: ${name}`);\n\n return () => {\n if (registration) {\n registration.unregister();\n console.log(`[useWebMCP] Unregistered tool: ${name}`);\n }\n };\n // Note: execute is intentionally omitted - it's stable (empty deps) and uses refs internally\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [name, description, inputSchema, outputSchema, annotations]);\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 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 // eslint-disable-next-line react-hooks/exhaustive-deps\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.2.0",
|
|
3
|
+
"version": "0.2.1-beta.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",
|
|
@@ -56,9 +56,9 @@
|
|
|
56
56
|
],
|
|
57
57
|
"dependencies": {
|
|
58
58
|
"@modelcontextprotocol/sdk": "1.24.3",
|
|
59
|
-
"@mcp-b/global": "1.2.0",
|
|
60
59
|
"@mcp-b/transports": "1.2.0",
|
|
61
|
-
"@mcp-b/webmcp-ts-sdk": "1.1.0"
|
|
60
|
+
"@mcp-b/webmcp-ts-sdk": "1.1.0",
|
|
61
|
+
"@mcp-b/global": "1.2.1-beta.0"
|
|
62
62
|
},
|
|
63
63
|
"devDependencies": {
|
|
64
64
|
"@types/node": "22.17.2",
|