@alcyone-labs/arg-parser 2.2.0 → 2.3.0

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.
Files changed (42) hide show
  1. package/README.md +226 -20
  2. package/dist/assets/.dxtignore.template +0 -1
  3. package/dist/config/ConfigurationManager.d.ts.map +1 -1
  4. package/dist/config/plugins/ConfigPlugin.d.ts.map +1 -1
  5. package/dist/config/plugins/ConfigPluginRegistry.d.ts +1 -1
  6. package/dist/config/plugins/ConfigPluginRegistry.d.ts.map +1 -1
  7. package/dist/config/plugins/TomlConfigPlugin.d.ts +1 -1
  8. package/dist/config/plugins/TomlConfigPlugin.d.ts.map +1 -1
  9. package/dist/config/plugins/YamlConfigPlugin.d.ts +1 -1
  10. package/dist/config/plugins/YamlConfigPlugin.d.ts.map +1 -1
  11. package/dist/config/plugins/index.d.ts +4 -4
  12. package/dist/config/plugins/index.d.ts.map +1 -1
  13. package/dist/core/ArgParser.d.ts +14 -3
  14. package/dist/core/ArgParser.d.ts.map +1 -1
  15. package/dist/core/ArgParserBase.d.ts.map +1 -1
  16. package/dist/core/log-path-utils.d.ts +59 -0
  17. package/dist/core/log-path-utils.d.ts.map +1 -0
  18. package/dist/core/types.d.ts +1 -1
  19. package/dist/core/types.d.ts.map +1 -1
  20. package/dist/dxt/DxtGenerator-testUtils.d.ts +22 -0
  21. package/dist/dxt/DxtGenerator-testUtils.d.ts.map +1 -0
  22. package/dist/dxt/DxtGenerator.d.ts +11 -65
  23. package/dist/dxt/DxtGenerator.d.ts.map +1 -1
  24. package/dist/index.cjs +799 -1260
  25. package/dist/index.cjs.map +1 -1
  26. package/dist/index.d.ts +5 -4
  27. package/dist/index.d.ts.map +1 -1
  28. package/dist/index.min.mjs +6303 -6736
  29. package/dist/index.min.mjs.map +1 -1
  30. package/dist/index.mjs +799 -1260
  31. package/dist/index.mjs.map +1 -1
  32. package/dist/mcp/ArgParserMcp.d.ts.map +1 -1
  33. package/dist/mcp/mcp-notifications.d.ts +4 -4
  34. package/dist/mcp/mcp-notifications.d.ts.map +1 -1
  35. package/dist/mcp/mcp-prompts.d.ts.map +1 -1
  36. package/dist/mcp/mcp-protocol-versions.d.ts +11 -11
  37. package/dist/mcp/mcp-protocol-versions.d.ts.map +1 -1
  38. package/dist/mcp/mcp-resources.d.ts.map +1 -1
  39. package/dist/testing/fuzzy-test-cli.d.ts.map +1 -1
  40. package/dist/testing/fuzzy-tester.d.ts.map +1 -1
  41. package/package.json +2 -3
  42. package/dist/assets/tsdown.dxt.config.ts +0 -37
package/dist/index.mjs CHANGED
@@ -284,7 +284,9 @@ class JsonConfigPlugin extends ConfigPlugin {
284
284
  const { _meta, ...config } = parsed;
285
285
  return config;
286
286
  } catch (error) {
287
- throw new Error(`Failed to parse JSON: ${error instanceof Error ? error.message : String(error)}`);
287
+ throw new Error(
288
+ `Failed to parse JSON: ${error instanceof Error ? error.message : String(error)}`
289
+ );
288
290
  }
289
291
  }
290
292
  generate(_config, flags, parsedArgs) {
@@ -500,7 +502,9 @@ function enableConfigPlugins(pluginNames) {
500
502
  globalConfigPluginRegistry.registerPlugin(tomlPlugin);
501
503
  }
502
504
  } catch (error) {
503
- console.warn(`Failed to enable TOML plugin: ${error instanceof Error ? error.message : String(error)}`);
505
+ console.warn(
506
+ `Failed to enable TOML plugin: ${error instanceof Error ? error.message : String(error)}`
507
+ );
504
508
  }
505
509
  break;
506
510
  case "yaml":
@@ -511,7 +515,9 @@ function enableConfigPlugins(pluginNames) {
511
515
  globalConfigPluginRegistry.registerPlugin(yamlPlugin);
512
516
  }
513
517
  } catch (error) {
514
- console.warn(`Failed to enable YAML plugin: ${error instanceof Error ? error.message : String(error)}`);
518
+ console.warn(
519
+ `Failed to enable YAML plugin: ${error instanceof Error ? error.message : String(error)}`
520
+ );
515
521
  }
516
522
  break;
517
523
  default:
@@ -535,14 +541,18 @@ class ConfigurationManager {
535
541
  } else if (appName && appName !== "Argument Parser") {
536
542
  baseName = appName;
537
543
  }
538
- baseName = baseName.split(/[\s-_]+/).map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()).join("");
544
+ baseName = baseName.split(/[\s-_]+/).map(
545
+ (word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()
546
+ ).join("");
539
547
  return `${baseName}.env`;
540
548
  }
541
549
  /**
542
550
  * Handles the --s-save-to-env system flag at the final parser level
543
551
  */
544
552
  handleSaveToEnvFlag(processArgs, parserChain) {
545
- const saveToEnvIndex = processArgs.findIndex((arg) => arg === "--s-save-to-env");
553
+ const saveToEnvIndex = processArgs.findIndex(
554
+ (arg) => arg === "--s-save-to-env"
555
+ );
546
556
  if (saveToEnvIndex !== -1) {
547
557
  let filePath;
548
558
  if (saveToEnvIndex + 1 < processArgs.length) {
@@ -568,7 +578,11 @@ class ConfigurationManager {
568
578
  const finalParser = parserChain[parserChain.length - 1];
569
579
  const parsedArgs = finalParser.getLastParseResult();
570
580
  if (!parsedArgs) {
571
- console.log(simpleChalk.yellow("No parsed arguments available. Run the command first to generate configuration."));
581
+ console.log(
582
+ simpleChalk.yellow(
583
+ "No parsed arguments available. Run the command first to generate configuration."
584
+ )
585
+ );
572
586
  return;
573
587
  }
574
588
  const allFlags = [];
@@ -601,10 +615,18 @@ class ConfigurationManager {
601
615
  fs.writeFileSync(filePath, content, "utf8");
602
616
  console.log(simpleChalk.green(`✅ Configuration saved to: ${filePath}`));
603
617
  console.log(simpleChalk.gray(`Format: ${ext || ".env"}`));
604
- console.log(simpleChalk.gray(`Flags saved: ${Object.keys(parsedArgs.args).length}`));
618
+ console.log(
619
+ simpleChalk.gray(`Flags saved: ${Object.keys(parsedArgs.args).length}`)
620
+ );
605
621
  } catch (error) {
606
- console.error(simpleChalk.red(`❌ Failed to save configuration: ${error instanceof Error ? error.message : String(error)}`));
607
- throw new Error(`Failed to save configuration: ${error instanceof Error ? error.message : String(error)}`);
622
+ console.error(
623
+ simpleChalk.red(
624
+ `❌ Failed to save configuration: ${error instanceof Error ? error.message : String(error)}`
625
+ )
626
+ );
627
+ throw new Error(
628
+ `Failed to save configuration: ${error instanceof Error ? error.message : String(error)}`
629
+ );
608
630
  }
609
631
  }
610
632
  /**
@@ -641,7 +663,11 @@ class ConfigurationManager {
641
663
  }
642
664
  return this.convertConfigToFlagValues(rawConfig, parserChain);
643
665
  } catch (error) {
644
- console.warn(simpleChalk.yellow(`Warning: Could not load config file ${filePath}: ${error instanceof Error ? error.message : String(error)}`));
666
+ console.warn(
667
+ simpleChalk.yellow(
668
+ `Warning: Could not load config file ${filePath}: ${error instanceof Error ? error.message : String(error)}`
669
+ )
670
+ );
645
671
  return {};
646
672
  }
647
673
  }
@@ -675,7 +701,9 @@ class ConfigurationManager {
675
701
  if (plugin) {
676
702
  return plugin.parse(content);
677
703
  }
678
- console.warn("YAML plugin not available, using simple parser. Install js-yaml and enable YAML plugin for full support.");
704
+ console.warn(
705
+ "YAML plugin not available, using simple parser. Install js-yaml and enable YAML plugin for full support."
706
+ );
679
707
  const config = {};
680
708
  const lines = content.split("\n");
681
709
  let currentKey = null;
@@ -726,7 +754,9 @@ class ConfigurationManager {
726
754
  try {
727
755
  return JSON.parse(content) || {};
728
756
  } catch (error) {
729
- throw new Error(`Failed to parse JSON: ${error instanceof Error ? error.message : String(error)}`);
757
+ throw new Error(
758
+ `Failed to parse JSON: ${error instanceof Error ? error.message : String(error)}`
759
+ );
730
760
  }
731
761
  }
732
762
  /**
@@ -737,7 +767,9 @@ class ConfigurationManager {
737
767
  if (plugin) {
738
768
  return plugin.parse(content);
739
769
  }
740
- console.warn("TOML plugin not available, using simple parser. Install smol-toml and enable TOML plugin for full support.");
770
+ console.warn(
771
+ "TOML plugin not available, using simple parser. Install smol-toml and enable TOML plugin for full support."
772
+ );
741
773
  const config = {};
742
774
  const lines = content.split("\n");
743
775
  for (const line of lines) {
@@ -768,13 +800,19 @@ class ConfigurationManager {
768
800
  for (const [key, value] of Object.entries(rawConfig)) {
769
801
  let flag = allFlags.find((f) => f["name"] === key);
770
802
  if (!flag) {
771
- flag = allFlags.find((f) => f["name"].toLowerCase() === key.toLowerCase());
803
+ flag = allFlags.find(
804
+ (f) => f["name"].toLowerCase() === key.toLowerCase()
805
+ );
772
806
  }
773
807
  if (flag) {
774
808
  try {
775
809
  flagValues[flag["name"]] = this.convertValueToFlagType(value, flag);
776
810
  } catch (error) {
777
- console.warn(simpleChalk.yellow(`Warning: Could not convert config value for flag '${key}': ${error instanceof Error ? error.message : String(error)}`));
811
+ console.warn(
812
+ simpleChalk.yellow(
813
+ `Warning: Could not convert config value for flag '${key}': ${error instanceof Error ? error.message : String(error)}`
814
+ )
815
+ );
778
816
  }
779
817
  }
780
818
  }
@@ -811,17 +849,23 @@ class ConfigurationManager {
811
849
  }
812
850
  const num = Number(value);
813
851
  if (isNaN(num)) {
814
- throw new Error(`Cannot convert '${value}' to number for flag '${flag["name"]}'`);
852
+ throw new Error(
853
+ `Cannot convert '${value}' to number for flag '${flag["name"]}'`
854
+ );
815
855
  }
816
856
  return num;
817
857
  } else if (isBooleanType) {
818
858
  if (typeof value === "boolean") return value;
819
859
  if (typeof value === "string") {
820
860
  const lower = value.toLowerCase();
821
- if (lower === "true" || lower === "1" || lower === "yes" || lower === "on") return true;
822
- if (lower === "false" || lower === "0" || lower === "no" || lower === "off") return false;
861
+ if (lower === "true" || lower === "1" || lower === "yes" || lower === "on")
862
+ return true;
863
+ if (lower === "false" || lower === "0" || lower === "no" || lower === "off")
864
+ return false;
823
865
  }
824
- throw new Error(`Cannot convert '${value}' to boolean for flag '${flag["name"]}'`);
866
+ throw new Error(
867
+ `Cannot convert '${value}' to boolean for flag '${flag["name"]}'`
868
+ );
825
869
  } else if (flagType === "table") {
826
870
  if (Array.isArray(value)) return value;
827
871
  if (typeof value === "string") {
@@ -832,13 +876,17 @@ class ConfigurationManager {
832
876
  return value.split(",").map((v) => v.trim());
833
877
  }
834
878
  }
835
- throw new Error(`Cannot convert '${value}' to table for flag '${flag["name"]}'`);
879
+ throw new Error(
880
+ `Cannot convert '${value}' to table for flag '${flag["name"]}'`
881
+ );
836
882
  } else {
837
883
  if (typeof flagType === "function") {
838
884
  try {
839
885
  return flagType(value);
840
886
  } catch (error) {
841
- throw new Error(`Custom type conversion failed for flag '${flag["name"]}': ${error instanceof Error ? error.message : String(error)}`);
887
+ throw new Error(
888
+ `Custom type conversion failed for flag '${flag["name"]}': ${error instanceof Error ? error.message : String(error)}`
889
+ );
842
890
  }
843
891
  }
844
892
  return String(value);
@@ -913,7 +961,9 @@ class ConfigurationManager {
913
961
  if (Array.isArray(value)) {
914
962
  lines.push(`${flag["name"]}:`);
915
963
  for (const item of value) {
916
- lines.push(` - ${typeof item === "string" && item.includes(" ") ? `"${item}"` : item}`);
964
+ lines.push(
965
+ ` - ${typeof item === "string" && item.includes(" ") ? `"${item}"` : item}`
966
+ );
917
967
  }
918
968
  } else if (typeof value === "string" && value.includes(" ")) {
919
969
  lines.push(`${flag["name"]}: "${value}"`);
@@ -1147,64 +1197,22 @@ function createOutputSchema(pattern2) {
1147
1197
  }
1148
1198
  return OutputSchemaPatterns.successError();
1149
1199
  }
1150
- class DxtGenerator {
1151
- constructor(argParserInstance) {
1200
+ class DxtGeneratorTestUtils {
1201
+ constructor(argParserInstance, extractMcpServerInfo, handleExit) {
1152
1202
  this.argParserInstance = argParserInstance;
1153
- }
1154
- /**
1155
- * Helper method to handle exit logic based on autoExit setting
1156
- */
1157
- _handleExit(exitCode, message, type2, data2) {
1158
- const result = {
1159
- success: exitCode === 0,
1160
- exitCode,
1161
- message,
1162
- type: type2 || (exitCode === 0 ? "success" : "error"),
1163
- shouldExit: true,
1164
- data: data2
1165
- };
1166
- if (this.argParserInstance.getAutoExit() && typeof process === "object" && typeof process.exit === "function") {
1167
- process.exit(exitCode);
1168
- }
1169
- return result;
1170
- }
1171
- /**
1172
- * Handles the --s-build-dxt system flag to generate DXT packages for MCP servers
1173
- */
1174
- async handleBuildDxtFlag(processArgs, buildDxtIndex) {
1175
- var _a, _b, _c;
1176
- try {
1177
- const isTestMode = process.env["NODE_ENV"] === "test" || ((_a = process.argv[0]) == null ? void 0 : _a.includes("vitest")) || ((_b = process.argv[1]) == null ? void 0 : _b.includes("vitest")) || ((_c = process.argv[1]) == null ? void 0 : _c.includes("tinypool"));
1178
- if (isTestMode) {
1179
- return await this.handleTestModeDxtGeneration(processArgs, buildDxtIndex);
1180
- }
1181
- const entryPointFile = process.argv[1];
1182
- if (!entryPointFile || !fs.existsSync(entryPointFile)) {
1183
- console.error(simpleChalk.red(`Error: Entry point file not found: ${entryPointFile}`));
1184
- return this._handleExit(1, "Entry point file not found", "error");
1185
- }
1186
- const outputDir = processArgs[buildDxtIndex + 1] || "./dxt";
1187
- console.log(simpleChalk.cyan(`
1188
- 🔧 Building DXT package for entry point: ${path.basename(entryPointFile)}`));
1189
- console.log(simpleChalk.gray(`Output directory: ${outputDir}`));
1190
- await this.buildDxtWithTsdown(entryPointFile, outputDir);
1191
- console.log(simpleChalk.green(`
1192
- ✅ DXT package generation completed!`));
1193
- return this._handleExit(0, "DXT package generation completed", "success", { entryPoint: entryPointFile, outputDir });
1194
- } catch (error) {
1195
- console.error(simpleChalk.red(`Error generating DXT package: ${error instanceof Error ? error.message : String(error)}`));
1196
- return this._handleExit(1, `Error generating DXT package: ${error instanceof Error ? error.message : String(error)}`, "error");
1197
- }
1203
+ this.extractMcpServerInfo = extractMcpServerInfo;
1204
+ this.handleExit = handleExit;
1198
1205
  }
1199
1206
  /**
1200
1207
  * Handles DXT generation in test mode by creating mock DXT package structure
1208
+ * This method creates simplified mock files for testing purposes
1201
1209
  */
1202
1210
  async handleTestModeDxtGeneration(processArgs, buildDxtIndex) {
1203
1211
  try {
1204
1212
  const outputDir = processArgs[buildDxtIndex + 1] || "./dxt-packages";
1205
1213
  const mcpTools = this.argParserInstance.toMcpTools();
1206
1214
  if (mcpTools.length === 0) {
1207
- return this._handleExit(0, "No MCP servers found", "success");
1215
+ return this.handleExit(0, "No MCP servers found", "success");
1208
1216
  }
1209
1217
  const serverInfo = this.extractMcpServerInfo();
1210
1218
  const folderName = `${serverInfo.name.replace(/[^a-zA-Z0-9_-]/g, "_")}-dxt`;
@@ -1233,7 +1241,10 @@ class DxtGenerator {
1233
1241
  })),
1234
1242
  icon: "logo.jpg"
1235
1243
  };
1236
- fs.writeFileSync(path.join(buildDir, "manifest.json"), JSON.stringify(manifest, null, 2));
1244
+ fs.writeFileSync(
1245
+ path.join(buildDir, "manifest.json"),
1246
+ JSON.stringify(manifest, null, 2)
1247
+ );
1237
1248
  const packageJson = {
1238
1249
  name: serverInfo.name,
1239
1250
  version: serverInfo.version,
@@ -1241,7 +1252,10 @@ class DxtGenerator {
1241
1252
  main: "index.mjs",
1242
1253
  type: "module"
1243
1254
  };
1244
- fs.writeFileSync(path.join(buildDir, "package.json"), JSON.stringify(packageJson, null, 2));
1255
+ fs.writeFileSync(
1256
+ path.join(buildDir, "package.json"),
1257
+ JSON.stringify(packageJson, null, 2)
1258
+ );
1245
1259
  const readme = `# ${serverInfo.name}
1246
1260
 
1247
1261
  ${serverInfo.description}
@@ -1250,88 +1264,172 @@ Generated by @alcyone-labs/arg-parser`;
1250
1264
  fs.writeFileSync(path.join(buildDir, "README.md"), readme);
1251
1265
  const buildScript = `#!/bin/bash
1252
1266
  echo "Mock DXT build script for ${serverInfo.name}"`;
1253
- fs.writeFileSync(path.join(buildDir, "build-dxt-package.sh"), buildScript);
1254
- return this._handleExit(0, "DXT package generation completed", "success", {
1267
+ fs.writeFileSync(
1268
+ path.join(buildDir, "build-dxt-package.sh"),
1269
+ buildScript
1270
+ );
1271
+ return this.handleExit(0, "DXT package generation completed", "success", {
1255
1272
  entryPoint: "test-mode",
1256
1273
  outputDir: buildDir
1257
1274
  });
1258
1275
  } catch (error) {
1259
- return this._handleExit(1, `Test mode DXT generation failed: ${error instanceof Error ? error.message : String(error)}`, "error");
1276
+ return this.handleExit(
1277
+ 1,
1278
+ `Test mode DXT generation failed: ${error instanceof Error ? error.message : String(error)}`,
1279
+ "error"
1280
+ );
1260
1281
  }
1261
1282
  }
1262
1283
  /**
1263
- * Generates a DXT package for the unified MCP server
1264
- * Now supports both withMcp() configuration and legacy addMcpSubCommand()
1284
+ * Checks if the current environment is in test mode
1285
+ * Used to determine whether to use test utilities or production code
1265
1286
  */
1266
- async generateDxtPackage(mcpSubCommand, outputDir) {
1267
- const serverInfo = this.extractMcpServerInfo(mcpSubCommand);
1268
- const tools = this.generateMcpToolsForDxt(mcpSubCommand);
1269
- const finalOutputDir = outputDir || "./dxt-packages";
1270
- const folderName = `${serverInfo.name.replace(/[^a-zA-Z0-9_-]/g, "_")}-dxt`;
1271
- const buildDir = path.join(finalOutputDir, folderName);
1272
- if (!fs.existsSync(buildDir)) {
1273
- fs.mkdirSync(buildDir, { recursive: true });
1274
- }
1275
- const serverDir = path.join(buildDir, "server");
1276
- if (!fs.existsSync(serverDir)) {
1277
- fs.mkdirSync(serverDir, { recursive: true });
1278
- }
1279
- const logoFilename = await this.addLogoToFolder(buildDir, serverInfo);
1280
- const manifest = this.createDxtManifest(serverInfo, tools, mcpSubCommand, logoFilename);
1281
- this.validateDxtManifest(manifest);
1282
- fs.writeFileSync(path.join(buildDir, "manifest.json"), JSON.stringify(manifest, null, 2));
1283
- this.addOriginalCliToFolder(buildDir);
1284
- const bundledCliPath = await this.bundleOriginalCliWithTsdown(serverDir);
1285
- const serverScript = this.createServerScript(serverInfo, bundledCliPath);
1286
- const serverScriptPath = path.join(serverDir, "index.mjs");
1287
- fs.writeFileSync(serverScriptPath, serverScript);
1288
- try {
1289
- fs.chmodSync(serverScriptPath, 493);
1290
- } catch (error) {
1291
- console.warn("⚠ Could not set executable permission on server script:", error instanceof Error ? error.message : String(error));
1292
- }
1293
- const packageJson = this.createDxtPackageJson(serverInfo);
1294
- fs.writeFileSync(path.join(buildDir, "package.json"), JSON.stringify(packageJson, null, 2));
1295
- const readme = this.createDxtReadme(serverInfo);
1296
- fs.writeFileSync(path.join(buildDir, "README.md"), readme);
1297
- const buildScript = this.createSimpleBuildScript(serverInfo);
1298
- fs.writeFileSync(path.join(buildDir, "build-dxt.sh"), buildScript);
1299
- const dxtIgnore = this.createDxtIgnore();
1300
- fs.writeFileSync(path.join(buildDir, ".dxtignore"), dxtIgnore);
1301
- try {
1302
- fs.chmodSync(path.join(buildDir, "build-dxt.sh"), 493);
1303
- } catch (error) {
1287
+ static isTestMode() {
1288
+ var _a, _b, _c;
1289
+ return process.env["NODE_ENV"] === "test" || ((_a = process.argv[0]) == null ? void 0 : _a.includes("vitest")) || ((_b = process.argv[1]) == null ? void 0 : _b.includes("vitest")) || ((_c = process.argv[1]) == null ? void 0 : _c.includes("tinypool"));
1290
+ }
1291
+ }
1292
+ class DxtGenerator {
1293
+ constructor(argParserInstance) {
1294
+ this.argParserInstance = argParserInstance;
1295
+ }
1296
+ /**
1297
+ * Helper method to handle exit logic based on autoExit setting
1298
+ */
1299
+ _handleExit(exitCode, message, type2, data2) {
1300
+ const result = {
1301
+ success: exitCode === 0,
1302
+ exitCode,
1303
+ message,
1304
+ type: type2 || (exitCode === 0 ? "success" : "error"),
1305
+ shouldExit: true,
1306
+ data: data2
1307
+ };
1308
+ if (this.argParserInstance.getAutoExit() && typeof process === "object" && typeof process.exit === "function") {
1309
+ process.exit(exitCode);
1304
1310
  }
1305
- console.log(simpleChalk.green(` ✓ Generated DXT package folder: ${folderName}`));
1306
- console.log(simpleChalk.gray(` Server: ${serverInfo.name} v${serverInfo.version}`));
1307
- console.log(simpleChalk.gray(` Tools: ${tools.length} tool(s)`));
1308
- console.log(simpleChalk.gray(` Location: ${buildDir}`));
1309
- console.log(simpleChalk.cyan(`
1310
- 📦 Creating DXT package using Anthropic's dxt pack...`));
1311
- console.log(simpleChalk.cyan(`
1312
- 📋 Manual steps to create your DXT package:`));
1313
- console.log(simpleChalk.white(` cd ${path.relative(process.cwd(), buildDir)}`));
1314
- console.log(simpleChalk.white(` ./build-dxt.sh`));
1311
+ return result;
1315
1312
  }
1316
1313
  /**
1317
- * Reads package.json to extract fallback information for DXT manifest
1314
+ * Handles the --s-build-dxt system flag to generate DXT packages for MCP servers
1318
1315
  */
1319
- readPackageJsonInfo() {
1316
+ async handleBuildDxtFlag(processArgs, buildDxtIndex) {
1320
1317
  try {
1321
- const packageJsonPath = path.join(process.cwd(), "package.json");
1322
- if (fs.existsSync(packageJsonPath)) {
1323
- const packageContent = fs.readFileSync(packageJsonPath, "utf-8");
1324
- const packageData = JSON.parse(packageContent);
1325
- return {
1326
- author: packageData.author,
1327
- repository: packageData.repository,
1328
- license: packageData.license,
1329
- homepage: packageData.homepage
1330
- };
1318
+ if (DxtGeneratorTestUtils.isTestMode()) {
1319
+ const testUtils = new DxtGeneratorTestUtils(
1320
+ this.argParserInstance,
1321
+ () => this.extractMcpServerInfo(),
1322
+ (exitCode, message, type2, data2) => this._handleExit(exitCode, message, type2, data2)
1323
+ );
1324
+ return await testUtils.handleTestModeDxtGeneration(
1325
+ processArgs,
1326
+ buildDxtIndex
1327
+ );
1328
+ }
1329
+ const withNodeModules = processArgs.includes("--s-with-node-modules");
1330
+ if (withNodeModules) {
1331
+ console.log(
1332
+ simpleChalk.yellow(
1333
+ "🗂️ --s-with-node-modules detected: will include node_modules in bundle"
1334
+ )
1335
+ );
1336
+ const nodeModulesPath = path.resolve("./node_modules");
1337
+ if (!fs.existsSync(nodeModulesPath)) {
1338
+ console.error(
1339
+ simpleChalk.red(
1340
+ "❌ Error: node_modules directory not found. Please run the installation command first."
1341
+ )
1342
+ );
1343
+ console.log(
1344
+ simpleChalk.cyan(
1345
+ "💡 Required command: pnpm install --prod --node-linker=hoisted"
1346
+ )
1347
+ );
1348
+ return this._handleExit(
1349
+ 1,
1350
+ "node_modules directory not found",
1351
+ "error"
1352
+ );
1353
+ }
1354
+ try {
1355
+ const nodeModulesContents = fs.readdirSync(nodeModulesPath);
1356
+ const hasNestedNodeModules = nodeModulesContents.filter((item) => !item.startsWith(".") && !item.startsWith("@")).some((item) => {
1357
+ const itemPath = path.join(nodeModulesPath, item);
1358
+ try {
1359
+ return fs.statSync(itemPath).isDirectory() && fs.existsSync(path.join(itemPath, "node_modules"));
1360
+ } catch {
1361
+ return false;
1362
+ }
1363
+ });
1364
+ if (hasNestedNodeModules) {
1365
+ console.warn(
1366
+ simpleChalk.yellow(
1367
+ "⚠️ Warning: Detected nested node_modules. For best results, ensure hoisted installation:"
1368
+ )
1369
+ );
1370
+ console.log(
1371
+ simpleChalk.cyan(
1372
+ " rm -rf node_modules && pnpm install --prod --node-linker=hoisted"
1373
+ )
1374
+ );
1375
+ } else {
1376
+ console.log(
1377
+ simpleChalk.green(
1378
+ "✅ node_modules appears properly hoisted and ready for bundling"
1379
+ )
1380
+ );
1381
+ }
1382
+ } catch (error) {
1383
+ console.warn(
1384
+ simpleChalk.yellow(
1385
+ `⚠️ Could not validate node_modules structure: ${error instanceof Error ? error.message : String(error)}`
1386
+ )
1387
+ );
1388
+ }
1389
+ console.log(
1390
+ simpleChalk.gray(
1391
+ "💡 This will create a fully autonomous DXT with all native dependencies included"
1392
+ )
1393
+ );
1331
1394
  }
1395
+ const entryPointFile = process.argv[1];
1396
+ if (!entryPointFile || !fs.existsSync(entryPointFile)) {
1397
+ console.error(
1398
+ simpleChalk.red(`Error: Entry point file not found: ${entryPointFile}`)
1399
+ );
1400
+ return this._handleExit(1, "Entry point file not found", "error");
1401
+ }
1402
+ let outputDir = processArgs[buildDxtIndex + 1] || "./dxt";
1403
+ if (outputDir.startsWith("--s-")) outputDir = "./dxt";
1404
+ console.log(
1405
+ simpleChalk.cyan(
1406
+ `
1407
+ 🔧 Building DXT package for entry point: ${entryPointFile}`
1408
+ )
1409
+ );
1410
+ console.log(simpleChalk.gray(`Output directory: ${outputDir}`));
1411
+ console.log(simpleChalk.gray(`Entrypoint file: ${entryPointFile}`));
1412
+ await this.buildDxtWithTsdown(entryPointFile, outputDir, withNodeModules);
1413
+ console.log(simpleChalk.green(`
1414
+ ✅ DXT package generation completed!`));
1415
+ return this._handleExit(
1416
+ 0,
1417
+ "DXT package generation completed",
1418
+ "success",
1419
+ { entryPoint: entryPointFile, outputDir }
1420
+ );
1332
1421
  } catch (error) {
1422
+ console.error(
1423
+ simpleChalk.red(
1424
+ `Error generating DXT package: ${error instanceof Error ? error.message : String(error)}`
1425
+ )
1426
+ );
1427
+ return this._handleExit(
1428
+ 1,
1429
+ `Error generating DXT package: ${error instanceof Error ? error.message : String(error)}`,
1430
+ "error"
1431
+ );
1333
1432
  }
1334
- return null;
1335
1433
  }
1336
1434
  /**
1337
1435
  * Extracts server information from MCP configuration
@@ -1370,7 +1468,9 @@ echo "Mock DXT build script for ${serverInfo.name}"`;
1370
1468
  toolOptions = mcpConfig.toolOptions;
1371
1469
  }
1372
1470
  }
1373
- const mcpTools = this.argParserInstance.toMcpTools(toolOptions);
1471
+ const mcpTools = this.argParserInstance.toMcpTools(
1472
+ toolOptions
1473
+ );
1374
1474
  return mcpTools.map((tool) => ({
1375
1475
  name: tool.name,
1376
1476
  description: tool.description
@@ -1394,511 +1494,29 @@ echo "Mock DXT build script for ${serverInfo.name}"`;
1394
1494
  });
1395
1495
  }
1396
1496
  }
1397
- return tools.length > 0 ? tools : [{
1398
- name: "main",
1399
- description: "Main command tool"
1400
- }];
1401
- } catch (error) {
1402
- console.warn(simpleChalk.yellow(`Warning: Could not generate detailed tool list: ${error instanceof Error ? error.message : String(error)}`));
1403
- return [{
1404
- name: "main",
1405
- description: "Main command tool"
1406
- }];
1407
- }
1408
- }
1409
- createDxtManifest(serverInfo, tools, mcpSubCommand, logoFilename) {
1410
- var _a;
1411
- const packageInfo = this.readPackageJsonInfo();
1412
- let author = serverInfo.author;
1413
- if (!author && (packageInfo == null ? void 0 : packageInfo.author)) {
1414
- if (typeof packageInfo.author === "string") {
1415
- const match = packageInfo.author.match(/^([^<]+?)(?:\s*<([^>]+)>)?$/);
1416
- if (match) {
1417
- author = {
1418
- name: match[1].trim(),
1419
- email: (_a = match[2]) == null ? void 0 : _a.trim()
1420
- };
1421
- } else {
1422
- author = { name: packageInfo.author };
1423
- }
1424
- } else {
1425
- author = packageInfo.author;
1426
- }
1427
- }
1428
- if (!author) {
1429
- throw new Error("DXT manifest requires author information. Please provide it via withMcp() serverInfo.author, addMcpSubCommand serverInfo.author, or in package.json");
1430
- }
1431
- const cliArgs = this.generateCliArgsForDxt(mcpSubCommand);
1432
- const { envVars, userConfig } = this.generateEnvAndUserConfig();
1433
- const manifest = {
1434
- dxt_version: "0.1",
1435
- name: serverInfo.name,
1436
- version: serverInfo.version,
1437
- description: serverInfo.description || "MCP server generated from ArgParser",
1438
- author,
1439
- server: {
1440
- type: "node",
1441
- entry_point: "server/index.mjs",
1442
- mcp_config: {
1443
- command: "node",
1444
- args: ["${__dirname}/server/index.mjs", ...cliArgs],
1445
- env: envVars
1446
- }
1447
- },
1448
- tools: tools.map((tool) => ({
1449
- name: tool.name,
1450
- description: tool.description
1451
- }))
1452
- };
1453
- if (logoFilename) {
1454
- manifest.icon = logoFilename;
1455
- }
1456
- if (userConfig && Object.keys(userConfig).length > 0) {
1457
- manifest.user_config = userConfig;
1458
- }
1459
- if (serverInfo.repository || (packageInfo == null ? void 0 : packageInfo.repository)) {
1460
- manifest.repository = serverInfo.repository || (packageInfo == null ? void 0 : packageInfo.repository);
1461
- }
1462
- if (serverInfo.license || (packageInfo == null ? void 0 : packageInfo.license)) {
1463
- manifest.license = serverInfo.license || (packageInfo == null ? void 0 : packageInfo.license);
1464
- }
1465
- if (serverInfo.homepage || (packageInfo == null ? void 0 : packageInfo.homepage)) {
1466
- manifest.homepage = serverInfo.homepage || (packageInfo == null ? void 0 : packageInfo.homepage);
1467
- }
1468
- return manifest;
1469
- }
1470
- validateDxtManifest(manifest) {
1471
- const errors = [];
1472
- if (!manifest.dxt_version) errors.push("Missing required field: dxt_version");
1473
- if (!manifest.name) errors.push("Missing required field: name");
1474
- if (!manifest.version) errors.push("Missing required field: version");
1475
- if (!manifest.server) errors.push("Missing required field: server");
1476
- if (!manifest.author) errors.push("Missing required field: author");
1477
- if (manifest.server) {
1478
- if (!manifest.server.type) errors.push("Missing required field: server.type");
1479
- if (!manifest.server.entry_point) errors.push("Missing required field: server.entry_point");
1480
- if (!manifest.server.mcp_config) errors.push("Missing required field: server.mcp_config");
1481
- if (manifest.server.mcp_config) {
1482
- if (!manifest.server.mcp_config.command) errors.push("Missing required field: server.mcp_config.command");
1483
- if (!manifest.server.mcp_config.args || !Array.isArray(manifest.server.mcp_config.args)) {
1484
- errors.push("Missing or invalid field: server.mcp_config.args (must be array)");
1497
+ return tools.length > 0 ? tools : [
1498
+ {
1499
+ name: "main",
1500
+ description: "Main command tool"
1485
1501
  }
1486
- }
1487
- }
1488
- if (manifest.author && typeof manifest.author === "object") {
1489
- if (!manifest.author.name) errors.push("Missing required field: author.name");
1490
- }
1491
- if (manifest.dxt_version && manifest.dxt_version !== "0.1") {
1492
- errors.push("Unsupported dxt_version: only '0.1' is currently supported");
1493
- }
1494
- if (errors.length > 0) {
1495
- throw new Error(`DXT manifest validation failed:
1496
- ${errors.map((e) => ` - ${e}`).join("\n")}`);
1497
- }
1498
- }
1499
- createServerScript(serverInfo, bundledCliPath) {
1500
- const cliImportPath = bundledCliPath || "original-cli.mjs";
1501
- return `#!/usr/bin/env node
1502
-
1503
- // Generated MCP server for ${serverInfo.name}
1504
- // This server uses @alcyone-labs/arg-parser's built-in MCP functionality for full protocol compliance
1505
-
1506
- // FIRST: Set up MCP-safe logging to prevent STDOUT contamination
1507
- import { createMcpLogger } from '@alcyone-labs/simple-mcp-logger';
1508
-
1509
- // Auto-detect MCP mode and hijack console to prevent protocol corruption
1510
- const mcpLogger = createMcpLogger('${serverInfo.name}');
1511
- globalThis.console = mcpLogger;
1512
-
1513
- // Now import the CLI which already has MCP functionality configured
1514
- import originalCli from './${cliImportPath}';
1515
-
1516
- // Server configuration
1517
- const serverInfo = ${JSON.stringify(serverInfo, null, 2)};
1518
-
1519
- // Use mcpError for debugging output (safe STDERR, visible in client logs)
1520
- console.error(\`MCP Server: \${serverInfo.name} v\${serverInfo.version}\`);
1521
- console.error(\`Description: \${serverInfo.description}\`);
1522
- console.error(\`Generated from @alcyone-labs/arg-parser with built-in MCP functionality\`);
1523
- ${bundledCliPath ? "console.error(`Using bundled CLI for autonomous execution`);" : ""}
1524
-
1525
- // The original CLI has MCP functionality configured via withMcp() or addMcpSubCommand()
1526
- // We use the centralized --s-mcp-serve system flag to start the unified MCP server
1527
-
1528
- // Start the MCP server using the library's built-in centralized serving
1529
- // This works with both withMcp() configuration and legacy addMcpSubCommand() setups
1530
- originalCli.parse(['--s-mcp-serve']);
1531
- `;
1532
- }
1533
- createDxtPackageJson(serverInfo) {
1534
- const useLocalBuild = process.env["LOCAL_BUILD"] === "1";
1535
- const argParserDependency = useLocalBuild ? "file:../../arg-parser-local.tgz" : "^1.3.0";
1536
- let originalDependencies = {};
1537
- try {
1538
- const originalPackageJsonPath = path.join(process.cwd(), "package.json");
1539
- if (fs.existsSync(originalPackageJsonPath)) {
1540
- const originalPackageJson = JSON.parse(fs.readFileSync(originalPackageJsonPath, "utf8"));
1541
- originalDependencies = originalPackageJson.dependencies || {};
1542
- }
1502
+ ];
1543
1503
  } catch (error) {
1544
- console.warn("⚠ Could not read original package.json for dependencies:", error instanceof Error ? error.message : String(error));
1545
- }
1546
- const dependencies2 = {
1547
- ...originalDependencies,
1548
- "@alcyone-labs/arg-parser": argParserDependency,
1549
- "@alcyone-labs/simple-mcp-logger": "^1.0.0",
1550
- "@modelcontextprotocol/sdk": "^1.15.0",
1551
- "zod": "^3.22.4"
1552
- };
1553
- const devDependencies = {
1554
- "tsup": "^8.3.5"
1555
- };
1556
- Object.keys(dependencies2).forEach((key) => {
1557
- const depValue = dependencies2[key];
1558
- if (key !== "@alcyone-labs/arg-parser" && typeof depValue === "string" && depValue.startsWith("file:")) {
1559
- delete dependencies2[key];
1560
- console.warn(`⚠ Removed file: dependency ${key} from DXT package (not suitable for distribution)`);
1561
- }
1562
- });
1563
- return {
1564
- name: serverInfo.name,
1565
- version: serverInfo.version,
1566
- description: serverInfo.description,
1567
- main: "server/index.mjs",
1568
- type: "module",
1569
- scripts: {
1570
- start: "node server/index.mjs",
1571
- "build-dxt": "./build-dxt.sh"
1572
- },
1573
- dependencies: dependencies2,
1574
- devDependencies,
1575
- engines: {
1576
- node: ">=22.0.0"
1577
- },
1578
- author: serverInfo.author,
1579
- license: serverInfo.license || "MIT",
1580
- repository: serverInfo.repository
1581
- };
1582
- }
1583
- /**
1584
- * Creates a .dxtignore file to exclude build artifacts and unnecessary files
1585
- */
1586
- createDxtIgnore() {
1587
- return `# DXT ignore file - exclude these files from the DXT package
1588
- # Generated by @alcyone-labs/arg-parser
1589
-
1590
- # Build artifacts and logs
1591
- *.log
1592
- *.tmp
1593
- temp-dxt-build/
1594
-
1595
- # Build scripts (not needed in final package)
1596
- build-dxt.sh
1597
- arg-parser-local.tgz
1598
- tsup.config.autonomous.js
1599
- tsdown.config.mjs
1600
-
1601
- # Original files (replaced by bundled autonomous build)
1602
- server/index.original.mjs
1603
- server/*.autonomous.mjs
1604
-
1605
- # NOTE: server/original-cli.mjs is NOT excluded because it's needed for the MCP server to function
1606
- # The bundled version (if created) will be server/original-cli.bundled.mjs
1607
-
1608
- # NOTE: node_modules/ is NOT excluded because TSDown bundling may not be 100% autonomous
1609
- # If bundling is successful, node_modules won't be needed, but we include it as fallback
1610
- # The bundled server/index.mjs should be fully autonomous and not require node_modules
1611
-
1612
- # Development files
1613
- .git/
1614
- .gitignore
1615
- .env
1616
- .env.*
1617
-
1618
- # OS files
1619
- .DS_Store
1620
- Thumbs.db
1621
-
1622
- # IDE files
1623
- .vscode/
1624
- .idea/
1625
- *.swp
1626
- *.swo
1627
- `;
1628
- }
1629
- /**
1630
- * Creates a simple build script that uses TSDown bundling and Anthropic's dxt pack
1631
- */
1632
- createSimpleBuildScript(serverInfo) {
1633
- return `#!/bin/bash
1634
-
1635
- # Simple DXT Build Script for ${serverInfo.name}
1636
- # Generated by @alcyone-labs/arg-parser with TSDown bundling
1637
-
1638
- set -e
1639
-
1640
- echo "📦 Creating DXT package for ${serverInfo.name}..."
1641
-
1642
- # Step 1: Make server executable (required for MCP)
1643
- echo "🔧 Making server executable..."
1644
- chmod +x server/index.mjs
1645
-
1646
- # Step 2: Handle local development dependencies
1647
- if grep -q "file:.*arg-parser-local.tgz" package.json; then
1648
- echo "🔧 Checking for local package tarball..."
1649
-
1650
- # Check if the tarball exists in the parent directory
1651
- if [ -f "../../arg-parser-local.tgz" ]; then
1652
- echo "✅ Found local package tarball: ../../arg-parser-local.tgz"
1653
- else
1654
- echo "⚠️ Local tarball not found, falling back to npm registry"
1655
- echo "💡 To use local build, run: cd /path/to/arg-parser && npm pack && cp *.tgz examples/community/canny-cli/"
1656
-
1657
- # Replace with npm version
1658
- sed -i.bak 's|"file:.*arg-parser-local.tgz"|"^1.3.0"|g' package.json 2>/dev/null || \\
1659
- sed -i 's|"file:.*arg-parser-local.tgz"|"^1.3.0"|g' package.json
1660
- fi
1661
- fi
1662
-
1663
- # Step 3: Install dependencies (for runtime only, bundling was done during generation)
1664
- echo "📦 Installing dependencies..."
1665
- npm install
1666
-
1667
- # Step 4: Validate manifest
1668
- echo "🔍 Validating DXT manifest..."
1669
- if command -v npx >/dev/null 2>&1; then
1670
- if npx @anthropic-ai/dxt validate manifest.json; then
1671
- echo "✅ DXT manifest validation passed"
1672
- else
1673
- echo "❌ DXT manifest validation failed"
1674
- exit 1
1675
- fi
1676
- else
1677
- echo "⚠️ npx not found, skipping DXT validation"
1678
- fi
1679
-
1680
- # Step 5: Create DXT package using Anthropic's official packer
1681
- echo "📦 Creating DXT package..."
1682
- if command -v npx >/dev/null 2>&1; then
1683
- # Use dxt pack directly with .dxtignore for clean packaging
1684
- npx @anthropic-ai/dxt pack . "${serverInfo.name}.dxt"
1685
- else
1686
- # Fallback to standard zip if npx not available
1687
- echo "⚠️ npx not found, using zip fallback"
1688
- zip -r "${serverInfo.name}.dxt" . -x "node_modules/*" "*.log" ".git/*" "build-dxt.sh" "temp-dxt-build/*"
1689
- fi
1690
-
1691
- # Step 6: Sign the DXT package (optional)
1692
- echo "🔐 Signing DXT package..."
1693
- if command -v npx >/dev/null 2>&1 && command -v openssl >/dev/null 2>&1; then
1694
- if npx @anthropic-ai/dxt sign "${serverInfo.name}.dxt" --self-signed; then
1695
- echo "✅ DXT package signed successfully"
1696
- else
1697
- echo "⚠️ DXT signing failed, but package is still usable"
1698
- fi
1699
- else
1700
- echo "⚠️ npx or openssl not found, skipping DXT signing"
1701
- fi
1702
-
1703
- echo "✅ DXT package created: ${serverInfo.name}.dxt"
1704
- echo "🎯 This package includes bundled CLI with all dependencies!"
1705
- echo ""
1706
- echo "🎉 Installation Instructions:"
1707
- echo "You can now take the file '${serverInfo.name}.dxt' and install it on Claude Desktop"
1708
- echo "or supporting applications by using drag & drop on the Extensions Settings page,"
1709
- echo "or directly pointing the file selector to this file."
1710
- echo ""
1711
- echo "📁 DXT file location: $(pwd)/${serverInfo.name}.dxt"
1712
- `;
1713
- }
1714
- createDxtReadme(serverInfo) {
1715
- return `# ${serverInfo.name}
1716
-
1717
- ${serverInfo.description}
1718
-
1719
- ## Installation
1720
-
1721
- This is a Desktop Extension (DXT) package generated from @alcyone-labs/arg-parser.
1722
-
1723
- ### Automatic Installation
1724
- Open this .dxt file with Claude Desktop or other DXT-compatible applications for single-click installation.
1725
-
1726
- ### Manual Installation
1727
- 1. Extract the .dxt file (it's a ZIP archive)
1728
- 2. Run \`npm install\` to install dependencies
1729
- 3. Start the server with \`npm start\`
1730
-
1731
- ## Tools
1732
-
1733
- This MCP server provides the following tools:
1734
- ${this.generateMcpToolsForDxt().map((tool) => `- **${tool.name}**: ${tool.description}`).join("\n")}
1735
-
1736
- ## Building DXT Packages
1737
-
1738
- To rebuild the DXT package:
1739
-
1740
- ### Prerequisites
1741
- - Node.js 18+ installed
1742
- - npm package manager
1743
-
1744
- ### Build Steps
1745
-
1746
- \`\`\`bash
1747
- # 1. Install dependencies
1748
- npm install
1749
-
1750
- # 2. Build DXT package
1751
- npm run build-dxt
1752
- # or
1753
- ./build-dxt.sh
1754
-
1755
- # 3. The build script will:
1756
- # - Install dependencies
1757
- # - Validate the manifest
1758
- # - Create the DXT package using Anthropic's official packer
1759
- # - Sign the package (optional)
1760
- \`\`\`
1761
-
1762
- ### Manual Build Process
1763
-
1764
- If the automated build script doesn't work, you can build manually:
1765
-
1766
- \`\`\`bash
1767
- # 1. Install dependencies
1768
- npm install
1769
-
1770
- # 2. Create DXT package
1771
- npx @anthropic-ai/dxt pack . ${serverInfo.name}.dxt
1772
-
1773
- # 2. Update manifest.json
1774
- # Change "entry_point" from "server/index.js" to "dist-autonomous/server.cjs"
1775
-
1776
- # 3. Create new DXT with bundled server
1777
- # Replace server/index.js with dist-autonomous/server.cjs
1778
- # Remove package.json dependencies (optional)
1779
- \`\`\`
1780
-
1781
- ### Result
1782
- The resulting DXT package will be completely autonomous and won't require \`npm install\`.
1783
-
1784
- ## Generated Information
1785
-
1786
- - **Generator**: @alcyone-labs/arg-parser v1.3.0
1787
- - **Generated**: ${(/* @__PURE__ */ new Date()).toISOString()}
1788
- - **DXT Version**: 0.1
1789
-
1790
- ## Note
1791
-
1792
- This is a simplified DXT package. For full functionality and the latest features, use the original CLI directly.
1793
- For autonomous packages, follow the build instructions above.
1794
- `;
1795
- }
1796
- /**
1797
- * Maps ArgParser flag types to DXT user config types
1798
- */
1799
- mapFlagTypeToUserConfigType(flagType) {
1800
- if (typeof flagType === "function") {
1801
- if (flagType === String) return "string";
1802
- if (flagType === Number) return "number";
1803
- if (flagType === Boolean) return "boolean";
1804
- if (flagType === Array) return "array";
1805
- if (flagType === Object) return "object";
1806
- return "string";
1807
- }
1808
- switch (String(flagType).toLowerCase()) {
1809
- case "string":
1810
- return "string";
1811
- case "number":
1812
- return "number";
1813
- case "boolean":
1814
- return "boolean";
1815
- case "table":
1816
- return "array";
1817
- case "array":
1818
- return "array";
1819
- case "object":
1820
- return "object";
1821
- default:
1822
- return "string";
1823
- }
1824
- }
1825
- /**
1826
- * Generates CLI arguments for DXT manifest based on ArgParser flags
1827
- */
1828
- generateCliArgsForDxt(_mcpSubCommand) {
1829
- const args = [];
1830
- args.push("--s-mcp-serve");
1831
- return args;
1832
- }
1833
- /**
1834
- * Generates environment variables and user config for DXT manifest
1835
- */
1836
- generateEnvAndUserConfig() {
1837
- const envVars = {};
1838
- const userConfig = {};
1839
- const flags = this.argParserInstance.flags || [];
1840
- for (const flag of flags) {
1841
- const flagName = flag["name"];
1842
- if (flagName === "help" || flagName === "mcp") continue;
1843
- if (flag["env"]) {
1844
- const envVarName = flag["env"];
1845
- envVars[envVarName] = `\${user_config.${envVarName}}`;
1846
- userConfig[envVarName] = {
1847
- type: this.mapFlagTypeToUserConfigType(flag["type"]),
1848
- title: this.generateUserConfigTitle(envVarName),
1849
- description: flag["description"] || `${envVarName} environment variable`,
1850
- required: true,
1851
- // Always require env vars in user_config for better UX
1852
- sensitive: this.isSensitiveField(envVarName)
1853
- };
1854
- }
1855
- }
1856
- if (typeof this.argParserInstance.getTools === "function") {
1857
- const tools = this.argParserInstance.getTools();
1858
- for (const [, toolConfig] of tools) {
1859
- const toolFlags = toolConfig.flags || [];
1860
- for (const flag of toolFlags) {
1861
- const flagName = flag["name"];
1862
- if (flagName === "help" || flagName.startsWith("s-")) continue;
1863
- if (flag["env"]) {
1864
- const envVarName = flag["env"];
1865
- if (!envVars[envVarName]) {
1866
- envVars[envVarName] = `\${user_config.${envVarName}}`;
1867
- userConfig[envVarName] = {
1868
- type: this.mapFlagTypeToUserConfigType(flag["type"]),
1869
- title: this.generateUserConfigTitle(envVarName),
1870
- description: flag["description"] || `${envVarName} environment variable`,
1871
- required: true,
1872
- // Always require env vars in user_config for better UX
1873
- sensitive: this.isSensitiveField(envVarName)
1874
- };
1875
- }
1876
- }
1504
+ console.warn(
1505
+ simpleChalk.yellow(
1506
+ `Warning: Could not generate detailed tool list: ${error instanceof Error ? error.message : String(error)}`
1507
+ )
1508
+ );
1509
+ return [
1510
+ {
1511
+ name: "main",
1512
+ description: "Main command tool"
1877
1513
  }
1878
- }
1514
+ ];
1879
1515
  }
1880
- return { envVars, userConfig };
1881
- }
1882
- /**
1883
- * Generates a user-friendly title for user config fields
1884
- */
1885
- generateUserConfigTitle(flagName) {
1886
- return flagName.split(/[-_]/).map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()).join(" ");
1887
1516
  }
1888
1517
  /**
1889
- * Checks if a field should be marked as sensitive in user config
1518
+ * Maps ArgParser flag types to DXT user config types
1890
1519
  */
1891
- isSensitiveField(fieldName) {
1892
- const sensitivePatterns = [
1893
- /key/i,
1894
- /token/i,
1895
- /secret/i,
1896
- /password/i,
1897
- /auth/i,
1898
- /credential/i
1899
- ];
1900
- return sensitivePatterns.some((pattern2) => pattern2.test(fieldName));
1901
- }
1902
1520
  /**
1903
1521
  * Adds the logo to the build folder if available
1904
1522
  * @returns The filename of the logo that was added, or undefined if no logo was added
@@ -1918,27 +1536,38 @@ For autonomous packages, follow the build instructions above.
1918
1536
  const urlPath = new URL(customLogo).pathname;
1919
1537
  const urlFilename = path.basename(urlPath);
1920
1538
  if (urlFilename && urlFilename.includes(".")) {
1921
- logoFilename = urlFilename;
1539
+ const ext = path.extname(urlFilename);
1540
+ logoFilename = `logo${ext}`;
1541
+ } else {
1542
+ logoFilename = "logo.jpg";
1922
1543
  }
1923
1544
  console.log("✓ Downloaded logo from URL");
1924
1545
  } else {
1925
- console.warn(`⚠ Failed to download logo: HTTP ${response.status}`);
1546
+ console.warn(
1547
+ `⚠ Failed to download logo: HTTP ${response.status}`
1548
+ );
1926
1549
  }
1927
1550
  } catch (error) {
1928
- console.warn("⚠ Failed to download logo from URL:", error instanceof Error ? error.message : String(error));
1551
+ console.warn(
1552
+ "⚠ Failed to download logo from URL:",
1553
+ error instanceof Error ? error.message : String(error)
1554
+ );
1929
1555
  }
1930
1556
  } else {
1931
1557
  let logoPath;
1932
1558
  if (entryPointFile && !path.isAbsolute(customLogo)) {
1933
1559
  const entryDir = path.dirname(entryPointFile);
1934
1560
  logoPath = path.resolve(entryDir, customLogo);
1935
- console.log(`📍 Resolving logo path relative to entry point: ${logoPath}`);
1561
+ console.log(
1562
+ `📍 Resolving logo path relative to entry point: ${logoPath}`
1563
+ );
1936
1564
  } else {
1937
1565
  logoPath = path.resolve(customLogo);
1938
1566
  }
1939
1567
  if (fs.existsSync(logoPath)) {
1940
1568
  logoBuffer = fs.readFileSync(logoPath);
1941
- logoFilename = path.basename(logoPath);
1569
+ const ext = path.extname(logoPath);
1570
+ logoFilename = `logo${ext}`;
1942
1571
  console.log("✓ Added custom logo from local file");
1943
1572
  } else {
1944
1573
  console.warn(`⚠ Custom logo file not found: ${logoPath}`);
@@ -1949,17 +1578,52 @@ For autonomous packages, follow the build instructions above.
1949
1578
  const currentDir = path.dirname(new URL(import.meta.url).pathname);
1950
1579
  let logoPath = path.join(currentDir, "assets", "logo_1_small.jpg");
1951
1580
  if (!fs.existsSync(logoPath)) {
1952
- logoPath = path.join(currentDir, "..", "docs", "MCP", "icons", "logo_1_small.jpg");
1581
+ logoPath = path.join(
1582
+ currentDir,
1583
+ "..",
1584
+ "docs",
1585
+ "MCP",
1586
+ "icons",
1587
+ "logo_1_small.jpg"
1588
+ );
1589
+ }
1590
+ if (!fs.existsSync(logoPath)) {
1591
+ logoPath = path.join(
1592
+ process.cwd(),
1593
+ "docs",
1594
+ "MCP",
1595
+ "icons",
1596
+ "logo_1_small.jpg"
1597
+ );
1953
1598
  }
1954
1599
  if (!fs.existsSync(logoPath)) {
1955
- logoPath = path.join(process.cwd(), "docs", "MCP", "icons", "logo_1_small.jpg");
1600
+ logoPath = path.join(
1601
+ process.cwd(),
1602
+ "node_modules",
1603
+ "@alcyone-labs",
1604
+ "arg-parser",
1605
+ "dist",
1606
+ "assets",
1607
+ "logo_1_small.jpg"
1608
+ );
1609
+ }
1610
+ if (!fs.existsSync(logoPath)) {
1611
+ logoPath = path.join(
1612
+ process.cwd(),
1613
+ "dist",
1614
+ "assets",
1615
+ "logo_1_small.jpg"
1616
+ );
1956
1617
  }
1957
1618
  if (fs.existsSync(logoPath)) {
1958
1619
  logoBuffer = fs.readFileSync(logoPath);
1959
- logoFilename = "logo.jpg";
1620
+ const ext = path.extname(logoPath);
1621
+ logoFilename = `logo${ext}`;
1960
1622
  console.log("✓ Added default logo to build folder");
1961
1623
  } else {
1962
- console.warn("⚠ No logo found (custom or default), build folder will be created without icon");
1624
+ console.warn(
1625
+ "⚠ No logo found (custom or default), build folder will be created without icon"
1626
+ );
1963
1627
  return void 0;
1964
1628
  }
1965
1629
  }
@@ -1969,260 +1633,89 @@ For autonomous packages, follow the build instructions above.
1969
1633
  }
1970
1634
  return void 0;
1971
1635
  } catch (error) {
1972
- console.warn("⚠ Failed to add logo to build folder:", error instanceof Error ? error.message : String(error));
1636
+ console.warn(
1637
+ "⚠ Failed to add logo to build folder:",
1638
+ error instanceof Error ? error.message : String(error)
1639
+ );
1973
1640
  return void 0;
1974
1641
  }
1975
1642
  }
1976
- /**
1977
- * Processes CLI source code to replace global console with MCP-compliant Logger
1978
- */
1979
- processCliSourceForMcp(cliSource) {
1980
- const consoleReplacement = `import { createMcpLogger } from '@alcyone-labs/arg-parser';
1981
-
1982
- // Replace global console with MCP-compliant logger for DXT packages
1983
- const mcpLogger = createMcpLogger('[CLI]');
1984
- const originalConsole = globalThis.console;
1985
- globalThis.console = {
1986
- ...originalConsole,
1987
- log: (...args) => mcpLogger.info(...args),
1988
- info: (...args) => mcpLogger.info(...args),
1989
- warn: (...args) => mcpLogger.warn(...args),
1990
- debug: (...args) => mcpLogger.debug(...args),
1991
- // Keep error/trace/etc as-is since they use stderr (MCP-compliant)
1992
- error: originalConsole.error,
1993
- trace: originalConsole.trace,
1994
- assert: originalConsole.assert,
1995
- clear: originalConsole.clear,
1996
- count: originalConsole.count,
1997
- countReset: originalConsole.countReset,
1998
- dir: originalConsole.dir,
1999
- dirxml: originalConsole.dirxml,
2000
- group: originalConsole.group,
2001
- groupCollapsed: originalConsole.groupCollapsed,
2002
- groupEnd: originalConsole.groupEnd,
2003
- table: originalConsole.table,
2004
- time: originalConsole.time,
2005
- timeEnd: originalConsole.timeEnd,
2006
- timeLog: originalConsole.timeLog,
2007
- timeStamp: originalConsole.timeStamp,
2008
- };
2009
-
2010
- `;
2011
- const lines = cliSource.split("\n");
2012
- let lastImportIndex = -1;
2013
- for (let i = 0; i < lines.length; i++) {
2014
- const line = lines[i].trim();
2015
- if (line.startsWith("import ") && line.includes("from")) {
2016
- lastImportIndex = i;
2017
- } else if (line && !line.startsWith("//") && !line.startsWith("/*") && lastImportIndex >= 0) {
2018
- break;
2019
- }
2020
- }
2021
- if (lastImportIndex >= 0) {
2022
- lines.splice(lastImportIndex + 1, 0, "", ...consoleReplacement.trim().split("\n"));
2023
- return lines.join("\n");
2024
- } else {
2025
- return consoleReplacement + cliSource;
2026
- }
2027
- }
2028
- /**
2029
- * Adds the original CLI source to the build folder for handler execution
2030
- */
2031
- addOriginalCliToFolder(buildDir) {
2032
- try {
2033
- const appCommandName = this.argParserInstance.getAppCommandName();
2034
- const appName = this.argParserInstance.getAppName();
2035
- const possibleCliFiles = [
2036
- // Current working directory common patterns
2037
- path.join(process.cwd(), "index.js"),
2038
- path.join(process.cwd(), "index.mjs"),
2039
- path.join(process.cwd(), "cli.js"),
2040
- path.join(process.cwd(), "cli.mjs"),
2041
- path.join(process.cwd(), "main.js"),
2042
- path.join(process.cwd(), "main.mjs"),
2043
- // Look for files with the app command name
2044
- path.join(process.cwd(), `${appCommandName}.js`),
2045
- path.join(process.cwd(), `${appCommandName}.mjs`),
2046
- // Look for files with the app command name (sanitized)
2047
- path.join(process.cwd(), `${appCommandName.replace(/[^a-zA-Z0-9-]/g, "-")}.js`),
2048
- path.join(process.cwd(), `${appCommandName.replace(/[^a-zA-Z0-9-]/g, "-")}.mjs`),
2049
- // Look for files with app name patterns
2050
- path.join(process.cwd(), `${appName.toLowerCase().replace(/\s+/g, "-")}-cli.js`),
2051
- path.join(process.cwd(), `${appName.toLowerCase().replace(/\s+/g, "-")}-cli.mjs`),
2052
- // Look for files with first word of app name + cli
2053
- path.join(process.cwd(), `${appName.split(" ")[0].toLowerCase()}-cli.js`),
2054
- path.join(process.cwd(), `${appName.split(" ")[0].toLowerCase()}-cli.mjs`)
2055
- ];
2056
- let cliSourcePath = null;
2057
- for (const filePath of possibleCliFiles) {
2058
- if (fs.existsSync(filePath)) {
2059
- cliSourcePath = filePath;
2060
- break;
2061
- }
2062
- }
2063
- if (cliSourcePath) {
2064
- let cliSource = fs.readFileSync(cliSourcePath, "utf8");
2065
- cliSource = cliSource.replace(
2066
- /import\s*{\s*([^}]+)\s*}\s*from\s*['"][^'"]*\/dist\/index\.mjs['"];?/g,
2067
- "import { $1 } from '@alcyone-labs/arg-parser';"
2068
- );
2069
- cliSource = cliSource.replace(
2070
- /import\s+(\w+)\s+from\s*['"][^'"]*\/dist\/index\.mjs['"];?/g,
2071
- "import $1 from '@alcyone-labs/arg-parser';"
2072
- );
2073
- cliSource = this.processCliSourceForMcp(cliSource);
2074
- const parserVariableMatch = cliSource.match(/const\s+(\w+)\s*=\s*ArgParser\.withMcp\(/);
2075
- if (parserVariableMatch) {
2076
- const parserVariable = parserVariableMatch[1];
2077
- cliSource += `
2078
-
2079
- // Export the parser instance for MCP server use
2080
- export default ${parserVariable};
2081
-
2082
- // Add debugging for main execution
2083
- console.error('[MCP-DEBUG] CLI source loaded, checking execution context...');
2084
- console.error('[MCP-DEBUG] import.meta.url:', import.meta.url);
2085
- console.error('[MCP-DEBUG] process.argv[1]:', process.argv[1]);
2086
-
2087
- // Ensure MCP server processes don't exit prematurely
2088
- console.error('[MCP-DEBUG] Process argv:', process.argv);
2089
- console.error('[MCP-DEBUG] Checking for serve command...');
2090
-
2091
- if (process.argv.includes('serve')) {
2092
- console.error('[MCP-DEBUG] Detected serve command, setting up MCP server lifecycle...');
2093
-
2094
- // Override the original parse method to handle async MCP server
2095
- const originalParse = ${parserVariable}.parse;
2096
- ${parserVariable}.parse = async function(args) {
2097
- console.error('[MCP-DEBUG] Starting parse with args:', args);
2098
-
2099
- try {
2100
- const result = originalParse.call(this, args);
2101
- console.error('[MCP-DEBUG] Parse result:', typeof result, result?.constructor?.name);
2102
-
2103
- // If result is a Promise (MCP server), await it and keep process alive
2104
- if (result && typeof result.then === 'function') {
2105
- console.error('[MCP-DEBUG] Detected Promise result, awaiting...');
2106
- const mcpResult = await result;
2107
- console.error('[MCP-DEBUG] MCP server started, keeping process alive...');
2108
-
2109
- // Keep the process alive indefinitely for MCP server
2110
- const keepAlive = setInterval(() => {
2111
- // Do nothing, just keep the event loop alive
2112
- }, 30000);
2113
-
2114
- // Handle graceful shutdown
2115
- process.on('SIGINT', () => {
2116
- console.error('[MCP-INFO] Received SIGINT, shutting down gracefully...');
2117
- clearInterval(keepAlive);
2118
- process.exit(0);
2119
- });
2120
-
2121
- process.on('SIGTERM', () => {
2122
- console.error('[MCP-INFO] Received SIGTERM, shutting down gracefully...');
2123
- clearInterval(keepAlive);
2124
- process.exit(0);
2125
- });
2126
-
2127
- return mcpResult;
2128
- } else {
2129
- console.error('[MCP-DEBUG] Non-Promise result, returning normally');
2130
- return result;
2131
- }
2132
- } catch (error) {
2133
- console.error('[MCP-ERROR] Error in parse:', error);
2134
- throw error;
2135
- }
2136
- };
2137
- }
2138
- `;
2139
- } else {
2140
- console.warn("⚠ Could not find ArgParser instance in CLI source, MCP server may not work properly");
2141
- }
2142
- const serverDir = path.join(buildDir, "server");
2143
- if (!fs.existsSync(serverDir)) {
2144
- fs.mkdirSync(serverDir, { recursive: true });
2145
- }
2146
- fs.writeFileSync(path.join(serverDir, "original-cli.mjs"), cliSource);
2147
- console.log(`✓ Added original CLI source to build folder: ${path.basename(cliSourcePath)}`);
2148
- } else {
2149
- console.warn("⚠ Original CLI source not found, handlers may not work properly");
2150
- console.warn(" Searched for:", possibleCliFiles.map((f) => path.basename(f)).join(", "));
2151
- }
2152
- } catch (error) {
2153
- console.warn("⚠ Failed to add original CLI source:", error instanceof Error ? error.message : String(error));
2154
- }
2155
- }
2156
1643
  /**
2157
1644
  * Builds a complete DXT package using TSDown CLI for autonomous execution
2158
1645
  */
2159
- async buildDxtWithTsdown(entryPointFile, outputDir = "./dxt") {
1646
+ async buildDxtWithTsdown(entryPointFile, outputDir = "./dxt", withNodeModules = false) {
2160
1647
  try {
2161
1648
  console.log(simpleChalk.cyan("🔧 Building DXT package with TSDown..."));
2162
- const entryDir = path.dirname(entryPointFile);
1649
+ const projectRoot = this.findProjectRoot(entryPointFile);
1650
+ const absoluteEntryPath = path.resolve(entryPointFile);
1651
+ const relativeEntryPath = path.relative(projectRoot, absoluteEntryPath);
2163
1652
  const entryFileName = path.basename(entryPointFile);
2164
1653
  console.log(simpleChalk.gray(`Entry point: ${entryPointFile}`));
2165
- console.log(simpleChalk.gray(`Working directory: ${entryDir}`));
1654
+ console.log(simpleChalk.gray(`Project root: ${projectRoot}`));
1655
+ console.log(simpleChalk.gray(`Relative entry path: ${relativeEntryPath}`));
2166
1656
  const dxtIgnorePath = this.getDxtIgnoreTemplatePath();
2167
1657
  if (fs.existsSync(dxtIgnorePath)) {
2168
- fs.copyFileSync(dxtIgnorePath, path.join(entryDir, ".dxtignore"));
1658
+ fs.copyFileSync(dxtIgnorePath, path.join(projectRoot, ".dxtignore"));
2169
1659
  }
1660
+ const serverInfo = this.extractMcpServerInfo();
1661
+ const logoFilename = await this.addLogoToFolder(
1662
+ projectRoot,
1663
+ serverInfo,
1664
+ entryPointFile
1665
+ );
1666
+ console.log(
1667
+ logoFilename ? simpleChalk.gray(`✓ Logo prepared: ${logoFilename}`) : simpleChalk.gray("⚠ No logo available")
1668
+ );
2170
1669
  const originalCwd = process.cwd();
2171
1670
  try {
2172
- process.chdir(entryDir);
1671
+ process.chdir(projectRoot);
2173
1672
  const { build } = await import("tsdown");
2174
- console.log(simpleChalk.gray(`Building with TSDown: ${entryFileName}`));
1673
+ console.log(simpleChalk.gray(`Building with TSDown: ${relativeEntryPath}`));
1674
+ console.log(
1675
+ simpleChalk.green(
1676
+ `${withNodeModules ? "with node_modules" : "without node_modules"}`
1677
+ )
1678
+ );
2175
1679
  const buildConfig = {
2176
- entry: [entryFileName],
2177
- outDir: path.resolve(process.cwd(), outputDir),
2178
- format: ["esm"],
1680
+ entry: [relativeEntryPath],
1681
+ outDir: path.resolve(originalCwd, outputDir),
1682
+ format: ["es"],
2179
1683
  target: "node22",
2180
- noExternal: () => true,
1684
+ define: {
1685
+ // Define any compile-time constants
1686
+ NODE_ENV: '"production"'
1687
+ },
2181
1688
  minify: false,
2182
1689
  sourcemap: false,
2183
- clean: false,
1690
+ // Remove all output folders and artefacts
1691
+ clean: [outputDir, "./.dxtignore", `${outputDir}.dxt`],
2184
1692
  silent: process.env["NO_SILENCE"] !== "1",
2185
- copy: [
2186
- // Copy logo from assets - try multiple possible locations
2187
- ...(() => {
2188
- const possibleLogoPaths = [
2189
- // From built library assets
2190
- path.join(path.dirname(new URL(import.meta.url).pathname), "..", "assets", "logo_1_small.jpg"),
2191
- // From node_modules
2192
- path.join(process.cwd(), "node_modules", "@alcyone-labs", "arg-parser", "dist", "assets", "logo_1_small.jpg"),
2193
- // From package root dist/assets (for local build)
2194
- path.join(process.cwd(), "dist", "assets", "logo_1_small.jpg"),
2195
- // From library root (development)
2196
- path.join(process.cwd(), "..", "..", "..", "docs", "MCP", "icons", "logo_1_small.jpg")
2197
- ];
2198
- for (const logoPath of possibleLogoPaths) {
2199
- if (fs.existsSync(logoPath)) {
2200
- console.log(simpleChalk.gray(`Found logo at: ${logoPath}`));
2201
- return [{ from: logoPath, to: "logo.jpg" }];
2202
- }
1693
+ external: (_, importer) => withNodeModules ? importer == null ? void 0 : importer.includes("node_modules") : false,
1694
+ noExternal: (_, importer) => withNodeModules ? (importer == null ? void 0 : importer.includes("node_modules")) === false : true,
1695
+ copy: async (options) => {
1696
+ const outputPaths = [
1697
+ "package.json"
1698
+ ];
1699
+ if (withNodeModules) {
1700
+ console.log(
1701
+ simpleChalk.gray(
1702
+ "📦 Including node_modules in bundle (may take longer)..."
1703
+ )
1704
+ );
1705
+ outputPaths.push("node_modules");
1706
+ }
1707
+ if (logoFilename) {
1708
+ const logoPath = path.join(process.cwd(), logoFilename);
1709
+ if (fs.existsSync(logoPath)) {
1710
+ console.log(simpleChalk.gray(`Adding logo from: ${logoPath}`));
1711
+ outputPaths.push({
1712
+ from: logoPath,
1713
+ to: path.join(options.outDir, logoFilename)
1714
+ });
2203
1715
  }
2204
- console.log(simpleChalk.yellow("⚠ Logo not found in any expected location"));
2205
- return [];
2206
- })()
2207
- ],
2208
- external: [
2209
- // Node.js built-ins only - everything else should be bundled for true autonomy
2210
- "stream",
2211
- "fs",
2212
- "path",
2213
- "url",
2214
- "util",
2215
- "events",
2216
- "child_process",
2217
- "os",
2218
- "tty",
2219
- "process",
2220
- "crypto",
2221
- "http",
2222
- "https",
2223
- "net",
2224
- "zlib"
2225
- ],
1716
+ }
1717
+ return outputPaths;
1718
+ },
2226
1719
  platform: "node",
2227
1720
  plugins: []
2228
1721
  };
@@ -2241,262 +1734,40 @@ export default ${JSON.stringify(buildConfig, null, 2)};
2241
1734
  // To run manually:
2242
1735
  // npx tsdown -c tsdown.config.dxt.ts
2243
1736
  `;
2244
- fs.writeFileSync(path.join("dxt", "tsdown.config.dxt.ts"), configContent);
2245
- console.log(simpleChalk.gray("📝 Debug config written to dxt/tsdown.config.dxt.ts"));
1737
+ fs.writeFileSync(
1738
+ path.join("dxt", "tsdown.config.dxt.ts"),
1739
+ configContent
1740
+ );
1741
+ console.log(
1742
+ simpleChalk.gray("📝 Debug config written to dxt/tsdown.config.dxt.ts")
1743
+ );
2246
1744
  }
2247
1745
  await build(buildConfig);
2248
1746
  console.log(simpleChalk.green("✅ TSDown bundling completed"));
2249
- await this.copyLogoManually(outputDir);
2250
- await this.setupDxtPackageFiles(entryPointFile, outputDir);
2251
- console.log(simpleChalk.cyan("📦 DXT package ready for packing"));
2252
- console.log(simpleChalk.gray(`To complete the process, run: npx @anthropic-ai/dxt pack ${outputDir}/`));
2253
- } finally {
2254
- process.chdir(originalCwd);
2255
- }
2256
- } catch (error) {
2257
- throw new Error(`TSDown DXT build failed: ${error instanceof Error ? error.message : String(error)}`);
2258
- }
2259
- }
2260
- /**
2261
- * Bundles the original CLI using TSDown for autonomous execution (legacy method)
2262
- */
2263
- async bundleOriginalCliWithTsdown(serverDir) {
2264
- try {
2265
- const { build } = await import("tsdown");
2266
- console.log(simpleChalk.cyan("🔧 Bundling CLI with TSDown for autonomous execution..."));
2267
- const configContent = this.getTsdownConfigContent();
2268
- const localConfigPath = path.join(serverDir, "tsdown.config.mjs");
2269
- fs.writeFileSync(localConfigPath, configContent);
2270
- const originalCliPath = path.join(serverDir, "original-cli.mjs");
2271
- if (!fs.existsSync(originalCliPath)) {
2272
- console.warn(simpleChalk.yellow("⚠ Original CLI not found, skipping TSDown bundling"));
2273
- return null;
2274
- }
2275
- const buildOptions = {
2276
- entry: ["original-cli.mjs"],
2277
- // Use relative path since we'll chdir to serverDir
2278
- outDir: ".",
2279
- // Output to current directory (serverDir)
2280
- format: "esm",
2281
- target: "node22",
2282
- // Bundle EVERYTHING except Node.js built-ins for true autonomy
2283
- noExternal: (id) => {
2284
- if (!id.startsWith("node:") && !this.isNodeBuiltin(id)) return true;
2285
- return false;
2286
- },
2287
- minify: false,
2288
- sourcemap: false,
2289
- clean: false,
2290
- outExtension: () => ({ js: ".bundled.mjs" }),
2291
- alias: {
2292
- // Alias chalk to SimpleChalk for autonomous builds
2293
- chalk: path.resolve(process.cwd(), "node_modules/@alcyone-labs/arg-parser/dist/SimpleChalk.mjs")
2294
- },
2295
- external: [
2296
- // Only Node.js built-ins - everything else gets bundled for true autonomy
2297
- "node:stream",
2298
- "node:fs",
2299
- "node:path",
2300
- "node:url",
2301
- "node:util",
2302
- "node:events",
2303
- "node:child_process",
2304
- "node:os",
2305
- "node:tty",
2306
- "node:process",
2307
- "node:crypto",
2308
- "node:http",
2309
- "node:https",
2310
- "node:net",
2311
- "node:zlib",
2312
- "node:fs/promises",
2313
- "node:timers",
2314
- "stream",
2315
- "fs",
2316
- "path",
2317
- "url",
2318
- "util",
2319
- "events",
2320
- "child_process",
2321
- "os",
2322
- "tty",
2323
- "process",
2324
- "crypto",
2325
- "http",
2326
- "https",
2327
- "net",
2328
- "zlib",
2329
- "fs/promises",
2330
- "timers",
2331
- "timers/promises",
2332
- "perf_hooks",
2333
- "async_hooks",
2334
- "inspector",
2335
- "v8",
2336
- "vm",
2337
- "assert",
2338
- "constants",
2339
- "module",
2340
- "repl",
2341
- "string_decoder",
2342
- "punycode",
2343
- "domain",
2344
- "querystring",
2345
- "readline",
2346
- "worker_threads",
2347
- "cluster",
2348
- "dgram",
2349
- "dns",
2350
- "buffer"
2351
- ],
2352
- platform: "node",
2353
- plugins: [],
2354
- // Resolve local dependencies properly
2355
- resolve: {
2356
- alias: {
2357
- // Handle local monorepo dependencies
2358
- "@alcyone-labs/arg-parser": path.resolve(process.cwd())
2359
- }
2360
- }
2361
- };
2362
- const originalCwd = process.cwd();
2363
- try {
2364
- process.chdir(serverDir);
2365
- await build(buildOptions);
1747
+ const detectedOutputFile = this.detectTsdownOutputFile(
1748
+ outputDir,
1749
+ entryFileName
1750
+ );
1751
+ await this.setupDxtPackageFiles(
1752
+ entryPointFile,
1753
+ outputDir,
1754
+ detectedOutputFile ?? void 0,
1755
+ logoFilename ?? "logo.jpg"
1756
+ );
1757
+ console.log(simpleChalk.cyan("📦 DXT package ready for packing"));
1758
+ console.log(
1759
+ simpleChalk.gray(
1760
+ `To complete the process, run: npx @anthropic-ai/dxt pack ${outputDir}/`
1761
+ )
1762
+ );
2366
1763
  } finally {
2367
1764
  process.chdir(originalCwd);
2368
1765
  }
2369
- const possibleBundledFiles = [
2370
- "original-cli.bundled.mjs",
2371
- "original-cli.js",
2372
- "original-cli.mjs"
2373
- ];
2374
- let bundledPath = null;
2375
- let bundledFileName = null;
2376
- for (const fileName of possibleBundledFiles) {
2377
- const filePath = path.join(serverDir, fileName);
2378
- if (fs.existsSync(filePath) && fileName !== "original-cli.mjs") {
2379
- bundledPath = filePath;
2380
- bundledFileName = fileName;
2381
- break;
2382
- }
2383
- }
2384
- if (bundledPath && bundledFileName) {
2385
- console.log(simpleChalk.green(`✅ TSDown bundling completed successfully: ${bundledFileName}`));
2386
- const expectedBundledPath = path.join(serverDir, "original-cli.bundled.mjs");
2387
- if (bundledPath !== expectedBundledPath) {
2388
- fs.renameSync(bundledPath, expectedBundledPath);
2389
- bundledFileName = "original-cli.bundled.mjs";
2390
- }
2391
- try {
2392
- fs.unlinkSync(localConfigPath);
2393
- } catch (error) {
2394
- }
2395
- try {
2396
- fs.chmodSync(expectedBundledPath, 493);
2397
- } catch (error) {
2398
- console.warn("⚠ Could not set executable permission on bundled file:", error instanceof Error ? error.message : String(error));
2399
- }
2400
- return bundledFileName;
2401
- } else {
2402
- console.warn(simpleChalk.yellow("⚠ TSDown bundling failed, bundled file not found"));
2403
- return null;
2404
- }
2405
1766
  } catch (error) {
2406
- console.warn(simpleChalk.yellow(`⚠ TSDown bundling failed: ${error instanceof Error ? error.message : String(error)}`));
2407
- console.log(simpleChalk.gray(" Falling back to non-bundled approach"));
2408
- return null;
2409
- }
2410
- }
2411
- /**
2412
- * Checks if a module ID is a Node.js built-in
2413
- */
2414
- isNodeBuiltin(id) {
2415
- const nodeBuiltins = [
2416
- "stream",
2417
- "fs",
2418
- "path",
2419
- "url",
2420
- "util",
2421
- "events",
2422
- "child_process",
2423
- "os",
2424
- "tty",
2425
- "process",
2426
- "crypto",
2427
- "http",
2428
- "https",
2429
- "net",
2430
- "zlib",
2431
- "fs/promises",
2432
- "timers",
2433
- "timers/promises",
2434
- "perf_hooks",
2435
- "async_hooks",
2436
- "inspector",
2437
- "v8",
2438
- "vm",
2439
- "assert",
2440
- "constants",
2441
- "module",
2442
- "repl",
2443
- "string_decoder",
2444
- "punycode",
2445
- "domain",
2446
- "querystring",
2447
- "readline",
2448
- "worker_threads",
2449
- "cluster",
2450
- "dgram",
2451
- "dns",
2452
- "buffer"
2453
- ];
2454
- return nodeBuiltins.includes(id) || id.startsWith("node:");
2455
- }
2456
- /**
2457
- * Gets the TSDown configuration content as a string
2458
- */
2459
- getTsdownConfigContent() {
2460
- const currentDir = path.dirname(new URL(import.meta.url).pathname);
2461
- const assetsConfigPath = path.join(currentDir, "..", "assets", "tsdown.dxt.config.ts");
2462
- if (fs.existsSync(assetsConfigPath)) {
2463
- try {
2464
- const content = fs.readFileSync(assetsConfigPath, "utf-8");
2465
- return content.replace('/// <reference types="tsdown" />', "").replace('import { defineConfig } from "tsdown/config";', 'import { defineConfig } from "tsdown";').replace("export default defineConfig(", "export default defineConfig(");
2466
- } catch (error) {
2467
- console.warn(simpleChalk.yellow("⚠ Could not read TSDown config from assets, using fallback"));
2468
- }
2469
- }
2470
- const rootConfigPath = path.join(process.cwd(), "tsdown.dxt.config.ts");
2471
- if (fs.existsSync(rootConfigPath)) {
2472
- try {
2473
- const content = fs.readFileSync(rootConfigPath, "utf-8");
2474
- return content.replace('/// <reference types="tsdown" />', "").replace('import { defineConfig } from "tsdown/config";', 'import { defineConfig } from "tsdown";');
2475
- } catch (error) {
2476
- console.warn(simpleChalk.yellow("⚠ Could not read TSDown config from root, using default"));
2477
- }
1767
+ throw new Error(
1768
+ `TSDown DXT build failed: ${error instanceof Error ? error.message : String(error)}`
1769
+ );
2478
1770
  }
2479
- return `import { defineConfig } from "tsdown";
2480
- import path from "path";
2481
-
2482
- export default defineConfig({
2483
- outDir: "server",
2484
- format: ["esm", "module"],
2485
- target: "node22",
2486
- noExternal: () => true,
2487
- minify: false,
2488
- sourcemap: false,
2489
- clean: false,
2490
- alias: {
2491
- chalk: path.resolve(process.cwd(), "node_modules/@alcyone-labs/arg-parser/dist/SimpleChalk.mjs"),
2492
- },
2493
- external: [
2494
- "stream", "fs", "path", "url", "util", "events", "child_process",
2495
- "os", "tty", "process", "crypto", "http", "https", "net", "zlib",
2496
- ],
2497
- platform: "node",
2498
- plugins: [],
2499
- });`;
2500
1771
  }
2501
1772
  /**
2502
1773
  * Gets the path to the .dxtignore template file in assets
@@ -2504,9 +1775,22 @@ export default defineConfig({
2504
1775
  getDxtIgnoreTemplatePath() {
2505
1776
  const possiblePaths = [
2506
1777
  // 1. From the built library assets (when installed via npm)
2507
- path.join(path.dirname(new URL(import.meta.url).pathname), "..", "assets", ".dxtignore.template"),
1778
+ path.join(
1779
+ path.dirname(new URL(import.meta.url).pathname),
1780
+ "..",
1781
+ "assets",
1782
+ ".dxtignore.template"
1783
+ ),
2508
1784
  // 2. From node_modules/@alcyone-labs/arg-parser/dist/assets (when installed via npm)
2509
- path.join(process.cwd(), "node_modules", "@alcyone-labs", "arg-parser", "dist", "assets", ".dxtignore.template"),
1785
+ path.join(
1786
+ process.cwd(),
1787
+ "node_modules",
1788
+ "@alcyone-labs",
1789
+ "arg-parser",
1790
+ "dist",
1791
+ "assets",
1792
+ ".dxtignore.template"
1793
+ ),
2510
1794
  // 3. From the root directory (development/local build)
2511
1795
  path.join(process.cwd(), ".dxtignore.template"),
2512
1796
  // 4. From the library root (when using local file dependency)
@@ -2524,8 +1808,8 @@ export default defineConfig({
2524
1808
  /**
2525
1809
  * Sets up DXT package files (manifest.json) in the output directory
2526
1810
  */
2527
- async setupDxtPackageFiles(entryPointFile, outputDir = "./dxt") {
2528
- var _a, _b, _c, _d, _e;
1811
+ async setupDxtPackageFiles(entryPointFile, outputDir = "./dxt", actualOutputFilename, logoFilename = "logo.jpg") {
1812
+ var _a, _b, _c, _d, _e, _f;
2529
1813
  const dxtDir = path.resolve(process.cwd(), outputDir);
2530
1814
  if (!fs.existsSync(dxtDir)) {
2531
1815
  throw new Error(`TSDown output directory (${outputDir}) not found`);
@@ -2547,11 +1831,15 @@ export default defineConfig({
2547
1831
  description: tool.description
2548
1832
  }));
2549
1833
  } catch (error) {
2550
- console.warn(simpleChalk.yellow(`Warning: Could not generate unified tool list: ${error instanceof Error ? error.message : String(error)}`));
2551
- const mainFlags2 = this.argParserInstance.flags;
1834
+ console.warn(
1835
+ simpleChalk.yellow(
1836
+ `Warning: Could not generate unified tool list: ${error instanceof Error ? error.message : String(error)}`
1837
+ )
1838
+ );
1839
+ const mainFlags = this.argParserInstance.flags;
2552
1840
  const properties2 = {};
2553
1841
  const required2 = [];
2554
- for (const flag of mainFlags2) {
1842
+ for (const flag of mainFlags) {
2555
1843
  if (flag.name === "help" || flag.name.startsWith("s-")) continue;
2556
1844
  properties2[flag.name] = {
2557
1845
  type: getJsonSchemaTypeFromFlag(flag.type),
@@ -2568,59 +1856,24 @@ export default defineConfig({
2568
1856
  }
2569
1857
  }
2570
1858
  const commandName = this.argParserInstance.getAppCommandName();
2571
- tools = [{
2572
- name: commandName || packageInfo.name || "cli-tool",
2573
- description: packageInfo.description || this.argParserInstance.getDescription() || "CLI tool"
2574
- }];
2575
- }
2576
- const envVars = {};
2577
- const userConfig = {};
2578
- const mainFlags = this.argParserInstance.flags;
2579
- for (const flag of mainFlags) {
2580
- const envVar = flag.env || flag.envVar;
2581
- if (envVar) {
2582
- envVars[envVar] = `\${user_config.${envVar}}`;
2583
- userConfig[envVar] = {
2584
- type: "string",
2585
- title: envVar.replace(/_/g, " ").replace(/\b\w/g, (l) => l.toUpperCase()),
2586
- description: flag.description || `${envVar} environment variable`,
2587
- required: true,
2588
- // Always require env vars in user_config for better UX
2589
- sensitive: true
2590
- // Assume env vars are sensitive
2591
- };
2592
- }
2593
- }
2594
- if (typeof this.argParserInstance.getTools === "function") {
2595
- const tools2 = this.argParserInstance.getTools();
2596
- for (const [, toolConfig] of tools2) {
2597
- const toolFlags = toolConfig.flags || [];
2598
- for (const flag of toolFlags) {
2599
- const envVar = flag.env || flag.envVar;
2600
- if (envVar && !envVars[envVar]) {
2601
- envVars[envVar] = `\${user_config.${envVar}}`;
2602
- userConfig[envVar] = {
2603
- type: "string",
2604
- title: envVar.replace(/_/g, " ").replace(/\b\w/g, (l) => l.toUpperCase()),
2605
- description: flag.description || `${envVar} environment variable`,
2606
- required: true,
2607
- // Always require env vars in user_config for better UX
2608
- sensitive: true
2609
- // Assume env vars are sensitive
2610
- };
2611
- }
1859
+ tools = [
1860
+ {
1861
+ name: commandName || packageInfo.name || "cli-tool",
1862
+ description: packageInfo.description || this.argParserInstance.getDescription() || "CLI tool"
2612
1863
  }
2613
- }
1864
+ ];
2614
1865
  }
1866
+ const { envVars, userConfig } = this.generateEnvAndUserConfig();
2615
1867
  const serverInfo = this.extractMcpServerInfo();
2616
- let logoFilename = "logo.jpg";
2617
- if (serverInfo == null ? void 0 : serverInfo.logo) {
2618
- const customLogoFilename = await this.addLogoToFolder(dxtDir, serverInfo, entryPointFile);
2619
- if (customLogoFilename) {
2620
- logoFilename = customLogoFilename;
2621
- }
1868
+ let entryFileName;
1869
+ if (actualOutputFilename) {
1870
+ entryFileName = actualOutputFilename;
1871
+ } else {
1872
+ const projectRoot = this.findProjectRoot(entryPointFile);
1873
+ const absoluteEntryPath = path.resolve(entryPointFile);
1874
+ const relativeEntryPath = path.relative(projectRoot, absoluteEntryPath);
1875
+ entryFileName = relativeEntryPath.replace(/\.ts$/, ".js");
2622
1876
  }
2623
- const entryFileName = path.basename(entryPointFile);
2624
1877
  const manifest = {
2625
1878
  dxt_version: "0.1",
2626
1879
  name: serverInfo.name || packageInfo.name || "mcp-server",
@@ -2638,7 +1891,10 @@ export default defineConfig({
2638
1891
  command: "node",
2639
1892
  args: [
2640
1893
  `\${__dirname}/${entryFileName}`,
2641
- "--s-mcp-serve"
1894
+ "--s-mcp-serve",
1895
+ // Overwrite the CLI config to only use stdio to avoid conflicts
1896
+ "--s-mcp-transport",
1897
+ "stdio"
2642
1898
  ],
2643
1899
  env: envVars
2644
1900
  }
@@ -2646,46 +1902,147 @@ export default defineConfig({
2646
1902
  tools,
2647
1903
  icon: logoFilename,
2648
1904
  ...Object.keys(userConfig).length > 0 && { user_config: userConfig },
2649
- repository: {
1905
+ repository: ((_e = packageInfo.repository) == null ? void 0 : _e.url) ? {
2650
1906
  type: "git",
2651
- url: ((_e = packageInfo.repository) == null ? void 0 : _e.url) || "https://github.com/alcyone-labs/arg-parser"
2652
- },
1907
+ url: (_f = packageInfo.repository) == null ? void 0 : _f.url
1908
+ } : void 0,
2653
1909
  license: packageInfo.license || "MIT"
2654
1910
  };
2655
- fs.writeFileSync(path.join(dxtDir, "manifest.json"), JSON.stringify(manifest, null, 2));
1911
+ fs.writeFileSync(
1912
+ path.join(dxtDir, "manifest.json"),
1913
+ JSON.stringify(manifest, null, 2)
1914
+ );
2656
1915
  console.log(simpleChalk.gray("✅ DXT package files set up"));
2657
1916
  }
2658
1917
  /**
2659
- * Manually copy logo since TSDown's copy option doesn't work programmatically
1918
+ * Detects the actual output filename generated by TSDown
2660
1919
  */
2661
- async copyLogoManually(outputDir = "./dxt") {
2662
- const dxtDir = path.resolve(process.cwd(), outputDir);
2663
- if (!fs.existsSync(dxtDir)) {
2664
- console.warn(simpleChalk.yellow(`⚠ Output directory (${outputDir}) not found, skipping logo copy`));
2665
- return;
1920
+ detectTsdownOutputFile(outputDir, expectedBaseName) {
1921
+ try {
1922
+ const dxtDir = path.resolve(process.cwd(), outputDir);
1923
+ if (!fs.existsSync(dxtDir)) {
1924
+ console.warn(
1925
+ simpleChalk.yellow(`⚠ Output directory (${outputDir}) not found`)
1926
+ );
1927
+ return null;
1928
+ }
1929
+ const files = fs.readdirSync(dxtDir).filter(
1930
+ (file) => (file.endsWith(".js") || file.endsWith(".mjs")) && !file.includes("chunk-") && !file.includes("dist-") && !file.startsWith(".")
1931
+ );
1932
+ const baseNameWithoutExt = path.parse(expectedBaseName).name;
1933
+ for (const ext of [".js", ".mjs"]) {
1934
+ const exactMatch = `${baseNameWithoutExt}${ext}`;
1935
+ if (files.includes(exactMatch)) {
1936
+ console.log(simpleChalk.gray(`✓ Detected TSDown output: ${exactMatch}`));
1937
+ return exactMatch;
1938
+ }
1939
+ }
1940
+ const mainFiles = files.filter(
1941
+ (file) => !file.includes("chunk") && !file.includes("dist") && file !== "logo.jpg" && file !== "manifest.json"
1942
+ );
1943
+ if (mainFiles.length === 1) {
1944
+ console.log(simpleChalk.gray(`✓ Detected TSDown output: ${mainFiles[0]}`));
1945
+ return mainFiles[0];
1946
+ }
1947
+ if (mainFiles.length > 1) {
1948
+ let bestMatch = mainFiles[0];
1949
+ let bestScore = 0;
1950
+ for (const file of mainFiles) {
1951
+ const filePath = path.join(dxtDir, file);
1952
+ const stats = fs.statSync(filePath);
1953
+ const nameScore = file.includes(baseNameWithoutExt) ? 100 : 0;
1954
+ const sizeScore = Math.min(stats.size / 1e3, 50);
1955
+ const totalScore = nameScore + sizeScore;
1956
+ if (totalScore > bestScore) {
1957
+ bestScore = totalScore;
1958
+ bestMatch = file;
1959
+ }
1960
+ }
1961
+ console.log(
1962
+ simpleChalk.gray(
1963
+ `✓ Detected TSDown output: ${bestMatch} (best match from ${mainFiles.length} candidates)`
1964
+ )
1965
+ );
1966
+ return bestMatch;
1967
+ }
1968
+ console.warn(
1969
+ simpleChalk.yellow(`⚠ Could not detect TSDown output file in ${outputDir}`)
1970
+ );
1971
+ return null;
1972
+ } catch (error) {
1973
+ console.warn(
1974
+ simpleChalk.yellow(
1975
+ `⚠ Error detecting TSDown output: ${error instanceof Error ? error.message : String(error)}`
1976
+ )
1977
+ );
1978
+ return null;
2666
1979
  }
2667
- const possibleLogoPaths = [
2668
- // From built library assets
2669
- path.join(path.dirname(new URL(import.meta.url).pathname), "..", "assets", "logo_1_small.jpg"),
2670
- // From node_modules
2671
- path.join(process.cwd(), "node_modules", "@alcyone-labs", "arg-parser", "dist", "assets", "logo_1_small.jpg"),
2672
- // From package root dist/assets (for local build)
2673
- path.join(process.cwd(), "dist", "assets", "logo_1_small.jpg"),
2674
- // From library root (development)
2675
- path.join(process.cwd(), "..", "..", "..", "docs", "MCP", "icons", "logo_1_small.jpg")
2676
- ];
2677
- for (const logoPath of possibleLogoPaths) {
2678
- if (fs.existsSync(logoPath)) {
2679
- try {
2680
- fs.copyFileSync(logoPath, path.join(dxtDir, "logo.jpg"));
2681
- console.log(simpleChalk.gray(`✅ Logo copied from: ${logoPath}`));
2682
- return;
2683
- } catch (error) {
2684
- console.warn(simpleChalk.yellow(`⚠ Failed to copy logo from ${logoPath}: ${error}`));
1980
+ }
1981
+ findProjectRoot(entryPointFile) {
1982
+ let currentDir = path.dirname(path.resolve(entryPointFile));
1983
+ let attempts = 0;
1984
+ const maxAttempts = 5;
1985
+ while (attempts < maxAttempts) {
1986
+ const packageJsonPath = path.join(currentDir, "package.json");
1987
+ if (fs.existsSync(packageJsonPath)) {
1988
+ return currentDir;
1989
+ }
1990
+ const parentDir = path.dirname(currentDir);
1991
+ if (parentDir === currentDir) {
1992
+ break;
1993
+ }
1994
+ currentDir = parentDir;
1995
+ attempts++;
1996
+ }
1997
+ throw new Error(
1998
+ `Could not find package.json within ${maxAttempts} directories up from ${entryPointFile}. Please ensure your entry point is within a project that has a package.json file.`
1999
+ );
2000
+ }
2001
+ /**
2002
+ * Generate environment variables and user configuration from ArgParser flags
2003
+ * @returns Object containing envVars and userConfig
2004
+ */
2005
+ generateEnvAndUserConfig() {
2006
+ const envVars = {};
2007
+ const userConfig = {};
2008
+ const mainFlags = this.argParserInstance.flags;
2009
+ for (const flag of mainFlags) {
2010
+ const envVar = flag.env || flag.envVar;
2011
+ if (envVar) {
2012
+ envVars[envVar] = `\${user_config.${envVar}}`;
2013
+ userConfig[envVar] = {
2014
+ type: "string",
2015
+ title: envVar.replace(/_/g, " ").replace(/\b\w/g, (l) => l.toUpperCase()),
2016
+ description: flag.description || `${envVar} environment variable`,
2017
+ required: true,
2018
+ // Always require env vars in user_config for better UX
2019
+ sensitive: true
2020
+ // Assume env vars are sensitive
2021
+ };
2022
+ }
2023
+ }
2024
+ if (typeof this.argParserInstance.getTools === "function") {
2025
+ const tools = this.argParserInstance.getTools();
2026
+ for (const [, toolConfig] of tools) {
2027
+ const toolFlags = toolConfig.flags || [];
2028
+ for (const flag of toolFlags) {
2029
+ const envVar = flag.env || flag.envVar;
2030
+ if (envVar && !envVars[envVar]) {
2031
+ envVars[envVar] = `\${user_config.${envVar}}`;
2032
+ userConfig[envVar] = {
2033
+ type: "string",
2034
+ title: envVar.replace(/_/g, " ").replace(/\b\w/g, (l) => l.toUpperCase()),
2035
+ description: flag.description || `${envVar} environment variable`,
2036
+ required: true,
2037
+ // Always require env vars in user_config for better UX
2038
+ sensitive: true
2039
+ // Assume env vars are sensitive
2040
+ };
2041
+ }
2685
2042
  }
2686
2043
  }
2687
2044
  }
2688
- console.warn(simpleChalk.yellow("⚠ Logo not found in any expected location"));
2045
+ return { envVars, userConfig };
2689
2046
  }
2690
2047
  }
2691
2048
  class McpNotificationsManager {
@@ -2840,10 +2197,16 @@ class McpNotificationsManager {
2840
2197
  sendNotificationToClient(client, type2) {
2841
2198
  try {
2842
2199
  if (client.connection && typeof client.connection.sendNotification === "function") {
2843
- client.connection.sendNotification(`notifications/${type2}/list_changed`, {});
2200
+ client.connection.sendNotification(
2201
+ `notifications/${type2}/list_changed`,
2202
+ {}
2203
+ );
2844
2204
  }
2845
2205
  } catch (error) {
2846
- console.error(`Error sending notification to client ${client.clientId}:`, error);
2206
+ console.error(
2207
+ `Error sending notification to client ${client.clientId}:`,
2208
+ error
2209
+ );
2847
2210
  this.removeClient(client.clientId);
2848
2211
  }
2849
2212
  }
@@ -2932,7 +2295,9 @@ class McpPromptsManager {
2932
2295
  return await entry.config.handler(validatedArgs);
2933
2296
  } catch (error) {
2934
2297
  if (error instanceof z.ZodError) {
2935
- throw new Error(`Invalid arguments for prompt '${name}': ${error.message}`);
2298
+ throw new Error(
2299
+ `Invalid arguments for prompt '${name}': ${error.message}`
2300
+ );
2936
2301
  }
2937
2302
  throw error;
2938
2303
  }
@@ -3132,7 +2497,9 @@ class McpResourcesManager {
3132
2497
  try {
3133
2498
  new ResourceTemplateParser(config.uriTemplate);
3134
2499
  } catch (error) {
3135
- throw new Error(`Invalid URI template '${config.uriTemplate}': ${error instanceof Error ? error.message : String(error)}`);
2500
+ throw new Error(
2501
+ `Invalid URI template '${config.uriTemplate}': ${error instanceof Error ? error.message : String(error)}`
2502
+ );
3136
2503
  }
3137
2504
  }
3138
2505
  /**
@@ -3237,6 +2604,97 @@ const _FlagManager = class _FlagManager {
3237
2604
  __flags = new WeakMap();
3238
2605
  _throwForDuplicateFlags = new WeakMap();
3239
2606
  let FlagManager = _FlagManager;
2607
+ function detectEntryPoint() {
2608
+ try {
2609
+ if (process.argv[1] && fs.existsSync(process.argv[1])) {
2610
+ return process.argv[1];
2611
+ }
2612
+ if (typeof require !== "undefined" && require.main && require.main.filename) {
2613
+ return require.main.filename;
2614
+ }
2615
+ return null;
2616
+ } catch {
2617
+ return null;
2618
+ }
2619
+ }
2620
+ function getEntryPointFromImportMeta(importMetaUrl) {
2621
+ if (importMetaUrl.startsWith("file://")) {
2622
+ return decodeURIComponent(importMetaUrl.replace("file://", ""));
2623
+ }
2624
+ return importMetaUrl;
2625
+ }
2626
+ function normalizePath(path2) {
2627
+ return path2.trim();
2628
+ }
2629
+ function resolveLogPath(logPath, fallbackEntryPoint) {
2630
+ if (typeof logPath === "string") {
2631
+ const normalizedPath2 = normalizePath(logPath);
2632
+ if (path.isAbsolute(normalizedPath2)) {
2633
+ return normalizedPath2;
2634
+ }
2635
+ if (normalizedPath2.startsWith("cwd:")) {
2636
+ const relativePath = normalizedPath2.slice(4);
2637
+ return path.resolve(process.cwd(), relativePath);
2638
+ }
2639
+ const entryPoint = detectEntryPoint() || fallbackEntryPoint;
2640
+ if (entryPoint) {
2641
+ return path.resolve(path.dirname(entryPoint), normalizedPath2);
2642
+ }
2643
+ console.warn(
2644
+ `Warning: Could not detect entry point for log path resolution. Using process.cwd() as fallback. Path: ${normalizedPath2}`
2645
+ );
2646
+ return path.resolve(process.cwd(), normalizedPath2);
2647
+ }
2648
+ const { path: logFilePath, relativeTo = "entry", basePath } = logPath;
2649
+ const normalizedPath = normalizePath(logFilePath);
2650
+ switch (relativeTo) {
2651
+ case "absolute":
2652
+ if (basePath) {
2653
+ return path.resolve(basePath, normalizedPath);
2654
+ }
2655
+ if (path.isAbsolute(normalizedPath)) {
2656
+ return normalizedPath;
2657
+ }
2658
+ console.warn(
2659
+ `Warning: relativeTo 'absolute' specified but no basePath provided and path is not absolute. Using process.cwd() as fallback. Path: ${normalizedPath}`
2660
+ );
2661
+ return path.resolve(process.cwd(), normalizedPath);
2662
+ case "cwd":
2663
+ return path.resolve(process.cwd(), normalizedPath);
2664
+ case "entry":
2665
+ default:
2666
+ const entryPoint = detectEntryPoint() || fallbackEntryPoint;
2667
+ if (entryPoint) {
2668
+ return path.resolve(path.dirname(entryPoint), normalizedPath);
2669
+ }
2670
+ console.warn(
2671
+ `Warning: Could not detect entry point for log path resolution. Using process.cwd() as fallback. Path: ${normalizedPath}`
2672
+ );
2673
+ return path.resolve(process.cwd(), normalizedPath);
2674
+ }
2675
+ }
2676
+ function entryRelative(path2) {
2677
+ return {
2678
+ path: path2,
2679
+ relativeTo: "entry"
2680
+ };
2681
+ }
2682
+ function cwdRelative(path2) {
2683
+ return {
2684
+ path: path2,
2685
+ relativeTo: "cwd"
2686
+ };
2687
+ }
2688
+ function absolutePath(path2, basePath) {
2689
+ return {
2690
+ path: path2,
2691
+ relativeTo: "absolute",
2692
+ basePath
2693
+ };
2694
+ }
2695
+ function legacyCwdPath(path2) {
2696
+ return `cwd:${path2}`;
2697
+ }
3240
2698
  class ArgParserError extends Error {
3241
2699
  constructor(message, cmdChain = []) {
3242
2700
  super(message);
@@ -4634,15 +4092,14 @@ _handleBuildDxtFlag_fn = async function(processArgs, buildDxtIndex) {
4634
4092
  };
4635
4093
  _handleMcpServeFlag_fn = async function(processArgs, _mcpServeIndex) {
4636
4094
  var _a;
4095
+ const transportOptions = __privateMethod(this, _ArgParserBase_instances, _parseMcpTransportOptions_fn).call(this, processArgs);
4096
+ const mcpServerConfig = __privateMethod(this, _ArgParserBase_instances, _getMcpServerConfiguration_fn).call(this);
4097
+ const effectiveLogPath = transportOptions.logPath || (mcpServerConfig == null ? void 0 : mcpServerConfig.logPath) || "./logs/mcp.log";
4098
+ const resolvedLogPath = resolveLogPath(effectiveLogPath);
4637
4099
  let mcpLogger;
4638
4100
  try {
4639
- const mcpLoggerModule = await Function(
4640
- 'return import("@alcyone-labs/simple-mcp-logger")'
4641
- )();
4642
- mcpLogger = mcpLoggerModule.createMcpLogger(
4643
- "MCP Serve",
4644
- "./logs/mcp.log"
4645
- );
4101
+ const mcpLoggerModule = await import("@alcyone-labs/simple-mcp-logger");
4102
+ mcpLogger = mcpLoggerModule.createMcpLogger("MCP Serve", resolvedLogPath);
4646
4103
  globalThis.console = mcpLogger;
4647
4104
  } catch {
4648
4105
  mcpLogger = {
@@ -4653,7 +4110,6 @@ _handleMcpServeFlag_fn = async function(processArgs, _mcpServeIndex) {
4653
4110
  mcpLogger.mcpError(
4654
4111
  "Starting --s-mcp-serve system flag handler - console hijacked for MCP safety"
4655
4112
  );
4656
- const mcpServerConfig = __privateMethod(this, _ArgParserBase_instances, _getMcpServerConfiguration_fn).call(this);
4657
4113
  if (!mcpServerConfig) {
4658
4114
  mcpLogger.mcpError(
4659
4115
  "No MCP server configuration found. Use withMcp() or addMcpSubCommand() to configure MCP server."
@@ -4667,13 +4123,16 @@ _handleMcpServeFlag_fn = async function(processArgs, _mcpServeIndex) {
4667
4123
  mcpLogger.mcpError(
4668
4124
  `Found MCP server configuration: ${((_a = mcpServerConfig.serverInfo) == null ? void 0 : _a.name) || "unnamed"}`
4669
4125
  );
4670
- const transportOptions = __privateMethod(this, _ArgParserBase_instances, _parseMcpTransportOptions_fn).call(this, processArgs);
4126
+ mcpLogger.mcpError(`Using log path: ${resolvedLogPath}`);
4671
4127
  mcpLogger.mcpError(
4672
4128
  `Transport options: ${JSON.stringify(transportOptions)}`
4673
4129
  );
4674
4130
  try {
4675
4131
  mcpLogger.mcpError("Starting unified MCP server with all tools");
4676
- await __privateMethod(this, _ArgParserBase_instances, _startUnifiedMcpServer_fn).call(this, mcpServerConfig, transportOptions);
4132
+ await __privateMethod(this, _ArgParserBase_instances, _startUnifiedMcpServer_fn).call(this, mcpServerConfig, {
4133
+ ...transportOptions,
4134
+ logPath: resolvedLogPath
4135
+ });
4677
4136
  mcpLogger.mcpError("Successfully started unified MCP server");
4678
4137
  } catch (error) {
4679
4138
  mcpLogger.mcpError(
@@ -4742,18 +4201,34 @@ _startUnifiedMcpServer_fn = async function(mcpServerConfig, transportOptions) {
4742
4201
  await mcpParser.startMcpServerWithMultipleTransports(
4743
4202
  serverInfo,
4744
4203
  transportConfigs,
4745
- toolOptions
4204
+ toolOptions,
4205
+ transportOptions.logPath
4746
4206
  );
4747
4207
  } catch (error) {
4748
4208
  throw new Error(
4749
4209
  `Error parsing transports configuration: ${error.message}. Expected JSON format: '[{"type":"stdio"},{"type":"sse","port":3001}]'`
4750
4210
  );
4751
4211
  }
4212
+ } else if (transportOptions.transportType) {
4213
+ const transportType = transportOptions.transportType;
4214
+ const finalTransportOptions = {
4215
+ port: transportOptions.port,
4216
+ host: transportOptions.host || "localhost",
4217
+ path: transportOptions.path || "/mcp"
4218
+ };
4219
+ await mcpParser.startMcpServerWithTransport(
4220
+ serverInfo,
4221
+ transportType,
4222
+ finalTransportOptions,
4223
+ toolOptions,
4224
+ transportOptions.logPath
4225
+ );
4752
4226
  } else if (defaultTransports && defaultTransports.length > 0) {
4753
4227
  await mcpParser.startMcpServerWithMultipleTransports(
4754
4228
  serverInfo,
4755
4229
  defaultTransports,
4756
- toolOptions
4230
+ toolOptions,
4231
+ transportOptions.logPath
4757
4232
  );
4758
4233
  } else if (defaultTransport) {
4759
4234
  await mcpParser.startMcpServerWithTransport(
@@ -4765,20 +4240,16 @@ _startUnifiedMcpServer_fn = async function(mcpServerConfig, transportOptions) {
4765
4240
  path: defaultTransport.path,
4766
4241
  sessionIdGenerator: defaultTransport.sessionIdGenerator
4767
4242
  },
4768
- toolOptions
4243
+ toolOptions,
4244
+ transportOptions.logPath
4769
4245
  );
4770
4246
  } else {
4771
- const transportType = transportOptions.transportType || "stdio";
4772
- const finalTransportOptions = {
4773
- port: transportOptions.port,
4774
- host: transportOptions.host || "localhost",
4775
- path: transportOptions.path || "/mcp"
4776
- };
4777
4247
  await mcpParser.startMcpServerWithTransport(
4778
4248
  serverInfo,
4779
- transportType,
4780
- finalTransportOptions,
4781
- toolOptions
4249
+ "stdio",
4250
+ {},
4251
+ toolOptions,
4252
+ transportOptions.logPath
4782
4253
  );
4783
4254
  }
4784
4255
  };
@@ -4845,6 +4316,12 @@ _parseMcpTransportOptions_fn = function(processArgs) {
4845
4316
  i++;
4846
4317
  }
4847
4318
  break;
4319
+ case "--s-mcp-log-path":
4320
+ if (nextArg && !nextArg.startsWith("-")) {
4321
+ options.logPath = nextArg;
4322
+ i++;
4323
+ }
4324
+ break;
4848
4325
  // Backward compatibility: support old flags but with deprecation warning
4849
4326
  case "--transport":
4850
4327
  case "--port":
@@ -6130,11 +5607,14 @@ Migration guide: https://github.com/alcyone-labs/arg-parser/blob/main/docs/MCP-M
6130
5607
  * @param toolOptions Optional MCP tool generation options
6131
5608
  * @returns Configured MCP server instance
6132
5609
  */
6133
- async createMcpServer(serverInfo, toolOptions) {
6134
- var _a;
6135
- const logger2 = createMcpLogger("MCP Server Creation", "./logs/mcp.log");
5610
+ async createMcpServer(serverInfo, toolOptions, logPath) {
5611
+ var _a, _b;
5612
+ const resolvedLogPath = resolveLogPath(
5613
+ logPath || ((_a = this._mcpServerConfig) == null ? void 0 : _a.logPath) || "./logs/mcp.log"
5614
+ );
5615
+ const logger2 = createMcpLogger("MCP Server Creation", resolvedLogPath);
6136
5616
  try {
6137
- const effectiveServerInfo = serverInfo || ((_a = this._mcpServerConfig) == null ? void 0 : _a.serverInfo);
5617
+ const effectiveServerInfo = serverInfo || ((_b = this._mcpServerConfig) == null ? void 0 : _b.serverInfo);
6138
5618
  if (!effectiveServerInfo) {
6139
5619
  throw new Error(
6140
5620
  "No MCP server configuration found. Use withMcp() to configure server info or provide serverInfo parameter."
@@ -6315,11 +5795,11 @@ Migration guide: https://github.com/alcyone-labs/arg-parser/blob/main/docs/MCP-M
6315
5795
  * @param toolOptions Optional MCP tool generation options
6316
5796
  * @returns Promise that resolves when all servers are started
6317
5797
  */
6318
- async startMcpServerWithMultipleTransports(serverInfo, transports, toolOptions) {
6319
- const server = await this.createMcpServer(serverInfo, toolOptions);
5798
+ async startMcpServerWithMultipleTransports(serverInfo, transports, toolOptions, logPath) {
5799
+ const server = await this.createMcpServer(serverInfo, toolOptions, logPath);
6320
5800
  const startPromises = [];
6321
5801
  for (const transportConfig of transports) {
6322
- const promise = __privateMethod(this, _ArgParser_instances, _startSingleTransport_fn).call(this, server, serverInfo, transportConfig);
5802
+ const promise = __privateMethod(this, _ArgParser_instances, _startSingleTransport_fn).call(this, server, serverInfo, transportConfig, logPath);
6323
5803
  startPromises.push(promise);
6324
5804
  }
6325
5805
  await Promise.all(startPromises);
@@ -6332,12 +5812,12 @@ Migration guide: https://github.com/alcyone-labs/arg-parser/blob/main/docs/MCP-M
6332
5812
  * @param toolOptions Optional MCP tool generation options
6333
5813
  * @returns Promise that resolves when server is connected
6334
5814
  */
6335
- async startMcpServerWithTransport(serverInfo, transportType, transportOptions = {}, toolOptions) {
6336
- const server = await this.createMcpServer(serverInfo, toolOptions);
5815
+ async startMcpServerWithTransport(serverInfo, transportType, transportOptions = {}, toolOptions, logPath) {
5816
+ const server = await this.createMcpServer(serverInfo, toolOptions, logPath);
6337
5817
  await __privateMethod(this, _ArgParser_instances, _startSingleTransport_fn).call(this, server, serverInfo, {
6338
5818
  type: transportType,
6339
5819
  ...transportOptions
6340
- });
5820
+ }, logPath);
6341
5821
  }
6342
5822
  async parse(processArgs, options) {
6343
5823
  let result = await ArgParserBase.prototype.parse.call(
@@ -6599,8 +6079,9 @@ registerToolAsSubCommand_fn = function(toolConfig) {
6599
6079
  handler: toolConfig.handler
6600
6080
  });
6601
6081
  };
6602
- _startSingleTransport_fn = async function(server, serverInfo, transportConfig) {
6603
- const logger2 = createMcpLogger("MCP Transport", "./logs/mcp.log");
6082
+ _startSingleTransport_fn = async function(server, serverInfo, transportConfig, logPath) {
6083
+ const resolvedLogPath = resolveLogPath(logPath || "./logs/mcp.log");
6084
+ const logger2 = createMcpLogger("MCP Transport", resolvedLogPath);
6604
6085
  try {
6605
6086
  logger2.mcpError(
6606
6087
  `Starting ${transportConfig.type} transport for server: ${serverInfo.name}`
@@ -6776,7 +6257,9 @@ class TomlConfigPlugin extends ConfigPlugin {
6776
6257
  }
6777
6258
  return parsed;
6778
6259
  } catch (error) {
6779
- throw new Error(`Failed to parse TOML: ${error instanceof Error ? error.message : String(error)}`);
6260
+ throw new Error(
6261
+ `Failed to parse TOML: ${error instanceof Error ? error.message : String(error)}`
6262
+ );
6780
6263
  }
6781
6264
  }
6782
6265
  generate(_config, flags, parsedArgs) {
@@ -6816,7 +6299,9 @@ class TomlConfigPlugin extends ConfigPlugin {
6816
6299
  const tomlContent = this.tomlModule.stringify(configWithValues);
6817
6300
  return lines.join("\n") + "\n" + tomlContent;
6818
6301
  } catch (error) {
6819
- throw new Error(`Failed to generate TOML: ${error instanceof Error ? error.message : String(error)}`);
6302
+ throw new Error(
6303
+ `Failed to generate TOML: ${error instanceof Error ? error.message : String(error)}`
6304
+ );
6820
6305
  }
6821
6306
  }
6822
6307
  /**
@@ -6852,7 +6337,10 @@ function createTomlPlugin() {
6852
6337
  try {
6853
6338
  return new TomlConfigPlugin();
6854
6339
  } catch (error) {
6855
- console.warn("TOML plugin not available:", error instanceof Error ? error.message : String(error));
6340
+ console.warn(
6341
+ "TOML plugin not available:",
6342
+ error instanceof Error ? error.message : String(error)
6343
+ );
6856
6344
  return null;
6857
6345
  }
6858
6346
  }
@@ -6868,7 +6356,10 @@ async function createTomlPluginAsync() {
6868
6356
  const tomlModule = await Promise.resolve().then(() => index$1);
6869
6357
  return new TomlConfigPlugin(tomlModule);
6870
6358
  } catch (error) {
6871
- console.warn("TOML plugin not available:", error instanceof Error ? error.message : String(error));
6359
+ console.warn(
6360
+ "TOML plugin not available:",
6361
+ error instanceof Error ? error.message : String(error)
6362
+ );
6872
6363
  return null;
6873
6364
  }
6874
6365
  }
@@ -6914,7 +6405,9 @@ class YamlConfigPlugin extends ConfigPlugin {
6914
6405
  }
6915
6406
  return parsed;
6916
6407
  } catch (error) {
6917
- throw new Error(`Failed to parse YAML: ${error instanceof Error ? error.message : String(error)}`);
6408
+ throw new Error(
6409
+ `Failed to parse YAML: ${error instanceof Error ? error.message : String(error)}`
6410
+ );
6918
6411
  }
6919
6412
  }
6920
6413
  generate(_config, flags, parsedArgs) {
@@ -6948,7 +6441,9 @@ class YamlConfigPlugin extends ConfigPlugin {
6948
6441
  });
6949
6442
  return lines.join("\n") + "\n" + yamlContent;
6950
6443
  } catch (error) {
6951
- throw new Error(`Failed to generate YAML: ${error instanceof Error ? error.message : String(error)}`);
6444
+ throw new Error(
6445
+ `Failed to generate YAML: ${error instanceof Error ? error.message : String(error)}`
6446
+ );
6952
6447
  }
6953
6448
  }
6954
6449
  /**
@@ -6978,7 +6473,10 @@ function createYamlPlugin() {
6978
6473
  try {
6979
6474
  return new YamlConfigPlugin();
6980
6475
  } catch (error) {
6981
- console.warn("YAML plugin not available:", error instanceof Error ? error.message : String(error));
6476
+ console.warn(
6477
+ "YAML plugin not available:",
6478
+ error instanceof Error ? error.message : String(error)
6479
+ );
6982
6480
  return null;
6983
6481
  }
6984
6482
  }
@@ -6994,7 +6492,10 @@ async function createYamlPluginAsync() {
6994
6492
  const yamlModule = await import("js-yaml");
6995
6493
  return new YamlConfigPlugin(yamlModule);
6996
6494
  } catch (error) {
6997
- console.warn("YAML plugin not available:", error instanceof Error ? error.message : String(error));
6495
+ console.warn(
6496
+ "YAML plugin not available:",
6497
+ error instanceof Error ? error.message : String(error)
6498
+ );
6998
6499
  return null;
6999
6500
  }
7000
6501
  }
@@ -7023,7 +6524,9 @@ class ArgParserFuzzyTester {
7023
6524
  const results = [];
7024
6525
  if (this.options.verbose) {
7025
6526
  console.log(`Discovered ${commandPaths.length} command paths:`);
7026
- commandPaths.forEach((path2) => console.log(` ${path2.join(" ") || "(root)"}`));
6527
+ commandPaths.forEach(
6528
+ (path2) => console.log(` ${path2.join(" ") || "(root)"}`)
6529
+ );
7027
6530
  }
7028
6531
  for (const commandPath of commandPaths) {
7029
6532
  const pathResults = await this.testCommandPath(commandPath);
@@ -7049,7 +6552,12 @@ class ArgParserFuzzyTester {
7049
6552
  for (const [subCommandName, subCommand] of subCommands) {
7050
6553
  const newPath = [...currentPath, subCommandName];
7051
6554
  allPaths.push(newPath);
7052
- this.discoverSubCommandPaths(subCommand.parser, newPath, allPaths, depth + 1);
6555
+ this.discoverSubCommandPaths(
6556
+ subCommand.parser,
6557
+ newPath,
6558
+ allPaths,
6559
+ depth + 1
6560
+ );
7053
6561
  }
7054
6562
  }
7055
6563
  /**
@@ -8266,6 +7774,7 @@ class Protocol {
8266
7774
  this._responseHandlers = /* @__PURE__ */ new Map();
8267
7775
  this._progressHandlers = /* @__PURE__ */ new Map();
8268
7776
  this._timeoutInfo = /* @__PURE__ */ new Map();
7777
+ this._pendingDebouncedNotifications = /* @__PURE__ */ new Set();
8269
7778
  this.setNotificationHandler(CancelledNotificationSchema, (notification) => {
8270
7779
  const controller = this._requestHandlerAbortControllers.get(notification.params.requestId);
8271
7780
  controller === null || controller === void 0 ? void 0 : controller.abort(notification.params.reason);
@@ -8347,6 +7856,7 @@ class Protocol {
8347
7856
  const responseHandlers = this._responseHandlers;
8348
7857
  this._responseHandlers = /* @__PURE__ */ new Map();
8349
7858
  this._progressHandlers.clear();
7859
+ this._pendingDebouncedNotifications.clear();
8350
7860
  this._transport = void 0;
8351
7861
  (_a = this.onclose) === null || _a === void 0 ? void 0 : _a.call(this);
8352
7862
  const error = new McpError(ErrorCode.ConnectionClosed, "Connection closed");
@@ -8546,10 +8056,32 @@ class Protocol {
8546
8056
  * Emits a notification, which is a one-way message that does not expect a response.
8547
8057
  */
8548
8058
  async notification(notification, options) {
8059
+ var _a, _b;
8549
8060
  if (!this._transport) {
8550
8061
  throw new Error("Not connected");
8551
8062
  }
8552
8063
  this.assertNotificationCapability(notification.method);
8064
+ const debouncedMethods = (_b = (_a = this._options) === null || _a === void 0 ? void 0 : _a.debouncedNotificationMethods) !== null && _b !== void 0 ? _b : [];
8065
+ const canDebounce = debouncedMethods.includes(notification.method) && !notification.params && !(options === null || options === void 0 ? void 0 : options.relatedRequestId);
8066
+ if (canDebounce) {
8067
+ if (this._pendingDebouncedNotifications.has(notification.method)) {
8068
+ return;
8069
+ }
8070
+ this._pendingDebouncedNotifications.add(notification.method);
8071
+ Promise.resolve().then(() => {
8072
+ var _a2;
8073
+ this._pendingDebouncedNotifications.delete(notification.method);
8074
+ if (!this._transport) {
8075
+ return;
8076
+ }
8077
+ const jsonrpcNotification2 = {
8078
+ ...notification,
8079
+ jsonrpc: "2.0"
8080
+ };
8081
+ (_a2 = this._transport) === null || _a2 === void 0 ? void 0 : _a2.send(jsonrpcNotification2, options).catch((error) => this._onerror(error));
8082
+ });
8083
+ return;
8084
+ }
8553
8085
  const jsonrpcNotification = {
8554
8086
  ...notification,
8555
8087
  jsonrpc: "2.0"
@@ -24941,6 +24473,7 @@ export {
24941
24473
  simpleChalk as SimpleChalk,
24942
24474
  TomlConfigPlugin,
24943
24475
  YamlConfigPlugin,
24476
+ absolutePath,
24944
24477
  convertFlagToJsonSchemaProperty,
24945
24478
  convertFlagsToJsonSchema,
24946
24479
  convertFlagsToZodSchema,
@@ -24954,14 +24487,20 @@ export {
24954
24487
  createTomlPluginAsync,
24955
24488
  createYamlPlugin,
24956
24489
  createYamlPluginAsync,
24490
+ cwdRelative,
24491
+ detectEntryPoint,
24957
24492
  enableConfigPlugins,
24958
24493
  enableOptionalConfigPlugins,
24959
24494
  enableOptionalConfigPluginsAsync,
24495
+ entryRelative,
24960
24496
  extractSimplifiedResponse,
24961
24497
  generateMcpToolsFromArgParser,
24498
+ getEntryPointFromImportMeta,
24962
24499
  getJsonSchemaTypeFromFlag,
24963
24500
  globalConfigPluginRegistry,
24501
+ legacyCwdPath,
24964
24502
  logger,
24503
+ resolveLogPath,
24965
24504
  zodFlagSchema
24966
24505
  };
24967
24506
  //# sourceMappingURL=index.mjs.map