react-webmcp 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +135 -16
- package/dist/index.d.mts +300 -13
- package/dist/index.d.ts +300 -13
- package/dist/index.js +389 -21
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +369 -10
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { jsx } from 'react/jsx-runtime';
|
|
1
|
+
import React6, { createContext, useRef, useEffect, useCallback, useMemo, useContext, useState } from 'react';
|
|
2
|
+
import { jsx, Fragment } from 'react/jsx-runtime';
|
|
3
3
|
|
|
4
4
|
// src/hooks/useWebMCPTool.ts
|
|
5
5
|
|
|
@@ -41,6 +41,23 @@ function useWebMCPTool(config) {
|
|
|
41
41
|
warnIfUnavailable("useWebMCPTool");
|
|
42
42
|
return;
|
|
43
43
|
}
|
|
44
|
+
if (!config.name) {
|
|
45
|
+
throw new Error("[react-webmcp] Tool name must be a non-empty string.");
|
|
46
|
+
}
|
|
47
|
+
if (!config.description) {
|
|
48
|
+
throw new Error(
|
|
49
|
+
`[react-webmcp] Tool "${config.name}" description must be a non-empty string.`
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
if (process.env.NODE_ENV !== "production" && config.inputSchema) {
|
|
53
|
+
try {
|
|
54
|
+
JSON.stringify(config.inputSchema);
|
|
55
|
+
} catch (e) {
|
|
56
|
+
throw new Error(
|
|
57
|
+
`[react-webmcp] Tool "${config.name}" inputSchema is not JSON-serializable: ${e instanceof Error ? e.message : String(e)}`
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
44
61
|
if (registeredNameRef.current && registeredNameRef.current !== config.name) {
|
|
45
62
|
try {
|
|
46
63
|
mc.unregisterTool(registeredNameRef.current);
|
|
@@ -66,10 +83,17 @@ function useWebMCPTool(config) {
|
|
|
66
83
|
registeredNameRef.current = config.name;
|
|
67
84
|
} catch (err) {
|
|
68
85
|
if (process.env.NODE_ENV !== "production") {
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
86
|
+
const isDuplicate = err instanceof DOMException && err.name === "InvalidStateError";
|
|
87
|
+
if (isDuplicate) {
|
|
88
|
+
console.warn(
|
|
89
|
+
`[react-webmcp] Tool "${config.name}" is already registered (InvalidStateError). This may indicate a duplicate registration or a missed unregisterTool().`
|
|
90
|
+
);
|
|
91
|
+
} else {
|
|
92
|
+
console.error(
|
|
93
|
+
`[react-webmcp] Failed to register tool "${config.name}":`,
|
|
94
|
+
err
|
|
95
|
+
);
|
|
96
|
+
}
|
|
73
97
|
}
|
|
74
98
|
}
|
|
75
99
|
return () => {
|
|
@@ -96,6 +120,16 @@ function useWebMCPContext(config) {
|
|
|
96
120
|
warnIfUnavailable("useWebMCPContext");
|
|
97
121
|
return;
|
|
98
122
|
}
|
|
123
|
+
for (const tool of toolsRef.current) {
|
|
124
|
+
if (!tool.name) {
|
|
125
|
+
throw new Error("[react-webmcp] Tool name must be a non-empty string.");
|
|
126
|
+
}
|
|
127
|
+
if (!tool.description) {
|
|
128
|
+
throw new Error(
|
|
129
|
+
`[react-webmcp] Tool "${tool.name}" description must be a non-empty string.`
|
|
130
|
+
);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
99
133
|
const stableTools = toolsRef.current.map((tool, idx) => {
|
|
100
134
|
const def = {
|
|
101
135
|
name: tool.name,
|
|
@@ -201,7 +235,7 @@ function WebMCPForm({
|
|
|
201
235
|
}
|
|
202
236
|
);
|
|
203
237
|
}
|
|
204
|
-
var WebMCPInput =
|
|
238
|
+
var WebMCPInput = React6.forwardRef(
|
|
205
239
|
({ toolParamTitle, toolParamDescription, ...rest }, ref) => {
|
|
206
240
|
const webmcpAttrs = {};
|
|
207
241
|
if (toolParamTitle) {
|
|
@@ -214,7 +248,7 @@ var WebMCPInput = React2.forwardRef(
|
|
|
214
248
|
}
|
|
215
249
|
);
|
|
216
250
|
WebMCPInput.displayName = "WebMCPInput";
|
|
217
|
-
var WebMCPSelect =
|
|
251
|
+
var WebMCPSelect = React6.forwardRef(({ toolParamTitle, toolParamDescription, children, ...rest }, ref) => {
|
|
218
252
|
const webmcpAttrs = {};
|
|
219
253
|
if (toolParamTitle) {
|
|
220
254
|
webmcpAttrs.toolparamtitle = toolParamTitle;
|
|
@@ -225,7 +259,7 @@ var WebMCPSelect = React2.forwardRef(({ toolParamTitle, toolParamDescription, ch
|
|
|
225
259
|
return /* @__PURE__ */ jsx("select", { ref, ...webmcpAttrs, ...rest, children });
|
|
226
260
|
});
|
|
227
261
|
WebMCPSelect.displayName = "WebMCPSelect";
|
|
228
|
-
var WebMCPTextarea =
|
|
262
|
+
var WebMCPTextarea = React6.forwardRef(({ toolParamTitle, toolParamDescription, ...rest }, ref) => {
|
|
229
263
|
const webmcpAttrs = {};
|
|
230
264
|
if (toolParamTitle) {
|
|
231
265
|
webmcpAttrs.toolparamtitle = toolParamTitle;
|
|
@@ -253,7 +287,332 @@ function WebMCPProvider({ children }) {
|
|
|
253
287
|
function useWebMCPStatus() {
|
|
254
288
|
return useContext(WebMCPReactContext);
|
|
255
289
|
}
|
|
290
|
+
function extractOptions(children) {
|
|
291
|
+
const results = [];
|
|
292
|
+
React6.Children.toArray(children).forEach((child) => {
|
|
293
|
+
if (!React6.isValidElement(child)) return;
|
|
294
|
+
const props = child.props;
|
|
295
|
+
if (props.value !== void 0 && props.value !== null) {
|
|
296
|
+
const value = props.value;
|
|
297
|
+
let label;
|
|
298
|
+
if (typeof props.children === "string") {
|
|
299
|
+
label = props.children;
|
|
300
|
+
} else {
|
|
301
|
+
label = String(value);
|
|
302
|
+
}
|
|
303
|
+
results.push({ value, label });
|
|
304
|
+
}
|
|
305
|
+
if (props.children) {
|
|
306
|
+
results.push(...extractOptions(props.children));
|
|
307
|
+
}
|
|
308
|
+
});
|
|
309
|
+
return results;
|
|
310
|
+
}
|
|
311
|
+
function extractFields(children) {
|
|
312
|
+
const fields = [];
|
|
313
|
+
React6.Children.toArray(children).forEach((child) => {
|
|
314
|
+
if (!React6.isValidElement(child)) return;
|
|
315
|
+
const props = child.props;
|
|
316
|
+
const inputProps = props.inputProps;
|
|
317
|
+
const slotInput = props.slotProps?.input;
|
|
318
|
+
const name = props.name ?? inputProps?.name ?? slotInput?.name;
|
|
319
|
+
if (name) {
|
|
320
|
+
const field = { name };
|
|
321
|
+
if (props.type !== void 0) field.type = props.type;
|
|
322
|
+
if (props.required !== void 0) field.required = Boolean(props.required);
|
|
323
|
+
if (props.min !== void 0) field.min = Number(props.min);
|
|
324
|
+
if (props.max !== void 0) field.max = Number(props.max);
|
|
325
|
+
if (props.minLength !== void 0) field.minLength = Number(props.minLength);
|
|
326
|
+
if (props.maxLength !== void 0) field.maxLength = Number(props.maxLength);
|
|
327
|
+
if (props.pattern !== void 0) field.pattern = props.pattern;
|
|
328
|
+
if (props.children) {
|
|
329
|
+
const options = extractOptions(props.children);
|
|
330
|
+
if (options.length > 0) {
|
|
331
|
+
field.enumValues = options.map((o) => o.value);
|
|
332
|
+
field.oneOf = options;
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
fields.push(field);
|
|
336
|
+
} else if (props.children) {
|
|
337
|
+
fields.push(...extractFields(props.children));
|
|
338
|
+
}
|
|
339
|
+
});
|
|
340
|
+
return fields;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
// src/adapters/buildSchema.ts
|
|
344
|
+
function mapHtmlTypeToSchemaType(htmlType) {
|
|
345
|
+
switch (htmlType) {
|
|
346
|
+
case "number":
|
|
347
|
+
case "range":
|
|
348
|
+
return "number";
|
|
349
|
+
case "checkbox":
|
|
350
|
+
return "boolean";
|
|
351
|
+
default:
|
|
352
|
+
return "string";
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
function buildInputSchema(fields) {
|
|
356
|
+
const properties = {};
|
|
357
|
+
const required = [];
|
|
358
|
+
const sortedFields = [...fields].sort((a, b) => a.name.localeCompare(b.name));
|
|
359
|
+
for (const field of sortedFields) {
|
|
360
|
+
const prop = {
|
|
361
|
+
type: mapHtmlTypeToSchemaType(field.type)
|
|
362
|
+
};
|
|
363
|
+
if (field.title) prop.title = field.title;
|
|
364
|
+
if (field.description) prop.description = field.description;
|
|
365
|
+
if (field.min !== void 0) prop.minimum = field.min;
|
|
366
|
+
if (field.max !== void 0) prop.maximum = field.max;
|
|
367
|
+
if (field.minLength !== void 0) prop.minLength = field.minLength;
|
|
368
|
+
if (field.maxLength !== void 0) prop.maxLength = field.maxLength;
|
|
369
|
+
if (field.pattern) prop.pattern = field.pattern;
|
|
370
|
+
if (field.enumValues && field.enumValues.length > 0) {
|
|
371
|
+
prop.enum = field.enumValues;
|
|
372
|
+
}
|
|
373
|
+
if (field.oneOf && field.oneOf.length > 0) {
|
|
374
|
+
prop.oneOf = field.oneOf.map((opt) => ({
|
|
375
|
+
const: opt.value,
|
|
376
|
+
title: opt.label
|
|
377
|
+
}));
|
|
378
|
+
}
|
|
379
|
+
properties[field.name] = prop;
|
|
380
|
+
if (field.required) {
|
|
381
|
+
required.push(field.name);
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
const schema = {
|
|
385
|
+
type: "object",
|
|
386
|
+
properties
|
|
387
|
+
};
|
|
388
|
+
if (required.length > 0) {
|
|
389
|
+
schema.required = required.sort();
|
|
390
|
+
}
|
|
391
|
+
return schema;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
// src/adapters/validateSchema.ts
|
|
395
|
+
function validateSchema(fields, options) {
|
|
396
|
+
if (process.env.NODE_ENV === "production") return;
|
|
397
|
+
const strict = options?.strict ?? false;
|
|
398
|
+
const issues = [];
|
|
399
|
+
const seen = /* @__PURE__ */ new Set();
|
|
400
|
+
for (const field of fields) {
|
|
401
|
+
if (seen.has(field.name)) {
|
|
402
|
+
issues.push(`Duplicate field name "${field.name}".`);
|
|
403
|
+
}
|
|
404
|
+
seen.add(field.name);
|
|
405
|
+
const schemaType = mapHtmlTypeToSchemaType(field.type);
|
|
406
|
+
if (field.pattern !== void 0 && schemaType !== "string") {
|
|
407
|
+
issues.push(
|
|
408
|
+
`Field "${field.name}": pattern is only valid for string types, but type is "${schemaType}".`
|
|
409
|
+
);
|
|
410
|
+
}
|
|
411
|
+
if ((field.min !== void 0 || field.max !== void 0) && schemaType !== "number") {
|
|
412
|
+
issues.push(
|
|
413
|
+
`Field "${field.name}": min/max are only valid for number types, but type is "${schemaType}".`
|
|
414
|
+
);
|
|
415
|
+
}
|
|
416
|
+
if ((field.minLength !== void 0 || field.maxLength !== void 0) && schemaType !== "string") {
|
|
417
|
+
issues.push(
|
|
418
|
+
`Field "${field.name}": minLength/maxLength are only valid for string types, but type is "${schemaType}".`
|
|
419
|
+
);
|
|
420
|
+
}
|
|
421
|
+
if (field.enumValues && field.enumValues.length > 0) {
|
|
422
|
+
for (const val of field.enumValues) {
|
|
423
|
+
const valType = typeof val;
|
|
424
|
+
if (schemaType === "string" && valType !== "string") {
|
|
425
|
+
issues.push(
|
|
426
|
+
`Field "${field.name}": enum value ${JSON.stringify(val)} is not a string.`
|
|
427
|
+
);
|
|
428
|
+
}
|
|
429
|
+
if (schemaType === "number" && valType !== "number") {
|
|
430
|
+
issues.push(
|
|
431
|
+
`Field "${field.name}": enum value ${JSON.stringify(val)} is not a number.`
|
|
432
|
+
);
|
|
433
|
+
}
|
|
434
|
+
if (schemaType === "boolean" && valType !== "boolean") {
|
|
435
|
+
issues.push(
|
|
436
|
+
`Field "${field.name}": enum value ${JSON.stringify(val)} is not a boolean.`
|
|
437
|
+
);
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
for (const issue of issues) {
|
|
443
|
+
if (strict) {
|
|
444
|
+
throw new Error(`[react-webmcp] ${issue}`);
|
|
445
|
+
}
|
|
446
|
+
console.warn(`[react-webmcp] ${issue}`);
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
// src/adapters/useSchemaCollector.ts
|
|
451
|
+
var ToolContext = createContext(null);
|
|
452
|
+
function fieldsFingerprint(fields) {
|
|
453
|
+
return fields.map(
|
|
454
|
+
(f) => `${f.name}::${f.type ?? ""}::${f.required ?? ""}::${f.title ?? ""}::${f.description ?? ""}::${JSON.stringify(f.enumValues ?? [])}::${JSON.stringify(f.oneOf ?? [])}::${f.min ?? ""}::${f.max ?? ""}::${f.minLength ?? ""}::${f.maxLength ?? ""}::${f.pattern ?? ""}`
|
|
455
|
+
).join("|");
|
|
456
|
+
}
|
|
457
|
+
function mergeField(base, override) {
|
|
458
|
+
const result = { ...base };
|
|
459
|
+
for (const key of Object.keys(override)) {
|
|
460
|
+
if (override[key] !== void 0) {
|
|
461
|
+
result[key] = override[key];
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
return result;
|
|
465
|
+
}
|
|
466
|
+
function useSchemaCollector({
|
|
467
|
+
children,
|
|
468
|
+
fields: fieldsProp,
|
|
469
|
+
strict
|
|
470
|
+
}) {
|
|
471
|
+
const contextFieldsRef = useRef(/* @__PURE__ */ new Map());
|
|
472
|
+
const [version, setVersion] = useState(0);
|
|
473
|
+
const registerField = useCallback((field) => {
|
|
474
|
+
contextFieldsRef.current.set(field.name, field);
|
|
475
|
+
setVersion((v) => v + 1);
|
|
476
|
+
}, []);
|
|
477
|
+
const unregisterField = useCallback((name) => {
|
|
478
|
+
contextFieldsRef.current.delete(name);
|
|
479
|
+
setVersion((v) => v + 1);
|
|
480
|
+
}, []);
|
|
481
|
+
const childrenFields = extractFields(children);
|
|
482
|
+
const childrenFP = fieldsFingerprint(childrenFields);
|
|
483
|
+
const merged = useMemo(() => {
|
|
484
|
+
const fieldMap = /* @__PURE__ */ new Map();
|
|
485
|
+
for (const field of childrenFields) {
|
|
486
|
+
fieldMap.set(field.name, field);
|
|
487
|
+
}
|
|
488
|
+
if (fieldsProp) {
|
|
489
|
+
for (const [name, overrides] of Object.entries(fieldsProp)) {
|
|
490
|
+
const existing = fieldMap.get(name);
|
|
491
|
+
if (existing) {
|
|
492
|
+
fieldMap.set(name, mergeField(existing, overrides));
|
|
493
|
+
} else {
|
|
494
|
+
fieldMap.set(name, { name, ...overrides });
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
for (const [name, field] of contextFieldsRef.current) {
|
|
499
|
+
const existing = fieldMap.get(name);
|
|
500
|
+
if (existing) {
|
|
501
|
+
fieldMap.set(name, mergeField(existing, field));
|
|
502
|
+
} else {
|
|
503
|
+
fieldMap.set(name, field);
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
const result = Array.from(fieldMap.values());
|
|
507
|
+
if (process.env.NODE_ENV !== "production") {
|
|
508
|
+
validateSchema(result, { strict });
|
|
509
|
+
}
|
|
510
|
+
return result;
|
|
511
|
+
}, [childrenFP, fieldsProp, version, strict]);
|
|
512
|
+
const mergedFP = fieldsFingerprint(merged);
|
|
513
|
+
const schema = useMemo(
|
|
514
|
+
() => buildInputSchema(merged),
|
|
515
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
516
|
+
[mergedFP]
|
|
517
|
+
);
|
|
518
|
+
return { schema, registerField, unregisterField };
|
|
519
|
+
}
|
|
520
|
+
function WebMCPTool({
|
|
521
|
+
name,
|
|
522
|
+
description,
|
|
523
|
+
onExecute,
|
|
524
|
+
fields: fieldsProp,
|
|
525
|
+
strict,
|
|
526
|
+
autoSubmit,
|
|
527
|
+
annotations,
|
|
528
|
+
onToolActivated,
|
|
529
|
+
onToolCancel,
|
|
530
|
+
children
|
|
531
|
+
}) {
|
|
532
|
+
const { schema, registerField, unregisterField } = useSchemaCollector({
|
|
533
|
+
children,
|
|
534
|
+
fields: fieldsProp,
|
|
535
|
+
strict
|
|
536
|
+
});
|
|
537
|
+
const executeRef = useRef(onExecute);
|
|
538
|
+
executeRef.current = onExecute;
|
|
539
|
+
useWebMCPTool({
|
|
540
|
+
name,
|
|
541
|
+
description,
|
|
542
|
+
inputSchema: schema,
|
|
543
|
+
annotations,
|
|
544
|
+
execute: (input) => executeRef.current(input)
|
|
545
|
+
});
|
|
546
|
+
useEffect(() => {
|
|
547
|
+
if (typeof window === "undefined") return;
|
|
548
|
+
if (!onToolActivated && !onToolCancel) return;
|
|
549
|
+
const handleActivated = (e) => {
|
|
550
|
+
const toolName = e.toolName ?? e.detail?.toolName;
|
|
551
|
+
if (toolName === name && onToolActivated) {
|
|
552
|
+
onToolActivated(toolName);
|
|
553
|
+
}
|
|
554
|
+
};
|
|
555
|
+
const handleCancel = (e) => {
|
|
556
|
+
const toolName = e.toolName ?? e.detail?.toolName;
|
|
557
|
+
if (toolName === name && onToolCancel) {
|
|
558
|
+
onToolCancel(toolName);
|
|
559
|
+
}
|
|
560
|
+
};
|
|
561
|
+
window.addEventListener("toolactivated", handleActivated);
|
|
562
|
+
window.addEventListener("toolcancel", handleCancel);
|
|
563
|
+
return () => {
|
|
564
|
+
window.removeEventListener("toolactivated", handleActivated);
|
|
565
|
+
window.removeEventListener("toolcancel", handleCancel);
|
|
566
|
+
};
|
|
567
|
+
}, [name, onToolActivated, onToolCancel]);
|
|
568
|
+
return /* @__PURE__ */ jsx(ToolContext.Provider, { value: { registerField, unregisterField }, children });
|
|
569
|
+
}
|
|
570
|
+
WebMCPTool.displayName = "WebMCP.Tool";
|
|
571
|
+
function fieldFingerprint(field) {
|
|
572
|
+
return `${field.name}::${field.type ?? ""}::${field.required ?? ""}::${field.title ?? ""}::${field.description ?? ""}::${JSON.stringify(field.enumValues ?? [])}::${JSON.stringify(field.oneOf ?? [])}::${field.min ?? ""}::${field.max ?? ""}::${field.minLength ?? ""}::${field.maxLength ?? ""}::${field.pattern ?? ""}`;
|
|
573
|
+
}
|
|
574
|
+
function useRegisterField(field) {
|
|
575
|
+
const ctx = useContext(ToolContext);
|
|
576
|
+
const fp = fieldFingerprint(field);
|
|
577
|
+
useEffect(() => {
|
|
578
|
+
if (!ctx) {
|
|
579
|
+
if (process.env.NODE_ENV !== "production") {
|
|
580
|
+
console.warn(
|
|
581
|
+
`[react-webmcp] useRegisterField: no WebMCP.Tool context found for field "${field.name}". Wrap this component in a <WebMCP.Tool> to register fields.`
|
|
582
|
+
);
|
|
583
|
+
}
|
|
584
|
+
return;
|
|
585
|
+
}
|
|
586
|
+
ctx.registerField(field);
|
|
587
|
+
return () => {
|
|
588
|
+
try {
|
|
589
|
+
ctx.unregisterField(field.name);
|
|
590
|
+
} catch {
|
|
591
|
+
}
|
|
592
|
+
};
|
|
593
|
+
}, [fp]);
|
|
594
|
+
}
|
|
595
|
+
function WebMCPField({
|
|
596
|
+
children,
|
|
597
|
+
name,
|
|
598
|
+
...rest
|
|
599
|
+
}) {
|
|
600
|
+
const field = { name, ...rest };
|
|
601
|
+
if (!field.enumValues && !field.oneOf) {
|
|
602
|
+
const options = extractOptions(children);
|
|
603
|
+
if (options.length > 0) {
|
|
604
|
+
field.enumValues = options.map((o) => o.value);
|
|
605
|
+
field.oneOf = options;
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
useRegisterField(field);
|
|
609
|
+
return /* @__PURE__ */ jsx(Fragment, { children });
|
|
610
|
+
}
|
|
611
|
+
WebMCPField.displayName = "WebMCP.Field";
|
|
612
|
+
|
|
613
|
+
// src/adapters/index.ts
|
|
614
|
+
var WebMCP = { Tool: WebMCPTool, Field: WebMCPField };
|
|
256
615
|
|
|
257
|
-
export { WebMCPForm, WebMCPInput, WebMCPProvider, WebMCPSelect, WebMCPTextarea, getModelContext, isWebMCPAvailable, isWebMCPTestingAvailable, useToolEvent, useWebMCPContext, useWebMCPStatus, useWebMCPTool };
|
|
616
|
+
export { WebMCP, WebMCPField, WebMCPForm, WebMCPInput, WebMCPProvider, WebMCPSelect, WebMCPTextarea, WebMCPTool, buildInputSchema, extractFields, extractOptions, getModelContext, isWebMCPAvailable, isWebMCPTestingAvailable, useRegisterField, useSchemaCollector, useToolEvent, useWebMCPContext, useWebMCPStatus, useWebMCPTool, validateSchema };
|
|
258
617
|
//# sourceMappingURL=index.mjs.map
|
|
259
618
|
//# sourceMappingURL=index.mjs.map
|