xiaozhi-client 1.6.3-beta.1 → 1.6.3-beta.2

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 CHANGED
@@ -86,7 +86,11 @@ cd my-app
86
86
  ## 安装依赖(主要是示例代码中mcp服务所使用的依赖)
87
87
  pnpm install
88
88
 
89
- # 修改 xiaozhi.config.json 中的 mcpEndpoint 为你的接入点地址(需要自行前往xiaozhi.me获取)
89
+ ## 初始化配置
90
+ xiaozhi config init
91
+
92
+ ## 设置接入点地址(需要自行前往xiaozhi.me获取)
93
+ xiaozhi config set mcpEndpoint "your-endpoint-url"
90
94
  # 小智AI配置MCP接入点使用说明:https://ccnphfhqs21z.feishu.cn/wiki/HiPEwZ37XiitnwktX13cEM5KnSb
91
95
 
92
96
  ## 运行
@@ -97,15 +101,19 @@ xiaozhi start
97
101
 
98
102
  ```bash
99
103
  # 创建项目
100
- npx -y xiaozhi-client create --template hello-world
104
+ npx -y xiaozhi-client create my-app --template hello-world
101
105
 
102
106
  # 进入项目目录
103
- cd hello-world
107
+ cd my-app
104
108
 
105
109
  # 安装依赖
106
110
  pnpm install
107
111
 
108
- # 修改 xiaozhi.config.json 中的 mcpEndpoint 为你的接入点地址(需要自行前往xiaozhi.me获取)
112
+ # 初始化配置
113
+ npx -y xiaozhi-client config init
114
+
115
+ # 设置接入点地址(需要自行前往xiaozhi.me获取)
116
+ npx -y xiaozhi-client config set mcpEndpoint "your-endpoint-url"
109
117
  # 小智AI配置MCP接入点使用说明:https://ccnphfhqs21z.feishu.cn/wiki/HiPEwZ37XiitnwktX13cEM5KnSb
110
118
 
111
119
  # 启动服务
@@ -360,18 +368,49 @@ docker run -d \
360
368
 
361
369
  ## 可用命令
362
370
 
371
+ ### 基本命令
372
+
363
373
  ```bash
364
374
  # 查看帮助
365
375
  xiaozhi --help
366
376
 
367
- # 启动服务
377
+ # 查看版本信息
378
+ xiaozhi --version
379
+
380
+ # 查看详细系统信息
381
+ xiaozhi --info
382
+ ```
383
+
384
+ ### 项目管理
385
+
386
+ ```bash
387
+ # 创建项目
388
+ xiaozhi create my-app --template hello-world
389
+
390
+ # 初始化配置文件
391
+ xiaozhi config init
392
+
393
+ # 查看配置
394
+ xiaozhi config get mcpEndpoint
395
+
396
+ # 设置配置
397
+ xiaozhi config set mcpEndpoint "your-endpoint-url"
398
+ ```
399
+
400
+ ### 服务管理
401
+
402
+ ```bash
403
+ # 启动服务(前台)
368
404
  xiaozhi start
369
405
 
370
406
  # 后台启动服务
371
- xiaozhi start --daemon
407
+ xiaozhi start -d
372
408
 
373
- # 将后台服务转到前台运行
374
- xiaozhi attach
409
+ # 启动并打开 Web UI
410
+ xiaozhi start -u
411
+
412
+ # 以 MCP Server 模式启动(用于 Cursor 等客户端)
413
+ xiaozhi start --stdio
375
414
 
376
415
  # 查看服务状态
377
416
  xiaozhi status
@@ -382,13 +421,45 @@ xiaozhi stop
382
421
  # 重启服务
383
422
  xiaozhi restart
384
423
 
385
- # 列出所有使用的mcp服务
424
+ # 将后台服务转到前台运行
425
+ xiaozhi attach
426
+ ```
427
+
428
+ ### MCP 管理
429
+
430
+ ```bash
431
+ # 列出所有 MCP 服务
386
432
  xiaozhi mcp list
387
433
 
388
- # 列出所有mcp所提供的tools
434
+ # 列出所有 MCP 工具
389
435
  xiaozhi mcp list --tools
436
+
437
+ # 查看特定服务
438
+ xiaozhi mcp server calculator
390
439
  ```
391
440
 
441
+ ### 端点管理
442
+
443
+ ```bash
444
+ # 列出所有端点
445
+ xiaozhi endpoint list
446
+
447
+ # 添加端点
448
+ xiaozhi endpoint add "ws://new-server:8080"
449
+
450
+ # 移除端点
451
+ xiaozhi endpoint remove "ws://old-server:8080"
452
+ ```
453
+
454
+ ### Web UI
455
+
456
+ ```bash
457
+ # 启动 Web 配置界面
458
+ xiaozhi ui
459
+ ```
460
+
461
+ > 📖 **详细使用说明**: 查看 [CLI 使用手册](docs/CLI.md) 获取完整的命令参考和使用示例。
462
+
392
463
  ## 多接入点配置
393
464
 
394
465
  xiaozhi-client 支持同时连接多个小智 AI 接入点
@@ -424,13 +495,16 @@ xiaozhi-client 支持同时连接多个小智 AI 接入点
424
495
  xiaozhi endpoint list
425
496
 
426
497
  # 添加新的接入点
427
- xiaozhi endpoint add wss://api.xiaozhi.me/mcp/new-endpoint
498
+ xiaozhi endpoint add "wss://api.xiaozhi.me/mcp/new-endpoint"
428
499
 
429
500
  # 移除指定的接入点
430
- xiaozhi endpoint remove wss://api.xiaozhi.me/mcp/old-endpoint
501
+ xiaozhi endpoint remove "wss://api.xiaozhi.me/mcp/old-endpoint"
502
+
503
+ # 设置单个接入点(覆盖现有配置)
504
+ xiaozhi endpoint set "wss://api.xiaozhi.me/mcp/endpoint-1"
431
505
 
432
- # 设置接入点(覆盖现有配置)
433
- xiaozhi endpoint set wss://api.xiaozhi.me/mcp/endpoint-1 wss://api.xiaozhi.me/mcp/endpoint-2
506
+ # 或者使用 config 命令设置
507
+ xiaozhi config set mcpEndpoint "wss://api.xiaozhi.me/mcp/endpoint-1"
434
508
  ```
435
509
 
436
510
  ### 示例配置
@@ -668,9 +742,15 @@ xiaozhi-client 提供了一个现代化的 Web UI 界面,让配置 MCP 服务
668
742
  ### 启动 Web UI
669
743
 
670
744
  ```bash
745
+ # 启动 Web 配置界面
671
746
  xiaozhi ui
747
+
748
+ # 或者在启动服务时同时启动 Web UI
749
+ xiaozhi start -u
672
750
  ```
673
751
 
752
+ 启动后访问 <http://localhost:9999> 进行可视化配置。
753
+
674
754
  ## 作为 MCP Server 集成到其他客户端
675
755
 
676
756
  > 需升级至 `1.5.0` 及以上版本
@@ -734,13 +814,13 @@ npm install -g xiaozhi-client
734
814
 
735
815
  ```bash
736
816
  # 使用默认端口 3000
737
- xiaozhi start --server
817
+ xiaozhi start -s
738
818
 
739
819
  # 使用自定义端口
740
- xiaozhi start --server 8080
820
+ xiaozhi start -s 8080
741
821
 
742
822
  # 后台运行
743
- xiaozhi start --server --daemon
823
+ xiaozhi start -s -d
744
824
  ```
745
825
 
746
826
  第二步:在 客户端 中配置 SSE 连接:
@@ -15,6 +15,11 @@ declare enum ToolCallErrorCode {
15
15
  SERVICE_UNAVAILABLE = -32001,// 服务不可用
16
16
  TIMEOUT = -32002
17
17
  }
18
+ declare class ToolCallError extends Error {
19
+ code: ToolCallErrorCode;
20
+ data?: any | undefined;
21
+ constructor(code: ToolCallErrorCode, message: string, data?: any | undefined);
22
+ }
18
23
  interface ToolCallOptions {
19
24
  timeout?: number;
20
25
  retryAttempts?: number;
@@ -286,4 +291,4 @@ declare class ProxyMCPServer {
286
291
  };
287
292
  }
288
293
 
289
- export { ProxyMCPServer };
294
+ export { ProxyMCPServer, ToolCallError, ToolCallErrorCode };
@@ -1,3 +1,3 @@
1
- var y=Object.defineProperty;var m=(h,e)=>y(h,"name",{value:e,configurable:!0});import d from"ws";import*as c from"fs";import*as l from"path";import u from"chalk";import p from"pino";function S(h){let e=h.getFullYear(),t=String(h.getMonth()+1).padStart(2,"0"),i=String(h.getDate()).padStart(2,"0"),n=String(h.getHours()).padStart(2,"0"),s=String(h.getMinutes()).padStart(2,"0"),o=String(h.getSeconds()).padStart(2,"0");return`${e}-${t}-${i} ${n}:${s}:${o}`}m(S,"formatDateTime");var f=class{static{m(this,"Logger")}logFilePath=null;pinoInstance;isDaemonMode;maxLogFileSize=10*1024*1024;maxLogFiles=5;constructor(){this.isDaemonMode=process.env.XIAOZHI_DAEMON==="true",this.pinoInstance=this.createPinoInstance()}createPinoInstance(){let e=[];if(!this.isDaemonMode){let t=this.createOptimizedConsoleStream();e.push({level:"debug",stream:t})}return this.logFilePath&&e.push({level:"debug",stream:p.destination({dest:this.logFilePath,sync:!1,append:!0,mkdir:!0})}),e.length===0&&e.push({level:"debug",stream:p.destination({dest:"/dev/null"})}),p({level:"debug",timestamp:p.stdTimeFunctions?.isoTime||(()=>`,"time":${Date.now()}`),formatters:{level:m((t,i)=>({level:i}),"level")},base:null,serializers:{err:p.stdSerializers?.err||(t=>t)}},p.multistream(e,{dedupe:!0}))}createOptimizedConsoleStream(){let e=new Map([[20,{name:"DEBUG",color:u.gray}],[30,{name:"INFO",color:u.blue}],[40,{name:"WARN",color:u.yellow}],[50,{name:"ERROR",color:u.red}],[60,{name:"FATAL",color:u.red}]]);return{write:m(t=>{try{let i=JSON.parse(t),n=this.formatConsoleMessageOptimized(i,e);this.safeWrite(`${n}
2
- `)}catch{this.safeWrite(t)}},"write")}}safeWrite(e){try{process.stderr&&typeof process.stderr.write=="function"?process.stderr.write(e):console&&typeof console.error=="function"&&console.error(e.trim())}catch{}}formatConsoleMessageOptimized(e,t){let i=S(new Date),n=t.get(e.level)||{name:"UNKNOWN",color:m(g=>g,"color")},s=n.color(`[${n.name}]`),o=e.msg;if(e.args&&Array.isArray(e.args)){let g=e.args.map(r=>typeof r=="object"?JSON.stringify(r):String(r)).join(" ");o=`${o} ${g}`}return`[${i}] ${s} ${o}`}initLogFile(e){this.logFilePath=l.join(e,"xiaozhi.log"),this.rotateLogFileIfNeeded(),c.existsSync(this.logFilePath)||c.writeFileSync(this.logFilePath,""),this.pinoInstance=this.createPinoInstance()}enableFileLogging(e){e&&this.logFilePath&&(this.pinoInstance=this.createPinoInstance())}info(e,...t){typeof e=="string"?t.length===0?this.pinoInstance.info(e):this.pinoInstance.info({args:t},e):this.pinoInstance.info(e,t[0]||"")}success(e,...t){typeof e=="string"?t.length===0?this.pinoInstance.info(e):this.pinoInstance.info({args:t},e):this.pinoInstance.info(e,t[0]||"")}warn(e,...t){typeof e=="string"?t.length===0?this.pinoInstance.warn(e):this.pinoInstance.warn({args:t},e):this.pinoInstance.warn(e,t[0]||"")}error(e,...t){if(typeof e=="string")if(t.length===0)this.pinoInstance.error(e);else{let i=t.map(n=>n instanceof Error?{message:n.message,stack:n.stack,name:n.name,cause:n.cause}:n);this.pinoInstance.error({args:i},e)}else{let i=this.enhanceErrorObject(e);this.pinoInstance.error(i,t[0]||"")}}debug(e,...t){typeof e=="string"?t.length===0?this.pinoInstance.debug(e):this.pinoInstance.debug({args:t},e):this.pinoInstance.debug(e,t[0]||"")}log(e,...t){typeof e=="string"?t.length===0?this.pinoInstance.info(e):this.pinoInstance.info({args:t},e):this.pinoInstance.info(e,t[0]||"")}enhanceErrorObject(e){let t={...e};for(let[i,n]of Object.entries(t))n instanceof Error&&(t[i]={message:n.message,stack:n.stack,name:n.name,cause:n.cause});return t}rotateLogFileIfNeeded(){if(!(!this.logFilePath||!c.existsSync(this.logFilePath)))try{c.statSync(this.logFilePath).size>this.maxLogFileSize&&this.rotateLogFile()}catch{}}rotateLogFile(){if(this.logFilePath)try{let e=l.dirname(this.logFilePath),t=l.basename(this.logFilePath,".log");for(let n=this.maxLogFiles-1;n>=1;n--){let s=l.join(e,`${t}.${n}.log`),o=l.join(e,`${t}.${n+1}.log`);c.existsSync(s)&&(n===this.maxLogFiles-1?c.unlinkSync(s):c.renameSync(s,o))}let i=l.join(e,`${t}.1.log`);c.renameSync(this.logFilePath,i)}catch{}}cleanupOldLogs(){if(this.logFilePath)try{let e=l.dirname(this.logFilePath),t=l.basename(this.logFilePath,".log");for(let i=this.maxLogFiles+1;i<=this.maxLogFiles+10;i++){let n=l.join(e,`${t}.${i}.log`);c.existsSync(n)&&c.unlinkSync(n)}}catch{}}setLogFileOptions(e,t){this.maxLogFileSize=e,this.maxLogFiles=t}withTag(e){return this}close(){}},C=new f;var a=class extends Error{constructor(t,i,n){super(i);this.code=t;this.data=n;this.name="ToolCallError"}static{m(this,"ToolCallError")}},v=class{static{m(this,"ProxyMCPServer")}endpointUrl;ws=null;logger;isConnected=!1;serverInitialized=!1;tools=new Map;connectionState="disconnected";reconnectOptions;reconnectState={attempts:0,nextInterval:0,timer:null,lastError:null,isManualDisconnect:!1};connectionTimeout=null;performanceMetrics={totalCalls:0,successfulCalls:0,failedCalls:0,averageResponseTime:0,minResponseTime:Number.MAX_VALUE,maxResponseTime:0,successRate:0,lastUpdated:new Date};callRecords=[];maxCallRecords=100;retryConfig={maxAttempts:3,initialDelay:1e3,maxDelay:1e4,backoffMultiplier:2,retryableErrors:[-32001,-32002]};toolCallConfig={timeout:3e4,retryAttempts:3,retryDelay:1e3};constructor(e,t){this.endpointUrl=e,this.logger=C,this.reconnectOptions={enabled:!0,maxAttempts:10,initialInterval:3e3,maxInterval:3e4,backoffStrategy:"exponential",backoffMultiplier:1.5,timeout:1e4,jitter:!0,...t?.reconnect},this.reconnectState.nextInterval=this.reconnectOptions.initialInterval}setServiceManager(e){this.serviceManager=e,this.logger.info("\u5DF2\u8BBE\u7F6E MCPServiceManager"),this.syncToolsFromServiceManager()}syncToolsFromServiceManager(){let e=this.serviceManager;if(!e){this.logger.debug("MCPServiceManager \u672A\u8BBE\u7F6E\uFF0C\u8DF3\u8FC7\u5DE5\u5177\u540C\u6B65");return}try{let t=e.getAllTools(),i=new Map;for(let n of t)i.set(n.name,{name:n.name,description:n.description,inputSchema:n.inputSchema});this.tools=i,this.logger.info(`\u5DF2\u4ECE MCPServiceManager \u540C\u6B65 ${this.tools.size} \u4E2A\u5DE5\u5177`)}catch(t){this.logger.error(`\u540C\u6B65\u5DE5\u5177\u5931\u8D25: ${t instanceof Error?t.message:String(t)}`)}}addTool(e,t){return this.validateTool(e,t),this.tools.set(e,t),this.logger.debug(`\u5DE5\u5177 '${e}' \u5DF2\u6DFB\u52A0`),this}addTools(e){for(let[t,i]of Object.entries(e))this.addTool(t,i);return this}removeTool(e){return this.tools.delete(e)?this.logger.debug(`\u5DE5\u5177 '${e}' \u5DF2\u79FB\u9664`):this.logger.warn(`\u5C1D\u8BD5\u79FB\u9664\u4E0D\u5B58\u5728\u7684\u5DE5\u5177: '${e}'`),this}getTools(){try{this.syncToolsFromServiceManager()}catch{}return Array.from(this.tools.values())}hasTool(e){return this.tools.has(e)}validateTool(e,t){if(!e||typeof e!="string"||e.trim()==="")throw new Error("\u5DE5\u5177\u540D\u79F0\u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32");if(this.tools.has(e))throw new Error(`\u5DE5\u5177 '${e}' \u5DF2\u5B58\u5728`);if(!t||typeof t!="object")throw new Error("\u5DE5\u5177\u5FC5\u987B\u662F\u6709\u6548\u7684\u5BF9\u8C61");if(!t.name||typeof t.name!="string")throw new Error("\u5DE5\u5177\u5FC5\u987B\u5305\u542B\u6709\u6548\u7684 'name' \u5B57\u6BB5");if(!t.description||typeof t.description!="string")throw new Error("\u5DE5\u5177\u5FC5\u987B\u5305\u542B\u6709\u6548\u7684 'description' \u5B57\u6BB5");if(!t.inputSchema||typeof t.inputSchema!="object")throw new Error("\u5DE5\u5177\u5FC5\u987B\u5305\u542B\u6709\u6548\u7684 'inputSchema' \u5B57\u6BB5");if(!t.inputSchema.type||!t.inputSchema.properties)throw new Error("\u5DE5\u5177\u7684 inputSchema \u5FC5\u987B\u5305\u542B 'type' \u548C 'properties' \u5B57\u6BB5")}async connect(){if(this.tools.size===0)throw new Error("\u672A\u914D\u7F6E\u4EFB\u4F55\u5DE5\u5177\u3002\u8BF7\u5728\u8FDE\u63A5\u524D\u81F3\u5C11\u6DFB\u52A0\u4E00\u4E2A\u5DE5\u5177\u3002");if(this.connectionState==="connecting")throw new Error("\u8FDE\u63A5\u6B63\u5728\u8FDB\u884C\u4E2D\uFF0C\u8BF7\u7B49\u5F85\u8FDE\u63A5\u5B8C\u6210");return this.cleanupConnection(),this.reconnectState.isManualDisconnect=!1,this.attemptConnection()}async attemptConnection(){return this.connectionState="connecting",this.logger.info(`\u6B63\u5728\u8FDE\u63A5 MCP \u63A5\u5165\u70B9: ${this.endpointUrl} (\u5C1D\u8BD5 ${this.reconnectState.attempts+1}/${this.reconnectOptions.maxAttempts})`),new Promise((e,t)=>{this.connectionTimeout=setTimeout(()=>{let i=new Error(`\u8FDE\u63A5\u8D85\u65F6 (${this.reconnectOptions.timeout}ms)`);this.handleConnectionError(i),t(i)},this.reconnectOptions.timeout),this.ws=new d(this.endpointUrl),this.ws.on("open",()=>{this.handleConnectionSuccess(),e()}),this.ws.on("message",i=>{try{let n=JSON.parse(i.toString());this.handleMessage(n)}catch(n){this.logger.error("MCP \u6D88\u606F\u89E3\u6790\u9519\u8BEF:",n)}}),this.ws.on("close",(i,n)=>{this.handleConnectionClose(i,n.toString())}),this.ws.on("error",i=>{this.handleConnectionError(i),t(i)})})}handleConnectionSuccess(){this.connectionTimeout&&(clearTimeout(this.connectionTimeout),this.connectionTimeout=null),this.isConnected=!0,this.connectionState="connected",this.reconnectState.attempts=0,this.reconnectState.nextInterval=this.reconnectOptions.initialInterval,this.reconnectState.lastError=null,this.logger.info("MCP WebSocket \u8FDE\u63A5\u5DF2\u5EFA\u7ACB")}handleConnectionError(e){this.connectionTimeout&&(clearTimeout(this.connectionTimeout),this.connectionTimeout=null),this.reconnectState.lastError=e,this.logger.error("MCP WebSocket \u9519\u8BEF:",e.message),this.cleanupConnection()}handleConnectionClose(e,t){if(this.isConnected=!1,this.serverInitialized=!1,this.logger.info(`MCP \u8FDE\u63A5\u5DF2\u5173\u95ED (\u4EE3\u7801: ${e}, \u539F\u56E0: ${t})`),this.reconnectState.isManualDisconnect){this.connectionState="disconnected";return}this.shouldReconnect()?this.scheduleReconnect():(this.connectionState="failed",this.logger.warn(`\u5DF2\u8FBE\u5230\u6700\u5927\u91CD\u8FDE\u6B21\u6570 (${this.reconnectOptions.maxAttempts})\uFF0C\u505C\u6B62\u91CD\u8FDE`))}shouldReconnect(){return this.reconnectOptions.enabled&&this.reconnectState.attempts<this.reconnectOptions.maxAttempts&&!this.reconnectState.isManualDisconnect}scheduleReconnect(){this.connectionState="reconnecting",this.reconnectState.attempts++,this.calculateNextInterval(),this.logger.info(`\u5C06\u5728 ${this.reconnectState.nextInterval}ms \u540E\u8FDB\u884C\u7B2C ${this.reconnectState.attempts} \u6B21\u91CD\u8FDE`),this.reconnectState.timer&&clearTimeout(this.reconnectState.timer),this.reconnectState.timer=setTimeout(async()=>{try{await this.attemptConnection()}catch{}},this.reconnectState.nextInterval)}calculateNextInterval(){let e;switch(this.reconnectOptions.backoffStrategy){case"fixed":e=this.reconnectOptions.initialInterval;break;case"linear":e=this.reconnectOptions.initialInterval+this.reconnectState.attempts*this.reconnectOptions.backoffMultiplier*1e3;break;case"exponential":e=this.reconnectOptions.initialInterval*this.reconnectOptions.backoffMultiplier**(this.reconnectState.attempts-1);break;default:e=this.reconnectOptions.initialInterval}if(e=Math.min(e,this.reconnectOptions.maxInterval),this.reconnectOptions.jitter){let t=e*.1,i=(Math.random()-.5)*2*t;e+=i}this.reconnectState.nextInterval=Math.max(e,1e3)}cleanupConnection(){if(this.ws){this.ws.removeAllListeners();try{this.ws.readyState===d.OPEN?this.ws.close(1e3,"Cleaning up connection"):this.ws.readyState===d.CONNECTING&&this.ws.terminate()}catch(e){this.logger.debug("WebSocket \u5173\u95ED\u65F6\u51FA\u73B0\u9519\u8BEF\uFF08\u5DF2\u5FFD\u7565\uFF09:",e)}this.ws=null}this.connectionTimeout&&(clearTimeout(this.connectionTimeout),this.connectionTimeout=null),this.isConnected=!1,this.serverInitialized=!1}stopReconnect(){this.reconnectState.timer&&(clearTimeout(this.reconnectState.timer),this.reconnectState.timer=null)}handleMessage(e){this.logger.debug("\u6536\u5230 MCP \u6D88\u606F:",JSON.stringify(e,null,2)),e.method&&this.handleServerRequest(e)}handleServerRequest(e){switch(e.method){case"initialize":case"notifications/initialized":this.sendResponse(e.id,{protocolVersion:"2024-11-05",capabilities:{tools:{listChanged:!0},logging:{}},serverInfo:{name:"xiaozhi-mcp-server",version:"1.0.0"}}),this.serverInitialized=!0,this.logger.info("MCP \u670D\u52A1\u5668\u521D\u59CB\u5316\u5B8C\u6210");break;case"tools/list":{let t=this.getTools();this.sendResponse(e.id,{tools:t}),this.logger.info(`MCP \u5DE5\u5177\u5217\u8868\u5DF2\u53D1\u9001 (${t.length}\u4E2A\u5DE5\u5177)`);break}case"tools/call":{this.handleToolCall(e).catch(t=>{this.logger.error("\u5904\u7406\u5DE5\u5177\u8C03\u7528\u65F6\u53D1\u751F\u672A\u6355\u83B7\u9519\u8BEF:",t)});break}case"ping":this.sendResponse(e.id,{}),this.logger.debug("\u56DE\u5E94 MCP ping \u6D88\u606F");break;default:this.logger.warn(`\u672A\u77E5\u7684 MCP \u8BF7\u6C42: ${e.method}`)}}sendResponse(e,t){if(this.isConnected&&this.ws?.readyState===d.OPEN){let i={jsonrpc:"2.0",id:e,result:t};this.ws.send(JSON.stringify(i))}}getStatus(){return{connected:this.isConnected,initialized:this.serverInitialized,url:this.endpointUrl,availableTools:this.tools.size,connectionState:this.connectionState,reconnectAttempts:this.reconnectState.attempts,lastError:this.reconnectState.lastError?.message||null}}disconnect(){this.logger.info("\u4E3B\u52A8\u65AD\u5F00 MCP \u8FDE\u63A5"),this.reconnectState.isManualDisconnect=!0,this.stopReconnect(),this.cleanupConnection(),this.connectionState="disconnected"}async reconnect(){this.logger.info("\u624B\u52A8\u91CD\u8FDE MCP \u63A5\u5165\u70B9"),this.stopReconnect(),this.reconnectState.attempts=0,this.reconnectState.nextInterval=this.reconnectOptions.initialInterval,this.reconnectState.isManualDisconnect=!1,this.cleanupConnection(),await this.connect()}enableReconnect(){this.reconnectOptions.enabled=!0,this.logger.info("\u81EA\u52A8\u91CD\u8FDE\u5DF2\u542F\u7528")}disableReconnect(){this.reconnectOptions.enabled=!1,this.stopReconnect(),this.logger.info("\u81EA\u52A8\u91CD\u8FDE\u5DF2\u7981\u7528")}updateReconnectOptions(e){this.reconnectOptions={...this.reconnectOptions,...e},this.logger.info("\u91CD\u8FDE\u914D\u7F6E\u5DF2\u66F4\u65B0",e)}getReconnectOptions(){return{...this.reconnectOptions}}resetReconnectState(){this.stopReconnect(),this.reconnectState.attempts=0,this.reconnectState.nextInterval=this.reconnectOptions.initialInterval,this.reconnectState.lastError=null,this.logger.info("\u91CD\u8FDE\u72B6\u6001\u5DF2\u91CD\u7F6E")}async handleToolCall(e){let t=String(e.id||"unknown"),i=null;try{let n=this.validateToolCallParams(e.params);i=this.recordCallStart(n.name,t),this.logger.info(`\u5F00\u59CB\u5904\u7406\u5DE5\u5177\u8C03\u7528: ${n.name}`,{requestId:t,toolName:n.name,hasArguments:!!n.arguments});let s=this.serviceManager;if(!s)throw new a(-32001,"MCPServiceManager \u672A\u8BBE\u7F6E");let o=await this.executeToolWithRetry(s,n.name,n.arguments||{});this.sendResponse(t,{content:o.content||[{type:"text",text:JSON.stringify(o)}],isError:o.isError||!1}),i&&this.recordCallEnd(i,!0),this.logger.info(`\u5DE5\u5177\u8C03\u7528\u6210\u529F: ${n.name}`,{requestId:t,duration:i?.duration?`${i.duration}ms`:"unknown"})}catch(n){if(i){let s=n instanceof a?n.code:-32e3,o=n instanceof Error?n.message:"\u672A\u77E5\u9519\u8BEF";this.recordCallEnd(i,!1,s,o)}this.handleToolCallError(n,t,i?.duration||0)}}validateToolCallParams(e){if(!e||typeof e!="object")throw new a(-32602,"\u8BF7\u6C42\u53C2\u6570\u5FC5\u987B\u662F\u5BF9\u8C61");if(!e.name||typeof e.name!="string")throw new a(-32602,"\u5DE5\u5177\u540D\u79F0\u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32");if(e.arguments!==void 0&&(typeof e.arguments!="object"||Array.isArray(e.arguments)))throw new a(-32602,"\u5DE5\u5177\u53C2\u6570\u5FC5\u987B\u662F\u5BF9\u8C61");return{name:e.name,arguments:e.arguments}}async executeToolWithRetry(e,t,i){let n=null;for(let s=1;s<=this.retryConfig.maxAttempts;s++)try{return await this.executeToolWithTimeout(e,t,i,this.toolCallConfig.timeout)}catch(o){if(o instanceof a?n=o:n=new a(-32e3,o instanceof Error?o.message:"\u672A\u77E5\u9519\u8BEF"),this.retryConfig.retryableErrors.includes(n.code)&&s<this.retryConfig.maxAttempts){let g=Math.min(this.retryConfig.initialDelay*this.retryConfig.backoffMultiplier**(s-1),this.retryConfig.maxDelay);this.logger.warn(`\u5DE5\u5177\u8C03\u7528\u5931\u8D25\uFF0C\u5C06\u5728 ${g}ms \u540E\u91CD\u8BD5 (${s}/${this.retryConfig.maxAttempts})`,{toolName:t,error:n.message,attempt:s,delay:g}),await new Promise(r=>setTimeout(r,g));continue}break}throw n}async executeToolWithTimeout(e,t,i,n=3e4){return new Promise((s,o)=>{let g=setTimeout(()=>{o(new a(-32002,`\u5DE5\u5177\u8C03\u7528\u8D85\u65F6 (${n}ms): ${t}`))},n);e.callTool(t,i).then(r=>{clearTimeout(g),s(r)}).catch(r=>{clearTimeout(g),r.message?.includes("\u672A\u627E\u5230\u5DE5\u5177")?o(new a(-32601,`\u5DE5\u5177\u4E0D\u5B58\u5728: ${t}`)):r.message?.includes("\u670D\u52A1")&&r.message?.includes("\u4E0D\u53EF\u7528")?o(new a(-32001,r.message)):r.message?.includes("\u6682\u65F6\u4E0D\u53EF\u7528")?o(new a(-32001,r.message)):r.message?.includes("\u6301\u7EED\u4E0D\u53EF\u7528")?o(new a(-32001,r.message)):o(new a(-32e3,`\u5DE5\u5177\u6267\u884C\u5931\u8D25: ${r.message}`))})})}handleToolCallError(e,t,i){let n;e instanceof a?n={code:e.code,message:e.message,data:e.data}:n={code:-32e3,message:e?.message||"\u672A\u77E5\u9519\u8BEF",data:{originalError:e?.toString()||"null"}},this.sendErrorResponse(t,n),this.logger.error("\u5DE5\u5177\u8C03\u7528\u5931\u8D25",{requestId:t,duration:`${i}ms`,error:n})}sendErrorResponse(e,t){if(this.isConnected&&this.ws?.readyState===d.OPEN){let i={jsonrpc:"2.0",id:e,error:t};this.ws.send(JSON.stringify(i)),this.logger.debug("\u5DF2\u53D1\u9001\u9519\u8BEF\u54CD\u5E94:",i)}}recordCallStart(e,t){let i={id:t,toolName:e,startTime:new Date,success:!1};return this.callRecords.push(i),this.callRecords.length>this.maxCallRecords&&this.callRecords.shift(),i}recordCallEnd(e,t,i,n){e.endTime=new Date,e.duration=e.endTime.getTime()-e.startTime.getTime(),e.success=t,e.errorCode=i,e.errorMessage=n,this.updatePerformanceMetrics(e)}updatePerformanceMetrics(e){if(this.performanceMetrics.totalCalls++,e.success?this.performanceMetrics.successfulCalls++:this.performanceMetrics.failedCalls++,e.duration!==void 0){e.duration<this.performanceMetrics.minResponseTime&&(this.performanceMetrics.minResponseTime=e.duration),e.duration>this.performanceMetrics.maxResponseTime&&(this.performanceMetrics.maxResponseTime=e.duration);let t=this.callRecords.filter(n=>n.duration!==void 0).reduce((n,s)=>n+(s.duration||0),0),i=this.callRecords.filter(n=>n.duration!==void 0).length;this.performanceMetrics.averageResponseTime=i>0?t/i:0}this.performanceMetrics.successRate=this.performanceMetrics.totalCalls>0?this.performanceMetrics.successfulCalls/this.performanceMetrics.totalCalls*100:0,this.performanceMetrics.lastUpdated=new Date}getPerformanceMetrics(){return{...this.performanceMetrics}}getCallRecords(e){let t=[...this.callRecords].reverse();return e?t.slice(0,e):t}resetPerformanceMetrics(){this.performanceMetrics={totalCalls:0,successfulCalls:0,failedCalls:0,averageResponseTime:0,minResponseTime:Number.MAX_VALUE,maxResponseTime:0,successRate:0,lastUpdated:new Date},this.callRecords=[]}updateToolCallConfig(e){this.toolCallConfig={...this.toolCallConfig,...e},this.logger.info("\u5DE5\u5177\u8C03\u7528\u914D\u7F6E\u5DF2\u66F4\u65B0",this.toolCallConfig)}updateRetryConfig(e){this.retryConfig={...this.retryConfig,...e},this.logger.info("\u91CD\u8BD5\u914D\u7F6E\u5DF2\u66F4\u65B0",this.retryConfig)}getConfiguration(){return{toolCall:{...this.toolCallConfig},retry:{...this.retryConfig}}}getEnhancedStatus(){return{connected:this.isConnected,initialized:this.serverInitialized,url:this.endpointUrl,availableTools:this.tools.size,connectionState:this.connectionState,reconnectAttempts:this.reconnectState.attempts,lastError:this.reconnectState.lastError?.message||null,performance:this.getPerformanceMetrics(),configuration:this.getConfiguration()}}};export{v as ProxyMCPServer};
1
+ var y=Object.defineProperty;var p=(h,e)=>y(h,"name",{value:e,configurable:!0});import m from"ws";import*as c from"fs";import*as l from"path";import u from"chalk";import d from"pino";function S(h){let e=h.getFullYear(),t=String(h.getMonth()+1).padStart(2,"0"),i=String(h.getDate()).padStart(2,"0"),n=String(h.getHours()).padStart(2,"0"),o=String(h.getMinutes()).padStart(2,"0"),s=String(h.getSeconds()).padStart(2,"0");return`${e}-${t}-${i} ${n}:${o}:${s}`}p(S,"formatDateTime");var f=class{static{p(this,"Logger")}logFilePath=null;pinoInstance;isDaemonMode;maxLogFileSize=10*1024*1024;maxLogFiles=5;constructor(){this.isDaemonMode=process.env.XIAOZHI_DAEMON==="true",this.pinoInstance=this.createPinoInstance()}createPinoInstance(){let e=[];if(!this.isDaemonMode){let t=this.createOptimizedConsoleStream();e.push({level:"debug",stream:t})}return this.logFilePath&&e.push({level:"debug",stream:d.destination({dest:this.logFilePath,sync:!1,append:!0,mkdir:!0})}),e.length===0&&e.push({level:"debug",stream:d.destination({dest:"/dev/null"})}),d({level:"debug",timestamp:d.stdTimeFunctions?.isoTime||(()=>`,"time":${Date.now()}`),formatters:{level:p((t,i)=>({level:i}),"level")},base:null,serializers:{err:d.stdSerializers?.err||(t=>t)}},d.multistream(e,{dedupe:!0}))}createOptimizedConsoleStream(){let e=new Map([[20,{name:"DEBUG",color:u.gray}],[30,{name:"INFO",color:u.blue}],[40,{name:"WARN",color:u.yellow}],[50,{name:"ERROR",color:u.red}],[60,{name:"FATAL",color:u.red}]]);return{write:p(t=>{try{let i=JSON.parse(t),n=this.formatConsoleMessageOptimized(i,e);this.safeWrite(`${n}
2
+ `)}catch{this.safeWrite(t)}},"write")}}safeWrite(e){try{process.stderr&&typeof process.stderr.write=="function"?process.stderr.write(e):console&&typeof console.error=="function"&&console.error(e.trim())}catch{}}formatConsoleMessageOptimized(e,t){let i=S(new Date),n=t.get(e.level)||{name:"UNKNOWN",color:p(g=>g,"color")},o=n.color(`[${n.name}]`),s=e.msg;if(e.args&&Array.isArray(e.args)){let g=e.args.map(r=>typeof r=="object"?JSON.stringify(r):String(r)).join(" ");s=`${s} ${g}`}return`[${i}] ${o} ${s}`}initLogFile(e){this.logFilePath=l.join(e,"xiaozhi.log"),this.rotateLogFileIfNeeded(),c.existsSync(this.logFilePath)||c.writeFileSync(this.logFilePath,""),this.pinoInstance=this.createPinoInstance()}enableFileLogging(e){e&&this.logFilePath&&(this.pinoInstance=this.createPinoInstance())}info(e,...t){typeof e=="string"?t.length===0?this.pinoInstance.info(e):this.pinoInstance.info({args:t},e):this.pinoInstance.info(e,t[0]||"")}success(e,...t){typeof e=="string"?t.length===0?this.pinoInstance.info(e):this.pinoInstance.info({args:t},e):this.pinoInstance.info(e,t[0]||"")}warn(e,...t){typeof e=="string"?t.length===0?this.pinoInstance.warn(e):this.pinoInstance.warn({args:t},e):this.pinoInstance.warn(e,t[0]||"")}error(e,...t){if(typeof e=="string")if(t.length===0)this.pinoInstance.error(e);else{let i=t.map(n=>n instanceof Error?{message:n.message,stack:n.stack,name:n.name,cause:n.cause}:n);this.pinoInstance.error({args:i},e)}else{let i=this.enhanceErrorObject(e);this.pinoInstance.error(i,t[0]||"")}}debug(e,...t){typeof e=="string"?t.length===0?this.pinoInstance.debug(e):this.pinoInstance.debug({args:t},e):this.pinoInstance.debug(e,t[0]||"")}log(e,...t){typeof e=="string"?t.length===0?this.pinoInstance.info(e):this.pinoInstance.info({args:t},e):this.pinoInstance.info(e,t[0]||"")}enhanceErrorObject(e){let t={...e};for(let[i,n]of Object.entries(t))n instanceof Error&&(t[i]={message:n.message,stack:n.stack,name:n.name,cause:n.cause});return t}rotateLogFileIfNeeded(){if(!(!this.logFilePath||!c.existsSync(this.logFilePath)))try{c.statSync(this.logFilePath).size>this.maxLogFileSize&&this.rotateLogFile()}catch{}}rotateLogFile(){if(this.logFilePath)try{let e=l.dirname(this.logFilePath),t=l.basename(this.logFilePath,".log");for(let n=this.maxLogFiles-1;n>=1;n--){let o=l.join(e,`${t}.${n}.log`),s=l.join(e,`${t}.${n+1}.log`);c.existsSync(o)&&(n===this.maxLogFiles-1?c.unlinkSync(o):c.renameSync(o,s))}let i=l.join(e,`${t}.1.log`);c.renameSync(this.logFilePath,i)}catch{}}cleanupOldLogs(){if(this.logFilePath)try{let e=l.dirname(this.logFilePath),t=l.basename(this.logFilePath,".log");for(let i=this.maxLogFiles+1;i<=this.maxLogFiles+10;i++){let n=l.join(e,`${t}.${i}.log`);c.existsSync(n)&&c.unlinkSync(n)}}catch{}}setLogFileOptions(e,t){this.maxLogFileSize=e,this.maxLogFiles=t}withTag(e){return this}close(){}},C=new f;var T=(o=>(o[o.INVALID_PARAMS=-32602]="INVALID_PARAMS",o[o.TOOL_NOT_FOUND=-32601]="TOOL_NOT_FOUND",o[o.TOOL_EXECUTION_ERROR=-32e3]="TOOL_EXECUTION_ERROR",o[o.SERVICE_UNAVAILABLE=-32001]="SERVICE_UNAVAILABLE",o[o.TIMEOUT=-32002]="TIMEOUT",o))(T||{}),a=class extends Error{constructor(t,i,n){super(i);this.code=t;this.data=n;this.name="ToolCallError"}static{p(this,"ToolCallError")}},v=class{static{p(this,"ProxyMCPServer")}endpointUrl;ws=null;logger;isConnected=!1;serverInitialized=!1;tools=new Map;connectionState="disconnected";reconnectOptions;reconnectState={attempts:0,nextInterval:0,timer:null,lastError:null,isManualDisconnect:!1};connectionTimeout=null;performanceMetrics={totalCalls:0,successfulCalls:0,failedCalls:0,averageResponseTime:0,minResponseTime:Number.MAX_VALUE,maxResponseTime:0,successRate:0,lastUpdated:new Date};callRecords=[];maxCallRecords=100;retryConfig={maxAttempts:3,initialDelay:1e3,maxDelay:1e4,backoffMultiplier:2,retryableErrors:[-32001,-32002]};toolCallConfig={timeout:3e4,retryAttempts:3,retryDelay:1e3};constructor(e,t){this.endpointUrl=e,this.logger=C,this.reconnectOptions={enabled:!0,maxAttempts:10,initialInterval:3e3,maxInterval:3e4,backoffStrategy:"exponential",backoffMultiplier:1.5,timeout:1e4,jitter:!0,...t?.reconnect},this.reconnectState.nextInterval=this.reconnectOptions.initialInterval}setServiceManager(e){this.serviceManager=e,this.logger.info("\u5DF2\u8BBE\u7F6E MCPServiceManager"),this.syncToolsFromServiceManager()}syncToolsFromServiceManager(){let e=this.serviceManager;if(!e){this.logger.debug("MCPServiceManager \u672A\u8BBE\u7F6E\uFF0C\u8DF3\u8FC7\u5DE5\u5177\u540C\u6B65");return}try{let t=e.getAllTools(),i=new Map;for(let n of t)i.set(n.name,{name:n.name,description:n.description,inputSchema:n.inputSchema});this.tools=i,this.logger.info(`\u5DF2\u4ECE MCPServiceManager \u540C\u6B65 ${this.tools.size} \u4E2A\u5DE5\u5177`)}catch(t){this.logger.error(`\u540C\u6B65\u5DE5\u5177\u5931\u8D25: ${t instanceof Error?t.message:String(t)}`)}}addTool(e,t){return this.validateTool(e,t),this.tools.set(e,t),this.logger.debug(`\u5DE5\u5177 '${e}' \u5DF2\u6DFB\u52A0`),this}addTools(e){for(let[t,i]of Object.entries(e))this.addTool(t,i);return this}removeTool(e){return this.tools.delete(e)?this.logger.debug(`\u5DE5\u5177 '${e}' \u5DF2\u79FB\u9664`):this.logger.warn(`\u5C1D\u8BD5\u79FB\u9664\u4E0D\u5B58\u5728\u7684\u5DE5\u5177: '${e}'`),this}getTools(){try{this.syncToolsFromServiceManager()}catch{}return Array.from(this.tools.values())}hasTool(e){return this.tools.has(e)}validateTool(e,t){if(!e||typeof e!="string"||e.trim()==="")throw new Error("\u5DE5\u5177\u540D\u79F0\u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32");if(this.tools.has(e))throw new Error(`\u5DE5\u5177 '${e}' \u5DF2\u5B58\u5728`);if(!t||typeof t!="object")throw new Error("\u5DE5\u5177\u5FC5\u987B\u662F\u6709\u6548\u7684\u5BF9\u8C61");if(!t.name||typeof t.name!="string")throw new Error("\u5DE5\u5177\u5FC5\u987B\u5305\u542B\u6709\u6548\u7684 'name' \u5B57\u6BB5");if(!t.description||typeof t.description!="string")throw new Error("\u5DE5\u5177\u5FC5\u987B\u5305\u542B\u6709\u6548\u7684 'description' \u5B57\u6BB5");if(!t.inputSchema||typeof t.inputSchema!="object")throw new Error("\u5DE5\u5177\u5FC5\u987B\u5305\u542B\u6709\u6548\u7684 'inputSchema' \u5B57\u6BB5");if(!t.inputSchema.type||!t.inputSchema.properties)throw new Error("\u5DE5\u5177\u7684 inputSchema \u5FC5\u987B\u5305\u542B 'type' \u548C 'properties' \u5B57\u6BB5")}async connect(){if(this.tools.size===0)throw new Error("\u672A\u914D\u7F6E\u4EFB\u4F55\u5DE5\u5177\u3002\u8BF7\u5728\u8FDE\u63A5\u524D\u81F3\u5C11\u6DFB\u52A0\u4E00\u4E2A\u5DE5\u5177\u3002");if(this.connectionState==="connecting")throw new Error("\u8FDE\u63A5\u6B63\u5728\u8FDB\u884C\u4E2D\uFF0C\u8BF7\u7B49\u5F85\u8FDE\u63A5\u5B8C\u6210");return this.cleanupConnection(),this.reconnectState.isManualDisconnect=!1,this.attemptConnection()}async attemptConnection(){return this.connectionState="connecting",this.logger.info(`\u6B63\u5728\u8FDE\u63A5 MCP \u63A5\u5165\u70B9: ${this.endpointUrl} (\u5C1D\u8BD5 ${this.reconnectState.attempts+1}/${this.reconnectOptions.maxAttempts})`),new Promise((e,t)=>{this.connectionTimeout=setTimeout(()=>{let i=new Error(`\u8FDE\u63A5\u8D85\u65F6 (${this.reconnectOptions.timeout}ms)`);this.handleConnectionError(i),t(i)},this.reconnectOptions.timeout),this.ws=new m(this.endpointUrl),this.ws.on("open",()=>{this.handleConnectionSuccess(),e()}),this.ws.on("message",i=>{try{let n=JSON.parse(i.toString());this.handleMessage(n)}catch(n){this.logger.error("MCP \u6D88\u606F\u89E3\u6790\u9519\u8BEF:",n)}}),this.ws.on("close",(i,n)=>{this.handleConnectionClose(i,n.toString())}),this.ws.on("error",i=>{this.handleConnectionError(i),t(i)})})}handleConnectionSuccess(){this.connectionTimeout&&(clearTimeout(this.connectionTimeout),this.connectionTimeout=null),this.isConnected=!0,this.connectionState="connected",this.reconnectState.attempts=0,this.reconnectState.nextInterval=this.reconnectOptions.initialInterval,this.reconnectState.lastError=null,this.logger.info("MCP WebSocket \u8FDE\u63A5\u5DF2\u5EFA\u7ACB")}handleConnectionError(e){this.connectionTimeout&&(clearTimeout(this.connectionTimeout),this.connectionTimeout=null),this.reconnectState.lastError=e,this.logger.error("MCP WebSocket \u9519\u8BEF:",e.message),this.cleanupConnection()}handleConnectionClose(e,t){if(this.isConnected=!1,this.serverInitialized=!1,this.logger.info(`MCP \u8FDE\u63A5\u5DF2\u5173\u95ED (\u4EE3\u7801: ${e}, \u539F\u56E0: ${t})`),this.reconnectState.isManualDisconnect){this.connectionState="disconnected";return}this.shouldReconnect()?this.scheduleReconnect():(this.connectionState="failed",this.logger.warn(`\u5DF2\u8FBE\u5230\u6700\u5927\u91CD\u8FDE\u6B21\u6570 (${this.reconnectOptions.maxAttempts})\uFF0C\u505C\u6B62\u91CD\u8FDE`))}shouldReconnect(){return this.reconnectOptions.enabled&&this.reconnectState.attempts<this.reconnectOptions.maxAttempts&&!this.reconnectState.isManualDisconnect}scheduleReconnect(){this.connectionState="reconnecting",this.reconnectState.attempts++,this.calculateNextInterval(),this.logger.info(`\u5C06\u5728 ${this.reconnectState.nextInterval}ms \u540E\u8FDB\u884C\u7B2C ${this.reconnectState.attempts} \u6B21\u91CD\u8FDE`),this.reconnectState.timer&&clearTimeout(this.reconnectState.timer),this.reconnectState.timer=setTimeout(async()=>{try{await this.attemptConnection()}catch{}},this.reconnectState.nextInterval)}calculateNextInterval(){let e;switch(this.reconnectOptions.backoffStrategy){case"fixed":e=this.reconnectOptions.initialInterval;break;case"linear":e=this.reconnectOptions.initialInterval+this.reconnectState.attempts*this.reconnectOptions.backoffMultiplier*1e3;break;case"exponential":e=this.reconnectOptions.initialInterval*this.reconnectOptions.backoffMultiplier**(this.reconnectState.attempts-1);break;default:e=this.reconnectOptions.initialInterval}if(e=Math.min(e,this.reconnectOptions.maxInterval),this.reconnectOptions.jitter){let t=e*.1,i=(Math.random()-.5)*2*t;e+=i}this.reconnectState.nextInterval=Math.max(e,1e3)}cleanupConnection(){if(this.ws){this.ws.removeAllListeners();try{this.ws.readyState===m.OPEN?this.ws.close(1e3,"Cleaning up connection"):this.ws.readyState===m.CONNECTING&&this.ws.terminate()}catch(e){this.logger.debug("WebSocket \u5173\u95ED\u65F6\u51FA\u73B0\u9519\u8BEF\uFF08\u5DF2\u5FFD\u7565\uFF09:",e)}this.ws=null}this.connectionTimeout&&(clearTimeout(this.connectionTimeout),this.connectionTimeout=null),this.isConnected=!1,this.serverInitialized=!1}stopReconnect(){this.reconnectState.timer&&(clearTimeout(this.reconnectState.timer),this.reconnectState.timer=null)}handleMessage(e){this.logger.debug("\u6536\u5230 MCP \u6D88\u606F:",JSON.stringify(e,null,2)),e.method&&this.handleServerRequest(e)}handleServerRequest(e){switch(e.method){case"initialize":case"notifications/initialized":this.sendResponse(e.id,{protocolVersion:"2024-11-05",capabilities:{tools:{listChanged:!0},logging:{}},serverInfo:{name:"xiaozhi-mcp-server",version:"1.0.0"}}),this.serverInitialized=!0,this.logger.info("MCP \u670D\u52A1\u5668\u521D\u59CB\u5316\u5B8C\u6210");break;case"tools/list":{let t=this.getTools();this.sendResponse(e.id,{tools:t}),this.logger.info(`MCP \u5DE5\u5177\u5217\u8868\u5DF2\u53D1\u9001 (${t.length}\u4E2A\u5DE5\u5177)`);break}case"tools/call":{this.handleToolCall(e).catch(t=>{this.logger.error("\u5904\u7406\u5DE5\u5177\u8C03\u7528\u65F6\u53D1\u751F\u672A\u6355\u83B7\u9519\u8BEF:",t)});break}case"ping":this.sendResponse(e.id,{}),this.logger.debug("\u56DE\u5E94 MCP ping \u6D88\u606F");break;default:this.logger.warn(`\u672A\u77E5\u7684 MCP \u8BF7\u6C42: ${e.method}`)}}sendResponse(e,t){if(this.logger.debug(`\u5C1D\u8BD5\u53D1\u9001\u54CD\u5E94: id=${e}, isConnected=${this.isConnected}, wsReadyState=${this.ws?.readyState}`),this.isConnected&&this.ws?.readyState===m.OPEN){let i={jsonrpc:"2.0",id:e,result:t};try{this.ws.send(JSON.stringify(i)),this.logger.info(`\u54CD\u5E94\u5DF2\u53D1\u9001: id=${e}`,{responseSize:JSON.stringify(i).length})}catch(n){this.logger.error(`\u53D1\u9001\u54CD\u5E94\u5931\u8D25: id=${e}`,n)}}else this.logger.error(`\u65E0\u6CD5\u53D1\u9001\u54CD\u5E94: id=${e}, \u8FDE\u63A5\u72B6\u6001\u68C0\u67E5\u5931\u8D25`,{isConnected:this.isConnected,wsReadyState:this.ws?.readyState,wsReadyStateText:this.ws?.readyState===m.OPEN?"OPEN":this.ws?.readyState===m.CONNECTING?"CONNECTING":this.ws?.readyState===m.CLOSING?"CLOSING":this.ws?.readyState===m.CLOSED?"CLOSED":"UNKNOWN"}),(!this.isConnected||this.ws?.readyState!==m.OPEN)&&(this.logger.warn(`\u5C1D\u8BD5\u91CD\u65B0\u8FDE\u63A5\u4EE5\u53D1\u9001\u54CD\u5E94: id=${e}`),this.scheduleReconnect())}getStatus(){return{connected:this.isConnected,initialized:this.serverInitialized,url:this.endpointUrl,availableTools:this.tools.size,connectionState:this.connectionState,reconnectAttempts:this.reconnectState.attempts,lastError:this.reconnectState.lastError?.message||null}}disconnect(){this.logger.info("\u4E3B\u52A8\u65AD\u5F00 MCP \u8FDE\u63A5"),this.reconnectState.isManualDisconnect=!0,this.stopReconnect(),this.cleanupConnection(),this.connectionState="disconnected"}async reconnect(){this.logger.info("\u624B\u52A8\u91CD\u8FDE MCP \u63A5\u5165\u70B9"),this.stopReconnect(),this.reconnectState.attempts=0,this.reconnectState.nextInterval=this.reconnectOptions.initialInterval,this.reconnectState.isManualDisconnect=!1,this.cleanupConnection(),await this.connect()}enableReconnect(){this.reconnectOptions.enabled=!0,this.logger.info("\u81EA\u52A8\u91CD\u8FDE\u5DF2\u542F\u7528")}disableReconnect(){this.reconnectOptions.enabled=!1,this.stopReconnect(),this.logger.info("\u81EA\u52A8\u91CD\u8FDE\u5DF2\u7981\u7528")}updateReconnectOptions(e){this.reconnectOptions={...this.reconnectOptions,...e},this.logger.info("\u91CD\u8FDE\u914D\u7F6E\u5DF2\u66F4\u65B0",e)}getReconnectOptions(){return{...this.reconnectOptions}}resetReconnectState(){this.stopReconnect(),this.reconnectState.attempts=0,this.reconnectState.nextInterval=this.reconnectOptions.initialInterval,this.reconnectState.lastError=null,this.logger.info("\u91CD\u8FDE\u72B6\u6001\u5DF2\u91CD\u7F6E")}async handleToolCall(e){if(e.id===void 0||e.id===null)throw new a(-32602,"\u8BF7\u6C42 ID \u4E0D\u80FD\u4E3A\u7A7A");let t=e.id,i=null;try{let n=this.validateToolCallParams(e.params);i=this.recordCallStart(n.name,t),this.logger.info(`\u5F00\u59CB\u5904\u7406\u5DE5\u5177\u8C03\u7528: ${n.name}`,{requestId:t,toolName:n.name,hasArguments:!!n.arguments});let o=this.serviceManager;if(!o)throw new a(-32001,"MCPServiceManager \u672A\u8BBE\u7F6E");let s=await this.executeToolWithRetry(o,n.name,n.arguments||{});this.sendResponse(t,{content:s.content||[{type:"text",text:JSON.stringify(s)}],isError:s.isError||!1}),i&&this.recordCallEnd(i,!0),this.logger.info(`\u5DE5\u5177\u8C03\u7528\u6210\u529F: ${n.name}`,{requestId:t,duration:i?.duration?`${i.duration}ms`:"unknown"})}catch(n){if(i){let o=n instanceof a?n.code:-32e3,s=n instanceof Error?n.message:"\u672A\u77E5\u9519\u8BEF";this.recordCallEnd(i,!1,o,s)}this.handleToolCallError(n,t,i?.duration||0)}}validateToolCallParams(e){if(!e||typeof e!="object")throw new a(-32602,"\u8BF7\u6C42\u53C2\u6570\u5FC5\u987B\u662F\u5BF9\u8C61");if(!e.name||typeof e.name!="string")throw new a(-32602,"\u5DE5\u5177\u540D\u79F0\u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32");if(e.arguments!==void 0&&(typeof e.arguments!="object"||Array.isArray(e.arguments)))throw new a(-32602,"\u5DE5\u5177\u53C2\u6570\u5FC5\u987B\u662F\u5BF9\u8C61");return{name:e.name,arguments:e.arguments}}async executeToolWithRetry(e,t,i){let n=null;for(let o=1;o<=this.retryConfig.maxAttempts;o++)try{return await this.executeToolWithTimeout(e,t,i,this.toolCallConfig.timeout)}catch(s){if(s instanceof a?n=s:n=new a(-32e3,s instanceof Error?s.message:"\u672A\u77E5\u9519\u8BEF"),this.retryConfig.retryableErrors.includes(n.code)&&o<this.retryConfig.maxAttempts){let g=Math.min(this.retryConfig.initialDelay*this.retryConfig.backoffMultiplier**(o-1),this.retryConfig.maxDelay);this.logger.warn(`\u5DE5\u5177\u8C03\u7528\u5931\u8D25\uFF0C\u5C06\u5728 ${g}ms \u540E\u91CD\u8BD5 (${o}/${this.retryConfig.maxAttempts})`,{toolName:t,error:n.message,attempt:o,delay:g}),await new Promise(r=>setTimeout(r,g));continue}break}throw n}async executeToolWithTimeout(e,t,i,n=3e4){return new Promise((o,s)=>{let g=setTimeout(()=>{s(new a(-32002,`\u5DE5\u5177\u8C03\u7528\u8D85\u65F6 (${n}ms): ${t}`))},n);e.callTool(t,i).then(r=>{clearTimeout(g),o(r)}).catch(r=>{clearTimeout(g),r.message?.includes("\u672A\u627E\u5230\u5DE5\u5177")?s(new a(-32601,`\u5DE5\u5177\u4E0D\u5B58\u5728: ${t}`)):r.message?.includes("\u670D\u52A1")&&r.message?.includes("\u4E0D\u53EF\u7528")?s(new a(-32001,r.message)):r.message?.includes("\u6682\u65F6\u4E0D\u53EF\u7528")?s(new a(-32001,r.message)):r.message?.includes("\u6301\u7EED\u4E0D\u53EF\u7528")?s(new a(-32001,r.message)):s(new a(-32e3,`\u5DE5\u5177\u6267\u884C\u5931\u8D25: ${r.message}`))})})}handleToolCallError(e,t,i){let n;e instanceof a?n={code:e.code,message:e.message,data:e.data}:n={code:-32e3,message:e?.message||"\u672A\u77E5\u9519\u8BEF",data:{originalError:e?.toString()||"null"}},this.sendErrorResponse(t,n),this.logger.error("\u5DE5\u5177\u8C03\u7528\u5931\u8D25",{requestId:t,duration:`${i}ms`,error:n})}sendErrorResponse(e,t){if(this.isConnected&&this.ws?.readyState===m.OPEN){let i={jsonrpc:"2.0",id:e,error:t};this.ws.send(JSON.stringify(i)),this.logger.debug("\u5DF2\u53D1\u9001\u9519\u8BEF\u54CD\u5E94:",i)}}recordCallStart(e,t){let i={id:String(t),toolName:e,startTime:new Date,success:!1};return this.callRecords.push(i),this.callRecords.length>this.maxCallRecords&&this.callRecords.shift(),i}recordCallEnd(e,t,i,n){e.endTime=new Date,e.duration=e.endTime.getTime()-e.startTime.getTime(),e.success=t,e.errorCode=i,e.errorMessage=n,this.updatePerformanceMetrics(e)}updatePerformanceMetrics(e){if(this.performanceMetrics.totalCalls++,e.success?this.performanceMetrics.successfulCalls++:this.performanceMetrics.failedCalls++,e.duration!==void 0){e.duration<this.performanceMetrics.minResponseTime&&(this.performanceMetrics.minResponseTime=e.duration),e.duration>this.performanceMetrics.maxResponseTime&&(this.performanceMetrics.maxResponseTime=e.duration);let t=this.callRecords.filter(n=>n.duration!==void 0).reduce((n,o)=>n+(o.duration||0),0),i=this.callRecords.filter(n=>n.duration!==void 0).length;this.performanceMetrics.averageResponseTime=i>0?t/i:0}this.performanceMetrics.successRate=this.performanceMetrics.totalCalls>0?this.performanceMetrics.successfulCalls/this.performanceMetrics.totalCalls*100:0,this.performanceMetrics.lastUpdated=new Date}getPerformanceMetrics(){return{...this.performanceMetrics}}getCallRecords(e){let t=[...this.callRecords].reverse();return e?t.slice(0,e):t}resetPerformanceMetrics(){this.performanceMetrics={totalCalls:0,successfulCalls:0,failedCalls:0,averageResponseTime:0,minResponseTime:Number.MAX_VALUE,maxResponseTime:0,successRate:0,lastUpdated:new Date},this.callRecords=[]}updateToolCallConfig(e){this.toolCallConfig={...this.toolCallConfig,...e},this.logger.info("\u5DE5\u5177\u8C03\u7528\u914D\u7F6E\u5DF2\u66F4\u65B0",this.toolCallConfig)}updateRetryConfig(e){this.retryConfig={...this.retryConfig,...e},this.logger.info("\u91CD\u8BD5\u914D\u7F6E\u5DF2\u66F4\u65B0",this.retryConfig)}getConfiguration(){return{toolCall:{...this.toolCallConfig},retry:{...this.retryConfig}}}getEnhancedStatus(){return{connected:this.isConnected,initialized:this.serverInitialized,url:this.endpointUrl,availableTools:this.tools.size,connectionState:this.connectionState,reconnectAttempts:this.reconnectState.attempts,lastError:this.reconnectState.lastError?.message||null,performance:this.getPerformanceMetrics(),configuration:this.getConfiguration()}}};export{v as ProxyMCPServer,a as ToolCallError,T as ToolCallErrorCode};
3
3
  //# sourceMappingURL=ProxyMCPServer.js.map