@agimon-ai/po-mcp-proxy 0.0.1-alpha.35 → 0.0.1-alpha.37
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/cli.cjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
const e=require("./src-
|
|
2
|
+
const e=require("./src-CO_hVzqo.cjs");let t=require("node:fs/promises"),n=require("node:path"),r=require("node:crypto"),i=require("@agimon-ai/foundation-process-registry"),a=require("js-yaml");a=e.ft(a,1);let o=require("@agimon-ai/foundation-port-registry"),s=require("liquidjs"),c=require("commander"),l=require("node:fs");function u(e){return`runtime:${e}`}function d(e){return e instanceof Error?e.message:String(e)}async function f(e){let t=await e.listProcesses({serviceName:`mcp-proxy-http`,liveOnly:!0}),n=/* @__PURE__ */ new Set;for(let e of t){let t=e.metadata?.serverId;typeof t==`string`&&n.add(t)}return n}async function p(e,t){let n=await e.listProcesses({serviceType:`tool`,tags:[`mcp-proxy-stdio-http-bridge`],liveOnly:!0}),r=u(t);return n.filter(e=>e.tags?.includes(r)).length}async function m(t){let n=t.probe??(async(t,n)=>(await e.b(t,n)).ok),r={stopped:[],removedFiles:[],kept:[]},i=await f(t.processRegistry),a=await t.runtimeStateService.listOrphans(i);for(let e of a){let i={serverId:e.serverId,host:e.host,port:e.port,reason:``};if(!await n(e.host,e.port)){i.reason=`unreachable runtime; removed stale state file`,t.dryRun||await t.runtimeStateService.remove(e.serverId),r.removedFiles.push(i);continue}if(e.runtimeLifecycle!==`stdio-http-managed`){i.reason=`manual runtime; left untouched`,r.kept.push(i);continue}let a=await p(t.processRegistry,e.serverId);if(a>0)i.reason=`managed runtime with ${a} live bridge(s); kept`,r.kept.push(i);else{if(i.reason=`orphaned stdio-http-managed runtime (no live bridges); stopped`,!t.dryRun)try{await t.stopServerService.stop({serverId:e.serverId,force:!0}),await t.runtimeStateService.remove(e.serverId).catch(()=>void 0)}catch(e){i.reason=`failed to stop orphaned runtime: ${d(e)}`,r.kept.push(i);continue}r.stopped.push(i)}}return r}function h(e,t){let n=t?`Would stop`:`Stopped`;for(let t of e.stopped)console.log(`${n} orphaned runtime '${t.serverId}' (http://${t.host}:${t.port}) — ${t.reason}`);let r=t?`Would remove`:`Removed`;for(let t of e.removedFiles)console.log(`${r} stale state for '${t.serverId}' (http://${t.host}:${t.port})`);for(let t of e.kept)console.log(`Kept '${t.serverId}' (http://${t.host}:${t.port}) — ${t.reason}`);console.log(`Summary: ${e.stopped.length} stopped, ${e.removedFiles.length} stale file(s), ${e.kept.length} kept.`)}const g=new c.Command(`cleanup`).description(`Reap orphaned/stale mcp-proxy HTTP runtimes and their leftover state files`).option(`-n, --dry-run`,`Report what would be done without stopping or removing anything`,!1).option(`-j, --json`,`Output as JSON`,!1).action(async t=>{try{let n=await m({runtimeStateService:new e.T,processRegistry:new i.ProcessRegistryService(process.env.PROCESS_REGISTRY_PATH),stopServerService:new e.C,dryRun:t.dryRun});if(t.json){console.log(JSON.stringify({dryRun:t.dryRun,...n},null,2));return}h(n,t.dryRun)}catch(e){let n=`Error executing cleanup: ${d(e)}`;t.json?console.log(JSON.stringify({ok:!1,error:n},null,2)):console.error(n),process.exit(1)}});function _(e){return e instanceof Error?e.message:String(e)}async function v(e,t){try{return(await fetch(`http://${e}:${t}/health`,{signal:AbortSignal.timeout(3e3)})).ok}catch{return!1}}async function y(t,n,r,i,a){let o=n.proxy?.host??`localhost`,s=n.proxy?.port,c=`http://${o}:${s}/mcp`;await v(o,s)||(i.json||console.error(`Starting HTTP proxy server in background...`),await e.y({host:o,port:s,config:r,cache:i.useCache!==!1,clearDefinitionsCache:!1,proxyMode:`flat`}));let l=await t.createClientManagerService();try{await l.connectToServer(`proxy`,{name:`proxy`,transport:`http`,config:{url:c}}),i.json||console.error(`✓ Connected to proxy at ${c}`)}catch(e){throw Error(`Failed to connect to proxy server at ${c}: ${_(e)}`)}try{return await a({container:t,configFilePaths:r,config:n,clientManager:l})}finally{await l.disconnectAll()}}async function b(e,t,n,r,i){let a=await e.createClientManagerService();if(await Promise.all(Object.entries(t.mcpServers).map(async([e,t])=>{try{await a.connectToServer(e,t),r.json||console.error(`✓ Connected to ${e}`)}catch(t){r.json||console.error(`✗ Failed to connect to ${e}: ${_(t)}`)}})),a.getAllClients().length===0)throw Error(`No MCP servers connected`);try{return await i({container:e,configFilePaths:n,config:t,clientManager:a})}finally{await a.disconnectAll()}}async function x(t,n){let r=(e,t)=>{t===void 0?console.error(e):console.error(e,t)},i=t.json?{trace:r,debug:r,info:r,warn:r,error:r}:console,a=e.o(i),o=e.I(t.config),s=o.length>0?null:await e.P(),c=s?[s]:o;if(c.length===0)throw Error(`No config file found. Use --config or create mcp-config.yaml`);let l=await a.createConfigFetcherService({configSources:e.L(c),useCache:t.useCache}).fetchConfiguration();return l.proxy?.port?await y(a,l,c,t,n):await b(a,l,c,t,n)}function S(e){return e instanceof Error?e.message:String(e)}const C=new c.Command(`describe-tools`).description(`Describe specific MCP tools`).argument(`<toolNames...>`,`Tool names to describe`).option(`-c, --config <path>`,`Path to MCP server configuration file (repeatable; later files override earlier ones)`,e.F).option(`-s, --server <name>`,`Filter by server name`).option(`-j, --json`,`Output as JSON`,!1).action(async(e,t)=>{try{await x(t,async({container:n,config:r,clientManager:i})=>{let a=t.server?[i.getClient(t.server)].filter(e=>e!==void 0):i.getAllClients();if(t.server&&a.length===0)throw Error(`Server "${t.server}" not found`);let o=process.env.PROJECT_PATH||process.cwd(),s=r.skills?.paths||[],c=s.length>0?n.createSkillService(o,s):void 0,l=[],u=[],d=[...e],f=await Promise.all(a.map(async e=>{try{return{client:e,tools:await e.listTools(),error:null}}catch(t){return{client:e,tools:[],error:t}}}));for(let{client:n,tools:r,error:i}of f)if(i)t.json||console.error(`Failed to list tools from ${n.serverName}:`,i);else for(let t of e){let e=r.find(e=>e.name===t);if(e){l.push({server:n.serverName,name:e.name,description:e.description,inputSchema:e.inputSchema});let r=d.indexOf(t);r>-1&&d.splice(r,1)}}if(c&&d.length>0){let e=await Promise.all([...d].map(async e=>{let t=e.startsWith(`skill__`)?e.slice(7):e;return{toolName:e,skill:await c.getSkill(t)}}));for(let{toolName:t,skill:n}of e)if(n){u.push({name:n.name,location:n.basePath,instructions:n.content});let e=d.indexOf(t);e>-1&&d.splice(e,1)}}let p=[];if(l.length>0&&p.push(`For MCP tools: Use the use_tool function with toolName and toolArgs based on the inputSchema above.`),u.length>0&&p.push(`For skill, just follow skill's description to continue.`),t.json){let e={};l.length>0&&(e.tools=l),u.length>0&&(e.skills=u),p.length>0&&(e.nextSteps=p),d.length>0&&(e.notFound=d),console.log(JSON.stringify(e,null,2))}else{if(l.length>0){console.log(`
|
|
3
3
|
Found tools:
|
|
4
4
|
`);for(let e of l)console.log(`Server: ${e.server}`),console.log(`Tool: ${e.name}`),console.log(`Description: ${e.description||`No description`}`),console.log(`Input Schema:`),console.log(JSON.stringify(e.inputSchema,null,2)),console.log(``)}if(u.length>0){console.log(`
|
|
5
5
|
Found skills:
|
package/dist/cli.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import{C as e,F as t,I as n,L as r,M as i,N as a,P as o,S as s,T as c,_ as l,b as u,d,dt as f,g as p,h as m,j as h,l as g,n as _,o as v,r as y,t as b,u as x,v as ee,x as S,y as C}from"./src-
|
|
2
|
+
import{C as e,F as t,I as n,L as r,M as i,N as a,P as o,S as s,T as c,_ as l,b as u,d,dt as f,g as p,h as m,j as h,l as g,n as _,o as v,r as y,t as b,u as x,v as ee,x as S,y as C}from"./src-BalhF-RW.mjs";import{access as w,readFile as T,writeFile as te}from"node:fs/promises";import{dirname as E,join as D,resolve as O}from"node:path";import{createHash as ne,randomUUID as re}from"node:crypto";import{ProcessRegistryService as k,createProcessLease as ie,normalizeRepositoryPath as ae,withFileLock as oe}from"@agimon-ai/foundation-process-registry";import*as se from"js-yaml";import{DEFAULT_PORT_RANGE as ce,PortRegistryService as le}from"@agimon-ai/foundation-port-registry";import{Liquid as ue}from"liquidjs";import{Command as A}from"commander";import{constants as de}from"node:fs";function fe(e){return`runtime:${e}`}function pe(e){return e instanceof Error?e.message:String(e)}async function me(e){let t=await e.listProcesses({serviceName:`mcp-proxy-http`,liveOnly:!0}),n=/* @__PURE__ */ new Set;for(let e of t){let t=e.metadata?.serverId;typeof t==`string`&&n.add(t)}return n}async function he(e,t){let n=await e.listProcesses({serviceType:`tool`,tags:[`mcp-proxy-stdio-http-bridge`],liveOnly:!0}),r=fe(t);return n.filter(e=>e.tags?.includes(r)).length}async function ge(e){let t=e.probe??(async(e,t)=>(await u(e,t)).ok),n={stopped:[],removedFiles:[],kept:[]},r=await me(e.processRegistry),i=await e.runtimeStateService.listOrphans(r);for(let r of i){let i={serverId:r.serverId,host:r.host,port:r.port,reason:``};if(!await t(r.host,r.port)){i.reason=`unreachable runtime; removed stale state file`,e.dryRun||await e.runtimeStateService.remove(r.serverId),n.removedFiles.push(i);continue}if(r.runtimeLifecycle!==`stdio-http-managed`){i.reason=`manual runtime; left untouched`,n.kept.push(i);continue}let a=await he(e.processRegistry,r.serverId);if(a>0)i.reason=`managed runtime with ${a} live bridge(s); kept`,n.kept.push(i);else{if(i.reason=`orphaned stdio-http-managed runtime (no live bridges); stopped`,!e.dryRun)try{await e.stopServerService.stop({serverId:r.serverId,force:!0}),await e.runtimeStateService.remove(r.serverId).catch(()=>void 0)}catch(e){i.reason=`failed to stop orphaned runtime: ${pe(e)}`,n.kept.push(i);continue}n.stopped.push(i)}}return n}function _e(e,t){let n=t?`Would stop`:`Stopped`;for(let t of e.stopped)console.log(`${n} orphaned runtime '${t.serverId}' (http://${t.host}:${t.port}) — ${t.reason}`);let r=t?`Would remove`:`Removed`;for(let t of e.removedFiles)console.log(`${r} stale state for '${t.serverId}' (http://${t.host}:${t.port})`);for(let t of e.kept)console.log(`Kept '${t.serverId}' (http://${t.host}:${t.port}) — ${t.reason}`);console.log(`Summary: ${e.stopped.length} stopped, ${e.removedFiles.length} stale file(s), ${e.kept.length} kept.`)}const ve=new A(`cleanup`).description(`Reap orphaned/stale mcp-proxy HTTP runtimes and their leftover state files`).option(`-n, --dry-run`,`Report what would be done without stopping or removing anything`,!1).option(`-j, --json`,`Output as JSON`,!1).action(async t=>{try{let n=await ge({runtimeStateService:new c,processRegistry:new k(process.env.PROCESS_REGISTRY_PATH),stopServerService:new e,dryRun:t.dryRun});if(t.json){console.log(JSON.stringify({dryRun:t.dryRun,...n},null,2));return}_e(n,t.dryRun)}catch(e){let n=`Error executing cleanup: ${pe(e)}`;t.json?console.log(JSON.stringify({ok:!1,error:n},null,2)):console.error(n),process.exit(1)}});function j(e){return e instanceof Error?e.message:String(e)}async function ye(e,t){try{return(await fetch(`http://${e}:${t}/health`,{signal:AbortSignal.timeout(3e3)})).ok}catch{return!1}}async function be(e,t,n,r,i){let a=t.proxy?.host??`localhost`,o=t.proxy?.port,s=`http://${a}:${o}/mcp`;await ye(a,o)||(r.json||console.error(`Starting HTTP proxy server in background...`),await C({host:a,port:o,config:n,cache:r.useCache!==!1,clearDefinitionsCache:!1,proxyMode:`flat`}));let c=await e.createClientManagerService();try{await c.connectToServer(`proxy`,{name:`proxy`,transport:`http`,config:{url:s}}),r.json||console.error(`✓ Connected to proxy at ${s}`)}catch(e){throw Error(`Failed to connect to proxy server at ${s}: ${j(e)}`)}try{return await i({container:e,configFilePaths:n,config:t,clientManager:c})}finally{await c.disconnectAll()}}async function xe(e,t,n,r,i){let a=await e.createClientManagerService();if(await Promise.all(Object.entries(t.mcpServers).map(async([e,t])=>{try{await a.connectToServer(e,t),r.json||console.error(`✓ Connected to ${e}`)}catch(t){r.json||console.error(`✗ Failed to connect to ${e}: ${j(t)}`)}})),a.getAllClients().length===0)throw Error(`No MCP servers connected`);try{return await i({container:e,configFilePaths:n,config:t,clientManager:a})}finally{await a.disconnectAll()}}async function M(e,t){let i=(e,t)=>{t===void 0?console.error(e):console.error(e,t)},a=e.json?{trace:i,debug:i,info:i,warn:i,error:i}:console,s=v(a),c=n(e.config),l=c.length>0?null:await o(),u=l?[l]:c;if(u.length===0)throw Error(`No config file found. Use --config or create mcp-config.yaml`);let d=await s.createConfigFetcherService({configSources:r(u),useCache:e.useCache}).fetchConfiguration();return d.proxy?.port?await be(s,d,u,e,t):await xe(s,d,u,e,t)}function Se(e){return e instanceof Error?e.message:String(e)}const Ce=new A(`describe-tools`).description(`Describe specific MCP tools`).argument(`<toolNames...>`,`Tool names to describe`).option(`-c, --config <path>`,`Path to MCP server configuration file (repeatable; later files override earlier ones)`,t).option(`-s, --server <name>`,`Filter by server name`).option(`-j, --json`,`Output as JSON`,!1).action(async(e,t)=>{try{await M(t,async({container:n,config:r,clientManager:i})=>{let a=t.server?[i.getClient(t.server)].filter(e=>e!==void 0):i.getAllClients();if(t.server&&a.length===0)throw Error(`Server "${t.server}" not found`);let o=process.env.PROJECT_PATH||process.cwd(),s=r.skills?.paths||[],c=s.length>0?n.createSkillService(o,s):void 0,l=[],u=[],d=[...e],f=await Promise.all(a.map(async e=>{try{return{client:e,tools:await e.listTools(),error:null}}catch(t){return{client:e,tools:[],error:t}}}));for(let{client:n,tools:r,error:i}of f)if(i)t.json||console.error(`Failed to list tools from ${n.serverName}:`,i);else for(let t of e){let e=r.find(e=>e.name===t);if(e){l.push({server:n.serverName,name:e.name,description:e.description,inputSchema:e.inputSchema});let r=d.indexOf(t);r>-1&&d.splice(r,1)}}if(c&&d.length>0){let e=await Promise.all([...d].map(async e=>{let t=e.startsWith(`skill__`)?e.slice(7):e;return{toolName:e,skill:await c.getSkill(t)}}));for(let{toolName:t,skill:n}of e)if(n){u.push({name:n.name,location:n.basePath,instructions:n.content});let e=d.indexOf(t);e>-1&&d.splice(e,1)}}let p=[];if(l.length>0&&p.push(`For MCP tools: Use the use_tool function with toolName and toolArgs based on the inputSchema above.`),u.length>0&&p.push(`For skill, just follow skill's description to continue.`),t.json){let e={};l.length>0&&(e.tools=l),u.length>0&&(e.skills=u),p.length>0&&(e.nextSteps=p),d.length>0&&(e.notFound=d),console.log(JSON.stringify(e,null,2))}else{if(l.length>0){console.log(`
|
|
3
3
|
Found tools:
|
|
4
4
|
`);for(let e of l)console.log(`Server: ${e.server}`),console.log(`Tool: ${e.name}`),console.log(`Description: ${e.description||`No description`}`),console.log(`Input Schema:`),console.log(JSON.stringify(e.inputSchema,null,2)),console.log(``)}if(u.length>0){console.log(`
|
|
5
5
|
Found skills:
|
package/dist/index.cjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./src-
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./src-CO_hVzqo.cjs");exports.CALLBACK_BIND_WAIT_MS=e.X,exports.CALLBACK_POLL_INTERVAL_MS=e.Z,exports.CALLBACK_PORT_SPAN=e.Q,exports.CALLBACK_TIMEOUT_MS=e.$,exports.CallbackRendezvous=e.ct,exports.ConfigFetcherService=e.R,exports.DEFAULT_CALLBACK_PATH=e.et,exports.DEFAULT_CALLBACK_PORT=e.tt,exports.DEFAULT_HTTP_RUNTIME_TIMEOUT_MS=e.g,exports.DefinitionsCacheService=e.j,exports.DescribeToolsTool=e.m,exports.FileTokenStore=e.G,exports.HttpTransportHandler=e.S,exports.McpClientManagerService=e.E,exports.McpOAuthProvider=e.W,exports.MemoryTokenStore=e.K,exports.OAuthCallbackServer=e.nt,exports.OutputGuardService=e.O,exports.RESERVATION_STALE_MS=e.lt,exports.RuntimeStateService=e.T,exports.SearchListToolsTool=e.p,exports.SkillService=e.w,exports.StdioHttpTransportHandler=e.u,exports.StdioTransportHandler=e.d,exports.StopServerService=e.C,exports.TRANSPORT_MODE=e.t,exports.UseToolTool=e.f,exports.acquireSharedCallbackServer=e.rt,exports.cleanupStaleRegistries=e.M,exports.closeSharedCallbackServer=e.it,exports.collectConfigPath=e.F,exports.createHttpTransportHandler=e.i,exports.createOAuthProvider=e.B,exports.createProxyContainer=e.a,exports.createProxyIoCContainer=e.o,exports.createProxyLogger=e.A,exports.createServer=e.n,exports.createSessionServer=e.r,exports.createStdioHttpTransportHandler=e.s,exports.createStdioTransportHandler=e.c,exports.findConfigFile=e.P,exports.findHealthyExistingRuntime=e._,exports.generateServerId=e.N,exports.getDefaultRendezvousRoot=e.ut,exports.getDefaultTokenStoreDir=e.q,exports.getSharedCallbackServer=e.at,exports.initializeSharedServices=e.l,exports.isAuthError=e.D,exports.parseMcpConfig=e.z,exports.prestartHttpRuntime=e.y,exports.probeRuntimeHealth=e.b,exports.readAuthEntryForUrl=e.J,exports.releaseSharedCallbackServer=e.ot,exports.resolveAuthScheme=e.V,exports.resolveBearerToken=e.H,exports.resolveCallbackPort=e.U,exports.resolveOutputGuardOptions=e.k,exports.resolveWorkspaceRoot=e.dt,exports.toConfigPaths=e.I,exports.toConfigSources=e.L,exports.withTokenStoreLock=e.Y,exports.workspaceCallbackPort=e.st;
|
package/dist/index.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export{X as CALLBACK_BIND_WAIT_MS,Z as CALLBACK_POLL_INTERVAL_MS,Q as CALLBACK_PORT_SPAN,$ as CALLBACK_TIMEOUT_MS,ct as CallbackRendezvous,R as ConfigFetcherService,et as DEFAULT_CALLBACK_PATH,tt as DEFAULT_CALLBACK_PORT,g as DEFAULT_HTTP_RUNTIME_TIMEOUT_MS,j as DefinitionsCacheService,m as DescribeToolsTool,G as FileTokenStore,S as HttpTransportHandler,E as McpClientManagerService,W as McpOAuthProvider,K as MemoryTokenStore,nt as OAuthCallbackServer,O as OutputGuardService,lt as RESERVATION_STALE_MS,T as RuntimeStateService,p as SearchListToolsTool,w as SkillService,u as StdioHttpTransportHandler,d as StdioTransportHandler,C as StopServerService,t as TRANSPORT_MODE,f as UseToolTool,rt as acquireSharedCallbackServer,M as cleanupStaleRegistries,it as closeSharedCallbackServer,F as collectConfigPath,i as createHttpTransportHandler,B as createOAuthProvider,a as createProxyContainer,o as createProxyIoCContainer,A as createProxyLogger,n as createServer,r as createSessionServer,s as createStdioHttpTransportHandler,c as createStdioTransportHandler,P as findConfigFile,_ as findHealthyExistingRuntime,N as generateServerId,ut as getDefaultRendezvousRoot,q as getDefaultTokenStoreDir,at as getSharedCallbackServer,l as initializeSharedServices,D as isAuthError,z as parseMcpConfig,y as prestartHttpRuntime,b as probeRuntimeHealth,J as readAuthEntryForUrl,ot as releaseSharedCallbackServer,V as resolveAuthScheme,H as resolveBearerToken,U as resolveCallbackPort,k as resolveOutputGuardOptions,dt as resolveWorkspaceRoot,I as toConfigPaths,L as toConfigSources,Y as withTokenStoreLock,st as workspaceCallbackPort}from"./src-
|
|
1
|
+
export{X as CALLBACK_BIND_WAIT_MS,Z as CALLBACK_POLL_INTERVAL_MS,Q as CALLBACK_PORT_SPAN,$ as CALLBACK_TIMEOUT_MS,ct as CallbackRendezvous,R as ConfigFetcherService,et as DEFAULT_CALLBACK_PATH,tt as DEFAULT_CALLBACK_PORT,g as DEFAULT_HTTP_RUNTIME_TIMEOUT_MS,j as DefinitionsCacheService,m as DescribeToolsTool,G as FileTokenStore,S as HttpTransportHandler,E as McpClientManagerService,W as McpOAuthProvider,K as MemoryTokenStore,nt as OAuthCallbackServer,O as OutputGuardService,lt as RESERVATION_STALE_MS,T as RuntimeStateService,p as SearchListToolsTool,w as SkillService,u as StdioHttpTransportHandler,d as StdioTransportHandler,C as StopServerService,t as TRANSPORT_MODE,f as UseToolTool,rt as acquireSharedCallbackServer,M as cleanupStaleRegistries,it as closeSharedCallbackServer,F as collectConfigPath,i as createHttpTransportHandler,B as createOAuthProvider,a as createProxyContainer,o as createProxyIoCContainer,A as createProxyLogger,n as createServer,r as createSessionServer,s as createStdioHttpTransportHandler,c as createStdioTransportHandler,P as findConfigFile,_ as findHealthyExistingRuntime,N as generateServerId,ut as getDefaultRendezvousRoot,q as getDefaultTokenStoreDir,at as getSharedCallbackServer,l as initializeSharedServices,D as isAuthError,z as parseMcpConfig,y as prestartHttpRuntime,b as probeRuntimeHealth,J as readAuthEntryForUrl,ot as releaseSharedCallbackServer,V as resolveAuthScheme,H as resolveBearerToken,U as resolveCallbackPort,k as resolveOutputGuardOptions,dt as resolveWorkspaceRoot,I as toConfigPaths,L as toConfigSources,Y as withTokenStoreLock,st as workspaceCallbackPort}from"./src-BalhF-RW.mjs";
|
|
@@ -6,7 +6,7 @@ import{access as e,chmod as t,lstat as n,mkdir as r,mkdtemp as i,open as a,readF
|
|
|
6
6
|
`),a=Wn(i);if(a.bytes<=this.maxBytes&&a.lines<=this.maxLines)return e;let{path:o,error:s}=await Vn(`output`,i),c=In(a,o,s),l=Nn(this.maxBytes,this.maxLines,c),u=`${Pn(i,l.maxBytes,l.maxLines)}\n\n${c}`,d=Wn(u),f=r.filter(e=>e.type===`image`).length,p={truncated:!0,originalBytes:a.bytes,returnedBytes:d.bytes,originalLines:a.lines,returnedLines:d.lines,...f>0?{imageBlocksPassedThrough:f}:{},...o?{fullOutputPath:o}:{},...s?{writeError:s}:{}};return{...e,content:[{type:`text`,text:u},...r],_meta:{...e._meta,"agiflowai/outputGuard":p}}}async guardStructuredContent(e){if(e.structuredContent===void 0)return e;let t=Un(e.structuredContent),n=U(t);if(n<=this.structuredMaxBytes)return e;let{path:r,error:i}=await Vn(`structured-content`,t);return{...e,structuredContent:{omitted:!0,reason:`structuredContent exceeded the size limit and was replaced with this summary to keep the caller context bounded.`,rawBytes:n,summary:Ln(e.structuredContent),...r?{fullResultPath:r}:{},...i?{resultWriteError:i}:{}}}}};function Nn(e,t,n){let r=Wn(`\n\n${n}`);return{maxBytes:Math.max(0,e-r.bytes),maxLines:Math.max(0,t-r.lines)}}function Pn(e,t,n){let r=e.split(`
|
|
7
7
|
`),i=[],a=0;for(let e of r){if(i.length>=n)break;let r=+(i.length>0),o=U(e);if(a+r+o>t){let n=t-a-r;n>0&&i.push(Fn(e,n));break}i.push(e),a+=r+o}return i.join(`
|
|
8
8
|
`)}function Fn(e,t){if(U(e)<=t)return e;let n=Buffer.from(e,`utf8`),r=Math.max(0,t);for(;r>0&&(n[r]&192)==128;)r--;return n.subarray(0,r).toString(`utf8`)}function In(e,t,n){let r=`[MCP tool output truncated: original ${e.lines.toLocaleString()} lines / ${qn(e.bytes)}.`;return t?`${r} Full text saved to: ${t}]`:`${r} Full output could not be saved: ${n??`unknown error`}]`}function Ln(e){let t=Hn(e);if(!t)return{type:e===null?`null`:typeof e,omitted:!0};let n=Object.keys(t);return{type:Array.isArray(e)?`array`:`object`,keyCount:Array.isArray(e)?e.length:n.length,keysPreview:n.slice(0,20).map(Rn),...Array.isArray(e)&&e.length>20?{itemsOmitted:e.length-20}:{},omitted:!0}}function Rn(e){return e.length<=120?e:`${e.slice(0,119)}…`}let zn;function Bn(){return zn??=i(v(E(),`mcp-proxy-output-`)),zn}async function Vn(e,t){try{let n=await Bn(),r=v(n,`${e}-${C(8).toString(`hex`)}.txt`);return await m(r,t,{encoding:`utf8`,mode:384}),{path:r}}catch(e){return zn=void 0,{error:e instanceof Error?e.message:String(e)}}}function Hn(e){return typeof e==`object`&&e?e:void 0}function Un(e){try{return JSON.stringify(e)??``}catch{return``}}function Wn(e){return{bytes:U(e),lines:e.length===0?0:e.split(`
|
|
9
|
-
`).length}}function U(e){return Buffer.byteLength(e,`utf8`)}function Gn(e){if(typeof e!=`number`||!Number.isFinite(e))return;let t=Math.floor(e);return t>0?t:void 0}function Kn(e){let t=process.env[e]?.trim().toLowerCase();if(t){if([`0`,`false`,`no`,`off`].includes(t))return!1;if([`1`,`true`,`yes`,`on`].includes(t))return!0}}function qn(e){return e<1024?`${e} B`:e<1048576?`${(e/1024).toFixed(1)} KiB`:`${(e/1048576).toFixed(1)} MiB`}const W=`x-mcp-proxy-original-output-schema`;function Jn(e){return Array.from(e.slice(0,256)).filter(e=>{let t=e.charCodeAt(0);return t>=32&&t<=126||t>=160}).join(``)}var Yn=class extends ve{serverName;compatibility;calls;constructor(e,t,n,r){let i=new Ae,a=new je;super(e,{...t,jsonSchemaValidator:{getValidator:e=>t=>{let n=i.getStore()?.toolName,r=Object.hasOwn(e,W)?e[W]:e;try{!a.getValidator(r)(t).valid&&n&&this.warn(n,`tools/call`,`structuredContent`,`Structured output does not match its output schema.`)}catch{n&&this.warn(n,`tools/call`,`outputSchema`,`Output schema could not be compiled or evaluated.`)}return{valid:!0,data:t,errorMessage:void 0}}}}),this.serverName=n,this.compatibility=r,this.calls=i}warn(e,t,n,r){try{Promise.resolve(this.compatibility.onWarning({serverName:Jn(this.serverName),toolName:Jn(e),method:t,path:n,message:r})).catch(()=>{})}catch{}}request(e,t,n){if(t&&`~standard`in t)return super.request(e,t,n);if(e.method!==`tools/list`){let n=super.request(e,t);return e.method===`tools/call`?n.then(e=>{let t=this.calls.getStore();return t&&(t.result=e),e}):n}return super.request(e,{"~standard":{version:1,vendor:`mcp-proxy-output-compatibility`,validate:e=>this.validateListing(e)}},t)}validateListing(e){if(!e||typeof e!=`object`||!(`tools`in e)||!Array.isArray(e.tools))return{issues:[{message:`Invalid tools/list result`}]};let t=e.tools,n=t.map(e=>{if(!e||typeof e!=`object`)return e;let{outputSchema:t,...n}=e;return n}),r=this._wireCodec().validateResult(`tools/list`,{...e,tools:n});if(!r.ok)return{issues:[{message:`Invalid non-output tools/list descriptor`}]};for(let[e,n]of r.value.tools.entries()){let r=t[e];Object.hasOwn(r,`outputSchema`)&&(n.outputSchema={type:`object`,[W]:r.outputSchema},this._wireCodec().validateResult(`tools/list`,{tools:[r]}).ok||this.warn(n.name,`tools/list`,`tools/${e}/outputSchema`,`Output schema descriptor is not MCP-compatible.`))}return{value:r.value}}async listTools(e,t){let n=await super.listTools(e,t);return{...n,tools:n.tools.map(e=>e.outputSchema&&Object.hasOwn(e.outputSchema,W)?{...e,outputSchema:e.outputSchema[W]}:e)}}async callTool(e,t){let n={toolName:e.name,result:void 0};return this.calls.run(n,async()=>{try{return await super.callTool(e,t)}catch(t){if(!(t instanceof be)||t.code!==xe.InvalidRequest||t.message!==`Tool ${e.name} has an output schema but did not return structured content`||!n.result||n.result.structuredContent!==void 0||n.result.isError)throw t;return this.warn(e.name,`tools/call`,`structuredContent`,`Tool omitted structured output required by its output schema.`),n.result}})}};const Xn=`mcp-proxy-client-manager`,Zn=[0,100];function Qn(e){return e===`reconnecting`?`connecting`:e}var $n=class extends Error{serverName;attempts;code=`MCP_SERVER_RECOVERY_EXHAUSTED`;constructor(e,t,n){super(`MCP server "${e}" did not recover after ${t} attempts.`,n),this.serverName=e,this.attempts=t,this.name=`McpServerRecoveryExhaustedError`}};async function G(e){let t=await Promise.allSettled([Promise.resolve().then(()=>e.client.close()),Promise.resolve().then(()=>e.stdioTransport?.close())]);try{await e.processLease?.release({kill:!1,releasePort:!1})}catch(e){t.push({status:`rejected`,reason:e})}let n=t.flatMap(e=>e.status===`rejected`?[e.reason]:[]);if(n.length)throw AggregateError(n,`MCP connection cleanup failed`)}function er(e){return ar(e).some(({message:e,code:t})=>{let n=e.toLowerCase(),r=rr(t);return n.includes(`unknown session`)||n.includes(`session not found`)||n.includes(`transport closed`)||n.includes(`connection closed`)||n.includes(`socket hang up`)||n.includes(`fetch failed`)||r===`econnreset`})}function tr(e){return e instanceof O||ar(e).some(({message:e,code:t})=>{let n=e.toLowerCase();return t===`401`||n.includes(`unauthorized`)||n.includes(`http 401`)||n.includes(`invalid_token`)})}function nr(e){return ar(e).some(({message:e,code:t})=>{let n=e.toLowerCase(),r=rr(t);return n.includes(`transport closed`)||n.includes(`connection closed`)||n.includes(`premature close`)||n.includes(`socket hang up`)||n.includes(`write epipe`)||n.includes(`read econnreset`)||n.includes(`fetch failed`)||r===`epipe`||r===`econnreset`})}function rr(e){return typeof e==`string`?e.toLowerCase():void 0}function ir(e){if(typeof e==`string`)return e;if(typeof e==`symbol`)return e.toString();if(typeof e==`object`&&e)try{return JSON.stringify(e)??`Unknown error`}catch{return`Unknown error`}return String(e)}function ar(e){let t=/* @__PURE__ */ new Set,n=[],r=e;for(;r&&!t.has(r);)if(t.add(r),r instanceof Error){let e=r;n.push({message:r.message,code:e.code}),r=e.cause}else if(typeof r==`object`){let e=r;n.push({message:typeof e.message==`string`?e.message:ir(r),code:typeof e.code==`string`?e.code:void 0}),r=e.cause}else{n.push({message:ir(r)});break}return n}var or=class{outputGuard;serverName;serverInstruction;toolBlacklist;omitToolDescription;prompts;transport;logger;client;stdioTransport;processLease;connected=!1;lifecycleState=`closed`;recoveryError;reconnectFn;reconnectPromise;recoverAuthorization;authorizationRecovery;pendingLeaseRelease;onDisconnected;onLifecycleChange;closing=!1;closePromise;lifetime=new AbortController;constructor(e,t,n,r,i,a){this.outputGuard=a,this.serverName=e,this.serverInstruction=i.instruction,this.toolBlacklist=i.toolBlacklist,this.omitToolDescription=i.omitToolDescription,this.prompts=i.prompts,this.transport=t,this.logger=r,this.client=n}setAuthorizationRecovery(e){this.recoverAuthorization=e}setStdioTransport(e){this.stdioTransport=e}setProcessLease(e){this.processLease=e}setConnected(e){this.connected=e,this.setLifecycleState(e?`connected`:`closed`),e&&(this.recoveryError=void 0)}getLifecycleState(){return this.lifecycleState}setLifecycleChangeCallback(e){this.onLifecycleChange=e}setLifecycleState(e,t){this.lifecycleState!==e&&(this.lifecycleState=e,this.onLifecycleChange?.(e,t))}async waitUntilReady(){await this.ensureReady()}getServerCapabilities(){return this.client.getServerCapabilities()}setReconnectFn(e){this.reconnectFn=e}setDisconnectedCallback(e){this.onDisconnected=e}handleStdioClosed(e){if(this.stdioTransport!==e||this.closing)return;let t=this.processLease;this.stdioTransport=void 0,this.processLease=void 0,this.connected=!1,this.recoveryError=void 0,this.setLifecycleState(`reconnecting`),this.pendingLeaseRelease=this.releaseProcessLease(t),!this.closing&&(this.logger.warn(`mcp_proxy.server.disconnected`,{"mcp.server.name":this.serverName,"mcp.server.transport":this.transport,"mcp.server.lifecycle":this.lifecycleState,"mcp.server.disconnect_reason":`stdio transport closed`,"mcp.server.recovery_mode":`proactive`}),this.onDisconnected?.(this),this.reconnectClient(`stdio transport closed`).catch(()=>{}))}async releaseProcessLease(e){try{await e?.release({kill:!1,releasePort:!1})}catch(e){this.logger.warn(`Failed to release process lease for ${this.serverName}`,e)}}async releaseOwnedResources(e){let t=this.stdioTransport,n=this.processLease,r=this.pendingLeaseRelease;this.stdioTransport=void 0,this.processLease=void 0,this.pendingLeaseRelease=void 0;try{t&&e.killChild&&await t.close()}finally{await Promise.all([r,n?.release({kill:!1,releasePort:!1})])}}async reconnectClient(e=`recoverable transport error`){if(this.lifetime.signal.throwIfAborted(),!this.reconnectFn)throw Error(`No reconnect function configured for ${this.serverName}`);let t=this.reconnectFn;this.reconnectPromise||=(async()=>{this.connected=!1,this.setLifecycleState(`reconnecting`),this.recoveryError=void 0,await this.releaseOwnedResources({killChild:!0});try{await this.client.close()}catch(e){this.logger.warn(`Failed to close stale client for ${this.serverName}`,e)}let n;for(let[r,i]of Zn.entries()){i>0&&await _e(i,void 0,{signal:this.lifetime.signal}),this.lifetime.signal.throwIfAborted();try{let n=await t(this.lifetime.signal);this.closing&&(await G(n),this.lifetime.signal.throwIfAborted()),this.client=n.client,this.recoverAuthorization=n.recoverAuthorization,n.stdioTransport&&(this.stdioTransport=n.stdioTransport),n.processLease&&(this.processLease=n.processLease),this.connected=!0,this.setLifecycleState(`connected`),this.logger.info(`mcp_proxy.server.recovered`,{"mcp.server.name":this.serverName,"mcp.server.transport":this.transport,"mcp.server.lifecycle":`connected`,"mcp.server.recovery_attempt":r+1,"mcp.server.recovery_trigger":e});return}catch(e){this.lifetime.signal.throwIfAborted(),n=e,this.logger.warn(`mcp_proxy.server.reconnect_failed`,{"mcp.server.name":this.serverName,"mcp.server.transport":this.transport,"mcp.server.recovery_attempt":r+1,"error.message":e instanceof Error?e.message:String(e)})}}throw this.connected=!1,this.recoveryError=new $n(this.serverName,Zn.length,{cause:n}),this.setLifecycleState(`degraded`,this.recoveryError.message),this.logger.error(`mcp_proxy.server.degraded`,{"mcp.server.name":this.serverName,"mcp.server.transport":this.transport,"mcp.server.lifecycle":`degraded`,"mcp.server.recovery_attempts":Zn.length,"error.message":n instanceof Error?n.message:String(n)}),this.recoveryError})();let n=this.reconnectPromise;try{await n}finally{this.reconnectPromise===n&&(this.reconnectPromise=void 0)}}async ensureReady(){if(this.lifetime.signal.throwIfAborted(),!this.connected){if(this.recoveryError)throw this.recoveryError;await this.reconnectClient(`operation requested while disconnected`)}}async withSessionRetry(e,t){t?.throwIfAborted(),await this.ensureReady(),t?.throwIfAborted();try{return await e()}catch(n){if(t?.throwIfAborted(),(n instanceof O||n instanceof ye)&&this.recoverAuthorization)return this.authorizationRecovery??=this.recoverAuthorization(n,t?AbortSignal.any([t,this.lifetime.signal]):this.lifetime.signal).then(()=>this.reconnectClient(`authorization recovered`)).finally(()=>{this.authorizationRecovery=void 0}),await this.authorizationRecovery,t?.throwIfAborted(),e();if(!this.reconnectFn||!er(n)&&!nr(n))throw n;return this.logger.warn(`Session error for ${this.serverName}, reconnecting: ${n instanceof Error?n.message:String(n)}`),await this.reconnectClient(n instanceof Error?n.message:String(n)),t?.throwIfAborted(),await e()}}async listTools(){return this.withSessionRetry(async()=>(await this.client.listTools()).tools)}async listResources(){return this.withSessionRetry(async()=>(await this.client.listResources()).resources)}async listPrompts(){return this.withSessionRetry(async()=>(await this.client.listPrompts()).prompts)}async callTool(e,t,n){let r=n?.signal?AbortSignal.any([n.signal,this.lifetime.signal]):this.lifetime.signal,i=await this.withSessionRetry(async()=>{let i={signal:r,...n?.timeout===void 0?{}:{timeout:n.timeout}},a=await this.client.callTool({name:e,arguments:t},i);if(!De(a))throw Error(`Unexpected interactive response from MCP tool '${e}': this call requires a context-aware relay`);return a},r);if(r.throwIfAborted(),n?.onResult)try{await n.onResult(structuredClone(i))}catch(t){this.logger.warn(`MCP result observer failed for ${this.serverName}.${e}`,t)}return r.throwIfAborted(),this.outputGuard?await this.outputGuard.guard(i):i}async readResource(e,t){let n=t?.signal?AbortSignal.any([t.signal,this.lifetime.signal]):this.lifetime.signal,r=await this.withSessionRetry(async()=>await this.client.readResource({uri:e},{signal:n,...t?.timeout===void 0?{}:{timeout:t.timeout}}),n);return n.throwIfAborted(),r}async getPrompt(e,t){return this.withSessionRetry(async()=>await this.client.getPrompt({name:e,arguments:t}))}close(){return this.closePromise||=(this.closing=!0,this.lifetime.abort(/* @__PURE__ */ Error(`MCP client '${this.serverName}' is closed`)),this.connected=!1,this.setLifecycleState(`closed`),(async()=>{let e=(await Promise.allSettled([this.releaseOwnedResources({killChild:!0}),Promise.resolve().then(()=>this.client.close())])).flatMap(e=>e.status===`rejected`?[e.reason]:[]);if(e.length)throw AggregateError(e,`Failed to close MCP client '${this.serverName}'`)})()),this.closePromise}},sr=class{outputSchemaValidation;clientCapabilities;clients=/* @__PURE__ */ new Map;serverConfigs=/* @__PURE__ */ new Map;connectionPromises=/* @__PURE__ */ new Map;connectionControllers=/* @__PURE__ */ new Map;disposed=!1;disposePromise;logger;repositoryPath;workspaceRoot;executionCwd;env;processOwnerId=w();outputGuard;stateListeners=/* @__PURE__ */ new Set;serverStates=/* @__PURE__ */ new Map;tokenStore;serverTokenStores=/* @__PURE__ */ new Map;serverRemovals=/* @__PURE__ */ new Map;onAuthorizationUrl;callbackTimeoutMs;callbackServer;resolveUrlCredentials;constructor(e=console,t=process.cwd(),n=new Mn,r={},i,a,o,s,c={}){this.outputSchemaValidation=s,this.clientCapabilities=c,this.logger=e,this.repositoryPath=t,this.workspaceRoot=i,this.executionCwd=o,this.env=a,this.outputGuard=n,this.tokenStore=r.tokenStore,this.onAuthorizationUrl=r.onAuthorizationUrl,this.callbackTimeoutMs=r.callbackTimeoutMs,this.callbackServer=r.callbackServer,this.resolveUrlCredentials=r.resolveUrlCredentials}onServerStateChange(e){this.stateListeners.add(e);for(let[t,n]of this.serverStates)this.notifyListener(e,{...n,serverName:t});return()=>{this.stateListeners.delete(e)}}getServerState(e){return this.serverStates.get(e)?.state}emitServerState(e){this.serverStates.set(e.serverName,e);for(let t of this.stateListeners)this.notifyListener(t,e)}notifyListener(e,t){try{e(t)}catch(e){this.logger.warn(`[mcp-proxy] Server state listener threw for ${t.serverName}: ${e instanceof Error?e.message:String(e)}`)}}setOutputGuard(e){this.outputGuard=e}async cleanupChildProcesses(){await this.disconnectAll()}async connectToServer(e,t,n){if(await this.serverRemovals.get(e),n?.signal?.throwIfAborted(),this.disposed)throw Error(`The MCP client manager is disposed`);n?.tokenStore&&this.serverTokenStores.set(e,n.tokenStore),this.serverConfigs.set(e,t);let r=()=>{this.removeServer(e).catch(()=>this.logger.warn(`Failed to remove cancelled MCP server '${e}'.`))};n?.signal?.addEventListener(`abort`,r,{once:!0});try{await this.ensureConnected(e),n?.signal?.throwIfAborted()}finally{n?.signal?.removeEventListener(`abort`,r)}}async removeServer(e){let t=this.serverRemovals.get(e);if(t)return t;let n=this.serverTokenStores.get(e);this.serverConfigs.delete(e);let r=(async()=>{try{await this.disconnectServer(e)}finally{this.serverTokenStores.get(e)===n&&this.serverTokenStores.delete(e),await n?.clear(e),this.serverStates.delete(e)}})();this.serverRemovals.set(e,r);try{await r}finally{this.serverRemovals.get(e)===r&&this.serverRemovals.delete(e)}}registerServerConfigs(e){for(let[t,n]of Object.entries(e))this.serverConfigs.set(t,n)}getKnownServerNames(){return Array.from(this.serverConfigs.keys())}getServerRequestTimeout(e){return this.serverConfigs.get(e)?.requestTimeout}async ensureConnected(e){if(this.disposed)throw Error(`The MCP client manager is disposed`);let t=this.clients.get(e);if(t)return await t.waitUntilReady(),t;let n=this.connectionPromises.get(e);if(n)return await n;let r=this.serverConfigs.get(e);if(!r)throw Error(`No configuration found for server "${e}"`);let i=this.createConnection(e,r);this.connectionPromises.set(e,i);try{return await i}finally{this.connectionPromises.get(e)===i&&this.connectionPromises.delete(e)}}async createConnection(e,t){let n=t.timeout??3e4,r=new AbortController;this.connectionControllers.set(e,r),this.emitServerState({serverName:e,state:`connecting`});let i,a=new Promise((e,t)=>{i=()=>t(r.signal.reason),r.signal.addEventListener(`abort`,i,{once:!0}),r.signal.aborted&&i()}),o,s,c=new Promise((e,t)=>{s=e=>{clearTimeout(o),!(e||r.signal.aborted)&&(o=setTimeout(()=>{let e=/* @__PURE__ */ Error(`Connection timeout after ${n}ms`);r.abort(e),t(e)},n))},s(!1)}),l;try{l=await Promise.race([this.createSdkClientConnection(e,t,r.signal,e=>s?.(e)),c,a])}catch(n){let i=t.transport===`http`||t.transport===`sse`?t.config.auth:void 0,a=typeof i==`object`&&i?.type===`api-key`,o=n instanceof Error&&[`Invalid MCP API-key configuration.`,`MCP API-key credential is unavailable.`,`MCP credential destination was rejected.`].includes(n.message),s=a&&!o?/* @__PURE__ */ Error(`MCP API-key connection failed.`):n;throw this.emitServerState({serverName:e,state:this.disposed||r.signal.aborted&&!String(r.signal.reason).includes(`timeout`)?`closed`:tr(s)?`needs-auth`:`failed`,error:s instanceof Error?s.message:String(s)}),s}finally{s=void 0,clearTimeout(o),r.signal.removeEventListener(`abort`,i),this.connectionControllers.get(e)===r&&this.connectionControllers.delete(e)}let u=new or(e,t.transport,l.client,this.logger,{instruction:t.instruction,toolBlacklist:t.toolBlacklist,omitToolDescription:t.omitToolDescription,prompts:t.prompts},this.outputGuard);u.setDisconnectedCallback(()=>void 0),u.setAuthorizationRecovery(l.recoverAuthorization),u.setLifecycleChangeCallback((t,n)=>{this.emitServerState({serverName:e,state:Qn(t),...n?{error:n}:{}})});try{if(l.stdioTransport&&u.setStdioTransport(l.stdioTransport),l.processLease&&u.setProcessLease(l.processLease),r.signal.throwIfAborted(),this.disposed)throw Error(`The MCP client manager is disposed`);if(u.setConnected(!0),u.setReconnectFn(async n=>{try{if(this.disposed)throw Error(`The MCP client manager is disposed`);return await this.createSdkClientConnection(e,t,n)}catch(t){throw this.logger.warn(`Failed to reconnect to ${e}`,t),t}}),!u.serverInstruction)try{let e=u.client.getInstructions();e&&(u.serverInstruction=e)}catch(t){this.logger.warn(`Failed to get server instruction from ${e}`,t)}return this.clients.set(e,u),u}catch(e){throw await u.close(),e}}createSdkClient(e,t=`auto`){let n={name:`@agimon-ai/po-mcp-proxy-client`,version:`0.1.0`},r={capabilities:structuredClone(this.clientCapabilities),versionNegotiation:t===`legacy`?{mode:`legacy`}:{mode:`auto`,probe:{timeoutMs:1e4,maxRetries:0}}};return this.outputSchemaValidation?new Yn(n,r,e,this.outputSchemaValidation):new ve(n,r)}async createSdkClientConnection(e,t,n,r){n?.throwIfAborted();let i;if(t.transport===`stdio`)i=await this.connectStdioSdkClient(e,this.createSdkClient(e),t.config,n);else if(t.transport===`http`||t.transport===`sse`)i=await this.connectUrlSdkClient(e,t.transport,t.config,!0,n,r);else{let{transport:e}=t;throw Error(`Unsupported transport type: ${e}`)}if(n?.aborted||this.disposed)throw await G(i),n?.throwIfAborted(),Error(`The MCP client manager is disposed`);return i}async connectUrlSdkClient(e,t,n,r=!0,i,a){i?.throwIfAborted();let o=n.auth,s=typeof o==`object`&&o?.type===`api-key`?o:void 0,c=this.resolveAuthHeaders(n),l;if(s){let r=new URL(n.url),a=s.mode===`bearer`?`Authorization`:s.name;if(s.mode!==`bearer`&&s.mode!==`header`||!/^[!#$%&'*+.^_`|~0-9A-Za-z-]+$/.test(a)||s.mode===`header`&&(a.toLowerCase()===`authorization`||/^(?:host|cookie|set-cookie|connection|keep-alive|te|trailer|upgrade|transfer-encoding|content-length|accept|origin|referer|forwarded|x-forwarded-.*|proxy-.*|sec-.*|mcp-session-id)$/i.test(a))||r.username||r.password||r.protocol!==`https:`&&!(r.protocol===`http:`&&[`localhost`,`127.0.0.1`,`[::1]`].includes(r.hostname))||n.bearerToken||n.bearerTokenEnv||n.oauth!==void 0||Object.keys(c??{}).some(e=>[`authorization`,a.toLowerCase()].includes(e.toLowerCase())))throw Error(`Invalid MCP API-key configuration.`);let o;try{o=await this.resolveUrlCredentials?.({serverName:e,transport:t,url:r.href,auth:s,signal:i})}catch{throw Error(`MCP API-key credential is unavailable.`)}i?.throwIfAborted();let u=o?.[a];if(!u||Object.keys(o??{}).length!==1||Array.from(u).some(e=>e.charCodeAt(0)<=31||e.charCodeAt(0)===127)||s.mode===`bearer`&&!/^Bearer [^\s]+$/.test(u))throw Error(`MCP API-key credential is unavailable.`);c={...c,[a]:u},l=async(e,t)=>{let n=new URL(typeof e==`string`?e:e instanceof URL?e.href:e.url);if(n.origin!==r.origin||n.username||n.password)throw Error(`MCP credential destination was rejected.`);return fetch(e,{...t,redirect:`error`})}}let u=this.createSdkClient(e,t===`sse`?`legacy`:`auto`),d=c?{headers:c}:void 0,f=await this.createAuthProvider(e,n);await f?.prepareCallback(),i?.throwIfAborted();let p=f?new An(f,n.url,i):void 0,m={requestInit:d,authProvider:p,...l?{fetch:l}:{}},h=t===`http`?new Ce(new URL(n.url),m):new Se(new URL(n.url),m),g=()=>{f?.cancelAuthorization(),u.close().catch(t=>this.logger.warn(`Failed to close cancelled client ${e}`,s?`API-key connection closed`:t))};i?.addEventListener(`abort`,g,{once:!0});let _=!1;try{return await u.connect(h),i?.throwIfAborted(),_=!0,{client:u,...p?{recoverAuthorization:(e,t)=>p.recover(e,t)}:{}}}catch(o){if(i?.throwIfAborted(),s)throw Error(`MCP API-key connection failed.`);if(!f||!p||!r||!(o instanceof ye)&&(!tr(o)||!f.hasPendingAuthorization()))throw o;return this.logger.info(`[oauth] Waiting for authorization of MCP server "${e}"`),a?.(!0),await p.recover(o,i,()=>{a?.(!1),this.emitServerState({serverName:e,state:`connecting`})}),i?.throwIfAborted(),await this.connectUrlSdkClient(e,t,n,!1,i,a)}finally{i?.removeEventListener(`abort`,g),_||(f?.cancelAuthorization(),await u.close().catch(t=>this.logger.warn(`Failed to close client ${e}`,s?`API-key connection closed`:t)))}}resolveAuthHeaders(e){let t={...e.headers};if(Tt(e)===`bearer`){let n=e.bearerTokenEnv?this.env?.[e.bearerTokenEnv]??Dt(e):Dt(e);n?t.Authorization=`Bearer ${n}`:this.logger.warn(`[oauth] Bearer auth configured for an MCP server but no token was resolved${e.bearerTokenEnv?` (env ${e.bearerTokenEnv} is unset)`:``}.`)}return Object.keys(t).length>0?t:void 0}async createAuthProvider(e,t){return Ot({serverName:e,config:t,store:this.serverTokenStores.get(e)??this.tokenStore,logger:this.logger,callbackTimeoutMs:this.callbackTimeoutMs,workspaceRoot:this.repositoryPath,...this.callbackServer?{callbackServer:this.callbackServer}:{},onAuthorizationUrl:(e,t)=>{if(this.emitServerState({serverName:t,state:`needs-auth`}),this.onAuthorizationUrl)return this.onAuthorizationUrl(e,t);this.logger.info(`[oauth] Authorize MCP server "${t}" by visiting: ${e.toString()}`)}})}relayChildStderr(e,t){t&&(t.on(`data`,t=>{let n=t.toString(`utf8`).trimEnd();n&&this.logger.debug(`[${e}] ${n}`)}),t.on(`error`,()=>void 0))}async connectStdioSdkClient(e,t,n,r){let i=new ke({command:n.command,args:n.args,cwd:this.executionCwd?y(this.executionCwd,n.cwd??`.`):this.workspaceRoot?y(this.workspaceRoot,n.cwd??`.`):n.cwd,env:{...process.env,...this.env,...n.env,PROJECT_PATH:this.repositoryPath,[oe]:Xn,[ae]:this.processOwnerId},stderr:`pipe`}),a=()=>{G({client:t,stdioTransport:i}).catch(t=>this.logger.warn(`Failed to close cancelled stdio connection for ${e}`,t))};r?.addEventListener(`abort`,a,{once:!0});try{r?.throwIfAborted(),await t.connect(i),r?.throwIfAborted(),this.relayChildStderr(e,i.stderr);let a=i.pid;if(typeof a!=`number`||a<=0)throw Error(`Failed to capture pid for stdio child process on server "${e}"`);let o=await ce({repositoryPath:this.repositoryPath,serviceName:e,serviceType:`tool`,pid:a,command:n.command,args:n.args,metadata:{transport:`stdio`},ownerKind:Xn,ownerId:this.processOwnerId,resourceKey:e});return t.onclose=()=>{let t=this.clients.get(e);t instanceof or&&t.handleStdioClosed(i)},{client:t,processLease:o,stdioTransport:i}}catch(n){try{await G({client:t,stdioTransport:i})}catch(t){this.logger.warn(`Failed to clean up incomplete stdio connection for ${e}`,t)}throw n}finally{r?.removeEventListener(`abort`,a)}}getClient(e){return this.clients.get(e)}getAllClients(){return Array.from(this.clients.values())}async disconnectServer(e){this.connectionControllers.get(e)?.abort(/* @__PURE__ */ Error(`MCP connection '${e}' was closed`));let t=this.connectionPromises.get(e);this.connectionPromises.delete(e);let n=this.clients.get(e);this.clients.delete(e),await Promise.all([t?.catch(()=>void 0),n?.close()])}async disconnectAll(){let e=[...this.clients.values()],t=[...this.connectionPromises.values()];this.clients.clear(),this.connectionPromises.clear();for(let e of this.connectionControllers.values())e.abort(/* @__PURE__ */ Error(`MCP connections were closed`));this.connectionControllers.clear();let n=(await Promise.allSettled([...e.map(e=>e.close()),...t.map(e=>e.catch(()=>void 0))])).flatMap(e=>e.status===`rejected`?[e.reason]:[]);if(n.length)throw AggregateError(n,`Failed to close MCP connections`)}dispose(){return this.disposed=!0,this.disposePromise??=this.disconnectAll(),this.disposePromise}isConnected(e){return this.clients.get(e)?.getLifecycleState()===`connected`}getConnectionState(e){return this.clients.get(e)?.getLifecycleState()}};const cr=`install`,lr=/^(@[a-zA-Z0-9_-]+\/)?[a-zA-Z0-9._-]+(@[a-zA-Z0-9._-]+)?$/,ur=`pipe`;function dr(e){return typeof e==`object`&&!!e&&`command`in e}var fr=class{config;constructor(e){this.config=e}extractPackages(){let e=[],{mcpConfig:t,filter:n}=this.config;for(let[r,i]of Object.entries(t.mcpServers)){if(i.disabled||i.transport!==`stdio`||!dr(i.config))continue;let t=this.extractPackageInfo(r,i.config);if(t){if(n&&t.packageManager!==n)continue;e.push(t)}}return e}async prefetch(){try{let e=this.extractPackages(),t=[];if(e.length===0)return{totalPackages:0,successful:0,failed:0,results:[]};if(this.config.parallel){let n=e.map(async e=>this.prefetchPackage(e));t.push(...await Promise.all(n))}else for(let n of e){let e=await this.prefetchPackage(n);t.push(e)}let n=t.filter(e=>e.success).length,r=t.filter(e=>!e.success).length;return{totalPackages:e.length,successful:n,failed:r,results:t}}catch(e){throw Error(`Failed to prefetch packages: ${e instanceof Error?e.message:String(e)}`)}}async prefetchPackage(e){try{let[t,...n]=e.fullCommand,r=await this.runCommand(t,n);return{package:e,success:r.success,output:r.output}}catch(t){return{package:e,success:!1,output:t instanceof Error?t.message:String(t)}}}isValidPackageName(e){return lr.test(e)}extractPackageInfo(e,t){let n=t.command.toLowerCase(),r=t.args||[];if(n===`npx`||n.endsWith(`/npx`)){let t=this.extractNpxPackage(r);if(t&&this.isValidPackageName(t))return{serverName:e,packageManager:`npx`,packageName:t,fullCommand:[`npm`,cr,`-g`,t]}}if(n===`pnpx`||n.endsWith(`/pnpx`)){let t=this.extractNpxPackage(r);if(t&&this.isValidPackageName(t))return{serverName:e,packageManager:`pnpx`,packageName:t,fullCommand:[`pnpm`,`add`,`-g`,t]}}if(n===`uvx`||n.endsWith(`/uvx`)){let t=this.extractUvxPackage(r);if(t&&this.isValidPackageName(t))return{serverName:e,packageManager:`uvx`,packageName:t,fullCommand:[`uvx`,t]}}if((n===`uv`||n.endsWith(`/uv`))&&r.includes(`run`)){let t=this.extractUvRunPackage(r);if(t&&this.isValidPackageName(t))return{serverName:e,packageManager:`uv`,packageName:t,fullCommand:[`uv`,`tool`,cr,t]}}return null}extractNpxPackage(e){for(let t=0;t<e.length;t++){let n=e[t];if(n.startsWith(`--package=`))return n.slice(10)||null;if(n===`--package`&&t+1<e.length){let n=e[t+1];if(!n.startsWith(`-`))return n}if(n===`-p`&&t+1<e.length){let n=e[t+1];if(!n.startsWith(`-`))return n}}for(let t of e)if(!t.startsWith(`-`))return t;return null}extractUvxPackage(e){for(let t of e)if(!t.startsWith(`-`))return t;return null}extractUvRunPackage(e){let t=e.indexOf(`run`);if(t===-1)return null;for(let n=t+1;n<e.length;n++){let t=e[n];if(!t.startsWith(`-`))return t}return null}runCommand(e,t){return new Promise(n=>{let r=re(e,t,{stdio:[`ignore`,ur,ur],shell:process.platform===`win32`}),i=``,a=``;r.stdout?.on(`data`,e=>{i+=e.toString()}),r.stderr?.on(`data`,e=>{a+=e.toString()}),r.on(`close`,e=>{n({success:e===0,output:i||a})}),r.on(`error`,e=>{n({success:!1,output:e.message})})})}};const pr=`.runtime.json`;async function mr(e){await r(e,{recursive:!0,mode:448});try{await t(e,448)}catch{}}function hr(e){return typeof e==`object`&&!!e}function gr(e){return hr(e)?typeof e.serverId==`string`&&typeof e.host==`string`&&typeof e.port==`number`&&e.transport===`http`&&typeof e.shutdownToken==`string`&&typeof e.startedAt==`string`&&typeof e.pid==`number`&&(e.configPath===void 0||typeof e.configPath==`string`)&&(e.runtimeLifecycle===void 0||e.runtimeLifecycle===`manual`||e.runtimeLifecycle===`stdio-http-managed`):!1}function _r(e){return e instanceof Error?e.message:String(e)}function vr(e){try{return process.kill(e,0),!0}catch{return!1}}var K=class e{runtimeDir;logger;constructor(t,n=console){this.runtimeDir=t??e.getDefaultRuntimeDir(),this.logger=n}static getDefaultRuntimeDir(){return v(T(),`.mcp-proxy`,`runtimes`)}getRecordPath(e){return v(this.runtimeDir,`${e}${pr}`)}async write(e){await mr(this.runtimeDir);let t=this.getRecordPath(e.serverId);await m(t,JSON.stringify(e,null,2),{encoding:`utf-8`,mode:384})}async read(e){let t=this.getRecordPath(e);try{let e=await o(t,`utf-8`),n=JSON.parse(e);return gr(n)?n:null}catch(n){if(hr(n)&&`code`in n&&n.code===`ENOENT`)return null;throw Error(`Failed to read runtime state for server '${e}' from '${t}': ${_r(n)}`)}}async list(){try{let e=(await s(this.runtimeDir,{withFileTypes:!0})).filter(e=>e.isFile()&&e.name.endsWith(pr));return(await Promise.all(e.map(async e=>{try{let t=await o(v(this.runtimeDir,e.name),`utf-8`),n=JSON.parse(t);return gr(n)?n:null}catch(t){return this.logger.debug(`Skipping unreadable runtime state file ${e.name}`,t),null}}))).filter(e=>e!==null)}catch(e){if(hr(e)&&`code`in e&&e.code===`ENOENT`)return[];throw Error(`Failed to list runtime states from '${this.runtimeDir}': ${_r(e)}`)}}async listOrphans(e){return(await this.list()).filter(t=>!e.has(t.serverId))}async pruneStale(){let e=(await this.list()).filter(e=>!vr(e.pid));return await Promise.all(e.map(async e=>this.remove(e.serverId))),e}async remove(e){await l(this.getRecordPath(e),{force:!0})}},yr=class extends Error{filePath;cause;constructor(e,t,n){super(e),this.filePath=t,this.cause=n,this.name=`SkillLoadError`}};async function br(t){try{return await e(t),!0}catch(e){if(e instanceof Error&&`code`in e&&e.code===`ENOENT`)return!1;throw Error(`Failed to check path existence for "${t}": ${e instanceof Error?e.message:`Unknown error`}`)}}var xr=class{cwd;skillPaths;cachedSkills=null;skillsByName=null;watchers=[];pollingTimers=[];onCacheInvalidated;logger;constructor(e,t,n,r=console){this.cwd=e,this.skillPaths=t,this.onCacheInvalidated=n?.onCacheInvalidated,this.logger=r}async getSkills(){if(this.cachedSkills!==null)return this.cachedSkills;let e=[],t=/* @__PURE__ */ new Set,n=await Promise.all(this.skillPaths.map(async e=>{let t=_(e)?e:v(this.cwd,e);return this.loadSkillsFromDirectory(t,`project`)}));for(let r of n)for(let n of r)t.has(n.name)||(e.push(n),t.add(n.name));return this.cachedSkills=e,this.skillsByName=new Map(e.map(e=>[e.name,e])),e}async getSkill(e){return this.skillsByName===null&&await this.getSkills(),this.skillsByName?.get(e)}clearCache(){this.cachedSkills=null,this.skillsByName=null}async startWatching(){this.stopWatching();let e=await Promise.all(this.skillPaths.map(async e=>{let t=_(e)?e:v(this.cwd,e);return{skillsDir:t,exists:await br(t)}}));for(let{skillsDir:t,exists:n}of e){if(!n)continue;let e=new AbortController;this.watchers.push(e),this.watchDirectory(t,e.signal).catch(n=>{if(n?.name!==`AbortError`){if(this.isWatchResourceLimitError(n)){this.startPollingDirectory(t,e.signal);return}this.logger.warn(`[skill-watcher] Error watching ${t}: ${n instanceof Error?n.message:`Unknown error`}`)}})}}stopWatching(){for(let e of this.watchers)e.abort();this.watchers=[];for(let e of this.pollingTimers)clearInterval(e);this.pollingTimers=[]}async watchDirectory(e,t){let n=p(e,{recursive:!0,signal:t});for await(let e of n)e.filename?.endsWith(`SKILL.md`)&&this.invalidateCache()}invalidateCache(){this.clearCache(),this.onCacheInvalidated?.()}isWatchResourceLimitError(e){return e instanceof Error&&`code`in e&&(e.code===`EMFILE`||e.code===`ENOSPC`)}startPollingDirectory(e,t){this.createSkillSnapshot(e).then(n=>{let r=n,i=setInterval(()=>{t.aborted?clearInterval(i):this.createSkillSnapshot(e).then(e=>{this.snapshotsEqual(r,e)||(r=e,this.invalidateCache())}).catch(t=>{this.logger.warn(`[skill-watcher] Polling failed for ${e}: ${t instanceof Error?t.message:`Unknown error`}`)})},3e3);this.pollingTimers.push(i),t.addEventListener(`abort`,()=>{clearInterval(i),this.pollingTimers=this.pollingTimers.filter(e=>e!==i)},{once:!0})})}async createSkillSnapshot(e){let t=/* @__PURE__ */ new Map;return await this.collectSkillSnapshots(e,t),t}async collectSkillSnapshots(e,t){let n;try{n=await s(e)}catch(e){if(e instanceof Error&&`code`in e&&e.code===`ENOENT`)return;throw e}await Promise.all(n.map(async n=>{let r=v(e,n),i=await u(r);i.isDirectory()?await this.collectSkillSnapshots(r,t):n===`SKILL.md`&&t.set(r,i.mtimeMs)}))}snapshotsEqual(e,t){if(e.size!==t.size)return!1;for(let[n,r]of e)if(t.get(n)!==r)return!1;return!0}async loadSkillsFromDirectory(e,t){let n=[];try{if(!await br(e))return n}catch(t){throw new yr(`Cannot access skills directory: ${t instanceof Error?t.message:`Unknown error`}`,e,t instanceof Error?t:void 0)}let r;try{r=await s(e)}catch(t){throw new yr(`Failed to read skills directory: ${t instanceof Error?t.message:`Unknown error`}`,e,t instanceof Error?t:void 0)}let i=await Promise.all(r.map(async t=>{let n=v(e,t);try{return{entry:t,entryPath:n,stat:await u(n),error:null}}catch(e){return this.logger.warn(`Skipping entry ${n}: ${e instanceof Error?e.message:`Unknown error`}`),{entry:t,entryPath:n,stat:null,error:e}}})),a=[];for(let{entry:e,entryPath:t,stat:n}of i)if(n){if(n.isDirectory()){let e=v(t,`SKILL.md`);a.push({filePath:e,isRootLevel:!1})}else e===`SKILL.md`&&a.push({filePath:t,isRootLevel:!0})}let o=await Promise.all(a.map(async({filePath:e,isRootLevel:n})=>{try{return!n&&!await br(e)?null:await this.loadSkillFile(e,t)}catch(t){return this.logger.warn(`Skipping skill at ${e}: ${t instanceof Error?t.message:`Unknown error`}`),null}}));for(let e of o)e&&n.push(e);return n}async loadSkillFile(e,t){let n;try{n=await o(e,`utf-8`)}catch(t){throw new yr(`Failed to read skill file: ${t instanceof Error?t.message:`Unknown error`}`,e,t instanceof Error?t:void 0)}let{frontMatter:r,content:i}=hn(n);return!r||!r.name||!r.description?null:{name:r.name,description:r.description,location:t,content:i,basePath:g(e)}}};const Sr=/* @__PURE__ */ new Set([`localhost`,`127.0.0.1`,`::1`]);function Cr(e){return e}function wr(e){if(typeof e!=`object`||!e)return!1;let t=Cr(e);return`status`in t&&t.status===`ok`&&`transport`in t&&t.transport===`http`&&(!(`serverId`in t)||t.serverId===void 0||typeof t.serverId==`string`)&&(!(`runtimeLifecycle`in t)||t.runtimeLifecycle===void 0||t.runtimeLifecycle===`manual`||t.runtimeLifecycle===`stdio-http-managed`)}function Tr(e){if(typeof e!=`object`||!e)return!1;let t=Cr(e);return`ok`in t&&typeof t.ok==`boolean`&&`message`in t&&typeof t.message==`string`&&(!(`serverId`in t)||t.serverId===void 0||typeof t.serverId==`string`)}function Er(e,t){if(!Sr.has(e.host))throw Error(`Refusing to connect to non-loopback host '${e.host}'. Only ${Array.from(Sr).join(`, `)} are allowed.`);return`http://${e.host}:${e.port}${t}`}function Dr(e){return e instanceof Error?e.message:String(e)}function Or(e){return new Promise(t=>{setTimeout(t,e)})}var kr=class{runtimeStateService;logger;constructor(e=new K,t=console){this.runtimeStateService=e,this.logger=t}async stop(e){let t=e.timeoutMs??5e3;await this.pruneStaleRuntimeRecords();let n=await this.resolveRuntime(e),r=await this.fetchHealth(n,t);if(!r.reachable)throw await this.runtimeStateService.remove(n.serverId),Error(`Runtime '${n.serverId}' is not reachable at http://${n.host}:${n.port}. Removed stale runtime record.`);if(!e.force&&r.payload?.serverId&&r.payload.serverId!==n.serverId)throw Error(`Refusing to stop runtime at http://${n.host}:${n.port}: expected server ID '${n.serverId}' but health endpoint reported '${r.payload.serverId}'. Use --force to override.`);let i=e.token??n.shutdownToken;if(!i)throw Error(`No shutdown token available for runtime '${n.serverId}'.`);let a=await this.requestShutdown(n,i,t);return await this.waitForShutdown(n,t),await this.runtimeStateService.remove(n.serverId),{ok:!0,serverId:n.serverId,host:n.host,port:n.port,message:a.message}}async resolveRuntime(e){if(e.serverId){let t=await this.runtimeStateService.read(e.serverId);if(!t)throw Error(`No runtime record found for server ID '${e.serverId}'. Start the server with 'po-mcp-proxy mcp-serve --type http' first.`);return t}if(e.host===void 0||e.port===void 0)throw Error(`Provide --id or both --host and --port to select a runtime.`);let t=(await this.runtimeStateService.list()).filter(t=>t.host===e.host&&t.port===e.port);if(t.length===0)throw Error(`No runtime record found for http://${e.host}:${e.port}. Start the server with 'po-mcp-proxy mcp-serve --type http' first.`);if(t.length>1)throw Error(`Multiple runtime records match http://${e.host}:${e.port}. Retry with --id to avoid stopping the wrong server.`);return t[0]}async pruneStaleRuntimeRecords(){if(this.runtimeStateService.pruneStale)try{await this.runtimeStateService.pruneStale()}catch(e){this.logger.debug(`Failed to prune stale runtime records before stop resolution`,e)}}async fetchHealth(e,t){try{let n=await this.fetchWithTimeout(Er(e,`/health`),{method:`GET`},t);if(!n.ok)return{reachable:!1};let r=await n.json();if(!wr(r))throw Error(`Received invalid health response payload.`);return{reachable:!0,payload:r}}catch(t){return this.logger.debug(`Health check failed for ${e.serverId}`,t),{reachable:!1}}}async requestShutdown(e,t,n){let r=await this.fetchWithTimeout(Er(e,`/admin/shutdown`),{method:`POST`,headers:{Authorization:`Bearer ${t}`}},n),i=await r.json();if(!Tr(i))throw Error(`Received invalid shutdown response payload.`);if(!r.ok||!i.ok)throw Error(i.message);return i}async waitForShutdown(e,t){let n=Date.now()+t;for(;Date.now()<n;){if(!(await this.fetchHealth(e,Math.max(250,n-Date.now()))).reachable)return;await Or(200)}throw Error(`Timed out waiting for runtime '${e.serverId}' to stop at http://${e.host}:${e.port}.`)}async fetchWithTimeout(e,t,n){let r=new AbortController,i=setTimeout(()=>{r.abort()},n);try{return await fetch(e,{...t,signal:r.signal})}catch(t){throw Error(`Request to '${e}' failed: ${Dr(t)}`)}finally{clearTimeout(i)}}};function q(e){return e instanceof Error?e.message:String(e)}function Ar(e,t){let n=Buffer.from(e,`utf-8`),r=Buffer.from(t,`utf-8`);return n.length===r.length&&ee(n,r)}var jr=class{requests=/* @__PURE__ */ new Map;isAllowed(e){let t=Date.now()-6e4,n=(this.requests.get(e)??[]).filter(e=>e>t);return n.length>=5?(this.requests.set(e,n),!1):(n.push(Date.now()),this.requests.set(e,n),!0)}},Mr=class{honoApp=new Le;adminRateLimiter=new jr;config;mcpHandler;nodeMcpHandler;adminOptions;logger;server=null;lastRequestAt=Date.now();activeRequests=0;idleTimer=null;constructor(e,t,n,r=console){this.config={mode:t.mode,host:t.host??`localhost`,port:t.port},this.adminOptions=n,this.logger=r,this.mcpHandler=me(e,{legacy:`reject`,onerror:e=>this.logger.error(`MCP handler failed: ${e.message}`)}),this.nodeMcpHandler=Ie(this.mcpHandler,{onerror:e=>this.logger.error(`Failed to adapt MCP request: ${e.message}`)}),this.setupHonoRoutes()}setupHonoRoutes(){this.honoApp.get(`/health`,e=>{let t={status:`ok`,transport:`http`,serverId:this.adminOptions?.serverId,runtimeLifecycle:this.adminOptions?.runtimeLifecycle,protocolVersion:`2026-07-28`,runtimeEpoch:`modern-2026-07-28-v1`};return e.json(t)}),this.honoApp.post(`/admin/shutdown`,async e=>{try{let t=e.req.header(`x-forwarded-for`)??`unknown`;if(!this.adminRateLimiter.isAllowed(t)){let t={ok:!1,message:`Too many shutdown requests. Try again later.`,serverId:this.adminOptions?.serverId};return e.json(t,429)}return await this.handleAdminShutdownRequest(e)}catch(t){this.logger.error(`Failed to process shutdown request: ${q(t)}`);let n={ok:!1,message:`Failed to process shutdown request.`,serverId:this.adminOptions?.serverId};return e.json(n,500)}})}isAuthorizedShutdownRequest(e){let t=this.adminOptions?.shutdownToken;if(!t)return!1;let n=e.authorization;if(typeof n==`string`&&n.startsWith(`Bearer `))return Ar(n.slice(7),t);let r=e[`x-mcp-proxy-shutdown-token`];return typeof r==`string`&&Ar(r,t)}async handleAdminShutdownRequest(e){if(!this.adminOptions?.onShutdownRequested)return e.json({ok:!1,message:`Shutdown endpoint is not enabled for this server instance.`,serverId:this.adminOptions?.serverId},404);if(!this.isAuthorizedShutdownRequest({authorization:e.req.header(`authorization`),"x-mcp-proxy-shutdown-token":e.req.header(`x-mcp-proxy-shutdown-token`)}))return e.json({ok:!1,message:`Unauthorized shutdown request: invalid or missing shutdown token.`,serverId:this.adminOptions?.serverId},401);let t=e.json({ok:!0,message:`Shutdown request accepted. Stopping server gracefully.`,serverId:this.adminOptions?.serverId});return this.adminOptions.onShutdownRequested().catch(e=>{this.logger.error(`Shutdown request failed: ${q(e)}`)}),t}async handleMcpRequest(e,t){this.activeRequests+=1;try{await this.nodeMcpHandler(e,t)}catch(e){this.logger.error(`MCP request failed: ${q(e)}`),t.headersSent||(t.writeHead(500,{"Content-Type":`application/json`}),t.end(JSON.stringify({jsonrpc:`2.0`,error:{code:-32603,message:`MCP request failed.`},id:null})))}finally{--this.activeRequests,this.lastRequestAt=Date.now()}}startIdleShutdownMonitor(){let e=this.adminOptions?.idleShutdownMs??0;e<=0||this.idleTimer||(this.lastRequestAt=Date.now(),this.idleTimer=setInterval(()=>{this.shutdownWhenIdle(e)},Math.min(e,6e4)))}stopIdleShutdownMonitor(){this.idleTimer&&=(clearInterval(this.idleTimer),null)}async shutdownWhenIdle(e){let t=this.adminOptions?.onShutdownRequested;if(!(!t||this.activeRequests>0||Date.now()-this.lastRequestAt<e)){this.stopIdleShutdownMonitor(),this.logger.info(`Stopping idle HTTP runtime after ${e}ms without active requests`);try{await t()}catch(e){this.logger.error(`Failed to stop idle HTTP runtime: ${q(e)}`)}}}async start(){if(this.server)throw Error(`HTTP transport is already running`);let e=Fe(this.honoApp.fetch),t=te((t,n)=>{this.lastRequestAt=Date.now(),new URL(t.url??`/`,`http://${t.headers.host??this.config.host}`).pathname===`/mcp`?this.handleMcpRequest(t,n):e(t,n)});try{t.listen(this.config.port,this.config.host),await Promise.race([Me(t,`listening`),Me(t,`error`).then(([e])=>Promise.reject(e))]),this.server=t,this.startIdleShutdownMonitor(),this.logger.info(`@agimon-ai/po-mcp-proxy MCP server started on http://${this.config.host}:${this.config.port}/mcp`),this.logger.info(`Health check: http://${this.config.host}:${this.config.port}/health`)}catch(e){throw t.close(),Error(`Failed to start HTTP transport: ${q(e)}`,{cause:e})}}async stop(){this.stopIdleShutdownMonitor();let e=this.server;if(e){this.server=null,await this.mcpHandler.close();try{await Pe(e.close.bind(e))()}catch(e){throw Error(`Failed to stop HTTP transport: ${q(e)}`,{cause:e})}}}getPort(){let e=this.server?.address();return e&&typeof e==`object`?e.port:this.config.port}getHost(){return this.config.host}};const Nr=`localhost`,Pr=12e4,Fr=`MCP_PROXY_RUNTIME_DETACHED`;function Ir(){return process.env[Fr]===`1`}async function Lr(e){return(await new se(process.env.PROCESS_REGISTRY_PATH).listProcesses({repositoryPath:e,serviceName:`mcp-proxy-http`,liveOnly:!0})).filter(e=>!!(e.host&&e.port)).map(e=>{let t=e.metadata;return{host:e.host,port:e.port,serverId:t?.serverId??`unknown`,runtimeLifecycle:t?.runtimeLifecycle===`stdio-http-managed`||t?.runtimeLifecycle===`manual`?t.runtimeLifecycle:void 0,createdAt:e.createdAt}}).sort((e,t)=>(e.createdAt??``).localeCompare(t.createdAt??``))}async function Rr(e){let t=await Lr(e);for(let e of t)if(zr(await Br(e.host,e.port)))return e;return null}function zr(e){return e.ok&&e.payload?.status===`ok`&&e.payload.transport===`http`&&e.payload.protocolVersion===`2026-07-28`&&e.payload.runtimeEpoch===`modern-2026-07-28-v1`}async function Br(e,t,n={}){let r=n.timeoutMs??1500,i=n.retries??2,a=n.retryDelayMs??150,o=i+1,s=`unknown error`;for(let n=1;n<=o;n+=1){try{let i=await fetch(`http://${e}:${t}/health`,{signal:AbortSignal.timeout(r)});if(i.ok)return{ok:!0,payload:await i.json().catch(()=>null)??void 0,attempts:n};s=`health endpoint returned status ${i.status}`}catch(e){s=e instanceof Error?e.message:String(e)}n<o&&await new Promise(e=>setTimeout(e,a))}return{ok:!1,error:s,attempts:o}}async function Vr(){let t=ie(import.meta.url),n=h.dirname(t),r=[h.resolve(n,`cli.mjs`),h.resolve(n,`..`,`dist`,`cli.mjs`),h.resolve(n,`..`,`..`,`dist`,`cli.mjs`)],i=[h.resolve(n,`..`,`cli.ts`),h.resolve(n,`..`,`..`,`src`,`cli.ts`)];for(let t of r)try{return await e(t),{command:process.execPath,args:[t]}}catch{continue}for(let t of i)try{return await e(t),{command:process.execPath,args:[`--import`,`tsx`,t]}}catch{continue}throw Error(`Unable to locate mcp-proxy CLI entrypoint`)}function Hr(e){if(!e)return Pr;let t=Number.parseInt(e,10);if(!Number.isInteger(t)||t<=0)throw Error(`Invalid timeout value: ${e}`);return t}async function Ur(t,n){let r=Date.now()+n;for(;Date.now()<r;){try{await e(t);return}catch{}await new Promise(e=>setTimeout(e,250))}throw Error(`Timed out waiting for runtime state file: ${t}`)}async function Wr(e,t){let n=new K,r=Date.now()+t;for(;Date.now()<r;){let t=await n.read(e);if(t&&zr(await Br(t.host,t.port,{retries:0})))return{host:t.host,port:t.port};await new Promise(e=>setTimeout(e,250))}throw Error(`Timed out waiting for HTTP runtime '${e}' to become healthy`)}async function Gr(e,t,n){let{command:r,args:i}=await Vr(),a=re(r,[...i,...e],{detached:!0,cwd:n,stdio:`ignore`,env:t});return a.unref(),a}async function Kr(e){let t=e.id||mn(),n=Hr(e.timeoutMs),r=e.registryPath||e.registryDir,i=await A();await new K().pruneStale();let a=await Rr(i);if(a)return{host:a.host,port:a.port,serverId:a.serverId,workspaceRoot:i,reusedExistingRuntime:!0,runtimeLifecycle:a.runtimeLifecycle??`manual`};let o=e.port,s=await Lr(i);if(e.id&&s.some(e=>e.serverId===t))throw Error(`Runtime ID '${t}' belongs to an incompatible HTTP runtime; choose a new ID`);if(o!==void 0&&s.some(t=>t.host===(e.host||Nr)&&t.port===o))throw Error(`Port ${o} belongs to a different HTTP runtime; choose a free port`);let c=o,l={...process.env,[Fr]:`1`,...e.runtimeLifecycle?{MCP_PROXY_RUNTIME_LIFECYCLE:e.runtimeLifecycle}:{},...r?{PORT_REGISTRY_PATH:r,PROCESS_REGISTRY_PATH:le(r,`processes.json`)}:{}},u=await Gr([`mcp-serve`,`--type`,`http`,`--id`,t,`--host`,e.host||Nr,...c===void 0?[]:[`--port`,String(c)],...dn(e.config).flatMap(e=>[`--config`,e]),...e.cache===!1?[`--no-cache`]:[],...e.definitionsCache?[`--definitions-cache`,e.definitionsCache]:[],...e.clearDefinitionsCache?[`--clear-definitions-cache`]:[],...e.idleShutdownMinutes===void 0?[]:[`--idle-shutdown-minutes`,String(e.idleShutdownMinutes)],`--proxy-mode`,e.proxyMode],l,i),d=new Promise((e,t)=>{u.once(`exit`,(e,n)=>{t(/* @__PURE__ */ Error(`Background runtime exited before becoming healthy (code=${e??`null`}, signal=${n??`null`})`))})}),f=h.join(K.getDefaultRuntimeDir(),`${t}.runtime.json`);try{await Promise.race([Ur(f,n),d]);let{host:r,port:a}=await Promise.race([Wr(t,n),d]);return{host:r,port:a,serverId:t,workspaceRoot:i,reusedExistingRuntime:!1,runtimeLifecycle:e.runtimeLifecycle??`manual`}}catch(e){throw Error(`Failed to prestart HTTP runtime '${t}': ${e instanceof Error?e.message:String(e)}`,{cause:e})}}function qr(e){process.stdout.write(`mcp-proxy HTTP runtime ready at http://${e.host}:${e.port} (${e.serverId})\n`),process.stdout.write(`runtimeId=${e.serverId}\n`),process.stdout.write(`runtimeUrl=http://${e.host}:${e.port}\n`),process.stdout.write(`workspaceRoot=${e.workspaceRoot}\n`)}var Jr=`0.0.1-alpha.35`,Yr=`<toolkit id="{{ serverId }}">
|
|
9
|
+
`).length}}function U(e){return Buffer.byteLength(e,`utf8`)}function Gn(e){if(typeof e!=`number`||!Number.isFinite(e))return;let t=Math.floor(e);return t>0?t:void 0}function Kn(e){let t=process.env[e]?.trim().toLowerCase();if(t){if([`0`,`false`,`no`,`off`].includes(t))return!1;if([`1`,`true`,`yes`,`on`].includes(t))return!0}}function qn(e){return e<1024?`${e} B`:e<1048576?`${(e/1024).toFixed(1)} KiB`:`${(e/1048576).toFixed(1)} MiB`}const W=`x-mcp-proxy-original-output-schema`;function Jn(e){return Array.from(e.slice(0,256)).filter(e=>{let t=e.charCodeAt(0);return t>=32&&t<=126||t>=160}).join(``)}var Yn=class extends ve{serverName;compatibility;calls;constructor(e,t,n,r){let i=new Ae,a=new je;super(e,{...t,jsonSchemaValidator:{getValidator:e=>t=>{let n=i.getStore()?.toolName,r=Object.hasOwn(e,W)?e[W]:e;try{!a.getValidator(r)(t).valid&&n&&this.warn(n,`tools/call`,`structuredContent`,`Structured output does not match its output schema.`)}catch{n&&this.warn(n,`tools/call`,`outputSchema`,`Output schema could not be compiled or evaluated.`)}return{valid:!0,data:t,errorMessage:void 0}}}}),this.serverName=n,this.compatibility=r,this.calls=i}warn(e,t,n,r){try{Promise.resolve(this.compatibility.onWarning({serverName:Jn(this.serverName),toolName:Jn(e),method:t,path:n,message:r})).catch(()=>{})}catch{}}request(e,t,n){if(t&&`~standard`in t)return super.request(e,t,n);if(e.method!==`tools/list`){let n=super.request(e,t);return e.method===`tools/call`?n.then(e=>{let t=this.calls.getStore();return t&&(t.result=e),e}):n}return super.request(e,{"~standard":{version:1,vendor:`mcp-proxy-output-compatibility`,validate:e=>this.validateListing(e)}},t)}validateListing(e){if(!e||typeof e!=`object`||!(`tools`in e)||!Array.isArray(e.tools))return{issues:[{message:`Invalid tools/list result`}]};let t=e.tools,n=t.map(e=>{if(!e||typeof e!=`object`)return e;let{outputSchema:t,...n}=e;return n}),r=this._wireCodec().validateResult(`tools/list`,{...e,tools:n});if(!r.ok)return{issues:[{message:`Invalid non-output tools/list descriptor`}]};for(let[e,n]of r.value.tools.entries()){let r=t[e];Object.hasOwn(r,`outputSchema`)&&(n.outputSchema={type:`object`,[W]:r.outputSchema},this._wireCodec().validateResult(`tools/list`,{tools:[r]}).ok||this.warn(n.name,`tools/list`,`tools/${e}/outputSchema`,`Output schema descriptor is not MCP-compatible.`))}return{value:r.value}}async listTools(e,t){let n=await super.listTools(e,t);return{...n,tools:n.tools.map(e=>e.outputSchema&&Object.hasOwn(e.outputSchema,W)?{...e,outputSchema:e.outputSchema[W]}:e)}}async callTool(e,t){let n={toolName:e.name,result:void 0};return this.calls.run(n,async()=>{try{return await super.callTool(e,t)}catch(t){if(!(t instanceof be)||t.code!==xe.InvalidRequest||t.message!==`Tool ${e.name} has an output schema but did not return structured content`||!n.result||n.result.structuredContent!==void 0||n.result.isError)throw t;return this.warn(e.name,`tools/call`,`structuredContent`,`Tool omitted structured output required by its output schema.`),n.result}})}};const Xn=`mcp-proxy-client-manager`,Zn=[0,100];function Qn(e){return e===`reconnecting`?`connecting`:e}var $n=class extends Error{serverName;attempts;code=`MCP_SERVER_RECOVERY_EXHAUSTED`;constructor(e,t,n){super(`MCP server "${e}" did not recover after ${t} attempts.`,n),this.serverName=e,this.attempts=t,this.name=`McpServerRecoveryExhaustedError`}};async function G(e){let t=await Promise.allSettled([Promise.resolve().then(()=>e.client.close()),Promise.resolve().then(()=>e.stdioTransport?.close())]);try{await e.processLease?.release({kill:!1,releasePort:!1})}catch(e){t.push({status:`rejected`,reason:e})}let n=t.flatMap(e=>e.status===`rejected`?[e.reason]:[]);if(n.length)throw AggregateError(n,`MCP connection cleanup failed`)}function er(e){return ar(e).some(({message:e,code:t})=>{let n=e.toLowerCase(),r=rr(t);return n.includes(`unknown session`)||n.includes(`session not found`)||n.includes(`transport closed`)||n.includes(`connection closed`)||n.includes(`socket hang up`)||n.includes(`fetch failed`)||r===`econnreset`})}function tr(e){return e instanceof O||ar(e).some(({message:e,code:t})=>{let n=e.toLowerCase();return t===`401`||n.includes(`unauthorized`)||n.includes(`http 401`)||n.includes(`invalid_token`)})}function nr(e){return ar(e).some(({message:e,code:t})=>{let n=e.toLowerCase(),r=rr(t);return n.includes(`transport closed`)||n.includes(`connection closed`)||n.includes(`premature close`)||n.includes(`socket hang up`)||n.includes(`write epipe`)||n.includes(`read econnreset`)||n.includes(`fetch failed`)||r===`epipe`||r===`econnreset`})}function rr(e){return typeof e==`string`?e.toLowerCase():void 0}function ir(e){if(typeof e==`string`)return e;if(typeof e==`symbol`)return e.toString();if(typeof e==`object`&&e)try{return JSON.stringify(e)??`Unknown error`}catch{return`Unknown error`}return String(e)}function ar(e){let t=/* @__PURE__ */ new Set,n=[],r=e;for(;r&&!t.has(r);)if(t.add(r),r instanceof Error){let e=r;n.push({message:r.message,code:e.code}),r=e.cause}else if(typeof r==`object`){let e=r;n.push({message:typeof e.message==`string`?e.message:ir(r),code:typeof e.code==`string`?e.code:void 0}),r=e.cause}else{n.push({message:ir(r)});break}return n}var or=class{outputGuard;serverName;serverInstruction;toolBlacklist;omitToolDescription;prompts;transport;logger;client;stdioTransport;processLease;connected=!1;lifecycleState=`closed`;recoveryError;reconnectFn;reconnectPromise;recoverAuthorization;authorizationRecovery;pendingLeaseRelease;onDisconnected;onLifecycleChange;closing=!1;closePromise;lifetime=new AbortController;constructor(e,t,n,r,i,a){this.outputGuard=a,this.serverName=e,this.serverInstruction=i.instruction,this.toolBlacklist=i.toolBlacklist,this.omitToolDescription=i.omitToolDescription,this.prompts=i.prompts,this.transport=t,this.logger=r,this.client=n}setAuthorizationRecovery(e){this.recoverAuthorization=e}setStdioTransport(e){this.stdioTransport=e}setProcessLease(e){this.processLease=e}setConnected(e){this.connected=e,this.setLifecycleState(e?`connected`:`closed`),e&&(this.recoveryError=void 0)}getLifecycleState(){return this.lifecycleState}setLifecycleChangeCallback(e){this.onLifecycleChange=e}setLifecycleState(e,t){this.lifecycleState!==e&&(this.lifecycleState=e,this.onLifecycleChange?.(e,t))}async waitUntilReady(){await this.ensureReady()}getServerCapabilities(){return this.client.getServerCapabilities()}setReconnectFn(e){this.reconnectFn=e}setDisconnectedCallback(e){this.onDisconnected=e}handleStdioClosed(e){if(this.stdioTransport!==e||this.closing)return;let t=this.processLease;this.stdioTransport=void 0,this.processLease=void 0,this.connected=!1,this.recoveryError=void 0,this.setLifecycleState(`reconnecting`),this.pendingLeaseRelease=this.releaseProcessLease(t),!this.closing&&(this.logger.warn(`mcp_proxy.server.disconnected`,{"mcp.server.name":this.serverName,"mcp.server.transport":this.transport,"mcp.server.lifecycle":this.lifecycleState,"mcp.server.disconnect_reason":`stdio transport closed`,"mcp.server.recovery_mode":`proactive`}),this.onDisconnected?.(this),this.reconnectClient(`stdio transport closed`).catch(()=>{}))}async releaseProcessLease(e){try{await e?.release({kill:!1,releasePort:!1})}catch(e){this.logger.warn(`Failed to release process lease for ${this.serverName}`,e)}}async releaseOwnedResources(e){let t=this.stdioTransport,n=this.processLease,r=this.pendingLeaseRelease;this.stdioTransport=void 0,this.processLease=void 0,this.pendingLeaseRelease=void 0;try{t&&e.killChild&&await t.close()}finally{await Promise.all([r,n?.release({kill:!1,releasePort:!1})])}}async reconnectClient(e=`recoverable transport error`){if(this.lifetime.signal.throwIfAborted(),!this.reconnectFn)throw Error(`No reconnect function configured for ${this.serverName}`);let t=this.reconnectFn;this.reconnectPromise||=(async()=>{this.connected=!1,this.setLifecycleState(`reconnecting`),this.recoveryError=void 0,await this.releaseOwnedResources({killChild:!0});try{await this.client.close()}catch(e){this.logger.warn(`Failed to close stale client for ${this.serverName}`,e)}let n;for(let[r,i]of Zn.entries()){i>0&&await _e(i,void 0,{signal:this.lifetime.signal}),this.lifetime.signal.throwIfAborted();try{let n=await t(this.lifetime.signal);this.closing&&(await G(n),this.lifetime.signal.throwIfAborted()),this.client=n.client,this.recoverAuthorization=n.recoverAuthorization,n.stdioTransport&&(this.stdioTransport=n.stdioTransport),n.processLease&&(this.processLease=n.processLease),this.connected=!0,this.setLifecycleState(`connected`),this.logger.info(`mcp_proxy.server.recovered`,{"mcp.server.name":this.serverName,"mcp.server.transport":this.transport,"mcp.server.lifecycle":`connected`,"mcp.server.recovery_attempt":r+1,"mcp.server.recovery_trigger":e});return}catch(e){this.lifetime.signal.throwIfAborted(),n=e,this.logger.warn(`mcp_proxy.server.reconnect_failed`,{"mcp.server.name":this.serverName,"mcp.server.transport":this.transport,"mcp.server.recovery_attempt":r+1,"error.message":e instanceof Error?e.message:String(e)})}}throw this.connected=!1,this.recoveryError=new $n(this.serverName,Zn.length,{cause:n}),this.setLifecycleState(`degraded`,this.recoveryError.message),this.logger.error(`mcp_proxy.server.degraded`,{"mcp.server.name":this.serverName,"mcp.server.transport":this.transport,"mcp.server.lifecycle":`degraded`,"mcp.server.recovery_attempts":Zn.length,"error.message":n instanceof Error?n.message:String(n)}),this.recoveryError})();let n=this.reconnectPromise;try{await n}finally{this.reconnectPromise===n&&(this.reconnectPromise=void 0)}}async ensureReady(){if(this.lifetime.signal.throwIfAborted(),!this.connected){if(this.recoveryError)throw this.recoveryError;await this.reconnectClient(`operation requested while disconnected`)}}async withSessionRetry(e,t){t?.throwIfAborted(),await this.ensureReady(),t?.throwIfAborted();try{return await e()}catch(n){if(t?.throwIfAborted(),(n instanceof O||n instanceof ye)&&this.recoverAuthorization)return this.authorizationRecovery??=this.recoverAuthorization(n,t?AbortSignal.any([t,this.lifetime.signal]):this.lifetime.signal).then(()=>this.reconnectClient(`authorization recovered`)).finally(()=>{this.authorizationRecovery=void 0}),await this.authorizationRecovery,t?.throwIfAborted(),e();if(!this.reconnectFn||!er(n)&&!nr(n))throw n;return this.logger.warn(`Session error for ${this.serverName}, reconnecting: ${n instanceof Error?n.message:String(n)}`),await this.reconnectClient(n instanceof Error?n.message:String(n)),t?.throwIfAborted(),await e()}}async listTools(){return this.withSessionRetry(async()=>(await this.client.listTools()).tools)}async listResources(){return this.withSessionRetry(async()=>(await this.client.listResources()).resources)}async listPrompts(){return this.withSessionRetry(async()=>(await this.client.listPrompts()).prompts)}async callTool(e,t,n){let r=n?.signal?AbortSignal.any([n.signal,this.lifetime.signal]):this.lifetime.signal,i=await this.withSessionRetry(async()=>{let i={signal:r,...n?.timeout===void 0?{}:{timeout:n.timeout}},a=await this.client.callTool({name:e,arguments:t},i);if(!De(a))throw Error(`Unexpected interactive response from MCP tool '${e}': this call requires a context-aware relay`);return a},r);if(r.throwIfAborted(),n?.onResult)try{await n.onResult(structuredClone(i))}catch(t){this.logger.warn(`MCP result observer failed for ${this.serverName}.${e}`,t)}return r.throwIfAborted(),this.outputGuard?await this.outputGuard.guard(i):i}async readResource(e,t){let n=t?.signal?AbortSignal.any([t.signal,this.lifetime.signal]):this.lifetime.signal,r=await this.withSessionRetry(async()=>await this.client.readResource({uri:e},{signal:n,...t?.timeout===void 0?{}:{timeout:t.timeout}}),n);return n.throwIfAborted(),r}async getPrompt(e,t){return this.withSessionRetry(async()=>await this.client.getPrompt({name:e,arguments:t}))}close(){return this.closePromise||=(this.closing=!0,this.lifetime.abort(/* @__PURE__ */ Error(`MCP client '${this.serverName}' is closed`)),this.connected=!1,this.setLifecycleState(`closed`),(async()=>{let e=(await Promise.allSettled([this.releaseOwnedResources({killChild:!0}),Promise.resolve().then(()=>this.client.close())])).flatMap(e=>e.status===`rejected`?[e.reason]:[]);if(e.length)throw AggregateError(e,`Failed to close MCP client '${this.serverName}'`)})()),this.closePromise}},sr=class{outputSchemaValidation;clientCapabilities;clients=/* @__PURE__ */ new Map;serverConfigs=/* @__PURE__ */ new Map;connectionPromises=/* @__PURE__ */ new Map;connectionControllers=/* @__PURE__ */ new Map;disposed=!1;disposePromise;logger;repositoryPath;workspaceRoot;executionCwd;env;processOwnerId=w();outputGuard;stateListeners=/* @__PURE__ */ new Set;serverStates=/* @__PURE__ */ new Map;tokenStore;serverTokenStores=/* @__PURE__ */ new Map;serverRemovals=/* @__PURE__ */ new Map;onAuthorizationUrl;callbackTimeoutMs;callbackServer;resolveUrlCredentials;constructor(e=console,t=process.cwd(),n=new Mn,r={},i,a,o,s,c={}){this.outputSchemaValidation=s,this.clientCapabilities=c,this.logger=e,this.repositoryPath=t,this.workspaceRoot=i,this.executionCwd=o,this.env=a,this.outputGuard=n,this.tokenStore=r.tokenStore,this.onAuthorizationUrl=r.onAuthorizationUrl,this.callbackTimeoutMs=r.callbackTimeoutMs,this.callbackServer=r.callbackServer,this.resolveUrlCredentials=r.resolveUrlCredentials}onServerStateChange(e){this.stateListeners.add(e);for(let[t,n]of this.serverStates)this.notifyListener(e,{...n,serverName:t});return()=>{this.stateListeners.delete(e)}}getServerState(e){return this.serverStates.get(e)?.state}emitServerState(e){this.serverStates.set(e.serverName,e);for(let t of this.stateListeners)this.notifyListener(t,e)}notifyListener(e,t){try{e(t)}catch(e){this.logger.warn(`[mcp-proxy] Server state listener threw for ${t.serverName}: ${e instanceof Error?e.message:String(e)}`)}}setOutputGuard(e){this.outputGuard=e}async cleanupChildProcesses(){await this.disconnectAll()}async connectToServer(e,t,n){if(await this.serverRemovals.get(e),n?.signal?.throwIfAborted(),this.disposed)throw Error(`The MCP client manager is disposed`);n?.tokenStore&&this.serverTokenStores.set(e,n.tokenStore),this.serverConfigs.set(e,t);let r=()=>{this.removeServer(e).catch(()=>this.logger.warn(`Failed to remove cancelled MCP server '${e}'.`))};n?.signal?.addEventListener(`abort`,r,{once:!0});try{await this.ensureConnected(e),n?.signal?.throwIfAborted()}finally{n?.signal?.removeEventListener(`abort`,r)}}async removeServer(e){let t=this.serverRemovals.get(e);if(t)return t;let n=this.serverTokenStores.get(e);this.serverConfigs.delete(e);let r=(async()=>{try{await this.disconnectServer(e)}finally{this.serverTokenStores.get(e)===n&&this.serverTokenStores.delete(e),await n?.clear(e),this.serverStates.delete(e)}})();this.serverRemovals.set(e,r);try{await r}finally{this.serverRemovals.get(e)===r&&this.serverRemovals.delete(e)}}registerServerConfigs(e){for(let[t,n]of Object.entries(e))this.serverConfigs.set(t,n)}getKnownServerNames(){return Array.from(this.serverConfigs.keys())}getServerRequestTimeout(e){return this.serverConfigs.get(e)?.requestTimeout}async ensureConnected(e){if(this.disposed)throw Error(`The MCP client manager is disposed`);let t=this.clients.get(e);if(t)return await t.waitUntilReady(),t;let n=this.connectionPromises.get(e);if(n)return await n;let r=this.serverConfigs.get(e);if(!r)throw Error(`No configuration found for server "${e}"`);let i=this.createConnection(e,r);this.connectionPromises.set(e,i);try{return await i}finally{this.connectionPromises.get(e)===i&&this.connectionPromises.delete(e)}}async createConnection(e,t){let n=t.timeout??3e4,r=new AbortController;this.connectionControllers.set(e,r),this.emitServerState({serverName:e,state:`connecting`});let i,a=new Promise((e,t)=>{i=()=>t(r.signal.reason),r.signal.addEventListener(`abort`,i,{once:!0}),r.signal.aborted&&i()}),o,s,c=new Promise((e,t)=>{s=e=>{clearTimeout(o),!(e||r.signal.aborted)&&(o=setTimeout(()=>{let e=/* @__PURE__ */ Error(`Connection timeout after ${n}ms`);r.abort(e),t(e)},n))},s(!1)}),l;try{l=await Promise.race([this.createSdkClientConnection(e,t,r.signal,e=>s?.(e)),c,a])}catch(n){let i=t.transport===`http`||t.transport===`sse`?t.config.auth:void 0,a=typeof i==`object`&&i?.type===`api-key`,o=n instanceof Error&&[`Invalid MCP API-key configuration.`,`MCP API-key credential is unavailable.`,`MCP credential destination was rejected.`].includes(n.message),s=a&&!o?/* @__PURE__ */ Error(`MCP API-key connection failed.`):n;throw this.emitServerState({serverName:e,state:this.disposed||r.signal.aborted&&!String(r.signal.reason).includes(`timeout`)?`closed`:tr(s)?`needs-auth`:`failed`,error:s instanceof Error?s.message:String(s)}),s}finally{s=void 0,clearTimeout(o),r.signal.removeEventListener(`abort`,i),this.connectionControllers.get(e)===r&&this.connectionControllers.delete(e)}let u=new or(e,t.transport,l.client,this.logger,{instruction:t.instruction,toolBlacklist:t.toolBlacklist,omitToolDescription:t.omitToolDescription,prompts:t.prompts},this.outputGuard);u.setDisconnectedCallback(()=>void 0),u.setAuthorizationRecovery(l.recoverAuthorization),u.setLifecycleChangeCallback((t,n)=>{this.emitServerState({serverName:e,state:Qn(t),...n?{error:n}:{}})});try{if(l.stdioTransport&&u.setStdioTransport(l.stdioTransport),l.processLease&&u.setProcessLease(l.processLease),r.signal.throwIfAborted(),this.disposed)throw Error(`The MCP client manager is disposed`);if(u.setConnected(!0),u.setReconnectFn(async n=>{try{if(this.disposed)throw Error(`The MCP client manager is disposed`);return await this.createSdkClientConnection(e,t,n)}catch(t){throw this.logger.warn(`Failed to reconnect to ${e}`,t),t}}),!u.serverInstruction)try{let e=u.client.getInstructions();e&&(u.serverInstruction=e)}catch(t){this.logger.warn(`Failed to get server instruction from ${e}`,t)}return this.clients.set(e,u),u}catch(e){throw await u.close(),e}}createSdkClient(e,t=`auto`){let n={name:`@agimon-ai/po-mcp-proxy-client`,version:`0.1.0`},r={capabilities:structuredClone(this.clientCapabilities),versionNegotiation:t===`legacy`?{mode:`legacy`}:{mode:`auto`,probe:{timeoutMs:1e4,maxRetries:0}}};return this.outputSchemaValidation?new Yn(n,r,e,this.outputSchemaValidation):new ve(n,r)}async createSdkClientConnection(e,t,n,r){n?.throwIfAborted();let i;if(t.transport===`stdio`)i=await this.connectStdioSdkClient(e,this.createSdkClient(e),t.config,n);else if(t.transport===`http`||t.transport===`sse`)i=await this.connectUrlSdkClient(e,t.transport,t.config,!0,n,r);else{let{transport:e}=t;throw Error(`Unsupported transport type: ${e}`)}if(n?.aborted||this.disposed)throw await G(i),n?.throwIfAborted(),Error(`The MCP client manager is disposed`);return i}async connectUrlSdkClient(e,t,n,r=!0,i,a){i?.throwIfAborted();let o=n.auth,s=typeof o==`object`&&o?.type===`api-key`?o:void 0,c=this.resolveAuthHeaders(n),l;if(s){let r=new URL(n.url),a=s.mode===`bearer`?`Authorization`:s.name;if(s.mode!==`bearer`&&s.mode!==`header`||!/^[!#$%&'*+.^_`|~0-9A-Za-z-]+$/.test(a)||s.mode===`header`&&(a.toLowerCase()===`authorization`||/^(?:host|cookie|set-cookie|connection|keep-alive|te|trailer|upgrade|transfer-encoding|content-length|accept|origin|referer|forwarded|x-forwarded-.*|proxy-.*|sec-.*|mcp-session-id)$/i.test(a))||r.username||r.password||r.protocol!==`https:`&&!(r.protocol===`http:`&&[`localhost`,`127.0.0.1`,`[::1]`].includes(r.hostname))||n.bearerToken||n.bearerTokenEnv||n.oauth!==void 0||Object.keys(c??{}).some(e=>[`authorization`,a.toLowerCase()].includes(e.toLowerCase())))throw Error(`Invalid MCP API-key configuration.`);let o;try{o=await this.resolveUrlCredentials?.({serverName:e,transport:t,url:r.href,auth:s,signal:i})}catch{throw Error(`MCP API-key credential is unavailable.`)}i?.throwIfAborted();let u=o?.[a];if(!u||Object.keys(o??{}).length!==1||Array.from(u).some(e=>e.charCodeAt(0)<=31||e.charCodeAt(0)===127)||s.mode===`bearer`&&!/^Bearer [^\s]+$/.test(u))throw Error(`MCP API-key credential is unavailable.`);c={...c,[a]:u},l=async(e,t)=>{let n=new URL(typeof e==`string`?e:e instanceof URL?e.href:e.url);if(n.origin!==r.origin||n.username||n.password)throw Error(`MCP credential destination was rejected.`);return fetch(e,{...t,redirect:`error`})}}let u=this.createSdkClient(e,t===`sse`?`legacy`:`auto`),d=c?{headers:c}:void 0,f=await this.createAuthProvider(e,n);await f?.prepareCallback(),i?.throwIfAborted();let p=f?new An(f,n.url,i):void 0,m={requestInit:d,authProvider:p,...l?{fetch:l}:{}},h=t===`http`?new Ce(new URL(n.url),m):new Se(new URL(n.url),m),g=()=>{f?.cancelAuthorization(),u.close().catch(t=>this.logger.warn(`Failed to close cancelled client ${e}`,s?`API-key connection closed`:t))};i?.addEventListener(`abort`,g,{once:!0});let _=!1;try{return await u.connect(h),i?.throwIfAborted(),_=!0,{client:u,...p?{recoverAuthorization:(e,t)=>p.recover(e,t)}:{}}}catch(o){if(i?.throwIfAborted(),s)throw Error(`MCP API-key connection failed.`);if(!f||!p||!r||!(o instanceof ye)&&(!tr(o)||!f.hasPendingAuthorization()))throw o;return this.logger.info(`[oauth] Waiting for authorization of MCP server "${e}"`),a?.(!0),await p.recover(o,i,()=>{a?.(!1),this.emitServerState({serverName:e,state:`connecting`})}),i?.throwIfAborted(),await this.connectUrlSdkClient(e,t,n,!1,i,a)}finally{i?.removeEventListener(`abort`,g),_||(f?.cancelAuthorization(),await u.close().catch(t=>this.logger.warn(`Failed to close client ${e}`,s?`API-key connection closed`:t)))}}resolveAuthHeaders(e){let t={...e.headers};if(Tt(e)===`bearer`){let n=e.bearerTokenEnv?this.env?.[e.bearerTokenEnv]??Dt(e):Dt(e);n?t.Authorization=`Bearer ${n}`:this.logger.warn(`[oauth] Bearer auth configured for an MCP server but no token was resolved${e.bearerTokenEnv?` (env ${e.bearerTokenEnv} is unset)`:``}.`)}return Object.keys(t).length>0?t:void 0}async createAuthProvider(e,t){return Ot({serverName:e,config:t,store:this.serverTokenStores.get(e)??this.tokenStore,logger:this.logger,callbackTimeoutMs:this.callbackTimeoutMs,workspaceRoot:this.repositoryPath,...this.callbackServer?{callbackServer:this.callbackServer}:{},onAuthorizationUrl:(e,t)=>{if(this.emitServerState({serverName:t,state:`needs-auth`}),this.onAuthorizationUrl)return this.onAuthorizationUrl(e,t);this.logger.info(`[oauth] Authorize MCP server "${t}" by visiting: ${e.toString()}`)}})}relayChildStderr(e,t){t&&(t.on(`data`,t=>{let n=t.toString(`utf8`).trimEnd();n&&this.logger.debug(`[${e}] ${n}`)}),t.on(`error`,()=>void 0))}async connectStdioSdkClient(e,t,n,r){let i=new ke({command:n.command,args:n.args,cwd:this.executionCwd?y(this.executionCwd,n.cwd??`.`):this.workspaceRoot?y(this.workspaceRoot,n.cwd??`.`):n.cwd,env:{...process.env,...this.env,...n.env,PROJECT_PATH:this.repositoryPath,[oe]:Xn,[ae]:this.processOwnerId},stderr:`pipe`}),a=()=>{G({client:t,stdioTransport:i}).catch(t=>this.logger.warn(`Failed to close cancelled stdio connection for ${e}`,t))};r?.addEventListener(`abort`,a,{once:!0});try{r?.throwIfAborted(),await t.connect(i),r?.throwIfAborted(),this.relayChildStderr(e,i.stderr);let a=i.pid;if(typeof a!=`number`||a<=0)throw Error(`Failed to capture pid for stdio child process on server "${e}"`);let o=await ce({repositoryPath:this.repositoryPath,serviceName:e,serviceType:`tool`,pid:a,command:n.command,args:n.args,metadata:{transport:`stdio`},ownerKind:Xn,ownerId:this.processOwnerId,resourceKey:e});return t.onclose=()=>{let t=this.clients.get(e);t instanceof or&&t.handleStdioClosed(i)},{client:t,processLease:o,stdioTransport:i}}catch(n){try{await G({client:t,stdioTransport:i})}catch(t){this.logger.warn(`Failed to clean up incomplete stdio connection for ${e}`,t)}throw n}finally{r?.removeEventListener(`abort`,a)}}getClient(e){return this.clients.get(e)}getAllClients(){return Array.from(this.clients.values())}async disconnectServer(e){this.connectionControllers.get(e)?.abort(/* @__PURE__ */ Error(`MCP connection '${e}' was closed`));let t=this.connectionPromises.get(e);this.connectionPromises.delete(e);let n=this.clients.get(e);this.clients.delete(e),await Promise.all([t?.catch(()=>void 0),n?.close()])}async disconnectAll(){let e=[...this.clients.values()],t=[...this.connectionPromises.values()];this.clients.clear(),this.connectionPromises.clear();for(let e of this.connectionControllers.values())e.abort(/* @__PURE__ */ Error(`MCP connections were closed`));this.connectionControllers.clear();let n=(await Promise.allSettled([...e.map(e=>e.close()),...t.map(e=>e.catch(()=>void 0))])).flatMap(e=>e.status===`rejected`?[e.reason]:[]);if(n.length)throw AggregateError(n,`Failed to close MCP connections`)}dispose(){return this.disposed=!0,this.disposePromise??=this.disconnectAll(),this.disposePromise}isConnected(e){return this.clients.get(e)?.getLifecycleState()===`connected`}getConnectionState(e){return this.clients.get(e)?.getLifecycleState()}};const cr=`install`,lr=/^(@[a-zA-Z0-9_-]+\/)?[a-zA-Z0-9._-]+(@[a-zA-Z0-9._-]+)?$/,ur=`pipe`;function dr(e){return typeof e==`object`&&!!e&&`command`in e}var fr=class{config;constructor(e){this.config=e}extractPackages(){let e=[],{mcpConfig:t,filter:n}=this.config;for(let[r,i]of Object.entries(t.mcpServers)){if(i.disabled||i.transport!==`stdio`||!dr(i.config))continue;let t=this.extractPackageInfo(r,i.config);if(t){if(n&&t.packageManager!==n)continue;e.push(t)}}return e}async prefetch(){try{let e=this.extractPackages(),t=[];if(e.length===0)return{totalPackages:0,successful:0,failed:0,results:[]};if(this.config.parallel){let n=e.map(async e=>this.prefetchPackage(e));t.push(...await Promise.all(n))}else for(let n of e){let e=await this.prefetchPackage(n);t.push(e)}let n=t.filter(e=>e.success).length,r=t.filter(e=>!e.success).length;return{totalPackages:e.length,successful:n,failed:r,results:t}}catch(e){throw Error(`Failed to prefetch packages: ${e instanceof Error?e.message:String(e)}`)}}async prefetchPackage(e){try{let[t,...n]=e.fullCommand,r=await this.runCommand(t,n);return{package:e,success:r.success,output:r.output}}catch(t){return{package:e,success:!1,output:t instanceof Error?t.message:String(t)}}}isValidPackageName(e){return lr.test(e)}extractPackageInfo(e,t){let n=t.command.toLowerCase(),r=t.args||[];if(n===`npx`||n.endsWith(`/npx`)){let t=this.extractNpxPackage(r);if(t&&this.isValidPackageName(t))return{serverName:e,packageManager:`npx`,packageName:t,fullCommand:[`npm`,cr,`-g`,t]}}if(n===`pnpx`||n.endsWith(`/pnpx`)){let t=this.extractNpxPackage(r);if(t&&this.isValidPackageName(t))return{serverName:e,packageManager:`pnpx`,packageName:t,fullCommand:[`pnpm`,`add`,`-g`,t]}}if(n===`uvx`||n.endsWith(`/uvx`)){let t=this.extractUvxPackage(r);if(t&&this.isValidPackageName(t))return{serverName:e,packageManager:`uvx`,packageName:t,fullCommand:[`uvx`,t]}}if((n===`uv`||n.endsWith(`/uv`))&&r.includes(`run`)){let t=this.extractUvRunPackage(r);if(t&&this.isValidPackageName(t))return{serverName:e,packageManager:`uv`,packageName:t,fullCommand:[`uv`,`tool`,cr,t]}}return null}extractNpxPackage(e){for(let t=0;t<e.length;t++){let n=e[t];if(n.startsWith(`--package=`))return n.slice(10)||null;if(n===`--package`&&t+1<e.length){let n=e[t+1];if(!n.startsWith(`-`))return n}if(n===`-p`&&t+1<e.length){let n=e[t+1];if(!n.startsWith(`-`))return n}}for(let t of e)if(!t.startsWith(`-`))return t;return null}extractUvxPackage(e){for(let t of e)if(!t.startsWith(`-`))return t;return null}extractUvRunPackage(e){let t=e.indexOf(`run`);if(t===-1)return null;for(let n=t+1;n<e.length;n++){let t=e[n];if(!t.startsWith(`-`))return t}return null}runCommand(e,t){return new Promise(n=>{let r=re(e,t,{stdio:[`ignore`,ur,ur],shell:process.platform===`win32`}),i=``,a=``;r.stdout?.on(`data`,e=>{i+=e.toString()}),r.stderr?.on(`data`,e=>{a+=e.toString()}),r.on(`close`,e=>{n({success:e===0,output:i||a})}),r.on(`error`,e=>{n({success:!1,output:e.message})})})}};const pr=`.runtime.json`;async function mr(e){await r(e,{recursive:!0,mode:448});try{await t(e,448)}catch{}}function hr(e){return typeof e==`object`&&!!e}function gr(e){return hr(e)?typeof e.serverId==`string`&&typeof e.host==`string`&&typeof e.port==`number`&&e.transport===`http`&&typeof e.shutdownToken==`string`&&typeof e.startedAt==`string`&&typeof e.pid==`number`&&(e.configPath===void 0||typeof e.configPath==`string`)&&(e.runtimeLifecycle===void 0||e.runtimeLifecycle===`manual`||e.runtimeLifecycle===`stdio-http-managed`):!1}function _r(e){return e instanceof Error?e.message:String(e)}function vr(e){try{return process.kill(e,0),!0}catch{return!1}}var K=class e{runtimeDir;logger;constructor(t,n=console){this.runtimeDir=t??e.getDefaultRuntimeDir(),this.logger=n}static getDefaultRuntimeDir(){return v(T(),`.mcp-proxy`,`runtimes`)}getRecordPath(e){return v(this.runtimeDir,`${e}${pr}`)}async write(e){await mr(this.runtimeDir);let t=this.getRecordPath(e.serverId);await m(t,JSON.stringify(e,null,2),{encoding:`utf-8`,mode:384})}async read(e){let t=this.getRecordPath(e);try{let e=await o(t,`utf-8`),n=JSON.parse(e);return gr(n)?n:null}catch(n){if(hr(n)&&`code`in n&&n.code===`ENOENT`)return null;throw Error(`Failed to read runtime state for server '${e}' from '${t}': ${_r(n)}`)}}async list(){try{let e=(await s(this.runtimeDir,{withFileTypes:!0})).filter(e=>e.isFile()&&e.name.endsWith(pr));return(await Promise.all(e.map(async e=>{try{let t=await o(v(this.runtimeDir,e.name),`utf-8`),n=JSON.parse(t);return gr(n)?n:null}catch(t){return this.logger.debug(`Skipping unreadable runtime state file ${e.name}`,t),null}}))).filter(e=>e!==null)}catch(e){if(hr(e)&&`code`in e&&e.code===`ENOENT`)return[];throw Error(`Failed to list runtime states from '${this.runtimeDir}': ${_r(e)}`)}}async listOrphans(e){return(await this.list()).filter(t=>!e.has(t.serverId))}async pruneStale(){let e=(await this.list()).filter(e=>!vr(e.pid));return await Promise.all(e.map(async e=>this.remove(e.serverId))),e}async remove(e){await l(this.getRecordPath(e),{force:!0})}},yr=class extends Error{filePath;cause;constructor(e,t,n){super(e),this.filePath=t,this.cause=n,this.name=`SkillLoadError`}};async function br(t){try{return await e(t),!0}catch(e){if(e instanceof Error&&`code`in e&&e.code===`ENOENT`)return!1;throw Error(`Failed to check path existence for "${t}": ${e instanceof Error?e.message:`Unknown error`}`)}}var xr=class{cwd;skillPaths;cachedSkills=null;skillsByName=null;watchers=[];pollingTimers=[];onCacheInvalidated;logger;constructor(e,t,n,r=console){this.cwd=e,this.skillPaths=t,this.onCacheInvalidated=n?.onCacheInvalidated,this.logger=r}async getSkills(){if(this.cachedSkills!==null)return this.cachedSkills;let e=[],t=/* @__PURE__ */ new Set,n=await Promise.all(this.skillPaths.map(async e=>{let t=_(e)?e:v(this.cwd,e);return this.loadSkillsFromDirectory(t,`project`)}));for(let r of n)for(let n of r)t.has(n.name)||(e.push(n),t.add(n.name));return this.cachedSkills=e,this.skillsByName=new Map(e.map(e=>[e.name,e])),e}async getSkill(e){return this.skillsByName===null&&await this.getSkills(),this.skillsByName?.get(e)}clearCache(){this.cachedSkills=null,this.skillsByName=null}async startWatching(){this.stopWatching();let e=await Promise.all(this.skillPaths.map(async e=>{let t=_(e)?e:v(this.cwd,e);return{skillsDir:t,exists:await br(t)}}));for(let{skillsDir:t,exists:n}of e){if(!n)continue;let e=new AbortController;this.watchers.push(e),this.watchDirectory(t,e.signal).catch(n=>{if(n?.name!==`AbortError`){if(this.isWatchResourceLimitError(n)){this.startPollingDirectory(t,e.signal);return}this.logger.warn(`[skill-watcher] Error watching ${t}: ${n instanceof Error?n.message:`Unknown error`}`)}})}}stopWatching(){for(let e of this.watchers)e.abort();this.watchers=[];for(let e of this.pollingTimers)clearInterval(e);this.pollingTimers=[]}async watchDirectory(e,t){let n=p(e,{recursive:!0,signal:t});for await(let e of n)e.filename?.endsWith(`SKILL.md`)&&this.invalidateCache()}invalidateCache(){this.clearCache(),this.onCacheInvalidated?.()}isWatchResourceLimitError(e){return e instanceof Error&&`code`in e&&(e.code===`EMFILE`||e.code===`ENOSPC`)}startPollingDirectory(e,t){this.createSkillSnapshot(e).then(n=>{let r=n,i=setInterval(()=>{t.aborted?clearInterval(i):this.createSkillSnapshot(e).then(e=>{this.snapshotsEqual(r,e)||(r=e,this.invalidateCache())}).catch(t=>{this.logger.warn(`[skill-watcher] Polling failed for ${e}: ${t instanceof Error?t.message:`Unknown error`}`)})},3e3);this.pollingTimers.push(i),t.addEventListener(`abort`,()=>{clearInterval(i),this.pollingTimers=this.pollingTimers.filter(e=>e!==i)},{once:!0})})}async createSkillSnapshot(e){let t=/* @__PURE__ */ new Map;return await this.collectSkillSnapshots(e,t),t}async collectSkillSnapshots(e,t){let n;try{n=await s(e)}catch(e){if(e instanceof Error&&`code`in e&&e.code===`ENOENT`)return;throw e}await Promise.all(n.map(async n=>{let r=v(e,n),i=await u(r);i.isDirectory()?await this.collectSkillSnapshots(r,t):n===`SKILL.md`&&t.set(r,i.mtimeMs)}))}snapshotsEqual(e,t){if(e.size!==t.size)return!1;for(let[n,r]of e)if(t.get(n)!==r)return!1;return!0}async loadSkillsFromDirectory(e,t){let n=[];try{if(!await br(e))return n}catch(t){throw new yr(`Cannot access skills directory: ${t instanceof Error?t.message:`Unknown error`}`,e,t instanceof Error?t:void 0)}let r;try{r=await s(e)}catch(t){throw new yr(`Failed to read skills directory: ${t instanceof Error?t.message:`Unknown error`}`,e,t instanceof Error?t:void 0)}let i=await Promise.all(r.map(async t=>{let n=v(e,t);try{return{entry:t,entryPath:n,stat:await u(n),error:null}}catch(e){return this.logger.warn(`Skipping entry ${n}: ${e instanceof Error?e.message:`Unknown error`}`),{entry:t,entryPath:n,stat:null,error:e}}})),a=[];for(let{entry:e,entryPath:t,stat:n}of i)if(n){if(n.isDirectory()){let e=v(t,`SKILL.md`);a.push({filePath:e,isRootLevel:!1})}else e===`SKILL.md`&&a.push({filePath:t,isRootLevel:!0})}let o=await Promise.all(a.map(async({filePath:e,isRootLevel:n})=>{try{return!n&&!await br(e)?null:await this.loadSkillFile(e,t)}catch(t){return this.logger.warn(`Skipping skill at ${e}: ${t instanceof Error?t.message:`Unknown error`}`),null}}));for(let e of o)e&&n.push(e);return n}async loadSkillFile(e,t){let n;try{n=await o(e,`utf-8`)}catch(t){throw new yr(`Failed to read skill file: ${t instanceof Error?t.message:`Unknown error`}`,e,t instanceof Error?t:void 0)}let{frontMatter:r,content:i}=hn(n);return!r||!r.name||!r.description?null:{name:r.name,description:r.description,location:t,content:i,basePath:g(e)}}};const Sr=/* @__PURE__ */ new Set([`localhost`,`127.0.0.1`,`::1`]);function Cr(e){return e}function wr(e){if(typeof e!=`object`||!e)return!1;let t=Cr(e);return`status`in t&&t.status===`ok`&&`transport`in t&&t.transport===`http`&&(!(`serverId`in t)||t.serverId===void 0||typeof t.serverId==`string`)&&(!(`runtimeLifecycle`in t)||t.runtimeLifecycle===void 0||t.runtimeLifecycle===`manual`||t.runtimeLifecycle===`stdio-http-managed`)}function Tr(e){if(typeof e!=`object`||!e)return!1;let t=Cr(e);return`ok`in t&&typeof t.ok==`boolean`&&`message`in t&&typeof t.message==`string`&&(!(`serverId`in t)||t.serverId===void 0||typeof t.serverId==`string`)}function Er(e,t){if(!Sr.has(e.host))throw Error(`Refusing to connect to non-loopback host '${e.host}'. Only ${Array.from(Sr).join(`, `)} are allowed.`);return`http://${e.host}:${e.port}${t}`}function Dr(e){return e instanceof Error?e.message:String(e)}function Or(e){return new Promise(t=>{setTimeout(t,e)})}var kr=class{runtimeStateService;logger;constructor(e=new K,t=console){this.runtimeStateService=e,this.logger=t}async stop(e){let t=e.timeoutMs??5e3;await this.pruneStaleRuntimeRecords();let n=await this.resolveRuntime(e),r=await this.fetchHealth(n,t);if(!r.reachable)throw await this.runtimeStateService.remove(n.serverId),Error(`Runtime '${n.serverId}' is not reachable at http://${n.host}:${n.port}. Removed stale runtime record.`);if(!e.force&&r.payload?.serverId&&r.payload.serverId!==n.serverId)throw Error(`Refusing to stop runtime at http://${n.host}:${n.port}: expected server ID '${n.serverId}' but health endpoint reported '${r.payload.serverId}'. Use --force to override.`);let i=e.token??n.shutdownToken;if(!i)throw Error(`No shutdown token available for runtime '${n.serverId}'.`);let a=await this.requestShutdown(n,i,t);return await this.waitForShutdown(n,t),await this.runtimeStateService.remove(n.serverId),{ok:!0,serverId:n.serverId,host:n.host,port:n.port,message:a.message}}async resolveRuntime(e){if(e.serverId){let t=await this.runtimeStateService.read(e.serverId);if(!t)throw Error(`No runtime record found for server ID '${e.serverId}'. Start the server with 'po-mcp-proxy mcp-serve --type http' first.`);return t}if(e.host===void 0||e.port===void 0)throw Error(`Provide --id or both --host and --port to select a runtime.`);let t=(await this.runtimeStateService.list()).filter(t=>t.host===e.host&&t.port===e.port);if(t.length===0)throw Error(`No runtime record found for http://${e.host}:${e.port}. Start the server with 'po-mcp-proxy mcp-serve --type http' first.`);if(t.length>1)throw Error(`Multiple runtime records match http://${e.host}:${e.port}. Retry with --id to avoid stopping the wrong server.`);return t[0]}async pruneStaleRuntimeRecords(){if(this.runtimeStateService.pruneStale)try{await this.runtimeStateService.pruneStale()}catch(e){this.logger.debug(`Failed to prune stale runtime records before stop resolution`,e)}}async fetchHealth(e,t){try{let n=await this.fetchWithTimeout(Er(e,`/health`),{method:`GET`},t);if(!n.ok)return{reachable:!1};let r=await n.json();if(!wr(r))throw Error(`Received invalid health response payload.`);return{reachable:!0,payload:r}}catch(t){return this.logger.debug(`Health check failed for ${e.serverId}`,t),{reachable:!1}}}async requestShutdown(e,t,n){let r=await this.fetchWithTimeout(Er(e,`/admin/shutdown`),{method:`POST`,headers:{Authorization:`Bearer ${t}`}},n),i=await r.json();if(!Tr(i))throw Error(`Received invalid shutdown response payload.`);if(!r.ok||!i.ok)throw Error(i.message);return i}async waitForShutdown(e,t){let n=Date.now()+t;for(;Date.now()<n;){if(!(await this.fetchHealth(e,Math.max(250,n-Date.now()))).reachable)return;await Or(200)}throw Error(`Timed out waiting for runtime '${e.serverId}' to stop at http://${e.host}:${e.port}.`)}async fetchWithTimeout(e,t,n){let r=new AbortController,i=setTimeout(()=>{r.abort()},n);try{return await fetch(e,{...t,signal:r.signal})}catch(t){throw Error(`Request to '${e}' failed: ${Dr(t)}`)}finally{clearTimeout(i)}}};function q(e){return e instanceof Error?e.message:String(e)}function Ar(e,t){let n=Buffer.from(e,`utf-8`),r=Buffer.from(t,`utf-8`);return n.length===r.length&&ee(n,r)}var jr=class{requests=/* @__PURE__ */ new Map;isAllowed(e){let t=Date.now()-6e4,n=(this.requests.get(e)??[]).filter(e=>e>t);return n.length>=5?(this.requests.set(e,n),!1):(n.push(Date.now()),this.requests.set(e,n),!0)}},Mr=class{honoApp=new Le;adminRateLimiter=new jr;config;mcpHandler;nodeMcpHandler;adminOptions;logger;server=null;lastRequestAt=Date.now();activeRequests=0;idleTimer=null;constructor(e,t,n,r=console){this.config={mode:t.mode,host:t.host??`localhost`,port:t.port},this.adminOptions=n,this.logger=r,this.mcpHandler=me(e,{legacy:`reject`,onerror:e=>this.logger.error(`MCP handler failed: ${e.message}`)}),this.nodeMcpHandler=Ie(this.mcpHandler,{onerror:e=>this.logger.error(`Failed to adapt MCP request: ${e.message}`)}),this.setupHonoRoutes()}setupHonoRoutes(){this.honoApp.get(`/health`,e=>{let t={status:`ok`,transport:`http`,serverId:this.adminOptions?.serverId,runtimeLifecycle:this.adminOptions?.runtimeLifecycle,protocolVersion:`2026-07-28`,runtimeEpoch:`modern-2026-07-28-v1`};return e.json(t)}),this.honoApp.post(`/admin/shutdown`,async e=>{try{let t=e.req.header(`x-forwarded-for`)??`unknown`;if(!this.adminRateLimiter.isAllowed(t)){let t={ok:!1,message:`Too many shutdown requests. Try again later.`,serverId:this.adminOptions?.serverId};return e.json(t,429)}return await this.handleAdminShutdownRequest(e)}catch(t){this.logger.error(`Failed to process shutdown request: ${q(t)}`);let n={ok:!1,message:`Failed to process shutdown request.`,serverId:this.adminOptions?.serverId};return e.json(n,500)}})}isAuthorizedShutdownRequest(e){let t=this.adminOptions?.shutdownToken;if(!t)return!1;let n=e.authorization;if(typeof n==`string`&&n.startsWith(`Bearer `))return Ar(n.slice(7),t);let r=e[`x-mcp-proxy-shutdown-token`];return typeof r==`string`&&Ar(r,t)}async handleAdminShutdownRequest(e){if(!this.adminOptions?.onShutdownRequested)return e.json({ok:!1,message:`Shutdown endpoint is not enabled for this server instance.`,serverId:this.adminOptions?.serverId},404);if(!this.isAuthorizedShutdownRequest({authorization:e.req.header(`authorization`),"x-mcp-proxy-shutdown-token":e.req.header(`x-mcp-proxy-shutdown-token`)}))return e.json({ok:!1,message:`Unauthorized shutdown request: invalid or missing shutdown token.`,serverId:this.adminOptions?.serverId},401);let t=e.json({ok:!0,message:`Shutdown request accepted. Stopping server gracefully.`,serverId:this.adminOptions?.serverId});return this.adminOptions.onShutdownRequested().catch(e=>{this.logger.error(`Shutdown request failed: ${q(e)}`)}),t}async handleMcpRequest(e,t){this.activeRequests+=1;try{await this.nodeMcpHandler(e,t)}catch(e){this.logger.error(`MCP request failed: ${q(e)}`),t.headersSent||(t.writeHead(500,{"Content-Type":`application/json`}),t.end(JSON.stringify({jsonrpc:`2.0`,error:{code:-32603,message:`MCP request failed.`},id:null})))}finally{--this.activeRequests,this.lastRequestAt=Date.now()}}startIdleShutdownMonitor(){let e=this.adminOptions?.idleShutdownMs??0;e<=0||this.idleTimer||(this.lastRequestAt=Date.now(),this.idleTimer=setInterval(()=>{this.shutdownWhenIdle(e)},Math.min(e,6e4)))}stopIdleShutdownMonitor(){this.idleTimer&&=(clearInterval(this.idleTimer),null)}async shutdownWhenIdle(e){let t=this.adminOptions?.onShutdownRequested;if(!(!t||this.activeRequests>0||Date.now()-this.lastRequestAt<e)){this.stopIdleShutdownMonitor(),this.logger.info(`Stopping idle HTTP runtime after ${e}ms without active requests`);try{await t()}catch(e){this.logger.error(`Failed to stop idle HTTP runtime: ${q(e)}`)}}}async start(){if(this.server)throw Error(`HTTP transport is already running`);let e=Fe(this.honoApp.fetch),t=te((t,n)=>{this.lastRequestAt=Date.now(),new URL(t.url??`/`,`http://${t.headers.host??this.config.host}`).pathname===`/mcp`?this.handleMcpRequest(t,n):e(t,n)});try{t.listen(this.config.port,this.config.host),await Promise.race([Me(t,`listening`),Me(t,`error`).then(([e])=>Promise.reject(e))]),this.server=t,this.startIdleShutdownMonitor(),this.logger.info(`@agimon-ai/po-mcp-proxy MCP server started on http://${this.config.host}:${this.config.port}/mcp`),this.logger.info(`Health check: http://${this.config.host}:${this.config.port}/health`)}catch(e){throw t.close(),Error(`Failed to start HTTP transport: ${q(e)}`,{cause:e})}}async stop(){this.stopIdleShutdownMonitor();let e=this.server;if(e){this.server=null,await this.mcpHandler.close();try{await Pe(e.close.bind(e))()}catch(e){throw Error(`Failed to stop HTTP transport: ${q(e)}`,{cause:e})}}}getPort(){let e=this.server?.address();return e&&typeof e==`object`?e.port:this.config.port}getHost(){return this.config.host}};const Nr=`localhost`,Pr=12e4,Fr=`MCP_PROXY_RUNTIME_DETACHED`;function Ir(){return process.env[Fr]===`1`}async function Lr(e){return(await new se(process.env.PROCESS_REGISTRY_PATH).listProcesses({repositoryPath:e,serviceName:`mcp-proxy-http`,liveOnly:!0})).filter(e=>!!(e.host&&e.port)).map(e=>{let t=e.metadata;return{host:e.host,port:e.port,serverId:t?.serverId??`unknown`,runtimeLifecycle:t?.runtimeLifecycle===`stdio-http-managed`||t?.runtimeLifecycle===`manual`?t.runtimeLifecycle:void 0,createdAt:e.createdAt}}).sort((e,t)=>(e.createdAt??``).localeCompare(t.createdAt??``))}async function Rr(e){let t=await Lr(e);for(let e of t)if(zr(await Br(e.host,e.port)))return e;return null}function zr(e){return e.ok&&e.payload?.status===`ok`&&e.payload.transport===`http`&&e.payload.protocolVersion===`2026-07-28`&&e.payload.runtimeEpoch===`modern-2026-07-28-v1`}async function Br(e,t,n={}){let r=n.timeoutMs??1500,i=n.retries??2,a=n.retryDelayMs??150,o=i+1,s=`unknown error`;for(let n=1;n<=o;n+=1){try{let i=await fetch(`http://${e}:${t}/health`,{signal:AbortSignal.timeout(r)});if(i.ok)return{ok:!0,payload:await i.json().catch(()=>null)??void 0,attempts:n};s=`health endpoint returned status ${i.status}`}catch(e){s=e instanceof Error?e.message:String(e)}n<o&&await new Promise(e=>setTimeout(e,a))}return{ok:!1,error:s,attempts:o}}async function Vr(){let t=ie(import.meta.url),n=h.dirname(t),r=[h.resolve(n,`cli.mjs`),h.resolve(n,`..`,`dist`,`cli.mjs`),h.resolve(n,`..`,`..`,`dist`,`cli.mjs`)],i=[h.resolve(n,`..`,`cli.ts`),h.resolve(n,`..`,`..`,`src`,`cli.ts`)];for(let t of r)try{return await e(t),{command:process.execPath,args:[t]}}catch{continue}for(let t of i)try{return await e(t),{command:process.execPath,args:[`--import`,`tsx`,t]}}catch{continue}throw Error(`Unable to locate mcp-proxy CLI entrypoint`)}function Hr(e){if(!e)return Pr;let t=Number.parseInt(e,10);if(!Number.isInteger(t)||t<=0)throw Error(`Invalid timeout value: ${e}`);return t}async function Ur(t,n){let r=Date.now()+n;for(;Date.now()<r;){try{await e(t);return}catch{}await new Promise(e=>setTimeout(e,250))}throw Error(`Timed out waiting for runtime state file: ${t}`)}async function Wr(e,t){let n=new K,r=Date.now()+t;for(;Date.now()<r;){let t=await n.read(e);if(t&&zr(await Br(t.host,t.port,{retries:0})))return{host:t.host,port:t.port};await new Promise(e=>setTimeout(e,250))}throw Error(`Timed out waiting for HTTP runtime '${e}' to become healthy`)}async function Gr(e,t,n){let{command:r,args:i}=await Vr(),a=re(r,[...i,...e],{detached:!0,cwd:n,stdio:`ignore`,env:t});return a.unref(),a}async function Kr(e){let t=e.id||mn(),n=Hr(e.timeoutMs),r=e.registryPath||e.registryDir,i=await A();await new K().pruneStale();let a=await Rr(i);if(a)return{host:a.host,port:a.port,serverId:a.serverId,workspaceRoot:i,reusedExistingRuntime:!0,runtimeLifecycle:a.runtimeLifecycle??`manual`};let o=e.port,s=await Lr(i);if(e.id&&s.some(e=>e.serverId===t))throw Error(`Runtime ID '${t}' belongs to an incompatible HTTP runtime; choose a new ID`);if(o!==void 0&&s.some(t=>t.host===(e.host||Nr)&&t.port===o))throw Error(`Port ${o} belongs to a different HTTP runtime; choose a free port`);let c=o,l={...process.env,[Fr]:`1`,...e.runtimeLifecycle?{MCP_PROXY_RUNTIME_LIFECYCLE:e.runtimeLifecycle}:{},...r?{PORT_REGISTRY_PATH:r,PROCESS_REGISTRY_PATH:le(r,`processes.json`)}:{}},u=await Gr([`mcp-serve`,`--type`,`http`,`--id`,t,`--host`,e.host||Nr,...c===void 0?[]:[`--port`,String(c)],...dn(e.config).flatMap(e=>[`--config`,e]),...e.cache===!1?[`--no-cache`]:[],...e.definitionsCache?[`--definitions-cache`,e.definitionsCache]:[],...e.clearDefinitionsCache?[`--clear-definitions-cache`]:[],...e.idleShutdownMinutes===void 0?[]:[`--idle-shutdown-minutes`,String(e.idleShutdownMinutes)],`--proxy-mode`,e.proxyMode],l,i),d=new Promise((e,t)=>{u.once(`exit`,(e,n)=>{t(/* @__PURE__ */ Error(`Background runtime exited before becoming healthy (code=${e??`null`}, signal=${n??`null`})`))})}),f=h.join(K.getDefaultRuntimeDir(),`${t}.runtime.json`);try{await Promise.race([Ur(f,n),d]);let{host:r,port:a}=await Promise.race([Wr(t,n),d]);return{host:r,port:a,serverId:t,workspaceRoot:i,reusedExistingRuntime:!1,runtimeLifecycle:e.runtimeLifecycle??`manual`}}catch(e){throw Error(`Failed to prestart HTTP runtime '${t}': ${e instanceof Error?e.message:String(e)}`,{cause:e})}}function qr(e){process.stdout.write(`mcp-proxy HTTP runtime ready at http://${e.host}:${e.port} (${e.serverId})\n`),process.stdout.write(`runtimeId=${e.serverId}\n`),process.stdout.write(`runtimeUrl=http://${e.host}:${e.port}\n`),process.stdout.write(`workspaceRoot=${e.workspaceRoot}\n`)}var Jr=`0.0.1-alpha.37`,Yr=`<toolkit id="{{ serverId }}">
|
|
10
10
|
<instruction>
|
|
11
11
|
Before you use any capabilities below, you MUST call this tool with a list of names to learn how to use them properly; this includes:
|
|
12
12
|
- For tools: Arguments schema needed to pass to use_tool
|
|
@@ -6,7 +6,7 @@ var e=Object.create,t=Object.defineProperty,n=Object.getOwnPropertyDescriptor,r=
|
|
|
6
6
|
`),a=nn(i);if(a.bytes<=this.maxBytes&&a.lines<=this.maxLines)return e;let{path:o,error:s}=await $t(`output`,i),c=Jt(a,o,s),l=Gt(this.maxBytes,this.maxLines,c),u=`${Kt(i,l.maxBytes,l.maxLines)}\n\n${c}`,d=nn(u),f=r.filter(e=>e.type===`image`).length,p={truncated:!0,originalBytes:a.bytes,returnedBytes:d.bytes,originalLines:a.lines,returnedLines:d.lines,...f>0?{imageBlocksPassedThrough:f}:{},...o?{fullOutputPath:o}:{},...s?{writeError:s}:{}};return{...e,content:[{type:`text`,text:u},...r],_meta:{...e._meta,"agiflowai/outputGuard":p}}}async guardStructuredContent(e){if(e.structuredContent===void 0)return e;let t=tn(e.structuredContent),n=W(t);if(n<=this.structuredMaxBytes)return e;let{path:r,error:i}=await $t(`structured-content`,t);return{...e,structuredContent:{omitted:!0,reason:`structuredContent exceeded the size limit and was replaced with this summary to keep the caller context bounded.`,rawBytes:n,summary:Yt(e.structuredContent),...r?{fullResultPath:r}:{},...i?{resultWriteError:i}:{}}}}};function Gt(e,t,n){let r=nn(`\n\n${n}`);return{maxBytes:Math.max(0,e-r.bytes),maxLines:Math.max(0,t-r.lines)}}function Kt(e,t,n){let r=e.split(`
|
|
7
7
|
`),i=[],a=0;for(let e of r){if(i.length>=n)break;let r=+(i.length>0),o=W(e);if(a+r+o>t){let n=t-a-r;n>0&&i.push(qt(e,n));break}i.push(e),a+=r+o}return i.join(`
|
|
8
8
|
`)}function qt(e,t){if(W(e)<=t)return e;let n=Buffer.from(e,`utf8`),r=Math.max(0,t);for(;r>0&&(n[r]&192)==128;)r--;return n.subarray(0,r).toString(`utf8`)}function Jt(e,t,n){let r=`[MCP tool output truncated: original ${e.lines.toLocaleString()} lines / ${on(e.bytes)}.`;return t?`${r} Full text saved to: ${t}]`:`${r} Full output could not be saved: ${n??`unknown error`}]`}function Yt(e){let t=en(e);if(!t)return{type:e===null?`null`:typeof e,omitted:!0};let n=Object.keys(t);return{type:Array.isArray(e)?`array`:`object`,keyCount:Array.isArray(e)?e.length:n.length,keysPreview:n.slice(0,20).map(Xt),...Array.isArray(e)&&e.length>20?{itemsOmitted:e.length-20}:{},omitted:!0}}function Xt(e){return e.length<=120?e:`${e.slice(0,119)}…`}let Zt;function Qt(){return Zt??=(0,c.mkdtemp)((0,l.join)((0,f.tmpdir)(),`mcp-proxy-output-`)),Zt}async function $t(e,t){try{let n=await Qt(),r=(0,l.join)(n,`${e}-${(0,u.randomBytes)(8).toString(`hex`)}.txt`);return await(0,c.writeFile)(r,t,{encoding:`utf8`,mode:384}),{path:r}}catch(e){return Zt=void 0,{error:e instanceof Error?e.message:String(e)}}}function en(e){return typeof e==`object`&&e?e:void 0}function tn(e){try{return JSON.stringify(e)??``}catch{return``}}function nn(e){return{bytes:W(e),lines:e.length===0?0:e.split(`
|
|
9
|
-
`).length}}function W(e){return Buffer.byteLength(e,`utf8`)}function rn(e){if(typeof e!=`number`||!Number.isFinite(e))return;let t=Math.floor(e);return t>0?t:void 0}function an(e){let t=process.env[e]?.trim().toLowerCase();if(t){if([`0`,`false`,`no`,`off`].includes(t))return!1;if([`1`,`true`,`yes`,`on`].includes(t))return!0}}function on(e){return e<1024?`${e} B`:e<1048576?`${(e/1024).toFixed(1)} KiB`:`${(e/1048576).toFixed(1)} MiB`}const G=`x-mcp-proxy-original-output-schema`;function sn(e){return Array.from(e.slice(0,256)).filter(e=>{let t=e.charCodeAt(0);return t>=32&&t<=126||t>=160}).join(``)}var cn=class extends S.Client{serverName;compatibility;calls;constructor(e,t,n,r){let i=new te.AsyncLocalStorage,a=new ne.AjvJsonSchemaValidator;super(e,{...t,jsonSchemaValidator:{getValidator:e=>t=>{let n=i.getStore()?.toolName,r=Object.hasOwn(e,G)?e[G]:e;try{!a.getValidator(r)(t).valid&&n&&this.warn(n,`tools/call`,`structuredContent`,`Structured output does not match its output schema.`)}catch{n&&this.warn(n,`tools/call`,`outputSchema`,`Output schema could not be compiled or evaluated.`)}return{valid:!0,data:t,errorMessage:void 0}}}}),this.serverName=n,this.compatibility=r,this.calls=i}warn(e,t,n,r){try{Promise.resolve(this.compatibility.onWarning({serverName:sn(this.serverName),toolName:sn(e),method:t,path:n,message:r})).catch(()=>{})}catch{}}request(e,t,n){if(t&&`~standard`in t)return super.request(e,t,n);if(e.method!==`tools/list`){let n=super.request(e,t);return e.method===`tools/call`?n.then(e=>{let t=this.calls.getStore();return t&&(t.result=e),e}):n}return super.request(e,{"~standard":{version:1,vendor:`mcp-proxy-output-compatibility`,validate:e=>this.validateListing(e)}},t)}validateListing(e){if(!e||typeof e!=`object`||!(`tools`in e)||!Array.isArray(e.tools))return{issues:[{message:`Invalid tools/list result`}]};let t=e.tools,n=t.map(e=>{if(!e||typeof e!=`object`)return e;let{outputSchema:t,...n}=e;return n}),r=this._wireCodec().validateResult(`tools/list`,{...e,tools:n});if(!r.ok)return{issues:[{message:`Invalid non-output tools/list descriptor`}]};for(let[e,n]of r.value.tools.entries()){let r=t[e];Object.hasOwn(r,`outputSchema`)&&(n.outputSchema={type:`object`,[G]:r.outputSchema},this._wireCodec().validateResult(`tools/list`,{tools:[r]}).ok||this.warn(n.name,`tools/list`,`tools/${e}/outputSchema`,`Output schema descriptor is not MCP-compatible.`))}return{value:r.value}}async listTools(e,t){let n=await super.listTools(e,t);return{...n,tools:n.tools.map(e=>e.outputSchema&&Object.hasOwn(e.outputSchema,G)?{...e,outputSchema:e.outputSchema[G]}:e)}}async callTool(e,t){let n={toolName:e.name,result:void 0};return this.calls.run(n,async()=>{try{return await super.callTool(e,t)}catch(t){if(!(t instanceof S.ProtocolError)||t.code!==S.ProtocolErrorCode.InvalidRequest||t.message!==`Tool ${e.name} has an output schema but did not return structured content`||!n.result||n.result.structuredContent!==void 0||n.result.isError)throw t;return this.warn(e.name,`tools/call`,`structuredContent`,`Tool omitted structured output required by its output schema.`),n.result}})}};const ln=`mcp-proxy-client-manager`,un=[0,100];function dn(e){return e===`reconnecting`?`connecting`:e}var fn=class extends Error{serverName;attempts;code=`MCP_SERVER_RECOVERY_EXHAUSTED`;constructor(e,t,n){super(`MCP server "${e}" did not recover after ${t} attempts.`,n),this.serverName=e,this.attempts=t,this.name=`McpServerRecoveryExhaustedError`}};async function pn(e){let t=await Promise.allSettled([Promise.resolve().then(()=>e.client.close()),Promise.resolve().then(()=>e.stdioTransport?.close())]);try{await e.processLease?.release({kill:!1,releasePort:!1})}catch(e){t.push({status:`rejected`,reason:e})}let n=t.flatMap(e=>e.status===`rejected`?[e.reason]:[]);if(n.length)throw AggregateError(n,`MCP connection cleanup failed`)}function mn(e){return yn(e).some(({message:e,code:t})=>{let n=e.toLowerCase(),r=_n(t);return n.includes(`unknown session`)||n.includes(`session not found`)||n.includes(`transport closed`)||n.includes(`connection closed`)||n.includes(`socket hang up`)||n.includes(`fetch failed`)||r===`econnreset`})}function hn(e){return e instanceof S.UnauthorizedError||yn(e).some(({message:e,code:t})=>{let n=e.toLowerCase();return t===`401`||n.includes(`unauthorized`)||n.includes(`http 401`)||n.includes(`invalid_token`)})}function gn(e){return yn(e).some(({message:e,code:t})=>{let n=e.toLowerCase(),r=_n(t);return n.includes(`transport closed`)||n.includes(`connection closed`)||n.includes(`premature close`)||n.includes(`socket hang up`)||n.includes(`write epipe`)||n.includes(`read econnreset`)||n.includes(`fetch failed`)||r===`epipe`||r===`econnreset`})}function _n(e){return typeof e==`string`?e.toLowerCase():void 0}function vn(e){if(typeof e==`string`)return e;if(typeof e==`symbol`)return e.toString();if(typeof e==`object`&&e)try{return JSON.stringify(e)??`Unknown error`}catch{return`Unknown error`}return String(e)}function yn(e){let t=/* @__PURE__ */ new Set,n=[],r=e;for(;r&&!t.has(r);)if(t.add(r),r instanceof Error){let e=r;n.push({message:r.message,code:e.code}),r=e.cause}else if(typeof r==`object`){let e=r;n.push({message:typeof e.message==`string`?e.message:vn(r),code:typeof e.code==`string`?e.code:void 0}),r=e.cause}else{n.push({message:vn(r)});break}return n}var bn=class{outputGuard;serverName;serverInstruction;toolBlacklist;omitToolDescription;prompts;transport;logger;client;stdioTransport;processLease;connected=!1;lifecycleState=`closed`;recoveryError;reconnectFn;reconnectPromise;recoverAuthorization;authorizationRecovery;pendingLeaseRelease;onDisconnected;onLifecycleChange;closing=!1;closePromise;lifetime=new AbortController;constructor(e,t,n,r,i,a){this.outputGuard=a,this.serverName=e,this.serverInstruction=i.instruction,this.toolBlacklist=i.toolBlacklist,this.omitToolDescription=i.omitToolDescription,this.prompts=i.prompts,this.transport=t,this.logger=r,this.client=n}setAuthorizationRecovery(e){this.recoverAuthorization=e}setStdioTransport(e){this.stdioTransport=e}setProcessLease(e){this.processLease=e}setConnected(e){this.connected=e,this.setLifecycleState(e?`connected`:`closed`),e&&(this.recoveryError=void 0)}getLifecycleState(){return this.lifecycleState}setLifecycleChangeCallback(e){this.onLifecycleChange=e}setLifecycleState(e,t){this.lifecycleState!==e&&(this.lifecycleState=e,this.onLifecycleChange?.(e,t))}async waitUntilReady(){await this.ensureReady()}getServerCapabilities(){return this.client.getServerCapabilities()}setReconnectFn(e){this.reconnectFn=e}setDisconnectedCallback(e){this.onDisconnected=e}handleStdioClosed(e){if(this.stdioTransport!==e||this.closing)return;let t=this.processLease;this.stdioTransport=void 0,this.processLease=void 0,this.connected=!1,this.recoveryError=void 0,this.setLifecycleState(`reconnecting`),this.pendingLeaseRelease=this.releaseProcessLease(t),!this.closing&&(this.logger.warn(`mcp_proxy.server.disconnected`,{"mcp.server.name":this.serverName,"mcp.server.transport":this.transport,"mcp.server.lifecycle":this.lifecycleState,"mcp.server.disconnect_reason":`stdio transport closed`,"mcp.server.recovery_mode":`proactive`}),this.onDisconnected?.(this),this.reconnectClient(`stdio transport closed`).catch(()=>{}))}async releaseProcessLease(e){try{await e?.release({kill:!1,releasePort:!1})}catch(e){this.logger.warn(`Failed to release process lease for ${this.serverName}`,e)}}async releaseOwnedResources(e){let t=this.stdioTransport,n=this.processLease,r=this.pendingLeaseRelease;this.stdioTransport=void 0,this.processLease=void 0,this.pendingLeaseRelease=void 0;try{t&&e.killChild&&await t.close()}finally{await Promise.all([r,n?.release({kill:!1,releasePort:!1})])}}async reconnectClient(e=`recoverable transport error`){if(this.lifetime.signal.throwIfAborted(),!this.reconnectFn)throw Error(`No reconnect function configured for ${this.serverName}`);let t=this.reconnectFn;this.reconnectPromise||=(async()=>{this.connected=!1,this.setLifecycleState(`reconnecting`),this.recoveryError=void 0,await this.releaseOwnedResources({killChild:!0});try{await this.client.close()}catch(e){this.logger.warn(`Failed to close stale client for ${this.serverName}`,e)}let n;for(let[r,i]of un.entries()){i>0&&await(0,x.setTimeout)(i,void 0,{signal:this.lifetime.signal}),this.lifetime.signal.throwIfAborted();try{let n=await t(this.lifetime.signal);this.closing&&(await pn(n),this.lifetime.signal.throwIfAborted()),this.client=n.client,this.recoverAuthorization=n.recoverAuthorization,n.stdioTransport&&(this.stdioTransport=n.stdioTransport),n.processLease&&(this.processLease=n.processLease),this.connected=!0,this.setLifecycleState(`connected`),this.logger.info(`mcp_proxy.server.recovered`,{"mcp.server.name":this.serverName,"mcp.server.transport":this.transport,"mcp.server.lifecycle":`connected`,"mcp.server.recovery_attempt":r+1,"mcp.server.recovery_trigger":e});return}catch(e){this.lifetime.signal.throwIfAborted(),n=e,this.logger.warn(`mcp_proxy.server.reconnect_failed`,{"mcp.server.name":this.serverName,"mcp.server.transport":this.transport,"mcp.server.recovery_attempt":r+1,"error.message":e instanceof Error?e.message:String(e)})}}throw this.connected=!1,this.recoveryError=new fn(this.serverName,un.length,{cause:n}),this.setLifecycleState(`degraded`,this.recoveryError.message),this.logger.error(`mcp_proxy.server.degraded`,{"mcp.server.name":this.serverName,"mcp.server.transport":this.transport,"mcp.server.lifecycle":`degraded`,"mcp.server.recovery_attempts":un.length,"error.message":n instanceof Error?n.message:String(n)}),this.recoveryError})();let n=this.reconnectPromise;try{await n}finally{this.reconnectPromise===n&&(this.reconnectPromise=void 0)}}async ensureReady(){if(this.lifetime.signal.throwIfAborted(),!this.connected){if(this.recoveryError)throw this.recoveryError;await this.reconnectClient(`operation requested while disconnected`)}}async withSessionRetry(e,t){t?.throwIfAborted(),await this.ensureReady(),t?.throwIfAborted();try{return await e()}catch(n){if(t?.throwIfAborted(),(n instanceof S.UnauthorizedError||n instanceof S.InsufficientScopeError)&&this.recoverAuthorization)return this.authorizationRecovery??=this.recoverAuthorization(n,t?AbortSignal.any([t,this.lifetime.signal]):this.lifetime.signal).then(()=>this.reconnectClient(`authorization recovered`)).finally(()=>{this.authorizationRecovery=void 0}),await this.authorizationRecovery,t?.throwIfAborted(),e();if(!this.reconnectFn||!mn(n)&&!gn(n))throw n;return this.logger.warn(`Session error for ${this.serverName}, reconnecting: ${n instanceof Error?n.message:String(n)}`),await this.reconnectClient(n instanceof Error?n.message:String(n)),t?.throwIfAborted(),await e()}}async listTools(){return this.withSessionRetry(async()=>(await this.client.listTools()).tools)}async listResources(){return this.withSessionRetry(async()=>(await this.client.listResources()).resources)}async listPrompts(){return this.withSessionRetry(async()=>(await this.client.listPrompts()).prompts)}async callTool(e,t,n){let r=n?.signal?AbortSignal.any([n.signal,this.lifetime.signal]):this.lifetime.signal,i=await this.withSessionRetry(async()=>{let i={signal:r,...n?.timeout===void 0?{}:{timeout:n.timeout}},a=await this.client.callTool({name:e,arguments:t},i);if(!(0,S.isCallToolResult)(a))throw Error(`Unexpected interactive response from MCP tool '${e}': this call requires a context-aware relay`);return a},r);if(r.throwIfAborted(),n?.onResult)try{await n.onResult(structuredClone(i))}catch(t){this.logger.warn(`MCP result observer failed for ${this.serverName}.${e}`,t)}return r.throwIfAborted(),this.outputGuard?await this.outputGuard.guard(i):i}async readResource(e,t){let n=t?.signal?AbortSignal.any([t.signal,this.lifetime.signal]):this.lifetime.signal,r=await this.withSessionRetry(async()=>await this.client.readResource({uri:e},{signal:n,...t?.timeout===void 0?{}:{timeout:t.timeout}}),n);return n.throwIfAborted(),r}async getPrompt(e,t){return this.withSessionRetry(async()=>await this.client.getPrompt({name:e,arguments:t}))}close(){return this.closePromise||=(this.closing=!0,this.lifetime.abort(/* @__PURE__ */ Error(`MCP client '${this.serverName}' is closed`)),this.connected=!1,this.setLifecycleState(`closed`),(async()=>{let e=(await Promise.allSettled([this.releaseOwnedResources({killChild:!0}),Promise.resolve().then(()=>this.client.close())])).flatMap(e=>e.status===`rejected`?[e.reason]:[]);if(e.length)throw AggregateError(e,`Failed to close MCP client '${this.serverName}'`)})()),this.closePromise}},xn=class{outputSchemaValidation;clientCapabilities;clients=/* @__PURE__ */ new Map;serverConfigs=/* @__PURE__ */ new Map;connectionPromises=/* @__PURE__ */ new Map;connectionControllers=/* @__PURE__ */ new Map;disposed=!1;disposePromise;logger;repositoryPath;workspaceRoot;executionCwd;env;processOwnerId=(0,u.randomUUID)();outputGuard;stateListeners=/* @__PURE__ */ new Set;serverStates=/* @__PURE__ */ new Map;tokenStore;serverTokenStores=/* @__PURE__ */ new Map;serverRemovals=/* @__PURE__ */ new Map;onAuthorizationUrl;callbackTimeoutMs;callbackServer;resolveUrlCredentials;constructor(e=console,t=process.cwd(),n=new U,r={},i,a,o,s,c={}){this.outputSchemaValidation=s,this.clientCapabilities=c,this.logger=e,this.repositoryPath=t,this.workspaceRoot=i,this.executionCwd=o,this.env=a,this.outputGuard=n,this.tokenStore=r.tokenStore,this.onAuthorizationUrl=r.onAuthorizationUrl,this.callbackTimeoutMs=r.callbackTimeoutMs,this.callbackServer=r.callbackServer,this.resolveUrlCredentials=r.resolveUrlCredentials}onServerStateChange(e){this.stateListeners.add(e);for(let[t,n]of this.serverStates)this.notifyListener(e,{...n,serverName:t});return()=>{this.stateListeners.delete(e)}}getServerState(e){return this.serverStates.get(e)?.state}emitServerState(e){this.serverStates.set(e.serverName,e);for(let t of this.stateListeners)this.notifyListener(t,e)}notifyListener(e,t){try{e(t)}catch(e){this.logger.warn(`[mcp-proxy] Server state listener threw for ${t.serverName}: ${e instanceof Error?e.message:String(e)}`)}}setOutputGuard(e){this.outputGuard=e}async cleanupChildProcesses(){await this.disconnectAll()}async connectToServer(e,t,n){if(await this.serverRemovals.get(e),n?.signal?.throwIfAborted(),this.disposed)throw Error(`The MCP client manager is disposed`);n?.tokenStore&&this.serverTokenStores.set(e,n.tokenStore),this.serverConfigs.set(e,t);let r=()=>{this.removeServer(e).catch(()=>this.logger.warn(`Failed to remove cancelled MCP server '${e}'.`))};n?.signal?.addEventListener(`abort`,r,{once:!0});try{await this.ensureConnected(e),n?.signal?.throwIfAborted()}finally{n?.signal?.removeEventListener(`abort`,r)}}async removeServer(e){let t=this.serverRemovals.get(e);if(t)return t;let n=this.serverTokenStores.get(e);this.serverConfigs.delete(e);let r=(async()=>{try{await this.disconnectServer(e)}finally{this.serverTokenStores.get(e)===n&&this.serverTokenStores.delete(e),await n?.clear(e),this.serverStates.delete(e)}})();this.serverRemovals.set(e,r);try{await r}finally{this.serverRemovals.get(e)===r&&this.serverRemovals.delete(e)}}registerServerConfigs(e){for(let[t,n]of Object.entries(e))this.serverConfigs.set(t,n)}getKnownServerNames(){return Array.from(this.serverConfigs.keys())}getServerRequestTimeout(e){return this.serverConfigs.get(e)?.requestTimeout}async ensureConnected(e){if(this.disposed)throw Error(`The MCP client manager is disposed`);let t=this.clients.get(e);if(t)return await t.waitUntilReady(),t;let n=this.connectionPromises.get(e);if(n)return await n;let r=this.serverConfigs.get(e);if(!r)throw Error(`No configuration found for server "${e}"`);let i=this.createConnection(e,r);this.connectionPromises.set(e,i);try{return await i}finally{this.connectionPromises.get(e)===i&&this.connectionPromises.delete(e)}}async createConnection(e,t){let n=t.timeout??3e4,r=new AbortController;this.connectionControllers.set(e,r),this.emitServerState({serverName:e,state:`connecting`});let i,a=new Promise((e,t)=>{i=()=>t(r.signal.reason),r.signal.addEventListener(`abort`,i,{once:!0}),r.signal.aborted&&i()}),o,s,c=new Promise((e,t)=>{s=e=>{clearTimeout(o),!(e||r.signal.aborted)&&(o=setTimeout(()=>{let e=/* @__PURE__ */ Error(`Connection timeout after ${n}ms`);r.abort(e),t(e)},n))},s(!1)}),l;try{l=await Promise.race([this.createSdkClientConnection(e,t,r.signal,e=>s?.(e)),c,a])}catch(n){let i=t.transport===`http`||t.transport===`sse`?t.config.auth:void 0,a=typeof i==`object`&&i?.type===`api-key`,o=n instanceof Error&&[`Invalid MCP API-key configuration.`,`MCP API-key credential is unavailable.`,`MCP credential destination was rejected.`].includes(n.message),s=a&&!o?/* @__PURE__ */ Error(`MCP API-key connection failed.`):n;throw this.emitServerState({serverName:e,state:this.disposed||r.signal.aborted&&!String(r.signal.reason).includes(`timeout`)?`closed`:hn(s)?`needs-auth`:`failed`,error:s instanceof Error?s.message:String(s)}),s}finally{s=void 0,clearTimeout(o),r.signal.removeEventListener(`abort`,i),this.connectionControllers.get(e)===r&&this.connectionControllers.delete(e)}let u=new bn(e,t.transport,l.client,this.logger,{instruction:t.instruction,toolBlacklist:t.toolBlacklist,omitToolDescription:t.omitToolDescription,prompts:t.prompts},this.outputGuard);u.setDisconnectedCallback(()=>void 0),u.setAuthorizationRecovery(l.recoverAuthorization),u.setLifecycleChangeCallback((t,n)=>{this.emitServerState({serverName:e,state:dn(t),...n?{error:n}:{}})});try{if(l.stdioTransport&&u.setStdioTransport(l.stdioTransport),l.processLease&&u.setProcessLease(l.processLease),r.signal.throwIfAborted(),this.disposed)throw Error(`The MCP client manager is disposed`);if(u.setConnected(!0),u.setReconnectFn(async n=>{try{if(this.disposed)throw Error(`The MCP client manager is disposed`);return await this.createSdkClientConnection(e,t,n)}catch(t){throw this.logger.warn(`Failed to reconnect to ${e}`,t),t}}),!u.serverInstruction)try{let e=u.client.getInstructions();e&&(u.serverInstruction=e)}catch(t){this.logger.warn(`Failed to get server instruction from ${e}`,t)}return this.clients.set(e,u),u}catch(e){throw await u.close(),e}}createSdkClient(e,t=`auto`){let n={name:`@agimon-ai/po-mcp-proxy-client`,version:`0.1.0`},r={capabilities:structuredClone(this.clientCapabilities),versionNegotiation:t===`legacy`?{mode:`legacy`}:{mode:`auto`,probe:{timeoutMs:1e4,maxRetries:0}}};return this.outputSchemaValidation?new cn(n,r,e,this.outputSchemaValidation):new S.Client(n,r)}async createSdkClientConnection(e,t,n,r){n?.throwIfAborted();let i;if(t.transport===`stdio`)i=await this.connectStdioSdkClient(e,this.createSdkClient(e),t.config,n);else if(t.transport===`http`||t.transport===`sse`)i=await this.connectUrlSdkClient(e,t.transport,t.config,!0,n,r);else{let{transport:e}=t;throw Error(`Unsupported transport type: ${e}`)}if(n?.aborted||this.disposed)throw await pn(i),n?.throwIfAborted(),Error(`The MCP client manager is disposed`);return i}async connectUrlSdkClient(e,t,n,r=!0,i,a){i?.throwIfAborted();let o=n.auth,s=typeof o==`object`&&o?.type===`api-key`?o:void 0,c=this.resolveAuthHeaders(n),l;if(s){let r=new URL(n.url),a=s.mode===`bearer`?`Authorization`:s.name;if(s.mode!==`bearer`&&s.mode!==`header`||!/^[!#$%&'*+.^_`|~0-9A-Za-z-]+$/.test(a)||s.mode===`header`&&(a.toLowerCase()===`authorization`||/^(?:host|cookie|set-cookie|connection|keep-alive|te|trailer|upgrade|transfer-encoding|content-length|accept|origin|referer|forwarded|x-forwarded-.*|proxy-.*|sec-.*|mcp-session-id)$/i.test(a))||r.username||r.password||r.protocol!==`https:`&&!(r.protocol===`http:`&&[`localhost`,`127.0.0.1`,`[::1]`].includes(r.hostname))||n.bearerToken||n.bearerTokenEnv||n.oauth!==void 0||Object.keys(c??{}).some(e=>[`authorization`,a.toLowerCase()].includes(e.toLowerCase())))throw Error(`Invalid MCP API-key configuration.`);let o;try{o=await this.resolveUrlCredentials?.({serverName:e,transport:t,url:r.href,auth:s,signal:i})}catch{throw Error(`MCP API-key credential is unavailable.`)}i?.throwIfAborted();let u=o?.[a];if(!u||Object.keys(o??{}).length!==1||Array.from(u).some(e=>e.charCodeAt(0)<=31||e.charCodeAt(0)===127)||s.mode===`bearer`&&!/^Bearer [^\s]+$/.test(u))throw Error(`MCP API-key credential is unavailable.`);c={...c,[a]:u},l=async(e,t)=>{let n=new URL(typeof e==`string`?e:e instanceof URL?e.href:e.url);if(n.origin!==r.origin||n.username||n.password)throw Error(`MCP credential destination was rejected.`);return fetch(e,{...t,redirect:`error`})}}let u=this.createSdkClient(e,t===`sse`?`legacy`:`auto`),d=c?{headers:c}:void 0,f=await this.createAuthProvider(e,n);await f?.prepareCallback(),i?.throwIfAborted();let p=f?new Ut(f,n.url,i):void 0,m={requestInit:d,authProvider:p,...l?{fetch:l}:{}},h=t===`http`?new S.StreamableHTTPClientTransport(new URL(n.url),m):new S.SSEClientTransport(new URL(n.url),m),g=()=>{f?.cancelAuthorization(),u.close().catch(t=>this.logger.warn(`Failed to close cancelled client ${e}`,s?`API-key connection closed`:t))};i?.addEventListener(`abort`,g,{once:!0});let _=!1;try{return await u.connect(h),i?.throwIfAborted(),_=!0,{client:u,...p?{recoverAuthorization:(e,t)=>p.recover(e,t)}:{}}}catch(o){if(i?.throwIfAborted(),s)throw Error(`MCP API-key connection failed.`);if(!f||!p||!r||!(o instanceof S.InsufficientScopeError)&&(!hn(o)||!f.hasPendingAuthorization()))throw o;return this.logger.info(`[oauth] Waiting for authorization of MCP server "${e}"`),a?.(!0),await p.recover(o,i,()=>{a?.(!1),this.emitServerState({serverName:e,state:`connecting`})}),i?.throwIfAborted(),await this.connectUrlSdkClient(e,t,n,!1,i,a)}finally{i?.removeEventListener(`abort`,g),_||(f?.cancelAuthorization(),await u.close().catch(t=>this.logger.warn(`Failed to close client ${e}`,s?`API-key connection closed`:t)))}}resolveAuthHeaders(e){let t={...e.headers};if(Be(e)===`bearer`){let n=e.bearerTokenEnv?this.env?.[e.bearerTokenEnv]??He(e):He(e);n?t.Authorization=`Bearer ${n}`:this.logger.warn(`[oauth] Bearer auth configured for an MCP server but no token was resolved${e.bearerTokenEnv?` (env ${e.bearerTokenEnv} is unset)`:``}.`)}return Object.keys(t).length>0?t:void 0}async createAuthProvider(e,t){return Ue({serverName:e,config:t,store:this.serverTokenStores.get(e)??this.tokenStore,logger:this.logger,callbackTimeoutMs:this.callbackTimeoutMs,workspaceRoot:this.repositoryPath,...this.callbackServer?{callbackServer:this.callbackServer}:{},onAuthorizationUrl:(e,t)=>{if(this.emitServerState({serverName:t,state:`needs-auth`}),this.onAuthorizationUrl)return this.onAuthorizationUrl(e,t);this.logger.info(`[oauth] Authorize MCP server "${t}" by visiting: ${e.toString()}`)}})}relayChildStderr(e,t){t&&(t.on(`data`,t=>{let n=t.toString(`utf8`).trimEnd();n&&this.logger.debug(`[${e}] ${n}`)}),t.on(`error`,()=>void 0))}async connectStdioSdkClient(e,t,n,r){let i=new C.StdioClientTransport({command:n.command,args:n.args,cwd:this.executionCwd?(0,l.resolve)(this.executionCwd,n.cwd??`.`):this.workspaceRoot?(0,l.resolve)(this.workspaceRoot,n.cwd??`.`):n.cwd,env:{...process.env,...this.env,...n.env,PROJECT_PATH:this.repositoryPath,[g.PROCESS_REGISTRY_OWNER_KIND_ENV_VAR]:ln,[g.PROCESS_REGISTRY_OWNER_ID_ENV_VAR]:this.processOwnerId},stderr:`pipe`}),a=()=>{pn({client:t,stdioTransport:i}).catch(t=>this.logger.warn(`Failed to close cancelled stdio connection for ${e}`,t))};r?.addEventListener(`abort`,a,{once:!0});try{r?.throwIfAborted(),await t.connect(i),r?.throwIfAborted(),this.relayChildStderr(e,i.stderr);let a=i.pid;if(typeof a!=`number`||a<=0)throw Error(`Failed to capture pid for stdio child process on server "${e}"`);let o=await(0,g.createProcessLease)({repositoryPath:this.repositoryPath,serviceName:e,serviceType:`tool`,pid:a,command:n.command,args:n.args,metadata:{transport:`stdio`},ownerKind:ln,ownerId:this.processOwnerId,resourceKey:e});return t.onclose=()=>{let t=this.clients.get(e);t instanceof bn&&t.handleStdioClosed(i)},{client:t,processLease:o,stdioTransport:i}}catch(n){try{await pn({client:t,stdioTransport:i})}catch(t){this.logger.warn(`Failed to clean up incomplete stdio connection for ${e}`,t)}throw n}finally{r?.removeEventListener(`abort`,a)}}getClient(e){return this.clients.get(e)}getAllClients(){return Array.from(this.clients.values())}async disconnectServer(e){this.connectionControllers.get(e)?.abort(/* @__PURE__ */ Error(`MCP connection '${e}' was closed`));let t=this.connectionPromises.get(e);this.connectionPromises.delete(e);let n=this.clients.get(e);this.clients.delete(e),await Promise.all([t?.catch(()=>void 0),n?.close()])}async disconnectAll(){let e=[...this.clients.values()],t=[...this.connectionPromises.values()];this.clients.clear(),this.connectionPromises.clear();for(let e of this.connectionControllers.values())e.abort(/* @__PURE__ */ Error(`MCP connections were closed`));this.connectionControllers.clear();let n=(await Promise.allSettled([...e.map(e=>e.close()),...t.map(e=>e.catch(()=>void 0))])).flatMap(e=>e.status===`rejected`?[e.reason]:[]);if(n.length)throw AggregateError(n,`Failed to close MCP connections`)}dispose(){return this.disposed=!0,this.disposePromise??=this.disconnectAll(),this.disposePromise}isConnected(e){return this.clients.get(e)?.getLifecycleState()===`connected`}getConnectionState(e){return this.clients.get(e)?.getLifecycleState()}};const Sn=`install`,Cn=/^(@[a-zA-Z0-9_-]+\/)?[a-zA-Z0-9._-]+(@[a-zA-Z0-9._-]+)?$/,wn=`pipe`;function Tn(e){return typeof e==`object`&&!!e&&`command`in e}var En=class{config;constructor(e){this.config=e}extractPackages(){let e=[],{mcpConfig:t,filter:n}=this.config;for(let[r,i]of Object.entries(t.mcpServers)){if(i.disabled||i.transport!==`stdio`||!Tn(i.config))continue;let t=this.extractPackageInfo(r,i.config);if(t){if(n&&t.packageManager!==n)continue;e.push(t)}}return e}async prefetch(){try{let e=this.extractPackages(),t=[];if(e.length===0)return{totalPackages:0,successful:0,failed:0,results:[]};if(this.config.parallel){let n=e.map(async e=>this.prefetchPackage(e));t.push(...await Promise.all(n))}else for(let n of e){let e=await this.prefetchPackage(n);t.push(e)}let n=t.filter(e=>e.success).length,r=t.filter(e=>!e.success).length;return{totalPackages:e.length,successful:n,failed:r,results:t}}catch(e){throw Error(`Failed to prefetch packages: ${e instanceof Error?e.message:String(e)}`)}}async prefetchPackage(e){try{let[t,...n]=e.fullCommand,r=await this.runCommand(t,n);return{package:e,success:r.success,output:r.output}}catch(t){return{package:e,success:!1,output:t instanceof Error?t.message:String(t)}}}isValidPackageName(e){return Cn.test(e)}extractPackageInfo(e,t){let n=t.command.toLowerCase(),r=t.args||[];if(n===`npx`||n.endsWith(`/npx`)){let t=this.extractNpxPackage(r);if(t&&this.isValidPackageName(t))return{serverName:e,packageManager:`npx`,packageName:t,fullCommand:[`npm`,Sn,`-g`,t]}}if(n===`pnpx`||n.endsWith(`/pnpx`)){let t=this.extractNpxPackage(r);if(t&&this.isValidPackageName(t))return{serverName:e,packageManager:`pnpx`,packageName:t,fullCommand:[`pnpm`,`add`,`-g`,t]}}if(n===`uvx`||n.endsWith(`/uvx`)){let t=this.extractUvxPackage(r);if(t&&this.isValidPackageName(t))return{serverName:e,packageManager:`uvx`,packageName:t,fullCommand:[`uvx`,t]}}if((n===`uv`||n.endsWith(`/uv`))&&r.includes(`run`)){let t=this.extractUvRunPackage(r);if(t&&this.isValidPackageName(t))return{serverName:e,packageManager:`uv`,packageName:t,fullCommand:[`uv`,`tool`,Sn,t]}}return null}extractNpxPackage(e){for(let t=0;t<e.length;t++){let n=e[t];if(n.startsWith(`--package=`))return n.slice(10)||null;if(n===`--package`&&t+1<e.length){let n=e[t+1];if(!n.startsWith(`-`))return n}if(n===`-p`&&t+1<e.length){let n=e[t+1];if(!n.startsWith(`-`))return n}}for(let t of e)if(!t.startsWith(`-`))return t;return null}extractUvxPackage(e){for(let t of e)if(!t.startsWith(`-`))return t;return null}extractUvRunPackage(e){let t=e.indexOf(`run`);if(t===-1)return null;for(let n=t+1;n<e.length;n++){let t=e[n];if(!t.startsWith(`-`))return t}return null}runCommand(e,t){return new Promise(n=>{let r=(0,m.spawn)(e,t,{stdio:[`ignore`,wn,wn],shell:process.platform===`win32`}),i=``,a=``;r.stdout?.on(`data`,e=>{i+=e.toString()}),r.stderr?.on(`data`,e=>{a+=e.toString()}),r.on(`close`,e=>{n({success:e===0,output:i||a})}),r.on(`error`,e=>{n({success:!1,output:e.message})})})}};const Dn=`.runtime.json`;async function On(e){await(0,c.mkdir)(e,{recursive:!0,mode:448});try{await(0,c.chmod)(e,448)}catch{}}function kn(e){return typeof e==`object`&&!!e}function An(e){return kn(e)?typeof e.serverId==`string`&&typeof e.host==`string`&&typeof e.port==`number`&&e.transport===`http`&&typeof e.shutdownToken==`string`&&typeof e.startedAt==`string`&&typeof e.pid==`number`&&(e.configPath===void 0||typeof e.configPath==`string`)&&(e.runtimeLifecycle===void 0||e.runtimeLifecycle===`manual`||e.runtimeLifecycle===`stdio-http-managed`):!1}function jn(e){return e instanceof Error?e.message:String(e)}function Mn(e){try{return process.kill(e,0),!0}catch{return!1}}var K=class e{runtimeDir;logger;constructor(t,n=console){this.runtimeDir=t??e.getDefaultRuntimeDir(),this.logger=n}static getDefaultRuntimeDir(){return(0,l.join)((0,f.homedir)(),`.mcp-proxy`,`runtimes`)}getRecordPath(e){return(0,l.join)(this.runtimeDir,`${e}${Dn}`)}async write(e){await On(this.runtimeDir);let t=this.getRecordPath(e.serverId);await(0,c.writeFile)(t,JSON.stringify(e,null,2),{encoding:`utf-8`,mode:384})}async read(e){let t=this.getRecordPath(e);try{let e=await(0,c.readFile)(t,`utf-8`),n=JSON.parse(e);return An(n)?n:null}catch(n){if(kn(n)&&`code`in n&&n.code===`ENOENT`)return null;throw Error(`Failed to read runtime state for server '${e}' from '${t}': ${jn(n)}`)}}async list(){try{let e=(await(0,c.readdir)(this.runtimeDir,{withFileTypes:!0})).filter(e=>e.isFile()&&e.name.endsWith(Dn));return(await Promise.all(e.map(async e=>{try{let t=await(0,c.readFile)((0,l.join)(this.runtimeDir,e.name),`utf-8`),n=JSON.parse(t);return An(n)?n:null}catch(t){return this.logger.debug(`Skipping unreadable runtime state file ${e.name}`,t),null}}))).filter(e=>e!==null)}catch(e){if(kn(e)&&`code`in e&&e.code===`ENOENT`)return[];throw Error(`Failed to list runtime states from '${this.runtimeDir}': ${jn(e)}`)}}async listOrphans(e){return(await this.list()).filter(t=>!e.has(t.serverId))}async pruneStale(){let e=(await this.list()).filter(e=>!Mn(e.pid));return await Promise.all(e.map(async e=>this.remove(e.serverId))),e}async remove(e){await(0,c.rm)(this.getRecordPath(e),{force:!0})}},Nn=class extends Error{filePath;cause;constructor(e,t,n){super(e),this.filePath=t,this.cause=n,this.name=`SkillLoadError`}};async function Pn(e){try{return await(0,c.access)(e),!0}catch(t){if(t instanceof Error&&`code`in t&&t.code===`ENOENT`)return!1;throw Error(`Failed to check path existence for "${e}": ${t instanceof Error?t.message:`Unknown error`}`)}}var Fn=class{cwd;skillPaths;cachedSkills=null;skillsByName=null;watchers=[];pollingTimers=[];onCacheInvalidated;logger;constructor(e,t,n,r=console){this.cwd=e,this.skillPaths=t,this.onCacheInvalidated=n?.onCacheInvalidated,this.logger=r}async getSkills(){if(this.cachedSkills!==null)return this.cachedSkills;let e=[],t=/* @__PURE__ */ new Set,n=await Promise.all(this.skillPaths.map(async e=>{let t=(0,l.isAbsolute)(e)?e:(0,l.join)(this.cwd,e);return this.loadSkillsFromDirectory(t,`project`)}));for(let r of n)for(let n of r)t.has(n.name)||(e.push(n),t.add(n.name));return this.cachedSkills=e,this.skillsByName=new Map(e.map(e=>[e.name,e])),e}async getSkill(e){return this.skillsByName===null&&await this.getSkills(),this.skillsByName?.get(e)}clearCache(){this.cachedSkills=null,this.skillsByName=null}async startWatching(){this.stopWatching();let e=await Promise.all(this.skillPaths.map(async e=>{let t=(0,l.isAbsolute)(e)?e:(0,l.join)(this.cwd,e);return{skillsDir:t,exists:await Pn(t)}}));for(let{skillsDir:t,exists:n}of e){if(!n)continue;let e=new AbortController;this.watchers.push(e),this.watchDirectory(t,e.signal).catch(n=>{if(n?.name!==`AbortError`){if(this.isWatchResourceLimitError(n)){this.startPollingDirectory(t,e.signal);return}this.logger.warn(`[skill-watcher] Error watching ${t}: ${n instanceof Error?n.message:`Unknown error`}`)}})}}stopWatching(){for(let e of this.watchers)e.abort();this.watchers=[];for(let e of this.pollingTimers)clearInterval(e);this.pollingTimers=[]}async watchDirectory(e,t){let n=(0,c.watch)(e,{recursive:!0,signal:t});for await(let e of n)e.filename?.endsWith(`SKILL.md`)&&this.invalidateCache()}invalidateCache(){this.clearCache(),this.onCacheInvalidated?.()}isWatchResourceLimitError(e){return e instanceof Error&&`code`in e&&(e.code===`EMFILE`||e.code===`ENOSPC`)}startPollingDirectory(e,t){this.createSkillSnapshot(e).then(n=>{let r=n,i=setInterval(()=>{t.aborted?clearInterval(i):this.createSkillSnapshot(e).then(e=>{this.snapshotsEqual(r,e)||(r=e,this.invalidateCache())}).catch(t=>{this.logger.warn(`[skill-watcher] Polling failed for ${e}: ${t instanceof Error?t.message:`Unknown error`}`)})},3e3);this.pollingTimers.push(i),t.addEventListener(`abort`,()=>{clearInterval(i),this.pollingTimers=this.pollingTimers.filter(e=>e!==i)},{once:!0})})}async createSkillSnapshot(e){let t=/* @__PURE__ */ new Map;return await this.collectSkillSnapshots(e,t),t}async collectSkillSnapshots(e,t){let n;try{n=await(0,c.readdir)(e)}catch(e){if(e instanceof Error&&`code`in e&&e.code===`ENOENT`)return;throw e}await Promise.all(n.map(async n=>{let r=(0,l.join)(e,n),i=await(0,c.stat)(r);i.isDirectory()?await this.collectSkillSnapshots(r,t):n===`SKILL.md`&&t.set(r,i.mtimeMs)}))}snapshotsEqual(e,t){if(e.size!==t.size)return!1;for(let[n,r]of e)if(t.get(n)!==r)return!1;return!0}async loadSkillsFromDirectory(e,t){let n=[];try{if(!await Pn(e))return n}catch(t){throw new Nn(`Cannot access skills directory: ${t instanceof Error?t.message:`Unknown error`}`,e,t instanceof Error?t:void 0)}let r;try{r=await(0,c.readdir)(e)}catch(t){throw new Nn(`Failed to read skills directory: ${t instanceof Error?t.message:`Unknown error`}`,e,t instanceof Error?t:void 0)}let i=await Promise.all(r.map(async t=>{let n=(0,l.join)(e,t);try{return{entry:t,entryPath:n,stat:await(0,c.stat)(n),error:null}}catch(e){return this.logger.warn(`Skipping entry ${n}: ${e instanceof Error?e.message:`Unknown error`}`),{entry:t,entryPath:n,stat:null,error:e}}})),a=[];for(let{entry:e,entryPath:t,stat:n}of i)if(n){if(n.isDirectory()){let e=(0,l.join)(t,`SKILL.md`);a.push({filePath:e,isRootLevel:!1})}else e===`SKILL.md`&&a.push({filePath:t,isRootLevel:!0})}let o=await Promise.all(a.map(async({filePath:e,isRootLevel:n})=>{try{return!n&&!await Pn(e)?null:await this.loadSkillFile(e,t)}catch(t){return this.logger.warn(`Skipping skill at ${e}: ${t instanceof Error?t.message:`Unknown error`}`),null}}));for(let e of o)e&&n.push(e);return n}async loadSkillFile(e,t){let n;try{n=await(0,c.readFile)(e,`utf-8`)}catch(t){throw new Nn(`Failed to read skill file: ${t instanceof Error?t.message:`Unknown error`}`,e,t instanceof Error?t:void 0)}let{frontMatter:r,content:i}=kt(n);return!r||!r.name||!r.description?null:{name:r.name,description:r.description,location:t,content:i,basePath:(0,l.dirname)(e)}}};const In=/* @__PURE__ */ new Set([`localhost`,`127.0.0.1`,`::1`]);function Ln(e){return e}function Rn(e){if(typeof e!=`object`||!e)return!1;let t=Ln(e);return`status`in t&&t.status===`ok`&&`transport`in t&&t.transport===`http`&&(!(`serverId`in t)||t.serverId===void 0||typeof t.serverId==`string`)&&(!(`runtimeLifecycle`in t)||t.runtimeLifecycle===void 0||t.runtimeLifecycle===`manual`||t.runtimeLifecycle===`stdio-http-managed`)}function zn(e){if(typeof e!=`object`||!e)return!1;let t=Ln(e);return`ok`in t&&typeof t.ok==`boolean`&&`message`in t&&typeof t.message==`string`&&(!(`serverId`in t)||t.serverId===void 0||typeof t.serverId==`string`)}function Bn(e,t){if(!In.has(e.host))throw Error(`Refusing to connect to non-loopback host '${e.host}'. Only ${Array.from(In).join(`, `)} are allowed.`);return`http://${e.host}:${e.port}${t}`}function Vn(e){return e instanceof Error?e.message:String(e)}function Hn(e){return new Promise(t=>{setTimeout(t,e)})}var Un=class{runtimeStateService;logger;constructor(e=new K,t=console){this.runtimeStateService=e,this.logger=t}async stop(e){let t=e.timeoutMs??5e3;await this.pruneStaleRuntimeRecords();let n=await this.resolveRuntime(e),r=await this.fetchHealth(n,t);if(!r.reachable)throw await this.runtimeStateService.remove(n.serverId),Error(`Runtime '${n.serverId}' is not reachable at http://${n.host}:${n.port}. Removed stale runtime record.`);if(!e.force&&r.payload?.serverId&&r.payload.serverId!==n.serverId)throw Error(`Refusing to stop runtime at http://${n.host}:${n.port}: expected server ID '${n.serverId}' but health endpoint reported '${r.payload.serverId}'. Use --force to override.`);let i=e.token??n.shutdownToken;if(!i)throw Error(`No shutdown token available for runtime '${n.serverId}'.`);let a=await this.requestShutdown(n,i,t);return await this.waitForShutdown(n,t),await this.runtimeStateService.remove(n.serverId),{ok:!0,serverId:n.serverId,host:n.host,port:n.port,message:a.message}}async resolveRuntime(e){if(e.serverId){let t=await this.runtimeStateService.read(e.serverId);if(!t)throw Error(`No runtime record found for server ID '${e.serverId}'. Start the server with 'po-mcp-proxy mcp-serve --type http' first.`);return t}if(e.host===void 0||e.port===void 0)throw Error(`Provide --id or both --host and --port to select a runtime.`);let t=(await this.runtimeStateService.list()).filter(t=>t.host===e.host&&t.port===e.port);if(t.length===0)throw Error(`No runtime record found for http://${e.host}:${e.port}. Start the server with 'po-mcp-proxy mcp-serve --type http' first.`);if(t.length>1)throw Error(`Multiple runtime records match http://${e.host}:${e.port}. Retry with --id to avoid stopping the wrong server.`);return t[0]}async pruneStaleRuntimeRecords(){if(this.runtimeStateService.pruneStale)try{await this.runtimeStateService.pruneStale()}catch(e){this.logger.debug(`Failed to prune stale runtime records before stop resolution`,e)}}async fetchHealth(e,t){try{let n=await this.fetchWithTimeout(Bn(e,`/health`),{method:`GET`},t);if(!n.ok)return{reachable:!1};let r=await n.json();if(!Rn(r))throw Error(`Received invalid health response payload.`);return{reachable:!0,payload:r}}catch(t){return this.logger.debug(`Health check failed for ${e.serverId}`,t),{reachable:!1}}}async requestShutdown(e,t,n){let r=await this.fetchWithTimeout(Bn(e,`/admin/shutdown`),{method:`POST`,headers:{Authorization:`Bearer ${t}`}},n),i=await r.json();if(!zn(i))throw Error(`Received invalid shutdown response payload.`);if(!r.ok||!i.ok)throw Error(i.message);return i}async waitForShutdown(e,t){let n=Date.now()+t;for(;Date.now()<n;){if(!(await this.fetchHealth(e,Math.max(250,n-Date.now()))).reachable)return;await Hn(200)}throw Error(`Timed out waiting for runtime '${e.serverId}' to stop at http://${e.host}:${e.port}.`)}async fetchWithTimeout(e,t,n){let r=new AbortController,i=setTimeout(()=>{r.abort()},n);try{return await fetch(e,{...t,signal:r.signal})}catch(t){throw Error(`Request to '${e}' failed: ${Vn(t)}`)}finally{clearTimeout(i)}}};function q(e){return e instanceof Error?e.message:String(e)}function Wn(e,t){let n=Buffer.from(e,`utf-8`),r=Buffer.from(t,`utf-8`);return n.length===r.length&&(0,u.timingSafeEqual)(n,r)}var Gn=class{requests=/* @__PURE__ */ new Map;isAllowed(e){let t=Date.now()-6e4,n=(this.requests.get(e)??[]).filter(e=>e>t);return n.length>=5?(this.requests.set(e,n),!1):(n.push(Date.now()),this.requests.set(e,n),!0)}},Kn=class{honoApp=new ae.Hono;adminRateLimiter=new Gn;config;mcpHandler;nodeMcpHandler;adminOptions;logger;server=null;lastRequestAt=Date.now();activeRequests=0;idleTimer=null;constructor(e,t,n,r=console){this.config={mode:t.mode,host:t.host??`localhost`,port:t.port},this.adminOptions=n,this.logger=r,this.mcpHandler=(0,y.createMcpHandler)(e,{legacy:`reject`,onerror:e=>this.logger.error(`MCP handler failed: ${e.message}`)}),this.nodeMcpHandler=(0,ie.toNodeHandler)(this.mcpHandler,{onerror:e=>this.logger.error(`Failed to adapt MCP request: ${e.message}`)}),this.setupHonoRoutes()}setupHonoRoutes(){this.honoApp.get(`/health`,e=>{let t={status:`ok`,transport:`http`,serverId:this.adminOptions?.serverId,runtimeLifecycle:this.adminOptions?.runtimeLifecycle,protocolVersion:`2026-07-28`,runtimeEpoch:`modern-2026-07-28-v1`};return e.json(t)}),this.honoApp.post(`/admin/shutdown`,async e=>{try{let t=e.req.header(`x-forwarded-for`)??`unknown`;if(!this.adminRateLimiter.isAllowed(t)){let t={ok:!1,message:`Too many shutdown requests. Try again later.`,serverId:this.adminOptions?.serverId};return e.json(t,429)}return await this.handleAdminShutdownRequest(e)}catch(t){this.logger.error(`Failed to process shutdown request: ${q(t)}`);let n={ok:!1,message:`Failed to process shutdown request.`,serverId:this.adminOptions?.serverId};return e.json(n,500)}})}isAuthorizedShutdownRequest(e){let t=this.adminOptions?.shutdownToken;if(!t)return!1;let n=e.authorization;if(typeof n==`string`&&n.startsWith(`Bearer `))return Wn(n.slice(7),t);let r=e[`x-mcp-proxy-shutdown-token`];return typeof r==`string`&&Wn(r,t)}async handleAdminShutdownRequest(e){if(!this.adminOptions?.onShutdownRequested)return e.json({ok:!1,message:`Shutdown endpoint is not enabled for this server instance.`,serverId:this.adminOptions?.serverId},404);if(!this.isAuthorizedShutdownRequest({authorization:e.req.header(`authorization`),"x-mcp-proxy-shutdown-token":e.req.header(`x-mcp-proxy-shutdown-token`)}))return e.json({ok:!1,message:`Unauthorized shutdown request: invalid or missing shutdown token.`,serverId:this.adminOptions?.serverId},401);let t=e.json({ok:!0,message:`Shutdown request accepted. Stopping server gracefully.`,serverId:this.adminOptions?.serverId});return this.adminOptions.onShutdownRequested().catch(e=>{this.logger.error(`Shutdown request failed: ${q(e)}`)}),t}async handleMcpRequest(e,t){this.activeRequests+=1;try{await this.nodeMcpHandler(e,t)}catch(e){this.logger.error(`MCP request failed: ${q(e)}`),t.headersSent||(t.writeHead(500,{"Content-Type":`application/json`}),t.end(JSON.stringify({jsonrpc:`2.0`,error:{code:-32603,message:`MCP request failed.`},id:null})))}finally{--this.activeRequests,this.lastRequestAt=Date.now()}}startIdleShutdownMonitor(){let e=this.adminOptions?.idleShutdownMs??0;e<=0||this.idleTimer||(this.lastRequestAt=Date.now(),this.idleTimer=setInterval(()=>{this.shutdownWhenIdle(e)},Math.min(e,6e4)))}stopIdleShutdownMonitor(){this.idleTimer&&=(clearInterval(this.idleTimer),null)}async shutdownWhenIdle(e){let t=this.adminOptions?.onShutdownRequested;if(!(!t||this.activeRequests>0||Date.now()-this.lastRequestAt<e)){this.stopIdleShutdownMonitor(),this.logger.info(`Stopping idle HTTP runtime after ${e}ms without active requests`);try{await t()}catch(e){this.logger.error(`Failed to stop idle HTTP runtime: ${q(e)}`)}}}async start(){if(this.server)throw Error(`HTTP transport is already running`);let e=(0,re.getRequestListener)(this.honoApp.fetch),t=(0,d.createServer)((t,n)=>{this.lastRequestAt=Date.now(),new URL(t.url??`/`,`http://${t.headers.host??this.config.host}`).pathname===`/mcp`?this.handleMcpRequest(t,n):e(t,n)});try{t.listen(this.config.port,this.config.host),await Promise.race([(0,w.once)(t,`listening`),(0,w.once)(t,`error`).then(([e])=>Promise.reject(e))]),this.server=t,this.startIdleShutdownMonitor(),this.logger.info(`@agimon-ai/po-mcp-proxy MCP server started on http://${this.config.host}:${this.config.port}/mcp`),this.logger.info(`Health check: http://${this.config.host}:${this.config.port}/health`)}catch(e){throw t.close(),Error(`Failed to start HTTP transport: ${q(e)}`,{cause:e})}}async stop(){this.stopIdleShutdownMonitor();let e=this.server;if(e){this.server=null,await this.mcpHandler.close();try{await(0,T.promisify)(e.close.bind(e))()}catch(e){throw Error(`Failed to stop HTTP transport: ${q(e)}`,{cause:e})}}}getPort(){let e=this.server?.address();return e&&typeof e==`object`?e.port:this.config.port}getHost(){return this.config.host}};const qn=`localhost`,Jn=12e4,Yn=`MCP_PROXY_RUNTIME_DETACHED`;function Xn(){return process.env[Yn]===`1`}async function Zn(e){return(await new g.ProcessRegistryService(process.env.PROCESS_REGISTRY_PATH).listProcesses({repositoryPath:e,serviceName:`mcp-proxy-http`,liveOnly:!0})).filter(e=>!!(e.host&&e.port)).map(e=>{let t=e.metadata;return{host:e.host,port:e.port,serverId:t?.serverId??`unknown`,runtimeLifecycle:t?.runtimeLifecycle===`stdio-http-managed`||t?.runtimeLifecycle===`manual`?t.runtimeLifecycle:void 0,createdAt:e.createdAt}}).sort((e,t)=>(e.createdAt??``).localeCompare(t.createdAt??``))}async function Qn(e){let t=await Zn(e);for(let e of t)if($n(await er(e.host,e.port)))return e;return null}function $n(e){return e.ok&&e.payload?.status===`ok`&&e.payload.transport===`http`&&e.payload.protocolVersion===`2026-07-28`&&e.payload.runtimeEpoch===`modern-2026-07-28-v1`}async function er(e,t,n={}){let r=n.timeoutMs??1500,i=n.retries??2,a=n.retryDelayMs??150,o=i+1,s=`unknown error`;for(let n=1;n<=o;n+=1){try{let i=await fetch(`http://${e}:${t}/health`,{signal:AbortSignal.timeout(r)});if(i.ok)return{ok:!0,payload:await i.json().catch(()=>null)??void 0,attempts:n};s=`health endpoint returned status ${i.status}`}catch(e){s=e instanceof Error?e.message:String(e)}n<o&&await new Promise(e=>setTimeout(e,a))}return{ok:!1,error:s,attempts:o}}async function tr(){let e=(0,h.fileURLToPath)(require("url").pathToFileURL(__filename).href),t=l.default.dirname(e),n=[l.default.resolve(t,`cli.mjs`),l.default.resolve(t,`..`,`dist`,`cli.mjs`),l.default.resolve(t,`..`,`..`,`dist`,`cli.mjs`)],r=[l.default.resolve(t,`..`,`cli.ts`),l.default.resolve(t,`..`,`..`,`src`,`cli.ts`)];for(let e of n)try{return await(0,c.access)(e),{command:process.execPath,args:[e]}}catch{continue}for(let e of r)try{return await(0,c.access)(e),{command:process.execPath,args:[`--import`,`tsx`,e]}}catch{continue}throw Error(`Unable to locate mcp-proxy CLI entrypoint`)}function nr(e){if(!e)return Jn;let t=Number.parseInt(e,10);if(!Number.isInteger(t)||t<=0)throw Error(`Invalid timeout value: ${e}`);return t}async function rr(e,t){let n=Date.now()+t;for(;Date.now()<n;){try{await(0,c.access)(e);return}catch{}await new Promise(e=>setTimeout(e,250))}throw Error(`Timed out waiting for runtime state file: ${e}`)}async function ir(e,t){let n=new K,r=Date.now()+t;for(;Date.now()<r;){let t=await n.read(e);if(t&&$n(await er(t.host,t.port,{retries:0})))return{host:t.host,port:t.port};await new Promise(e=>setTimeout(e,250))}throw Error(`Timed out waiting for HTTP runtime '${e}' to become healthy`)}async function ar(e,t,n){let{command:r,args:i}=await tr(),a=(0,m.spawn)(r,[...i,...e],{detached:!0,cwd:n,stdio:`ignore`,env:t});return a.unref(),a}async function or(e){let t=e.id||Ot(),n=nr(e.timeoutMs),r=e.registryPath||e.registryDir,i=await D();await new K().pruneStale();let a=await Qn(i);if(a)return{host:a.host,port:a.port,serverId:a.serverId,workspaceRoot:i,reusedExistingRuntime:!0,runtimeLifecycle:a.runtimeLifecycle??`manual`};let o=e.port,s=await Zn(i);if(e.id&&s.some(e=>e.serverId===t))throw Error(`Runtime ID '${t}' belongs to an incompatible HTTP runtime; choose a new ID`);if(o!==void 0&&s.some(t=>t.host===(e.host||qn)&&t.port===o))throw Error(`Port ${o} belongs to a different HTTP runtime; choose a free port`);let c=o,u={...process.env,[Yn]:`1`,...e.runtimeLifecycle?{MCP_PROXY_RUNTIME_LIFECYCLE:e.runtimeLifecycle}:{},...r?{PORT_REGISTRY_PATH:r,PROCESS_REGISTRY_PATH:(0,g.resolveSiblingRegistryPath)(r,`processes.json`)}:{}},d=await ar([`mcp-serve`,`--type`,`http`,`--id`,t,`--host`,e.host||qn,...c===void 0?[]:[`--port`,String(c)],...Tt(e.config).flatMap(e=>[`--config`,e]),...e.cache===!1?[`--no-cache`]:[],...e.definitionsCache?[`--definitions-cache`,e.definitionsCache]:[],...e.clearDefinitionsCache?[`--clear-definitions-cache`]:[],...e.idleShutdownMinutes===void 0?[]:[`--idle-shutdown-minutes`,String(e.idleShutdownMinutes)],`--proxy-mode`,e.proxyMode],u,i),f=new Promise((e,t)=>{d.once(`exit`,(e,n)=>{t(/* @__PURE__ */ Error(`Background runtime exited before becoming healthy (code=${e??`null`}, signal=${n??`null`})`))})}),p=l.default.join(K.getDefaultRuntimeDir(),`${t}.runtime.json`);try{await Promise.race([rr(p,n),f]);let{host:r,port:a}=await Promise.race([ir(t,n),f]);return{host:r,port:a,serverId:t,workspaceRoot:i,reusedExistingRuntime:!1,runtimeLifecycle:e.runtimeLifecycle??`manual`}}catch(e){throw Error(`Failed to prestart HTTP runtime '${t}': ${e instanceof Error?e.message:String(e)}`,{cause:e})}}function sr(e){process.stdout.write(`mcp-proxy HTTP runtime ready at http://${e.host}:${e.port} (${e.serverId})\n`),process.stdout.write(`runtimeId=${e.serverId}\n`),process.stdout.write(`runtimeUrl=http://${e.host}:${e.port}\n`),process.stdout.write(`workspaceRoot=${e.workspaceRoot}\n`)}var cr=`0.0.1-alpha.35`,lr=`<toolkit id="{{ serverId }}">
|
|
9
|
+
`).length}}function W(e){return Buffer.byteLength(e,`utf8`)}function rn(e){if(typeof e!=`number`||!Number.isFinite(e))return;let t=Math.floor(e);return t>0?t:void 0}function an(e){let t=process.env[e]?.trim().toLowerCase();if(t){if([`0`,`false`,`no`,`off`].includes(t))return!1;if([`1`,`true`,`yes`,`on`].includes(t))return!0}}function on(e){return e<1024?`${e} B`:e<1048576?`${(e/1024).toFixed(1)} KiB`:`${(e/1048576).toFixed(1)} MiB`}const G=`x-mcp-proxy-original-output-schema`;function sn(e){return Array.from(e.slice(0,256)).filter(e=>{let t=e.charCodeAt(0);return t>=32&&t<=126||t>=160}).join(``)}var cn=class extends S.Client{serverName;compatibility;calls;constructor(e,t,n,r){let i=new te.AsyncLocalStorage,a=new ne.AjvJsonSchemaValidator;super(e,{...t,jsonSchemaValidator:{getValidator:e=>t=>{let n=i.getStore()?.toolName,r=Object.hasOwn(e,G)?e[G]:e;try{!a.getValidator(r)(t).valid&&n&&this.warn(n,`tools/call`,`structuredContent`,`Structured output does not match its output schema.`)}catch{n&&this.warn(n,`tools/call`,`outputSchema`,`Output schema could not be compiled or evaluated.`)}return{valid:!0,data:t,errorMessage:void 0}}}}),this.serverName=n,this.compatibility=r,this.calls=i}warn(e,t,n,r){try{Promise.resolve(this.compatibility.onWarning({serverName:sn(this.serverName),toolName:sn(e),method:t,path:n,message:r})).catch(()=>{})}catch{}}request(e,t,n){if(t&&`~standard`in t)return super.request(e,t,n);if(e.method!==`tools/list`){let n=super.request(e,t);return e.method===`tools/call`?n.then(e=>{let t=this.calls.getStore();return t&&(t.result=e),e}):n}return super.request(e,{"~standard":{version:1,vendor:`mcp-proxy-output-compatibility`,validate:e=>this.validateListing(e)}},t)}validateListing(e){if(!e||typeof e!=`object`||!(`tools`in e)||!Array.isArray(e.tools))return{issues:[{message:`Invalid tools/list result`}]};let t=e.tools,n=t.map(e=>{if(!e||typeof e!=`object`)return e;let{outputSchema:t,...n}=e;return n}),r=this._wireCodec().validateResult(`tools/list`,{...e,tools:n});if(!r.ok)return{issues:[{message:`Invalid non-output tools/list descriptor`}]};for(let[e,n]of r.value.tools.entries()){let r=t[e];Object.hasOwn(r,`outputSchema`)&&(n.outputSchema={type:`object`,[G]:r.outputSchema},this._wireCodec().validateResult(`tools/list`,{tools:[r]}).ok||this.warn(n.name,`tools/list`,`tools/${e}/outputSchema`,`Output schema descriptor is not MCP-compatible.`))}return{value:r.value}}async listTools(e,t){let n=await super.listTools(e,t);return{...n,tools:n.tools.map(e=>e.outputSchema&&Object.hasOwn(e.outputSchema,G)?{...e,outputSchema:e.outputSchema[G]}:e)}}async callTool(e,t){let n={toolName:e.name,result:void 0};return this.calls.run(n,async()=>{try{return await super.callTool(e,t)}catch(t){if(!(t instanceof S.ProtocolError)||t.code!==S.ProtocolErrorCode.InvalidRequest||t.message!==`Tool ${e.name} has an output schema but did not return structured content`||!n.result||n.result.structuredContent!==void 0||n.result.isError)throw t;return this.warn(e.name,`tools/call`,`structuredContent`,`Tool omitted structured output required by its output schema.`),n.result}})}};const ln=`mcp-proxy-client-manager`,un=[0,100];function dn(e){return e===`reconnecting`?`connecting`:e}var fn=class extends Error{serverName;attempts;code=`MCP_SERVER_RECOVERY_EXHAUSTED`;constructor(e,t,n){super(`MCP server "${e}" did not recover after ${t} attempts.`,n),this.serverName=e,this.attempts=t,this.name=`McpServerRecoveryExhaustedError`}};async function pn(e){let t=await Promise.allSettled([Promise.resolve().then(()=>e.client.close()),Promise.resolve().then(()=>e.stdioTransport?.close())]);try{await e.processLease?.release({kill:!1,releasePort:!1})}catch(e){t.push({status:`rejected`,reason:e})}let n=t.flatMap(e=>e.status===`rejected`?[e.reason]:[]);if(n.length)throw AggregateError(n,`MCP connection cleanup failed`)}function mn(e){return yn(e).some(({message:e,code:t})=>{let n=e.toLowerCase(),r=_n(t);return n.includes(`unknown session`)||n.includes(`session not found`)||n.includes(`transport closed`)||n.includes(`connection closed`)||n.includes(`socket hang up`)||n.includes(`fetch failed`)||r===`econnreset`})}function hn(e){return e instanceof S.UnauthorizedError||yn(e).some(({message:e,code:t})=>{let n=e.toLowerCase();return t===`401`||n.includes(`unauthorized`)||n.includes(`http 401`)||n.includes(`invalid_token`)})}function gn(e){return yn(e).some(({message:e,code:t})=>{let n=e.toLowerCase(),r=_n(t);return n.includes(`transport closed`)||n.includes(`connection closed`)||n.includes(`premature close`)||n.includes(`socket hang up`)||n.includes(`write epipe`)||n.includes(`read econnreset`)||n.includes(`fetch failed`)||r===`epipe`||r===`econnreset`})}function _n(e){return typeof e==`string`?e.toLowerCase():void 0}function vn(e){if(typeof e==`string`)return e;if(typeof e==`symbol`)return e.toString();if(typeof e==`object`&&e)try{return JSON.stringify(e)??`Unknown error`}catch{return`Unknown error`}return String(e)}function yn(e){let t=/* @__PURE__ */ new Set,n=[],r=e;for(;r&&!t.has(r);)if(t.add(r),r instanceof Error){let e=r;n.push({message:r.message,code:e.code}),r=e.cause}else if(typeof r==`object`){let e=r;n.push({message:typeof e.message==`string`?e.message:vn(r),code:typeof e.code==`string`?e.code:void 0}),r=e.cause}else{n.push({message:vn(r)});break}return n}var bn=class{outputGuard;serverName;serverInstruction;toolBlacklist;omitToolDescription;prompts;transport;logger;client;stdioTransport;processLease;connected=!1;lifecycleState=`closed`;recoveryError;reconnectFn;reconnectPromise;recoverAuthorization;authorizationRecovery;pendingLeaseRelease;onDisconnected;onLifecycleChange;closing=!1;closePromise;lifetime=new AbortController;constructor(e,t,n,r,i,a){this.outputGuard=a,this.serverName=e,this.serverInstruction=i.instruction,this.toolBlacklist=i.toolBlacklist,this.omitToolDescription=i.omitToolDescription,this.prompts=i.prompts,this.transport=t,this.logger=r,this.client=n}setAuthorizationRecovery(e){this.recoverAuthorization=e}setStdioTransport(e){this.stdioTransport=e}setProcessLease(e){this.processLease=e}setConnected(e){this.connected=e,this.setLifecycleState(e?`connected`:`closed`),e&&(this.recoveryError=void 0)}getLifecycleState(){return this.lifecycleState}setLifecycleChangeCallback(e){this.onLifecycleChange=e}setLifecycleState(e,t){this.lifecycleState!==e&&(this.lifecycleState=e,this.onLifecycleChange?.(e,t))}async waitUntilReady(){await this.ensureReady()}getServerCapabilities(){return this.client.getServerCapabilities()}setReconnectFn(e){this.reconnectFn=e}setDisconnectedCallback(e){this.onDisconnected=e}handleStdioClosed(e){if(this.stdioTransport!==e||this.closing)return;let t=this.processLease;this.stdioTransport=void 0,this.processLease=void 0,this.connected=!1,this.recoveryError=void 0,this.setLifecycleState(`reconnecting`),this.pendingLeaseRelease=this.releaseProcessLease(t),!this.closing&&(this.logger.warn(`mcp_proxy.server.disconnected`,{"mcp.server.name":this.serverName,"mcp.server.transport":this.transport,"mcp.server.lifecycle":this.lifecycleState,"mcp.server.disconnect_reason":`stdio transport closed`,"mcp.server.recovery_mode":`proactive`}),this.onDisconnected?.(this),this.reconnectClient(`stdio transport closed`).catch(()=>{}))}async releaseProcessLease(e){try{await e?.release({kill:!1,releasePort:!1})}catch(e){this.logger.warn(`Failed to release process lease for ${this.serverName}`,e)}}async releaseOwnedResources(e){let t=this.stdioTransport,n=this.processLease,r=this.pendingLeaseRelease;this.stdioTransport=void 0,this.processLease=void 0,this.pendingLeaseRelease=void 0;try{t&&e.killChild&&await t.close()}finally{await Promise.all([r,n?.release({kill:!1,releasePort:!1})])}}async reconnectClient(e=`recoverable transport error`){if(this.lifetime.signal.throwIfAborted(),!this.reconnectFn)throw Error(`No reconnect function configured for ${this.serverName}`);let t=this.reconnectFn;this.reconnectPromise||=(async()=>{this.connected=!1,this.setLifecycleState(`reconnecting`),this.recoveryError=void 0,await this.releaseOwnedResources({killChild:!0});try{await this.client.close()}catch(e){this.logger.warn(`Failed to close stale client for ${this.serverName}`,e)}let n;for(let[r,i]of un.entries()){i>0&&await(0,x.setTimeout)(i,void 0,{signal:this.lifetime.signal}),this.lifetime.signal.throwIfAborted();try{let n=await t(this.lifetime.signal);this.closing&&(await pn(n),this.lifetime.signal.throwIfAborted()),this.client=n.client,this.recoverAuthorization=n.recoverAuthorization,n.stdioTransport&&(this.stdioTransport=n.stdioTransport),n.processLease&&(this.processLease=n.processLease),this.connected=!0,this.setLifecycleState(`connected`),this.logger.info(`mcp_proxy.server.recovered`,{"mcp.server.name":this.serverName,"mcp.server.transport":this.transport,"mcp.server.lifecycle":`connected`,"mcp.server.recovery_attempt":r+1,"mcp.server.recovery_trigger":e});return}catch(e){this.lifetime.signal.throwIfAborted(),n=e,this.logger.warn(`mcp_proxy.server.reconnect_failed`,{"mcp.server.name":this.serverName,"mcp.server.transport":this.transport,"mcp.server.recovery_attempt":r+1,"error.message":e instanceof Error?e.message:String(e)})}}throw this.connected=!1,this.recoveryError=new fn(this.serverName,un.length,{cause:n}),this.setLifecycleState(`degraded`,this.recoveryError.message),this.logger.error(`mcp_proxy.server.degraded`,{"mcp.server.name":this.serverName,"mcp.server.transport":this.transport,"mcp.server.lifecycle":`degraded`,"mcp.server.recovery_attempts":un.length,"error.message":n instanceof Error?n.message:String(n)}),this.recoveryError})();let n=this.reconnectPromise;try{await n}finally{this.reconnectPromise===n&&(this.reconnectPromise=void 0)}}async ensureReady(){if(this.lifetime.signal.throwIfAborted(),!this.connected){if(this.recoveryError)throw this.recoveryError;await this.reconnectClient(`operation requested while disconnected`)}}async withSessionRetry(e,t){t?.throwIfAborted(),await this.ensureReady(),t?.throwIfAborted();try{return await e()}catch(n){if(t?.throwIfAborted(),(n instanceof S.UnauthorizedError||n instanceof S.InsufficientScopeError)&&this.recoverAuthorization)return this.authorizationRecovery??=this.recoverAuthorization(n,t?AbortSignal.any([t,this.lifetime.signal]):this.lifetime.signal).then(()=>this.reconnectClient(`authorization recovered`)).finally(()=>{this.authorizationRecovery=void 0}),await this.authorizationRecovery,t?.throwIfAborted(),e();if(!this.reconnectFn||!mn(n)&&!gn(n))throw n;return this.logger.warn(`Session error for ${this.serverName}, reconnecting: ${n instanceof Error?n.message:String(n)}`),await this.reconnectClient(n instanceof Error?n.message:String(n)),t?.throwIfAborted(),await e()}}async listTools(){return this.withSessionRetry(async()=>(await this.client.listTools()).tools)}async listResources(){return this.withSessionRetry(async()=>(await this.client.listResources()).resources)}async listPrompts(){return this.withSessionRetry(async()=>(await this.client.listPrompts()).prompts)}async callTool(e,t,n){let r=n?.signal?AbortSignal.any([n.signal,this.lifetime.signal]):this.lifetime.signal,i=await this.withSessionRetry(async()=>{let i={signal:r,...n?.timeout===void 0?{}:{timeout:n.timeout}},a=await this.client.callTool({name:e,arguments:t},i);if(!(0,S.isCallToolResult)(a))throw Error(`Unexpected interactive response from MCP tool '${e}': this call requires a context-aware relay`);return a},r);if(r.throwIfAborted(),n?.onResult)try{await n.onResult(structuredClone(i))}catch(t){this.logger.warn(`MCP result observer failed for ${this.serverName}.${e}`,t)}return r.throwIfAborted(),this.outputGuard?await this.outputGuard.guard(i):i}async readResource(e,t){let n=t?.signal?AbortSignal.any([t.signal,this.lifetime.signal]):this.lifetime.signal,r=await this.withSessionRetry(async()=>await this.client.readResource({uri:e},{signal:n,...t?.timeout===void 0?{}:{timeout:t.timeout}}),n);return n.throwIfAborted(),r}async getPrompt(e,t){return this.withSessionRetry(async()=>await this.client.getPrompt({name:e,arguments:t}))}close(){return this.closePromise||=(this.closing=!0,this.lifetime.abort(/* @__PURE__ */ Error(`MCP client '${this.serverName}' is closed`)),this.connected=!1,this.setLifecycleState(`closed`),(async()=>{let e=(await Promise.allSettled([this.releaseOwnedResources({killChild:!0}),Promise.resolve().then(()=>this.client.close())])).flatMap(e=>e.status===`rejected`?[e.reason]:[]);if(e.length)throw AggregateError(e,`Failed to close MCP client '${this.serverName}'`)})()),this.closePromise}},xn=class{outputSchemaValidation;clientCapabilities;clients=/* @__PURE__ */ new Map;serverConfigs=/* @__PURE__ */ new Map;connectionPromises=/* @__PURE__ */ new Map;connectionControllers=/* @__PURE__ */ new Map;disposed=!1;disposePromise;logger;repositoryPath;workspaceRoot;executionCwd;env;processOwnerId=(0,u.randomUUID)();outputGuard;stateListeners=/* @__PURE__ */ new Set;serverStates=/* @__PURE__ */ new Map;tokenStore;serverTokenStores=/* @__PURE__ */ new Map;serverRemovals=/* @__PURE__ */ new Map;onAuthorizationUrl;callbackTimeoutMs;callbackServer;resolveUrlCredentials;constructor(e=console,t=process.cwd(),n=new U,r={},i,a,o,s,c={}){this.outputSchemaValidation=s,this.clientCapabilities=c,this.logger=e,this.repositoryPath=t,this.workspaceRoot=i,this.executionCwd=o,this.env=a,this.outputGuard=n,this.tokenStore=r.tokenStore,this.onAuthorizationUrl=r.onAuthorizationUrl,this.callbackTimeoutMs=r.callbackTimeoutMs,this.callbackServer=r.callbackServer,this.resolveUrlCredentials=r.resolveUrlCredentials}onServerStateChange(e){this.stateListeners.add(e);for(let[t,n]of this.serverStates)this.notifyListener(e,{...n,serverName:t});return()=>{this.stateListeners.delete(e)}}getServerState(e){return this.serverStates.get(e)?.state}emitServerState(e){this.serverStates.set(e.serverName,e);for(let t of this.stateListeners)this.notifyListener(t,e)}notifyListener(e,t){try{e(t)}catch(e){this.logger.warn(`[mcp-proxy] Server state listener threw for ${t.serverName}: ${e instanceof Error?e.message:String(e)}`)}}setOutputGuard(e){this.outputGuard=e}async cleanupChildProcesses(){await this.disconnectAll()}async connectToServer(e,t,n){if(await this.serverRemovals.get(e),n?.signal?.throwIfAborted(),this.disposed)throw Error(`The MCP client manager is disposed`);n?.tokenStore&&this.serverTokenStores.set(e,n.tokenStore),this.serverConfigs.set(e,t);let r=()=>{this.removeServer(e).catch(()=>this.logger.warn(`Failed to remove cancelled MCP server '${e}'.`))};n?.signal?.addEventListener(`abort`,r,{once:!0});try{await this.ensureConnected(e),n?.signal?.throwIfAborted()}finally{n?.signal?.removeEventListener(`abort`,r)}}async removeServer(e){let t=this.serverRemovals.get(e);if(t)return t;let n=this.serverTokenStores.get(e);this.serverConfigs.delete(e);let r=(async()=>{try{await this.disconnectServer(e)}finally{this.serverTokenStores.get(e)===n&&this.serverTokenStores.delete(e),await n?.clear(e),this.serverStates.delete(e)}})();this.serverRemovals.set(e,r);try{await r}finally{this.serverRemovals.get(e)===r&&this.serverRemovals.delete(e)}}registerServerConfigs(e){for(let[t,n]of Object.entries(e))this.serverConfigs.set(t,n)}getKnownServerNames(){return Array.from(this.serverConfigs.keys())}getServerRequestTimeout(e){return this.serverConfigs.get(e)?.requestTimeout}async ensureConnected(e){if(this.disposed)throw Error(`The MCP client manager is disposed`);let t=this.clients.get(e);if(t)return await t.waitUntilReady(),t;let n=this.connectionPromises.get(e);if(n)return await n;let r=this.serverConfigs.get(e);if(!r)throw Error(`No configuration found for server "${e}"`);let i=this.createConnection(e,r);this.connectionPromises.set(e,i);try{return await i}finally{this.connectionPromises.get(e)===i&&this.connectionPromises.delete(e)}}async createConnection(e,t){let n=t.timeout??3e4,r=new AbortController;this.connectionControllers.set(e,r),this.emitServerState({serverName:e,state:`connecting`});let i,a=new Promise((e,t)=>{i=()=>t(r.signal.reason),r.signal.addEventListener(`abort`,i,{once:!0}),r.signal.aborted&&i()}),o,s,c=new Promise((e,t)=>{s=e=>{clearTimeout(o),!(e||r.signal.aborted)&&(o=setTimeout(()=>{let e=/* @__PURE__ */ Error(`Connection timeout after ${n}ms`);r.abort(e),t(e)},n))},s(!1)}),l;try{l=await Promise.race([this.createSdkClientConnection(e,t,r.signal,e=>s?.(e)),c,a])}catch(n){let i=t.transport===`http`||t.transport===`sse`?t.config.auth:void 0,a=typeof i==`object`&&i?.type===`api-key`,o=n instanceof Error&&[`Invalid MCP API-key configuration.`,`MCP API-key credential is unavailable.`,`MCP credential destination was rejected.`].includes(n.message),s=a&&!o?/* @__PURE__ */ Error(`MCP API-key connection failed.`):n;throw this.emitServerState({serverName:e,state:this.disposed||r.signal.aborted&&!String(r.signal.reason).includes(`timeout`)?`closed`:hn(s)?`needs-auth`:`failed`,error:s instanceof Error?s.message:String(s)}),s}finally{s=void 0,clearTimeout(o),r.signal.removeEventListener(`abort`,i),this.connectionControllers.get(e)===r&&this.connectionControllers.delete(e)}let u=new bn(e,t.transport,l.client,this.logger,{instruction:t.instruction,toolBlacklist:t.toolBlacklist,omitToolDescription:t.omitToolDescription,prompts:t.prompts},this.outputGuard);u.setDisconnectedCallback(()=>void 0),u.setAuthorizationRecovery(l.recoverAuthorization),u.setLifecycleChangeCallback((t,n)=>{this.emitServerState({serverName:e,state:dn(t),...n?{error:n}:{}})});try{if(l.stdioTransport&&u.setStdioTransport(l.stdioTransport),l.processLease&&u.setProcessLease(l.processLease),r.signal.throwIfAborted(),this.disposed)throw Error(`The MCP client manager is disposed`);if(u.setConnected(!0),u.setReconnectFn(async n=>{try{if(this.disposed)throw Error(`The MCP client manager is disposed`);return await this.createSdkClientConnection(e,t,n)}catch(t){throw this.logger.warn(`Failed to reconnect to ${e}`,t),t}}),!u.serverInstruction)try{let e=u.client.getInstructions();e&&(u.serverInstruction=e)}catch(t){this.logger.warn(`Failed to get server instruction from ${e}`,t)}return this.clients.set(e,u),u}catch(e){throw await u.close(),e}}createSdkClient(e,t=`auto`){let n={name:`@agimon-ai/po-mcp-proxy-client`,version:`0.1.0`},r={capabilities:structuredClone(this.clientCapabilities),versionNegotiation:t===`legacy`?{mode:`legacy`}:{mode:`auto`,probe:{timeoutMs:1e4,maxRetries:0}}};return this.outputSchemaValidation?new cn(n,r,e,this.outputSchemaValidation):new S.Client(n,r)}async createSdkClientConnection(e,t,n,r){n?.throwIfAborted();let i;if(t.transport===`stdio`)i=await this.connectStdioSdkClient(e,this.createSdkClient(e),t.config,n);else if(t.transport===`http`||t.transport===`sse`)i=await this.connectUrlSdkClient(e,t.transport,t.config,!0,n,r);else{let{transport:e}=t;throw Error(`Unsupported transport type: ${e}`)}if(n?.aborted||this.disposed)throw await pn(i),n?.throwIfAborted(),Error(`The MCP client manager is disposed`);return i}async connectUrlSdkClient(e,t,n,r=!0,i,a){i?.throwIfAborted();let o=n.auth,s=typeof o==`object`&&o?.type===`api-key`?o:void 0,c=this.resolveAuthHeaders(n),l;if(s){let r=new URL(n.url),a=s.mode===`bearer`?`Authorization`:s.name;if(s.mode!==`bearer`&&s.mode!==`header`||!/^[!#$%&'*+.^_`|~0-9A-Za-z-]+$/.test(a)||s.mode===`header`&&(a.toLowerCase()===`authorization`||/^(?:host|cookie|set-cookie|connection|keep-alive|te|trailer|upgrade|transfer-encoding|content-length|accept|origin|referer|forwarded|x-forwarded-.*|proxy-.*|sec-.*|mcp-session-id)$/i.test(a))||r.username||r.password||r.protocol!==`https:`&&!(r.protocol===`http:`&&[`localhost`,`127.0.0.1`,`[::1]`].includes(r.hostname))||n.bearerToken||n.bearerTokenEnv||n.oauth!==void 0||Object.keys(c??{}).some(e=>[`authorization`,a.toLowerCase()].includes(e.toLowerCase())))throw Error(`Invalid MCP API-key configuration.`);let o;try{o=await this.resolveUrlCredentials?.({serverName:e,transport:t,url:r.href,auth:s,signal:i})}catch{throw Error(`MCP API-key credential is unavailable.`)}i?.throwIfAborted();let u=o?.[a];if(!u||Object.keys(o??{}).length!==1||Array.from(u).some(e=>e.charCodeAt(0)<=31||e.charCodeAt(0)===127)||s.mode===`bearer`&&!/^Bearer [^\s]+$/.test(u))throw Error(`MCP API-key credential is unavailable.`);c={...c,[a]:u},l=async(e,t)=>{let n=new URL(typeof e==`string`?e:e instanceof URL?e.href:e.url);if(n.origin!==r.origin||n.username||n.password)throw Error(`MCP credential destination was rejected.`);return fetch(e,{...t,redirect:`error`})}}let u=this.createSdkClient(e,t===`sse`?`legacy`:`auto`),d=c?{headers:c}:void 0,f=await this.createAuthProvider(e,n);await f?.prepareCallback(),i?.throwIfAborted();let p=f?new Ut(f,n.url,i):void 0,m={requestInit:d,authProvider:p,...l?{fetch:l}:{}},h=t===`http`?new S.StreamableHTTPClientTransport(new URL(n.url),m):new S.SSEClientTransport(new URL(n.url),m),g=()=>{f?.cancelAuthorization(),u.close().catch(t=>this.logger.warn(`Failed to close cancelled client ${e}`,s?`API-key connection closed`:t))};i?.addEventListener(`abort`,g,{once:!0});let _=!1;try{return await u.connect(h),i?.throwIfAborted(),_=!0,{client:u,...p?{recoverAuthorization:(e,t)=>p.recover(e,t)}:{}}}catch(o){if(i?.throwIfAborted(),s)throw Error(`MCP API-key connection failed.`);if(!f||!p||!r||!(o instanceof S.InsufficientScopeError)&&(!hn(o)||!f.hasPendingAuthorization()))throw o;return this.logger.info(`[oauth] Waiting for authorization of MCP server "${e}"`),a?.(!0),await p.recover(o,i,()=>{a?.(!1),this.emitServerState({serverName:e,state:`connecting`})}),i?.throwIfAborted(),await this.connectUrlSdkClient(e,t,n,!1,i,a)}finally{i?.removeEventListener(`abort`,g),_||(f?.cancelAuthorization(),await u.close().catch(t=>this.logger.warn(`Failed to close client ${e}`,s?`API-key connection closed`:t)))}}resolveAuthHeaders(e){let t={...e.headers};if(Be(e)===`bearer`){let n=e.bearerTokenEnv?this.env?.[e.bearerTokenEnv]??He(e):He(e);n?t.Authorization=`Bearer ${n}`:this.logger.warn(`[oauth] Bearer auth configured for an MCP server but no token was resolved${e.bearerTokenEnv?` (env ${e.bearerTokenEnv} is unset)`:``}.`)}return Object.keys(t).length>0?t:void 0}async createAuthProvider(e,t){return Ue({serverName:e,config:t,store:this.serverTokenStores.get(e)??this.tokenStore,logger:this.logger,callbackTimeoutMs:this.callbackTimeoutMs,workspaceRoot:this.repositoryPath,...this.callbackServer?{callbackServer:this.callbackServer}:{},onAuthorizationUrl:(e,t)=>{if(this.emitServerState({serverName:t,state:`needs-auth`}),this.onAuthorizationUrl)return this.onAuthorizationUrl(e,t);this.logger.info(`[oauth] Authorize MCP server "${t}" by visiting: ${e.toString()}`)}})}relayChildStderr(e,t){t&&(t.on(`data`,t=>{let n=t.toString(`utf8`).trimEnd();n&&this.logger.debug(`[${e}] ${n}`)}),t.on(`error`,()=>void 0))}async connectStdioSdkClient(e,t,n,r){let i=new C.StdioClientTransport({command:n.command,args:n.args,cwd:this.executionCwd?(0,l.resolve)(this.executionCwd,n.cwd??`.`):this.workspaceRoot?(0,l.resolve)(this.workspaceRoot,n.cwd??`.`):n.cwd,env:{...process.env,...this.env,...n.env,PROJECT_PATH:this.repositoryPath,[g.PROCESS_REGISTRY_OWNER_KIND_ENV_VAR]:ln,[g.PROCESS_REGISTRY_OWNER_ID_ENV_VAR]:this.processOwnerId},stderr:`pipe`}),a=()=>{pn({client:t,stdioTransport:i}).catch(t=>this.logger.warn(`Failed to close cancelled stdio connection for ${e}`,t))};r?.addEventListener(`abort`,a,{once:!0});try{r?.throwIfAborted(),await t.connect(i),r?.throwIfAborted(),this.relayChildStderr(e,i.stderr);let a=i.pid;if(typeof a!=`number`||a<=0)throw Error(`Failed to capture pid for stdio child process on server "${e}"`);let o=await(0,g.createProcessLease)({repositoryPath:this.repositoryPath,serviceName:e,serviceType:`tool`,pid:a,command:n.command,args:n.args,metadata:{transport:`stdio`},ownerKind:ln,ownerId:this.processOwnerId,resourceKey:e});return t.onclose=()=>{let t=this.clients.get(e);t instanceof bn&&t.handleStdioClosed(i)},{client:t,processLease:o,stdioTransport:i}}catch(n){try{await pn({client:t,stdioTransport:i})}catch(t){this.logger.warn(`Failed to clean up incomplete stdio connection for ${e}`,t)}throw n}finally{r?.removeEventListener(`abort`,a)}}getClient(e){return this.clients.get(e)}getAllClients(){return Array.from(this.clients.values())}async disconnectServer(e){this.connectionControllers.get(e)?.abort(/* @__PURE__ */ Error(`MCP connection '${e}' was closed`));let t=this.connectionPromises.get(e);this.connectionPromises.delete(e);let n=this.clients.get(e);this.clients.delete(e),await Promise.all([t?.catch(()=>void 0),n?.close()])}async disconnectAll(){let e=[...this.clients.values()],t=[...this.connectionPromises.values()];this.clients.clear(),this.connectionPromises.clear();for(let e of this.connectionControllers.values())e.abort(/* @__PURE__ */ Error(`MCP connections were closed`));this.connectionControllers.clear();let n=(await Promise.allSettled([...e.map(e=>e.close()),...t.map(e=>e.catch(()=>void 0))])).flatMap(e=>e.status===`rejected`?[e.reason]:[]);if(n.length)throw AggregateError(n,`Failed to close MCP connections`)}dispose(){return this.disposed=!0,this.disposePromise??=this.disconnectAll(),this.disposePromise}isConnected(e){return this.clients.get(e)?.getLifecycleState()===`connected`}getConnectionState(e){return this.clients.get(e)?.getLifecycleState()}};const Sn=`install`,Cn=/^(@[a-zA-Z0-9_-]+\/)?[a-zA-Z0-9._-]+(@[a-zA-Z0-9._-]+)?$/,wn=`pipe`;function Tn(e){return typeof e==`object`&&!!e&&`command`in e}var En=class{config;constructor(e){this.config=e}extractPackages(){let e=[],{mcpConfig:t,filter:n}=this.config;for(let[r,i]of Object.entries(t.mcpServers)){if(i.disabled||i.transport!==`stdio`||!Tn(i.config))continue;let t=this.extractPackageInfo(r,i.config);if(t){if(n&&t.packageManager!==n)continue;e.push(t)}}return e}async prefetch(){try{let e=this.extractPackages(),t=[];if(e.length===0)return{totalPackages:0,successful:0,failed:0,results:[]};if(this.config.parallel){let n=e.map(async e=>this.prefetchPackage(e));t.push(...await Promise.all(n))}else for(let n of e){let e=await this.prefetchPackage(n);t.push(e)}let n=t.filter(e=>e.success).length,r=t.filter(e=>!e.success).length;return{totalPackages:e.length,successful:n,failed:r,results:t}}catch(e){throw Error(`Failed to prefetch packages: ${e instanceof Error?e.message:String(e)}`)}}async prefetchPackage(e){try{let[t,...n]=e.fullCommand,r=await this.runCommand(t,n);return{package:e,success:r.success,output:r.output}}catch(t){return{package:e,success:!1,output:t instanceof Error?t.message:String(t)}}}isValidPackageName(e){return Cn.test(e)}extractPackageInfo(e,t){let n=t.command.toLowerCase(),r=t.args||[];if(n===`npx`||n.endsWith(`/npx`)){let t=this.extractNpxPackage(r);if(t&&this.isValidPackageName(t))return{serverName:e,packageManager:`npx`,packageName:t,fullCommand:[`npm`,Sn,`-g`,t]}}if(n===`pnpx`||n.endsWith(`/pnpx`)){let t=this.extractNpxPackage(r);if(t&&this.isValidPackageName(t))return{serverName:e,packageManager:`pnpx`,packageName:t,fullCommand:[`pnpm`,`add`,`-g`,t]}}if(n===`uvx`||n.endsWith(`/uvx`)){let t=this.extractUvxPackage(r);if(t&&this.isValidPackageName(t))return{serverName:e,packageManager:`uvx`,packageName:t,fullCommand:[`uvx`,t]}}if((n===`uv`||n.endsWith(`/uv`))&&r.includes(`run`)){let t=this.extractUvRunPackage(r);if(t&&this.isValidPackageName(t))return{serverName:e,packageManager:`uv`,packageName:t,fullCommand:[`uv`,`tool`,Sn,t]}}return null}extractNpxPackage(e){for(let t=0;t<e.length;t++){let n=e[t];if(n.startsWith(`--package=`))return n.slice(10)||null;if(n===`--package`&&t+1<e.length){let n=e[t+1];if(!n.startsWith(`-`))return n}if(n===`-p`&&t+1<e.length){let n=e[t+1];if(!n.startsWith(`-`))return n}}for(let t of e)if(!t.startsWith(`-`))return t;return null}extractUvxPackage(e){for(let t of e)if(!t.startsWith(`-`))return t;return null}extractUvRunPackage(e){let t=e.indexOf(`run`);if(t===-1)return null;for(let n=t+1;n<e.length;n++){let t=e[n];if(!t.startsWith(`-`))return t}return null}runCommand(e,t){return new Promise(n=>{let r=(0,m.spawn)(e,t,{stdio:[`ignore`,wn,wn],shell:process.platform===`win32`}),i=``,a=``;r.stdout?.on(`data`,e=>{i+=e.toString()}),r.stderr?.on(`data`,e=>{a+=e.toString()}),r.on(`close`,e=>{n({success:e===0,output:i||a})}),r.on(`error`,e=>{n({success:!1,output:e.message})})})}};const Dn=`.runtime.json`;async function On(e){await(0,c.mkdir)(e,{recursive:!0,mode:448});try{await(0,c.chmod)(e,448)}catch{}}function kn(e){return typeof e==`object`&&!!e}function An(e){return kn(e)?typeof e.serverId==`string`&&typeof e.host==`string`&&typeof e.port==`number`&&e.transport===`http`&&typeof e.shutdownToken==`string`&&typeof e.startedAt==`string`&&typeof e.pid==`number`&&(e.configPath===void 0||typeof e.configPath==`string`)&&(e.runtimeLifecycle===void 0||e.runtimeLifecycle===`manual`||e.runtimeLifecycle===`stdio-http-managed`):!1}function jn(e){return e instanceof Error?e.message:String(e)}function Mn(e){try{return process.kill(e,0),!0}catch{return!1}}var K=class e{runtimeDir;logger;constructor(t,n=console){this.runtimeDir=t??e.getDefaultRuntimeDir(),this.logger=n}static getDefaultRuntimeDir(){return(0,l.join)((0,f.homedir)(),`.mcp-proxy`,`runtimes`)}getRecordPath(e){return(0,l.join)(this.runtimeDir,`${e}${Dn}`)}async write(e){await On(this.runtimeDir);let t=this.getRecordPath(e.serverId);await(0,c.writeFile)(t,JSON.stringify(e,null,2),{encoding:`utf-8`,mode:384})}async read(e){let t=this.getRecordPath(e);try{let e=await(0,c.readFile)(t,`utf-8`),n=JSON.parse(e);return An(n)?n:null}catch(n){if(kn(n)&&`code`in n&&n.code===`ENOENT`)return null;throw Error(`Failed to read runtime state for server '${e}' from '${t}': ${jn(n)}`)}}async list(){try{let e=(await(0,c.readdir)(this.runtimeDir,{withFileTypes:!0})).filter(e=>e.isFile()&&e.name.endsWith(Dn));return(await Promise.all(e.map(async e=>{try{let t=await(0,c.readFile)((0,l.join)(this.runtimeDir,e.name),`utf-8`),n=JSON.parse(t);return An(n)?n:null}catch(t){return this.logger.debug(`Skipping unreadable runtime state file ${e.name}`,t),null}}))).filter(e=>e!==null)}catch(e){if(kn(e)&&`code`in e&&e.code===`ENOENT`)return[];throw Error(`Failed to list runtime states from '${this.runtimeDir}': ${jn(e)}`)}}async listOrphans(e){return(await this.list()).filter(t=>!e.has(t.serverId))}async pruneStale(){let e=(await this.list()).filter(e=>!Mn(e.pid));return await Promise.all(e.map(async e=>this.remove(e.serverId))),e}async remove(e){await(0,c.rm)(this.getRecordPath(e),{force:!0})}},Nn=class extends Error{filePath;cause;constructor(e,t,n){super(e),this.filePath=t,this.cause=n,this.name=`SkillLoadError`}};async function Pn(e){try{return await(0,c.access)(e),!0}catch(t){if(t instanceof Error&&`code`in t&&t.code===`ENOENT`)return!1;throw Error(`Failed to check path existence for "${e}": ${t instanceof Error?t.message:`Unknown error`}`)}}var Fn=class{cwd;skillPaths;cachedSkills=null;skillsByName=null;watchers=[];pollingTimers=[];onCacheInvalidated;logger;constructor(e,t,n,r=console){this.cwd=e,this.skillPaths=t,this.onCacheInvalidated=n?.onCacheInvalidated,this.logger=r}async getSkills(){if(this.cachedSkills!==null)return this.cachedSkills;let e=[],t=/* @__PURE__ */ new Set,n=await Promise.all(this.skillPaths.map(async e=>{let t=(0,l.isAbsolute)(e)?e:(0,l.join)(this.cwd,e);return this.loadSkillsFromDirectory(t,`project`)}));for(let r of n)for(let n of r)t.has(n.name)||(e.push(n),t.add(n.name));return this.cachedSkills=e,this.skillsByName=new Map(e.map(e=>[e.name,e])),e}async getSkill(e){return this.skillsByName===null&&await this.getSkills(),this.skillsByName?.get(e)}clearCache(){this.cachedSkills=null,this.skillsByName=null}async startWatching(){this.stopWatching();let e=await Promise.all(this.skillPaths.map(async e=>{let t=(0,l.isAbsolute)(e)?e:(0,l.join)(this.cwd,e);return{skillsDir:t,exists:await Pn(t)}}));for(let{skillsDir:t,exists:n}of e){if(!n)continue;let e=new AbortController;this.watchers.push(e),this.watchDirectory(t,e.signal).catch(n=>{if(n?.name!==`AbortError`){if(this.isWatchResourceLimitError(n)){this.startPollingDirectory(t,e.signal);return}this.logger.warn(`[skill-watcher] Error watching ${t}: ${n instanceof Error?n.message:`Unknown error`}`)}})}}stopWatching(){for(let e of this.watchers)e.abort();this.watchers=[];for(let e of this.pollingTimers)clearInterval(e);this.pollingTimers=[]}async watchDirectory(e,t){let n=(0,c.watch)(e,{recursive:!0,signal:t});for await(let e of n)e.filename?.endsWith(`SKILL.md`)&&this.invalidateCache()}invalidateCache(){this.clearCache(),this.onCacheInvalidated?.()}isWatchResourceLimitError(e){return e instanceof Error&&`code`in e&&(e.code===`EMFILE`||e.code===`ENOSPC`)}startPollingDirectory(e,t){this.createSkillSnapshot(e).then(n=>{let r=n,i=setInterval(()=>{t.aborted?clearInterval(i):this.createSkillSnapshot(e).then(e=>{this.snapshotsEqual(r,e)||(r=e,this.invalidateCache())}).catch(t=>{this.logger.warn(`[skill-watcher] Polling failed for ${e}: ${t instanceof Error?t.message:`Unknown error`}`)})},3e3);this.pollingTimers.push(i),t.addEventListener(`abort`,()=>{clearInterval(i),this.pollingTimers=this.pollingTimers.filter(e=>e!==i)},{once:!0})})}async createSkillSnapshot(e){let t=/* @__PURE__ */ new Map;return await this.collectSkillSnapshots(e,t),t}async collectSkillSnapshots(e,t){let n;try{n=await(0,c.readdir)(e)}catch(e){if(e instanceof Error&&`code`in e&&e.code===`ENOENT`)return;throw e}await Promise.all(n.map(async n=>{let r=(0,l.join)(e,n),i=await(0,c.stat)(r);i.isDirectory()?await this.collectSkillSnapshots(r,t):n===`SKILL.md`&&t.set(r,i.mtimeMs)}))}snapshotsEqual(e,t){if(e.size!==t.size)return!1;for(let[n,r]of e)if(t.get(n)!==r)return!1;return!0}async loadSkillsFromDirectory(e,t){let n=[];try{if(!await Pn(e))return n}catch(t){throw new Nn(`Cannot access skills directory: ${t instanceof Error?t.message:`Unknown error`}`,e,t instanceof Error?t:void 0)}let r;try{r=await(0,c.readdir)(e)}catch(t){throw new Nn(`Failed to read skills directory: ${t instanceof Error?t.message:`Unknown error`}`,e,t instanceof Error?t:void 0)}let i=await Promise.all(r.map(async t=>{let n=(0,l.join)(e,t);try{return{entry:t,entryPath:n,stat:await(0,c.stat)(n),error:null}}catch(e){return this.logger.warn(`Skipping entry ${n}: ${e instanceof Error?e.message:`Unknown error`}`),{entry:t,entryPath:n,stat:null,error:e}}})),a=[];for(let{entry:e,entryPath:t,stat:n}of i)if(n){if(n.isDirectory()){let e=(0,l.join)(t,`SKILL.md`);a.push({filePath:e,isRootLevel:!1})}else e===`SKILL.md`&&a.push({filePath:t,isRootLevel:!0})}let o=await Promise.all(a.map(async({filePath:e,isRootLevel:n})=>{try{return!n&&!await Pn(e)?null:await this.loadSkillFile(e,t)}catch(t){return this.logger.warn(`Skipping skill at ${e}: ${t instanceof Error?t.message:`Unknown error`}`),null}}));for(let e of o)e&&n.push(e);return n}async loadSkillFile(e,t){let n;try{n=await(0,c.readFile)(e,`utf-8`)}catch(t){throw new Nn(`Failed to read skill file: ${t instanceof Error?t.message:`Unknown error`}`,e,t instanceof Error?t:void 0)}let{frontMatter:r,content:i}=kt(n);return!r||!r.name||!r.description?null:{name:r.name,description:r.description,location:t,content:i,basePath:(0,l.dirname)(e)}}};const In=/* @__PURE__ */ new Set([`localhost`,`127.0.0.1`,`::1`]);function Ln(e){return e}function Rn(e){if(typeof e!=`object`||!e)return!1;let t=Ln(e);return`status`in t&&t.status===`ok`&&`transport`in t&&t.transport===`http`&&(!(`serverId`in t)||t.serverId===void 0||typeof t.serverId==`string`)&&(!(`runtimeLifecycle`in t)||t.runtimeLifecycle===void 0||t.runtimeLifecycle===`manual`||t.runtimeLifecycle===`stdio-http-managed`)}function zn(e){if(typeof e!=`object`||!e)return!1;let t=Ln(e);return`ok`in t&&typeof t.ok==`boolean`&&`message`in t&&typeof t.message==`string`&&(!(`serverId`in t)||t.serverId===void 0||typeof t.serverId==`string`)}function Bn(e,t){if(!In.has(e.host))throw Error(`Refusing to connect to non-loopback host '${e.host}'. Only ${Array.from(In).join(`, `)} are allowed.`);return`http://${e.host}:${e.port}${t}`}function Vn(e){return e instanceof Error?e.message:String(e)}function Hn(e){return new Promise(t=>{setTimeout(t,e)})}var Un=class{runtimeStateService;logger;constructor(e=new K,t=console){this.runtimeStateService=e,this.logger=t}async stop(e){let t=e.timeoutMs??5e3;await this.pruneStaleRuntimeRecords();let n=await this.resolveRuntime(e),r=await this.fetchHealth(n,t);if(!r.reachable)throw await this.runtimeStateService.remove(n.serverId),Error(`Runtime '${n.serverId}' is not reachable at http://${n.host}:${n.port}. Removed stale runtime record.`);if(!e.force&&r.payload?.serverId&&r.payload.serverId!==n.serverId)throw Error(`Refusing to stop runtime at http://${n.host}:${n.port}: expected server ID '${n.serverId}' but health endpoint reported '${r.payload.serverId}'. Use --force to override.`);let i=e.token??n.shutdownToken;if(!i)throw Error(`No shutdown token available for runtime '${n.serverId}'.`);let a=await this.requestShutdown(n,i,t);return await this.waitForShutdown(n,t),await this.runtimeStateService.remove(n.serverId),{ok:!0,serverId:n.serverId,host:n.host,port:n.port,message:a.message}}async resolveRuntime(e){if(e.serverId){let t=await this.runtimeStateService.read(e.serverId);if(!t)throw Error(`No runtime record found for server ID '${e.serverId}'. Start the server with 'po-mcp-proxy mcp-serve --type http' first.`);return t}if(e.host===void 0||e.port===void 0)throw Error(`Provide --id or both --host and --port to select a runtime.`);let t=(await this.runtimeStateService.list()).filter(t=>t.host===e.host&&t.port===e.port);if(t.length===0)throw Error(`No runtime record found for http://${e.host}:${e.port}. Start the server with 'po-mcp-proxy mcp-serve --type http' first.`);if(t.length>1)throw Error(`Multiple runtime records match http://${e.host}:${e.port}. Retry with --id to avoid stopping the wrong server.`);return t[0]}async pruneStaleRuntimeRecords(){if(this.runtimeStateService.pruneStale)try{await this.runtimeStateService.pruneStale()}catch(e){this.logger.debug(`Failed to prune stale runtime records before stop resolution`,e)}}async fetchHealth(e,t){try{let n=await this.fetchWithTimeout(Bn(e,`/health`),{method:`GET`},t);if(!n.ok)return{reachable:!1};let r=await n.json();if(!Rn(r))throw Error(`Received invalid health response payload.`);return{reachable:!0,payload:r}}catch(t){return this.logger.debug(`Health check failed for ${e.serverId}`,t),{reachable:!1}}}async requestShutdown(e,t,n){let r=await this.fetchWithTimeout(Bn(e,`/admin/shutdown`),{method:`POST`,headers:{Authorization:`Bearer ${t}`}},n),i=await r.json();if(!zn(i))throw Error(`Received invalid shutdown response payload.`);if(!r.ok||!i.ok)throw Error(i.message);return i}async waitForShutdown(e,t){let n=Date.now()+t;for(;Date.now()<n;){if(!(await this.fetchHealth(e,Math.max(250,n-Date.now()))).reachable)return;await Hn(200)}throw Error(`Timed out waiting for runtime '${e.serverId}' to stop at http://${e.host}:${e.port}.`)}async fetchWithTimeout(e,t,n){let r=new AbortController,i=setTimeout(()=>{r.abort()},n);try{return await fetch(e,{...t,signal:r.signal})}catch(t){throw Error(`Request to '${e}' failed: ${Vn(t)}`)}finally{clearTimeout(i)}}};function q(e){return e instanceof Error?e.message:String(e)}function Wn(e,t){let n=Buffer.from(e,`utf-8`),r=Buffer.from(t,`utf-8`);return n.length===r.length&&(0,u.timingSafeEqual)(n,r)}var Gn=class{requests=/* @__PURE__ */ new Map;isAllowed(e){let t=Date.now()-6e4,n=(this.requests.get(e)??[]).filter(e=>e>t);return n.length>=5?(this.requests.set(e,n),!1):(n.push(Date.now()),this.requests.set(e,n),!0)}},Kn=class{honoApp=new ae.Hono;adminRateLimiter=new Gn;config;mcpHandler;nodeMcpHandler;adminOptions;logger;server=null;lastRequestAt=Date.now();activeRequests=0;idleTimer=null;constructor(e,t,n,r=console){this.config={mode:t.mode,host:t.host??`localhost`,port:t.port},this.adminOptions=n,this.logger=r,this.mcpHandler=(0,y.createMcpHandler)(e,{legacy:`reject`,onerror:e=>this.logger.error(`MCP handler failed: ${e.message}`)}),this.nodeMcpHandler=(0,ie.toNodeHandler)(this.mcpHandler,{onerror:e=>this.logger.error(`Failed to adapt MCP request: ${e.message}`)}),this.setupHonoRoutes()}setupHonoRoutes(){this.honoApp.get(`/health`,e=>{let t={status:`ok`,transport:`http`,serverId:this.adminOptions?.serverId,runtimeLifecycle:this.adminOptions?.runtimeLifecycle,protocolVersion:`2026-07-28`,runtimeEpoch:`modern-2026-07-28-v1`};return e.json(t)}),this.honoApp.post(`/admin/shutdown`,async e=>{try{let t=e.req.header(`x-forwarded-for`)??`unknown`;if(!this.adminRateLimiter.isAllowed(t)){let t={ok:!1,message:`Too many shutdown requests. Try again later.`,serverId:this.adminOptions?.serverId};return e.json(t,429)}return await this.handleAdminShutdownRequest(e)}catch(t){this.logger.error(`Failed to process shutdown request: ${q(t)}`);let n={ok:!1,message:`Failed to process shutdown request.`,serverId:this.adminOptions?.serverId};return e.json(n,500)}})}isAuthorizedShutdownRequest(e){let t=this.adminOptions?.shutdownToken;if(!t)return!1;let n=e.authorization;if(typeof n==`string`&&n.startsWith(`Bearer `))return Wn(n.slice(7),t);let r=e[`x-mcp-proxy-shutdown-token`];return typeof r==`string`&&Wn(r,t)}async handleAdminShutdownRequest(e){if(!this.adminOptions?.onShutdownRequested)return e.json({ok:!1,message:`Shutdown endpoint is not enabled for this server instance.`,serverId:this.adminOptions?.serverId},404);if(!this.isAuthorizedShutdownRequest({authorization:e.req.header(`authorization`),"x-mcp-proxy-shutdown-token":e.req.header(`x-mcp-proxy-shutdown-token`)}))return e.json({ok:!1,message:`Unauthorized shutdown request: invalid or missing shutdown token.`,serverId:this.adminOptions?.serverId},401);let t=e.json({ok:!0,message:`Shutdown request accepted. Stopping server gracefully.`,serverId:this.adminOptions?.serverId});return this.adminOptions.onShutdownRequested().catch(e=>{this.logger.error(`Shutdown request failed: ${q(e)}`)}),t}async handleMcpRequest(e,t){this.activeRequests+=1;try{await this.nodeMcpHandler(e,t)}catch(e){this.logger.error(`MCP request failed: ${q(e)}`),t.headersSent||(t.writeHead(500,{"Content-Type":`application/json`}),t.end(JSON.stringify({jsonrpc:`2.0`,error:{code:-32603,message:`MCP request failed.`},id:null})))}finally{--this.activeRequests,this.lastRequestAt=Date.now()}}startIdleShutdownMonitor(){let e=this.adminOptions?.idleShutdownMs??0;e<=0||this.idleTimer||(this.lastRequestAt=Date.now(),this.idleTimer=setInterval(()=>{this.shutdownWhenIdle(e)},Math.min(e,6e4)))}stopIdleShutdownMonitor(){this.idleTimer&&=(clearInterval(this.idleTimer),null)}async shutdownWhenIdle(e){let t=this.adminOptions?.onShutdownRequested;if(!(!t||this.activeRequests>0||Date.now()-this.lastRequestAt<e)){this.stopIdleShutdownMonitor(),this.logger.info(`Stopping idle HTTP runtime after ${e}ms without active requests`);try{await t()}catch(e){this.logger.error(`Failed to stop idle HTTP runtime: ${q(e)}`)}}}async start(){if(this.server)throw Error(`HTTP transport is already running`);let e=(0,re.getRequestListener)(this.honoApp.fetch),t=(0,d.createServer)((t,n)=>{this.lastRequestAt=Date.now(),new URL(t.url??`/`,`http://${t.headers.host??this.config.host}`).pathname===`/mcp`?this.handleMcpRequest(t,n):e(t,n)});try{t.listen(this.config.port,this.config.host),await Promise.race([(0,w.once)(t,`listening`),(0,w.once)(t,`error`).then(([e])=>Promise.reject(e))]),this.server=t,this.startIdleShutdownMonitor(),this.logger.info(`@agimon-ai/po-mcp-proxy MCP server started on http://${this.config.host}:${this.config.port}/mcp`),this.logger.info(`Health check: http://${this.config.host}:${this.config.port}/health`)}catch(e){throw t.close(),Error(`Failed to start HTTP transport: ${q(e)}`,{cause:e})}}async stop(){this.stopIdleShutdownMonitor();let e=this.server;if(e){this.server=null,await this.mcpHandler.close();try{await(0,T.promisify)(e.close.bind(e))()}catch(e){throw Error(`Failed to stop HTTP transport: ${q(e)}`,{cause:e})}}}getPort(){let e=this.server?.address();return e&&typeof e==`object`?e.port:this.config.port}getHost(){return this.config.host}};const qn=`localhost`,Jn=12e4,Yn=`MCP_PROXY_RUNTIME_DETACHED`;function Xn(){return process.env[Yn]===`1`}async function Zn(e){return(await new g.ProcessRegistryService(process.env.PROCESS_REGISTRY_PATH).listProcesses({repositoryPath:e,serviceName:`mcp-proxy-http`,liveOnly:!0})).filter(e=>!!(e.host&&e.port)).map(e=>{let t=e.metadata;return{host:e.host,port:e.port,serverId:t?.serverId??`unknown`,runtimeLifecycle:t?.runtimeLifecycle===`stdio-http-managed`||t?.runtimeLifecycle===`manual`?t.runtimeLifecycle:void 0,createdAt:e.createdAt}}).sort((e,t)=>(e.createdAt??``).localeCompare(t.createdAt??``))}async function Qn(e){let t=await Zn(e);for(let e of t)if($n(await er(e.host,e.port)))return e;return null}function $n(e){return e.ok&&e.payload?.status===`ok`&&e.payload.transport===`http`&&e.payload.protocolVersion===`2026-07-28`&&e.payload.runtimeEpoch===`modern-2026-07-28-v1`}async function er(e,t,n={}){let r=n.timeoutMs??1500,i=n.retries??2,a=n.retryDelayMs??150,o=i+1,s=`unknown error`;for(let n=1;n<=o;n+=1){try{let i=await fetch(`http://${e}:${t}/health`,{signal:AbortSignal.timeout(r)});if(i.ok)return{ok:!0,payload:await i.json().catch(()=>null)??void 0,attempts:n};s=`health endpoint returned status ${i.status}`}catch(e){s=e instanceof Error?e.message:String(e)}n<o&&await new Promise(e=>setTimeout(e,a))}return{ok:!1,error:s,attempts:o}}async function tr(){let e=(0,h.fileURLToPath)(require("url").pathToFileURL(__filename).href),t=l.default.dirname(e),n=[l.default.resolve(t,`cli.mjs`),l.default.resolve(t,`..`,`dist`,`cli.mjs`),l.default.resolve(t,`..`,`..`,`dist`,`cli.mjs`)],r=[l.default.resolve(t,`..`,`cli.ts`),l.default.resolve(t,`..`,`..`,`src`,`cli.ts`)];for(let e of n)try{return await(0,c.access)(e),{command:process.execPath,args:[e]}}catch{continue}for(let e of r)try{return await(0,c.access)(e),{command:process.execPath,args:[`--import`,`tsx`,e]}}catch{continue}throw Error(`Unable to locate mcp-proxy CLI entrypoint`)}function nr(e){if(!e)return Jn;let t=Number.parseInt(e,10);if(!Number.isInteger(t)||t<=0)throw Error(`Invalid timeout value: ${e}`);return t}async function rr(e,t){let n=Date.now()+t;for(;Date.now()<n;){try{await(0,c.access)(e);return}catch{}await new Promise(e=>setTimeout(e,250))}throw Error(`Timed out waiting for runtime state file: ${e}`)}async function ir(e,t){let n=new K,r=Date.now()+t;for(;Date.now()<r;){let t=await n.read(e);if(t&&$n(await er(t.host,t.port,{retries:0})))return{host:t.host,port:t.port};await new Promise(e=>setTimeout(e,250))}throw Error(`Timed out waiting for HTTP runtime '${e}' to become healthy`)}async function ar(e,t,n){let{command:r,args:i}=await tr(),a=(0,m.spawn)(r,[...i,...e],{detached:!0,cwd:n,stdio:`ignore`,env:t});return a.unref(),a}async function or(e){let t=e.id||Ot(),n=nr(e.timeoutMs),r=e.registryPath||e.registryDir,i=await D();await new K().pruneStale();let a=await Qn(i);if(a)return{host:a.host,port:a.port,serverId:a.serverId,workspaceRoot:i,reusedExistingRuntime:!0,runtimeLifecycle:a.runtimeLifecycle??`manual`};let o=e.port,s=await Zn(i);if(e.id&&s.some(e=>e.serverId===t))throw Error(`Runtime ID '${t}' belongs to an incompatible HTTP runtime; choose a new ID`);if(o!==void 0&&s.some(t=>t.host===(e.host||qn)&&t.port===o))throw Error(`Port ${o} belongs to a different HTTP runtime; choose a free port`);let c=o,u={...process.env,[Yn]:`1`,...e.runtimeLifecycle?{MCP_PROXY_RUNTIME_LIFECYCLE:e.runtimeLifecycle}:{},...r?{PORT_REGISTRY_PATH:r,PROCESS_REGISTRY_PATH:(0,g.resolveSiblingRegistryPath)(r,`processes.json`)}:{}},d=await ar([`mcp-serve`,`--type`,`http`,`--id`,t,`--host`,e.host||qn,...c===void 0?[]:[`--port`,String(c)],...Tt(e.config).flatMap(e=>[`--config`,e]),...e.cache===!1?[`--no-cache`]:[],...e.definitionsCache?[`--definitions-cache`,e.definitionsCache]:[],...e.clearDefinitionsCache?[`--clear-definitions-cache`]:[],...e.idleShutdownMinutes===void 0?[]:[`--idle-shutdown-minutes`,String(e.idleShutdownMinutes)],`--proxy-mode`,e.proxyMode],u,i),f=new Promise((e,t)=>{d.once(`exit`,(e,n)=>{t(/* @__PURE__ */ Error(`Background runtime exited before becoming healthy (code=${e??`null`}, signal=${n??`null`})`))})}),p=l.default.join(K.getDefaultRuntimeDir(),`${t}.runtime.json`);try{await Promise.race([rr(p,n),f]);let{host:r,port:a}=await Promise.race([ir(t,n),f]);return{host:r,port:a,serverId:t,workspaceRoot:i,reusedExistingRuntime:!1,runtimeLifecycle:e.runtimeLifecycle??`manual`}}catch(e){throw Error(`Failed to prestart HTTP runtime '${t}': ${e instanceof Error?e.message:String(e)}`,{cause:e})}}function sr(e){process.stdout.write(`mcp-proxy HTTP runtime ready at http://${e.host}:${e.port} (${e.serverId})\n`),process.stdout.write(`runtimeId=${e.serverId}\n`),process.stdout.write(`runtimeUrl=http://${e.host}:${e.port}\n`),process.stdout.write(`workspaceRoot=${e.workspaceRoot}\n`)}var cr=`0.0.1-alpha.37`,lr=`<toolkit id="{{ serverId }}">
|
|
10
10
|
<instruction>
|
|
11
11
|
Before you use any capabilities below, you MUST call this tool with a list of names to learn how to use them properly; this includes:
|
|
12
12
|
- For tools: Arguments schema needed to pass to use_tool
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agimon-ai/po-mcp-proxy",
|
|
3
|
-
"version": "0.0.1-alpha.
|
|
3
|
+
"version": "0.0.1-alpha.37",
|
|
4
4
|
"description": "MCP proxy server package",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ai",
|
|
@@ -52,7 +52,7 @@
|
|
|
52
52
|
"@agimon-ai/foundation-port-registry": "0.30.14",
|
|
53
53
|
"@agimon-ai/foundation-process-registry": "0.30.14",
|
|
54
54
|
"@agimon-ai/foundation-validator": "0.27.14",
|
|
55
|
-
"@agimon-ai/po-logsink": "0.0.1-alpha.
|
|
55
|
+
"@agimon-ai/po-logsink": "0.0.1-alpha.40",
|
|
56
56
|
"@hono/node-server": "2.1.5",
|
|
57
57
|
"@modelcontextprotocol/client": "2.3.1",
|
|
58
58
|
"@modelcontextprotocol/core": "2.3.1",
|