@agimon-ai/po-mcp-proxy 0.0.1-alpha.2 → 0.0.1-alpha.20

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -246,6 +246,10 @@ Scheme selection when `auth` is not set: a server with a bearer token or a crede
246
246
 
247
247
  For `authorization_code`, mcp-proxy logs an authorization URL, listens on `http://127.0.0.1:19876/callback`, exchanges the code once you approve, and retries the connection. Tokens are persisted under `~/.mcp-proxy/oauth` with `0600` permissions and are refreshed automatically, so later runs do not prompt. Credentials are bound to the URL they were issued for: repointing a server at a different host discards them rather than replaying them against the new host.
248
248
 
249
+ OAuth refresh runs before the next request when an access token approaches its saved absolute expiry. Idle connections do not run background refresh timers. A 401 also refreshes through the same per-credential lock, so healthy file-backed stores coordinate rotating refresh tokens across processes. Lock failures fail closed; a store that degraded to memory coordinates only inside its process. Older credentials without expiry continue to refresh on 401. Transient refresh failures retain the current credentials, and rejected refresh grants can request authorization again on an established connection. A changed callback address does not replace the client that issued a still-valid refresh token.
250
+
251
+ Embedded hosts can add a session-only URL server with `clientManager.connectToServer(name, config, { tokenStore: new MemoryTokenStore(), signal })`. The override survives reconnects but never reads persistent credentials. `removeServer(name)` disconnects and clears that override. Memory-store authorization uses an in-process callback listener with no rendezvous files. Definitions-cache collection persists only config-source server names, not dynamically added test servers.
252
+
249
253
  ### Output Guard
250
254
 
251
255
  Downstream tool results are capped before they reach the calling agent, so one chatty or hostile server cannot evict the agent's context. Oversized text is truncated to a head preview and the full payload is written to an owner-only temp file named in the truncation notice; a `_meta['agiflowai/outputGuard']` report records the original and returned sizes. Images and other non-text blocks pass through untouched.
@@ -485,7 +489,7 @@ Returns detailed tool schemas and skill instructions.
485
489
 
486
490
  ### `use_tool`
487
491
 
488
- Executes a proxied tool in `meta` and `search` modes. In `group` mode, use the server-scoped form such as `browse-tool__use_tool`.
492
+ Executes a proxied tool in `meta` and `search` modes. In `group` mode, use the server-scoped form such as `example__use_tool`.
489
493
 
490
494
  ```json
491
495
  {
package/dist/cli.cjs CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- const e=require("./src-Bq40B1Wm.cjs");let t=require("node:fs"),n=require("node:path"),r=require("node:crypto"),i=require("node:fs/promises"),a=require("@agimon-ai/foundation-process-registry"),o=require("js-yaml");o=e.dt(o,1);let s=require("@agimon-ai/foundation-port-registry"),c=require("liquidjs"),l=require("commander");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 l.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 a.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=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=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: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 l.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(`
2
+ const e=require("./src-BJeQYWZ1.cjs");let t=require("node:fs"),n=require("node:path"),r=require("node:crypto"),i=require("node:fs/promises"),a=require("@agimon-ai/foundation-process-registry"),o=require("js-yaml");o=e.ft(o,1);let s=require("@agimon-ai/foundation-port-registry"),c=require("liquidjs"),l=require("commander");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 l.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 a.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=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=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: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 l.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:
@@ -138,7 +138,7 @@ mcpServers:
138
138
  # # omitToolDescription: true
139
139
  # # instruction: Optional user override
140
140
  {% endif %}
141
- `;function T(e=``){console.log(e)}function E(e,t){console.error(t?`${e} ${t}`:e)}const D={info:e=>T(e),error:(e,t)=>E(e,t)},O={info:e=>T(e),warning:e=>T(`Warning: ${e}`),error:e=>E(e),success:e=>T(e),newline:()=>T(),header:e=>T(e),item:e=>T(`- ${e}`),indent:e=>T(` ${e}`)},re=new l.Command(`init`).description(`Initialize MCP configuration file`).option(`-o, --output <path>`,`Output file path`,`mcp-config.yaml`).option(`--json`,`Generate JSON config instead of YAML`,!1).option(`-f, --force`,`Overwrite existing config file`,!1).option(`--mcp-servers <json>`,`JSON string of MCP servers to add to config (optional)`).action(async e=>{try{let t=(0,n.resolve)(e.output),r=!e.json&&(t.endsWith(`.yaml`)||t.endsWith(`.yml`)),a;if(r){let t=new c.Liquid,n=null;if(e.mcpServers)try{let t=JSON.parse(e.mcpServers);n=Object.entries(t).map(([e,t])=>({name:e,command:t.command,args:t.args}))}catch(e){D.error(`Failed to parse --mcp-servers JSON:`,e instanceof Error?e.message:String(e)),process.exit(1)}a=await t.parseAndRender(ne,{mcpServers:n})}else a=te;try{await(0,i.writeFile)(t,a,{encoding:`utf-8`,flag:e.force?`w`:`wx`})}catch(e){throw e&&typeof e==`object`&&`code`in e&&e.code===`EEXIST`&&(D.error(`Config file already exists: ${t}`),D.info(`Use --force to overwrite`),process.exit(1)),e}D.info(`MCP configuration file created: ${t}`),D.info(`Next steps:`),D.info(`1. Edit the configuration file to add your MCP servers`),D.info(`2. Run: po-mcp-proxy mcp-serve --config ${t}`)}catch(e){D.error(`Error executing init:`,e instanceof Error?e.message:String(e)),process.exit(1)}});function k(e){return e instanceof Error?e.message:String(e)}const ie=new l.Command(`list-prompts`).description(`List all available prompts from connected MCP servers`).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=>{try{await x(e,async({clientManager:t})=>{let n=e.server?[t.getClient(e.server)].filter(e=>e!==void 0):t.getAllClients();if(e.server&&n.length===0)throw Error(`Server "${e.server}" not found`);let r={};if(await Promise.all(n.map(async t=>{try{r[t.serverName]=await t.listPrompts()}catch(n){r[t.serverName]=[],e.json||console.error(`Failed to list prompts from ${t.serverName}: ${k(n)}`)}})),e.json)console.log(JSON.stringify(r,null,2));else for(let[e,t]of Object.entries(r))if(console.log(`\n${e}:`),t.length===0)console.log(` No prompts available`);else for(let e of t)if(console.log(` - ${e.name}: ${e.description||`No description`}`),e.arguments&&e.arguments.length>0){let t=e.arguments.map(e=>`${e.name}${e.required?` (required)`:``}`).join(`, `);console.log(` args: ${t}`)}})}catch(e){console.error(`Error executing list-prompts: ${k(e)}`),process.exit(1)}});function A(e){return e instanceof Error?e.message:String(e)}const ae=new l.Command(`list-resources`).description(`List all available resources from connected MCP servers`).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=>{try{await x(e,async({clientManager:t})=>{let n=e.server?[t.getClient(e.server)].filter(e=>e!==void 0):t.getAllClients();if(e.server&&n.length===0)throw Error(`Server "${e.server}" not found`);let r={},i=await Promise.all(n.map(async e=>{try{let t=await e.listResources();return{serverName:e.serverName,resources:t,error:null}}catch(t){return{serverName:e.serverName,resources:[],error:t}}}));for(let{serverName:t,resources:n,error:a}of i)a&&!e.json&&console.error(`Failed to list resources from ${t}: ${A(a)}`),r[t]=n;if(e.json)console.log(JSON.stringify(r,null,2));else for(let[e,t]of Object.entries(r))if(console.log(`\n${e}:`),t.length===0)console.log(` No resources available`);else for(let e of t){let t=e.name?`${e.name} (${e.uri})`:e.uri;console.log(` - ${t}${e.description?`: ${e.description}`:``}`)}})}catch(e){console.error(`Error executing list-resources: ${A(e)}`),process.exit(1)}});function oe(e){return e instanceof Error?e.message:String(e)}function se(e){for(let t of e.servers)if(console.log(`\n${t.server}:`),t.capabilities&&t.capabilities.length>0&&console.log(` capabilities: ${t.capabilities.join(`, `)}`),t.summary&&console.log(` summary: ${t.summary}`),t.tools.length===0)console.log(` no tools`);else for(let e of t.tools){let t=e.capabilities&&e.capabilities.length>0?` [${e.capabilities.join(`, `)}]`:``;console.log(` - ${e.name}${t}`),e.description&&console.log(` ${e.description}`)}}const ce=new l.Command(`search-tools`).description(`Search proxied MCP tools by capability or server`).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(`--capability <name>`,`Filter by capability tag, summary, tool name, or description`).option(`--definitions-cache <path>`,`Path to definitions cache file`).option(`-j, --json`,`Output as JSON`,!1).action(async t=>{try{await x(t,async({container:n,config:r,clientManager:i,configFilePaths:a})=>{i.registerServerConfigs(r.mcpServers);let o=t.definitionsCache||e.j.getDefaultCachePath(a),s;try{s=await e.j.readFromFile(o)}catch{s=void 0}let c=n.createDefinitionsCacheService(i,void 0,{cacheData:s}),l=(await n.createSearchListToolsTool(i,c).execute({capability:t.capability,serverName:t.server})).content.find(e=>e.type===`text`),u=l?.type===`text`?JSON.parse(l.text):{servers:[]};if(t.json)console.log(JSON.stringify(u,null,2));else{if(!u.servers||u.servers.length===0)throw Error(`No tools matched the requested filters`);se(u)}})}catch(e){console.error(`Error executing search-tools: ${oe(e)}`),process.exit(1)}}),j=[`mcp-config.yaml`,`mcp-config.yml`,`mcp-config.json`],M=`/mcp`,N=`localhost`,P=`stdio`,F=`http`,I=`stdio-http`,L=`mcp-proxy-http`,R=`service`;function le(){return e.ut()}const z=`mcp-proxy-stdio-http-bridge`,B=`stdio-http-managed`,V=`manual`,H=`MCP_PROXY_IDLE_SHUTDOWN_MINUTES`;function U(){return le()}function W(e){return e instanceof Error?e.message:String(e)}function ue(e){let t=process.env[H],n=e??(t?Number.parseFloat(t):void 0);return n===void 0||!Number.isFinite(n)||n<=0?0:Math.round(n*6e4)}function G(e){return e===P||e===F||e===I}function de(e){return e===`meta`||e===`flat`||e===`search`||e===`group`}async function K(e){try{return await(0,i.access)(e,t.constants.F_OK),!0}catch{return!1}}async function fe(){try{let e=process.env.PROJECT_PATH;if(e)for(let t of j){let r=(0,n.resolve)(e,t);if(await K(r))return r}let t=process.cwd();for(let e=0;e<=3;e++){for(let e of j){let r=(0,n.join)(t,e);if(await K(r))return r}let e=(0,n.dirname)(t);if(e===t)break;t=e}return null}catch(e){throw Error(`Failed to discover MCP config file: ${W(e)}`)}}function pe(e){try{let n=(0,t.readFileSync)(e,`utf-8`),r=(e.endsWith(`.yaml`)||e.endsWith(`.yml`)?o.load(n):JSON.parse(n))?.proxy;if(!r||typeof r!=`object`)return{};let i=r;return{type:typeof i.type==`string`?i.type:void 0,port:typeof i.port==`number`&&Number.isInteger(i.port)&&i.port>0?i.port:void 0,host:typeof i.host==`string`?i.host:void 0,keepAlive:typeof i.keepAlive==`boolean`?i.keepAlive:void 0}}catch{return{}}}function me(e){let t={};for(let n of e){let e=pe(n);e.type!==void 0&&(t.type=e.type),e.port!==void 0&&(t.port=e.port),e.host!==void 0&&(t.host=e.host),e.keepAlive!==void 0&&(t.keepAlive=e.keepAlive)}return t}async function he(t,n){let r=e.o();if(t.id)return t.id;if(n.length>0)try{let i=await r.createConfigFetcherService({configSources:e.L(n),useCache:t.cache!==!1}).fetchConfiguration(t.cache===!1);if(i.id)return i.id}catch(e){throw Error(`Failed to resolve server ID from config '${n.join(`, `)}': ${W(e)}`)}return e.N()}function ge(e){if(!G(e))throw Error(`Unknown transport type: '${e}'. Valid options: ${P}, ${F}, ${I}`);return e}function _e(e){if(!de(e))throw Error(`Unknown proxy mode: '${String(e)}'. Valid options: meta, flat, search, group`)}function ve(e){return e===B?B:V}function q(e,t,n){let r=process.env.MCP_PORT?Number(process.env.MCP_PORT):void 0;return{mode:t,port:e.port??(Number.isFinite(r)?r:void 0)??n?.port,host:e.host??process.env.MCP_HOST??n?.host??N}}function J(){let e=(e,t)=>{t===void 0?console.error(e):console.error(e,t)};return{trace:e,debug:e,info:e,warn:e,error:e}}function ye(e,t,n){return{configFilePath:t.length>0?t:void 0,noCache:e.cache===!1,serverId:n,definitionsCachePath:e.definitionsCache,clearDefinitionsCache:e.clearDefinitionsCache,proxyMode:e.proxyMode}}function be(e,t,n,r){let i=W(r);return e===P?`Failed to start MCP server with transport '${e}': ${i}`:`Failed to start MCP server with transport '${e}' on ${n===void 0?`${t} (dynamic port)`:`${t}:${n}`}: ${i}`}function xe(e,t,n,r,i,a=V){return{serverId:e,host:t.host??N,port:n,transport:`http`,shutdownToken:r,startedAt:(/* @__PURE__ */ new Date()).toISOString(),pid:process.pid,configPath:i,runtimeLifecycle:a}}function Se(){return new s.PortRegistryService(process.env.PORT_REGISTRY_PATH)}function Y(){return process.env.NODE_ENV??`development`}async function Ce(e,t,n,r,i,a,o=n===void 0?s.DEFAULT_PORT_RANGE:{min:n,max:n}){let c=Se(),l=await c.reservePort({repositoryPath:U(),serviceName:e,serviceType:R,environment:Y(),pid:process.pid,host:t,preferredPort:n,portRange:o,force:!0,metadata:{transport:i,serverId:r,...a?{configPath:a}:{}}});if(!l.success||!l.record){let e=n===void 0?`dynamic port`:`port ${n}`;throw Error(l.error||`Failed to reserve ${e} in port registry`)}let u=!1;return{port:l.record.port,release:async()=>{if(u)return;u=!0;let t=await c.releasePort({repositoryPath:U(),serviceName:e,serviceType:R,pid:process.pid,environment:Y(),force:!0});if(!t.success&&t.error&&!t.error.includes(`No matching registry entry`))throw Error(t.error||`Failed to release port for ${e}`)}}}async function we(e){e&&await e.release()}function Te(e,t,n,r,i){return{serverId:e,shutdownToken:t,onShutdownRequested:n,runtimeLifecycle:r,idleShutdownMs:i}}async function X(e,t){try{await e.remove(t)}catch(e){throw Error(`Failed to remove runtime state for '${t}': ${W(e)}`)}}async function Ee(e,t){try{await e.write(t)}catch(e){throw Error(`Failed to persist runtime state for '${t.serverId}': ${W(e)}`)}}async function De(e,t,n,r){try{try{await e.stop()}catch(e){throw Error(`Failed to stop owned HTTP transport '${n}': ${W(e)}`)}}finally{await r?.release({kill:!1}),await X(t,n)}}async function Oe(e,t,n){await n?.release({kill:!1}),await X(e,t)}async function ke(e,t,n,r){try{try{await e.stop()}catch(e){throw Error(`Failed to stop HTTP transport during cleanup for '${n}': ${W(e)}`)}}finally{await r?.release({kill:!1}),await X(t,n)}}async function Z(e,t,n={}){try{await e.start()}catch(e){throw Error(`Failed to start transport handler: ${W(e)}`)}let r=!1,i=async n=>{if(!r){r=!0,console.error(`\nReceived ${n}, shutting down gracefully...`);try{await e.stop(),t&&await t(),process.exit(0)}catch(e){console.error(`Failed to gracefully stop transport during ${n}: ${W(e)}`),process.exit(1)}}};process.on(`SIGINT`,async()=>await i(`SIGINT`)),process.on(`SIGTERM`,async()=>await i(`SIGTERM`)),n.shutdownOnStdinClose&&(process.stdin.on(`end`,async()=>await i(`stdin end`)),process.stdin.on(`close`,async()=>await i(`stdin close`)))}async function Ae(t,n,i,o){e.M();let c=i[0],l=await e.l(t),u=new e.T,d=(0,r.randomUUID)(),f=t.serverId??e.N(),p=ve(process.env.MCP_PROXY_RUNTIME_LIFECYCLE),m=n.port,h=m===void 0?s.DEFAULT_PORT_RANGE:{min:m,max:m},g=await Ce(L,n.host??N,m,f,F,c,h),_=g.port,v={...n,port:_},y=await(0,a.createProcessLease)({repositoryPath:U(),serviceName:`mcp-proxy-http`,serviceType:`service`,environment:Y(),host:v.host??N,port:_,command:process.argv[1],args:process.argv.slice(2),...e.v()?{processGroupId:process.pid,terminationMode:`process-group`}:{terminationMode:`direct`},metadata:{transport:F,serverId:f,runtimeLifecycle:p,...c?{configPath:c}:{}}}),b=async()=>{await we(g??null),b=async()=>void 0},x=xe(f,v,_,d,c,p),S,C=!1,w=async()=>{if(!C){C=!0;try{await De(S,u,x.serverId,y),await b(),await l.dispose(),process.exit(0)}catch(e){throw Error(`Failed to stop HTTP runtime '${x.serverId}' from admin shutdown: ${W(e)}`)}}};try{let t=Te(x.serverId,d,w,p,o);S=new e.S(()=>e.r(l),v,t)}catch(e){throw await b(),await y.release({kill:!1}),await l.dispose(),Error(`Failed to create HTTP runtime server: ${W(e)}`)}try{await Z(S,async()=>{await b(),await l.dispose(),await Oe(u,x.serverId,y)}),await Ee(u,x)}catch(e){throw await b(),await l.dispose(),await ke(S,u,x.serverId,y),Error(`Failed to start HTTP runtime '${x.serverId}': ${W(e)}`)}console.error(`Runtime state: http://${x.host}:${x.port} (${x.serverId})`)}async function je(t){let n;try{n=await e.n(t),await Z(new e.d(n,J()),async()=>{await n?.dispose?.()},{shutdownOnStdinClose:!0})}catch(e){throw await n?.dispose?.(),Error(`Failed to start stdio transport: ${W(e)}`)}}async function Me(t){if(t)try{return(await new e.T().read(t))?.runtimeLifecycle}catch{return}}function Ne(t){if(!t)return e.g;let n=Number.parseInt(t,10);return Number.isInteger(n)&&n>0?n:e.g}function Q(e,t){let i=a.ProcessRegistryService.resolveRegistryPath(process.env.PROCESS_REGISTRY_PATH),o=(0,r.createHash)(`sha1`).update(`${(0,a.normalizeRepositoryPath)(e)}|${t}`).digest(`hex`);return(0,n.join)((0,n.dirname)(i),`mcp-proxy-runtime-${o.slice(0,16)}.lock`)}async function $(e,t,n){if(e.port!==void 0)return{endpoint:new URL(`http://${e.host??N}:${e.port}${M}`),runtimeLifecycle:V};let r=U(),i=Ne(t.timeoutMs)+1e4;return await(0,a.withFileLock)(Q(r,L),async()=>Pe(r,e,t,n),{staleAfterMs:i,retryDelayMs:250,maxRetries:Math.ceil(i/250)})}async function Pe(t,n,r,i){let a=await e._(t);if(a){let e=n.host??a.host,t=a.serverId===`unknown`?void 0:a.serverId;return{endpoint:new URL(`http://${e}:${a.port}${M}`),runtimeServerId:t,runtimeLifecycle:a.runtimeLifecycle??await Me(t)??V}}let o=await e.y({host:n.host??N,config:i,cache:r.cache,definitionsCache:r.definitionsCache,clearDefinitionsCache:r.clearDefinitionsCache,proxyMode:r.proxyMode,runtimeLifecycle:B,idleShutdownMinutes:r.idleShutdownMinutes});return{endpoint:new URL(`http://${o.host}:${o.port}${M}`),runtimeServerId:o.serverId,runtimeLifecycle:o.runtimeLifecycle??V}}async function Fe(e,t,n){let{endpoint:r}=await $(e,t,n);return r}function Ie(e){return`runtime:${e}`}async function Le(e){if(e.runtimeLifecycle===B&&e.runtimeServerId)return await(0,a.createProcessLease)({repositoryPath:U(),serviceName:`mcp-proxy-stdio-http-bridge:${(0,r.randomUUID)()}`,serviceType:`tool`,environment:Y(),pid:process.pid,command:process.argv[1],args:process.argv.slice(2),tags:[z,Ie(e.runtimeServerId)],metadata:{transport:I,runtimeServerId:e.runtimeServerId,runtimeLifecycle:e.runtimeLifecycle,endpoint:e.endpoint.toString()},force:!1})}function Re(e){return e.pid===process.pid}async function ze(e){return await new a.ProcessRegistryService(process.env.PROCESS_REGISTRY_PATH).listProcesses({repositoryPath:U(),serviceType:`tool`,tags:[z,Ie(e)],environment:Y(),liveOnly:!0})}async function Be(e){return(await ze(e)).filter(e=>!Re(e)).length>0?!1:(await new Promise(e=>setTimeout(e,500)),(await ze(e)).filter(e=>!Re(e)).length===0)}async function Ve(t){if(t.runtimeLifecycle!==B||!t.runtimeServerId)return;let n=t.runtimeServerId,r=Q(U(),L);try{await(0,a.withFileLock)(r,async()=>{await Be(n)&&await new e.C().stop({serverId:n,force:!0})})}catch(e){console.error(`Failed to stop managed stdio-http runtime; leaving it alive: ${W(e)}`)}}async function He(t,n,r,i){let a=n.autoDisconnect?!0:i?.keepAlive!==void 0&&!i.keepAlive,o,s;try{o=await $(t,n,r),s=await Le(o);let{endpoint:i}=o;await Z(new e.u({endpoint:i,resolveEndpoint:async()=>await Fe(t,n,r)},J()),async()=>{try{await s?.release({kill:!1,releasePort:!1})}catch(e){console.error(`Failed to release stdio-http bridge lease; leaving runtime alive: ${W(e)}`);return}a&&o&&await Ve(o)},{shutdownOnStdinClose:!0})}catch(e){try{await s?.release({kill:!1,releasePort:!1})}catch(t){throw Error(`Failed to start stdio-http transport: ${W(e)}; also failed to release bridge lease: ${W(t)}`)}throw Error(`Failed to start stdio-http transport: ${W(e)}`)}}async function Ue(t,n,r,i,a){try{if(t===P){await je(i);return}if(t===F){await Ae(i,q(n,e.t.HTTP,a),r,ue(n.idleShutdownMinutes));return}await He(q(n,e.t.HTTP,a),n,r,a)}catch(e){throw Error(`Failed to start transport '${t}': ${W(e)}`)}}async function We(t,n){try{let r=await e.y({id:t.id,host:t.host??N,port:t.port,config:n,cache:t.cache,definitionsCache:t.definitionsCache,clearDefinitionsCache:t.clearDefinitionsCache,proxyMode:t.proxyMode,timeoutMs:t.timeoutMs,idleShutdownMinutes:t.idleShutdownMinutes});e.x(r)}catch(e){console.error(`Failed to prestart HTTP runtime '${t.id||`generated-server-id`}': ${W(e)}`),process.exit(1)}}const Ge=new l.Command(`mcp-serve`).description(`Start MCP server with specified transport or prestart the HTTP runtime in the background`).option(`-t, --type <type>`,`Transport type: ${P}, ${F}, or ${I}`).option(`-p, --port <port>`,`Port to listen on (http) or backend port for stdio-http`,e=>Number.parseInt(e,10)).option(`--host <host>`,`Host to bind to (http) or backend host for stdio-http`).option(`-c, --config <path>`,`Path to MCP server configuration file (repeatable; later files override earlier ones)`,e.F).option(`--no-cache`,`Disable configuration caching, always reload from config file`).option(`--definitions-cache <path>`,`Path to prefetched tool/prompt/skill definitions cache file`).option(`--clear-definitions-cache`,`Delete definitions cache before startup`,!1).option(`--proxy-mode <mode>`,`How mcp-proxy exposes downstream tools: meta, flat, search, or group`,`meta`).option(`--id <id>`,`Unique server identifier (overrides config file id, auto-generated if not provided)`).option(`--auto-disconnect`,`Stop the managed HTTP runtime when the last stdio-http bridge exits (default: keep runtime alive for reuse)`,!1).option(`--idle-shutdown-minutes <minutes>`,`Stop the HTTP runtime after N minutes with no MCP session and no request (0 disables; overrides ${H})`,e=>Number.parseFloat(e)).option(`--prestart-http`,`Prestart the HTTP runtime in the background and exit after it becomes healthy`,!1).option(`--timeout-ms <ms>`,`How long to wait for the HTTP runtime to become healthy`,String(e.g)).action(async t=>{try{let n=e.I(t.config),r=n.length>0?null:await fe(),i=r?[r]:n;if(t.prestartHttp){await We(t,i);return}let a=me(i),o=ge((t.type??a.type??P).toLowerCase());_e(t.proxyMode),await Ue(o,t,i,ye(t,i,await he(t,i)),a)}catch(e){let n=(t.type??P).toLowerCase(),r=G(n)?n:P,i=process.env.MCP_PORT?Number(process.env.MCP_PORT):void 0,a=t.port??(Number.isFinite(i)?i:void 0);console.error(be(r,t.host??N,a,e)),process.exit(1)}}),Ke=new l.Command(`prefetch`).description(`Pre-download packages used by MCP servers (npx, pnpx, uvx, uv)`).option(`-c, --config <path>`,`Path to MCP server configuration file (repeatable; later files override earlier ones)`,e.F).option(`-p, --parallel`,`Run prefetch commands in parallel`,!1).option(`-d, --dry-run`,`Show what would be prefetched without executing`,!1).option(`-f, --filter <type>`,`Filter by package manager type: npx, pnpx, uvx, or uv`).option(`--definitions-out <path>`,`Write discovered definitions to a JSON or YAML cache file`).option(`--skip-packages`,`Skip package prefetch and only build definitions cache`,!1).option(`--clear-definitions-cache`,`Delete the definitions cache file before continuing`,!1).action(async t=>{try{let n=e.o(),r=e.I(t.config),i=r.length>0?null:e.P(),a=i?[i]:r;a.length===0&&(O.error(`No MCP configuration file found.`),O.info(`Use --config <path> to specify a config file, or run "po-mcp-proxy init" to create one.`),process.exit(1)),O.info(`Loading configuration from: ${a.join(`, `)}`);let o=await n.createConfigFetcherService({configSources:e.L(a),useCache:!1}).fetchConfiguration(!0),s=o.id||e.N(),c=e.j.generateConfigHash(o),l=t.definitionsOut||e.j.getDefaultCachePath(a),u=n.createPrefetchService({mcpConfig:o,filter:t.filter,parallel:t.parallel}),d=u.extractPackages(),f=!t.skipPackages,p=!!t.definitionsOut;if(t.clearDefinitionsCache&&(t.dryRun?O.info(`Would clear definitions cache: ${l}`):(await e.j.clearFile(l),O.success(`Cleared definitions cache: ${l}`))),f){if(d.length===0)O.warning(`No packages found to prefetch.`),O.info(`Prefetch supports: npx, pnpx, uvx, and uv run commands`);else{O.info(`Found ${d.length} package(s) to prefetch:`);for(let e of d)O.item(`${e.serverName}: ${e.packageManager} ${e.packageName}`)}}if(!f&&!p){O.warning(`Nothing to do. Use package prefetch or provide --definitions-out.`);return}if(t.dryRun){if(f&&d.length>0){O.newline(),O.header(`Dry run mode - commands that would be executed:`);for(let e of d)O.indent(e.fullCommand.join(` `))}p&&(O.newline(),O.info(`Would write definitions cache to: ${l}`));return}let m=!1;if(f&&d.length>0){O.newline(),O.info(`Prefetching packages...`);let e=await u.prefetch();if(O.newline(),e.failed===0?O.success(`Package prefetch complete: ${e.successful} succeeded, ${e.failed} failed`):O.warning(`Package prefetch complete: ${e.successful} succeeded, ${e.failed} failed`),e.failed>0){m=!0,O.newline(),O.error(`Failed packages:`);for(let t of e.results.filter(e=>!e.success))O.item(`${t.package.serverName} (${t.package.packageName}): ${t.output.trim()}`)}}if(p){O.newline(),O.info(`Collecting definitions cache...`);let t=n.createClientManagerService(),r=o.skills?.paths||[],i=r.length>0?n.createSkillService(process.cwd(),r):void 0,u=n.createDefinitionsCacheService(t,i);await Promise.all(Object.entries(o.mcpServers).map(async([e,n])=>{try{await t.connectToServer(e,n),O.item(`Connected for definitions: ${e}`)}catch(t){O.warning(`Failed to connect for definitions: ${e} (${t instanceof Error?t.message:String(t)})`)}}));let d=await u.collectForCache({configPath:a[0],configHash:c,oneMcpVersion:e.h,serverId:s});await e.j.writeToFile(l,d),O.success(`Definitions cache written: ${l} (${Object.keys(d.servers).length} servers, ${d.skills.length} skills)`),d.failures.length>0&&O.warning(`Definitions cache completed with ${d.failures.length} server failure(s)`),await t.disconnectAll()}m&&process.exit(1)}catch(e){O.error(`Error executing prefetch: ${e instanceof Error?e.message:String(e)}`),process.exit(1)}});function qe(e){return e instanceof Error?e.message:String(e)}const Je=new l.Command(`read-resource`).description(`Read a resource by URI from a connected MCP server`).argument(`<uri>`,`Resource URI to read`).option(`-c, --config <path>`,`Path to MCP server configuration file (repeatable; later files override earlier ones)`,e.F).option(`-s, --server <name>`,`Server name (required if resource exists on multiple servers)`).option(`-j, --json`,`Output as JSON`,!1).action(async(e,t)=>{try{await x(t,async({clientManager:n})=>{let r=n.getAllClients();if(t.server){let r=n.getClient(t.server);if(!r)throw Error(`Server "${t.server}" not found`);t.json||console.error(`Reading ${e} from ${t.server}...`);let i=await r.readResource(e);if(t.json)console.log(JSON.stringify(i,null,2));else for(let e of i.contents)`text`in e?console.log(e.text):console.log(JSON.stringify(e,null,2));return}let i=await Promise.all(r.map(async t=>{try{let n=(await t.listResources()).some(t=>t.uri===e);return{serverName:t.serverName,hasResource:n,error:null}}catch(e){return{serverName:t.serverName,hasResource:!1,error:e}}})),a=[];for(let{serverName:e,hasResource:t,error:n}of i)n?console.error(`Failed to list resources from ${e}: ${qe(n)}`):t&&a.push(e);if(a.length===0)throw Error(`Resource "${e}" not found on any connected server`);if(a.length>1)throw Error(`Resource "${e}" found on multiple servers: ${a.join(`, `)}. Use --server to disambiguate`);let o=a[0],s=n.getClient(o);if(!s)throw Error(`Internal error: Server "${o}" not connected`);t.json||console.error(`Reading ${e} from ${o}...`);let c=await s.readResource(e);if(t.json)console.log(JSON.stringify(c,null,2));else for(let e of c.contents)`text`in e?console.log(e.text):console.log(JSON.stringify(e,null,2))})}catch(e){console.error(`Error executing read-resource: ${qe(e)}`),process.exit(1)}});function Ye(e){return e instanceof Error?e.message:String(e)}function Xe(e){console.log(`Stopped mcp-proxy server '${e.serverId}'.`),console.log(`Endpoint: http://${e.host}:${e.port}`),console.log(`Result: ${e.message}`)}const Ze=new l.Command(`stop`).description(`Stop a running HTTP mcp-proxy server`).option(`--id <id>`,`Target server ID from the runtime registry`).option(`--host <host>`,`Target runtime host`).option(`--port <port>`,`Target runtime port`,e=>Number.parseInt(e,10)).option(`-c, --config <path>`,`Reserved for future config-based targeting support`).option(`--token <token>`,`Override the persisted shutdown token`).option(`--force`,`Skip server ID verification against the /health response`,!1).option(`-j, --json`,`Output as JSON`,!1).option(`--timeout <ms>`,`Maximum time to wait for shutdown completion`,e=>Number.parseInt(e,10),5e3).action(async t=>{try{t.config&&console.error(`Warning: --config is not used yet; runtime resolution uses the persisted registry.`);let n=await e.o().createStopServerService().stop({serverId:t.id,host:t.host,port:t.port,token:t.token,force:t.force,timeoutMs:t.timeout});if(t.json){console.log(JSON.stringify(n,null,2));return}Xe(n)}catch(e){let n=`Error executing stop: ${Ye(e)}`;t.json?console.log(JSON.stringify({ok:!1,error:n},null,2)):console.error(n),process.exit(1)}});function Qe(e){return e instanceof Error?e.message:String(e)}const $e=new l.Command(`use-tool`).description(`Execute an MCP tool with arguments`).argument(`<toolName>`,`Tool name to execute`).option(`-c, --config <path>`,`Path to MCP server configuration file (repeatable; later files override earlier ones)`,e.F).option(`-s, --server <name>`,`Server name (required if tool exists on multiple servers)`).option(`-a, --args <json>`,`Tool arguments as JSON string`,`{}`).option(`-t, --timeout <ms>`,`Request timeout in milliseconds for tool execution (default: 60000)`,Number.parseInt).option(`-j, --json`,`Output as JSON`,!1).action(async(e,t)=>{try{let n={};try{n=JSON.parse(t.args)}catch{console.error(`Error: Invalid JSON in --args`),process.exit(1)}await x(t,async({container:r,config:i,clientManager:a})=>{let o=a.getAllClients();if(t.server){let r=a.getClient(t.server);if(!r)throw Error(`Server "${t.server}" not found`);t.json||console.error(`Executing ${e} on ${t.server}...`);let i=t.timeout?{timeout:t.timeout}:void 0,o=await r.callTool(e,n,i);if(t.json)console.log(JSON.stringify(o,null,2));else{if(console.log(`
141
+ `;function T(e=``){console.log(e)}function E(e,t){console.error(t?`${e} ${t}`:e)}const D={info:e=>T(e),error:(e,t)=>E(e,t)},O={info:e=>T(e),warning:e=>T(`Warning: ${e}`),error:e=>E(e),success:e=>T(e),newline:()=>T(),header:e=>T(e),item:e=>T(`- ${e}`),indent:e=>T(` ${e}`)},re=new l.Command(`init`).description(`Initialize MCP configuration file`).option(`-o, --output <path>`,`Output file path`,`mcp-config.yaml`).option(`--json`,`Generate JSON config instead of YAML`,!1).option(`-f, --force`,`Overwrite existing config file`,!1).option(`--mcp-servers <json>`,`JSON string of MCP servers to add to config (optional)`).action(async e=>{try{let t=(0,n.resolve)(e.output),r=!e.json&&(t.endsWith(`.yaml`)||t.endsWith(`.yml`)),a;if(r){let t=new c.Liquid,n=null;if(e.mcpServers)try{let t=JSON.parse(e.mcpServers);n=Object.entries(t).map(([e,t])=>({name:e,command:t.command,args:t.args}))}catch(e){D.error(`Failed to parse --mcp-servers JSON:`,e instanceof Error?e.message:String(e)),process.exit(1)}a=await t.parseAndRender(ne,{mcpServers:n})}else a=te;try{await(0,i.writeFile)(t,a,{encoding:`utf-8`,flag:e.force?`w`:`wx`})}catch(e){throw e&&typeof e==`object`&&`code`in e&&e.code===`EEXIST`&&(D.error(`Config file already exists: ${t}`),D.info(`Use --force to overwrite`),process.exit(1)),e}D.info(`MCP configuration file created: ${t}`),D.info(`Next steps:`),D.info(`1. Edit the configuration file to add your MCP servers`),D.info(`2. Run: po-mcp-proxy mcp-serve --config ${t}`)}catch(e){D.error(`Error executing init:`,e instanceof Error?e.message:String(e)),process.exit(1)}});function k(e){return e instanceof Error?e.message:String(e)}const ie=new l.Command(`list-prompts`).description(`List all available prompts from connected MCP servers`).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=>{try{await x(e,async({clientManager:t})=>{let n=e.server?[t.getClient(e.server)].filter(e=>e!==void 0):t.getAllClients();if(e.server&&n.length===0)throw Error(`Server "${e.server}" not found`);let r={};if(await Promise.all(n.map(async t=>{try{r[t.serverName]=await t.listPrompts()}catch(n){r[t.serverName]=[],e.json||console.error(`Failed to list prompts from ${t.serverName}: ${k(n)}`)}})),e.json)console.log(JSON.stringify(r,null,2));else for(let[e,t]of Object.entries(r))if(console.log(`\n${e}:`),t.length===0)console.log(` No prompts available`);else for(let e of t)if(console.log(` - ${e.name}: ${e.description||`No description`}`),e.arguments&&e.arguments.length>0){let t=e.arguments.map(e=>`${e.name}${e.required?` (required)`:``}`).join(`, `);console.log(` args: ${t}`)}})}catch(e){console.error(`Error executing list-prompts: ${k(e)}`),process.exit(1)}});function A(e){return e instanceof Error?e.message:String(e)}const ae=new l.Command(`list-resources`).description(`List all available resources from connected MCP servers`).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=>{try{await x(e,async({clientManager:t})=>{let n=e.server?[t.getClient(e.server)].filter(e=>e!==void 0):t.getAllClients();if(e.server&&n.length===0)throw Error(`Server "${e.server}" not found`);let r={},i=await Promise.all(n.map(async e=>{try{let t=await e.listResources();return{serverName:e.serverName,resources:t,error:null}}catch(t){return{serverName:e.serverName,resources:[],error:t}}}));for(let{serverName:t,resources:n,error:a}of i)a&&!e.json&&console.error(`Failed to list resources from ${t}: ${A(a)}`),r[t]=n;if(e.json)console.log(JSON.stringify(r,null,2));else for(let[e,t]of Object.entries(r))if(console.log(`\n${e}:`),t.length===0)console.log(` No resources available`);else for(let e of t){let t=e.name?`${e.name} (${e.uri})`:e.uri;console.log(` - ${t}${e.description?`: ${e.description}`:``}`)}})}catch(e){console.error(`Error executing list-resources: ${A(e)}`),process.exit(1)}});function oe(e){return e instanceof Error?e.message:String(e)}function se(e){for(let t of e.servers)if(console.log(`\n${t.server}:`),t.capabilities&&t.capabilities.length>0&&console.log(` capabilities: ${t.capabilities.join(`, `)}`),t.summary&&console.log(` summary: ${t.summary}`),t.tools.length===0)console.log(` no tools`);else for(let e of t.tools){let t=e.capabilities&&e.capabilities.length>0?` [${e.capabilities.join(`, `)}]`:``;console.log(` - ${e.name}${t}`),e.description&&console.log(` ${e.description}`)}}const ce=new l.Command(`search-tools`).description(`Search proxied MCP tools by capability or server`).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(`--capability <name>`,`Filter by capability tag, summary, tool name, or description`).option(`--definitions-cache <path>`,`Path to definitions cache file`).option(`-j, --json`,`Output as JSON`,!1).action(async t=>{try{await x(t,async({container:n,config:r,clientManager:i,configFilePaths:a})=>{i.registerServerConfigs(r.mcpServers);let o=t.definitionsCache||e.j.getDefaultCachePath(a),s;try{s=await e.j.readFromFile(o)}catch{s=void 0}let c=n.createDefinitionsCacheService(i,void 0,{cacheData:s}),l=(await n.createSearchListToolsTool(i,c).execute({capability:t.capability,serverName:t.server})).content.find(e=>e.type===`text`),u=l?.type===`text`?JSON.parse(l.text):{servers:[]};if(t.json)console.log(JSON.stringify(u,null,2));else{if(!u.servers||u.servers.length===0)throw Error(`No tools matched the requested filters`);se(u)}})}catch(e){console.error(`Error executing search-tools: ${oe(e)}`),process.exit(1)}}),j=[`mcp-config.yaml`,`mcp-config.yml`,`mcp-config.json`],M=`/mcp`,N=`localhost`,P=`stdio`,F=`http`,I=`stdio-http`,L=`mcp-proxy-http`,R=`service`;function le(){return e.dt()}const z=`mcp-proxy-stdio-http-bridge`,B=`stdio-http-managed`,V=`manual`,H=`MCP_PROXY_IDLE_SHUTDOWN_MINUTES`;function U(){return le()}function W(e){return e instanceof Error?e.message:String(e)}function ue(e){let t=process.env[H],n=e??(t?Number.parseFloat(t):void 0);return n===void 0||!Number.isFinite(n)||n<=0?0:Math.round(n*6e4)}function G(e){return e===P||e===F||e===I}function de(e){return e===`meta`||e===`flat`||e===`search`||e===`group`}async function K(e){try{return await(0,i.access)(e,t.constants.F_OK),!0}catch{return!1}}async function fe(){try{let e=process.env.PROJECT_PATH;if(e)for(let t of j){let r=(0,n.resolve)(e,t);if(await K(r))return r}let t=process.cwd();for(let e=0;e<=3;e++){for(let e of j){let r=(0,n.join)(t,e);if(await K(r))return r}let e=(0,n.dirname)(t);if(e===t)break;t=e}return null}catch(e){throw Error(`Failed to discover MCP config file: ${W(e)}`)}}function pe(e){try{let n=(0,t.readFileSync)(e,`utf-8`),r=(e.endsWith(`.yaml`)||e.endsWith(`.yml`)?o.load(n):JSON.parse(n))?.proxy;if(!r||typeof r!=`object`)return{};let i=r;return{type:typeof i.type==`string`?i.type:void 0,port:typeof i.port==`number`&&Number.isInteger(i.port)&&i.port>0?i.port:void 0,host:typeof i.host==`string`?i.host:void 0,keepAlive:typeof i.keepAlive==`boolean`?i.keepAlive:void 0}}catch{return{}}}function me(e){let t={};for(let n of e){let e=pe(n);e.type!==void 0&&(t.type=e.type),e.port!==void 0&&(t.port=e.port),e.host!==void 0&&(t.host=e.host),e.keepAlive!==void 0&&(t.keepAlive=e.keepAlive)}return t}async function he(t,n){let r=e.o();if(t.id)return t.id;if(n.length>0)try{let i=await r.createConfigFetcherService({configSources:e.L(n),useCache:t.cache!==!1}).fetchConfiguration(t.cache===!1);if(i.id)return i.id}catch(e){throw Error(`Failed to resolve server ID from config '${n.join(`, `)}': ${W(e)}`)}return e.N()}function ge(e){if(!G(e))throw Error(`Unknown transport type: '${e}'. Valid options: ${P}, ${F}, ${I}`);return e}function _e(e){if(!de(e))throw Error(`Unknown proxy mode: '${String(e)}'. Valid options: meta, flat, search, group`)}function ve(e){return e===B?B:V}function q(e,t,n){let r=process.env.MCP_PORT?Number(process.env.MCP_PORT):void 0;return{mode:t,port:e.port??(Number.isFinite(r)?r:void 0)??n?.port,host:e.host??process.env.MCP_HOST??n?.host??N}}function J(){let e=(e,t)=>{t===void 0?console.error(e):console.error(e,t)};return{trace:e,debug:e,info:e,warn:e,error:e}}function ye(e,t,n){return{configFilePath:t.length>0?t:void 0,noCache:e.cache===!1,serverId:n,definitionsCachePath:e.definitionsCache,clearDefinitionsCache:e.clearDefinitionsCache,proxyMode:e.proxyMode}}function be(e,t,n,r){let i=W(r);return e===P?`Failed to start MCP server with transport '${e}': ${i}`:`Failed to start MCP server with transport '${e}' on ${n===void 0?`${t} (dynamic port)`:`${t}:${n}`}: ${i}`}function xe(e,t,n,r,i,a=V){return{serverId:e,host:t.host??N,port:n,transport:`http`,shutdownToken:r,startedAt:(/* @__PURE__ */ new Date()).toISOString(),pid:process.pid,configPath:i,runtimeLifecycle:a}}function Se(){return new s.PortRegistryService(process.env.PORT_REGISTRY_PATH)}function Y(){return process.env.NODE_ENV??`development`}async function Ce(e,t,n,r,i,a,o=n===void 0?s.DEFAULT_PORT_RANGE:{min:n,max:n}){let c=Se(),l=await c.reservePort({repositoryPath:U(),serviceName:e,serviceType:R,environment:Y(),pid:process.pid,host:t,preferredPort:n,portRange:o,force:!0,metadata:{transport:i,serverId:r,...a?{configPath:a}:{}}});if(!l.success||!l.record){let e=n===void 0?`dynamic port`:`port ${n}`;throw Error(l.error||`Failed to reserve ${e} in port registry`)}let u=!1;return{port:l.record.port,release:async()=>{if(u)return;u=!0;let t=await c.releasePort({repositoryPath:U(),serviceName:e,serviceType:R,pid:process.pid,environment:Y(),force:!0});if(!t.success&&t.error&&!t.error.includes(`No matching registry entry`))throw Error(t.error||`Failed to release port for ${e}`)}}}async function we(e){e&&await e.release()}function Te(e,t,n,r,i){return{serverId:e,shutdownToken:t,onShutdownRequested:n,runtimeLifecycle:r,idleShutdownMs:i}}async function X(e,t){try{await e.remove(t)}catch(e){throw Error(`Failed to remove runtime state for '${t}': ${W(e)}`)}}async function Ee(e,t){try{await e.write(t)}catch(e){throw Error(`Failed to persist runtime state for '${t.serverId}': ${W(e)}`)}}async function De(e,t,n,r){try{try{await e.stop()}catch(e){throw Error(`Failed to stop owned HTTP transport '${n}': ${W(e)}`)}}finally{await r?.release({kill:!1}),await X(t,n)}}async function Oe(e,t,n){await n?.release({kill:!1}),await X(e,t)}async function ke(e,t,n,r){try{try{await e.stop()}catch(e){throw Error(`Failed to stop HTTP transport during cleanup for '${n}': ${W(e)}`)}}finally{await r?.release({kill:!1}),await X(t,n)}}async function Z(e,t,n={}){try{await e.start()}catch(e){throw Error(`Failed to start transport handler: ${W(e)}`)}let r=!1,i=async n=>{if(!r){r=!0,console.error(`\nReceived ${n}, shutting down gracefully...`);try{await e.stop(),t&&await t(),process.exit(0)}catch(e){console.error(`Failed to gracefully stop transport during ${n}: ${W(e)}`),process.exit(1)}}};process.on(`SIGINT`,async()=>await i(`SIGINT`)),process.on(`SIGTERM`,async()=>await i(`SIGTERM`)),n.shutdownOnStdinClose&&(process.stdin.on(`end`,async()=>await i(`stdin end`)),process.stdin.on(`close`,async()=>await i(`stdin close`)))}async function Ae(t,n,i,o){e.M();let c=i[0],l=await e.l(t),u=new e.T,d=(0,r.randomUUID)(),f=t.serverId??e.N(),p=ve(process.env.MCP_PROXY_RUNTIME_LIFECYCLE),m=n.port,h=m===void 0?s.DEFAULT_PORT_RANGE:{min:m,max:m},g=await Ce(L,n.host??N,m,f,F,c,h),_=g.port,v={...n,port:_},y=await(0,a.createProcessLease)({repositoryPath:U(),serviceName:`mcp-proxy-http`,serviceType:`service`,environment:Y(),host:v.host??N,port:_,command:process.argv[1],args:process.argv.slice(2),...e.v()?{processGroupId:process.pid,terminationMode:`process-group`}:{terminationMode:`direct`},metadata:{transport:F,serverId:f,runtimeLifecycle:p,...c?{configPath:c}:{}}}),b=async()=>{await we(g??null),b=async()=>void 0},x=xe(f,v,_,d,c,p),S,C=!1,w=async()=>{if(!C){C=!0;try{await De(S,u,x.serverId,y),await b(),await l.dispose(),process.exit(0)}catch(e){throw Error(`Failed to stop HTTP runtime '${x.serverId}' from admin shutdown: ${W(e)}`)}}};try{let t=Te(x.serverId,d,w,p,o);S=new e.S(()=>e.r(l),v,t)}catch(e){throw await b(),await y.release({kill:!1}),await l.dispose(),Error(`Failed to create HTTP runtime server: ${W(e)}`)}try{await Z(S,async()=>{await b(),await l.dispose(),await Oe(u,x.serverId,y)}),await Ee(u,x)}catch(e){throw await b(),await l.dispose(),await ke(S,u,x.serverId,y),Error(`Failed to start HTTP runtime '${x.serverId}': ${W(e)}`)}console.error(`Runtime state: http://${x.host}:${x.port} (${x.serverId})`)}async function je(t){let n;try{n=await e.n(t),await Z(new e.d(n,J()),async()=>{await n?.dispose?.()},{shutdownOnStdinClose:!0})}catch(e){throw await n?.dispose?.(),Error(`Failed to start stdio transport: ${W(e)}`)}}async function Me(t){if(t)try{return(await new e.T().read(t))?.runtimeLifecycle}catch{return}}function Ne(t){if(!t)return e.g;let n=Number.parseInt(t,10);return Number.isInteger(n)&&n>0?n:e.g}function Q(e,t){let i=a.ProcessRegistryService.resolveRegistryPath(process.env.PROCESS_REGISTRY_PATH),o=(0,r.createHash)(`sha1`).update(`${(0,a.normalizeRepositoryPath)(e)}|${t}`).digest(`hex`);return(0,n.join)((0,n.dirname)(i),`mcp-proxy-runtime-${o.slice(0,16)}.lock`)}async function $(e,t,n){if(e.port!==void 0)return{endpoint:new URL(`http://${e.host??N}:${e.port}${M}`),runtimeLifecycle:V};let r=U(),i=Ne(t.timeoutMs)+1e4;return await(0,a.withFileLock)(Q(r,L),async()=>Pe(r,e,t,n),{staleAfterMs:i,retryDelayMs:250,maxRetries:Math.ceil(i/250)})}async function Pe(t,n,r,i){let a=await e._(t);if(a){let e=n.host??a.host,t=a.serverId===`unknown`?void 0:a.serverId;return{endpoint:new URL(`http://${e}:${a.port}${M}`),runtimeServerId:t,runtimeLifecycle:a.runtimeLifecycle??await Me(t)??V}}let o=await e.y({host:n.host??N,config:i,cache:r.cache,definitionsCache:r.definitionsCache,clearDefinitionsCache:r.clearDefinitionsCache,proxyMode:r.proxyMode,runtimeLifecycle:B,idleShutdownMinutes:r.idleShutdownMinutes});return{endpoint:new URL(`http://${o.host}:${o.port}${M}`),runtimeServerId:o.serverId,runtimeLifecycle:o.runtimeLifecycle??V}}async function Fe(e,t,n){let{endpoint:r}=await $(e,t,n);return r}function Ie(e){return`runtime:${e}`}async function Le(e){if(e.runtimeLifecycle===B&&e.runtimeServerId)return await(0,a.createProcessLease)({repositoryPath:U(),serviceName:`mcp-proxy-stdio-http-bridge:${(0,r.randomUUID)()}`,serviceType:`tool`,environment:Y(),pid:process.pid,command:process.argv[1],args:process.argv.slice(2),tags:[z,Ie(e.runtimeServerId)],metadata:{transport:I,runtimeServerId:e.runtimeServerId,runtimeLifecycle:e.runtimeLifecycle,endpoint:e.endpoint.toString()},force:!1})}function Re(e){return e.pid===process.pid}async function ze(e){return await new a.ProcessRegistryService(process.env.PROCESS_REGISTRY_PATH).listProcesses({repositoryPath:U(),serviceType:`tool`,tags:[z,Ie(e)],environment:Y(),liveOnly:!0})}async function Be(e){return(await ze(e)).filter(e=>!Re(e)).length>0?!1:(await new Promise(e=>setTimeout(e,500)),(await ze(e)).filter(e=>!Re(e)).length===0)}async function Ve(t){if(t.runtimeLifecycle!==B||!t.runtimeServerId)return;let n=t.runtimeServerId,r=Q(U(),L);try{await(0,a.withFileLock)(r,async()=>{await Be(n)&&await new e.C().stop({serverId:n,force:!0})})}catch(e){console.error(`Failed to stop managed stdio-http runtime; leaving it alive: ${W(e)}`)}}async function He(t,n,r,i){let a=n.autoDisconnect?!0:i?.keepAlive!==void 0&&!i.keepAlive,o,s;try{o=await $(t,n,r),s=await Le(o);let{endpoint:i}=o;await Z(new e.u({endpoint:i,resolveEndpoint:async()=>await Fe(t,n,r)},J()),async()=>{try{await s?.release({kill:!1,releasePort:!1})}catch(e){console.error(`Failed to release stdio-http bridge lease; leaving runtime alive: ${W(e)}`);return}a&&o&&await Ve(o)},{shutdownOnStdinClose:!0})}catch(e){try{await s?.release({kill:!1,releasePort:!1})}catch(t){throw Error(`Failed to start stdio-http transport: ${W(e)}; also failed to release bridge lease: ${W(t)}`)}throw Error(`Failed to start stdio-http transport: ${W(e)}`)}}async function Ue(t,n,r,i,a){try{if(t===P){await je(i);return}if(t===F){await Ae(i,q(n,e.t.HTTP,a),r,ue(n.idleShutdownMinutes));return}await He(q(n,e.t.HTTP,a),n,r,a)}catch(e){throw Error(`Failed to start transport '${t}': ${W(e)}`)}}async function We(t,n){try{let r=await e.y({id:t.id,host:t.host??N,port:t.port,config:n,cache:t.cache,definitionsCache:t.definitionsCache,clearDefinitionsCache:t.clearDefinitionsCache,proxyMode:t.proxyMode,timeoutMs:t.timeoutMs,idleShutdownMinutes:t.idleShutdownMinutes});e.x(r)}catch(e){console.error(`Failed to prestart HTTP runtime '${t.id||`generated-server-id`}': ${W(e)}`),process.exit(1)}}const Ge=new l.Command(`mcp-serve`).description(`Start MCP server with specified transport or prestart the HTTP runtime in the background`).option(`-t, --type <type>`,`Transport type: ${P}, ${F}, or ${I}`).option(`-p, --port <port>`,`Port to listen on (http) or backend port for stdio-http`,e=>Number.parseInt(e,10)).option(`--host <host>`,`Host to bind to (http) or backend host for stdio-http`).option(`-c, --config <path>`,`Path to MCP server configuration file (repeatable; later files override earlier ones)`,e.F).option(`--no-cache`,`Disable configuration caching, always reload from config file`).option(`--definitions-cache <path>`,`Path to prefetched tool/prompt/skill definitions cache file`).option(`--clear-definitions-cache`,`Delete definitions cache before startup`,!1).option(`--proxy-mode <mode>`,`How mcp-proxy exposes downstream tools: meta, flat, search, or group`,`meta`).option(`--id <id>`,`Unique server identifier (overrides config file id, auto-generated if not provided)`).option(`--auto-disconnect`,`Stop the managed HTTP runtime when the last stdio-http bridge exits (default: keep runtime alive for reuse)`,!1).option(`--idle-shutdown-minutes <minutes>`,`Stop the HTTP runtime after N minutes with no MCP session and no request (0 disables; overrides ${H})`,e=>Number.parseFloat(e)).option(`--prestart-http`,`Prestart the HTTP runtime in the background and exit after it becomes healthy`,!1).option(`--timeout-ms <ms>`,`How long to wait for the HTTP runtime to become healthy`,String(e.g)).action(async t=>{try{let n=e.I(t.config),r=n.length>0?null:await fe(),i=r?[r]:n;if(t.prestartHttp){await We(t,i);return}let a=me(i),o=ge((t.type??a.type??P).toLowerCase());_e(t.proxyMode),await Ue(o,t,i,ye(t,i,await he(t,i)),a)}catch(e){let n=(t.type??P).toLowerCase(),r=G(n)?n:P,i=process.env.MCP_PORT?Number(process.env.MCP_PORT):void 0,a=t.port??(Number.isFinite(i)?i:void 0);console.error(be(r,t.host??N,a,e)),process.exit(1)}}),Ke=new l.Command(`prefetch`).description(`Pre-download packages used by MCP servers (npx, pnpx, uvx, uv)`).option(`-c, --config <path>`,`Path to MCP server configuration file (repeatable; later files override earlier ones)`,e.F).option(`-p, --parallel`,`Run prefetch commands in parallel`,!1).option(`-d, --dry-run`,`Show what would be prefetched without executing`,!1).option(`-f, --filter <type>`,`Filter by package manager type: npx, pnpx, uvx, or uv`).option(`--definitions-out <path>`,`Write discovered definitions to a JSON or YAML cache file`).option(`--skip-packages`,`Skip package prefetch and only build definitions cache`,!1).option(`--clear-definitions-cache`,`Delete the definitions cache file before continuing`,!1).action(async t=>{try{let n=e.o(),r=e.I(t.config),i=r.length>0?null:e.P(),a=i?[i]:r;a.length===0&&(O.error(`No MCP configuration file found.`),O.info(`Use --config <path> to specify a config file, or run "po-mcp-proxy init" to create one.`),process.exit(1)),O.info(`Loading configuration from: ${a.join(`, `)}`);let o=await n.createConfigFetcherService({configSources:e.L(a),useCache:!1}).fetchConfiguration(!0),s=o.id||e.N(),c=e.j.generateConfigHash(o),l=t.definitionsOut||e.j.getDefaultCachePath(a),u=n.createPrefetchService({mcpConfig:o,filter:t.filter,parallel:t.parallel}),d=u.extractPackages(),f=!t.skipPackages,p=!!t.definitionsOut;if(t.clearDefinitionsCache&&(t.dryRun?O.info(`Would clear definitions cache: ${l}`):(await e.j.clearFile(l),O.success(`Cleared definitions cache: ${l}`))),f){if(d.length===0)O.warning(`No packages found to prefetch.`),O.info(`Prefetch supports: npx, pnpx, uvx, and uv run commands`);else{O.info(`Found ${d.length} package(s) to prefetch:`);for(let e of d)O.item(`${e.serverName}: ${e.packageManager} ${e.packageName}`)}}if(!f&&!p){O.warning(`Nothing to do. Use package prefetch or provide --definitions-out.`);return}if(t.dryRun){if(f&&d.length>0){O.newline(),O.header(`Dry run mode - commands that would be executed:`);for(let e of d)O.indent(e.fullCommand.join(` `))}p&&(O.newline(),O.info(`Would write definitions cache to: ${l}`));return}let m=!1;if(f&&d.length>0){O.newline(),O.info(`Prefetching packages...`);let e=await u.prefetch();if(O.newline(),e.failed===0?O.success(`Package prefetch complete: ${e.successful} succeeded, ${e.failed} failed`):O.warning(`Package prefetch complete: ${e.successful} succeeded, ${e.failed} failed`),e.failed>0){m=!0,O.newline(),O.error(`Failed packages:`);for(let t of e.results.filter(e=>!e.success))O.item(`${t.package.serverName} (${t.package.packageName}): ${t.output.trim()}`)}}if(p){O.newline(),O.info(`Collecting definitions cache...`);let t=n.createClientManagerService(),r=o.skills?.paths||[],i=r.length>0?n.createSkillService(process.cwd(),r):void 0,u=n.createDefinitionsCacheService(t,i);await Promise.all(Object.entries(o.mcpServers).map(async([e,n])=>{try{await t.connectToServer(e,n),O.item(`Connected for definitions: ${e}`)}catch(t){O.warning(`Failed to connect for definitions: ${e} (${t instanceof Error?t.message:String(t)})`)}}));let d=await u.collectForCache({configPath:a[0],configHash:c,oneMcpVersion:e.h,serverId:s});await e.j.writeToFile(l,d),O.success(`Definitions cache written: ${l} (${Object.keys(d.servers).length} servers, ${d.skills.length} skills)`),d.failures.length>0&&O.warning(`Definitions cache completed with ${d.failures.length} server failure(s)`),await t.disconnectAll()}m&&process.exit(1)}catch(e){O.error(`Error executing prefetch: ${e instanceof Error?e.message:String(e)}`),process.exit(1)}});function qe(e){return e instanceof Error?e.message:String(e)}const Je=new l.Command(`read-resource`).description(`Read a resource by URI from a connected MCP server`).argument(`<uri>`,`Resource URI to read`).option(`-c, --config <path>`,`Path to MCP server configuration file (repeatable; later files override earlier ones)`,e.F).option(`-s, --server <name>`,`Server name (required if resource exists on multiple servers)`).option(`-j, --json`,`Output as JSON`,!1).action(async(e,t)=>{try{await x(t,async({clientManager:n})=>{let r=n.getAllClients();if(t.server){let r=n.getClient(t.server);if(!r)throw Error(`Server "${t.server}" not found`);t.json||console.error(`Reading ${e} from ${t.server}...`);let i=await r.readResource(e);if(t.json)console.log(JSON.stringify(i,null,2));else for(let e of i.contents)`text`in e?console.log(e.text):console.log(JSON.stringify(e,null,2));return}let i=await Promise.all(r.map(async t=>{try{let n=(await t.listResources()).some(t=>t.uri===e);return{serverName:t.serverName,hasResource:n,error:null}}catch(e){return{serverName:t.serverName,hasResource:!1,error:e}}})),a=[];for(let{serverName:e,hasResource:t,error:n}of i)n?console.error(`Failed to list resources from ${e}: ${qe(n)}`):t&&a.push(e);if(a.length===0)throw Error(`Resource "${e}" not found on any connected server`);if(a.length>1)throw Error(`Resource "${e}" found on multiple servers: ${a.join(`, `)}. Use --server to disambiguate`);let o=a[0],s=n.getClient(o);if(!s)throw Error(`Internal error: Server "${o}" not connected`);t.json||console.error(`Reading ${e} from ${o}...`);let c=await s.readResource(e);if(t.json)console.log(JSON.stringify(c,null,2));else for(let e of c.contents)`text`in e?console.log(e.text):console.log(JSON.stringify(e,null,2))})}catch(e){console.error(`Error executing read-resource: ${qe(e)}`),process.exit(1)}});function Ye(e){return e instanceof Error?e.message:String(e)}function Xe(e){console.log(`Stopped mcp-proxy server '${e.serverId}'.`),console.log(`Endpoint: http://${e.host}:${e.port}`),console.log(`Result: ${e.message}`)}const Ze=new l.Command(`stop`).description(`Stop a running HTTP mcp-proxy server`).option(`--id <id>`,`Target server ID from the runtime registry`).option(`--host <host>`,`Target runtime host`).option(`--port <port>`,`Target runtime port`,e=>Number.parseInt(e,10)).option(`-c, --config <path>`,`Reserved for future config-based targeting support`).option(`--token <token>`,`Override the persisted shutdown token`).option(`--force`,`Skip server ID verification against the /health response`,!1).option(`-j, --json`,`Output as JSON`,!1).option(`--timeout <ms>`,`Maximum time to wait for shutdown completion`,e=>Number.parseInt(e,10),5e3).action(async t=>{try{t.config&&console.error(`Warning: --config is not used yet; runtime resolution uses the persisted registry.`);let n=await e.o().createStopServerService().stop({serverId:t.id,host:t.host,port:t.port,token:t.token,force:t.force,timeoutMs:t.timeout});if(t.json){console.log(JSON.stringify(n,null,2));return}Xe(n)}catch(e){let n=`Error executing stop: ${Ye(e)}`;t.json?console.log(JSON.stringify({ok:!1,error:n},null,2)):console.error(n),process.exit(1)}});function Qe(e){return e instanceof Error?e.message:String(e)}const $e=new l.Command(`use-tool`).description(`Execute an MCP tool with arguments`).argument(`<toolName>`,`Tool name to execute`).option(`-c, --config <path>`,`Path to MCP server configuration file (repeatable; later files override earlier ones)`,e.F).option(`-s, --server <name>`,`Server name (required if tool exists on multiple servers)`).option(`-a, --args <json>`,`Tool arguments as JSON string`,`{}`).option(`-t, --timeout <ms>`,`Request timeout in milliseconds for tool execution (default: 60000)`,Number.parseInt).option(`-j, --json`,`Output as JSON`,!1).action(async(e,t)=>{try{let n={};try{n=JSON.parse(t.args)}catch{console.error(`Error: Invalid JSON in --args`),process.exit(1)}await x(t,async({container:r,config:i,clientManager:a})=>{let o=a.getAllClients();if(t.server){let r=a.getClient(t.server);if(!r)throw Error(`Server "${t.server}" not found`);t.json||console.error(`Executing ${e} on ${t.server}...`);let i=t.timeout?{timeout:t.timeout}:void 0,o=await r.callTool(e,n,i);if(t.json)console.log(JSON.stringify(o,null,2));else{if(console.log(`
142
142
  Result:`),o.content)for(let e of o.content)e.type===`text`?console.log(e.text):console.log(JSON.stringify(e,null,2));o.isError&&(console.error(`
143
143
  ⚠️ Tool execution returned an error`),process.exit(1))}return}let s=await Promise.all(o.map(async t=>{try{let n=(await t.listTools()).some(t=>t.name===e);return{serverName:t.serverName,hasTool:n,error:null}}catch(e){return{serverName:t.serverName,hasTool:!1,error:e}}})),c=[];for(let{serverName:e,hasTool:n,error:r}of s)r?t.json||console.error(`Failed to list tools from ${e}:`,r):n&&c.push(e);if(c.length===0){let n=i.skills?.paths||[];if(n.length>0)try{let i=process.env.PROJECT_PATH||process.cwd(),a=r.createSkillService(i,n),o=e.startsWith(`skill__`)?e.slice(7):e,s=await a.getSkill(o);if(s){let e={content:[{type:`text`,text:s.content}]};t.json?console.log(JSON.stringify(e,null,2)):(console.log(`
144
144
  Skill content:`),console.log(s.content));return}}catch(n){t.json||console.error(`Failed to lookup skill "${e}":`,n)}throw Error(`Tool or skill "${e}" not found on any connected server or configured skill paths`)}if(c.length>1)throw Error(`Tool "${e}" found on multiple servers: ${c.join(`, `)}`);let l=c[0],u=a.getClient(l);if(!u)throw Error(`Internal error: Server "${l}" not connected`);t.json||console.error(`Executing ${e} on ${l}...`);let d=t.timeout?{timeout:t.timeout}:void 0,f=await u.callTool(e,n,d);if(t.json)console.log(JSON.stringify(f,null,2));else{if(console.log(`
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,g as f,h as p,j as m,l as h,n as g,o as _,r as v,t as y,u as b,ut as x,v as ee,x as S,y as C}from"./src-Crejf8_V.mjs";import{constants as w,readFileSync as T}from"node:fs";import{dirname as E,join as te,resolve as D}from"node:path";import{createHash as ne,randomUUID as O}from"node:crypto";import{access as re,writeFile as ie}from"node:fs/promises";import{ProcessRegistryService as k,createProcessLease as ae,normalizeRepositoryPath as oe,withFileLock as se}from"@agimon-ai/foundation-process-registry";import*as ce from"js-yaml";import{DEFAULT_PORT_RANGE as le,PortRegistryService as ue}from"@agimon-ai/foundation-port-registry";import{Liquid as de}from"liquidjs";import{Command as A}from"commander";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=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=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=_(a),c=n(e.config),l=c.length>0?null: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(`
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-CzKYOfjt.mjs";import{constants as w,readFileSync as T}from"node:fs";import{dirname as E,join as te,resolve as D}from"node:path";import{createHash as ne,randomUUID as O}from"node:crypto";import{access as re,writeFile as ie}from"node:fs/promises";import{ProcessRegistryService as k,createProcessLease as ae,normalizeRepositoryPath as oe,withFileLock as se}from"@agimon-ai/foundation-process-registry";import*as ce from"js-yaml";import{DEFAULT_PORT_RANGE as le,PortRegistryService as ue}from"@agimon-ai/foundation-port-registry";import{Liquid as de}from"liquidjs";import{Command as A}from"commander";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=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=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: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:
@@ -138,9 +138,9 @@ mcpServers:
138
138
  # # omitToolDescription: true
139
139
  # # instruction: Optional user override
140
140
  {% endif %}
141
- `;function P(e=``){console.log(e)}function F(e,t){console.error(t?`${e} ${t}`:e)}const I={info:e=>P(e),error:(e,t)=>F(e,t)},L={info:e=>P(e),warning:e=>P(`Warning: ${e}`),error:e=>F(e),success:e=>P(e),newline:()=>P(),header:e=>P(e),item:e=>P(`- ${e}`),indent:e=>P(` ${e}`)},De=new A(`init`).description(`Initialize MCP configuration file`).option(`-o, --output <path>`,`Output file path`,`mcp-config.yaml`).option(`--json`,`Generate JSON config instead of YAML`,!1).option(`-f, --force`,`Overwrite existing config file`,!1).option(`--mcp-servers <json>`,`JSON string of MCP servers to add to config (optional)`).action(async e=>{try{let t=D(e.output),n=!e.json&&(t.endsWith(`.yaml`)||t.endsWith(`.yml`)),r;if(n){let t=new de,n=null;if(e.mcpServers)try{let t=JSON.parse(e.mcpServers);n=Object.entries(t).map(([e,t])=>({name:e,command:t.command,args:t.args}))}catch(e){I.error(`Failed to parse --mcp-servers JSON:`,e instanceof Error?e.message:String(e)),process.exit(1)}r=await t.parseAndRender(Ee,{mcpServers:n})}else r=Te;try{await ie(t,r,{encoding:`utf-8`,flag:e.force?`w`:`wx`})}catch(e){throw e&&typeof e==`object`&&`code`in e&&e.code===`EEXIST`&&(I.error(`Config file already exists: ${t}`),I.info(`Use --force to overwrite`),process.exit(1)),e}I.info(`MCP configuration file created: ${t}`),I.info(`Next steps:`),I.info(`1. Edit the configuration file to add your MCP servers`),I.info(`2. Run: po-mcp-proxy mcp-serve --config ${t}`)}catch(e){I.error(`Error executing init:`,e instanceof Error?e.message:String(e)),process.exit(1)}});function R(e){return e instanceof Error?e.message:String(e)}const Oe=new A(`list-prompts`).description(`List all available prompts from connected MCP servers`).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=>{try{await M(e,async({clientManager:t})=>{let n=e.server?[t.getClient(e.server)].filter(e=>e!==void 0):t.getAllClients();if(e.server&&n.length===0)throw Error(`Server "${e.server}" not found`);let r={};if(await Promise.all(n.map(async t=>{try{r[t.serverName]=await t.listPrompts()}catch(n){r[t.serverName]=[],e.json||console.error(`Failed to list prompts from ${t.serverName}: ${R(n)}`)}})),e.json)console.log(JSON.stringify(r,null,2));else for(let[e,t]of Object.entries(r))if(console.log(`\n${e}:`),t.length===0)console.log(` No prompts available`);else for(let e of t)if(console.log(` - ${e.name}: ${e.description||`No description`}`),e.arguments&&e.arguments.length>0){let t=e.arguments.map(e=>`${e.name}${e.required?` (required)`:``}`).join(`, `);console.log(` args: ${t}`)}})}catch(e){console.error(`Error executing list-prompts: ${R(e)}`),process.exit(1)}});function ke(e){return e instanceof Error?e.message:String(e)}const Ae=new A(`list-resources`).description(`List all available resources from connected MCP servers`).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=>{try{await M(e,async({clientManager:t})=>{let n=e.server?[t.getClient(e.server)].filter(e=>e!==void 0):t.getAllClients();if(e.server&&n.length===0)throw Error(`Server "${e.server}" not found`);let r={},i=await Promise.all(n.map(async e=>{try{let t=await e.listResources();return{serverName:e.serverName,resources:t,error:null}}catch(t){return{serverName:e.serverName,resources:[],error:t}}}));for(let{serverName:t,resources:n,error:a}of i)a&&!e.json&&console.error(`Failed to list resources from ${t}: ${ke(a)}`),r[t]=n;if(e.json)console.log(JSON.stringify(r,null,2));else for(let[e,t]of Object.entries(r))if(console.log(`\n${e}:`),t.length===0)console.log(` No resources available`);else for(let e of t){let t=e.name?`${e.name} (${e.uri})`:e.uri;console.log(` - ${t}${e.description?`: ${e.description}`:``}`)}})}catch(e){console.error(`Error executing list-resources: ${ke(e)}`),process.exit(1)}});function je(e){return e instanceof Error?e.message:String(e)}function Me(e){for(let t of e.servers)if(console.log(`\n${t.server}:`),t.capabilities&&t.capabilities.length>0&&console.log(` capabilities: ${t.capabilities.join(`, `)}`),t.summary&&console.log(` summary: ${t.summary}`),t.tools.length===0)console.log(` no tools`);else for(let e of t.tools){let t=e.capabilities&&e.capabilities.length>0?` [${e.capabilities.join(`, `)}]`:``;console.log(` - ${e.name}${t}`),e.description&&console.log(` ${e.description}`)}}const Ne=new A(`search-tools`).description(`Search proxied MCP tools by capability or server`).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(`--capability <name>`,`Filter by capability tag, summary, tool name, or description`).option(`--definitions-cache <path>`,`Path to definitions cache file`).option(`-j, --json`,`Output as JSON`,!1).action(async e=>{try{await M(e,async({container:t,config:n,clientManager:r,configFilePaths:i})=>{r.registerServerConfigs(n.mcpServers);let a=e.definitionsCache||m.getDefaultCachePath(i),o;try{o=await m.readFromFile(a)}catch{o=void 0}let s=t.createDefinitionsCacheService(r,void 0,{cacheData:o}),c=(await t.createSearchListToolsTool(r,s).execute({capability:e.capability,serverName:e.server})).content.find(e=>e.type===`text`),l=c?.type===`text`?JSON.parse(c.text):{servers:[]};if(e.json)console.log(JSON.stringify(l,null,2));else{if(!l.servers||l.servers.length===0)throw Error(`No tools matched the requested filters`);Me(l)}})}catch(e){console.error(`Error executing search-tools: ${je(e)}`),process.exit(1)}}),Pe=[`mcp-config.yaml`,`mcp-config.yml`,`mcp-config.json`],z=`/mcp`,B=`localhost`,V=`stdio`,H=`http`,U=`stdio-http`,W=`mcp-proxy-http`,Fe=`service`;function Ie(){return x()}const G=`mcp-proxy-stdio-http-bridge`,K=`stdio-http-managed`,q=`manual`,Le=`MCP_PROXY_IDLE_SHUTDOWN_MINUTES`;function J(){return Ie()}function Y(e){return e instanceof Error?e.message:String(e)}function Re(e){let t=process.env[Le],n=e??(t?Number.parseFloat(t):void 0);return n===void 0||!Number.isFinite(n)||n<=0?0:Math.round(n*6e4)}function ze(e){return e===V||e===H||e===U}function Be(e){return e===`meta`||e===`flat`||e===`search`||e===`group`}async function Ve(e){try{return await re(e,w.F_OK),!0}catch{return!1}}async function He(){try{let e=process.env.PROJECT_PATH;if(e)for(let t of Pe){let n=D(e,t);if(await Ve(n))return n}let t=process.cwd();for(let e=0;e<=3;e++){for(let e of Pe){let n=te(t,e);if(await Ve(n))return n}let e=E(t);if(e===t)break;t=e}return null}catch(e){throw Error(`Failed to discover MCP config file: ${Y(e)}`)}}function Ue(e){try{let t=T(e,`utf-8`),n=(e.endsWith(`.yaml`)||e.endsWith(`.yml`)?ce.load(t):JSON.parse(t))?.proxy;if(!n||typeof n!=`object`)return{};let r=n;return{type:typeof r.type==`string`?r.type:void 0,port:typeof r.port==`number`&&Number.isInteger(r.port)&&r.port>0?r.port:void 0,host:typeof r.host==`string`?r.host:void 0,keepAlive:typeof r.keepAlive==`boolean`?r.keepAlive:void 0}}catch{return{}}}function We(e){let t={};for(let n of e){let e=Ue(n);e.type!==void 0&&(t.type=e.type),e.port!==void 0&&(t.port=e.port),e.host!==void 0&&(t.host=e.host),e.keepAlive!==void 0&&(t.keepAlive=e.keepAlive)}return t}async function Ge(e,t){let n=_();if(e.id)return e.id;if(t.length>0)try{let i=await n.createConfigFetcherService({configSources:r(t),useCache:e.cache!==!1}).fetchConfiguration(e.cache===!1);if(i.id)return i.id}catch(e){throw Error(`Failed to resolve server ID from config '${t.join(`, `)}': ${Y(e)}`)}return a()}function Ke(e){if(!ze(e))throw Error(`Unknown transport type: '${e}'. Valid options: ${V}, ${H}, ${U}`);return e}function qe(e){if(!Be(e))throw Error(`Unknown proxy mode: '${String(e)}'. Valid options: meta, flat, search, group`)}function Je(e){return e===K?K:q}function Ye(e,t,n){let r=process.env.MCP_PORT?Number(process.env.MCP_PORT):void 0;return{mode:t,port:e.port??(Number.isFinite(r)?r:void 0)??n?.port,host:e.host??process.env.MCP_HOST??n?.host??B}}function Xe(){let e=(e,t)=>{t===void 0?console.error(e):console.error(e,t)};return{trace:e,debug:e,info:e,warn:e,error:e}}function Ze(e,t,n){return{configFilePath:t.length>0?t:void 0,noCache:e.cache===!1,serverId:n,definitionsCachePath:e.definitionsCache,clearDefinitionsCache:e.clearDefinitionsCache,proxyMode:e.proxyMode}}function Qe(e,t,n,r){let i=Y(r);return e===V?`Failed to start MCP server with transport '${e}': ${i}`:`Failed to start MCP server with transport '${e}' on ${n===void 0?`${t} (dynamic port)`:`${t}:${n}`}: ${i}`}function $e(e,t,n,r,i,a=q){return{serverId:e,host:t.host??B,port:n,transport:`http`,shutdownToken:r,startedAt:(/* @__PURE__ */ new Date()).toISOString(),pid:process.pid,configPath:i,runtimeLifecycle:a}}function et(){return new ue(process.env.PORT_REGISTRY_PATH)}function X(){return process.env.NODE_ENV??`development`}async function tt(e,t,n,r,i,a,o=n===void 0?le:{min:n,max:n}){let s=et(),c=await s.reservePort({repositoryPath:J(),serviceName:e,serviceType:Fe,environment:X(),pid:process.pid,host:t,preferredPort:n,portRange:o,force:!0,metadata:{transport:i,serverId:r,...a?{configPath:a}:{}}});if(!c.success||!c.record){let e=n===void 0?`dynamic port`:`port ${n}`;throw Error(c.error||`Failed to reserve ${e} in port registry`)}let l=!1;return{port:c.record.port,release:async()=>{if(l)return;l=!0;let t=await s.releasePort({repositoryPath:J(),serviceName:e,serviceType:Fe,pid:process.pid,environment:X(),force:!0});if(!t.success&&t.error&&!t.error.includes(`No matching registry entry`))throw Error(t.error||`Failed to release port for ${e}`)}}}async function nt(e){e&&await e.release()}function rt(e,t,n,r,i){return{serverId:e,shutdownToken:t,onShutdownRequested:n,runtimeLifecycle:r,idleShutdownMs:i}}async function Z(e,t){try{await e.remove(t)}catch(e){throw Error(`Failed to remove runtime state for '${t}': ${Y(e)}`)}}async function it(e,t){try{await e.write(t)}catch(e){throw Error(`Failed to persist runtime state for '${t.serverId}': ${Y(e)}`)}}async function at(e,t,n,r){try{try{await e.stop()}catch(e){throw Error(`Failed to stop owned HTTP transport '${n}': ${Y(e)}`)}}finally{await r?.release({kill:!1}),await Z(t,n)}}async function ot(e,t,n){await n?.release({kill:!1}),await Z(e,t)}async function st(e,t,n,r){try{try{await e.stop()}catch(e){throw Error(`Failed to stop HTTP transport during cleanup for '${n}': ${Y(e)}`)}}finally{await r?.release({kill:!1}),await Z(t,n)}}async function Q(e,t,n={}){try{await e.start()}catch(e){throw Error(`Failed to start transport handler: ${Y(e)}`)}let r=!1,i=async n=>{if(!r){r=!0,console.error(`\nReceived ${n}, shutting down gracefully...`);try{await e.stop(),t&&await t(),process.exit(0)}catch(e){console.error(`Failed to gracefully stop transport during ${n}: ${Y(e)}`),process.exit(1)}}};process.on(`SIGINT`,async()=>await i(`SIGINT`)),process.on(`SIGTERM`,async()=>await i(`SIGTERM`)),n.shutdownOnStdinClose&&(process.stdin.on(`end`,async()=>await i(`stdin end`)),process.stdin.on(`close`,async()=>await i(`stdin close`)))}async function ct(e,t,n,r){i();let o=n[0],l=await h(e),u=new c,d=O(),f=e.serverId??a(),p=Je(process.env.MCP_PROXY_RUNTIME_LIFECYCLE),m=t.port,g=m===void 0?le:{min:m,max:m},_=await tt(W,t.host??B,m,f,H,o,g),y=_.port,b={...t,port:y},x=await ae({repositoryPath:J(),serviceName:`mcp-proxy-http`,serviceType:`service`,environment:X(),host:b.host??B,port:y,command:process.argv[1],args:process.argv.slice(2),...ee()?{processGroupId:process.pid,terminationMode:`process-group`}:{terminationMode:`direct`},metadata:{transport:H,serverId:f,runtimeLifecycle:p,...o?{configPath:o}:{}}}),S=async()=>{await nt(_??null),S=async()=>void 0},C=$e(f,b,y,d,o,p),w,T=!1,E=async()=>{if(!T){T=!0;try{await at(w,u,C.serverId,x),await S(),await l.dispose(),process.exit(0)}catch(e){throw Error(`Failed to stop HTTP runtime '${C.serverId}' from admin shutdown: ${Y(e)}`)}}};try{let e=rt(C.serverId,d,E,p,r);w=new s(()=>v(l),b,e)}catch(e){throw await S(),await x.release({kill:!1}),await l.dispose(),Error(`Failed to create HTTP runtime server: ${Y(e)}`)}try{await Q(w,async()=>{await S(),await l.dispose(),await ot(u,C.serverId,x)}),await it(u,C)}catch(e){throw await S(),await l.dispose(),await st(w,u,C.serverId,x),Error(`Failed to start HTTP runtime '${C.serverId}': ${Y(e)}`)}console.error(`Runtime state: http://${C.host}:${C.port} (${C.serverId})`)}async function lt(e){let t;try{t=await g(e),await Q(new d(t,Xe()),async()=>{await t?.dispose?.()},{shutdownOnStdinClose:!0})}catch(e){throw await t?.dispose?.(),Error(`Failed to start stdio transport: ${Y(e)}`)}}async function ut(e){if(e)try{return(await new c().read(e))?.runtimeLifecycle}catch{return}}function dt(e){if(!e)return f;let t=Number.parseInt(e,10);return Number.isInteger(t)&&t>0?t:f}function ft(e,t){let n=k.resolveRegistryPath(process.env.PROCESS_REGISTRY_PATH),r=ne(`sha1`).update(`${oe(e)}|${t}`).digest(`hex`);return te(E(n),`mcp-proxy-runtime-${r.slice(0,16)}.lock`)}async function pt(e,t,n){if(e.port!==void 0)return{endpoint:new URL(`http://${e.host??B}:${e.port}${z}`),runtimeLifecycle:q};let r=J(),i=dt(t.timeoutMs)+1e4;return await se(ft(r,W),async()=>mt(r,e,t,n),{staleAfterMs:i,retryDelayMs:250,maxRetries:Math.ceil(i/250)})}async function mt(e,t,n,r){let i=await l(e);if(i){let e=t.host??i.host,n=i.serverId===`unknown`?void 0:i.serverId;return{endpoint:new URL(`http://${e}:${i.port}${z}`),runtimeServerId:n,runtimeLifecycle:i.runtimeLifecycle??await ut(n)??q}}let a=await C({host:t.host??B,config:r,cache:n.cache,definitionsCache:n.definitionsCache,clearDefinitionsCache:n.clearDefinitionsCache,proxyMode:n.proxyMode,runtimeLifecycle:K,idleShutdownMinutes:n.idleShutdownMinutes});return{endpoint:new URL(`http://${a.host}:${a.port}${z}`),runtimeServerId:a.serverId,runtimeLifecycle:a.runtimeLifecycle??q}}async function ht(e,t,n){let{endpoint:r}=await pt(e,t,n);return r}function gt(e){return`runtime:${e}`}async function _t(e){if(e.runtimeLifecycle===K&&e.runtimeServerId)return await ae({repositoryPath:J(),serviceName:`mcp-proxy-stdio-http-bridge:${O()}`,serviceType:`tool`,environment:X(),pid:process.pid,command:process.argv[1],args:process.argv.slice(2),tags:[G,gt(e.runtimeServerId)],metadata:{transport:U,runtimeServerId:e.runtimeServerId,runtimeLifecycle:e.runtimeLifecycle,endpoint:e.endpoint.toString()},force:!1})}function vt(e){return e.pid===process.pid}async function yt(e){return await new k(process.env.PROCESS_REGISTRY_PATH).listProcesses({repositoryPath:J(),serviceType:`tool`,tags:[G,gt(e)],environment:X(),liveOnly:!0})}async function bt(e){return(await yt(e)).filter(e=>!vt(e)).length>0?!1:(await new Promise(e=>setTimeout(e,500)),(await yt(e)).filter(e=>!vt(e)).length===0)}async function xt(t){if(t.runtimeLifecycle!==K||!t.runtimeServerId)return;let n=t.runtimeServerId,r=ft(J(),W);try{await se(r,async()=>{await bt(n)&&await new e().stop({serverId:n,force:!0})})}catch(e){console.error(`Failed to stop managed stdio-http runtime; leaving it alive: ${Y(e)}`)}}async function St(e,t,n,r){let i=t.autoDisconnect?!0:r?.keepAlive!==void 0&&!r.keepAlive,a,o;try{a=await pt(e,t,n),o=await _t(a);let{endpoint:r}=a;await Q(new b({endpoint:r,resolveEndpoint:async()=>await ht(e,t,n)},Xe()),async()=>{try{await o?.release({kill:!1,releasePort:!1})}catch(e){console.error(`Failed to release stdio-http bridge lease; leaving runtime alive: ${Y(e)}`);return}i&&a&&await xt(a)},{shutdownOnStdinClose:!0})}catch(e){try{await o?.release({kill:!1,releasePort:!1})}catch(t){throw Error(`Failed to start stdio-http transport: ${Y(e)}; also failed to release bridge lease: ${Y(t)}`)}throw Error(`Failed to start stdio-http transport: ${Y(e)}`)}}async function Ct(e,t,n,r,i){try{if(e===V){await lt(r);return}if(e===H){await ct(r,Ye(t,y.HTTP,i),n,Re(t.idleShutdownMinutes));return}await St(Ye(t,y.HTTP,i),t,n,i)}catch(t){throw Error(`Failed to start transport '${e}': ${Y(t)}`)}}async function wt(e,t){try{let n=await C({id:e.id,host:e.host??B,port:e.port,config:t,cache:e.cache,definitionsCache:e.definitionsCache,clearDefinitionsCache:e.clearDefinitionsCache,proxyMode:e.proxyMode,timeoutMs:e.timeoutMs,idleShutdownMinutes:e.idleShutdownMinutes});S(n)}catch(t){console.error(`Failed to prestart HTTP runtime '${e.id||`generated-server-id`}': ${Y(t)}`),process.exit(1)}}const Tt=new A(`mcp-serve`).description(`Start MCP server with specified transport or prestart the HTTP runtime in the background`).option(`-t, --type <type>`,`Transport type: ${V}, ${H}, or ${U}`).option(`-p, --port <port>`,`Port to listen on (http) or backend port for stdio-http`,e=>Number.parseInt(e,10)).option(`--host <host>`,`Host to bind to (http) or backend host for stdio-http`).option(`-c, --config <path>`,`Path to MCP server configuration file (repeatable; later files override earlier ones)`,t).option(`--no-cache`,`Disable configuration caching, always reload from config file`).option(`--definitions-cache <path>`,`Path to prefetched tool/prompt/skill definitions cache file`).option(`--clear-definitions-cache`,`Delete definitions cache before startup`,!1).option(`--proxy-mode <mode>`,`How mcp-proxy exposes downstream tools: meta, flat, search, or group`,`meta`).option(`--id <id>`,`Unique server identifier (overrides config file id, auto-generated if not provided)`).option(`--auto-disconnect`,`Stop the managed HTTP runtime when the last stdio-http bridge exits (default: keep runtime alive for reuse)`,!1).option(`--idle-shutdown-minutes <minutes>`,`Stop the HTTP runtime after N minutes with no MCP session and no request (0 disables; overrides ${Le})`,e=>Number.parseFloat(e)).option(`--prestart-http`,`Prestart the HTTP runtime in the background and exit after it becomes healthy`,!1).option(`--timeout-ms <ms>`,`How long to wait for the HTTP runtime to become healthy`,String(f)).action(async e=>{try{let t=n(e.config),r=t.length>0?null:await He(),i=r?[r]:t;if(e.prestartHttp){await wt(e,i);return}let a=We(i),o=Ke((e.type??a.type??V).toLowerCase());qe(e.proxyMode),await Ct(o,e,i,Ze(e,i,await Ge(e,i)),a)}catch(t){let n=(e.type??V).toLowerCase(),r=ze(n)?n:V,i=process.env.MCP_PORT?Number(process.env.MCP_PORT):void 0,a=e.port??(Number.isFinite(i)?i:void 0);console.error(Qe(r,e.host??B,a,t)),process.exit(1)}}),Et=new A(`prefetch`).description(`Pre-download packages used by MCP servers (npx, pnpx, uvx, uv)`).option(`-c, --config <path>`,`Path to MCP server configuration file (repeatable; later files override earlier ones)`,t).option(`-p, --parallel`,`Run prefetch commands in parallel`,!1).option(`-d, --dry-run`,`Show what would be prefetched without executing`,!1).option(`-f, --filter <type>`,`Filter by package manager type: npx, pnpx, uvx, or uv`).option(`--definitions-out <path>`,`Write discovered definitions to a JSON or YAML cache file`).option(`--skip-packages`,`Skip package prefetch and only build definitions cache`,!1).option(`--clear-definitions-cache`,`Delete the definitions cache file before continuing`,!1).action(async e=>{try{let t=_(),i=n(e.config),s=i.length>0?null:o(),c=s?[s]:i;c.length===0&&(L.error(`No MCP configuration file found.`),L.info(`Use --config <path> to specify a config file, or run "po-mcp-proxy init" to create one.`),process.exit(1)),L.info(`Loading configuration from: ${c.join(`, `)}`);let l=await t.createConfigFetcherService({configSources:r(c),useCache:!1}).fetchConfiguration(!0),u=l.id||a(),d=m.generateConfigHash(l),f=e.definitionsOut||m.getDefaultCachePath(c),h=t.createPrefetchService({mcpConfig:l,filter:e.filter,parallel:e.parallel}),g=h.extractPackages(),v=!e.skipPackages,y=!!e.definitionsOut;if(e.clearDefinitionsCache&&(e.dryRun?L.info(`Would clear definitions cache: ${f}`):(await m.clearFile(f),L.success(`Cleared definitions cache: ${f}`))),v){if(g.length===0)L.warning(`No packages found to prefetch.`),L.info(`Prefetch supports: npx, pnpx, uvx, and uv run commands`);else{L.info(`Found ${g.length} package(s) to prefetch:`);for(let e of g)L.item(`${e.serverName}: ${e.packageManager} ${e.packageName}`)}}if(!v&&!y){L.warning(`Nothing to do. Use package prefetch or provide --definitions-out.`);return}if(e.dryRun){if(v&&g.length>0){L.newline(),L.header(`Dry run mode - commands that would be executed:`);for(let e of g)L.indent(e.fullCommand.join(` `))}y&&(L.newline(),L.info(`Would write definitions cache to: ${f}`));return}let b=!1;if(v&&g.length>0){L.newline(),L.info(`Prefetching packages...`);let e=await h.prefetch();if(L.newline(),e.failed===0?L.success(`Package prefetch complete: ${e.successful} succeeded, ${e.failed} failed`):L.warning(`Package prefetch complete: ${e.successful} succeeded, ${e.failed} failed`),e.failed>0){b=!0,L.newline(),L.error(`Failed packages:`);for(let t of e.results.filter(e=>!e.success))L.item(`${t.package.serverName} (${t.package.packageName}): ${t.output.trim()}`)}}if(y){L.newline(),L.info(`Collecting definitions cache...`);let e=t.createClientManagerService(),n=l.skills?.paths||[],r=n.length>0?t.createSkillService(process.cwd(),n):void 0,i=t.createDefinitionsCacheService(e,r);await Promise.all(Object.entries(l.mcpServers).map(async([t,n])=>{try{await e.connectToServer(t,n),L.item(`Connected for definitions: ${t}`)}catch(e){L.warning(`Failed to connect for definitions: ${t} (${e instanceof Error?e.message:String(e)})`)}}));let a=await i.collectForCache({configPath:c[0],configHash:d,oneMcpVersion:p,serverId:u});await m.writeToFile(f,a),L.success(`Definitions cache written: ${f} (${Object.keys(a.servers).length} servers, ${a.skills.length} skills)`),a.failures.length>0&&L.warning(`Definitions cache completed with ${a.failures.length} server failure(s)`),await e.disconnectAll()}b&&process.exit(1)}catch(e){L.error(`Error executing prefetch: ${e instanceof Error?e.message:String(e)}`),process.exit(1)}});function $(e){return e instanceof Error?e.message:String(e)}const Dt=new A(`read-resource`).description(`Read a resource by URI from a connected MCP server`).argument(`<uri>`,`Resource URI to read`).option(`-c, --config <path>`,`Path to MCP server configuration file (repeatable; later files override earlier ones)`,t).option(`-s, --server <name>`,`Server name (required if resource exists on multiple servers)`).option(`-j, --json`,`Output as JSON`,!1).action(async(e,t)=>{try{await M(t,async({clientManager:n})=>{let r=n.getAllClients();if(t.server){let r=n.getClient(t.server);if(!r)throw Error(`Server "${t.server}" not found`);t.json||console.error(`Reading ${e} from ${t.server}...`);let i=await r.readResource(e);if(t.json)console.log(JSON.stringify(i,null,2));else for(let e of i.contents)`text`in e?console.log(e.text):console.log(JSON.stringify(e,null,2));return}let i=await Promise.all(r.map(async t=>{try{let n=(await t.listResources()).some(t=>t.uri===e);return{serverName:t.serverName,hasResource:n,error:null}}catch(e){return{serverName:t.serverName,hasResource:!1,error:e}}})),a=[];for(let{serverName:e,hasResource:t,error:n}of i)n?console.error(`Failed to list resources from ${e}: ${$(n)}`):t&&a.push(e);if(a.length===0)throw Error(`Resource "${e}" not found on any connected server`);if(a.length>1)throw Error(`Resource "${e}" found on multiple servers: ${a.join(`, `)}. Use --server to disambiguate`);let o=a[0],s=n.getClient(o);if(!s)throw Error(`Internal error: Server "${o}" not connected`);t.json||console.error(`Reading ${e} from ${o}...`);let c=await s.readResource(e);if(t.json)console.log(JSON.stringify(c,null,2));else for(let e of c.contents)`text`in e?console.log(e.text):console.log(JSON.stringify(e,null,2))})}catch(e){console.error(`Error executing read-resource: ${$(e)}`),process.exit(1)}});function Ot(e){return e instanceof Error?e.message:String(e)}function kt(e){console.log(`Stopped mcp-proxy server '${e.serverId}'.`),console.log(`Endpoint: http://${e.host}:${e.port}`),console.log(`Result: ${e.message}`)}const At=new A(`stop`).description(`Stop a running HTTP mcp-proxy server`).option(`--id <id>`,`Target server ID from the runtime registry`).option(`--host <host>`,`Target runtime host`).option(`--port <port>`,`Target runtime port`,e=>Number.parseInt(e,10)).option(`-c, --config <path>`,`Reserved for future config-based targeting support`).option(`--token <token>`,`Override the persisted shutdown token`).option(`--force`,`Skip server ID verification against the /health response`,!1).option(`-j, --json`,`Output as JSON`,!1).option(`--timeout <ms>`,`Maximum time to wait for shutdown completion`,e=>Number.parseInt(e,10),5e3).action(async e=>{try{e.config&&console.error(`Warning: --config is not used yet; runtime resolution uses the persisted registry.`);let t=await _().createStopServerService().stop({serverId:e.id,host:e.host,port:e.port,token:e.token,force:e.force,timeoutMs:e.timeout});if(e.json){console.log(JSON.stringify(t,null,2));return}kt(t)}catch(t){let n=`Error executing stop: ${Ot(t)}`;e.json?console.log(JSON.stringify({ok:!1,error:n},null,2)):console.error(n),process.exit(1)}});function jt(e){return e instanceof Error?e.message:String(e)}const Mt=new A(`use-tool`).description(`Execute an MCP tool with arguments`).argument(`<toolName>`,`Tool name to execute`).option(`-c, --config <path>`,`Path to MCP server configuration file (repeatable; later files override earlier ones)`,t).option(`-s, --server <name>`,`Server name (required if tool exists on multiple servers)`).option(`-a, --args <json>`,`Tool arguments as JSON string`,`{}`).option(`-t, --timeout <ms>`,`Request timeout in milliseconds for tool execution (default: 60000)`,Number.parseInt).option(`-j, --json`,`Output as JSON`,!1).action(async(e,t)=>{try{let n={};try{n=JSON.parse(t.args)}catch{console.error(`Error: Invalid JSON in --args`),process.exit(1)}await M(t,async({container:r,config:i,clientManager:a})=>{let o=a.getAllClients();if(t.server){let r=a.getClient(t.server);if(!r)throw Error(`Server "${t.server}" not found`);t.json||console.error(`Executing ${e} on ${t.server}...`);let i=t.timeout?{timeout:t.timeout}:void 0,o=await r.callTool(e,n,i);if(t.json)console.log(JSON.stringify(o,null,2));else{if(console.log(`
141
+ `;function P(e=``){console.log(e)}function F(e,t){console.error(t?`${e} ${t}`:e)}const I={info:e=>P(e),error:(e,t)=>F(e,t)},L={info:e=>P(e),warning:e=>P(`Warning: ${e}`),error:e=>F(e),success:e=>P(e),newline:()=>P(),header:e=>P(e),item:e=>P(`- ${e}`),indent:e=>P(` ${e}`)},De=new A(`init`).description(`Initialize MCP configuration file`).option(`-o, --output <path>`,`Output file path`,`mcp-config.yaml`).option(`--json`,`Generate JSON config instead of YAML`,!1).option(`-f, --force`,`Overwrite existing config file`,!1).option(`--mcp-servers <json>`,`JSON string of MCP servers to add to config (optional)`).action(async e=>{try{let t=D(e.output),n=!e.json&&(t.endsWith(`.yaml`)||t.endsWith(`.yml`)),r;if(n){let t=new de,n=null;if(e.mcpServers)try{let t=JSON.parse(e.mcpServers);n=Object.entries(t).map(([e,t])=>({name:e,command:t.command,args:t.args}))}catch(e){I.error(`Failed to parse --mcp-servers JSON:`,e instanceof Error?e.message:String(e)),process.exit(1)}r=await t.parseAndRender(Ee,{mcpServers:n})}else r=Te;try{await ie(t,r,{encoding:`utf-8`,flag:e.force?`w`:`wx`})}catch(e){throw e&&typeof e==`object`&&`code`in e&&e.code===`EEXIST`&&(I.error(`Config file already exists: ${t}`),I.info(`Use --force to overwrite`),process.exit(1)),e}I.info(`MCP configuration file created: ${t}`),I.info(`Next steps:`),I.info(`1. Edit the configuration file to add your MCP servers`),I.info(`2. Run: po-mcp-proxy mcp-serve --config ${t}`)}catch(e){I.error(`Error executing init:`,e instanceof Error?e.message:String(e)),process.exit(1)}});function R(e){return e instanceof Error?e.message:String(e)}const Oe=new A(`list-prompts`).description(`List all available prompts from connected MCP servers`).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=>{try{await M(e,async({clientManager:t})=>{let n=e.server?[t.getClient(e.server)].filter(e=>e!==void 0):t.getAllClients();if(e.server&&n.length===0)throw Error(`Server "${e.server}" not found`);let r={};if(await Promise.all(n.map(async t=>{try{r[t.serverName]=await t.listPrompts()}catch(n){r[t.serverName]=[],e.json||console.error(`Failed to list prompts from ${t.serverName}: ${R(n)}`)}})),e.json)console.log(JSON.stringify(r,null,2));else for(let[e,t]of Object.entries(r))if(console.log(`\n${e}:`),t.length===0)console.log(` No prompts available`);else for(let e of t)if(console.log(` - ${e.name}: ${e.description||`No description`}`),e.arguments&&e.arguments.length>0){let t=e.arguments.map(e=>`${e.name}${e.required?` (required)`:``}`).join(`, `);console.log(` args: ${t}`)}})}catch(e){console.error(`Error executing list-prompts: ${R(e)}`),process.exit(1)}});function ke(e){return e instanceof Error?e.message:String(e)}const Ae=new A(`list-resources`).description(`List all available resources from connected MCP servers`).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=>{try{await M(e,async({clientManager:t})=>{let n=e.server?[t.getClient(e.server)].filter(e=>e!==void 0):t.getAllClients();if(e.server&&n.length===0)throw Error(`Server "${e.server}" not found`);let r={},i=await Promise.all(n.map(async e=>{try{let t=await e.listResources();return{serverName:e.serverName,resources:t,error:null}}catch(t){return{serverName:e.serverName,resources:[],error:t}}}));for(let{serverName:t,resources:n,error:a}of i)a&&!e.json&&console.error(`Failed to list resources from ${t}: ${ke(a)}`),r[t]=n;if(e.json)console.log(JSON.stringify(r,null,2));else for(let[e,t]of Object.entries(r))if(console.log(`\n${e}:`),t.length===0)console.log(` No resources available`);else for(let e of t){let t=e.name?`${e.name} (${e.uri})`:e.uri;console.log(` - ${t}${e.description?`: ${e.description}`:``}`)}})}catch(e){console.error(`Error executing list-resources: ${ke(e)}`),process.exit(1)}});function je(e){return e instanceof Error?e.message:String(e)}function Me(e){for(let t of e.servers)if(console.log(`\n${t.server}:`),t.capabilities&&t.capabilities.length>0&&console.log(` capabilities: ${t.capabilities.join(`, `)}`),t.summary&&console.log(` summary: ${t.summary}`),t.tools.length===0)console.log(` no tools`);else for(let e of t.tools){let t=e.capabilities&&e.capabilities.length>0?` [${e.capabilities.join(`, `)}]`:``;console.log(` - ${e.name}${t}`),e.description&&console.log(` ${e.description}`)}}const Ne=new A(`search-tools`).description(`Search proxied MCP tools by capability or server`).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(`--capability <name>`,`Filter by capability tag, summary, tool name, or description`).option(`--definitions-cache <path>`,`Path to definitions cache file`).option(`-j, --json`,`Output as JSON`,!1).action(async e=>{try{await M(e,async({container:t,config:n,clientManager:r,configFilePaths:i})=>{r.registerServerConfigs(n.mcpServers);let a=e.definitionsCache||h.getDefaultCachePath(i),o;try{o=await h.readFromFile(a)}catch{o=void 0}let s=t.createDefinitionsCacheService(r,void 0,{cacheData:o}),c=(await t.createSearchListToolsTool(r,s).execute({capability:e.capability,serverName:e.server})).content.find(e=>e.type===`text`),l=c?.type===`text`?JSON.parse(c.text):{servers:[]};if(e.json)console.log(JSON.stringify(l,null,2));else{if(!l.servers||l.servers.length===0)throw Error(`No tools matched the requested filters`);Me(l)}})}catch(e){console.error(`Error executing search-tools: ${je(e)}`),process.exit(1)}}),Pe=[`mcp-config.yaml`,`mcp-config.yml`,`mcp-config.json`],z=`/mcp`,B=`localhost`,V=`stdio`,H=`http`,U=`stdio-http`,W=`mcp-proxy-http`,Fe=`service`;function Ie(){return f()}const G=`mcp-proxy-stdio-http-bridge`,K=`stdio-http-managed`,q=`manual`,Le=`MCP_PROXY_IDLE_SHUTDOWN_MINUTES`;function J(){return Ie()}function Y(e){return e instanceof Error?e.message:String(e)}function Re(e){let t=process.env[Le],n=e??(t?Number.parseFloat(t):void 0);return n===void 0||!Number.isFinite(n)||n<=0?0:Math.round(n*6e4)}function ze(e){return e===V||e===H||e===U}function Be(e){return e===`meta`||e===`flat`||e===`search`||e===`group`}async function Ve(e){try{return await re(e,w.F_OK),!0}catch{return!1}}async function He(){try{let e=process.env.PROJECT_PATH;if(e)for(let t of Pe){let n=D(e,t);if(await Ve(n))return n}let t=process.cwd();for(let e=0;e<=3;e++){for(let e of Pe){let n=te(t,e);if(await Ve(n))return n}let e=E(t);if(e===t)break;t=e}return null}catch(e){throw Error(`Failed to discover MCP config file: ${Y(e)}`)}}function Ue(e){try{let t=T(e,`utf-8`),n=(e.endsWith(`.yaml`)||e.endsWith(`.yml`)?ce.load(t):JSON.parse(t))?.proxy;if(!n||typeof n!=`object`)return{};let r=n;return{type:typeof r.type==`string`?r.type:void 0,port:typeof r.port==`number`&&Number.isInteger(r.port)&&r.port>0?r.port:void 0,host:typeof r.host==`string`?r.host:void 0,keepAlive:typeof r.keepAlive==`boolean`?r.keepAlive:void 0}}catch{return{}}}function We(e){let t={};for(let n of e){let e=Ue(n);e.type!==void 0&&(t.type=e.type),e.port!==void 0&&(t.port=e.port),e.host!==void 0&&(t.host=e.host),e.keepAlive!==void 0&&(t.keepAlive=e.keepAlive)}return t}async function Ge(e,t){let n=v();if(e.id)return e.id;if(t.length>0)try{let i=await n.createConfigFetcherService({configSources:r(t),useCache:e.cache!==!1}).fetchConfiguration(e.cache===!1);if(i.id)return i.id}catch(e){throw Error(`Failed to resolve server ID from config '${t.join(`, `)}': ${Y(e)}`)}return a()}function Ke(e){if(!ze(e))throw Error(`Unknown transport type: '${e}'. Valid options: ${V}, ${H}, ${U}`);return e}function qe(e){if(!Be(e))throw Error(`Unknown proxy mode: '${String(e)}'. Valid options: meta, flat, search, group`)}function Je(e){return e===K?K:q}function Ye(e,t,n){let r=process.env.MCP_PORT?Number(process.env.MCP_PORT):void 0;return{mode:t,port:e.port??(Number.isFinite(r)?r:void 0)??n?.port,host:e.host??process.env.MCP_HOST??n?.host??B}}function Xe(){let e=(e,t)=>{t===void 0?console.error(e):console.error(e,t)};return{trace:e,debug:e,info:e,warn:e,error:e}}function Ze(e,t,n){return{configFilePath:t.length>0?t:void 0,noCache:e.cache===!1,serverId:n,definitionsCachePath:e.definitionsCache,clearDefinitionsCache:e.clearDefinitionsCache,proxyMode:e.proxyMode}}function Qe(e,t,n,r){let i=Y(r);return e===V?`Failed to start MCP server with transport '${e}': ${i}`:`Failed to start MCP server with transport '${e}' on ${n===void 0?`${t} (dynamic port)`:`${t}:${n}`}: ${i}`}function $e(e,t,n,r,i,a=q){return{serverId:e,host:t.host??B,port:n,transport:`http`,shutdownToken:r,startedAt:(/* @__PURE__ */ new Date()).toISOString(),pid:process.pid,configPath:i,runtimeLifecycle:a}}function et(){return new ue(process.env.PORT_REGISTRY_PATH)}function X(){return process.env.NODE_ENV??`development`}async function tt(e,t,n,r,i,a,o=n===void 0?le:{min:n,max:n}){let s=et(),c=await s.reservePort({repositoryPath:J(),serviceName:e,serviceType:Fe,environment:X(),pid:process.pid,host:t,preferredPort:n,portRange:o,force:!0,metadata:{transport:i,serverId:r,...a?{configPath:a}:{}}});if(!c.success||!c.record){let e=n===void 0?`dynamic port`:`port ${n}`;throw Error(c.error||`Failed to reserve ${e} in port registry`)}let l=!1;return{port:c.record.port,release:async()=>{if(l)return;l=!0;let t=await s.releasePort({repositoryPath:J(),serviceName:e,serviceType:Fe,pid:process.pid,environment:X(),force:!0});if(!t.success&&t.error&&!t.error.includes(`No matching registry entry`))throw Error(t.error||`Failed to release port for ${e}`)}}}async function nt(e){e&&await e.release()}function rt(e,t,n,r,i){return{serverId:e,shutdownToken:t,onShutdownRequested:n,runtimeLifecycle:r,idleShutdownMs:i}}async function Z(e,t){try{await e.remove(t)}catch(e){throw Error(`Failed to remove runtime state for '${t}': ${Y(e)}`)}}async function it(e,t){try{await e.write(t)}catch(e){throw Error(`Failed to persist runtime state for '${t.serverId}': ${Y(e)}`)}}async function at(e,t,n,r){try{try{await e.stop()}catch(e){throw Error(`Failed to stop owned HTTP transport '${n}': ${Y(e)}`)}}finally{await r?.release({kill:!1}),await Z(t,n)}}async function ot(e,t,n){await n?.release({kill:!1}),await Z(e,t)}async function st(e,t,n,r){try{try{await e.stop()}catch(e){throw Error(`Failed to stop HTTP transport during cleanup for '${n}': ${Y(e)}`)}}finally{await r?.release({kill:!1}),await Z(t,n)}}async function Q(e,t,n={}){try{await e.start()}catch(e){throw Error(`Failed to start transport handler: ${Y(e)}`)}let r=!1,i=async n=>{if(!r){r=!0,console.error(`\nReceived ${n}, shutting down gracefully...`);try{await e.stop(),t&&await t(),process.exit(0)}catch(e){console.error(`Failed to gracefully stop transport during ${n}: ${Y(e)}`),process.exit(1)}}};process.on(`SIGINT`,async()=>await i(`SIGINT`)),process.on(`SIGTERM`,async()=>await i(`SIGTERM`)),n.shutdownOnStdinClose&&(process.stdin.on(`end`,async()=>await i(`stdin end`)),process.stdin.on(`close`,async()=>await i(`stdin close`)))}async function ct(e,t,n,r){i();let o=n[0],l=await g(e),u=new c,d=O(),f=e.serverId??a(),p=Je(process.env.MCP_PROXY_RUNTIME_LIFECYCLE),m=t.port,h=m===void 0?le:{min:m,max:m},_=await tt(W,t.host??B,m,f,H,o,h),v=_.port,b={...t,port:v},x=await ae({repositoryPath:J(),serviceName:`mcp-proxy-http`,serviceType:`service`,environment:X(),host:b.host??B,port:v,command:process.argv[1],args:process.argv.slice(2),...ee()?{processGroupId:process.pid,terminationMode:`process-group`}:{terminationMode:`direct`},metadata:{transport:H,serverId:f,runtimeLifecycle:p,...o?{configPath:o}:{}}}),S=async()=>{await nt(_??null),S=async()=>void 0},C=$e(f,b,v,d,o,p),w,T=!1,E=async()=>{if(!T){T=!0;try{await at(w,u,C.serverId,x),await S(),await l.dispose(),process.exit(0)}catch(e){throw Error(`Failed to stop HTTP runtime '${C.serverId}' from admin shutdown: ${Y(e)}`)}}};try{let e=rt(C.serverId,d,E,p,r);w=new s(()=>y(l),b,e)}catch(e){throw await S(),await x.release({kill:!1}),await l.dispose(),Error(`Failed to create HTTP runtime server: ${Y(e)}`)}try{await Q(w,async()=>{await S(),await l.dispose(),await ot(u,C.serverId,x)}),await it(u,C)}catch(e){throw await S(),await l.dispose(),await st(w,u,C.serverId,x),Error(`Failed to start HTTP runtime '${C.serverId}': ${Y(e)}`)}console.error(`Runtime state: http://${C.host}:${C.port} (${C.serverId})`)}async function lt(e){let t;try{t=await _(e),await Q(new d(t,Xe()),async()=>{await t?.dispose?.()},{shutdownOnStdinClose:!0})}catch(e){throw await t?.dispose?.(),Error(`Failed to start stdio transport: ${Y(e)}`)}}async function ut(e){if(e)try{return(await new c().read(e))?.runtimeLifecycle}catch{return}}function dt(e){if(!e)return p;let t=Number.parseInt(e,10);return Number.isInteger(t)&&t>0?t:p}function ft(e,t){let n=k.resolveRegistryPath(process.env.PROCESS_REGISTRY_PATH),r=ne(`sha1`).update(`${oe(e)}|${t}`).digest(`hex`);return te(E(n),`mcp-proxy-runtime-${r.slice(0,16)}.lock`)}async function pt(e,t,n){if(e.port!==void 0)return{endpoint:new URL(`http://${e.host??B}:${e.port}${z}`),runtimeLifecycle:q};let r=J(),i=dt(t.timeoutMs)+1e4;return await se(ft(r,W),async()=>mt(r,e,t,n),{staleAfterMs:i,retryDelayMs:250,maxRetries:Math.ceil(i/250)})}async function mt(e,t,n,r){let i=await l(e);if(i){let e=t.host??i.host,n=i.serverId===`unknown`?void 0:i.serverId;return{endpoint:new URL(`http://${e}:${i.port}${z}`),runtimeServerId:n,runtimeLifecycle:i.runtimeLifecycle??await ut(n)??q}}let a=await C({host:t.host??B,config:r,cache:n.cache,definitionsCache:n.definitionsCache,clearDefinitionsCache:n.clearDefinitionsCache,proxyMode:n.proxyMode,runtimeLifecycle:K,idleShutdownMinutes:n.idleShutdownMinutes});return{endpoint:new URL(`http://${a.host}:${a.port}${z}`),runtimeServerId:a.serverId,runtimeLifecycle:a.runtimeLifecycle??q}}async function ht(e,t,n){let{endpoint:r}=await pt(e,t,n);return r}function gt(e){return`runtime:${e}`}async function _t(e){if(e.runtimeLifecycle===K&&e.runtimeServerId)return await ae({repositoryPath:J(),serviceName:`mcp-proxy-stdio-http-bridge:${O()}`,serviceType:`tool`,environment:X(),pid:process.pid,command:process.argv[1],args:process.argv.slice(2),tags:[G,gt(e.runtimeServerId)],metadata:{transport:U,runtimeServerId:e.runtimeServerId,runtimeLifecycle:e.runtimeLifecycle,endpoint:e.endpoint.toString()},force:!1})}function vt(e){return e.pid===process.pid}async function yt(e){return await new k(process.env.PROCESS_REGISTRY_PATH).listProcesses({repositoryPath:J(),serviceType:`tool`,tags:[G,gt(e)],environment:X(),liveOnly:!0})}async function bt(e){return(await yt(e)).filter(e=>!vt(e)).length>0?!1:(await new Promise(e=>setTimeout(e,500)),(await yt(e)).filter(e=>!vt(e)).length===0)}async function xt(t){if(t.runtimeLifecycle!==K||!t.runtimeServerId)return;let n=t.runtimeServerId,r=ft(J(),W);try{await se(r,async()=>{await bt(n)&&await new e().stop({serverId:n,force:!0})})}catch(e){console.error(`Failed to stop managed stdio-http runtime; leaving it alive: ${Y(e)}`)}}async function St(e,t,n,r){let i=t.autoDisconnect?!0:r?.keepAlive!==void 0&&!r.keepAlive,a,o;try{a=await pt(e,t,n),o=await _t(a);let{endpoint:r}=a;await Q(new x({endpoint:r,resolveEndpoint:async()=>await ht(e,t,n)},Xe()),async()=>{try{await o?.release({kill:!1,releasePort:!1})}catch(e){console.error(`Failed to release stdio-http bridge lease; leaving runtime alive: ${Y(e)}`);return}i&&a&&await xt(a)},{shutdownOnStdinClose:!0})}catch(e){try{await o?.release({kill:!1,releasePort:!1})}catch(t){throw Error(`Failed to start stdio-http transport: ${Y(e)}; also failed to release bridge lease: ${Y(t)}`)}throw Error(`Failed to start stdio-http transport: ${Y(e)}`)}}async function Ct(e,t,n,r,i){try{if(e===V){await lt(r);return}if(e===H){await ct(r,Ye(t,b.HTTP,i),n,Re(t.idleShutdownMinutes));return}await St(Ye(t,b.HTTP,i),t,n,i)}catch(t){throw Error(`Failed to start transport '${e}': ${Y(t)}`)}}async function wt(e,t){try{let n=await C({id:e.id,host:e.host??B,port:e.port,config:t,cache:e.cache,definitionsCache:e.definitionsCache,clearDefinitionsCache:e.clearDefinitionsCache,proxyMode:e.proxyMode,timeoutMs:e.timeoutMs,idleShutdownMinutes:e.idleShutdownMinutes});S(n)}catch(t){console.error(`Failed to prestart HTTP runtime '${e.id||`generated-server-id`}': ${Y(t)}`),process.exit(1)}}const Tt=new A(`mcp-serve`).description(`Start MCP server with specified transport or prestart the HTTP runtime in the background`).option(`-t, --type <type>`,`Transport type: ${V}, ${H}, or ${U}`).option(`-p, --port <port>`,`Port to listen on (http) or backend port for stdio-http`,e=>Number.parseInt(e,10)).option(`--host <host>`,`Host to bind to (http) or backend host for stdio-http`).option(`-c, --config <path>`,`Path to MCP server configuration file (repeatable; later files override earlier ones)`,t).option(`--no-cache`,`Disable configuration caching, always reload from config file`).option(`--definitions-cache <path>`,`Path to prefetched tool/prompt/skill definitions cache file`).option(`--clear-definitions-cache`,`Delete definitions cache before startup`,!1).option(`--proxy-mode <mode>`,`How mcp-proxy exposes downstream tools: meta, flat, search, or group`,`meta`).option(`--id <id>`,`Unique server identifier (overrides config file id, auto-generated if not provided)`).option(`--auto-disconnect`,`Stop the managed HTTP runtime when the last stdio-http bridge exits (default: keep runtime alive for reuse)`,!1).option(`--idle-shutdown-minutes <minutes>`,`Stop the HTTP runtime after N minutes with no MCP session and no request (0 disables; overrides ${Le})`,e=>Number.parseFloat(e)).option(`--prestart-http`,`Prestart the HTTP runtime in the background and exit after it becomes healthy`,!1).option(`--timeout-ms <ms>`,`How long to wait for the HTTP runtime to become healthy`,String(p)).action(async e=>{try{let t=n(e.config),r=t.length>0?null:await He(),i=r?[r]:t;if(e.prestartHttp){await wt(e,i);return}let a=We(i),o=Ke((e.type??a.type??V).toLowerCase());qe(e.proxyMode),await Ct(o,e,i,Ze(e,i,await Ge(e,i)),a)}catch(t){let n=(e.type??V).toLowerCase(),r=ze(n)?n:V,i=process.env.MCP_PORT?Number(process.env.MCP_PORT):void 0,a=e.port??(Number.isFinite(i)?i:void 0);console.error(Qe(r,e.host??B,a,t)),process.exit(1)}}),Et=new A(`prefetch`).description(`Pre-download packages used by MCP servers (npx, pnpx, uvx, uv)`).option(`-c, --config <path>`,`Path to MCP server configuration file (repeatable; later files override earlier ones)`,t).option(`-p, --parallel`,`Run prefetch commands in parallel`,!1).option(`-d, --dry-run`,`Show what would be prefetched without executing`,!1).option(`-f, --filter <type>`,`Filter by package manager type: npx, pnpx, uvx, or uv`).option(`--definitions-out <path>`,`Write discovered definitions to a JSON or YAML cache file`).option(`--skip-packages`,`Skip package prefetch and only build definitions cache`,!1).option(`--clear-definitions-cache`,`Delete the definitions cache file before continuing`,!1).action(async e=>{try{let t=v(),i=n(e.config),s=i.length>0?null:o(),c=s?[s]:i;c.length===0&&(L.error(`No MCP configuration file found.`),L.info(`Use --config <path> to specify a config file, or run "po-mcp-proxy init" to create one.`),process.exit(1)),L.info(`Loading configuration from: ${c.join(`, `)}`);let l=await t.createConfigFetcherService({configSources:r(c),useCache:!1}).fetchConfiguration(!0),u=l.id||a(),d=h.generateConfigHash(l),f=e.definitionsOut||h.getDefaultCachePath(c),p=t.createPrefetchService({mcpConfig:l,filter:e.filter,parallel:e.parallel}),g=p.extractPackages(),_=!e.skipPackages,y=!!e.definitionsOut;if(e.clearDefinitionsCache&&(e.dryRun?L.info(`Would clear definitions cache: ${f}`):(await h.clearFile(f),L.success(`Cleared definitions cache: ${f}`))),_){if(g.length===0)L.warning(`No packages found to prefetch.`),L.info(`Prefetch supports: npx, pnpx, uvx, and uv run commands`);else{L.info(`Found ${g.length} package(s) to prefetch:`);for(let e of g)L.item(`${e.serverName}: ${e.packageManager} ${e.packageName}`)}}if(!_&&!y){L.warning(`Nothing to do. Use package prefetch or provide --definitions-out.`);return}if(e.dryRun){if(_&&g.length>0){L.newline(),L.header(`Dry run mode - commands that would be executed:`);for(let e of g)L.indent(e.fullCommand.join(` `))}y&&(L.newline(),L.info(`Would write definitions cache to: ${f}`));return}let b=!1;if(_&&g.length>0){L.newline(),L.info(`Prefetching packages...`);let e=await p.prefetch();if(L.newline(),e.failed===0?L.success(`Package prefetch complete: ${e.successful} succeeded, ${e.failed} failed`):L.warning(`Package prefetch complete: ${e.successful} succeeded, ${e.failed} failed`),e.failed>0){b=!0,L.newline(),L.error(`Failed packages:`);for(let t of e.results.filter(e=>!e.success))L.item(`${t.package.serverName} (${t.package.packageName}): ${t.output.trim()}`)}}if(y){L.newline(),L.info(`Collecting definitions cache...`);let e=t.createClientManagerService(),n=l.skills?.paths||[],r=n.length>0?t.createSkillService(process.cwd(),n):void 0,i=t.createDefinitionsCacheService(e,r);await Promise.all(Object.entries(l.mcpServers).map(async([t,n])=>{try{await e.connectToServer(t,n),L.item(`Connected for definitions: ${t}`)}catch(e){L.warning(`Failed to connect for definitions: ${t} (${e instanceof Error?e.message:String(e)})`)}}));let a=await i.collectForCache({configPath:c[0],configHash:d,oneMcpVersion:m,serverId:u});await h.writeToFile(f,a),L.success(`Definitions cache written: ${f} (${Object.keys(a.servers).length} servers, ${a.skills.length} skills)`),a.failures.length>0&&L.warning(`Definitions cache completed with ${a.failures.length} server failure(s)`),await e.disconnectAll()}b&&process.exit(1)}catch(e){L.error(`Error executing prefetch: ${e instanceof Error?e.message:String(e)}`),process.exit(1)}});function $(e){return e instanceof Error?e.message:String(e)}const Dt=new A(`read-resource`).description(`Read a resource by URI from a connected MCP server`).argument(`<uri>`,`Resource URI to read`).option(`-c, --config <path>`,`Path to MCP server configuration file (repeatable; later files override earlier ones)`,t).option(`-s, --server <name>`,`Server name (required if resource exists on multiple servers)`).option(`-j, --json`,`Output as JSON`,!1).action(async(e,t)=>{try{await M(t,async({clientManager:n})=>{let r=n.getAllClients();if(t.server){let r=n.getClient(t.server);if(!r)throw Error(`Server "${t.server}" not found`);t.json||console.error(`Reading ${e} from ${t.server}...`);let i=await r.readResource(e);if(t.json)console.log(JSON.stringify(i,null,2));else for(let e of i.contents)`text`in e?console.log(e.text):console.log(JSON.stringify(e,null,2));return}let i=await Promise.all(r.map(async t=>{try{let n=(await t.listResources()).some(t=>t.uri===e);return{serverName:t.serverName,hasResource:n,error:null}}catch(e){return{serverName:t.serverName,hasResource:!1,error:e}}})),a=[];for(let{serverName:e,hasResource:t,error:n}of i)n?console.error(`Failed to list resources from ${e}: ${$(n)}`):t&&a.push(e);if(a.length===0)throw Error(`Resource "${e}" not found on any connected server`);if(a.length>1)throw Error(`Resource "${e}" found on multiple servers: ${a.join(`, `)}. Use --server to disambiguate`);let o=a[0],s=n.getClient(o);if(!s)throw Error(`Internal error: Server "${o}" not connected`);t.json||console.error(`Reading ${e} from ${o}...`);let c=await s.readResource(e);if(t.json)console.log(JSON.stringify(c,null,2));else for(let e of c.contents)`text`in e?console.log(e.text):console.log(JSON.stringify(e,null,2))})}catch(e){console.error(`Error executing read-resource: ${$(e)}`),process.exit(1)}});function Ot(e){return e instanceof Error?e.message:String(e)}function kt(e){console.log(`Stopped mcp-proxy server '${e.serverId}'.`),console.log(`Endpoint: http://${e.host}:${e.port}`),console.log(`Result: ${e.message}`)}const At=new A(`stop`).description(`Stop a running HTTP mcp-proxy server`).option(`--id <id>`,`Target server ID from the runtime registry`).option(`--host <host>`,`Target runtime host`).option(`--port <port>`,`Target runtime port`,e=>Number.parseInt(e,10)).option(`-c, --config <path>`,`Reserved for future config-based targeting support`).option(`--token <token>`,`Override the persisted shutdown token`).option(`--force`,`Skip server ID verification against the /health response`,!1).option(`-j, --json`,`Output as JSON`,!1).option(`--timeout <ms>`,`Maximum time to wait for shutdown completion`,e=>Number.parseInt(e,10),5e3).action(async e=>{try{e.config&&console.error(`Warning: --config is not used yet; runtime resolution uses the persisted registry.`);let t=await v().createStopServerService().stop({serverId:e.id,host:e.host,port:e.port,token:e.token,force:e.force,timeoutMs:e.timeout});if(e.json){console.log(JSON.stringify(t,null,2));return}kt(t)}catch(t){let n=`Error executing stop: ${Ot(t)}`;e.json?console.log(JSON.stringify({ok:!1,error:n},null,2)):console.error(n),process.exit(1)}});function jt(e){return e instanceof Error?e.message:String(e)}const Mt=new A(`use-tool`).description(`Execute an MCP tool with arguments`).argument(`<toolName>`,`Tool name to execute`).option(`-c, --config <path>`,`Path to MCP server configuration file (repeatable; later files override earlier ones)`,t).option(`-s, --server <name>`,`Server name (required if tool exists on multiple servers)`).option(`-a, --args <json>`,`Tool arguments as JSON string`,`{}`).option(`-t, --timeout <ms>`,`Request timeout in milliseconds for tool execution (default: 60000)`,Number.parseInt).option(`-j, --json`,`Output as JSON`,!1).action(async(e,t)=>{try{let n={};try{n=JSON.parse(t.args)}catch{console.error(`Error: Invalid JSON in --args`),process.exit(1)}await M(t,async({container:r,config:i,clientManager:a})=>{let o=a.getAllClients();if(t.server){let r=a.getClient(t.server);if(!r)throw Error(`Server "${t.server}" not found`);t.json||console.error(`Executing ${e} on ${t.server}...`);let i=t.timeout?{timeout:t.timeout}:void 0,o=await r.callTool(e,n,i);if(t.json)console.log(JSON.stringify(o,null,2));else{if(console.log(`
142
142
  Result:`),o.content)for(let e of o.content)e.type===`text`?console.log(e.text):console.log(JSON.stringify(e,null,2));o.isError&&(console.error(`
143
143
  ⚠️ Tool execution returned an error`),process.exit(1))}return}let s=await Promise.all(o.map(async t=>{try{let n=(await t.listTools()).some(t=>t.name===e);return{serverName:t.serverName,hasTool:n,error:null}}catch(e){return{serverName:t.serverName,hasTool:!1,error:e}}})),c=[];for(let{serverName:e,hasTool:n,error:r}of s)r?t.json||console.error(`Failed to list tools from ${e}:`,r):n&&c.push(e);if(c.length===0){let n=i.skills?.paths||[];if(n.length>0)try{let i=process.env.PROJECT_PATH||process.cwd(),a=r.createSkillService(i,n),o=e.startsWith(`skill__`)?e.slice(7):e,s=await a.getSkill(o);if(s){let e={content:[{type:`text`,text:s.content}]};t.json?console.log(JSON.stringify(e,null,2)):(console.log(`
144
144
  Skill content:`),console.log(s.content));return}}catch(n){t.json||console.error(`Failed to lookup skill "${e}":`,n)}throw Error(`Tool or skill "${e}" not found on any connected server or configured skill paths`)}if(c.length>1)throw Error(`Tool "${e}" found on multiple servers: ${c.join(`, `)}`);let l=c[0],u=a.getClient(l);if(!u)throw Error(`Internal error: Server "${l}" not connected`);t.json||console.error(`Executing ${e} on ${l}...`);let d=t.timeout?{timeout:t.timeout}:void 0,f=await u.callTool(e,n,d);if(t.json)console.log(JSON.stringify(f,null,2));else{if(console.log(`
145
145
  Result:`),f.content)for(let e of f.content)e.type===`text`?console.log(e.text):console.log(JSON.stringify(e,null,2));f.isError&&(console.error(`
146
- ⚠️ Tool execution returned an error`),process.exit(1))}})}catch(e){console.error(`Error executing use-tool: ${jt(e)}`),process.exit(1)}});async function Nt(){try{let e=new A;e.name(`po-mcp-proxy`).description(`MCP proxy server package`).version(p),e.addCommand(De),e.addCommand(Tt),e.addCommand(Ne),e.addCommand(Ce),e.addCommand(Mt),e.addCommand(Ae),e.addCommand(Dt),e.addCommand(Oe),e.addCommand(we),e.addCommand(Et),e.addCommand(At),e.addCommand(ve),await e.parseAsync(process.argv)}catch(e){console.error(`CLI execution failed: ${e instanceof Error?e.message:String(e)}`),process.exit(1)}}Nt().catch(e=>{console.error(`Fatal error: ${e instanceof Error?e.message:String(e)}`),process.exit(1)});export{};
146
+ ⚠️ Tool execution returned an error`),process.exit(1))}})}catch(e){console.error(`Error executing use-tool: ${jt(e)}`),process.exit(1)}});async function Nt(){try{let e=new A;e.name(`po-mcp-proxy`).description(`MCP proxy server package`).version(m),e.addCommand(De),e.addCommand(Tt),e.addCommand(Ne),e.addCommand(Ce),e.addCommand(Mt),e.addCommand(Ae),e.addCommand(Dt),e.addCommand(Oe),e.addCommand(we),e.addCommand(Et),e.addCommand(At),e.addCommand(ve),await e.parseAsync(process.argv)}catch(e){console.error(`CLI execution failed: ${e instanceof Error?e.message:String(e)}`),process.exit(1)}}Nt().catch(e=>{console.error(`Fatal error: ${e instanceof Error?e.message:String(e)}`),process.exit(1)});export{};
package/dist/index.cjs CHANGED
@@ -1 +1 @@
1
- Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./src-Bq40B1Wm.cjs");exports.CALLBACK_BIND_WAIT_MS=e.Y,exports.CALLBACK_POLL_INTERVAL_MS=e.X,exports.CALLBACK_PORT_SPAN=e.Z,exports.CALLBACK_TIMEOUT_MS=e.Q,exports.CallbackRendezvous=e.st,exports.ConfigFetcherService=e.R,exports.DEFAULT_CALLBACK_PATH=e.$,exports.DEFAULT_CALLBACK_PORT=e.et,exports.DEFAULT_HTTP_RUNTIME_TIMEOUT_MS=e.g,exports.DefinitionsCacheService=e.j,exports.DescribeToolsTool=e.m,exports.FileTokenStore=e.W,exports.HttpTransportHandler=e.S,exports.McpClientManagerService=e.E,exports.McpOAuthProvider=e.J,exports.MemoryTokenStore=e.G,exports.OAuthCallbackServer=e.tt,exports.OutputGuardService=e.O,exports.RESERVATION_STALE_MS=e.ct,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.nt,exports.cleanupStaleRegistries=e.M,exports.closeSharedCallbackServer=e.rt,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.lt,exports.getDefaultTokenStoreDir=e.K,exports.getSharedCallbackServer=e.it,exports.initializeSharedServices=e.l,exports.isAuthError=e.D,exports.parseMcpConfig=e.z,exports.prestartHttpRuntime=e.y,exports.probeRuntimeHealth=e.b,exports.readAuthEntryForUrl=e.q,exports.releaseSharedCallbackServer=e.at,exports.resolveAuthScheme=e.V,exports.resolveBearerToken=e.H,exports.resolveCallbackPort=e.U,exports.resolveOutputGuardOptions=e.k,exports.resolveWorkspaceRoot=e.ut,exports.toConfigPaths=e.I,exports.toConfigSources=e.L,exports.workspaceCallbackPort=e.ot;
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./src-BJeQYWZ1.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.d.cts CHANGED
@@ -677,6 +677,8 @@ interface CallbackResult {
677
677
  iss?: string;
678
678
  }
679
679
  interface OAuthCallbackServerOptions extends CallbackRendezvousOptions {
680
+ /** Disable all disk rendezvous state for explicitly ephemeral credentials. */
681
+ rendezvous?: boolean;
680
682
  /** Path the authorization server redirects back to. */
681
683
  path?: string;
682
684
  /** How often a contended bind is retried and a guest polls, for tests. */
@@ -745,7 +747,7 @@ export declare class OAuthCallbackServer implements OAuthCallbackSink {
745
747
  private listening?;
746
748
  private readonly pending;
747
749
  private readonly path;
748
- private readonly rendezvous;
750
+ private readonly rendezvous?;
749
751
  private readonly pollIntervalMs;
750
752
  private readonly bindWaitMs;
751
753
  constructor(pathOrOptions?: string | OAuthCallbackServerOptions);
@@ -947,6 +949,9 @@ interface AuthEntry {
947
949
  clientInformation?: StoredOAuthClientInformation;
948
950
  codeVerifier?: string;
949
951
  discoveryState?: OAuthDiscoveryState;
952
+ generation?: number;
953
+ updatedAt?: number;
954
+ expiresAt?: number;
950
955
  }
951
956
  /**
952
957
  * Pluggable persistence for OAuth credentials.
@@ -958,7 +963,13 @@ interface TokenStore {
958
963
  read(serverName: string): Promise<AuthEntry | undefined>;
959
964
  write(serverName: string, entry: AuthEntry): Promise<void>;
960
965
  clear(serverName: string): Promise<void>;
966
+ readonly isDegraded?: boolean;
967
+ /** Keeps authorization-code rendezvous in memory too, independent of module identity. */
968
+ readonly isEphemeral?: boolean;
969
+ withLock?<T>(serverName: string, operation: () => Promise<T>, signal?: AbortSignal): Promise<T>;
961
970
  }
971
+ /** Serialize a complete SDK auth transaction, not individual persistence callbacks. */
972
+ export declare function withTokenStoreLock<T>(store: TokenStore, serverName: string, operation: () => Promise<T>, signal?: AbortSignal): Promise<T>;
962
973
  /** Default location for the file-backed store. */
963
974
  export declare function getDefaultTokenStoreDir(): string;
964
975
  /** File-backed encrypted token store with a per-directory owner-only key. */
@@ -967,6 +978,8 @@ export declare class FileTokenStore implements TokenStore {
967
978
  private readonly fallback;
968
979
  private unavailable;
969
980
  constructor(baseDir?: string);
981
+ get isDegraded(): boolean;
982
+ withLock<T>(serverName: string, operation: () => Promise<T>, signal?: AbortSignal): Promise<T>;
970
983
  private pathFor;
971
984
  /** Never follow a symlink, use a key with loose permissions or change another user's directory. */
972
985
  private key;
@@ -978,6 +991,7 @@ export declare class FileTokenStore implements TokenStore {
978
991
  * In-memory token store, for tests and ephemeral runtimes.
979
992
  */
980
993
  export declare class MemoryTokenStore implements TokenStore {
994
+ readonly isEphemeral = true;
981
995
  private readonly entries;
982
996
  read(serverName: string): Promise<AuthEntry | undefined>;
983
997
  write(serverName: string, entry: AuthEntry): Promise<void>;
@@ -1065,7 +1079,12 @@ export declare class McpOAuthProvider implements OAuthClientProvider {
1065
1079
  * pointless.
1066
1080
  */
1067
1081
  private flowTokenFingerprint?;
1082
+ private tokenGeneration?;
1083
+ private tokenFingerprint?;
1084
+ private interactiveGrant;
1068
1085
  constructor(options: McpOAuthProviderOptions);
1086
+ /** Ephemeral clients must know their actual port before dynamic registration. */
1087
+ prepareCallback(): Promise<void>;
1069
1088
  /**
1070
1089
  * Redirect URI presented to the authorization server.
1071
1090
  *
@@ -1146,6 +1165,10 @@ export declare class McpOAuthProvider implements OAuthClientProvider {
1146
1165
  */
1147
1166
  private registrationMatchesRedirect;
1148
1167
  private mergeEntry;
1168
+ /** Unlocked reads for the outer auth coordinator. SDK persistence callbacks must not relock. */
1169
+ readCredential(): Promise<AuthEntry | undefined>;
1170
+ prepareInteractiveGrant(): void;
1171
+ withCredentialLock<T>(operation: () => Promise<T>, signal?: AbortSignal): Promise<T>;
1149
1172
  }
1150
1173
  //#endregion
1151
1174
  //#region src/auth/index.d.ts
@@ -1809,6 +1832,8 @@ export declare class McpClientManagerService {
1809
1832
  /** Latest published state per server, replayed to listeners that subscribe later. */
1810
1833
  private readonly serverStates;
1811
1834
  private readonly tokenStore?;
1835
+ private readonly serverTokenStores;
1836
+ private readonly serverRemovals;
1812
1837
  private readonly onAuthorizationUrl?;
1813
1838
  private readonly callbackTimeoutMs?;
1814
1839
  private readonly callbackServer?;
@@ -1854,7 +1879,12 @@ export declare class McpClientManagerService {
1854
1879
  * Connect to an MCP server based on its configuration with timeout
1855
1880
  * Uses the timeout from server config, falling back to default (30s)
1856
1881
  */
1857
- connectToServer(serverName: string, config: McpServerConfig): Promise<void>;
1882
+ connectToServer(serverName: string, config: McpServerConfig, options?: {
1883
+ tokenStore?: TokenStore;
1884
+ signal?: AbortSignal;
1885
+ }): Promise<void>;
1886
+ /** Terminal removal, unlike disconnectServer which permits a later reconnect. */
1887
+ removeServer(serverName: string): Promise<void>;
1858
1888
  registerServerConfigs(configs: Record<string, McpServerConfig>): void;
1859
1889
  getKnownServerNames(): string[];
1860
1890
  getServerRequestTimeout(serverName: string): number | undefined;
@@ -2075,6 +2105,7 @@ interface CollectOptions {
2075
2105
  configPath?: string;
2076
2106
  configHash?: string;
2077
2107
  oneMcpVersion?: string;
2108
+ serverNames?: readonly string[];
2078
2109
  }
2079
2110
  type DefinitionsCacheInspection = {
2080
2111
  state: 'absent';
package/dist/index.d.mts CHANGED
@@ -677,6 +677,8 @@ interface CallbackResult {
677
677
  iss?: string;
678
678
  }
679
679
  interface OAuthCallbackServerOptions extends CallbackRendezvousOptions {
680
+ /** Disable all disk rendezvous state for explicitly ephemeral credentials. */
681
+ rendezvous?: boolean;
680
682
  /** Path the authorization server redirects back to. */
681
683
  path?: string;
682
684
  /** How often a contended bind is retried and a guest polls, for tests. */
@@ -745,7 +747,7 @@ export declare class OAuthCallbackServer implements OAuthCallbackSink {
745
747
  private listening?;
746
748
  private readonly pending;
747
749
  private readonly path;
748
- private readonly rendezvous;
750
+ private readonly rendezvous?;
749
751
  private readonly pollIntervalMs;
750
752
  private readonly bindWaitMs;
751
753
  constructor(pathOrOptions?: string | OAuthCallbackServerOptions);
@@ -947,6 +949,9 @@ interface AuthEntry {
947
949
  clientInformation?: StoredOAuthClientInformation;
948
950
  codeVerifier?: string;
949
951
  discoveryState?: OAuthDiscoveryState;
952
+ generation?: number;
953
+ updatedAt?: number;
954
+ expiresAt?: number;
950
955
  }
951
956
  /**
952
957
  * Pluggable persistence for OAuth credentials.
@@ -958,7 +963,13 @@ interface TokenStore {
958
963
  read(serverName: string): Promise<AuthEntry | undefined>;
959
964
  write(serverName: string, entry: AuthEntry): Promise<void>;
960
965
  clear(serverName: string): Promise<void>;
966
+ readonly isDegraded?: boolean;
967
+ /** Keeps authorization-code rendezvous in memory too, independent of module identity. */
968
+ readonly isEphemeral?: boolean;
969
+ withLock?<T>(serverName: string, operation: () => Promise<T>, signal?: AbortSignal): Promise<T>;
961
970
  }
971
+ /** Serialize a complete SDK auth transaction, not individual persistence callbacks. */
972
+ export declare function withTokenStoreLock<T>(store: TokenStore, serverName: string, operation: () => Promise<T>, signal?: AbortSignal): Promise<T>;
962
973
  /** Default location for the file-backed store. */
963
974
  export declare function getDefaultTokenStoreDir(): string;
964
975
  /** File-backed encrypted token store with a per-directory owner-only key. */
@@ -967,6 +978,8 @@ export declare class FileTokenStore implements TokenStore {
967
978
  private readonly fallback;
968
979
  private unavailable;
969
980
  constructor(baseDir?: string);
981
+ get isDegraded(): boolean;
982
+ withLock<T>(serverName: string, operation: () => Promise<T>, signal?: AbortSignal): Promise<T>;
970
983
  private pathFor;
971
984
  /** Never follow a symlink, use a key with loose permissions or change another user's directory. */
972
985
  private key;
@@ -978,6 +991,7 @@ export declare class FileTokenStore implements TokenStore {
978
991
  * In-memory token store, for tests and ephemeral runtimes.
979
992
  */
980
993
  export declare class MemoryTokenStore implements TokenStore {
994
+ readonly isEphemeral = true;
981
995
  private readonly entries;
982
996
  read(serverName: string): Promise<AuthEntry | undefined>;
983
997
  write(serverName: string, entry: AuthEntry): Promise<void>;
@@ -1065,7 +1079,12 @@ export declare class McpOAuthProvider implements OAuthClientProvider {
1065
1079
  * pointless.
1066
1080
  */
1067
1081
  private flowTokenFingerprint?;
1082
+ private tokenGeneration?;
1083
+ private tokenFingerprint?;
1084
+ private interactiveGrant;
1068
1085
  constructor(options: McpOAuthProviderOptions);
1086
+ /** Ephemeral clients must know their actual port before dynamic registration. */
1087
+ prepareCallback(): Promise<void>;
1069
1088
  /**
1070
1089
  * Redirect URI presented to the authorization server.
1071
1090
  *
@@ -1146,6 +1165,10 @@ export declare class McpOAuthProvider implements OAuthClientProvider {
1146
1165
  */
1147
1166
  private registrationMatchesRedirect;
1148
1167
  private mergeEntry;
1168
+ /** Unlocked reads for the outer auth coordinator. SDK persistence callbacks must not relock. */
1169
+ readCredential(): Promise<AuthEntry | undefined>;
1170
+ prepareInteractiveGrant(): void;
1171
+ withCredentialLock<T>(operation: () => Promise<T>, signal?: AbortSignal): Promise<T>;
1149
1172
  }
1150
1173
  //#endregion
1151
1174
  //#region src/auth/index.d.ts
@@ -1809,6 +1832,8 @@ export declare class McpClientManagerService {
1809
1832
  /** Latest published state per server, replayed to listeners that subscribe later. */
1810
1833
  private readonly serverStates;
1811
1834
  private readonly tokenStore?;
1835
+ private readonly serverTokenStores;
1836
+ private readonly serverRemovals;
1812
1837
  private readonly onAuthorizationUrl?;
1813
1838
  private readonly callbackTimeoutMs?;
1814
1839
  private readonly callbackServer?;
@@ -1854,7 +1879,12 @@ export declare class McpClientManagerService {
1854
1879
  * Connect to an MCP server based on its configuration with timeout
1855
1880
  * Uses the timeout from server config, falling back to default (30s)
1856
1881
  */
1857
- connectToServer(serverName: string, config: McpServerConfig): Promise<void>;
1882
+ connectToServer(serverName: string, config: McpServerConfig, options?: {
1883
+ tokenStore?: TokenStore;
1884
+ signal?: AbortSignal;
1885
+ }): Promise<void>;
1886
+ /** Terminal removal, unlike disconnectServer which permits a later reconnect. */
1887
+ removeServer(serverName: string): Promise<void>;
1858
1888
  registerServerConfigs(configs: Record<string, McpServerConfig>): void;
1859
1889
  getKnownServerNames(): string[];
1860
1890
  getServerRequestTimeout(serverName: string): number | undefined;
@@ -2075,6 +2105,7 @@ interface CollectOptions {
2075
2105
  configPath?: string;
2076
2106
  configHash?: string;
2077
2107
  oneMcpVersion?: string;
2108
+ serverNames?: readonly string[];
2078
2109
  }
2079
2110
  type DefinitionsCacheInspection = {
2080
2111
  state: 'absent';