@alcyone-labs/arg-parser 2.6.0 → 2.7.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.mjs CHANGED
@@ -12,6 +12,7 @@ var __privateMethod = (obj, member, method) => (__accessCheck(obj, member, "acce
12
12
  var __flags, _throwForDuplicateFlags, _appName, _appCommandName, _subCommandName, _parameters, _handler, _throwForDuplicateFlags2, _description, _handleErrors, _autoExit, _parentParser, _lastParseResult, _inheritParentFlags, _subCommands, _flagManager, _dxtGenerator, _configurationManager, _fuzzyMode, _mcpResourcesManager, _mcpPromptsManager, _mcpNotificationsManager, _ArgParserBase_instances, _identifyCommandChainAndParsers_fn, _handleGlobalChecks_fn, _validateMandatoryFlags_fn, _applyDefaultValues_fn, _prepareAndExecuteHandler_fn, parseFlags_fn, _enableFuzzyMode_fn, displayErrorAndExit_fn, _printRecursiveToConsole_fn, _buildRecursiveString_fn, _buildRecursiveJson_fn, _handleSaveToEnvFlag_fn, _handleBuildDxtFlag_fn, _handleMcpServeFlag_fn, _resolveLoggerConfigForServe_fn, _getMcpServerConfiguration_fn, _startUnifiedMcpServer_fn, _findAllMcpSubCommands_fn, _parseMcpTransportOptions_fn, _ArgParser_instances, _resolveLoggerConfig_fn, registerToolAsSubCommand_fn, _startSingleTransport_fn, processAsyncHandlerPromise_fn, _hasDate, _hasTime, _offset;
13
13
  import * as fs from "node:fs";
14
14
  import * as path from "node:path";
15
+ import { fileURLToPath } from "node:url";
15
16
  import { createRegExp, anyOf as anyOf$1, oneOrMore, char } from "magic-regexp";
16
17
  import { getTsconfig, createPathsMatcher } from "get-tsconfig";
17
18
  import { z } from "zod";
@@ -3911,22 +3912,41 @@ const _ArgParserBase = class _ArgParserBase {
3911
3912
  console.log("--- End Configuration Dump ---\\n");
3912
3913
  }
3913
3914
  }
3915
+ /**
3916
+ * Detects if the current script is being executed directly (not imported)
3917
+ * Uses a robust method that works across different environments and sandboxes
3918
+ * @param importMetaUrl The import.meta.url from the calling script (optional)
3919
+ * @returns true if the script is being executed directly, false if imported
3920
+ */
3921
+ static isExecutedDirectly(importMetaUrl) {
3922
+ try {
3923
+ if (importMetaUrl) {
3924
+ const currentFile = fileURLToPath(importMetaUrl);
3925
+ const executedFile = path.resolve(process.argv[1]);
3926
+ return currentFile === executedFile;
3927
+ }
3928
+ if (typeof process !== "undefined" && process.argv && process.argv[1]) {
3929
+ return false;
3930
+ }
3931
+ return false;
3932
+ } catch {
3933
+ return false;
3934
+ }
3935
+ }
3914
3936
  async parse(processArgs, options) {
3915
- var _a, _b;
3937
+ var _a;
3916
3938
  debug.log("ArgParserBase.parse() called with args:", processArgs);
3939
+ const shouldCheckAutoExecution = (options == null ? void 0 : options.importMetaUrl) && (options == null ? void 0 : options.autoExecute) !== false;
3940
+ if (shouldCheckAutoExecution) {
3941
+ const isDirectExecution = _ArgParserBase.isExecutedDirectly(options.importMetaUrl);
3942
+ if (!isDirectExecution) {
3943
+ debug.log("Auto-execution enabled but script is imported, skipping execution");
3944
+ return {};
3945
+ }
3946
+ }
3917
3947
  if (processArgs === void 0) {
3918
3948
  if (typeof process !== "undefined" && process.argv && Array.isArray(process.argv)) {
3919
3949
  processArgs = process.argv.slice(2);
3920
- const isCliMode = !__privateGet(this, _parentParser) && !!__privateGet(this, _appCommandName);
3921
- const isMcpMode = (options == null ? void 0 : options.isMcp) || ((_a = globalThis.console) == null ? void 0 : _a.mcpError);
3922
- if (isCliMode && !isMcpMode) {
3923
- console.warn(
3924
- `Warning: parse() called without arguments. Auto-detected Node.js environment and using process.argv.slice(2).`
3925
- );
3926
- console.warn(
3927
- `For explicit control, call parse(process.argv.slice(2)) instead.`
3928
- );
3929
- }
3930
3950
  } else {
3931
3951
  throw new Error(
3932
3952
  "parse() called without arguments in non-Node.js environment. Please provide arguments explicitly: parse(['--flag', 'value'])"
@@ -3951,7 +3971,7 @@ const _ArgParserBase = class _ArgParserBase {
3951
3971
  commandChain: identifiedCommandChain,
3952
3972
  parserChain: identifiedParserChain
3953
3973
  } = __privateMethod(this, _ArgParserBase_instances, _identifyCommandChainAndParsers_fn).call(this, processArgs, this, [], [this]);
3954
- const saveToEnvResult = __privateMethod(_b = identifiedFinalParser, _ArgParserBase_instances, _handleSaveToEnvFlag_fn).call(_b, processArgs, identifiedParserChain);
3974
+ const saveToEnvResult = __privateMethod(_a = identifiedFinalParser, _ArgParserBase_instances, _handleSaveToEnvFlag_fn).call(_a, processArgs, identifiedParserChain);
3955
3975
  if (saveToEnvResult !== false) {
3956
3976
  return saveToEnvResult === true ? {} : saveToEnvResult;
3957
3977
  }
@@ -5270,7 +5290,10 @@ _startUnifiedMcpServer_fn = async function(mcpServerConfig, transportOptions) {
5270
5290
  const finalTransportOptions = {
5271
5291
  port: transportOptions.port,
5272
5292
  host: transportOptions.host || "localhost",
5273
- path: transportOptions.path || "/mcp"
5293
+ path: transportOptions.path || "/mcp",
5294
+ // Pass-through for streamable-http only; harmlessly ignored for others
5295
+ cors: transportOptions.cors,
5296
+ auth: transportOptions.auth
5274
5297
  };
5275
5298
  await mcpParser.startMcpServerWithTransport(
5276
5299
  serverInfo,
@@ -5378,6 +5401,27 @@ _parseMcpTransportOptions_fn = function(processArgs) {
5378
5401
  i++;
5379
5402
  }
5380
5403
  break;
5404
+ // Streamable HTTP extras (accept JSON string)
5405
+ case "--s-mcp-cors":
5406
+ if (nextArg && !nextArg.startsWith("-")) {
5407
+ try {
5408
+ options.cors = JSON.parse(nextArg);
5409
+ } catch {
5410
+ options.cors = nextArg;
5411
+ }
5412
+ i++;
5413
+ }
5414
+ break;
5415
+ case "--s-mcp-auth":
5416
+ if (nextArg && !nextArg.startsWith("-")) {
5417
+ try {
5418
+ options.auth = JSON.parse(nextArg);
5419
+ } catch {
5420
+ options.auth = nextArg;
5421
+ }
5422
+ i++;
5423
+ }
5424
+ break;
5381
5425
  // Backward compatibility: support old flags but with deprecation warning
5382
5426
  case "--transport":
5383
5427
  case "--port":
@@ -7415,6 +7459,34 @@ Migration guide: https://github.com/alcyone-labs/arg-parser/blob/main/docs/MCP-M
7415
7459
  parseAsync(processArgs, options) {
7416
7460
  return this.parse(processArgs, options);
7417
7461
  }
7462
+ /**
7463
+ * Convenience method for auto-execution: only runs if the script is executed directly (not imported).
7464
+ * This eliminates the need for boilerplate code to check if the script is being run directly.
7465
+ *
7466
+ * @param importMetaUrl Pass import.meta.url from your script for reliable detection
7467
+ * @param processArgs Optional arguments to parse (defaults to process.argv.slice(2))
7468
+ * @param options Additional parse options
7469
+ * @returns Promise that resolves to the parse result, or empty object if script is imported
7470
+ *
7471
+ * @example
7472
+ * ```typescript
7473
+ * // At the bottom of your CLI script:
7474
+ * await cli.parseIfExecutedDirectly(import.meta.url);
7475
+ *
7476
+ * // With error handling:
7477
+ * await cli.parseIfExecutedDirectly(import.meta.url).catch((error) => {
7478
+ * console.error("Fatal error:", error instanceof Error ? error.message : String(error));
7479
+ * process.exit(1);
7480
+ * });
7481
+ * ```
7482
+ */
7483
+ async parseIfExecutedDirectly(importMetaUrl, processArgs, options) {
7484
+ return this.parse(processArgs, {
7485
+ ...options,
7486
+ autoExecute: true,
7487
+ importMetaUrl
7488
+ });
7489
+ }
7418
7490
  addMcpSubCommand(subCommandName = "mcp-server", serverInfo, optionsOrToolOptions) {
7419
7491
  console.warn(`[DEPRECATED] addMcpSubCommand() is deprecated and will be removed in v2.0.
7420
7492
  Please use withMcp() to configure server metadata and the --s-mcp-serve system flag instead.
@@ -7693,6 +7765,7 @@ registerToolAsSubCommand_fn = function(toolConfig) {
7693
7765
  });
7694
7766
  };
7695
7767
  _startSingleTransport_fn = async function(server, serverInfo, transportConfig, logPath) {
7768
+ var _a, _b, _c, _d, _e, _f;
7696
7769
  const resolvedLogPath = resolveLogPath(logPath || "./logs/mcp.log");
7697
7770
  const logger2 = createMcpLogger("MCP Transport", resolvedLogPath);
7698
7771
  try {
@@ -7736,10 +7809,142 @@ _startSingleTransport_fn = async function(server, serverInfo, transportConfig, l
7736
7809
  const express = (await import("express")).default;
7737
7810
  const app = express();
7738
7811
  app.use(express.json());
7812
+ try {
7813
+ (_c = (_b = (_a = this._mcpServerConfig) == null ? void 0 : _a.httpServer) == null ? void 0 : _b.configureExpress) == null ? void 0 : _c.call(_b, app);
7814
+ } catch (e) {
7815
+ }
7739
7816
  const port = transportConfig.port || 3e3;
7740
7817
  const path2 = transportConfig.path || "/mcp";
7818
+ if (transportConfig.cors) {
7819
+ const cors = transportConfig.cors;
7820
+ const allowMethods = ((_d = cors.methods) == null ? void 0 : _d.join(", ")) || "GET,POST,PUT,PATCH,DELETE,OPTIONS";
7821
+ const allowHeaders = (req) => {
7822
+ var _a2;
7823
+ return ((_a2 = cors.headers) == null ? void 0 : _a2.join(", ")) || req.headers["access-control-request-headers"] || "Content-Type, Authorization, MCP-Session-Id";
7824
+ };
7825
+ const exposed = ((_e = cors.exposedHeaders) == null ? void 0 : _e.join(", ")) || void 0;
7826
+ const resolveOrigin = (req) => {
7827
+ const reqOrigin = req.headers.origin;
7828
+ const origins = cors.origins ?? "*";
7829
+ if (origins === "*") return cors.credentials ? reqOrigin : "*";
7830
+ if (!reqOrigin) return void 0;
7831
+ const list = Array.isArray(origins) ? origins : [origins];
7832
+ for (const o of list) {
7833
+ if (typeof o === "string" && o === reqOrigin) return reqOrigin;
7834
+ if (o instanceof RegExp && o.test(reqOrigin)) return reqOrigin;
7835
+ }
7836
+ return void 0;
7837
+ };
7838
+ const applyCorsHeaders = (req, res) => {
7839
+ const origin = resolveOrigin(req);
7840
+ if (origin) res.setHeader("Access-Control-Allow-Origin", origin);
7841
+ if (cors.credentials) res.setHeader("Access-Control-Allow-Credentials", "true");
7842
+ res.setHeader("Vary", "Origin");
7843
+ res.setHeader("Access-Control-Allow-Methods", allowMethods);
7844
+ const hdrs = allowHeaders(req);
7845
+ if (hdrs) res.setHeader("Access-Control-Allow-Headers", hdrs);
7846
+ if (exposed) res.setHeader("Access-Control-Expose-Headers", exposed);
7847
+ if (typeof cors.maxAge === "number") res.setHeader("Access-Control-Max-Age", String(cors.maxAge));
7848
+ };
7849
+ app.options(path2, (req, res) => {
7850
+ applyCorsHeaders(req, res);
7851
+ res.status(204).end();
7852
+ });
7853
+ app.use((req, res, next) => {
7854
+ if (req.path === path2) applyCorsHeaders(req, res);
7855
+ next();
7856
+ });
7857
+ }
7858
+ if ((_f = transportConfig.auth) == null ? void 0 : _f.customMiddleware) {
7859
+ app.use(transportConfig.auth.customMiddleware);
7860
+ }
7861
+ const authOpts = transportConfig.auth;
7862
+ const shouldRequireAuthFor = (req) => {
7863
+ if (!authOpts) return false;
7864
+ const reqPath = req.path;
7865
+ const pub = authOpts.publicPaths || [];
7866
+ const prot = authOpts.protectedPaths;
7867
+ if (pub.includes(reqPath)) return false;
7868
+ if (prot && !prot.includes(reqPath)) return false;
7869
+ return authOpts.required !== false;
7870
+ };
7871
+ const base64urlDecode = (s) => Buffer.from(s.replace(/-/g, "+").replace(/_/g, "/"), "base64");
7872
+ const verifyJwt = async (token) => {
7873
+ if (!(authOpts == null ? void 0 : authOpts.jwt)) return false;
7874
+ const [h, p, sig] = token.split(".");
7875
+ if (!h || !p || !sig) return false;
7876
+ const header = JSON.parse(base64urlDecode(h).toString("utf8"));
7877
+ const payload = JSON.parse(base64urlDecode(p).toString("utf8"));
7878
+ const alg = header.alg;
7879
+ if (authOpts.jwt.algorithms && !authOpts.jwt.algorithms.includes(alg)) return false;
7880
+ const data2 = Buffer.from(`${h}.${p}`);
7881
+ const signature = base64urlDecode(sig);
7882
+ if (alg === "HS256") {
7883
+ const secret = authOpts.jwt.secret;
7884
+ if (!secret) return false;
7885
+ const hmac = (await import("node:crypto")).createHmac("sha256", secret).update(data2).digest();
7886
+ if (!hmac.equals(signature)) return false;
7887
+ } else if (alg === "RS256") {
7888
+ const crypto = await import("node:crypto");
7889
+ let key = authOpts.jwt.publicKey;
7890
+ if (!key && authOpts.jwt.getPublicKey) {
7891
+ key = await authOpts.jwt.getPublicKey(header, payload);
7892
+ }
7893
+ if (!key) return false;
7894
+ const verify = crypto.createVerify("RSA-SHA256");
7895
+ verify.update(data2);
7896
+ verify.end();
7897
+ const ok = verify.verify(key, signature);
7898
+ if (!ok) return false;
7899
+ } else {
7900
+ return false;
7901
+ }
7902
+ if (authOpts.jwt.audience) {
7903
+ const allowed = Array.isArray(authOpts.jwt.audience) ? authOpts.jwt.audience : [authOpts.jwt.audience];
7904
+ if (!allowed.includes(payload.aud)) return false;
7905
+ }
7906
+ if (authOpts.jwt.issuer) {
7907
+ const allowed = Array.isArray(authOpts.jwt.issuer) ? authOpts.jwt.issuer : [authOpts.jwt.issuer];
7908
+ if (!allowed.includes(payload.iss)) return false;
7909
+ }
7910
+ const nowSec = Math.floor(Date.now() / 1e3);
7911
+ const tol = authOpts.jwt.clockToleranceSec || 0;
7912
+ if (payload.nbf && nowSec + tol < payload.nbf) return false;
7913
+ if (payload.exp && nowSec - tol >= payload.exp) return false;
7914
+ return true;
7915
+ };
7916
+ const authenticate = async (req) => {
7917
+ if (!authOpts) return true;
7918
+ const authz = req.headers.authorization;
7919
+ const token = (authz == null ? void 0 : authz.startsWith("Bearer ")) ? authz.slice(7) : void 0;
7920
+ if (!token) {
7921
+ if (authOpts.validator) return !!await authOpts.validator(req, token);
7922
+ return false;
7923
+ }
7924
+ if (authOpts.scheme === "jwt" || authOpts.jwt) {
7925
+ const ok = await verifyJwt(token);
7926
+ if (!ok) return false;
7927
+ } else if (authOpts.scheme === "bearer" || !authOpts.scheme) {
7928
+ if (authOpts.allowedTokens && !authOpts.allowedTokens.includes(token)) {
7929
+ if (authOpts.validator) return !!await authOpts.validator(req, token);
7930
+ return false;
7931
+ }
7932
+ }
7933
+ if (authOpts.validator) {
7934
+ const ok = await authOpts.validator(req, token);
7935
+ if (!ok) return false;
7936
+ }
7937
+ return true;
7938
+ };
7741
7939
  const transports = {};
7742
7940
  app.all(path2, async (req, res) => {
7941
+ if (shouldRequireAuthFor(req)) {
7942
+ const ok = await authenticate(req);
7943
+ if (!ok) {
7944
+ res.status(401).json({ error: "Unauthorized" });
7945
+ return;
7946
+ }
7947
+ }
7743
7948
  const sessionId = req.headers["mcp-session-id"];
7744
7949
  let transport;
7745
7950
  if (sessionId && transports[sessionId]) {
@@ -7752,9 +7957,7 @@ _startSingleTransport_fn = async function(server, serverInfo, transportConfig, l
7752
7957
  }
7753
7958
  });
7754
7959
  transport.onclose = () => {
7755
- if (transport.sessionId) {
7756
- delete transports[transport.sessionId];
7757
- }
7960
+ if (transport.sessionId) delete transports[transport.sessionId];
7758
7961
  };
7759
7962
  await server.connect(transport);
7760
7963
  }