@xiaozhi-client/endpoint 1.9.7-beta.9 → 1.9.8-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -3
- package/dist/index.d.ts +239 -29
- package/dist/index.js +253 -108
- package/dist/index.js.map +1 -1
- package/package.json +11 -5
package/README.md
CHANGED
|
@@ -370,11 +370,11 @@ interface SSEMCPServerConfig {
|
|
|
370
370
|
}
|
|
371
371
|
```
|
|
372
372
|
|
|
373
|
-
####
|
|
373
|
+
#### HTTP MCP 服务器
|
|
374
374
|
|
|
375
375
|
```typescript
|
|
376
|
-
interface
|
|
377
|
-
type?: "
|
|
376
|
+
interface HTTPMCPServerConfig {
|
|
377
|
+
type?: "http";
|
|
378
378
|
url: string;
|
|
379
379
|
headers?: Record<string, string>;
|
|
380
380
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -99,6 +99,10 @@ interface IMCPServiceManager {
|
|
|
99
99
|
getAllTools(): EnhancedToolInfo[];
|
|
100
100
|
/** 调用工具 */
|
|
101
101
|
callTool(toolName: string, arguments_: Record<string, unknown>): Promise<ToolCallResult>;
|
|
102
|
+
/** 初始化 */
|
|
103
|
+
initialize(): Promise<void>;
|
|
104
|
+
/** 清理资源 */
|
|
105
|
+
cleanup(): Promise<void>;
|
|
102
106
|
}
|
|
103
107
|
/**
|
|
104
108
|
* 连接状态枚举
|
|
@@ -186,9 +190,11 @@ interface ReconnectResult {
|
|
|
186
190
|
* 支持三种配置方式:
|
|
187
191
|
* 1. 本地命令 (stdio): { command: string; args: string[]; env?: Record<string, string> }
|
|
188
192
|
* 2. SSE: { type: "sse"; url: string; headers?: Record<string, string> }
|
|
189
|
-
* 3.
|
|
193
|
+
* 3. HTTP: { type?: "http"; url: string; headers?: Record<string, string> }
|
|
194
|
+
*
|
|
195
|
+
* 向后兼容:自动将 streamable-http/streamable_http/streamableHttp 转换为 http
|
|
190
196
|
*/
|
|
191
|
-
type MCPServerConfig = LocalMCPServerConfig | SSEMCPServerConfig |
|
|
197
|
+
type MCPServerConfig = LocalMCPServerConfig | SSEMCPServerConfig | HTTPMCPServerConfig;
|
|
192
198
|
/**
|
|
193
199
|
* 本地 MCP 服务器配置
|
|
194
200
|
*/
|
|
@@ -206,16 +212,20 @@ interface SSEMCPServerConfig {
|
|
|
206
212
|
headers?: Record<string, string>;
|
|
207
213
|
}
|
|
208
214
|
/**
|
|
209
|
-
*
|
|
215
|
+
* HTTP MCP 服务器配置
|
|
216
|
+
* 使用 type: "http"
|
|
217
|
+
* 向后兼容 streamable-http 写法
|
|
210
218
|
*/
|
|
211
|
-
interface
|
|
212
|
-
type?: "streamable-http";
|
|
219
|
+
interface HTTPMCPServerConfig {
|
|
220
|
+
type?: "http" | "streamable-http";
|
|
213
221
|
url: string;
|
|
214
222
|
headers?: Record<string, string>;
|
|
215
223
|
}
|
|
224
|
+
/** @deprecated 使用 HTTPMCPServerConfig 代替 */
|
|
225
|
+
type StreamableHTTPMCPServerConfig = HTTPMCPServerConfig;
|
|
216
226
|
/**
|
|
217
227
|
* Endpoint 配置接口
|
|
218
|
-
*
|
|
228
|
+
* @deprecated 不再使用,Endpoint 构造函数改为接收 IMCPServiceManager
|
|
219
229
|
*/
|
|
220
230
|
interface EndpointConfig {
|
|
221
231
|
/** MCP 服务器配置(声明式) */
|
|
@@ -226,21 +236,67 @@ interface EndpointConfig {
|
|
|
226
236
|
modelscopeApiKey?: string;
|
|
227
237
|
}
|
|
228
238
|
/**
|
|
229
|
-
* EndpointManager
|
|
239
|
+
* EndpointManager 配置接口
|
|
230
240
|
*/
|
|
231
241
|
interface EndpointManagerConfig {
|
|
232
242
|
/** 可选:默认重连延迟(毫秒) */
|
|
233
243
|
defaultReconnectDelay?: number;
|
|
234
244
|
}
|
|
245
|
+
/**
|
|
246
|
+
* 小智平台 JWT Token Payload 接口
|
|
247
|
+
*
|
|
248
|
+
* @example
|
|
249
|
+
* ```typescript
|
|
250
|
+
* // 从 endpoint URL 解码得到的 payload
|
|
251
|
+
* const payload: XiaozhiTokenPayload = {
|
|
252
|
+
* userId: 302720,
|
|
253
|
+
* agentId: 1324149,
|
|
254
|
+
* endpointId: "agent_1324149",
|
|
255
|
+
* purpose: "mcp-endpoint",
|
|
256
|
+
* iat: 1768480930,
|
|
257
|
+
* exp: 1800038530
|
|
258
|
+
* };
|
|
259
|
+
* ```
|
|
260
|
+
*/
|
|
261
|
+
interface XiaozhiTokenPayload {
|
|
262
|
+
/** 用户 ID */
|
|
263
|
+
userId: number;
|
|
264
|
+
/** 代理 ID */
|
|
265
|
+
agentId: number;
|
|
266
|
+
/** 接入点 ID,格式为 "agent_{agentId}" */
|
|
267
|
+
endpointId: string;
|
|
268
|
+
/** Token 用途 */
|
|
269
|
+
purpose: string;
|
|
270
|
+
/** 签发时间(Unix 时间戳) */
|
|
271
|
+
iat: number;
|
|
272
|
+
/** 过期时间(Unix 时间戳) */
|
|
273
|
+
exp: number;
|
|
274
|
+
}
|
|
275
|
+
/**
|
|
276
|
+
* 解析后的 Endpoint URL 信息
|
|
277
|
+
*/
|
|
278
|
+
interface ParsedEndpointInfo {
|
|
279
|
+
/** 完整的 endpoint URL */
|
|
280
|
+
url: string;
|
|
281
|
+
/** 提取的 JWT Token */
|
|
282
|
+
token: string;
|
|
283
|
+
/** 解码后的 Token Payload */
|
|
284
|
+
payload: XiaozhiTokenPayload;
|
|
285
|
+
/** WebSocket 服务器地址(不含 token 参数) */
|
|
286
|
+
wsUrl: string;
|
|
287
|
+
}
|
|
235
288
|
|
|
236
289
|
/**
|
|
237
|
-
* Endpoint
|
|
290
|
+
* Endpoint 类
|
|
238
291
|
* 管理单个小智接入点的 WebSocket 连接
|
|
239
292
|
* 实现 MCP (Model Context Protocol) 协议通信
|
|
240
293
|
*
|
|
241
|
-
*
|
|
242
|
-
*
|
|
243
|
-
*
|
|
294
|
+
* 使用方式:
|
|
295
|
+
* ```typescript
|
|
296
|
+
* const mcpManager = new SharedMCPAdapter(globalMCPManager);
|
|
297
|
+
* const endpoint = new Endpoint("ws://...", mcpManager);
|
|
298
|
+
* await endpoint.connect();
|
|
299
|
+
* ```
|
|
244
300
|
*/
|
|
245
301
|
|
|
246
302
|
/**
|
|
@@ -263,9 +319,10 @@ declare class Endpoint {
|
|
|
263
319
|
* 构造函数
|
|
264
320
|
*
|
|
265
321
|
* @param endpointUrl - 小智接入点 URL
|
|
266
|
-
* @param
|
|
322
|
+
* @param mcpManager - MCP 服务管理器(依赖注入)
|
|
323
|
+
* @param reconnectDelay - 可选的重连延迟(毫秒)
|
|
267
324
|
*/
|
|
268
|
-
constructor(endpointUrl: string,
|
|
325
|
+
constructor(endpointUrl: string, mcpManager: IMCPServiceManager, reconnectDelay?: number);
|
|
269
326
|
/**
|
|
270
327
|
* 获取 Endpoint URL
|
|
271
328
|
*/
|
|
@@ -341,23 +398,39 @@ declare class Endpoint {
|
|
|
341
398
|
}
|
|
342
399
|
|
|
343
400
|
/**
|
|
344
|
-
* EndpointManager
|
|
345
|
-
*
|
|
401
|
+
* EndpointManager
|
|
402
|
+
* 管理多个小智接入点的连接,共享外部传入的 MCPManager
|
|
403
|
+
*
|
|
404
|
+
* 使用方式:
|
|
405
|
+
* ```typescript
|
|
406
|
+
* // 1. 先创建并配置 MCPManager
|
|
407
|
+
* const mcpManager = new MCPManager();
|
|
408
|
+
* mcpManager.addServer("calculator", { command: "npx", args: ["-y", "calculator-mcp"] });
|
|
409
|
+
* await mcpManager.connect();
|
|
346
410
|
*
|
|
347
|
-
*
|
|
348
|
-
*
|
|
349
|
-
*
|
|
350
|
-
*
|
|
411
|
+
* // 2. 创建 EndpointManager 并设置 MCPManager
|
|
412
|
+
* const manager = new EndpointManager();
|
|
413
|
+
* manager.setMcpManager(mcpManager);
|
|
414
|
+
*
|
|
415
|
+
* // 3. 添加接入点
|
|
416
|
+
* manager.addEndpoint("ws://endpoint1");
|
|
417
|
+
* await manager.connect();
|
|
418
|
+
* ```
|
|
351
419
|
*/
|
|
352
420
|
|
|
353
421
|
/**
|
|
354
|
-
*
|
|
355
|
-
*
|
|
422
|
+
* 小智接入点管理器
|
|
423
|
+
* 负责管理多个小智接入点的连接,共享 MCP 服务
|
|
424
|
+
*
|
|
425
|
+
* 注意:MCPManager 必须通过 setMcpManager() 方法设置,且由外部管理其生命周期
|
|
356
426
|
*/
|
|
357
427
|
declare class EndpointManager extends EventEmitter {
|
|
358
428
|
private config?;
|
|
359
429
|
private endpoints;
|
|
360
430
|
private connectionStates;
|
|
431
|
+
private mcpManager;
|
|
432
|
+
private sharedMCPAdapter;
|
|
433
|
+
private mcpEventListeners;
|
|
361
434
|
/**
|
|
362
435
|
* 构造函数
|
|
363
436
|
*
|
|
@@ -365,11 +438,23 @@ declare class EndpointManager extends EventEmitter {
|
|
|
365
438
|
*/
|
|
366
439
|
constructor(config?: EndpointManagerConfig | undefined);
|
|
367
440
|
/**
|
|
368
|
-
*
|
|
441
|
+
* 设置 MCPManager 实例
|
|
369
442
|
*
|
|
370
|
-
*
|
|
443
|
+
* 注意:MCPManager 的生命周期由外部管理,EndpointManager 不负责连接和断开
|
|
444
|
+
*
|
|
445
|
+
* @param mcpManager - 外部创建并已连接的 MCPManager 实例
|
|
446
|
+
*/
|
|
447
|
+
setMcpManager(mcpManager: IMCPServiceManager): void;
|
|
448
|
+
/**
|
|
449
|
+
* 添加 Endpoint(支持 URL 字符串或 Endpoint 实例)
|
|
450
|
+
*
|
|
451
|
+
* @param endpoint - Endpoint URL 字符串或 Endpoint 实例
|
|
452
|
+
*/
|
|
453
|
+
addEndpoint(endpoint: string | Endpoint): void;
|
|
454
|
+
/**
|
|
455
|
+
* 内部添加 Endpoint 方法
|
|
371
456
|
*/
|
|
372
|
-
|
|
457
|
+
private addEndpointInternal;
|
|
373
458
|
/**
|
|
374
459
|
* 移除 Endpoint 实例
|
|
375
460
|
*
|
|
@@ -377,13 +462,21 @@ declare class EndpointManager extends EventEmitter {
|
|
|
377
462
|
*/
|
|
378
463
|
removeEndpoint(endpoint: Endpoint): void;
|
|
379
464
|
/**
|
|
380
|
-
*
|
|
465
|
+
* 连接 Endpoint
|
|
466
|
+
*
|
|
467
|
+
* 注意:此方法不负责连接 MCPManager,MCPManager 必须在调用此方法前已连接
|
|
468
|
+
*
|
|
469
|
+
* @param endpoint - 可选,指定要连接的端点 URL。如果不传入,则连接所有端点
|
|
381
470
|
*/
|
|
382
|
-
connect(): Promise<void>;
|
|
471
|
+
connect(endpoint?: string): Promise<void>;
|
|
383
472
|
/**
|
|
384
|
-
*
|
|
473
|
+
* 断开连接
|
|
474
|
+
*
|
|
475
|
+
* 注意:此方法不断开 MCPManager,MCPManager 的生命周期由外部管理
|
|
476
|
+
*
|
|
477
|
+
* @param endpoint - 可选,指定要断开的端点 URL。如果不传入,则断开所有端点
|
|
385
478
|
*/
|
|
386
|
-
disconnect(): Promise<void>;
|
|
479
|
+
disconnect(endpoint?: string): Promise<void>;
|
|
387
480
|
/**
|
|
388
481
|
* 获取所有 Endpoint URL
|
|
389
482
|
*/
|
|
@@ -414,6 +507,14 @@ declare class EndpointManager extends EventEmitter {
|
|
|
414
507
|
* @param url - 端点 URL
|
|
415
508
|
*/
|
|
416
509
|
getEndpointStatus(url: string): SimpleConnectionStatus | undefined;
|
|
510
|
+
/**
|
|
511
|
+
* 重连
|
|
512
|
+
*
|
|
513
|
+
* @param endpoint - 可选,指定要重连的端点 URL。如果不传入,则重连所有端点
|
|
514
|
+
* @param delay - 可选,disconnect 和 connect 之间的等待时间(毫秒),默认为 1000ms。
|
|
515
|
+
* 注意:此参数只控制断开和重新连接之间的等待时间,不影响底层 Endpoint 实例的重连延迟
|
|
516
|
+
*/
|
|
517
|
+
reconnect(endpoint?: string, delay?: number): Promise<void>;
|
|
417
518
|
/**
|
|
418
519
|
* 重连所有端点
|
|
419
520
|
*/
|
|
@@ -426,10 +527,14 @@ declare class EndpointManager extends EventEmitter {
|
|
|
426
527
|
reconnectEndpoint(url: string): Promise<void>;
|
|
427
528
|
/**
|
|
428
529
|
* 清除所有端点
|
|
530
|
+
*
|
|
531
|
+
* 注意:此方法不会清理 MCPManager,MCPManager 的生命周期由外部管理
|
|
429
532
|
*/
|
|
430
533
|
clearEndpoints(): Promise<void>;
|
|
431
534
|
/**
|
|
432
535
|
* 清理资源
|
|
536
|
+
*
|
|
537
|
+
* 注意:此方法不会清理 MCPManager,MCPManager 的生命周期由外部管理
|
|
433
538
|
*/
|
|
434
539
|
cleanup(): Promise<void>;
|
|
435
540
|
/**
|
|
@@ -442,6 +547,64 @@ declare class EndpointManager extends EventEmitter {
|
|
|
442
547
|
private reconnectSingleEndpoint;
|
|
443
548
|
}
|
|
444
549
|
|
|
550
|
+
/**
|
|
551
|
+
* 共享 MCP 管理器适配器
|
|
552
|
+
*
|
|
553
|
+
* 接收全局 MCPManager 实例的引用,不创建独立连接
|
|
554
|
+
* 用于多个 Endpoint 共享同一个 MCPManager 实例
|
|
555
|
+
*
|
|
556
|
+
* @example
|
|
557
|
+
* ```typescript
|
|
558
|
+
* const globalMCPManager = new MCPManager();
|
|
559
|
+
* await globalMCPManager.connect();
|
|
560
|
+
*
|
|
561
|
+
* const adapter = new SharedMCPAdapter(globalMCPManager);
|
|
562
|
+
* const endpoint = new Endpoint("ws://...", adapter);
|
|
563
|
+
* ```
|
|
564
|
+
*/
|
|
565
|
+
|
|
566
|
+
/**
|
|
567
|
+
* 共享 MCP 管理器适配器
|
|
568
|
+
* 实现 IMCPServiceManager 接口,将操作委托给全局 MCPManager
|
|
569
|
+
*/
|
|
570
|
+
declare class SharedMCPAdapter implements IMCPServiceManager {
|
|
571
|
+
private globalMCPManager;
|
|
572
|
+
private isInitialized;
|
|
573
|
+
/**
|
|
574
|
+
* 构造函数
|
|
575
|
+
*
|
|
576
|
+
* @param globalMCPManager - 全局 MCPManager 实例
|
|
577
|
+
*/
|
|
578
|
+
constructor(globalMCPManager: IMCPServiceManager);
|
|
579
|
+
/**
|
|
580
|
+
* 初始化适配器
|
|
581
|
+
*
|
|
582
|
+
* 注意:此方法不执行任何连接操作,仅标记初始化状态,因为全局 MCPManager 的生命周期由外部管理
|
|
583
|
+
*/
|
|
584
|
+
initialize(): Promise<void>;
|
|
585
|
+
/**
|
|
586
|
+
* 获取所有工具列表
|
|
587
|
+
*
|
|
588
|
+
* 从全局 MCPManager 获取工具列表,并转换为增强格式
|
|
589
|
+
*/
|
|
590
|
+
getAllTools(): EnhancedToolInfo[];
|
|
591
|
+
/**
|
|
592
|
+
* 调用工具
|
|
593
|
+
*
|
|
594
|
+
* 将工具调用委托给全局 MCPManager
|
|
595
|
+
*
|
|
596
|
+
* @param toolName - 工具名称(格式:serviceName__toolName)
|
|
597
|
+
* @param arguments_ - 工具调用参数
|
|
598
|
+
*/
|
|
599
|
+
callTool(toolName: string, arguments_: Record<string, unknown>): Promise<ToolCallResult>;
|
|
600
|
+
/**
|
|
601
|
+
* 清理资源
|
|
602
|
+
*
|
|
603
|
+
* 注意:此方法不会断开全局 MCPManager,由创建者负责管理
|
|
604
|
+
*/
|
|
605
|
+
cleanup(): Promise<void>;
|
|
606
|
+
}
|
|
607
|
+
|
|
445
608
|
/**
|
|
446
609
|
* 工具函数模块
|
|
447
610
|
*
|
|
@@ -515,6 +678,53 @@ declare function sleep(ms: number): Promise<void>;
|
|
|
515
678
|
* @returns 格式化后的错误消息
|
|
516
679
|
*/
|
|
517
680
|
declare function formatErrorMessage(error: unknown): string;
|
|
681
|
+
/**
|
|
682
|
+
* 解码 JWT Token(仅解析 payload,不验证签名)
|
|
683
|
+
*
|
|
684
|
+
* @param token - JWT Token 字符串
|
|
685
|
+
* @returns 解码后的 Token Payload,解码失败返回 null
|
|
686
|
+
*
|
|
687
|
+
* @example
|
|
688
|
+
* ```typescript
|
|
689
|
+
* const payload = decodeJWTToken("eyJ...token...");
|
|
690
|
+
* if (payload) {
|
|
691
|
+
* console.log(payload.endpointId); // "agent_1324149"
|
|
692
|
+
* }
|
|
693
|
+
* ```
|
|
694
|
+
*/
|
|
695
|
+
declare function decodeJWTToken(token: string): XiaozhiTokenPayload | null;
|
|
696
|
+
/**
|
|
697
|
+
* 从 endpoint URL 中提取 token 参数
|
|
698
|
+
*
|
|
699
|
+
* @param url - 完整的 endpoint URL
|
|
700
|
+
* @returns 提取的 token 字符串,未找到返回 null
|
|
701
|
+
*
|
|
702
|
+
* @example
|
|
703
|
+
* ```typescript
|
|
704
|
+
* const token = extractTokenFromUrl(
|
|
705
|
+
* "wss://api.xiaozhi.me/mcp/?token=eyJ..."
|
|
706
|
+
* );
|
|
707
|
+
* ```
|
|
708
|
+
*/
|
|
709
|
+
declare function extractTokenFromUrl(url: string): string | null;
|
|
710
|
+
/**
|
|
711
|
+
* 解析 endpoint URL 获取完整信息
|
|
712
|
+
*
|
|
713
|
+
* @param url - 完整的 endpoint URL
|
|
714
|
+
* @returns 解析后的 endpoint 信息,解析失败返回 null
|
|
715
|
+
*
|
|
716
|
+
* @example
|
|
717
|
+
* ```typescript
|
|
718
|
+
* const info = parseEndpointUrl(
|
|
719
|
+
* "wss://api.xiaozhi.me/mcp/?token=eyJ..."
|
|
720
|
+
* );
|
|
721
|
+
* if (info) {
|
|
722
|
+
* console.log(info.payload.endpointId); // "agent_1324149"
|
|
723
|
+
* console.log(info.wsUrl); // "wss://api.xiaozhi.me/mcp/"
|
|
724
|
+
* }
|
|
725
|
+
* ```
|
|
726
|
+
*/
|
|
727
|
+
declare function parseEndpointUrl(url: string): ParsedEndpointInfo | null;
|
|
518
728
|
|
|
519
729
|
/**
|
|
520
730
|
* MCP 消息类型定义
|
|
@@ -548,4 +758,4 @@ interface ExtendedMCPMessage {
|
|
|
548
758
|
};
|
|
549
759
|
}
|
|
550
760
|
|
|
551
|
-
export { type ConfigChangeEvent, type ConnectionOptions, ConnectionState, type ConnectionStatus, Endpoint, type EndpointConfig, type EndpointConnectionStatus, EndpointManager, type EndpointManagerConfig, type ExtendedMCPMessage, type IMCPServiceManager, type JSONSchema, type LocalMCPServerConfig, type MCPMessage, type MCPServerConfig, type ReconnectResult, type SSEMCPServerConfig, type SimpleConnectionStatus, type StreamableHTTPMCPServerConfig, ToolCallError, ToolCallErrorCode, type ToolCallParams, type ToolCallResult, type ValidatedToolCallParams, deepMerge, ensureToolJSONSchema, formatErrorMessage, isValidEndpointUrl, sleep, sliceEndpoint, validateToolCallParams };
|
|
761
|
+
export { type ConfigChangeEvent, type ConnectionOptions, ConnectionState, type ConnectionStatus, Endpoint, type EndpointConfig, type EndpointConnectionStatus, EndpointManager, type EndpointManagerConfig, type ExtendedMCPMessage, type IMCPServiceManager, type JSONSchema, type LocalMCPServerConfig, type MCPMessage, type MCPServerConfig, type ParsedEndpointInfo, type ReconnectResult, type SSEMCPServerConfig, SharedMCPAdapter, type SimpleConnectionStatus, type StreamableHTTPMCPServerConfig, ToolCallError, ToolCallErrorCode, type ToolCallParams, type ToolCallResult, type ValidatedToolCallParams, type XiaozhiTokenPayload, decodeJWTToken, deepMerge, ensureToolJSONSchema, extractTokenFromUrl, formatErrorMessage, isValidEndpointUrl, parseEndpointUrl, sleep, sliceEndpoint, validateToolCallParams };
|
package/dist/index.js
CHANGED
|
@@ -133,105 +133,73 @@ function formatErrorMessage(error) {
|
|
|
133
133
|
return String(error);
|
|
134
134
|
}
|
|
135
135
|
__name(formatErrorMessage, "formatErrorMessage");
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
var InternalMCPManagerAdapter = class {
|
|
141
|
-
constructor(config) {
|
|
142
|
-
this.config = config;
|
|
143
|
-
this.mcpManager = new MCPManager();
|
|
144
|
-
for (const [serviceName, serverConfig] of Object.entries(
|
|
145
|
-
config.mcpServers
|
|
146
|
-
)) {
|
|
147
|
-
const mcpConfig = this.convertToMCPServiceConfig(serviceName, serverConfig);
|
|
148
|
-
this.mcpManager.addServer(serviceName, mcpConfig);
|
|
149
|
-
}
|
|
150
|
-
this.mcpManager.on("connected", (data) => {
|
|
151
|
-
});
|
|
152
|
-
this.mcpManager.on("error", (data) => {
|
|
153
|
-
});
|
|
136
|
+
function base64UrlDecode(input) {
|
|
137
|
+
let base64 = input.replace(/-/g, "+").replace(/_/g, "/");
|
|
138
|
+
while (base64.length % 4) {
|
|
139
|
+
base64 += "=";
|
|
154
140
|
}
|
|
155
|
-
|
|
156
|
-
|
|
141
|
+
return Buffer.from(base64, "base64").toString("utf-8");
|
|
142
|
+
}
|
|
143
|
+
__name(base64UrlDecode, "base64UrlDecode");
|
|
144
|
+
function decodeJWTToken(token) {
|
|
145
|
+
if (!token || typeof token !== "string") {
|
|
146
|
+
return null;
|
|
157
147
|
}
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
* 初始化并启动所有 MCP 服务
|
|
163
|
-
*/
|
|
164
|
-
async initialize() {
|
|
165
|
-
if (this.isInitialized) {
|
|
166
|
-
return;
|
|
148
|
+
try {
|
|
149
|
+
const parts = token.split(".");
|
|
150
|
+
if (parts.length !== 3) {
|
|
151
|
+
return null;
|
|
167
152
|
}
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
153
|
+
const payloadStr = base64UrlDecode(parts[1]);
|
|
154
|
+
const payload = JSON.parse(payloadStr);
|
|
155
|
+
if (typeof payload !== "object" || payload === null || !("userId" in payload) || !("agentId" in payload) || !("endpointId" in payload) || !("purpose" in payload) || !("iat" in payload) || !("exp" in payload)) {
|
|
156
|
+
return null;
|
|
157
|
+
}
|
|
158
|
+
return payload;
|
|
159
|
+
} catch {
|
|
160
|
+
return null;
|
|
171
161
|
}
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
return
|
|
162
|
+
}
|
|
163
|
+
__name(decodeJWTToken, "decodeJWTToken");
|
|
164
|
+
function extractTokenFromUrl(url) {
|
|
165
|
+
if (!url || typeof url !== "string") {
|
|
166
|
+
return null;
|
|
177
167
|
}
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
return this.mcpManager.callTool(serviceName, actualToolName, arguments_);
|
|
168
|
+
try {
|
|
169
|
+
const urlObj = new URL(url);
|
|
170
|
+
return urlObj.searchParams.get("token");
|
|
171
|
+
} catch {
|
|
172
|
+
return null;
|
|
184
173
|
}
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
this.tools.clear();
|
|
191
|
-
this.isInitialized = false;
|
|
174
|
+
}
|
|
175
|
+
__name(extractTokenFromUrl, "extractTokenFromUrl");
|
|
176
|
+
function parseEndpointUrl(url) {
|
|
177
|
+
if (!url || typeof url !== "string") {
|
|
178
|
+
return null;
|
|
192
179
|
}
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
this.tools.clear();
|
|
198
|
-
const mcpTools = this.mcpManager.listTools();
|
|
199
|
-
for (const mcpTool of mcpTools) {
|
|
200
|
-
const enhancedTool = {
|
|
201
|
-
name: `${mcpTool.serverName}__${mcpTool.name}`,
|
|
202
|
-
description: mcpTool.description,
|
|
203
|
-
inputSchema: mcpTool.inputSchema,
|
|
204
|
-
serviceName: mcpTool.serverName,
|
|
205
|
-
originalName: mcpTool.name,
|
|
206
|
-
enabled: true,
|
|
207
|
-
usageCount: 0,
|
|
208
|
-
lastUsedTime: (/* @__PURE__ */ new Date()).toISOString()
|
|
209
|
-
};
|
|
210
|
-
this.tools.set(enhancedTool.name, enhancedTool);
|
|
180
|
+
try {
|
|
181
|
+
const token = extractTokenFromUrl(url);
|
|
182
|
+
if (!token) {
|
|
183
|
+
return null;
|
|
211
184
|
}
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
*/
|
|
216
|
-
parseToolName(toolName) {
|
|
217
|
-
const parts = toolName.split("__");
|
|
218
|
-
if (parts.length < 2) {
|
|
219
|
-
throw new Error(`\u65E0\u6548\u7684\u5DE5\u5177\u540D\u79F0\u683C\u5F0F: ${toolName}`);
|
|
185
|
+
const payload = decodeJWTToken(token);
|
|
186
|
+
if (!payload) {
|
|
187
|
+
return null;
|
|
220
188
|
}
|
|
221
|
-
const
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
return rest;
|
|
189
|
+
const urlObj = new URL(url);
|
|
190
|
+
urlObj.searchParams.delete("token");
|
|
191
|
+
const wsUrl = urlObj.toString();
|
|
192
|
+
return {
|
|
193
|
+
url,
|
|
194
|
+
token,
|
|
195
|
+
payload,
|
|
196
|
+
wsUrl
|
|
197
|
+
};
|
|
198
|
+
} catch {
|
|
199
|
+
return null;
|
|
233
200
|
}
|
|
234
|
-
}
|
|
201
|
+
}
|
|
202
|
+
__name(parseEndpointUrl, "parseEndpointUrl");
|
|
235
203
|
|
|
236
204
|
// src/endpoint.ts
|
|
237
205
|
var Endpoint = class {
|
|
@@ -257,12 +225,13 @@ var Endpoint = class {
|
|
|
257
225
|
* 构造函数
|
|
258
226
|
*
|
|
259
227
|
* @param endpointUrl - 小智接入点 URL
|
|
260
|
-
* @param
|
|
228
|
+
* @param mcpManager - MCP 服务管理器(依赖注入)
|
|
229
|
+
* @param reconnectDelay - 可选的重连延迟(毫秒)
|
|
261
230
|
*/
|
|
262
|
-
constructor(endpointUrl,
|
|
231
|
+
constructor(endpointUrl, mcpManager, reconnectDelay) {
|
|
263
232
|
this.endpointUrl = endpointUrl;
|
|
264
|
-
this.reconnectDelay =
|
|
265
|
-
this.mcpAdapter =
|
|
233
|
+
this.reconnectDelay = reconnectDelay ?? 2e3;
|
|
234
|
+
this.mcpAdapter = mcpManager;
|
|
266
235
|
}
|
|
267
236
|
/**
|
|
268
237
|
* 获取 Endpoint URL
|
|
@@ -577,6 +546,65 @@ var Endpoint = class {
|
|
|
577
546
|
|
|
578
547
|
// src/manager.ts
|
|
579
548
|
import { EventEmitter } from "events";
|
|
549
|
+
|
|
550
|
+
// src/shared-mcp-adapter.ts
|
|
551
|
+
var SharedMCPAdapter = class {
|
|
552
|
+
/**
|
|
553
|
+
* 构造函数
|
|
554
|
+
*
|
|
555
|
+
* @param globalMCPManager - 全局 MCPManager 实例
|
|
556
|
+
*/
|
|
557
|
+
constructor(globalMCPManager) {
|
|
558
|
+
this.globalMCPManager = globalMCPManager;
|
|
559
|
+
if (!globalMCPManager) {
|
|
560
|
+
throw new Error("\u5168\u5C40 MCPManager \u4E0D\u80FD\u4E3A\u7A7A");
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
static {
|
|
564
|
+
__name(this, "SharedMCPAdapter");
|
|
565
|
+
}
|
|
566
|
+
isInitialized = false;
|
|
567
|
+
/**
|
|
568
|
+
* 初始化适配器
|
|
569
|
+
*
|
|
570
|
+
* 注意:此方法不执行任何连接操作,仅标记初始化状态,因为全局 MCPManager 的生命周期由外部管理
|
|
571
|
+
*/
|
|
572
|
+
async initialize() {
|
|
573
|
+
if (this.isInitialized) {
|
|
574
|
+
return;
|
|
575
|
+
}
|
|
576
|
+
this.isInitialized = true;
|
|
577
|
+
}
|
|
578
|
+
/**
|
|
579
|
+
* 获取所有工具列表
|
|
580
|
+
*
|
|
581
|
+
* 从全局 MCPManager 获取工具列表,并转换为增强格式
|
|
582
|
+
*/
|
|
583
|
+
getAllTools() {
|
|
584
|
+
return this.globalMCPManager.getAllTools();
|
|
585
|
+
}
|
|
586
|
+
/**
|
|
587
|
+
* 调用工具
|
|
588
|
+
*
|
|
589
|
+
* 将工具调用委托给全局 MCPManager
|
|
590
|
+
*
|
|
591
|
+
* @param toolName - 工具名称(格式:serviceName__toolName)
|
|
592
|
+
* @param arguments_ - 工具调用参数
|
|
593
|
+
*/
|
|
594
|
+
async callTool(toolName, arguments_) {
|
|
595
|
+
return this.globalMCPManager.callTool(toolName, arguments_);
|
|
596
|
+
}
|
|
597
|
+
/**
|
|
598
|
+
* 清理资源
|
|
599
|
+
*
|
|
600
|
+
* 注意:此方法不会断开全局 MCPManager,由创建者负责管理
|
|
601
|
+
*/
|
|
602
|
+
async cleanup() {
|
|
603
|
+
this.isInitialized = false;
|
|
604
|
+
}
|
|
605
|
+
};
|
|
606
|
+
|
|
607
|
+
// src/manager.ts
|
|
580
608
|
var EndpointManager = class extends EventEmitter {
|
|
581
609
|
/**
|
|
582
610
|
* 构造函数
|
|
@@ -592,12 +620,65 @@ var EndpointManager = class extends EventEmitter {
|
|
|
592
620
|
}
|
|
593
621
|
endpoints = /* @__PURE__ */ new Map();
|
|
594
622
|
connectionStates = /* @__PURE__ */ new Map();
|
|
623
|
+
mcpManager = null;
|
|
624
|
+
sharedMCPAdapter = null;
|
|
625
|
+
mcpEventListeners = [];
|
|
595
626
|
/**
|
|
596
|
-
*
|
|
627
|
+
* 设置 MCPManager 实例
|
|
597
628
|
*
|
|
598
|
-
*
|
|
629
|
+
* 注意:MCPManager 的生命周期由外部管理,EndpointManager 不负责连接和断开
|
|
630
|
+
*
|
|
631
|
+
* @param mcpManager - 外部创建并已连接的 MCPManager 实例
|
|
632
|
+
*/
|
|
633
|
+
setMcpManager(mcpManager) {
|
|
634
|
+
if (this.sharedMCPAdapter) {
|
|
635
|
+
throw new Error("MCPManager \u5DF2\u7ECF\u8BBE\u7F6E\uFF0C\u4E0D\u80FD\u91CD\u590D\u8BBE\u7F6E");
|
|
636
|
+
}
|
|
637
|
+
this.mcpManager = mcpManager;
|
|
638
|
+
this.sharedMCPAdapter = new SharedMCPAdapter(mcpManager);
|
|
639
|
+
if ("on" in mcpManager && typeof mcpManager.on === "function") {
|
|
640
|
+
const connectedHandler = /* @__PURE__ */ __name((...args) => {
|
|
641
|
+
const data = args[0];
|
|
642
|
+
}, "connectedHandler");
|
|
643
|
+
const errorHandler = /* @__PURE__ */ __name((...args) => {
|
|
644
|
+
const data = args[0];
|
|
645
|
+
}, "errorHandler");
|
|
646
|
+
mcpManager.on("connected", connectedHandler);
|
|
647
|
+
mcpManager.on("error", errorHandler);
|
|
648
|
+
this.mcpEventListeners.push(connectedHandler, errorHandler);
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
/**
|
|
652
|
+
* 添加 Endpoint(支持 URL 字符串或 Endpoint 实例)
|
|
653
|
+
*
|
|
654
|
+
* @param endpoint - Endpoint URL 字符串或 Endpoint 实例
|
|
599
655
|
*/
|
|
600
656
|
addEndpoint(endpoint) {
|
|
657
|
+
if (typeof endpoint === "string") {
|
|
658
|
+
if (!this.sharedMCPAdapter) {
|
|
659
|
+
throw new Error(
|
|
660
|
+
"MCPManager \u672A\u8BBE\u7F6E\uFF0C\u8BF7\u5148\u8C03\u7528 setMcpManager() \u65B9\u6CD5\u8BBE\u7F6E MCPManager"
|
|
661
|
+
);
|
|
662
|
+
}
|
|
663
|
+
const endpointInstance = new Endpoint(
|
|
664
|
+
endpoint,
|
|
665
|
+
this.sharedMCPAdapter,
|
|
666
|
+
this.config?.defaultReconnectDelay
|
|
667
|
+
);
|
|
668
|
+
this.addEndpointInternal(endpointInstance);
|
|
669
|
+
return;
|
|
670
|
+
}
|
|
671
|
+
if (this.sharedMCPAdapter) {
|
|
672
|
+
throw new Error(
|
|
673
|
+
"[EndpointManager] \u5F53\u4F7F\u7528\u5171\u4EAB MCPManager \u65F6\uFF0C\u4E0D\u652F\u6301\u4F20\u5165\u81EA\u5B9A\u4E49 Endpoint \u5B9E\u4F8B\uFF0C\u8BF7\u4F20\u5165 Endpoint URL \u5B57\u7B26\u4E32\u4EE5\u4FBF\u7531 EndpointManager \u521B\u5EFA\u5B9E\u4F8B"
|
|
674
|
+
);
|
|
675
|
+
}
|
|
676
|
+
this.addEndpointInternal(endpoint);
|
|
677
|
+
}
|
|
678
|
+
/**
|
|
679
|
+
* 内部添加 Endpoint 方法
|
|
680
|
+
*/
|
|
681
|
+
addEndpointInternal(endpoint) {
|
|
601
682
|
const url = endpoint.getUrl();
|
|
602
683
|
if (this.endpoints.has(url)) {
|
|
603
684
|
return;
|
|
@@ -626,18 +707,38 @@ var EndpointManager = class extends EventEmitter {
|
|
|
626
707
|
this.emit("endpointRemoved", { endpoint: url });
|
|
627
708
|
}
|
|
628
709
|
/**
|
|
629
|
-
*
|
|
710
|
+
* 连接 Endpoint
|
|
711
|
+
*
|
|
712
|
+
* 注意:此方法不负责连接 MCPManager,MCPManager 必须在调用此方法前已连接
|
|
713
|
+
*
|
|
714
|
+
* @param endpoint - 可选,指定要连接的端点 URL。如果不传入,则连接所有端点
|
|
630
715
|
*/
|
|
631
|
-
async connect() {
|
|
716
|
+
async connect(endpoint) {
|
|
717
|
+
if (endpoint) {
|
|
718
|
+
const endpointInstance = this.endpoints.get(endpoint);
|
|
719
|
+
if (!endpointInstance) {
|
|
720
|
+
throw new Error(`\u63A5\u5165\u70B9\u4E0D\u5B58\u5728: ${sliceEndpoint(endpoint)}`);
|
|
721
|
+
}
|
|
722
|
+
const status = this.connectionStates.get(endpoint);
|
|
723
|
+
if (status?.connected) {
|
|
724
|
+
return;
|
|
725
|
+
}
|
|
726
|
+
await this.connectSingleEndpoint(endpoint, endpointInstance);
|
|
727
|
+
return;
|
|
728
|
+
}
|
|
632
729
|
const promises = [];
|
|
633
|
-
for (const [url,
|
|
730
|
+
for (const [url, endpoint2] of this.endpoints) {
|
|
731
|
+
const status = this.connectionStates.get(url);
|
|
732
|
+
if (status?.connected) {
|
|
733
|
+
continue;
|
|
734
|
+
}
|
|
634
735
|
promises.push(
|
|
635
|
-
this.connectSingleEndpoint(url,
|
|
636
|
-
const
|
|
637
|
-
if (
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
736
|
+
this.connectSingleEndpoint(url, endpoint2).catch((error) => {
|
|
737
|
+
const status2 = this.connectionStates.get(url);
|
|
738
|
+
if (status2) {
|
|
739
|
+
status2.connected = false;
|
|
740
|
+
status2.initialized = false;
|
|
741
|
+
status2.lastError = error instanceof Error ? error.message : String(error);
|
|
641
742
|
}
|
|
642
743
|
})
|
|
643
744
|
);
|
|
@@ -648,14 +749,31 @@ var EndpointManager = class extends EventEmitter {
|
|
|
648
749
|
).length;
|
|
649
750
|
}
|
|
650
751
|
/**
|
|
651
|
-
*
|
|
752
|
+
* 断开连接
|
|
753
|
+
*
|
|
754
|
+
* 注意:此方法不断开 MCPManager,MCPManager 的生命周期由外部管理
|
|
755
|
+
*
|
|
756
|
+
* @param endpoint - 可选,指定要断开的端点 URL。如果不传入,则断开所有端点
|
|
652
757
|
*/
|
|
653
|
-
async disconnect() {
|
|
758
|
+
async disconnect(endpoint) {
|
|
759
|
+
if (endpoint) {
|
|
760
|
+
const endpointInstance = this.endpoints.get(endpoint);
|
|
761
|
+
if (!endpointInstance) {
|
|
762
|
+
throw new Error(`\u63A5\u5165\u70B9\u4E0D\u5B58\u5728: ${sliceEndpoint(endpoint)}`);
|
|
763
|
+
}
|
|
764
|
+
endpointInstance.disconnect();
|
|
765
|
+
const status = this.connectionStates.get(endpoint);
|
|
766
|
+
if (status) {
|
|
767
|
+
status.connected = false;
|
|
768
|
+
status.initialized = false;
|
|
769
|
+
}
|
|
770
|
+
return;
|
|
771
|
+
}
|
|
654
772
|
const promises = [];
|
|
655
|
-
for (const
|
|
773
|
+
for (const endpoint2 of this.endpoints.values()) {
|
|
656
774
|
promises.push(
|
|
657
775
|
Promise.resolve().then(() => {
|
|
658
|
-
|
|
776
|
+
endpoint2.disconnect();
|
|
659
777
|
})
|
|
660
778
|
);
|
|
661
779
|
}
|
|
@@ -713,6 +831,18 @@ var EndpointManager = class extends EventEmitter {
|
|
|
713
831
|
getEndpointStatus(url) {
|
|
714
832
|
return this.connectionStates.get(url);
|
|
715
833
|
}
|
|
834
|
+
/**
|
|
835
|
+
* 重连
|
|
836
|
+
*
|
|
837
|
+
* @param endpoint - 可选,指定要重连的端点 URL。如果不传入,则重连所有端点
|
|
838
|
+
* @param delay - 可选,disconnect 和 connect 之间的等待时间(毫秒),默认为 1000ms。
|
|
839
|
+
* 注意:此参数只控制断开和重新连接之间的等待时间,不影响底层 Endpoint 实例的重连延迟
|
|
840
|
+
*/
|
|
841
|
+
async reconnect(endpoint, delay = 1e3) {
|
|
842
|
+
await this.disconnect(endpoint);
|
|
843
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
844
|
+
await this.connect(endpoint);
|
|
845
|
+
}
|
|
716
846
|
/**
|
|
717
847
|
* 重连所有端点
|
|
718
848
|
*/
|
|
@@ -740,6 +870,8 @@ var EndpointManager = class extends EventEmitter {
|
|
|
740
870
|
}
|
|
741
871
|
/**
|
|
742
872
|
* 清除所有端点
|
|
873
|
+
*
|
|
874
|
+
* 注意:此方法不会清理 MCPManager,MCPManager 的生命周期由外部管理
|
|
743
875
|
*/
|
|
744
876
|
async clearEndpoints() {
|
|
745
877
|
await this.disconnect();
|
|
@@ -748,8 +880,17 @@ var EndpointManager = class extends EventEmitter {
|
|
|
748
880
|
}
|
|
749
881
|
/**
|
|
750
882
|
* 清理资源
|
|
883
|
+
*
|
|
884
|
+
* 注意:此方法不会清理 MCPManager,MCPManager 的生命周期由外部管理
|
|
751
885
|
*/
|
|
752
886
|
async cleanup() {
|
|
887
|
+
if (this.mcpManager && "removeListener" in this.mcpManager && typeof this.mcpManager.removeListener === "function") {
|
|
888
|
+
for (const listener of this.mcpEventListeners) {
|
|
889
|
+
this.mcpManager.removeListener("connected", listener);
|
|
890
|
+
this.mcpManager.removeListener("error", listener);
|
|
891
|
+
}
|
|
892
|
+
}
|
|
893
|
+
this.mcpEventListeners = [];
|
|
753
894
|
await this.clearEndpoints();
|
|
754
895
|
}
|
|
755
896
|
// ==================== 私有方法 ====================
|
|
@@ -788,12 +929,16 @@ export {
|
|
|
788
929
|
ConnectionState,
|
|
789
930
|
Endpoint,
|
|
790
931
|
EndpointManager,
|
|
932
|
+
SharedMCPAdapter,
|
|
791
933
|
ToolCallError,
|
|
792
934
|
ToolCallErrorCode,
|
|
935
|
+
decodeJWTToken,
|
|
793
936
|
deepMerge,
|
|
794
937
|
ensureToolJSONSchema,
|
|
938
|
+
extractTokenFromUrl,
|
|
795
939
|
formatErrorMessage,
|
|
796
940
|
isValidEndpointUrl,
|
|
941
|
+
parseEndpointUrl,
|
|
797
942
|
sleep,
|
|
798
943
|
sliceEndpoint,
|
|
799
944
|
validateToolCallParams
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/endpoint.ts","../src/types.ts","../src/utils.ts","../src/internal-mcp-manager.ts","../src/manager.ts"],"sourcesContent":["/**\n * Endpoint 类(新 API)\n * 管理单个小智接入点的 WebSocket 连接\n * 实现 MCP (Model Context Protocol) 协议通信\n *\n * 与 EndpointConnection 的区别:\n * - 构造函数直接传入 mcpServers 配置,不需要单独调用 setServiceManager()\n * - 配置更简洁直观\n */\n\nimport type { Tool } from \"@modelcontextprotocol/sdk/types.js\";\nimport WebSocket from \"ws\";\nimport type { ExtendedMCPMessage, MCPMessage } from \"./mcp.js\";\nimport type {\n EndpointConfig,\n EndpointConnectionStatus,\n ToolCallResult,\n} from \"./types.js\";\nimport {\n ConnectionState,\n ToolCallError as ToolCallErrorClass,\n ToolCallErrorCode as ToolCallErrorCodeEnum,\n ensureToolJSONSchema as ensureToolJSONSchemaFn,\n} from \"./types.js\";\nimport { validateToolCallParams } from \"./utils.js\";\nimport { sliceEndpoint } from \"./utils.js\";\nimport { InternalMCPManagerAdapter } from \"./internal-mcp-manager.js\";\n\n// 导出错误类型供外部使用\nexport {\n ToolCallErrorCodeEnum as ToolCallErrorCode,\n ToolCallErrorClass as ToolCallError,\n};\n\n/**\n * Endpoint 类\n * 负责管理单个小智接入点的 WebSocket 连接\n * 使用新的配置方式:直接在构造函数中传入 mcpServers 配置\n */\nexport class Endpoint {\n private endpointUrl: string;\n private ws: WebSocket | null = null;\n private connectionStatus = false;\n private serverInitialized = false;\n private mcpAdapter: InternalMCPManagerAdapter;\n\n // 连接状态管理\n private connectionState: ConnectionState = ConnectionState.DISCONNECTED;\n\n // 最后一次错误信息\n private lastError: string | null = null;\n\n // 连接超时定时器\n private connectionTimeout: NodeJS.Timeout | null = null;\n\n // 工具调用超时配置\n private toolCallTimeout = 30000;\n private reconnectDelay: number; // 重连延迟(毫秒)\n\n /**\n * 构造函数\n *\n * @param endpointUrl - 小智接入点 URL\n * @param config - Endpoint 配置(包含 mcpServers)\n */\n constructor(endpointUrl: string, config: EndpointConfig) {\n this.endpointUrl = endpointUrl;\n this.reconnectDelay = config.reconnectDelay ?? 2000;\n\n // 创建内部 MCP 适配器\n this.mcpAdapter = new InternalMCPManagerAdapter(config);\n }\n\n /**\n * 获取 Endpoint URL\n */\n getUrl(): string {\n return this.endpointUrl;\n }\n\n /**\n * 获取当前所有工具列表\n */\n getTools(): Tool[] {\n try {\n const allTools = this.mcpAdapter.getAllTools();\n\n return allTools.map((toolInfo) => ({\n name: toolInfo.name,\n description: toolInfo.description,\n inputSchema: ensureToolJSONSchemaFn(toolInfo.inputSchema),\n }));\n } catch (error) {\n console.error(\n `获取工具列表失败: ${error instanceof Error ? error.message : String(error)}`\n );\n return [];\n }\n }\n\n /**\n * 连接小智接入点\n */\n public async connect(): Promise<void> {\n // 初始化 MCP 适配器\n await this.mcpAdapter.initialize();\n\n // 如果正在连接中,等待当前连接完成\n if (this.connectionState === ConnectionState.CONNECTING) {\n throw new Error(\"连接正在进行中,请等待连接完成\");\n }\n\n // 清理之前的连接\n this.cleanupConnection();\n\n return this.attemptConnection();\n }\n\n /**\n * 尝试建立连接\n */\n private async attemptConnection(): Promise<void> {\n this.connectionState = ConnectionState.CONNECTING;\n console.debug(`正在连接小智接入点: ${sliceEndpoint(this.endpointUrl)}`);\n\n return new Promise((resolve, reject) => {\n // 设置连接超时\n this.connectionTimeout = setTimeout(() => {\n const error = new Error(\"连接超时 (10000ms)\");\n this.handleConnectionError(error);\n reject(error);\n }, 10000);\n\n this.ws = new WebSocket(this.endpointUrl);\n\n this.ws.on(\"open\", () => {\n this.handleConnectionSuccess();\n resolve();\n });\n\n this.ws.on(\"message\", (data) => {\n try {\n const message: MCPMessage = JSON.parse(data.toString());\n this.handleMessage(message);\n } catch (error) {\n console.error(\"MCP 消息解析错误:\", error);\n }\n });\n\n this.ws.on(\"close\", (code, reason) => {\n this.handleConnectionClose(code, reason.toString());\n });\n\n this.ws.on(\"error\", (error) => {\n this.handleConnectionError(error);\n reject(error);\n });\n });\n }\n\n /**\n * 处理连接成功\n */\n private handleConnectionSuccess(): void {\n // 清理连接超时定时器\n if (this.connectionTimeout) {\n clearTimeout(this.connectionTimeout);\n this.connectionTimeout = null;\n }\n\n this.connectionStatus = true;\n this.connectionState = ConnectionState.CONNECTED;\n\n console.debug(\"MCP WebSocket 连接已建立\");\n }\n\n /**\n * 处理连接错误\n */\n private handleConnectionError(error: Error): void {\n // 记录最后一次错误信息\n this.lastError = error.message;\n\n // 清理连接超时定时器\n if (this.connectionTimeout) {\n clearTimeout(this.connectionTimeout);\n this.connectionTimeout = null;\n }\n\n console.error(\"MCP WebSocket 错误:\", error.message);\n\n // 清理当前连接\n this.cleanupConnection();\n }\n\n /**\n * 处理连接关闭\n */\n private handleConnectionClose(code: number, reason: string): void {\n this.connectionStatus = false;\n this.serverInitialized = false;\n this.connectionState = ConnectionState.DISCONNECTED;\n console.info(`小智连接已关闭 (代码: ${code}, 原因: ${reason})`);\n }\n\n /**\n * 清理连接资源\n */\n private cleanupConnection(): void {\n // 清理 WebSocket\n if (this.ws) {\n this.ws.removeAllListeners();\n\n try {\n if (this.ws.readyState === WebSocket.OPEN) {\n this.ws.close(1000, \"Cleaning up connection\");\n } else if (this.ws.readyState === WebSocket.CONNECTING) {\n this.ws.terminate();\n }\n } catch (error) {\n console.debug(\"WebSocket 关闭时出现错误(已忽略):\", error);\n }\n\n this.ws = null;\n }\n\n // 清理连接超时定时器\n if (this.connectionTimeout) {\n clearTimeout(this.connectionTimeout);\n this.connectionTimeout = null;\n }\n\n // 重置连接状态\n this.connectionStatus = false;\n this.serverInitialized = false;\n\n // 重置连接状态为已断开\n this.connectionState = ConnectionState.DISCONNECTED;\n }\n\n /**\n * 处理 MCP 消息\n */\n private handleMessage(message: MCPMessage): void {\n console.debug(\"收到 MCP 消息:\", JSON.stringify(message, null, 2));\n\n if (!message.method) {\n console.debug(\"收到没有 method 字段的消息,忽略\");\n return;\n }\n\n switch (message.method) {\n case \"initialize\":\n case \"notifications/initialized\":\n this.sendResponse(message.id, {\n protocolVersion: \"2024-11-05\",\n capabilities: {\n tools: { listChanged: true },\n logging: {},\n },\n serverInfo: {\n name: \"xiaozhi-mcp-server\",\n version: \"1.0.0\",\n },\n });\n this.serverInitialized = true;\n console.debug(\"MCP 服务器初始化完成\");\n break;\n\n case \"tools/list\": {\n const toolsList = this.getTools();\n this.sendResponse(message.id, { tools: toolsList });\n console.debug(`MCP 工具列表已发送 (${toolsList.length}个工具)`);\n break;\n }\n\n case \"tools/call\": {\n this.handleToolCall(message).catch((error) => {\n console.error(\"处理工具调用时发生未捕获错误:\", error);\n });\n break;\n }\n\n case \"ping\":\n this.sendResponse(message.id, {});\n console.debug(\"回应 MCP ping 消息\");\n break;\n\n default:\n console.warn(`未知的 MCP 请求: ${message.method}`);\n }\n }\n\n /**\n * 发送响应消息\n */\n private sendResponse(id: number | string, result: unknown): void {\n console.debug(\n `尝试发送响应: id=${id}, isConnected=${this.connectionStatus}, wsReadyState=${this.ws?.readyState}`\n );\n\n if (this.connectionStatus && this.ws?.readyState === WebSocket.OPEN) {\n const response: ExtendedMCPMessage = {\n jsonrpc: \"2.0\",\n id,\n result,\n };\n\n try {\n this.ws.send(JSON.stringify(response));\n console.debug(\"响应已发送\", {\n id,\n responseSize: JSON.stringify(response).length,\n });\n } catch (error) {\n console.error(\"发送响应失败\", {\n id,\n error,\n });\n }\n } else {\n console.error(\"无法发送响应\", {\n id,\n isConnected: this.connectionStatus,\n wsReadyState: this.ws?.readyState,\n });\n }\n }\n\n /**\n * 获取服务器状态\n */\n public getStatus(): EndpointConnectionStatus {\n const availableTools = this.mcpAdapter.getAllTools().length;\n\n return {\n connected: this.connectionStatus,\n initialized: this.serverInitialized,\n url: this.endpointUrl,\n availableTools,\n connectionState: this.connectionState,\n lastError: this.lastError,\n };\n }\n\n /**\n * 检查连接状态\n */\n public isConnected(): boolean {\n return this.connectionStatus;\n }\n\n /**\n * 主动断开小智连接\n */\n public async disconnect(): Promise<void> {\n console.info(\"主动断开小智连接\");\n\n // 清理 MCP 适配器\n await this.mcpAdapter.cleanup();\n\n // 清理 WebSocket 连接\n this.cleanupConnection();\n }\n\n /**\n * 重连小智接入点\n */\n public async reconnect(): Promise<void> {\n console.info(`重连小智接入点: ${sliceEndpoint(this.endpointUrl)}`);\n\n // 先断开连接\n this.disconnect();\n\n // 等待可配置的时间确保连接完全断开\n await new Promise((resolve) => setTimeout(resolve, this.reconnectDelay));\n\n // 重新连接\n await this.connect();\n }\n\n /**\n * 处理工具调用请求\n */\n private async handleToolCall(request: MCPMessage): Promise<void> {\n if (request.id === undefined || request.id === null) {\n throw new ToolCallErrorClass(\n ToolCallErrorCodeEnum.INVALID_PARAMS,\n \"请求 ID 不能为空\"\n );\n }\n\n const requestId = request.id;\n const startTime = Date.now();\n\n try {\n const params = validateToolCallParams(request.params);\n\n console.info(\"开始处理工具调用\", {\n requestId,\n toolName: params.name,\n hasArguments: !!params.arguments,\n });\n\n const result = await this.executeToolWithTimeout(\n params.name,\n params.arguments || {},\n this.toolCallTimeout\n );\n\n this.sendResponse(requestId, {\n content: result.content || [\n { type: \"text\", text: JSON.stringify(result) },\n ],\n isError: result.isError || false,\n });\n\n console.info(\"工具调用成功\", {\n requestId,\n toolName: params.name,\n duration: `${Date.now() - startTime}ms`,\n });\n } catch (error) {\n this.handleToolCallError(error, requestId, Date.now() - startTime);\n }\n }\n\n /**\n * 带超时控制的工具执行\n */\n private async executeToolWithTimeout(\n toolName: string,\n arguments_: Record<string, unknown>,\n timeoutMs = 30000\n ): Promise<ToolCallResult> {\n return new Promise((resolve, reject) => {\n const timeoutId = setTimeout(() => {\n reject(\n new ToolCallErrorClass(\n ToolCallErrorCodeEnum.TIMEOUT,\n `工具调用超时 (${timeoutMs}ms): ${toolName}`\n )\n );\n }, timeoutMs);\n\n this.mcpAdapter\n .callTool(toolName, arguments_)\n .then((result: ToolCallResult) => {\n clearTimeout(timeoutId);\n resolve(result);\n })\n .catch((error: unknown) => {\n clearTimeout(timeoutId);\n\n const errorMessage =\n error instanceof Error ? error.message : String(error);\n\n if (errorMessage.includes(\"未找到工具\")) {\n reject(\n new ToolCallErrorClass(\n ToolCallErrorCodeEnum.TOOL_NOT_FOUND,\n `工具不存在: ${toolName}`\n )\n );\n } else {\n reject(\n new ToolCallErrorClass(\n ToolCallErrorCodeEnum.TOOL_EXECUTION_ERROR,\n `工具执行失败: ${errorMessage}`\n )\n );\n }\n });\n });\n }\n\n /**\n * 处理工具调用错误\n */\n private handleToolCallError(\n error: unknown,\n requestId: string | number | undefined,\n duration: number\n ): void {\n let errorResponse: {\n code: number;\n message: string;\n data?: unknown;\n };\n\n if (error instanceof ToolCallErrorClass) {\n errorResponse = {\n code: error.code,\n message: error.message,\n data: error.data,\n };\n } else {\n const errorMessage = error instanceof Error ? error.message : \"未知错误\";\n errorResponse = {\n code: ToolCallErrorCodeEnum.TOOL_EXECUTION_ERROR,\n message: errorMessage,\n data: { originalError: String(error) || \"null\" },\n };\n }\n\n this.sendErrorResponse(requestId, errorResponse);\n\n console.error(\"工具调用失败\", {\n requestId,\n duration: `${duration}ms`,\n error: errorResponse,\n });\n }\n\n /**\n * 发送错误响应\n */\n private sendErrorResponse(\n id: string | number | undefined,\n error: { code: number; message: string; data?: unknown }\n ): void {\n if (this.connectionStatus && this.ws?.readyState === WebSocket.OPEN) {\n const response = {\n jsonrpc: \"2.0\",\n id,\n error,\n };\n this.ws.send(JSON.stringify(response));\n console.debug(\"已发送错误响应:\", response);\n }\n }\n}\n","// =========================\n// 1. 工具调用相关类型\n// =========================\n\n/**\n * 工具调用结果接口\n */\nexport interface ToolCallResult {\n content: Array<{\n type: string;\n text: string;\n }>;\n isError?: boolean;\n}\n\n/**\n * 工具调用参数接口\n */\nexport interface ToolCallParams {\n name: string;\n arguments?: Record<string, unknown>;\n}\n\n/**\n * 验证后的工具调用参数\n */\nexport interface ValidatedToolCallParams {\n name: string;\n arguments?: Record<string, unknown>;\n}\n\n/**\n * 工具调用错误码枚举\n */\nexport enum ToolCallErrorCode {\n /** 无效参数 */\n INVALID_PARAMS = -32602,\n /** 工具不存在 */\n TOOL_NOT_FOUND = -32601,\n /** 服务不可用 */\n SERVICE_UNAVAILABLE = -32001,\n /** 调用超时 */\n TIMEOUT = -32002,\n /** 工具执行错误 */\n TOOL_EXECUTION_ERROR = -32000,\n}\n\n/**\n * 工具调用错误类\n */\nexport class ToolCallError extends Error {\n constructor(\n public code: ToolCallErrorCode,\n message: string,\n public data?: unknown\n ) {\n super(message);\n this.name = \"ToolCallError\";\n }\n}\n\n// =========================\n// 2. JSON Schema 类型\n// =========================\n\n/**\n * JSON Schema 类型定义\n * 兼容 MCP SDK 的 JSON Schema 格式\n */\nexport type JSONSchema =\n | (Record<string, unknown> & {\n type: \"object\";\n properties?: Record<string, unknown>;\n required?: string[];\n additionalProperties?: boolean;\n })\n | Record<string, unknown>;\n\n/**\n * 确保对象符合 MCP Tool JSON Schema 格式\n * 返回类型兼容 MCP SDK 的 Tool 类型\n */\nexport function ensureToolJSONSchema(schema: JSONSchema): {\n type: \"object\";\n properties?: Record<string, object>;\n required?: string[];\n additionalProperties?: boolean;\n} {\n if (\n typeof schema === \"object\" &&\n schema !== null &&\n \"type\" in schema &&\n schema.type === \"object\"\n ) {\n return schema as {\n type: \"object\";\n properties?: Record<string, object>;\n required?: string[];\n additionalProperties?: boolean;\n };\n }\n\n // 如果不符合标准格式,返回默认的空对象 schema\n return {\n type: \"object\",\n properties: {},\n required: [],\n additionalProperties: true,\n };\n}\n\n// =========================\n// 3. 工具信息类型\n// =========================\n\n/**\n * 增强的工具信息接口\n * 包含工具的启用状态和使用统计信息\n */\nexport interface EnhancedToolInfo {\n /** 工具唯一标识符,格式为 \"{serviceName}__{originalName}\" */\n name: string;\n\n /** 工具描述信息 */\n description: string;\n\n /** 工具输入参数的 JSON Schema 定义 */\n inputSchema: JSONSchema;\n\n /** 工具所属的 MCP 服务名称 */\n serviceName: string;\n\n /** 工具在 MCP 服务中的原始名称 */\n originalName: string;\n\n /** 工具是否启用 */\n enabled: boolean;\n\n /** 工具使用次数统计 */\n usageCount: number;\n\n /** 工具最后使用时间 (ISO 8601 格式字符串) */\n lastUsedTime: string;\n}\n\n/**\n * MCP 服务管理器接口\n * 用于工具调用,避免循环依赖\n */\nexport interface IMCPServiceManager {\n /** 获取所有工具列表 */\n getAllTools(): EnhancedToolInfo[];\n\n /** 调用工具 */\n callTool(\n toolName: string,\n arguments_: Record<string, unknown>\n ): Promise<ToolCallResult>;\n}\n\n// =========================\n// 4. 连接状态类型\n// =========================\n\n/**\n * 连接状态枚举\n */\nexport enum ConnectionState {\n DISCONNECTED = \"disconnected\",\n CONNECTING = \"connecting\",\n CONNECTED = \"connected\",\n FAILED = \"failed\",\n}\n\n/**\n * 连接选项接口\n */\nexport interface ConnectionOptions {\n /** 连接超时时间(毫秒),默认 10000 */\n connectionTimeout?: number;\n /** 重连延迟时间(毫秒),默认 2000 */\n reconnectDelay?: number;\n}\n\n// =========================\n// 5. EndpointConnection 状态类型\n// =========================\n\n/**\n * EndpointConnection 状态接口\n */\nexport interface EndpointConnectionStatus {\n /** 是否已连接 */\n connected: boolean;\n /** 是否已初始化 */\n initialized: boolean;\n /** 接入点 URL */\n url: string;\n /** 可用工具数量 */\n availableTools: number;\n /** 连接状态 */\n connectionState: ConnectionState;\n /** 最后一次错误信息 */\n lastError: string | null;\n}\n\n// =========================\n// 6. EndpointManager 状态类型\n// =========================\n\n/**\n * 简单连接状态接口\n */\nexport interface SimpleConnectionStatus {\n /** 接入点地址 */\n endpoint: string;\n /** 是否已连接 */\n connected: boolean;\n /** 是否已初始化 */\n initialized: boolean;\n /** 最后连接时间 */\n lastConnected?: Date;\n /** 最后错误信息 */\n lastError?: string;\n}\n\n/**\n * 完整连接状态接口(扩展 SimpleConnectionStatus)\n */\nexport interface ConnectionStatus extends SimpleConnectionStatus {\n // 扩展字段可以在这里添加\n}\n\n/**\n * 配置变更事件类型\n */\nexport interface ConfigChangeEvent {\n type:\n | \"endpoints_added\"\n | \"endpoints_removed\"\n | \"endpoints_updated\"\n | \"options_updated\";\n data: {\n added?: string[];\n removed?: string[];\n updated?: string[];\n oldOptions?: Partial<ConnectionOptions>;\n newOptions?: Partial<ConnectionOptions>;\n };\n timestamp: Date;\n}\n\n/**\n * 重连结果接口\n */\nexport interface ReconnectResult {\n successCount: number;\n failureCount: number;\n results: Array<{\n endpoint: string;\n success: boolean;\n error?: string;\n }>;\n}\n\n// =========================\n// 7. 新 API 配置类型\n// =========================\n\n/**\n * MCP 服务器配置类型\n * 支持三种配置方式:\n * 1. 本地命令 (stdio): { command: string; args: string[]; env?: Record<string, string> }\n * 2. SSE: { type: \"sse\"; url: string; headers?: Record<string, string> }\n * 3. Streamable HTTP: { type?: \"streamable-http\"; url: string; headers?: Record<string, string> }\n */\nexport type MCPServerConfig =\n | LocalMCPServerConfig\n | SSEMCPServerConfig\n | StreamableHTTPMCPServerConfig;\n\n/**\n * 本地 MCP 服务器配置\n */\nexport interface LocalMCPServerConfig {\n command: string;\n args: string[];\n env?: Record<string, string>;\n}\n\n/**\n * SSE MCP 服务器配置\n */\nexport interface SSEMCPServerConfig {\n type: \"sse\";\n url: string;\n headers?: Record<string, string>;\n}\n\n/**\n * Streamable HTTP MCP 服务器配置\n */\nexport interface StreamableHTTPMCPServerConfig {\n type?: \"streamable-http\"; // 可选,默认就是 streamable-http\n url: string;\n headers?: Record<string, string>;\n}\n\n/**\n * Endpoint 配置接口\n * 用于新 API:直接在构造函数中传入 MCP 服务器配置\n */\nexport interface EndpointConfig {\n /** MCP 服务器配置(声明式) */\n mcpServers: Record<string, MCPServerConfig>;\n /** 可选:重连延迟(毫秒),默认 2000 */\n reconnectDelay?: number;\n /** 可选:ModelScope API Key(全局) */\n modelscopeApiKey?: string;\n}\n\n/**\n * EndpointManager 配置接口(新 API 简化版)\n */\nexport interface EndpointManagerConfig {\n /** 可选:默认重连延迟(毫秒) */\n defaultReconnectDelay?: number;\n}\n","/**\n * 工具函数模块\n *\n * 内联的必要工具函数,确保包的独立性\n */\n\nimport type { ValidatedToolCallParams } from \"./types.js\";\n\n/**\n * 截断端点 URL 用于日志显示\n *\n * @param endpoint - 完整的端点 URL\n * @returns 截断后的 URL\n *\n * @example\n * ```typescript\n * sliceEndpoint(\"ws://very-long-endpoint-url-here.example.com/endpoint\")\n * // 返回: \"ws://very-long-endpoint-u...e.com/endpoint\"\n * ```\n */\nexport function sliceEndpoint(endpoint: string): string {\n return `${endpoint.slice(0, 30)}...${endpoint.slice(-10)}`;\n}\n\n/**\n * 验证工具调用参数\n *\n * @param params - 待验证的参数\n * @returns 验证后的参数\n * @throws {Error} 如果参数无效\n *\n * @example\n * ```typescript\n * const params = validateToolCallParams({\n * name: \"test_tool\",\n * arguments: { foo: \"bar\" }\n * });\n * ```\n */\nexport function validateToolCallParams(\n params: unknown\n): ValidatedToolCallParams {\n // 基础类型检查\n if (!params || typeof params !== \"object\") {\n throw new Error(\"工具调用参数必须是对象\");\n }\n\n const p = params as Record<string, unknown>;\n\n // 验证工具名称\n if (!p.name || typeof p.name !== \"string\") {\n throw new Error(\"工具名称必须是字符串\");\n }\n\n // 构建验证后的参数\n const validated: ValidatedToolCallParams = {\n name: p.name,\n };\n\n // 验证参数(可选)\n if (p.arguments !== undefined) {\n if (typeof p.arguments !== \"object\" || p.arguments === null) {\n throw new Error(\"工具参数必须是对象\");\n }\n validated.arguments = p.arguments as Record<string, unknown>;\n }\n\n return validated;\n}\n\n/**\n * 验证端点 URL 格式\n *\n * @param endpoint - 待验证的端点 URL\n * @returns 是否为有效的 WebSocket URL\n */\nexport function isValidEndpointUrl(endpoint: string): boolean {\n if (!endpoint || typeof endpoint !== \"string\") {\n return false;\n }\n\n if (!endpoint.startsWith(\"ws://\") && !endpoint.startsWith(\"wss://\")) {\n return false;\n }\n\n try {\n new URL(endpoint);\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * 检查属性名是否安全,防止原型污染攻击\n *\n * @param key - 待检查的属性名\n * @returns 是否为安全的属性名\n *\n * @example\n * ```typescript\n * isSafeKey(\"name\") // true\n * isSafeKey(\"__proto__\") // false\n * isSafeKey(\"constructor\") // false\n * ```\n */\nfunction isSafeKey(key: string): boolean {\n // 拒绝已知的危险属性名\n const dangerousKeys = [\"__proto__\", \"constructor\", \"prototype\"];\n if (dangerousKeys.includes(key)) {\n return false;\n }\n\n // 确保属性名是普通的字符串(不是 Symbol 等)\n return typeof key === \"string\" && key.length > 0;\n}\n\n/**\n * 深度合并对象\n *\n * @param target - 目标对象\n * @param sources - 源对象\n * @returns 合并后的对象\n *\n * @example\n * ```typescript\n * const result = deepMerge(\n * { a: 1, b: { x: 1 } },\n * { b: { y: 2 }, c: 3 }\n * );\n * // 返回: { a: 1, b: { x: 1, y: 2 }, c: 3 }\n * ```\n */\nexport function deepMerge<T>(\n target: Partial<T>,\n ...sources: Array<Partial<T>>\n): T {\n if (sources.length === 0) {\n return target as T;\n }\n\n const source = sources.shift();\n\n if (source === undefined) {\n return target as T;\n }\n\n // 使用 Object.keys() 替代 for...in,只遍历对象自身的可枚举属性\n const keys = Object.keys(source as Record<string, unknown>);\n\n for (const key of keys) {\n // 跳过不安全的 key,防止原型污染攻击\n if (!isSafeKey(key)) {\n continue;\n }\n\n const sourceValue = (source as Record<string, unknown>)[key];\n const targetValue = (target as Record<string, unknown>)[key];\n\n if (\n typeof sourceValue === \"object\" &&\n sourceValue !== null &&\n !Array.isArray(sourceValue) &&\n typeof targetValue === \"object\" &&\n targetValue !== null &&\n !Array.isArray(targetValue)\n ) {\n (target as Record<string, unknown>)[key] = deepMerge(\n targetValue as Record<string, unknown>,\n sourceValue as Record<string, unknown>\n );\n } else {\n (target as Record<string, unknown>)[key] = sourceValue;\n }\n }\n\n return deepMerge(target, ...sources);\n}\n\n/**\n * 延迟执行\n *\n * @param ms - 延迟时间(毫秒)\n * @returns Promise\n */\nexport function sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\n/**\n * 格式化错误消息\n *\n * @param error - 错误对象\n * @returns 格式化后的错误消息\n */\nexport function formatErrorMessage(error: unknown): string {\n if (error instanceof Error) {\n return error.message;\n }\n\n if (typeof error === \"string\") {\n return error;\n }\n\n return String(error);\n}\n","/**\n * 内部 MCP 服务管理器适配器\n * 将 mcpServers 配置转换为 IMCPServiceManager 接口\n * 使用 @xiaozhi-client/mcp-core 的 MCPManager 实现真实的 MCP 功能\n */\n\nimport { MCPManager } from \"@xiaozhi-client/mcp-core\";\nimport type { MCPServiceConfig } from \"@xiaozhi-client/mcp-core\";\nimport type { EnhancedToolInfo, ToolCallResult } from \"./types.js\";\nimport type { IMCPServiceManager } from \"./types.js\";\nimport type { EndpointConfig, MCPServerConfig } from \"./types.js\";\nimport { convertLegacyToNew } from \"@xiaozhi-client/config\";\n\n/**\n * 内部 MCP 服务管理器适配器\n * 实现 IMCPServiceManager 接口,使用真正的 MCPManager\n */\nexport class InternalMCPManagerAdapter implements IMCPServiceManager {\n private mcpManager: MCPManager;\n private tools: Map<string, EnhancedToolInfo> = new Map();\n private isInitialized = false;\n\n constructor(private config: EndpointConfig) {\n this.mcpManager = new MCPManager();\n\n // 转换配置并添加到 MCPManager\n for (const [serviceName, serverConfig] of Object.entries(\n config.mcpServers\n )) {\n const mcpConfig = this.convertToMCPServiceConfig(serviceName, serverConfig);\n this.mcpManager.addServer(serviceName, mcpConfig);\n }\n\n // 设置事件监听\n this.mcpManager.on(\"connected\", (data) => {\n console.info(\n `MCP 服务 ${data.serverName} 已连接,工具数: ${data.tools.length}`\n );\n });\n\n this.mcpManager.on(\"error\", (data) => {\n console.error(`MCP 服务 ${data.serverName} 出错:`, data.error);\n });\n }\n\n /**\n * 初始化并启动所有 MCP 服务\n */\n async initialize(): Promise<void> {\n if (this.isInitialized) {\n return;\n }\n\n // 连接所有 MCP 服务\n await this.mcpManager.connect();\n\n // 刷新工具列表\n await this.refreshTools();\n\n this.isInitialized = true;\n }\n\n /**\n * 获取所有工具列表\n */\n getAllTools(): EnhancedToolInfo[] {\n return Array.from(this.tools.values());\n }\n\n /**\n * 调用工具(真实实现)\n */\n async callTool(\n toolName: string,\n arguments_: Record<string, unknown>\n ): Promise<ToolCallResult> {\n // 解析工具名称:serviceName__toolName\n const [serviceName, actualToolName] = this.parseToolName(toolName);\n\n // 调用真实的 MCP 工具\n return this.mcpManager.callTool(serviceName, actualToolName, arguments_);\n }\n\n /**\n * 清理资源\n */\n async cleanup(): Promise<void> {\n await this.mcpManager.disconnect();\n this.tools.clear();\n this.isInitialized = false;\n }\n\n /**\n * 刷新工具列表\n */\n private async refreshTools(): Promise<void> {\n this.tools.clear();\n\n const mcpTools = this.mcpManager.listTools();\n\n for (const mcpTool of mcpTools) {\n const enhancedTool: EnhancedToolInfo = {\n name: `${mcpTool.serverName}__${mcpTool.name}`,\n description: mcpTool.description,\n inputSchema: mcpTool.inputSchema as any,\n serviceName: mcpTool.serverName,\n originalName: mcpTool.name,\n enabled: true,\n usageCount: 0,\n lastUsedTime: new Date().toISOString(),\n };\n this.tools.set(enhancedTool.name, enhancedTool);\n }\n }\n\n /**\n * 解析工具名称\n */\n private parseToolName(toolName: string): [string, string] {\n const parts = toolName.split(\"__\");\n if (parts.length < 2) {\n throw new Error(`无效的工具名称格式: ${toolName}`);\n }\n const serviceName = parts[0];\n const actualToolName = parts.slice(1).join(\"__\"); // 支持工具名中包含 __\n return [serviceName, actualToolName];\n }\n\n /**\n * 将 MCPServerConfig 转换为 MCPServiceConfig\n * 使用统一的配置适配器,确保路径解析逻辑一致\n */\n private convertToMCPServiceConfig(\n serviceName: string,\n config: MCPServerConfig\n ): Omit<MCPServiceConfig, \"name\"> {\n // 使用统一的转换函数,自动处理相对路径解析\n // convertLegacyToNew 会:\n // 1. 解析 command 中的相对路径(相对于配置文件目录)\n // 2. 解析 args 中的相对路径\n // 3. 返回带有 name 字段的完整配置\n const converted = convertLegacyToNew(serviceName, config);\n\n // 移除 name 字段(因为 InternalMCPManagerAdapter 不需要)\n const { name, ...rest } = converted;\n\n return rest;\n }\n}\n","/**\n * EndpointManager(新 API)\n * 管理多个小智接入点的连接,每个小智接入点独立运行\n *\n * 与旧 EndpointManager 的区别:\n * - 使用 addEndpoint(endpoint) 添加 Endpoint 实例\n * - 不需要 initialize() 和 setServiceManager()\n * - 配置更简洁直观\n */\n\nimport { EventEmitter } from \"node:events\";\nimport type { Endpoint } from \"./endpoint.js\";\nimport type { EndpointManagerConfig, SimpleConnectionStatus } from \"./types.js\";\nimport { sliceEndpoint } from \"./utils.js\";\n\n/**\n * 小智接入点管理器(新 API)\n * 负责管理多个小智接入点的连接\n */\nexport class EndpointManager extends EventEmitter {\n private endpoints: Map<string, Endpoint> = new Map();\n private connectionStates: Map<string, SimpleConnectionStatus> = new Map();\n\n /**\n * 构造函数\n *\n * @param config - 可选的配置\n */\n constructor(private config?: EndpointManagerConfig) {\n super();\n console.debug(\"[EndpointManager] 实例已创建(新 API)\");\n }\n\n /**\n * 添加 Endpoint 实例\n *\n * @param endpoint - Endpoint 实例\n */\n addEndpoint(endpoint: Endpoint): void {\n const url = endpoint.getUrl();\n\n if (this.endpoints.has(url)) {\n console.debug(\n `[EndpointManager] 接入点 ${sliceEndpoint(url)} 已存在,跳过添加`\n );\n return;\n }\n\n console.debug(`[EndpointManager] 添加接入点: ${sliceEndpoint(url)}`);\n\n this.endpoints.set(url, endpoint);\n this.connectionStates.set(url, {\n endpoint: url,\n connected: false,\n initialized: false,\n });\n\n // 发射事件\n this.emit(\"endpointAdded\", { endpoint: url });\n }\n\n /**\n * 移除 Endpoint 实例\n *\n * @param endpoint - Endpoint 实例\n */\n removeEndpoint(endpoint: Endpoint): void {\n const url = endpoint.getUrl();\n\n if (!this.endpoints.has(url)) {\n console.debug(\n `[EndpointManager] 接入点 ${sliceEndpoint(url)} 不存在,跳过移除`\n );\n return;\n }\n\n console.debug(`[EndpointManager] 移除接入点: ${sliceEndpoint(url)}`);\n\n // 断开连接\n endpoint.disconnect();\n\n // 清理状态\n this.endpoints.delete(url);\n this.connectionStates.delete(url);\n\n // 发射事件\n this.emit(\"endpointRemoved\", { endpoint: url });\n }\n\n /**\n * 连接所有 Endpoint\n */\n async connect(): Promise<void> {\n console.debug(`[EndpointManager] 开始连接所有接入点,总数: ${this.endpoints.size}`);\n\n const promises: Promise<void>[] = [];\n\n for (const [url, endpoint] of this.endpoints) {\n promises.push(\n this.connectSingleEndpoint(url, endpoint).catch((error) => {\n console.error(`[EndpointManager] 连接失败: ${sliceEndpoint(url)}`, error);\n // 更新失败状态\n const status = this.connectionStates.get(url);\n if (status) {\n status.connected = false;\n status.initialized = false;\n status.lastError = error instanceof Error ? error.message : String(error);\n }\n })\n );\n }\n\n await Promise.allSettled(promises);\n\n // 统计连接结果\n const connectedCount = Array.from(this.connectionStates.values()).filter(\n (s) => s.connected\n ).length;\n\n console.info(`[EndpointManager] 连接完成: 成功 ${connectedCount}/${this.endpoints.size}`);\n }\n\n /**\n * 断开所有连接\n */\n async disconnect(): Promise<void> {\n console.debug(\"[EndpointManager] 开始断开所有连接\");\n\n const promises: Promise<void>[] = [];\n\n for (const endpoint of this.endpoints.values()) {\n promises.push(\n Promise.resolve().then(() => {\n endpoint.disconnect();\n })\n );\n }\n\n await Promise.allSettled(promises);\n\n // 重置所有状态\n for (const status of this.connectionStates.values()) {\n status.connected = false;\n status.initialized = false;\n }\n\n console.debug(\"[EndpointManager] 所有接入点已断开连接\");\n }\n\n /**\n * 获取所有 Endpoint URL\n */\n getEndpoints(): string[] {\n return Array.from(this.endpoints.keys());\n }\n\n /**\n * 获取指定 Endpoint 实例\n *\n * @param url - Endpoint URL\n */\n getEndpoint(url: string): Endpoint | undefined {\n return this.endpoints.get(url);\n }\n\n /**\n * 获取所有连接状态\n */\n getConnectionStatus(): SimpleConnectionStatus[] {\n return Array.from(this.connectionStates.values());\n }\n\n /**\n * 检查是否有任何连接处于连接状态\n */\n isAnyConnected(): boolean {\n for (const status of this.connectionStates.values()) {\n if (status.connected) {\n return true;\n }\n }\n return false;\n }\n\n /**\n * 检查指定端点是否已连接\n *\n * @param url - 端点 URL\n */\n isEndpointConnected(url: string): boolean {\n const status = this.connectionStates.get(url);\n return status?.connected ?? false;\n }\n\n /**\n * 获取指定端点的状态\n *\n * @param url - 端点 URL\n */\n getEndpointStatus(url: string): SimpleConnectionStatus | undefined {\n return this.connectionStates.get(url);\n }\n\n /**\n * 重连所有端点\n */\n async reconnectAll(): Promise<void> {\n console.info(\"[EndpointManager] 开始重连所有接入点\");\n\n const promises: Promise<void>[] = [];\n\n for (const [url, endpoint] of this.endpoints) {\n promises.push(\n this.reconnectSingleEndpoint(url, endpoint).catch((error) => {\n console.error(`[EndpointManager] 重连失败: ${sliceEndpoint(url)}`, error);\n })\n );\n }\n\n await Promise.allSettled(promises);\n }\n\n /**\n * 重连指定的端点\n *\n * @param url - 要重连的端点 URL\n */\n async reconnectEndpoint(url: string): Promise<void> {\n const endpoint = this.endpoints.get(url);\n if (!endpoint) {\n throw new Error(`接入点不存在: ${sliceEndpoint(url)}`);\n }\n\n await this.reconnectSingleEndpoint(url, endpoint);\n }\n\n /**\n * 清除所有端点\n */\n async clearEndpoints(): Promise<void> {\n console.debug(\"[EndpointManager] 清除所有接入点\");\n\n await this.disconnect();\n\n this.endpoints.clear();\n this.connectionStates.clear();\n\n console.info(\"[EndpointManager] 所有接入点已清除\");\n }\n\n /**\n * 清理资源\n */\n async cleanup(): Promise<void> {\n console.debug(\"[EndpointManager] 开始清理资源\");\n\n await this.clearEndpoints();\n\n console.debug(\"[EndpointManager] 资源清理完成\");\n }\n\n // ==================== 私有方法 ====================\n\n /**\n * 连接单个端点\n */\n private async connectSingleEndpoint(\n url: string,\n endpoint: Endpoint\n ): Promise<void> {\n const status = this.connectionStates.get(url);\n if (!status) {\n throw new Error(`端点状态不存在: ${sliceEndpoint(url)}`);\n }\n\n console.debug(`[EndpointManager] 连接端点: ${sliceEndpoint(url)}`);\n\n // 更新状态为连接中\n status.connected = false;\n status.initialized = false;\n\n // 执行连接\n await endpoint.connect();\n\n // 更新连接成功状态\n status.connected = true;\n status.initialized = true;\n status.lastConnected = new Date();\n status.lastError = undefined;\n\n console.info(`[EndpointManager] 端点连接成功: ${sliceEndpoint(url)}`);\n }\n\n /**\n * 重连单个端点\n */\n private async reconnectSingleEndpoint(\n url: string,\n endpoint: Endpoint\n ): Promise<void> {\n const status = this.connectionStates.get(url);\n if (!status) {\n throw new Error(`端点状态不存在: ${sliceEndpoint(url)}`);\n }\n\n console.debug(`[EndpointManager] 重连端点: ${sliceEndpoint(url)}`);\n\n // 执行重连\n await endpoint.reconnect();\n\n // 更新连接成功状态\n status.connected = true;\n status.initialized = true;\n status.lastConnected = new Date();\n status.lastError = undefined;\n\n console.info(`[EndpointManager] 端点重连成功: ${sliceEndpoint(url)}`);\n }\n}\n"],"mappings":"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","names":["ToolCallErrorCode","ConnectionState"]}
|
|
1
|
+
{"version":3,"sources":["../src/endpoint.ts","../src/types.ts","../src/utils.ts","../src/manager.ts","../src/shared-mcp-adapter.ts"],"sourcesContent":["/**\n * Endpoint 类\n * 管理单个小智接入点的 WebSocket 连接\n * 实现 MCP (Model Context Protocol) 协议通信\n *\n * 使用方式:\n * ```typescript\n * const mcpManager = new SharedMCPAdapter(globalMCPManager);\n * const endpoint = new Endpoint(\"ws://...\", mcpManager);\n * await endpoint.connect();\n * ```\n */\n\nimport type { Tool } from \"@modelcontextprotocol/sdk/types.js\";\nimport WebSocket from \"ws\";\nimport type { ExtendedMCPMessage, MCPMessage } from \"./mcp.js\";\nimport type {\n IMCPServiceManager,\n EndpointConnectionStatus,\n ToolCallResult,\n} from \"./types.js\";\nimport {\n ConnectionState,\n ToolCallError as ToolCallErrorClass,\n ToolCallErrorCode as ToolCallErrorCodeEnum,\n ensureToolJSONSchema as ensureToolJSONSchemaFn,\n} from \"./types.js\";\nimport { validateToolCallParams } from \"./utils.js\";\nimport { sliceEndpoint } from \"./utils.js\";\n\n// 导出错误类型供外部使用\nexport {\n ToolCallErrorCodeEnum as ToolCallErrorCode,\n ToolCallErrorClass as ToolCallError,\n};\n\n/**\n * Endpoint 类\n * 负责管理单个小智接入点的 WebSocket 连接\n * 使用新的配置方式:直接在构造函数中传入 mcpServers 配置\n */\nexport class Endpoint {\n private endpointUrl: string;\n private ws: WebSocket | null = null;\n private connectionStatus = false;\n private serverInitialized = false;\n private mcpAdapter: IMCPServiceManager;\n\n // 连接状态管理\n private connectionState: ConnectionState = ConnectionState.DISCONNECTED;\n\n // 最后一次错误信息\n private lastError: string | null = null;\n\n // 连接超时定时器\n private connectionTimeout: NodeJS.Timeout | null = null;\n\n // 工具调用超时配置\n private toolCallTimeout = 30000;\n private reconnectDelay: number; // 重连延迟(毫秒)\n\n /**\n * 构造函数\n *\n * @param endpointUrl - 小智接入点 URL\n * @param mcpManager - MCP 服务管理器(依赖注入)\n * @param reconnectDelay - 可选的重连延迟(毫秒)\n */\n constructor(\n endpointUrl: string,\n mcpManager: IMCPServiceManager,\n reconnectDelay?: number\n ) {\n this.endpointUrl = endpointUrl;\n this.reconnectDelay = reconnectDelay ?? 2000;\n\n // 使用注入的 MCP 管理器\n this.mcpAdapter = mcpManager;\n }\n\n /**\n * 获取 Endpoint URL\n */\n getUrl(): string {\n return this.endpointUrl;\n }\n\n /**\n * 获取当前所有工具列表\n */\n getTools(): Tool[] {\n try {\n const allTools = this.mcpAdapter.getAllTools();\n\n return allTools.map((toolInfo) => ({\n name: toolInfo.name,\n description: toolInfo.description,\n inputSchema: ensureToolJSONSchemaFn(toolInfo.inputSchema),\n }));\n } catch (error) {\n console.error(\n `获取工具列表失败: ${error instanceof Error ? error.message : String(error)}`\n );\n return [];\n }\n }\n\n /**\n * 连接小智接入点\n */\n public async connect(): Promise<void> {\n // 初始化 MCP 适配器\n await this.mcpAdapter.initialize();\n\n // 如果正在连接中,等待当前连接完成\n if (this.connectionState === ConnectionState.CONNECTING) {\n throw new Error(\"连接正在进行中,请等待连接完成\");\n }\n\n // 清理之前的连接\n this.cleanupConnection();\n\n return this.attemptConnection();\n }\n\n /**\n * 尝试建立连接\n */\n private async attemptConnection(): Promise<void> {\n this.connectionState = ConnectionState.CONNECTING;\n console.debug(`正在连接小智接入点: ${sliceEndpoint(this.endpointUrl)}`);\n\n return new Promise((resolve, reject) => {\n // 设置连接超时\n this.connectionTimeout = setTimeout(() => {\n const error = new Error(\"连接超时 (10000ms)\");\n this.handleConnectionError(error);\n reject(error);\n }, 10000);\n\n this.ws = new WebSocket(this.endpointUrl);\n\n this.ws.on(\"open\", () => {\n this.handleConnectionSuccess();\n resolve();\n });\n\n this.ws.on(\"message\", (data) => {\n try {\n const message: MCPMessage = JSON.parse(data.toString());\n this.handleMessage(message);\n } catch (error) {\n console.error(\"MCP 消息解析错误:\", error);\n }\n });\n\n this.ws.on(\"close\", (code, reason) => {\n this.handleConnectionClose(code, reason.toString());\n });\n\n this.ws.on(\"error\", (error) => {\n this.handleConnectionError(error);\n reject(error);\n });\n });\n }\n\n /**\n * 处理连接成功\n */\n private handleConnectionSuccess(): void {\n // 清理连接超时定时器\n if (this.connectionTimeout) {\n clearTimeout(this.connectionTimeout);\n this.connectionTimeout = null;\n }\n\n this.connectionStatus = true;\n this.connectionState = ConnectionState.CONNECTED;\n\n console.debug(\"MCP WebSocket 连接已建立\");\n }\n\n /**\n * 处理连接错误\n */\n private handleConnectionError(error: Error): void {\n // 记录最后一次错误信息\n this.lastError = error.message;\n\n // 清理连接超时定时器\n if (this.connectionTimeout) {\n clearTimeout(this.connectionTimeout);\n this.connectionTimeout = null;\n }\n\n console.error(\"MCP WebSocket 错误:\", error.message);\n\n // 清理当前连接\n this.cleanupConnection();\n }\n\n /**\n * 处理连接关闭\n */\n private handleConnectionClose(code: number, reason: string): void {\n this.connectionStatus = false;\n this.serverInitialized = false;\n this.connectionState = ConnectionState.DISCONNECTED;\n console.info(`小智连接已关闭 (代码: ${code}, 原因: ${reason})`);\n }\n\n /**\n * 清理连接资源\n */\n private cleanupConnection(): void {\n // 清理 WebSocket\n if (this.ws) {\n this.ws.removeAllListeners();\n\n try {\n if (this.ws.readyState === WebSocket.OPEN) {\n this.ws.close(1000, \"Cleaning up connection\");\n } else if (this.ws.readyState === WebSocket.CONNECTING) {\n this.ws.terminate();\n }\n } catch (error) {\n console.debug(\"WebSocket 关闭时出现错误(已忽略):\", error);\n }\n\n this.ws = null;\n }\n\n // 清理连接超时定时器\n if (this.connectionTimeout) {\n clearTimeout(this.connectionTimeout);\n this.connectionTimeout = null;\n }\n\n // 重置连接状态\n this.connectionStatus = false;\n this.serverInitialized = false;\n\n // 重置连接状态为已断开\n this.connectionState = ConnectionState.DISCONNECTED;\n }\n\n /**\n * 处理 MCP 消息\n */\n private handleMessage(message: MCPMessage): void {\n console.debug(\"收到 MCP 消息:\", JSON.stringify(message, null, 2));\n\n if (!message.method) {\n console.debug(\"收到没有 method 字段的消息,忽略\");\n return;\n }\n\n switch (message.method) {\n case \"initialize\":\n case \"notifications/initialized\":\n this.sendResponse(message.id, {\n protocolVersion: \"2024-11-05\",\n capabilities: {\n tools: { listChanged: true },\n logging: {},\n },\n serverInfo: {\n name: \"xiaozhi-mcp-server\",\n version: \"1.0.0\",\n },\n });\n this.serverInitialized = true;\n console.debug(\"MCP 服务器初始化完成\");\n break;\n\n case \"tools/list\": {\n const toolsList = this.getTools();\n this.sendResponse(message.id, { tools: toolsList });\n console.debug(`MCP 工具列表已发送 (${toolsList.length}个工具)`);\n break;\n }\n\n case \"tools/call\": {\n this.handleToolCall(message).catch((error) => {\n console.error(\"处理工具调用时发生未捕获错误:\", error);\n });\n break;\n }\n\n case \"ping\":\n this.sendResponse(message.id, {});\n console.debug(\"回应 MCP ping 消息\");\n break;\n\n default:\n console.warn(`未知的 MCP 请求: ${message.method}`);\n }\n }\n\n /**\n * 发送响应消息\n */\n private sendResponse(id: number | string, result: unknown): void {\n console.debug(\n `尝试发送响应: id=${id}, isConnected=${this.connectionStatus}, wsReadyState=${this.ws?.readyState}`\n );\n\n if (this.connectionStatus && this.ws?.readyState === WebSocket.OPEN) {\n const response: ExtendedMCPMessage = {\n jsonrpc: \"2.0\",\n id,\n result,\n };\n\n try {\n this.ws.send(JSON.stringify(response));\n console.debug(\"响应已发送\", {\n id,\n responseSize: JSON.stringify(response).length,\n });\n } catch (error) {\n console.error(\"发送响应失败\", {\n id,\n error,\n });\n }\n } else {\n console.error(\"无法发送响应\", {\n id,\n isConnected: this.connectionStatus,\n wsReadyState: this.ws?.readyState,\n });\n }\n }\n\n /**\n * 获取服务器状态\n */\n public getStatus(): EndpointConnectionStatus {\n const availableTools = this.mcpAdapter.getAllTools().length;\n\n return {\n connected: this.connectionStatus,\n initialized: this.serverInitialized,\n url: this.endpointUrl,\n availableTools,\n connectionState: this.connectionState,\n lastError: this.lastError,\n };\n }\n\n /**\n * 检查连接状态\n */\n public isConnected(): boolean {\n return this.connectionStatus;\n }\n\n /**\n * 主动断开小智连接\n */\n public async disconnect(): Promise<void> {\n console.info(\"主动断开小智连接\");\n\n // 清理 MCP 适配器\n await this.mcpAdapter.cleanup();\n\n // 清理 WebSocket 连接\n this.cleanupConnection();\n }\n\n /**\n * 重连小智接入点\n */\n public async reconnect(): Promise<void> {\n console.info(`重连小智接入点: ${sliceEndpoint(this.endpointUrl)}`);\n\n // 先断开连接\n this.disconnect();\n\n // 等待可配置的时间确保连接完全断开\n await new Promise((resolve) => setTimeout(resolve, this.reconnectDelay));\n\n // 重新连接\n await this.connect();\n }\n\n /**\n * 处理工具调用请求\n */\n private async handleToolCall(request: MCPMessage): Promise<void> {\n if (request.id === undefined || request.id === null) {\n throw new ToolCallErrorClass(\n ToolCallErrorCodeEnum.INVALID_PARAMS,\n \"请求 ID 不能为空\"\n );\n }\n\n const requestId = request.id;\n const startTime = Date.now();\n\n try {\n const params = validateToolCallParams(request.params);\n\n console.info(\"开始处理工具调用\", {\n requestId,\n toolName: params.name,\n hasArguments: !!params.arguments,\n });\n\n const result = await this.executeToolWithTimeout(\n params.name,\n params.arguments || {},\n this.toolCallTimeout\n );\n\n this.sendResponse(requestId, {\n content: result.content || [\n { type: \"text\", text: JSON.stringify(result) },\n ],\n isError: result.isError || false,\n });\n\n console.info(\"工具调用成功\", {\n requestId,\n toolName: params.name,\n duration: `${Date.now() - startTime}ms`,\n });\n } catch (error) {\n this.handleToolCallError(error, requestId, Date.now() - startTime);\n }\n }\n\n /**\n * 带超时控制的工具执行\n */\n private async executeToolWithTimeout(\n toolName: string,\n arguments_: Record<string, unknown>,\n timeoutMs = 30000\n ): Promise<ToolCallResult> {\n return new Promise((resolve, reject) => {\n const timeoutId = setTimeout(() => {\n reject(\n new ToolCallErrorClass(\n ToolCallErrorCodeEnum.TIMEOUT,\n `工具调用超时 (${timeoutMs}ms): ${toolName}`\n )\n );\n }, timeoutMs);\n\n this.mcpAdapter\n .callTool(toolName, arguments_)\n .then((result: ToolCallResult) => {\n clearTimeout(timeoutId);\n resolve(result);\n })\n .catch((error: unknown) => {\n clearTimeout(timeoutId);\n\n const errorMessage =\n error instanceof Error ? error.message : String(error);\n\n if (errorMessage.includes(\"未找到工具\")) {\n reject(\n new ToolCallErrorClass(\n ToolCallErrorCodeEnum.TOOL_NOT_FOUND,\n `工具不存在: ${toolName}`\n )\n );\n } else {\n reject(\n new ToolCallErrorClass(\n ToolCallErrorCodeEnum.TOOL_EXECUTION_ERROR,\n `工具执行失败: ${errorMessage}`\n )\n );\n }\n });\n });\n }\n\n /**\n * 处理工具调用错误\n */\n private handleToolCallError(\n error: unknown,\n requestId: string | number | undefined,\n duration: number\n ): void {\n let errorResponse: {\n code: number;\n message: string;\n data?: unknown;\n };\n\n if (error instanceof ToolCallErrorClass) {\n errorResponse = {\n code: error.code,\n message: error.message,\n data: error.data,\n };\n } else {\n const errorMessage = error instanceof Error ? error.message : \"未知错误\";\n errorResponse = {\n code: ToolCallErrorCodeEnum.TOOL_EXECUTION_ERROR,\n message: errorMessage,\n data: { originalError: String(error) || \"null\" },\n };\n }\n\n this.sendErrorResponse(requestId, errorResponse);\n\n console.error(\"工具调用失败\", {\n requestId,\n duration: `${duration}ms`,\n error: errorResponse,\n });\n }\n\n /**\n * 发送错误响应\n */\n private sendErrorResponse(\n id: string | number | undefined,\n error: { code: number; message: string; data?: unknown }\n ): void {\n if (this.connectionStatus && this.ws?.readyState === WebSocket.OPEN) {\n const response = {\n jsonrpc: \"2.0\",\n id,\n error,\n };\n this.ws.send(JSON.stringify(response));\n console.debug(\"已发送错误响应:\", response);\n }\n }\n}\n","// =========================\n// 1. 工具调用相关类型\n// =========================\n\n/**\n * 工具调用结果接口\n */\nexport interface ToolCallResult {\n content: Array<{\n type: string;\n text: string;\n }>;\n isError?: boolean;\n}\n\n/**\n * 工具调用参数接口\n */\nexport interface ToolCallParams {\n name: string;\n arguments?: Record<string, unknown>;\n}\n\n/**\n * 验证后的工具调用参数\n */\nexport interface ValidatedToolCallParams {\n name: string;\n arguments?: Record<string, unknown>;\n}\n\n/**\n * 工具调用错误码枚举\n */\nexport enum ToolCallErrorCode {\n /** 无效参数 */\n INVALID_PARAMS = -32602,\n /** 工具不存在 */\n TOOL_NOT_FOUND = -32601,\n /** 服务不可用 */\n SERVICE_UNAVAILABLE = -32001,\n /** 调用超时 */\n TIMEOUT = -32002,\n /** 工具执行错误 */\n TOOL_EXECUTION_ERROR = -32000,\n}\n\n/**\n * 工具调用错误类\n */\nexport class ToolCallError extends Error {\n constructor(\n public code: ToolCallErrorCode,\n message: string,\n public data?: unknown\n ) {\n super(message);\n this.name = \"ToolCallError\";\n }\n}\n\n// =========================\n// 2. JSON Schema 类型\n// =========================\n\n/**\n * JSON Schema 类型定义\n * 兼容 MCP SDK 的 JSON Schema 格式\n */\nexport type JSONSchema =\n | (Record<string, unknown> & {\n type: \"object\";\n properties?: Record<string, unknown>;\n required?: string[];\n additionalProperties?: boolean;\n })\n | Record<string, unknown>;\n\n/**\n * 确保对象符合 MCP Tool JSON Schema 格式\n * 返回类型兼容 MCP SDK 的 Tool 类型\n */\nexport function ensureToolJSONSchema(schema: JSONSchema): {\n type: \"object\";\n properties?: Record<string, object>;\n required?: string[];\n additionalProperties?: boolean;\n} {\n if (\n typeof schema === \"object\" &&\n schema !== null &&\n \"type\" in schema &&\n schema.type === \"object\"\n ) {\n return schema as {\n type: \"object\";\n properties?: Record<string, object>;\n required?: string[];\n additionalProperties?: boolean;\n };\n }\n\n // 如果不符合标准格式,返回默认的空对象 schema\n return {\n type: \"object\",\n properties: {},\n required: [],\n additionalProperties: true,\n };\n}\n\n// =========================\n// 3. 工具信息类型\n// =========================\n\n/**\n * 增强的工具信息接口\n * 包含工具的启用状态和使用统计信息\n */\nexport interface EnhancedToolInfo {\n /** 工具唯一标识符,格式为 \"{serviceName}__{originalName}\" */\n name: string;\n\n /** 工具描述信息 */\n description: string;\n\n /** 工具输入参数的 JSON Schema 定义 */\n inputSchema: JSONSchema;\n\n /** 工具所属的 MCP 服务名称 */\n serviceName: string;\n\n /** 工具在 MCP 服务中的原始名称 */\n originalName: string;\n\n /** 工具是否启用 */\n enabled: boolean;\n\n /** 工具使用次数统计 */\n usageCount: number;\n\n /** 工具最后使用时间 (ISO 8601 格式字符串) */\n lastUsedTime: string;\n}\n\n/**\n * MCP 服务管理器接口\n * 用于工具调用,避免循环依赖\n */\nexport interface IMCPServiceManager {\n /** 获取所有工具列表 */\n getAllTools(): EnhancedToolInfo[];\n\n /** 调用工具 */\n callTool(\n toolName: string,\n arguments_: Record<string, unknown>\n ): Promise<ToolCallResult>;\n\n /** 初始化 */\n initialize(): Promise<void>;\n\n /** 清理资源 */\n cleanup(): Promise<void>;\n}\n\n// =========================\n// 4. 连接状态类型\n// =========================\n\n/**\n * 连接状态枚举\n */\nexport enum ConnectionState {\n DISCONNECTED = \"disconnected\",\n CONNECTING = \"connecting\",\n CONNECTED = \"connected\",\n FAILED = \"failed\",\n}\n\n/**\n * 连接选项接口\n */\nexport interface ConnectionOptions {\n /** 连接超时时间(毫秒),默认 10000 */\n connectionTimeout?: number;\n /** 重连延迟时间(毫秒),默认 2000 */\n reconnectDelay?: number;\n}\n\n// =========================\n// 5. EndpointConnection 状态类型\n// =========================\n\n/**\n * EndpointConnection 状态接口\n */\nexport interface EndpointConnectionStatus {\n /** 是否已连接 */\n connected: boolean;\n /** 是否已初始化 */\n initialized: boolean;\n /** 接入点 URL */\n url: string;\n /** 可用工具数量 */\n availableTools: number;\n /** 连接状态 */\n connectionState: ConnectionState;\n /** 最后一次错误信息 */\n lastError: string | null;\n}\n\n// =========================\n// 6. EndpointManager 状态类型\n// =========================\n\n/**\n * 简单连接状态接口\n */\nexport interface SimpleConnectionStatus {\n /** 接入点地址 */\n endpoint: string;\n /** 是否已连接 */\n connected: boolean;\n /** 是否已初始化 */\n initialized: boolean;\n /** 最后连接时间 */\n lastConnected?: Date;\n /** 最后错误信息 */\n lastError?: string;\n}\n\n/**\n * 完整连接状态接口(扩展 SimpleConnectionStatus)\n */\nexport interface ConnectionStatus extends SimpleConnectionStatus {\n // 扩展字段可以在这里添加\n}\n\n/**\n * 配置变更事件类型\n */\nexport interface ConfigChangeEvent {\n type:\n | \"endpoints_added\"\n | \"endpoints_removed\"\n | \"endpoints_updated\"\n | \"options_updated\";\n data: {\n added?: string[];\n removed?: string[];\n updated?: string[];\n oldOptions?: Partial<ConnectionOptions>;\n newOptions?: Partial<ConnectionOptions>;\n };\n timestamp: Date;\n}\n\n/**\n * 重连结果接口\n */\nexport interface ReconnectResult {\n successCount: number;\n failureCount: number;\n results: Array<{\n endpoint: string;\n success: boolean;\n error?: string;\n }>;\n}\n\n// =========================\n// 7. 新 API 配置类型\n// =========================\n\n/**\n * MCP 服务器配置类型\n * 支持三种配置方式:\n * 1. 本地命令 (stdio): { command: string; args: string[]; env?: Record<string, string> }\n * 2. SSE: { type: \"sse\"; url: string; headers?: Record<string, string> }\n * 3. HTTP: { type?: \"http\"; url: string; headers?: Record<string, string> }\n *\n * 向后兼容:自动将 streamable-http/streamable_http/streamableHttp 转换为 http\n */\nexport type MCPServerConfig =\n | LocalMCPServerConfig\n | SSEMCPServerConfig\n | HTTPMCPServerConfig;\n\n/**\n * 本地 MCP 服务器配置\n */\nexport interface LocalMCPServerConfig {\n command: string;\n args: string[];\n env?: Record<string, string>;\n}\n\n/**\n * SSE MCP 服务器配置\n */\nexport interface SSEMCPServerConfig {\n type: \"sse\";\n url: string;\n headers?: Record<string, string>;\n}\n\n/**\n * HTTP MCP 服务器配置\n * 使用 type: \"http\"\n * 向后兼容 streamable-http 写法\n */\nexport interface HTTPMCPServerConfig {\n type?: \"http\" | \"streamable-http\"; // 可选,默认就是 http\n url: string;\n headers?: Record<string, string>;\n}\n\n// 向后兼容的别名\n/** @deprecated 使用 HTTPMCPServerConfig 代替 */\nexport type StreamableHTTPMCPServerConfig = HTTPMCPServerConfig;\n\n/**\n * Endpoint 配置接口\n * @deprecated 不再使用,Endpoint 构造函数改为接收 IMCPServiceManager\n */\nexport interface EndpointConfig {\n /** MCP 服务器配置(声明式) */\n mcpServers: Record<string, MCPServerConfig>;\n /** 可选:重连延迟(毫秒),默认 2000 */\n reconnectDelay?: number;\n /** 可选:ModelScope API Key(全局) */\n modelscopeApiKey?: string;\n}\n\n/**\n * EndpointManager 配置接口\n */\nexport interface EndpointManagerConfig {\n /** 可选:默认重连延迟(毫秒) */\n defaultReconnectDelay?: number;\n}\n\n// =========================\n// 8. JWT Token 类型\n// =========================\n\n/**\n * 小智平台 JWT Token Payload 接口\n *\n * @example\n * ```typescript\n * // 从 endpoint URL 解码得到的 payload\n * const payload: XiaozhiTokenPayload = {\n * userId: 302720,\n * agentId: 1324149,\n * endpointId: \"agent_1324149\",\n * purpose: \"mcp-endpoint\",\n * iat: 1768480930,\n * exp: 1800038530\n * };\n * ```\n */\nexport interface XiaozhiTokenPayload {\n /** 用户 ID */\n userId: number;\n /** 代理 ID */\n agentId: number;\n /** 接入点 ID,格式为 \"agent_{agentId}\" */\n endpointId: string;\n /** Token 用途 */\n purpose: string;\n /** 签发时间(Unix 时间戳) */\n iat: number;\n /** 过期时间(Unix 时间戳) */\n exp: number;\n}\n\n/**\n * 解析后的 Endpoint URL 信息\n */\nexport interface ParsedEndpointInfo {\n /** 完整的 endpoint URL */\n url: string;\n /** 提取的 JWT Token */\n token: string;\n /** 解码后的 Token Payload */\n payload: XiaozhiTokenPayload;\n /** WebSocket 服务器地址(不含 token 参数) */\n wsUrl: string;\n}\n","/**\n * 工具函数模块\n *\n * 内联的必要工具函数,确保包的独立性\n */\n\nimport type {\n ParsedEndpointInfo,\n ValidatedToolCallParams,\n XiaozhiTokenPayload,\n} from \"./types.js\";\n\n/**\n * 截断端点 URL 用于日志显示\n *\n * @param endpoint - 完整的端点 URL\n * @returns 截断后的 URL\n *\n * @example\n * ```typescript\n * sliceEndpoint(\"ws://very-long-endpoint-url-here.example.com/endpoint\")\n * // 返回: \"ws://very-long-endpoint-u...e.com/endpoint\"\n * ```\n */\nexport function sliceEndpoint(endpoint: string): string {\n return `${endpoint.slice(0, 30)}...${endpoint.slice(-10)}`;\n}\n\n/**\n * 验证工具调用参数\n *\n * @param params - 待验证的参数\n * @returns 验证后的参数\n * @throws {Error} 如果参数无效\n *\n * @example\n * ```typescript\n * const params = validateToolCallParams({\n * name: \"test_tool\",\n * arguments: { foo: \"bar\" }\n * });\n * ```\n */\nexport function validateToolCallParams(\n params: unknown\n): ValidatedToolCallParams {\n // 基础类型检查\n if (!params || typeof params !== \"object\") {\n throw new Error(\"工具调用参数必须是对象\");\n }\n\n const p = params as Record<string, unknown>;\n\n // 验证工具名称\n if (!p.name || typeof p.name !== \"string\") {\n throw new Error(\"工具名称必须是字符串\");\n }\n\n // 构建验证后的参数\n const validated: ValidatedToolCallParams = {\n name: p.name,\n };\n\n // 验证参数(可选)\n if (p.arguments !== undefined) {\n if (typeof p.arguments !== \"object\" || p.arguments === null) {\n throw new Error(\"工具参数必须是对象\");\n }\n validated.arguments = p.arguments as Record<string, unknown>;\n }\n\n return validated;\n}\n\n/**\n * 验证端点 URL 格式\n *\n * @param endpoint - 待验证的端点 URL\n * @returns 是否为有效的 WebSocket URL\n */\nexport function isValidEndpointUrl(endpoint: string): boolean {\n if (!endpoint || typeof endpoint !== \"string\") {\n return false;\n }\n\n if (!endpoint.startsWith(\"ws://\") && !endpoint.startsWith(\"wss://\")) {\n return false;\n }\n\n try {\n new URL(endpoint);\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * 检查属性名是否安全,防止原型污染攻击\n *\n * @param key - 待检查的属性名\n * @returns 是否为安全的属性名\n *\n * @example\n * ```typescript\n * isSafeKey(\"name\") // true\n * isSafeKey(\"__proto__\") // false\n * isSafeKey(\"constructor\") // false\n * ```\n */\nfunction isSafeKey(key: string): boolean {\n // 拒绝已知的危险属性名\n const dangerousKeys = [\"__proto__\", \"constructor\", \"prototype\"];\n if (dangerousKeys.includes(key)) {\n return false;\n }\n\n // 确保属性名是普通的字符串(不是 Symbol 等)\n return typeof key === \"string\" && key.length > 0;\n}\n\n/**\n * 深度合并对象\n *\n * @param target - 目标对象\n * @param sources - 源对象\n * @returns 合并后的对象\n *\n * @example\n * ```typescript\n * const result = deepMerge(\n * { a: 1, b: { x: 1 } },\n * { b: { y: 2 }, c: 3 }\n * );\n * // 返回: { a: 1, b: { x: 1, y: 2 }, c: 3 }\n * ```\n */\nexport function deepMerge<T>(\n target: Partial<T>,\n ...sources: Array<Partial<T>>\n): T {\n if (sources.length === 0) {\n return target as T;\n }\n\n const source = sources.shift();\n\n if (source === undefined) {\n return target as T;\n }\n\n // 使用 Object.keys() 替代 for...in,只遍历对象自身的可枚举属性\n const keys = Object.keys(source as Record<string, unknown>);\n\n for (const key of keys) {\n // 跳过不安全的 key,防止原型污染攻击\n if (!isSafeKey(key)) {\n continue;\n }\n\n const sourceValue = (source as Record<string, unknown>)[key];\n const targetValue = (target as Record<string, unknown>)[key];\n\n if (\n typeof sourceValue === \"object\" &&\n sourceValue !== null &&\n !Array.isArray(sourceValue) &&\n typeof targetValue === \"object\" &&\n targetValue !== null &&\n !Array.isArray(targetValue)\n ) {\n (target as Record<string, unknown>)[key] = deepMerge(\n targetValue as Record<string, unknown>,\n sourceValue as Record<string, unknown>\n );\n } else {\n (target as Record<string, unknown>)[key] = sourceValue;\n }\n }\n\n return deepMerge(target, ...sources);\n}\n\n/**\n * 延迟执行\n *\n * @param ms - 延迟时间(毫秒)\n * @returns Promise\n */\nexport function sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\n/**\n * 格式化错误消息\n *\n * @param error - 错误对象\n * @returns 格式化后的错误消息\n */\nexport function formatErrorMessage(error: unknown): string {\n if (error instanceof Error) {\n return error.message;\n }\n\n if (typeof error === \"string\") {\n return error;\n }\n\n return String(error);\n}\n\n// =========================\n// JWT Token 解码相关函数\n// =========================\n\n/**\n * Base64URL 解码\n *\n * @param input - Base64URL 编码的字符串\n * @returns 解码后的字符串\n *\n * @example\n * ```typescript\n * base64UrlDecode(\"SGVsbG8gV29ybGQ\") // 返回: \"Hello World\"\n * ```\n */\nfunction base64UrlDecode(input: string): string {\n // 将 Base64URL 格式转换为标准 Base64 格式\n let base64 = input.replace(/-/g, \"+\").replace(/_/g, \"/\");\n\n // 补全 padding\n while (base64.length % 4) {\n base64 += \"=\";\n }\n\n // 使用 Node.js Buffer 进行解码\n return Buffer.from(base64, \"base64\").toString(\"utf-8\");\n}\n\n/**\n * 解码 JWT Token(仅解析 payload,不验证签名)\n *\n * @param token - JWT Token 字符串\n * @returns 解码后的 Token Payload,解码失败返回 null\n *\n * @example\n * ```typescript\n * const payload = decodeJWTToken(\"eyJ...token...\");\n * if (payload) {\n * console.log(payload.endpointId); // \"agent_1324149\"\n * }\n * ```\n */\nexport function decodeJWTToken(token: string): XiaozhiTokenPayload | null {\n if (!token || typeof token !== \"string\") {\n return null;\n }\n\n try {\n // JWT 格式: header.payload.signature\n const parts = token.split(\".\");\n if (parts.length !== 3) {\n return null;\n }\n\n // 解码 payload 部分(第二部分)\n const payloadStr = base64UrlDecode(parts[1]);\n const payload = JSON.parse(payloadStr) as unknown;\n\n // 验证 payload 结构\n if (\n typeof payload !== \"object\" ||\n payload === null ||\n !(\"userId\" in payload) ||\n !(\"agentId\" in payload) ||\n !(\"endpointId\" in payload) ||\n !(\"purpose\" in payload) ||\n !(\"iat\" in payload) ||\n !(\"exp\" in payload)\n ) {\n return null;\n }\n\n return payload as XiaozhiTokenPayload;\n } catch {\n return null;\n }\n}\n\n/**\n * 从 endpoint URL 中提取 token 参数\n *\n * @param url - 完整的 endpoint URL\n * @returns 提取的 token 字符串,未找到返回 null\n *\n * @example\n * ```typescript\n * const token = extractTokenFromUrl(\n * \"wss://api.xiaozhi.me/mcp/?token=eyJ...\"\n * );\n * ```\n */\nexport function extractTokenFromUrl(url: string): string | null {\n if (!url || typeof url !== \"string\") {\n return null;\n }\n\n try {\n const urlObj = new URL(url);\n return urlObj.searchParams.get(\"token\");\n } catch {\n return null;\n }\n}\n\n/**\n * 解析 endpoint URL 获取完整信息\n *\n * @param url - 完整的 endpoint URL\n * @returns 解析后的 endpoint 信息,解析失败返回 null\n *\n * @example\n * ```typescript\n * const info = parseEndpointUrl(\n * \"wss://api.xiaozhi.me/mcp/?token=eyJ...\"\n * );\n * if (info) {\n * console.log(info.payload.endpointId); // \"agent_1324149\"\n * console.log(info.wsUrl); // \"wss://api.xiaozhi.me/mcp/\"\n * }\n * ```\n */\nexport function parseEndpointUrl(url: string): ParsedEndpointInfo | null {\n if (!url || typeof url !== \"string\") {\n return null;\n }\n\n try {\n // 提取 token\n const token = extractTokenFromUrl(url);\n if (!token) {\n return null;\n }\n\n // 解码 token\n const payload = decodeJWTToken(token);\n if (!payload) {\n return null;\n }\n\n // 移除 token 参数得到纯净的 WebSocket URL\n const urlObj = new URL(url);\n urlObj.searchParams.delete(\"token\");\n const wsUrl = urlObj.toString();\n\n return {\n url,\n token,\n payload,\n wsUrl,\n };\n } catch {\n return null;\n }\n}\n","/**\n * EndpointManager\n * 管理多个小智接入点的连接,共享外部传入的 MCPManager\n *\n * 使用方式:\n * ```typescript\n * // 1. 先创建并配置 MCPManager\n * const mcpManager = new MCPManager();\n * mcpManager.addServer(\"calculator\", { command: \"npx\", args: [\"-y\", \"calculator-mcp\"] });\n * await mcpManager.connect();\n *\n * // 2. 创建 EndpointManager 并设置 MCPManager\n * const manager = new EndpointManager();\n * manager.setMcpManager(mcpManager);\n *\n * // 3. 添加接入点\n * manager.addEndpoint(\"ws://endpoint1\");\n * await manager.connect();\n * ```\n */\n\nimport { EventEmitter } from \"node:events\";\nimport type { Endpoint } from \"./endpoint.js\";\nimport { Endpoint as EndpointClass } from \"./endpoint.js\";\nimport { SharedMCPAdapter } from \"./shared-mcp-adapter.js\";\nimport type {\n EndpointManagerConfig,\n IMCPServiceManager,\n SimpleConnectionStatus,\n} from \"./types.js\";\nimport { sliceEndpoint } from \"./utils.js\";\n\n/**\n * MCP 服务连接事件数据\n */\ninterface MCPConnectedEvent {\n serverName: string;\n tools?: Array<unknown>;\n}\n\n/**\n * MCP 服务错误事件数据\n */\ninterface MCPErrorEvent {\n serverName: string;\n error: Error | unknown;\n}\n\n/**\n * 小智接入点管理器\n * 负责管理多个小智接入点的连接,共享 MCP 服务\n *\n * 注意:MCPManager 必须通过 setMcpManager() 方法设置,且由外部管理其生命周期\n */\nexport class EndpointManager extends EventEmitter {\n private endpoints: Map<string, Endpoint> = new Map();\n private connectionStates: Map<string, SimpleConnectionStatus> = new Map();\n private mcpManager: IMCPServiceManager | null = null;\n private sharedMCPAdapter: SharedMCPAdapter | null = null;\n private mcpEventListeners: Array<(...args: unknown[]) => void> = [];\n\n /**\n * 构造函数\n *\n * @param config - 可选的配置\n */\n constructor(private config?: EndpointManagerConfig) {\n super();\n console.debug(\"[EndpointManager] 实例已创建\");\n }\n\n /**\n * 设置 MCPManager 实例\n *\n * 注意:MCPManager 的生命周期由外部管理,EndpointManager 不负责连接和断开\n *\n * @param mcpManager - 外部创建并已连接的 MCPManager 实例\n */\n setMcpManager(mcpManager: IMCPServiceManager): void {\n if (this.sharedMCPAdapter) {\n throw new Error(\"MCPManager 已经设置,不能重复设置\");\n }\n\n this.mcpManager = mcpManager;\n this.sharedMCPAdapter = new SharedMCPAdapter(mcpManager);\n\n // 监听 MCP 服务连接事件(如果支持 EventEmitter)\n if (\"on\" in mcpManager && typeof mcpManager.on === \"function\") {\n const connectedHandler = (...args: unknown[]) => {\n const data = args[0] as MCPConnectedEvent;\n console.info(\n `[EndpointManager] MCP 服务已连接: ${data.serverName}, 工具数: ${data.tools?.length || 0}`\n );\n };\n const errorHandler = (...args: unknown[]) => {\n const data = args[0] as MCPErrorEvent;\n console.error(\n `[EndpointManager] MCP 服务连接失败: ${data.serverName}`,\n data.error\n );\n };\n\n mcpManager.on(\"connected\", connectedHandler);\n mcpManager.on(\"error\", errorHandler);\n\n // 保存监听器引用以便后续清理\n this.mcpEventListeners.push(connectedHandler, errorHandler);\n }\n\n console.info(\"[EndpointManager] MCPManager 已设置\");\n }\n\n /**\n * 添加 Endpoint(支持 URL 字符串或 Endpoint 实例)\n *\n * @param endpoint - Endpoint URL 字符串或 Endpoint 实例\n */\n addEndpoint(endpoint: string | Endpoint): void {\n // 如果是字符串,创建新的 Endpoint 实例\n if (typeof endpoint === \"string\") {\n if (!this.sharedMCPAdapter) {\n throw new Error(\n \"MCPManager 未设置,请先调用 setMcpManager() 方法设置 MCPManager\"\n );\n }\n\n const endpointInstance = new EndpointClass(\n endpoint,\n this.sharedMCPAdapter,\n this.config?.defaultReconnectDelay\n );\n\n this.addEndpointInternal(endpointInstance);\n return;\n }\n\n // 原有的 Endpoint 实例处理逻辑\n // 当 EndpointManager 已配置共享 MCPManager 时,不允许再传入外部构造的 Endpoint 实例,\n // 以避免不同 Endpoint 使用不同的 MCP 管理器,破坏共享 MCPManager 的设计。\n if (this.sharedMCPAdapter) {\n throw new Error(\n \"[EndpointManager] 当使用共享 MCPManager 时,不支持传入自定义 Endpoint 实例,请传入 Endpoint URL 字符串以便由 EndpointManager 创建实例\"\n );\n }\n\n this.addEndpointInternal(endpoint);\n }\n\n /**\n * 内部添加 Endpoint 方法\n */\n private addEndpointInternal(endpoint: Endpoint): void {\n const url = endpoint.getUrl();\n\n if (this.endpoints.has(url)) {\n console.debug(\n `[EndpointManager] 接入点 ${sliceEndpoint(url)} 已存在,跳过添加`\n );\n return;\n }\n\n console.debug(`[EndpointManager] 添加接入点: ${sliceEndpoint(url)}`);\n\n this.endpoints.set(url, endpoint);\n this.connectionStates.set(url, {\n endpoint: url,\n connected: false,\n initialized: false,\n });\n\n // 发射事件\n this.emit(\"endpointAdded\", { endpoint: url });\n }\n\n /**\n * 移除 Endpoint 实例\n *\n * @param endpoint - Endpoint 实例\n */\n removeEndpoint(endpoint: Endpoint): void {\n const url = endpoint.getUrl();\n\n if (!this.endpoints.has(url)) {\n console.debug(\n `[EndpointManager] 接入点 ${sliceEndpoint(url)} 不存在,跳过移除`\n );\n return;\n }\n\n console.debug(`[EndpointManager] 移除接入点: ${sliceEndpoint(url)}`);\n\n // 断开连接\n endpoint.disconnect();\n\n // 清理状态\n this.endpoints.delete(url);\n this.connectionStates.delete(url);\n\n // 发射事件\n this.emit(\"endpointRemoved\", { endpoint: url });\n }\n\n /**\n * 连接 Endpoint\n *\n * 注意:此方法不负责连接 MCPManager,MCPManager 必须在调用此方法前已连接\n *\n * @param endpoint - 可选,指定要连接的端点 URL。如果不传入,则连接所有端点\n */\n async connect(endpoint?: string): Promise<void> {\n // 如果指定了端点,只连接该端点\n if (endpoint) {\n const endpointInstance = this.endpoints.get(endpoint);\n if (!endpointInstance) {\n throw new Error(`接入点不存在: ${sliceEndpoint(endpoint)}`);\n }\n\n const status = this.connectionStates.get(endpoint);\n if (status?.connected) {\n console.debug(\n `[EndpointManager] 接入点已连接,跳过: ${sliceEndpoint(endpoint)}`\n );\n return;\n }\n\n await this.connectSingleEndpoint(endpoint, endpointInstance);\n return;\n }\n\n // 连接所有未连接的 Endpoint\n console.debug(\n `[EndpointManager] 开始连接接入点,总数: ${this.endpoints.size}`\n );\n\n const promises: Promise<void>[] = [];\n\n for (const [url, endpoint] of this.endpoints) {\n const status = this.connectionStates.get(url);\n\n // 跳过已连接的端点\n if (status?.connected) {\n console.debug(\n `[EndpointManager] 接入点已连接,跳过: ${sliceEndpoint(url)}`\n );\n continue;\n }\n\n promises.push(\n this.connectSingleEndpoint(url, endpoint).catch((error) => {\n console.error(\n `[EndpointManager] 连接失败: ${sliceEndpoint(url)}`,\n error\n );\n // 更新失败状态\n const status = this.connectionStates.get(url);\n if (status) {\n status.connected = false;\n status.initialized = false;\n status.lastError =\n error instanceof Error ? error.message : String(error);\n }\n })\n );\n }\n\n await Promise.allSettled(promises);\n\n // 统计连接结果\n const connectedCount = Array.from(this.connectionStates.values()).filter(\n (s) => s.connected\n ).length;\n\n console.info(\n `[EndpointManager] 连接完成: 成功 ${connectedCount}/${this.endpoints.size}`\n );\n }\n\n /**\n * 断开连接\n *\n * 注意:此方法不断开 MCPManager,MCPManager 的生命周期由外部管理\n *\n * @param endpoint - 可选,指定要断开的端点 URL。如果不传入,则断开所有端点\n */\n async disconnect(endpoint?: string): Promise<void> {\n // 如果指定了端点,只断开该端点\n if (endpoint) {\n const endpointInstance = this.endpoints.get(endpoint);\n if (!endpointInstance) {\n throw new Error(`接入点不存在: ${sliceEndpoint(endpoint)}`);\n }\n\n endpointInstance.disconnect();\n\n const status = this.connectionStates.get(endpoint);\n if (status) {\n status.connected = false;\n status.initialized = false;\n }\n\n console.debug(\n `[EndpointManager] 接入点已断开: ${sliceEndpoint(endpoint)}`\n );\n return;\n }\n\n // 断开所有连接\n console.debug(\"[EndpointManager] 开始断开所有连接\");\n\n const promises: Promise<void>[] = [];\n\n for (const endpoint of this.endpoints.values()) {\n promises.push(\n Promise.resolve().then(() => {\n endpoint.disconnect();\n })\n );\n }\n\n await Promise.allSettled(promises);\n\n // 重置所有状态\n for (const status of this.connectionStates.values()) {\n status.connected = false;\n status.initialized = false;\n }\n\n console.debug(\"[EndpointManager] 所有接入点已断开连接\");\n }\n\n /**\n * 获取所有 Endpoint URL\n */\n getEndpoints(): string[] {\n return Array.from(this.endpoints.keys());\n }\n\n /**\n * 获取指定 Endpoint 实例\n *\n * @param url - Endpoint URL\n */\n getEndpoint(url: string): Endpoint | undefined {\n return this.endpoints.get(url);\n }\n\n /**\n * 获取所有连接状态\n */\n getConnectionStatus(): SimpleConnectionStatus[] {\n return Array.from(this.connectionStates.values());\n }\n\n /**\n * 检查是否有任何连接处于连接状态\n */\n isAnyConnected(): boolean {\n for (const status of this.connectionStates.values()) {\n if (status.connected) {\n return true;\n }\n }\n return false;\n }\n\n /**\n * 检查指定端点是否已连接\n *\n * @param url - 端点 URL\n */\n isEndpointConnected(url: string): boolean {\n const status = this.connectionStates.get(url);\n return status?.connected ?? false;\n }\n\n /**\n * 获取指定端点的状态\n *\n * @param url - 端点 URL\n */\n getEndpointStatus(url: string): SimpleConnectionStatus | undefined {\n return this.connectionStates.get(url);\n }\n\n /**\n * 重连\n *\n * @param endpoint - 可选,指定要重连的端点 URL。如果不传入,则重连所有端点\n * @param delay - 可选,disconnect 和 connect 之间的等待时间(毫秒),默认为 1000ms。\n * 注意:此参数只控制断开和重新连接之间的等待时间,不影响底层 Endpoint 实例的重连延迟\n */\n async reconnect(endpoint?: string, delay = 1000): Promise<void> {\n console.info(\"[EndpointManager] 开始重连\");\n\n // 先断开连接\n await this.disconnect(endpoint);\n\n // 等待一段时间\n console.debug(`[EndpointManager] 等待 ${delay}ms 后重连`);\n await new Promise((resolve) => setTimeout(resolve, delay));\n\n // 再重新连接\n await this.connect(endpoint);\n\n console.info(\"[EndpointManager] 重连完成\");\n }\n\n /**\n * 重连所有端点\n */\n async reconnectAll(): Promise<void> {\n console.info(\"[EndpointManager] 开始重连所有接入点\");\n\n const promises: Promise<void>[] = [];\n\n for (const [url, endpoint] of this.endpoints) {\n promises.push(\n this.reconnectSingleEndpoint(url, endpoint).catch((error) => {\n console.error(\n `[EndpointManager] 重连失败: ${sliceEndpoint(url)}`,\n error\n );\n })\n );\n }\n\n await Promise.allSettled(promises);\n }\n\n /**\n * 重连指定的端点\n *\n * @param url - 要重连的端点 URL\n */\n async reconnectEndpoint(url: string): Promise<void> {\n const endpoint = this.endpoints.get(url);\n if (!endpoint) {\n throw new Error(`接入点不存在: ${sliceEndpoint(url)}`);\n }\n\n await this.reconnectSingleEndpoint(url, endpoint);\n }\n\n /**\n * 清除所有端点\n *\n * 注意:此方法不会清理 MCPManager,MCPManager 的生命周期由外部管理\n */\n async clearEndpoints(): Promise<void> {\n console.debug(\"[EndpointManager] 清除所有接入点\");\n\n await this.disconnect();\n\n this.endpoints.clear();\n this.connectionStates.clear();\n\n // 注意:不清理 MCPManager,由外部管理\n // this.mcpManager = null;\n // this.sharedMCPAdapter = null;\n\n console.info(\"[EndpointManager] 所有接入点已清除\");\n }\n\n /**\n * 清理资源\n *\n * 注意:此方法不会清理 MCPManager,MCPManager 的生命周期由外部管理\n */\n async cleanup(): Promise<void> {\n console.debug(\"[EndpointManager] 开始清理资源\");\n\n // 移除 MCP 服务事件监听器\n if (\n this.mcpManager &&\n \"removeListener\" in this.mcpManager &&\n typeof this.mcpManager.removeListener === \"function\"\n ) {\n for (const listener of this.mcpEventListeners) {\n this.mcpManager.removeListener(\"connected\", listener);\n this.mcpManager.removeListener(\"error\", listener);\n }\n }\n this.mcpEventListeners = [];\n\n await this.clearEndpoints();\n\n console.debug(\"[EndpointManager] 资源清理完成\");\n }\n\n // ==================== 私有方法 ====================\n\n /**\n * 连接单个端点\n */\n private async connectSingleEndpoint(\n url: string,\n endpoint: Endpoint\n ): Promise<void> {\n const status = this.connectionStates.get(url);\n if (!status) {\n throw new Error(`端点状态不存在: ${sliceEndpoint(url)}`);\n }\n\n console.debug(`[EndpointManager] 连接端点: ${sliceEndpoint(url)}`);\n\n // 更新状态为连接中\n status.connected = false;\n status.initialized = false;\n\n // 执行连接\n await endpoint.connect();\n\n // 更新连接成功状态\n status.connected = true;\n status.initialized = true;\n status.lastConnected = new Date();\n status.lastError = undefined;\n\n console.info(`[EndpointManager] 端点连接成功: ${sliceEndpoint(url)}`);\n }\n\n /**\n * 重连单个端点\n */\n private async reconnectSingleEndpoint(\n url: string,\n endpoint: Endpoint\n ): Promise<void> {\n const status = this.connectionStates.get(url);\n if (!status) {\n throw new Error(`端点状态不存在: ${sliceEndpoint(url)}`);\n }\n\n console.debug(`[EndpointManager] 重连端点: ${sliceEndpoint(url)}`);\n\n // 执行重连\n await endpoint.reconnect();\n\n // 更新连接成功状态\n status.connected = true;\n status.initialized = true;\n status.lastConnected = new Date();\n status.lastError = undefined;\n\n console.info(`[EndpointManager] 端点重连成功: ${sliceEndpoint(url)}`);\n }\n}\n","/**\n * 共享 MCP 管理器适配器\n *\n * 接收全局 MCPManager 实例的引用,不创建独立连接\n * 用于多个 Endpoint 共享同一个 MCPManager 实例\n *\n * @example\n * ```typescript\n * const globalMCPManager = new MCPManager();\n * await globalMCPManager.connect();\n *\n * const adapter = new SharedMCPAdapter(globalMCPManager);\n * const endpoint = new Endpoint(\"ws://...\", adapter);\n * ```\n */\n\nimport type {\n IMCPServiceManager,\n EnhancedToolInfo,\n ToolCallResult,\n} from \"./types.js\";\n\n/**\n * 共享 MCP 管理器适配器\n * 实现 IMCPServiceManager 接口,将操作委托给全局 MCPManager\n */\nexport class SharedMCPAdapter implements IMCPServiceManager {\n private isInitialized = false;\n\n /**\n * 构造函数\n *\n * @param globalMCPManager - 全局 MCPManager 实例\n */\n constructor(private globalMCPManager: IMCPServiceManager) {\n if (!globalMCPManager) {\n throw new Error(\"全局 MCPManager 不能为空\");\n }\n }\n\n /**\n * 初始化适配器\n *\n * 注意:此方法不执行任何连接操作,仅标记初始化状态,因为全局 MCPManager 的生命周期由外部管理\n */\n async initialize(): Promise<void> {\n if (this.isInitialized) {\n return;\n }\n\n // 全局实例已经在外部连接,这里只标记状态\n this.isInitialized = true;\n }\n\n /**\n * 获取所有工具列表\n *\n * 从全局 MCPManager 获取工具列表,并转换为增强格式\n */\n getAllTools(): EnhancedToolInfo[] {\n // 直接使用 IMCPServiceManager 接口的 getAllTools() 方法\n return this.globalMCPManager.getAllTools();\n }\n\n /**\n * 调用工具\n *\n * 将工具调用委托给全局 MCPManager\n *\n * @param toolName - 工具名称(格式:serviceName__toolName)\n * @param arguments_ - 工具调用参数\n */\n async callTool(\n toolName: string,\n arguments_: Record<string, unknown>\n ): Promise<ToolCallResult> {\n // 直接使用 IMCPServiceManager 接口的 callTool() 方法\n // toolName 已经是完整格式(serviceName__toolName)\n return this.globalMCPManager.callTool(toolName, arguments_);\n }\n\n /**\n * 清理资源\n *\n * 注意:此方法不会断开全局 MCPManager,由创建者负责管理\n */\n async cleanup(): Promise<void> {\n this.isInitialized = false;\n // 不断开全局 MCPManager\n }\n}\n"],"mappings":"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","names":["ToolCallErrorCode","ConnectionState","endpoint","status"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xiaozhi-client/endpoint",
|
|
3
|
-
"version": "1.9.
|
|
3
|
+
"version": "1.9.8-beta.0",
|
|
4
4
|
"description": "小智接入点 WebSocket 连接管理库",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
"repository": {
|
|
9
9
|
"type": "git",
|
|
10
10
|
"url": "https://github.com/shenjingnan/xiaozhi-client.git",
|
|
11
|
-
"directory": "packages/
|
|
11
|
+
"directory": "packages/endpoint"
|
|
12
12
|
},
|
|
13
13
|
"exports": {
|
|
14
14
|
".": {
|
|
@@ -21,8 +21,8 @@
|
|
|
21
21
|
],
|
|
22
22
|
"dependencies": {
|
|
23
23
|
"ws": "^8.14.2",
|
|
24
|
-
"@xiaozhi-client/
|
|
25
|
-
"@xiaozhi-client/
|
|
24
|
+
"@xiaozhi-client/config": "1.9.8-beta.0",
|
|
25
|
+
"@xiaozhi-client/mcp-core": "1.9.8-beta.0"
|
|
26
26
|
},
|
|
27
27
|
"peerDependencies": {
|
|
28
28
|
"@modelcontextprotocol/sdk": "^1.24.0"
|
|
@@ -31,6 +31,7 @@
|
|
|
31
31
|
"@types/node": "^24.3.0",
|
|
32
32
|
"@types/ws": "^8.5.0",
|
|
33
33
|
"tsup": "^8.3.5",
|
|
34
|
+
"tsx": "^4.19.2",
|
|
34
35
|
"typescript": "^5.9.2",
|
|
35
36
|
"vitest": "^3.0.5"
|
|
36
37
|
},
|
|
@@ -50,6 +51,11 @@
|
|
|
50
51
|
"build": "tsup",
|
|
51
52
|
"dev": "tsup --watch",
|
|
52
53
|
"test": "vitest",
|
|
53
|
-
"type-check": "tsc --noEmit"
|
|
54
|
+
"type-check": "tsc --noEmit",
|
|
55
|
+
"start": "tsx examples/single-endpoint.ts",
|
|
56
|
+
"start:agg": "tsx examples/mcp-aggregation.ts",
|
|
57
|
+
"start:iso": "tsx examples/endpoint-isolation.ts",
|
|
58
|
+
"start:manage": "tsx examples/manage-endpoints.ts",
|
|
59
|
+
"start:shared": "tsx examples/shared-mcp-to-multi-endpoints.ts"
|
|
54
60
|
}
|
|
55
61
|
}
|