@mcp-b/react-webmcp 5.0.3 → 6.0.0-beta.20261001010549
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +82 -146
- package/dist/index.d.ts +44 -22
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/package.json +17 -15
package/README.md
CHANGED
|
@@ -1,183 +1,119 @@
|
|
|
1
1
|
# @mcp-b/react-webmcp
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
React hooks for WebMCP tools with MCP output schemas, prompts, resources, and client connections,
|
|
4
|
+
built on the core [`usewebmcp`](../usewebmcp/README.md) tool hook.
|
|
4
5
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
[](https://opensource.org/licenses/MIT)
|
|
8
|
-
[](https://www.typescriptlang.org/)
|
|
9
|
-
[](https://react.dev/)
|
|
10
|
-
|
|
11
|
-
**[Reference](https://docs.mcp-b.ai/packages/react-webmcp/reference)** | **[React Tutorial](https://docs.mcp-b.ai/tutorials/first-react-tool)** | **[Framework Guides](https://docs.mcp-b.ai/how-to/frameworks)**
|
|
12
|
-
|
|
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.
|
|
6
|
+
```tsx
|
|
7
|
+
'use client';
|
|
14
8
|
|
|
15
|
-
|
|
9
|
+
import '@mcp-b/global';
|
|
10
|
+
import { useWebMCP } from '@mcp-b/react-webmcp';
|
|
11
|
+
import { z } from 'zod';
|
|
16
12
|
|
|
17
|
-
|
|
18
|
-
| ---------------------------- | --------------------------------------------------------------------------------- |
|
|
19
|
-
| **React-First Design** | Hooks follow React patterns with automatic cleanup and StrictMode support |
|
|
20
|
-
| **Type-Safe Schemas** | JSON Schema and Standard JSON Schema input typing, plus JSON Schema output typing |
|
|
21
|
-
| **Two-Way Integration** | Both expose tools TO AI agents AND consume tools FROM MCP servers |
|
|
22
|
-
| **Execution State Tracking** | Built-in loading, success, and error states for UI feedback |
|
|
23
|
-
| **Works with Any AI** | Compatible with Claude, ChatGPT, Gemini, Cursor, Copilot, and any MCP client |
|
|
13
|
+
const calculatorInput = z.object({ left: z.number(), right: z.number() });
|
|
24
14
|
|
|
25
|
-
|
|
15
|
+
export function CalculatorTool() {
|
|
16
|
+
const tool = useWebMCP({
|
|
17
|
+
name: 'add_numbers',
|
|
18
|
+
description: 'Add two numbers',
|
|
19
|
+
inputSchema: calculatorInput,
|
|
20
|
+
outputSchema: {
|
|
21
|
+
type: 'object',
|
|
22
|
+
properties: { total: { type: 'number' } },
|
|
23
|
+
required: ['total'],
|
|
24
|
+
},
|
|
25
|
+
annotations: { readOnlyHint: true, idempotentHint: true },
|
|
26
|
+
execute: ({ left, right }) => ({ total: left + right }),
|
|
27
|
+
});
|
|
26
28
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
+
return <output>Last total: {tool.state.lastResult?.total ?? 'Not called yet'}</output>;
|
|
30
|
+
}
|
|
29
31
|
```
|
|
30
32
|
|
|
31
|
-
|
|
32
|
-
WebMCP implementation supplies `document.modelContext` and you only use the core `useWebMCP` tool
|
|
33
|
-
hook. Prompt and resource hooks require the MCP-B extensions installed by
|
|
34
|
-
`@mcp-b/global`. If you only want strict core WebMCP hooks, install `usewebmcp` directly.
|
|
33
|
+
For `{ left: 3, right: 4 }`, React state holds `{ total: 7 }`; the agent receives MCP text and `structuredContent`.
|
|
35
34
|
|
|
36
|
-
|
|
35
|
+
[API reference](https://docs.mcp-b.ai/packages/react-webmcp/reference) · [Framework setup](https://docs.mcp-b.ai/how-to/frameworks)
|
|
36
|
+
|
|
37
|
+
## Install
|
|
37
38
|
|
|
38
39
|
```bash
|
|
39
|
-
pnpm add @mcp-b/
|
|
40
|
+
pnpm add @mcp-b/react-webmcp @mcp-b/global zod@^4.2
|
|
40
41
|
```
|
|
41
42
|
|
|
42
|
-
|
|
43
|
-
a native WebMCP implementation for the core `useWebMCP` tool hook.
|
|
43
|
+
Import `@mcp-b/global` once in your client entry. Zod is optional; compatible schema libraries and plain JSON Schema also work.
|
|
44
44
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
`
|
|
48
|
-
`
|
|
49
|
-
|
|
45
|
+
| What you use | Runtime needed |
|
|
46
|
+
| ---------------------------------------- | ------------------------------------------------------------------ |
|
|
47
|
+
| `useWebMCP` or `useWebMCPContext` | Native WebMCP, an initialized polyfill, or `@mcp-b/global` |
|
|
48
|
+
| `useWebMCPPrompt` or `useWebMCPResource` | `@mcp-b/global` or a configured `BrowserMcpServer` |
|
|
49
|
+
| `McpClientProvider` and `useMcpClient` | Your MCP client and transport; no `document.modelContext` required |
|
|
50
50
|
|
|
51
|
-
`outputSchema`
|
|
52
|
-
responses. Native Chrome WebMCP does not currently define or enforce it.
|
|
51
|
+
Native WebMCP and the standalone polyfill do not advertise MCP `outputSchema` metadata. Use the MCP-B runtime to expose that metadata to MCP clients.
|
|
53
52
|
|
|
54
|
-
##
|
|
53
|
+
## Schemas and results
|
|
55
54
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
55
|
+
- The hook calls your schema's converter once and runs its validator before every handler call (local, `executeTool`, and MCP), including async transforms. It ships no validator.
|
|
56
|
+
- Plain JSON Schema supplies metadata and inference only. Reuse immutable schemas to cache conversion and serialization.
|
|
57
|
+
- `outputSchema` types the result. The MCP server validates it on MCP calls; local and native calls bypass that validation. When that check fails, the MCP client receives an error while `state` records the success.
|
|
58
|
+
- Local execution and React state retain your value. Agent calls receive MCP formatting; `formatOutput` can override it.
|
|
59
|
+
- `formatError` defaults to an MCP response with `isError: true` whose text is `error.message`. Async formatters are awaited; local failures and cancellation always reject. A handler that returns `undefined` yields the text `undefined`.
|
|
59
60
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
description: 'Like a post by ID. Increments the like count.',
|
|
64
|
-
inputSchema: {
|
|
65
|
-
type: 'object',
|
|
66
|
-
properties: {
|
|
67
|
-
postId: { type: 'string', description: 'The post ID to like' },
|
|
68
|
-
},
|
|
69
|
-
required: ['postId'],
|
|
70
|
-
} as const,
|
|
71
|
-
outputSchema: {
|
|
72
|
-
type: 'object',
|
|
73
|
-
properties: {
|
|
74
|
-
success: { type: 'boolean' },
|
|
75
|
-
postId: { type: 'string' },
|
|
76
|
-
},
|
|
77
|
-
required: ['success', 'postId'],
|
|
78
|
-
} as const,
|
|
79
|
-
annotations: {
|
|
80
|
-
title: 'Like Post',
|
|
81
|
-
readOnlyHint: false,
|
|
82
|
-
idempotentHint: true,
|
|
83
|
-
},
|
|
84
|
-
execute: async (input) => {
|
|
85
|
-
await api.posts.like(input.postId);
|
|
86
|
-
return { success: true, postId: input.postId };
|
|
87
|
-
},
|
|
88
|
-
});
|
|
61
|
+
[Input example](#mcp-breact-webmcp) ·
|
|
62
|
+
[Schema guide](https://docs.mcp-b.ai/how-to/use-schemas-and-structured-output) ·
|
|
63
|
+
[Output reference](https://docs.mcp-b.ai/packages/react-webmcp/reference#schema-compatibility)
|
|
89
64
|
|
|
90
|
-
|
|
91
|
-
<div>
|
|
92
|
-
{likeTool.state.isExecuting && <Spinner />}
|
|
93
|
-
{likeTool.state.error && <ErrorAlert error={likeTool.state.error} />}
|
|
94
|
-
</div>
|
|
95
|
-
);
|
|
96
|
-
}
|
|
97
|
-
```
|
|
65
|
+
## Expose context, prompts, and resources
|
|
98
66
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
```tsx
|
|
102
|
-
import { McpClientProvider, useMcpClient } from '@mcp-b/react-webmcp';
|
|
103
|
-
import { TabClientTransport } from '@mcp-b/transports';
|
|
104
|
-
import { Client } from '@modelcontextprotocol/client';
|
|
105
|
-
|
|
106
|
-
const client = new Client(
|
|
107
|
-
{ name: 'MyApp', version: '1.0.0' },
|
|
108
|
-
{ versionNegotiation: { mode: 'auto' } }
|
|
109
|
-
);
|
|
110
|
-
const transport = new TabClientTransport({
|
|
111
|
-
channelId: 'mcp',
|
|
112
|
-
targetOrigin: window.location.origin,
|
|
113
|
-
});
|
|
114
|
-
|
|
115
|
-
function App() {
|
|
116
|
-
return (
|
|
117
|
-
<McpClientProvider client={client} transport={transport}>
|
|
118
|
-
<ToolConsumer />
|
|
119
|
-
</McpClientProvider>
|
|
120
|
-
);
|
|
121
|
-
}
|
|
67
|
+
```ts
|
|
68
|
+
'use client';
|
|
122
69
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
const handleCallTool = async () => {
|
|
127
|
-
const result = await client.callTool({ name: 'posts_like', arguments: { postId: '123' } });
|
|
128
|
-
console.log('Result:', result.content[0].text);
|
|
129
|
-
};
|
|
130
|
-
|
|
131
|
-
return (
|
|
132
|
-
<div>
|
|
133
|
-
<p>Connected: {isConnected ? 'Yes' : 'No'}</p>
|
|
134
|
-
<p>Available Tools: {tools.length}</p>
|
|
135
|
-
<button onClick={handleCallTool} disabled={!isConnected}>
|
|
136
|
-
Call Tool
|
|
137
|
-
</button>
|
|
138
|
-
</div>
|
|
139
|
-
);
|
|
140
|
-
}
|
|
141
|
-
```
|
|
70
|
+
import '@mcp-b/global';
|
|
71
|
+
import { useWebMCPContext, useWebMCPPrompt, useWebMCPResource } from '@mcp-b/react-webmcp';
|
|
142
72
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
`reconnect(newTransport)`; closed transport instances are not generally reusable.
|
|
73
|
+
export function PageTools({ title }: { title: string }) {
|
|
74
|
+
useWebMCPContext('page_context', 'Get the current page title', () => ({ title }));
|
|
146
75
|
|
|
147
|
-
|
|
76
|
+
useWebMCPPrompt({
|
|
77
|
+
name: 'summarize_page',
|
|
78
|
+
get: () => ({
|
|
79
|
+
messages: [{ role: 'user', content: { type: 'text', text: `Summarize: ${title}` } }],
|
|
80
|
+
}),
|
|
81
|
+
});
|
|
148
82
|
|
|
149
|
-
|
|
83
|
+
useWebMCPResource({
|
|
84
|
+
name: 'Page title',
|
|
85
|
+
uri: 'page://title',
|
|
86
|
+
read: async (uri) => ({
|
|
87
|
+
contents: [{ uri: uri.href, mimeType: 'text/plain', text: title }],
|
|
88
|
+
}),
|
|
89
|
+
});
|
|
150
90
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
| `useWebMCPContext(name, description, getValue)` | Simplified hook for read-only context exposure |
|
|
155
|
-
| `useWebMCPPrompt(config)` | Register a reusable MCP prompt |
|
|
156
|
-
| `useWebMCPResource(config)` | Register an MCP resource |
|
|
91
|
+
return null;
|
|
92
|
+
}
|
|
93
|
+
```
|
|
157
94
|
|
|
158
|
-
|
|
95
|
+
These hooks use the latest committed props and clean up on unmount. Set `enabled: false`
|
|
96
|
+
to unregister. [Prompt and resource guide](https://docs.mcp-b.ai/how-to/register-prompts-and-resources).
|
|
159
97
|
|
|
160
|
-
|
|
161
|
-
| ------------------- | --------------------------------------------------------- |
|
|
162
|
-
| `McpClientProvider` | Provider component managing an MCP client connection |
|
|
163
|
-
| `useMcpClient()` | Access client, tools, connection status, and capabilities |
|
|
98
|
+
## Consume an MCP server
|
|
164
99
|
|
|
165
|
-
|
|
100
|
+
Wrap your UI in `McpClientProvider`, supplying stable client and transport instances.
|
|
101
|
+
`useMcpClient()` exposes tools, resources, connection state, and errors.
|
|
102
|
+
[Client setup and example](https://docs.mcp-b.ai/packages/react-webmcp/reference#client-hooks).
|
|
166
103
|
|
|
167
|
-
|
|
104
|
+
## State and lifecycle
|
|
168
105
|
|
|
169
|
-
|
|
106
|
+
The tool hook returns the core hook's state and controls and shares its `enabled`, cancellation,
|
|
107
|
+
and registration behavior ([lifecycle reference](https://docs.mcp-b.ai/packages/usewebmcp/reference)).
|
|
108
|
+
Prompt and resource hooks return `isRegistered`.
|
|
109
|
+
`'use client'` is preserved; server rendering and StrictMode are tested on React 19, and React 18 is in the peer range.
|
|
170
110
|
|
|
171
|
-
|
|
172
|
-
- [`@mcp-b/transports`](https://docs.mcp-b.ai/packages/transports/reference) - Browser-specific MCP transports
|
|
173
|
-
- [`chrome-devtools-mcp`](https://github.com/ChromeDevTools/chrome-devtools-mcp) - Upstream Chrome DevTools MCP server
|
|
174
|
-
- [`usewebmcp`](../usewebmcp) - React hooks for strict core WebMCP API only
|
|
111
|
+
[Declarative form attributes](https://docs.mcp-b.ai/packages/react-webmcp/reference#declarative-form-attributes)
|
|
175
112
|
|
|
176
|
-
##
|
|
113
|
+
## Development
|
|
177
114
|
|
|
178
|
-
|
|
179
|
-
- [Model Context Protocol Spec](https://modelcontextprotocol.io)
|
|
115
|
+
Run `pnpm build` then `pnpm test:hooks` from the repository root. [Harness details](../../docs/TESTING.md#react-hook-harness).
|
|
180
116
|
|
|
181
117
|
## License
|
|
182
118
|
|
|
183
|
-
MIT
|
|
119
|
+
[MIT](../../LICENSE)
|
package/dist/index.d.ts
CHANGED
|
@@ -1,10 +1,46 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import { BrowserMcpServer as ModelContextProtocol, PromptDescriptor, PromptDescriptor as PromptDescriptor$1, ResourceDescriptor, ResourceDescriptor as ResourceDescriptor$1 } from "@mcp-b/webmcp-ts-sdk";
|
|
1
|
+
import { DependencyList, ReactElement, ReactNode } from "react";
|
|
2
|
+
import { ToolInputSchema, ToolInputSchema as ToolInputSchema$1 } from "@mcp-b/webmcp-ts-sdk/schema";
|
|
3
|
+
import { InferToolInput as InferToolInput$1, ToolExecutionState, WebMCP, WebMCP as WebMCP$1, WebMCPConfig as WebMCPConfig$1, WebMCPReturn as WebMCPReturn$1 } from "usewebmcp";
|
|
4
|
+
import { BrowserMcpServer as ModelContextProtocol, CallToolResult, InferJsonSchema, InputSchema, JsonSchemaForInference, MaybePromise, PromptDescriptor, PromptDescriptor as PromptDescriptor$1, ResourceDescriptor, ResourceDescriptor as ResourceDescriptor$1, ToolAnnotations, ToolAnnotations as ToolAnnotations$1, ToolDescriptor } from "@mcp-b/webmcp-ts-sdk";
|
|
5
|
+
import { StandardJSONSchemaV1, StandardSchemaV1 } from "@standard-schema/spec";
|
|
5
6
|
import { Client, ConnectOptions, Resource, ServerCapabilities, Tool, Transport } from "@modelcontextprotocol/client";
|
|
6
|
-
import { CallToolResult, MaybePromise, ToolAnnotations, ToolDescriptor } from "@mcp-b/webmcp-types";
|
|
7
7
|
|
|
8
|
+
//#region src/types.d.ts
|
|
9
|
+
/** Infers MCP-B structured output from its JSON Schema. */
|
|
10
|
+
type InferOutput<T extends JsonSchemaForInference | undefined = undefined> = [T] extends [undefined] ? unknown : T extends JsonSchemaForInference ? InferJsonSchema<T> : unknown;
|
|
11
|
+
type InferToolInput<T extends ToolInputSchema$1> = T extends StandardJSONSchemaV1 ? StandardJSONSchemaV1.InferInput<T> : InferToolInput$1<Exclude<T, StandardJSONSchemaV1>>;
|
|
12
|
+
type InferValidatedToolInput<T extends ToolInputSchema$1> = T extends StandardSchemaV1 ? StandardSchemaV1.InferOutput<T> : InferToolInput<T>;
|
|
13
|
+
/** Core hook configuration with opt-in MCP metadata. */
|
|
14
|
+
interface WebMCPConfig<TInput extends ToolInputSchema$1 = InputSchema, TOutput extends JsonSchemaForInference | undefined = undefined> extends Omit<WebMCPConfig$1<InputSchema, InferOutput<TOutput>>, 'annotations' | 'execute' | 'inputSchema'> {
|
|
15
|
+
inputSchema?: TInput;
|
|
16
|
+
execute: ToolExecuteFunction<TInput, TOutput>;
|
|
17
|
+
outputSchema?: TOutput;
|
|
18
|
+
annotations?: ToolAnnotations$1;
|
|
19
|
+
formatOutput?: (result: InferOutput<TOutput>) => MaybePromise<unknown>;
|
|
20
|
+
formatError?: (error: Error) => MaybePromise<unknown>;
|
|
21
|
+
}
|
|
22
|
+
type ToolExecuteFunction<TInput extends ToolInputSchema$1 = InputSchema, TOutput extends JsonSchemaForInference | undefined = undefined> = (input: InferValidatedToolInput<TInput>, options: WebMCP$1.ToolExecuteCallbackOptions) => MaybePromise<InferOutput<TOutput>>;
|
|
23
|
+
type WebMCPReturn<TOutput extends JsonSchemaForInference | undefined = undefined, TInput extends ToolInputSchema$1 = InputSchema> = Omit<WebMCPReturn$1<InputSchema, InferOutput<TOutput>>, 'execute'> & {
|
|
24
|
+
execute: (input: InferToolInput<TInput>, options?: WebMCP$1.ToolExecuteCallbackOptions) => Promise<InferOutput<TOutput>>;
|
|
25
|
+
};
|
|
26
|
+
/** A single message returned by {@link WebMCPPromptConfig.get}. */
|
|
27
|
+
type PromptMessage = Awaited<ReturnType<PromptDescriptor$1['get']>>['messages'][number];
|
|
28
|
+
/** A single entry returned by {@link WebMCPResourceConfig.read}. */
|
|
29
|
+
type ResourceContents = Awaited<ReturnType<ResourceDescriptor$1['read']>>['contents'][number];
|
|
30
|
+
type WebMCPPromptConfig = Pick<PromptDescriptor$1, 'name' | 'description'> & Pick<WebMCPConfig, 'enabled'> & {
|
|
31
|
+
argsSchema?: ToolInputSchema$1;
|
|
32
|
+
get: (args: Parameters<PromptDescriptor$1['get']>[0]) => MaybePromise<Awaited<ReturnType<PromptDescriptor$1['get']>>>;
|
|
33
|
+
};
|
|
34
|
+
interface WebMCPPromptReturn {
|
|
35
|
+
isRegistered: boolean;
|
|
36
|
+
}
|
|
37
|
+
type WebMCPResourceConfig = ResourceDescriptor$1 & Pick<WebMCPConfig, 'enabled'>;
|
|
38
|
+
type WebMCPResourceReturn = WebMCPPromptReturn;
|
|
39
|
+
//#endregion
|
|
40
|
+
//#region src/useWebMCP.d.ts
|
|
41
|
+
/** The core React lifecycle with MCP result formatting and output metadata. */
|
|
42
|
+
declare function useWebMCP<const TInput extends ToolInputSchema$1 = InputSchema, const TOutput extends JsonSchemaForInference | undefined = undefined>(config: WebMCPConfig<TInput, TOutput>, deps?: DependencyList): WebMCPReturn<TOutput, TInput>;
|
|
43
|
+
//#endregion
|
|
8
44
|
//#region src/client/McpClientProvider.d.ts
|
|
9
45
|
/**
|
|
10
46
|
* Context value provided by McpClientProvider.
|
|
@@ -158,21 +194,6 @@ declare function McpClientProvider({
|
|
|
158
194
|
*/
|
|
159
195
|
declare function useMcpClient(): McpClientContextValue;
|
|
160
196
|
//#endregion
|
|
161
|
-
//#region src/types.d.ts
|
|
162
|
-
/** A single message returned by {@link WebMCPPromptConfig.get}. */
|
|
163
|
-
type PromptMessage = Awaited<ReturnType<PromptDescriptor$1['get']>>['messages'][number];
|
|
164
|
-
/** A single entry returned by {@link WebMCPResourceConfig.read}. */
|
|
165
|
-
type ResourceContents = Awaited<ReturnType<ResourceDescriptor$1['read']>>['contents'][number];
|
|
166
|
-
type WebMCPPromptConfig = Pick<PromptDescriptor$1, 'name' | 'description'> & {
|
|
167
|
-
argsSchema?: ToolInputSchema$1;
|
|
168
|
-
get: (args: Parameters<PromptDescriptor$1['get']>[0]) => MaybePromise<Awaited<ReturnType<PromptDescriptor$1['get']>>>;
|
|
169
|
-
};
|
|
170
|
-
interface WebMCPPromptReturn {
|
|
171
|
-
isRegistered: boolean;
|
|
172
|
-
}
|
|
173
|
-
type WebMCPResourceConfig = ResourceDescriptor$1;
|
|
174
|
-
type WebMCPResourceReturn = WebMCPPromptReturn;
|
|
175
|
-
//#endregion
|
|
176
197
|
//#region src/useWebMCPContext.d.ts
|
|
177
198
|
/**
|
|
178
199
|
* Convenience hook for exposing read-only context data to AI assistants.
|
|
@@ -189,6 +210,7 @@ type WebMCPResourceReturn = WebMCPPromptReturn;
|
|
|
189
210
|
* @param name - Unique identifier for the context tool (e.g., 'context_current_post')
|
|
190
211
|
* @param description - Human-readable description of the context for AI assistants
|
|
191
212
|
* @param getValue - Function that returns the current context value
|
|
213
|
+
* @param options - Registration options; enabled defaults to true
|
|
192
214
|
* @returns Tool execution state and control methods
|
|
193
215
|
*
|
|
194
216
|
* @public
|
|
@@ -237,7 +259,7 @@ type WebMCPResourceReturn = WebMCPPromptReturn;
|
|
|
237
259
|
* }
|
|
238
260
|
* ```
|
|
239
261
|
*/
|
|
240
|
-
declare function useWebMCPContext<T>(name: string, description: string, getValue: () => T): WebMCPReturn
|
|
262
|
+
declare function useWebMCPContext<T>(name: string, description: string, getValue: () => T, options?: Pick<WebMCPConfig, 'enabled'>): WebMCPReturn;
|
|
241
263
|
//#endregion
|
|
242
264
|
//#region src/useWebMCPPrompt.d.ts
|
|
243
265
|
/**
|
|
@@ -387,5 +409,5 @@ declare module 'react' {
|
|
|
387
409
|
}
|
|
388
410
|
}
|
|
389
411
|
//#endregion
|
|
390
|
-
export { type CallToolResult, type InferOutput, type InferToolInput, McpClientProvider, type McpClientProviderProps, type ModelContextProtocol, type PromptDescriptor, type PromptMessage, type ResourceContents, type ResourceDescriptor, type ToolAnnotations, type ToolDescriptor, type ToolExecutionState, type ToolInputSchema, type WebMCPConfig, type WebMCPPromptConfig, type WebMCPPromptReturn, type WebMCPResourceConfig, type WebMCPResourceReturn, type WebMCPReturn, useMcpClient, useWebMCP, useWebMCPContext, useWebMCPPrompt, useWebMCPResource };
|
|
412
|
+
export { type CallToolResult, type InferOutput, type InferToolInput, type InferValidatedToolInput, McpClientProvider, type McpClientProviderProps, type ModelContextProtocol, type PromptDescriptor, type PromptMessage, type ResourceContents, type ResourceDescriptor, type ToolAnnotations, type ToolDescriptor, type ToolExecuteFunction, type ToolExecutionState, type ToolInputSchema, type WebMCP, type WebMCPConfig, type WebMCPPromptConfig, type WebMCPPromptReturn, type WebMCPResourceConfig, type WebMCPResourceReturn, type WebMCPReturn, useMcpClient, useWebMCP, useWebMCPContext, useWebMCPPrompt, useWebMCPResource };
|
|
391
413
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use client";import{
|
|
1
|
+
"use client";import{createContext as e,useCallback as t,useContext as n,useEffect as r,useLayoutEffect as i,useMemo as a,useRef as o,useState as s}from"react";import{isMcpStandardSchema as c,normalizeInputSchema as l,normalizeToolResponse as u}from"@mcp-b/webmcp-ts-sdk/schema";import{useWebMCPWithAdapter as d}from"usewebmcp/internal";import{jsx as f}from"react/jsx-runtime";async function p(e,t){if(!e||!c(e))return t;let n=await e[`~standard`].validate(t);if(`value`in n)return n.value;throw TypeError(`Invalid tool input: ${(n.issues??[]).map(e=>e.message).join(`; `)}`)}function m(e,t){let n=a(()=>{try{return{schema:e.inputSchema&&{...l(e.inputSchema).inputSchema}}}catch(e){return{error:e instanceof Error?e:Error(String(e))}}},[e.inputSchema]);return d({name:e.name,...e.title!==void 0&&{title:e.title},description:e.description,...n.schema!==void 0&&{inputSchema:n.schema},...e.enabled!==void 0&&{enabled:e.enabled},...e.exposedTo!==void 0&&{exposedTo:e.exposedTo},execute:async(t,n)=>{let r=await e.execute(await p(e.inputSchema,t),n);if(e.outputSchema&&u(r).structuredContent===void 0)throw TypeError(`Tool "${e.name}" outputSchema requires execute to return a JSON-serializable result`);return r}},t,{descriptor:{...e.outputSchema!==void 0&&{outputSchema:e.outputSchema},...e.annotations!==void 0&&{annotations:e.annotations}},...n.error&&{preparationError:n.error},formatOutput:e.formatOutput??u,formatError:e.formatError??(e=>({content:[{type:`text`,text:e.message}],isError:!0}))})}const h=globalThis.window===void 0?r:i;function g(e){let t=o(e);return h(()=>{t.current=e},[e]),t}const _=e(null);function v({children:e,client:n,transport:i,opts:c}){let[l,u]=s([]),[d,p]=s([]),[m,h]=s(null),[v,y]=s(`disconnected`),[b,x]=s(null),S=v===`initializing`||v===`connected`,C=v===`connecting`||v===`initializing`,w=o(`disconnected`),T=o(0),E=o(null),D=o(null),O=o(0),k=g(c),A=t(async e=>{let t=++O.current,r=()=>e===T.current&&t===O.current,i=n.getServerCapabilities();try{let[e,t]=await Promise.all([i?.resources?n.listResources(void 0,{cacheMode:`refresh`}):void 0,i?.tools?n.listTools(void 0,{cacheMode:`refresh`}):void 0]);if(!r())return;u(e?.resources??[]),p(t?.tools??[]),h(null)}catch(e){if(!r())return;let t=e instanceof Error?e:Error(String(e));throw h(t),t}},[n]),j=t(async e=>{let t=T.current;if(w.current!==`connected`||e!==void 0){if(w.current!==`disconnected`)return;w.current=`connecting`,y(`connecting`),h(null);try{if(await E.current,t!==T.current||(await n.connect(e??i,k.current),t!==T.current))return}catch(e){let n=e instanceof Error?e:Error(String(e));throw t===T.current&&(w.current=`disconnected`,y(`disconnected`),h(n)),n}x(n.getServerCapabilities()??null)}w.current=`initializing`,y(`initializing`);try{await A(t)}catch{}t===T.current&&(w.current=`connected`,y(`connected`))},[n,i,A,k]);r(()=>{if(!S)return;let e=n.getServerCapabilities(),t=e?.resources?.listChanged===!0,r=e?.tools?.listChanged===!0;if(!t&&!r)return;let i=async()=>{try{await A(T.current)}catch(e){console.error(`[ReactWebMCP:McpClientProvider]`,`Failed to refresh tools/resources after list_changed:`,e)}};t&&n.setNotificationHandler(`notifications/resources/list_changed`,i),r&&n.setNotificationHandler(`notifications/tools/list_changed`,i);let a=new AbortController;return n.getProtocolEra()===`modern`?n.listen({...r&&{toolsListChanged:!0},...t&&{resourcesListChanged:!0}},{signal:a.signal}).then(async e=>{if(a.signal.aborted){await e.close();return}await i()}).catch(e=>{a.signal.aborted||console.error(`[ReactWebMCP:McpClientProvider]`,`Failed to listen for list_changed notifications:`,e)}):i(),()=>{t&&n.removeNotificationHandler(`notifications/resources/list_changed`),r&&n.removeNotificationHandler(`notifications/tools/list_changed`),a.abort()}},[n,S,A]),r(()=>{let e=!0,t=T.current,r=n.onclose,i=()=>{try{r?.()}finally{e&&D.current===null&&(T.current+=1,O.current+=1,w.current=`disconnected`,y(`disconnected`),x(null),u([]),p([]))}};return n.onclose=i,w.current=`disconnected`,y(`disconnected`),x(null),u([]),p([]),j().catch(e=>{t===T.current&&console.error(`[ReactWebMCP:McpClientProvider]`,`Failed to connect MCP client:`,e)}),()=>{e=!1,n.onclose===i&&(r?n.onclose=r:Reflect.deleteProperty(n,`onclose`)),T.current+=1,O.current+=1,w.current=`disconnected`;let t={};D.current=t,E.current=n.close().catch(e=>{console.error(`[ReactWebMCP:McpClientProvider]`,`Failed to close MCP client:`,e)}).finally(()=>{D.current===t&&(D.current=null)})}},[n,i,j]);let M=a(()=>({client:n,tools:d,resources:l,isConnected:S,isLoading:C,error:m,capabilities:b,reconnect:j}),[n,d,l,S,C,m,b,j]);return f(_.Provider,{value:M,children:e})}function y(){let e=n(_);if(!e)throw Error(`useMcpClient must be used within an McpClientProvider`);return e}function b(e,t,n,r={}){return m({...r,name:e,description:t,annotations:{title:`Context: ${e}`,readOnlyHint:!0,idempotentHint:!0,destructiveHint:!1,openWorldHint:!1},execute:n})}function x(e){return`__isBrowserMcpServer`in e&&e.__isBrowserMcpServer===!0}function S(){let e=globalThis.document?.modelContext;return e&&x(e)?e:void 0}function C(e,t=!0){let[n,i]=s(!1);return r(()=>{let n=t?e():void 0;if(!n){i(!1);return}return i(!0),()=>{n.unregister()}},[e,t]),n}function w(e){let{name:n,description:r}=e,i=g(e);return{isRegistered:C(t(()=>{let e=S();if(!e){console.warn(`[ReactWebMCP] BrowserMcpServer is not available. Prompt "${n}" will not be registered.`);return}let{argsSchema:t}=i.current,a=t?l(t).inputSchema:void 0;return e.registerPrompt({name:n,...r!==void 0&&{description:r},...a&&{argsSchema:a},get:async e=>i.current.get(e)})},[n,r,i]),e.enabled)}}function T(e){let{uri:n,name:r,description:i,mimeType:a,read:o}=e,s=g(o);return{isRegistered:C(t(()=>{let e=S();if(!e){console.warn(`[ReactWebMCP] BrowserMcpServer is not available. Resource "${n}" will not be registered.`);return}return e.registerResource({uri:n,name:r,...i!==void 0&&{description:i},...a!==void 0&&{mimeType:a},read:(e,t)=>s.current(e,t)})},[n,r,i,a,s]),e.enabled)}}export{v as McpClientProvider,y as useMcpClient,m as useWebMCP,b as useWebMCPContext,w as useWebMCPPrompt,T as useWebMCPResource};
|
|
2
2
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":["useWebMCP"],"sources":["../src/useCommittedRef.ts","../src/client/McpClientProvider.tsx","../src/useWebMCPContext.ts","../src/model-context.ts","../src/useMcpRegistration.ts","../src/useWebMCPPrompt.ts","../src/useWebMCPResource.ts"],"sourcesContent":["import { useEffect, useLayoutEffect, useRef } from 'react';\n\nconst useIsomorphicLayoutEffect = typeof window === 'undefined' ? useEffect : useLayoutEffect;\n\nexport function useCommittedRef<T>(value: T) {\n const ref = useRef(value);\n\n // External MCP work may read values after commit but before passive effects. SSR cannot.\n useIsomorphicLayoutEffect(() => {\n ref.current = value;\n }, [value]);\n\n return ref;\n}\n","import {\n type Client,\n type ConnectOptions,\n type Resource,\n type ServerCapabilities,\n type SubscriptionFilter,\n type Tool as McpTool,\n type Transport,\n} from '@modelcontextprotocol/client';\nimport {\n createContext,\n type ReactElement,\n type ReactNode,\n useCallback,\n useContext,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from 'react';\nimport { useCommittedRef } from '../useCommittedRef.js';\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: (freshTransport?: Transport) => Promise<void>;\n}\n\ntype ConnectionState = 'disconnected' | 'connecting' | 'initializing' | 'connected';\n\nconst McpClientContext = createContext<McpClientContextValue | null>(null);\n\nfunction startListChangedSubscription(\n client: Client,\n filter: SubscriptionFilter,\n refreshLists: () => Promise<void>\n): () => void {\n const controller = new AbortController();\n\n void client\n .listen(filter, { signal: controller.signal })\n .then(async (subscription) => {\n if (controller.signal.aborted) {\n await subscription.close();\n return;\n }\n\n // Modern servers deliver list_changed only after listen is acknowledged.\n await refreshLists();\n })\n .catch((error) => {\n if (!controller.signal.aborted) {\n console.error(\n '[ReactWebMCP:McpClientProvider]',\n 'Failed to listen for list_changed notifications:',\n error\n );\n }\n });\n\n return () => controller.abort();\n}\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 connection options.\n */\n opts?: ConnectOptions;\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 { TabClientTransport } from '@mcp-b/transports';\n * import { McpClientProvider } from '@mcp-b/react-webmcp';\n * import { Client } from '@modelcontextprotocol/client';\n *\n * const client = new Client(\n * { name: 'my-app', version: '1.0.0' },\n * { versionNegotiation: { mode: 'auto' } }\n * );\n *\n * const transport = new TabClientTransport({\n * channelId: 'mcp',\n * targetOrigin: window.location.origin,\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 [error, setError] = useState<Error | null>(null);\n const [connectionState, setConnectionState] = useState<ConnectionState>('disconnected');\n const [capabilities, setCapabilities] = useState<ServerCapabilities | null>(null);\n const isConnected = connectionState === 'initializing' || connectionState === 'connected';\n const isLoading = connectionState === 'connecting' || connectionState === 'initializing';\n const connectionStateRef = useRef<ConnectionState>('disconnected');\n const connectionGenerationRef = useRef(0);\n const closePromiseRef = useRef<Promise<void> | null>(null);\n const providerCloseRef = useRef<object | null>(null);\n const inventoryRequestRef = useRef(0);\n const requestOptsRef = useCommittedRef(opts);\n\n /**\n * Refreshes every provider-owned inventory list and clears a prior inventory\n * error only after the complete refresh succeeds.\n */\n const refreshInventory = useCallback(\n async (connectionGeneration: number): Promise<void> => {\n const inventoryRequest = ++inventoryRequestRef.current;\n const serverCapabilities = client.getServerCapabilities();\n\n try {\n const [resourceResponse, toolResponse] = await Promise.all([\n serverCapabilities?.resources\n ? client.listResources(undefined, { cacheMode: 'refresh' })\n : undefined,\n serverCapabilities?.tools\n ? client.listTools(undefined, { cacheMode: 'refresh' })\n : undefined,\n ]);\n\n if (\n connectionGeneration !== connectionGenerationRef.current ||\n inventoryRequest !== inventoryRequestRef.current\n ) {\n return;\n }\n\n setResources(resourceResponse?.resources ?? []);\n setTools(toolResponse?.tools ?? []);\n setError(null);\n } catch (cause) {\n const error = cause instanceof Error ? cause : new Error(String(cause));\n if (\n connectionGeneration !== connectionGenerationRef.current ||\n inventoryRequest !== inventoryRequestRef.current\n ) {\n return;\n }\n setError(error);\n throw error;\n }\n },\n [client]\n );\n\n /**\n * Connects a disconnected client, or retries inventory discovery when the\n * MCP handshake is already alive. Calls made while work is in progress no-op.\n *\n * Pass a fresh transport after a one-shot transport has closed.\n */\n const reconnect = useCallback(\n async (freshTransport?: Transport): Promise<void> => {\n if (connectionStateRef.current === 'connected' && freshTransport === undefined) {\n const connectionGeneration = connectionGenerationRef.current;\n connectionStateRef.current = 'initializing';\n setConnectionState('initializing');\n\n try {\n await refreshInventory(connectionGeneration);\n } catch {\n // Inventory failure does not undo the completed MCP handshake.\n }\n\n if (connectionGeneration === connectionGenerationRef.current) {\n connectionStateRef.current = 'connected';\n setConnectionState('connected');\n }\n return;\n }\n\n if (connectionStateRef.current !== 'disconnected') {\n return;\n }\n\n connectionStateRef.current = 'connecting';\n setConnectionState('connecting');\n setError(null);\n const connectionGeneration = connectionGenerationRef.current;\n\n try {\n await closePromiseRef.current;\n if (connectionGeneration !== connectionGenerationRef.current) {\n return;\n }\n await client.connect(freshTransport ?? transport, requestOptsRef.current);\n if (connectionGeneration !== connectionGenerationRef.current) {\n return;\n }\n } catch (e) {\n const err = e instanceof Error ? e : new Error(String(e));\n if (connectionGeneration === connectionGenerationRef.current) {\n connectionStateRef.current = 'disconnected';\n setConnectionState('disconnected');\n setError(err);\n }\n throw err;\n }\n\n const caps = client.getServerCapabilities();\n setCapabilities(caps ?? null);\n connectionStateRef.current = 'initializing';\n setConnectionState('initializing');\n\n try {\n await refreshInventory(connectionGeneration);\n } catch {\n // Inventory failure does not undo the completed MCP handshake.\n }\n\n if (connectionGeneration === connectionGenerationRef.current) {\n connectionStateRef.current = 'connected';\n setConnectionState('connected');\n }\n },\n [client, transport, refreshInventory, requestOptsRef]\n );\n\n useEffect(() => {\n if (!isConnected) {\n return;\n }\n\n const serverCapabilities = client.getServerCapabilities();\n\n const resourcesListChanged = serverCapabilities?.resources?.listChanged === true;\n const toolsListChanged = serverCapabilities?.tools?.listChanged === true;\n const refreshLists = async () => {\n try {\n await refreshInventory(connectionGenerationRef.current);\n } catch (error) {\n console.error(\n '[ReactWebMCP:McpClientProvider]',\n 'Failed to refresh tools/resources after list_changed:',\n error\n );\n }\n };\n\n if (resourcesListChanged) {\n client.setNotificationHandler('notifications/resources/list_changed', refreshLists);\n }\n\n if (toolsListChanged) {\n client.setNotificationHandler('notifications/tools/list_changed', refreshLists);\n }\n\n const hasListChanged = resourcesListChanged || toolsListChanged;\n const stopListening =\n client.getProtocolEra() === 'modern' && hasListChanged\n ? startListChangedSubscription(\n client,\n {\n ...(toolsListChanged && { toolsListChanged: true }),\n ...(resourcesListChanged && { resourcesListChanged: true }),\n },\n refreshLists\n )\n : undefined;\n if (!stopListening && hasListChanged) {\n // Legacy servers deliver list_changed unsolicited once handlers are installed.\n void refreshLists();\n }\n\n return () => {\n if (resourcesListChanged) {\n client.removeNotificationHandler('notifications/resources/list_changed');\n }\n\n if (toolsListChanged) {\n client.removeNotificationHandler('notifications/tools/list_changed');\n }\n\n stopListening?.();\n };\n }, [client, isConnected, refreshInventory]);\n\n useEffect(() => {\n let active = true;\n const connectionGeneration = connectionGenerationRef.current;\n const previousOnclose = client.onclose;\n const handleClientClose = () => {\n try {\n previousOnclose?.();\n } finally {\n if (active && providerCloseRef.current === null) {\n connectionGenerationRef.current += 1;\n inventoryRequestRef.current += 1;\n connectionStateRef.current = 'disconnected';\n setConnectionState('disconnected');\n setCapabilities(null);\n setResources([]);\n setTools([]);\n }\n }\n };\n client.onclose = handleClientClose;\n connectionStateRef.current = 'disconnected';\n setConnectionState('disconnected');\n setCapabilities(null);\n setResources([]);\n setTools([]);\n\n // Initial connection - reconnect() has its own guard to prevent concurrent connections\n reconnect().catch((err) => {\n if (connectionGeneration === connectionGenerationRef.current) {\n console.error('[ReactWebMCP:McpClientProvider]', 'Failed to connect MCP client:', err);\n }\n });\n\n return () => {\n active = false;\n if (client.onclose === handleClientClose) {\n if (previousOnclose) {\n client.onclose = previousOnclose;\n } else {\n Reflect.deleteProperty(client, 'onclose');\n }\n }\n connectionGenerationRef.current += 1;\n inventoryRequestRef.current += 1;\n connectionStateRef.current = 'disconnected';\n const closeToken = {};\n providerCloseRef.current = closeToken;\n closePromiseRef.current = client\n .close()\n .catch((error: unknown) => {\n console.error('[ReactWebMCP:McpClientProvider]', 'Failed to close MCP client:', error);\n })\n .finally(() => {\n if (providerCloseRef.current === closeToken) {\n providerCloseRef.current = null;\n }\n });\n };\n }, [client, transport, reconnect]);\n\n const contextValue = useMemo(\n () => ({\n client,\n tools,\n resources,\n isConnected,\n isLoading,\n error,\n capabilities,\n reconnect,\n }),\n [client, tools, resources, isConnected, isLoading, error, capabilities, reconnect]\n );\n\n return <McpClientContext.Provider value={contextValue}>{children}</McpClientContext.Provider>;\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={() => void 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","import { useWebMCP, type WebMCPReturn } from 'usewebmcp';\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 * JSON-compatible values are normalized to text plus `structuredContent`.\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 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 execute: getValue,\n });\n}\n","import { isBrowserMcpServer, type BrowserMcpServer } from '@mcp-b/webmcp-ts-sdk';\n\nexport function getBrowserMcpServer(): BrowserMcpServer | undefined {\n if (typeof document === 'undefined' || typeof navigator === 'undefined') {\n return undefined;\n }\n\n const modelContext = document.modelContext ?? navigator.modelContext;\n return modelContext && isBrowserMcpServer(modelContext) ? modelContext : undefined;\n}\n","import type { RegistrationHandle } from '@mcp-b/webmcp-types';\nimport { useEffect, useState } from 'react';\n\nexport function useMcpRegistration(register: () => RegistrationHandle | undefined): boolean {\n const [isRegistered, setIsRegistered] = useState(false);\n\n useEffect(() => {\n const registration = register();\n if (!registration) {\n setIsRegistered(false);\n return;\n }\n\n setIsRegistered(true);\n return () => {\n registration.unregister();\n };\n }, [register]);\n\n return isRegistered;\n}\n","import { normalizeInputSchema } from '@mcp-b/webmcp-polyfill/schema';\nimport { useCallback } from 'react';\nimport type { WebMCPPromptConfig, WebMCPPromptReturn } from './types.js';\nimport { getBrowserMcpServer } from './model-context.js';\nimport { useCommittedRef } from './useCommittedRef.js';\nimport { useMcpRegistration } from './useMcpRegistration.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 the installed MCP-B `BrowserMcpServer`\n * - Automatically unregisters on component unmount\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 * type: 'object',\n * properties: {\n * code: { type: 'string', description: 'The code to review' },\n * language: { type: 'string', description: 'Programming language' },\n * },\n * required: ['code'],\n * } as const,\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(config: WebMCPPromptConfig): WebMCPPromptReturn {\n const { name, description } = config;\n\n const configRef = useCommittedRef(config);\n\n const register = useCallback(() => {\n const modelContext = getBrowserMcpServer();\n if (!modelContext) {\n console.warn(\n `[ReactWebMCP] BrowserMcpServer is not available. Prompt \"${name}\" will not be registered.`\n );\n return;\n }\n\n // ponytail: read at registration time so an inline `argsSchema` literal cannot re-register\n // every render. Ceiling: a changed schema is picked up only when `name` or `description`\n // changes; add a caller-supplied deps list, as `useWebMCP` has, if that is ever needed.\n const { argsSchema } = configRef.current;\n const resolvedArgsSchema = argsSchema\n ? normalizeInputSchema(argsSchema).inputSchema\n : undefined;\n\n return modelContext.registerPrompt({\n name,\n ...(description !== undefined && { description }),\n ...(resolvedArgsSchema && { argsSchema: resolvedArgsSchema }),\n get: async (args) => configRef.current.get(args),\n });\n }, [name, description, configRef]);\n\n return { isRegistered: useMcpRegistration(register) };\n}\n","import { useCallback } from 'react';\nimport { getBrowserMcpServer } from './model-context.js';\nimport type { WebMCPResourceConfig, WebMCPResourceReturn } from './types.js';\nimport { useCommittedRef } from './useCommittedRef.js';\nimport { useMcpRegistration } from './useMcpRegistration.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 the installed MCP-B `BrowserMcpServer`\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 = typeof params?.userId === 'string' ? 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 readRef = useCommittedRef(read);\n\n const register = useCallback(() => {\n const modelContext = getBrowserMcpServer();\n if (!modelContext) {\n console.warn(\n `[ReactWebMCP] BrowserMcpServer is not available. Resource \"${uri}\" will not be registered.`\n );\n return;\n }\n\n return modelContext.registerResource({\n uri,\n name,\n ...(description !== undefined && { description }),\n ...(mimeType !== undefined && { mimeType }),\n read: (resolvedUri, params) => readRef.current(resolvedUri, params),\n });\n }, [uri, name, description, mimeType, readRef]);\n\n return { isRegistered: useMcpRegistration(register) };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../src/useWebMCP.ts","../src/useCommittedRef.ts","../src/client/McpClientProvider.tsx","../src/useWebMCPContext.ts","../src/model-context.ts","../src/useMcpRegistration.ts","../src/useWebMCPPrompt.ts","../src/useWebMCPResource.ts"],"sourcesContent":["'use client';\n\nimport { useMemo, type DependencyList } from 'react';\nimport {\n isMcpStandardSchema,\n normalizeInputSchema,\n normalizeToolResponse,\n type ToolInputSchema,\n} from '@mcp-b/webmcp-ts-sdk/schema';\nimport type { InputSchema, JsonSchemaForInference } from '@mcp-b/webmcp-ts-sdk';\nimport { useWebMCPWithAdapter, type WebMCPAdapter } from 'usewebmcp/internal';\nimport type { WebMCPConfig as CoreWebMCPConfig } from 'usewebmcp';\nimport type { InferOutput, InferValidatedToolInput, WebMCPConfig, WebMCPReturn } from './types.js';\n\nasync function validateInput<T extends ToolInputSchema>(\n schema: T | undefined,\n input: unknown\n): Promise<InferValidatedToolInput<T>> {\n if (!schema || !isMcpStandardSchema(schema)) {\n // SAFETY: plain JSON Schema carries no validator; MCP checks it on the wire and local callers type their input.\n return input as InferValidatedToolInput<T>;\n }\n\n const result = await schema['~standard'].validate(input);\n if ('value' in result) {\n // SAFETY: Standard Schema's successful result is its declared output; the guard preserves T.\n return result.value as InferValidatedToolInput<T>;\n }\n throw new TypeError(\n `Invalid tool input: ${(result.issues ?? []).map((issue) => issue.message).join('; ')}`\n );\n}\n\n/** The core React lifecycle with MCP result formatting and output metadata. */\nexport function useWebMCP<\n const TInput extends ToolInputSchema = InputSchema,\n const TOutput extends JsonSchemaForInference | undefined = undefined,\n>(config: WebMCPConfig<TInput, TOutput>, deps?: DependencyList): WebMCPReturn<TOutput, TInput> {\n const input = useMemo(() => {\n try {\n // The copy drops the validator normalizeInputSchema hides on the schema, so the MCP\n // server registers plain JSON Schema and only this hook runs the validator.\n return {\n schema: config.inputSchema && { ...normalizeInputSchema(config.inputSchema).inputSchema },\n };\n } catch (error) {\n return { error: error instanceof Error ? error : new Error(String(error)) };\n }\n }, [config.inputSchema]);\n const coreConfig: CoreWebMCPConfig<InputSchema, InferOutput<TOutput>> = {\n name: config.name,\n ...(config.title !== undefined && { title: config.title }),\n description: config.description,\n ...(input.schema !== undefined && { inputSchema: input.schema }),\n ...(config.enabled !== undefined && { enabled: config.enabled }),\n ...(config.exposedTo !== undefined && { exposedTo: config.exposedTo }),\n execute: async (args, options) => {\n const result = await config.execute(await validateInput(config.inputSchema, args), options);\n if (config.outputSchema && normalizeToolResponse(result).structuredContent === undefined) {\n throw new TypeError(\n `Tool \"${config.name}\" outputSchema requires execute to return a JSON-serializable result`\n );\n }\n return result;\n },\n };\n const adapter: WebMCPAdapter<InferOutput<TOutput>> = {\n descriptor: {\n ...(config.outputSchema !== undefined && { outputSchema: config.outputSchema }),\n ...(config.annotations !== undefined && { annotations: config.annotations }),\n },\n ...(input.error && { preparationError: input.error }),\n formatOutput: config.formatOutput ?? normalizeToolResponse,\n formatError:\n config.formatError ??\n ((error) => ({ content: [{ type: 'text', text: error.message }], isError: true })),\n };\n // SAFETY: the adapter validates Standard Schemas and the core registration uses the normalized JSON Schema.\n return useWebMCPWithAdapter(coreConfig, deps, adapter) as WebMCPReturn<TOutput, TInput>;\n}\n","import { useEffect, useLayoutEffect, useRef } from 'react';\n\nconst useIsomorphicLayoutEffect = globalThis.window === undefined ? useEffect : useLayoutEffect;\n\nexport function useCommittedRef<T>(value: T) {\n const ref = useRef(value);\n\n // External MCP work may read values after commit but before passive effects. SSR cannot.\n useIsomorphicLayoutEffect(() => {\n ref.current = value;\n }, [value]);\n\n return ref;\n}\n","import {\n type Client,\n type ConnectOptions,\n type Resource,\n type ServerCapabilities,\n type Tool as McpTool,\n type Transport,\n} from '@modelcontextprotocol/client';\nimport {\n createContext,\n type ReactElement,\n type ReactNode,\n useCallback,\n useContext,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from 'react';\nimport { useCommittedRef } from '../useCommittedRef.js';\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: (freshTransport?: Transport) => Promise<void>;\n}\n\ntype ConnectionState = 'disconnected' | 'connecting' | 'initializing' | 'connected';\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 connection options.\n */\n opts?: ConnectOptions;\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 { TabClientTransport } from '@mcp-b/transports';\n * import { McpClientProvider } from '@mcp-b/react-webmcp';\n * import { Client } from '@modelcontextprotocol/client';\n *\n * const client = new Client(\n * { name: 'my-app', version: '1.0.0' },\n * { versionNegotiation: { mode: 'auto' } }\n * );\n *\n * const transport = new TabClientTransport({\n * channelId: 'mcp',\n * targetOrigin: window.location.origin,\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 [error, setError] = useState<Error | null>(null);\n const [connectionState, setConnectionState] = useState<ConnectionState>('disconnected');\n const [capabilities, setCapabilities] = useState<ServerCapabilities | null>(null);\n const isConnected = connectionState === 'initializing' || connectionState === 'connected';\n const isLoading = connectionState === 'connecting' || connectionState === 'initializing';\n const connectionStateRef = useRef<ConnectionState>('disconnected');\n const connectionGenerationRef = useRef(0);\n const closePromiseRef = useRef<Promise<void> | null>(null);\n const providerCloseRef = useRef<object | null>(null);\n const inventoryRequestRef = useRef(0);\n const requestOptsRef = useCommittedRef(opts);\n\n /**\n * Refreshes every provider-owned inventory list and clears a prior inventory\n * error only after the complete refresh succeeds.\n */\n const refreshInventory = useCallback(\n async (connectionGeneration: number): Promise<void> => {\n const inventoryRequest = ++inventoryRequestRef.current;\n const isCurrent = () =>\n connectionGeneration === connectionGenerationRef.current &&\n inventoryRequest === inventoryRequestRef.current;\n const serverCapabilities = client.getServerCapabilities();\n\n try {\n const [resourceResponse, toolResponse] = await Promise.all([\n serverCapabilities?.resources\n ? client.listResources(undefined, { cacheMode: 'refresh' })\n : undefined,\n serverCapabilities?.tools\n ? client.listTools(undefined, { cacheMode: 'refresh' })\n : undefined,\n ]);\n if (!isCurrent()) {\n return;\n }\n\n setResources(resourceResponse?.resources ?? []);\n setTools(toolResponse?.tools ?? []);\n setError(null);\n } catch (cause) {\n if (!isCurrent()) {\n return;\n }\n const error = cause instanceof Error ? cause : new Error(String(cause));\n setError(error);\n throw error;\n }\n },\n [client]\n );\n\n /**\n * Connects a disconnected client, or retries inventory discovery when the\n * MCP handshake is already alive. Calls made while work is in progress no-op.\n *\n * Pass a fresh transport after a one-shot transport has closed.\n */\n const reconnect = useCallback(\n async (freshTransport?: Transport): Promise<void> => {\n const connectionGeneration = connectionGenerationRef.current;\n if (connectionStateRef.current !== 'connected' || freshTransport !== undefined) {\n if (connectionStateRef.current !== 'disconnected') {\n return;\n }\n\n connectionStateRef.current = 'connecting';\n setConnectionState('connecting');\n setError(null);\n\n try {\n await closePromiseRef.current;\n if (connectionGeneration !== connectionGenerationRef.current) {\n return;\n }\n await client.connect(freshTransport ?? transport, requestOptsRef.current);\n if (connectionGeneration !== connectionGenerationRef.current) {\n return;\n }\n } catch (e) {\n const err = e instanceof Error ? e : new Error(String(e));\n if (connectionGeneration === connectionGenerationRef.current) {\n connectionStateRef.current = 'disconnected';\n setConnectionState('disconnected');\n setError(err);\n }\n throw err;\n }\n\n setCapabilities(client.getServerCapabilities() ?? null);\n }\n\n connectionStateRef.current = 'initializing';\n setConnectionState('initializing');\n try {\n await refreshInventory(connectionGeneration);\n } catch {\n // Inventory failure does not undo the completed MCP handshake.\n }\n\n if (connectionGeneration === connectionGenerationRef.current) {\n connectionStateRef.current = 'connected';\n setConnectionState('connected');\n }\n },\n [client, transport, refreshInventory, requestOptsRef]\n );\n\n useEffect(() => {\n if (!isConnected) {\n return;\n }\n\n const serverCapabilities = client.getServerCapabilities();\n const resourcesListChanged = serverCapabilities?.resources?.listChanged === true;\n const toolsListChanged = serverCapabilities?.tools?.listChanged === true;\n if (!resourcesListChanged && !toolsListChanged) {\n return;\n }\n\n const refreshLists = async () => {\n try {\n await refreshInventory(connectionGenerationRef.current);\n } catch (error) {\n console.error(\n '[ReactWebMCP:McpClientProvider]',\n 'Failed to refresh tools/resources after list_changed:',\n error\n );\n }\n };\n\n if (resourcesListChanged) {\n client.setNotificationHandler('notifications/resources/list_changed', refreshLists);\n }\n\n if (toolsListChanged) {\n client.setNotificationHandler('notifications/tools/list_changed', refreshLists);\n }\n\n const controller = new AbortController();\n if (client.getProtocolEra() === 'modern') {\n void client\n .listen(\n {\n ...(toolsListChanged && { toolsListChanged: true }),\n ...(resourcesListChanged && { resourcesListChanged: true }),\n },\n { signal: controller.signal }\n )\n .then(async (subscription) => {\n if (controller.signal.aborted) {\n await subscription.close();\n return;\n }\n\n // Modern servers deliver list_changed only after listen is acknowledged.\n await refreshLists();\n })\n .catch((error) => {\n if (!controller.signal.aborted) {\n console.error(\n '[ReactWebMCP:McpClientProvider]',\n 'Failed to listen for list_changed notifications:',\n error\n );\n }\n });\n } else {\n // Legacy servers deliver list_changed unsolicited once handlers are installed.\n void refreshLists();\n }\n\n return () => {\n if (resourcesListChanged) {\n client.removeNotificationHandler('notifications/resources/list_changed');\n }\n\n if (toolsListChanged) {\n client.removeNotificationHandler('notifications/tools/list_changed');\n }\n\n controller.abort();\n };\n }, [client, isConnected, refreshInventory]);\n\n useEffect(() => {\n let active = true;\n const connectionGeneration = connectionGenerationRef.current;\n const previousOnclose = client.onclose;\n const handleClientClose = () => {\n try {\n previousOnclose?.();\n } finally {\n if (active && providerCloseRef.current === null) {\n connectionGenerationRef.current += 1;\n inventoryRequestRef.current += 1;\n connectionStateRef.current = 'disconnected';\n setConnectionState('disconnected');\n setCapabilities(null);\n setResources([]);\n setTools([]);\n }\n }\n };\n client.onclose = handleClientClose;\n connectionStateRef.current = 'disconnected';\n setConnectionState('disconnected');\n setCapabilities(null);\n setResources([]);\n setTools([]);\n\n // Initial connection - reconnect() has its own guard to prevent concurrent connections\n reconnect().catch((err) => {\n if (connectionGeneration === connectionGenerationRef.current) {\n console.error('[ReactWebMCP:McpClientProvider]', 'Failed to connect MCP client:', err);\n }\n });\n\n return () => {\n active = false;\n if (client.onclose === handleClientClose) {\n if (previousOnclose) {\n client.onclose = previousOnclose;\n } else {\n Reflect.deleteProperty(client, 'onclose');\n }\n }\n connectionGenerationRef.current += 1;\n inventoryRequestRef.current += 1;\n connectionStateRef.current = 'disconnected';\n const closeToken = {};\n providerCloseRef.current = closeToken;\n closePromiseRef.current = client\n .close()\n .catch((cause: unknown) => {\n console.error('[ReactWebMCP:McpClientProvider]', 'Failed to close MCP client:', cause);\n })\n .finally(() => {\n if (providerCloseRef.current === closeToken) {\n providerCloseRef.current = null;\n }\n });\n };\n }, [client, transport, reconnect]);\n\n const contextValue = useMemo(\n () => ({\n client,\n tools,\n resources,\n isConnected,\n isLoading,\n error,\n capabilities,\n reconnect,\n }),\n [client, tools, resources, isConnected, isLoading, error, capabilities, reconnect]\n );\n\n return <McpClientContext.Provider value={contextValue}>{children}</McpClientContext.Provider>;\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={() => void 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","import type { WebMCPConfig, 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 * JSON-compatible values are normalized to text plus `structuredContent`.\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 * @param options - Registration options; enabled defaults to true\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 options: Pick<WebMCPConfig, 'enabled'> = {}\n): WebMCPReturn {\n return useWebMCP({\n ...options,\n name,\n description,\n annotations: {\n title: `Context: ${name}`,\n readOnlyHint: true,\n idempotentHint: true,\n destructiveHint: false,\n openWorldHint: false,\n },\n execute: getValue,\n });\n}\n","import type { BrowserMcpServer } from '@mcp-b/webmcp-ts-sdk';\n\n// The SDK exports this guard from its root entry, which would bundle the MCP server into clients.\nfunction isBrowserMcpServer(context: EventTarget): context is BrowserMcpServer {\n return '__isBrowserMcpServer' in context && context.__isBrowserMcpServer === true;\n}\n\nexport function getBrowserMcpServer(): BrowserMcpServer | undefined {\n const modelContext = globalThis.document?.modelContext;\n return modelContext && isBrowserMcpServer(modelContext) ? modelContext : undefined;\n}\n","import type { RegistrationHandle } from '@mcp-b/webmcp-ts-sdk';\nimport { useEffect, useState } from 'react';\n\nexport function useMcpRegistration(\n register: () => RegistrationHandle | undefined,\n enabled = true\n): boolean {\n const [isRegistered, setIsRegistered] = useState(false);\n\n useEffect(() => {\n const registration = enabled ? register() : undefined;\n if (!registration) {\n setIsRegistered(false);\n return;\n }\n\n setIsRegistered(true);\n return () => {\n registration.unregister();\n };\n }, [register, enabled]);\n\n return isRegistered;\n}\n","import { normalizeInputSchema } from '@mcp-b/webmcp-ts-sdk/schema';\nimport { useCallback } from 'react';\nimport type { WebMCPPromptConfig, WebMCPPromptReturn } from './types.js';\nimport { getBrowserMcpServer } from './model-context.js';\nimport { useCommittedRef } from './useCommittedRef.js';\nimport { useMcpRegistration } from './useMcpRegistration.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 the installed MCP-B `BrowserMcpServer`\n * - Automatically unregisters on component unmount\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 * type: 'object',\n * properties: {\n * code: { type: 'string', description: 'The code to review' },\n * language: { type: 'string', description: 'Programming language' },\n * },\n * required: ['code'],\n * } as const,\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(config: WebMCPPromptConfig): WebMCPPromptReturn {\n const { name, description } = config;\n\n const configRef = useCommittedRef(config);\n\n const register = useCallback(() => {\n const modelContext = getBrowserMcpServer();\n if (!modelContext) {\n console.warn(\n `[ReactWebMCP] BrowserMcpServer is not available. Prompt \"${name}\" will not be registered.`\n );\n return;\n }\n\n // Read at registration time so an inline `argsSchema` literal cannot re-register every\n // render; a changed schema takes effect at the next registration.\n const { argsSchema } = configRef.current;\n const resolvedArgsSchema = argsSchema\n ? normalizeInputSchema(argsSchema).inputSchema\n : undefined;\n\n return modelContext.registerPrompt({\n name,\n ...(description !== undefined && { description }),\n ...(resolvedArgsSchema && { argsSchema: resolvedArgsSchema }),\n get: async (args) => configRef.current.get(args),\n });\n }, [name, description, configRef]);\n\n return { isRegistered: useMcpRegistration(register, config.enabled) };\n}\n","import { useCallback } from 'react';\nimport { getBrowserMcpServer } from './model-context.js';\nimport type { WebMCPResourceConfig, WebMCPResourceReturn } from './types.js';\nimport { useCommittedRef } from './useCommittedRef.js';\nimport { useMcpRegistration } from './useMcpRegistration.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 the installed MCP-B `BrowserMcpServer`\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 = typeof params?.userId === 'string' ? 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 readRef = useCommittedRef(read);\n\n const register = useCallback(() => {\n const modelContext = getBrowserMcpServer();\n if (!modelContext) {\n console.warn(\n `[ReactWebMCP] BrowserMcpServer is not available. Resource \"${uri}\" will not be registered.`\n );\n return;\n }\n\n return modelContext.registerResource({\n uri,\n name,\n ...(description !== undefined && { description }),\n ...(mimeType !== undefined && { mimeType }),\n read: (resolvedUri, params) => readRef.current(resolvedUri, params),\n });\n }, [uri, name, description, mimeType, readRef]);\n\n return { isRegistered: useMcpRegistration(register, config.enabled) };\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": "
|
|
3
|
+
"version": "6.0.0-beta.20261001010549",
|
|
4
4
|
"description": "React hooks for Model Context Protocol (MCP) - expose React components as AI tools for Claude, ChatGPT, Cursor, and Copilot",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ai",
|
|
@@ -61,24 +61,26 @@
|
|
|
61
61
|
},
|
|
62
62
|
"dependencies": {
|
|
63
63
|
"@modelcontextprotocol/client": "2.0.0",
|
|
64
|
-
"@
|
|
65
|
-
"@mcp-b/webmcp-ts-sdk": "
|
|
66
|
-
"
|
|
67
|
-
"usewebmcp": "5.0.3"
|
|
64
|
+
"@standard-schema/spec": "^1.1.0",
|
|
65
|
+
"@mcp-b/webmcp-ts-sdk": "6.0.0-beta.20261001010549",
|
|
66
|
+
"usewebmcp": "6.0.0-beta.20261001010549"
|
|
68
67
|
},
|
|
69
68
|
"devDependencies": {
|
|
69
|
+
"@modelcontextprotocol/core": "2.0.0",
|
|
70
70
|
"@modelcontextprotocol/server": "2.0.0",
|
|
71
|
-
"@types/node": "22.
|
|
72
|
-
"@types/react": "^19.2.
|
|
73
|
-
"@vitest/coverage-v8": "4.1.
|
|
74
|
-
"playwright": "^1.
|
|
75
|
-
"react": "^19.
|
|
76
|
-
"react-dom": "^19.
|
|
71
|
+
"@types/node": "22.20.1",
|
|
72
|
+
"@types/react": "^19.2.18",
|
|
73
|
+
"@vitest/coverage-v8": "4.1.8",
|
|
74
|
+
"playwright": "^1.62.1",
|
|
75
|
+
"react": "^19.2.8",
|
|
76
|
+
"react-dom": "^19.2.8",
|
|
77
77
|
"typescript": "^5.8.3",
|
|
78
|
-
"vite-plus": "0.1.
|
|
79
|
-
"vitest": "npm:@voidzero-dev/vite-plus-test@0.1.
|
|
80
|
-
"vitest-browser-react": "^2.0
|
|
81
|
-
"
|
|
78
|
+
"vite-plus": "0.1.24",
|
|
79
|
+
"vitest": "npm:@voidzero-dev/vite-plus-test@0.1.24",
|
|
80
|
+
"vitest-browser-react": "^2.2.0",
|
|
81
|
+
"zod": "4.4.3",
|
|
82
|
+
"@mcp-b/transports": "6.0.0-beta.20261001010549",
|
|
83
|
+
"@mcp-b/global": "6.0.0-beta.20261001010549"
|
|
82
84
|
},
|
|
83
85
|
"peerDependencies": {
|
|
84
86
|
"react": "^18.0.0 || ^19.0.0"
|