@alcyone-labs/arg-parser 2.2.1 → 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.
package/dist/index.cjs CHANGED
@@ -1238,86 +1238,22 @@ function createOutputSchema(pattern2) {
1238
1238
  }
1239
1239
  return OutputSchemaPatterns.successError();
1240
1240
  }
1241
- class DxtGenerator {
1242
- constructor(argParserInstance) {
1241
+ class DxtGeneratorTestUtils {
1242
+ constructor(argParserInstance, extractMcpServerInfo, handleExit) {
1243
1243
  this.argParserInstance = argParserInstance;
1244
- }
1245
- /**
1246
- * Helper method to handle exit logic based on autoExit setting
1247
- */
1248
- _handleExit(exitCode, message, type2, data2) {
1249
- const result = {
1250
- success: exitCode === 0,
1251
- exitCode,
1252
- message,
1253
- type: type2 || (exitCode === 0 ? "success" : "error"),
1254
- shouldExit: true,
1255
- data: data2
1256
- };
1257
- if (this.argParserInstance.getAutoExit() && typeof process === "object" && typeof process.exit === "function") {
1258
- process.exit(exitCode);
1259
- }
1260
- return result;
1261
- }
1262
- /**
1263
- * Handles the --s-build-dxt system flag to generate DXT packages for MCP servers
1264
- */
1265
- async handleBuildDxtFlag(processArgs, buildDxtIndex) {
1266
- var _a, _b, _c;
1267
- try {
1268
- 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"));
1269
- if (isTestMode) {
1270
- return await this.handleTestModeDxtGeneration(
1271
- processArgs,
1272
- buildDxtIndex
1273
- );
1274
- }
1275
- const entryPointFile = process.argv[1];
1276
- if (!entryPointFile || !fs__namespace.existsSync(entryPointFile)) {
1277
- console.error(
1278
- simpleChalk.red(`Error: Entry point file not found: ${entryPointFile}`)
1279
- );
1280
- return this._handleExit(1, "Entry point file not found", "error");
1281
- }
1282
- const outputDir = processArgs[buildDxtIndex + 1] || "./dxt";
1283
- console.log(
1284
- simpleChalk.cyan(
1285
- `
1286
- 🔧 Building DXT package for entry point: ${path__namespace.basename(entryPointFile)}`
1287
- )
1288
- );
1289
- console.log(simpleChalk.gray(`Output directory: ${outputDir}`));
1290
- await this.buildDxtWithTsdown(entryPointFile, outputDir);
1291
- console.log(simpleChalk.green(`
1292
- ✅ DXT package generation completed!`));
1293
- return this._handleExit(
1294
- 0,
1295
- "DXT package generation completed",
1296
- "success",
1297
- { entryPoint: entryPointFile, outputDir }
1298
- );
1299
- } catch (error) {
1300
- console.error(
1301
- simpleChalk.red(
1302
- `Error generating DXT package: ${error instanceof Error ? error.message : String(error)}`
1303
- )
1304
- );
1305
- return this._handleExit(
1306
- 1,
1307
- `Error generating DXT package: ${error instanceof Error ? error.message : String(error)}`,
1308
- "error"
1309
- );
1310
- }
1244
+ this.extractMcpServerInfo = extractMcpServerInfo;
1245
+ this.handleExit = handleExit;
1311
1246
  }
1312
1247
  /**
1313
1248
  * Handles DXT generation in test mode by creating mock DXT package structure
1249
+ * This method creates simplified mock files for testing purposes
1314
1250
  */
1315
1251
  async handleTestModeDxtGeneration(processArgs, buildDxtIndex) {
1316
1252
  try {
1317
1253
  const outputDir = processArgs[buildDxtIndex + 1] || "./dxt-packages";
1318
1254
  const mcpTools = this.argParserInstance.toMcpTools();
1319
1255
  if (mcpTools.length === 0) {
1320
- return this._handleExit(0, "No MCP servers found", "success");
1256
+ return this.handleExit(0, "No MCP servers found", "success");
1321
1257
  }
1322
1258
  const serverInfo = this.extractMcpServerInfo();
1323
1259
  const folderName = `${serverInfo.name.replace(/[^a-zA-Z0-9_-]/g, "_")}-dxt`;
@@ -1373,17 +1309,12 @@ echo "Mock DXT build script for ${serverInfo.name}"`;
1373
1309
  path__namespace.join(buildDir, "build-dxt-package.sh"),
1374
1310
  buildScript
1375
1311
  );
1376
- return this._handleExit(
1377
- 0,
1378
- "DXT package generation completed",
1379
- "success",
1380
- {
1381
- entryPoint: "test-mode",
1382
- outputDir: buildDir
1383
- }
1384
- );
1312
+ return this.handleExit(0, "DXT package generation completed", "success", {
1313
+ entryPoint: "test-mode",
1314
+ outputDir: buildDir
1315
+ });
1385
1316
  } catch (error) {
1386
- return this._handleExit(
1317
+ return this.handleExit(
1387
1318
  1,
1388
1319
  `Test mode DXT generation failed: ${error instanceof Error ? error.message : String(error)}`,
1389
1320
  "error"
@@ -1391,96 +1322,155 @@ echo "Mock DXT build script for ${serverInfo.name}"`;
1391
1322
  }
1392
1323
  }
1393
1324
  /**
1394
- * Generates a DXT package for the unified MCP server
1395
- * Now supports both withMcp() configuration and legacy addMcpSubCommand()
1325
+ * Checks if the current environment is in test mode
1326
+ * Used to determine whether to use test utilities or production code
1396
1327
  */
1397
- async generateDxtPackage(mcpSubCommand, outputDir) {
1398
- const serverInfo = this.extractMcpServerInfo(mcpSubCommand);
1399
- const tools = this.generateMcpToolsForDxt(mcpSubCommand);
1400
- const finalOutputDir = outputDir || "./dxt-packages";
1401
- const folderName = `${serverInfo.name.replace(/[^a-zA-Z0-9_-]/g, "_")}-dxt`;
1402
- const buildDir = path__namespace.join(finalOutputDir, folderName);
1403
- if (!fs__namespace.existsSync(buildDir)) {
1404
- fs__namespace.mkdirSync(buildDir, { recursive: true });
1405
- }
1406
- const serverDir = path__namespace.join(buildDir, "server");
1407
- if (!fs__namespace.existsSync(serverDir)) {
1408
- fs__namespace.mkdirSync(serverDir, { recursive: true });
1409
- }
1410
- const logoFilename = await this.addLogoToFolder(buildDir, serverInfo);
1411
- const manifest = this.createDxtManifest(
1412
- serverInfo,
1413
- tools,
1414
- mcpSubCommand,
1415
- logoFilename
1416
- );
1417
- this.validateDxtManifest(manifest);
1418
- fs__namespace.writeFileSync(
1419
- path__namespace.join(buildDir, "manifest.json"),
1420
- JSON.stringify(manifest, null, 2)
1421
- );
1422
- this.addOriginalCliToFolder(buildDir);
1423
- const bundledCliPath = await this.bundleOriginalCliWithTsdown(serverDir);
1424
- const serverScript = this.createServerScript(serverInfo, bundledCliPath);
1425
- const serverScriptPath = path__namespace.join(serverDir, "index.mjs");
1426
- fs__namespace.writeFileSync(serverScriptPath, serverScript);
1427
- try {
1428
- fs__namespace.chmodSync(serverScriptPath, 493);
1429
- } catch (error) {
1430
- console.warn(
1431
- "⚠ Could not set executable permission on server script:",
1432
- error instanceof Error ? error.message : String(error)
1433
- );
1434
- }
1435
- const packageJson = this.createDxtPackageJson(serverInfo);
1436
- fs__namespace.writeFileSync(
1437
- path__namespace.join(buildDir, "package.json"),
1438
- JSON.stringify(packageJson, null, 2)
1439
- );
1440
- const readme = this.createDxtReadme(serverInfo);
1441
- fs__namespace.writeFileSync(path__namespace.join(buildDir, "README.md"), readme);
1442
- const buildScript = this.createSimpleBuildScript(serverInfo);
1443
- fs__namespace.writeFileSync(path__namespace.join(buildDir, "build-dxt.sh"), buildScript);
1444
- const dxtIgnore = this.createDxtIgnore();
1445
- fs__namespace.writeFileSync(path__namespace.join(buildDir, ".dxtignore"), dxtIgnore);
1446
- try {
1447
- fs__namespace.chmodSync(path__namespace.join(buildDir, "build-dxt.sh"), 493);
1448
- } catch (error) {
1328
+ static isTestMode() {
1329
+ var _a, _b, _c;
1330
+ 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"));
1331
+ }
1332
+ }
1333
+ class DxtGenerator {
1334
+ constructor(argParserInstance) {
1335
+ this.argParserInstance = argParserInstance;
1336
+ }
1337
+ /**
1338
+ * Helper method to handle exit logic based on autoExit setting
1339
+ */
1340
+ _handleExit(exitCode, message, type2, data2) {
1341
+ const result = {
1342
+ success: exitCode === 0,
1343
+ exitCode,
1344
+ message,
1345
+ type: type2 || (exitCode === 0 ? "success" : "error"),
1346
+ shouldExit: true,
1347
+ data: data2
1348
+ };
1349
+ if (this.argParserInstance.getAutoExit() && typeof process === "object" && typeof process.exit === "function") {
1350
+ process.exit(exitCode);
1449
1351
  }
1450
- console.log(simpleChalk.green(` ✓ Generated DXT package folder: ${folderName}`));
1451
- console.log(
1452
- simpleChalk.gray(` Server: ${serverInfo.name} v${serverInfo.version}`)
1453
- );
1454
- console.log(simpleChalk.gray(` Tools: ${tools.length} tool(s)`));
1455
- console.log(simpleChalk.gray(` Location: ${buildDir}`));
1456
- console.log(
1457
- simpleChalk.cyan(`
1458
- 📦 Creating DXT package using Anthropic's dxt pack...`)
1459
- );
1460
- console.log(simpleChalk.cyan(`
1461
- 📋 Manual steps to create your DXT package:`));
1462
- console.log(simpleChalk.white(` cd ${path__namespace.relative(process.cwd(), buildDir)}`));
1463
- console.log(simpleChalk.white(` ./build-dxt.sh`));
1352
+ return result;
1464
1353
  }
1465
1354
  /**
1466
- * Reads package.json to extract fallback information for DXT manifest
1355
+ * Handles the --s-build-dxt system flag to generate DXT packages for MCP servers
1467
1356
  */
1468
- readPackageJsonInfo() {
1357
+ async handleBuildDxtFlag(processArgs, buildDxtIndex) {
1469
1358
  try {
1470
- const packageJsonPath = path__namespace.join(process.cwd(), "package.json");
1471
- if (fs__namespace.existsSync(packageJsonPath)) {
1472
- const packageContent = fs__namespace.readFileSync(packageJsonPath, "utf-8");
1473
- const packageData = JSON.parse(packageContent);
1474
- return {
1475
- author: packageData.author,
1476
- repository: packageData.repository,
1477
- license: packageData.license,
1478
- homepage: packageData.homepage
1479
- };
1359
+ if (DxtGeneratorTestUtils.isTestMode()) {
1360
+ const testUtils = new DxtGeneratorTestUtils(
1361
+ this.argParserInstance,
1362
+ () => this.extractMcpServerInfo(),
1363
+ (exitCode, message, type2, data2) => this._handleExit(exitCode, message, type2, data2)
1364
+ );
1365
+ return await testUtils.handleTestModeDxtGeneration(
1366
+ processArgs,
1367
+ buildDxtIndex
1368
+ );
1369
+ }
1370
+ const withNodeModules = processArgs.includes("--s-with-node-modules");
1371
+ if (withNodeModules) {
1372
+ console.log(
1373
+ simpleChalk.yellow(
1374
+ "🗂️ --s-with-node-modules detected: will include node_modules in bundle"
1375
+ )
1376
+ );
1377
+ const nodeModulesPath = path__namespace.resolve("./node_modules");
1378
+ if (!fs__namespace.existsSync(nodeModulesPath)) {
1379
+ console.error(
1380
+ simpleChalk.red(
1381
+ "❌ Error: node_modules directory not found. Please run the installation command first."
1382
+ )
1383
+ );
1384
+ console.log(
1385
+ simpleChalk.cyan(
1386
+ "💡 Required command: pnpm install --prod --node-linker=hoisted"
1387
+ )
1388
+ );
1389
+ return this._handleExit(
1390
+ 1,
1391
+ "node_modules directory not found",
1392
+ "error"
1393
+ );
1394
+ }
1395
+ try {
1396
+ const nodeModulesContents = fs__namespace.readdirSync(nodeModulesPath);
1397
+ const hasNestedNodeModules = nodeModulesContents.filter((item) => !item.startsWith(".") && !item.startsWith("@")).some((item) => {
1398
+ const itemPath = path__namespace.join(nodeModulesPath, item);
1399
+ try {
1400
+ return fs__namespace.statSync(itemPath).isDirectory() && fs__namespace.existsSync(path__namespace.join(itemPath, "node_modules"));
1401
+ } catch {
1402
+ return false;
1403
+ }
1404
+ });
1405
+ if (hasNestedNodeModules) {
1406
+ console.warn(
1407
+ simpleChalk.yellow(
1408
+ "⚠️ Warning: Detected nested node_modules. For best results, ensure hoisted installation:"
1409
+ )
1410
+ );
1411
+ console.log(
1412
+ simpleChalk.cyan(
1413
+ " rm -rf node_modules && pnpm install --prod --node-linker=hoisted"
1414
+ )
1415
+ );
1416
+ } else {
1417
+ console.log(
1418
+ simpleChalk.green(
1419
+ "✅ node_modules appears properly hoisted and ready for bundling"
1420
+ )
1421
+ );
1422
+ }
1423
+ } catch (error) {
1424
+ console.warn(
1425
+ simpleChalk.yellow(
1426
+ `⚠️ Could not validate node_modules structure: ${error instanceof Error ? error.message : String(error)}`
1427
+ )
1428
+ );
1429
+ }
1430
+ console.log(
1431
+ simpleChalk.gray(
1432
+ "💡 This will create a fully autonomous DXT with all native dependencies included"
1433
+ )
1434
+ );
1480
1435
  }
1436
+ const entryPointFile = process.argv[1];
1437
+ if (!entryPointFile || !fs__namespace.existsSync(entryPointFile)) {
1438
+ console.error(
1439
+ simpleChalk.red(`Error: Entry point file not found: ${entryPointFile}`)
1440
+ );
1441
+ return this._handleExit(1, "Entry point file not found", "error");
1442
+ }
1443
+ let outputDir = processArgs[buildDxtIndex + 1] || "./dxt";
1444
+ if (outputDir.startsWith("--s-")) outputDir = "./dxt";
1445
+ console.log(
1446
+ simpleChalk.cyan(
1447
+ `
1448
+ 🔧 Building DXT package for entry point: ${entryPointFile}`
1449
+ )
1450
+ );
1451
+ console.log(simpleChalk.gray(`Output directory: ${outputDir}`));
1452
+ console.log(simpleChalk.gray(`Entrypoint file: ${entryPointFile}`));
1453
+ await this.buildDxtWithTsdown(entryPointFile, outputDir, withNodeModules);
1454
+ console.log(simpleChalk.green(`
1455
+ ✅ DXT package generation completed!`));
1456
+ return this._handleExit(
1457
+ 0,
1458
+ "DXT package generation completed",
1459
+ "success",
1460
+ { entryPoint: entryPointFile, outputDir }
1461
+ );
1481
1462
  } catch (error) {
1463
+ console.error(
1464
+ simpleChalk.red(
1465
+ `Error generating DXT package: ${error instanceof Error ? error.message : String(error)}`
1466
+ )
1467
+ );
1468
+ return this._handleExit(
1469
+ 1,
1470
+ `Error generating DXT package: ${error instanceof Error ? error.message : String(error)}`,
1471
+ "error"
1472
+ );
1482
1473
  }
1483
- return null;
1484
1474
  }
1485
1475
  /**
1486
1476
  * Extracts server information from MCP configuration
@@ -1565,518 +1555,9 @@ echo "Mock DXT build script for ${serverInfo.name}"`;
1565
1555
  ];
1566
1556
  }
1567
1557
  }
1568
- createDxtManifest(serverInfo, tools, mcpSubCommand, logoFilename) {
1569
- var _a;
1570
- const packageInfo = this.readPackageJsonInfo();
1571
- let author = serverInfo.author;
1572
- if (!author && (packageInfo == null ? void 0 : packageInfo.author)) {
1573
- if (typeof packageInfo.author === "string") {
1574
- const match = packageInfo.author.match(/^([^<]+?)(?:\s*<([^>]+)>)?$/);
1575
- if (match) {
1576
- author = {
1577
- name: match[1].trim(),
1578
- email: (_a = match[2]) == null ? void 0 : _a.trim()
1579
- };
1580
- } else {
1581
- author = { name: packageInfo.author };
1582
- }
1583
- } else {
1584
- author = packageInfo.author;
1585
- }
1586
- }
1587
- if (!author) {
1588
- throw new Error(
1589
- "DXT manifest requires author information. Please provide it via withMcp() serverInfo.author, addMcpSubCommand serverInfo.author, or in package.json"
1590
- );
1591
- }
1592
- const cliArgs = this.generateCliArgsForDxt(mcpSubCommand);
1593
- const { envVars, userConfig } = this.generateEnvAndUserConfig();
1594
- const manifest = {
1595
- dxt_version: "0.1",
1596
- name: serverInfo.name,
1597
- version: serverInfo.version,
1598
- description: serverInfo.description || "MCP server generated from ArgParser",
1599
- author,
1600
- server: {
1601
- type: "node",
1602
- entry_point: "server/index.mjs",
1603
- mcp_config: {
1604
- command: "node",
1605
- args: ["${__dirname}/server/index.mjs", ...cliArgs],
1606
- env: envVars
1607
- }
1608
- },
1609
- tools: tools.map((tool) => ({
1610
- name: tool.name,
1611
- description: tool.description
1612
- }))
1613
- };
1614
- if (logoFilename) {
1615
- manifest.icon = logoFilename;
1616
- }
1617
- if (userConfig && Object.keys(userConfig).length > 0) {
1618
- manifest.user_config = userConfig;
1619
- }
1620
- if (serverInfo.repository || (packageInfo == null ? void 0 : packageInfo.repository)) {
1621
- manifest.repository = serverInfo.repository || (packageInfo == null ? void 0 : packageInfo.repository);
1622
- }
1623
- if (serverInfo.license || (packageInfo == null ? void 0 : packageInfo.license)) {
1624
- manifest.license = serverInfo.license || (packageInfo == null ? void 0 : packageInfo.license);
1625
- }
1626
- if (serverInfo.homepage || (packageInfo == null ? void 0 : packageInfo.homepage)) {
1627
- manifest.homepage = serverInfo.homepage || (packageInfo == null ? void 0 : packageInfo.homepage);
1628
- }
1629
- return manifest;
1630
- }
1631
- validateDxtManifest(manifest) {
1632
- const errors = [];
1633
- if (!manifest.dxt_version)
1634
- errors.push("Missing required field: dxt_version");
1635
- if (!manifest.name) errors.push("Missing required field: name");
1636
- if (!manifest.version) errors.push("Missing required field: version");
1637
- if (!manifest.server) errors.push("Missing required field: server");
1638
- if (!manifest.author) errors.push("Missing required field: author");
1639
- if (manifest.server) {
1640
- if (!manifest.server.type)
1641
- errors.push("Missing required field: server.type");
1642
- if (!manifest.server.entry_point)
1643
- errors.push("Missing required field: server.entry_point");
1644
- if (!manifest.server.mcp_config)
1645
- errors.push("Missing required field: server.mcp_config");
1646
- if (manifest.server.mcp_config) {
1647
- if (!manifest.server.mcp_config.command)
1648
- errors.push("Missing required field: server.mcp_config.command");
1649
- if (!manifest.server.mcp_config.args || !Array.isArray(manifest.server.mcp_config.args)) {
1650
- errors.push(
1651
- "Missing or invalid field: server.mcp_config.args (must be array)"
1652
- );
1653
- }
1654
- }
1655
- }
1656
- if (manifest.author && typeof manifest.author === "object") {
1657
- if (!manifest.author.name)
1658
- errors.push("Missing required field: author.name");
1659
- }
1660
- if (manifest.dxt_version && manifest.dxt_version !== "0.1") {
1661
- errors.push("Unsupported dxt_version: only '0.1' is currently supported");
1662
- }
1663
- if (errors.length > 0) {
1664
- throw new Error(
1665
- `DXT manifest validation failed:
1666
- ${errors.map((e) => ` - ${e}`).join("\n")}`
1667
- );
1668
- }
1669
- }
1670
- createServerScript(serverInfo, bundledCliPath) {
1671
- const cliImportPath = bundledCliPath || "original-cli.mjs";
1672
- return `#!/usr/bin/env node
1673
-
1674
- // Generated MCP server for ${serverInfo.name}
1675
- // This server uses @alcyone-labs/arg-parser's built-in MCP functionality for full protocol compliance
1676
-
1677
- // FIRST: Set up MCP-safe logging to prevent STDOUT contamination
1678
- import { createMcpLogger } from '@alcyone-labs/simple-mcp-logger';
1679
-
1680
- // Auto-detect MCP mode and hijack console to prevent protocol corruption
1681
- const mcpLogger = createMcpLogger('${serverInfo.name}');
1682
- globalThis.console = mcpLogger;
1683
-
1684
- // Now import the CLI which already has MCP functionality configured
1685
- import originalCli from './${cliImportPath}';
1686
-
1687
- // Server configuration
1688
- const serverInfo = ${JSON.stringify(serverInfo, null, 2)};
1689
-
1690
- // Use mcpError for debugging output (safe STDERR, visible in client logs)
1691
- console.error(\`MCP Server: \${serverInfo.name} v\${serverInfo.version}\`);
1692
- console.error(\`Description: \${serverInfo.description}\`);
1693
- console.error(\`Generated from @alcyone-labs/arg-parser with built-in MCP functionality\`);
1694
- ${bundledCliPath ? "console.error(`Using bundled CLI for autonomous execution`);" : ""}
1695
-
1696
- // The original CLI has MCP functionality configured via withMcp() or addMcpSubCommand()
1697
- // We use the centralized --s-mcp-serve system flag to start the unified MCP server
1698
-
1699
- // Start the MCP server using the library's built-in centralized serving
1700
- // This works with both withMcp() configuration and legacy addMcpSubCommand() setups
1701
- originalCli.parse(['--s-mcp-serve']);
1702
- `;
1703
- }
1704
- createDxtPackageJson(serverInfo) {
1705
- const useLocalBuild = process.env["LOCAL_BUILD"] === "1";
1706
- const argParserDependency = useLocalBuild ? "file:../../arg-parser-local.tgz" : "^1.3.0";
1707
- let originalDependencies = {};
1708
- try {
1709
- const originalPackageJsonPath = path__namespace.join(process.cwd(), "package.json");
1710
- if (fs__namespace.existsSync(originalPackageJsonPath)) {
1711
- const originalPackageJson = JSON.parse(
1712
- fs__namespace.readFileSync(originalPackageJsonPath, "utf8")
1713
- );
1714
- originalDependencies = originalPackageJson.dependencies || {};
1715
- }
1716
- } catch (error) {
1717
- console.warn(
1718
- "⚠ Could not read original package.json for dependencies:",
1719
- error instanceof Error ? error.message : String(error)
1720
- );
1721
- }
1722
- const dependencies2 = {
1723
- ...originalDependencies,
1724
- "@alcyone-labs/arg-parser": argParserDependency,
1725
- "@alcyone-labs/simple-mcp-logger": "^1.0.0",
1726
- "@modelcontextprotocol/sdk": "^1.15.0",
1727
- zod: "^3.22.4"
1728
- };
1729
- const devDependencies = {
1730
- tsup: "^8.3.5"
1731
- };
1732
- Object.keys(dependencies2).forEach((key) => {
1733
- const depValue = dependencies2[key];
1734
- if (key !== "@alcyone-labs/arg-parser" && typeof depValue === "string" && depValue.startsWith("file:")) {
1735
- delete dependencies2[key];
1736
- console.warn(
1737
- `⚠ Removed file: dependency ${key} from DXT package (not suitable for distribution)`
1738
- );
1739
- }
1740
- });
1741
- return {
1742
- name: serverInfo.name,
1743
- version: serverInfo.version,
1744
- description: serverInfo.description,
1745
- main: "server/index.mjs",
1746
- type: "module",
1747
- scripts: {
1748
- start: "node server/index.mjs",
1749
- "build-dxt": "./build-dxt.sh"
1750
- },
1751
- dependencies: dependencies2,
1752
- devDependencies,
1753
- engines: {
1754
- node: ">=22.0.0"
1755
- },
1756
- author: serverInfo.author,
1757
- license: serverInfo.license || "MIT",
1758
- repository: serverInfo.repository
1759
- };
1760
- }
1761
- /**
1762
- * Creates a .dxtignore file to exclude build artifacts and unnecessary files
1763
- */
1764
- createDxtIgnore() {
1765
- return `# DXT ignore file - exclude these files from the DXT package
1766
- # Generated by @alcyone-labs/arg-parser
1767
-
1768
- # Build artifacts and logs
1769
- *.log
1770
- *.tmp
1771
- temp-dxt-build/
1772
-
1773
- # Build scripts (not needed in final package)
1774
- build-dxt.sh
1775
- arg-parser-local.tgz
1776
- tsup.config.autonomous.js
1777
- tsdown.config.mjs
1778
-
1779
- # Original files (replaced by bundled autonomous build)
1780
- server/index.original.mjs
1781
- server/*.autonomous.mjs
1782
-
1783
- # NOTE: server/original-cli.mjs is NOT excluded because it's needed for the MCP server to function
1784
- # The bundled version (if created) will be server/original-cli.bundled.mjs
1785
-
1786
- # NOTE: node_modules/ is NOT excluded because TSDown bundling may not be 100% autonomous
1787
- # If bundling is successful, node_modules won't be needed, but we include it as fallback
1788
- # The bundled server/index.mjs should be fully autonomous and not require node_modules
1789
-
1790
- # Development files
1791
- .git/
1792
- .gitignore
1793
- .env
1794
- .env.*
1795
-
1796
- # OS files
1797
- .DS_Store
1798
- Thumbs.db
1799
-
1800
- # IDE files
1801
- .vscode/
1802
- .idea/
1803
- *.swp
1804
- *.swo
1805
- `;
1806
- }
1807
- /**
1808
- * Creates a simple build script that uses TSDown bundling and Anthropic's dxt pack
1809
- */
1810
- createSimpleBuildScript(serverInfo) {
1811
- return `#!/bin/bash
1812
-
1813
- # Simple DXT Build Script for ${serverInfo.name}
1814
- # Generated by @alcyone-labs/arg-parser with TSDown bundling
1815
-
1816
- set -e
1817
-
1818
- echo "📦 Creating DXT package for ${serverInfo.name}..."
1819
-
1820
- # Step 1: Make server executable (required for MCP)
1821
- echo "🔧 Making server executable..."
1822
- chmod +x server/index.mjs
1823
-
1824
- # Step 2: Handle local development dependencies
1825
- if grep -q "file:.*arg-parser-local.tgz" package.json; then
1826
- echo "🔧 Checking for local package tarball..."
1827
-
1828
- # Check if the tarball exists in the parent directory
1829
- if [ -f "../../arg-parser-local.tgz" ]; then
1830
- echo "✅ Found local package tarball: ../../arg-parser-local.tgz"
1831
- else
1832
- echo "⚠️ Local tarball not found, falling back to npm registry"
1833
- echo "💡 To use local build, run: cd /path/to/arg-parser && npm pack && cp *.tgz examples/community/canny-cli/"
1834
-
1835
- # Replace with npm version
1836
- sed -i.bak 's|"file:.*arg-parser-local.tgz"|"^1.3.0"|g' package.json 2>/dev/null || \\
1837
- sed -i 's|"file:.*arg-parser-local.tgz"|"^1.3.0"|g' package.json
1838
- fi
1839
- fi
1840
-
1841
- # Step 3: Install dependencies (for runtime only, bundling was done during generation)
1842
- echo "📦 Installing dependencies..."
1843
- npm install
1844
-
1845
- # Step 4: Validate manifest
1846
- echo "🔍 Validating DXT manifest..."
1847
- if command -v npx >/dev/null 2>&1; then
1848
- if npx @anthropic-ai/dxt validate manifest.json; then
1849
- echo "✅ DXT manifest validation passed"
1850
- else
1851
- echo "❌ DXT manifest validation failed"
1852
- exit 1
1853
- fi
1854
- else
1855
- echo "⚠️ npx not found, skipping DXT validation"
1856
- fi
1857
-
1858
- # Step 5: Create DXT package using Anthropic's official packer
1859
- echo "📦 Creating DXT package..."
1860
- if command -v npx >/dev/null 2>&1; then
1861
- # Use dxt pack directly with .dxtignore for clean packaging
1862
- npx @anthropic-ai/dxt pack . "${serverInfo.name}.dxt"
1863
- else
1864
- # Fallback to standard zip if npx not available
1865
- echo "⚠️ npx not found, using zip fallback"
1866
- zip -r "${serverInfo.name}.dxt" . -x "node_modules/*" "*.log" ".git/*" "build-dxt.sh" "temp-dxt-build/*"
1867
- fi
1868
-
1869
- # Step 6: Sign the DXT package (optional)
1870
- echo "🔐 Signing DXT package..."
1871
- if command -v npx >/dev/null 2>&1 && command -v openssl >/dev/null 2>&1; then
1872
- if npx @anthropic-ai/dxt sign "${serverInfo.name}.dxt" --self-signed; then
1873
- echo "✅ DXT package signed successfully"
1874
- else
1875
- echo "⚠️ DXT signing failed, but package is still usable"
1876
- fi
1877
- else
1878
- echo "⚠️ npx or openssl not found, skipping DXT signing"
1879
- fi
1880
-
1881
- echo "✅ DXT package created: ${serverInfo.name}.dxt"
1882
- echo "🎯 This package includes bundled CLI with all dependencies!"
1883
- echo ""
1884
- echo "🎉 Installation Instructions:"
1885
- echo "You can now take the file '${serverInfo.name}.dxt' and install it on Claude Desktop"
1886
- echo "or supporting applications by using drag & drop on the Extensions Settings page,"
1887
- echo "or directly pointing the file selector to this file."
1888
- echo ""
1889
- echo "📁 DXT file location: $(pwd)/${serverInfo.name}.dxt"
1890
- `;
1891
- }
1892
- createDxtReadme(serverInfo) {
1893
- return `# ${serverInfo.name}
1894
-
1895
- ${serverInfo.description}
1896
-
1897
- ## Installation
1898
-
1899
- This is a Desktop Extension (DXT) package generated from @alcyone-labs/arg-parser.
1900
-
1901
- ### Automatic Installation
1902
- Open this .dxt file with Claude Desktop or other DXT-compatible applications for single-click installation.
1903
-
1904
- ### Manual Installation
1905
- 1. Extract the .dxt file (it's a ZIP archive)
1906
- 2. Run \`npm install\` to install dependencies
1907
- 3. Start the server with \`npm start\`
1908
-
1909
- ## Tools
1910
-
1911
- This MCP server provides the following tools:
1912
- ${this.generateMcpToolsForDxt().map((tool) => `- **${tool.name}**: ${tool.description}`).join("\n")}
1913
-
1914
- ## Building DXT Packages
1915
-
1916
- To rebuild the DXT package:
1917
-
1918
- ### Prerequisites
1919
- - Node.js 18+ installed
1920
- - npm package manager
1921
-
1922
- ### Build Steps
1923
-
1924
- \`\`\`bash
1925
- # 1. Install dependencies
1926
- npm install
1927
-
1928
- # 2. Build DXT package
1929
- npm run build-dxt
1930
- # or
1931
- ./build-dxt.sh
1932
-
1933
- # 3. The build script will:
1934
- # - Install dependencies
1935
- # - Validate the manifest
1936
- # - Create the DXT package using Anthropic's official packer
1937
- # - Sign the package (optional)
1938
- \`\`\`
1939
-
1940
- ### Manual Build Process
1941
-
1942
- If the automated build script doesn't work, you can build manually:
1943
-
1944
- \`\`\`bash
1945
- # 1. Install dependencies
1946
- npm install
1947
-
1948
- # 2. Create DXT package
1949
- npx @anthropic-ai/dxt pack . ${serverInfo.name}.dxt
1950
-
1951
- # 2. Update manifest.json
1952
- # Change "entry_point" from "server/index.js" to "dist-autonomous/server.cjs"
1953
-
1954
- # 3. Create new DXT with bundled server
1955
- # Replace server/index.js with dist-autonomous/server.cjs
1956
- # Remove package.json dependencies (optional)
1957
- \`\`\`
1958
-
1959
- ### Result
1960
- The resulting DXT package will be completely autonomous and won't require \`npm install\`.
1961
-
1962
- ## Generated Information
1963
-
1964
- - **Generator**: @alcyone-labs/arg-parser v1.3.0
1965
- - **Generated**: ${(/* @__PURE__ */ new Date()).toISOString()}
1966
- - **DXT Version**: 0.1
1967
-
1968
- ## Note
1969
-
1970
- This is a simplified DXT package. For full functionality and the latest features, use the original CLI directly.
1971
- For autonomous packages, follow the build instructions above.
1972
- `;
1973
- }
1974
1558
  /**
1975
1559
  * Maps ArgParser flag types to DXT user config types
1976
1560
  */
1977
- mapFlagTypeToUserConfigType(flagType) {
1978
- if (typeof flagType === "function") {
1979
- if (flagType === String) return "string";
1980
- if (flagType === Number) return "number";
1981
- if (flagType === Boolean) return "boolean";
1982
- if (flagType === Array) return "array";
1983
- if (flagType === Object) return "object";
1984
- return "string";
1985
- }
1986
- switch (String(flagType).toLowerCase()) {
1987
- case "string":
1988
- return "string";
1989
- case "number":
1990
- return "number";
1991
- case "boolean":
1992
- return "boolean";
1993
- case "table":
1994
- return "array";
1995
- case "array":
1996
- return "array";
1997
- case "object":
1998
- return "object";
1999
- default:
2000
- return "string";
2001
- }
2002
- }
2003
- /**
2004
- * Generates CLI arguments for DXT manifest based on ArgParser flags
2005
- */
2006
- generateCliArgsForDxt(_mcpSubCommand) {
2007
- const args = [];
2008
- args.push("--s-mcp-serve");
2009
- return args;
2010
- }
2011
- /**
2012
- * Generates environment variables and user config for DXT manifest
2013
- */
2014
- generateEnvAndUserConfig() {
2015
- const envVars = {};
2016
- const userConfig = {};
2017
- const flags = this.argParserInstance.flags || [];
2018
- for (const flag of flags) {
2019
- const flagName = flag["name"];
2020
- if (flagName === "help" || flagName === "mcp") continue;
2021
- if (flag["env"]) {
2022
- const envVarName = flag["env"];
2023
- envVars[envVarName] = `\${user_config.${envVarName}}`;
2024
- userConfig[envVarName] = {
2025
- type: this.mapFlagTypeToUserConfigType(flag["type"]),
2026
- title: this.generateUserConfigTitle(envVarName),
2027
- description: flag["description"] || `${envVarName} environment variable`,
2028
- required: true,
2029
- // Always require env vars in user_config for better UX
2030
- sensitive: this.isSensitiveField(envVarName)
2031
- };
2032
- }
2033
- }
2034
- if (typeof this.argParserInstance.getTools === "function") {
2035
- const tools = this.argParserInstance.getTools();
2036
- for (const [, toolConfig] of tools) {
2037
- const toolFlags = toolConfig.flags || [];
2038
- for (const flag of toolFlags) {
2039
- const flagName = flag["name"];
2040
- if (flagName === "help" || flagName.startsWith("s-")) continue;
2041
- if (flag["env"]) {
2042
- const envVarName = flag["env"];
2043
- if (!envVars[envVarName]) {
2044
- envVars[envVarName] = `\${user_config.${envVarName}}`;
2045
- userConfig[envVarName] = {
2046
- type: this.mapFlagTypeToUserConfigType(flag["type"]),
2047
- title: this.generateUserConfigTitle(envVarName),
2048
- description: flag["description"] || `${envVarName} environment variable`,
2049
- required: true,
2050
- // Always require env vars in user_config for better UX
2051
- sensitive: this.isSensitiveField(envVarName)
2052
- };
2053
- }
2054
- }
2055
- }
2056
- }
2057
- }
2058
- return { envVars, userConfig };
2059
- }
2060
- /**
2061
- * Generates a user-friendly title for user config fields
2062
- */
2063
- generateUserConfigTitle(flagName) {
2064
- return flagName.split(/[-_]/).map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()).join(" ");
2065
- }
2066
- /**
2067
- * Checks if a field should be marked as sensitive in user config
2068
- */
2069
- isSensitiveField(fieldName) {
2070
- const sensitivePatterns = [
2071
- /key/i,
2072
- /token/i,
2073
- /secret/i,
2074
- /password/i,
2075
- /auth/i,
2076
- /credential/i
2077
- ];
2078
- return sensitivePatterns.some((pattern2) => pattern2.test(fieldName));
2079
- }
2080
1561
  /**
2081
1562
  * Adds the logo to the build folder if available
2082
1563
  * @returns The filename of the logo that was added, or undefined if no logo was added
@@ -2096,7 +1577,10 @@ For autonomous packages, follow the build instructions above.
2096
1577
  const urlPath = new URL(customLogo).pathname;
2097
1578
  const urlFilename = path__namespace.basename(urlPath);
2098
1579
  if (urlFilename && urlFilename.includes(".")) {
2099
- logoFilename = urlFilename;
1580
+ const ext = path__namespace.extname(urlFilename);
1581
+ logoFilename = `logo${ext}`;
1582
+ } else {
1583
+ logoFilename = "logo.jpg";
2100
1584
  }
2101
1585
  console.log("✓ Downloaded logo from URL");
2102
1586
  } else {
@@ -2123,7 +1607,8 @@ For autonomous packages, follow the build instructions above.
2123
1607
  }
2124
1608
  if (fs__namespace.existsSync(logoPath)) {
2125
1609
  logoBuffer = fs__namespace.readFileSync(logoPath);
2126
- logoFilename = path__namespace.basename(logoPath);
1610
+ const ext = path__namespace.extname(logoPath);
1611
+ logoFilename = `logo${ext}`;
2127
1612
  console.log("✓ Added custom logo from local file");
2128
1613
  } else {
2129
1614
  console.warn(`⚠ Custom logo file not found: ${logoPath}`);
@@ -2152,9 +1637,29 @@ For autonomous packages, follow the build instructions above.
2152
1637
  "logo_1_small.jpg"
2153
1638
  );
2154
1639
  }
1640
+ if (!fs__namespace.existsSync(logoPath)) {
1641
+ logoPath = path__namespace.join(
1642
+ process.cwd(),
1643
+ "node_modules",
1644
+ "@alcyone-labs",
1645
+ "arg-parser",
1646
+ "dist",
1647
+ "assets",
1648
+ "logo_1_small.jpg"
1649
+ );
1650
+ }
1651
+ if (!fs__namespace.existsSync(logoPath)) {
1652
+ logoPath = path__namespace.join(
1653
+ process.cwd(),
1654
+ "dist",
1655
+ "assets",
1656
+ "logo_1_small.jpg"
1657
+ );
1658
+ }
2155
1659
  if (fs__namespace.existsSync(logoPath)) {
2156
1660
  logoBuffer = fs__namespace.readFileSync(logoPath);
2157
- logoFilename = "logo.jpg";
1661
+ const ext = path__namespace.extname(logoPath);
1662
+ logoFilename = `logo${ext}`;
2158
1663
  console.log("✓ Added default logo to build folder");
2159
1664
  } else {
2160
1665
  console.warn(
@@ -2176,317 +1681,82 @@ For autonomous packages, follow the build instructions above.
2176
1681
  return void 0;
2177
1682
  }
2178
1683
  }
2179
- /**
2180
- * Processes CLI source code to replace global console with MCP-compliant Logger
2181
- */
2182
- processCliSourceForMcp(cliSource) {
2183
- const consoleReplacement = `import { createMcpLogger } from '@alcyone-labs/arg-parser';
2184
-
2185
- // Replace global console with MCP-compliant logger for DXT packages
2186
- const mcpLogger = createMcpLogger('[CLI]');
2187
- const originalConsole = globalThis.console;
2188
- globalThis.console = {
2189
- ...originalConsole,
2190
- log: (...args) => mcpLogger.info(...args),
2191
- info: (...args) => mcpLogger.info(...args),
2192
- warn: (...args) => mcpLogger.warn(...args),
2193
- debug: (...args) => mcpLogger.debug(...args),
2194
- // Keep error/trace/etc as-is since they use stderr (MCP-compliant)
2195
- error: originalConsole.error,
2196
- trace: originalConsole.trace,
2197
- assert: originalConsole.assert,
2198
- clear: originalConsole.clear,
2199
- count: originalConsole.count,
2200
- countReset: originalConsole.countReset,
2201
- dir: originalConsole.dir,
2202
- dirxml: originalConsole.dirxml,
2203
- group: originalConsole.group,
2204
- groupCollapsed: originalConsole.groupCollapsed,
2205
- groupEnd: originalConsole.groupEnd,
2206
- table: originalConsole.table,
2207
- time: originalConsole.time,
2208
- timeEnd: originalConsole.timeEnd,
2209
- timeLog: originalConsole.timeLog,
2210
- timeStamp: originalConsole.timeStamp,
2211
- };
2212
-
2213
- `;
2214
- const lines = cliSource.split("\n");
2215
- let lastImportIndex = -1;
2216
- for (let i = 0; i < lines.length; i++) {
2217
- const line = lines[i].trim();
2218
- if (line.startsWith("import ") && line.includes("from")) {
2219
- lastImportIndex = i;
2220
- } else if (line && !line.startsWith("//") && !line.startsWith("/*") && lastImportIndex >= 0) {
2221
- break;
2222
- }
2223
- }
2224
- if (lastImportIndex >= 0) {
2225
- lines.splice(
2226
- lastImportIndex + 1,
2227
- 0,
2228
- "",
2229
- ...consoleReplacement.trim().split("\n")
2230
- );
2231
- return lines.join("\n");
2232
- } else {
2233
- return consoleReplacement + cliSource;
2234
- }
2235
- }
2236
- /**
2237
- * Adds the original CLI source to the build folder for handler execution
2238
- */
2239
- addOriginalCliToFolder(buildDir) {
2240
- try {
2241
- const appCommandName = this.argParserInstance.getAppCommandName();
2242
- const appName = this.argParserInstance.getAppName();
2243
- const possibleCliFiles = [
2244
- // Current working directory common patterns
2245
- path__namespace.join(process.cwd(), "index.js"),
2246
- path__namespace.join(process.cwd(), "index.mjs"),
2247
- path__namespace.join(process.cwd(), "cli.js"),
2248
- path__namespace.join(process.cwd(), "cli.mjs"),
2249
- path__namespace.join(process.cwd(), "main.js"),
2250
- path__namespace.join(process.cwd(), "main.mjs"),
2251
- // Look for files with the app command name
2252
- path__namespace.join(process.cwd(), `${appCommandName}.js`),
2253
- path__namespace.join(process.cwd(), `${appCommandName}.mjs`),
2254
- // Look for files with the app command name (sanitized)
2255
- path__namespace.join(
2256
- process.cwd(),
2257
- `${appCommandName.replace(/[^a-zA-Z0-9-]/g, "-")}.js`
2258
- ),
2259
- path__namespace.join(
2260
- process.cwd(),
2261
- `${appCommandName.replace(/[^a-zA-Z0-9-]/g, "-")}.mjs`
2262
- ),
2263
- // Look for files with app name patterns
2264
- path__namespace.join(
2265
- process.cwd(),
2266
- `${appName.toLowerCase().replace(/\s+/g, "-")}-cli.js`
2267
- ),
2268
- path__namespace.join(
2269
- process.cwd(),
2270
- `${appName.toLowerCase().replace(/\s+/g, "-")}-cli.mjs`
2271
- ),
2272
- // Look for files with first word of app name + cli
2273
- path__namespace.join(
2274
- process.cwd(),
2275
- `${appName.split(" ")[0].toLowerCase()}-cli.js`
2276
- ),
2277
- path__namespace.join(
2278
- process.cwd(),
2279
- `${appName.split(" ")[0].toLowerCase()}-cli.mjs`
2280
- )
2281
- ];
2282
- let cliSourcePath = null;
2283
- for (const filePath of possibleCliFiles) {
2284
- if (fs__namespace.existsSync(filePath)) {
2285
- cliSourcePath = filePath;
2286
- break;
2287
- }
2288
- }
2289
- if (cliSourcePath) {
2290
- let cliSource = fs__namespace.readFileSync(cliSourcePath, "utf8");
2291
- cliSource = cliSource.replace(
2292
- /import\s*{\s*([^}]+)\s*}\s*from\s*['"][^'"]*\/dist\/index\.mjs['"];?/g,
2293
- "import { $1 } from '@alcyone-labs/arg-parser';"
2294
- );
2295
- cliSource = cliSource.replace(
2296
- /import\s+(\w+)\s+from\s*['"][^'"]*\/dist\/index\.mjs['"];?/g,
2297
- "import $1 from '@alcyone-labs/arg-parser';"
2298
- );
2299
- cliSource = this.processCliSourceForMcp(cliSource);
2300
- const parserVariableMatch = cliSource.match(
2301
- /const\s+(\w+)\s*=\s*ArgParser\.withMcp\(/
2302
- );
2303
- if (parserVariableMatch) {
2304
- const parserVariable = parserVariableMatch[1];
2305
- cliSource += `
2306
-
2307
- // Export the parser instance for MCP server use
2308
- export default ${parserVariable};
2309
-
2310
- // Add debugging for main execution
2311
- console.error('[MCP-DEBUG] CLI source loaded, checking execution context...');
2312
- console.error('[MCP-DEBUG] import.meta.url:', import.meta.url);
2313
- console.error('[MCP-DEBUG] process.argv[1]:', process.argv[1]);
2314
-
2315
- // Ensure MCP server processes don't exit prematurely
2316
- console.error('[MCP-DEBUG] Process argv:', process.argv);
2317
- console.error('[MCP-DEBUG] Checking for serve command...');
2318
-
2319
- if (process.argv.includes('serve')) {
2320
- console.error('[MCP-DEBUG] Detected serve command, setting up MCP server lifecycle...');
2321
-
2322
- // Override the original parse method to handle async MCP server
2323
- const originalParse = ${parserVariable}.parse;
2324
- ${parserVariable}.parse = async function(args) {
2325
- console.error('[MCP-DEBUG] Starting parse with args:', args);
2326
-
2327
- try {
2328
- const result = originalParse.call(this, args);
2329
- console.error('[MCP-DEBUG] Parse result:', typeof result, result?.constructor?.name);
2330
-
2331
- // If result is a Promise (MCP server), await it and keep process alive
2332
- if (result && typeof result.then === 'function') {
2333
- console.error('[MCP-DEBUG] Detected Promise result, awaiting...');
2334
- const mcpResult = await result;
2335
- console.error('[MCP-DEBUG] MCP server started, keeping process alive...');
2336
-
2337
- // Keep the process alive indefinitely for MCP server
2338
- const keepAlive = setInterval(() => {
2339
- // Do nothing, just keep the event loop alive
2340
- }, 30000);
2341
-
2342
- // Handle graceful shutdown
2343
- process.on('SIGINT', () => {
2344
- console.error('[MCP-INFO] Received SIGINT, shutting down gracefully...');
2345
- clearInterval(keepAlive);
2346
- process.exit(0);
2347
- });
2348
-
2349
- process.on('SIGTERM', () => {
2350
- console.error('[MCP-INFO] Received SIGTERM, shutting down gracefully...');
2351
- clearInterval(keepAlive);
2352
- process.exit(0);
2353
- });
2354
-
2355
- return mcpResult;
2356
- } else {
2357
- console.error('[MCP-DEBUG] Non-Promise result, returning normally');
2358
- return result;
2359
- }
2360
- } catch (error) {
2361
- console.error('[MCP-ERROR] Error in parse:', error);
2362
- throw error;
2363
- }
2364
- };
2365
- }
2366
- `;
2367
- } else {
2368
- console.warn(
2369
- "⚠ Could not find ArgParser instance in CLI source, MCP server may not work properly"
2370
- );
2371
- }
2372
- const serverDir = path__namespace.join(buildDir, "server");
2373
- if (!fs__namespace.existsSync(serverDir)) {
2374
- fs__namespace.mkdirSync(serverDir, { recursive: true });
2375
- }
2376
- fs__namespace.writeFileSync(path__namespace.join(serverDir, "original-cli.mjs"), cliSource);
2377
- console.log(
2378
- `✓ Added original CLI source to build folder: ${path__namespace.basename(cliSourcePath)}`
2379
- );
2380
- } else {
2381
- console.warn(
2382
- "⚠ Original CLI source not found, handlers may not work properly"
2383
- );
2384
- console.warn(
2385
- " Searched for:",
2386
- possibleCliFiles.map((f) => path__namespace.basename(f)).join(", ")
2387
- );
2388
- }
2389
- } catch (error) {
2390
- console.warn(
2391
- "⚠ Failed to add original CLI source:",
2392
- error instanceof Error ? error.message : String(error)
2393
- );
2394
- }
2395
- }
2396
1684
  /**
2397
1685
  * Builds a complete DXT package using TSDown CLI for autonomous execution
2398
1686
  */
2399
- async buildDxtWithTsdown(entryPointFile, outputDir = "./dxt") {
1687
+ async buildDxtWithTsdown(entryPointFile, outputDir = "./dxt", withNodeModules = false) {
2400
1688
  try {
2401
1689
  console.log(simpleChalk.cyan("🔧 Building DXT package with TSDown..."));
2402
- const entryDir = path__namespace.dirname(entryPointFile);
1690
+ const projectRoot = this.findProjectRoot(entryPointFile);
1691
+ const absoluteEntryPath = path__namespace.resolve(entryPointFile);
1692
+ const relativeEntryPath = path__namespace.relative(projectRoot, absoluteEntryPath);
2403
1693
  const entryFileName = path__namespace.basename(entryPointFile);
2404
1694
  console.log(simpleChalk.gray(`Entry point: ${entryPointFile}`));
2405
- console.log(simpleChalk.gray(`Working directory: ${entryDir}`));
1695
+ console.log(simpleChalk.gray(`Project root: ${projectRoot}`));
1696
+ console.log(simpleChalk.gray(`Relative entry path: ${relativeEntryPath}`));
2406
1697
  const dxtIgnorePath = this.getDxtIgnoreTemplatePath();
2407
1698
  if (fs__namespace.existsSync(dxtIgnorePath)) {
2408
- fs__namespace.copyFileSync(dxtIgnorePath, path__namespace.join(entryDir, ".dxtignore"));
1699
+ fs__namespace.copyFileSync(dxtIgnorePath, path__namespace.join(projectRoot, ".dxtignore"));
2409
1700
  }
1701
+ const serverInfo = this.extractMcpServerInfo();
1702
+ const logoFilename = await this.addLogoToFolder(
1703
+ projectRoot,
1704
+ serverInfo,
1705
+ entryPointFile
1706
+ );
1707
+ console.log(
1708
+ logoFilename ? simpleChalk.gray(`✓ Logo prepared: ${logoFilename}`) : simpleChalk.gray("⚠ No logo available")
1709
+ );
2410
1710
  const originalCwd = process.cwd();
2411
1711
  try {
2412
- process.chdir(entryDir);
1712
+ process.chdir(projectRoot);
2413
1713
  const { build } = await import("tsdown");
2414
- console.log(simpleChalk.gray(`Building with TSDown: ${entryFileName}`));
1714
+ console.log(simpleChalk.gray(`Building with TSDown: ${relativeEntryPath}`));
1715
+ console.log(
1716
+ simpleChalk.green(
1717
+ `${withNodeModules ? "with node_modules" : "without node_modules"}`
1718
+ )
1719
+ );
2415
1720
  const buildConfig = {
2416
- entry: [entryFileName],
2417
- outDir: path__namespace.resolve(process.cwd(), outputDir),
2418
- format: ["esm"],
1721
+ entry: [relativeEntryPath],
1722
+ outDir: path__namespace.resolve(originalCwd, outputDir),
1723
+ format: ["es"],
2419
1724
  target: "node22",
2420
- noExternal: () => true,
1725
+ define: {
1726
+ // Define any compile-time constants
1727
+ NODE_ENV: '"production"'
1728
+ },
2421
1729
  minify: false,
2422
1730
  sourcemap: false,
2423
- clean: false,
1731
+ // Remove all output folders and artefacts
1732
+ clean: [outputDir, "./.dxtignore", `${outputDir}.dxt`],
2424
1733
  silent: process.env["NO_SILENCE"] !== "1",
2425
- copy: [
2426
- // Copy logo from assets - try multiple possible locations
2427
- ...(() => {
2428
- const possibleLogoPaths = [
2429
- // From built library assets
2430
- path__namespace.join(
2431
- path__namespace.dirname(new URL(typeof document === "undefined" ? require("url").pathToFileURL(__filename).href : _documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === "SCRIPT" && _documentCurrentScript.src || new URL("index.cjs", document.baseURI).href).pathname),
2432
- "..",
2433
- "assets",
2434
- "logo_1_small.jpg"
2435
- ),
2436
- // From node_modules
2437
- path__namespace.join(
2438
- process.cwd(),
2439
- "node_modules",
2440
- "@alcyone-labs",
2441
- "arg-parser",
2442
- "dist",
2443
- "assets",
2444
- "logo_1_small.jpg"
2445
- ),
2446
- // From package root dist/assets (for local build)
2447
- path__namespace.join(process.cwd(), "dist", "assets", "logo_1_small.jpg"),
2448
- // From library root (development)
2449
- path__namespace.join(
2450
- process.cwd(),
2451
- "..",
2452
- "..",
2453
- "..",
2454
- "docs",
2455
- "MCP",
2456
- "icons",
2457
- "logo_1_small.jpg"
2458
- )
2459
- ];
2460
- for (const logoPath of possibleLogoPaths) {
2461
- if (fs__namespace.existsSync(logoPath)) {
2462
- console.log(simpleChalk.gray(`Found logo at: ${logoPath}`));
2463
- return [{ from: logoPath, to: "logo.jpg" }];
2464
- }
2465
- }
1734
+ external: (_, importer) => withNodeModules ? importer == null ? void 0 : importer.includes("node_modules") : false,
1735
+ noExternal: (_, importer) => withNodeModules ? (importer == null ? void 0 : importer.includes("node_modules")) === false : true,
1736
+ copy: async (options) => {
1737
+ const outputPaths = [
1738
+ "package.json"
1739
+ ];
1740
+ if (withNodeModules) {
2466
1741
  console.log(
2467
- simpleChalk.yellow("⚠ Logo not found in any expected location")
1742
+ simpleChalk.gray(
1743
+ "📦 Including node_modules in bundle (may take longer)..."
1744
+ )
2468
1745
  );
2469
- return [];
2470
- })()
2471
- ],
2472
- external: [
2473
- // Node.js built-ins only - everything else should be bundled for true autonomy
2474
- "stream",
2475
- "fs",
2476
- "path",
2477
- "url",
2478
- "util",
2479
- "events",
2480
- "child_process",
2481
- "os",
2482
- "tty",
2483
- "process",
2484
- "crypto",
2485
- "http",
2486
- "https",
2487
- "net",
2488
- "zlib"
2489
- ],
1746
+ outputPaths.push("node_modules");
1747
+ }
1748
+ if (logoFilename) {
1749
+ const logoPath = path__namespace.join(process.cwd(), logoFilename);
1750
+ if (fs__namespace.existsSync(logoPath)) {
1751
+ console.log(simpleChalk.gray(`Adding logo from: ${logoPath}`));
1752
+ outputPaths.push({
1753
+ from: logoPath,
1754
+ to: path__namespace.join(options.outDir, logoFilename)
1755
+ });
1756
+ }
1757
+ }
1758
+ return outputPaths;
1759
+ },
2490
1760
  platform: "node",
2491
1761
  plugins: []
2492
1762
  };
@@ -2515,15 +1785,15 @@ export default ${JSON.stringify(buildConfig, null, 2)};
2515
1785
  }
2516
1786
  await build(buildConfig);
2517
1787
  console.log(simpleChalk.green("✅ TSDown bundling completed"));
2518
- const actualOutputFilename = this.detectTsdownOutputFile(
1788
+ const detectedOutputFile = this.detectTsdownOutputFile(
2519
1789
  outputDir,
2520
1790
  entryFileName
2521
1791
  );
2522
- await this.copyLogoManually(outputDir);
2523
1792
  await this.setupDxtPackageFiles(
2524
1793
  entryPointFile,
2525
1794
  outputDir,
2526
- actualOutputFilename ?? void 0
1795
+ detectedOutputFile ?? void 0,
1796
+ logoFilename ?? "logo.jpg"
2527
1797
  );
2528
1798
  console.log(simpleChalk.cyan("📦 DXT package ready for packing"));
2529
1799
  console.log(
@@ -2540,292 +1810,6 @@ export default ${JSON.stringify(buildConfig, null, 2)};
2540
1810
  );
2541
1811
  }
2542
1812
  }
2543
- /**
2544
- * Bundles the original CLI using TSDown for autonomous execution (legacy method)
2545
- */
2546
- async bundleOriginalCliWithTsdown(serverDir) {
2547
- try {
2548
- const { build } = await import("tsdown");
2549
- console.log(
2550
- simpleChalk.cyan("🔧 Bundling CLI with TSDown for autonomous execution...")
2551
- );
2552
- const configContent = this.getTsdownConfigContent();
2553
- const localConfigPath = path__namespace.join(serverDir, "tsdown.config.mjs");
2554
- fs__namespace.writeFileSync(localConfigPath, configContent);
2555
- const originalCliPath = path__namespace.join(serverDir, "original-cli.mjs");
2556
- if (!fs__namespace.existsSync(originalCliPath)) {
2557
- console.warn(
2558
- simpleChalk.yellow("⚠ Original CLI not found, skipping TSDown bundling")
2559
- );
2560
- return null;
2561
- }
2562
- const buildOptions = {
2563
- entry: ["original-cli.mjs"],
2564
- // Use relative path since we'll chdir to serverDir
2565
- outDir: ".",
2566
- // Output to current directory (serverDir)
2567
- format: "esm",
2568
- target: "node22",
2569
- // Bundle EVERYTHING except Node.js built-ins for true autonomy
2570
- noExternal: (id) => {
2571
- if (!id.startsWith("node:") && !this.isNodeBuiltin(id)) return true;
2572
- return false;
2573
- },
2574
- minify: false,
2575
- sourcemap: false,
2576
- clean: false,
2577
- outExtension: () => ({ js: ".bundled.mjs" }),
2578
- alias: {
2579
- // Alias chalk to SimpleChalk for autonomous builds
2580
- chalk: path__namespace.resolve(
2581
- process.cwd(),
2582
- "node_modules/@alcyone-labs/arg-parser/dist/SimpleChalk.mjs"
2583
- )
2584
- },
2585
- external: [
2586
- // Only Node.js built-ins - everything else gets bundled for true autonomy
2587
- "node:stream",
2588
- "node:fs",
2589
- "node:path",
2590
- "node:url",
2591
- "node:util",
2592
- "node:events",
2593
- "node:child_process",
2594
- "node:os",
2595
- "node:tty",
2596
- "node:process",
2597
- "node:crypto",
2598
- "node:http",
2599
- "node:https",
2600
- "node:net",
2601
- "node:zlib",
2602
- "node:fs/promises",
2603
- "node:timers",
2604
- "stream",
2605
- "fs",
2606
- "path",
2607
- "url",
2608
- "util",
2609
- "events",
2610
- "child_process",
2611
- "os",
2612
- "tty",
2613
- "process",
2614
- "crypto",
2615
- "http",
2616
- "https",
2617
- "net",
2618
- "zlib",
2619
- "fs/promises",
2620
- "timers",
2621
- "timers/promises",
2622
- "perf_hooks",
2623
- "async_hooks",
2624
- "inspector",
2625
- "v8",
2626
- "vm",
2627
- "assert",
2628
- "constants",
2629
- "module",
2630
- "repl",
2631
- "string_decoder",
2632
- "punycode",
2633
- "domain",
2634
- "querystring",
2635
- "readline",
2636
- "worker_threads",
2637
- "cluster",
2638
- "dgram",
2639
- "dns",
2640
- "buffer"
2641
- ],
2642
- platform: "node",
2643
- plugins: [],
2644
- // Resolve local dependencies properly
2645
- resolve: {
2646
- alias: {
2647
- // Handle local monorepo dependencies
2648
- "@alcyone-labs/arg-parser": path__namespace.resolve(process.cwd())
2649
- }
2650
- }
2651
- };
2652
- const originalCwd = process.cwd();
2653
- try {
2654
- process.chdir(serverDir);
2655
- await build(buildOptions);
2656
- } finally {
2657
- process.chdir(originalCwd);
2658
- }
2659
- const possibleBundledFiles = [
2660
- "original-cli.bundled.mjs",
2661
- "original-cli.js",
2662
- "original-cli.mjs"
2663
- ];
2664
- let bundledPath = null;
2665
- let bundledFileName = null;
2666
- for (const fileName of possibleBundledFiles) {
2667
- const filePath = path__namespace.join(serverDir, fileName);
2668
- if (fs__namespace.existsSync(filePath) && fileName !== "original-cli.mjs") {
2669
- bundledPath = filePath;
2670
- bundledFileName = fileName;
2671
- break;
2672
- }
2673
- }
2674
- if (bundledPath && bundledFileName) {
2675
- console.log(
2676
- simpleChalk.green(
2677
- `✅ TSDown bundling completed successfully: ${bundledFileName}`
2678
- )
2679
- );
2680
- const expectedBundledPath = path__namespace.join(
2681
- serverDir,
2682
- "original-cli.bundled.mjs"
2683
- );
2684
- if (bundledPath !== expectedBundledPath) {
2685
- fs__namespace.renameSync(bundledPath, expectedBundledPath);
2686
- bundledFileName = "original-cli.bundled.mjs";
2687
- }
2688
- try {
2689
- fs__namespace.unlinkSync(localConfigPath);
2690
- } catch (error) {
2691
- }
2692
- try {
2693
- fs__namespace.chmodSync(expectedBundledPath, 493);
2694
- } catch (error) {
2695
- console.warn(
2696
- "⚠ Could not set executable permission on bundled file:",
2697
- error instanceof Error ? error.message : String(error)
2698
- );
2699
- }
2700
- return bundledFileName;
2701
- } else {
2702
- console.warn(
2703
- simpleChalk.yellow("⚠ TSDown bundling failed, bundled file not found")
2704
- );
2705
- return null;
2706
- }
2707
- } catch (error) {
2708
- console.warn(
2709
- simpleChalk.yellow(
2710
- `⚠ TSDown bundling failed: ${error instanceof Error ? error.message : String(error)}`
2711
- )
2712
- );
2713
- console.log(simpleChalk.gray(" Falling back to non-bundled approach"));
2714
- return null;
2715
- }
2716
- }
2717
- /**
2718
- * Checks if a module ID is a Node.js built-in
2719
- */
2720
- isNodeBuiltin(id) {
2721
- const nodeBuiltins = [
2722
- "stream",
2723
- "fs",
2724
- "path",
2725
- "url",
2726
- "util",
2727
- "events",
2728
- "child_process",
2729
- "os",
2730
- "tty",
2731
- "process",
2732
- "crypto",
2733
- "http",
2734
- "https",
2735
- "net",
2736
- "zlib",
2737
- "fs/promises",
2738
- "timers",
2739
- "timers/promises",
2740
- "perf_hooks",
2741
- "async_hooks",
2742
- "inspector",
2743
- "v8",
2744
- "vm",
2745
- "assert",
2746
- "constants",
2747
- "module",
2748
- "repl",
2749
- "string_decoder",
2750
- "punycode",
2751
- "domain",
2752
- "querystring",
2753
- "readline",
2754
- "worker_threads",
2755
- "cluster",
2756
- "dgram",
2757
- "dns",
2758
- "buffer"
2759
- ];
2760
- return nodeBuiltins.includes(id) || id.startsWith("node:");
2761
- }
2762
- /**
2763
- * Gets the TSDown configuration content as a string
2764
- */
2765
- getTsdownConfigContent() {
2766
- const currentDir = path__namespace.dirname(new URL(typeof document === "undefined" ? require("url").pathToFileURL(__filename).href : _documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === "SCRIPT" && _documentCurrentScript.src || new URL("index.cjs", document.baseURI).href).pathname);
2767
- const assetsConfigPath = path__namespace.join(
2768
- currentDir,
2769
- "..",
2770
- "assets",
2771
- "tsdown.dxt.config.ts"
2772
- );
2773
- if (fs__namespace.existsSync(assetsConfigPath)) {
2774
- try {
2775
- const content = fs__namespace.readFileSync(assetsConfigPath, "utf-8");
2776
- return content.replace('/// <reference types="tsdown" />', "").replace(
2777
- 'import { defineConfig } from "tsdown/config";',
2778
- 'import { defineConfig } from "tsdown";'
2779
- ).replace(
2780
- "export default defineConfig(",
2781
- "export default defineConfig("
2782
- );
2783
- } catch (error) {
2784
- console.warn(
2785
- simpleChalk.yellow(
2786
- "⚠ Could not read TSDown config from assets, using fallback"
2787
- )
2788
- );
2789
- }
2790
- }
2791
- const rootConfigPath = path__namespace.join(process.cwd(), "tsdown.dxt.config.ts");
2792
- if (fs__namespace.existsSync(rootConfigPath)) {
2793
- try {
2794
- const content = fs__namespace.readFileSync(rootConfigPath, "utf-8");
2795
- return content.replace('/// <reference types="tsdown" />', "").replace(
2796
- 'import { defineConfig } from "tsdown/config";',
2797
- 'import { defineConfig } from "tsdown";'
2798
- );
2799
- } catch (error) {
2800
- console.warn(
2801
- simpleChalk.yellow(
2802
- "⚠ Could not read TSDown config from root, using default"
2803
- )
2804
- );
2805
- }
2806
- }
2807
- return `import { defineConfig } from "tsdown";
2808
- import path from "path";
2809
-
2810
- export default defineConfig({
2811
- outDir: "server",
2812
- format: ["esm", "module"],
2813
- target: "node22",
2814
- noExternal: () => true,
2815
- minify: false,
2816
- sourcemap: false,
2817
- clean: false,
2818
- alias: {
2819
- chalk: path.resolve(process.cwd(), "node_modules/@alcyone-labs/arg-parser/dist/SimpleChalk.mjs"),
2820
- },
2821
- external: [
2822
- "stream", "fs", "path", "url", "util", "events", "child_process",
2823
- "os", "tty", "process", "crypto", "http", "https", "net", "zlib",
2824
- ],
2825
- platform: "node",
2826
- plugins: [],
2827
- });`;
2828
- }
2829
1813
  /**
2830
1814
  * Gets the path to the .dxtignore template file in assets
2831
1815
  */
@@ -2865,8 +1849,8 @@ export default defineConfig({
2865
1849
  /**
2866
1850
  * Sets up DXT package files (manifest.json) in the output directory
2867
1851
  */
2868
- async setupDxtPackageFiles(entryPointFile, outputDir = "./dxt", actualOutputFilename) {
2869
- var _a, _b, _c, _d, _e;
1852
+ async setupDxtPackageFiles(entryPointFile, outputDir = "./dxt", actualOutputFilename, logoFilename = "logo.jpg") {
1853
+ var _a, _b, _c, _d, _e, _f;
2870
1854
  const dxtDir = path__namespace.resolve(process.cwd(), outputDir);
2871
1855
  if (!fs__namespace.existsSync(dxtDir)) {
2872
1856
  throw new Error(`TSDown output directory (${outputDir}) not found`);
@@ -2893,10 +1877,10 @@ export default defineConfig({
2893
1877
  `Warning: Could not generate unified tool list: ${error instanceof Error ? error.message : String(error)}`
2894
1878
  )
2895
1879
  );
2896
- const mainFlags2 = this.argParserInstance.flags;
1880
+ const mainFlags = this.argParserInstance.flags;
2897
1881
  const properties2 = {};
2898
1882
  const required2 = [];
2899
- for (const flag of mainFlags2) {
1883
+ for (const flag of mainFlags) {
2900
1884
  if (flag.name === "help" || flag.name.startsWith("s-")) continue;
2901
1885
  properties2[flag.name] = {
2902
1886
  type: getJsonSchemaTypeFromFlag(flag.type),
@@ -2920,58 +1904,17 @@ export default defineConfig({
2920
1904
  }
2921
1905
  ];
2922
1906
  }
2923
- const envVars = {};
2924
- const userConfig = {};
2925
- const mainFlags = this.argParserInstance.flags;
2926
- for (const flag of mainFlags) {
2927
- const envVar = flag.env || flag.envVar;
2928
- if (envVar) {
2929
- envVars[envVar] = `\${user_config.${envVar}}`;
2930
- userConfig[envVar] = {
2931
- type: "string",
2932
- title: envVar.replace(/_/g, " ").replace(/\b\w/g, (l) => l.toUpperCase()),
2933
- description: flag.description || `${envVar} environment variable`,
2934
- required: true,
2935
- // Always require env vars in user_config for better UX
2936
- sensitive: true
2937
- // Assume env vars are sensitive
2938
- };
2939
- }
2940
- }
2941
- if (typeof this.argParserInstance.getTools === "function") {
2942
- const tools2 = this.argParserInstance.getTools();
2943
- for (const [, toolConfig] of tools2) {
2944
- const toolFlags = toolConfig.flags || [];
2945
- for (const flag of toolFlags) {
2946
- const envVar = flag.env || flag.envVar;
2947
- if (envVar && !envVars[envVar]) {
2948
- envVars[envVar] = `\${user_config.${envVar}}`;
2949
- userConfig[envVar] = {
2950
- type: "string",
2951
- title: envVar.replace(/_/g, " ").replace(/\b\w/g, (l) => l.toUpperCase()),
2952
- description: flag.description || `${envVar} environment variable`,
2953
- required: true,
2954
- // Always require env vars in user_config for better UX
2955
- sensitive: true
2956
- // Assume env vars are sensitive
2957
- };
2958
- }
2959
- }
2960
- }
2961
- }
1907
+ const { envVars, userConfig } = this.generateEnvAndUserConfig();
2962
1908
  const serverInfo = this.extractMcpServerInfo();
2963
- let logoFilename = "logo.jpg";
2964
- if (serverInfo == null ? void 0 : serverInfo.logo) {
2965
- const customLogoFilename = await this.addLogoToFolder(
2966
- dxtDir,
2967
- serverInfo,
2968
- entryPointFile
2969
- );
2970
- if (customLogoFilename) {
2971
- logoFilename = customLogoFilename;
2972
- }
1909
+ let entryFileName;
1910
+ if (actualOutputFilename) {
1911
+ entryFileName = actualOutputFilename;
1912
+ } else {
1913
+ const projectRoot = this.findProjectRoot(entryPointFile);
1914
+ const absoluteEntryPath = path__namespace.resolve(entryPointFile);
1915
+ const relativeEntryPath = path__namespace.relative(projectRoot, absoluteEntryPath);
1916
+ entryFileName = relativeEntryPath.replace(/\.ts$/, ".js");
2973
1917
  }
2974
- const entryFileName = actualOutputFilename || path__namespace.basename(entryPointFile).replace(/\.ts$/, ".js");
2975
1918
  const manifest = {
2976
1919
  dxt_version: "0.1",
2977
1920
  name: serverInfo.name || packageInfo.name || "mcp-server",
@@ -2987,17 +1930,23 @@ export default defineConfig({
2987
1930
  entry_point: entryFileName,
2988
1931
  mcp_config: {
2989
1932
  command: "node",
2990
- args: [`\${__dirname}/${entryFileName}`, "--s-mcp-serve"],
1933
+ args: [
1934
+ `\${__dirname}/${entryFileName}`,
1935
+ "--s-mcp-serve",
1936
+ // Overwrite the CLI config to only use stdio to avoid conflicts
1937
+ "--s-mcp-transport",
1938
+ "stdio"
1939
+ ],
2991
1940
  env: envVars
2992
1941
  }
2993
1942
  },
2994
1943
  tools,
2995
1944
  icon: logoFilename,
2996
1945
  ...Object.keys(userConfig).length > 0 && { user_config: userConfig },
2997
- repository: {
1946
+ repository: ((_e = packageInfo.repository) == null ? void 0 : _e.url) ? {
2998
1947
  type: "git",
2999
- url: ((_e = packageInfo.repository) == null ? void 0 : _e.url) || "https://github.com/alcyone-labs/arg-parser"
3000
- },
1948
+ url: (_f = packageInfo.repository) == null ? void 0 : _f.url
1949
+ } : void 0,
3001
1950
  license: packageInfo.license || "MIT"
3002
1951
  };
3003
1952
  fs__namespace.writeFileSync(
@@ -3006,66 +1955,6 @@ export default defineConfig({
3006
1955
  );
3007
1956
  console.log(simpleChalk.gray("✅ DXT package files set up"));
3008
1957
  }
3009
- /**
3010
- * Manually copy logo since TSDown's copy option doesn't work programmatically
3011
- */
3012
- async copyLogoManually(outputDir = "./dxt") {
3013
- const dxtDir = path__namespace.resolve(process.cwd(), outputDir);
3014
- if (!fs__namespace.existsSync(dxtDir)) {
3015
- console.warn(
3016
- simpleChalk.yellow(
3017
- `⚠ Output directory (${outputDir}) not found, skipping logo copy`
3018
- )
3019
- );
3020
- return;
3021
- }
3022
- const possibleLogoPaths = [
3023
- // From built library assets
3024
- path__namespace.join(
3025
- path__namespace.dirname(new URL(typeof document === "undefined" ? require("url").pathToFileURL(__filename).href : _documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === "SCRIPT" && _documentCurrentScript.src || new URL("index.cjs", document.baseURI).href).pathname),
3026
- "..",
3027
- "assets",
3028
- "logo_1_small.jpg"
3029
- ),
3030
- // From node_modules
3031
- path__namespace.join(
3032
- process.cwd(),
3033
- "node_modules",
3034
- "@alcyone-labs",
3035
- "arg-parser",
3036
- "dist",
3037
- "assets",
3038
- "logo_1_small.jpg"
3039
- ),
3040
- // From package root dist/assets (for local build)
3041
- path__namespace.join(process.cwd(), "dist", "assets", "logo_1_small.jpg"),
3042
- // From library root (development)
3043
- path__namespace.join(
3044
- process.cwd(),
3045
- "..",
3046
- "..",
3047
- "..",
3048
- "docs",
3049
- "MCP",
3050
- "icons",
3051
- "logo_1_small.jpg"
3052
- )
3053
- ];
3054
- for (const logoPath of possibleLogoPaths) {
3055
- if (fs__namespace.existsSync(logoPath)) {
3056
- try {
3057
- fs__namespace.copyFileSync(logoPath, path__namespace.join(dxtDir, "logo.jpg"));
3058
- console.log(simpleChalk.gray(`✅ Logo copied from: ${logoPath}`));
3059
- return;
3060
- } catch (error) {
3061
- console.warn(
3062
- simpleChalk.yellow(`⚠ Failed to copy logo from ${logoPath}: ${error}`)
3063
- );
3064
- }
3065
- }
3066
- }
3067
- console.warn(simpleChalk.yellow("⚠ Logo not found in any expected location"));
3068
- }
3069
1958
  /**
3070
1959
  * Detects the actual output filename generated by TSDown
3071
1960
  */
@@ -3130,6 +2019,72 @@ export default defineConfig({
3130
2019
  return null;
3131
2020
  }
3132
2021
  }
2022
+ findProjectRoot(entryPointFile) {
2023
+ let currentDir = path__namespace.dirname(path__namespace.resolve(entryPointFile));
2024
+ let attempts = 0;
2025
+ const maxAttempts = 5;
2026
+ while (attempts < maxAttempts) {
2027
+ const packageJsonPath = path__namespace.join(currentDir, "package.json");
2028
+ if (fs__namespace.existsSync(packageJsonPath)) {
2029
+ return currentDir;
2030
+ }
2031
+ const parentDir = path__namespace.dirname(currentDir);
2032
+ if (parentDir === currentDir) {
2033
+ break;
2034
+ }
2035
+ currentDir = parentDir;
2036
+ attempts++;
2037
+ }
2038
+ throw new Error(
2039
+ `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.`
2040
+ );
2041
+ }
2042
+ /**
2043
+ * Generate environment variables and user configuration from ArgParser flags
2044
+ * @returns Object containing envVars and userConfig
2045
+ */
2046
+ generateEnvAndUserConfig() {
2047
+ const envVars = {};
2048
+ const userConfig = {};
2049
+ const mainFlags = this.argParserInstance.flags;
2050
+ for (const flag of mainFlags) {
2051
+ const envVar = flag.env || flag.envVar;
2052
+ if (envVar) {
2053
+ envVars[envVar] = `\${user_config.${envVar}}`;
2054
+ userConfig[envVar] = {
2055
+ type: "string",
2056
+ title: envVar.replace(/_/g, " ").replace(/\b\w/g, (l) => l.toUpperCase()),
2057
+ description: flag.description || `${envVar} environment variable`,
2058
+ required: true,
2059
+ // Always require env vars in user_config for better UX
2060
+ sensitive: true
2061
+ // Assume env vars are sensitive
2062
+ };
2063
+ }
2064
+ }
2065
+ if (typeof this.argParserInstance.getTools === "function") {
2066
+ const tools = this.argParserInstance.getTools();
2067
+ for (const [, toolConfig] of tools) {
2068
+ const toolFlags = toolConfig.flags || [];
2069
+ for (const flag of toolFlags) {
2070
+ const envVar = flag.env || flag.envVar;
2071
+ if (envVar && !envVars[envVar]) {
2072
+ envVars[envVar] = `\${user_config.${envVar}}`;
2073
+ userConfig[envVar] = {
2074
+ type: "string",
2075
+ title: envVar.replace(/_/g, " ").replace(/\b\w/g, (l) => l.toUpperCase()),
2076
+ description: flag.description || `${envVar} environment variable`,
2077
+ required: true,
2078
+ // Always require env vars in user_config for better UX
2079
+ sensitive: true
2080
+ // Assume env vars are sensitive
2081
+ };
2082
+ }
2083
+ }
2084
+ }
2085
+ }
2086
+ return { envVars, userConfig };
2087
+ }
3133
2088
  }
3134
2089
  class McpNotificationsManager {
3135
2090
  constructor() {
@@ -5184,9 +4139,7 @@ _handleMcpServeFlag_fn = async function(processArgs, _mcpServeIndex) {
5184
4139
  const resolvedLogPath = resolveLogPath(effectiveLogPath);
5185
4140
  let mcpLogger;
5186
4141
  try {
5187
- const mcpLoggerModule = await Function(
5188
- 'return import("@alcyone-labs/simple-mcp-logger")'
5189
- )();
4142
+ const mcpLoggerModule = await import("@alcyone-labs/simple-mcp-logger");
5190
4143
  mcpLogger = mcpLoggerModule.createMcpLogger("MCP Serve", resolvedLogPath);
5191
4144
  globalThis.console = mcpLogger;
5192
4145
  } catch {
@@ -5297,6 +4250,20 @@ _startUnifiedMcpServer_fn = async function(mcpServerConfig, transportOptions) {
5297
4250
  `Error parsing transports configuration: ${error.message}. Expected JSON format: '[{"type":"stdio"},{"type":"sse","port":3001}]'`
5298
4251
  );
5299
4252
  }
4253
+ } else if (transportOptions.transportType) {
4254
+ const transportType = transportOptions.transportType;
4255
+ const finalTransportOptions = {
4256
+ port: transportOptions.port,
4257
+ host: transportOptions.host || "localhost",
4258
+ path: transportOptions.path || "/mcp"
4259
+ };
4260
+ await mcpParser.startMcpServerWithTransport(
4261
+ serverInfo,
4262
+ transportType,
4263
+ finalTransportOptions,
4264
+ toolOptions,
4265
+ transportOptions.logPath
4266
+ );
5300
4267
  } else if (defaultTransports && defaultTransports.length > 0) {
5301
4268
  await mcpParser.startMcpServerWithMultipleTransports(
5302
4269
  serverInfo,
@@ -5318,16 +4285,10 @@ _startUnifiedMcpServer_fn = async function(mcpServerConfig, transportOptions) {
5318
4285
  transportOptions.logPath
5319
4286
  );
5320
4287
  } else {
5321
- const transportType = transportOptions.transportType || "stdio";
5322
- const finalTransportOptions = {
5323
- port: transportOptions.port,
5324
- host: transportOptions.host || "localhost",
5325
- path: transportOptions.path || "/mcp"
5326
- };
5327
4288
  await mcpParser.startMcpServerWithTransport(
5328
4289
  serverInfo,
5329
- transportType,
5330
- finalTransportOptions,
4290
+ "stdio",
4291
+ {},
5331
4292
  toolOptions,
5332
4293
  transportOptions.logPath
5333
4294
  );
@@ -8854,6 +7815,7 @@ class Protocol {
8854
7815
  this._responseHandlers = /* @__PURE__ */ new Map();
8855
7816
  this._progressHandlers = /* @__PURE__ */ new Map();
8856
7817
  this._timeoutInfo = /* @__PURE__ */ new Map();
7818
+ this._pendingDebouncedNotifications = /* @__PURE__ */ new Set();
8857
7819
  this.setNotificationHandler(CancelledNotificationSchema, (notification) => {
8858
7820
  const controller = this._requestHandlerAbortControllers.get(notification.params.requestId);
8859
7821
  controller === null || controller === void 0 ? void 0 : controller.abort(notification.params.reason);
@@ -8935,6 +7897,7 @@ class Protocol {
8935
7897
  const responseHandlers = this._responseHandlers;
8936
7898
  this._responseHandlers = /* @__PURE__ */ new Map();
8937
7899
  this._progressHandlers.clear();
7900
+ this._pendingDebouncedNotifications.clear();
8938
7901
  this._transport = void 0;
8939
7902
  (_a = this.onclose) === null || _a === void 0 ? void 0 : _a.call(this);
8940
7903
  const error = new McpError(ErrorCode.ConnectionClosed, "Connection closed");
@@ -9134,10 +8097,32 @@ class Protocol {
9134
8097
  * Emits a notification, which is a one-way message that does not expect a response.
9135
8098
  */
9136
8099
  async notification(notification, options) {
8100
+ var _a, _b;
9137
8101
  if (!this._transport) {
9138
8102
  throw new Error("Not connected");
9139
8103
  }
9140
8104
  this.assertNotificationCapability(notification.method);
8105
+ const debouncedMethods = (_b = (_a = this._options) === null || _a === void 0 ? void 0 : _a.debouncedNotificationMethods) !== null && _b !== void 0 ? _b : [];
8106
+ const canDebounce = debouncedMethods.includes(notification.method) && !notification.params && !(options === null || options === void 0 ? void 0 : options.relatedRequestId);
8107
+ if (canDebounce) {
8108
+ if (this._pendingDebouncedNotifications.has(notification.method)) {
8109
+ return;
8110
+ }
8111
+ this._pendingDebouncedNotifications.add(notification.method);
8112
+ Promise.resolve().then(() => {
8113
+ var _a2;
8114
+ this._pendingDebouncedNotifications.delete(notification.method);
8115
+ if (!this._transport) {
8116
+ return;
8117
+ }
8118
+ const jsonrpcNotification2 = {
8119
+ ...notification,
8120
+ jsonrpc: "2.0"
8121
+ };
8122
+ (_a2 = this._transport) === null || _a2 === void 0 ? void 0 : _a2.send(jsonrpcNotification2, options).catch((error) => this._onerror(error));
8123
+ });
8124
+ return;
8125
+ }
9141
8126
  const jsonrpcNotification = {
9142
8127
  ...notification,
9143
8128
  jsonrpc: "2.0"