webmcp-nexus-sdk 0.1.11 → 0.1.13
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 +48 -4
- package/dist/index.cjs +128 -21
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +28 -1
- package/dist/index.d.ts +28 -1
- package/dist/index.js +125 -20
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -30,12 +30,13 @@
|
|
|
30
30
|
|
|
31
31
|
`webmcp-nexus-sdk` 是 [WebMCP Nexus](https://github.com/alibaba/webmcp-nexus) 项目的运行时 SDK,围绕 [W3C WebMCP 标准提案](https://webmcp.org) 提供生产可用的 React 集成方案。
|
|
32
32
|
|
|
33
|
-
|
|
33
|
+
它只导出三个 API —— `registerGlobalTools`、`useWebMcpTools` 和 `withWebMcpTools` —— 即可覆盖**全局**、**路由**、**组件**三种生命周期,让任何普通的 TypeScript 函数无需包装即可被 MCP 客户端(Claude Desktop、Cursor、VS Code 等)直接调用。
|
|
34
34
|
|
|
35
35
|
## 核心特性
|
|
36
36
|
|
|
37
|
-
- 🪶 **极简 API** —— 仅
|
|
37
|
+
- 🪶 **极简 API** —— 仅 3 个函数即可完成所有注册场景。
|
|
38
38
|
- 🧩 **三级作用域** —— 全局工具、路由级工具、组件级工具自动随生命周期挂载 / 注销。
|
|
39
|
+
- 🏛️ **Class 组件支持** —— `withWebMcpTools` HOC 让 class 组件方法与函数组件 Hook 享受同等的自动注册体验。
|
|
39
40
|
- 🌐 **跨浏览器兼容** —— Chrome 146+ 使用原生 `navigator.modelContext`;其他环境在首次注册时自动启用内置的 [`@mcp-b/webmcp-polyfill`](https://www.npmjs.com/package/@mcp-b/webmcp-polyfill),业务代码完全无感。
|
|
40
41
|
- 🔁 **HMR 友好** —— 开发阶段修改函数签名后,工具 schema 会自动重新注册。
|
|
41
42
|
- 🛡️ **冲突感知** —— 内置 scope ownership registry,多个 scope 注册同名工具时仅警告不阻断,注销严格隔离。
|
|
@@ -45,7 +46,7 @@
|
|
|
45
46
|
|
|
46
47
|
| 维度 | 业内常见做法 | webmcp-nexus-sdk |
|
|
47
48
|
| ---------- | ------------------------------------ | ---------------------------------------------------- |
|
|
48
|
-
| API 表面 | 装饰器 / 包装函数 / 显式 schema 配置 | **
|
|
49
|
+
| API 表面 | 装饰器 / 包装函数 / 显式 schema 配置 | **3 个 API** 覆盖全部场景 |
|
|
49
50
|
| 函数侵入度 | `defineApi` / `createTool` 等包装 | **零侵入**——函数保持原样,原有调用方完全无感 |
|
|
50
51
|
| 生命周期 | 仅支持全局注册,需手动维护 | 全局 / 路由 / 组件 **三级作用域**,组件卸载自动注销 |
|
|
51
52
|
| 浏览器兼容 | 调用方自行判断 + 兜底 | SDK 内置 polyfill **惰性加载** |
|
|
@@ -122,6 +123,27 @@ export default function TasksPage() {
|
|
|
122
123
|
|
|
123
124
|
组件卸载时同名工具会自动从 `modelContext` 注销,**避免 Agent 在错误的页面调用错误的工具**。
|
|
124
125
|
|
|
126
|
+
### 4. Class 组件注册
|
|
127
|
+
|
|
128
|
+
```tsx
|
|
129
|
+
import { withWebMcpTools } from 'webmcp-nexus-sdk';
|
|
130
|
+
|
|
131
|
+
class MyPanel extends React.Component {
|
|
132
|
+
/** 在面板中搜索 @readonly */
|
|
133
|
+
searchInPanel(params: { query: string }) { /* ... */ }
|
|
134
|
+
|
|
135
|
+
/** 清除搜索 */
|
|
136
|
+
clearSearch = (params: { confirm?: boolean }) => { /* ... */ };
|
|
137
|
+
|
|
138
|
+
render() { return <div />; }
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
export default withWebMcpTools(MyPanel);
|
|
142
|
+
// 或仅注册指定方法:withWebMcpTools(MyPanel, ['searchInPanel'])
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
`withWebMcpTools` 必须作为最内层 HOC 直接包裹 class 组件。
|
|
146
|
+
|
|
125
147
|
## API 速览
|
|
126
148
|
|
|
127
149
|
### `registerGlobalTools(tools)`
|
|
@@ -150,6 +172,28 @@ function MyDialog() {
|
|
|
150
172
|
}
|
|
151
173
|
```
|
|
152
174
|
|
|
175
|
+
### `withWebMcpTools(Component, methodNames?)`
|
|
176
|
+
|
|
177
|
+
高阶组件,将 class 组件的方法注册为 WebMCP 工具 —— mount 时注册、unmount 时注销。
|
|
178
|
+
|
|
179
|
+
```tsx
|
|
180
|
+
import { withWebMcpTools } from 'webmcp-nexus-sdk';
|
|
181
|
+
|
|
182
|
+
class OrderPanel extends React.Component {
|
|
183
|
+
/** 搜索订单 @readonly */
|
|
184
|
+
searchOrders(params: { keyword: string }) { /* ... */ }
|
|
185
|
+
|
|
186
|
+
/** 导出订单 */
|
|
187
|
+
exportOrders = (params: { format: 'csv' | 'json' }) => { /* ... */ };
|
|
188
|
+
|
|
189
|
+
render() { return <div />; }
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
export default withWebMcpTools(OrderPanel);
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
> **约束**:`withWebMcpTools` 必须作为最内层 HOC 直接包裹 class 组件。
|
|
196
|
+
|
|
153
197
|
> 完整 API 文档与最佳实践参见 [仓库 README](https://github.com/alibaba/webmcp-nexus#readme)。
|
|
154
198
|
|
|
155
199
|
## 生态包
|
|
@@ -158,7 +202,7 @@ WebMCP Nexus 是一个 monorepo 工程化方案,下表是发布到 npm 的所
|
|
|
158
202
|
|
|
159
203
|
| 包 | 用途 |
|
|
160
204
|
| ------------------------------------------------------------------------------------------ | --------------------------------------------- |
|
|
161
|
-
| **`webmcp-nexus-sdk`** (本包) | 运行时 SDK,提供
|
|
205
|
+
| **`webmcp-nexus-sdk`** (本包) | 运行时 SDK,提供 3 个核心 API |
|
|
162
206
|
| [`webmcp-nexus-core`](https://www.npmjs.com/package/webmcp-nexus-core) | 构建时核心:TS 类型抽取 + JSON Schema 生成 |
|
|
163
207
|
| [`vite-plugin-webmcp-nexus`](https://www.npmjs.com/package/vite-plugin-webmcp-nexus) | Vite 构建插件 |
|
|
164
208
|
| [`webpack-plugin-webmcp-nexus`](https://www.npmjs.com/package/webpack-plugin-webmcp-nexus) | Webpack 构建插件 |
|
package/dist/index.cjs
CHANGED
|
@@ -21,12 +21,60 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
21
21
|
var index_exports = {};
|
|
22
22
|
__export(index_exports, {
|
|
23
23
|
registerGlobalTools: () => registerGlobalTools,
|
|
24
|
-
useWebMcpTools: () => useWebMcpTools
|
|
24
|
+
useWebMcpTools: () => useWebMcpTools,
|
|
25
|
+
withWebMcpTools: () => withWebMcpTools
|
|
25
26
|
});
|
|
26
27
|
module.exports = __toCommonJS(index_exports);
|
|
27
28
|
|
|
28
29
|
// src/registry.ts
|
|
29
30
|
var modelContextPatched = false;
|
|
31
|
+
function isCallToolResult(value) {
|
|
32
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return false;
|
|
33
|
+
const obj = value;
|
|
34
|
+
if (!Array.isArray(obj.content)) return false;
|
|
35
|
+
if (obj.content.length === 0) return true;
|
|
36
|
+
const first = obj.content[0];
|
|
37
|
+
return first !== null && typeof first === "object" && "type" in first && (first.type === "text" || first.type === "image" || first.type === "resource");
|
|
38
|
+
}
|
|
39
|
+
function normalizeToCallToolResult(value) {
|
|
40
|
+
if (isCallToolResult(value)) {
|
|
41
|
+
return value;
|
|
42
|
+
}
|
|
43
|
+
let text;
|
|
44
|
+
if (typeof value === "string") {
|
|
45
|
+
text = value;
|
|
46
|
+
} else {
|
|
47
|
+
try {
|
|
48
|
+
const serialized = JSON.stringify(value);
|
|
49
|
+
text = serialized === void 0 ? String(value) : serialized;
|
|
50
|
+
} catch {
|
|
51
|
+
text = String(value);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
return {
|
|
55
|
+
content: [{ type: "text", text }],
|
|
56
|
+
isError: false
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
function patchDispatchEventForCoalescing(mc) {
|
|
60
|
+
if (mc.__toolchangeCoalesced) return;
|
|
61
|
+
const originalDispatch = mc.dispatchEvent.bind(mc);
|
|
62
|
+
let pending = false;
|
|
63
|
+
mc.dispatchEvent = (event) => {
|
|
64
|
+
if (event.type === "toolchange") {
|
|
65
|
+
if (!pending) {
|
|
66
|
+
pending = true;
|
|
67
|
+
queueMicrotask(() => {
|
|
68
|
+
originalDispatch(new Event("toolchange"));
|
|
69
|
+
pending = false;
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
return true;
|
|
73
|
+
}
|
|
74
|
+
return originalDispatch(event);
|
|
75
|
+
};
|
|
76
|
+
mc.__toolchangeCoalesced = true;
|
|
77
|
+
}
|
|
30
78
|
function patchModelContextEventSupport() {
|
|
31
79
|
if (modelContextPatched) {
|
|
32
80
|
return;
|
|
@@ -39,6 +87,7 @@ function patchModelContextEventSupport() {
|
|
|
39
87
|
const hasListTools = typeof mc.listTools === "function";
|
|
40
88
|
const hasCallTool = typeof mc.callTool === "function";
|
|
41
89
|
if (hasEventTarget && hasListTools && hasCallTool) {
|
|
90
|
+
patchDispatchEventForCoalescing(mc);
|
|
42
91
|
modelContextPatched = true;
|
|
43
92
|
return;
|
|
44
93
|
}
|
|
@@ -104,9 +153,10 @@ function patchModelContextEventSupport() {
|
|
|
104
153
|
};
|
|
105
154
|
}
|
|
106
155
|
try {
|
|
107
|
-
|
|
156
|
+
const parsed = JSON.parse(result);
|
|
157
|
+
return normalizeToCallToolResult(parsed);
|
|
108
158
|
} catch {
|
|
109
|
-
throw new Error(`Tool returned invalid JSON: ${String(result).slice(0,
|
|
159
|
+
throw new Error(`Tool returned invalid JSON: ${String(result).slice(0, 500)}`);
|
|
110
160
|
}
|
|
111
161
|
};
|
|
112
162
|
}
|
|
@@ -143,6 +193,7 @@ function patchModelContextEventSupport() {
|
|
|
143
193
|
}
|
|
144
194
|
};
|
|
145
195
|
}
|
|
196
|
+
patchDispatchEventForCoalescing(mc);
|
|
146
197
|
modelContextPatched = true;
|
|
147
198
|
}
|
|
148
199
|
var registry = /* @__PURE__ */ new Map();
|
|
@@ -260,10 +311,9 @@ function unregisterScopedTool(name, owner) {
|
|
|
260
311
|
if (record.owners.size > 0) return false;
|
|
261
312
|
if (!record.controller.signal.aborted) {
|
|
262
313
|
record.controller.abort();
|
|
263
|
-
notifyToolsChanged({ immediate: true });
|
|
264
314
|
} else {
|
|
265
315
|
activeTools.delete(name);
|
|
266
|
-
notifyToolsChanged(
|
|
316
|
+
notifyToolsChanged();
|
|
267
317
|
}
|
|
268
318
|
return true;
|
|
269
319
|
} catch {
|
|
@@ -273,11 +323,13 @@ function unregisterScopedTool(name, owner) {
|
|
|
273
323
|
var pushToolsTimer = null;
|
|
274
324
|
function pushToolsToWidget() {
|
|
275
325
|
try {
|
|
276
|
-
const tools = getToolsForWidget();
|
|
277
326
|
if (typeof document === "undefined") return;
|
|
278
327
|
const iframes = document.querySelectorAll("iframe");
|
|
328
|
+
if (iframes.length === 0) return;
|
|
329
|
+
const tools = getToolsForWidget();
|
|
279
330
|
for (let i = 0; i < iframes.length; i++) {
|
|
280
331
|
const iframe = iframes[i];
|
|
332
|
+
if (iframe.hasAttribute("data-webmcp-relay")) continue;
|
|
281
333
|
try {
|
|
282
334
|
if (iframe.contentWindow) {
|
|
283
335
|
iframe.contentWindow.postMessage(
|
|
@@ -328,24 +380,13 @@ function schedulePushToolsToWidget() {
|
|
|
328
380
|
pushToolsToWidget();
|
|
329
381
|
}, 100);
|
|
330
382
|
}
|
|
331
|
-
function
|
|
332
|
-
if (pushToolsTimer) {
|
|
333
|
-
clearTimeout(pushToolsTimer);
|
|
334
|
-
pushToolsTimer = null;
|
|
335
|
-
}
|
|
336
|
-
pushToolsToWidget();
|
|
337
|
-
}
|
|
338
|
-
function notifyToolsChanged(options = {}) {
|
|
383
|
+
function notifyToolsChanged() {
|
|
339
384
|
if (typeof navigator === "undefined" || !("modelContext" in navigator)) return;
|
|
340
385
|
try {
|
|
341
|
-
navigator.modelContext.dispatchEvent(new Event("
|
|
386
|
+
navigator.modelContext.dispatchEvent(new Event("toolchange"));
|
|
342
387
|
} catch {
|
|
343
388
|
}
|
|
344
|
-
|
|
345
|
-
pushToolsToWidgetImmediately();
|
|
346
|
-
} else {
|
|
347
|
-
schedulePushToolsToWidget();
|
|
348
|
-
}
|
|
389
|
+
schedulePushToolsToWidget();
|
|
349
390
|
}
|
|
350
391
|
|
|
351
392
|
// src/polyfill.ts
|
|
@@ -483,9 +524,75 @@ function useWebMcpTools(...toolMaps) {
|
|
|
483
524
|
};
|
|
484
525
|
}, [toolKeys, localHmrVersion]);
|
|
485
526
|
}
|
|
527
|
+
|
|
528
|
+
// src/withWebMcpTools.tsx
|
|
529
|
+
var import_react2 = require("react");
|
|
530
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
531
|
+
var import_meta2 = {};
|
|
532
|
+
var LIFECYCLE_METHODS = /* @__PURE__ */ new Set([
|
|
533
|
+
"constructor",
|
|
534
|
+
"render",
|
|
535
|
+
"componentDidMount",
|
|
536
|
+
"componentDidUpdate",
|
|
537
|
+
"componentWillUnmount",
|
|
538
|
+
"shouldComponentUpdate",
|
|
539
|
+
"getSnapshotBeforeUpdate",
|
|
540
|
+
"componentDidCatch"
|
|
541
|
+
]);
|
|
542
|
+
function withWebMcpTools(WrappedComponent, methodNames) {
|
|
543
|
+
const displayName = WrappedComponent.displayName || WrappedComponent.name || "Component";
|
|
544
|
+
function WebMcpToolsWrapper(props) {
|
|
545
|
+
const instanceRef = (0, import_react2.useRef)(null);
|
|
546
|
+
const [hmrVersion2, setHmrVersion] = (0, import_react2.useState)(0);
|
|
547
|
+
(0, import_react2.useEffect)(() => {
|
|
548
|
+
const hot = typeof import_meta2 !== "undefined" ? import_meta2.hot : void 0;
|
|
549
|
+
if (hot && typeof hot.on === "function") {
|
|
550
|
+
const handler = () => setHmrVersion((v) => v + 1);
|
|
551
|
+
hot.on("vite:afterUpdate", handler);
|
|
552
|
+
return () => {
|
|
553
|
+
if (typeof hot.off === "function") {
|
|
554
|
+
hot.off("vite:afterUpdate", handler);
|
|
555
|
+
}
|
|
556
|
+
};
|
|
557
|
+
}
|
|
558
|
+
}, []);
|
|
559
|
+
const toolMap = (0, import_react2.useMemo)(() => {
|
|
560
|
+
const map = {};
|
|
561
|
+
const candidates = methodNames ? new Set(methodNames) : null;
|
|
562
|
+
const proto = WrappedComponent.prototype;
|
|
563
|
+
for (const name of Object.getOwnPropertyNames(proto)) {
|
|
564
|
+
if (LIFECYCLE_METHODS.has(name)) continue;
|
|
565
|
+
if (candidates && !candidates.has(name)) continue;
|
|
566
|
+
const method = proto[name];
|
|
567
|
+
if (typeof method !== "function") continue;
|
|
568
|
+
const schema = method.__webmcpSchema;
|
|
569
|
+
if (!schema) continue;
|
|
570
|
+
const wrapper = (input) => instanceRef.current?.[name](input);
|
|
571
|
+
wrapper.__webmcpSchema = schema;
|
|
572
|
+
map[name] = wrapper;
|
|
573
|
+
}
|
|
574
|
+
const fieldSchemas = WrappedComponent.__webmcpFieldSchemas;
|
|
575
|
+
if (fieldSchemas) {
|
|
576
|
+
for (const [name, schema] of Object.entries(fieldSchemas)) {
|
|
577
|
+
if (candidates && !candidates.has(name)) continue;
|
|
578
|
+
if (map[name]) continue;
|
|
579
|
+
const wrapper = (input) => instanceRef.current?.[name](input);
|
|
580
|
+
wrapper.__webmcpSchema = schema;
|
|
581
|
+
map[name] = wrapper;
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
return map;
|
|
585
|
+
}, [hmrVersion2]);
|
|
586
|
+
useWebMcpTools(toolMap);
|
|
587
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(WrappedComponent, { ref: instanceRef, ...props });
|
|
588
|
+
}
|
|
589
|
+
WebMcpToolsWrapper.displayName = `withWebMcpTools(${displayName})`;
|
|
590
|
+
return WebMcpToolsWrapper;
|
|
591
|
+
}
|
|
486
592
|
// Annotate the CommonJS export names for ESM import in node:
|
|
487
593
|
0 && (module.exports = {
|
|
488
594
|
registerGlobalTools,
|
|
489
|
-
useWebMcpTools
|
|
595
|
+
useWebMcpTools,
|
|
596
|
+
withWebMcpTools
|
|
490
597
|
});
|
|
491
598
|
//# sourceMappingURL=index.cjs.map
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/registry.ts","../src/polyfill.ts","../src/registerGlobalTools.ts","../src/useWebMcpTools.ts"],"sourcesContent":["// packages/webmcp-sdk/src/index.ts\nexport { registerGlobalTools } from './registerGlobalTools';\nexport { useWebMcpTools } from './useWebMcpTools';\nexport type { WebMcpToolFn, WebMcpToolSchema, WebMcpAnnotatedFn, WebMcpToolConfig } from './types';\n","// packages/webmcp-sdk/src/registry.ts\nimport type { WebMcpToolConfig } from './types';\n\nlet modelContextPatched = false;\n\n/**\n * Patches navigator.modelContext to ensure embed.js can discover tools.\n *\n * Handles three scenarios:\n * 1. Chrome 146+ native / @mcp-b/webmcp-polyfill: modelContext lacks\n * listTools/callTool/EventTarget; modelContextTesting shim provides\n * listTools/executeTool\n * → Bridge listTools/callTool from modelContextTesting, add EventTarget,\n * wrap registerTool/unregisterTool to fire toolchange\n * 2. Older polyfills (e.g. @mcp-b/global before EventTarget support):\n * modelContext has listTools/callTool but lacks EventTarget\n * → Add EventTarget, wrap registerTool/unregisterTool to fire toolchange\n * 3. Full MCP-B environment: modelContext has everything → skip\n */\nexport function patchModelContextEventSupport(): void {\n if (modelContextPatched) {\n return;\n }\n if (\n typeof navigator === 'undefined' ||\n !('modelContext' in navigator) ||\n !navigator.modelContext\n ) {\n return;\n }\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const mc = navigator.modelContext as any;\n const hasEventTarget = typeof mc.addEventListener === 'function';\n const hasListTools = typeof mc.listTools === 'function';\n const hasCallTool = typeof mc.callTool === 'function';\n\n // Scenario 3: fully featured — nothing to patch\n if (hasEventTarget && hasListTools && hasCallTool) {\n modelContextPatched = true;\n return;\n }\n\n // Add EventTarget support if missing (Chrome native & polyfill)\n if (!hasEventTarget) {\n const listeners = new Map<string, Set<EventListener>>();\n\n mc.addEventListener = (type: string, callback: EventListener) => {\n if (!listeners.has(type)) listeners.set(type, new Set());\n listeners.get(type)!.add(callback);\n };\n\n mc.removeEventListener = (type: string, callback: EventListener) => {\n listeners.get(type)?.delete(callback);\n };\n\n mc.dispatchEvent = (event: Event): boolean => {\n const set = listeners.get(event.type);\n if (set) {\n set.forEach(fn => {\n try {\n fn.call(mc, event);\n } catch {\n // Ignore listener errors\n }\n });\n }\n return true;\n };\n }\n\n // Bridge listTools/callTool from modelContextTesting if missing (Chrome native)\n // Chrome 146+ has these methods on modelContextTesting but not on modelContext.\n // embed.js requires them on modelContext for getExtendedModelContext() to succeed.\n if (!hasListTools || !hasCallTool) {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const testing = (navigator as any).modelContextTesting;\n if (testing) {\n if (\n typeof testing.listTools === 'function' &&\n !testing.__webmcpNexusListToolsPatched\n ) {\n const originalTestingListTools = testing.listTools.bind(testing);\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n testing.listTools = (...args: any[]) => {\n const nativeTools = originalTestingListTools(...args);\n return Array.isArray(nativeTools)\n ? nativeTools.filter((tool: any) => {\n if (!managedToolNames.has(tool.name)) return true;\n return activeTools.has(tool.name);\n })\n : nativeTools;\n };\n testing.__webmcpNexusListToolsPatched = true;\n }\n if (!hasListTools && typeof testing.listTools === 'function') {\n // Convert testing format (inputSchema as JSON string) to extended format (inputSchema as object)\n mc.listTools = () => {\n try {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n return testing.listTools().map((t: any) => ({\n name: t.name,\n description: t.description || '',\n inputSchema:\n typeof t.inputSchema === 'string' && t.inputSchema.length > 0\n ? JSON.parse(t.inputSchema)\n : { type: 'object', properties: {} },\n }));\n } catch {\n return [];\n }\n };\n }\n if (!hasCallTool && typeof testing.executeTool === 'function') {\n mc.callTool = async (params: { name: string; arguments?: Record<string, unknown> }) => {\n const result = await testing.executeTool(\n params.name,\n JSON.stringify(params.arguments || {}),\n );\n if (result === null) {\n return {\n isError: true,\n content: [{ type: 'text', text: 'Tool execution interrupted by navigation' }],\n };\n }\n try {\n return JSON.parse(result);\n } catch {\n throw new Error(`Tool returned invalid JSON: ${String(result).slice(0, 200)}`);\n }\n };\n }\n }\n }\n\n // Wrap registerTool/unregisterTool to auto-fire 'toolchange'\n // For Chrome native: registerTool fires toolchange on modelContextTesting (not modelContext),\n // but embed.js subscribes on modelContext after our patch — so we need to bridge the event.\n // For polyfill: BrowserMcpServer doesn't fire any events, so wrapping is required.\n if (typeof mc.registerTool === 'function') {\n const originalRegisterTool = mc.registerTool.bind(mc);\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n mc.registerTool = (tool: any, options?: any) => {\n let registrationError: unknown;\n try {\n originalRegisterTool(tool, options);\n } catch (error) {\n registrationError = error;\n }\n try {\n mc.dispatchEvent(new Event('toolchange'));\n } catch {\n // Ignore dispatch errors\n }\n if (registrationError) {\n throw registrationError;\n }\n };\n }\n\n if (typeof mc.unregisterTool === 'function') {\n const originalUnregisterTool = mc.unregisterTool.bind(mc);\n mc.unregisterTool = (name: string) => {\n try {\n originalUnregisterTool(name);\n } catch {\n // Swallow unregister errors\n }\n try {\n mc.dispatchEvent(new Event('toolchange'));\n } catch {\n // Ignore dispatch errors\n }\n };\n }\n\n modelContextPatched = true;\n}\n\ntype ToolScope = 'global' | 'route' | 'component';\n\ninterface ToolEntry {\n name: string;\n scope: ToolScope;\n scopeId: string;\n}\n\ninterface ToolOwner {\n scope: ToolScope;\n scopeId: string;\n}\n\ninterface ActiveToolRecord {\n name: string;\n config: WebMcpToolConfig;\n configs: Map<string, WebMcpToolConfig>;\n controller: AbortController;\n owners: Map<string, ToolOwner>;\n nativeRegistered: boolean;\n}\n\nconst registry = new Map<string, ToolEntry[]>();\nconst activeTools = new Map<string, ActiveToolRecord>();\nconst managedToolNames = new Set<string>();\nlet hasManagedTools = false;\n\nfunction getOwnerKey(owner: ToolOwner): string {\n return `${owner.scope}:${owner.scopeId}`;\n}\n\n/** 获取当前活跃工具名列表(仅用于测试和内部同步) */\nexport function getActiveToolNames(): string[] {\n return Array.from(activeTools.keys());\n}\n\n/** 获取当前活跃工具配置列表(供 pushToolsToWidget 在 listTools 缺失时兜底) */\nexport function getActiveToolConfigs(): Array<{\n name: string;\n description: string;\n inputSchema: object;\n}> {\n return Array.from(activeTools.values()).map(record => ({\n name: record.name,\n description: record.config.description,\n inputSchema: record.config.inputSchema || { type: 'object', properties: {} },\n }));\n}\n\n/**\n * 在内部注册表中记录工具的所有权信息。\n * 如果同名工具已被其他 scope 注册,输出警告但仍允许注册。\n */\nexport function registerEntry(name: string, scope: ToolScope, scopeId: string): void {\n const entries = registry.get(name) || [];\n if (entries.length > 0) {\n const isSameScopeAndId = entries.some(e => e.scope === scope && e.scopeId === scopeId);\n if (!isSameScopeAndId) {\n const existing = entries[0];\n console.warn(\n `[webmcp] Tool \"${name}\" is already registered by scope \"${existing.scope}:${existing.scopeId}\". ` +\n `Re-registering from \"${scope}:${scopeId}\". This may cause unexpected behavior.`,\n );\n }\n }\n entries.push({ name, scope, scopeId });\n registry.set(name, entries);\n}\n\n/**\n * 从内部注册表中移除指定 scope 的工具所有权记录。\n * @returns true 表示可以安全调用 unregisterTool(最后一个持有者已移除);\n * false 表示还有其他 scope 持有该工具,不应注销。\n */\nexport function unregisterEntry(name: string, scope: ToolScope, scopeId: string): boolean {\n const entries = registry.get(name);\n if (!entries) return false;\n const idx = entries.findIndex(e => e.scope === scope && e.scopeId === scopeId);\n if (idx === -1) return false;\n entries.splice(idx, 1);\n if (entries.length === 0) {\n registry.delete(name);\n return true; // 可以安全注销\n }\n return false; // 其他 scope 仍持有该工具\n}\n\n/**\n * 清空注册表(仅用于测试)\n */\nexport function clearRegistry(): void {\n registry.clear();\n activeTools.clear();\n managedToolNames.clear();\n hasManagedTools = false;\n modelContextPatched = false;\n if (pushToolsTimer) {\n clearTimeout(pushToolsTimer);\n pushToolsTimer = null;\n }\n}\n\n/**\n * 注册带 owner 的工具。\n * 同名工具只会原生注册一次;多个组件/作用域通过 owners 聚合生命周期。\n */\nexport function registerScopedTool(toolConfig: WebMcpToolConfig, owner: ToolOwner): void {\n try {\n const ownerKey = getOwnerKey(owner);\n hasManagedTools = true;\n managedToolNames.add(toolConfig.name);\n const existing = activeTools.get(toolConfig.name);\n if (existing) {\n existing.owners.set(ownerKey, owner);\n existing.configs.set(ownerKey, toolConfig);\n return;\n }\n\n const controller = new AbortController();\n const owners = new Map<string, ToolOwner>([[ownerKey, owner]]);\n const record: ActiveToolRecord = {\n name: toolConfig.name,\n config: toolConfig,\n configs: new Map([[ownerKey, toolConfig]]),\n controller,\n owners,\n nativeRegistered: false,\n };\n\n activeTools.set(toolConfig.name, record);\n\n const mc =\n typeof navigator !== 'undefined'\n ? // eslint-disable-next-line @typescript-eslint/no-explicit-any\n (navigator.modelContext as any)\n : undefined;\n\n controller.signal.addEventListener(\n 'abort',\n () => {\n try {\n // L2 fallback: 仅当 modelContext 不是带原生 signal 处理的实现时才调用 unregisterTool。\n // - Chrome 148+ 原生:移除了 unregisterTool,typeof === 'function' 为 false,自动跳过\n // - Chrome 146/147 原生:registerTool 不识别 signal,必须靠 unregisterTool 注销\n // - @mcp-b/webmcp-polyfill 2.x:registerTool 内部已 hook signal abort 自动删除 tool,\n // 再调 unregisterTool 既是双重操作,又会触发 polyfill 的 deprecation 警告。\n // 通过 __isWebMCPPolyfill 标记跳过这条路径。\n if (\n mc &&\n typeof mc.unregisterTool === 'function' &&\n !mc.__isWebMCPPolyfill\n ) {\n mc.unregisterTool(toolConfig.name);\n }\n } catch {\n // Ignore legacy fallback errors\n }\n try {\n activeTools.delete(toolConfig.name);\n notifyToolsChanged();\n } catch {\n // Ignore notification errors\n }\n },\n { once: true },\n );\n\n if (mc && typeof mc.registerTool === 'function') {\n const nativeToolConfig = {\n ...toolConfig,\n execute: async (input: Record<string, unknown>) => {\n const latestRecord = activeTools.get(toolConfig.name);\n return latestRecord?.config.execute(input);\n },\n };\n try {\n mc.registerTool(nativeToolConfig, { signal: controller.signal });\n record.nativeRegistered = true;\n } catch {\n try {\n if (typeof mc.unregisterTool === 'function') {\n mc.unregisterTool(toolConfig.name);\n mc.registerTool(nativeToolConfig, { signal: controller.signal });\n record.nativeRegistered = true;\n }\n } catch {\n // Keep the internal mirror so widget fallback still reflects SDK ownership.\n }\n }\n }\n } catch {\n // SDK public paths should never surface browser API errors.\n }\n}\n\n/**\n * 移除工具 owner。只有最后一个 owner 移除时才 abort 原生注册。\n * @returns true 表示工具列表发生了实际注销;false 表示仍有其他 owner。\n */\nexport function unregisterScopedTool(name: string, owner: ToolOwner): boolean {\n try {\n const record = activeTools.get(name);\n if (!record) {\n return false;\n }\n\n const ownerKey = getOwnerKey(owner);\n const removedConfig = record.configs.get(ownerKey);\n record.owners.delete(ownerKey);\n record.configs.delete(ownerKey);\n if (removedConfig && record.config === removedConfig) {\n const nextConfig = record.configs.values().next().value as WebMcpToolConfig | undefined;\n if (nextConfig) {\n record.config = nextConfig;\n }\n }\n if (record.owners.size > 0) return false;\n\n if (!record.controller.signal.aborted) {\n record.controller.abort();\n notifyToolsChanged({ immediate: true });\n } else {\n activeTools.delete(name);\n notifyToolsChanged({ immediate: true });\n }\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Debounce timer for pushing tools to widget iframe.\n */\nlet pushToolsTimer: ReturnType<typeof setTimeout> | null = null;\n\n/**\n * Directly push the current tool list to any widget iframes via postMessage.\n * This bypasses Chrome's registerToolsChangedCallback mechanism which doesn't\n * fire on unregisterTool, ensuring the relay always has the correct tool list.\n */\nfunction pushToolsToWidget(): void {\n try {\n const tools = getToolsForWidget();\n\n if (typeof document === 'undefined') return;\n\n // Find all iframes and send updated tool list\n // The widget iframe listens for 'webmcp.tools.changed' messages\n const iframes = document.querySelectorAll('iframe');\n for (let i = 0; i < iframes.length; i++) {\n const iframe = iframes[i];\n try {\n if (iframe.contentWindow) {\n iframe.contentWindow.postMessage(\n {\n type: 'webmcp.tools.changed',\n tools,\n },\n '*',\n );\n }\n } catch {\n // Cross-origin or other iframe access error, skip\n }\n }\n } catch {\n // Ignore errors\n }\n}\n\nfunction getToolsForWidget(): Array<{ name: string; description: string; inputSchema: object }> {\n if (hasManagedTools) {\n return getActiveToolConfigs();\n }\n try {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const mc = typeof navigator !== 'undefined' ? (navigator.modelContext as any) : undefined;\n if (mc && typeof mc.listTools === 'function') {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const tools = mc.listTools().map((tool: any) => ({\n name: tool.name,\n description: tool.description || '',\n inputSchema:\n typeof tool.inputSchema === 'string' && tool.inputSchema.length > 0\n ? safeJsonParse(tool.inputSchema)\n : tool.inputSchema || { type: 'object', properties: {} },\n }));\n return tools;\n }\n } catch {\n // Fall through to activeTools mirror\n }\n return getActiveToolConfigs();\n}\n\nfunction safeJsonParse(value: string): object {\n try {\n return JSON.parse(value);\n } catch {\n return { type: 'object', properties: {} };\n }\n}\n\n/**\n * Schedule a debounced push of tools to widget iframes.\n * Uses 100ms delay to coalesce rapid register/unregister cycles\n * (e.g., during SPA navigation when old component unmounts and new one mounts).\n */\nfunction schedulePushToolsToWidget(): void {\n if (pushToolsTimer) {\n clearTimeout(pushToolsTimer);\n }\n pushToolsTimer = setTimeout(() => {\n pushToolsTimer = null;\n pushToolsToWidget();\n }, 100);\n}\n\nfunction pushToolsToWidgetImmediately(): void {\n if (pushToolsTimer) {\n clearTimeout(pushToolsTimer);\n pushToolsTimer = null;\n }\n pushToolsToWidget();\n}\n\n/**\n * 通知 MCP relay 工具列表已变化。\n * 发射 W3C 规范事件名 (toolschanged),toolchange 由 registerTool/unregisterTool 包装器负责。\n */\nexport function notifyToolsChanged(options: { immediate?: boolean } = {}): void {\n if (typeof navigator === 'undefined' || !('modelContext' in navigator)) return;\n try {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n (navigator.modelContext as any).dispatchEvent(new Event('toolschanged'));\n } catch {\n // Ignore dispatch errors\n }\n\n // Direct push to widget iframe as fallback for Chrome native environment\n // where registerToolsChangedCallback doesn't fire on unregisterTool\n if (options.immediate) {\n pushToolsToWidgetImmediately();\n } else {\n schedulePushToolsToWidget();\n }\n}\n\n/**\n * 安全注册工具,处理重复名称的情况。\n * 如果 registerTool 因重复名称抛错,则先 unregister 再重试。\n */\nexport function safeRegisterTool(toolConfig: WebMcpToolConfig): void {\n registerScopedTool(toolConfig, { scope: 'global', scopeId: 'safe-register' });\n}\n","// packages/webmcp-sdk/src/polyfill.ts\nimport { initializeWebMCPPolyfill, cleanupWebMCPPolyfill } from '@mcp-b/webmcp-polyfill';\n\nlet attempted = false;\n\n/**\n * 惰性、幂等地确保 navigator.modelContext 存在。\n *\n * - 原生(Chrome 146+)/ 已被其他 polyfill 安装:直接返回,不动 navigator\n * - 非浏览器环境(SSR):直接返回\n * - 缺失 modelContext:调 initializeWebMCPPolyfill 装上严格 W3C 核心 + modelContextTesting shim\n *\n * 调用方应在判定 'modelContext' in navigator 之前调用本函数。整体包 try/catch,\n * polyfill 加载或初始化失败不向调用方传播异常——SDK 后续逻辑会按\"无 modelContext\"路径继续 no-op。\n */\nexport function ensureModelContextPolyfill(): void {\n if (attempted) return;\n attempted = true;\n if (typeof navigator === 'undefined') return;\n if ('modelContext' in navigator) return;\n try {\n initializeWebMCPPolyfill({ installTestingShim: true });\n } catch {\n // polyfill 初始化失败兜底\n }\n}\n\n/**\n * 仅供单元测试使用:重置模块级 attempted 标志并卸载 polyfill。\n * 生产代码不应调用本函数。\n */\nexport function __resetPolyfillStateForTest(): void {\n attempted = false;\n try {\n cleanupWebMCPPolyfill();\n } catch {\n // cleanup 失败兜底\n }\n}\n","// packages/webmcp-sdk/src/registerGlobalTools.ts\nimport {\n registerEntry,\n registerScopedTool,\n notifyToolsChanged,\n patchModelContextEventSupport,\n} from './registry';\nimport { ensureModelContextPolyfill } from './polyfill';\nimport type { WebMcpAnnotatedFn } from './types';\n\n/**\n * 全局注册 WebMCP 工具。应用启动时调用一次。\n * 支持可变参数,兼容 import * as module 批量导入。\n *\n * @param toolMaps - 一个或多个 Record<string, Function> 对象\n *\n * @example\n * import * as userApi from './api/user';\n * import * as productApi from './api/product';\n * registerGlobalTools(userApi, productApi);\n *\n * @example\n * registerGlobalTools({ getUser, searchUsers }, { searchProducts });\n */\n// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type\nexport function registerGlobalTools(...toolMaps: Record<string, Function>[]): void {\n ensureModelContextPolyfill();\n if (typeof navigator === 'undefined' || !('modelContext' in navigator)) {\n return;\n }\n\n try {\n patchModelContextEventSupport();\n\n let registeredCount = 0;\n\n for (const toolMap of toolMaps) {\n for (const [name, fn] of Object.entries(toolMap)) {\n // 跳过非函数值(如 TypeScript 类型导出在运行时可能不存在)\n if (typeof fn !== 'function') continue;\n\n // __webmcpSchema 由 Vite 插件在构建时注入\n const schema = (fn as WebMcpAnnotatedFn).__webmcpSchema;\n if (!schema) continue;\n\n registerEntry(name, 'global', 'app');\n registerScopedTool(\n {\n name,\n description: schema.description,\n inputSchema: schema.inputSchema,\n execute: async (input: unknown) => fn(input),\n annotations: { readOnlyHint: schema.readOnly ?? false },\n },\n { scope: 'global', scopeId: 'app' },\n );\n registeredCount++;\n }\n }\n\n if (registeredCount > 0) {\n notifyToolsChanged();\n }\n } catch {\n // 全局兜底:SDK 入口不向调用方传播浏览器 API 异常\n }\n}\n","// packages/webmcp-sdk/src/useWebMcpTools.ts\nimport { useEffect, useRef, useState } from 'react';\nimport {\n registerScopedTool,\n unregisterScopedTool,\n notifyToolsChanged,\n patchModelContextEventSupport,\n} from './registry';\nimport { ensureModelContextPolyfill } from './polyfill';\nimport type { WebMcpAnnotatedFn } from './types';\n\nlet scopeCounter = 0;\nfunction generateScopeId(): string {\n return `component-${++scopeCounter}`;\n}\n\n// HMR support: track a global version counter that increments on hot updates.\n// Function body updates are already handled via toolsRef.current indirect calls.\n// This counter ensures schema changes (__webmcpSchema) trigger re-registration.\nlet hmrVersion = 0;\nconst hmrHot = typeof import.meta !== 'undefined' ? import.meta.hot : undefined;\nif (hmrHot && typeof hmrHot.on === 'function') {\n hmrHot.on('vite:afterUpdate', () => {\n hmrVersion++;\n });\n}\n\n/**\n * React Hook:将函数注册为 WebMCP 工具,绑定组件生命周期。\n * mount 时注册,unmount 时自动注销。\n *\n * 使用 useRef 持有最新函数引用,避免闭包陷阱。\n * 使用 toolKeys(工具名集合的字符串)作为 useEffect 依赖,\n * 当工具集合变化时重新注册,函数体变化不触发重新注册。\n *\n * @param toolMaps - 一个或多个 Record<string, Function> 对象\n *\n * @example\n * // 组件级注册\n * useWebMcpTools({ searchInPanel, clearSearch });\n *\n * @example\n * // 路由级注册(配合 React Router 使用)\n * useWebMcpTools({ setUserFilter });\n */\nexport function useWebMcpTools(...toolMaps: Record<string, Function>[]): void {\n // 合并所有 toolMap 为一个对象\n const merged: Record<string, Function> = {};\n for (const toolMap of toolMaps) {\n for (const [name, fn] of Object.entries(toolMap)) {\n if (typeof fn === 'function') {\n merged[name] = fn;\n }\n }\n }\n\n // 用 ref 持有最新的函数引用,避免闭包陷阱\n const toolsRef = useRef(merged);\n toolsRef.current = merged;\n\n // 生成唯一 scopeId,确保同一组件实例的 scopeId 一致\n const scopeIdRef = useRef<string>('');\n if (!scopeIdRef.current) {\n scopeIdRef.current = generateScopeId();\n }\n\n // 工具名集合作为依赖 — 工具集合变化时重新注册\n const toolKeys = Object.keys(merged).sort().join(',');\n\n // DEV: listen for HMR updates to force re-registration when schema changes\n const [localHmrVersion, setLocalHmrVersion] = useState(hmrVersion);\n useEffect(() => {\n const hot = typeof import.meta !== 'undefined' ? import.meta.hot : undefined;\n if (hot && typeof hot.on === 'function') {\n const handler = () => setLocalHmrVersion(v => v + 1);\n hot.on('vite:afterUpdate', handler);\n return () => {\n if (typeof hot.off === 'function') {\n hot.off('vite:afterUpdate', handler);\n }\n };\n }\n }, []);\n\n useEffect(() => {\n ensureModelContextPolyfill();\n if (typeof navigator === 'undefined' || !('modelContext' in navigator)) {\n return;\n }\n\n patchModelContextEventSupport();\n\n const registeredNames: string[] = [];\n\n for (const [name, fn] of Object.entries(toolsRef.current)) {\n const schema = (fn as WebMcpAnnotatedFn).__webmcpSchema;\n if (!schema) continue;\n\n registerScopedTool(\n {\n name,\n description: schema.description,\n inputSchema: schema.inputSchema,\n // 通过 ref 间接调用,保证始终执行最新版本的函数\n execute: async (input: unknown) => toolsRef.current[name](input),\n annotations: { readOnlyHint: schema.readOnly ?? false },\n },\n { scope: 'component', scopeId: scopeIdRef.current },\n );\n\n registeredNames.push(name);\n }\n\n if (registeredNames.length > 0) {\n notifyToolsChanged();\n }\n\n // 组件卸载时释放 owner;最后一个 owner 才会触发原生 abort。\n return () => {\n let unregisteredAny = false;\n for (const name of registeredNames) {\n const shouldUnregister = unregisterScopedTool(name, {\n scope: 'component',\n scopeId: scopeIdRef.current,\n });\n if (shouldUnregister) {\n unregisteredAny = true;\n }\n }\n if (unregisteredAny) {\n notifyToolsChanged();\n }\n };\n }, [toolKeys, localHmrVersion]); // 工具集合变化或 HMR 更新时重新注册\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/registry.ts","../src/polyfill.ts","../src/registerGlobalTools.ts","../src/useWebMcpTools.ts","../src/withWebMcpTools.tsx"],"sourcesContent":["// packages/webmcp-sdk/src/index.ts\nexport { registerGlobalTools } from './registerGlobalTools';\nexport { useWebMcpTools } from './useWebMcpTools';\nexport { withWebMcpTools } from './withWebMcpTools';\nexport type { WebMcpToolFn, WebMcpToolSchema, WebMcpAnnotatedFn, WebMcpToolConfig } from './types';\n","// packages/webmcp-sdk/src/registry.ts\nimport type { WebMcpToolConfig } from './types';\n\nlet modelContextPatched = false;\n\n/**\n * 严格判断值是否为合法的 MCP CallToolResult。\n * 检查 content 数组存在且首元素含合法 type 字段,防止业务对象误判。\n */\nexport function isCallToolResult(value: unknown): boolean {\n if (!value || typeof value !== 'object' || Array.isArray(value)) return false;\n const obj = value as Record<string, unknown>;\n if (!Array.isArray(obj.content)) return false;\n if (obj.content.length === 0) return true;\n const first = obj.content[0];\n return (\n first !== null &&\n typeof first === 'object' &&\n 'type' in first &&\n ((first as Record<string, unknown>).type === 'text' ||\n (first as Record<string, unknown>).type === 'image' ||\n (first as Record<string, unknown>).type === 'resource')\n );\n}\n\n/**\n * 将工具原始返回值规范化为 MCP CallToolResult 格式。\n * 若已是合法 CallToolResult 则直接返回,否则将完整内容包装为 text content。\n * 注意:不对结果做任何截断,完整保留工具返回的全部数据。\n */\nexport function normalizeToCallToolResult(value: unknown): Record<string, unknown> {\n if (isCallToolResult(value)) {\n return value as Record<string, unknown>;\n }\n let text: string;\n if (typeof value === 'string') {\n text = value;\n } else {\n try {\n const serialized = JSON.stringify(value);\n text = serialized === undefined ? String(value) : serialized;\n } catch {\n text = String(value);\n }\n }\n return {\n content: [{ type: 'text', text }],\n isError: false,\n };\n}\n\n/**\n * 在 dispatchEvent 层面合并 toolchange 事件。\n * 无论来源(SDK 包装器、polyfill 内部、第三方代码),同一微任务内只触发 1 次。\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nfunction patchDispatchEventForCoalescing(mc: any): void {\n if (mc.__toolchangeCoalesced) return;\n const originalDispatch: (event: Event) => boolean =\n mc.dispatchEvent.bind(mc);\n let pending = false;\n mc.dispatchEvent = (event: Event): boolean => {\n if (event.type === 'toolchange') {\n if (!pending) {\n pending = true;\n queueMicrotask(() => {\n originalDispatch(new Event('toolchange'));\n pending = false;\n });\n }\n return true;\n }\n return originalDispatch(event);\n };\n mc.__toolchangeCoalesced = true;\n}\n\n/**\n * Patches navigator.modelContext to ensure embed.js can discover tools.\n *\n * Handles three scenarios:\n * 1. Chrome 146+ native / @mcp-b/webmcp-polyfill: modelContext lacks\n * listTools/callTool/EventTarget; modelContextTesting shim provides\n * listTools/executeTool\n * → Bridge listTools/callTool from modelContextTesting, add EventTarget,\n * wrap registerTool/unregisterTool to fire toolchange\n * 2. Older polyfills (e.g. @mcp-b/global before EventTarget support):\n * modelContext has listTools/callTool but lacks EventTarget\n * → Add EventTarget, wrap registerTool/unregisterTool to fire toolchange\n * 3. Full MCP-B environment: modelContext has everything → skip\n */\nexport function patchModelContextEventSupport(): void {\n if (modelContextPatched) {\n return;\n }\n if (\n typeof navigator === 'undefined' ||\n !('modelContext' in navigator) ||\n !navigator.modelContext\n ) {\n return;\n }\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const mc = navigator.modelContext as any;\n const hasEventTarget = typeof mc.addEventListener === 'function';\n const hasListTools = typeof mc.listTools === 'function';\n const hasCallTool = typeof mc.callTool === 'function';\n\n // Scenario 3: fully featured — only apply toolchange coalescing\n if (hasEventTarget && hasListTools && hasCallTool) {\n patchDispatchEventForCoalescing(mc);\n modelContextPatched = true;\n return;\n }\n\n // Add EventTarget support if missing (Chrome native & polyfill)\n if (!hasEventTarget) {\n const listeners = new Map<string, Set<EventListener>>();\n\n mc.addEventListener = (type: string, callback: EventListener) => {\n if (!listeners.has(type)) listeners.set(type, new Set());\n listeners.get(type)!.add(callback);\n };\n\n mc.removeEventListener = (type: string, callback: EventListener) => {\n listeners.get(type)?.delete(callback);\n };\n\n mc.dispatchEvent = (event: Event): boolean => {\n const set = listeners.get(event.type);\n if (set) {\n set.forEach(fn => {\n try {\n fn.call(mc, event);\n } catch {\n // Ignore listener errors\n }\n });\n }\n return true;\n };\n }\n\n // Bridge listTools/callTool from modelContextTesting if missing (Chrome native)\n // Chrome 146+ has these methods on modelContextTesting but not on modelContext.\n // embed.js requires them on modelContext for getExtendedModelContext() to succeed.\n if (!hasListTools || !hasCallTool) {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const testing = (navigator as any).modelContextTesting;\n if (testing) {\n if (\n typeof testing.listTools === 'function' &&\n !testing.__webmcpNexusListToolsPatched\n ) {\n const originalTestingListTools = testing.listTools.bind(testing);\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n testing.listTools = (...args: any[]) => {\n const nativeTools = originalTestingListTools(...args);\n return Array.isArray(nativeTools)\n ? nativeTools.filter((tool: any) => {\n if (!managedToolNames.has(tool.name)) return true;\n return activeTools.has(tool.name);\n })\n : nativeTools;\n };\n testing.__webmcpNexusListToolsPatched = true;\n }\n if (!hasListTools && typeof testing.listTools === 'function') {\n // Convert testing format (inputSchema as JSON string) to extended format (inputSchema as object)\n mc.listTools = () => {\n try {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n return testing.listTools().map((t: any) => ({\n name: t.name,\n description: t.description || '',\n inputSchema:\n typeof t.inputSchema === 'string' && t.inputSchema.length > 0\n ? JSON.parse(t.inputSchema)\n : { type: 'object', properties: {} },\n }));\n } catch {\n return [];\n }\n };\n }\n if (!hasCallTool && typeof testing.executeTool === 'function') {\n mc.callTool = async (params: { name: string; arguments?: Record<string, unknown> }) => {\n const result = await testing.executeTool(\n params.name,\n JSON.stringify(params.arguments || {}),\n );\n if (result === null) {\n return {\n isError: true,\n content: [{ type: 'text', text: 'Tool execution interrupted by navigation' }],\n };\n }\n try {\n const parsed = JSON.parse(result);\n return normalizeToCallToolResult(parsed);\n } catch {\n throw new Error(`Tool returned invalid JSON: ${String(result).slice(0, 500)}`);\n }\n };\n }\n }\n }\n\n // Wrap registerTool/unregisterTool to auto-fire 'toolchange'\n // For Chrome native: registerTool fires toolchange on modelContextTesting (not modelContext),\n // but embed.js subscribes on modelContext after our patch — so we need to bridge the event.\n // For polyfill: BrowserMcpServer doesn't fire any events, so wrapping is required.\n if (typeof mc.registerTool === 'function') {\n const originalRegisterTool = mc.registerTool.bind(mc);\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n mc.registerTool = (tool: any, options?: any) => {\n let registrationError: unknown;\n try {\n originalRegisterTool(tool, options);\n } catch (error) {\n registrationError = error;\n }\n try {\n mc.dispatchEvent(new Event('toolchange'));\n } catch {\n // Ignore dispatch errors\n }\n if (registrationError) {\n throw registrationError;\n }\n };\n }\n\n if (typeof mc.unregisterTool === 'function') {\n const originalUnregisterTool = mc.unregisterTool.bind(mc);\n mc.unregisterTool = (name: string) => {\n try {\n originalUnregisterTool(name);\n } catch {\n // Swallow unregister errors\n }\n try {\n mc.dispatchEvent(new Event('toolchange'));\n } catch {\n // Ignore dispatch errors\n }\n };\n }\n\n patchDispatchEventForCoalescing(mc);\n modelContextPatched = true;\n}\n\ntype ToolScope = 'global' | 'route' | 'component';\n\ninterface ToolEntry {\n name: string;\n scope: ToolScope;\n scopeId: string;\n}\n\ninterface ToolOwner {\n scope: ToolScope;\n scopeId: string;\n}\n\ninterface ActiveToolRecord {\n name: string;\n config: WebMcpToolConfig;\n configs: Map<string, WebMcpToolConfig>;\n controller: AbortController;\n owners: Map<string, ToolOwner>;\n nativeRegistered: boolean;\n}\n\nconst registry = new Map<string, ToolEntry[]>();\nconst activeTools = new Map<string, ActiveToolRecord>();\nconst managedToolNames = new Set<string>();\nlet hasManagedTools = false;\n\nfunction getOwnerKey(owner: ToolOwner): string {\n return `${owner.scope}:${owner.scopeId}`;\n}\n\n/** 获取当前活跃工具名列表(仅用于测试和内部同步) */\nexport function getActiveToolNames(): string[] {\n return Array.from(activeTools.keys());\n}\n\n/** 获取当前活跃工具配置列表(供 pushToolsToWidget 在 listTools 缺失时兜底) */\nexport function getActiveToolConfigs(): Array<{\n name: string;\n description: string;\n inputSchema: object;\n}> {\n return Array.from(activeTools.values()).map(record => ({\n name: record.name,\n description: record.config.description,\n inputSchema: record.config.inputSchema || { type: 'object', properties: {} },\n }));\n}\n\n/**\n * 在内部注册表中记录工具的所有权信息。\n * 如果同名工具已被其他 scope 注册,输出警告但仍允许注册。\n */\nexport function registerEntry(name: string, scope: ToolScope, scopeId: string): void {\n const entries = registry.get(name) || [];\n if (entries.length > 0) {\n const isSameScopeAndId = entries.some(e => e.scope === scope && e.scopeId === scopeId);\n if (!isSameScopeAndId) {\n const existing = entries[0];\n console.warn(\n `[webmcp] Tool \"${name}\" is already registered by scope \"${existing.scope}:${existing.scopeId}\". ` +\n `Re-registering from \"${scope}:${scopeId}\". This may cause unexpected behavior.`,\n );\n }\n }\n entries.push({ name, scope, scopeId });\n registry.set(name, entries);\n}\n\n/**\n * 从内部注册表中移除指定 scope 的工具所有权记录。\n * @returns true 表示可以安全调用 unregisterTool(最后一个持有者已移除);\n * false 表示还有其他 scope 持有该工具,不应注销。\n */\nexport function unregisterEntry(name: string, scope: ToolScope, scopeId: string): boolean {\n const entries = registry.get(name);\n if (!entries) return false;\n const idx = entries.findIndex(e => e.scope === scope && e.scopeId === scopeId);\n if (idx === -1) return false;\n entries.splice(idx, 1);\n if (entries.length === 0) {\n registry.delete(name);\n return true; // 可以安全注销\n }\n return false; // 其他 scope 仍持有该工具\n}\n\n/**\n * 清空注册表(仅用于测试)\n */\nexport function clearRegistry(): void {\n registry.clear();\n activeTools.clear();\n managedToolNames.clear();\n hasManagedTools = false;\n modelContextPatched = false;\n if (pushToolsTimer) {\n clearTimeout(pushToolsTimer);\n pushToolsTimer = null;\n }\n}\n\n/**\n * 注册带 owner 的工具。\n * 同名工具只会原生注册一次;多个组件/作用域通过 owners 聚合生命周期。\n */\nexport function registerScopedTool(toolConfig: WebMcpToolConfig, owner: ToolOwner): void {\n try {\n const ownerKey = getOwnerKey(owner);\n hasManagedTools = true;\n managedToolNames.add(toolConfig.name);\n const existing = activeTools.get(toolConfig.name);\n if (existing) {\n existing.owners.set(ownerKey, owner);\n existing.configs.set(ownerKey, toolConfig);\n return;\n }\n\n const controller = new AbortController();\n const owners = new Map<string, ToolOwner>([[ownerKey, owner]]);\n const record: ActiveToolRecord = {\n name: toolConfig.name,\n config: toolConfig,\n configs: new Map([[ownerKey, toolConfig]]),\n controller,\n owners,\n nativeRegistered: false,\n };\n\n activeTools.set(toolConfig.name, record);\n\n const mc =\n typeof navigator !== 'undefined'\n ? // eslint-disable-next-line @typescript-eslint/no-explicit-any\n (navigator.modelContext as any)\n : undefined;\n\n controller.signal.addEventListener(\n 'abort',\n () => {\n try {\n // L2 fallback: 仅当 modelContext 不是带原生 signal 处理的实现时才调用 unregisterTool。\n // - Chrome 148+ 原生:移除了 unregisterTool,typeof === 'function' 为 false,自动跳过\n // - Chrome 146/147 原生:registerTool 不识别 signal,必须靠 unregisterTool 注销\n // - @mcp-b/webmcp-polyfill 2.x:registerTool 内部已 hook signal abort 自动删除 tool,\n // 再调 unregisterTool 既是双重操作,又会触发 polyfill 的 deprecation 警告。\n // 通过 __isWebMCPPolyfill 标记跳过这条路径。\n if (\n mc &&\n typeof mc.unregisterTool === 'function' &&\n !mc.__isWebMCPPolyfill\n ) {\n mc.unregisterTool(toolConfig.name);\n }\n } catch {\n // Ignore legacy fallback errors\n }\n try {\n activeTools.delete(toolConfig.name);\n notifyToolsChanged();\n } catch {\n // Ignore notification errors\n }\n },\n { once: true },\n );\n\n if (mc && typeof mc.registerTool === 'function') {\n const nativeToolConfig = {\n ...toolConfig,\n execute: async (input: Record<string, unknown>) => {\n const latestRecord = activeTools.get(toolConfig.name);\n return latestRecord?.config.execute(input);\n },\n };\n try {\n mc.registerTool(nativeToolConfig, { signal: controller.signal });\n record.nativeRegistered = true;\n } catch {\n try {\n if (typeof mc.unregisterTool === 'function') {\n mc.unregisterTool(toolConfig.name);\n mc.registerTool(nativeToolConfig, { signal: controller.signal });\n record.nativeRegistered = true;\n }\n } catch {\n // Keep the internal mirror so widget fallback still reflects SDK ownership.\n }\n }\n }\n } catch {\n // SDK public paths should never surface browser API errors.\n }\n}\n\n/**\n * 移除工具 owner。只有最后一个 owner 移除时才 abort 原生注册。\n * @returns true 表示工具列表发生了实际注销;false 表示仍有其他 owner。\n */\nexport function unregisterScopedTool(name: string, owner: ToolOwner): boolean {\n try {\n const record = activeTools.get(name);\n if (!record) {\n return false;\n }\n\n const ownerKey = getOwnerKey(owner);\n const removedConfig = record.configs.get(ownerKey);\n record.owners.delete(ownerKey);\n record.configs.delete(ownerKey);\n if (removedConfig && record.config === removedConfig) {\n const nextConfig = record.configs.values().next().value as WebMcpToolConfig | undefined;\n if (nextConfig) {\n record.config = nextConfig;\n }\n }\n if (record.owners.size > 0) return false;\n\n if (!record.controller.signal.aborted) {\n record.controller.abort();\n } else {\n activeTools.delete(name);\n notifyToolsChanged();\n }\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Debounce timer for pushing tools to widget iframe.\n */\nlet pushToolsTimer: ReturnType<typeof setTimeout> | null = null;\n\n/**\n * Directly push the current tool list to any widget iframes via postMessage.\n * This bypasses Chrome's registerToolsChangedCallback mechanism which doesn't\n * fire on unregisterTool, ensuring the relay always has the correct tool list.\n */\nfunction pushToolsToWidget(): void {\n try {\n if (typeof document === 'undefined') return;\n\n const iframes = document.querySelectorAll('iframe');\n if (iframes.length === 0) return;\n\n const tools = getToolsForWidget();\n\n for (let i = 0; i < iframes.length; i++) {\n const iframe = iframes[i];\n // embed.js(@mcp-b/webmcp-local-relay)已通过 toolchange 监听自行推送\n // relay iframe,跳过以避免重复 webmcp.tools.changed 消息\n if (iframe.hasAttribute('data-webmcp-relay')) continue;\n try {\n if (iframe.contentWindow) {\n iframe.contentWindow.postMessage(\n {\n type: 'webmcp.tools.changed',\n tools,\n },\n '*',\n );\n }\n } catch {\n // Cross-origin or other iframe access error, skip\n }\n }\n } catch {\n // Ignore errors\n }\n}\n\nfunction getToolsForWidget(): Array<{ name: string; description: string; inputSchema: object }> {\n if (hasManagedTools) {\n return getActiveToolConfigs();\n }\n try {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const mc = typeof navigator !== 'undefined' ? (navigator.modelContext as any) : undefined;\n if (mc && typeof mc.listTools === 'function') {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const tools = mc.listTools().map((tool: any) => ({\n name: tool.name,\n description: tool.description || '',\n inputSchema:\n typeof tool.inputSchema === 'string' && tool.inputSchema.length > 0\n ? safeJsonParse(tool.inputSchema)\n : tool.inputSchema || { type: 'object', properties: {} },\n }));\n return tools;\n }\n } catch {\n // Fall through to activeTools mirror\n }\n return getActiveToolConfigs();\n}\n\nfunction safeJsonParse(value: string): object {\n try {\n return JSON.parse(value);\n } catch {\n return { type: 'object', properties: {} };\n }\n}\n\n/**\n * Schedule a debounced push of tools to widget iframes.\n * Uses 100ms delay to coalesce rapid register/unregister cycles\n * (e.g., during SPA navigation when old component unmounts and new one mounts).\n */\nfunction schedulePushToolsToWidget(): void {\n if (pushToolsTimer) {\n clearTimeout(pushToolsTimer);\n }\n pushToolsTimer = setTimeout(() => {\n pushToolsTimer = null;\n pushToolsToWidget();\n }, 100);\n}\n\n/**\n * 通知工具列表已变化,发射标准 toolchange 事件并推送至 widget iframe。\n */\nexport function notifyToolsChanged(): void {\n if (typeof navigator === 'undefined' || !('modelContext' in navigator)) return;\n try {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n (navigator.modelContext as any).dispatchEvent(new Event('toolchange'));\n } catch {\n // Ignore dispatch errors\n }\n\n schedulePushToolsToWidget();\n}\n\n/**\n * 安全注册工具,处理重复名称的情况。\n * 如果 registerTool 因重复名称抛错,则先 unregister 再重试。\n */\nexport function safeRegisterTool(toolConfig: WebMcpToolConfig): void {\n registerScopedTool(toolConfig, { scope: 'global', scopeId: 'safe-register' });\n}\n","// packages/webmcp-sdk/src/polyfill.ts\nimport { initializeWebMCPPolyfill, cleanupWebMCPPolyfill } from '@mcp-b/webmcp-polyfill';\n\nlet attempted = false;\n\n/**\n * 惰性、幂等地确保 navigator.modelContext 存在。\n *\n * - 原生(Chrome 146+)/ 已被其他 polyfill 安装:直接返回,不动 navigator\n * - 非浏览器环境(SSR):直接返回\n * - 缺失 modelContext:调 initializeWebMCPPolyfill 装上严格 W3C 核心 + modelContextTesting shim\n *\n * 调用方应在判定 'modelContext' in navigator 之前调用本函数。整体包 try/catch,\n * polyfill 加载或初始化失败不向调用方传播异常——SDK 后续逻辑会按\"无 modelContext\"路径继续 no-op。\n */\nexport function ensureModelContextPolyfill(): void {\n if (attempted) return;\n attempted = true;\n if (typeof navigator === 'undefined') return;\n if ('modelContext' in navigator) return;\n try {\n initializeWebMCPPolyfill({ installTestingShim: true });\n } catch {\n // polyfill 初始化失败兜底\n }\n}\n\n/**\n * 仅供单元测试使用:重置模块级 attempted 标志并卸载 polyfill。\n * 生产代码不应调用本函数。\n */\nexport function __resetPolyfillStateForTest(): void {\n attempted = false;\n try {\n cleanupWebMCPPolyfill();\n } catch {\n // cleanup 失败兜底\n }\n}\n","// packages/webmcp-sdk/src/registerGlobalTools.ts\nimport {\n registerEntry,\n registerScopedTool,\n notifyToolsChanged,\n patchModelContextEventSupport,\n} from './registry';\nimport { ensureModelContextPolyfill } from './polyfill';\nimport type { WebMcpAnnotatedFn } from './types';\n\n/**\n * 全局注册 WebMCP 工具。应用启动时调用一次。\n * 支持可变参数,兼容 import * as module 批量导入。\n *\n * @param toolMaps - 一个或多个 Record<string, Function> 对象\n *\n * @example\n * import * as userApi from './api/user';\n * import * as productApi from './api/product';\n * registerGlobalTools(userApi, productApi);\n *\n * @example\n * registerGlobalTools({ getUser, searchUsers }, { searchProducts });\n */\n// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type\nexport function registerGlobalTools(...toolMaps: Record<string, Function>[]): void {\n ensureModelContextPolyfill();\n if (typeof navigator === 'undefined' || !('modelContext' in navigator)) {\n return;\n }\n\n try {\n patchModelContextEventSupport();\n\n let registeredCount = 0;\n\n for (const toolMap of toolMaps) {\n for (const [name, fn] of Object.entries(toolMap)) {\n // 跳过非函数值(如 TypeScript 类型导出在运行时可能不存在)\n if (typeof fn !== 'function') continue;\n\n // __webmcpSchema 由 Vite 插件在构建时注入\n const schema = (fn as WebMcpAnnotatedFn).__webmcpSchema;\n if (!schema) continue;\n\n registerEntry(name, 'global', 'app');\n registerScopedTool(\n {\n name,\n description: schema.description,\n inputSchema: schema.inputSchema,\n execute: async (input: unknown) => fn(input),\n annotations: { readOnlyHint: schema.readOnly ?? false },\n },\n { scope: 'global', scopeId: 'app' },\n );\n registeredCount++;\n }\n }\n\n if (registeredCount > 0) {\n notifyToolsChanged();\n }\n } catch {\n // 全局兜底:SDK 入口不向调用方传播浏览器 API 异常\n }\n}\n","// packages/webmcp-sdk/src/useWebMcpTools.ts\nimport { useEffect, useRef, useState } from 'react';\nimport {\n registerScopedTool,\n unregisterScopedTool,\n notifyToolsChanged,\n patchModelContextEventSupport,\n} from './registry';\nimport { ensureModelContextPolyfill } from './polyfill';\nimport type { WebMcpAnnotatedFn } from './types';\n\nlet scopeCounter = 0;\nfunction generateScopeId(): string {\n return `component-${++scopeCounter}`;\n}\n\n// HMR support: track a global version counter that increments on hot updates.\n// Function body updates are already handled via toolsRef.current indirect calls.\n// This counter ensures schema changes (__webmcpSchema) trigger re-registration.\nlet hmrVersion = 0;\nconst hmrHot = typeof import.meta !== 'undefined' ? import.meta.hot : undefined;\nif (hmrHot && typeof hmrHot.on === 'function') {\n hmrHot.on('vite:afterUpdate', () => {\n hmrVersion++;\n });\n}\n\n/**\n * React Hook:将函数注册为 WebMCP 工具,绑定组件生命周期。\n * mount 时注册,unmount 时自动注销。\n *\n * 使用 useRef 持有最新函数引用,避免闭包陷阱。\n * 使用 toolKeys(工具名集合的字符串)作为 useEffect 依赖,\n * 当工具集合变化时重新注册,函数体变化不触发重新注册。\n *\n * @param toolMaps - 一个或多个 Record<string, Function> 对象\n *\n * @example\n * // 组件级注册\n * useWebMcpTools({ searchInPanel, clearSearch });\n *\n * @example\n * // 路由级注册(配合 React Router 使用)\n * useWebMcpTools({ setUserFilter });\n */\nexport function useWebMcpTools(...toolMaps: Record<string, Function>[]): void {\n // 合并所有 toolMap 为一个对象\n const merged: Record<string, Function> = {};\n for (const toolMap of toolMaps) {\n for (const [name, fn] of Object.entries(toolMap)) {\n if (typeof fn === 'function') {\n merged[name] = fn;\n }\n }\n }\n\n // 用 ref 持有最新的函数引用,避免闭包陷阱\n const toolsRef = useRef(merged);\n toolsRef.current = merged;\n\n // 生成唯一 scopeId,确保同一组件实例的 scopeId 一致\n const scopeIdRef = useRef<string>('');\n if (!scopeIdRef.current) {\n scopeIdRef.current = generateScopeId();\n }\n\n // 工具名集合作为依赖 — 工具集合变化时重新注册\n const toolKeys = Object.keys(merged).sort().join(',');\n\n // DEV: listen for HMR updates to force re-registration when schema changes\n const [localHmrVersion, setLocalHmrVersion] = useState(hmrVersion);\n useEffect(() => {\n const hot = typeof import.meta !== 'undefined' ? import.meta.hot : undefined;\n if (hot && typeof hot.on === 'function') {\n const handler = () => setLocalHmrVersion(v => v + 1);\n hot.on('vite:afterUpdate', handler);\n return () => {\n if (typeof hot.off === 'function') {\n hot.off('vite:afterUpdate', handler);\n }\n };\n }\n }, []);\n\n useEffect(() => {\n ensureModelContextPolyfill();\n if (typeof navigator === 'undefined' || !('modelContext' in navigator)) {\n return;\n }\n\n patchModelContextEventSupport();\n\n const registeredNames: string[] = [];\n\n for (const [name, fn] of Object.entries(toolsRef.current)) {\n const schema = (fn as WebMcpAnnotatedFn).__webmcpSchema;\n if (!schema) continue;\n\n registerScopedTool(\n {\n name,\n description: schema.description,\n inputSchema: schema.inputSchema,\n // 通过 ref 间接调用,保证始终执行最新版本的函数\n execute: async (input: unknown) => toolsRef.current[name](input),\n annotations: { readOnlyHint: schema.readOnly ?? false },\n },\n { scope: 'component', scopeId: scopeIdRef.current },\n );\n\n registeredNames.push(name);\n }\n\n if (registeredNames.length > 0) {\n notifyToolsChanged();\n }\n\n // 组件卸载时释放 owner;最后一个 owner 才会触发原生 abort。\n return () => {\n let unregisteredAny = false;\n for (const name of registeredNames) {\n const shouldUnregister = unregisterScopedTool(name, {\n scope: 'component',\n scopeId: scopeIdRef.current,\n });\n if (shouldUnregister) {\n unregisteredAny = true;\n }\n }\n if (unregisteredAny) {\n notifyToolsChanged();\n }\n };\n }, [toolKeys, localHmrVersion]); // 工具集合变化或 HMR 更新时重新注册\n}\n","// packages/webmcp-sdk/src/withWebMcpTools.tsx\nimport React, { useRef, useMemo, useState, useEffect } from 'react';\nimport { useWebMcpTools } from './useWebMcpTools';\nimport type { WebMcpAnnotatedFn } from './types';\n\n/** React 生命周期方法黑名单 */\nconst LIFECYCLE_METHODS = new Set([\n 'constructor',\n 'render',\n 'componentDidMount',\n 'componentDidUpdate',\n 'componentWillUnmount',\n 'shouldComponentUpdate',\n 'getSnapshotBeforeUpdate',\n 'componentDidCatch',\n]);\n\n/**\n * 高阶组件:将 class 组件的方法注册为 WebMCP 工具。\n * mount 时注册,unmount 时自动注销。\n *\n * 支持两种方法形式:\n * - 原型方法:`methodName(params: T) { ... }` — schema 挂在 prototype 上\n * - Class field 箭头函数:`methodName = (params: T) => { ... }` — schema 挂在静态属性上\n *\n * @param WrappedComponent - React class 组件\n * @param methodNames - 可选,显式指定要注册的方法名列表\n *\n * @example\n * class MyPanel extends React.Component {\n * /\\** 搜索面板 @readonly *\\/\n * searchInPanel(params: { query: string }) { ... }\n *\n * /\\** 清除搜索 *\\/\n * clearSearch = (params: { confirm?: boolean }) => { ... };\n *\n * render() { return <div />; }\n * }\n * export default withWebMcpTools(MyPanel);\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport function withWebMcpTools<P extends Record<string, any>>(\n WrappedComponent: React.ComponentClass<P>,\n methodNames?: string[],\n): React.ComponentType<P> {\n const displayName = WrappedComponent.displayName || WrappedComponent.name || 'Component';\n\n function WebMcpToolsWrapper(props: P) {\n const instanceRef = useRef<InstanceType<typeof WrappedComponent> | null>(null);\n\n // HMR 版本追踪:schema 更新时重建 toolMap\n const [hmrVersion, setHmrVersion] = useState(0);\n useEffect(() => {\n const hot = typeof import.meta !== 'undefined' ? import.meta.hot : undefined;\n if (hot && typeof hot.on === 'function') {\n const handler = () => setHmrVersion(v => v + 1);\n hot.on('vite:afterUpdate', handler);\n return () => {\n if (typeof hot.off === 'function') {\n hot.off('vite:afterUpdate', handler);\n }\n };\n }\n }, []);\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const toolMap = useMemo((): Record<string, any> => {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const map: Record<string, any> = {};\n const candidates = methodNames ? new Set(methodNames) : null;\n const proto = WrappedComponent.prototype;\n\n // 来源 1:原型方法(__webmcpSchema 挂在 prototype 方法上)\n for (const name of Object.getOwnPropertyNames(proto)) {\n if (LIFECYCLE_METHODS.has(name)) continue;\n if (candidates && !candidates.has(name)) continue;\n const method = proto[name];\n if (typeof method !== 'function') continue;\n const schema = (method as WebMcpAnnotatedFn).__webmcpSchema;\n if (!schema) continue;\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const wrapper: any = (input: unknown) => (instanceRef.current as any)?.[name](input);\n wrapper.__webmcpSchema = schema;\n map[name] = wrapper;\n }\n\n // 来源 2:class field 箭头函数(schema 挂在静态属性 __webmcpFieldSchemas 上)\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const fieldSchemas = (WrappedComponent as any).__webmcpFieldSchemas as\n | Record<string, { description: string; inputSchema: object; readOnly?: boolean }>\n | undefined;\n if (fieldSchemas) {\n for (const [name, schema] of Object.entries(fieldSchemas)) {\n if (candidates && !candidates.has(name)) continue;\n if (map[name]) continue; // 原型方法优先\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const wrapper: any = (input: unknown) => (instanceRef.current as any)?.[name](input);\n wrapper.__webmcpSchema = schema;\n map[name] = wrapper;\n }\n }\n\n return map;\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [hmrVersion]);\n\n useWebMcpTools(toolMap);\n\n return <WrappedComponent ref={instanceRef} {...props} />;\n }\n\n WebMcpToolsWrapper.displayName = `withWebMcpTools(${displayName})`;\n return WebMcpToolsWrapper;\n}\n"],"mappings":"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","names":["import_react","import_meta","hmrVersion"]}
|
package/dist/index.d.cts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
4
|
* 全局注册 WebMCP 工具。应用启动时调用一次。
|
|
3
5
|
* 支持可变参数,兼容 import * as module 批量导入。
|
|
@@ -34,6 +36,31 @@ declare function registerGlobalTools(...toolMaps: Record<string, Function>[]): v
|
|
|
34
36
|
*/
|
|
35
37
|
declare function useWebMcpTools(...toolMaps: Record<string, Function>[]): void;
|
|
36
38
|
|
|
39
|
+
/**
|
|
40
|
+
* 高阶组件:将 class 组件的方法注册为 WebMCP 工具。
|
|
41
|
+
* mount 时注册,unmount 时自动注销。
|
|
42
|
+
*
|
|
43
|
+
* 支持两种方法形式:
|
|
44
|
+
* - 原型方法:`methodName(params: T) { ... }` — schema 挂在 prototype 上
|
|
45
|
+
* - Class field 箭头函数:`methodName = (params: T) => { ... }` — schema 挂在静态属性上
|
|
46
|
+
*
|
|
47
|
+
* @param WrappedComponent - React class 组件
|
|
48
|
+
* @param methodNames - 可选,显式指定要注册的方法名列表
|
|
49
|
+
*
|
|
50
|
+
* @example
|
|
51
|
+
* class MyPanel extends React.Component {
|
|
52
|
+
* /\** 搜索面板 @readonly *\/
|
|
53
|
+
* searchInPanel(params: { query: string }) { ... }
|
|
54
|
+
*
|
|
55
|
+
* /\** 清除搜索 *\/
|
|
56
|
+
* clearSearch = (params: { confirm?: boolean }) => { ... };
|
|
57
|
+
*
|
|
58
|
+
* render() { return <div />; }
|
|
59
|
+
* }
|
|
60
|
+
* export default withWebMcpTools(MyPanel);
|
|
61
|
+
*/
|
|
62
|
+
declare function withWebMcpTools<P extends Record<string, any>>(WrappedComponent: React.ComponentClass<P>, methodNames?: string[]): React.ComponentType<P>;
|
|
63
|
+
|
|
37
64
|
/** 工具 schema 元数据(由 Vite 插件构建时注入到函数的 __webmcpSchema 属性) */
|
|
38
65
|
interface WebMcpToolSchema {
|
|
39
66
|
/** 工具描述(来自 JSDoc) */
|
|
@@ -74,4 +101,4 @@ interface WebMcpToolConfig {
|
|
|
74
101
|
};
|
|
75
102
|
}
|
|
76
103
|
|
|
77
|
-
export { type WebMcpAnnotatedFn, type WebMcpToolConfig, type WebMcpToolFn, type WebMcpToolSchema, registerGlobalTools, useWebMcpTools };
|
|
104
|
+
export { type WebMcpAnnotatedFn, type WebMcpToolConfig, type WebMcpToolFn, type WebMcpToolSchema, registerGlobalTools, useWebMcpTools, withWebMcpTools };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
4
|
* 全局注册 WebMCP 工具。应用启动时调用一次。
|
|
3
5
|
* 支持可变参数,兼容 import * as module 批量导入。
|
|
@@ -34,6 +36,31 @@ declare function registerGlobalTools(...toolMaps: Record<string, Function>[]): v
|
|
|
34
36
|
*/
|
|
35
37
|
declare function useWebMcpTools(...toolMaps: Record<string, Function>[]): void;
|
|
36
38
|
|
|
39
|
+
/**
|
|
40
|
+
* 高阶组件:将 class 组件的方法注册为 WebMCP 工具。
|
|
41
|
+
* mount 时注册,unmount 时自动注销。
|
|
42
|
+
*
|
|
43
|
+
* 支持两种方法形式:
|
|
44
|
+
* - 原型方法:`methodName(params: T) { ... }` — schema 挂在 prototype 上
|
|
45
|
+
* - Class field 箭头函数:`methodName = (params: T) => { ... }` — schema 挂在静态属性上
|
|
46
|
+
*
|
|
47
|
+
* @param WrappedComponent - React class 组件
|
|
48
|
+
* @param methodNames - 可选,显式指定要注册的方法名列表
|
|
49
|
+
*
|
|
50
|
+
* @example
|
|
51
|
+
* class MyPanel extends React.Component {
|
|
52
|
+
* /\** 搜索面板 @readonly *\/
|
|
53
|
+
* searchInPanel(params: { query: string }) { ... }
|
|
54
|
+
*
|
|
55
|
+
* /\** 清除搜索 *\/
|
|
56
|
+
* clearSearch = (params: { confirm?: boolean }) => { ... };
|
|
57
|
+
*
|
|
58
|
+
* render() { return <div />; }
|
|
59
|
+
* }
|
|
60
|
+
* export default withWebMcpTools(MyPanel);
|
|
61
|
+
*/
|
|
62
|
+
declare function withWebMcpTools<P extends Record<string, any>>(WrappedComponent: React.ComponentClass<P>, methodNames?: string[]): React.ComponentType<P>;
|
|
63
|
+
|
|
37
64
|
/** 工具 schema 元数据(由 Vite 插件构建时注入到函数的 __webmcpSchema 属性) */
|
|
38
65
|
interface WebMcpToolSchema {
|
|
39
66
|
/** 工具描述(来自 JSDoc) */
|
|
@@ -74,4 +101,4 @@ interface WebMcpToolConfig {
|
|
|
74
101
|
};
|
|
75
102
|
}
|
|
76
103
|
|
|
77
|
-
export { type WebMcpAnnotatedFn, type WebMcpToolConfig, type WebMcpToolFn, type WebMcpToolSchema, registerGlobalTools, useWebMcpTools };
|
|
104
|
+
export { type WebMcpAnnotatedFn, type WebMcpToolConfig, type WebMcpToolFn, type WebMcpToolSchema, registerGlobalTools, useWebMcpTools, withWebMcpTools };
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,52 @@
|
|
|
1
1
|
// src/registry.ts
|
|
2
2
|
var modelContextPatched = false;
|
|
3
|
+
function isCallToolResult(value) {
|
|
4
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return false;
|
|
5
|
+
const obj = value;
|
|
6
|
+
if (!Array.isArray(obj.content)) return false;
|
|
7
|
+
if (obj.content.length === 0) return true;
|
|
8
|
+
const first = obj.content[0];
|
|
9
|
+
return first !== null && typeof first === "object" && "type" in first && (first.type === "text" || first.type === "image" || first.type === "resource");
|
|
10
|
+
}
|
|
11
|
+
function normalizeToCallToolResult(value) {
|
|
12
|
+
if (isCallToolResult(value)) {
|
|
13
|
+
return value;
|
|
14
|
+
}
|
|
15
|
+
let text;
|
|
16
|
+
if (typeof value === "string") {
|
|
17
|
+
text = value;
|
|
18
|
+
} else {
|
|
19
|
+
try {
|
|
20
|
+
const serialized = JSON.stringify(value);
|
|
21
|
+
text = serialized === void 0 ? String(value) : serialized;
|
|
22
|
+
} catch {
|
|
23
|
+
text = String(value);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
return {
|
|
27
|
+
content: [{ type: "text", text }],
|
|
28
|
+
isError: false
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
function patchDispatchEventForCoalescing(mc) {
|
|
32
|
+
if (mc.__toolchangeCoalesced) return;
|
|
33
|
+
const originalDispatch = mc.dispatchEvent.bind(mc);
|
|
34
|
+
let pending = false;
|
|
35
|
+
mc.dispatchEvent = (event) => {
|
|
36
|
+
if (event.type === "toolchange") {
|
|
37
|
+
if (!pending) {
|
|
38
|
+
pending = true;
|
|
39
|
+
queueMicrotask(() => {
|
|
40
|
+
originalDispatch(new Event("toolchange"));
|
|
41
|
+
pending = false;
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
return true;
|
|
45
|
+
}
|
|
46
|
+
return originalDispatch(event);
|
|
47
|
+
};
|
|
48
|
+
mc.__toolchangeCoalesced = true;
|
|
49
|
+
}
|
|
3
50
|
function patchModelContextEventSupport() {
|
|
4
51
|
if (modelContextPatched) {
|
|
5
52
|
return;
|
|
@@ -12,6 +59,7 @@ function patchModelContextEventSupport() {
|
|
|
12
59
|
const hasListTools = typeof mc.listTools === "function";
|
|
13
60
|
const hasCallTool = typeof mc.callTool === "function";
|
|
14
61
|
if (hasEventTarget && hasListTools && hasCallTool) {
|
|
62
|
+
patchDispatchEventForCoalescing(mc);
|
|
15
63
|
modelContextPatched = true;
|
|
16
64
|
return;
|
|
17
65
|
}
|
|
@@ -77,9 +125,10 @@ function patchModelContextEventSupport() {
|
|
|
77
125
|
};
|
|
78
126
|
}
|
|
79
127
|
try {
|
|
80
|
-
|
|
128
|
+
const parsed = JSON.parse(result);
|
|
129
|
+
return normalizeToCallToolResult(parsed);
|
|
81
130
|
} catch {
|
|
82
|
-
throw new Error(`Tool returned invalid JSON: ${String(result).slice(0,
|
|
131
|
+
throw new Error(`Tool returned invalid JSON: ${String(result).slice(0, 500)}`);
|
|
83
132
|
}
|
|
84
133
|
};
|
|
85
134
|
}
|
|
@@ -116,6 +165,7 @@ function patchModelContextEventSupport() {
|
|
|
116
165
|
}
|
|
117
166
|
};
|
|
118
167
|
}
|
|
168
|
+
patchDispatchEventForCoalescing(mc);
|
|
119
169
|
modelContextPatched = true;
|
|
120
170
|
}
|
|
121
171
|
var registry = /* @__PURE__ */ new Map();
|
|
@@ -233,10 +283,9 @@ function unregisterScopedTool(name, owner) {
|
|
|
233
283
|
if (record.owners.size > 0) return false;
|
|
234
284
|
if (!record.controller.signal.aborted) {
|
|
235
285
|
record.controller.abort();
|
|
236
|
-
notifyToolsChanged({ immediate: true });
|
|
237
286
|
} else {
|
|
238
287
|
activeTools.delete(name);
|
|
239
|
-
notifyToolsChanged(
|
|
288
|
+
notifyToolsChanged();
|
|
240
289
|
}
|
|
241
290
|
return true;
|
|
242
291
|
} catch {
|
|
@@ -246,11 +295,13 @@ function unregisterScopedTool(name, owner) {
|
|
|
246
295
|
var pushToolsTimer = null;
|
|
247
296
|
function pushToolsToWidget() {
|
|
248
297
|
try {
|
|
249
|
-
const tools = getToolsForWidget();
|
|
250
298
|
if (typeof document === "undefined") return;
|
|
251
299
|
const iframes = document.querySelectorAll("iframe");
|
|
300
|
+
if (iframes.length === 0) return;
|
|
301
|
+
const tools = getToolsForWidget();
|
|
252
302
|
for (let i = 0; i < iframes.length; i++) {
|
|
253
303
|
const iframe = iframes[i];
|
|
304
|
+
if (iframe.hasAttribute("data-webmcp-relay")) continue;
|
|
254
305
|
try {
|
|
255
306
|
if (iframe.contentWindow) {
|
|
256
307
|
iframe.contentWindow.postMessage(
|
|
@@ -301,24 +352,13 @@ function schedulePushToolsToWidget() {
|
|
|
301
352
|
pushToolsToWidget();
|
|
302
353
|
}, 100);
|
|
303
354
|
}
|
|
304
|
-
function
|
|
305
|
-
if (pushToolsTimer) {
|
|
306
|
-
clearTimeout(pushToolsTimer);
|
|
307
|
-
pushToolsTimer = null;
|
|
308
|
-
}
|
|
309
|
-
pushToolsToWidget();
|
|
310
|
-
}
|
|
311
|
-
function notifyToolsChanged(options = {}) {
|
|
355
|
+
function notifyToolsChanged() {
|
|
312
356
|
if (typeof navigator === "undefined" || !("modelContext" in navigator)) return;
|
|
313
357
|
try {
|
|
314
|
-
navigator.modelContext.dispatchEvent(new Event("
|
|
358
|
+
navigator.modelContext.dispatchEvent(new Event("toolchange"));
|
|
315
359
|
} catch {
|
|
316
360
|
}
|
|
317
|
-
|
|
318
|
-
pushToolsToWidgetImmediately();
|
|
319
|
-
} else {
|
|
320
|
-
schedulePushToolsToWidget();
|
|
321
|
-
}
|
|
361
|
+
schedulePushToolsToWidget();
|
|
322
362
|
}
|
|
323
363
|
|
|
324
364
|
// src/polyfill.ts
|
|
@@ -455,8 +495,73 @@ function useWebMcpTools(...toolMaps) {
|
|
|
455
495
|
};
|
|
456
496
|
}, [toolKeys, localHmrVersion]);
|
|
457
497
|
}
|
|
498
|
+
|
|
499
|
+
// src/withWebMcpTools.tsx
|
|
500
|
+
import { useRef as useRef2, useMemo, useState as useState2, useEffect as useEffect2 } from "react";
|
|
501
|
+
import { jsx } from "react/jsx-runtime";
|
|
502
|
+
var LIFECYCLE_METHODS = /* @__PURE__ */ new Set([
|
|
503
|
+
"constructor",
|
|
504
|
+
"render",
|
|
505
|
+
"componentDidMount",
|
|
506
|
+
"componentDidUpdate",
|
|
507
|
+
"componentWillUnmount",
|
|
508
|
+
"shouldComponentUpdate",
|
|
509
|
+
"getSnapshotBeforeUpdate",
|
|
510
|
+
"componentDidCatch"
|
|
511
|
+
]);
|
|
512
|
+
function withWebMcpTools(WrappedComponent, methodNames) {
|
|
513
|
+
const displayName = WrappedComponent.displayName || WrappedComponent.name || "Component";
|
|
514
|
+
function WebMcpToolsWrapper(props) {
|
|
515
|
+
const instanceRef = useRef2(null);
|
|
516
|
+
const [hmrVersion2, setHmrVersion] = useState2(0);
|
|
517
|
+
useEffect2(() => {
|
|
518
|
+
const hot = typeof import.meta !== "undefined" ? import.meta.hot : void 0;
|
|
519
|
+
if (hot && typeof hot.on === "function") {
|
|
520
|
+
const handler = () => setHmrVersion((v) => v + 1);
|
|
521
|
+
hot.on("vite:afterUpdate", handler);
|
|
522
|
+
return () => {
|
|
523
|
+
if (typeof hot.off === "function") {
|
|
524
|
+
hot.off("vite:afterUpdate", handler);
|
|
525
|
+
}
|
|
526
|
+
};
|
|
527
|
+
}
|
|
528
|
+
}, []);
|
|
529
|
+
const toolMap = useMemo(() => {
|
|
530
|
+
const map = {};
|
|
531
|
+
const candidates = methodNames ? new Set(methodNames) : null;
|
|
532
|
+
const proto = WrappedComponent.prototype;
|
|
533
|
+
for (const name of Object.getOwnPropertyNames(proto)) {
|
|
534
|
+
if (LIFECYCLE_METHODS.has(name)) continue;
|
|
535
|
+
if (candidates && !candidates.has(name)) continue;
|
|
536
|
+
const method = proto[name];
|
|
537
|
+
if (typeof method !== "function") continue;
|
|
538
|
+
const schema = method.__webmcpSchema;
|
|
539
|
+
if (!schema) continue;
|
|
540
|
+
const wrapper = (input) => instanceRef.current?.[name](input);
|
|
541
|
+
wrapper.__webmcpSchema = schema;
|
|
542
|
+
map[name] = wrapper;
|
|
543
|
+
}
|
|
544
|
+
const fieldSchemas = WrappedComponent.__webmcpFieldSchemas;
|
|
545
|
+
if (fieldSchemas) {
|
|
546
|
+
for (const [name, schema] of Object.entries(fieldSchemas)) {
|
|
547
|
+
if (candidates && !candidates.has(name)) continue;
|
|
548
|
+
if (map[name]) continue;
|
|
549
|
+
const wrapper = (input) => instanceRef.current?.[name](input);
|
|
550
|
+
wrapper.__webmcpSchema = schema;
|
|
551
|
+
map[name] = wrapper;
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
return map;
|
|
555
|
+
}, [hmrVersion2]);
|
|
556
|
+
useWebMcpTools(toolMap);
|
|
557
|
+
return /* @__PURE__ */ jsx(WrappedComponent, { ref: instanceRef, ...props });
|
|
558
|
+
}
|
|
559
|
+
WebMcpToolsWrapper.displayName = `withWebMcpTools(${displayName})`;
|
|
560
|
+
return WebMcpToolsWrapper;
|
|
561
|
+
}
|
|
458
562
|
export {
|
|
459
563
|
registerGlobalTools,
|
|
460
|
-
useWebMcpTools
|
|
564
|
+
useWebMcpTools,
|
|
565
|
+
withWebMcpTools
|
|
461
566
|
};
|
|
462
567
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/registry.ts","../src/polyfill.ts","../src/registerGlobalTools.ts","../src/useWebMcpTools.ts"],"sourcesContent":["// packages/webmcp-sdk/src/registry.ts\nimport type { WebMcpToolConfig } from './types';\n\nlet modelContextPatched = false;\n\n/**\n * Patches navigator.modelContext to ensure embed.js can discover tools.\n *\n * Handles three scenarios:\n * 1. Chrome 146+ native / @mcp-b/webmcp-polyfill: modelContext lacks\n * listTools/callTool/EventTarget; modelContextTesting shim provides\n * listTools/executeTool\n * → Bridge listTools/callTool from modelContextTesting, add EventTarget,\n * wrap registerTool/unregisterTool to fire toolchange\n * 2. Older polyfills (e.g. @mcp-b/global before EventTarget support):\n * modelContext has listTools/callTool but lacks EventTarget\n * → Add EventTarget, wrap registerTool/unregisterTool to fire toolchange\n * 3. Full MCP-B environment: modelContext has everything → skip\n */\nexport function patchModelContextEventSupport(): void {\n if (modelContextPatched) {\n return;\n }\n if (\n typeof navigator === 'undefined' ||\n !('modelContext' in navigator) ||\n !navigator.modelContext\n ) {\n return;\n }\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const mc = navigator.modelContext as any;\n const hasEventTarget = typeof mc.addEventListener === 'function';\n const hasListTools = typeof mc.listTools === 'function';\n const hasCallTool = typeof mc.callTool === 'function';\n\n // Scenario 3: fully featured — nothing to patch\n if (hasEventTarget && hasListTools && hasCallTool) {\n modelContextPatched = true;\n return;\n }\n\n // Add EventTarget support if missing (Chrome native & polyfill)\n if (!hasEventTarget) {\n const listeners = new Map<string, Set<EventListener>>();\n\n mc.addEventListener = (type: string, callback: EventListener) => {\n if (!listeners.has(type)) listeners.set(type, new Set());\n listeners.get(type)!.add(callback);\n };\n\n mc.removeEventListener = (type: string, callback: EventListener) => {\n listeners.get(type)?.delete(callback);\n };\n\n mc.dispatchEvent = (event: Event): boolean => {\n const set = listeners.get(event.type);\n if (set) {\n set.forEach(fn => {\n try {\n fn.call(mc, event);\n } catch {\n // Ignore listener errors\n }\n });\n }\n return true;\n };\n }\n\n // Bridge listTools/callTool from modelContextTesting if missing (Chrome native)\n // Chrome 146+ has these methods on modelContextTesting but not on modelContext.\n // embed.js requires them on modelContext for getExtendedModelContext() to succeed.\n if (!hasListTools || !hasCallTool) {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const testing = (navigator as any).modelContextTesting;\n if (testing) {\n if (\n typeof testing.listTools === 'function' &&\n !testing.__webmcpNexusListToolsPatched\n ) {\n const originalTestingListTools = testing.listTools.bind(testing);\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n testing.listTools = (...args: any[]) => {\n const nativeTools = originalTestingListTools(...args);\n return Array.isArray(nativeTools)\n ? nativeTools.filter((tool: any) => {\n if (!managedToolNames.has(tool.name)) return true;\n return activeTools.has(tool.name);\n })\n : nativeTools;\n };\n testing.__webmcpNexusListToolsPatched = true;\n }\n if (!hasListTools && typeof testing.listTools === 'function') {\n // Convert testing format (inputSchema as JSON string) to extended format (inputSchema as object)\n mc.listTools = () => {\n try {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n return testing.listTools().map((t: any) => ({\n name: t.name,\n description: t.description || '',\n inputSchema:\n typeof t.inputSchema === 'string' && t.inputSchema.length > 0\n ? JSON.parse(t.inputSchema)\n : { type: 'object', properties: {} },\n }));\n } catch {\n return [];\n }\n };\n }\n if (!hasCallTool && typeof testing.executeTool === 'function') {\n mc.callTool = async (params: { name: string; arguments?: Record<string, unknown> }) => {\n const result = await testing.executeTool(\n params.name,\n JSON.stringify(params.arguments || {}),\n );\n if (result === null) {\n return {\n isError: true,\n content: [{ type: 'text', text: 'Tool execution interrupted by navigation' }],\n };\n }\n try {\n return JSON.parse(result);\n } catch {\n throw new Error(`Tool returned invalid JSON: ${String(result).slice(0, 200)}`);\n }\n };\n }\n }\n }\n\n // Wrap registerTool/unregisterTool to auto-fire 'toolchange'\n // For Chrome native: registerTool fires toolchange on modelContextTesting (not modelContext),\n // but embed.js subscribes on modelContext after our patch — so we need to bridge the event.\n // For polyfill: BrowserMcpServer doesn't fire any events, so wrapping is required.\n if (typeof mc.registerTool === 'function') {\n const originalRegisterTool = mc.registerTool.bind(mc);\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n mc.registerTool = (tool: any, options?: any) => {\n let registrationError: unknown;\n try {\n originalRegisterTool(tool, options);\n } catch (error) {\n registrationError = error;\n }\n try {\n mc.dispatchEvent(new Event('toolchange'));\n } catch {\n // Ignore dispatch errors\n }\n if (registrationError) {\n throw registrationError;\n }\n };\n }\n\n if (typeof mc.unregisterTool === 'function') {\n const originalUnregisterTool = mc.unregisterTool.bind(mc);\n mc.unregisterTool = (name: string) => {\n try {\n originalUnregisterTool(name);\n } catch {\n // Swallow unregister errors\n }\n try {\n mc.dispatchEvent(new Event('toolchange'));\n } catch {\n // Ignore dispatch errors\n }\n };\n }\n\n modelContextPatched = true;\n}\n\ntype ToolScope = 'global' | 'route' | 'component';\n\ninterface ToolEntry {\n name: string;\n scope: ToolScope;\n scopeId: string;\n}\n\ninterface ToolOwner {\n scope: ToolScope;\n scopeId: string;\n}\n\ninterface ActiveToolRecord {\n name: string;\n config: WebMcpToolConfig;\n configs: Map<string, WebMcpToolConfig>;\n controller: AbortController;\n owners: Map<string, ToolOwner>;\n nativeRegistered: boolean;\n}\n\nconst registry = new Map<string, ToolEntry[]>();\nconst activeTools = new Map<string, ActiveToolRecord>();\nconst managedToolNames = new Set<string>();\nlet hasManagedTools = false;\n\nfunction getOwnerKey(owner: ToolOwner): string {\n return `${owner.scope}:${owner.scopeId}`;\n}\n\n/** 获取当前活跃工具名列表(仅用于测试和内部同步) */\nexport function getActiveToolNames(): string[] {\n return Array.from(activeTools.keys());\n}\n\n/** 获取当前活跃工具配置列表(供 pushToolsToWidget 在 listTools 缺失时兜底) */\nexport function getActiveToolConfigs(): Array<{\n name: string;\n description: string;\n inputSchema: object;\n}> {\n return Array.from(activeTools.values()).map(record => ({\n name: record.name,\n description: record.config.description,\n inputSchema: record.config.inputSchema || { type: 'object', properties: {} },\n }));\n}\n\n/**\n * 在内部注册表中记录工具的所有权信息。\n * 如果同名工具已被其他 scope 注册,输出警告但仍允许注册。\n */\nexport function registerEntry(name: string, scope: ToolScope, scopeId: string): void {\n const entries = registry.get(name) || [];\n if (entries.length > 0) {\n const isSameScopeAndId = entries.some(e => e.scope === scope && e.scopeId === scopeId);\n if (!isSameScopeAndId) {\n const existing = entries[0];\n console.warn(\n `[webmcp] Tool \"${name}\" is already registered by scope \"${existing.scope}:${existing.scopeId}\". ` +\n `Re-registering from \"${scope}:${scopeId}\". This may cause unexpected behavior.`,\n );\n }\n }\n entries.push({ name, scope, scopeId });\n registry.set(name, entries);\n}\n\n/**\n * 从内部注册表中移除指定 scope 的工具所有权记录。\n * @returns true 表示可以安全调用 unregisterTool(最后一个持有者已移除);\n * false 表示还有其他 scope 持有该工具,不应注销。\n */\nexport function unregisterEntry(name: string, scope: ToolScope, scopeId: string): boolean {\n const entries = registry.get(name);\n if (!entries) return false;\n const idx = entries.findIndex(e => e.scope === scope && e.scopeId === scopeId);\n if (idx === -1) return false;\n entries.splice(idx, 1);\n if (entries.length === 0) {\n registry.delete(name);\n return true; // 可以安全注销\n }\n return false; // 其他 scope 仍持有该工具\n}\n\n/**\n * 清空注册表(仅用于测试)\n */\nexport function clearRegistry(): void {\n registry.clear();\n activeTools.clear();\n managedToolNames.clear();\n hasManagedTools = false;\n modelContextPatched = false;\n if (pushToolsTimer) {\n clearTimeout(pushToolsTimer);\n pushToolsTimer = null;\n }\n}\n\n/**\n * 注册带 owner 的工具。\n * 同名工具只会原生注册一次;多个组件/作用域通过 owners 聚合生命周期。\n */\nexport function registerScopedTool(toolConfig: WebMcpToolConfig, owner: ToolOwner): void {\n try {\n const ownerKey = getOwnerKey(owner);\n hasManagedTools = true;\n managedToolNames.add(toolConfig.name);\n const existing = activeTools.get(toolConfig.name);\n if (existing) {\n existing.owners.set(ownerKey, owner);\n existing.configs.set(ownerKey, toolConfig);\n return;\n }\n\n const controller = new AbortController();\n const owners = new Map<string, ToolOwner>([[ownerKey, owner]]);\n const record: ActiveToolRecord = {\n name: toolConfig.name,\n config: toolConfig,\n configs: new Map([[ownerKey, toolConfig]]),\n controller,\n owners,\n nativeRegistered: false,\n };\n\n activeTools.set(toolConfig.name, record);\n\n const mc =\n typeof navigator !== 'undefined'\n ? // eslint-disable-next-line @typescript-eslint/no-explicit-any\n (navigator.modelContext as any)\n : undefined;\n\n controller.signal.addEventListener(\n 'abort',\n () => {\n try {\n // L2 fallback: 仅当 modelContext 不是带原生 signal 处理的实现时才调用 unregisterTool。\n // - Chrome 148+ 原生:移除了 unregisterTool,typeof === 'function' 为 false,自动跳过\n // - Chrome 146/147 原生:registerTool 不识别 signal,必须靠 unregisterTool 注销\n // - @mcp-b/webmcp-polyfill 2.x:registerTool 内部已 hook signal abort 自动删除 tool,\n // 再调 unregisterTool 既是双重操作,又会触发 polyfill 的 deprecation 警告。\n // 通过 __isWebMCPPolyfill 标记跳过这条路径。\n if (\n mc &&\n typeof mc.unregisterTool === 'function' &&\n !mc.__isWebMCPPolyfill\n ) {\n mc.unregisterTool(toolConfig.name);\n }\n } catch {\n // Ignore legacy fallback errors\n }\n try {\n activeTools.delete(toolConfig.name);\n notifyToolsChanged();\n } catch {\n // Ignore notification errors\n }\n },\n { once: true },\n );\n\n if (mc && typeof mc.registerTool === 'function') {\n const nativeToolConfig = {\n ...toolConfig,\n execute: async (input: Record<string, unknown>) => {\n const latestRecord = activeTools.get(toolConfig.name);\n return latestRecord?.config.execute(input);\n },\n };\n try {\n mc.registerTool(nativeToolConfig, { signal: controller.signal });\n record.nativeRegistered = true;\n } catch {\n try {\n if (typeof mc.unregisterTool === 'function') {\n mc.unregisterTool(toolConfig.name);\n mc.registerTool(nativeToolConfig, { signal: controller.signal });\n record.nativeRegistered = true;\n }\n } catch {\n // Keep the internal mirror so widget fallback still reflects SDK ownership.\n }\n }\n }\n } catch {\n // SDK public paths should never surface browser API errors.\n }\n}\n\n/**\n * 移除工具 owner。只有最后一个 owner 移除时才 abort 原生注册。\n * @returns true 表示工具列表发生了实际注销;false 表示仍有其他 owner。\n */\nexport function unregisterScopedTool(name: string, owner: ToolOwner): boolean {\n try {\n const record = activeTools.get(name);\n if (!record) {\n return false;\n }\n\n const ownerKey = getOwnerKey(owner);\n const removedConfig = record.configs.get(ownerKey);\n record.owners.delete(ownerKey);\n record.configs.delete(ownerKey);\n if (removedConfig && record.config === removedConfig) {\n const nextConfig = record.configs.values().next().value as WebMcpToolConfig | undefined;\n if (nextConfig) {\n record.config = nextConfig;\n }\n }\n if (record.owners.size > 0) return false;\n\n if (!record.controller.signal.aborted) {\n record.controller.abort();\n notifyToolsChanged({ immediate: true });\n } else {\n activeTools.delete(name);\n notifyToolsChanged({ immediate: true });\n }\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Debounce timer for pushing tools to widget iframe.\n */\nlet pushToolsTimer: ReturnType<typeof setTimeout> | null = null;\n\n/**\n * Directly push the current tool list to any widget iframes via postMessage.\n * This bypasses Chrome's registerToolsChangedCallback mechanism which doesn't\n * fire on unregisterTool, ensuring the relay always has the correct tool list.\n */\nfunction pushToolsToWidget(): void {\n try {\n const tools = getToolsForWidget();\n\n if (typeof document === 'undefined') return;\n\n // Find all iframes and send updated tool list\n // The widget iframe listens for 'webmcp.tools.changed' messages\n const iframes = document.querySelectorAll('iframe');\n for (let i = 0; i < iframes.length; i++) {\n const iframe = iframes[i];\n try {\n if (iframe.contentWindow) {\n iframe.contentWindow.postMessage(\n {\n type: 'webmcp.tools.changed',\n tools,\n },\n '*',\n );\n }\n } catch {\n // Cross-origin or other iframe access error, skip\n }\n }\n } catch {\n // Ignore errors\n }\n}\n\nfunction getToolsForWidget(): Array<{ name: string; description: string; inputSchema: object }> {\n if (hasManagedTools) {\n return getActiveToolConfigs();\n }\n try {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const mc = typeof navigator !== 'undefined' ? (navigator.modelContext as any) : undefined;\n if (mc && typeof mc.listTools === 'function') {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const tools = mc.listTools().map((tool: any) => ({\n name: tool.name,\n description: tool.description || '',\n inputSchema:\n typeof tool.inputSchema === 'string' && tool.inputSchema.length > 0\n ? safeJsonParse(tool.inputSchema)\n : tool.inputSchema || { type: 'object', properties: {} },\n }));\n return tools;\n }\n } catch {\n // Fall through to activeTools mirror\n }\n return getActiveToolConfigs();\n}\n\nfunction safeJsonParse(value: string): object {\n try {\n return JSON.parse(value);\n } catch {\n return { type: 'object', properties: {} };\n }\n}\n\n/**\n * Schedule a debounced push of tools to widget iframes.\n * Uses 100ms delay to coalesce rapid register/unregister cycles\n * (e.g., during SPA navigation when old component unmounts and new one mounts).\n */\nfunction schedulePushToolsToWidget(): void {\n if (pushToolsTimer) {\n clearTimeout(pushToolsTimer);\n }\n pushToolsTimer = setTimeout(() => {\n pushToolsTimer = null;\n pushToolsToWidget();\n }, 100);\n}\n\nfunction pushToolsToWidgetImmediately(): void {\n if (pushToolsTimer) {\n clearTimeout(pushToolsTimer);\n pushToolsTimer = null;\n }\n pushToolsToWidget();\n}\n\n/**\n * 通知 MCP relay 工具列表已变化。\n * 发射 W3C 规范事件名 (toolschanged),toolchange 由 registerTool/unregisterTool 包装器负责。\n */\nexport function notifyToolsChanged(options: { immediate?: boolean } = {}): void {\n if (typeof navigator === 'undefined' || !('modelContext' in navigator)) return;\n try {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n (navigator.modelContext as any).dispatchEvent(new Event('toolschanged'));\n } catch {\n // Ignore dispatch errors\n }\n\n // Direct push to widget iframe as fallback for Chrome native environment\n // where registerToolsChangedCallback doesn't fire on unregisterTool\n if (options.immediate) {\n pushToolsToWidgetImmediately();\n } else {\n schedulePushToolsToWidget();\n }\n}\n\n/**\n * 安全注册工具,处理重复名称的情况。\n * 如果 registerTool 因重复名称抛错,则先 unregister 再重试。\n */\nexport function safeRegisterTool(toolConfig: WebMcpToolConfig): void {\n registerScopedTool(toolConfig, { scope: 'global', scopeId: 'safe-register' });\n}\n","// packages/webmcp-sdk/src/polyfill.ts\nimport { initializeWebMCPPolyfill, cleanupWebMCPPolyfill } from '@mcp-b/webmcp-polyfill';\n\nlet attempted = false;\n\n/**\n * 惰性、幂等地确保 navigator.modelContext 存在。\n *\n * - 原生(Chrome 146+)/ 已被其他 polyfill 安装:直接返回,不动 navigator\n * - 非浏览器环境(SSR):直接返回\n * - 缺失 modelContext:调 initializeWebMCPPolyfill 装上严格 W3C 核心 + modelContextTesting shim\n *\n * 调用方应在判定 'modelContext' in navigator 之前调用本函数。整体包 try/catch,\n * polyfill 加载或初始化失败不向调用方传播异常——SDK 后续逻辑会按\"无 modelContext\"路径继续 no-op。\n */\nexport function ensureModelContextPolyfill(): void {\n if (attempted) return;\n attempted = true;\n if (typeof navigator === 'undefined') return;\n if ('modelContext' in navigator) return;\n try {\n initializeWebMCPPolyfill({ installTestingShim: true });\n } catch {\n // polyfill 初始化失败兜底\n }\n}\n\n/**\n * 仅供单元测试使用:重置模块级 attempted 标志并卸载 polyfill。\n * 生产代码不应调用本函数。\n */\nexport function __resetPolyfillStateForTest(): void {\n attempted = false;\n try {\n cleanupWebMCPPolyfill();\n } catch {\n // cleanup 失败兜底\n }\n}\n","// packages/webmcp-sdk/src/registerGlobalTools.ts\nimport {\n registerEntry,\n registerScopedTool,\n notifyToolsChanged,\n patchModelContextEventSupport,\n} from './registry';\nimport { ensureModelContextPolyfill } from './polyfill';\nimport type { WebMcpAnnotatedFn } from './types';\n\n/**\n * 全局注册 WebMCP 工具。应用启动时调用一次。\n * 支持可变参数,兼容 import * as module 批量导入。\n *\n * @param toolMaps - 一个或多个 Record<string, Function> 对象\n *\n * @example\n * import * as userApi from './api/user';\n * import * as productApi from './api/product';\n * registerGlobalTools(userApi, productApi);\n *\n * @example\n * registerGlobalTools({ getUser, searchUsers }, { searchProducts });\n */\n// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type\nexport function registerGlobalTools(...toolMaps: Record<string, Function>[]): void {\n ensureModelContextPolyfill();\n if (typeof navigator === 'undefined' || !('modelContext' in navigator)) {\n return;\n }\n\n try {\n patchModelContextEventSupport();\n\n let registeredCount = 0;\n\n for (const toolMap of toolMaps) {\n for (const [name, fn] of Object.entries(toolMap)) {\n // 跳过非函数值(如 TypeScript 类型导出在运行时可能不存在)\n if (typeof fn !== 'function') continue;\n\n // __webmcpSchema 由 Vite 插件在构建时注入\n const schema = (fn as WebMcpAnnotatedFn).__webmcpSchema;\n if (!schema) continue;\n\n registerEntry(name, 'global', 'app');\n registerScopedTool(\n {\n name,\n description: schema.description,\n inputSchema: schema.inputSchema,\n execute: async (input: unknown) => fn(input),\n annotations: { readOnlyHint: schema.readOnly ?? false },\n },\n { scope: 'global', scopeId: 'app' },\n );\n registeredCount++;\n }\n }\n\n if (registeredCount > 0) {\n notifyToolsChanged();\n }\n } catch {\n // 全局兜底:SDK 入口不向调用方传播浏览器 API 异常\n }\n}\n","// packages/webmcp-sdk/src/useWebMcpTools.ts\nimport { useEffect, useRef, useState } from 'react';\nimport {\n registerScopedTool,\n unregisterScopedTool,\n notifyToolsChanged,\n patchModelContextEventSupport,\n} from './registry';\nimport { ensureModelContextPolyfill } from './polyfill';\nimport type { WebMcpAnnotatedFn } from './types';\n\nlet scopeCounter = 0;\nfunction generateScopeId(): string {\n return `component-${++scopeCounter}`;\n}\n\n// HMR support: track a global version counter that increments on hot updates.\n// Function body updates are already handled via toolsRef.current indirect calls.\n// This counter ensures schema changes (__webmcpSchema) trigger re-registration.\nlet hmrVersion = 0;\nconst hmrHot = typeof import.meta !== 'undefined' ? import.meta.hot : undefined;\nif (hmrHot && typeof hmrHot.on === 'function') {\n hmrHot.on('vite:afterUpdate', () => {\n hmrVersion++;\n });\n}\n\n/**\n * React Hook:将函数注册为 WebMCP 工具,绑定组件生命周期。\n * mount 时注册,unmount 时自动注销。\n *\n * 使用 useRef 持有最新函数引用,避免闭包陷阱。\n * 使用 toolKeys(工具名集合的字符串)作为 useEffect 依赖,\n * 当工具集合变化时重新注册,函数体变化不触发重新注册。\n *\n * @param toolMaps - 一个或多个 Record<string, Function> 对象\n *\n * @example\n * // 组件级注册\n * useWebMcpTools({ searchInPanel, clearSearch });\n *\n * @example\n * // 路由级注册(配合 React Router 使用)\n * useWebMcpTools({ setUserFilter });\n */\nexport function useWebMcpTools(...toolMaps: Record<string, Function>[]): void {\n // 合并所有 toolMap 为一个对象\n const merged: Record<string, Function> = {};\n for (const toolMap of toolMaps) {\n for (const [name, fn] of Object.entries(toolMap)) {\n if (typeof fn === 'function') {\n merged[name] = fn;\n }\n }\n }\n\n // 用 ref 持有最新的函数引用,避免闭包陷阱\n const toolsRef = useRef(merged);\n toolsRef.current = merged;\n\n // 生成唯一 scopeId,确保同一组件实例的 scopeId 一致\n const scopeIdRef = useRef<string>('');\n if (!scopeIdRef.current) {\n scopeIdRef.current = generateScopeId();\n }\n\n // 工具名集合作为依赖 — 工具集合变化时重新注册\n const toolKeys = Object.keys(merged).sort().join(',');\n\n // DEV: listen for HMR updates to force re-registration when schema changes\n const [localHmrVersion, setLocalHmrVersion] = useState(hmrVersion);\n useEffect(() => {\n const hot = typeof import.meta !== 'undefined' ? import.meta.hot : undefined;\n if (hot && typeof hot.on === 'function') {\n const handler = () => setLocalHmrVersion(v => v + 1);\n hot.on('vite:afterUpdate', handler);\n return () => {\n if (typeof hot.off === 'function') {\n hot.off('vite:afterUpdate', handler);\n }\n };\n }\n }, []);\n\n useEffect(() => {\n ensureModelContextPolyfill();\n if (typeof navigator === 'undefined' || !('modelContext' in navigator)) {\n return;\n }\n\n patchModelContextEventSupport();\n\n const registeredNames: string[] = [];\n\n for (const [name, fn] of Object.entries(toolsRef.current)) {\n const schema = (fn as WebMcpAnnotatedFn).__webmcpSchema;\n if (!schema) continue;\n\n registerScopedTool(\n {\n name,\n description: schema.description,\n inputSchema: schema.inputSchema,\n // 通过 ref 间接调用,保证始终执行最新版本的函数\n execute: async (input: unknown) => toolsRef.current[name](input),\n annotations: { readOnlyHint: schema.readOnly ?? false },\n },\n { scope: 'component', scopeId: scopeIdRef.current },\n );\n\n registeredNames.push(name);\n }\n\n if (registeredNames.length > 0) {\n notifyToolsChanged();\n }\n\n // 组件卸载时释放 owner;最后一个 owner 才会触发原生 abort。\n return () => {\n let unregisteredAny = false;\n for (const name of registeredNames) {\n const shouldUnregister = unregisterScopedTool(name, {\n scope: 'component',\n scopeId: scopeIdRef.current,\n });\n if (shouldUnregister) {\n unregisteredAny = true;\n }\n }\n if (unregisteredAny) {\n notifyToolsChanged();\n }\n };\n }, [toolKeys, localHmrVersion]); // 工具集合变化或 HMR 更新时重新注册\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/registry.ts","../src/polyfill.ts","../src/registerGlobalTools.ts","../src/useWebMcpTools.ts","../src/withWebMcpTools.tsx"],"sourcesContent":["// packages/webmcp-sdk/src/registry.ts\nimport type { WebMcpToolConfig } from './types';\n\nlet modelContextPatched = false;\n\n/**\n * 严格判断值是否为合法的 MCP CallToolResult。\n * 检查 content 数组存在且首元素含合法 type 字段,防止业务对象误判。\n */\nexport function isCallToolResult(value: unknown): boolean {\n if (!value || typeof value !== 'object' || Array.isArray(value)) return false;\n const obj = value as Record<string, unknown>;\n if (!Array.isArray(obj.content)) return false;\n if (obj.content.length === 0) return true;\n const first = obj.content[0];\n return (\n first !== null &&\n typeof first === 'object' &&\n 'type' in first &&\n ((first as Record<string, unknown>).type === 'text' ||\n (first as Record<string, unknown>).type === 'image' ||\n (first as Record<string, unknown>).type === 'resource')\n );\n}\n\n/**\n * 将工具原始返回值规范化为 MCP CallToolResult 格式。\n * 若已是合法 CallToolResult 则直接返回,否则将完整内容包装为 text content。\n * 注意:不对结果做任何截断,完整保留工具返回的全部数据。\n */\nexport function normalizeToCallToolResult(value: unknown): Record<string, unknown> {\n if (isCallToolResult(value)) {\n return value as Record<string, unknown>;\n }\n let text: string;\n if (typeof value === 'string') {\n text = value;\n } else {\n try {\n const serialized = JSON.stringify(value);\n text = serialized === undefined ? String(value) : serialized;\n } catch {\n text = String(value);\n }\n }\n return {\n content: [{ type: 'text', text }],\n isError: false,\n };\n}\n\n/**\n * 在 dispatchEvent 层面合并 toolchange 事件。\n * 无论来源(SDK 包装器、polyfill 内部、第三方代码),同一微任务内只触发 1 次。\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nfunction patchDispatchEventForCoalescing(mc: any): void {\n if (mc.__toolchangeCoalesced) return;\n const originalDispatch: (event: Event) => boolean =\n mc.dispatchEvent.bind(mc);\n let pending = false;\n mc.dispatchEvent = (event: Event): boolean => {\n if (event.type === 'toolchange') {\n if (!pending) {\n pending = true;\n queueMicrotask(() => {\n originalDispatch(new Event('toolchange'));\n pending = false;\n });\n }\n return true;\n }\n return originalDispatch(event);\n };\n mc.__toolchangeCoalesced = true;\n}\n\n/**\n * Patches navigator.modelContext to ensure embed.js can discover tools.\n *\n * Handles three scenarios:\n * 1. Chrome 146+ native / @mcp-b/webmcp-polyfill: modelContext lacks\n * listTools/callTool/EventTarget; modelContextTesting shim provides\n * listTools/executeTool\n * → Bridge listTools/callTool from modelContextTesting, add EventTarget,\n * wrap registerTool/unregisterTool to fire toolchange\n * 2. Older polyfills (e.g. @mcp-b/global before EventTarget support):\n * modelContext has listTools/callTool but lacks EventTarget\n * → Add EventTarget, wrap registerTool/unregisterTool to fire toolchange\n * 3. Full MCP-B environment: modelContext has everything → skip\n */\nexport function patchModelContextEventSupport(): void {\n if (modelContextPatched) {\n return;\n }\n if (\n typeof navigator === 'undefined' ||\n !('modelContext' in navigator) ||\n !navigator.modelContext\n ) {\n return;\n }\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const mc = navigator.modelContext as any;\n const hasEventTarget = typeof mc.addEventListener === 'function';\n const hasListTools = typeof mc.listTools === 'function';\n const hasCallTool = typeof mc.callTool === 'function';\n\n // Scenario 3: fully featured — only apply toolchange coalescing\n if (hasEventTarget && hasListTools && hasCallTool) {\n patchDispatchEventForCoalescing(mc);\n modelContextPatched = true;\n return;\n }\n\n // Add EventTarget support if missing (Chrome native & polyfill)\n if (!hasEventTarget) {\n const listeners = new Map<string, Set<EventListener>>();\n\n mc.addEventListener = (type: string, callback: EventListener) => {\n if (!listeners.has(type)) listeners.set(type, new Set());\n listeners.get(type)!.add(callback);\n };\n\n mc.removeEventListener = (type: string, callback: EventListener) => {\n listeners.get(type)?.delete(callback);\n };\n\n mc.dispatchEvent = (event: Event): boolean => {\n const set = listeners.get(event.type);\n if (set) {\n set.forEach(fn => {\n try {\n fn.call(mc, event);\n } catch {\n // Ignore listener errors\n }\n });\n }\n return true;\n };\n }\n\n // Bridge listTools/callTool from modelContextTesting if missing (Chrome native)\n // Chrome 146+ has these methods on modelContextTesting but not on modelContext.\n // embed.js requires them on modelContext for getExtendedModelContext() to succeed.\n if (!hasListTools || !hasCallTool) {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const testing = (navigator as any).modelContextTesting;\n if (testing) {\n if (\n typeof testing.listTools === 'function' &&\n !testing.__webmcpNexusListToolsPatched\n ) {\n const originalTestingListTools = testing.listTools.bind(testing);\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n testing.listTools = (...args: any[]) => {\n const nativeTools = originalTestingListTools(...args);\n return Array.isArray(nativeTools)\n ? nativeTools.filter((tool: any) => {\n if (!managedToolNames.has(tool.name)) return true;\n return activeTools.has(tool.name);\n })\n : nativeTools;\n };\n testing.__webmcpNexusListToolsPatched = true;\n }\n if (!hasListTools && typeof testing.listTools === 'function') {\n // Convert testing format (inputSchema as JSON string) to extended format (inputSchema as object)\n mc.listTools = () => {\n try {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n return testing.listTools().map((t: any) => ({\n name: t.name,\n description: t.description || '',\n inputSchema:\n typeof t.inputSchema === 'string' && t.inputSchema.length > 0\n ? JSON.parse(t.inputSchema)\n : { type: 'object', properties: {} },\n }));\n } catch {\n return [];\n }\n };\n }\n if (!hasCallTool && typeof testing.executeTool === 'function') {\n mc.callTool = async (params: { name: string; arguments?: Record<string, unknown> }) => {\n const result = await testing.executeTool(\n params.name,\n JSON.stringify(params.arguments || {}),\n );\n if (result === null) {\n return {\n isError: true,\n content: [{ type: 'text', text: 'Tool execution interrupted by navigation' }],\n };\n }\n try {\n const parsed = JSON.parse(result);\n return normalizeToCallToolResult(parsed);\n } catch {\n throw new Error(`Tool returned invalid JSON: ${String(result).slice(0, 500)}`);\n }\n };\n }\n }\n }\n\n // Wrap registerTool/unregisterTool to auto-fire 'toolchange'\n // For Chrome native: registerTool fires toolchange on modelContextTesting (not modelContext),\n // but embed.js subscribes on modelContext after our patch — so we need to bridge the event.\n // For polyfill: BrowserMcpServer doesn't fire any events, so wrapping is required.\n if (typeof mc.registerTool === 'function') {\n const originalRegisterTool = mc.registerTool.bind(mc);\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n mc.registerTool = (tool: any, options?: any) => {\n let registrationError: unknown;\n try {\n originalRegisterTool(tool, options);\n } catch (error) {\n registrationError = error;\n }\n try {\n mc.dispatchEvent(new Event('toolchange'));\n } catch {\n // Ignore dispatch errors\n }\n if (registrationError) {\n throw registrationError;\n }\n };\n }\n\n if (typeof mc.unregisterTool === 'function') {\n const originalUnregisterTool = mc.unregisterTool.bind(mc);\n mc.unregisterTool = (name: string) => {\n try {\n originalUnregisterTool(name);\n } catch {\n // Swallow unregister errors\n }\n try {\n mc.dispatchEvent(new Event('toolchange'));\n } catch {\n // Ignore dispatch errors\n }\n };\n }\n\n patchDispatchEventForCoalescing(mc);\n modelContextPatched = true;\n}\n\ntype ToolScope = 'global' | 'route' | 'component';\n\ninterface ToolEntry {\n name: string;\n scope: ToolScope;\n scopeId: string;\n}\n\ninterface ToolOwner {\n scope: ToolScope;\n scopeId: string;\n}\n\ninterface ActiveToolRecord {\n name: string;\n config: WebMcpToolConfig;\n configs: Map<string, WebMcpToolConfig>;\n controller: AbortController;\n owners: Map<string, ToolOwner>;\n nativeRegistered: boolean;\n}\n\nconst registry = new Map<string, ToolEntry[]>();\nconst activeTools = new Map<string, ActiveToolRecord>();\nconst managedToolNames = new Set<string>();\nlet hasManagedTools = false;\n\nfunction getOwnerKey(owner: ToolOwner): string {\n return `${owner.scope}:${owner.scopeId}`;\n}\n\n/** 获取当前活跃工具名列表(仅用于测试和内部同步) */\nexport function getActiveToolNames(): string[] {\n return Array.from(activeTools.keys());\n}\n\n/** 获取当前活跃工具配置列表(供 pushToolsToWidget 在 listTools 缺失时兜底) */\nexport function getActiveToolConfigs(): Array<{\n name: string;\n description: string;\n inputSchema: object;\n}> {\n return Array.from(activeTools.values()).map(record => ({\n name: record.name,\n description: record.config.description,\n inputSchema: record.config.inputSchema || { type: 'object', properties: {} },\n }));\n}\n\n/**\n * 在内部注册表中记录工具的所有权信息。\n * 如果同名工具已被其他 scope 注册,输出警告但仍允许注册。\n */\nexport function registerEntry(name: string, scope: ToolScope, scopeId: string): void {\n const entries = registry.get(name) || [];\n if (entries.length > 0) {\n const isSameScopeAndId = entries.some(e => e.scope === scope && e.scopeId === scopeId);\n if (!isSameScopeAndId) {\n const existing = entries[0];\n console.warn(\n `[webmcp] Tool \"${name}\" is already registered by scope \"${existing.scope}:${existing.scopeId}\". ` +\n `Re-registering from \"${scope}:${scopeId}\". This may cause unexpected behavior.`,\n );\n }\n }\n entries.push({ name, scope, scopeId });\n registry.set(name, entries);\n}\n\n/**\n * 从内部注册表中移除指定 scope 的工具所有权记录。\n * @returns true 表示可以安全调用 unregisterTool(最后一个持有者已移除);\n * false 表示还有其他 scope 持有该工具,不应注销。\n */\nexport function unregisterEntry(name: string, scope: ToolScope, scopeId: string): boolean {\n const entries = registry.get(name);\n if (!entries) return false;\n const idx = entries.findIndex(e => e.scope === scope && e.scopeId === scopeId);\n if (idx === -1) return false;\n entries.splice(idx, 1);\n if (entries.length === 0) {\n registry.delete(name);\n return true; // 可以安全注销\n }\n return false; // 其他 scope 仍持有该工具\n}\n\n/**\n * 清空注册表(仅用于测试)\n */\nexport function clearRegistry(): void {\n registry.clear();\n activeTools.clear();\n managedToolNames.clear();\n hasManagedTools = false;\n modelContextPatched = false;\n if (pushToolsTimer) {\n clearTimeout(pushToolsTimer);\n pushToolsTimer = null;\n }\n}\n\n/**\n * 注册带 owner 的工具。\n * 同名工具只会原生注册一次;多个组件/作用域通过 owners 聚合生命周期。\n */\nexport function registerScopedTool(toolConfig: WebMcpToolConfig, owner: ToolOwner): void {\n try {\n const ownerKey = getOwnerKey(owner);\n hasManagedTools = true;\n managedToolNames.add(toolConfig.name);\n const existing = activeTools.get(toolConfig.name);\n if (existing) {\n existing.owners.set(ownerKey, owner);\n existing.configs.set(ownerKey, toolConfig);\n return;\n }\n\n const controller = new AbortController();\n const owners = new Map<string, ToolOwner>([[ownerKey, owner]]);\n const record: ActiveToolRecord = {\n name: toolConfig.name,\n config: toolConfig,\n configs: new Map([[ownerKey, toolConfig]]),\n controller,\n owners,\n nativeRegistered: false,\n };\n\n activeTools.set(toolConfig.name, record);\n\n const mc =\n typeof navigator !== 'undefined'\n ? // eslint-disable-next-line @typescript-eslint/no-explicit-any\n (navigator.modelContext as any)\n : undefined;\n\n controller.signal.addEventListener(\n 'abort',\n () => {\n try {\n // L2 fallback: 仅当 modelContext 不是带原生 signal 处理的实现时才调用 unregisterTool。\n // - Chrome 148+ 原生:移除了 unregisterTool,typeof === 'function' 为 false,自动跳过\n // - Chrome 146/147 原生:registerTool 不识别 signal,必须靠 unregisterTool 注销\n // - @mcp-b/webmcp-polyfill 2.x:registerTool 内部已 hook signal abort 自动删除 tool,\n // 再调 unregisterTool 既是双重操作,又会触发 polyfill 的 deprecation 警告。\n // 通过 __isWebMCPPolyfill 标记跳过这条路径。\n if (\n mc &&\n typeof mc.unregisterTool === 'function' &&\n !mc.__isWebMCPPolyfill\n ) {\n mc.unregisterTool(toolConfig.name);\n }\n } catch {\n // Ignore legacy fallback errors\n }\n try {\n activeTools.delete(toolConfig.name);\n notifyToolsChanged();\n } catch {\n // Ignore notification errors\n }\n },\n { once: true },\n );\n\n if (mc && typeof mc.registerTool === 'function') {\n const nativeToolConfig = {\n ...toolConfig,\n execute: async (input: Record<string, unknown>) => {\n const latestRecord = activeTools.get(toolConfig.name);\n return latestRecord?.config.execute(input);\n },\n };\n try {\n mc.registerTool(nativeToolConfig, { signal: controller.signal });\n record.nativeRegistered = true;\n } catch {\n try {\n if (typeof mc.unregisterTool === 'function') {\n mc.unregisterTool(toolConfig.name);\n mc.registerTool(nativeToolConfig, { signal: controller.signal });\n record.nativeRegistered = true;\n }\n } catch {\n // Keep the internal mirror so widget fallback still reflects SDK ownership.\n }\n }\n }\n } catch {\n // SDK public paths should never surface browser API errors.\n }\n}\n\n/**\n * 移除工具 owner。只有最后一个 owner 移除时才 abort 原生注册。\n * @returns true 表示工具列表发生了实际注销;false 表示仍有其他 owner。\n */\nexport function unregisterScopedTool(name: string, owner: ToolOwner): boolean {\n try {\n const record = activeTools.get(name);\n if (!record) {\n return false;\n }\n\n const ownerKey = getOwnerKey(owner);\n const removedConfig = record.configs.get(ownerKey);\n record.owners.delete(ownerKey);\n record.configs.delete(ownerKey);\n if (removedConfig && record.config === removedConfig) {\n const nextConfig = record.configs.values().next().value as WebMcpToolConfig | undefined;\n if (nextConfig) {\n record.config = nextConfig;\n }\n }\n if (record.owners.size > 0) return false;\n\n if (!record.controller.signal.aborted) {\n record.controller.abort();\n } else {\n activeTools.delete(name);\n notifyToolsChanged();\n }\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Debounce timer for pushing tools to widget iframe.\n */\nlet pushToolsTimer: ReturnType<typeof setTimeout> | null = null;\n\n/**\n * Directly push the current tool list to any widget iframes via postMessage.\n * This bypasses Chrome's registerToolsChangedCallback mechanism which doesn't\n * fire on unregisterTool, ensuring the relay always has the correct tool list.\n */\nfunction pushToolsToWidget(): void {\n try {\n if (typeof document === 'undefined') return;\n\n const iframes = document.querySelectorAll('iframe');\n if (iframes.length === 0) return;\n\n const tools = getToolsForWidget();\n\n for (let i = 0; i < iframes.length; i++) {\n const iframe = iframes[i];\n // embed.js(@mcp-b/webmcp-local-relay)已通过 toolchange 监听自行推送\n // relay iframe,跳过以避免重复 webmcp.tools.changed 消息\n if (iframe.hasAttribute('data-webmcp-relay')) continue;\n try {\n if (iframe.contentWindow) {\n iframe.contentWindow.postMessage(\n {\n type: 'webmcp.tools.changed',\n tools,\n },\n '*',\n );\n }\n } catch {\n // Cross-origin or other iframe access error, skip\n }\n }\n } catch {\n // Ignore errors\n }\n}\n\nfunction getToolsForWidget(): Array<{ name: string; description: string; inputSchema: object }> {\n if (hasManagedTools) {\n return getActiveToolConfigs();\n }\n try {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const mc = typeof navigator !== 'undefined' ? (navigator.modelContext as any) : undefined;\n if (mc && typeof mc.listTools === 'function') {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const tools = mc.listTools().map((tool: any) => ({\n name: tool.name,\n description: tool.description || '',\n inputSchema:\n typeof tool.inputSchema === 'string' && tool.inputSchema.length > 0\n ? safeJsonParse(tool.inputSchema)\n : tool.inputSchema || { type: 'object', properties: {} },\n }));\n return tools;\n }\n } catch {\n // Fall through to activeTools mirror\n }\n return getActiveToolConfigs();\n}\n\nfunction safeJsonParse(value: string): object {\n try {\n return JSON.parse(value);\n } catch {\n return { type: 'object', properties: {} };\n }\n}\n\n/**\n * Schedule a debounced push of tools to widget iframes.\n * Uses 100ms delay to coalesce rapid register/unregister cycles\n * (e.g., during SPA navigation when old component unmounts and new one mounts).\n */\nfunction schedulePushToolsToWidget(): void {\n if (pushToolsTimer) {\n clearTimeout(pushToolsTimer);\n }\n pushToolsTimer = setTimeout(() => {\n pushToolsTimer = null;\n pushToolsToWidget();\n }, 100);\n}\n\n/**\n * 通知工具列表已变化,发射标准 toolchange 事件并推送至 widget iframe。\n */\nexport function notifyToolsChanged(): void {\n if (typeof navigator === 'undefined' || !('modelContext' in navigator)) return;\n try {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n (navigator.modelContext as any).dispatchEvent(new Event('toolchange'));\n } catch {\n // Ignore dispatch errors\n }\n\n schedulePushToolsToWidget();\n}\n\n/**\n * 安全注册工具,处理重复名称的情况。\n * 如果 registerTool 因重复名称抛错,则先 unregister 再重试。\n */\nexport function safeRegisterTool(toolConfig: WebMcpToolConfig): void {\n registerScopedTool(toolConfig, { scope: 'global', scopeId: 'safe-register' });\n}\n","// packages/webmcp-sdk/src/polyfill.ts\nimport { initializeWebMCPPolyfill, cleanupWebMCPPolyfill } from '@mcp-b/webmcp-polyfill';\n\nlet attempted = false;\n\n/**\n * 惰性、幂等地确保 navigator.modelContext 存在。\n *\n * - 原生(Chrome 146+)/ 已被其他 polyfill 安装:直接返回,不动 navigator\n * - 非浏览器环境(SSR):直接返回\n * - 缺失 modelContext:调 initializeWebMCPPolyfill 装上严格 W3C 核心 + modelContextTesting shim\n *\n * 调用方应在判定 'modelContext' in navigator 之前调用本函数。整体包 try/catch,\n * polyfill 加载或初始化失败不向调用方传播异常——SDK 后续逻辑会按\"无 modelContext\"路径继续 no-op。\n */\nexport function ensureModelContextPolyfill(): void {\n if (attempted) return;\n attempted = true;\n if (typeof navigator === 'undefined') return;\n if ('modelContext' in navigator) return;\n try {\n initializeWebMCPPolyfill({ installTestingShim: true });\n } catch {\n // polyfill 初始化失败兜底\n }\n}\n\n/**\n * 仅供单元测试使用:重置模块级 attempted 标志并卸载 polyfill。\n * 生产代码不应调用本函数。\n */\nexport function __resetPolyfillStateForTest(): void {\n attempted = false;\n try {\n cleanupWebMCPPolyfill();\n } catch {\n // cleanup 失败兜底\n }\n}\n","// packages/webmcp-sdk/src/registerGlobalTools.ts\nimport {\n registerEntry,\n registerScopedTool,\n notifyToolsChanged,\n patchModelContextEventSupport,\n} from './registry';\nimport { ensureModelContextPolyfill } from './polyfill';\nimport type { WebMcpAnnotatedFn } from './types';\n\n/**\n * 全局注册 WebMCP 工具。应用启动时调用一次。\n * 支持可变参数,兼容 import * as module 批量导入。\n *\n * @param toolMaps - 一个或多个 Record<string, Function> 对象\n *\n * @example\n * import * as userApi from './api/user';\n * import * as productApi from './api/product';\n * registerGlobalTools(userApi, productApi);\n *\n * @example\n * registerGlobalTools({ getUser, searchUsers }, { searchProducts });\n */\n// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type\nexport function registerGlobalTools(...toolMaps: Record<string, Function>[]): void {\n ensureModelContextPolyfill();\n if (typeof navigator === 'undefined' || !('modelContext' in navigator)) {\n return;\n }\n\n try {\n patchModelContextEventSupport();\n\n let registeredCount = 0;\n\n for (const toolMap of toolMaps) {\n for (const [name, fn] of Object.entries(toolMap)) {\n // 跳过非函数值(如 TypeScript 类型导出在运行时可能不存在)\n if (typeof fn !== 'function') continue;\n\n // __webmcpSchema 由 Vite 插件在构建时注入\n const schema = (fn as WebMcpAnnotatedFn).__webmcpSchema;\n if (!schema) continue;\n\n registerEntry(name, 'global', 'app');\n registerScopedTool(\n {\n name,\n description: schema.description,\n inputSchema: schema.inputSchema,\n execute: async (input: unknown) => fn(input),\n annotations: { readOnlyHint: schema.readOnly ?? false },\n },\n { scope: 'global', scopeId: 'app' },\n );\n registeredCount++;\n }\n }\n\n if (registeredCount > 0) {\n notifyToolsChanged();\n }\n } catch {\n // 全局兜底:SDK 入口不向调用方传播浏览器 API 异常\n }\n}\n","// packages/webmcp-sdk/src/useWebMcpTools.ts\nimport { useEffect, useRef, useState } from 'react';\nimport {\n registerScopedTool,\n unregisterScopedTool,\n notifyToolsChanged,\n patchModelContextEventSupport,\n} from './registry';\nimport { ensureModelContextPolyfill } from './polyfill';\nimport type { WebMcpAnnotatedFn } from './types';\n\nlet scopeCounter = 0;\nfunction generateScopeId(): string {\n return `component-${++scopeCounter}`;\n}\n\n// HMR support: track a global version counter that increments on hot updates.\n// Function body updates are already handled via toolsRef.current indirect calls.\n// This counter ensures schema changes (__webmcpSchema) trigger re-registration.\nlet hmrVersion = 0;\nconst hmrHot = typeof import.meta !== 'undefined' ? import.meta.hot : undefined;\nif (hmrHot && typeof hmrHot.on === 'function') {\n hmrHot.on('vite:afterUpdate', () => {\n hmrVersion++;\n });\n}\n\n/**\n * React Hook:将函数注册为 WebMCP 工具,绑定组件生命周期。\n * mount 时注册,unmount 时自动注销。\n *\n * 使用 useRef 持有最新函数引用,避免闭包陷阱。\n * 使用 toolKeys(工具名集合的字符串)作为 useEffect 依赖,\n * 当工具集合变化时重新注册,函数体变化不触发重新注册。\n *\n * @param toolMaps - 一个或多个 Record<string, Function> 对象\n *\n * @example\n * // 组件级注册\n * useWebMcpTools({ searchInPanel, clearSearch });\n *\n * @example\n * // 路由级注册(配合 React Router 使用)\n * useWebMcpTools({ setUserFilter });\n */\nexport function useWebMcpTools(...toolMaps: Record<string, Function>[]): void {\n // 合并所有 toolMap 为一个对象\n const merged: Record<string, Function> = {};\n for (const toolMap of toolMaps) {\n for (const [name, fn] of Object.entries(toolMap)) {\n if (typeof fn === 'function') {\n merged[name] = fn;\n }\n }\n }\n\n // 用 ref 持有最新的函数引用,避免闭包陷阱\n const toolsRef = useRef(merged);\n toolsRef.current = merged;\n\n // 生成唯一 scopeId,确保同一组件实例的 scopeId 一致\n const scopeIdRef = useRef<string>('');\n if (!scopeIdRef.current) {\n scopeIdRef.current = generateScopeId();\n }\n\n // 工具名集合作为依赖 — 工具集合变化时重新注册\n const toolKeys = Object.keys(merged).sort().join(',');\n\n // DEV: listen for HMR updates to force re-registration when schema changes\n const [localHmrVersion, setLocalHmrVersion] = useState(hmrVersion);\n useEffect(() => {\n const hot = typeof import.meta !== 'undefined' ? import.meta.hot : undefined;\n if (hot && typeof hot.on === 'function') {\n const handler = () => setLocalHmrVersion(v => v + 1);\n hot.on('vite:afterUpdate', handler);\n return () => {\n if (typeof hot.off === 'function') {\n hot.off('vite:afterUpdate', handler);\n }\n };\n }\n }, []);\n\n useEffect(() => {\n ensureModelContextPolyfill();\n if (typeof navigator === 'undefined' || !('modelContext' in navigator)) {\n return;\n }\n\n patchModelContextEventSupport();\n\n const registeredNames: string[] = [];\n\n for (const [name, fn] of Object.entries(toolsRef.current)) {\n const schema = (fn as WebMcpAnnotatedFn).__webmcpSchema;\n if (!schema) continue;\n\n registerScopedTool(\n {\n name,\n description: schema.description,\n inputSchema: schema.inputSchema,\n // 通过 ref 间接调用,保证始终执行最新版本的函数\n execute: async (input: unknown) => toolsRef.current[name](input),\n annotations: { readOnlyHint: schema.readOnly ?? false },\n },\n { scope: 'component', scopeId: scopeIdRef.current },\n );\n\n registeredNames.push(name);\n }\n\n if (registeredNames.length > 0) {\n notifyToolsChanged();\n }\n\n // 组件卸载时释放 owner;最后一个 owner 才会触发原生 abort。\n return () => {\n let unregisteredAny = false;\n for (const name of registeredNames) {\n const shouldUnregister = unregisterScopedTool(name, {\n scope: 'component',\n scopeId: scopeIdRef.current,\n });\n if (shouldUnregister) {\n unregisteredAny = true;\n }\n }\n if (unregisteredAny) {\n notifyToolsChanged();\n }\n };\n }, [toolKeys, localHmrVersion]); // 工具集合变化或 HMR 更新时重新注册\n}\n","// packages/webmcp-sdk/src/withWebMcpTools.tsx\nimport React, { useRef, useMemo, useState, useEffect } from 'react';\nimport { useWebMcpTools } from './useWebMcpTools';\nimport type { WebMcpAnnotatedFn } from './types';\n\n/** React 生命周期方法黑名单 */\nconst LIFECYCLE_METHODS = new Set([\n 'constructor',\n 'render',\n 'componentDidMount',\n 'componentDidUpdate',\n 'componentWillUnmount',\n 'shouldComponentUpdate',\n 'getSnapshotBeforeUpdate',\n 'componentDidCatch',\n]);\n\n/**\n * 高阶组件:将 class 组件的方法注册为 WebMCP 工具。\n * mount 时注册,unmount 时自动注销。\n *\n * 支持两种方法形式:\n * - 原型方法:`methodName(params: T) { ... }` — schema 挂在 prototype 上\n * - Class field 箭头函数:`methodName = (params: T) => { ... }` — schema 挂在静态属性上\n *\n * @param WrappedComponent - React class 组件\n * @param methodNames - 可选,显式指定要注册的方法名列表\n *\n * @example\n * class MyPanel extends React.Component {\n * /\\** 搜索面板 @readonly *\\/\n * searchInPanel(params: { query: string }) { ... }\n *\n * /\\** 清除搜索 *\\/\n * clearSearch = (params: { confirm?: boolean }) => { ... };\n *\n * render() { return <div />; }\n * }\n * export default withWebMcpTools(MyPanel);\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport function withWebMcpTools<P extends Record<string, any>>(\n WrappedComponent: React.ComponentClass<P>,\n methodNames?: string[],\n): React.ComponentType<P> {\n const displayName = WrappedComponent.displayName || WrappedComponent.name || 'Component';\n\n function WebMcpToolsWrapper(props: P) {\n const instanceRef = useRef<InstanceType<typeof WrappedComponent> | null>(null);\n\n // HMR 版本追踪:schema 更新时重建 toolMap\n const [hmrVersion, setHmrVersion] = useState(0);\n useEffect(() => {\n const hot = typeof import.meta !== 'undefined' ? import.meta.hot : undefined;\n if (hot && typeof hot.on === 'function') {\n const handler = () => setHmrVersion(v => v + 1);\n hot.on('vite:afterUpdate', handler);\n return () => {\n if (typeof hot.off === 'function') {\n hot.off('vite:afterUpdate', handler);\n }\n };\n }\n }, []);\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const toolMap = useMemo((): Record<string, any> => {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const map: Record<string, any> = {};\n const candidates = methodNames ? new Set(methodNames) : null;\n const proto = WrappedComponent.prototype;\n\n // 来源 1:原型方法(__webmcpSchema 挂在 prototype 方法上)\n for (const name of Object.getOwnPropertyNames(proto)) {\n if (LIFECYCLE_METHODS.has(name)) continue;\n if (candidates && !candidates.has(name)) continue;\n const method = proto[name];\n if (typeof method !== 'function') continue;\n const schema = (method as WebMcpAnnotatedFn).__webmcpSchema;\n if (!schema) continue;\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const wrapper: any = (input: unknown) => (instanceRef.current as any)?.[name](input);\n wrapper.__webmcpSchema = schema;\n map[name] = wrapper;\n }\n\n // 来源 2:class field 箭头函数(schema 挂在静态属性 __webmcpFieldSchemas 上)\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const fieldSchemas = (WrappedComponent as any).__webmcpFieldSchemas as\n | Record<string, { description: string; inputSchema: object; readOnly?: boolean }>\n | undefined;\n if (fieldSchemas) {\n for (const [name, schema] of Object.entries(fieldSchemas)) {\n if (candidates && !candidates.has(name)) continue;\n if (map[name]) continue; // 原型方法优先\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const wrapper: any = (input: unknown) => (instanceRef.current as any)?.[name](input);\n wrapper.__webmcpSchema = schema;\n map[name] = wrapper;\n }\n }\n\n return map;\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [hmrVersion]);\n\n useWebMcpTools(toolMap);\n\n return <WrappedComponent ref={instanceRef} {...props} />;\n }\n\n WebMcpToolsWrapper.displayName = `withWebMcpTools(${displayName})`;\n return WebMcpToolsWrapper;\n}\n"],"mappings":"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","names":["useRef","useState","useEffect","useRef","hmrVersion","useState","useEffect"]}
|