react-webmcp 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +294 -0
- package/dist/index.d.mts +407 -0
- package/dist/index.d.ts +407 -0
- package/dist/index.js +255 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +238 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +59 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,407 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* WebMCP type definitions for the W3C navigator.modelContext API.
|
|
6
|
+
*
|
|
7
|
+
* Based on the WebMCP Early Preview specification (Feb 2026) and
|
|
8
|
+
* the Chrome 146+ implementation.
|
|
9
|
+
*/
|
|
10
|
+
interface JSONSchemaProperty {
|
|
11
|
+
type?: "string" | "number" | "integer" | "boolean" | "array" | "object";
|
|
12
|
+
description?: string;
|
|
13
|
+
enum?: Array<string | number | boolean>;
|
|
14
|
+
oneOf?: Array<{
|
|
15
|
+
const: string | number | boolean;
|
|
16
|
+
title?: string;
|
|
17
|
+
}>;
|
|
18
|
+
const?: string | number | boolean;
|
|
19
|
+
title?: string;
|
|
20
|
+
pattern?: string;
|
|
21
|
+
format?: string;
|
|
22
|
+
minLength?: number;
|
|
23
|
+
maxLength?: number;
|
|
24
|
+
minimum?: number;
|
|
25
|
+
maximum?: number;
|
|
26
|
+
items?: JSONSchemaProperty;
|
|
27
|
+
properties?: Record<string, JSONSchemaProperty>;
|
|
28
|
+
required?: string[];
|
|
29
|
+
default?: unknown;
|
|
30
|
+
}
|
|
31
|
+
interface JSONSchema {
|
|
32
|
+
type?: "object" | "array" | "string" | "number" | "integer" | "boolean";
|
|
33
|
+
properties?: Record<string, JSONSchemaProperty>;
|
|
34
|
+
required?: string[];
|
|
35
|
+
description?: string;
|
|
36
|
+
items?: JSONSchemaProperty;
|
|
37
|
+
}
|
|
38
|
+
interface ToolAnnotations {
|
|
39
|
+
/** Indicates the tool only reads data and does not modify state. */
|
|
40
|
+
readOnlyHint?: "true" | "false";
|
|
41
|
+
/** Indicates the tool performs a destructive/irreversible operation. */
|
|
42
|
+
destructiveHint?: "true" | "false";
|
|
43
|
+
/** Indicates the tool is idempotent (safe to retry). */
|
|
44
|
+
idempotentHint?: "true" | "false";
|
|
45
|
+
/** Indicates results can be cached. */
|
|
46
|
+
cache?: boolean;
|
|
47
|
+
}
|
|
48
|
+
interface ToolContentText {
|
|
49
|
+
type: "text";
|
|
50
|
+
text: string;
|
|
51
|
+
}
|
|
52
|
+
interface ToolContentJSON {
|
|
53
|
+
type: "json";
|
|
54
|
+
json: unknown;
|
|
55
|
+
}
|
|
56
|
+
type ToolContent = ToolContentText | ToolContentJSON;
|
|
57
|
+
interface WebMCPToolDefinition {
|
|
58
|
+
/** Unique tool name (e.g. "searchFlights"). */
|
|
59
|
+
name: string;
|
|
60
|
+
/** Human-readable description for agents. */
|
|
61
|
+
description: string;
|
|
62
|
+
/** JSON Schema describing the tool's input parameters. */
|
|
63
|
+
inputSchema: JSONSchema | Record<string, never>;
|
|
64
|
+
/** Optional JSON Schema describing the tool's output. */
|
|
65
|
+
outputSchema?: JSONSchema | JSONSchemaProperty;
|
|
66
|
+
/** Optional metadata hints for agents. */
|
|
67
|
+
annotations?: ToolAnnotations;
|
|
68
|
+
/** The function called when an agent invokes this tool. */
|
|
69
|
+
execute: (input: Record<string, unknown>) => unknown | Promise<unknown>;
|
|
70
|
+
}
|
|
71
|
+
interface UseWebMCPToolConfig {
|
|
72
|
+
/** Unique tool name. */
|
|
73
|
+
name: string;
|
|
74
|
+
/** Human-readable description for agents. */
|
|
75
|
+
description: string;
|
|
76
|
+
/** JSON Schema for the tool's input parameters. */
|
|
77
|
+
inputSchema: JSONSchema | Record<string, never>;
|
|
78
|
+
/** Optional JSON Schema for the tool's output. */
|
|
79
|
+
outputSchema?: JSONSchema | JSONSchemaProperty;
|
|
80
|
+
/** Optional metadata hints for agents. */
|
|
81
|
+
annotations?: ToolAnnotations;
|
|
82
|
+
/** The handler function called when the tool is invoked. */
|
|
83
|
+
execute: (input: Record<string, unknown>) => unknown | Promise<unknown>;
|
|
84
|
+
}
|
|
85
|
+
interface WebMCPContextConfig {
|
|
86
|
+
tools: WebMCPToolDefinition[];
|
|
87
|
+
}
|
|
88
|
+
interface WebMCPFormSubmitEvent extends Event {
|
|
89
|
+
/** True when the form submission was triggered by an AI agent. */
|
|
90
|
+
agentInvoked: boolean;
|
|
91
|
+
/** Pass a promise that resolves with the tool's result data. */
|
|
92
|
+
respondWith: (response: Promise<unknown> | unknown) => void;
|
|
93
|
+
}
|
|
94
|
+
interface ToolActivatedEvent extends Event {
|
|
95
|
+
/** The name of the tool that was activated. */
|
|
96
|
+
toolName: string;
|
|
97
|
+
}
|
|
98
|
+
interface ToolCancelEvent extends Event {
|
|
99
|
+
/** The name of the tool whose execution was cancelled. */
|
|
100
|
+
toolName: string;
|
|
101
|
+
}
|
|
102
|
+
interface ModelContext {
|
|
103
|
+
registerTool(tool: WebMCPToolDefinition): void;
|
|
104
|
+
unregisterTool(name: string): void;
|
|
105
|
+
provideContext(config: {
|
|
106
|
+
tools: WebMCPToolDefinition[];
|
|
107
|
+
}): void;
|
|
108
|
+
clearContext(): void;
|
|
109
|
+
}
|
|
110
|
+
interface ModelContextTesting {
|
|
111
|
+
listTools(): Array<{
|
|
112
|
+
name: string;
|
|
113
|
+
description: string;
|
|
114
|
+
inputSchema: JSONSchema | string;
|
|
115
|
+
}>;
|
|
116
|
+
executeTool(name: string, inputArgs: Record<string, unknown>): Promise<unknown>;
|
|
117
|
+
registerToolsChangedCallback(callback: () => void): void;
|
|
118
|
+
getCrossDocumentScriptToolResult(): Promise<unknown>;
|
|
119
|
+
}
|
|
120
|
+
declare global {
|
|
121
|
+
interface Navigator {
|
|
122
|
+
modelContext?: ModelContext;
|
|
123
|
+
modelContextTesting?: ModelContextTesting;
|
|
124
|
+
}
|
|
125
|
+
interface WindowEventMap {
|
|
126
|
+
toolactivated: CustomEvent & {
|
|
127
|
+
toolName: string;
|
|
128
|
+
};
|
|
129
|
+
toolcancel: CustomEvent & {
|
|
130
|
+
toolName: string;
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Register a single WebMCP tool via the imperative API.
|
|
137
|
+
*
|
|
138
|
+
* The tool is registered with `navigator.modelContext.registerTool()` when
|
|
139
|
+
* the component mounts and unregistered with `unregisterTool()` on unmount.
|
|
140
|
+
* If the tool definition changes (name, description, schemas, or
|
|
141
|
+
* annotations), the previous tool is unregistered and the new one is
|
|
142
|
+
* registered.
|
|
143
|
+
*
|
|
144
|
+
* Object/array props like `inputSchema` and `annotations` are compared by
|
|
145
|
+
* value (serialised fingerprint), so passing inline literals on every render
|
|
146
|
+
* will **not** cause unnecessary re-registration.
|
|
147
|
+
*
|
|
148
|
+
* The `execute` callback is always called through a ref, so it does not
|
|
149
|
+
* need to be memoised by the consumer.
|
|
150
|
+
*
|
|
151
|
+
* @example
|
|
152
|
+
* ```tsx
|
|
153
|
+
* useWebMCPTool({
|
|
154
|
+
* name: "searchFlights",
|
|
155
|
+
* description: "Search for flights with the given parameters.",
|
|
156
|
+
* inputSchema: {
|
|
157
|
+
* type: "object",
|
|
158
|
+
* properties: {
|
|
159
|
+
* origin: { type: "string", description: "Origin IATA code" },
|
|
160
|
+
* destination: { type: "string", description: "Destination IATA code" },
|
|
161
|
+
* },
|
|
162
|
+
* required: ["origin", "destination"],
|
|
163
|
+
* },
|
|
164
|
+
* execute: async ({ origin, destination }) => {
|
|
165
|
+
* const results = await api.searchFlights(origin, destination);
|
|
166
|
+
* return { content: [{ type: "text", text: JSON.stringify(results) }] };
|
|
167
|
+
* },
|
|
168
|
+
* });
|
|
169
|
+
* ```
|
|
170
|
+
*/
|
|
171
|
+
declare function useWebMCPTool(config: UseWebMCPToolConfig): void;
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Register multiple WebMCP tools at once using `provideContext()`.
|
|
175
|
+
*
|
|
176
|
+
* Unlike `useWebMCPTool` which manages a single tool, `useWebMCPContext`
|
|
177
|
+
* replaces the entire set of registered tools. This is useful when the
|
|
178
|
+
* application state changes significantly and you want to expose a
|
|
179
|
+
* completely different set of tools.
|
|
180
|
+
*
|
|
181
|
+
* On unmount, all tools are cleared via `clearContext()`.
|
|
182
|
+
*
|
|
183
|
+
* The hook performs a deep comparison of tool definitions (name, description,
|
|
184
|
+
* inputSchema, annotations) so that passing a new array reference on every
|
|
185
|
+
* render does **not** cause unnecessary re-registration.
|
|
186
|
+
*
|
|
187
|
+
* @example
|
|
188
|
+
* ```tsx
|
|
189
|
+
* useWebMCPContext({
|
|
190
|
+
* tools: [
|
|
191
|
+
* {
|
|
192
|
+
* name: "addTodo",
|
|
193
|
+
* description: "Add a new item to the todo list",
|
|
194
|
+
* inputSchema: { type: "object", properties: { text: { type: "string" } } },
|
|
195
|
+
* execute: ({ text }) => ({ content: [{ type: "text", text: `Added: ${text}` }] }),
|
|
196
|
+
* },
|
|
197
|
+
* {
|
|
198
|
+
* name: "markComplete",
|
|
199
|
+
* description: "Mark a todo item as complete",
|
|
200
|
+
* inputSchema: { type: "object", properties: { id: { type: "string" } } },
|
|
201
|
+
* execute: ({ id }) => ({ content: [{ type: "text", text: `Completed: ${id}` }] }),
|
|
202
|
+
* },
|
|
203
|
+
* ],
|
|
204
|
+
* });
|
|
205
|
+
* ```
|
|
206
|
+
*/
|
|
207
|
+
declare function useWebMCPContext(config: {
|
|
208
|
+
tools: WebMCPToolDefinition[];
|
|
209
|
+
}): void;
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Listen for WebMCP tool lifecycle events on the window.
|
|
213
|
+
*
|
|
214
|
+
* The browser fires `toolactivated` when an AI agent invokes a declarative
|
|
215
|
+
* tool (form fields are pre-filled) and `toolcancel` when the agent or
|
|
216
|
+
* user cancels the operation.
|
|
217
|
+
*
|
|
218
|
+
* @param event - The event name: "toolactivated" or "toolcancel"
|
|
219
|
+
* @param callback - Called with the tool name when the event fires
|
|
220
|
+
* @param toolNameFilter - Optional: only fire for a specific tool name
|
|
221
|
+
*
|
|
222
|
+
* @example
|
|
223
|
+
* ```tsx
|
|
224
|
+
* useToolEvent("toolactivated", (toolName) => {
|
|
225
|
+
* console.log(`Agent activated tool: ${toolName}`);
|
|
226
|
+
* validateForm();
|
|
227
|
+
* }, "book_table");
|
|
228
|
+
* ```
|
|
229
|
+
*/
|
|
230
|
+
declare function useToolEvent(event: "toolactivated" | "toolcancel", callback: (toolName: string) => void, toolNameFilter?: string): void;
|
|
231
|
+
|
|
232
|
+
interface WebMCPFormProps extends Omit<React.FormHTMLAttributes<HTMLFormElement>, "onSubmit"> {
|
|
233
|
+
/** The tool name exposed to AI agents. Maps to the `toolname` HTML attribute. */
|
|
234
|
+
toolName: string;
|
|
235
|
+
/** Description of what this tool does. Maps to `tooldescription`. */
|
|
236
|
+
toolDescription: string;
|
|
237
|
+
/** If true, the form auto-submits when filled by an agent. Maps to `toolautosubmit`. */
|
|
238
|
+
toolAutoSubmit?: boolean;
|
|
239
|
+
/**
|
|
240
|
+
* Submit handler that receives the enhanced SubmitEvent with
|
|
241
|
+
* `agentInvoked` and `respondWith` properties.
|
|
242
|
+
*/
|
|
243
|
+
onSubmit?: (event: WebMCPFormSubmitEvent) => void;
|
|
244
|
+
/** Called when a tool activation event fires for this form's tool. */
|
|
245
|
+
onToolActivated?: (toolName: string) => void;
|
|
246
|
+
/** Called when a tool cancel event fires for this form's tool. */
|
|
247
|
+
onToolCancel?: (toolName: string) => void;
|
|
248
|
+
children: React.ReactNode;
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* A React wrapper for the WebMCP declarative API.
|
|
252
|
+
*
|
|
253
|
+
* Renders a `<form>` element with the appropriate WebMCP HTML attributes
|
|
254
|
+
* (`toolname`, `tooldescription`, `toolautosubmit`) so the browser
|
|
255
|
+
* automatically registers it as a WebMCP tool.
|
|
256
|
+
*
|
|
257
|
+
* @example
|
|
258
|
+
* ```tsx
|
|
259
|
+
* <WebMCPForm
|
|
260
|
+
* toolName="book_table"
|
|
261
|
+
* toolDescription="Book a table at the restaurant"
|
|
262
|
+
* onSubmit={(e) => {
|
|
263
|
+
* e.preventDefault();
|
|
264
|
+
* if (e.agentInvoked) {
|
|
265
|
+
* e.respondWith(Promise.resolve("Booking confirmed!"));
|
|
266
|
+
* }
|
|
267
|
+
* }}
|
|
268
|
+
* >
|
|
269
|
+
* <WebMCPInput name="name" label="Full Name" />
|
|
270
|
+
* <button type="submit">Book</button>
|
|
271
|
+
* </WebMCPForm>
|
|
272
|
+
* ```
|
|
273
|
+
*/
|
|
274
|
+
declare function WebMCPForm({ toolName, toolDescription, toolAutoSubmit, onSubmit, onToolActivated, onToolCancel, children, ...rest }: WebMCPFormProps): react_jsx_runtime.JSX.Element;
|
|
275
|
+
|
|
276
|
+
interface WebMCPInputProps extends React.InputHTMLAttributes<HTMLInputElement> {
|
|
277
|
+
/** Maps to the `toolparamtitle` attribute (overrides the JSON Schema property key). */
|
|
278
|
+
toolParamTitle?: string;
|
|
279
|
+
/** Maps to the `toolparamdescription` attribute (describes this parameter to agents). */
|
|
280
|
+
toolParamDescription?: string;
|
|
281
|
+
}
|
|
282
|
+
/**
|
|
283
|
+
* An `<input>` element enhanced with WebMCP declarative attributes.
|
|
284
|
+
*
|
|
285
|
+
* Use inside a `<WebMCPForm>` to annotate individual form fields
|
|
286
|
+
* for AI agents.
|
|
287
|
+
*
|
|
288
|
+
* @example
|
|
289
|
+
* ```tsx
|
|
290
|
+
* <WebMCPInput
|
|
291
|
+
* type="text"
|
|
292
|
+
* name="name"
|
|
293
|
+
* toolParamDescription="Customer's full name (min 2 chars)"
|
|
294
|
+
* required
|
|
295
|
+
* minLength={2}
|
|
296
|
+
* />
|
|
297
|
+
* ```
|
|
298
|
+
*/
|
|
299
|
+
declare const WebMCPInput: React.ForwardRefExoticComponent<WebMCPInputProps & React.RefAttributes<HTMLInputElement>>;
|
|
300
|
+
|
|
301
|
+
interface WebMCPSelectProps extends React.SelectHTMLAttributes<HTMLSelectElement> {
|
|
302
|
+
/** Maps to the `toolparamtitle` attribute (overrides the JSON Schema property key). */
|
|
303
|
+
toolParamTitle?: string;
|
|
304
|
+
/** Maps to the `toolparamdescription` attribute (describes this parameter to agents). */
|
|
305
|
+
toolParamDescription?: string;
|
|
306
|
+
children: React.ReactNode;
|
|
307
|
+
}
|
|
308
|
+
/**
|
|
309
|
+
* A `<select>` element enhanced with WebMCP declarative attributes.
|
|
310
|
+
*
|
|
311
|
+
* Use inside a `<WebMCPForm>` to annotate select inputs for AI agents.
|
|
312
|
+
* The `<option>` values and text are automatically mapped to the tool's
|
|
313
|
+
* JSON Schema `enum` / `oneOf` definitions by the browser.
|
|
314
|
+
*
|
|
315
|
+
* @example
|
|
316
|
+
* ```tsx
|
|
317
|
+
* <WebMCPSelect
|
|
318
|
+
* name="seating"
|
|
319
|
+
* toolParamDescription="Preferred seating area"
|
|
320
|
+
* >
|
|
321
|
+
* <option value="Main Dining">Main Dining Room</option>
|
|
322
|
+
* <option value="Terrace">Terrace (Outdoor)</option>
|
|
323
|
+
* </WebMCPSelect>
|
|
324
|
+
* ```
|
|
325
|
+
*/
|
|
326
|
+
declare const WebMCPSelect: React.ForwardRefExoticComponent<WebMCPSelectProps & React.RefAttributes<HTMLSelectElement>>;
|
|
327
|
+
|
|
328
|
+
interface WebMCPTextareaProps extends React.TextareaHTMLAttributes<HTMLTextAreaElement> {
|
|
329
|
+
/** Maps to the `toolparamtitle` attribute (overrides the JSON Schema property key). */
|
|
330
|
+
toolParamTitle?: string;
|
|
331
|
+
/** Maps to the `toolparamdescription` attribute (describes this parameter to agents). */
|
|
332
|
+
toolParamDescription?: string;
|
|
333
|
+
}
|
|
334
|
+
/**
|
|
335
|
+
* A `<textarea>` element enhanced with WebMCP declarative attributes.
|
|
336
|
+
*
|
|
337
|
+
* Use inside a `<WebMCPForm>` to annotate textarea inputs for AI agents.
|
|
338
|
+
*
|
|
339
|
+
* @example
|
|
340
|
+
* ```tsx
|
|
341
|
+
* <WebMCPTextarea
|
|
342
|
+
* name="requests"
|
|
343
|
+
* rows={3}
|
|
344
|
+
* toolParamDescription="Special requests (allergies, occasions, etc.)"
|
|
345
|
+
* />
|
|
346
|
+
* ```
|
|
347
|
+
*/
|
|
348
|
+
declare const WebMCPTextarea: React.ForwardRefExoticComponent<WebMCPTextareaProps & React.RefAttributes<HTMLTextAreaElement>>;
|
|
349
|
+
|
|
350
|
+
interface WebMCPContextValue {
|
|
351
|
+
/** Whether navigator.modelContext is available in this browser. */
|
|
352
|
+
available: boolean;
|
|
353
|
+
/** Whether navigator.modelContextTesting is available (inspector API). */
|
|
354
|
+
testingAvailable: boolean;
|
|
355
|
+
}
|
|
356
|
+
/**
|
|
357
|
+
* Provides WebMCP availability information to the component tree.
|
|
358
|
+
*
|
|
359
|
+
* Wrap your application (or a subtree) with `<WebMCPProvider>` to let
|
|
360
|
+
* child components check WebMCP availability via the `useWebMCPStatus` hook.
|
|
361
|
+
*
|
|
362
|
+
* @example
|
|
363
|
+
* ```tsx
|
|
364
|
+
* function App() {
|
|
365
|
+
* return (
|
|
366
|
+
* <WebMCPProvider>
|
|
367
|
+
* <MyComponent />
|
|
368
|
+
* </WebMCPProvider>
|
|
369
|
+
* );
|
|
370
|
+
* }
|
|
371
|
+
* ```
|
|
372
|
+
*/
|
|
373
|
+
declare function WebMCPProvider({ children }: {
|
|
374
|
+
children: React.ReactNode;
|
|
375
|
+
}): react_jsx_runtime.JSX.Element;
|
|
376
|
+
/**
|
|
377
|
+
* Returns the current WebMCP availability status.
|
|
378
|
+
*
|
|
379
|
+
* Must be used within a `<WebMCPProvider>`.
|
|
380
|
+
*
|
|
381
|
+
* @example
|
|
382
|
+
* ```tsx
|
|
383
|
+
* function StatusBadge() {
|
|
384
|
+
* const { available } = useWebMCPStatus();
|
|
385
|
+
* return <span>{available ? "WebMCP Ready" : "WebMCP Not Available"}</span>;
|
|
386
|
+
* }
|
|
387
|
+
* ```
|
|
388
|
+
*/
|
|
389
|
+
declare function useWebMCPStatus(): WebMCPContextValue;
|
|
390
|
+
|
|
391
|
+
/**
|
|
392
|
+
* Returns the navigator.modelContext API if available, or null.
|
|
393
|
+
*/
|
|
394
|
+
declare function getModelContext(): ModelContext | null;
|
|
395
|
+
/**
|
|
396
|
+
* Returns true if the WebMCP API (navigator.modelContext) is available
|
|
397
|
+
* in the current browsing context.
|
|
398
|
+
*/
|
|
399
|
+
declare function isWebMCPAvailable(): boolean;
|
|
400
|
+
/**
|
|
401
|
+
* Returns true if the WebMCP testing API (navigator.modelContextTesting)
|
|
402
|
+
* is available. This is the API used by the Model Context Tool Inspector
|
|
403
|
+
* extension and requires the "WebMCP for testing" Chrome flag.
|
|
404
|
+
*/
|
|
405
|
+
declare function isWebMCPTestingAvailable(): boolean;
|
|
406
|
+
|
|
407
|
+
export { type JSONSchema, type JSONSchemaProperty, type ModelContext, type ModelContextTesting, type ToolActivatedEvent, type ToolAnnotations, type ToolCancelEvent, type ToolContent, type ToolContentJSON, type ToolContentText, type UseWebMCPToolConfig, type WebMCPContextConfig, WebMCPForm, type WebMCPFormProps, type WebMCPFormSubmitEvent, WebMCPInput, type WebMCPInputProps, WebMCPProvider, WebMCPSelect, type WebMCPSelectProps, WebMCPTextarea, type WebMCPTextareaProps, type WebMCPToolDefinition, getModelContext, isWebMCPAvailable, isWebMCPTestingAvailable, useToolEvent, useWebMCPContext, useWebMCPStatus, useWebMCPTool };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var React2 = require('react');
|
|
4
|
+
var jsxRuntime = require('react/jsx-runtime');
|
|
5
|
+
|
|
6
|
+
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
7
|
+
|
|
8
|
+
var React2__default = /*#__PURE__*/_interopDefault(React2);
|
|
9
|
+
|
|
10
|
+
// src/hooks/useWebMCPTool.ts
|
|
11
|
+
|
|
12
|
+
// src/utils/modelContext.ts
|
|
13
|
+
function getModelContext() {
|
|
14
|
+
if (typeof window !== "undefined" && typeof window.navigator !== "undefined" && window.navigator.modelContext) {
|
|
15
|
+
return window.navigator.modelContext;
|
|
16
|
+
}
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
function isWebMCPAvailable() {
|
|
20
|
+
return getModelContext() !== null;
|
|
21
|
+
}
|
|
22
|
+
function isWebMCPTestingAvailable() {
|
|
23
|
+
return typeof window !== "undefined" && typeof window.navigator !== "undefined" && !!window.navigator.modelContextTesting;
|
|
24
|
+
}
|
|
25
|
+
function warnIfUnavailable(hookName) {
|
|
26
|
+
if (!isWebMCPAvailable()) {
|
|
27
|
+
console.warn(
|
|
28
|
+
`[react-webmcp] ${hookName}: navigator.modelContext is not available. Ensure you are running Chrome 146+ with the "WebMCP for testing" flag enabled.`
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// src/hooks/useWebMCPTool.ts
|
|
34
|
+
function toolFingerprint(config) {
|
|
35
|
+
return `${config.name}::${config.description}::${JSON.stringify(config.inputSchema)}::${JSON.stringify(config.outputSchema ?? {})}::${JSON.stringify(config.annotations ?? {})}`;
|
|
36
|
+
}
|
|
37
|
+
function useWebMCPTool(config) {
|
|
38
|
+
const registeredNameRef = React2.useRef(null);
|
|
39
|
+
const configRef = React2.useRef(config);
|
|
40
|
+
configRef.current = config;
|
|
41
|
+
const fingerprint = toolFingerprint(config);
|
|
42
|
+
React2.useEffect(() => {
|
|
43
|
+
const mc = getModelContext();
|
|
44
|
+
if (!mc) {
|
|
45
|
+
warnIfUnavailable("useWebMCPTool");
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
if (registeredNameRef.current && registeredNameRef.current !== config.name) {
|
|
49
|
+
try {
|
|
50
|
+
mc.unregisterTool(registeredNameRef.current);
|
|
51
|
+
} catch {
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
const toolDef = {
|
|
55
|
+
name: config.name,
|
|
56
|
+
description: config.description,
|
|
57
|
+
inputSchema: config.inputSchema,
|
|
58
|
+
...config.outputSchema ? { outputSchema: config.outputSchema } : {},
|
|
59
|
+
...config.annotations ? { annotations: config.annotations } : {},
|
|
60
|
+
execute: (input) => {
|
|
61
|
+
return configRef.current.execute(input);
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
try {
|
|
65
|
+
mc.registerTool(toolDef);
|
|
66
|
+
registeredNameRef.current = config.name;
|
|
67
|
+
} catch (err) {
|
|
68
|
+
console.error(`[react-webmcp] Failed to register tool "${config.name}":`, err);
|
|
69
|
+
}
|
|
70
|
+
return () => {
|
|
71
|
+
try {
|
|
72
|
+
mc.unregisterTool(config.name);
|
|
73
|
+
} catch {
|
|
74
|
+
}
|
|
75
|
+
registeredNameRef.current = null;
|
|
76
|
+
};
|
|
77
|
+
}, [fingerprint, config.name]);
|
|
78
|
+
}
|
|
79
|
+
function toolsFingerprint(tools) {
|
|
80
|
+
return tools.map(
|
|
81
|
+
(t) => `${t.name}::${t.description}::${JSON.stringify(t.inputSchema)}::${JSON.stringify(t.annotations ?? {})}`
|
|
82
|
+
).join("|");
|
|
83
|
+
}
|
|
84
|
+
function useWebMCPContext(config) {
|
|
85
|
+
const prevFingerprintRef = React2.useRef("");
|
|
86
|
+
const toolsRef = React2.useRef(config.tools);
|
|
87
|
+
toolsRef.current = config.tools;
|
|
88
|
+
const fingerprint = toolsFingerprint(config.tools);
|
|
89
|
+
React2.useEffect(() => {
|
|
90
|
+
if (fingerprint === prevFingerprintRef.current) {
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
prevFingerprintRef.current = fingerprint;
|
|
94
|
+
const mc = getModelContext();
|
|
95
|
+
if (!mc) {
|
|
96
|
+
warnIfUnavailable("useWebMCPContext");
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
const stableTools = toolsRef.current.map((tool, idx) => ({
|
|
100
|
+
...tool,
|
|
101
|
+
execute: (input) => {
|
|
102
|
+
return toolsRef.current[idx].execute(input);
|
|
103
|
+
}
|
|
104
|
+
}));
|
|
105
|
+
try {
|
|
106
|
+
mc.provideContext({ tools: stableTools });
|
|
107
|
+
} catch (err) {
|
|
108
|
+
console.error("[react-webmcp] Failed to provide context:", err);
|
|
109
|
+
}
|
|
110
|
+
return () => {
|
|
111
|
+
try {
|
|
112
|
+
mc.clearContext();
|
|
113
|
+
} catch {
|
|
114
|
+
}
|
|
115
|
+
};
|
|
116
|
+
}, [fingerprint]);
|
|
117
|
+
}
|
|
118
|
+
function useToolEvent(event, callback, toolNameFilter) {
|
|
119
|
+
React2.useEffect(() => {
|
|
120
|
+
const handler = (e) => {
|
|
121
|
+
const toolName = e.toolName ?? e.detail?.toolName;
|
|
122
|
+
if (!toolName) return;
|
|
123
|
+
if (toolNameFilter && toolName !== toolNameFilter) return;
|
|
124
|
+
callback(toolName);
|
|
125
|
+
};
|
|
126
|
+
window.addEventListener(event, handler);
|
|
127
|
+
return () => {
|
|
128
|
+
window.removeEventListener(event, handler);
|
|
129
|
+
};
|
|
130
|
+
}, [event, callback, toolNameFilter]);
|
|
131
|
+
}
|
|
132
|
+
function WebMCPForm({
|
|
133
|
+
toolName,
|
|
134
|
+
toolDescription,
|
|
135
|
+
toolAutoSubmit,
|
|
136
|
+
onSubmit,
|
|
137
|
+
onToolActivated,
|
|
138
|
+
onToolCancel,
|
|
139
|
+
children,
|
|
140
|
+
...rest
|
|
141
|
+
}) {
|
|
142
|
+
const formRef = React2.useRef(null);
|
|
143
|
+
React2.useEffect(() => {
|
|
144
|
+
const handleActivated = (e) => {
|
|
145
|
+
const name = e.toolName ?? e.detail?.toolName;
|
|
146
|
+
if (name === toolName && onToolActivated) {
|
|
147
|
+
onToolActivated(name);
|
|
148
|
+
}
|
|
149
|
+
};
|
|
150
|
+
const handleCancel = (e) => {
|
|
151
|
+
const name = e.toolName ?? e.detail?.toolName;
|
|
152
|
+
if (name === toolName && onToolCancel) {
|
|
153
|
+
onToolCancel(name);
|
|
154
|
+
}
|
|
155
|
+
};
|
|
156
|
+
window.addEventListener("toolactivated", handleActivated);
|
|
157
|
+
window.addEventListener("toolcancel", handleCancel);
|
|
158
|
+
return () => {
|
|
159
|
+
window.removeEventListener("toolactivated", handleActivated);
|
|
160
|
+
window.removeEventListener("toolcancel", handleCancel);
|
|
161
|
+
};
|
|
162
|
+
}, [toolName, onToolActivated, onToolCancel]);
|
|
163
|
+
const handleSubmit = React2.useCallback(
|
|
164
|
+
(e) => {
|
|
165
|
+
if (onSubmit) {
|
|
166
|
+
onSubmit(e.nativeEvent);
|
|
167
|
+
}
|
|
168
|
+
},
|
|
169
|
+
[onSubmit]
|
|
170
|
+
);
|
|
171
|
+
const webmcpAttrs = {
|
|
172
|
+
toolname: toolName,
|
|
173
|
+
tooldescription: toolDescription
|
|
174
|
+
};
|
|
175
|
+
if (toolAutoSubmit) {
|
|
176
|
+
webmcpAttrs.toolautosubmit = "";
|
|
177
|
+
}
|
|
178
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
179
|
+
"form",
|
|
180
|
+
{
|
|
181
|
+
ref: formRef,
|
|
182
|
+
onSubmit: handleSubmit,
|
|
183
|
+
...webmcpAttrs,
|
|
184
|
+
...rest,
|
|
185
|
+
children
|
|
186
|
+
}
|
|
187
|
+
);
|
|
188
|
+
}
|
|
189
|
+
var WebMCPInput = React2__default.default.forwardRef(
|
|
190
|
+
({ toolParamTitle, toolParamDescription, ...rest }, ref) => {
|
|
191
|
+
const webmcpAttrs = {};
|
|
192
|
+
if (toolParamTitle) {
|
|
193
|
+
webmcpAttrs.toolparamtitle = toolParamTitle;
|
|
194
|
+
}
|
|
195
|
+
if (toolParamDescription) {
|
|
196
|
+
webmcpAttrs.toolparamdescription = toolParamDescription;
|
|
197
|
+
}
|
|
198
|
+
return /* @__PURE__ */ jsxRuntime.jsx("input", { ref, ...webmcpAttrs, ...rest });
|
|
199
|
+
}
|
|
200
|
+
);
|
|
201
|
+
WebMCPInput.displayName = "WebMCPInput";
|
|
202
|
+
var WebMCPSelect = React2__default.default.forwardRef(({ toolParamTitle, toolParamDescription, children, ...rest }, ref) => {
|
|
203
|
+
const webmcpAttrs = {};
|
|
204
|
+
if (toolParamTitle) {
|
|
205
|
+
webmcpAttrs.toolparamtitle = toolParamTitle;
|
|
206
|
+
}
|
|
207
|
+
if (toolParamDescription) {
|
|
208
|
+
webmcpAttrs.toolparamdescription = toolParamDescription;
|
|
209
|
+
}
|
|
210
|
+
return /* @__PURE__ */ jsxRuntime.jsx("select", { ref, ...webmcpAttrs, ...rest, children });
|
|
211
|
+
});
|
|
212
|
+
WebMCPSelect.displayName = "WebMCPSelect";
|
|
213
|
+
var WebMCPTextarea = React2__default.default.forwardRef(({ toolParamTitle, toolParamDescription, ...rest }, ref) => {
|
|
214
|
+
const webmcpAttrs = {};
|
|
215
|
+
if (toolParamTitle) {
|
|
216
|
+
webmcpAttrs.toolparamtitle = toolParamTitle;
|
|
217
|
+
}
|
|
218
|
+
if (toolParamDescription) {
|
|
219
|
+
webmcpAttrs.toolparamdescription = toolParamDescription;
|
|
220
|
+
}
|
|
221
|
+
return /* @__PURE__ */ jsxRuntime.jsx("textarea", { ref, ...webmcpAttrs, ...rest });
|
|
222
|
+
});
|
|
223
|
+
WebMCPTextarea.displayName = "WebMCPTextarea";
|
|
224
|
+
var WebMCPReactContext = React2.createContext({
|
|
225
|
+
available: false,
|
|
226
|
+
testingAvailable: false
|
|
227
|
+
});
|
|
228
|
+
function WebMCPProvider({ children }) {
|
|
229
|
+
const value = React2.useMemo(
|
|
230
|
+
() => ({
|
|
231
|
+
available: isWebMCPAvailable(),
|
|
232
|
+
testingAvailable: isWebMCPTestingAvailable()
|
|
233
|
+
}),
|
|
234
|
+
[]
|
|
235
|
+
);
|
|
236
|
+
return /* @__PURE__ */ jsxRuntime.jsx(WebMCPReactContext.Provider, { value, children });
|
|
237
|
+
}
|
|
238
|
+
function useWebMCPStatus() {
|
|
239
|
+
return React2.useContext(WebMCPReactContext);
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
exports.WebMCPForm = WebMCPForm;
|
|
243
|
+
exports.WebMCPInput = WebMCPInput;
|
|
244
|
+
exports.WebMCPProvider = WebMCPProvider;
|
|
245
|
+
exports.WebMCPSelect = WebMCPSelect;
|
|
246
|
+
exports.WebMCPTextarea = WebMCPTextarea;
|
|
247
|
+
exports.getModelContext = getModelContext;
|
|
248
|
+
exports.isWebMCPAvailable = isWebMCPAvailable;
|
|
249
|
+
exports.isWebMCPTestingAvailable = isWebMCPTestingAvailable;
|
|
250
|
+
exports.useToolEvent = useToolEvent;
|
|
251
|
+
exports.useWebMCPContext = useWebMCPContext;
|
|
252
|
+
exports.useWebMCPStatus = useWebMCPStatus;
|
|
253
|
+
exports.useWebMCPTool = useWebMCPTool;
|
|
254
|
+
//# sourceMappingURL=index.js.map
|
|
255
|
+
//# sourceMappingURL=index.js.map
|