dynmcp 0.0.1 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -29,7 +29,7 @@ var import_commander = require("commander");
29
29
  // package.json
30
30
  var package_default = {
31
31
  name: "dynmcp",
32
- version: "0.0.1",
32
+ version: "0.1.1",
33
33
  description: "Dynamic MCP context management tool for AI MCP-enabled agents and clients.",
34
34
  author: "Brandon Burrus <brandon@burrus.io>",
35
35
  license: "MIT",
@@ -74,9 +74,12 @@ var package_default = {
74
74
  }
75
75
  },
76
76
  files: [
77
- "dist"
77
+ "dist",
78
+ "schema"
78
79
  ],
79
80
  scripts: {
81
+ "generate:schema": "tsx scripts/generate-schema.ts",
82
+ prebuild: "tsx scripts/generate-schema.ts",
80
83
  build: "tsup",
81
84
  dev: "tsx src/index.ts",
82
85
  typecheck: "tsc --noEmit",
@@ -97,6 +100,7 @@ var package_default = {
97
100
  fastmcp: "^4.0.1",
98
101
  figlet: "^1.11.0",
99
102
  figures: "^6.1.0",
103
+ yaml: "^2.9.0",
100
104
  zod: "^4.4.3"
101
105
  },
102
106
  devDependencies: {
@@ -118,21 +122,146 @@ var import_chalk = __toESM(require("chalk"), 1);
118
122
 
119
123
  // src/proxy/index.ts
120
124
  var import_node_process3 = __toESM(require("process"), 1);
125
+ var import_stdio2 = require("@modelcontextprotocol/sdk/client/stdio.js");
126
+
127
+ // src/config/schema.ts
128
+ var import_zod = require("zod");
129
+ var MCP_NAME_PATTERN = /^[a-z0-9][a-z0-9-]*$/;
130
+ var mcpName = import_zod.z.string().regex(MCP_NAME_PATTERN);
131
+ var stdioTransport = import_zod.z.object({
132
+ transport: import_zod.z.literal("stdio"),
133
+ command: import_zod.z.string(),
134
+ args: import_zod.z.array(import_zod.z.string()).optional(),
135
+ env: import_zod.z.record(import_zod.z.string(), import_zod.z.string()).optional()
136
+ }).strict();
137
+ var httpUrl = import_zod.z.string().url().refine((u) => u.startsWith("http://") || u.startsWith("https://"), {
138
+ message: "URL must use http:// or https:// scheme"
139
+ });
140
+ var streamableHttpTransport = import_zod.z.object({
141
+ transport: import_zod.z.literal("streamable-http"),
142
+ url: httpUrl,
143
+ headers: import_zod.z.record(import_zod.z.string(), import_zod.z.string()).optional()
144
+ }).strict();
145
+ var sseTransport = import_zod.z.object({
146
+ transport: import_zod.z.literal("sse"),
147
+ url: httpUrl,
148
+ headers: import_zod.z.record(import_zod.z.string(), import_zod.z.string()).optional()
149
+ }).strict();
150
+ var transportConfig = import_zod.z.discriminatedUnion("transport", [
151
+ stdioTransport,
152
+ streamableHttpTransport,
153
+ sseTransport
154
+ ]);
155
+ var mcpConfigSchema = import_zod.z.object({
156
+ mcp: import_zod.z.record(mcpName, transportConfig).refine((obj) => Object.keys(obj).length > 0, { message: "At least one MCP must be configured" })
157
+ });
158
+
159
+ // src/config/loader.ts
160
+ var import_node_fs = require("fs");
161
+ var import_node_fs2 = require("fs");
162
+ var import_node_path = require("path");
163
+ var import_yaml = require("yaml");
164
+ var AUTO_DISCOVER_NAMES = ["mcp.json", ".mcp.json"];
165
+ function resolveConfigPath(explicitPath) {
166
+ if (explicitPath) {
167
+ const resolved = (0, import_node_path.resolve)(explicitPath);
168
+ if (!(0, import_node_fs2.existsSync)(resolved)) {
169
+ throw new Error(`Config file not found: ${resolved}`);
170
+ }
171
+ return resolved;
172
+ }
173
+ const cwd = process.cwd();
174
+ for (const name of AUTO_DISCOVER_NAMES) {
175
+ const candidate = (0, import_node_path.resolve)(cwd, name);
176
+ if ((0, import_node_fs2.existsSync)(candidate)) {
177
+ return candidate;
178
+ }
179
+ }
180
+ const searched = AUTO_DISCOVER_NAMES.map((n) => (0, import_node_path.resolve)(cwd, n)).join(", ");
181
+ throw new Error(`No config file found. Searched: ${searched}`);
182
+ }
183
+ function loadConfig(explicitPath) {
184
+ const configPath = resolveConfigPath(explicitPath);
185
+ const raw = (0, import_node_fs.readFileSync)(configPath, "utf-8");
186
+ let content;
187
+ try {
188
+ content = isYamlFile(configPath) ? (0, import_yaml.parse)(raw) : JSON.parse(raw);
189
+ } catch (parseError) {
190
+ const message = parseError instanceof Error ? parseError.message : String(parseError);
191
+ throw new Error(`Failed to parse config file (${configPath}): ${message}`);
192
+ }
193
+ const result = mcpConfigSchema.safeParse(content);
194
+ if (!result.success) {
195
+ const formatted = result.error.issues.map((issue) => ` - ${issue.path.join(".")}: ${issue.message}`).join("\n");
196
+ throw new Error(`Invalid config file (${configPath}):
197
+ ${formatted}`);
198
+ }
199
+ return result.data;
200
+ }
201
+ function isYamlFile(filePath) {
202
+ return filePath.endsWith(".yml") || filePath.endsWith(".yaml");
203
+ }
204
+
205
+ // src/config/json-schema.ts
206
+ var import_zod2 = require("zod");
207
+
208
+ // src/proxy/transport-factory.ts
209
+ var import_stdio = require("@modelcontextprotocol/sdk/client/stdio.js");
210
+ var import_streamableHttp = require("@modelcontextprotocol/sdk/client/streamableHttp.js");
211
+ var import_sse = require("@modelcontextprotocol/sdk/client/sse.js");
212
+ function createTransport(config) {
213
+ switch (config.transport) {
214
+ case "stdio":
215
+ return new import_stdio.StdioClientTransport({
216
+ command: config.command,
217
+ args: config.args,
218
+ env: config.env
219
+ });
220
+ case "streamable-http":
221
+ return new import_streamableHttp.StreamableHTTPClientTransport(
222
+ new URL(config.url),
223
+ config.headers ? { requestInit: { headers: config.headers } } : void 0
224
+ );
225
+ case "sse":
226
+ return new import_sse.SSEClientTransport(
227
+ new URL(config.url),
228
+ config.headers ? { requestInit: { headers: config.headers } } : void 0
229
+ );
230
+ default: {
231
+ const _exhaustive = config;
232
+ return _exhaustive;
233
+ }
234
+ }
235
+ }
121
236
 
122
237
  // src/proxy/tool-catalog.ts
123
238
  var DISCOVER_TOOL_PREAMBLE = `Use this tool to look up the full schema of a tool before calling it with use_tool.
124
239
  Call discover_tool with a tool name from the list below to get its complete description,
125
240
  input parameters, and output schema. Always discover a tool before using it.`;
126
- var ToolCatalog = class {
241
+ var ToolCatalog = class _ToolCatalog {
127
242
  tools;
128
243
  discoverToolDescription;
129
- constructor(upstreamTools) {
244
+ constructor(tools, description) {
245
+ this.tools = tools;
246
+ this.discoverToolDescription = description;
247
+ }
248
+ static fromFlat(upstreamTools) {
130
249
  const toolMap = /* @__PURE__ */ new Map();
131
250
  for (const tool of upstreamTools) {
132
251
  toolMap.set(tool.name, tool);
133
252
  }
134
- this.tools = toolMap;
135
- this.discoverToolDescription = buildDiscoverToolDescription(upstreamTools);
253
+ const description = buildFlatDescription(upstreamTools);
254
+ return new _ToolCatalog(toolMap, description);
255
+ }
256
+ static fromGrouped(groups) {
257
+ const toolMap = /* @__PURE__ */ new Map();
258
+ for (const [mcpName2, tools] of groups) {
259
+ for (const tool of tools) {
260
+ toolMap.set(`${mcpName2}/${tool.name}`, tool);
261
+ }
262
+ }
263
+ const description = buildGroupedDescription(groups);
264
+ return new _ToolCatalog(toolMap, description);
136
265
  }
137
266
  getToolDetails(toolName) {
138
267
  const tool = this.tools.get(toolName);
@@ -140,10 +269,10 @@ var ToolCatalog = class {
140
269
  const sortedNames = [...this.tools.keys()].sort().join(", ");
141
270
  return `Unknown tool: "${toolName}". Available tools: ${sortedNames}`;
142
271
  }
143
- return buildToolDetailsString(tool);
272
+ return buildToolDetailsString(toolName, tool);
144
273
  }
145
274
  };
146
- function buildDiscoverToolDescription(tools) {
275
+ function buildFlatDescription(tools) {
147
276
  const sortedTools = [...tools].sort((a, b) => a.name.localeCompare(b.name));
148
277
  const toolLines = sortedTools.map((tool) => `- ${tool.name}: ${tool.description}`).join("\n");
149
278
  return `${DISCOVER_TOOL_PREAMBLE}
@@ -152,9 +281,24 @@ function buildDiscoverToolDescription(tools) {
152
281
  ${toolLines}
153
282
  </tools>`;
154
283
  }
155
- function buildToolDetailsString(tool) {
284
+ function buildGroupedDescription(groups) {
285
+ const sortedMcpNames = [...groups.keys()].sort();
286
+ const sections = sortedMcpNames.map((mcpName2) => {
287
+ const tools = groups.get(mcpName2);
288
+ const sortedTools = [...tools].sort((a, b) => a.name.localeCompare(b.name));
289
+ const toolLines = sortedTools.map((tool) => `- ${mcpName2}/${tool.name}: ${tool.description}`).join("\n");
290
+ return `${mcpName2}:
291
+ ${toolLines}`;
292
+ });
293
+ return `${DISCOVER_TOOL_PREAMBLE}
294
+
295
+ <tools>
296
+ ${sections.join("\n\n")}
297
+ </tools>`;
298
+ }
299
+ function buildToolDetailsString(displayName, tool) {
156
300
  const lines = [
157
- `Tool: ${tool.name}`,
301
+ `Tool: ${displayName}`,
158
302
  `Description: ${tool.description}`,
159
303
  "",
160
304
  "Input Schema:",
@@ -193,73 +337,21 @@ function buildAnnotationLines(tool) {
193
337
  return lines;
194
338
  }
195
339
 
196
- // src/proxy/server.ts
197
- var import_node_process = __toESM(require("process"), 1);
198
- var import_fastmcp = require("fastmcp");
199
- var import_zod = require("zod");
200
- var ProxyServer = class {
201
- catalog;
202
- upstreamClient;
203
- constructor({ catalog, upstreamClient }) {
204
- this.catalog = catalog;
205
- this.upstreamClient = upstreamClient;
206
- }
207
- async start() {
208
- const server = new import_fastmcp.FastMCP({
209
- name: "dynamic-discovery-mcp",
210
- version: package_default.version
211
- });
212
- server.addTool({
213
- name: "discover_tool",
214
- description: this.catalog.discoverToolDescription,
215
- parameters: import_zod.z.object({ tool_name: import_zod.z.string() }),
216
- execute: async ({ tool_name }) => {
217
- return this.catalog.getToolDetails(tool_name);
218
- }
219
- });
220
- server.addTool({
221
- name: "use_tool",
222
- description: "Use a tool that was previously discovered with the discover_tool tool.",
223
- parameters: import_zod.z.object({
224
- tool_name: import_zod.z.string(),
225
- tool_input: import_zod.z.record(import_zod.z.string(), import_zod.z.unknown()).default({})
226
- }),
227
- execute: async ({ tool_name, tool_input }) => {
228
- if (!this.catalog.tools.has(tool_name)) {
229
- return this.catalog.getToolDetails(tool_name);
230
- }
231
- const result = await this.upstreamClient.callTool(tool_name, tool_input);
232
- return result;
233
- }
234
- });
235
- import_node_process.default.stderr.write("Starting dynamic-discovery-mcp server over stdio\n");
236
- await server.start({ transportType: "stdio" });
237
- }
238
- };
239
-
240
340
  // src/proxy/upstream-client.ts
241
- var import_node_process2 = __toESM(require("process"), 1);
341
+ var import_node_process = __toESM(require("process"), 1);
242
342
  var import_client = require("@modelcontextprotocol/sdk/client/index.js");
243
- var import_stdio = require("@modelcontextprotocol/sdk/client/stdio.js");
244
343
  var UpstreamClient = class {
245
- command;
246
- args;
344
+ transport;
247
345
  onTransportError;
248
346
  client = null;
249
- transport = null;
250
- constructor({ command, args, onTransportError }) {
251
- this.command = command;
252
- this.args = args;
347
+ constructor({ name, transport, onTransportError }) {
348
+ this.transport = transport;
253
349
  this.onTransportError = onTransportError ?? ((error) => {
254
- import_node_process2.default.stderr.write(`Upstream MCP transport error: ${error.message}
350
+ import_node_process.default.stderr.write(`[${name}] Upstream MCP transport error: ${error.message}
255
351
  `);
256
352
  });
257
353
  }
258
354
  async connect() {
259
- this.transport = new import_stdio.StdioClientTransport({
260
- command: this.command,
261
- args: this.args
262
- });
263
355
  this.transport.onerror = this.onTransportError;
264
356
  this.client = new import_client.Client({ name: "dynamic-discovery-mcp", version: "1.0.0" });
265
357
  await this.client.connect(this.transport);
@@ -294,10 +386,8 @@ var UpstreamClient = class {
294
386
  if (this.client === null) {
295
387
  throw new Error("Client is not connected. Call connect() first.");
296
388
  }
297
- return this.client.callTool({
298
- name,
299
- arguments: input
300
- });
389
+ const result = await this.client.callTool({ name, arguments: input });
390
+ return result;
301
391
  }
302
392
  async disconnect() {
303
393
  if (this.client === null) {
@@ -305,13 +395,126 @@ var UpstreamClient = class {
305
395
  }
306
396
  await this.client.close();
307
397
  this.client = null;
308
- this.transport = null;
398
+ }
399
+ };
400
+
401
+ // src/proxy/orchestrator.ts
402
+ var Orchestrator = class {
403
+ config;
404
+ clients = /* @__PURE__ */ new Map();
405
+ toolCatalog = null;
406
+ constructor(config) {
407
+ this.config = config;
408
+ }
409
+ async connect() {
410
+ const groups = /* @__PURE__ */ new Map();
411
+ try {
412
+ for (const [mcpName2, { transport }] of this.config.mcps) {
413
+ const client = new UpstreamClient({
414
+ name: mcpName2,
415
+ transport,
416
+ onTransportError: (error) => {
417
+ this.config.onTransportError?.(mcpName2, error);
418
+ }
419
+ });
420
+ await client.connect();
421
+ const tools = await client.listTools();
422
+ this.clients.set(mcpName2, client);
423
+ groups.set(mcpName2, tools);
424
+ }
425
+ } catch (error) {
426
+ await this.disconnectAll();
427
+ throw error;
428
+ }
429
+ this.toolCatalog = ToolCatalog.fromGrouped(groups);
430
+ }
431
+ get catalog() {
432
+ if (this.toolCatalog === null) {
433
+ throw new Error("Orchestrator is not connected. Call connect() first.");
434
+ }
435
+ return this.toolCatalog;
436
+ }
437
+ async callTool(namespacedName, input) {
438
+ const separatorIndex = namespacedName.indexOf("/");
439
+ if (separatorIndex === -1) {
440
+ throw new Error(
441
+ `Invalid namespaced tool name: "${namespacedName}". Expected format: "mcpName/toolName".`
442
+ );
443
+ }
444
+ const mcpName2 = namespacedName.slice(0, separatorIndex);
445
+ const toolName = namespacedName.slice(separatorIndex + 1);
446
+ const client = this.clients.get(mcpName2);
447
+ if (client === void 0) {
448
+ const available = [...this.clients.keys()].sort().join(", ");
449
+ throw new Error(`Unknown MCP: "${mcpName2}". Available MCPs: ${available}`);
450
+ }
451
+ return client.callTool(toolName, input);
452
+ }
453
+ async disconnectAll() {
454
+ const disconnections = [...this.clients.values()].map((client) => client.disconnect());
455
+ await Promise.all(disconnections);
456
+ this.clients.clear();
457
+ this.toolCatalog = null;
458
+ }
459
+ };
460
+
461
+ // src/proxy/server.ts
462
+ var import_node_process2 = __toESM(require("process"), 1);
463
+ var import_fastmcp = require("fastmcp");
464
+ var import_zod3 = require("zod");
465
+ var ProxyServer = class {
466
+ catalog;
467
+ callTool;
468
+ constructor({ catalog, callTool }) {
469
+ this.catalog = catalog;
470
+ this.callTool = callTool;
471
+ }
472
+ async start() {
473
+ const server = new import_fastmcp.FastMCP({
474
+ name: "dynamic-discovery-mcp",
475
+ version: package_default.version
476
+ });
477
+ server.addTool({
478
+ name: "discover_tool",
479
+ description: this.catalog.discoverToolDescription,
480
+ parameters: import_zod3.z.object({ tool_name: import_zod3.z.string() }),
481
+ execute: async ({ tool_name }) => {
482
+ return this.catalog.getToolDetails(tool_name);
483
+ }
484
+ });
485
+ server.addTool({
486
+ name: "use_tool",
487
+ description: "Use a tool that was previously discovered with the discover_tool tool.",
488
+ parameters: import_zod3.z.object({
489
+ tool_name: import_zod3.z.string(),
490
+ tool_input: import_zod3.z.record(import_zod3.z.string(), import_zod3.z.unknown()).default({})
491
+ }),
492
+ execute: async ({ tool_name, tool_input }) => {
493
+ if (!this.catalog.tools.has(tool_name)) {
494
+ return this.catalog.getToolDetails(tool_name);
495
+ }
496
+ const result = await this.callTool(tool_name, tool_input);
497
+ return result;
498
+ }
499
+ });
500
+ import_node_process2.default.stderr.write("Starting dynamic-discovery-mcp server over stdio\n");
501
+ await server.start({ transportType: "stdio" });
309
502
  }
310
503
  };
311
504
 
312
505
  // src/proxy/index.ts
313
506
  async function startProxy(command, args) {
314
507
  let isShuttingDown = false;
508
+ const transport = new import_stdio2.StdioClientTransport({ command, args });
509
+ const upstreamClient = new UpstreamClient({
510
+ name: command,
511
+ transport,
512
+ onTransportError: (error) => {
513
+ import_node_process3.default.stderr.write(`Upstream MCP transport error: ${error.message}
514
+ `);
515
+ shutdown(1);
516
+ }
517
+ });
315
518
  const shutdown = (exitCode) => {
316
519
  if (isShuttingDown) return;
317
520
  isShuttingDown = true;
@@ -322,15 +525,6 @@ async function startProxy(command, args) {
322
525
  );
323
526
  }).finally(() => import_node_process3.default.exit(exitCode));
324
527
  };
325
- const upstreamClient = new UpstreamClient({
326
- command,
327
- args,
328
- onTransportError: (error) => {
329
- import_node_process3.default.stderr.write(`Upstream MCP transport error: ${error.message}
330
- `);
331
- shutdown(1);
332
- }
333
- });
334
528
  try {
335
529
  await upstreamClient.connect();
336
530
  } catch (error) {
@@ -346,13 +540,68 @@ async function startProxy(command, args) {
346
540
  `);
347
541
  import_node_process3.default.exit(1);
348
542
  }
349
- const catalog = new ToolCatalog(tools);
350
- const proxyServer = new ProxyServer({ catalog, upstreamClient });
543
+ const catalog = ToolCatalog.fromFlat(tools);
544
+ const proxyServer = new ProxyServer({
545
+ catalog,
546
+ callTool: (name, input) => upstreamClient.callTool(name, input)
547
+ });
548
+ import_node_process3.default.on("SIGINT", () => shutdown(0));
549
+ import_node_process3.default.on("SIGTERM", () => shutdown(0));
550
+ import_node_process3.default.stdin.on("end", () => shutdown(0));
551
+ import_node_process3.default.stdin.on("close", () => shutdown(0));
552
+ try {
553
+ await proxyServer.start();
554
+ } catch (error) {
555
+ shutdown(1);
556
+ throw error;
557
+ }
558
+ }
559
+ async function startProxyFromConfig(configPath) {
560
+ let isShuttingDown = false;
561
+ const config = loadConfig(configPath);
562
+ const mcps = /* @__PURE__ */ new Map();
563
+ for (const [name, entry] of Object.entries(config.mcp)) {
564
+ mcps.set(name, { transport: createTransport(entry) });
565
+ }
566
+ const orchestrator = new Orchestrator({
567
+ mcps,
568
+ onTransportError: (mcpName2, error) => {
569
+ import_node_process3.default.stderr.write(`Upstream MCP "${mcpName2}" transport error: ${error.message}
570
+ `);
571
+ shutdown(1);
572
+ }
573
+ });
574
+ const shutdown = (exitCode) => {
575
+ if (isShuttingDown) return;
576
+ isShuttingDown = true;
577
+ orchestrator.disconnectAll().catch((error) => {
578
+ import_node_process3.default.stderr.write(
579
+ `dynmcp: error during disconnect: ${error instanceof Error ? error.message : String(error)}
580
+ `
581
+ );
582
+ }).finally(() => import_node_process3.default.exit(exitCode));
583
+ };
584
+ try {
585
+ await orchestrator.connect();
586
+ } catch (error) {
587
+ import_node_process3.default.stderr.write(`dynmcp: ${error instanceof Error ? error.message : String(error)}
588
+ `);
589
+ import_node_process3.default.exit(1);
590
+ }
591
+ const proxyServer = new ProxyServer({
592
+ catalog: orchestrator.catalog,
593
+ callTool: (name, input) => orchestrator.callTool(name, input)
594
+ });
351
595
  import_node_process3.default.on("SIGINT", () => shutdown(0));
352
596
  import_node_process3.default.on("SIGTERM", () => shutdown(0));
353
597
  import_node_process3.default.stdin.on("end", () => shutdown(0));
354
598
  import_node_process3.default.stdin.on("close", () => shutdown(0));
355
- await proxyServer.start();
599
+ try {
600
+ await proxyServer.start();
601
+ } catch (error) {
602
+ shutdown(1);
603
+ throw error;
604
+ }
356
605
  }
357
606
 
358
607
  // src/cli.ts
@@ -363,23 +612,31 @@ var cliBanner = import_chalk.default.bold.magentaBright(
363
612
  verticalLayout: "fitted"
364
613
  })
365
614
  );
366
- var cli = new import_commander.Command(package_default.name).description(package_default.description).version(package_default.version).addHelpText("beforeAll", cliBanner).addHelpText("after", "\nExample:\n dynmcp -- npx -y chrome-devtools-mcp@latest\n").allowExcessArguments(true).passThroughOptions(true).action(async () => {
615
+ var cli = new import_commander.Command(package_default.name).description(package_default.description).version(package_default.version).addHelpText("beforeAll", cliBanner).addHelpText(
616
+ "after",
617
+ "\nExamples:\n dynmcp -- npx -y chrome-devtools-mcp@latest\n dynmcp --config ./mcp.json\n"
618
+ ).option("-c, --config <path>", "Path to config file (JSON or YAML)").allowExcessArguments(true).passThroughOptions(true).action(async (_options, cmd) => {
367
619
  const separatorIndex = import_node_process4.default.argv.indexOf("--");
368
- if (separatorIndex === -1) {
369
- import_node_process4.default.stderr.write(
370
- "dynmcp: no upstream command provided.\nUsage: dynmcp -- <command> [args...]\nExample: dynmcp -- npx -y chrome-devtools-mcp@latest\n"
371
- );
372
- import_node_process4.default.exit(1);
373
- }
374
- const [command, ...args] = import_node_process4.default.argv.slice(separatorIndex + 1);
375
- if (command === void 0) {
376
- import_node_process4.default.stderr.write(
377
- "dynmcp: no upstream command provided.\nUsage: dynmcp -- <command> [args...]\nExample: dynmcp -- npx -y chrome-devtools-mcp@latest\n"
378
- );
379
- import_node_process4.default.exit(1);
620
+ const configPath = cmd.opts().config;
621
+ if (separatorIndex !== -1) {
622
+ const [command, ...args] = import_node_process4.default.argv.slice(separatorIndex + 1);
623
+ if (command === void 0) {
624
+ import_node_process4.default.stderr.write(
625
+ "dynmcp: no upstream command provided after --.\nUsage: dynmcp -- <command> [args...]\n"
626
+ );
627
+ import_node_process4.default.exit(1);
628
+ }
629
+ try {
630
+ await startProxy(command, args);
631
+ } catch (error) {
632
+ import_node_process4.default.stderr.write(`dynmcp: ${error instanceof Error ? error.message : String(error)}
633
+ `);
634
+ import_node_process4.default.exit(1);
635
+ }
636
+ return;
380
637
  }
381
638
  try {
382
- await startProxy(command, args);
639
+ await startProxyFromConfig(configPath);
383
640
  } catch (error) {
384
641
  import_node_process4.default.stderr.write(`dynmcp: ${error instanceof Error ? error.message : String(error)}
385
642
  `);