@mcp-b/react-webmcp 1.1.0 → 1.1.1-canary.20260214192802

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -8,7 +8,7 @@
8
8
  [![TypeScript](https://img.shields.io/badge/TypeScript-5.0+-blue?style=flat-square)](https://www.typescriptlang.org/)
9
9
  [![React](https://img.shields.io/badge/React-18+-61DAFB?style=flat-square&logo=react)](https://reactjs.org/)
10
10
 
11
- 📖 **[Full Documentation](https://docs.mcp-b.ai/packages/react-webmcp)** | 🚀 **[Quick Start](https://docs.mcp-b.ai/quickstart)** | ⚛️ **[AI Framework Integration](https://docs.mcp-b.ai/ai-frameworks)**
11
+ **[Full Documentation](https://docs.mcp-b.ai/packages/react-webmcp)** | **[Quick Start](https://docs.mcp-b.ai/quickstart)** | **[AI Framework Integration](https://docs.mcp-b.ai/ai-frameworks)**
12
12
 
13
13
  **@mcp-b/react-webmcp** provides React hooks that expose your components as AI-callable tools via the Model Context Protocol. Build AI-powered React applications where Claude, ChatGPT, Gemini, Cursor, and Copilot can interact with your app's functionality.
14
14
 
@@ -22,73 +22,22 @@
22
22
  | **Execution State Tracking** | Built-in loading, success, and error states for UI feedback |
23
23
  | **Works with Any AI** | Compatible with Claude, ChatGPT, Gemini, Cursor, Copilot, and any MCP client |
24
24
 
25
- ## Use Cases
26
-
27
- - **AI-Controllable Dashboards**: Let AI agents filter data, generate reports, and navigate views
28
- - **Form Automation**: Expose form submission as tools for AI-powered data entry
29
- - **E-commerce Integration**: AI agents can search products, add to cart, and checkout
30
- - **Content Management**: Let AI edit, publish, and organize content in your CMS
31
- - **Data Visualization**: AI can adjust chart parameters, zoom, and export visualizations
32
-
33
- ## Features
34
-
35
- ### Provider Hooks (Registering Tools)
36
- - **Type-Safe**: Full TypeScript support with Zod schema validation
37
- - **State-Aware**: Track execution state (loading, success, error) for UI feedback
38
- - **React-Native**: Designed for React's lifecycle, including StrictMode compatibility
39
- - **Developer-Friendly**: Intuitive API with comprehensive examples
40
-
41
- ### Client Hooks (Consuming Tools)
42
- - **MCP Client Provider**: Connect to MCP servers and consume their tools
43
- - **Real-time Updates**: Listen for tool list changes via MCP notifications
44
- - **Connection Management**: Automatic connection handling with reconnect support
45
- - **Type-Safe Tool Calls**: Full TypeScript support for client operations
46
-
47
25
  ## Installation
48
26
 
49
27
  ```bash
50
28
  pnpm add @mcp-b/react-webmcp zod
51
29
  ```
52
30
 
53
- ## ⚠️ Zod Version Compatibility
54
-
55
- This package supports **Zod 3.25+** and **Zod 4.x**. Simply use the standard import:
56
-
57
- ```typescript
58
- import { z } from 'zod';
59
-
60
- function MyComponent() {
61
- useWebMCP({
62
- name: 'my_tool',
63
- description: 'My tool',
64
- inputSchema: {
65
- name: z.string().describe('User name'),
66
- },
67
- handler: async ({ name }) => ({ message: `Hello, ${name}!` }),
68
- });
69
- }
70
- ```
31
+ If you only want strict core WebMCP hooks (without MCP-B extension APIs like prompts/resources/sampling/elicitation), use `usewebmcp` instead.
71
32
 
72
33
  For client functionality, you'll also need:
73
34
  ```bash
74
35
  pnpm add @mcp-b/transports @modelcontextprotocol/sdk
75
36
  ```
76
37
 
77
- ## Prerequisites
78
-
79
- **For Provider Hooks:** Requires the global `navigator.modelContext` API. Install `@mcp-b/global` or use a browser that implements the Web Model Context API.
80
-
81
- **For Client Hooks:** Requires an MCP server to connect to (e.g., one created with `@mcp-b/global` or the Model Context Protocol SDK).
82
-
83
- ---
84
-
85
- # Part 1: Provider API (Registering Tools)
86
-
87
- Use these hooks to expose tools from your React app that AI agents can discover and call.
88
-
89
- ## Quick Start - Provider
38
+ **Prerequisites:** Provider hooks require the `navigator.modelContext` API. Install `@mcp-b/global` or use a browser that implements the Web Model Context API.
90
39
 
91
- ### Basic Tool Registration
40
+ ## Quick Start - Provider (Registering Tools)
92
41
 
93
42
  ```tsx
94
43
  import { useWebMCP } from '@mcp-b/react-webmcp';
@@ -117,206 +66,18 @@ function PostsPage() {
117
66
  <div>
118
67
  {likeTool.state.isExecuting && <Spinner />}
119
68
  {likeTool.state.error && <ErrorAlert error={likeTool.state.error} />}
120
- {/* Your UI */}
121
- </div>
122
- );
123
- }
124
- ```
125
-
126
- ### Tool with Output Schema (Recommended)
127
-
128
- **Output schemas are essential for modern AI integrations** - they enable AI agents to return structured, type-safe responses:
129
-
130
- ```tsx
131
- import { useWebMCP } from '@mcp-b/react-webmcp';
132
- import { z } from 'zod';
133
-
134
- function ProductSearch() {
135
- const searchTool = useWebMCP({
136
- name: 'products_search',
137
- description: 'Search for products in the catalog',
138
- inputSchema: {
139
- query: z.string().describe('Search query'),
140
- maxResults: z.number().min(1).max(50).default(10),
141
- category: z.enum(['electronics', 'clothing', 'books']).optional(),
142
- },
143
- // Output schema enables structured responses
144
- outputSchema: {
145
- products: z.array(z.object({
146
- id: z.string(),
147
- name: z.string(),
148
- price: z.number(),
149
- inStock: z.boolean(),
150
- })),
151
- total: z.number().describe('Total matching products'),
152
- hasMore: z.boolean(),
153
- },
154
- handler: async ({ query, maxResults, category }) => {
155
- const results = await api.products.search({ query, maxResults, category });
156
- return {
157
- products: results.items,
158
- total: results.totalCount,
159
- hasMore: results.totalCount > maxResults,
160
- };
161
- },
162
- // Format for text display (structuredContent is auto-generated from return value)
163
- formatOutput: (result) => `Found ${result.total} products`,
164
- });
165
-
166
- return (
167
- <div>
168
- {searchTool.state.isExecuting && <Spinner />}
169
- {searchTool.state.lastResult && (
170
- <p>Found {searchTool.state.lastResult.total} products</p>
171
- )}
172
69
  </div>
173
70
  );
174
71
  }
175
72
  ```
176
73
 
177
- **Why use output schemas?**
178
- - AI providers compile schemas to TypeScript, enabling type-safe code generation
179
- - Responses are validated against the schema
180
- - Better AI reasoning about expected output format
181
-
182
- ### Context Tool
183
-
184
- Expose read-only context to AI:
185
-
186
- ```tsx
187
- import { useWebMCPContext } from '@mcp-b/react-webmcp';
188
-
189
- function PostDetailPage() {
190
- const { postId } = useParams();
191
- const { data: post } = useQuery(['post', postId], () => fetchPost(postId));
192
-
193
- useWebMCPContext(
194
- 'context_current_post',
195
- 'Get the currently viewed post ID and metadata',
196
- () => ({
197
- postId,
198
- title: post?.title,
199
- author: post?.author,
200
- tags: post?.tags,
201
- })
202
- );
203
-
204
- return <div>{/* Post UI */}</div>;
205
- }
206
- ```
207
-
208
- ## Provider API Reference
209
-
210
- ### `useWebMCP`
211
-
212
- Main hook for registering MCP tools with full control over behavior and state.
213
-
214
- ```tsx
215
- function useWebMCP<
216
- TInputSchema extends Record<string, z.ZodTypeAny> = Record<string, never>,
217
- TOutputSchema extends Record<string, z.ZodTypeAny> = Record<string, never>
218
- >(
219
- config: WebMCPConfig<TInputSchema, TOutputSchema>,
220
- deps?: DependencyList
221
- ): WebMCPReturn<TOutputSchema>
222
- ```
223
-
224
- `InferOutput<TOutputSchema>` is the output type inferred from `outputSchema`.
225
-
226
- #### Configuration Options
227
-
228
- | Option | Type | Required | Description |
229
- |--------|------|----------|-------------|
230
- | `name` | `string` | ✓ | Unique tool identifier (e.g., 'posts_like') |
231
- | `description` | `string` | ✓ | Human-readable description for AI |
232
- | `inputSchema` | `Record<string, ZodType>` | - | Input validation using Zod schemas |
233
- | `outputSchema` | `Record<string, ZodType>` | - | Output schema for structured responses (recommended) |
234
- | `annotations` | `ToolAnnotations` | - | Metadata hints for the AI |
235
- | `handler` | `(input) => Promise<TOutput>` | ✓ | Function that executes the tool |
236
- | `formatOutput` | `(output) => string` | - | Custom output formatter |
237
- | `onSuccess` | `(result, input) => void` | - | Success callback |
238
- | `onError` | `(error, input) => void` | - | Error handler callback |
239
-
240
- #### Memoization and `deps` (important)
241
-
242
- `useWebMCP` uses reference equality to decide when to re-register a tool. Inline
243
- objects/arrays/functions can cause constant re-registration.
244
-
245
- Bad:
246
- ```tsx
247
- useWebMCP({
248
- name: 'counter',
249
- description: `Count: ${count}`,
250
- outputSchema: { count: z.number() },
251
- handler: async () => ({ count }),
252
- });
253
- ```
254
-
255
- Good:
256
- ```tsx
257
- const OUTPUT_SCHEMA = { count: z.number() };
258
- const description = useMemo(() => `Count: ${count}`, [count]);
259
-
260
- useWebMCP(
261
- {
262
- name: 'counter',
263
- description,
264
- outputSchema: OUTPUT_SCHEMA,
265
- handler: async () => ({ count }),
266
- },
267
- [count]
268
- );
269
- ```
270
-
271
- `deps` behaves like `useEffect` dependencies (reference comparison). Prefer
272
- primitive values or memoized objects to avoid unnecessary re-registrations.
273
-
274
- `handler` is stored in a ref to avoid re-registration when it changes. If you
275
- memoize `handler` with stale dependencies, you'll still capture stale values.
276
-
277
- #### Return Value
278
-
279
- ```tsx
280
- interface WebMCPReturn<TOutputSchema> {
281
- state: {
282
- isExecuting: boolean; // Currently running
283
- lastResult: InferOutput<TOutputSchema> | null; // Last successful result
284
- error: Error | null; // Last error
285
- executionCount: number; // Total executions
286
- };
287
- execute: (input: unknown) => Promise<InferOutput<TOutputSchema>>; // Manual execution
288
- reset: () => void; // Reset state
289
- }
290
- ```
291
-
292
- ### `useWebMCPContext`
293
-
294
- Simplified hook for read-only context exposure:
295
-
296
- ```tsx
297
- function useWebMCPContext<T>(
298
- name: string,
299
- description: string,
300
- getValue: () => T
301
- ): WebMCPReturn
302
- ```
303
-
304
- ---
305
-
306
- # Part 2: Client API (Consuming Tools)
307
-
308
- Use these hooks to connect to MCP servers and call their tools from your React app.
309
-
310
- ## Quick Start - Client
311
-
312
- ### Connecting to an MCP Server
74
+ ## Quick Start - Client (Consuming Tools)
313
75
 
314
76
  ```tsx
315
77
  import { McpClientProvider, useMcpClient } from '@mcp-b/react-webmcp';
316
78
  import { Client } from '@modelcontextprotocol/sdk/client/index.js';
317
79
  import { TabClientTransport } from '@mcp-b/transports';
318
80
 
319
- // Create client and transport
320
81
  const client = new Client({ name: 'MyApp', version: '1.0.0' });
321
82
  const transport = new TabClientTransport('mcp', { clientInstanceId: 'my-app' });
322
83
 
@@ -329,13 +90,10 @@ function App() {
329
90
  }
330
91
 
331
92
  function ToolConsumer() {
332
- const { client, tools, isConnected, capabilities } = useMcpClient();
93
+ const { client, tools, isConnected } = useMcpClient();
333
94
 
334
95
  const handleCallTool = async () => {
335
- const result = await client.callTool({
336
- name: 'posts_like',
337
- arguments: { postId: '123' }
338
- });
96
+ const result = await client.callTool({ name: 'posts_like', arguments: { postId: '123' } });
339
97
  console.log('Result:', result.content[0].text);
340
98
  };
341
99
 
@@ -343,347 +101,48 @@ function ToolConsumer() {
343
101
  <div>
344
102
  <p>Connected: {isConnected ? 'Yes' : 'No'}</p>
345
103
  <p>Available Tools: {tools.length}</p>
346
- <ul>
347
- {tools.map(tool => (
348
- <li key={tool.name}>{tool.name} - {tool.description}</li>
349
- ))}
350
- </ul>
351
- <button onClick={handleCallTool} disabled={!isConnected}>
352
- Call Tool
353
- </button>
104
+ <button onClick={handleCallTool} disabled={!isConnected}>Call Tool</button>
354
105
  </div>
355
106
  );
356
107
  }
357
108
  ```
358
109
 
359
- ### Listening for Tool List Changes
110
+ ## API Overview
360
111
 
361
- ```tsx
362
- function ToolList() {
363
- const { tools, isConnected, capabilities } = useMcpClient();
112
+ ### Provider Hooks
364
113
 
365
- // Tools automatically update when server sends notifications
366
- // if capabilities.tools.listChanged is true
114
+ | Hook | Description |
115
+ |------|-------------|
116
+ | `useWebMCP(config, deps?)` | Register a tool with full control over behavior and state |
117
+ | `useWebMCPContext(name, description, getValue)` | Simplified hook for read-only context exposure |
367
118
 
368
- return (
369
- <div>
370
- <h3>Tools ({tools.length})</h3>
371
- {capabilities?.tools?.listChanged && (
372
- <p>✓ Server supports real-time tool updates</p>
373
- )}
374
- {tools.map(tool => (
375
- <div key={tool.name}>
376
- <h4>{tool.name}</h4>
377
- <p>{tool.description}</p>
378
- </div>
379
- ))}
380
- </div>
381
- );
382
- }
383
- ```
119
+ ### Client Hooks
384
120
 
385
- ## Client API Reference
121
+ | Hook / Component | Description |
122
+ |-------------------|-------------|
123
+ | `McpClientProvider` | Provider component managing an MCP client connection |
124
+ | `useMcpClient()` | Access client, tools, connection status, and capabilities |
386
125
 
387
- ### `McpClientProvider`
126
+ ## Zod Version Compatibility
388
127
 
389
- Provider component that manages an MCP client connection.
128
+ This package supports **Zod 3.25.76+** (3.x only).
390
129
 
391
- ```tsx
392
- interface McpClientProviderProps {
393
- children: ReactNode;
394
- client: Client; // MCP client instance
395
- transport: Transport; // Transport for connection
396
- opts?: RequestOptions; // Optional connection options
397
- }
398
- ```
399
-
400
- #### Example Transports
401
-
402
- ```tsx
403
- // Connect to same-page MCP server (via @mcp-b/global)
404
- import { TabClientTransport } from '@mcp-b/transports';
405
- const transport = new TabClientTransport('mcp', { clientInstanceId: 'my-app' });
406
-
407
- // Connect to Chrome extension MCP server
408
- import { ExtensionClientTransport } from '@mcp-b/transports';
409
- const transport = new ExtensionClientTransport({ portName: 'mcp' });
410
-
411
- // In-memory connection (for testing)
412
- import { InMemoryTransport } from '@modelcontextprotocol/sdk/inMemory.js';
413
- const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair();
414
- ```
415
-
416
- ### `useMcpClient`
130
+ ## Documentation
417
131
 
418
- Hook to access the MCP client context. Must be used within `McpClientProvider`.
419
-
420
- ```tsx
421
- interface McpClientContextValue {
422
- client: Client; // MCP client instance
423
- tools: Tool[]; // Available tools from server
424
- resources: Resource[]; // Available resources
425
- isConnected: boolean; // Connection status
426
- isLoading: boolean; // Currently connecting
427
- error: Error | null; // Connection error
428
- capabilities: ServerCapabilities | null; // Server capabilities
429
- reconnect: () => Promise<void>; // Manual reconnection
430
- }
431
- ```
432
-
433
- #### Calling Tools
434
-
435
- ```tsx
436
- function MyComponent() {
437
- const { client, isConnected } = useMcpClient();
438
-
439
- const callTool = async () => {
440
- if (!isConnected) return;
441
-
442
- try {
443
- const result = await client.callTool({
444
- name: 'my_tool',
445
- arguments: { foo: 'bar' }
446
- });
447
-
448
- // Extract text from result
449
- const text = result.content
450
- .filter(c => c.type === 'text')
451
- .map(c => c.text)
452
- .join('\n');
453
-
454
- console.log(text);
455
- } catch (error) {
456
- console.error('Tool call failed:', error);
457
- }
458
- };
459
-
460
- return <button onClick={callTool}>Call Tool</button>;
461
- }
462
- ```
463
-
464
- ---
465
-
466
- # Complete Example: Both Provider and Client
467
-
468
- This example shows a React app that both exposes tools AND consumes tools from an MCP server.
469
-
470
- ```tsx
471
- import '@mcp-b/global'; // Provides navigator.modelContext
472
- import { McpClientProvider, useWebMCP, useMcpClient } from '@mcp-b/react-webmcp';
473
- import { Client } from '@modelcontextprotocol/sdk/client/index.js';
474
- import { TabClientTransport } from '@mcp-b/transports';
475
- import { z } from 'zod';
476
-
477
- // Create client to consume tools
478
- const client = new Client({ name: 'MyApp', version: '1.0.0' });
479
- const transport = new TabClientTransport('mcp', { clientInstanceId: 'my-app' });
480
-
481
- function App() {
482
- return (
483
- <McpClientProvider client={client} transport={transport}>
484
- <ToolProvider />
485
- <ToolConsumer />
486
- </McpClientProvider>
487
- );
488
- }
489
-
490
- // Component that REGISTERS tools
491
- function ToolProvider() {
492
- const [count, setCount] = useState(0);
493
-
494
- // Expose a tool that increments the counter
495
- useWebMCP({
496
- name: 'increment_counter',
497
- description: 'Increment the counter',
498
- inputSchema: {
499
- amount: z.number().default(1)
500
- },
501
- handler: async ({ amount }) => {
502
- setCount(prev => prev + amount);
503
- return { newValue: count + amount };
504
- }
505
- });
506
-
507
- return <div>Counter: {count}</div>;
508
- }
509
-
510
- // Component that CONSUMES tools
511
- function ToolConsumer() {
512
- const { client, tools, isConnected } = useMcpClient();
513
- const [result, setResult] = useState('');
514
-
515
- const callIncrementTool = async () => {
516
- const res = await client.callTool({
517
- name: 'increment_counter',
518
- arguments: { amount: 5 }
519
- });
520
- setResult(res.content[0].text);
521
- };
522
-
523
- return (
524
- <div>
525
- <p>Available Tools: {tools.map(t => t.name).join(', ')}</p>
526
- <button onClick={callIncrementTool} disabled={!isConnected}>
527
- Call increment_counter Tool
528
- </button>
529
- {result && <p>Result: {result}</p>}
530
- </div>
531
- );
532
- }
533
- ```
534
-
535
- ---
536
-
537
- # Migration from @mcp-b/mcp-react-hooks
538
-
539
- If you're migrating from the deprecated `@mcp-b/mcp-react-hooks` package:
540
-
541
- ## What Changed
542
-
543
- - **Server providers removed**: `McpServerProvider` and `McpMemoryProvider` are gone
544
- - **Everything in one package**: Both client and provider hooks are now in `@mcp-b/react-webmcp`
545
- - **Tool registration**: Use `useWebMCP` instead of server providers
546
- - **Client unchanged**: `McpClientProvider` and `useMcpClient` work the same way
547
-
548
- ## Migration Guide
549
-
550
- ### Before (mcp-react-hooks)
551
-
552
- ```tsx
553
- import { McpClientProvider, useMcpClient } from '@mcp-b/mcp-react-hooks';
554
- import { McpServerProvider, useMcpServer } from '@mcp-b/mcp-react-hooks';
555
- ```
556
-
557
- ### After (react-webmcp)
558
-
559
- ```tsx
560
- // Client hooks - same API
561
- import { McpClientProvider, useMcpClient } from '@mcp-b/react-webmcp';
562
-
563
- // For registering tools, use useWebMCP instead of server providers
564
- import { useWebMCP } from '@mcp-b/react-webmcp';
565
- ```
566
-
567
- ### Converting Server to Provider
568
-
569
- **Before:**
570
- ```tsx
571
- function MyApp() {
572
- const { registerTool } = useMcpServer();
573
-
574
- useEffect(() => {
575
- const tool = registerTool('my_tool', { description: '...' }, handler);
576
- return () => tool.remove();
577
- }, []);
578
- }
579
- ```
580
-
581
- **After:**
582
- ```tsx
583
- function MyApp() {
584
- useWebMCP({
585
- name: 'my_tool',
586
- description: '...',
587
- handler: handler
588
- });
589
- // Auto-registers and cleans up on unmount
590
- }
591
- ```
592
-
593
- ---
594
-
595
- # Best Practices
596
-
597
- ### Tool Naming
598
- - Use verb-noun format: `posts_like`, `graph_navigate`, `table_filter`
599
- - Prefix with domain: `posts_`, `comments_`, `graph_`
600
- - Be specific and descriptive
601
-
602
- ### Annotations
603
- - Always set `readOnlyHint` (true for queries, false for mutations)
604
- - Set `idempotentHint` (true if repeated calls are safe)
605
- - Set `destructiveHint` for delete/permanent operations
606
-
607
- ### Error Handling
608
- - Throw descriptive errors from tool handlers
609
- - Use `onError` callback for side effects (logging, toasts)
610
- - Handle connection errors in client components
611
-
612
- ### Performance
613
- - Tools automatically prevent duplicate registration in React StrictMode
614
- - Use `useWebMCPContext` for lightweight read-only data exposure
615
- - Client automatically manages reconnection and tool list updates
616
-
617
- ## Frequently Asked Questions
618
-
619
- ### What AI agents can use my React tools?
620
-
621
- Any MCP-compatible client can discover and call your tools, including:
622
- - **Claude Desktop** and Claude.ai
623
- - **ChatGPT** (via plugins/GPTs)
624
- - **Cursor** IDE
625
- - **VS Code Copilot**
626
- - **Gemini** applications
627
- - **Windsurf**, **Cline**, and other MCP clients
628
-
629
- ### How do AI agents connect to my React app?
630
-
631
- AI agents connect via browser extensions or the `@mcp-b/chrome-devtools-mcp` server, which bridges desktop AI clients to browser-based MCP tools.
632
-
633
- ### Is this production-ready?
634
-
635
- Yes! The hooks handle React StrictMode, automatic cleanup, and proper lifecycle management. Tools are automatically unregistered when components unmount.
636
-
637
- ### Can I use this with Next.js / Remix / Gatsby?
638
-
639
- Yes! These hooks work with any React framework. Just ensure `@mcp-b/global` is loaded on the client side.
640
-
641
- ### How do I validate tool inputs?
642
-
643
- Use Zod schemas in `inputSchema`. Invalid inputs are automatically rejected with descriptive error messages.
644
-
645
- ```tsx
646
- inputSchema: {
647
- email: z.string().email().describe('User email address'),
648
- age: z.number().min(0).max(120).describe('User age')
649
- }
650
- ```
651
-
652
- ### Can tools access React state?
653
-
654
- Yes! Tool handlers have access to component state via closures. State updates trigger re-renders as expected.
655
-
656
- ## Comparison with Alternatives
657
-
658
- | Feature | @mcp-b/react-webmcp | Raw MCP SDK | Custom Implementation |
659
- |---------|---------------------|-------------|----------------------|
660
- | React Lifecycle Integration | Automatic | Manual | Manual |
661
- | StrictMode Support | Yes | N/A | Manual |
662
- | Zod Schema Validation | Built-in | Manual | Manual |
663
- | Execution State Tracking | Built-in | Manual | Manual |
664
- | TypeScript Support | Full | Partial | Varies |
132
+ For full API reference, output schemas, memoization patterns, migration guide, best practices, and complete examples, see the [React WebMCP Guide](../../docs/react-webmcp-guide.md).
665
133
 
666
134
  ## Related Packages
667
135
 
668
136
  - [`@mcp-b/global`](https://docs.mcp-b.ai/packages/global) - W3C Web Model Context API polyfill (required for provider hooks)
669
137
  - [`@mcp-b/transports`](https://docs.mcp-b.ai/packages/transports) - Browser-specific MCP transports
670
138
  - [`@mcp-b/chrome-devtools-mcp`](https://docs.mcp-b.ai/packages/chrome-devtools-mcp) - Connect desktop AI agents to browser tools
671
- - [`@modelcontextprotocol/sdk`](https://www.npmjs.com/package/@modelcontextprotocol/sdk) - Official MCP SDK
139
+ - [`usewebmcp`](../usewebmcp) - React hooks for strict core WebMCP API only
672
140
 
673
141
  ## Resources
674
142
 
675
143
  - [WebMCP Documentation](https://docs.mcp-b.ai)
676
- - [AI Framework Integration](https://docs.mcp-b.ai/ai-frameworks)
677
- - [Best Practices](https://docs.mcp-b.ai/best-practices)
678
144
  - [Model Context Protocol Spec](https://modelcontextprotocol.io)
679
- - [MCP GitHub Repository](https://github.com/modelcontextprotocol)
680
145
 
681
146
  ## License
682
147
 
683
148
  MIT - see [LICENSE](../../LICENSE) for details
684
-
685
- ## Support
686
-
687
- - [GitHub Issues](https://github.com/WebMCP-org/npm-packages/issues)
688
- - [Documentation](https://docs.mcp-b.ai)
689
- - [Discord Community](https://discord.gg/a9fBR6Bw)
package/dist/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
- import { ElicitationFormParams, ElicitationParams, ElicitationParams as ElicitationParams$1, ElicitationResult, ElicitationResult as ElicitationResult$1, ElicitationUrlParams, ModelContext as ModelContextProtocol, PromptDescriptor, ResourceDescriptor, SamplingRequestParams, SamplingRequestParams as SamplingRequestParams$1, SamplingResult, SamplingResult as SamplingResult$1, ToolDescriptor } from "@mcp-b/global";
1
+ import { CallToolResult, ElicitationFormParams, ElicitationParams, ElicitationParams as ElicitationParams$1, ElicitationResult, ElicitationResult as ElicitationResult$1, ElicitationUrlParams, ModelContext as ModelContextProtocol, PromptDescriptor, PromptMessage, ResourceContents, ResourceDescriptor, SamplingRequestParams, SamplingRequestParams as SamplingRequestParams$1, SamplingResult, SamplingResult as SamplingResult$1, ToolAnnotations, ToolDescriptor } from "@mcp-b/global";
2
2
  import { DependencyList, ReactElement, ReactNode } from "react";
3
3
  import { z } from "zod";
4
- import { CallToolResult, Client, PromptMessage, RequestOptions, Resource, Resource as Resource$1, ResourceContents, ServerCapabilities, ServerCapabilities as ServerCapabilities$1, Tool, Tool as Tool$1, ToolAnnotations, Transport } from "@mcp-b/webmcp-ts-sdk";
4
+ import { Client, RequestOptions, Resource, Resource as Resource$1, ServerCapabilities, ServerCapabilities as ServerCapabilities$1, Tool, Tool as Tool$1, Transport } from "@mcp-b/webmcp-ts-sdk";
5
5
 
6
6
  //#region src/types.d.ts
7
7
 
@@ -42,7 +42,7 @@ interface ToolExecutionState<TOutput = unknown> {
42
42
  error: Error | null;
43
43
  /**
44
44
  * Total number of times this tool has been executed.
45
- * Increments on both successful and failed executions.
45
+ * Increments on successful executions only.
46
46
  */
47
47
  executionCount: number;
48
48
  }
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","names":[],"sources":["../src/types.ts","../src/useWebMCP.ts","../src/useWebMCPContext.ts","../src/useWebMCPPrompt.ts","../src/useWebMCPResource.ts","../src/useElicitationHandler.ts","../src/useSamplingHandler.ts","../src/client/McpClientProvider.tsx"],"sourcesContent":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../src/types.ts","../src/useWebMCP.ts","../src/useWebMCPContext.ts","../src/useWebMCPPrompt.ts","../src/useWebMCPResource.ts","../src/useElicitationHandler.ts","../src/useSamplingHandler.ts","../src/client/McpClientProvider.tsx"],"sourcesContent":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -1,2 +1,2 @@
1
- import{zodToJsonSchema as e}from"@mcp-b/global";import{createContext as t,useCallback as n,useContext as r,useEffect as i,useMemo as a,useRef as o,useState as s}from"react";import{z as c}from"zod";import{ResourceListChangedNotificationSchema as l,ToolListChangedNotificationSchema as u}from"@mcp-b/webmcp-ts-sdk";import{jsx as d}from"react/jsx-runtime";function f(e){return typeof e==`string`?e:JSON.stringify(e,null,2)}function p(t,r){let{name:l,description:u,inputSchema:d,outputSchema:p,annotations:m,handler:h,formatOutput:g=f,onSuccess:_,onError:v}=t,[y,b]=s({isExecuting:!1,lastResult:null,error:null,executionCount:0}),x=o(h),S=o(_),C=o(v),w=o(g),T=o(!0),E=o(new Set),D=o({inputSchema:d,outputSchema:p,annotations:m,description:u,deps:r}),O=(()=>{let e=globalThis.process?.env?.NODE_ENV;return e===void 0?!1:e!==`production`})();i(()=>{x.current=h,S.current=_,C.current=v,w.current=g},[h,_,v,g]),i(()=>(T.current=!0,()=>{T.current=!1}),[]),i(()=>{if(!O){D.current={inputSchema:d,outputSchema:p,annotations:m,description:u,deps:r};return}let e=(e,t)=>{E.current.has(e)||(console.warn(`[useWebMCP] ${t}`),E.current.add(e))},t=D.current;d&&t.inputSchema&&t.inputSchema!==d&&e(`inputSchema`,`Tool "${l}" inputSchema reference changed; memoize or define it outside the component to avoid re-registration.`),p&&t.outputSchema&&t.outputSchema!==p&&e(`outputSchema`,`Tool "${l}" outputSchema reference changed; memoize or define it outside the component to avoid re-registration.`),m&&t.annotations&&t.annotations!==m&&e(`annotations`,`Tool "${l}" annotations reference changed; memoize or define it outside the component to avoid re-registration.`),u!==t.description&&e(`description`,`Tool "${l}" description changed; this re-registers the tool. Memoize the description if it does not need to update.`),r?.some(e=>typeof e==`object`&&!!e||typeof e==`function`)&&e(`deps`,`Tool "${l}" deps contains non-primitive values; prefer primitives or memoize objects/functions to reduce re-registration.`),D.current={inputSchema:d,outputSchema:p,annotations:m,description:u,deps:r}},[m,r,u,d,O,l,p]);let k=d?JSON.stringify(e(d)):null,A=p?JSON.stringify(e(p)):null,j=m?JSON.stringify(m):null,M=a(()=>k?JSON.parse(k):void 0,[k]),N=a(()=>A?JSON.parse(A):void 0,[A]),P=a(()=>j?JSON.parse(j):void 0,[j]),F=a(()=>d?c.object(d):null,[d]),I=n(async e=>{b(e=>({...e,isExecuting:!0,error:null}));try{let t=F?F.parse(e):e,n=await x.current(t);return T.current&&b(e=>({isExecuting:!1,lastResult:n,error:null,executionCount:e.executionCount+1})),S.current&&S.current(n,e),n}catch(t){let n=t instanceof Error?t:Error(String(t));throw T.current&&b(e=>({...e,isExecuting:!1,error:n})),C.current&&C.current(n,e),n}},[F]),L=n(()=>{b({isExecuting:!1,lastResult:null,error:null,executionCount:0})},[]);return i(()=>{if(typeof window>`u`||!window.navigator?.modelContext){console.warn(`[useWebMCP] window.navigator.modelContext is not available. Tool "${l}" will not be registered.`);return}let e=async e=>{try{let t=await I(e),n={content:[{type:`text`,text:w.current(t)}]};return N&&(n.structuredContent=t),n}catch(e){return{content:[{type:`text`,text:`Error: ${e instanceof Error?e.message:String(e)}`}],isError:!0}}},t=window.navigator.modelContext?.registerTool({name:l,description:u,inputSchema:M||{type:`object`,properties:{}},...N&&{outputSchema:N},...P&&{annotations:P},execute:e});return()=>{t?.unregister()}},[l,u,M,N,P,...r??[],I]),{state:y,execute:I,reset:L}}function m(e,t,n){let r=o(n);return r.current=n,p({name:e,description:t,annotations:{title:`Context: ${e}`,readOnlyHint:!0,idempotentHint:!0,destructiveHint:!1,openWorldHint:!1},handler:async e=>r.current(),formatOutput:e=>typeof e==`string`?e:JSON.stringify(e,null,2)})}function h(t){let{name:n,description:r,argsSchema:c,get:l}=t,[u,d]=s(!1),f=o(l),p=(()=>{let e=globalThis.process?.env?.NODE_ENV;return e===void 0?!1:e!==`production`})(),m=a(()=>c?e(c):void 0,[c]);return i(()=>{f.current=l},[l]),i(()=>{if(typeof window>`u`||!window.navigator?.modelContext){p&&console.warn(`[useWebMCPPrompt] window.navigator.modelContext is not available. Prompt "${n}" will not be registered.`);return}let e=async e=>f.current(e),t=window.navigator.modelContext?.registerPrompt({name:n,...r!==void 0&&{description:r},...m&&{argsSchema:m},get:e});return p&&console.log(`[useWebMCPPrompt] Registered prompt: ${n}`),d(!0),()=>{t&&(t.unregister(),p&&console.log(`[useWebMCPPrompt] Unregistered prompt: ${n}`),d(!1))}},[n,r,m,p]),{isRegistered:u}}function g(e){let{uri:t,name:n,description:r,mimeType:a,read:c}=e,[l,u]=s(!1),d=o(c),f=(()=>{let e=globalThis.process?.env?.NODE_ENV;return e===void 0?!1:e!==`production`})();return i(()=>{d.current=c},[c]),i(()=>{if(typeof window>`u`||!window.navigator?.modelContext){f&&console.warn(`[useWebMCPResource] window.navigator.modelContext is not available. Resource "${t}" will not be registered.`);return}let e=async(e,t)=>d.current(e,t),i=window.navigator.modelContext?.registerResource({uri:t,name:n,...r!==void 0&&{description:r},...a!==void 0&&{mimeType:a},read:e});return f&&console.log(`[useWebMCPResource] Registered resource: ${t}`),u(!0),()=>{i&&(i.unregister(),f&&console.log(`[useWebMCPResource] Unregistered resource: ${t}`),u(!1))}},[t,n,r,a,f]),{isRegistered:l}}function _(e={}){let{onSuccess:t,onError:r}=e,[i,a]=s({isLoading:!1,result:null,error:null,requestCount:0}),o=n(()=>{a({isLoading:!1,result:null,error:null,requestCount:0})},[]);return{state:i,elicitInput:n(async e=>{if(typeof window>`u`||!window.navigator?.modelContext)throw Error(`navigator.modelContext is not available`);a(e=>({...e,isLoading:!0,error:null}));try{let n=await window.navigator.modelContext.elicitInput(e);return a(e=>({isLoading:!1,result:n,error:null,requestCount:e.requestCount+1})),t?.(n),n}catch(e){let t=e instanceof Error?e:Error(String(e));throw a(e=>({...e,isLoading:!1,error:t})),r?.(t),t}},[t,r]),reset:o}}function v(e={}){let{onSuccess:t,onError:r}=e,[i,a]=s({isLoading:!1,result:null,error:null,requestCount:0}),o=n(()=>{a({isLoading:!1,result:null,error:null,requestCount:0})},[]);return{state:i,createMessage:n(async e=>{if(typeof window>`u`||!window.navigator?.modelContext)throw Error(`navigator.modelContext is not available`);a(e=>({...e,isLoading:!0,error:null}));try{let n=await window.navigator.modelContext.createMessage(e);return a(e=>({isLoading:!1,result:n,error:null,requestCount:e.requestCount+1})),t?.(n),n}catch(e){let t=e instanceof Error?e:Error(String(e));throw a(e=>({...e,isLoading:!1,error:t})),r?.(t),t}},[t,r]),reset:o}}const y=t(null);function b({children:e,client:t,transport:r,opts:a={}}){let[c,f]=s([]),[p,m]=s([]),[h,g]=s(!1),[_,v]=s(null),[b,x]=s(!1),[S,C]=s(null),w=o(`disconnected`),T=n(async()=>{if(t){if(!t.getServerCapabilities()?.resources){f([]);return}try{f((await t.listResources()).resources)}catch(e){throw console.error(`Error fetching resources:`,e),e}}},[t]),E=n(async()=>{if(t){if(!t.getServerCapabilities()?.tools){m([]);return}try{m((await t.listTools()).tools)}catch(e){throw console.error(`Error fetching tools:`,e),e}}},[t]),D=n(async()=>{if(!t||!r)throw Error(`Client or transport not available`);if(w.current===`disconnected`){w.current=`connecting`,g(!0),v(null);try{await t.connect(r,a);let e=t.getServerCapabilities();x(!0),C(e||null),w.current=`connected`,await Promise.all([T(),E()])}catch(e){let t=e instanceof Error?e:Error(String(e));throw w.current=`disconnected`,v(t),t}finally{g(!1)}}},[t,r,a,T,E]);return i(()=>{if(!b||!t)return;let e=t.getServerCapabilities();return e?.resources?.listChanged&&t.setNotificationHandler(l,()=>{T().catch(console.error)}),e?.tools?.listChanged&&t.setNotificationHandler(u,()=>{E().catch(console.error)}),Promise.all([T(),E()]).catch(console.error),()=>{e?.resources?.listChanged&&t.removeNotificationHandler(`notifications/resources/list_changed`),e?.tools?.listChanged&&t.removeNotificationHandler(`notifications/tools/list_changed`)}},[t,b,T,E]),i(()=>(D().catch(e=>{console.error(`Failed to connect MCP client:`,e)}),()=>{w.current=`disconnected`,x(!1)}),[t,r]),d(y.Provider,{value:{client:t,tools:p,resources:c,isConnected:b,isLoading:h,error:_,capabilities:S,reconnect:D},children:e})}function x(){let e=r(y);if(!e)throw Error(`useMcpClient must be used within an McpClientProvider`);return e}export{b as McpClientProvider,_ as useElicitation,_ as useElicitationHandler,x as useMcpClient,v as useSampling,v as useSamplingHandler,p as useWebMCP,m as useWebMCPContext,h as useWebMCPPrompt,g as useWebMCPResource};
1
+ "use client";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"@mcp-b/webmcp-ts-sdk";import{jsx as d}from"react/jsx-runtime";function f(e){return typeof e==`string`?e:JSON.stringify(e,null,2)}const p=new Map;function m(e){if(!e||typeof e!=`object`||Array.isArray(e))return null;try{let t=JSON.parse(JSON.stringify(e));return!t||typeof t!=`object`||Array.isArray(t)?null:t}catch{return null}}function h(t,r){let{name:l,description:u,inputSchema:d,outputSchema:h,annotations:g,handler:_,formatOutput:v=f,onSuccess:y,onError:b}=t,[x,S]=s({isExecuting:!1,lastResult:null,error:null,executionCount:0}),C=o(_),w=o(y),T=o(b),E=o(v),D=o(!0),O=o(new Set),k=o({inputSchema:d,outputSchema:h,annotations:g,description:u,deps:r}),A=(()=>{let e=globalThis.process?.env?.NODE_ENV;return e===void 0?!1:e!==`production`})();i(()=>{C.current=_,w.current=y,T.current=b,E.current=v},[_,y,b,v]),i(()=>(D.current=!0,()=>{D.current=!1}),[]),i(()=>{if(!A){k.current={inputSchema:d,outputSchema:h,annotations:g,description:u,deps:r};return}let e=(e,t)=>{O.current.has(e)||(console.warn(`[useWebMCP] ${t}`),O.current.add(e))},t=k.current;d&&t.inputSchema&&t.inputSchema!==d&&e(`inputSchema`,`Tool "${l}" inputSchema reference changed; memoize or define it outside the component to avoid re-registration.`),h&&t.outputSchema&&t.outputSchema!==h&&e(`outputSchema`,`Tool "${l}" outputSchema reference changed; memoize or define it outside the component to avoid re-registration.`),g&&t.annotations&&t.annotations!==g&&e(`annotations`,`Tool "${l}" annotations reference changed; memoize or define it outside the component to avoid re-registration.`),u!==t.description&&e(`description`,`Tool "${l}" description changed; this re-registers the tool. Memoize the description if it does not need to update.`),r?.some(e=>typeof e==`object`&&!!e||typeof e==`function`)&&e(`deps`,`Tool "${l}" deps contains non-primitive values; prefer primitives or memoize objects/functions to reduce re-registration.`),k.current={inputSchema:d,outputSchema:h,annotations:g,description:u,deps:r}},[g,r,u,d,A,l,h]);let j=a(()=>d?e(d):void 0,[d]),M=a(()=>h?e(h):void 0,[h]),N=a(()=>d?c.object(d):null,[d]),P=n(async e=>{S(e=>({...e,isExecuting:!0,error:null}));try{let t=N?N.parse(e):e,n=await C.current(t);return D.current&&S(e=>({isExecuting:!1,lastResult:n,error:null,executionCount:e.executionCount+1})),w.current&&w.current(n,e),n}catch(t){let n=t instanceof Error?t:Error(String(t));throw D.current&&S(e=>({...e,isExecuting:!1,error:n})),T.current&&T.current(n,e),n}},[N]),F=o(P);i(()=>{F.current=P},[P]);let I=n(()=>{S({isExecuting:!1,lastResult:null,error:null,executionCount:0})},[]);return i(()=>{if(typeof window>`u`||!window.navigator?.modelContext){console.warn(`[useWebMCP] window.navigator.modelContext is not available. Tool "${l}" will not be registered.`);return}let e=window.navigator.modelContext,t=async e=>{try{let t=await F.current(e),n={content:[{type:`text`,text:E.current(t)}]};if(M){let e=m(t);if(!e)throw Error(`Tool "${l}" outputSchema requires the handler to return a JSON object result`);n.structuredContent=e}return n}catch(e){return{content:[{type:`text`,text:`Error: ${e instanceof Error?e.message:String(e)}`}],isError:!0}}},n={type:`object`,properties:{}},r=Symbol(l);return e.registerTool({name:l,description:u,inputSchema:j||n,...M&&{outputSchema:M},...g&&{annotations:g},execute:t}),p.set(l,r),()=>{if(p.get(l)===r){p.delete(l);try{e.unregisterTool(l)}catch(e){A&&console.warn(`[useWebMCP] Failed to unregister tool "${l}":`,e)}}}},[l,u,j,M,g,A,...r??[]]),{state:x,execute:P,reset:I}}function g(e,t,n){let r=o(n);return r.current=n,h({name:e,description:t,annotations:a(()=>({title:`Context: ${e}`,readOnlyHint:!0,idempotentHint:!0,destructiveHint:!1,openWorldHint:!1}),[e]),handler:async e=>r.current(),formatOutput:e=>typeof e==`string`?e:JSON.stringify(e,null,2)})}function _(t){let{name:n,description:r,argsSchema:c,get:l}=t,[u,d]=s(!1),f=o(l),p=(()=>{let e=globalThis.process?.env?.NODE_ENV;return e===void 0?!1:e!==`production`})(),m=a(()=>c?e(c):void 0,[c]);return i(()=>{f.current=l},[l]),i(()=>{if(typeof window>`u`||!window.navigator?.modelContext){p&&console.warn(`[useWebMCPPrompt] window.navigator.modelContext is not available. Prompt "${n}" will not be registered.`);return}let e=window.navigator.modelContext,t=async e=>f.current(e),i;try{i=e.registerPrompt({name:n,...r!==void 0&&{description:r},...m&&{argsSchema:m},get:t})}catch(e){throw d(!1),e}if(!i){p&&console.warn(`[useWebMCPPrompt] Prompt "${n}" did not return a registration handle.`),d(!1);return}return p&&console.log(`[useWebMCPPrompt] Registered prompt: ${n}`),d(!0),()=>{i.unregister(),p&&console.log(`[useWebMCPPrompt] Unregistered prompt: ${n}`),d(!1)}},[n,r,m,p]),{isRegistered:u}}function v(e){let{uri:t,name:n,description:r,mimeType:a,read:c}=e,[l,u]=s(!1),d=o(c),f=(()=>{let e=globalThis.process?.env?.NODE_ENV;return e===void 0?!1:e!==`production`})();return i(()=>{d.current=c},[c]),i(()=>{if(typeof window>`u`||!window.navigator?.modelContext){f&&console.warn(`[useWebMCPResource] window.navigator.modelContext is not available. Resource "${t}" will not be registered.`);return}let e=window.navigator.modelContext,i=async(e,t)=>d.current(e,t),o;try{o=e.registerResource({uri:t,name:n,...r!==void 0&&{description:r},...a!==void 0&&{mimeType:a},read:i})}catch(e){throw u(!1),e}if(!o){f&&console.warn(`[useWebMCPResource] Resource "${t}" did not return a registration handle.`),u(!1);return}return f&&console.log(`[useWebMCPResource] Registered resource: ${t}`),u(!0),()=>{o.unregister(),f&&console.log(`[useWebMCPResource] Unregistered resource: ${t}`),u(!1)}},[t,n,r,a,f]),{isRegistered:l}}function y(e={}){let{onSuccess:t,onError:r}=e,[i,a]=s({isLoading:!1,result:null,error:null,requestCount:0}),o=n(()=>{a({isLoading:!1,result:null,error:null,requestCount:0})},[]);return{state:i,elicitInput:n(async e=>{if(typeof window>`u`||!window.navigator?.modelContext)throw Error(`navigator.modelContext is not available`);let n=window.navigator.modelContext;a(e=>({...e,isLoading:!0,error:null}));try{let r=await n.elicitInput(e);return a(e=>({isLoading:!1,result:r,error:null,requestCount:e.requestCount+1})),t?.(r),r}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 b(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`);let n=window.navigator.modelContext;a(e=>({...e,isLoading:!0,error:null}));try{let r=await n.createMessage(e);return a(e=>({isLoading:!1,result:r,error:null,requestCount:e.requestCount+1})),t?.(r),r}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 x=t(null),S={};function C({children:e,client:t,transport:r,opts:a}){let[c,f]=s([]),[p,m]=s([]),[h,g]=s(!1),[_,v]=s(null),[y,b]=s(!1),[C,w]=s(null),T=a??S,E=o(`disconnected`),D=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]),O=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]),k=n(async()=>{if(!t||!r)throw Error(`Client or transport not available`);if(E.current===`disconnected`){E.current=`connecting`,g(!0),v(null);try{await t.connect(r,T);let e=t.getServerCapabilities();b(!0),w(e||null),E.current=`connected`,await Promise.all([D(),O()])}catch(e){let t=e instanceof Error?e:Error(String(e));throw E.current=`disconnected`,v(t),t}finally{g(!1)}}},[t,r,T,D,O]);return i(()=>{if(!y||!t)return;let e=t.getServerCapabilities();return e?.resources?.listChanged&&t.setNotificationHandler(l,()=>{D().catch(console.error)}),e?.tools?.listChanged&&t.setNotificationHandler(u,()=>{O().catch(console.error)}),Promise.all([D(),O()]).catch(console.error),()=>{e?.resources?.listChanged&&t.removeNotificationHandler(`notifications/resources/list_changed`),e?.tools?.listChanged&&t.removeNotificationHandler(`notifications/tools/list_changed`)}},[t,y,D,O]),i(()=>(k().catch(e=>{console.error(`Failed to connect MCP client:`,e)}),()=>{E.current=`disconnected`,b(!1)}),[t,r]),d(x.Provider,{value:{client:t,tools:p,resources:c,isConnected:y,isLoading:h,error:_,capabilities:C,reconnect:k},children:e})}function w(){let e=r(x);if(!e)throw Error(`useMcpClient must be used within an McpClientProvider`);return e}export{C as McpClientProvider,y as useElicitation,y as useElicitationHandler,w as useMcpClient,b as useSampling,b as useSamplingHandler,h as useWebMCP,g as useWebMCPContext,_ as useWebMCPPrompt,v as useWebMCPResource};
2
2
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["validatedInput: TInput","response: CallToolResult"],"sources":["../src/useWebMCP.ts","../src/useWebMCPContext.ts","../src/useWebMCPPrompt.ts","../src/useWebMCPResource.ts","../src/useElicitationHandler.ts","../src/useSamplingHandler.ts","../src/client/McpClientProvider.tsx"],"sourcesContent":["import type { InputSchema } from '@mcp-b/global';\nimport { zodToJsonSchema } from '@mcp-b/global';\nimport type { CallToolResult } from '@mcp-b/webmcp-ts-sdk';\nimport type { DependencyList } from 'react';\nimport { useCallback, useEffect, useMemo, useRef, useState } from 'react';\nimport { z } from 'zod';\nimport type { InferOutput, ToolExecutionState, WebMCPConfig, WebMCPReturn } from './types.js';\n\n/**\n * Default output formatter that converts values to formatted JSON strings.\n *\n * String values are returned as-is; all other types are serialized to\n * indented JSON for readability.\n *\n * @internal\n */\nfunction defaultFormatOutput(output: unknown): string {\n if (typeof output === 'string') {\n return output;\n }\n return JSON.stringify(output, null, 2);\n}\n\n/**\n * React hook for registering and managing Model Context Protocol (MCP) tools.\n *\n * This hook handles the complete lifecycle of an MCP tool:\n * - Registers the tool with `window.navigator.modelContext`\n * - Manages execution state (loading, results, errors)\n * - Validates input using Zod schemas\n * - Handles tool execution and lifecycle callbacks\n * - Automatically unregisters on component unmount\n * - Returns `structuredContent` when `outputSchema` is defined\n *\n * ## Output Schema (Recommended)\n *\n * Always define an `outputSchema` for your tools. This provides:\n * - **Type Safety**: Handler return type is inferred from the schema\n * - **MCP structuredContent**: AI models receive structured, typed data\n * - **Better AI Understanding**: Models can reason about your tool's output format\n *\n * ```tsx\n * useWebMCP({\n * name: 'get_user',\n * description: 'Get user by ID',\n * inputSchema: { userId: z.string() },\n * outputSchema: {\n * id: z.string(),\n * name: z.string(),\n * email: z.string().email(),\n * },\n * handler: async ({ userId }) => {\n * const user = await fetchUser(userId);\n * return { id: user.id, name: user.name, email: user.email };\n * },\n * });\n * ```\n *\n * ## Re-render Optimization\n *\n * This hook is optimized to minimize unnecessary tool re-registrations:\n *\n * - **Memoized JSON conversion**: Zod-to-JSON schema conversions are memoized to\n * avoid recomputation on every render.\n *\n * - **Ref-based callbacks**: `handler`, `onSuccess`, `onError`, and `formatOutput`\n * are stored in refs, so changing these functions won't trigger re-registration.\n *\n * **IMPORTANT**: If `inputSchema`, `outputSchema`, or `annotations` are defined inline\n * or change on every render, the tool will re-register unnecessarily. To avoid this,\n * memoize these values using `useMemo` or define them outside your component:\n *\n * ```tsx\n * // Good: Memoized schema (won't change unless deps change)\n * const outputSchema = useMemo(() => ({\n * count: z.number(),\n * items: z.array(z.string()),\n * }), []);\n *\n * // Good: Static schema defined outside component\n * const OUTPUT_SCHEMA = {\n * count: z.number(),\n * items: z.array(z.string()),\n * };\n *\n * // Bad: Inline schema (creates new object every render)\n * useWebMCP({\n * outputSchema: { count: z.number() }, // Re-registers every render!\n * });\n * ```\n *\n * **What triggers re-registration:**\n * - Changes to `name` or `description`\n * - Changes to `inputSchema`, `outputSchema`, or `annotations` (reference comparison)\n * - Changes to any value in the `deps` argument (reference comparison)\n *\n * **What does NOT trigger re-registration:**\n * - Changes to `handler`, `onSuccess`, `onError`, or `formatOutput` functions\n *\n * @template TInputSchema - Zod schema object defining input parameter types\n * @template TOutputSchema - Zod schema object defining output structure (enables structuredContent)\n *\n * @param config - Configuration object for the tool\n * @param deps - Optional dependency array that triggers tool re-registration when values change.\n * Similar to React's `useEffect` dependencies. When any value changes (by reference),\n * the tool will be unregistered and re-registered. Prefer primitive values over\n * objects/arrays to minimize re-registrations.\n *\n * @returns Object containing execution state and control methods\n *\n * @remarks\n * The hook uses React refs to store callbacks (`handler`, `onSuccess`, `onError`, `formatOutput`)\n * which prevents re-registration when these functions change. This is a performance optimization\n * that follows the \"latest ref\" pattern.\n *\n * When `outputSchema` is provided, the MCP response includes both text content and\n * `structuredContent` per the MCP specification. The type system ensures that the handler's\n * return type matches the output schema through Zod's type inference.\n *\n * @public\n *\n * @example\n * Basic tool with outputSchema (recommended):\n * ```tsx\n * function PostActions() {\n * const likeTool = useWebMCP({\n * name: 'posts_like',\n * description: 'Like a post by ID',\n * inputSchema: {\n * postId: z.string().uuid().describe('The ID of the post to like'),\n * },\n * outputSchema: {\n * success: z.boolean().describe('Whether the like was successful'),\n * likeCount: z.number().describe('Updated like count'),\n * },\n * handler: async ({ postId }) => {\n * const result = await api.posts.like(postId);\n * return { success: true, likeCount: result.likes };\n * },\n * });\n *\n * // likeTool.state.lastResult is typed as { success: boolean; likeCount: number } | null\n * if (likeTool.state.isExecuting) {\n * return <Spinner />;\n * }\n *\n * return <div>Likes: {likeTool.state.lastResult?.likeCount ?? 0}</div>;\n * }\n * ```\n *\n * @example\n * Tool with annotations and callbacks:\n * ```tsx\n * const deleteTool = useWebMCP({\n * name: 'posts_delete',\n * description: 'Delete a post permanently',\n * inputSchema: {\n * postId: z.string().uuid(),\n * },\n * outputSchema: {\n * deleted: z.boolean(),\n * deletedAt: z.string().describe('ISO timestamp of deletion'),\n * },\n * annotations: {\n * destructiveHint: true,\n * idempotentHint: false,\n * },\n * handler: async ({ postId }) => {\n * await api.posts.delete(postId);\n * return { deleted: true, deletedAt: new Date().toISOString() };\n * },\n * onSuccess: () => {\n * navigate('/posts');\n * toast.success('Post deleted');\n * },\n * onError: (error) => {\n * toast.error(`Failed to delete: ${error.message}`);\n * },\n * });\n * ```\n *\n * @example\n * Tool with deps for automatic re-registration:\n * ```tsx\n * function SitesManager({ sites }: { sites: Site[] }) {\n * // Without deps, you'd need getter functions like: getSiteCount: () => sites.length\n * // With deps, values can be used directly in description and handler\n * const sitesTool = useWebMCP(\n * {\n * name: 'sites_query',\n * description: `Query available sites. Current count: ${sites.length}`,\n * outputSchema: {\n * count: z.number(),\n * sites: z.array(z.object({ id: z.string(), name: z.string() })),\n * },\n * handler: async () => ({\n * count: sites.length,\n * sites: sites.map(s => ({ id: s.id, name: s.name })),\n * }),\n * },\n * [sites] // Re-register tool when sites array changes (by reference)\n * );\n *\n * return <SitesList sites={sites} />;\n * }\n * ```\n *\n * @example\n * Optimizing with memoization and deps:\n * ```tsx\n * function OptimizedSites({ sites }: { sites: Site[] }) {\n * // Memoize schema to prevent re-registration on every render\n * const outputSchema = useMemo(() => ({\n * sites: z.array(z.object({ id: z.string(), name: z.string() })),\n * }), []);\n *\n * // Use primitive values in deps for better control\n * const siteIds = sites.map(s => s.id).join(',');\n * const siteCount = sites.length;\n *\n * const sitesTool = useWebMCP(\n * {\n * name: 'sites_query',\n * description: `Query ${siteCount} available sites`,\n * outputSchema,\n * handler: async () => ({\n * sites: sites.map(s => ({ id: s.id, name: s.name })),\n * }),\n * },\n * [siteIds, siteCount] // Only re-register when IDs or count actually change\n * );\n *\n * return <SitesList sites={sites} />;\n * }\n * ```\n */\nexport function useWebMCP<\n TInputSchema extends Record<string, z.ZodTypeAny> = Record<string, never>,\n TOutputSchema extends Record<string, z.ZodTypeAny> = Record<string, never>,\n>(\n config: WebMCPConfig<TInputSchema, TOutputSchema>,\n deps?: DependencyList\n): WebMCPReturn<TOutputSchema> {\n type TOutput = InferOutput<TOutputSchema>;\n type TInput = z.infer<z.ZodObject<TInputSchema>>;\n const {\n name,\n description,\n inputSchema,\n outputSchema,\n annotations,\n handler,\n formatOutput = defaultFormatOutput,\n onSuccess,\n onError,\n } = config;\n\n const [state, setState] = useState<ToolExecutionState<TOutput>>({\n isExecuting: false,\n lastResult: null,\n error: null,\n executionCount: 0,\n });\n\n const handlerRef = useRef(handler);\n const onSuccessRef = useRef(onSuccess);\n const onErrorRef = useRef(onError);\n const formatOutputRef = useRef(formatOutput);\n const isMountedRef = useRef(true);\n const warnedRef = useRef(new Set<string>());\n const prevConfigRef = useRef({\n inputSchema,\n outputSchema,\n annotations,\n description,\n deps,\n });\n const isDev = (() => {\n const env = (globalThis as { process?: { env?: { NODE_ENV?: string } } }).process?.env\n ?.NODE_ENV;\n return env !== undefined ? env !== 'production' : false;\n })();\n\n // Update refs when callbacks change (doesn't trigger re-registration)\n useEffect(() => {\n handlerRef.current = handler;\n onSuccessRef.current = onSuccess;\n onErrorRef.current = onError;\n formatOutputRef.current = formatOutput;\n }, [handler, onSuccess, onError, formatOutput]);\n\n // Cleanup: mark component as unmounted\n useEffect(() => {\n isMountedRef.current = true;\n return () => {\n isMountedRef.current = false;\n };\n }, []);\n\n useEffect(() => {\n if (!isDev) {\n prevConfigRef.current = { inputSchema, outputSchema, annotations, description, deps };\n return;\n }\n\n const warnOnce = (key: string, message: string) => {\n if (warnedRef.current.has(key)) {\n return;\n }\n console.warn(`[useWebMCP] ${message}`);\n warnedRef.current.add(key);\n };\n\n const prev = prevConfigRef.current;\n\n if (inputSchema && prev.inputSchema && prev.inputSchema !== inputSchema) {\n warnOnce(\n 'inputSchema',\n `Tool \"${name}\" inputSchema reference changed; memoize or define it outside the component to avoid re-registration.`\n );\n }\n\n if (outputSchema && prev.outputSchema && prev.outputSchema !== outputSchema) {\n warnOnce(\n 'outputSchema',\n `Tool \"${name}\" outputSchema reference changed; memoize or define it outside the component to avoid re-registration.`\n );\n }\n\n if (annotations && prev.annotations && prev.annotations !== annotations) {\n warnOnce(\n 'annotations',\n `Tool \"${name}\" annotations reference changed; memoize or define it outside the component to avoid re-registration.`\n );\n }\n\n if (description !== prev.description) {\n warnOnce(\n 'description',\n `Tool \"${name}\" description changed; this re-registers the tool. Memoize the description if it does not need to update.`\n );\n }\n\n if (\n deps?.some(\n (value) => (typeof value === 'object' && value !== null) || typeof value === 'function'\n )\n ) {\n warnOnce(\n 'deps',\n `Tool \"${name}\" deps contains non-primitive values; prefer primitives or memoize objects/functions to reduce re-registration.`\n );\n }\n\n prevConfigRef.current = { inputSchema, outputSchema, annotations, description, deps };\n }, [annotations, deps, description, inputSchema, isDev, name, outputSchema]);\n\n // Stable schema memoization: Convert to JSON strings first, then memoize based on string value.\n // This prevents infinite re-registration when users pass inline schema objects, because\n // strings are compared by value (not reference) in React's dependency comparison.\n const inputSchemaStr = inputSchema ? JSON.stringify(zodToJsonSchema(inputSchema)) : null;\n const outputSchemaStr = outputSchema ? JSON.stringify(zodToJsonSchema(outputSchema)) : null;\n const annotationsStr = annotations ? JSON.stringify(annotations) : null;\n\n const inputJsonSchema = useMemo(\n () => (inputSchemaStr ? JSON.parse(inputSchemaStr) : undefined),\n [inputSchemaStr]\n );\n const outputJsonSchema = useMemo(\n () => (outputSchemaStr ? JSON.parse(outputSchemaStr) : undefined),\n [outputSchemaStr]\n );\n const stableAnnotations = useMemo(\n () => (annotationsStr ? JSON.parse(annotationsStr) : undefined),\n [annotationsStr]\n );\n\n const validator = useMemo(() => (inputSchema ? z.object(inputSchema) : null), [inputSchema]);\n\n /**\n * Executes the tool handler with input validation and state management.\n *\n * @param input - The input parameters to validate and pass to the handler\n * @returns Promise resolving to the handler's output\n * @throws Error if validation fails or the handler throws\n */\n const execute = useCallback(\n async (input: unknown): Promise<TOutput> => {\n setState((prev) => ({\n ...prev,\n isExecuting: true,\n error: null,\n }));\n\n try {\n const validatedInput: TInput = validator ? validator.parse(input) : (input as TInput);\n const result = await handlerRef.current(validatedInput);\n\n // Only update state if component is still mounted\n if (isMountedRef.current) {\n setState((prev) => ({\n isExecuting: false,\n lastResult: result,\n error: null,\n executionCount: prev.executionCount + 1,\n }));\n }\n\n if (onSuccessRef.current) {\n onSuccessRef.current(result, input);\n }\n\n return result;\n } catch (error) {\n const err = error instanceof Error ? error : new Error(String(error));\n\n // Only update state if component is still mounted\n if (isMountedRef.current) {\n setState((prev) => ({\n ...prev,\n isExecuting: false,\n error: err,\n }));\n }\n\n if (onErrorRef.current) {\n onErrorRef.current(err, input);\n }\n\n throw err;\n }\n },\n [validator]\n );\n\n /**\n * Resets the execution state to initial values.\n */\n const reset = useCallback(() => {\n setState({\n isExecuting: false,\n lastResult: null,\n error: null,\n executionCount: 0,\n });\n }, []);\n\n useEffect(() => {\n if (typeof window === 'undefined' || !window.navigator?.modelContext) {\n console.warn(\n `[useWebMCP] window.navigator.modelContext is not available. Tool \"${name}\" will not be registered.`\n );\n return;\n }\n\n /**\n * Handles MCP tool execution by running the handler and formatting the response.\n *\n * @param input - The input parameters from the MCP client\n * @returns CallToolResult with text content and optional structuredContent\n */\n const mcpHandler = async (input: unknown): Promise<CallToolResult> => {\n try {\n const result = await execute(input);\n const formattedOutput = formatOutputRef.current(result);\n\n const response: CallToolResult = {\n content: [\n {\n type: 'text',\n text: formattedOutput,\n },\n ],\n };\n\n if (outputJsonSchema) {\n response.structuredContent = result as Record<string, unknown>;\n }\n\n return response;\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n\n return {\n content: [\n {\n type: 'text',\n text: `Error: ${errorMessage}`,\n },\n ],\n isError: true,\n };\n }\n };\n\n const fallbackInputSchema: InputSchema = {\n type: 'object',\n properties: {},\n };\n\n const registration = window.navigator.modelContext?.registerTool({\n name,\n description,\n inputSchema: (inputJsonSchema || fallbackInputSchema) as InputSchema,\n ...(outputJsonSchema && { outputSchema: outputJsonSchema as InputSchema }),\n ...(stableAnnotations && { annotations: stableAnnotations }),\n execute: mcpHandler,\n });\n\n return () => {\n registration?.unregister();\n };\n // Spread operator in dependencies: Allows users to provide additional dependencies\n // via the `deps` parameter. While unconventional, this pattern is intentional to support\n // dynamic dependency injection. The spread is safe because deps is validated and warned\n // about non-primitive values earlier in this hook.\n }, [\n name,\n description,\n inputJsonSchema,\n outputJsonSchema,\n stableAnnotations,\n ...(deps ?? []),\n execute,\n ]);\n\n return {\n state,\n execute,\n reset,\n };\n}\n","import { useRef } from 'react';\nimport type { WebMCPReturn } from './types.js';\nimport { useWebMCP } from './useWebMCP.js';\n\n/**\n * Convenience hook for exposing read-only context data to AI assistants.\n *\n * This is a simplified wrapper around {@link useWebMCP} specifically designed for\n * context tools that expose data without performing actions. The hook automatically\n * configures appropriate annotations (read-only, idempotent) and handles value\n * serialization.\n *\n * Note: This hook does not use an output schema, so the result will not include\n * `structuredContent` in the MCP response. Use {@link useWebMCP} directly with\n * `outputSchema` if you need structured output for MCP compliance.\n *\n * @template T - The type of context data to expose\n *\n * @param name - Unique identifier for the context tool (e.g., 'context_current_post')\n * @param description - Human-readable description of the context for AI assistants\n * @param getValue - Function that returns the current context value\n * @returns Tool execution state and control methods\n *\n * @public\n *\n * @example\n * Expose current post context:\n * ```tsx\n * function PostDetailPage() {\n * const { postId } = useParams();\n * const { data: post } = useQuery(['post', postId], () => fetchPost(postId));\n *\n * useWebMCPContext(\n * 'context_current_post',\n * 'Get the currently viewed post ID and metadata',\n * () => ({\n * postId,\n * title: post?.title,\n * author: post?.author,\n * tags: post?.tags,\n * createdAt: post?.createdAt,\n * })\n * );\n *\n * return <PostContent post={post} />;\n * }\n * ```\n *\n * @example\n * Expose user session context:\n * ```tsx\n * function AppRoot() {\n * const { user, isAuthenticated } = useAuth();\n *\n * useWebMCPContext(\n * 'context_user_session',\n * 'Get the current user session information',\n * () => ({\n * isAuthenticated,\n * userId: user?.id,\n * email: user?.email,\n * permissions: user?.permissions,\n * })\n * );\n *\n * return <App />;\n * }\n * ```\n */\nexport function useWebMCPContext<T>(\n name: string,\n description: string,\n getValue: () => T\n): WebMCPReturn {\n const getValueRef = useRef(getValue);\n getValueRef.current = getValue;\n\n // Use default generics (no input/output schema) since context tools\n // don't define structured schemas. The handler returns T but it's\n // treated as `unknown` in the return type since no outputSchema is defined.\n return useWebMCP({\n name,\n description,\n annotations: {\n title: `Context: ${name}`,\n readOnlyHint: true,\n idempotentHint: true,\n destructiveHint: false,\n openWorldHint: false,\n },\n // Cast to unknown since context tools return arbitrary types\n // that don't need to conform to a specific schema\n handler: async (_input: Record<string, unknown>) => {\n return getValueRef.current() as Record<string, unknown>;\n },\n formatOutput: (output) => {\n if (typeof output === 'string') {\n return output as string;\n }\n return JSON.stringify(output, null, 2);\n },\n });\n}\n","import type { InputSchema } from '@mcp-b/global';\nimport { zodToJsonSchema } from '@mcp-b/global';\nimport { useEffect, useMemo, useRef, useState } from 'react';\nimport type { z } from 'zod';\nimport type { PromptMessage, WebMCPPromptConfig, WebMCPPromptReturn } from './types.js';\n\n/**\n * React hook for registering Model Context Protocol (MCP) prompts.\n *\n * This hook handles the complete lifecycle of an MCP prompt:\n * - Registers the prompt with `window.navigator.modelContext`\n * - Converts Zod schemas to JSON Schema for argument validation\n * - Automatically unregisters on component unmount\n *\n * @template TArgsSchema - Zod schema object defining argument types\n *\n * @param config - Configuration object for the prompt\n * @returns Object indicating registration status\n *\n * @public\n *\n * @example\n * Simple prompt without arguments:\n * ```tsx\n * function HelpPrompt() {\n * const { isRegistered } = useWebMCPPrompt({\n * name: 'help',\n * description: 'Get help with using the application',\n * get: async () => ({\n * messages: [{\n * role: 'user',\n * content: { type: 'text', text: 'How do I use this application?' }\n * }]\n * }),\n * });\n *\n * return <div>Help prompt {isRegistered ? 'ready' : 'loading'}</div>;\n * }\n * ```\n *\n * @example\n * Prompt with typed arguments:\n * ```tsx\n * function CodeReviewPrompt() {\n * const { isRegistered } = useWebMCPPrompt({\n * name: 'review_code',\n * description: 'Review code for best practices',\n * argsSchema: {\n * code: z.string().describe('The code to review'),\n * language: z.string().optional().describe('Programming language'),\n * },\n * get: async ({ code, language }) => ({\n * messages: [{\n * role: 'user',\n * content: {\n * type: 'text',\n * text: `Please review this ${language ?? ''} code:\\n\\n${code}`\n * }\n * }]\n * }),\n * });\n *\n * return <div>Code review prompt {isRegistered ? 'ready' : 'loading'}</div>;\n * }\n * ```\n */\nexport function useWebMCPPrompt<\n TArgsSchema extends Record<string, z.ZodTypeAny> = Record<string, never>,\n>(config: WebMCPPromptConfig<TArgsSchema>): WebMCPPromptReturn {\n const { name, description, argsSchema, get } = config;\n\n const [isRegistered, setIsRegistered] = useState(false);\n\n const getRef = useRef(get);\n\n const isDev = (() => {\n const env = (globalThis as { process?: { env?: { NODE_ENV?: string } } }).process?.env\n ?.NODE_ENV;\n return env !== undefined ? env !== 'production' : false;\n })();\n\n const argsJsonSchema = useMemo(\n () => (argsSchema ? zodToJsonSchema(argsSchema) : undefined),\n [argsSchema]\n );\n\n useEffect(() => {\n getRef.current = get;\n }, [get]);\n\n useEffect(() => {\n if (typeof window === 'undefined' || !window.navigator?.modelContext) {\n if (isDev) {\n console.warn(\n `[useWebMCPPrompt] window.navigator.modelContext is not available. Prompt \"${name}\" will not be registered.`\n );\n }\n return;\n }\n\n const promptHandler = async (\n args: Record<string, unknown>\n ): Promise<{ messages: PromptMessage[] }> => {\n return getRef.current(args as never);\n };\n\n const registration = window.navigator.modelContext?.registerPrompt({\n name,\n ...(description !== undefined && { description }),\n ...(argsJsonSchema && { argsSchema: argsJsonSchema as InputSchema }),\n get: promptHandler,\n });\n\n if (isDev) {\n console.log(`[useWebMCPPrompt] Registered prompt: ${name}`);\n }\n setIsRegistered(true);\n\n return () => {\n if (registration) {\n registration.unregister();\n if (isDev) {\n console.log(`[useWebMCPPrompt] Unregistered prompt: ${name}`);\n }\n setIsRegistered(false);\n }\n };\n }, [name, description, argsJsonSchema, isDev]);\n\n return {\n isRegistered,\n };\n}\n","import { useEffect, useRef, useState } from 'react';\nimport type { ResourceContents, WebMCPResourceConfig, WebMCPResourceReturn } from './types.js';\n\n/**\n * React hook for registering Model Context Protocol (MCP) resources.\n *\n * This hook handles the complete lifecycle of an MCP resource:\n * - Registers the resource with `window.navigator.modelContext`\n * - Supports both static URIs and URI templates with parameters\n * - Automatically unregisters on component unmount\n *\n * @param config - Configuration object for the resource\n * @returns Object indicating registration status\n *\n * @public\n *\n * @example\n * Static resource:\n * ```tsx\n * function AppSettingsResource() {\n * const { isRegistered } = useWebMCPResource({\n * uri: 'config://app-settings',\n * name: 'App Settings',\n * description: 'Application configuration',\n * mimeType: 'application/json',\n * read: async (uri) => ({\n * contents: [{\n * uri: uri.href,\n * text: JSON.stringify({ theme: 'dark', language: 'en' })\n * }]\n * }),\n * });\n *\n * return <div>Settings resource {isRegistered ? 'ready' : 'loading'}</div>;\n * }\n * ```\n *\n * @example\n * Dynamic resource with URI template:\n * ```tsx\n * function UserProfileResource() {\n * const { isRegistered } = useWebMCPResource({\n * uri: 'user://{userId}/profile',\n * name: 'User Profile',\n * description: 'User profile data by ID',\n * mimeType: 'application/json',\n * read: async (uri, params) => {\n * const userId = params?.userId ?? '';\n * const profile = await fetchUserProfile(userId);\n * return {\n * contents: [{\n * uri: uri.href,\n * text: JSON.stringify(profile)\n * }]\n * };\n * },\n * });\n *\n * return <div>User profile resource {isRegistered ? 'ready' : 'loading'}</div>;\n * }\n * ```\n */\nexport function useWebMCPResource(config: WebMCPResourceConfig): WebMCPResourceReturn {\n const { uri, name, description, mimeType, read } = config;\n\n const [isRegistered, setIsRegistered] = useState(false);\n\n const readRef = useRef(read);\n\n const isDev = (() => {\n const env = (globalThis as { process?: { env?: { NODE_ENV?: string } } }).process?.env\n ?.NODE_ENV;\n return env !== undefined ? env !== 'production' : false;\n })();\n\n useEffect(() => {\n readRef.current = read;\n }, [read]);\n\n useEffect(() => {\n if (typeof window === 'undefined' || !window.navigator?.modelContext) {\n if (isDev) {\n console.warn(\n `[useWebMCPResource] window.navigator.modelContext is not available. Resource \"${uri}\" will not be registered.`\n );\n }\n return;\n }\n\n const resourceHandler = async (\n resolvedUri: URL,\n params?: Record<string, string>\n ): Promise<{ contents: ResourceContents[] }> => {\n return readRef.current(resolvedUri, params);\n };\n\n const registration = window.navigator.modelContext?.registerResource({\n uri,\n name,\n ...(description !== undefined && { description }),\n ...(mimeType !== undefined && { mimeType }),\n read: resourceHandler,\n });\n\n if (isDev) {\n console.log(`[useWebMCPResource] Registered resource: ${uri}`);\n }\n setIsRegistered(true);\n\n return () => {\n if (registration) {\n registration.unregister();\n if (isDev) {\n console.log(`[useWebMCPResource] Unregistered resource: ${uri}`);\n }\n setIsRegistered(false);\n }\n };\n }, [uri, name, description, mimeType, isDev]);\n\n return {\n isRegistered,\n };\n}\n","import type { ElicitationParams, ElicitationResult } from '@mcp-b/global';\nimport { useCallback, useState } from 'react';\n\n/**\n * State for elicitation requests, tracking the current request and results.\n */\nexport interface ElicitationState {\n /** Whether an elicitation request is currently in progress */\n isLoading: boolean;\n /** The last elicitation result received */\n result: ElicitationResult | null;\n /** Any error that occurred during the last request */\n error: Error | null;\n /** Total number of requests made */\n requestCount: number;\n}\n\n/**\n * Configuration options for the useElicitation hook.\n */\nexport interface UseElicitationConfig {\n /**\n * Optional callback invoked when an elicitation request completes successfully.\n */\n onSuccess?: (result: ElicitationResult) => void;\n\n /**\n * Optional callback invoked when an elicitation request fails.\n */\n onError?: (error: Error) => void;\n}\n\n/**\n * Return value from the useElicitation hook.\n */\nexport interface UseElicitationReturn {\n /** Current state of elicitation */\n state: ElicitationState;\n /** Function to request user input from the connected client */\n elicitInput: (params: ElicitationParams) => Promise<ElicitationResult>;\n /** Reset the state */\n reset: () => void;\n}\n\n/**\n * React hook for requesting user input from the connected MCP client.\n *\n * Elicitation allows the server (webpage) to request user input from the\n * connected client. This is useful when the page needs additional information\n * from the user, such as API keys, configuration options, or confirmations.\n *\n * There are two modes:\n * 1. **Form mode**: For non-sensitive data collection using a schema-driven form.\n * 2. **URL mode**: For sensitive data collection via a web URL (API keys, OAuth, etc.).\n *\n * @param config - Optional configuration including callbacks\n * @returns Object containing state and the elicitInput function\n *\n * @example Form elicitation:\n * ```tsx\n * function ConfigForm() {\n * const { state, elicitInput } = useElicitation({\n * onSuccess: (result) => console.log('Got input:', result),\n * onError: (error) => console.error('Elicitation failed:', error),\n * });\n *\n * const handleConfigure = async () => {\n * const result = await elicitInput({\n * message: 'Please provide your configuration',\n * requestedSchema: {\n * type: 'object',\n * properties: {\n * apiKey: { type: 'string', title: 'API Key', description: 'Your API key' },\n * model: { type: 'string', enum: ['gpt-4', 'gpt-3.5'], title: 'Model' }\n * },\n * required: ['apiKey']\n * }\n * });\n *\n * if (result.action === 'accept') {\n * console.log('Config:', result.content);\n * }\n * };\n *\n * return (\n * <button onClick={handleConfigure} disabled={state.isLoading}>\n * Configure\n * </button>\n * );\n * }\n * ```\n *\n * @example URL elicitation (for sensitive data):\n * ```tsx\n * const { elicitInput } = useElicitation();\n *\n * const handleOAuth = async () => {\n * const result = await elicitInput({\n * mode: 'url',\n * message: 'Please authenticate with GitHub',\n * elicitationId: 'github-oauth-123',\n * url: 'https://github.com/login/oauth/authorize?client_id=...'\n * });\n *\n * if (result.action === 'accept') {\n * console.log('OAuth completed');\n * }\n * };\n * ```\n */\nexport function useElicitation(config: UseElicitationConfig = {}): UseElicitationReturn {\n const { onSuccess, onError } = config;\n\n const [state, setState] = useState<ElicitationState>({\n isLoading: false,\n result: null,\n error: null,\n requestCount: 0,\n });\n\n const reset = useCallback(() => {\n setState({\n isLoading: false,\n result: null,\n error: null,\n requestCount: 0,\n });\n }, []);\n\n const elicitInput = useCallback(\n async (params: ElicitationParams): Promise<ElicitationResult> => {\n if (typeof window === 'undefined' || !window.navigator?.modelContext) {\n throw new Error('navigator.modelContext is not available');\n }\n\n setState((prev) => ({\n ...prev,\n isLoading: true,\n error: null,\n }));\n\n try {\n const result = await window.navigator.modelContext.elicitInput(params);\n\n setState((prev) => ({\n isLoading: false,\n result,\n error: null,\n requestCount: prev.requestCount + 1,\n }));\n\n onSuccess?.(result);\n return result;\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n\n setState((prev) => ({\n ...prev,\n isLoading: false,\n error,\n }));\n\n onError?.(error);\n throw error;\n }\n },\n [onSuccess, onError]\n );\n\n return {\n state,\n elicitInput,\n reset,\n };\n}\n\n// Also export with the old name for backwards compatibility during migration\nexport { useElicitation as useElicitationHandler };\nexport type { ElicitationState as ElicitationHandlerState };\nexport type { UseElicitationConfig as UseElicitationHandlerConfig };\nexport type { UseElicitationReturn as UseElicitationHandlerReturn };\n","import type { SamplingRequestParams, SamplingResult } from '@mcp-b/global';\nimport { useCallback, useState } from 'react';\n\n/**\n * State for sampling requests, tracking the current request and results.\n */\nexport interface SamplingState {\n /** Whether a sampling request is currently in progress */\n isLoading: boolean;\n /** The last sampling result received */\n result: SamplingResult | null;\n /** Any error that occurred during the last request */\n error: Error | null;\n /** Total number of requests made */\n requestCount: number;\n}\n\n/**\n * Configuration options for the useSampling hook.\n */\nexport interface UseSamplingConfig {\n /**\n * Optional callback invoked when a sampling request completes successfully.\n */\n onSuccess?: (result: SamplingResult) => void;\n\n /**\n * Optional callback invoked when a sampling request fails.\n */\n onError?: (error: Error) => void;\n}\n\n/**\n * Return value from the useSampling hook.\n */\nexport interface UseSamplingReturn {\n /** Current state of sampling */\n state: SamplingState;\n /** Function to request LLM completion from the connected client */\n createMessage: (params: SamplingRequestParams) => Promise<SamplingResult>;\n /** Reset the state */\n reset: () => void;\n}\n\n/**\n * React hook for requesting LLM completions from the connected MCP client.\n *\n * Sampling allows the server (webpage) to request LLM completions from the\n * connected client. This is useful when the page needs AI capabilities like\n * summarization, generation, or analysis.\n *\n * @param config - Optional configuration including callbacks\n * @returns Object containing state and the createMessage function\n *\n * @example Basic usage:\n * ```tsx\n * function AIAssistant() {\n * const { state, createMessage } = useSampling({\n * onSuccess: (result) => console.log('Got response:', result),\n * onError: (error) => console.error('Sampling failed:', error),\n * });\n *\n * const handleAsk = async () => {\n * const result = await createMessage({\n * messages: [\n * { role: 'user', content: { type: 'text', text: 'What is 2+2?' } }\n * ],\n * maxTokens: 100,\n * });\n * console.log(result.content);\n * };\n *\n * return (\n * <div>\n * <button onClick={handleAsk} disabled={state.isLoading}>\n * Ask AI\n * </button>\n * {state.result && <p>{JSON.stringify(state.result.content)}</p>}\n * </div>\n * );\n * }\n * ```\n */\nexport function useSampling(config: UseSamplingConfig = {}): UseSamplingReturn {\n const { onSuccess, onError } = config;\n\n const [state, setState] = useState<SamplingState>({\n isLoading: false,\n result: null,\n error: null,\n requestCount: 0,\n });\n\n const reset = useCallback(() => {\n setState({\n isLoading: false,\n result: null,\n error: null,\n requestCount: 0,\n });\n }, []);\n\n const createMessage = useCallback(\n async (params: SamplingRequestParams): Promise<SamplingResult> => {\n if (typeof window === 'undefined' || !window.navigator?.modelContext) {\n throw new Error('navigator.modelContext is not available');\n }\n\n setState((prev) => ({\n ...prev,\n isLoading: true,\n error: null,\n }));\n\n try {\n const result = await window.navigator.modelContext.createMessage(params);\n\n setState((prev) => ({\n isLoading: false,\n result,\n error: null,\n requestCount: prev.requestCount + 1,\n }));\n\n onSuccess?.(result);\n return result;\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n\n setState((prev) => ({\n ...prev,\n isLoading: false,\n error,\n }));\n\n onError?.(error);\n throw error;\n }\n },\n [onSuccess, onError]\n );\n\n return {\n state,\n createMessage,\n reset,\n };\n}\n\n// Also export with the old name for backwards compatibility during migration\nexport { useSampling as useSamplingHandler };\nexport type { SamplingState as SamplingHandlerState };\nexport type { UseSamplingConfig as UseSamplingHandlerConfig };\nexport type { UseSamplingReturn as UseSamplingHandlerReturn };\n","import {\n type Client,\n type Tool as McpTool,\n type RequestOptions,\n type Resource,\n ResourceListChangedNotificationSchema,\n type ServerCapabilities,\n ToolListChangedNotificationSchema,\n type Transport,\n} from '@mcp-b/webmcp-ts-sdk';\nimport {\n createContext,\n type ReactElement,\n type ReactNode,\n useCallback,\n useContext,\n useEffect,\n useRef,\n useState,\n} from 'react';\n\n/**\n * Context value provided by McpClientProvider.\n *\n * @internal\n */\ninterface McpClientContextValue {\n client: Client;\n tools: McpTool[];\n resources: Resource[];\n isConnected: boolean;\n isLoading: boolean;\n error: Error | null;\n capabilities: ServerCapabilities | null;\n reconnect: () => Promise<void>;\n}\n\nconst McpClientContext = createContext<McpClientContextValue | null>(null);\n\n/**\n * Props for the McpClientProvider component.\n *\n * @public\n */\nexport interface McpClientProviderProps {\n /**\n * React children to render within the provider.\n */\n children: ReactNode;\n\n /**\n * MCP Client instance to use for communication.\n */\n client: Client;\n\n /**\n * Transport instance for the client to connect through.\n */\n transport: Transport;\n\n /**\n * Optional request options for the connection.\n */\n opts?: RequestOptions;\n}\n\n/**\n * Provider component that manages an MCP client connection and exposes\n * tools, resources, and connection state to child components.\n *\n * This provider handles:\n * - Establishing and maintaining the MCP client connection\n * - Fetching available tools and resources from the server\n * - Listening for server notifications about tool/resource changes\n * - Managing connection state and errors\n * - Automatic cleanup on unmount\n *\n * @param props - Component props\n * @returns Provider component wrapping children\n *\n * @public\n *\n * @example\n * Connect to an MCP server via tab transport:\n * ```tsx\n * import { Client } from '@modelcontextprotocol/sdk/client/index.js';\n * import { TabClientTransport } from '@mcp-b/transports';\n * import { McpClientProvider } from '@mcp-b/react-webmcp';\n *\n * const client = new Client(\n * { name: 'my-app', version: '1.0.0' },\n * { capabilities: {} }\n * );\n *\n * const transport = new TabClientTransport('mcp', {\n * clientInstanceId: 'my-app-instance',\n * });\n *\n * function App() {\n * return (\n * <McpClientProvider client={client} transport={transport}>\n * <MyAppContent />\n * </McpClientProvider>\n * );\n * }\n * ```\n *\n * @example\n * Access tools from child components:\n * ```tsx\n * function MyAppContent() {\n * const { tools, isConnected, isLoading } = useMcpClient();\n *\n * if (isLoading) {\n * return <div>Connecting to MCP server...</div>;\n * }\n *\n * if (!isConnected) {\n * return <div>Failed to connect to MCP server</div>;\n * }\n *\n * return (\n * <div>\n * <h2>Available Tools:</h2>\n * <ul>\n * {tools.map(tool => (\n * <li key={tool.name}>{tool.description}</li>\n * ))}\n * </ul>\n * </div>\n * );\n * }\n * ```\n */\nexport function McpClientProvider({\n children,\n client,\n transport,\n opts = {},\n}: McpClientProviderProps): ReactElement {\n const [resources, setResources] = useState<Resource[]>([]);\n const [tools, setTools] = useState<McpTool[]>([]);\n const [isLoading, setIsLoading] = useState<boolean>(false);\n const [error, setError] = useState<Error | null>(null);\n const [isConnected, setIsConnected] = useState<boolean>(false);\n const [capabilities, setCapabilities] = useState<ServerCapabilities | null>(null);\n\n const connectionStateRef = useRef<'disconnected' | 'connecting' | 'connected'>('disconnected');\n\n /**\n * Fetches available resources from the MCP server.\n * Only fetches if the server supports the resources capability.\n */\n const fetchResourcesInternal = useCallback(async () => {\n if (!client) return;\n\n const serverCapabilities = client.getServerCapabilities();\n if (!serverCapabilities?.resources) {\n setResources([]);\n return;\n }\n\n try {\n const response = await client.listResources();\n setResources(response.resources);\n } catch (e) {\n console.error('Error fetching resources:', e);\n throw e;\n }\n }, [client]);\n\n /**\n * Fetches available tools from the MCP server.\n * Only fetches if the server supports the tools capability.\n */\n const fetchToolsInternal = useCallback(async () => {\n if (!client) return;\n\n const serverCapabilities = client.getServerCapabilities();\n if (!serverCapabilities?.tools) {\n setTools([]);\n return;\n }\n\n try {\n const response = await client.listTools();\n setTools(response.tools);\n } catch (e) {\n console.error('Error fetching tools:', e);\n throw e;\n }\n }, [client]);\n\n /**\n * Establishes connection to the MCP server.\n * Safe to call multiple times - will no-op if already connected or connecting.\n */\n const reconnect = useCallback(async () => {\n if (!client || !transport) {\n throw new Error('Client or transport not available');\n }\n\n if (connectionStateRef.current !== 'disconnected') {\n return;\n }\n\n connectionStateRef.current = 'connecting';\n setIsLoading(true);\n setError(null);\n\n try {\n await client.connect(transport, opts);\n const caps = client.getServerCapabilities();\n setIsConnected(true);\n setCapabilities(caps || null);\n connectionStateRef.current = 'connected';\n\n await Promise.all([fetchResourcesInternal(), fetchToolsInternal()]);\n } catch (e) {\n const err = e instanceof Error ? e : new Error(String(e));\n connectionStateRef.current = 'disconnected';\n setError(err);\n throw err;\n } finally {\n setIsLoading(false);\n }\n }, [client, transport, opts, fetchResourcesInternal, fetchToolsInternal]);\n\n useEffect(() => {\n if (!isConnected || !client) {\n return;\n }\n\n const serverCapabilities = client.getServerCapabilities();\n\n const handleResourcesChanged = () => {\n fetchResourcesInternal().catch(console.error);\n };\n\n const handleToolsChanged = () => {\n fetchToolsInternal().catch(console.error);\n };\n\n if (serverCapabilities?.resources?.listChanged) {\n client.setNotificationHandler(ResourceListChangedNotificationSchema, handleResourcesChanged);\n }\n\n if (serverCapabilities?.tools?.listChanged) {\n client.setNotificationHandler(ToolListChangedNotificationSchema, handleToolsChanged);\n }\n\n // Re-fetch after setting up handlers to catch any changes that occurred\n // during the gap between initial fetch and handler setup\n Promise.all([fetchResourcesInternal(), fetchToolsInternal()]).catch(console.error);\n\n return () => {\n if (serverCapabilities?.resources?.listChanged) {\n client.removeNotificationHandler('notifications/resources/list_changed');\n }\n\n if (serverCapabilities?.tools?.listChanged) {\n client.removeNotificationHandler('notifications/tools/list_changed');\n }\n };\n }, [client, isConnected, fetchResourcesInternal, fetchToolsInternal]);\n\n // biome-ignore lint/correctness/useExhaustiveDependencies: intentional - reconnect when client/transport props change\n useEffect(() => {\n // Initial connection - reconnect() has its own guard to prevent concurrent connections\n reconnect().catch((err) => {\n console.error('Failed to connect MCP client:', err);\n });\n\n // Cleanup: mark as disconnected so next mount will reconnect\n return () => {\n connectionStateRef.current = 'disconnected';\n setIsConnected(false);\n };\n }, [client, transport]);\n\n return (\n <McpClientContext.Provider\n value={{\n client,\n tools,\n resources,\n isConnected,\n isLoading,\n error,\n capabilities,\n reconnect,\n }}\n >\n {children}\n </McpClientContext.Provider>\n );\n}\n\n/**\n * Hook to access the MCP client context.\n * Must be used within an {@link McpClientProvider}.\n *\n * @returns The MCP client context including client instance, tools, resources, and connection state\n * @throws Error if used outside of McpClientProvider\n *\n * @public\n *\n * @example\n * ```tsx\n * function ToolsList() {\n * const { tools, isConnected, error, reconnect } = useMcpClient();\n *\n * if (error) {\n * return (\n * <div>\n * Error: {error.message}\n * <button onClick={reconnect}>Retry</button>\n * </div>\n * );\n * }\n *\n * if (!isConnected) {\n * return <div>Not connected</div>;\n * }\n *\n * return (\n * <ul>\n * {tools.map(tool => (\n * <li key={tool.name}>{tool.description}</li>\n * ))}\n * </ul>\n * );\n * }\n * ```\n */\nexport function useMcpClient() {\n const context = useContext(McpClientContext);\n if (!context) {\n throw new Error('useMcpClient must be used within an McpClientProvider');\n }\n return context;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":["validatedInput: TInput","response: CallToolResult","fallbackInputSchema: InputSchema","registration: { unregister: () => void } | undefined","registration: { unregister: () => void } | undefined","EMPTY_REQUEST_OPTS: RequestOptions"],"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 { CallToolResult, InputSchema, ModelContext } from '@mcp-b/global';\nimport { zodToJsonSchema } from '@mcp-b/global';\nimport type { DependencyList } from 'react';\nimport { useCallback, useEffect, useMemo, useRef, useState } from 'react';\nimport { z } from 'zod';\nimport type { InferOutput, ToolExecutionState, WebMCPConfig, WebMCPReturn } from './types.js';\n\n/**\n * Default output formatter that converts values to formatted JSON strings.\n *\n * String values are returned as-is; all other types are serialized to\n * indented JSON for readability.\n *\n * @internal\n */\nfunction defaultFormatOutput(output: unknown): string {\n if (typeof output === 'string') {\n return output;\n }\n return JSON.stringify(output, null, 2);\n}\n\nconst TOOL_OWNER_BY_NAME = new Map<string, symbol>();\ntype StructuredContent = Exclude<CallToolResult['structuredContent'], undefined>;\n\nfunction toStructuredContent(value: unknown): StructuredContent | null {\n if (!value || typeof value !== 'object' || Array.isArray(value)) {\n return null;\n }\n\n try {\n const normalized = JSON.parse(JSON.stringify(value)) as unknown;\n if (!normalized || typeof normalized !== 'object' || Array.isArray(normalized)) {\n return null;\n }\n return normalized as StructuredContent;\n } catch {\n return null;\n }\n}\n\n/**\n * React hook for registering and managing Model Context Protocol (MCP) tools.\n *\n * This hook handles the complete lifecycle of an MCP tool:\n * - Registers the tool with `window.navigator.modelContext`\n * - Manages execution state (loading, results, errors)\n * - Validates input using Zod schemas\n * - Handles tool execution and lifecycle callbacks\n * - Automatically unregisters on component unmount\n * - Returns `structuredContent` when `outputSchema` is defined\n *\n * ## Output Schema (Recommended)\n *\n * Always define an `outputSchema` for your tools. This provides:\n * - **Type Safety**: Handler return type is inferred from the schema\n * - **MCP structuredContent**: AI models receive structured, typed data\n * - **Better AI Understanding**: Models can reason about your tool's output format\n *\n * ```tsx\n * useWebMCP({\n * name: 'get_user',\n * description: 'Get user by ID',\n * inputSchema: { userId: z.string() },\n * outputSchema: {\n * id: z.string(),\n * name: z.string(),\n * email: z.string().email(),\n * },\n * handler: async ({ userId }) => {\n * const user = await fetchUser(userId);\n * return { id: user.id, name: user.name, email: user.email };\n * },\n * });\n * ```\n *\n * ## Re-render Optimization\n *\n * This hook is optimized to minimize unnecessary tool re-registrations:\n *\n * - **Memoized JSON conversion**: Zod-to-JSON schema conversions are memoized to\n * avoid recomputation on every render.\n *\n * - **Ref-based callbacks**: `handler`, `onSuccess`, `onError`, and `formatOutput`\n * are stored in refs, so changing these functions won't trigger re-registration.\n *\n * **IMPORTANT**: If `inputSchema`, `outputSchema`, or `annotations` are defined inline\n * or change on every render, the tool will re-register unnecessarily. To avoid this,\n * memoize these values using `useMemo` or define them outside your component:\n *\n * ```tsx\n * // Good: Memoized schema (won't change unless deps change)\n * const outputSchema = useMemo(() => ({\n * count: z.number(),\n * items: z.array(z.string()),\n * }), []);\n *\n * // Good: Static schema defined outside component\n * const OUTPUT_SCHEMA = {\n * count: z.number(),\n * items: z.array(z.string()),\n * };\n *\n * // Bad: Inline schema (creates new object every render)\n * useWebMCP({\n * outputSchema: { count: z.number() }, // Re-registers every render!\n * });\n * ```\n *\n * **What triggers re-registration:**\n * - Changes to `name` or `description`\n * - Changes to `inputSchema`, `outputSchema`, or `annotations` (reference comparison)\n * - Changes to any value in the `deps` argument (reference comparison)\n *\n * **What does NOT trigger re-registration:**\n * - Changes to `handler`, `onSuccess`, `onError`, or `formatOutput` functions\n *\n * @template TInputSchema - Zod schema object defining input parameter types\n * @template TOutputSchema - Zod schema object defining output structure (enables structuredContent)\n *\n * @param config - Configuration object for the tool\n * @param deps - Optional dependency array that triggers tool re-registration when values change.\n * Similar to React's `useEffect` dependencies. When any value changes (by reference),\n * the tool will be unregistered and re-registered. Prefer primitive values over\n * objects/arrays to minimize re-registrations.\n *\n * @returns Object containing execution state and control methods\n *\n * @remarks\n * The hook uses React refs to store callbacks (`handler`, `onSuccess`, `onError`, `formatOutput`)\n * which prevents re-registration when these functions change. This is a performance optimization\n * that follows the \"latest ref\" pattern.\n *\n * When `outputSchema` is provided, the MCP response includes both text content and\n * `structuredContent` per the MCP specification. The type system ensures that the handler's\n * return type matches the output schema through Zod's type inference.\n *\n * @public\n *\n * @example\n * Basic tool with outputSchema (recommended):\n * ```tsx\n * function PostActions() {\n * const likeTool = useWebMCP({\n * name: 'posts_like',\n * description: 'Like a post by ID',\n * inputSchema: {\n * postId: z.string().uuid().describe('The ID of the post to like'),\n * },\n * outputSchema: {\n * success: z.boolean().describe('Whether the like was successful'),\n * likeCount: z.number().describe('Updated like count'),\n * },\n * handler: async ({ postId }) => {\n * const result = await api.posts.like(postId);\n * return { success: true, likeCount: result.likes };\n * },\n * });\n *\n * // likeTool.state.lastResult is typed as { success: boolean; likeCount: number } | null\n * if (likeTool.state.isExecuting) {\n * return <Spinner />;\n * }\n *\n * return <div>Likes: {likeTool.state.lastResult?.likeCount ?? 0}</div>;\n * }\n * ```\n *\n * @example\n * Tool with annotations and callbacks:\n * ```tsx\n * const deleteTool = useWebMCP({\n * name: 'posts_delete',\n * description: 'Delete a post permanently',\n * inputSchema: {\n * postId: z.string().uuid(),\n * },\n * outputSchema: {\n * deleted: z.boolean(),\n * deletedAt: z.string().describe('ISO timestamp of deletion'),\n * },\n * annotations: {\n * destructiveHint: true,\n * idempotentHint: false,\n * },\n * handler: async ({ postId }) => {\n * await api.posts.delete(postId);\n * return { deleted: true, deletedAt: new Date().toISOString() };\n * },\n * onSuccess: () => {\n * navigate('/posts');\n * toast.success('Post deleted');\n * },\n * onError: (error) => {\n * toast.error(`Failed to delete: ${error.message}`);\n * },\n * });\n * ```\n *\n * @example\n * Tool with deps for automatic re-registration:\n * ```tsx\n * function SitesManager({ sites }: { sites: Site[] }) {\n * // Without deps, you'd need getter functions like: getSiteCount: () => sites.length\n * // With deps, values can be used directly in description and handler\n * const sitesTool = useWebMCP(\n * {\n * name: 'sites_query',\n * description: `Query available sites. Current count: ${sites.length}`,\n * outputSchema: {\n * count: z.number(),\n * sites: z.array(z.object({ id: z.string(), name: z.string() })),\n * },\n * handler: async () => ({\n * count: sites.length,\n * sites: sites.map(s => ({ id: s.id, name: s.name })),\n * }),\n * },\n * [sites] // Re-register tool when sites array changes (by reference)\n * );\n *\n * return <SitesList sites={sites} />;\n * }\n * ```\n *\n * @example\n * Optimizing with memoization and deps:\n * ```tsx\n * function OptimizedSites({ sites }: { sites: Site[] }) {\n * // Memoize schema to prevent re-registration on every render\n * const outputSchema = useMemo(() => ({\n * sites: z.array(z.object({ id: z.string(), name: z.string() })),\n * }), []);\n *\n * // Use primitive values in deps for better control\n * const siteIds = sites.map(s => s.id).join(',');\n * const siteCount = sites.length;\n *\n * const sitesTool = useWebMCP(\n * {\n * name: 'sites_query',\n * description: `Query ${siteCount} available sites`,\n * outputSchema,\n * handler: async () => ({\n * sites: sites.map(s => ({ id: s.id, name: s.name })),\n * }),\n * },\n * [siteIds, siteCount] // Only re-register when IDs or count actually change\n * );\n *\n * return <SitesList sites={sites} />;\n * }\n * ```\n */\nexport function useWebMCP<\n TInputSchema extends Record<string, z.ZodTypeAny> = Record<string, never>,\n TOutputSchema extends Record<string, z.ZodTypeAny> = Record<string, never>,\n>(\n config: WebMCPConfig<TInputSchema, TOutputSchema>,\n deps?: DependencyList\n): WebMCPReturn<TOutputSchema> {\n type TOutput = InferOutput<TOutputSchema>;\n type TInput = z.infer<z.ZodObject<TInputSchema>>;\n const {\n name,\n description,\n inputSchema,\n outputSchema,\n annotations,\n handler,\n formatOutput = defaultFormatOutput,\n onSuccess,\n onError,\n } = config;\n\n const [state, setState] = useState<ToolExecutionState<TOutput>>({\n isExecuting: false,\n lastResult: null,\n error: null,\n executionCount: 0,\n });\n\n const handlerRef = useRef(handler);\n const onSuccessRef = useRef(onSuccess);\n const onErrorRef = useRef(onError);\n const formatOutputRef = useRef(formatOutput);\n const isMountedRef = useRef(true);\n const warnedRef = useRef(new Set<string>());\n const prevConfigRef = useRef({\n inputSchema,\n outputSchema,\n annotations,\n description,\n deps,\n });\n const isDev = (() => {\n const env = (globalThis as { process?: { env?: { NODE_ENV?: string } } }).process?.env\n ?.NODE_ENV;\n return env !== undefined ? env !== 'production' : false;\n })();\n\n // Update refs when callbacks change (doesn't trigger re-registration)\n useEffect(() => {\n handlerRef.current = handler;\n onSuccessRef.current = onSuccess;\n onErrorRef.current = onError;\n formatOutputRef.current = formatOutput;\n }, [handler, onSuccess, onError, formatOutput]);\n\n // Cleanup: mark component as unmounted\n useEffect(() => {\n isMountedRef.current = true;\n return () => {\n isMountedRef.current = false;\n };\n }, []);\n\n useEffect(() => {\n if (!isDev) {\n prevConfigRef.current = { inputSchema, outputSchema, annotations, description, deps };\n return;\n }\n\n const warnOnce = (key: string, message: string) => {\n if (warnedRef.current.has(key)) {\n return;\n }\n console.warn(`[useWebMCP] ${message}`);\n warnedRef.current.add(key);\n };\n\n const prev = prevConfigRef.current;\n\n if (inputSchema && prev.inputSchema && prev.inputSchema !== inputSchema) {\n warnOnce(\n 'inputSchema',\n `Tool \"${name}\" inputSchema reference changed; memoize or define it outside the component to avoid re-registration.`\n );\n }\n\n if (outputSchema && prev.outputSchema && prev.outputSchema !== outputSchema) {\n warnOnce(\n 'outputSchema',\n `Tool \"${name}\" outputSchema reference changed; memoize or define it outside the component to avoid re-registration.`\n );\n }\n\n if (annotations && prev.annotations && prev.annotations !== annotations) {\n warnOnce(\n 'annotations',\n `Tool \"${name}\" annotations reference changed; memoize or define it outside the component to avoid re-registration.`\n );\n }\n\n if (description !== prev.description) {\n warnOnce(\n 'description',\n `Tool \"${name}\" description changed; this re-registers the tool. Memoize the description if it does not need to update.`\n );\n }\n\n if (\n deps?.some(\n (value) => (typeof value === 'object' && value !== null) || typeof value === 'function'\n )\n ) {\n warnOnce(\n 'deps',\n `Tool \"${name}\" deps contains non-primitive values; prefer primitives or memoize objects/functions to reduce re-registration.`\n );\n }\n\n prevConfigRef.current = { inputSchema, outputSchema, annotations, description, deps };\n }, [annotations, deps, description, inputSchema, isDev, name, outputSchema]);\n\n const inputJsonSchema = useMemo(\n () => (inputSchema ? zodToJsonSchema(inputSchema) : undefined),\n [inputSchema]\n );\n const outputJsonSchema = useMemo(\n () => (outputSchema ? zodToJsonSchema(outputSchema) : undefined),\n [outputSchema]\n );\n\n const validator = useMemo(() => (inputSchema ? z.object(inputSchema) : null), [inputSchema]);\n\n /**\n * Executes the tool handler with input validation and state management.\n *\n * @param input - The input parameters to validate and pass to the handler\n * @returns Promise resolving to the handler's output\n * @throws Error if validation fails or the handler throws\n */\n const execute = useCallback(\n async (input: unknown): Promise<TOutput> => {\n setState((prev) => ({\n ...prev,\n isExecuting: true,\n error: null,\n }));\n\n try {\n const validatedInput: TInput = validator ? validator.parse(input) : (input as TInput);\n const result = await handlerRef.current(validatedInput);\n\n // Only update state if component is still mounted\n if (isMountedRef.current) {\n setState((prev) => ({\n isExecuting: false,\n lastResult: result,\n error: null,\n executionCount: prev.executionCount + 1,\n }));\n }\n\n if (onSuccessRef.current) {\n onSuccessRef.current(result, input);\n }\n\n return result;\n } catch (error) {\n const err = error instanceof Error ? error : new Error(String(error));\n\n // Only update state if component is still mounted\n if (isMountedRef.current) {\n setState((prev) => ({\n ...prev,\n isExecuting: false,\n error: err,\n }));\n }\n\n if (onErrorRef.current) {\n onErrorRef.current(err, input);\n }\n\n throw err;\n }\n },\n [validator]\n );\n const executeRef = useRef(execute);\n\n useEffect(() => {\n executeRef.current = execute;\n }, [execute]);\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 const modelContext = window.navigator.modelContext as ModelContext;\n\n /**\n * Handles MCP tool execution by running the handler and formatting the response.\n *\n * @param input - The input parameters from the MCP client\n * @returns CallToolResult with text content and optional structuredContent\n */\n const mcpHandler = async (input: unknown): Promise<CallToolResult> => {\n try {\n const result = await executeRef.current(input);\n const formattedOutput = formatOutputRef.current(result);\n\n const response: CallToolResult = {\n content: [\n {\n type: 'text',\n text: formattedOutput,\n },\n ],\n };\n\n if (outputJsonSchema) {\n const structuredContent = toStructuredContent(result);\n if (!structuredContent) {\n throw new Error(\n `Tool \"${name}\" outputSchema requires the handler to return a JSON object result`\n );\n }\n response.structuredContent = structuredContent;\n }\n\n return response;\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n\n return {\n content: [\n {\n type: 'text',\n text: `Error: ${errorMessage}`,\n },\n ],\n isError: true,\n };\n }\n };\n\n const fallbackInputSchema: InputSchema = {\n type: 'object',\n properties: {},\n };\n\n const ownerToken = Symbol(name);\n modelContext.registerTool({\n name,\n description,\n inputSchema: (inputJsonSchema || fallbackInputSchema) as InputSchema,\n ...(outputJsonSchema && { outputSchema: outputJsonSchema as InputSchema }),\n ...(annotations && { annotations }),\n execute: mcpHandler,\n });\n TOOL_OWNER_BY_NAME.set(name, ownerToken);\n\n return () => {\n const currentOwner = TOOL_OWNER_BY_NAME.get(name);\n if (currentOwner !== ownerToken) {\n return;\n }\n\n TOOL_OWNER_BY_NAME.delete(name);\n try {\n modelContext.unregisterTool(name);\n } catch (error) {\n if (isDev) {\n console.warn(`[useWebMCP] Failed to unregister tool \"${name}\":`, error);\n }\n }\n };\n // Spread operator in dependencies: Allows users to provide additional dependencies\n // via the `deps` parameter. While unconventional, this pattern is intentional to support\n // dynamic dependency injection. The spread is safe because deps is validated and warned\n // about non-primitive values earlier in this hook.\n }, [name, description, inputJsonSchema, outputJsonSchema, annotations, isDev, ...(deps ?? [])]);\n\n return {\n state,\n execute,\n reset,\n };\n}\n","import { useMemo, 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 const annotations = useMemo(\n () => ({\n title: `Context: ${name}`,\n readOnlyHint: true,\n idempotentHint: true,\n destructiveHint: false,\n openWorldHint: false,\n }),\n [name]\n );\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 // Cast to unknown since context tools return arbitrary types\n // that don't need to conform to a specific schema\n handler: async (_input: Record<string, unknown>) => {\n return getValueRef.current() as Record<string, unknown>;\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, ModelContext } from '@mcp-b/global';\nimport { zodToJsonSchema } from '@mcp-b/global';\nimport { useEffect, useMemo, 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 const isDev = (() => {\n const env = (globalThis as { process?: { env?: { NODE_ENV?: string } } }).process?.env\n ?.NODE_ENV;\n return env !== undefined ? env !== 'production' : false;\n })();\n\n const argsJsonSchema = useMemo(\n () => (argsSchema ? zodToJsonSchema(argsSchema) : undefined),\n [argsSchema]\n );\n\n useEffect(() => {\n getRef.current = get;\n }, [get]);\n\n useEffect(() => {\n if (typeof window === 'undefined' || !window.navigator?.modelContext) {\n if (isDev) {\n console.warn(\n `[useWebMCPPrompt] window.navigator.modelContext is not available. Prompt \"${name}\" will not be registered.`\n );\n }\n return;\n }\n const modelContext = window.navigator.modelContext as ModelContext;\n\n const promptHandler = async (\n args: Record<string, unknown>\n ): Promise<{ messages: PromptMessage[] }> => {\n return getRef.current(args as never);\n };\n\n let registration: { unregister: () => void } | undefined;\n try {\n registration = modelContext.registerPrompt({\n name,\n ...(description !== undefined && { description }),\n ...(argsJsonSchema && { argsSchema: argsJsonSchema as InputSchema }),\n get: promptHandler,\n });\n } catch (error) {\n setIsRegistered(false);\n throw error;\n }\n\n if (!registration) {\n if (isDev) {\n console.warn(`[useWebMCPPrompt] Prompt \"${name}\" did not return a registration handle.`);\n }\n setIsRegistered(false);\n return;\n }\n\n if (isDev) {\n console.log(`[useWebMCPPrompt] Registered prompt: ${name}`);\n }\n setIsRegistered(true);\n\n return () => {\n registration.unregister();\n if (isDev) {\n console.log(`[useWebMCPPrompt] Unregistered prompt: ${name}`);\n }\n setIsRegistered(false);\n };\n }, [name, description, argsJsonSchema, isDev]);\n\n return {\n isRegistered,\n };\n}\n","import type { ModelContext } from '@mcp-b/global';\nimport { useEffect, useRef, useState } from 'react';\nimport type { ResourceContents, WebMCPResourceConfig, WebMCPResourceReturn } from './types.js';\n\n/**\n * React hook for registering Model Context Protocol (MCP) resources.\n *\n * This hook handles the complete lifecycle of an MCP resource:\n * - Registers the resource with `window.navigator.modelContext`\n * - Supports both static URIs and URI templates with parameters\n * - Automatically unregisters on component unmount\n *\n * @param config - Configuration object for the resource\n * @returns Object indicating registration status\n *\n * @public\n *\n * @example\n * Static resource:\n * ```tsx\n * function AppSettingsResource() {\n * const { isRegistered } = useWebMCPResource({\n * uri: 'config://app-settings',\n * name: 'App Settings',\n * description: 'Application configuration',\n * mimeType: 'application/json',\n * read: async (uri) => ({\n * contents: [{\n * uri: uri.href,\n * text: JSON.stringify({ theme: 'dark', language: 'en' })\n * }]\n * }),\n * });\n *\n * return <div>Settings resource {isRegistered ? 'ready' : 'loading'}</div>;\n * }\n * ```\n *\n * @example\n * Dynamic resource with URI template:\n * ```tsx\n * function UserProfileResource() {\n * const { isRegistered } = useWebMCPResource({\n * uri: 'user://{userId}/profile',\n * name: 'User Profile',\n * description: 'User profile data by ID',\n * mimeType: 'application/json',\n * read: async (uri, params) => {\n * const userId = params?.userId ?? '';\n * const profile = await fetchUserProfile(userId);\n * return {\n * contents: [{\n * uri: uri.href,\n * text: JSON.stringify(profile)\n * }]\n * };\n * },\n * });\n *\n * return <div>User profile resource {isRegistered ? 'ready' : 'loading'}</div>;\n * }\n * ```\n */\nexport function useWebMCPResource(config: WebMCPResourceConfig): WebMCPResourceReturn {\n const { uri, name, description, mimeType, read } = config;\n\n const [isRegistered, setIsRegistered] = useState(false);\n\n const readRef = useRef(read);\n\n const isDev = (() => {\n const env = (globalThis as { process?: { env?: { NODE_ENV?: string } } }).process?.env\n ?.NODE_ENV;\n return env !== undefined ? env !== 'production' : false;\n })();\n\n useEffect(() => {\n readRef.current = read;\n }, [read]);\n\n useEffect(() => {\n if (typeof window === 'undefined' || !window.navigator?.modelContext) {\n if (isDev) {\n console.warn(\n `[useWebMCPResource] window.navigator.modelContext is not available. Resource \"${uri}\" will not be registered.`\n );\n }\n return;\n }\n const modelContext = window.navigator.modelContext as ModelContext;\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 let registration: { unregister: () => void } | undefined;\n try {\n registration = modelContext.registerResource({\n uri,\n name,\n ...(description !== undefined && { description }),\n ...(mimeType !== undefined && { mimeType }),\n read: resourceHandler,\n });\n } catch (error) {\n setIsRegistered(false);\n throw error;\n }\n\n if (!registration) {\n if (isDev) {\n console.warn(`[useWebMCPResource] Resource \"${uri}\" did not return a registration handle.`);\n }\n setIsRegistered(false);\n return;\n }\n\n if (isDev) {\n console.log(`[useWebMCPResource] Registered resource: ${uri}`);\n }\n setIsRegistered(true);\n\n return () => {\n registration.unregister();\n if (isDev) {\n console.log(`[useWebMCPResource] Unregistered resource: ${uri}`);\n }\n setIsRegistered(false);\n };\n }, [uri, name, description, mimeType, isDev]);\n\n return {\n isRegistered,\n };\n}\n","import type { ElicitationParams, ElicitationResult, ModelContext } 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 const modelContext = window.navigator.modelContext as ModelContext;\n\n setState((prev) => ({\n ...prev,\n isLoading: true,\n error: null,\n }));\n\n try {\n const result = await 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 { ModelContext, 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 const modelContext = window.navigator.modelContext as ModelContext;\n\n setState((prev) => ({\n ...prev,\n isLoading: true,\n error: null,\n }));\n\n try {\n const result = await modelContext.createMessage(params);\n\n setState((prev) => ({\n isLoading: false,\n result,\n error: null,\n requestCount: prev.requestCount + 1,\n }));\n\n onSuccess?.(result);\n return result;\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n\n setState((prev) => ({\n ...prev,\n isLoading: false,\n error,\n }));\n\n onError?.(error);\n throw error;\n }\n },\n [onSuccess, onError]\n );\n\n return {\n state,\n createMessage,\n reset,\n };\n}\n\n// Also export with the old name for backwards compatibility during migration\nexport { useSampling as useSamplingHandler };\nexport type { SamplingState as SamplingHandlerState };\nexport type { UseSamplingConfig as UseSamplingHandlerConfig };\nexport type { UseSamplingReturn as UseSamplingHandlerReturn };\n","import {\n type Client,\n type Tool as McpTool,\n type RequestOptions,\n type Resource,\n ResourceListChangedNotificationSchema,\n type ServerCapabilities,\n ToolListChangedNotificationSchema,\n type Transport,\n} from '@mcp-b/webmcp-ts-sdk';\nimport {\n createContext,\n type ReactElement,\n type ReactNode,\n useCallback,\n useContext,\n useEffect,\n useRef,\n useState,\n} from 'react';\n\n/**\n * Context value provided by McpClientProvider.\n *\n * @internal\n */\ninterface McpClientContextValue {\n client: Client;\n tools: McpTool[];\n resources: Resource[];\n isConnected: boolean;\n isLoading: boolean;\n error: Error | null;\n capabilities: ServerCapabilities | null;\n reconnect: () => Promise<void>;\n}\n\nconst McpClientContext = createContext<McpClientContextValue | null>(null);\nconst EMPTY_REQUEST_OPTS: RequestOptions = {};\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 const requestOpts = opts ?? EMPTY_REQUEST_OPTS;\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, requestOpts);\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, requestOpts, fetchResourcesInternal, fetchToolsInternal]);\n\n useEffect(() => {\n if (!isConnected || !client) {\n return;\n }\n\n const serverCapabilities = client.getServerCapabilities();\n\n const handleResourcesChanged = () => {\n fetchResourcesInternal().catch(console.error);\n };\n\n const handleToolsChanged = () => {\n fetchToolsInternal().catch(console.error);\n };\n\n if (serverCapabilities?.resources?.listChanged) {\n client.setNotificationHandler(ResourceListChangedNotificationSchema, handleResourcesChanged);\n }\n\n if (serverCapabilities?.tools?.listChanged) {\n client.setNotificationHandler(ToolListChangedNotificationSchema, handleToolsChanged);\n }\n\n // Re-fetch after setting up handlers to catch any changes that occurred\n // during the gap between initial fetch and handler setup\n Promise.all([fetchResourcesInternal(), fetchToolsInternal()]).catch(console.error);\n\n return () => {\n if (serverCapabilities?.resources?.listChanged) {\n client.removeNotificationHandler('notifications/resources/list_changed');\n }\n\n if (serverCapabilities?.tools?.listChanged) {\n client.removeNotificationHandler('notifications/tools/list_changed');\n }\n };\n }, [client, isConnected, fetchResourcesInternal, fetchToolsInternal]);\n\n // biome-ignore lint/correctness/useExhaustiveDependencies: intentional - reconnect when client/transport props change\n useEffect(() => {\n // Initial connection - reconnect() has its own guard to prevent concurrent connections\n reconnect().catch((err) => {\n console.error('Failed to connect MCP client:', err);\n });\n\n // Cleanup: mark as disconnected so next mount will reconnect\n return () => {\n connectionStateRef.current = 'disconnected';\n setIsConnected(false);\n };\n }, [client, transport]);\n\n return (\n <McpClientContext.Provider\n value={{\n client,\n tools,\n resources,\n isConnected,\n isLoading,\n error,\n capabilities,\n reconnect,\n }}\n >\n {children}\n </McpClientContext.Provider>\n );\n}\n\n/**\n * Hook to access the MCP client context.\n * Must be used within an {@link McpClientProvider}.\n *\n * @returns The MCP client context including client instance, tools, resources, and connection state\n * @throws Error if used outside of McpClientProvider\n *\n * @public\n *\n * @example\n * ```tsx\n * function ToolsList() {\n * const { tools, isConnected, error, reconnect } = useMcpClient();\n *\n * if (error) {\n * return (\n * <div>\n * Error: {error.message}\n * <button onClick={reconnect}>Retry</button>\n * </div>\n * );\n * }\n *\n * if (!isConnected) {\n * return <div>Not connected</div>;\n * }\n *\n * return (\n * <ul>\n * {tools.map(tool => (\n * <li key={tool.name}>{tool.description}</li>\n * ))}\n * </ul>\n * );\n * }\n * ```\n */\nexport function useMcpClient() {\n const context = useContext(McpClientContext);\n if (!context) {\n throw new Error('useMcpClient must be used within an McpClientProvider');\n }\n return context;\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mcp-b/react-webmcp",
3
- "version": "1.1.0",
3
+ "version": "1.1.1-canary.20260214192802",
4
4
  "description": "React hooks for Model Context Protocol (MCP) - expose React components as AI tools for Claude, ChatGPT, Cursor, and Copilot with Zod validation",
5
5
  "keywords": [
6
6
  "mcp",
@@ -55,9 +55,9 @@
55
55
  "dist"
56
56
  ],
57
57
  "dependencies": {
58
- "@mcp-b/transports": "1.3.0",
59
- "@mcp-b/webmcp-ts-sdk": "1.2.0",
60
- "@mcp-b/global": "1.3.0"
58
+ "@mcp-b/global": "1.5.0-canary.20260214192802",
59
+ "@mcp-b/webmcp-ts-sdk": "1.2.0-canary.20260214192802",
60
+ "@mcp-b/transports": "1.3.0-canary.20260214192802"
61
61
  },
62
62
  "devDependencies": {
63
63
  "@types/node": "22.17.2",
@@ -76,7 +76,7 @@
76
76
  },
77
77
  "peerDependencies": {
78
78
  "react": "^17.0.0 || ^18.0.0 || ^19.0.0",
79
- "zod": "^3.25.0"
79
+ "zod": ">=3.25.76 <4"
80
80
  },
81
81
  "publishConfig": {
82
82
  "access": "public",
@@ -93,7 +93,7 @@
93
93
  "publish:npm": "pnpm publish --access public",
94
94
  "test": "vitest run",
95
95
  "test:watch": "vitest",
96
- "typecheck": "tsc --noEmit",
96
+ "typecheck": "tsc --noEmit && vitest run --typecheck --silent",
97
97
  "version:major": "pnpm version major --no-git-tag-version",
98
98
  "version:minor": "pnpm version minor --no-git-tag-version",
99
99
  "version:patch": "pnpm version patch --no-git-tag-version"