@agimon-ai/po-mcp-proxy 0.0.1-alpha.3 → 0.0.1-alpha.30
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 +5 -1
- package/dist/cli.cjs +4 -4
- package/dist/cli.mjs +3 -3
- package/dist/index.cjs +1 -1
- package/dist/index.d.cts +39 -8
- package/dist/index.d.mts +39 -8
- package/dist/index.mjs +1 -1
- package/dist/src-C5X4b1JV.mjs +72 -0
- package/dist/src-CN6eJHnZ.cjs +72 -0
- package/package.json +9 -7
- package/dist/src-CU4DqqSL.mjs +0 -72
- package/dist/src-CsLCzpRF.cjs +0 -72
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 `
|
|
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,10 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
const e=require("./src-
|
|
2
|
+
const e=require("./src-CN6eJHnZ.cjs");let t=require("node:fs/promises"),n=require("node:path"),r=require("node:crypto"),i=require("@agimon-ai/foundation-process-registry"),a=require("js-yaml");a=e.ft(a,1);let o=require("@agimon-ai/foundation-port-registry"),s=require("liquidjs"),c=require("commander"),l=require("node:fs");function u(e){return`runtime:${e}`}function d(e){return e instanceof Error?e.message:String(e)}async function f(e){let t=await e.listProcesses({serviceName:`mcp-proxy-http`,liveOnly:!0}),n=/* @__PURE__ */ new Set;for(let e of t){let t=e.metadata?.serverId;typeof t==`string`&&n.add(t)}return n}async function p(e,t){let n=await e.listProcesses({serviceType:`tool`,tags:[`mcp-proxy-stdio-http-bridge`],liveOnly:!0}),r=u(t);return n.filter(e=>e.tags?.includes(r)).length}async function m(t){let n=t.probe??(async(t,n)=>(await e.b(t,n)).ok),r={stopped:[],removedFiles:[],kept:[]},i=await f(t.processRegistry),a=await t.runtimeStateService.listOrphans(i);for(let e of a){let i={serverId:e.serverId,host:e.host,port:e.port,reason:``};if(!await n(e.host,e.port)){i.reason=`unreachable runtime; removed stale state file`,t.dryRun||await t.runtimeStateService.remove(e.serverId),r.removedFiles.push(i);continue}if(e.runtimeLifecycle!==`stdio-http-managed`){i.reason=`manual runtime; left untouched`,r.kept.push(i);continue}let a=await p(t.processRegistry,e.serverId);if(a>0)i.reason=`managed runtime with ${a} live bridge(s); kept`,r.kept.push(i);else{if(i.reason=`orphaned stdio-http-managed runtime (no live bridges); stopped`,!t.dryRun)try{await t.stopServerService.stop({serverId:e.serverId,force:!0}),await t.runtimeStateService.remove(e.serverId).catch(()=>void 0)}catch(e){i.reason=`failed to stop orphaned runtime: ${d(e)}`,r.kept.push(i);continue}r.stopped.push(i)}}return r}function h(e,t){let n=t?`Would stop`:`Stopped`;for(let t of e.stopped)console.log(`${n} orphaned runtime '${t.serverId}' (http://${t.host}:${t.port}) — ${t.reason}`);let r=t?`Would remove`:`Removed`;for(let t of e.removedFiles)console.log(`${r} stale state for '${t.serverId}' (http://${t.host}:${t.port})`);for(let t of e.kept)console.log(`Kept '${t.serverId}' (http://${t.host}:${t.port}) — ${t.reason}`);console.log(`Summary: ${e.stopped.length} stopped, ${e.removedFiles.length} stale file(s), ${e.kept.length} kept.`)}const g=new c.Command(`cleanup`).description(`Reap orphaned/stale mcp-proxy HTTP runtimes and their leftover state files`).option(`-n, --dry-run`,`Report what would be done without stopping or removing anything`,!1).option(`-j, --json`,`Output as JSON`,!1).action(async t=>{try{let n=await m({runtimeStateService:new e.T,processRegistry:new i.ProcessRegistryService(process.env.PROCESS_REGISTRY_PATH),stopServerService:new e.C,dryRun:t.dryRun});if(t.json){console.log(JSON.stringify({dryRun:t.dryRun,...n},null,2));return}h(n,t.dryRun)}catch(e){let n=`Error executing cleanup: ${d(e)}`;t.json?console.log(JSON.stringify({ok:!1,error:n},null,2)):console.error(n),process.exit(1)}});function _(e){return e instanceof Error?e.message:String(e)}async function v(e,t){try{return(await fetch(`http://${e}:${t}/health`,{signal:AbortSignal.timeout(3e3)})).ok}catch{return!1}}async function y(t,n,r,i,a){let o=n.proxy?.host??`localhost`,s=n.proxy?.port,c=`http://${o}:${s}/mcp`;await v(o,s)||(i.json||console.error(`Starting HTTP proxy server in background...`),await e.y({host:o,port:s,config:r,cache:i.useCache!==!1,clearDefinitionsCache:!1,proxyMode:`flat`}));let l=await t.createClientManagerService();try{await l.connectToServer(`proxy`,{name:`proxy`,transport:`http`,config:{url:c}}),i.json||console.error(`✓ Connected to proxy at ${c}`)}catch(e){throw Error(`Failed to connect to proxy server at ${c}: ${_(e)}`)}try{return await a({container:t,configFilePaths:r,config:n,clientManager:l})}finally{await l.disconnectAll()}}async function b(e,t,n,r,i){let a=await e.createClientManagerService();if(await Promise.all(Object.entries(t.mcpServers).map(async([e,t])=>{try{await a.connectToServer(e,t),r.json||console.error(`✓ Connected to ${e}`)}catch(t){r.json||console.error(`✗ Failed to connect to ${e}: ${_(t)}`)}})),a.getAllClients().length===0)throw Error(`No MCP servers connected`);try{return await i({container:e,configFilePaths:n,config:t,clientManager:a})}finally{await a.disconnectAll()}}async function x(t,n){let r=(e,t)=>{t===void 0?console.error(e):console.error(e,t)},i=t.json?{trace:r,debug:r,info:r,warn:r,error:r}:console,a=e.o(i),o=e.I(t.config),s=o.length>0?null:await e.P(),c=s?[s]:o;if(c.length===0)throw Error(`No config file found. Use --config or create mcp-config.yaml`);let l=await a.createConfigFetcherService({configSources:e.L(c),useCache:t.useCache}).fetchConfiguration();return l.proxy?.port?await y(a,l,c,t,n):await b(a,l,c,t,n)}function S(e){return e instanceof Error?e.message:String(e)}const C=new c.Command(`describe-tools`).description(`Describe specific MCP tools`).argument(`<toolNames...>`,`Tool names to describe`).option(`-c, --config <path>`,`Path to MCP server configuration file (repeatable; later files override earlier ones)`,e.F).option(`-s, --server <name>`,`Filter by server name`).option(`-j, --json`,`Output as JSON`,!1).action(async(e,t)=>{try{await x(t,async({container:n,config:r,clientManager:i})=>{let a=t.server?[i.getClient(t.server)].filter(e=>e!==void 0):i.getAllClients();if(t.server&&a.length===0)throw Error(`Server "${t.server}" not found`);let o=process.env.PROJECT_PATH||process.cwd(),s=r.skills?.paths||[],c=s.length>0?n.createSkillService(o,s):void 0,l=[],u=[],d=[...e],f=await Promise.all(a.map(async e=>{try{return{client:e,tools:await e.listTools(),error:null}}catch(t){return{client:e,tools:[],error:t}}}));for(let{client:n,tools:r,error:i}of f)if(i)t.json||console.error(`Failed to list tools from ${n.serverName}:`,i);else for(let t of e){let e=r.find(e=>e.name===t);if(e){l.push({server:n.serverName,name:e.name,description:e.description,inputSchema:e.inputSchema});let r=d.indexOf(t);r>-1&&d.splice(r,1)}}if(c&&d.length>0){let e=await Promise.all([...d].map(async e=>{let t=e.startsWith(`skill__`)?e.slice(7):e;return{toolName:e,skill:await c.getSkill(t)}}));for(let{toolName:t,skill:n}of e)if(n){u.push({name:n.name,location:n.basePath,instructions:n.content});let e=d.indexOf(t);e>-1&&d.splice(e,1)}}let p=[];if(l.length>0&&p.push(`For MCP tools: Use the use_tool function with toolName and toolArgs based on the inputSchema above.`),u.length>0&&p.push(`For skill, just follow skill's description to continue.`),t.json){let e={};l.length>0&&(e.tools=l),u.length>0&&(e.skills=u),p.length>0&&(e.nextSteps=p),d.length>0&&(e.notFound=d),console.log(JSON.stringify(e,null,2))}else{if(l.length>0){console.log(`
|
|
3
3
|
Found tools:
|
|
4
4
|
`);for(let e of l)console.log(`Server: ${e.server}`),console.log(`Tool: ${e.name}`),console.log(`Description: ${e.description||`No description`}`),console.log(`Input Schema:`),console.log(JSON.stringify(e.inputSchema,null,2)),console.log(``)}if(u.length>0){console.log(`
|
|
5
5
|
Found skills:
|
|
6
6
|
`);for(let e of u)console.log(`Skill: ${e.name}`),console.log(`Location: ${e.location}`),console.log(`Instructions:\n${e.instructions}`),console.log(``)}if(p.length>0){console.log(`
|
|
7
|
-
Next steps:`);for(let e of p)console.log(` • ${e}`);console.log(``)}d.length>0&&console.error(`\nTools/skills not found: ${d.join(`, `)}`),l.length===0&&u.length===0&&(console.error(`No tools or skills found`),process.exit(1))}})}catch(e){console.error(`Error executing describe-tools: ${S(e)}`),process.exit(1)}});function w(e){return e instanceof Error?e.message:String(e)}const ee=new
|
|
7
|
+
Next steps:`);for(let e of p)console.log(` • ${e}`);console.log(``)}d.length>0&&console.error(`\nTools/skills not found: ${d.join(`, `)}`),l.length===0&&u.length===0&&(console.error(`No tools or skills found`),process.exit(1))}})}catch(e){console.error(`Error executing describe-tools: ${S(e)}`),process.exit(1)}});function w(e){return e instanceof Error?e.message:String(e)}const ee=new c.Command(`get-prompt`).description(`Get a prompt by name from a connected MCP server`).argument(`<promptName>`,`Prompt name to fetch`).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 prompt exists on multiple servers)`).option(`-a, --args <json>`,`Prompt arguments as JSON string`,`{}`).option(`-j, --json`,`Output as JSON`,!1).action(async(e,t)=>{try{let n={};try{n=JSON.parse(t.args)}catch{throw Error(`Invalid JSON in --args`)}await x(t,async({clientManager:r})=>{let i=r.getAllClients();if(t.server){let i=r.getClient(t.server);if(!i)throw Error(`Server "${t.server}" not found`);let a=await i.getPrompt(e,n);if(t.json)console.log(JSON.stringify(a,null,2));else for(let e of a.messages){let t=e.content;typeof t==`object`&&t&&`text`in t?console.log(t.text):console.log(JSON.stringify(e,null,2))}return}let a=[];if(await Promise.all(i.map(async n=>{try{(await n.listPrompts()).some(t=>t.name===e)&&a.push(n.serverName)}catch(e){t.json||console.error(`Failed to list prompts from ${n.serverName}: ${w(e)}`)}})),a.length===0)throw Error(`Prompt "${e}" not found on any connected server`);if(a.length>1)throw Error(`Prompt "${e}" found on multiple servers: ${a.join(`, `)}. Use --server to disambiguate`);let o=r.getClient(a[0]);if(!o)throw Error(`Internal error: Server "${a[0]}" not connected`);let s=await o.getPrompt(e,n);if(t.json)console.log(JSON.stringify(s,null,2));else for(let e of s.messages){let t=e.content;typeof t==`object`&&t&&`text`in t?console.log(t.text):console.log(JSON.stringify(e,null,2))}})}catch(e){console.error(`Error executing get-prompt: ${w(e)}`),process.exit(1)}});var te=`{
|
|
8
8
|
"_comment": "MCP Server Configuration - Use \${VAR_NAME} syntax for environment variable interpolation",
|
|
9
9
|
"_instructions": "config.instruction: Server's default instruction | instruction: User override (takes precedence)",
|
|
10
10
|
"mcpServers": {
|
|
@@ -138,9 +138,9 @@ 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 c.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 r=(0,n.resolve)(e.output),i=!e.json&&(r.endsWith(`.yaml`)||r.endsWith(`.yml`)),a;if(i){let t=new s.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,t.writeFile)(r,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: ${r}`),D.info(`Use --force to overwrite`),process.exit(1)),e}D.info(`MCP configuration file created: ${r}`),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 ${r}`)}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 c.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 c.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 c.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`;async function le(){return e.dt()}const z=`mcp-proxy-stdio-http-bridge`,B=`stdio-http-managed`,V=`manual`,H=`MCP_PROXY_IDLE_SHUTDOWN_MINUTES`;async 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,t.access)(e,l.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)}`)}}async function pe(e){try{let n=await(0,t.readFile)(e,`utf-8`),r=(e.endsWith(`.yaml`)||e.endsWith(`.yml`)?a.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{}}}async function me(e){let t={};for(let n of e){let e=await 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 o.PortRegistryService(process.env.PORT_REGISTRY_PATH)}function Y(){return process.env.NODE_ENV??`development`}async function Ce(e,t,n,r,i,a,s=n===void 0?o.DEFAULT_PORT_RANGE:{min:n,max:n}){let c=Se(),l=await c.reservePort({repositoryPath:await U(),serviceName:e,serviceType:R,environment:Y(),pid:process.pid,host:t,preferredPort:n,portRange:s,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:await 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,a,s){e.M();let c=a[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?o.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,i.createProcessLease)({repositoryPath:await 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,s);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 a=i.ProcessRegistryService.resolveRegistryPath(process.env.PROCESS_REGISTRY_PATH),o=(0,r.createHash)(`sha1`).update(`${(0,i.normalizeRepositoryPath)(e)}|${t}`).digest(`hex`);return(0,n.join)((0,n.dirname)(a),`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=await U(),a=Ne(t.timeoutMs)+1e4;return await(0,i.withFileLock)(Q(r,L),async()=>Pe(r,e,t,n),{staleAfterMs:a,retryDelayMs:250,maxRetries:Math.ceil(a/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,i.createProcessLease)({repositoryPath:await 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 i.ProcessRegistryService(process.env.PROCESS_REGISTRY_PATH).listProcesses({repositoryPath:await 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(await U(),L);try{await(0,i.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 c.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=await 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 c.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:await 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=await 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 c.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 c.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 c.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(`
|
|
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: ${Qe(e)}`),process.exit(1)}});async function et(){try{let t=new
|
|
146
|
+
⚠️ Tool execution returned an error`),process.exit(1))}})}catch(e){console.error(`Error executing use-tool: ${Qe(e)}`),process.exit(1)}});async function et(){try{let t=new c.Command;t.name(`po-mcp-proxy`).description(`MCP proxy server package`).version(e.h),t.addCommand(re),t.addCommand(Ge),t.addCommand(ce),t.addCommand(C),t.addCommand($e),t.addCommand(ae),t.addCommand(Je),t.addCommand(ie),t.addCommand(ee),t.addCommand(Ke),t.addCommand(Ze),t.addCommand(g),await t.parseAsync(process.argv)}catch(e){console.error(`CLI execution failed: ${e instanceof Error?e.message:String(e)}`),process.exit(1)}}et().catch(e=>{console.error(`Fatal error: ${e instanceof Error?e.message:String(e)}`),process.exit(1)});
|
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,
|
|
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-C5X4b1JV.mjs";import{access as w,readFile as T,writeFile as te}from"node:fs/promises";import{dirname as E,join as D,resolve as O}from"node:path";import{createHash as ne,randomUUID as re}from"node:crypto";import{ProcessRegistryService as k,createProcessLease as ie,normalizeRepositoryPath as ae,withFileLock as oe}from"@agimon-ai/foundation-process-registry";import*as se from"js-yaml";import{DEFAULT_PORT_RANGE as ce,PortRegistryService as le}from"@agimon-ai/foundation-port-registry";import{Liquid as ue}from"liquidjs";import{Command as A}from"commander";import{constants as de}from"node:fs";function fe(e){return`runtime:${e}`}function pe(e){return e instanceof Error?e.message:String(e)}async function me(e){let t=await e.listProcesses({serviceName:`mcp-proxy-http`,liveOnly:!0}),n=/* @__PURE__ */ new Set;for(let e of t){let t=e.metadata?.serverId;typeof t==`string`&&n.add(t)}return n}async function he(e,t){let n=await e.listProcesses({serviceType:`tool`,tags:[`mcp-proxy-stdio-http-bridge`],liveOnly:!0}),r=fe(t);return n.filter(e=>e.tags?.includes(r)).length}async function ge(e){let t=e.probe??(async(e,t)=>(await u(e,t)).ok),n={stopped:[],removedFiles:[],kept:[]},r=await me(e.processRegistry),i=await e.runtimeStateService.listOrphans(r);for(let r of i){let i={serverId:r.serverId,host:r.host,port:r.port,reason:``};if(!await t(r.host,r.port)){i.reason=`unreachable runtime; removed stale state file`,e.dryRun||await e.runtimeStateService.remove(r.serverId),n.removedFiles.push(i);continue}if(r.runtimeLifecycle!==`stdio-http-managed`){i.reason=`manual runtime; left untouched`,n.kept.push(i);continue}let a=await he(e.processRegistry,r.serverId);if(a>0)i.reason=`managed runtime with ${a} live bridge(s); kept`,n.kept.push(i);else{if(i.reason=`orphaned stdio-http-managed runtime (no live bridges); stopped`,!e.dryRun)try{await e.stopServerService.stop({serverId:r.serverId,force:!0}),await e.runtimeStateService.remove(r.serverId).catch(()=>void 0)}catch(e){i.reason=`failed to stop orphaned runtime: ${pe(e)}`,n.kept.push(i);continue}n.stopped.push(i)}}return n}function _e(e,t){let n=t?`Would stop`:`Stopped`;for(let t of e.stopped)console.log(`${n} orphaned runtime '${t.serverId}' (http://${t.host}:${t.port}) — ${t.reason}`);let r=t?`Would remove`:`Removed`;for(let t of e.removedFiles)console.log(`${r} stale state for '${t.serverId}' (http://${t.host}:${t.port})`);for(let t of e.kept)console.log(`Kept '${t.serverId}' (http://${t.host}:${t.port}) — ${t.reason}`);console.log(`Summary: ${e.stopped.length} stopped, ${e.removedFiles.length} stale file(s), ${e.kept.length} kept.`)}const ve=new A(`cleanup`).description(`Reap orphaned/stale mcp-proxy HTTP runtimes and their leftover state files`).option(`-n, --dry-run`,`Report what would be done without stopping or removing anything`,!1).option(`-j, --json`,`Output as JSON`,!1).action(async t=>{try{let n=await ge({runtimeStateService:new c,processRegistry:new k(process.env.PROCESS_REGISTRY_PATH),stopServerService:new e,dryRun:t.dryRun});if(t.json){console.log(JSON.stringify({dryRun:t.dryRun,...n},null,2));return}_e(n,t.dryRun)}catch(e){let n=`Error executing cleanup: ${pe(e)}`;t.json?console.log(JSON.stringify({ok:!1,error:n},null,2)):console.error(n),process.exit(1)}});function j(e){return e instanceof Error?e.message:String(e)}async function ye(e,t){try{return(await fetch(`http://${e}:${t}/health`,{signal:AbortSignal.timeout(3e3)})).ok}catch{return!1}}async function be(e,t,n,r,i){let a=t.proxy?.host??`localhost`,o=t.proxy?.port,s=`http://${a}:${o}/mcp`;await ye(a,o)||(r.json||console.error(`Starting HTTP proxy server in background...`),await C({host:a,port:o,config:n,cache:r.useCache!==!1,clearDefinitionsCache:!1,proxyMode:`flat`}));let c=await e.createClientManagerService();try{await c.connectToServer(`proxy`,{name:`proxy`,transport:`http`,config:{url:s}}),r.json||console.error(`✓ Connected to proxy at ${s}`)}catch(e){throw Error(`Failed to connect to proxy server at ${s}: ${j(e)}`)}try{return await i({container:e,configFilePaths:n,config:t,clientManager:c})}finally{await c.disconnectAll()}}async function xe(e,t,n,r,i){let a=await e.createClientManagerService();if(await Promise.all(Object.entries(t.mcpServers).map(async([e,t])=>{try{await a.connectToServer(e,t),r.json||console.error(`✓ Connected to ${e}`)}catch(t){r.json||console.error(`✗ Failed to connect to ${e}: ${j(t)}`)}})),a.getAllClients().length===0)throw Error(`No MCP servers connected`);try{return await i({container:e,configFilePaths:n,config:t,clientManager:a})}finally{await a.disconnectAll()}}async function M(e,t){let i=(e,t)=>{t===void 0?console.error(e):console.error(e,t)},a=e.json?{trace:i,debug:i,info:i,warn:i,error:i}:console,s=v(a),c=n(e.config),l=c.length>0?null:await o(),u=l?[l]:c;if(u.length===0)throw Error(`No config file found. Use --config or create mcp-config.yaml`);let d=await s.createConfigFetcherService({configSources:r(u),useCache:e.useCache}).fetchConfiguration();return d.proxy?.port?await be(s,d,u,e,t):await xe(s,d,u,e,t)}function Se(e){return e instanceof Error?e.message:String(e)}const Ce=new A(`describe-tools`).description(`Describe specific MCP tools`).argument(`<toolNames...>`,`Tool names to describe`).option(`-c, --config <path>`,`Path to MCP server configuration file (repeatable; later files override earlier ones)`,t).option(`-s, --server <name>`,`Filter by server name`).option(`-j, --json`,`Output as JSON`,!1).action(async(e,t)=>{try{await M(t,async({container:n,config:r,clientManager:i})=>{let a=t.server?[i.getClient(t.server)].filter(e=>e!==void 0):i.getAllClients();if(t.server&&a.length===0)throw Error(`Server "${t.server}" not found`);let o=process.env.PROJECT_PATH||process.cwd(),s=r.skills?.paths||[],c=s.length>0?n.createSkillService(o,s):void 0,l=[],u=[],d=[...e],f=await Promise.all(a.map(async e=>{try{return{client:e,tools:await e.listTools(),error:null}}catch(t){return{client:e,tools:[],error:t}}}));for(let{client:n,tools:r,error:i}of f)if(i)t.json||console.error(`Failed to list tools from ${n.serverName}:`,i);else for(let t of e){let e=r.find(e=>e.name===t);if(e){l.push({server:n.serverName,name:e.name,description:e.description,inputSchema:e.inputSchema});let r=d.indexOf(t);r>-1&&d.splice(r,1)}}if(c&&d.length>0){let e=await Promise.all([...d].map(async e=>{let t=e.startsWith(`skill__`)?e.slice(7):e;return{toolName:e,skill:await c.getSkill(t)}}));for(let{toolName:t,skill:n}of e)if(n){u.push({name:n.name,location:n.basePath,instructions:n.content});let e=d.indexOf(t);e>-1&&d.splice(e,1)}}let p=[];if(l.length>0&&p.push(`For MCP tools: Use the use_tool function with toolName and toolArgs based on the inputSchema above.`),u.length>0&&p.push(`For skill, just follow skill's description to continue.`),t.json){let e={};l.length>0&&(e.tools=l),u.length>0&&(e.skills=u),p.length>0&&(e.nextSteps=p),d.length>0&&(e.notFound=d),console.log(JSON.stringify(e,null,2))}else{if(l.length>0){console.log(`
|
|
3
3
|
Found tools:
|
|
4
4
|
`);for(let e of l)console.log(`Server: ${e.server}`),console.log(`Tool: ${e.name}`),console.log(`Description: ${e.description||`No description`}`),console.log(`Input Schema:`),console.log(JSON.stringify(e.inputSchema,null,2)),console.log(``)}if(u.length>0){console.log(`
|
|
5
5
|
Found skills:
|
|
@@ -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=O(e.output),n=!e.json&&(t.endsWith(`.yaml`)||t.endsWith(`.yml`)),r;if(n){let t=new ue,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 te(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`;async function Ie(){return f()}const G=`mcp-proxy-stdio-http-bridge`,K=`stdio-http-managed`,q=`manual`,Le=`MCP_PROXY_IDLE_SHUTDOWN_MINUTES`;async 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 w(e,de.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=O(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=D(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)}`)}}async function Ue(e){try{let t=await T(e,`utf-8`),n=(e.endsWith(`.yaml`)||e.endsWith(`.yml`)?se.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{}}}async function We(e){let t={};for(let n of e){let e=await 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 le(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?ce:{min:n,max:n}){let s=et(),c=await s.reservePort({repositoryPath:await 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:await 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=re(),f=e.serverId??a(),p=Je(process.env.MCP_PROXY_RUNTIME_LIFECYCLE),m=t.port,h=m===void 0?ce:{min:m,max:m},_=await tt(W,t.host??B,m,f,H,o,h),v=_.port,b={...t,port:v},x=await ie({repositoryPath:await 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,te=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,te,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(`${ae(e)}|${t}`).digest(`hex`);return D(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=await J(),i=dt(t.timeoutMs)+1e4;return await oe(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 ie({repositoryPath:await J(),serviceName:`mcp-proxy-stdio-http-bridge:${re()}`,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:await 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(await J(),W);try{await oe(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=await 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:await 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=await 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(
|
|
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-
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./src-CN6eJHnZ.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
|
|
@@ -1189,7 +1212,7 @@ interface CreateOAuthProviderOptions {
|
|
|
1189
1212
|
* @param options - Server identity, configuration and injected collaborators
|
|
1190
1213
|
* @returns A provider ready to hand to an SDK transport
|
|
1191
1214
|
*/
|
|
1192
|
-
export declare function createOAuthProvider(options: CreateOAuthProviderOptions): McpOAuthProvider | undefined
|
|
1215
|
+
export declare function createOAuthProvider(options: CreateOAuthProviderOptions): Promise<McpOAuthProvider | undefined>;
|
|
1193
1216
|
/**
|
|
1194
1217
|
* Port the loopback listener must bind for this server.
|
|
1195
1218
|
*
|
|
@@ -1210,7 +1233,7 @@ export declare function createOAuthProvider(options: CreateOAuthProviderOptions)
|
|
|
1210
1233
|
* @param workspaceRoot - Checkout the port belongs to
|
|
1211
1234
|
* @returns The port to bind
|
|
1212
1235
|
*/
|
|
1213
|
-
export declare function resolveCallbackPort(config: UrlServerConfig, workspaceRoot?: string): number
|
|
1236
|
+
export declare function resolveCallbackPort(config: UrlServerConfig, workspaceRoot?: string): Promise<number>;
|
|
1214
1237
|
//#endregion
|
|
1215
1238
|
//#region src/services/ConfigFetcherService.d.ts
|
|
1216
1239
|
/**
|
|
@@ -1354,7 +1377,7 @@ export declare function toConfigSources(paths: string[]): ConfigSource[];
|
|
|
1354
1377
|
* - Environment variable configuration pattern
|
|
1355
1378
|
*
|
|
1356
1379
|
* CODING STANDARDS:
|
|
1357
|
-
* - Use
|
|
1380
|
+
* - Use asynchronous filesystem operations for config discovery
|
|
1358
1381
|
* - Check PROJECT_PATH environment variable first
|
|
1359
1382
|
* - Fall back to current working directory
|
|
1360
1383
|
* - Support both .yaml and .json extensions
|
|
@@ -1371,7 +1394,7 @@ export declare function toConfigSources(paths: string[]): ConfigSource[];
|
|
|
1371
1394
|
*
|
|
1372
1395
|
* @returns Absolute path to config file, or null if not found
|
|
1373
1396
|
*/
|
|
1374
|
-
export declare function findConfigFile(): string | null
|
|
1397
|
+
export declare function findConfigFile(): Promise<string | null>;
|
|
1375
1398
|
//#endregion
|
|
1376
1399
|
//#region src/utils/generateServerId.d.ts
|
|
1377
1400
|
/**
|
|
@@ -1594,7 +1617,7 @@ export declare function cleanupStaleRegistries(): Promise<void>;
|
|
|
1594
1617
|
* @param startPath - Where to begin the walk
|
|
1595
1618
|
* @returns The workspace root, or the working directory when no marker is found
|
|
1596
1619
|
*/
|
|
1597
|
-
export declare function resolveWorkspaceRoot(startPath?: string): string
|
|
1620
|
+
export declare function resolveWorkspaceRoot(startPath?: string): Promise<string>;
|
|
1598
1621
|
//#endregion
|
|
1599
1622
|
//#region src/commands/http-runtime.d.ts
|
|
1600
1623
|
export declare const DEFAULT_HTTP_RUNTIME_TIMEOUT_MS = 120000;
|
|
@@ -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
|
|
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';
|
|
@@ -2855,7 +2886,7 @@ interface SharedServices {
|
|
|
2855
2886
|
}
|
|
2856
2887
|
interface ProxyContainer {
|
|
2857
2888
|
createConfigFetcherService(options: ConfigFetcherOptions): ConfigFetcherService;
|
|
2858
|
-
createClientManagerService(outputGuardOptions?: OutputGuardOptions, authOptions?: McpClientManagerAuthOptions, outputSchemaValidation?: OutputSchemaValidationOptions, clientCapabilities?: ClientCapabilities): McpClientManagerService
|
|
2889
|
+
createClientManagerService(outputGuardOptions?: OutputGuardOptions, authOptions?: McpClientManagerAuthOptions, outputSchemaValidation?: OutputSchemaValidationOptions, clientCapabilities?: ClientCapabilities): Promise<McpClientManagerService>;
|
|
2859
2890
|
createRuntimeStateService(runtimeDir?: string): RuntimeStateService;
|
|
2860
2891
|
createStopServerService(runtimeStateService?: RuntimeStateService): StopServerService;
|
|
2861
2892
|
createSkillService(cwd: string, skillPaths: string[], options?: {
|