@easbot/mcp 0.3.2 → 0.3.3

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.en.md CHANGED
@@ -21,10 +21,11 @@ MCP (Model Context Protocol) integration library providing complete MCP server a
21
21
  ### Create MCP Server
22
22
 
23
23
  ```typescript
24
- import { HttpServerAdapter, tools } from '@easbot/mcp';
24
+ import { HttpServerAdapter, ToolRegistry } from '@easbot/mcp';
25
25
  import { z } from 'zod';
26
26
 
27
- // Register tools
27
+ // Caller creates their own ToolRegistry (constructor-injected pattern)
28
+ const tools = new ToolRegistry();
28
29
  tools.register({
29
30
  name: 'my-tool',
30
31
  description: 'A sample tool',
@@ -32,14 +33,14 @@ tools.register({
32
33
  handler: async (args) => ({ content: [{ type: 'text', text: `Hello, ${args.name}!` }] })
33
34
  });
34
35
 
35
- // Create and start server
36
+ // Create and start server (inject tools)
36
37
  const server = new HttpServerAdapter({
37
38
  id: 'my-server',
38
39
  name: 'My MCP Server',
39
40
  version: '1.0.0',
40
41
  transportType: 'http',
41
42
  url: 'http://localhost:3000'
42
- });
43
+ }, tools);
43
44
 
44
45
  await server.start();
45
46
  ```
@@ -70,19 +71,23 @@ await client.disconnect();
70
71
  ### Create Servers from Configuration
71
72
 
72
73
  ```typescript
73
- import { createServerFromConfig, createServersFromConfigMap } from '@easbot/mcp';
74
+ import { createServerFromConfig, createServersFromConfigMap, ToolRegistry } from '@easbot/mcp';
75
+
76
+ // Caller creates their own ToolRegistry
77
+ const tools = new ToolRegistry();
78
+ tools.register({ name: 'shared-tool', handler: ... });
74
79
 
75
80
  // Create single server
76
81
  const server = await createServerFromConfig('my-server', {
77
82
  type: 'local',
78
83
  command: ['node', 'server.js']
79
- });
84
+ }, tools);
80
85
 
81
- // Create multiple servers from config map
86
+ // Create multiple servers from config map (share same ToolRegistry)
82
87
  const servers = await createServersFromConfigMap({
83
88
  'server-1': { type: 'local', command: ['node', 'server1.js'] },
84
89
  'server-2': { type: 'remote', url: 'https://mcp.example.com' }
85
- });
90
+ }, tools);
86
91
  ```
87
92
 
88
93
  ## Development
package/README.md CHANGED
@@ -1,112 +1,117 @@
1
- [English](./README.en.md) | 中文
2
-
3
- # @easbot/mcp
4
-
5
- MCP (Model Context Protocol) 集成库,为 EASBot 生态系统提供完整的 MCP 服务器和客户端实现。
6
-
7
- > 基于 @modelcontextprotocol/sdk 构建,提供 stdio 和 HTTP 两种传输方式,支持工具注册和动态管理。
8
-
9
- ## 特性
10
-
11
- - **双传输支持**: 支持 stdio(本地进程)和 HTTP(远程服务)两种传输方式
12
- - **服务器端实现**: 创建和管理 MCP 服务器,向客户端提供工具和资源
13
- - **客户端实现**: 连接到 MCP 服务器,调用远程工具和读取资源
14
- - **工具注册**: 全局工具注册表,支持动态注册/注销工具
15
- - **OAuth 认证**: 支持 OAuth 2.0 认证,确保安全连接
16
- - **配置驱动**: 支持从配置文件批量创建服务器实例
17
- - **完整的类型定义**: 提供完整的 TypeScript 类型定义
18
-
19
- ## 快速开始
20
-
21
- ### 创建 MCP 服务器
22
-
23
- ```typescript
24
- import { HttpServerAdapter, tools } from '@easbot/mcp';
25
- import { z } from 'zod';
26
-
27
- // 注册工具
28
- tools.register({
29
- name: 'my-tool',
30
- description: '一个示例工具',
31
- inputSchema: z.object({ name: z.string() }),
32
- handler: async (args) => ({ content: [{ type: 'text', text: `Hello, ${args.name}!` }] })
33
- });
34
-
35
- // 创建并启动服务器
36
- const server = new HttpServerAdapter({
37
- id: 'my-server',
38
- name: 'My MCP Server',
39
- version: '1.0.0',
40
- transportType: 'http',
41
- url: 'http://localhost:3000'
42
- });
43
-
44
- await server.start();
45
- ```
46
-
47
- ### 连接到 MCP 服务器
48
-
49
- ```typescript
50
- import { MCPClient } from '@easbot/mcp';
51
-
52
- const client = new MCPClient();
53
-
54
- // 连接本地服务器
55
- await client.connect('filesystem', {
56
- type: 'local',
57
- command: ['npx', '-y', '@modelcontextprotocol/server-filesystem', '/tmp']
58
- });
59
-
60
- // 查看客户端信息
61
- console.log(client.info);
62
-
63
- // 调用工具
64
- const result = await client.callTool('read_file', { path: '/tmp/test.txt' });
65
-
66
- // 断开连接
67
- await client.disconnect();
68
- ```
69
-
70
- ### 从配置创建服务器
71
-
72
- ```typescript
73
- import { createServerFromConfig, createServersFromConfigMap } from '@easbot/mcp';
74
-
75
- // 创建单个服务器
76
- const server = await createServerFromConfig('my-server', {
77
- type: 'local',
78
- command: ['node', 'server.js']
79
- });
80
-
81
- // 从配置映射创建多个服务器
82
- const servers = await createServersFromConfigMap({
83
- 'server-1': { type: 'local', command: ['node', 'server1.js'] },
84
- 'server-2': { type: 'remote', url: 'https://mcp.example.com' }
85
- });
86
- ```
87
-
88
- ## 开发
89
-
90
- ```bash
91
- # 安装依赖
92
- pnpm install
93
-
94
- # 构建
95
- pnpm build
96
-
97
- # 测试
98
- pnpm test
99
-
100
- # 类型检查
101
- pnpm type-check
102
-
103
- # 代码检查
104
- pnpm lint
105
-
106
- # 代码格式化
107
- pnpm format
108
- ```
109
-
110
- ## 许可证
111
-
112
- MIT
1
+ [English](./README.en.md) | 中文
2
+
3
+ # @easbot/mcp
4
+
5
+ MCP (Model Context Protocol) 集成库,为 EASBot 生态系统提供完整的 MCP 服务器和客户端实现。
6
+
7
+ > 基于 @modelcontextprotocol/sdk 构建,提供 stdio 和 HTTP 两种传输方式,支持工具注册和动态管理。
8
+
9
+ ## 特性
10
+
11
+ - **双传输支持**: 支持 stdio(本地进程)和 HTTP(远程服务)两种传输方式
12
+ - **服务器端实现**: 创建和管理 MCP 服务器,向客户端提供工具和资源
13
+ - **客户端实现**: 连接到 MCP 服务器,调用远程工具和读取资源
14
+ - **工具注册**: 全局工具注册表,支持动态注册/注销工具
15
+ - **OAuth 认证**: 支持 OAuth 2.0 认证,确保安全连接
16
+ - **配置驱动**: 支持从配置文件批量创建服务器实例
17
+ - **完整的类型定义**: 提供完整的 TypeScript 类型定义
18
+
19
+ ## 快速开始
20
+
21
+ ### 创建 MCP 服务器
22
+
23
+ ```typescript
24
+ import { HttpServerAdapter, ToolRegistry } from '@easbot/mcp';
25
+ import { z } from 'zod';
26
+
27
+ // 调用方自己 new ToolRegistry 并注册工具(构造注入模式)
28
+ const tools = new ToolRegistry();
29
+ tools.register({
30
+ name: 'my-tool',
31
+ description: '一个示例工具',
32
+ inputSchema: z.object({ name: z.string() }),
33
+ handler: async (args) => ({ content: [{ type: 'text', text: `Hello, ${args.name}!` }] })
34
+ });
35
+
36
+ // 创建并启动服务器(注入 tools)
37
+ const server = new HttpServerAdapter({
38
+ id: 'my-server',
39
+ name: 'My MCP Server',
40
+ version: '1.0.0',
41
+ transportType: 'http',
42
+ url: 'http://localhost:3000'
43
+ }, tools);
44
+
45
+ await server.start();
46
+ ```
47
+
48
+ ### 连接到 MCP 服务器
49
+
50
+ ```typescript
51
+ import { MCPClient } from '@easbot/mcp';
52
+
53
+ const client = new MCPClient();
54
+
55
+ // 连接本地服务器
56
+ await client.connect('filesystem', {
57
+ type: 'local',
58
+ command: ['npx', '-y', '@modelcontextprotocol/server-filesystem', '/tmp']
59
+ });
60
+
61
+ // 查看客户端信息
62
+ console.log(client.info);
63
+
64
+ // 调用工具
65
+ const result = await client.callTool('read_file', { path: '/tmp/test.txt' });
66
+
67
+ // 断开连接
68
+ await client.disconnect();
69
+ ```
70
+
71
+ ### 从配置创建服务器
72
+
73
+ ```typescript
74
+ import { createServerFromConfig, createServersFromConfigMap, ToolRegistry } from '@easbot/mcp';
75
+
76
+ // 调用方自己 new ToolRegistry
77
+ const tools = new ToolRegistry();
78
+ tools.register({ name: 'shared-tool', handler: ... });
79
+
80
+ // 创建单个服务器
81
+ const server = await createServerFromConfig('my-server', {
82
+ type: 'local',
83
+ command: ['node', 'server.js']
84
+ }, tools);
85
+
86
+ // 从配置映射创建多个服务器(共享同一 ToolRegistry)
87
+ const servers = await createServersFromConfigMap({
88
+ 'server-1': { type: 'local', command: ['node', 'server1.js'] },
89
+ 'server-2': { type: 'remote', url: 'https://mcp.example.com' }
90
+ }, tools);
91
+ ```
92
+
93
+ ## 开发
94
+
95
+ ```bash
96
+ # 安装依赖
97
+ pnpm install
98
+
99
+ # 构建
100
+ pnpm build
101
+
102
+ # 测试
103
+ pnpm test
104
+
105
+ # 类型检查
106
+ pnpm type-check
107
+
108
+ # 代码检查
109
+ pnpm lint
110
+
111
+ # 代码格式化
112
+ pnpm format
113
+ ```
114
+
115
+ ## 许可证
116
+
117
+ MIT
package/dist/index.cjs CHANGED
@@ -1,3 +1,3 @@
1
- 'use strict';var mcp_js=require('@modelcontextprotocol/sdk/server/mcp.js'),stdio_js=require('@modelcontextprotocol/sdk/server/stdio.js'),utils=require('@easbot/utils'),events=require('events'),d=require('zod/v4'),V=require('http'),streamableHttp_js=require('@modelcontextprotocol/sdk/server/streamableHttp.js'),index_js=require('@modelcontextprotocol/sdk/client/index.js'),stdio_js$1=require('@modelcontextprotocol/sdk/client/stdio.js'),streamableHttp_js$1=require('@modelcontextprotocol/sdk/client/streamableHttp.js'),sse_js=require('@modelcontextprotocol/sdk/client/sse.js'),types_js=require('@modelcontextprotocol/sdk/types.js'),auth_js=require('@modelcontextprotocol/sdk/client/auth.js'),v=require('zod');function _interopDefault(e){return e&&e.__esModule?e:{default:e}}var d__default=/*#__PURE__*/_interopDefault(d);var V__default=/*#__PURE__*/_interopDefault(V);var v__default=/*#__PURE__*/_interopDefault(v);var j=Object.defineProperty;var _=(s,t,e)=>t in s?j(s,t,{enumerable:true,configurable:true,writable:true,value:e}):s[t]=e;var a=(s,t,e)=>_(s,typeof t!="symbol"?t+"":t,e);var P={STDIO:"stdio",HTTP:"http"},l={STOPPED:"stopped",STARTING:"starting",RUNNING:"running",STOPPING:"stopping",ERROR:"error"};var L=utils.Log.create({service:"tool-registry"}),C={REGISTERED:"mcp.tool.registered",UNREGISTERED:"mcp.tool.unregistered",CLEARED:"mcp.tool.cleared"},M=class extends events.EventEmitter{constructor(){super();a(this,"tools",new Map);}register(e){if(!e.name||typeof e.name!="string")throw new Error("\u5DE5\u5177\u540D\u79F0\u65E0\u6548\uFF1A\u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32");if(this.tools.has(e.name)){L?.debug("tool already registered, skipping",{tool:e.name});return}this.tools.set(e.name,e),this.emit(C.REGISTERED,e);}unregister(e){return this.tools.has(e)?(this.tools.delete(e),this.emit(C.UNREGISTERED,e),true):false}get(e){return this.tools.get(e)}getAll(){return Array.from(this.tools.values())}getTools(){return Array.from(this.tools.values()).map(({handler:e,...r})=>r)}has(e){return this.tools.has(e)}getSize(){return this.tools.size}clear(){this.tools.clear(),this.emit(C.CLEARED);}},T=new M;var y=utils.Log.create({service:"mcp.server.stdio"}),b=class{constructor(t){a(this,"config");a(this,"server",null);a(this,"info");a(this,"isStarting",false);a(this,"isStopping",false);a(this,"toolEventCleanup",null);a(this,"handleToolRegistered",t=>{if(this.server){let e=this.convertToZodSchema(t.inputSchema),r=this.adaptToolHandler(t.handler);this.server.registerTool(t.name,{description:t.description,inputSchema:e},r),y.info("tool registered",{tool:t.name});}});this.config=t,this.info={id:t.id,name:t.name,version:t.version,transportType:t.transportType,status:l.STOPPED,createdAt:Date.now(),lastActivityAt:Date.now(),enabled:t.enabled};}getInfo(){return this.info}getConfig(){return this.config}createInstance(){let t=this;return {info:this.info,get server(){return t.server},start:()=>this.start(),stop:()=>this.stop(),restart:()=>this.restart()}}loadTools(){if(this.server)for(let t of T.getAll()){let e=this.convertToZodSchema(t.inputSchema),r=this.adaptToolHandler(t.handler);this.server.registerTool(t.name,{description:t.description,inputSchema:e},r),y.info("tool loaded",{tool:t.name});}}subscribeToolEvents(){this.toolEventCleanup=()=>{T.off(C.REGISTERED,this.handleToolRegistered);},T.on(C.REGISTERED,this.handleToolRegistered);}adaptToolHandler(t){return async(e,r)=>{let n={signal:r.signal,sessionId:r.sessionId,requestId:r.requestId,taskId:r.taskId,_meta:r._meta,authInfo:r.authInfo?{type:"bearer",claims:{clientId:r.authInfo.clientId,scopes:r.authInfo.scopes}}:void 0};return t(e,n)}}unsubscribeToolEvents(){this.toolEventCleanup&&(this.toolEventCleanup(),this.toolEventCleanup=null);}convertToZodSchema(t){if(t instanceof d__default.default.ZodType)return t;if(typeof t=="object"&&t!==null){let e=t;if(e.type==="object"){let r=e.properties||{},n=e.required||[],o={};for(let[i,m]of Object.entries(r))o[i]=this.jsonSchemaToZod(m);if(Object.keys(r).filter(i=>!n.includes(i)).length===0)return d__default.default.object(o);let f={};for(let[i,m]of Object.entries(r)){let c=o[i];c&&(n.includes(i)?f[i]=c:f[i]=c.optional());}return d__default.default.object(f)}if(e.type==="string")return d__default.default.string();if(e.type==="number")return d__default.default.number();if(e.type==="boolean")return d__default.default.boolean();if(e.type==="array"){let r=e.items;return r?d__default.default.array(this.jsonSchemaToZod(r)):d__default.default.array(d__default.default.unknown())}}return d__default.default.unknown()}jsonSchemaToZod(t){switch(t.type){case "string":return d__default.default.string();case "number":return d__default.default.number();case "integer":return d__default.default.number().int();case "boolean":return d__default.default.boolean();case "null":return d__default.default.null();case "array":{let r=t.items;return r?d__default.default.array(this.jsonSchemaToZod(r)):d__default.default.array(d__default.default.unknown())}case "object":{let r=t.properties||{},n=t.required||[],o={};for(let[i,m]of Object.entries(r))o[i]=this.jsonSchemaToZod(m);if(Object.keys(r).filter(i=>!n.includes(i)).length===0)return d__default.default.object(o);let f={};for(let[i,m]of Object.entries(r)){let c=o[i];c&&(n.includes(i)?f[i]=c:f[i]=c.optional());}return d__default.default.object(f)}default:return d__default.default.unknown()}}async start(){if(this.isStarting){y.warn("server is already starting",{id:this.info.id});return}if(this.info.status===l.RUNNING){y.debug("server is already running",{id:this.info.id});return}this.isStarting=true,this.info.status=l.STARTING;try{y.info("starting stdio mcp server",{id:this.info.id,name:this.info.name,command:this.config.command,args:this.config.args});let t=new stdio_js.StdioServerTransport;this.server=new mcp_js.McpServer({name:this.config.name,version:this.config.version}),this.loadTools(),this.subscribeToolEvents(),await this.server.connect(t),this.info.status=l.RUNNING,this.info.lastActivityAt=Date.now(),y.info("stdio mcp server started successfully",{id:this.info.id,name:this.info.name,toolCount:T.getSize()});}catch(t){throw this.info.status=l.ERROR,y.error("failed to start stdio mcp server",{id:this.info.id,error:t instanceof Error?t.message:String(t)}),t}finally{this.isStarting=false;}}async stop(){if(this.isStopping){y.warn("server is already stopping",{id:this.info.id});return}if(this.info.status!==l.RUNNING&&this.info.status!==l.ERROR){y.debug("server is not running",{id:this.info.id,status:this.info.status});return}this.isStopping=true,this.info.status=l.STOPPING;try{y.info("stopping stdio mcp server",{id:this.info.id,name:this.info.name}),this.unsubscribeToolEvents(),this.server&&(await this.server.close(),this.server=null),this.info.status=l.STOPPED,y.info("stdio mcp server stopped",{id:this.info.id,name:this.info.name});}catch(t){this.info.status=l.ERROR,y.error("error stopping stdio mcp server",{id:this.info.id,error:t instanceof Error?t.message:String(t)});}finally{this.isStopping=false;}}async restart(){y.info("restarting stdio mcp server",{id:this.info.id}),await this.stop(),await this.start();}};function q(s){return new b(s)}async function G(s){let t=new b(s);return await t.start(),t.createInstance()}var g=utils.Log.create({service:"mcp.server.http"}),R=class{constructor(t){a(this,"config");a(this,"server",null);a(this,"transport",null);a(this,"httpServer",null);a(this,"info");a(this,"isStarting",false);a(this,"isStopping",false);a(this,"toolCleanup",null);a(this,"handleTool",t=>{if(this.server){let e=this.convertToZodSchema(t.inputSchema),r=this.adaptToolHandler(t.handler);this.server.registerTool(t.name,{description:t.description,inputSchema:e},r),g.info("tool registered",{tool:t.name});}});this.config=t,this.info={id:t.id,name:t.name,version:t.version,transportType:t.transportType,status:l.STOPPED,createdAt:Date.now(),lastActivityAt:Date.now()};}getInfo(){return this.info}getConfig(){return this.config}createInstance(){let t=this;return {info:this.info,get server(){return t.server},start:()=>this.start(),stop:()=>this.stop(),restart:()=>this.restart()}}loadTools(){if(this.server)for(let t of T.getAll()){let e=this.convertToZodSchema(t.inputSchema),r=this.adaptToolHandler(t.handler);this.server.registerTool(t.name,{description:t.description,inputSchema:e},r),g.info("tool loaded",{tool:t.name});}}subscribeTools(){this.toolCleanup=()=>{T.off(C.REGISTERED,this.handleTool);},T.on(C.REGISTERED,this.handleTool);}convertToZodSchema(t){if(t instanceof d__default.default.ZodType)return t;if(typeof t=="object"&&t!==null){let e=t;if(e.type==="object"){let r=e.properties||{},n=e.required||[],o={};for(let[i,m]of Object.entries(r))o[i]=this.jsonSchemaToZod(m);if(Object.keys(r).filter(i=>!n.includes(i)).length===0)return d__default.default.object(o);let f={};for(let[i,m]of Object.entries(r)){let c=o[i];c&&(n.includes(i)?f[i]=c:f[i]=c.optional());}return d__default.default.object(f)}if(e.type==="string")return d__default.default.string();if(e.type==="number")return d__default.default.number();if(e.type==="boolean")return d__default.default.boolean();if(e.type==="array"){let r=e.items;return r?d__default.default.array(this.jsonSchemaToZod(r)):d__default.default.array(d__default.default.unknown())}}return d__default.default.unknown()}jsonSchemaToZod(t){switch(t.type){case "string":return d__default.default.string();case "number":return d__default.default.number();case "integer":return d__default.default.number().int();case "boolean":return d__default.default.boolean();case "null":return d__default.default.null();case "array":{let r=t.items;return r?d__default.default.array(this.jsonSchemaToZod(r)):d__default.default.array(d__default.default.unknown())}case "object":{let r=t.properties||{},n=t.required||[],o={};for(let[i,m]of Object.entries(r))o[i]=this.jsonSchemaToZod(m);if(Object.keys(r).filter(i=>!n.includes(i)).length===0)return d__default.default.object(o);let f={};for(let[i,m]of Object.entries(r)){let c=o[i];c&&(n.includes(i)?f[i]=c:f[i]=c.optional());}return d__default.default.object(f)}default:return d__default.default.unknown()}}adaptToolHandler(t){return async(e,r)=>{let n={signal:r.signal,sessionId:r.sessionId,requestId:r.requestId,taskId:r.taskId,_meta:r._meta,authInfo:r.authInfo?{type:"bearer",claims:{clientId:r.authInfo.clientId,scopes:r.authInfo.scopes}}:void 0};return t(e,n)}}unsubscribeTools(){this.toolCleanup&&(this.toolCleanup(),this.toolCleanup=null);}async start(){if(this.isStarting){g.warn("already starting",{id:this.info.id});return}if(this.info.status===l.RUNNING){g.debug("already running",{id:this.info.id});return}this.isStarting=true,this.info.status=l.STARTING;try{g.info("starting",{id:this.info.id,name:this.info.name,url:this.config.url});let t;try{t=new URL(this.config.url);}catch{throw new Error(`\u65E0\u6548\u7684 URL: ${this.config.url}`)}let e=parseInt(t.port||"3000",10),r=t.hostname||"0.0.0.0";if(Number.isNaN(e)||e<1||e>65535)throw new Error(`\u65E0\u6548\u7684\u7AEF\u53E3\u53F7: ${t.port}`);this.transport=new streamableHttp_js.StreamableHTTPServerTransport({sessionIdGenerator:()=>crypto.randomUUID()}),this.server=new mcp_js.McpServer({name:this.config.name,version:this.config.version}),this.loadTools(),this.subscribeTools(),await this.server.connect(this.transport),this.httpServer=V__default.default.createServer((n,o)=>{let h=n.headers.origin,f=(m,c,k)=>{o.writeHead(m,c),k?o.end(k):o.end();},i={"Access-Control-Allow-Origin":h||"*","Access-Control-Allow-Methods":"GET, POST, OPTIONS","Access-Control-Allow-Headers":"Content-Type, mcpsessionid, mcp-protocol-version","Access-Control-Max-Age":"86400"};if(n.method==="OPTIONS"){f(204,i);return}if(n.method==="POST"){let m="";n.setEncoding("utf8"),n.on("data",c=>{m+=c;}),n.on("end",()=>{let c;if(m)try{c=JSON.parse(m);}catch{c=void 0;}this.transport&&this.transport.handleRequest(n,o,c);}),n.on("error",c=>{g.error("HTTP request error",{error:c.message}),f(500,i,JSON.stringify({jsonrpc:"2.0",error:{code:-32603,message:"Internal error"}}));});}else n.method==="GET"?this.transport&&this.transport.handleRequest(n,o,void 0):f(405,i,JSON.stringify({jsonrpc:"2.0",error:{code:-32601,message:"Method not found"}}));}),this.httpServer.on("error",n=>{g.error("HTTP server error",{error:n.message}),this.info.status=l.ERROR;}),this.httpServer.on("clientError",(n,o)=>{g.warn("HTTP client error",{error:n.message}),o.writable&&o.end(`HTTP/1.1 400 Bad Request\r
1
+ 'use strict';var mcp_js=require('@modelcontextprotocol/sdk/server/mcp.js'),stdio_js=require('@modelcontextprotocol/sdk/server/stdio.js'),utils=require('@easbot/utils'),events=require('events'),d=require('zod'),G=require('http'),streamableHttp_js=require('@modelcontextprotocol/sdk/server/streamableHttp.js'),u=require('zod/v4'),index_js=require('@modelcontextprotocol/sdk/client/index.js'),stdio_js$1=require('@modelcontextprotocol/sdk/client/stdio.js'),streamableHttp_js$1=require('@modelcontextprotocol/sdk/client/streamableHttp.js'),sse_js=require('@modelcontextprotocol/sdk/client/sse.js'),types_js=require('@modelcontextprotocol/sdk/types.js'),auth_js=require('@modelcontextprotocol/sdk/client/auth.js');function _interopDefault(e){return e&&e.__esModule?e:{default:e}}var d__default=/*#__PURE__*/_interopDefault(d);var G__default=/*#__PURE__*/_interopDefault(G);var u__default=/*#__PURE__*/_interopDefault(u);var O=Object.defineProperty;var j=(s,t,e)=>t in s?O(s,t,{enumerable:true,configurable:true,writable:true,value:e}):s[t]=e;var a=(s,t,e)=>j(s,typeof t!="symbol"?t+"":t,e);var w={STDIO:"stdio",HTTP:"http"},l={STOPPED:"stopped",STARTING:"starting",RUNNING:"running",STOPPING:"stopping",ERROR:"error"};var D=utils.Log.create({service:"tool-registry"}),T={REGISTERED:"mcp.tool.registered",UNREGISTERED:"mcp.tool.unregistered",CLEARED:"mcp.tool.cleared"},M=class extends events.EventEmitter{constructor(){super();a(this,"tools",new Map);}register(e){if(!e.name||typeof e.name!="string")throw new Error("\u5DE5\u5177\u540D\u79F0\u65E0\u6548\uFF1A\u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32");if(this.tools.has(e.name)){D?.debug("tool already registered, skipping",{tool:e.name});return}this.tools.set(e.name,e),this.emit(T.REGISTERED,e);}unregister(e){return this.tools.has(e)?(this.tools.delete(e),this.emit(T.UNREGISTERED,e),true):false}get(e){return this.tools.get(e)}getAll(){return Array.from(this.tools.values())}getTools(){return Array.from(this.tools.values()).map(({handler:e,...r})=>r)}has(e){return this.tools.has(e)}getSize(){return this.tools.size}clear(){this.tools.clear(),this.emit(T.CLEARED);}};var y=utils.Log.create({service:"mcp.server.stdio"}),C=class{constructor(t,e){a(this,"config");a(this,"server",null);a(this,"info");a(this,"isStarting",false);a(this,"isStopping",false);a(this,"toolEventCleanup",null);a(this,"tools");a(this,"handleToolRegistered",t=>{if(this.server){let e=this.convertToZodSchema(t.inputSchema),r=this.adaptToolHandler(t.handler);this.server.registerTool(t.name,{description:t.description,inputSchema:e},r),y.info("tool registered",{tool:t.name});}});this.config=t,this.tools=e,this.info={id:t.id,name:t.name,version:t.version,transportType:t.transportType,status:l.STOPPED,createdAt:Date.now(),lastActivityAt:Date.now(),enabled:t.enabled};}getInfo(){return this.info}getConfig(){return this.config}createInstance(){let t=this;return {info:this.info,get server(){return t.server},start:()=>this.start(),stop:()=>this.stop(),restart:()=>this.restart()}}loadTools(){if(this.server)for(let t of this.tools.getAll()){let e=this.convertToZodSchema(t.inputSchema),r=this.adaptToolHandler(t.handler);this.server.registerTool(t.name,{description:t.description,inputSchema:e},r),y.info("tool loaded",{tool:t.name});}}subscribeToolEvents(){this.toolEventCleanup=()=>{this.tools.off(T.REGISTERED,this.handleToolRegistered);},this.tools.on(T.REGISTERED,this.handleToolRegistered);}adaptToolHandler(t){return async(e,r)=>{let o={signal:r.signal,sessionId:r.sessionId,requestId:r.requestId,taskId:r.taskId,_meta:r._meta,authInfo:r.authInfo?{type:"bearer",claims:{clientId:r.authInfo.clientId,scopes:r.authInfo.scopes}}:void 0};return t(e,o)}}unsubscribeToolEvents(){this.toolEventCleanup&&(this.toolEventCleanup(),this.toolEventCleanup=null);}convertToZodSchema(t){if(t instanceof d__default.default.ZodType)return t;if(typeof t=="object"&&t!==null){let e=t;if(e.type==="object"){let r=e.properties||{},o=e.required||[],n={};for(let[i,m]of Object.entries(r))n[i]=this.jsonSchemaToZod(m);if(Object.keys(r).filter(i=>!o.includes(i)).length===0)return d__default.default.object(n);let f={};for(let[i,m]of Object.entries(r)){let c=n[i];c&&(o.includes(i)?f[i]=c:f[i]=c.optional());}return d__default.default.object(f)}if(e.type==="string")return d__default.default.string();if(e.type==="number")return d__default.default.number();if(e.type==="boolean")return d__default.default.boolean();if(e.type==="array"){let r=e.items;return r?d__default.default.array(this.jsonSchemaToZod(r)):d__default.default.array(d__default.default.unknown())}}return d__default.default.unknown()}jsonSchemaToZod(t){switch(t.type){case "string":return d__default.default.string();case "number":return d__default.default.number();case "integer":return d__default.default.number().int();case "boolean":return d__default.default.boolean();case "null":return d__default.default.null();case "array":{let r=t.items;return r?d__default.default.array(this.jsonSchemaToZod(r)):d__default.default.array(d__default.default.unknown())}case "object":{let r=t.properties||{},o=t.required||[],n={};for(let[i,m]of Object.entries(r))n[i]=this.jsonSchemaToZod(m);if(Object.keys(r).filter(i=>!o.includes(i)).length===0)return d__default.default.object(n);let f={};for(let[i,m]of Object.entries(r)){let c=n[i];c&&(o.includes(i)?f[i]=c:f[i]=c.optional());}return d__default.default.object(f)}default:return d__default.default.unknown()}}async start(){if(this.isStarting){y.warn("server is already starting",{id:this.info.id});return}if(this.info.status===l.RUNNING){y.debug("server is already running",{id:this.info.id});return}this.isStarting=true,this.info.status=l.STARTING;try{y.info("starting stdio mcp server",{id:this.info.id,name:this.info.name,command:this.config.command,args:this.config.args});let t=new stdio_js.StdioServerTransport;this.server=new mcp_js.McpServer({name:this.config.name,version:this.config.version}),this.loadTools(),this.subscribeToolEvents(),await this.server.connect(t),this.info.status=l.RUNNING,this.info.lastActivityAt=Date.now(),y.info("stdio mcp server started successfully",{id:this.info.id,name:this.info.name,toolCount:this.tools.getSize()});}catch(t){throw this.info.status=l.ERROR,y.error("failed to start stdio mcp server",{id:this.info.id,error:t instanceof Error?t.message:String(t)}),t}finally{this.isStarting=false;}}async stop(){if(this.isStopping){y.warn("server is already stopping",{id:this.info.id});return}if(this.info.status!==l.RUNNING&&this.info.status!==l.ERROR){y.debug("server is not running",{id:this.info.id,status:this.info.status});return}this.isStopping=true,this.info.status=l.STOPPING;try{y.info("stopping stdio mcp server",{id:this.info.id,name:this.info.name}),this.unsubscribeToolEvents(),this.server&&(await this.server.close(),this.server=null),this.info.status=l.STOPPED,y.info("stdio mcp server stopped",{id:this.info.id,name:this.info.name});}catch(t){this.info.status=l.ERROR,y.error("error stopping stdio mcp server",{id:this.info.id,error:t instanceof Error?t.message:String(t)});}finally{this.isStopping=false;}}async restart(){y.info("restarting stdio mcp server",{id:this.info.id}),await this.stop(),await this.start();}};function z(s,t){return new C(s,t)}async function q(s,t){let e=new C(s,t);return await e.start(),e.createInstance()}var g=utils.Log.create({service:"mcp.server.http"}),R=class{constructor(t,e){a(this,"config");a(this,"server",null);a(this,"transport",null);a(this,"httpServer",null);a(this,"info");a(this,"isStarting",false);a(this,"isStopping",false);a(this,"toolCleanup",null);a(this,"tools");a(this,"handleTool",t=>{if(this.server){let e=this.convertToZodSchema(t.inputSchema),r=this.adaptToolHandler(t.handler);this.server.registerTool(t.name,{description:t.description,inputSchema:e},r),g.info("tool registered",{tool:t.name});}});this.config=t,this.tools=e,this.info={id:t.id,name:t.name,version:t.version,transportType:t.transportType,status:l.STOPPED,createdAt:Date.now(),lastActivityAt:Date.now()};}getInfo(){return this.info}getConfig(){return this.config}createInstance(){let t=this;return {info:this.info,get server(){return t.server},start:()=>this.start(),stop:()=>this.stop(),restart:()=>this.restart()}}loadTools(){if(this.server)for(let t of this.tools.getAll()){let e=this.convertToZodSchema(t.inputSchema),r=this.adaptToolHandler(t.handler);this.server.registerTool(t.name,{description:t.description,inputSchema:e},r),g.info("tool loaded",{tool:t.name});}}subscribeTools(){this.toolCleanup=()=>{this.tools.off(T.REGISTERED,this.handleTool);},this.tools.on(T.REGISTERED,this.handleTool);}convertToZodSchema(t){if(t instanceof u__default.default.ZodType)return t;if(typeof t=="object"&&t!==null){let e=t;if(e.type==="object"){let r=e.properties||{},o=e.required||[],n={};for(let[i,m]of Object.entries(r))n[i]=this.jsonSchemaToZod(m);if(Object.keys(r).filter(i=>!o.includes(i)).length===0)return u__default.default.object(n);let f={};for(let[i,m]of Object.entries(r)){let c=n[i];c&&(o.includes(i)?f[i]=c:f[i]=c.optional());}return u__default.default.object(f)}if(e.type==="string")return u__default.default.string();if(e.type==="number")return u__default.default.number();if(e.type==="boolean")return u__default.default.boolean();if(e.type==="array"){let r=e.items;return r?u__default.default.array(this.jsonSchemaToZod(r)):u__default.default.array(u__default.default.unknown())}}return u__default.default.unknown()}jsonSchemaToZod(t){switch(t.type){case "string":return u__default.default.string();case "number":return u__default.default.number();case "integer":return u__default.default.number().int();case "boolean":return u__default.default.boolean();case "null":return u__default.default.null();case "array":{let r=t.items;return r?u__default.default.array(this.jsonSchemaToZod(r)):u__default.default.array(u__default.default.unknown())}case "object":{let r=t.properties||{},o=t.required||[],n={};for(let[i,m]of Object.entries(r))n[i]=this.jsonSchemaToZod(m);if(Object.keys(r).filter(i=>!o.includes(i)).length===0)return u__default.default.object(n);let f={};for(let[i,m]of Object.entries(r)){let c=n[i];c&&(o.includes(i)?f[i]=c:f[i]=c.optional());}return u__default.default.object(f)}default:return u__default.default.unknown()}}adaptToolHandler(t){return async(e,r)=>{let o={signal:r.signal,sessionId:r.sessionId,requestId:r.requestId,taskId:r.taskId,_meta:r._meta,authInfo:r.authInfo?{type:"bearer",claims:{clientId:r.authInfo.clientId,scopes:r.authInfo.scopes}}:void 0};return t(e,o)}}unsubscribeTools(){this.toolCleanup&&(this.toolCleanup(),this.toolCleanup=null);}async start(){if(this.isStarting){g.warn("already starting",{id:this.info.id});return}if(this.info.status===l.RUNNING){g.debug("already running",{id:this.info.id});return}this.isStarting=true,this.info.status=l.STARTING;try{g.info("starting",{id:this.info.id,name:this.info.name,url:this.config.url});let t;try{t=new URL(this.config.url);}catch{throw new Error(`\u65E0\u6548\u7684 URL: ${this.config.url}`)}let e=parseInt(t.port||"3000",10),r=t.hostname||"0.0.0.0";if(Number.isNaN(e)||e<1||e>65535)throw new Error(`\u65E0\u6548\u7684\u7AEF\u53E3\u53F7: ${t.port}`);this.transport=new streamableHttp_js.StreamableHTTPServerTransport({sessionIdGenerator:()=>crypto.randomUUID()}),this.server=new mcp_js.McpServer({name:this.config.name,version:this.config.version}),this.loadTools(),this.subscribeTools(),await this.server.connect(this.transport),this.httpServer=G__default.default.createServer((o,n)=>{let h=o.headers.origin,f=(m,c,x)=>{n.writeHead(m,c),x?n.end(x):n.end();},i={"Access-Control-Allow-Origin":h||"*","Access-Control-Allow-Methods":"GET, POST, OPTIONS","Access-Control-Allow-Headers":"Content-Type, mcpsessionid, mcp-protocol-version","Access-Control-Max-Age":"86400"};if(o.method==="OPTIONS"){f(204,i);return}if(o.method==="POST"){let m="";o.setEncoding("utf8"),o.on("data",c=>{m+=c;}),o.on("end",()=>{let c;if(m)try{c=JSON.parse(m);}catch{c=void 0;}this.transport&&this.transport.handleRequest(o,n,c);}),o.on("error",c=>{g.error("HTTP request error",{error:c.message}),f(500,i,JSON.stringify({jsonrpc:"2.0",error:{code:-32603,message:"Internal error"}}));});}else o.method==="GET"?this.transport&&this.transport.handleRequest(o,n,void 0):f(405,i,JSON.stringify({jsonrpc:"2.0",error:{code:-32601,message:"Method not found"}}));}),this.httpServer.on("error",o=>{g.error("HTTP server error",{error:o.message}),this.info.status=l.ERROR;}),this.httpServer.on("clientError",(o,n)=>{g.warn("HTTP client error",{error:o.message}),n.writable&&n.end(`HTTP/1.1 400 Bad Request\r
2
2
  \r
3
- `);}),this.httpServer.listen(e,r),g.info("HTTP server listening",{hostname:r,port:e}),this.info.status=l.RUNNING,this.info.lastActivityAt=Date.now(),g.info("started",{id:this.info.id,name:this.info.name,toolCount:T.getSize()});}catch(t){throw this.info.status=l.ERROR,g.error("failed to start",{id:this.info.id,error:t instanceof Error?t.message:String(t)}),t}finally{this.isStarting=false;}}async stop(){if(this.isStopping){g.warn("already stopping",{id:this.info.id});return}if(this.info.status!==l.RUNNING&&this.info.status!==l.ERROR){g.debug("not running",{id:this.info.id,status:this.info.status});return}this.isStopping=true,this.info.status=l.STOPPING;try{g.info("stopping",{id:this.info.id,name:this.info.name}),this.unsubscribeTools(),this.httpServer&&(await new Promise(t=>{this.httpServer.close(()=>t());}),this.httpServer=null),this.transport&&(await this.transport.close(),this.transport=null),this.server&&(await this.server.close(),this.server=null),this.info.status=l.STOPPED,g.info("stopped",{id:this.info.id});}catch(t){this.info.status=l.ERROR,g.error("failed to stop",{id:this.info.id,error:t instanceof Error?t.message:String(t)});}finally{this.isStopping=false;}}async restart(){g.info("restarting",{id:this.info.id}),await this.stop(),await this.start();}};function $(s){return new R(s)}async function J(s){let t=new R(s);return await t.start(),t.createInstance()}var A=utils.Log.create({service:"mcp.server.factory"});function Q(s){return s.type==="local"}function X(s){return s.type==="remote"}function Y(s,t){if(!t.command)throw new Error(`MCP server ${s} has no command specified`);return {id:s,name:s,version:"1.0.0",transportType:P.STDIO,enabled:t.enabled,timeout:t.timeout,command:t.command,args:(t?.args?.length??0)>0?t.args:void 0,env:t.env}}function tt(s,t){return {id:s,name:s,version:"1.0.0",transportType:P.HTTP,enabled:t.enabled,timeout:t.timeout,url:t.url,headers:t.headers}}async function et(s,t){if(A.debug("creating server from config",{name:s,type:t.type}),Q(t)){let e=Y(s,t),r=new b(e);return await r.start(),r.createInstance()}if(X(t)){let e=tt(s,t),r=new R(e);return await r.start(),r.createInstance()}throw new Error(`Unsupported MCP config type: ${t.type}`)}async function qt(s){let t={};for(let[e,r]of Object.entries(s))try{let n=await et(e,r);t[e]=n;}catch(n){A.error("failed to create server from config",{name:e,error:n instanceof Error?n.message:String(n)});}return t}var H={version:"0.3.2"};function I(){return H.version}var p=utils.Log.create({service:"mcp.client"}),E=6e4,x=class{constructor(){a(this,"id","");a(this,"client",null);a(this,"_info");a(this,"notificationCleanup",null);a(this,"pendingTransport",null);a(this,"oauthState","");a(this,"capturedRedirectUrl",null);a(this,"oauthConfig");a(this,"toolListChangedHandler",null);a(this,"oauthProvider",null);this._info=this.createInitialInfo();}get info(){return {...this._info}}get status(){return this._info.status}get tools(){return [...this._info.tools]}get resources(){return [...this._info.resources]}get prompts(){return [...this._info.prompts]}get clientId(){return this.id}setToolListChangedHandler(t){this.toolListChangedHandler=t;}setOAuthProvider(t){this.oauthProvider=t;}async connect(t,e){p.info("connecting to MCP server",{id:t,type:e.type}),await this.disconnect(),this.id=t,this._info=this.createInitialInfo(),this._info.id=t,this._info.name=t;try{if(e.type==="remote"&&(this.oauthConfig=e.oauth===!1?void 0:e.oauth??{}),this.client=await this.createClient(e),!this.client)return;this._info.status={status:"connected"},this._info.connectedAt=Date.now(),await this.fetchCapabilities(),p.info("connected to MCP server",{id:t,toolCount:this._info.tools.length});}catch(r){let n=r instanceof Error?r.message:String(r);this._info.status={status:"failed",error:n},this._info.error=n,p.error("failed to connect to MCP server",{id:t,error:n});}}async startAuth(){if(!this.pendingTransport)throw new Error("No pending OAuth flow. Please connect to a server that requires authentication first.");return this.oauthState=Array.from(crypto.getRandomValues(new Uint8Array(32))).map(t=>t.toString(16).padStart(2,"0")).join(""),this.oauthProvider&&await this.oauthProvider.saveState(this.oauthState),{authorizationUrl:this.capturedRedirectUrl?.toString()??"",state:this.oauthState}}async finishAuth(t){if(!this.pendingTransport)throw new Error("No pending OAuth flow.");try{await this.pendingTransport.finishAuth?.(t);}catch(e){throw p.error("failed to finish OAuth",{id:this.id,error:e}),e}this.pendingTransport=null,this.capturedRedirectUrl=null,p.info("OAuth completed, ready to reconnect",{id:this.id});}isAuthRequired(){return this._info.status.status==="needs_auth"}isClientRegistrationRequired(){return this._info.status.status==="needs_client_registration"}getPendingTransport(){return this.pendingTransport}getAuthorizationUrl(){return this.capturedRedirectUrl?.toString()??""}async disconnect(){this.notificationCleanup&&(this.notificationCleanup(),this.notificationCleanup=null),this.client&&(await this.client.close().catch(t=>{p.error("Failed to close MCP client",{id:this.id,error:t});}),this.client=null),this.pendingTransport=null,this.capturedRedirectUrl=null,this.toolListChangedHandler=null,this._info.status.status==="connected"&&(this._info.status={status:"disconnected"}),p.info("disconnected from MCP server",{id:this.id});}async listTools(){if(!this.client||this._info.status.status!=="connected")return {};let t={};for(let e of this._info.tools)t[e.name]={name:e.name,description:e.description,inputSchema:e.inputSchema};return t}async callTool(t,e,r){let n=Date.now();if(!this.client||this._info.status.status!=="connected")return {toolName:t,args:e,content:[],success:false,error:"Client not connected",duration:Date.now()-n};try{let o=await utils.withTimeout(this.client.callTool({name:t,arguments:e},types_js.CallToolResultSchema,{resetTimeoutOnProgress:!0,timeout:r}),r??E);return {toolName:t,args:e,content:o.content.map(h=>h.type==="text"?{type:"text",text:h.text}:h.type==="image"?{type:"image",data:h.data,mimeType:h.mimeType}:h.type==="resource"?{type:"resource",resource:h.resource}:{type:"text",text:String(h)}),success:!0,duration:Date.now()-n,metadata:o.metadata}}catch(o){return {toolName:t,args:e,content:[],success:false,error:o instanceof Error?o.message:String(o),duration:Date.now()-n}}}async listResources(){return [...this._info.resources]}async readResource(t){if(!this.client||this._info.status.status!=="connected"){p.warn("client not connected for readResource",{id:this.id});return}return this.client.readResource({uri:t}).catch(e=>{p.error("failed to read resource",{id:this.id,uri:t,error:e.message});})}async listPrompts(){return [...this._info.prompts]}async getPrompt(t,e){if(!this.client||this._info.status.status!=="connected"){p.warn("client not connected for getPrompt",{id:this.id});return}return this.client.getPrompt({name:t,arguments:e}).catch(r=>{p.error("failed to get prompt",{id:this.id,name:t,error:r.message});})}async refreshTools(){if(!(!this.client||this._info.status.status!=="connected"))try{let t=await utils.withTimeout(this.client.listTools(),E);this._info.tools=t.tools.map(e=>({name:e.name,description:e.description,inputSchema:e.inputSchema})),p.info("tools refreshed",{id:this.id,count:this._info.tools.length});}catch(t){p.error("failed to refresh tools",{id:this.id,error:t instanceof Error?t.message:String(t)});}}async createClient(t){return t.type==="local"?await this.createLocalClient(t):t.type==="remote"?await this.createRemoteClient(t):null}async fetchCapabilities(){if(this.client){try{let t=await utils.withTimeout(this.client.listTools(),E);this._info.tools=t.tools.map(e=>({name:e.name,description:e.description,inputSchema:e.inputSchema}));}catch(t){p.debug("failed to get tools",{id:this.id,error:t instanceof Error?t.message:String(t)});}try{let t=await utils.withTimeout(this.client.listResources(),5e3);this._info.resources=t.resources.map(e=>({name:e.name,uri:e.uri,description:e.description,mimeType:e.mimeType}));}catch{p.debug("failed to get resources",{id:this.id});}try{let t=await utils.withTimeout(this.client.listPrompts(),5e3);this._info.prompts=t.prompts.map(e=>({name:e.name,description:e.description,arguments:e.arguments?.map(r=>({name:r.name,description:r.description,required:r.required}))}));}catch{p.debug("failed to get prompts",{id:this.id});}}}async createLocalClient(t){let[e,...r]=t.command;if(!e)return this._info.status={status:"failed",error:"Command is empty"},null;let n=new stdio_js$1.StdioClientTransport({stderr:"pipe",command:e,args:r.length>0?r:void 0,cwd:t.cwd??process.cwd(),env:Object.fromEntries(Object.entries({...process.env,...t.environment}).filter(([,o])=>o!==void 0))});n.stderr?.on("data",o=>{p.info(`mcp stderr: ${o.toString()}`,{id:this.id});});try{let o=new index_js.Client({name:"easbot-mcp",version:I()});return await utils.withTimeout(o.connect(n),t.timeout??E),this.registerNotificationHandler(o),o}catch(o){let h=o instanceof Error?o.message:String(o);return this._info.status={status:"failed",error:h},p.error("local mcp startup failed",{id:this.id,command:[e,...r],error:h}),null}}async createRemoteClient(t){let e={headers:t.headers};if((t.transport||"streamable-http")==="sse"){p.info("using SSE transport",{id:this.id,url:t.url});let n=new sse_js.SSEClientTransport(new URL(t.url),{requestInit:e});return await this.connectWithTransport(n,t)}else {p.info("using StreamableHTTP transport",{id:this.id,url:t.url});let n=new streamableHttp_js$1.StreamableHTTPClientTransport(new URL(t.url),{requestInit:e});return await this.connectWithTransport(n,t)}}async connectWithTransport(t,e){try{let r=new index_js.Client({name:"easbot-mcp",version:I()});return await r.connect(t),this.registerNotificationHandler(r),r}catch(r){return this.handleConnectionError(r,t,e),null}}handleConnectionError(t,e,r){if(t instanceof auth_js.UnauthorizedError){let n=t instanceof Error?t.message:String(t);n.includes("registration")||n.includes("client_id")?this._info.status={status:"needs_client_registration",error:"Server does not support dynamic client registration. Please provide clientId in config."}:(this._info.status={status:"needs_auth"},this.pendingTransport=e,this.capturedRedirectUrl=this.extractRedirectUrl(t));}else {let n=t instanceof Error?t.message:String(t);this._info.status={status:"failed",error:n};}p.error("remote mcp connection failed",{id:this.id,url:r.url,status:this._info.status});}extractRedirectUrl(t){let r=t.message.match(/https?:\/\/[^\s]+/);if(r)try{return new URL(r[0])}catch{return null}return null}registerNotificationHandler(t){let e=async()=>{p.info("tools list changed notification received",{id:this.id}),await this.refreshTools(),this.toolListChangedHandler&&await this.toolListChangedHandler();};t.setNotificationHandler(types_js.ToolListChangedNotificationSchema,e),this.notificationCleanup=()=>{t.removeNotificationHandler("notifications/tools/list_changed");};}createInitialInfo(){return {id:"",name:"",status:{status:"disconnected"},tools:[],resources:[],prompts:[]}}};function pt(){return new x}var he=v__default.default.discriminatedUnion("status",[v__default.default.object({status:v__default.default.literal("connected")}).meta({ref:"MCPStatusConnected"}),v__default.default.object({status:v__default.default.literal("disconnected")}).meta({ref:"MCPStatusDisconnected"}),v__default.default.object({status:v__default.default.literal("failed"),error:v__default.default.string()}).meta({ref:"MCPStatusFailed"}),v__default.default.object({status:v__default.default.literal("needs_auth")}).meta({ref:"MCPStatusNeedsAuth"}),v__default.default.object({status:v__default.default.literal("needs_client_registration"),error:v__default.default.string()}).meta({ref:"MCPStatusNeedsClientRegistration"}),v__default.default.object({status:v__default.default.literal("disabled")}).meta({ref:"MCPStatusDisabled"})]);var Ce="@easbot/mcp";exports.HttpServerAdapter=R;exports.MCPClient=x;exports.NAME=Ce;exports.ServerStatus=l;exports.ServerTransportType=P;exports.Status=he;exports.StdioServerAdapter=b;exports.ToolRegistry=M;exports.ToolRegistryEvent=C;exports.createAndStartHttpServer=J;exports.createAndStartStdioServer=G;exports.createHttpServer=$;exports.createMCPClient=pt;exports.createServerFromConfig=et;exports.createServersFromConfigMap=qt;exports.createStdioServer=q;exports.getVersion=I;exports.tools=T;
3
+ `);}),this.httpServer.listen(e,r),g.info("HTTP server listening",{hostname:r,port:e}),this.info.status=l.RUNNING,this.info.lastActivityAt=Date.now(),g.info("started",{id:this.info.id,name:this.info.name,toolCount:this.tools.getSize()});}catch(t){throw this.info.status=l.ERROR,g.error("failed to start",{id:this.info.id,error:t instanceof Error?t.message:String(t)}),t}finally{this.isStarting=false;}}async stop(){if(this.isStopping){g.warn("already stopping",{id:this.info.id});return}if(this.info.status!==l.RUNNING&&this.info.status!==l.ERROR){g.debug("not running",{id:this.info.id,status:this.info.status});return}this.isStopping=true,this.info.status=l.STOPPING;try{g.info("stopping",{id:this.info.id,name:this.info.name}),this.unsubscribeTools(),this.httpServer&&(await new Promise(t=>{this.httpServer.close(()=>t());}),this.httpServer=null),this.transport&&(await this.transport.close(),this.transport=null),this.server&&(await this.server.close(),this.server=null),this.info.status=l.STOPPED,g.info("stopped",{id:this.info.id});}catch(t){this.info.status=l.ERROR,g.error("failed to stop",{id:this.info.id,error:t instanceof Error?t.message:String(t)});}finally{this.isStopping=false;}}async restart(){g.info("restarting",{id:this.info.id}),await this.stop(),await this.start();}};function B(s,t){return new R(s,t)}async function $(s,t){let e=new R(s,t);return await e.start(),e.createInstance()}var k=utils.Log.create({service:"mcp.server.factory"});function W(s){return s.type==="local"}function Q(s){return s.type==="remote"}function X(s,t){if(!t.command)throw new Error(`MCP server ${s} has no command specified`);return {id:s,name:s,version:"1.0.0",transportType:w.STDIO,enabled:t.enabled,timeout:t.timeout,command:t.command,args:(t?.args?.length??0)>0?t.args:void 0,env:t.env}}function Y(s,t){return {id:s,name:s,version:"1.0.0",transportType:w.HTTP,enabled:t.enabled,timeout:t.timeout,url:t.url,headers:t.headers}}async function tt(s,t,e){if(k.debug("creating server from config",{name:s,type:t.type}),W(t)){let r=X(s,t),o=new C(r,e);return await o.start(),o.createInstance()}if(Q(t)){let r=Y(s,t),o=new R(r,e);return await o.start(),o.createInstance()}throw new Error(`Unsupported MCP config type: ${t.type}`)}async function Kt(s,t){let e={};for(let[r,o]of Object.entries(s))try{let n=await tt(r,o,t);e[r]=n;}catch(n){k.error("failed to create server from config",{name:r,error:n instanceof Error?n.message:String(n)});}return e}var A={version:"0.3.3"};function P(){return A.version}var p=utils.Log.create({service:"mcp.client"}),I=6e4,E=class{constructor(){a(this,"id","");a(this,"client",null);a(this,"_info");a(this,"notificationCleanup",null);a(this,"pendingTransport",null);a(this,"oauthState","");a(this,"capturedRedirectUrl",null);a(this,"oauthConfig");a(this,"toolListChangedHandler",null);a(this,"oauthProvider",null);this._info=this.createInitialInfo();}get info(){return {...this._info}}get status(){return this._info.status}get tools(){return [...this._info.tools]}get resources(){return [...this._info.resources]}get prompts(){return [...this._info.prompts]}get clientId(){return this.id}setToolListChangedHandler(t){this.toolListChangedHandler=t;}setOAuthProvider(t){this.oauthProvider=t;}async connect(t,e){p.info("connecting to MCP server",{id:t,type:e.type}),await this.disconnect(),this.id=t,this._info=this.createInitialInfo(),this._info.id=t,this._info.name=t;try{if(e.type==="remote"&&(this.oauthConfig=e.oauth===!1?void 0:e.oauth??{}),this.client=await this.createClient(e),!this.client)return;this._info.status={status:"connected"},this._info.connectedAt=Date.now(),await this.fetchCapabilities(),p.info("connected to MCP server",{id:t,toolCount:this._info.tools.length});}catch(r){let o=r instanceof Error?r.message:String(r);this._info.status={status:"failed",error:o},this._info.error=o,p.error("failed to connect to MCP server",{id:t,error:o});}}async startAuth(){if(!this.pendingTransport)throw new Error("No pending OAuth flow. Please connect to a server that requires authentication first.");return this.oauthState=Array.from(crypto.getRandomValues(new Uint8Array(32))).map(t=>t.toString(16).padStart(2,"0")).join(""),this.oauthProvider&&await this.oauthProvider.saveState(this.oauthState),{authorizationUrl:this.capturedRedirectUrl?.toString()??"",state:this.oauthState}}async finishAuth(t){if(!this.pendingTransport)throw new Error("No pending OAuth flow.");try{await this.pendingTransport.finishAuth?.(t);}catch(e){throw p.error("failed to finish OAuth",{id:this.id,error:e}),e}this.pendingTransport=null,this.capturedRedirectUrl=null,p.info("OAuth completed, ready to reconnect",{id:this.id});}isAuthRequired(){return this._info.status.status==="needs_auth"}isClientRegistrationRequired(){return this._info.status.status==="needs_client_registration"}getPendingTransport(){return this.pendingTransport}getAuthorizationUrl(){return this.capturedRedirectUrl?.toString()??""}async disconnect(){this.notificationCleanup&&(this.notificationCleanup(),this.notificationCleanup=null),this.client&&(await this.client.close().catch(t=>{p.error("Failed to close MCP client",{id:this.id,error:t});}),this.client=null),this.pendingTransport=null,this.capturedRedirectUrl=null,this.toolListChangedHandler=null,this._info.status.status==="connected"&&(this._info.status={status:"disconnected"}),p.info("disconnected from MCP server",{id:this.id});}async listTools(){if(!this.client||this._info.status.status!=="connected")return {};let t={};for(let e of this._info.tools)t[e.name]={name:e.name,description:e.description,inputSchema:e.inputSchema};return t}async callTool(t,e,r){let o=Date.now();if(!this.client||this._info.status.status!=="connected")return {toolName:t,args:e,content:[],success:false,error:"Client not connected",duration:Date.now()-o};try{let n=await utils.withTimeout(this.client.callTool({name:t,arguments:e},types_js.CallToolResultSchema,{resetTimeoutOnProgress:!0,timeout:r}),r??I);return {toolName:t,args:e,content:n.content.map(h=>h.type==="text"?{type:"text",text:h.text}:h.type==="image"?{type:"image",data:h.data,mimeType:h.mimeType}:h.type==="resource"?{type:"resource",resource:h.resource}:{type:"text",text:String(h)}),success:!0,duration:Date.now()-o,metadata:n.metadata}}catch(n){return {toolName:t,args:e,content:[],success:false,error:n instanceof Error?n.message:String(n),duration:Date.now()-o}}}async listResources(){return [...this._info.resources]}async readResource(t){if(!this.client||this._info.status.status!=="connected"){p.warn("client not connected for readResource",{id:this.id});return}return this.client.readResource({uri:t}).catch(e=>{p.error("failed to read resource",{id:this.id,uri:t,error:e.message});})}async listPrompts(){return [...this._info.prompts]}async getPrompt(t,e){if(!this.client||this._info.status.status!=="connected"){p.warn("client not connected for getPrompt",{id:this.id});return}return this.client.getPrompt({name:t,arguments:e}).catch(r=>{p.error("failed to get prompt",{id:this.id,name:t,error:r.message});})}async refreshTools(){if(!(!this.client||this._info.status.status!=="connected"))try{let t=await utils.withTimeout(this.client.listTools(),I);this._info.tools=t.tools.map(e=>({name:e.name,description:e.description,inputSchema:e.inputSchema})),p.info("tools refreshed",{id:this.id,count:this._info.tools.length});}catch(t){p.error("failed to refresh tools",{id:this.id,error:t instanceof Error?t.message:String(t)});}}async createClient(t){return t.type==="local"?await this.createLocalClient(t):t.type==="remote"?await this.createRemoteClient(t):null}async fetchCapabilities(){if(this.client){try{let t=await utils.withTimeout(this.client.listTools(),I);this._info.tools=t.tools.map(e=>({name:e.name,description:e.description,inputSchema:e.inputSchema}));}catch(t){p.debug("failed to get tools",{id:this.id,error:t instanceof Error?t.message:String(t)});}try{let t=await utils.withTimeout(this.client.listResources(),5e3);this._info.resources=t.resources.map(e=>({name:e.name,uri:e.uri,description:e.description,mimeType:e.mimeType}));}catch{p.debug("failed to get resources",{id:this.id});}try{let t=await utils.withTimeout(this.client.listPrompts(),5e3);this._info.prompts=t.prompts.map(e=>({name:e.name,description:e.description,arguments:e.arguments?.map(r=>({name:r.name,description:r.description,required:r.required}))}));}catch{p.debug("failed to get prompts",{id:this.id});}}}async createLocalClient(t){let[e,...r]=t.command;if(!e)return this._info.status={status:"failed",error:"Command is empty"},null;let o=new stdio_js$1.StdioClientTransport({stderr:"pipe",command:e,args:r.length>0?r:void 0,cwd:t.cwd??process.cwd(),env:Object.fromEntries(Object.entries({...process.env,...t.environment}).filter(([,n])=>n!==void 0))});o.stderr?.on("data",n=>{p.info(`mcp stderr: ${n.toString()}`,{id:this.id});});try{let n=new index_js.Client({name:"easbot-mcp",version:P()});return await utils.withTimeout(n.connect(o),t.timeout??I),this.registerNotificationHandler(n),n}catch(n){let h=n instanceof Error?n.message:String(n);return this._info.status={status:"failed",error:h},p.error("local mcp startup failed",{id:this.id,command:[e,...r],error:h}),null}}async createRemoteClient(t){let e={headers:t.headers};if((t.transport||"streamable-http")==="sse"){p.info("using SSE transport",{id:this.id,url:t.url});let o=new sse_js.SSEClientTransport(new URL(t.url),{requestInit:e});return await this.connectWithTransport(o,t)}else {p.info("using StreamableHTTP transport",{id:this.id,url:t.url});let o=new streamableHttp_js$1.StreamableHTTPClientTransport(new URL(t.url),{requestInit:e});return await this.connectWithTransport(o,t)}}async connectWithTransport(t,e){try{let r=new index_js.Client({name:"easbot-mcp",version:P()});return await r.connect(t),this.registerNotificationHandler(r),r}catch(r){return this.handleConnectionError(r,t,e),null}}handleConnectionError(t,e,r){if(t instanceof auth_js.UnauthorizedError){let o=t instanceof Error?t.message:String(t);o.includes("registration")||o.includes("client_id")?this._info.status={status:"needs_client_registration",error:"Server does not support dynamic client registration. Please provide clientId in config."}:(this._info.status={status:"needs_auth"},this.pendingTransport=e,this.capturedRedirectUrl=this.extractRedirectUrl(t));}else {let o=t instanceof Error?t.message:String(t);this._info.status={status:"failed",error:o};}p.error("remote mcp connection failed",{id:this.id,url:r.url,status:this._info.status});}extractRedirectUrl(t){let r=t.message.match(/https?:\/\/[^\s]+/);if(r)try{return new URL(r[0])}catch{return null}return null}registerNotificationHandler(t){let e=async()=>{p.info("tools list changed notification received",{id:this.id}),await this.refreshTools(),this.toolListChangedHandler&&await this.toolListChangedHandler();};t.setNotificationHandler(types_js.ToolListChangedNotificationSchema,e),this.notificationCleanup=()=>{t.removeNotificationHandler("notifications/tools/list_changed");};}createInitialInfo(){return {id:"",name:"",status:{status:"disconnected"},tools:[],resources:[],prompts:[]}}};function lt(){return new E}var ve=d__default.default.discriminatedUnion("status",[d__default.default.object({status:d__default.default.literal("connected")}).meta({ref:"MCPStatusConnected"}),d__default.default.object({status:d__default.default.literal("disconnected")}).meta({ref:"MCPStatusDisconnected"}),d__default.default.object({status:d__default.default.literal("failed"),error:d__default.default.string()}).meta({ref:"MCPStatusFailed"}),d__default.default.object({status:d__default.default.literal("needs_auth")}).meta({ref:"MCPStatusNeedsAuth"}),d__default.default.object({status:d__default.default.literal("needs_client_registration"),error:d__default.default.string()}).meta({ref:"MCPStatusNeedsClientRegistration"}),d__default.default.object({status:d__default.default.literal("disabled")}).meta({ref:"MCPStatusDisabled"})]);var we="@easbot/mcp";exports.HttpServerAdapter=R;exports.MCPClient=E;exports.NAME=we;exports.ServerStatus=l;exports.ServerTransportType=w;exports.Status=ve;exports.StdioServerAdapter=C;exports.ToolRegistry=M;exports.ToolRegistryEvent=T;exports.createAndStartHttpServer=$;exports.createAndStartStdioServer=q;exports.createHttpServer=B;exports.createMCPClient=lt;exports.createServerFromConfig=tt;exports.createServersFromConfigMap=Kt;exports.createStdioServer=z;exports.getVersion=P;
package/dist/index.d.cts CHANGED
@@ -1,9 +1,9 @@
1
1
  import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
- import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js';
3
- import { SSEClientTransport } from '@modelcontextprotocol/sdk/client/sse.js';
4
- import z, { ZodType } from 'zod';
5
2
  import { EventEmitter } from 'node:events';
6
3
  import { Tool, CallToolResult } from '@modelcontextprotocol/sdk/types.js';
4
+ import z, { ZodType } from 'zod';
5
+ import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js';
6
+ import { SSEClientTransport } from '@modelcontextprotocol/sdk/client/sse.js';
7
7
 
8
8
  interface McpLocalConfig {
9
9
  type: 'local';
@@ -82,6 +82,43 @@ interface ServerInstance$2 {
82
82
  restart(): Promise<void>;
83
83
  }
84
84
 
85
+ type ToolInputSchema = ZodType | Record<string, unknown>;
86
+ type RequestId = string | number;
87
+ interface ToolContext {
88
+ signal?: AbortSignal;
89
+ sessionId?: string;
90
+ requestId: RequestId;
91
+ taskId?: string;
92
+ _meta?: Record<string, unknown>;
93
+ authInfo?: {
94
+ type: string;
95
+ claims?: Record<string, unknown>;
96
+ };
97
+ }
98
+ type ToolHandler = (args: unknown, ctx: ToolContext) => Promise<CallToolResult>;
99
+ interface ToolDefinition extends Omit<Tool, 'inputSchema'> {
100
+ inputSchema?: ToolInputSchema;
101
+ handler: ToolHandler;
102
+ }
103
+ declare const ToolRegistryEvent: {
104
+ readonly REGISTERED: "mcp.tool.registered";
105
+ readonly UNREGISTERED: "mcp.tool.unregistered";
106
+ readonly CLEARED: "mcp.tool.cleared";
107
+ };
108
+ type ToolRegistryEvent = (typeof ToolRegistryEvent)[keyof typeof ToolRegistryEvent];
109
+ declare class ToolRegistry extends EventEmitter {
110
+ private tools;
111
+ constructor();
112
+ register(tool: ToolDefinition): void;
113
+ unregister(name: string): boolean;
114
+ get(name: string): ToolDefinition | undefined;
115
+ getAll(): ToolDefinition[];
116
+ getTools(): Omit<ToolDefinition, 'handler'>[];
117
+ has(name: string): boolean;
118
+ getSize(): number;
119
+ clear(): void;
120
+ }
121
+
85
122
  interface ServerInstance$1 {
86
123
  info: ServerInfo;
87
124
  get server(): McpServer | null;
@@ -96,7 +133,8 @@ declare class StdioServerAdapter {
96
133
  private isStarting;
97
134
  private isStopping;
98
135
  private toolEventCleanup;
99
- constructor(config: StdioServerConfig);
136
+ private readonly tools;
137
+ constructor(config: StdioServerConfig, tools: ToolRegistry);
100
138
  getInfo(): ServerInfo;
101
139
  getConfig(): StdioServerConfig;
102
140
  createInstance(): ServerInstance$1;
@@ -111,8 +149,8 @@ declare class StdioServerAdapter {
111
149
  stop(): Promise<void>;
112
150
  restart(): Promise<void>;
113
151
  }
114
- declare function createStdioServer(config: StdioServerConfig): StdioServerAdapter;
115
- declare function createAndStartStdioServer(config: StdioServerConfig): Promise<ServerInstance$1>;
152
+ declare function createStdioServer(config: StdioServerConfig, tools: ToolRegistry): StdioServerAdapter;
153
+ declare function createAndStartStdioServer(config: StdioServerConfig, tools: ToolRegistry): Promise<ServerInstance$1>;
116
154
 
117
155
  interface ServerInstance {
118
156
  info: ServerInfo;
@@ -130,7 +168,8 @@ declare class HttpServerAdapter {
130
168
  private isStarting;
131
169
  private isStopping;
132
170
  private toolCleanup;
133
- constructor(config: HttpServerConfig);
171
+ private readonly tools;
172
+ constructor(config: HttpServerConfig, tools: ToolRegistry);
134
173
  getInfo(): ServerInfo;
135
174
  getConfig(): HttpServerConfig;
136
175
  createInstance(): ServerInstance;
@@ -145,11 +184,11 @@ declare class HttpServerAdapter {
145
184
  stop(): Promise<void>;
146
185
  restart(): Promise<void>;
147
186
  }
148
- declare function createHttpServer(config: HttpServerConfig): HttpServerAdapter;
149
- declare function createAndStartHttpServer(config: HttpServerConfig): Promise<ServerInstance>;
187
+ declare function createHttpServer(config: HttpServerConfig, tools: ToolRegistry): HttpServerAdapter;
188
+ declare function createAndStartHttpServer(config: HttpServerConfig, tools: ToolRegistry): Promise<ServerInstance>;
150
189
 
151
- declare function createServerFromConfig(name: string, config: McpConfig): Promise<ServerInstance$2>;
152
- declare function createServersFromConfigMap(configs: Record<string, McpConfig>): Promise<Record<string, ServerInstance$2>>;
190
+ declare function createServerFromConfig(name: string, config: McpConfig, tools: ToolRegistry): Promise<ServerInstance$2>;
191
+ declare function createServersFromConfigMap(configs: Record<string, McpConfig>, tools: ToolRegistry): Promise<Record<string, ServerInstance$2>>;
153
192
 
154
193
  interface OAuthConfig {
155
194
  clientId?: string;
@@ -428,46 +467,8 @@ declare class MCPClient {
428
467
  }
429
468
  declare function createMCPClient(): MCPClient;
430
469
 
431
- type ToolInputSchema = ZodType | Record<string, unknown>;
432
- type RequestId = string | number;
433
- interface ToolContext {
434
- signal?: AbortSignal;
435
- sessionId?: string;
436
- requestId: RequestId;
437
- taskId?: string;
438
- _meta?: Record<string, unknown>;
439
- authInfo?: {
440
- type: string;
441
- claims?: Record<string, unknown>;
442
- };
443
- }
444
- type ToolHandler = (args: unknown, ctx: ToolContext) => Promise<CallToolResult>;
445
- interface ToolDefinition extends Omit<Tool, 'inputSchema'> {
446
- inputSchema?: ToolInputSchema;
447
- handler: ToolHandler;
448
- }
449
- declare const ToolRegistryEvent: {
450
- readonly REGISTERED: "mcp.tool.registered";
451
- readonly UNREGISTERED: "mcp.tool.unregistered";
452
- readonly CLEARED: "mcp.tool.cleared";
453
- };
454
- type ToolRegistryEvent = (typeof ToolRegistryEvent)[keyof typeof ToolRegistryEvent];
455
- declare class ToolRegistry extends EventEmitter {
456
- private tools;
457
- constructor();
458
- register(tool: ToolDefinition): void;
459
- unregister(name: string): boolean;
460
- get(name: string): ToolDefinition | undefined;
461
- getAll(): ToolDefinition[];
462
- getTools(): Omit<ToolDefinition, 'handler'>[];
463
- has(name: string): boolean;
464
- getSize(): number;
465
- clear(): void;
466
- }
467
- declare const tools: ToolRegistry;
468
-
469
470
  declare function getVersion(): string;
470
471
 
471
472
  declare const NAME = "@easbot/mcp";
472
473
 
473
- export { type AuthResult, type ClientInfo, HttpServerAdapter, type HttpServerConfig, type LocalMCPConfig, MCPClient, type MCPConfig, type McpConfig, type McpLocalConfig, type McpOAuthConfig, type McpRemoteConfig, NAME, type OAuthClientProviderAdapter, type OAuthConfig, type PromptMetadata, type RemoteMCPConfig, type RequestId, type ResourceMetadata, type ServerInfo, type ServerInstance$2 as ServerInstance, ServerStatus, ServerTransportType, Status, StdioServerAdapter, type StdioServerConfig, type ToolCallResult, type ToolContentItem, type ToolContext, type ToolDefinition, type ToolHandler, type ToolInputSchema, type ToolListChangedHandler, type ToolMetadata, ToolRegistry, ToolRegistryEvent, createAndStartHttpServer, createAndStartStdioServer, createHttpServer, createMCPClient, createServerFromConfig, createServersFromConfigMap, createStdioServer, getVersion, tools };
474
+ export { type AuthResult, type ClientInfo, HttpServerAdapter, type HttpServerConfig, type LocalMCPConfig, MCPClient, type MCPConfig, type McpConfig, type McpLocalConfig, type McpOAuthConfig, type McpRemoteConfig, NAME, type OAuthClientProviderAdapter, type OAuthConfig, type PromptMetadata, type RemoteMCPConfig, type RequestId, type ResourceMetadata, type ServerInfo, type ServerInstance$2 as ServerInstance, ServerStatus, ServerTransportType, Status, StdioServerAdapter, type StdioServerConfig, type ToolCallResult, type ToolContentItem, type ToolContext, type ToolDefinition, type ToolHandler, type ToolInputSchema, type ToolListChangedHandler, type ToolMetadata, ToolRegistry, ToolRegistryEvent, createAndStartHttpServer, createAndStartStdioServer, createHttpServer, createMCPClient, createServerFromConfig, createServersFromConfigMap, createStdioServer, getVersion };
package/dist/index.d.ts CHANGED
@@ -1,9 +1,9 @@
1
1
  import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
- import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js';
3
- import { SSEClientTransport } from '@modelcontextprotocol/sdk/client/sse.js';
4
- import z, { ZodType } from 'zod';
5
2
  import { EventEmitter } from 'node:events';
6
3
  import { Tool, CallToolResult } from '@modelcontextprotocol/sdk/types.js';
4
+ import z, { ZodType } from 'zod';
5
+ import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js';
6
+ import { SSEClientTransport } from '@modelcontextprotocol/sdk/client/sse.js';
7
7
 
8
8
  interface McpLocalConfig {
9
9
  type: 'local';
@@ -82,6 +82,43 @@ interface ServerInstance$2 {
82
82
  restart(): Promise<void>;
83
83
  }
84
84
 
85
+ type ToolInputSchema = ZodType | Record<string, unknown>;
86
+ type RequestId = string | number;
87
+ interface ToolContext {
88
+ signal?: AbortSignal;
89
+ sessionId?: string;
90
+ requestId: RequestId;
91
+ taskId?: string;
92
+ _meta?: Record<string, unknown>;
93
+ authInfo?: {
94
+ type: string;
95
+ claims?: Record<string, unknown>;
96
+ };
97
+ }
98
+ type ToolHandler = (args: unknown, ctx: ToolContext) => Promise<CallToolResult>;
99
+ interface ToolDefinition extends Omit<Tool, 'inputSchema'> {
100
+ inputSchema?: ToolInputSchema;
101
+ handler: ToolHandler;
102
+ }
103
+ declare const ToolRegistryEvent: {
104
+ readonly REGISTERED: "mcp.tool.registered";
105
+ readonly UNREGISTERED: "mcp.tool.unregistered";
106
+ readonly CLEARED: "mcp.tool.cleared";
107
+ };
108
+ type ToolRegistryEvent = (typeof ToolRegistryEvent)[keyof typeof ToolRegistryEvent];
109
+ declare class ToolRegistry extends EventEmitter {
110
+ private tools;
111
+ constructor();
112
+ register(tool: ToolDefinition): void;
113
+ unregister(name: string): boolean;
114
+ get(name: string): ToolDefinition | undefined;
115
+ getAll(): ToolDefinition[];
116
+ getTools(): Omit<ToolDefinition, 'handler'>[];
117
+ has(name: string): boolean;
118
+ getSize(): number;
119
+ clear(): void;
120
+ }
121
+
85
122
  interface ServerInstance$1 {
86
123
  info: ServerInfo;
87
124
  get server(): McpServer | null;
@@ -96,7 +133,8 @@ declare class StdioServerAdapter {
96
133
  private isStarting;
97
134
  private isStopping;
98
135
  private toolEventCleanup;
99
- constructor(config: StdioServerConfig);
136
+ private readonly tools;
137
+ constructor(config: StdioServerConfig, tools: ToolRegistry);
100
138
  getInfo(): ServerInfo;
101
139
  getConfig(): StdioServerConfig;
102
140
  createInstance(): ServerInstance$1;
@@ -111,8 +149,8 @@ declare class StdioServerAdapter {
111
149
  stop(): Promise<void>;
112
150
  restart(): Promise<void>;
113
151
  }
114
- declare function createStdioServer(config: StdioServerConfig): StdioServerAdapter;
115
- declare function createAndStartStdioServer(config: StdioServerConfig): Promise<ServerInstance$1>;
152
+ declare function createStdioServer(config: StdioServerConfig, tools: ToolRegistry): StdioServerAdapter;
153
+ declare function createAndStartStdioServer(config: StdioServerConfig, tools: ToolRegistry): Promise<ServerInstance$1>;
116
154
 
117
155
  interface ServerInstance {
118
156
  info: ServerInfo;
@@ -130,7 +168,8 @@ declare class HttpServerAdapter {
130
168
  private isStarting;
131
169
  private isStopping;
132
170
  private toolCleanup;
133
- constructor(config: HttpServerConfig);
171
+ private readonly tools;
172
+ constructor(config: HttpServerConfig, tools: ToolRegistry);
134
173
  getInfo(): ServerInfo;
135
174
  getConfig(): HttpServerConfig;
136
175
  createInstance(): ServerInstance;
@@ -145,11 +184,11 @@ declare class HttpServerAdapter {
145
184
  stop(): Promise<void>;
146
185
  restart(): Promise<void>;
147
186
  }
148
- declare function createHttpServer(config: HttpServerConfig): HttpServerAdapter;
149
- declare function createAndStartHttpServer(config: HttpServerConfig): Promise<ServerInstance>;
187
+ declare function createHttpServer(config: HttpServerConfig, tools: ToolRegistry): HttpServerAdapter;
188
+ declare function createAndStartHttpServer(config: HttpServerConfig, tools: ToolRegistry): Promise<ServerInstance>;
150
189
 
151
- declare function createServerFromConfig(name: string, config: McpConfig): Promise<ServerInstance$2>;
152
- declare function createServersFromConfigMap(configs: Record<string, McpConfig>): Promise<Record<string, ServerInstance$2>>;
190
+ declare function createServerFromConfig(name: string, config: McpConfig, tools: ToolRegistry): Promise<ServerInstance$2>;
191
+ declare function createServersFromConfigMap(configs: Record<string, McpConfig>, tools: ToolRegistry): Promise<Record<string, ServerInstance$2>>;
153
192
 
154
193
  interface OAuthConfig {
155
194
  clientId?: string;
@@ -428,46 +467,8 @@ declare class MCPClient {
428
467
  }
429
468
  declare function createMCPClient(): MCPClient;
430
469
 
431
- type ToolInputSchema = ZodType | Record<string, unknown>;
432
- type RequestId = string | number;
433
- interface ToolContext {
434
- signal?: AbortSignal;
435
- sessionId?: string;
436
- requestId: RequestId;
437
- taskId?: string;
438
- _meta?: Record<string, unknown>;
439
- authInfo?: {
440
- type: string;
441
- claims?: Record<string, unknown>;
442
- };
443
- }
444
- type ToolHandler = (args: unknown, ctx: ToolContext) => Promise<CallToolResult>;
445
- interface ToolDefinition extends Omit<Tool, 'inputSchema'> {
446
- inputSchema?: ToolInputSchema;
447
- handler: ToolHandler;
448
- }
449
- declare const ToolRegistryEvent: {
450
- readonly REGISTERED: "mcp.tool.registered";
451
- readonly UNREGISTERED: "mcp.tool.unregistered";
452
- readonly CLEARED: "mcp.tool.cleared";
453
- };
454
- type ToolRegistryEvent = (typeof ToolRegistryEvent)[keyof typeof ToolRegistryEvent];
455
- declare class ToolRegistry extends EventEmitter {
456
- private tools;
457
- constructor();
458
- register(tool: ToolDefinition): void;
459
- unregister(name: string): boolean;
460
- get(name: string): ToolDefinition | undefined;
461
- getAll(): ToolDefinition[];
462
- getTools(): Omit<ToolDefinition, 'handler'>[];
463
- has(name: string): boolean;
464
- getSize(): number;
465
- clear(): void;
466
- }
467
- declare const tools: ToolRegistry;
468
-
469
470
  declare function getVersion(): string;
470
471
 
471
472
  declare const NAME = "@easbot/mcp";
472
473
 
473
- export { type AuthResult, type ClientInfo, HttpServerAdapter, type HttpServerConfig, type LocalMCPConfig, MCPClient, type MCPConfig, type McpConfig, type McpLocalConfig, type McpOAuthConfig, type McpRemoteConfig, NAME, type OAuthClientProviderAdapter, type OAuthConfig, type PromptMetadata, type RemoteMCPConfig, type RequestId, type ResourceMetadata, type ServerInfo, type ServerInstance$2 as ServerInstance, ServerStatus, ServerTransportType, Status, StdioServerAdapter, type StdioServerConfig, type ToolCallResult, type ToolContentItem, type ToolContext, type ToolDefinition, type ToolHandler, type ToolInputSchema, type ToolListChangedHandler, type ToolMetadata, ToolRegistry, ToolRegistryEvent, createAndStartHttpServer, createAndStartStdioServer, createHttpServer, createMCPClient, createServerFromConfig, createServersFromConfigMap, createStdioServer, getVersion, tools };
474
+ export { type AuthResult, type ClientInfo, HttpServerAdapter, type HttpServerConfig, type LocalMCPConfig, MCPClient, type MCPConfig, type McpConfig, type McpLocalConfig, type McpOAuthConfig, type McpRemoteConfig, NAME, type OAuthClientProviderAdapter, type OAuthConfig, type PromptMetadata, type RemoteMCPConfig, type RequestId, type ResourceMetadata, type ServerInfo, type ServerInstance$2 as ServerInstance, ServerStatus, ServerTransportType, Status, StdioServerAdapter, type StdioServerConfig, type ToolCallResult, type ToolContentItem, type ToolContext, type ToolDefinition, type ToolHandler, type ToolInputSchema, type ToolListChangedHandler, type ToolMetadata, ToolRegistry, ToolRegistryEvent, createAndStartHttpServer, createAndStartStdioServer, createHttpServer, createMCPClient, createServerFromConfig, createServersFromConfigMap, createStdioServer, getVersion };
package/dist/index.mjs CHANGED
@@ -1,3 +1,3 @@
1
- import {McpServer}from'@modelcontextprotocol/sdk/server/mcp.js';import {StdioServerTransport}from'@modelcontextprotocol/sdk/server/stdio.js';import {Log,withTimeout}from'@easbot/utils';import {EventEmitter}from'events';import d from'zod/v4';import K from'http';import {StreamableHTTPServerTransport}from'@modelcontextprotocol/sdk/server/streamableHttp.js';import {Client}from'@modelcontextprotocol/sdk/client/index.js';import {StdioClientTransport}from'@modelcontextprotocol/sdk/client/stdio.js';import {StreamableHTTPClientTransport}from'@modelcontextprotocol/sdk/client/streamableHttp.js';import {SSEClientTransport}from'@modelcontextprotocol/sdk/client/sse.js';import {CallToolResultSchema,ToolListChangedNotificationSchema}from'@modelcontextprotocol/sdk/types.js';import {UnauthorizedError}from'@modelcontextprotocol/sdk/client/auth.js';import v from'zod';var _=Object.defineProperty;var N=(s,t,e)=>t in s?_(s,t,{enumerable:true,configurable:true,writable:true,value:e}):s[t]=e;var a=(s,t,e)=>N(s,typeof t!="symbol"?t+"":t,e);var I={STDIO:"stdio",HTTP:"http"},l={STOPPED:"stopped",STARTING:"starting",RUNNING:"running",STOPPING:"stopping",ERROR:"error"};var U=Log.create({service:"tool-registry"}),b={REGISTERED:"mcp.tool.registered",UNREGISTERED:"mcp.tool.unregistered",CLEARED:"mcp.tool.cleared"},k=class extends EventEmitter{constructor(){super();a(this,"tools",new Map);}register(e){if(!e.name||typeof e.name!="string")throw new Error("\u5DE5\u5177\u540D\u79F0\u65E0\u6548\uFF1A\u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32");if(this.tools.has(e.name)){U?.debug("tool already registered, skipping",{tool:e.name});return}this.tools.set(e.name,e),this.emit(b.REGISTERED,e);}unregister(e){return this.tools.has(e)?(this.tools.delete(e),this.emit(b.UNREGISTERED,e),true):false}get(e){return this.tools.get(e)}getAll(){return Array.from(this.tools.values())}getTools(){return Array.from(this.tools.values()).map(({handler:e,...r})=>r)}has(e){return this.tools.has(e)}getSize(){return this.tools.size}clear(){this.tools.clear(),this.emit(b.CLEARED);}},C=new k;var y=Log.create({service:"mcp.server.stdio"}),R=class{constructor(t){a(this,"config");a(this,"server",null);a(this,"info");a(this,"isStarting",false);a(this,"isStopping",false);a(this,"toolEventCleanup",null);a(this,"handleToolRegistered",t=>{if(this.server){let e=this.convertToZodSchema(t.inputSchema),r=this.adaptToolHandler(t.handler);this.server.registerTool(t.name,{description:t.description,inputSchema:e},r),y.info("tool registered",{tool:t.name});}});this.config=t,this.info={id:t.id,name:t.name,version:t.version,transportType:t.transportType,status:l.STOPPED,createdAt:Date.now(),lastActivityAt:Date.now(),enabled:t.enabled};}getInfo(){return this.info}getConfig(){return this.config}createInstance(){let t=this;return {info:this.info,get server(){return t.server},start:()=>this.start(),stop:()=>this.stop(),restart:()=>this.restart()}}loadTools(){if(this.server)for(let t of C.getAll()){let e=this.convertToZodSchema(t.inputSchema),r=this.adaptToolHandler(t.handler);this.server.registerTool(t.name,{description:t.description,inputSchema:e},r),y.info("tool loaded",{tool:t.name});}}subscribeToolEvents(){this.toolEventCleanup=()=>{C.off(b.REGISTERED,this.handleToolRegistered);},C.on(b.REGISTERED,this.handleToolRegistered);}adaptToolHandler(t){return async(e,r)=>{let n={signal:r.signal,sessionId:r.sessionId,requestId:r.requestId,taskId:r.taskId,_meta:r._meta,authInfo:r.authInfo?{type:"bearer",claims:{clientId:r.authInfo.clientId,scopes:r.authInfo.scopes}}:void 0};return t(e,n)}}unsubscribeToolEvents(){this.toolEventCleanup&&(this.toolEventCleanup(),this.toolEventCleanup=null);}convertToZodSchema(t){if(t instanceof d.ZodType)return t;if(typeof t=="object"&&t!==null){let e=t;if(e.type==="object"){let r=e.properties||{},n=e.required||[],o={};for(let[i,m]of Object.entries(r))o[i]=this.jsonSchemaToZod(m);if(Object.keys(r).filter(i=>!n.includes(i)).length===0)return d.object(o);let f={};for(let[i,m]of Object.entries(r)){let c=o[i];c&&(n.includes(i)?f[i]=c:f[i]=c.optional());}return d.object(f)}if(e.type==="string")return d.string();if(e.type==="number")return d.number();if(e.type==="boolean")return d.boolean();if(e.type==="array"){let r=e.items;return r?d.array(this.jsonSchemaToZod(r)):d.array(d.unknown())}}return d.unknown()}jsonSchemaToZod(t){switch(t.type){case "string":return d.string();case "number":return d.number();case "integer":return d.number().int();case "boolean":return d.boolean();case "null":return d.null();case "array":{let r=t.items;return r?d.array(this.jsonSchemaToZod(r)):d.array(d.unknown())}case "object":{let r=t.properties||{},n=t.required||[],o={};for(let[i,m]of Object.entries(r))o[i]=this.jsonSchemaToZod(m);if(Object.keys(r).filter(i=>!n.includes(i)).length===0)return d.object(o);let f={};for(let[i,m]of Object.entries(r)){let c=o[i];c&&(n.includes(i)?f[i]=c:f[i]=c.optional());}return d.object(f)}default:return d.unknown()}}async start(){if(this.isStarting){y.warn("server is already starting",{id:this.info.id});return}if(this.info.status===l.RUNNING){y.debug("server is already running",{id:this.info.id});return}this.isStarting=true,this.info.status=l.STARTING;try{y.info("starting stdio mcp server",{id:this.info.id,name:this.info.name,command:this.config.command,args:this.config.args});let t=new StdioServerTransport;this.server=new McpServer({name:this.config.name,version:this.config.version}),this.loadTools(),this.subscribeToolEvents(),await this.server.connect(t),this.info.status=l.RUNNING,this.info.lastActivityAt=Date.now(),y.info("stdio mcp server started successfully",{id:this.info.id,name:this.info.name,toolCount:C.getSize()});}catch(t){throw this.info.status=l.ERROR,y.error("failed to start stdio mcp server",{id:this.info.id,error:t instanceof Error?t.message:String(t)}),t}finally{this.isStarting=false;}}async stop(){if(this.isStopping){y.warn("server is already stopping",{id:this.info.id});return}if(this.info.status!==l.RUNNING&&this.info.status!==l.ERROR){y.debug("server is not running",{id:this.info.id,status:this.info.status});return}this.isStopping=true,this.info.status=l.STOPPING;try{y.info("stopping stdio mcp server",{id:this.info.id,name:this.info.name}),this.unsubscribeToolEvents(),this.server&&(await this.server.close(),this.server=null),this.info.status=l.STOPPED,y.info("stdio mcp server stopped",{id:this.info.id,name:this.info.name});}catch(t){this.info.status=l.ERROR,y.error("error stopping stdio mcp server",{id:this.info.id,error:t instanceof Error?t.message:String(t)});}finally{this.isStopping=false;}}async restart(){y.info("restarting stdio mcp server",{id:this.info.id}),await this.stop(),await this.start();}};function G(s){return new R(s)}async function V(s){let t=new R(s);return await t.start(),t.createInstance()}var g=Log.create({service:"mcp.server.http"}),w=class{constructor(t){a(this,"config");a(this,"server",null);a(this,"transport",null);a(this,"httpServer",null);a(this,"info");a(this,"isStarting",false);a(this,"isStopping",false);a(this,"toolCleanup",null);a(this,"handleTool",t=>{if(this.server){let e=this.convertToZodSchema(t.inputSchema),r=this.adaptToolHandler(t.handler);this.server.registerTool(t.name,{description:t.description,inputSchema:e},r),g.info("tool registered",{tool:t.name});}});this.config=t,this.info={id:t.id,name:t.name,version:t.version,transportType:t.transportType,status:l.STOPPED,createdAt:Date.now(),lastActivityAt:Date.now()};}getInfo(){return this.info}getConfig(){return this.config}createInstance(){let t=this;return {info:this.info,get server(){return t.server},start:()=>this.start(),stop:()=>this.stop(),restart:()=>this.restart()}}loadTools(){if(this.server)for(let t of C.getAll()){let e=this.convertToZodSchema(t.inputSchema),r=this.adaptToolHandler(t.handler);this.server.registerTool(t.name,{description:t.description,inputSchema:e},r),g.info("tool loaded",{tool:t.name});}}subscribeTools(){this.toolCleanup=()=>{C.off(b.REGISTERED,this.handleTool);},C.on(b.REGISTERED,this.handleTool);}convertToZodSchema(t){if(t instanceof d.ZodType)return t;if(typeof t=="object"&&t!==null){let e=t;if(e.type==="object"){let r=e.properties||{},n=e.required||[],o={};for(let[i,m]of Object.entries(r))o[i]=this.jsonSchemaToZod(m);if(Object.keys(r).filter(i=>!n.includes(i)).length===0)return d.object(o);let f={};for(let[i,m]of Object.entries(r)){let c=o[i];c&&(n.includes(i)?f[i]=c:f[i]=c.optional());}return d.object(f)}if(e.type==="string")return d.string();if(e.type==="number")return d.number();if(e.type==="boolean")return d.boolean();if(e.type==="array"){let r=e.items;return r?d.array(this.jsonSchemaToZod(r)):d.array(d.unknown())}}return d.unknown()}jsonSchemaToZod(t){switch(t.type){case "string":return d.string();case "number":return d.number();case "integer":return d.number().int();case "boolean":return d.boolean();case "null":return d.null();case "array":{let r=t.items;return r?d.array(this.jsonSchemaToZod(r)):d.array(d.unknown())}case "object":{let r=t.properties||{},n=t.required||[],o={};for(let[i,m]of Object.entries(r))o[i]=this.jsonSchemaToZod(m);if(Object.keys(r).filter(i=>!n.includes(i)).length===0)return d.object(o);let f={};for(let[i,m]of Object.entries(r)){let c=o[i];c&&(n.includes(i)?f[i]=c:f[i]=c.optional());}return d.object(f)}default:return d.unknown()}}adaptToolHandler(t){return async(e,r)=>{let n={signal:r.signal,sessionId:r.sessionId,requestId:r.requestId,taskId:r.taskId,_meta:r._meta,authInfo:r.authInfo?{type:"bearer",claims:{clientId:r.authInfo.clientId,scopes:r.authInfo.scopes}}:void 0};return t(e,n)}}unsubscribeTools(){this.toolCleanup&&(this.toolCleanup(),this.toolCleanup=null);}async start(){if(this.isStarting){g.warn("already starting",{id:this.info.id});return}if(this.info.status===l.RUNNING){g.debug("already running",{id:this.info.id});return}this.isStarting=true,this.info.status=l.STARTING;try{g.info("starting",{id:this.info.id,name:this.info.name,url:this.config.url});let t;try{t=new URL(this.config.url);}catch{throw new Error(`\u65E0\u6548\u7684 URL: ${this.config.url}`)}let e=parseInt(t.port||"3000",10),r=t.hostname||"0.0.0.0";if(Number.isNaN(e)||e<1||e>65535)throw new Error(`\u65E0\u6548\u7684\u7AEF\u53E3\u53F7: ${t.port}`);this.transport=new StreamableHTTPServerTransport({sessionIdGenerator:()=>crypto.randomUUID()}),this.server=new McpServer({name:this.config.name,version:this.config.version}),this.loadTools(),this.subscribeTools(),await this.server.connect(this.transport),this.httpServer=K.createServer((n,o)=>{let h=n.headers.origin,f=(m,c,A)=>{o.writeHead(m,c),A?o.end(A):o.end();},i={"Access-Control-Allow-Origin":h||"*","Access-Control-Allow-Methods":"GET, POST, OPTIONS","Access-Control-Allow-Headers":"Content-Type, mcpsessionid, mcp-protocol-version","Access-Control-Max-Age":"86400"};if(n.method==="OPTIONS"){f(204,i);return}if(n.method==="POST"){let m="";n.setEncoding("utf8"),n.on("data",c=>{m+=c;}),n.on("end",()=>{let c;if(m)try{c=JSON.parse(m);}catch{c=void 0;}this.transport&&this.transport.handleRequest(n,o,c);}),n.on("error",c=>{g.error("HTTP request error",{error:c.message}),f(500,i,JSON.stringify({jsonrpc:"2.0",error:{code:-32603,message:"Internal error"}}));});}else n.method==="GET"?this.transport&&this.transport.handleRequest(n,o,void 0):f(405,i,JSON.stringify({jsonrpc:"2.0",error:{code:-32601,message:"Method not found"}}));}),this.httpServer.on("error",n=>{g.error("HTTP server error",{error:n.message}),this.info.status=l.ERROR;}),this.httpServer.on("clientError",(n,o)=>{g.warn("HTTP client error",{error:n.message}),o.writable&&o.end(`HTTP/1.1 400 Bad Request\r
1
+ import {McpServer}from'@modelcontextprotocol/sdk/server/mcp.js';import {StdioServerTransport}from'@modelcontextprotocol/sdk/server/stdio.js';import {Log,withTimeout}from'@easbot/utils';import {EventEmitter}from'events';import d from'zod';import V from'http';import {StreamableHTTPServerTransport}from'@modelcontextprotocol/sdk/server/streamableHttp.js';import u from'zod/v4';import {Client}from'@modelcontextprotocol/sdk/client/index.js';import {StdioClientTransport}from'@modelcontextprotocol/sdk/client/stdio.js';import {StreamableHTTPClientTransport}from'@modelcontextprotocol/sdk/client/streamableHttp.js';import {SSEClientTransport}from'@modelcontextprotocol/sdk/client/sse.js';import {CallToolResultSchema,ToolListChangedNotificationSchema}from'@modelcontextprotocol/sdk/types.js';import {UnauthorizedError}from'@modelcontextprotocol/sdk/client/auth.js';var j=Object.defineProperty;var _=(s,t,e)=>t in s?j(s,t,{enumerable:true,configurable:true,writable:true,value:e}):s[t]=e;var a=(s,t,e)=>_(s,typeof t!="symbol"?t+"":t,e);var P={STDIO:"stdio",HTTP:"http"},l={STOPPED:"stopped",STARTING:"starting",RUNNING:"running",STOPPING:"stopping",ERROR:"error"};var L=Log.create({service:"tool-registry"}),C={REGISTERED:"mcp.tool.registered",UNREGISTERED:"mcp.tool.unregistered",CLEARED:"mcp.tool.cleared"},k=class extends EventEmitter{constructor(){super();a(this,"tools",new Map);}register(e){if(!e.name||typeof e.name!="string")throw new Error("\u5DE5\u5177\u540D\u79F0\u65E0\u6548\uFF1A\u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32");if(this.tools.has(e.name)){L?.debug("tool already registered, skipping",{tool:e.name});return}this.tools.set(e.name,e),this.emit(C.REGISTERED,e);}unregister(e){return this.tools.has(e)?(this.tools.delete(e),this.emit(C.UNREGISTERED,e),true):false}get(e){return this.tools.get(e)}getAll(){return Array.from(this.tools.values())}getTools(){return Array.from(this.tools.values()).map(({handler:e,...r})=>r)}has(e){return this.tools.has(e)}getSize(){return this.tools.size}clear(){this.tools.clear(),this.emit(C.CLEARED);}};var y=Log.create({service:"mcp.server.stdio"}),R=class{constructor(t,e){a(this,"config");a(this,"server",null);a(this,"info");a(this,"isStarting",false);a(this,"isStopping",false);a(this,"toolEventCleanup",null);a(this,"tools");a(this,"handleToolRegistered",t=>{if(this.server){let e=this.convertToZodSchema(t.inputSchema),r=this.adaptToolHandler(t.handler);this.server.registerTool(t.name,{description:t.description,inputSchema:e},r),y.info("tool registered",{tool:t.name});}});this.config=t,this.tools=e,this.info={id:t.id,name:t.name,version:t.version,transportType:t.transportType,status:l.STOPPED,createdAt:Date.now(),lastActivityAt:Date.now(),enabled:t.enabled};}getInfo(){return this.info}getConfig(){return this.config}createInstance(){let t=this;return {info:this.info,get server(){return t.server},start:()=>this.start(),stop:()=>this.stop(),restart:()=>this.restart()}}loadTools(){if(this.server)for(let t of this.tools.getAll()){let e=this.convertToZodSchema(t.inputSchema),r=this.adaptToolHandler(t.handler);this.server.registerTool(t.name,{description:t.description,inputSchema:e},r),y.info("tool loaded",{tool:t.name});}}subscribeToolEvents(){this.toolEventCleanup=()=>{this.tools.off(C.REGISTERED,this.handleToolRegistered);},this.tools.on(C.REGISTERED,this.handleToolRegistered);}adaptToolHandler(t){return async(e,r)=>{let o={signal:r.signal,sessionId:r.sessionId,requestId:r.requestId,taskId:r.taskId,_meta:r._meta,authInfo:r.authInfo?{type:"bearer",claims:{clientId:r.authInfo.clientId,scopes:r.authInfo.scopes}}:void 0};return t(e,o)}}unsubscribeToolEvents(){this.toolEventCleanup&&(this.toolEventCleanup(),this.toolEventCleanup=null);}convertToZodSchema(t){if(t instanceof d.ZodType)return t;if(typeof t=="object"&&t!==null){let e=t;if(e.type==="object"){let r=e.properties||{},o=e.required||[],n={};for(let[i,m]of Object.entries(r))n[i]=this.jsonSchemaToZod(m);if(Object.keys(r).filter(i=>!o.includes(i)).length===0)return d.object(n);let f={};for(let[i,m]of Object.entries(r)){let c=n[i];c&&(o.includes(i)?f[i]=c:f[i]=c.optional());}return d.object(f)}if(e.type==="string")return d.string();if(e.type==="number")return d.number();if(e.type==="boolean")return d.boolean();if(e.type==="array"){let r=e.items;return r?d.array(this.jsonSchemaToZod(r)):d.array(d.unknown())}}return d.unknown()}jsonSchemaToZod(t){switch(t.type){case "string":return d.string();case "number":return d.number();case "integer":return d.number().int();case "boolean":return d.boolean();case "null":return d.null();case "array":{let r=t.items;return r?d.array(this.jsonSchemaToZod(r)):d.array(d.unknown())}case "object":{let r=t.properties||{},o=t.required||[],n={};for(let[i,m]of Object.entries(r))n[i]=this.jsonSchemaToZod(m);if(Object.keys(r).filter(i=>!o.includes(i)).length===0)return d.object(n);let f={};for(let[i,m]of Object.entries(r)){let c=n[i];c&&(o.includes(i)?f[i]=c:f[i]=c.optional());}return d.object(f)}default:return d.unknown()}}async start(){if(this.isStarting){y.warn("server is already starting",{id:this.info.id});return}if(this.info.status===l.RUNNING){y.debug("server is already running",{id:this.info.id});return}this.isStarting=true,this.info.status=l.STARTING;try{y.info("starting stdio mcp server",{id:this.info.id,name:this.info.name,command:this.config.command,args:this.config.args});let t=new StdioServerTransport;this.server=new McpServer({name:this.config.name,version:this.config.version}),this.loadTools(),this.subscribeToolEvents(),await this.server.connect(t),this.info.status=l.RUNNING,this.info.lastActivityAt=Date.now(),y.info("stdio mcp server started successfully",{id:this.info.id,name:this.info.name,toolCount:this.tools.getSize()});}catch(t){throw this.info.status=l.ERROR,y.error("failed to start stdio mcp server",{id:this.info.id,error:t instanceof Error?t.message:String(t)}),t}finally{this.isStarting=false;}}async stop(){if(this.isStopping){y.warn("server is already stopping",{id:this.info.id});return}if(this.info.status!==l.RUNNING&&this.info.status!==l.ERROR){y.debug("server is not running",{id:this.info.id,status:this.info.status});return}this.isStopping=true,this.info.status=l.STOPPING;try{y.info("stopping stdio mcp server",{id:this.info.id,name:this.info.name}),this.unsubscribeToolEvents(),this.server&&(await this.server.close(),this.server=null),this.info.status=l.STOPPED,y.info("stdio mcp server stopped",{id:this.info.id,name:this.info.name});}catch(t){this.info.status=l.ERROR,y.error("error stopping stdio mcp server",{id:this.info.id,error:t instanceof Error?t.message:String(t)});}finally{this.isStopping=false;}}async restart(){y.info("restarting stdio mcp server",{id:this.info.id}),await this.stop(),await this.start();}};function q(s,t){return new R(s,t)}async function G(s,t){let e=new R(s,t);return await e.start(),e.createInstance()}var g=Log.create({service:"mcp.server.http"}),b=class{constructor(t,e){a(this,"config");a(this,"server",null);a(this,"transport",null);a(this,"httpServer",null);a(this,"info");a(this,"isStarting",false);a(this,"isStopping",false);a(this,"toolCleanup",null);a(this,"tools");a(this,"handleTool",t=>{if(this.server){let e=this.convertToZodSchema(t.inputSchema),r=this.adaptToolHandler(t.handler);this.server.registerTool(t.name,{description:t.description,inputSchema:e},r),g.info("tool registered",{tool:t.name});}});this.config=t,this.tools=e,this.info={id:t.id,name:t.name,version:t.version,transportType:t.transportType,status:l.STOPPED,createdAt:Date.now(),lastActivityAt:Date.now()};}getInfo(){return this.info}getConfig(){return this.config}createInstance(){let t=this;return {info:this.info,get server(){return t.server},start:()=>this.start(),stop:()=>this.stop(),restart:()=>this.restart()}}loadTools(){if(this.server)for(let t of this.tools.getAll()){let e=this.convertToZodSchema(t.inputSchema),r=this.adaptToolHandler(t.handler);this.server.registerTool(t.name,{description:t.description,inputSchema:e},r),g.info("tool loaded",{tool:t.name});}}subscribeTools(){this.toolCleanup=()=>{this.tools.off(C.REGISTERED,this.handleTool);},this.tools.on(C.REGISTERED,this.handleTool);}convertToZodSchema(t){if(t instanceof u.ZodType)return t;if(typeof t=="object"&&t!==null){let e=t;if(e.type==="object"){let r=e.properties||{},o=e.required||[],n={};for(let[i,m]of Object.entries(r))n[i]=this.jsonSchemaToZod(m);if(Object.keys(r).filter(i=>!o.includes(i)).length===0)return u.object(n);let f={};for(let[i,m]of Object.entries(r)){let c=n[i];c&&(o.includes(i)?f[i]=c:f[i]=c.optional());}return u.object(f)}if(e.type==="string")return u.string();if(e.type==="number")return u.number();if(e.type==="boolean")return u.boolean();if(e.type==="array"){let r=e.items;return r?u.array(this.jsonSchemaToZod(r)):u.array(u.unknown())}}return u.unknown()}jsonSchemaToZod(t){switch(t.type){case "string":return u.string();case "number":return u.number();case "integer":return u.number().int();case "boolean":return u.boolean();case "null":return u.null();case "array":{let r=t.items;return r?u.array(this.jsonSchemaToZod(r)):u.array(u.unknown())}case "object":{let r=t.properties||{},o=t.required||[],n={};for(let[i,m]of Object.entries(r))n[i]=this.jsonSchemaToZod(m);if(Object.keys(r).filter(i=>!o.includes(i)).length===0)return u.object(n);let f={};for(let[i,m]of Object.entries(r)){let c=n[i];c&&(o.includes(i)?f[i]=c:f[i]=c.optional());}return u.object(f)}default:return u.unknown()}}adaptToolHandler(t){return async(e,r)=>{let o={signal:r.signal,sessionId:r.sessionId,requestId:r.requestId,taskId:r.taskId,_meta:r._meta,authInfo:r.authInfo?{type:"bearer",claims:{clientId:r.authInfo.clientId,scopes:r.authInfo.scopes}}:void 0};return t(e,o)}}unsubscribeTools(){this.toolCleanup&&(this.toolCleanup(),this.toolCleanup=null);}async start(){if(this.isStarting){g.warn("already starting",{id:this.info.id});return}if(this.info.status===l.RUNNING){g.debug("already running",{id:this.info.id});return}this.isStarting=true,this.info.status=l.STARTING;try{g.info("starting",{id:this.info.id,name:this.info.name,url:this.config.url});let t;try{t=new URL(this.config.url);}catch{throw new Error(`\u65E0\u6548\u7684 URL: ${this.config.url}`)}let e=parseInt(t.port||"3000",10),r=t.hostname||"0.0.0.0";if(Number.isNaN(e)||e<1||e>65535)throw new Error(`\u65E0\u6548\u7684\u7AEF\u53E3\u53F7: ${t.port}`);this.transport=new StreamableHTTPServerTransport({sessionIdGenerator:()=>crypto.randomUUID()}),this.server=new McpServer({name:this.config.name,version:this.config.version}),this.loadTools(),this.subscribeTools(),await this.server.connect(this.transport),this.httpServer=V.createServer((o,n)=>{let h=o.headers.origin,f=(m,c,M)=>{n.writeHead(m,c),M?n.end(M):n.end();},i={"Access-Control-Allow-Origin":h||"*","Access-Control-Allow-Methods":"GET, POST, OPTIONS","Access-Control-Allow-Headers":"Content-Type, mcpsessionid, mcp-protocol-version","Access-Control-Max-Age":"86400"};if(o.method==="OPTIONS"){f(204,i);return}if(o.method==="POST"){let m="";o.setEncoding("utf8"),o.on("data",c=>{m+=c;}),o.on("end",()=>{let c;if(m)try{c=JSON.parse(m);}catch{c=void 0;}this.transport&&this.transport.handleRequest(o,n,c);}),o.on("error",c=>{g.error("HTTP request error",{error:c.message}),f(500,i,JSON.stringify({jsonrpc:"2.0",error:{code:-32603,message:"Internal error"}}));});}else o.method==="GET"?this.transport&&this.transport.handleRequest(o,n,void 0):f(405,i,JSON.stringify({jsonrpc:"2.0",error:{code:-32601,message:"Method not found"}}));}),this.httpServer.on("error",o=>{g.error("HTTP server error",{error:o.message}),this.info.status=l.ERROR;}),this.httpServer.on("clientError",(o,n)=>{g.warn("HTTP client error",{error:o.message}),n.writable&&n.end(`HTTP/1.1 400 Bad Request\r
2
2
  \r
3
- `);}),this.httpServer.listen(e,r),g.info("HTTP server listening",{hostname:r,port:e}),this.info.status=l.RUNNING,this.info.lastActivityAt=Date.now(),g.info("started",{id:this.info.id,name:this.info.name,toolCount:C.getSize()});}catch(t){throw this.info.status=l.ERROR,g.error("failed to start",{id:this.info.id,error:t instanceof Error?t.message:String(t)}),t}finally{this.isStarting=false;}}async stop(){if(this.isStopping){g.warn("already stopping",{id:this.info.id});return}if(this.info.status!==l.RUNNING&&this.info.status!==l.ERROR){g.debug("not running",{id:this.info.id,status:this.info.status});return}this.isStopping=true,this.info.status=l.STOPPING;try{g.info("stopping",{id:this.info.id,name:this.info.name}),this.unsubscribeTools(),this.httpServer&&(await new Promise(t=>{this.httpServer.close(()=>t());}),this.httpServer=null),this.transport&&(await this.transport.close(),this.transport=null),this.server&&(await this.server.close(),this.server=null),this.info.status=l.STOPPED,g.info("stopped",{id:this.info.id});}catch(t){this.info.status=l.ERROR,g.error("failed to stop",{id:this.info.id,error:t instanceof Error?t.message:String(t)});}finally{this.isStopping=false;}}async restart(){g.info("restarting",{id:this.info.id}),await this.stop(),await this.start();}};function J(s){return new w(s)}async function W(s){let t=new w(s);return await t.start(),t.createInstance()}var H=Log.create({service:"mcp.server.factory"});function X(s){return s.type==="local"}function Y(s){return s.type==="remote"}function tt(s,t){if(!t.command)throw new Error(`MCP server ${s} has no command specified`);return {id:s,name:s,version:"1.0.0",transportType:I.STDIO,enabled:t.enabled,timeout:t.timeout,command:t.command,args:(t?.args?.length??0)>0?t.args:void 0,env:t.env}}function et(s,t){return {id:s,name:s,version:"1.0.0",transportType:I.HTTP,enabled:t.enabled,timeout:t.timeout,url:t.url,headers:t.headers}}async function rt(s,t){if(H.debug("creating server from config",{name:s,type:t.type}),X(t)){let e=tt(s,t),r=new R(e);return await r.start(),r.createInstance()}if(Y(t)){let e=et(s,t),r=new w(e);return await r.start(),r.createInstance()}throw new Error(`Unsupported MCP config type: ${t.type}`)}async function Gt(s){let t={};for(let[e,r]of Object.entries(s))try{let n=await rt(e,r);t[e]=n;}catch(n){H.error("failed to create server from config",{name:e,error:n instanceof Error?n.message:String(n)});}return t}var O={version:"0.3.2"};function E(){return O.version}var p=Log.create({service:"mcp.client"}),x=6e4,M=class{constructor(){a(this,"id","");a(this,"client",null);a(this,"_info");a(this,"notificationCleanup",null);a(this,"pendingTransport",null);a(this,"oauthState","");a(this,"capturedRedirectUrl",null);a(this,"oauthConfig");a(this,"toolListChangedHandler",null);a(this,"oauthProvider",null);this._info=this.createInitialInfo();}get info(){return {...this._info}}get status(){return this._info.status}get tools(){return [...this._info.tools]}get resources(){return [...this._info.resources]}get prompts(){return [...this._info.prompts]}get clientId(){return this.id}setToolListChangedHandler(t){this.toolListChangedHandler=t;}setOAuthProvider(t){this.oauthProvider=t;}async connect(t,e){p.info("connecting to MCP server",{id:t,type:e.type}),await this.disconnect(),this.id=t,this._info=this.createInitialInfo(),this._info.id=t,this._info.name=t;try{if(e.type==="remote"&&(this.oauthConfig=e.oauth===!1?void 0:e.oauth??{}),this.client=await this.createClient(e),!this.client)return;this._info.status={status:"connected"},this._info.connectedAt=Date.now(),await this.fetchCapabilities(),p.info("connected to MCP server",{id:t,toolCount:this._info.tools.length});}catch(r){let n=r instanceof Error?r.message:String(r);this._info.status={status:"failed",error:n},this._info.error=n,p.error("failed to connect to MCP server",{id:t,error:n});}}async startAuth(){if(!this.pendingTransport)throw new Error("No pending OAuth flow. Please connect to a server that requires authentication first.");return this.oauthState=Array.from(crypto.getRandomValues(new Uint8Array(32))).map(t=>t.toString(16).padStart(2,"0")).join(""),this.oauthProvider&&await this.oauthProvider.saveState(this.oauthState),{authorizationUrl:this.capturedRedirectUrl?.toString()??"",state:this.oauthState}}async finishAuth(t){if(!this.pendingTransport)throw new Error("No pending OAuth flow.");try{await this.pendingTransport.finishAuth?.(t);}catch(e){throw p.error("failed to finish OAuth",{id:this.id,error:e}),e}this.pendingTransport=null,this.capturedRedirectUrl=null,p.info("OAuth completed, ready to reconnect",{id:this.id});}isAuthRequired(){return this._info.status.status==="needs_auth"}isClientRegistrationRequired(){return this._info.status.status==="needs_client_registration"}getPendingTransport(){return this.pendingTransport}getAuthorizationUrl(){return this.capturedRedirectUrl?.toString()??""}async disconnect(){this.notificationCleanup&&(this.notificationCleanup(),this.notificationCleanup=null),this.client&&(await this.client.close().catch(t=>{p.error("Failed to close MCP client",{id:this.id,error:t});}),this.client=null),this.pendingTransport=null,this.capturedRedirectUrl=null,this.toolListChangedHandler=null,this._info.status.status==="connected"&&(this._info.status={status:"disconnected"}),p.info("disconnected from MCP server",{id:this.id});}async listTools(){if(!this.client||this._info.status.status!=="connected")return {};let t={};for(let e of this._info.tools)t[e.name]={name:e.name,description:e.description,inputSchema:e.inputSchema};return t}async callTool(t,e,r){let n=Date.now();if(!this.client||this._info.status.status!=="connected")return {toolName:t,args:e,content:[],success:false,error:"Client not connected",duration:Date.now()-n};try{let o=await withTimeout(this.client.callTool({name:t,arguments:e},CallToolResultSchema,{resetTimeoutOnProgress:!0,timeout:r}),r??x);return {toolName:t,args:e,content:o.content.map(h=>h.type==="text"?{type:"text",text:h.text}:h.type==="image"?{type:"image",data:h.data,mimeType:h.mimeType}:h.type==="resource"?{type:"resource",resource:h.resource}:{type:"text",text:String(h)}),success:!0,duration:Date.now()-n,metadata:o.metadata}}catch(o){return {toolName:t,args:e,content:[],success:false,error:o instanceof Error?o.message:String(o),duration:Date.now()-n}}}async listResources(){return [...this._info.resources]}async readResource(t){if(!this.client||this._info.status.status!=="connected"){p.warn("client not connected for readResource",{id:this.id});return}return this.client.readResource({uri:t}).catch(e=>{p.error("failed to read resource",{id:this.id,uri:t,error:e.message});})}async listPrompts(){return [...this._info.prompts]}async getPrompt(t,e){if(!this.client||this._info.status.status!=="connected"){p.warn("client not connected for getPrompt",{id:this.id});return}return this.client.getPrompt({name:t,arguments:e}).catch(r=>{p.error("failed to get prompt",{id:this.id,name:t,error:r.message});})}async refreshTools(){if(!(!this.client||this._info.status.status!=="connected"))try{let t=await withTimeout(this.client.listTools(),x);this._info.tools=t.tools.map(e=>({name:e.name,description:e.description,inputSchema:e.inputSchema})),p.info("tools refreshed",{id:this.id,count:this._info.tools.length});}catch(t){p.error("failed to refresh tools",{id:this.id,error:t instanceof Error?t.message:String(t)});}}async createClient(t){return t.type==="local"?await this.createLocalClient(t):t.type==="remote"?await this.createRemoteClient(t):null}async fetchCapabilities(){if(this.client){try{let t=await withTimeout(this.client.listTools(),x);this._info.tools=t.tools.map(e=>({name:e.name,description:e.description,inputSchema:e.inputSchema}));}catch(t){p.debug("failed to get tools",{id:this.id,error:t instanceof Error?t.message:String(t)});}try{let t=await withTimeout(this.client.listResources(),5e3);this._info.resources=t.resources.map(e=>({name:e.name,uri:e.uri,description:e.description,mimeType:e.mimeType}));}catch{p.debug("failed to get resources",{id:this.id});}try{let t=await withTimeout(this.client.listPrompts(),5e3);this._info.prompts=t.prompts.map(e=>({name:e.name,description:e.description,arguments:e.arguments?.map(r=>({name:r.name,description:r.description,required:r.required}))}));}catch{p.debug("failed to get prompts",{id:this.id});}}}async createLocalClient(t){let[e,...r]=t.command;if(!e)return this._info.status={status:"failed",error:"Command is empty"},null;let n=new StdioClientTransport({stderr:"pipe",command:e,args:r.length>0?r:void 0,cwd:t.cwd??process.cwd(),env:Object.fromEntries(Object.entries({...process.env,...t.environment}).filter(([,o])=>o!==void 0))});n.stderr?.on("data",o=>{p.info(`mcp stderr: ${o.toString()}`,{id:this.id});});try{let o=new Client({name:"easbot-mcp",version:E()});return await withTimeout(o.connect(n),t.timeout??x),this.registerNotificationHandler(o),o}catch(o){let h=o instanceof Error?o.message:String(o);return this._info.status={status:"failed",error:h},p.error("local mcp startup failed",{id:this.id,command:[e,...r],error:h}),null}}async createRemoteClient(t){let e={headers:t.headers};if((t.transport||"streamable-http")==="sse"){p.info("using SSE transport",{id:this.id,url:t.url});let n=new SSEClientTransport(new URL(t.url),{requestInit:e});return await this.connectWithTransport(n,t)}else {p.info("using StreamableHTTP transport",{id:this.id,url:t.url});let n=new StreamableHTTPClientTransport(new URL(t.url),{requestInit:e});return await this.connectWithTransport(n,t)}}async connectWithTransport(t,e){try{let r=new Client({name:"easbot-mcp",version:E()});return await r.connect(t),this.registerNotificationHandler(r),r}catch(r){return this.handleConnectionError(r,t,e),null}}handleConnectionError(t,e,r){if(t instanceof UnauthorizedError){let n=t instanceof Error?t.message:String(t);n.includes("registration")||n.includes("client_id")?this._info.status={status:"needs_client_registration",error:"Server does not support dynamic client registration. Please provide clientId in config."}:(this._info.status={status:"needs_auth"},this.pendingTransport=e,this.capturedRedirectUrl=this.extractRedirectUrl(t));}else {let n=t instanceof Error?t.message:String(t);this._info.status={status:"failed",error:n};}p.error("remote mcp connection failed",{id:this.id,url:r.url,status:this._info.status});}extractRedirectUrl(t){let r=t.message.match(/https?:\/\/[^\s]+/);if(r)try{return new URL(r[0])}catch{return null}return null}registerNotificationHandler(t){let e=async()=>{p.info("tools list changed notification received",{id:this.id}),await this.refreshTools(),this.toolListChangedHandler&&await this.toolListChangedHandler();};t.setNotificationHandler(ToolListChangedNotificationSchema,e),this.notificationCleanup=()=>{t.removeNotificationHandler("notifications/tools/list_changed");};}createInitialInfo(){return {id:"",name:"",status:{status:"disconnected"},tools:[],resources:[],prompts:[]}}};function dt(){return new M}var me=v.discriminatedUnion("status",[v.object({status:v.literal("connected")}).meta({ref:"MCPStatusConnected"}),v.object({status:v.literal("disconnected")}).meta({ref:"MCPStatusDisconnected"}),v.object({status:v.literal("failed"),error:v.string()}).meta({ref:"MCPStatusFailed"}),v.object({status:v.literal("needs_auth")}).meta({ref:"MCPStatusNeedsAuth"}),v.object({status:v.literal("needs_client_registration"),error:v.string()}).meta({ref:"MCPStatusNeedsClientRegistration"}),v.object({status:v.literal("disabled")}).meta({ref:"MCPStatusDisabled"})]);var be="@easbot/mcp";export{w as HttpServerAdapter,M as MCPClient,be as NAME,l as ServerStatus,I as ServerTransportType,me as Status,R as StdioServerAdapter,k as ToolRegistry,b as ToolRegistryEvent,W as createAndStartHttpServer,V as createAndStartStdioServer,J as createHttpServer,dt as createMCPClient,rt as createServerFromConfig,Gt as createServersFromConfigMap,G as createStdioServer,E as getVersion,C as tools};
3
+ `);}),this.httpServer.listen(e,r),g.info("HTTP server listening",{hostname:r,port:e}),this.info.status=l.RUNNING,this.info.lastActivityAt=Date.now(),g.info("started",{id:this.info.id,name:this.info.name,toolCount:this.tools.getSize()});}catch(t){throw this.info.status=l.ERROR,g.error("failed to start",{id:this.info.id,error:t instanceof Error?t.message:String(t)}),t}finally{this.isStarting=false;}}async stop(){if(this.isStopping){g.warn("already stopping",{id:this.info.id});return}if(this.info.status!==l.RUNNING&&this.info.status!==l.ERROR){g.debug("not running",{id:this.info.id,status:this.info.status});return}this.isStopping=true,this.info.status=l.STOPPING;try{g.info("stopping",{id:this.info.id,name:this.info.name}),this.unsubscribeTools(),this.httpServer&&(await new Promise(t=>{this.httpServer.close(()=>t());}),this.httpServer=null),this.transport&&(await this.transport.close(),this.transport=null),this.server&&(await this.server.close(),this.server=null),this.info.status=l.STOPPED,g.info("stopped",{id:this.info.id});}catch(t){this.info.status=l.ERROR,g.error("failed to stop",{id:this.info.id,error:t instanceof Error?t.message:String(t)});}finally{this.isStopping=false;}}async restart(){g.info("restarting",{id:this.info.id}),await this.stop(),await this.start();}};function $(s,t){return new b(s,t)}async function J(s,t){let e=new b(s,t);return await e.start(),e.createInstance()}var A=Log.create({service:"mcp.server.factory"});function Q(s){return s.type==="local"}function X(s){return s.type==="remote"}function Y(s,t){if(!t.command)throw new Error(`MCP server ${s} has no command specified`);return {id:s,name:s,version:"1.0.0",transportType:P.STDIO,enabled:t.enabled,timeout:t.timeout,command:t.command,args:(t?.args?.length??0)>0?t.args:void 0,env:t.env}}function tt(s,t){return {id:s,name:s,version:"1.0.0",transportType:P.HTTP,enabled:t.enabled,timeout:t.timeout,url:t.url,headers:t.headers}}async function et(s,t,e){if(A.debug("creating server from config",{name:s,type:t.type}),Q(t)){let r=Y(s,t),o=new R(r,e);return await o.start(),o.createInstance()}if(X(t)){let r=tt(s,t),o=new b(r,e);return await o.start(),o.createInstance()}throw new Error(`Unsupported MCP config type: ${t.type}`)}async function Ft(s,t){let e={};for(let[r,o]of Object.entries(s))try{let n=await et(r,o,t);e[r]=n;}catch(n){A.error("failed to create server from config",{name:r,error:n instanceof Error?n.message:String(n)});}return e}var H={version:"0.3.3"};function I(){return H.version}var p=Log.create({service:"mcp.client"}),E=6e4,x=class{constructor(){a(this,"id","");a(this,"client",null);a(this,"_info");a(this,"notificationCleanup",null);a(this,"pendingTransport",null);a(this,"oauthState","");a(this,"capturedRedirectUrl",null);a(this,"oauthConfig");a(this,"toolListChangedHandler",null);a(this,"oauthProvider",null);this._info=this.createInitialInfo();}get info(){return {...this._info}}get status(){return this._info.status}get tools(){return [...this._info.tools]}get resources(){return [...this._info.resources]}get prompts(){return [...this._info.prompts]}get clientId(){return this.id}setToolListChangedHandler(t){this.toolListChangedHandler=t;}setOAuthProvider(t){this.oauthProvider=t;}async connect(t,e){p.info("connecting to MCP server",{id:t,type:e.type}),await this.disconnect(),this.id=t,this._info=this.createInitialInfo(),this._info.id=t,this._info.name=t;try{if(e.type==="remote"&&(this.oauthConfig=e.oauth===!1?void 0:e.oauth??{}),this.client=await this.createClient(e),!this.client)return;this._info.status={status:"connected"},this._info.connectedAt=Date.now(),await this.fetchCapabilities(),p.info("connected to MCP server",{id:t,toolCount:this._info.tools.length});}catch(r){let o=r instanceof Error?r.message:String(r);this._info.status={status:"failed",error:o},this._info.error=o,p.error("failed to connect to MCP server",{id:t,error:o});}}async startAuth(){if(!this.pendingTransport)throw new Error("No pending OAuth flow. Please connect to a server that requires authentication first.");return this.oauthState=Array.from(crypto.getRandomValues(new Uint8Array(32))).map(t=>t.toString(16).padStart(2,"0")).join(""),this.oauthProvider&&await this.oauthProvider.saveState(this.oauthState),{authorizationUrl:this.capturedRedirectUrl?.toString()??"",state:this.oauthState}}async finishAuth(t){if(!this.pendingTransport)throw new Error("No pending OAuth flow.");try{await this.pendingTransport.finishAuth?.(t);}catch(e){throw p.error("failed to finish OAuth",{id:this.id,error:e}),e}this.pendingTransport=null,this.capturedRedirectUrl=null,p.info("OAuth completed, ready to reconnect",{id:this.id});}isAuthRequired(){return this._info.status.status==="needs_auth"}isClientRegistrationRequired(){return this._info.status.status==="needs_client_registration"}getPendingTransport(){return this.pendingTransport}getAuthorizationUrl(){return this.capturedRedirectUrl?.toString()??""}async disconnect(){this.notificationCleanup&&(this.notificationCleanup(),this.notificationCleanup=null),this.client&&(await this.client.close().catch(t=>{p.error("Failed to close MCP client",{id:this.id,error:t});}),this.client=null),this.pendingTransport=null,this.capturedRedirectUrl=null,this.toolListChangedHandler=null,this._info.status.status==="connected"&&(this._info.status={status:"disconnected"}),p.info("disconnected from MCP server",{id:this.id});}async listTools(){if(!this.client||this._info.status.status!=="connected")return {};let t={};for(let e of this._info.tools)t[e.name]={name:e.name,description:e.description,inputSchema:e.inputSchema};return t}async callTool(t,e,r){let o=Date.now();if(!this.client||this._info.status.status!=="connected")return {toolName:t,args:e,content:[],success:false,error:"Client not connected",duration:Date.now()-o};try{let n=await withTimeout(this.client.callTool({name:t,arguments:e},CallToolResultSchema,{resetTimeoutOnProgress:!0,timeout:r}),r??E);return {toolName:t,args:e,content:n.content.map(h=>h.type==="text"?{type:"text",text:h.text}:h.type==="image"?{type:"image",data:h.data,mimeType:h.mimeType}:h.type==="resource"?{type:"resource",resource:h.resource}:{type:"text",text:String(h)}),success:!0,duration:Date.now()-o,metadata:n.metadata}}catch(n){return {toolName:t,args:e,content:[],success:false,error:n instanceof Error?n.message:String(n),duration:Date.now()-o}}}async listResources(){return [...this._info.resources]}async readResource(t){if(!this.client||this._info.status.status!=="connected"){p.warn("client not connected for readResource",{id:this.id});return}return this.client.readResource({uri:t}).catch(e=>{p.error("failed to read resource",{id:this.id,uri:t,error:e.message});})}async listPrompts(){return [...this._info.prompts]}async getPrompt(t,e){if(!this.client||this._info.status.status!=="connected"){p.warn("client not connected for getPrompt",{id:this.id});return}return this.client.getPrompt({name:t,arguments:e}).catch(r=>{p.error("failed to get prompt",{id:this.id,name:t,error:r.message});})}async refreshTools(){if(!(!this.client||this._info.status.status!=="connected"))try{let t=await withTimeout(this.client.listTools(),E);this._info.tools=t.tools.map(e=>({name:e.name,description:e.description,inputSchema:e.inputSchema})),p.info("tools refreshed",{id:this.id,count:this._info.tools.length});}catch(t){p.error("failed to refresh tools",{id:this.id,error:t instanceof Error?t.message:String(t)});}}async createClient(t){return t.type==="local"?await this.createLocalClient(t):t.type==="remote"?await this.createRemoteClient(t):null}async fetchCapabilities(){if(this.client){try{let t=await withTimeout(this.client.listTools(),E);this._info.tools=t.tools.map(e=>({name:e.name,description:e.description,inputSchema:e.inputSchema}));}catch(t){p.debug("failed to get tools",{id:this.id,error:t instanceof Error?t.message:String(t)});}try{let t=await withTimeout(this.client.listResources(),5e3);this._info.resources=t.resources.map(e=>({name:e.name,uri:e.uri,description:e.description,mimeType:e.mimeType}));}catch{p.debug("failed to get resources",{id:this.id});}try{let t=await withTimeout(this.client.listPrompts(),5e3);this._info.prompts=t.prompts.map(e=>({name:e.name,description:e.description,arguments:e.arguments?.map(r=>({name:r.name,description:r.description,required:r.required}))}));}catch{p.debug("failed to get prompts",{id:this.id});}}}async createLocalClient(t){let[e,...r]=t.command;if(!e)return this._info.status={status:"failed",error:"Command is empty"},null;let o=new StdioClientTransport({stderr:"pipe",command:e,args:r.length>0?r:void 0,cwd:t.cwd??process.cwd(),env:Object.fromEntries(Object.entries({...process.env,...t.environment}).filter(([,n])=>n!==void 0))});o.stderr?.on("data",n=>{p.info(`mcp stderr: ${n.toString()}`,{id:this.id});});try{let n=new Client({name:"easbot-mcp",version:I()});return await withTimeout(n.connect(o),t.timeout??E),this.registerNotificationHandler(n),n}catch(n){let h=n instanceof Error?n.message:String(n);return this._info.status={status:"failed",error:h},p.error("local mcp startup failed",{id:this.id,command:[e,...r],error:h}),null}}async createRemoteClient(t){let e={headers:t.headers};if((t.transport||"streamable-http")==="sse"){p.info("using SSE transport",{id:this.id,url:t.url});let o=new SSEClientTransport(new URL(t.url),{requestInit:e});return await this.connectWithTransport(o,t)}else {p.info("using StreamableHTTP transport",{id:this.id,url:t.url});let o=new StreamableHTTPClientTransport(new URL(t.url),{requestInit:e});return await this.connectWithTransport(o,t)}}async connectWithTransport(t,e){try{let r=new Client({name:"easbot-mcp",version:I()});return await r.connect(t),this.registerNotificationHandler(r),r}catch(r){return this.handleConnectionError(r,t,e),null}}handleConnectionError(t,e,r){if(t instanceof UnauthorizedError){let o=t instanceof Error?t.message:String(t);o.includes("registration")||o.includes("client_id")?this._info.status={status:"needs_client_registration",error:"Server does not support dynamic client registration. Please provide clientId in config."}:(this._info.status={status:"needs_auth"},this.pendingTransport=e,this.capturedRedirectUrl=this.extractRedirectUrl(t));}else {let o=t instanceof Error?t.message:String(t);this._info.status={status:"failed",error:o};}p.error("remote mcp connection failed",{id:this.id,url:r.url,status:this._info.status});}extractRedirectUrl(t){let r=t.message.match(/https?:\/\/[^\s]+/);if(r)try{return new URL(r[0])}catch{return null}return null}registerNotificationHandler(t){let e=async()=>{p.info("tools list changed notification received",{id:this.id}),await this.refreshTools(),this.toolListChangedHandler&&await this.toolListChangedHandler();};t.setNotificationHandler(ToolListChangedNotificationSchema,e),this.notificationCleanup=()=>{t.removeNotificationHandler("notifications/tools/list_changed");};}createInitialInfo(){return {id:"",name:"",status:{status:"disconnected"},tools:[],resources:[],prompts:[]}}};function pt(){return new x}var ye=d.discriminatedUnion("status",[d.object({status:d.literal("connected")}).meta({ref:"MCPStatusConnected"}),d.object({status:d.literal("disconnected")}).meta({ref:"MCPStatusDisconnected"}),d.object({status:d.literal("failed"),error:d.string()}).meta({ref:"MCPStatusFailed"}),d.object({status:d.literal("needs_auth")}).meta({ref:"MCPStatusNeedsAuth"}),d.object({status:d.literal("needs_client_registration"),error:d.string()}).meta({ref:"MCPStatusNeedsClientRegistration"}),d.object({status:d.literal("disabled")}).meta({ref:"MCPStatusDisabled"})]);var Pe="@easbot/mcp";export{b as HttpServerAdapter,x as MCPClient,Pe as NAME,l as ServerStatus,P as ServerTransportType,ye as Status,R as StdioServerAdapter,k as ToolRegistry,C as ToolRegistryEvent,J as createAndStartHttpServer,G as createAndStartStdioServer,$ as createHttpServer,pt as createMCPClient,et as createServerFromConfig,Ft as createServersFromConfigMap,q as createStdioServer,I as getVersion};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@easbot/mcp",
3
- "version": "0.3.2",
3
+ "version": "0.3.3",
4
4
  "description": "MCP (Model Context Protocol) integration library for EASBOT ecosystem",
5
5
  "type": "module",
6
6
  "main": "dist/index.cjs",
@@ -44,8 +44,8 @@
44
44
  "ai": "^6.0.176",
45
45
  "zod": "^4.4.3",
46
46
  "@modelcontextprotocol/sdk": "^1.29.0",
47
- "@easbot/types": "0.3.2",
48
- "@easbot/utils": "0.3.2"
47
+ "@easbot/types": "0.3.3",
48
+ "@easbot/utils": "0.3.3"
49
49
  },
50
50
  "devDependencies": {
51
51
  "@biomejs/biome": "^2.4.14",