@easbot/mcp 0.2.21 → 0.2.22
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/dist/index.cjs +1 -1
- package/dist/index.mjs +1 -1
- package/package.json +3 -3
package/dist/index.cjs
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
'use strict';var chunk4DCCWUIJ_cjs=require('./chunks/chunk-4DCCWUIJ.cjs'),mcp=require('@modelcontextprotocol/sdk/server/mcp'),stdio=require('@modelcontextprotocol/sdk/server/stdio'),events=require('events'),l=require('zod/v4'),z=require('http'),streamableHttp=require('@modelcontextprotocol/sdk/server/streamableHttp'),index_js=require('@modelcontextprotocol/sdk/client/index.js'),stdio_js=require('@modelcontextprotocol/sdk/client/stdio.js'),streamableHttp_js=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'),utils=require('@easbot/utils'),v=require('zod');function _interopDefault(e){return e&&e.__esModule?e:{default:e}}var l__default=/*#__PURE__*/_interopDefault(l);var z__default=/*#__PURE__*/_interopDefault(z);var v__default=/*#__PURE__*/_interopDefault(v);var H={STDIO:"stdio",HTTP:"http"},c={STOPPED:"stopped",STARTING:"starting",RUNNING:"running",STOPPING:"stopping",ERROR:"error"};var j=chunk4DCCWUIJ_cjs.b.create({service:"tool-registry"}),T={REGISTERED:"mcp.tool.registered",UNREGISTERED:"mcp.tool.unregistered",CLEARED:"mcp.tool.cleared"},x=class extends events.EventEmitter{constructor(){super();chunk4DCCWUIJ_cjs.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)){j?.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);}},y=new x;var g=chunk4DCCWUIJ_cjs.b.create({service:"mcp.server.stdio"}),w=class{constructor(t){chunk4DCCWUIJ_cjs.a(this,"config");chunk4DCCWUIJ_cjs.a(this,"server",null);chunk4DCCWUIJ_cjs.a(this,"info");chunk4DCCWUIJ_cjs.a(this,"isStarting",false);chunk4DCCWUIJ_cjs.a(this,"isStopping",false);chunk4DCCWUIJ_cjs.a(this,"toolEventCleanup",null);chunk4DCCWUIJ_cjs.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),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:c.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 y.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});}}subscribeToolEvents(){this.toolEventCleanup=()=>{y.off(T.REGISTERED,this.handleToolRegistered);},y.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 l__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,f]of Object.entries(r))n[i]=this.jsonSchemaToZod(f);if(Object.keys(r).filter(i=>!o.includes(i)).length===0)return l__default.default.object(n);let u={};for(let[i,f]of Object.entries(r)){let a=n[i];a&&(o.includes(i)?u[i]=a:u[i]=a.optional());}return l__default.default.object(u)}if(e.type==="string")return l__default.default.string();if(e.type==="number")return l__default.default.number();if(e.type==="boolean")return l__default.default.boolean();if(e.type==="array"){let r=e.items;return r?l__default.default.array(this.jsonSchemaToZod(r)):l__default.default.array(l__default.default.unknown())}}return l__default.default.unknown()}jsonSchemaToZod(t){switch(t.type){case "string":return l__default.default.string();case "number":return l__default.default.number();case "integer":return l__default.default.number().int();case "boolean":return l__default.default.boolean();case "null":return l__default.default.null();case "array":{let r=t.items;return r?l__default.default.array(this.jsonSchemaToZod(r)):l__default.default.array(l__default.default.unknown())}case "object":{let r=t.properties||{},o=t.required||[],n={};for(let[i,f]of Object.entries(r))n[i]=this.jsonSchemaToZod(f);if(Object.keys(r).filter(i=>!o.includes(i)).length===0)return l__default.default.object(n);let u={};for(let[i,f]of Object.entries(r)){let a=n[i];a&&(o.includes(i)?u[i]=a:u[i]=a.optional());}return l__default.default.object(u)}default:return l__default.default.unknown()}}async start(){if(this.isStarting){g.warn("server is already starting",{id:this.info.id});return}if(this.info.status===c.RUNNING){g.debug("server is already running",{id:this.info.id});return}this.isStarting=true,this.info.status=c.STARTING;try{g.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.StdioServerTransport;await t.start(),this.server=new mcp.McpServer({name:this.config.name,version:this.config.version}),this.loadTools(),this.subscribeToolEvents(),await this.server.connect(t),this.info.status=c.RUNNING,this.info.lastActivityAt=Date.now(),g.info("stdio mcp server started successfully",{id:this.info.id,name:this.info.name,toolCount:y.getSize()});}catch(t){throw this.info.status=c.ERROR,g.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){g.warn("server is already stopping",{id:this.info.id});return}if(this.info.status!==c.RUNNING&&this.info.status!==c.ERROR){g.debug("server is not running",{id:this.info.id,status:this.info.status});return}this.isStopping=true,this.info.status=c.STOPPING;try{g.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=c.STOPPED,g.info("stdio mcp server stopped",{id:this.info.id,name:this.info.name});}catch(t){this.info.status=c.ERROR,g.error("error stopping stdio mcp server",{id:this.info.id,error:t instanceof Error?t.message:String(t)});}finally{this.isStopping=false;}}async restart(){g.info("restarting stdio mcp server",{id:this.info.id}),await this.stop(),await this.start();}};function L(m){return new w(m)}async function Z(m){let t=new w(m);return await t.start(),t.createInstance()}var h=chunk4DCCWUIJ_cjs.b.create({service:"mcp.server.http"}),E=class{constructor(t){chunk4DCCWUIJ_cjs.a(this,"config");chunk4DCCWUIJ_cjs.a(this,"server",null);chunk4DCCWUIJ_cjs.a(this,"transport",null);chunk4DCCWUIJ_cjs.a(this,"httpServer",null);chunk4DCCWUIJ_cjs.a(this,"info");chunk4DCCWUIJ_cjs.a(this,"isStarting",false);chunk4DCCWUIJ_cjs.a(this,"isStopping",false);chunk4DCCWUIJ_cjs.a(this,"toolCleanup",null);chunk4DCCWUIJ_cjs.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),h.info("tool registered",{tool:t.name});}});this.config=t,this.info={id:t.id,name:t.name,version:t.version,transportType:t.transportType,status:c.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 y.getAll()){let e=this.convertToZodSchema(t.inputSchema),r=this.adaptToolHandler(t.handler);this.server.registerTool(t.name,{description:t.description,inputSchema:e},r),h.info("tool loaded",{tool:t.name});}}subscribeTools(){this.toolCleanup=()=>{y.off(T.REGISTERED,this.handleTool);},y.on(T.REGISTERED,this.handleTool);}convertToZodSchema(t){if(t instanceof l__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,f]of Object.entries(r))n[i]=this.jsonSchemaToZod(f);if(Object.keys(r).filter(i=>!o.includes(i)).length===0)return l__default.default.object(n);let u={};for(let[i,f]of Object.entries(r)){let a=n[i];a&&(o.includes(i)?u[i]=a:u[i]=a.optional());}return l__default.default.object(u)}if(e.type==="string")return l__default.default.string();if(e.type==="number")return l__default.default.number();if(e.type==="boolean")return l__default.default.boolean();if(e.type==="array"){let r=e.items;return r?l__default.default.array(this.jsonSchemaToZod(r)):l__default.default.array(l__default.default.unknown())}}return l__default.default.unknown()}jsonSchemaToZod(t){switch(t.type){case "string":return l__default.default.string();case "number":return l__default.default.number();case "integer":return l__default.default.number().int();case "boolean":return l__default.default.boolean();case "null":return l__default.default.null();case "array":{let r=t.items;return r?l__default.default.array(this.jsonSchemaToZod(r)):l__default.default.array(l__default.default.unknown())}case "object":{let r=t.properties||{},o=t.required||[],n={};for(let[i,f]of Object.entries(r))n[i]=this.jsonSchemaToZod(f);if(Object.keys(r).filter(i=>!o.includes(i)).length===0)return l__default.default.object(n);let u={};for(let[i,f]of Object.entries(r)){let a=n[i];a&&(o.includes(i)?u[i]=a:u[i]=a.optional());}return l__default.default.object(u)}default:return l__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){h.warn("already starting",{id:this.info.id});return}if(this.info.status===c.RUNNING){h.debug("already running",{id:this.info.id});return}this.isStarting=true,this.info.status=c.STARTING;try{h.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.StreamableHTTPServerTransport({sessionIdGenerator:()=>crypto.randomUUID()}),this.server=new mcp.McpServer({name:this.config.name,version:this.config.version}),this.loadTools(),this.subscribeTools(),await this.server.connect(this.transport),this.httpServer=z__default.default.createServer((o,n)=>{let b=o.headers.origin,u=(f,a,A)=>{n.writeHead(f,a),A?n.end(A):n.end();},i={"Access-Control-Allow-Origin":b||"*","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"){u(204,i);return}if(o.method==="POST"){let f="";o.setEncoding("utf8"),o.on("data",a=>{f+=a;}),o.on("end",()=>{let a;if(f)try{a=JSON.parse(f);}catch{a=void 0;}this.transport&&this.transport.handleRequest(o,n,a);}),o.on("error",a=>{h.error("HTTP request error",{error:a.message}),u(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):u(405,i,JSON.stringify({jsonrpc:"2.0",error:{code:-32601,message:"Method not found"}}));}),this.httpServer.on("error",o=>{h.error("HTTP server error",{error:o.message}),this.info.status=c.ERROR;}),this.httpServer.on("clientError",(o,n)=>{h.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),h.info("HTTP server listening",{hostname:r,port:e}),this.info.status=c.RUNNING,this.info.lastActivityAt=Date.now(),h.info("started",{id:this.info.id,name:this.info.name,toolCount:y.getSize()});}catch(t){throw this.info.status=c.ERROR,h.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){h.warn("already stopping",{id:this.info.id});return}if(this.info.status!==c.RUNNING&&this.info.status!==c.ERROR){h.debug("not running",{id:this.info.id,status:this.info.status});return}this.isStopping=true,this.info.status=c.STOPPING;try{h.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=c.STOPPED,h.info("stopped",{id:this.info.id});}catch(t){this.info.status=c.ERROR,h.error("failed to stop",{id:this.info.id,error:t instanceof Error?t.message:String(t)});}finally{this.isStopping=false;}}async restart(){h.info("restarting",{id:this.info.id}),await this.stop(),await this.start();}};function U(m){return new E(m)}async function V(m){let t=new E(m);return await t.start(),t.createInstance()}var M={version:"0.2.21"};function P(){return M.version}var d=chunk4DCCWUIJ_cjs.b.create({service:"mcp.client"}),I=3e4,Vt=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"})]),k=class{constructor(){chunk4DCCWUIJ_cjs.a(this,"id","");chunk4DCCWUIJ_cjs.a(this,"client",null);chunk4DCCWUIJ_cjs.a(this,"_info");chunk4DCCWUIJ_cjs.a(this,"notificationCleanup",null);chunk4DCCWUIJ_cjs.a(this,"pendingTransport",null);chunk4DCCWUIJ_cjs.a(this,"oauthState","");chunk4DCCWUIJ_cjs.a(this,"toolListChangedHandler",null);this._info=this.createInitialInfo();}get info(){return {...this._info}}get status(){return this._info.status}get tools(){return [...this._info.tools]}async connect(t,e){d.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(this.client=await this.createClient(e),!this.client)return;this._info.status={status:"connected"},this._info.connectedAt=Date.now(),await this.fetchCapabilities(),d.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,d.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(""),{authorizationUrl:"",state:this.oauthState}}async finishAuth(t){if(!this.pendingTransport)throw new Error("No pending OAuth flow.");t&&await this.pendingTransport.finishAuth?.(t),this.pendingTransport=null,d.info("OAuth completed, reconnecting...",{id:this.id});}async disconnect(){this.notificationCleanup&&(this.notificationCleanup(),this.notificationCleanup=null),this.client&&(await this.client.close().catch(t=>{d.error("Failed to close MCP client",{error:t});}),this.client=null),this.pendingTransport=null,this.toolListChangedHandler=null,this._info.status.status==="connected"&&(this._info.status={status:"disconnected"}),d.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,result:null,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,result:n.content,success:!0,duration:Date.now()-o}}catch(n){return {toolName:t,args:e,result:null,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"){d.warn("client not connected for readResource");return}return this.client.readResource({uri:t}).catch(e=>{d.error("failed to read resource",{uri:t,error:e.message});})}async listPrompts(){return [...this._info.prompts]}async getPrompt(t,e){if(!this.client||this._info.status.status!=="connected"){d.warn("client not connected for getPrompt");return}return this.client.getPrompt({name:t,arguments:e}).catch(r=>{d.error("failed to get prompt",{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})),d.info("tools refreshed",{id:this.id,count:this._info.tools.length});}catch(t){d.error("failed to refresh tools",{id:this.id,error:t instanceof Error?t.message:String(t)});}}async createClient(t){return t.type==="local"?this.createLocalClient(t):t.type==="remote"?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){d.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{d.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{d.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.StdioClientTransport({stderr:"pipe",command:e,args:r.length>0?r:void 0,cwd:process.cwd(),env:Object.fromEntries(Object.entries({...process.env,...t.environment}).filter(([,n])=>n!==void 0))});o.stderr?.on("data",n=>{d.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 b=n instanceof Error?n.message:String(n);return this._info.status={status:"failed",error:b},d.error("local mcp startup failed",{command:[e,...r],error:b}),null}}async createRemoteClient(t){t.oauth===false;let r={headers:t.headers};if((t.transport||"streamable-http")==="sse"){d.info("using SSE transport",{url:t.url});let n=new sse_js.SSEClientTransport(new URL(t.url),{requestInit:r});return this.connectWithTransport(n,t)}else {d.info("using StreamableHTTP transport",{url:t.url});let n=new streamableHttp_js.StreamableHTTPClientTransport(new URL(t.url),{requestInit:r});return this.connectWithTransport(n,t)}}async connectWithTransport(t,e){try{let r=new index_js.Client({name:"easbot-mcp",version:P()});return await utils.withTimeout(r.connect(t),e.timeout??I),this.registerNotificationHandler(r),r}catch(r){if(r instanceof auth_js.UnauthorizedError){let o=r instanceof Error?r.message:String(r);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=t);}else {let o=r instanceof Error?r.message:String(r);this._info.status={status:"failed",error:o};}return d.error("remote mcp connection failed",{url:e.url,error:this._info.status}),null}}registerToolListChangedHandler(t){this.toolListChangedHandler=t;}registerNotificationHandler(t){let e=async()=>{d.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 Bt(){return new k}var Wt="0.2.20",$t="@easbot/mcp";async function Qt(m){let{Log:t}=await import('./chunks/log-WXSBDTHF.cjs'),e=false;await t.init({logDir:m.logDir??process.env.EASBOT_LOG_PATH??process.cwd(),print:m.print??false,dev:m.dev??e,level:m.level??("INFO")});}Object.defineProperty(exports,"Log",{enumerable:true,get:function(){return chunk4DCCWUIJ_cjs.b}});exports.HttpServerAdapter=E;exports.MCPClient=k;exports.NAME=$t;exports.ServerStatus=c;exports.ServerTransportType=H;exports.Status=Vt;exports.StdioServerAdapter=w;exports.VERSION=Wt;exports.createAndStartHttpServer=V;exports.createAndStartStdioServer=Z;exports.createHttpServer=U;exports.createMCPClient=Bt;exports.createStdioServer=L;exports.getVersion=P;exports.initLog=Qt;
|
|
3
|
+
`);}),this.httpServer.listen(e,r),h.info("HTTP server listening",{hostname:r,port:e}),this.info.status=c.RUNNING,this.info.lastActivityAt=Date.now(),h.info("started",{id:this.info.id,name:this.info.name,toolCount:y.getSize()});}catch(t){throw this.info.status=c.ERROR,h.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){h.warn("already stopping",{id:this.info.id});return}if(this.info.status!==c.RUNNING&&this.info.status!==c.ERROR){h.debug("not running",{id:this.info.id,status:this.info.status});return}this.isStopping=true,this.info.status=c.STOPPING;try{h.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=c.STOPPED,h.info("stopped",{id:this.info.id});}catch(t){this.info.status=c.ERROR,h.error("failed to stop",{id:this.info.id,error:t instanceof Error?t.message:String(t)});}finally{this.isStopping=false;}}async restart(){h.info("restarting",{id:this.info.id}),await this.stop(),await this.start();}};function U(m){return new E(m)}async function V(m){let t=new E(m);return await t.start(),t.createInstance()}var M={version:"0.2.22"};function P(){return M.version}var d=chunk4DCCWUIJ_cjs.b.create({service:"mcp.client"}),I=3e4,Vt=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"})]),k=class{constructor(){chunk4DCCWUIJ_cjs.a(this,"id","");chunk4DCCWUIJ_cjs.a(this,"client",null);chunk4DCCWUIJ_cjs.a(this,"_info");chunk4DCCWUIJ_cjs.a(this,"notificationCleanup",null);chunk4DCCWUIJ_cjs.a(this,"pendingTransport",null);chunk4DCCWUIJ_cjs.a(this,"oauthState","");chunk4DCCWUIJ_cjs.a(this,"toolListChangedHandler",null);this._info=this.createInitialInfo();}get info(){return {...this._info}}get status(){return this._info.status}get tools(){return [...this._info.tools]}async connect(t,e){d.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(this.client=await this.createClient(e),!this.client)return;this._info.status={status:"connected"},this._info.connectedAt=Date.now(),await this.fetchCapabilities(),d.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,d.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(""),{authorizationUrl:"",state:this.oauthState}}async finishAuth(t){if(!this.pendingTransport)throw new Error("No pending OAuth flow.");t&&await this.pendingTransport.finishAuth?.(t),this.pendingTransport=null,d.info("OAuth completed, reconnecting...",{id:this.id});}async disconnect(){this.notificationCleanup&&(this.notificationCleanup(),this.notificationCleanup=null),this.client&&(await this.client.close().catch(t=>{d.error("Failed to close MCP client",{error:t});}),this.client=null),this.pendingTransport=null,this.toolListChangedHandler=null,this._info.status.status==="connected"&&(this._info.status={status:"disconnected"}),d.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,result:null,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,result:n.content,success:!0,duration:Date.now()-o}}catch(n){return {toolName:t,args:e,result:null,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"){d.warn("client not connected for readResource");return}return this.client.readResource({uri:t}).catch(e=>{d.error("failed to read resource",{uri:t,error:e.message});})}async listPrompts(){return [...this._info.prompts]}async getPrompt(t,e){if(!this.client||this._info.status.status!=="connected"){d.warn("client not connected for getPrompt");return}return this.client.getPrompt({name:t,arguments:e}).catch(r=>{d.error("failed to get prompt",{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})),d.info("tools refreshed",{id:this.id,count:this._info.tools.length});}catch(t){d.error("failed to refresh tools",{id:this.id,error:t instanceof Error?t.message:String(t)});}}async createClient(t){return t.type==="local"?this.createLocalClient(t):t.type==="remote"?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){d.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{d.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{d.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.StdioClientTransport({stderr:"pipe",command:e,args:r.length>0?r:void 0,cwd:process.cwd(),env:Object.fromEntries(Object.entries({...process.env,...t.environment}).filter(([,n])=>n!==void 0))});o.stderr?.on("data",n=>{d.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 b=n instanceof Error?n.message:String(n);return this._info.status={status:"failed",error:b},d.error("local mcp startup failed",{command:[e,...r],error:b}),null}}async createRemoteClient(t){t.oauth===false;let r={headers:t.headers};if((t.transport||"streamable-http")==="sse"){d.info("using SSE transport",{url:t.url});let n=new sse_js.SSEClientTransport(new URL(t.url),{requestInit:r});return this.connectWithTransport(n,t)}else {d.info("using StreamableHTTP transport",{url:t.url});let n=new streamableHttp_js.StreamableHTTPClientTransport(new URL(t.url),{requestInit:r});return this.connectWithTransport(n,t)}}async connectWithTransport(t,e){try{let r=new index_js.Client({name:"easbot-mcp",version:P()});return await utils.withTimeout(r.connect(t),e.timeout??I),this.registerNotificationHandler(r),r}catch(r){if(r instanceof auth_js.UnauthorizedError){let o=r instanceof Error?r.message:String(r);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=t);}else {let o=r instanceof Error?r.message:String(r);this._info.status={status:"failed",error:o};}return d.error("remote mcp connection failed",{url:e.url,error:this._info.status}),null}}registerToolListChangedHandler(t){this.toolListChangedHandler=t;}registerNotificationHandler(t){let e=async()=>{d.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 Bt(){return new k}var Wt="0.2.20",$t="@easbot/mcp";async function Qt(m){let{Log:t}=await import('./chunks/log-WXSBDTHF.cjs'),e=false;await t.init({logDir:m.logDir??process.env.EASBOT_LOG_PATH??process.cwd(),print:m.print??false,dev:m.dev??e,level:m.level??("INFO")});}Object.defineProperty(exports,"Log",{enumerable:true,get:function(){return chunk4DCCWUIJ_cjs.b}});exports.HttpServerAdapter=E;exports.MCPClient=k;exports.NAME=$t;exports.ServerStatus=c;exports.ServerTransportType=H;exports.Status=Vt;exports.StdioServerAdapter=w;exports.VERSION=Wt;exports.createAndStartHttpServer=V;exports.createAndStartStdioServer=Z;exports.createHttpServer=U;exports.createMCPClient=Bt;exports.createStdioServer=L;exports.getVersion=P;exports.initLog=Qt;
|
package/dist/index.mjs
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import {b,a}from'./chunks/chunk-PJM3ON7Z.mjs';export{b as Log}from'./chunks/chunk-PJM3ON7Z.mjs';import {McpServer}from'@modelcontextprotocol/sdk/server/mcp';import {StdioServerTransport}from'@modelcontextprotocol/sdk/server/stdio';import {EventEmitter}from'events';import l from'zod/v4';import q from'http';import {StreamableHTTPServerTransport}from'@modelcontextprotocol/sdk/server/streamableHttp';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 {withTimeout}from'@easbot/utils';import v from'zod';var N={STDIO:"stdio",HTTP:"http"},c={STOPPED:"stopped",STARTING:"starting",RUNNING:"running",STOPPING:"stopping",ERROR:"error"};var _=b.create({service:"tool-registry"}),T={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)){_?.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);}},y=new k;var g=b.create({service:"mcp.server.stdio"}),E=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),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:c.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 y.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});}}subscribeToolEvents(){this.toolEventCleanup=()=>{y.off(T.REGISTERED,this.handleToolRegistered);},y.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 l.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,f]of Object.entries(r))n[i]=this.jsonSchemaToZod(f);if(Object.keys(r).filter(i=>!o.includes(i)).length===0)return l.object(n);let u={};for(let[i,f]of Object.entries(r)){let a=n[i];a&&(o.includes(i)?u[i]=a:u[i]=a.optional());}return l.object(u)}if(e.type==="string")return l.string();if(e.type==="number")return l.number();if(e.type==="boolean")return l.boolean();if(e.type==="array"){let r=e.items;return r?l.array(this.jsonSchemaToZod(r)):l.array(l.unknown())}}return l.unknown()}jsonSchemaToZod(t){switch(t.type){case "string":return l.string();case "number":return l.number();case "integer":return l.number().int();case "boolean":return l.boolean();case "null":return l.null();case "array":{let r=t.items;return r?l.array(this.jsonSchemaToZod(r)):l.array(l.unknown())}case "object":{let r=t.properties||{},o=t.required||[],n={};for(let[i,f]of Object.entries(r))n[i]=this.jsonSchemaToZod(f);if(Object.keys(r).filter(i=>!o.includes(i)).length===0)return l.object(n);let u={};for(let[i,f]of Object.entries(r)){let a=n[i];a&&(o.includes(i)?u[i]=a:u[i]=a.optional());}return l.object(u)}default:return l.unknown()}}async start(){if(this.isStarting){g.warn("server is already starting",{id:this.info.id});return}if(this.info.status===c.RUNNING){g.debug("server is already running",{id:this.info.id});return}this.isStarting=true,this.info.status=c.STARTING;try{g.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;await t.start(),this.server=new McpServer({name:this.config.name,version:this.config.version}),this.loadTools(),this.subscribeToolEvents(),await this.server.connect(t),this.info.status=c.RUNNING,this.info.lastActivityAt=Date.now(),g.info("stdio mcp server started successfully",{id:this.info.id,name:this.info.name,toolCount:y.getSize()});}catch(t){throw this.info.status=c.ERROR,g.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){g.warn("server is already stopping",{id:this.info.id});return}if(this.info.status!==c.RUNNING&&this.info.status!==c.ERROR){g.debug("server is not running",{id:this.info.id,status:this.info.status});return}this.isStopping=true,this.info.status=c.STOPPING;try{g.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=c.STOPPED,g.info("stdio mcp server stopped",{id:this.info.id,name:this.info.name});}catch(t){this.info.status=c.ERROR,g.error("error stopping stdio mcp server",{id:this.info.id,error:t instanceof Error?t.message:String(t)});}finally{this.isStopping=false;}}async restart(){g.info("restarting stdio mcp server",{id:this.info.id}),await this.stop(),await this.start();}};function Z(m){return new E(m)}async function z(m){let t=new E(m);return await t.start(),t.createInstance()}var h=b.create({service:"mcp.server.http"}),I=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),h.info("tool registered",{tool:t.name});}});this.config=t,this.info={id:t.id,name:t.name,version:t.version,transportType:t.transportType,status:c.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 y.getAll()){let e=this.convertToZodSchema(t.inputSchema),r=this.adaptToolHandler(t.handler);this.server.registerTool(t.name,{description:t.description,inputSchema:e},r),h.info("tool loaded",{tool:t.name});}}subscribeTools(){this.toolCleanup=()=>{y.off(T.REGISTERED,this.handleTool);},y.on(T.REGISTERED,this.handleTool);}convertToZodSchema(t){if(t instanceof l.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,f]of Object.entries(r))n[i]=this.jsonSchemaToZod(f);if(Object.keys(r).filter(i=>!o.includes(i)).length===0)return l.object(n);let u={};for(let[i,f]of Object.entries(r)){let a=n[i];a&&(o.includes(i)?u[i]=a:u[i]=a.optional());}return l.object(u)}if(e.type==="string")return l.string();if(e.type==="number")return l.number();if(e.type==="boolean")return l.boolean();if(e.type==="array"){let r=e.items;return r?l.array(this.jsonSchemaToZod(r)):l.array(l.unknown())}}return l.unknown()}jsonSchemaToZod(t){switch(t.type){case "string":return l.string();case "number":return l.number();case "integer":return l.number().int();case "boolean":return l.boolean();case "null":return l.null();case "array":{let r=t.items;return r?l.array(this.jsonSchemaToZod(r)):l.array(l.unknown())}case "object":{let r=t.properties||{},o=t.required||[],n={};for(let[i,f]of Object.entries(r))n[i]=this.jsonSchemaToZod(f);if(Object.keys(r).filter(i=>!o.includes(i)).length===0)return l.object(n);let u={};for(let[i,f]of Object.entries(r)){let a=n[i];a&&(o.includes(i)?u[i]=a:u[i]=a.optional());}return l.object(u)}default:return l.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){h.warn("already starting",{id:this.info.id});return}if(this.info.status===c.RUNNING){h.debug("already running",{id:this.info.id});return}this.isStarting=true,this.info.status=c.STARTING;try{h.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=q.createServer((o,n)=>{let b=o.headers.origin,u=(f,a,M)=>{n.writeHead(f,a),M?n.end(M):n.end();},i={"Access-Control-Allow-Origin":b||"*","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"){u(204,i);return}if(o.method==="POST"){let f="";o.setEncoding("utf8"),o.on("data",a=>{f+=a;}),o.on("end",()=>{let a;if(f)try{a=JSON.parse(f);}catch{a=void 0;}this.transport&&this.transport.handleRequest(o,n,a);}),o.on("error",a=>{h.error("HTTP request error",{error:a.message}),u(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):u(405,i,JSON.stringify({jsonrpc:"2.0",error:{code:-32601,message:"Method not found"}}));}),this.httpServer.on("error",o=>{h.error("HTTP server error",{error:o.message}),this.info.status=c.ERROR;}),this.httpServer.on("clientError",(o,n)=>{h.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),h.info("HTTP server listening",{hostname:r,port:e}),this.info.status=c.RUNNING,this.info.lastActivityAt=Date.now(),h.info("started",{id:this.info.id,name:this.info.name,toolCount:y.getSize()});}catch(t){throw this.info.status=c.ERROR,h.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){h.warn("already stopping",{id:this.info.id});return}if(this.info.status!==c.RUNNING&&this.info.status!==c.ERROR){h.debug("not running",{id:this.info.id,status:this.info.status});return}this.isStopping=true,this.info.status=c.STOPPING;try{h.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=c.STOPPED,h.info("stopped",{id:this.info.id});}catch(t){this.info.status=c.ERROR,h.error("failed to stop",{id:this.info.id,error:t instanceof Error?t.message:String(t)});}finally{this.isStopping=false;}}async restart(){h.info("restarting",{id:this.info.id}),await this.stop(),await this.start();}};function V(m){return new I(m)}async function B(m){let t=new I(m);return await t.start(),t.createInstance()}var O={version:"0.2.
|
|
3
|
+
`);}),this.httpServer.listen(e,r),h.info("HTTP server listening",{hostname:r,port:e}),this.info.status=c.RUNNING,this.info.lastActivityAt=Date.now(),h.info("started",{id:this.info.id,name:this.info.name,toolCount:y.getSize()});}catch(t){throw this.info.status=c.ERROR,h.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){h.warn("already stopping",{id:this.info.id});return}if(this.info.status!==c.RUNNING&&this.info.status!==c.ERROR){h.debug("not running",{id:this.info.id,status:this.info.status});return}this.isStopping=true,this.info.status=c.STOPPING;try{h.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=c.STOPPED,h.info("stopped",{id:this.info.id});}catch(t){this.info.status=c.ERROR,h.error("failed to stop",{id:this.info.id,error:t instanceof Error?t.message:String(t)});}finally{this.isStopping=false;}}async restart(){h.info("restarting",{id:this.info.id}),await this.stop(),await this.start();}};function V(m){return new I(m)}async function B(m){let t=new I(m);return await t.start(),t.createInstance()}var O={version:"0.2.22"};function x(){return O.version}var d=b.create({service:"mcp.client"}),P=3e4,Bt=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"})]),A=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,"toolListChangedHandler",null);this._info=this.createInitialInfo();}get info(){return {...this._info}}get status(){return this._info.status}get tools(){return [...this._info.tools]}async connect(t,e){d.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(this.client=await this.createClient(e),!this.client)return;this._info.status={status:"connected"},this._info.connectedAt=Date.now(),await this.fetchCapabilities(),d.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,d.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(""),{authorizationUrl:"",state:this.oauthState}}async finishAuth(t){if(!this.pendingTransport)throw new Error("No pending OAuth flow.");t&&await this.pendingTransport.finishAuth?.(t),this.pendingTransport=null,d.info("OAuth completed, reconnecting...",{id:this.id});}async disconnect(){this.notificationCleanup&&(this.notificationCleanup(),this.notificationCleanup=null),this.client&&(await this.client.close().catch(t=>{d.error("Failed to close MCP client",{error:t});}),this.client=null),this.pendingTransport=null,this.toolListChangedHandler=null,this._info.status.status==="connected"&&(this._info.status={status:"disconnected"}),d.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,result:null,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??P);return {toolName:t,args:e,result:n.content,success:!0,duration:Date.now()-o}}catch(n){return {toolName:t,args:e,result:null,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"){d.warn("client not connected for readResource");return}return this.client.readResource({uri:t}).catch(e=>{d.error("failed to read resource",{uri:t,error:e.message});})}async listPrompts(){return [...this._info.prompts]}async getPrompt(t,e){if(!this.client||this._info.status.status!=="connected"){d.warn("client not connected for getPrompt");return}return this.client.getPrompt({name:t,arguments:e}).catch(r=>{d.error("failed to get prompt",{name:t,error:r.message});})}async refreshTools(){if(!(!this.client||this._info.status.status!=="connected"))try{let t=await withTimeout(this.client.listTools(),P);this._info.tools=t.tools.map(e=>({name:e.name,description:e.description,inputSchema:e.inputSchema})),d.info("tools refreshed",{id:this.id,count:this._info.tools.length});}catch(t){d.error("failed to refresh tools",{id:this.id,error:t instanceof Error?t.message:String(t)});}}async createClient(t){return t.type==="local"?this.createLocalClient(t):t.type==="remote"?this.createRemoteClient(t):null}async fetchCapabilities(){if(this.client){try{let t=await withTimeout(this.client.listTools(),P);this._info.tools=t.tools.map(e=>({name:e.name,description:e.description,inputSchema:e.inputSchema}));}catch(t){d.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{d.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{d.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:process.cwd(),env:Object.fromEntries(Object.entries({...process.env,...t.environment}).filter(([,n])=>n!==void 0))});o.stderr?.on("data",n=>{d.info(`mcp stderr: ${n.toString()}`,{id:this.id});});try{let n=new Client({name:"easbot-mcp",version:x()});return await withTimeout(n.connect(o),t.timeout??P),this.registerNotificationHandler(n),n}catch(n){let b=n instanceof Error?n.message:String(n);return this._info.status={status:"failed",error:b},d.error("local mcp startup failed",{command:[e,...r],error:b}),null}}async createRemoteClient(t){t.oauth===false;let r={headers:t.headers};if((t.transport||"streamable-http")==="sse"){d.info("using SSE transport",{url:t.url});let n=new SSEClientTransport(new URL(t.url),{requestInit:r});return this.connectWithTransport(n,t)}else {d.info("using StreamableHTTP transport",{url:t.url});let n=new StreamableHTTPClientTransport(new URL(t.url),{requestInit:r});return this.connectWithTransport(n,t)}}async connectWithTransport(t,e){try{let r=new Client({name:"easbot-mcp",version:x()});return await withTimeout(r.connect(t),e.timeout??P),this.registerNotificationHandler(r),r}catch(r){if(r instanceof UnauthorizedError){let o=r instanceof Error?r.message:String(r);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=t);}else {let o=r instanceof Error?r.message:String(r);this._info.status={status:"failed",error:o};}return d.error("remote mcp connection failed",{url:e.url,error:this._info.status}),null}}registerToolListChangedHandler(t){this.toolListChangedHandler=t;}registerNotificationHandler(t){let e=async()=>{d.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 Kt(){return new A}var $t="0.2.20",Qt="@easbot/mcp";async function Xt(m){let{Log:t}=await import('./chunks/log-2GAL6FAC.mjs'),e=false;await t.init({logDir:m.logDir??process.env.EASBOT_LOG_PATH??process.cwd(),print:m.print??false,dev:m.dev??e,level:m.level??("INFO")});}export{I as HttpServerAdapter,A as MCPClient,Qt as NAME,c as ServerStatus,N as ServerTransportType,Bt as Status,E as StdioServerAdapter,$t as VERSION,B as createAndStartHttpServer,z as createAndStartStdioServer,V as createHttpServer,Kt as createMCPClient,Z as createStdioServer,x as getVersion,Xt as initLog};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@easbot/mcp",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.22",
|
|
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.2.
|
|
48
|
-
"@easbot/utils": "0.2.
|
|
47
|
+
"@easbot/types": "0.2.22",
|
|
48
|
+
"@easbot/utils": "0.2.22"
|
|
49
49
|
},
|
|
50
50
|
"devDependencies": {
|
|
51
51
|
"@biomejs/biome": "^2.4.14",
|