@chkp/harmony-email-mcp 0.1.1 → 0.1.4

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.js CHANGED
@@ -365,6 +365,84 @@ var init_ui_dialog = __esm({
365
365
  }
366
366
  });
367
367
 
368
+ // ../mcp-utils/dist/cli-options.js
369
+ function booleanOptionDefault(envValue, optionDefault) {
370
+ return envValue === "true" || optionDefault === "true";
371
+ }
372
+ function resolveTransportType(transportOption, env = process.env) {
373
+ return (transportOption || env.MCP_TRANSPORT_TYPE || "stdio").toLowerCase() === "http" ? "http" : "stdio";
374
+ }
375
+ function optionKey(flag) {
376
+ const long = flag.split(/[,\s]+/).find((part) => part.startsWith("--"));
377
+ if (!long)
378
+ return void 0;
379
+ const negated = long.startsWith("--no-");
380
+ return {
381
+ key: long.replace(/^--(no-)?/, "").replace(/-([a-z])/g, (_match, char) => char.toUpperCase()),
382
+ negated
383
+ };
384
+ }
385
+ function operatorValues(options, parsed, env = process.env) {
386
+ const values = { ...env };
387
+ for (const option of options) {
388
+ const derived = option.env ? optionKey(option.flag) : void 0;
389
+ if (!derived || derived.negated)
390
+ continue;
391
+ const value = parsed[derived.key];
392
+ if (value !== void 0)
393
+ values[option.env] = value;
394
+ }
395
+ return values;
396
+ }
397
+
398
+ // ../mcp-utils/dist/debug-view.js
399
+ import { createHash } from "node:crypto";
400
+ var BUILTIN_LOGGABLE_KEYS = [
401
+ "accept",
402
+ "accept-encoding",
403
+ "accept-language",
404
+ "connection",
405
+ "content-length",
406
+ "content-type",
407
+ "host",
408
+ "mcp-protocol-version",
409
+ "traceparent",
410
+ "user-agent",
411
+ "x-correlation-id",
412
+ "x-gw-auth-method",
413
+ "x-request-id",
414
+ // Launcher options
415
+ "debug",
416
+ "telemetry",
417
+ "telemetry-url",
418
+ "transport",
419
+ "transport-port"
420
+ ];
421
+ function normalizeDebugKey(key) {
422
+ const split = /[-_]/.test(key) ? key : key.replace(/([a-z][0-9]*)([A-Z])/g, "$1-$2");
423
+ return split.replace(/_/g, "-").toLowerCase();
424
+ }
425
+ function debugLoggableKeys(extra = []) {
426
+ return new Set([...BUILTIN_LOGGABLE_KEYS, ...extra].map(normalizeDebugKey));
427
+ }
428
+ function describe(key, value, loggable) {
429
+ if (value === void 0 || value === null || value === "")
430
+ return "(unset)";
431
+ if (loggable.has(normalizeDebugKey(key))) {
432
+ return typeof value === "string" ? value : JSON.stringify(value);
433
+ }
434
+ const text = typeof value === "string" ? value : Array.isArray(value) ? value.join(", ") : void 0;
435
+ return text === void 0 ? "(hidden)" : `(hidden, ${text.length} chars)`;
436
+ }
437
+ function debugView(record, loggable = debugLoggableKeys()) {
438
+ return Object.fromEntries(Object.entries(record ?? {}).map(([key, value]) => [key, describe(key, value, loggable)]));
439
+ }
440
+ function sessionLogLabel(sessionId) {
441
+ if (!sessionId || sessionId === "default")
442
+ return "default";
443
+ return `session#${createHash("sha256").update(sessionId).digest("hex").slice(0, 8)}`;
444
+ }
445
+
368
446
  // ../mcp-utils/dist/launcher.js
369
447
  import { Command } from "commander";
370
448
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
@@ -373,6 +451,89 @@ import fs from "fs";
373
451
  import http from "http";
374
452
  import { randomUUID } from "crypto";
375
453
 
454
+ // ../mcp-utils/dist/connection-source.js
455
+ function hasValue(value) {
456
+ return value !== void 0 && value !== null && value !== "" && !(Array.isArray(value) && value.length === 0);
457
+ }
458
+ function headersRead(fromHeaders, headers) {
459
+ const read = /* @__PURE__ */ new Set();
460
+ const recording = new Proxy(headers, {
461
+ get(target, prop, receiver) {
462
+ const value = Reflect.get(target, prop, receiver);
463
+ if (typeof prop === "string" && hasValue(value))
464
+ read.add(prop);
465
+ return value;
466
+ }
467
+ });
468
+ try {
469
+ fromHeaders(recording);
470
+ } catch {
471
+ }
472
+ return read;
473
+ }
474
+ function keySet(keys = []) {
475
+ return new Set(keys.map(normalizeDebugKey));
476
+ }
477
+ function chooseConnectionSource(read, basicKeys) {
478
+ for (const key of read) {
479
+ if (!basicKeys.has(normalizeDebugKey(key)))
480
+ return "caller";
481
+ }
482
+ return "operator";
483
+ }
484
+ function basicOnly(headers, basicKeys) {
485
+ return Object.fromEntries(Object.entries(headers).filter(([key]) => basicKeys.has(normalizeDebugKey(key))));
486
+ }
487
+ function mergeHeaders(base, overrides) {
488
+ const merged = {};
489
+ const index = /* @__PURE__ */ new Map();
490
+ for (const [key, value] of [...Object.entries(base), ...Object.entries(overrides)]) {
491
+ const id = normalizeDebugKey(key);
492
+ const previous = index.get(id);
493
+ if (previous !== void 0)
494
+ delete merged[previous];
495
+ index.set(id, key);
496
+ merged[key] = value;
497
+ }
498
+ return merged;
499
+ }
500
+ function operatorHeaders(values) {
501
+ const headers = {};
502
+ for (const [name, value] of Object.entries(values)) {
503
+ if (!hasValue(value))
504
+ continue;
505
+ headers[name.replace(/_/g, "-")] = Array.isArray(value) ? value.map(String) : String(value);
506
+ }
507
+ return headers;
508
+ }
509
+ function canonicalizeAliases(headers, aliases) {
510
+ if (aliases.size === 0)
511
+ return headers;
512
+ const canonical = new Set([...aliases.values()].map(normalizeDebugKey));
513
+ const present = new Set(Object.keys(headers).map(normalizeDebugKey).filter((key) => canonical.has(key)));
514
+ const result = {};
515
+ for (const [key, value] of Object.entries(headers)) {
516
+ const target = aliases.get(normalizeDebugKey(key));
517
+ if (target === void 0) {
518
+ result[key] = value;
519
+ } else if (!present.has(normalizeDebugKey(target))) {
520
+ result[target] = value;
521
+ present.add(normalizeDebugKey(target));
522
+ }
523
+ }
524
+ return result;
525
+ }
526
+ function aliasMap(aliases = {}) {
527
+ return new Map(Object.entries(aliases).map(([alias, target]) => [normalizeDebugKey(alias), target]));
528
+ }
529
+ function sessionHeaders(source, callerHeaders, operator, basicKeys, operatorSettingsOverHttp) {
530
+ if (source === "caller") {
531
+ return mergeHeaders(basicOnly(operator, basicKeys), callerHeaders);
532
+ }
533
+ const base = operatorSettingsOverHttp ? operator : basicOnly(operator, basicKeys);
534
+ return mergeHeaders(base, basicOnly(callerHeaders, basicKeys));
535
+ }
536
+
376
537
  // ../mcp-utils/dist/settings-manager.js
377
538
  var SettingsManager = class _SettingsManager {
378
539
  /**
@@ -382,7 +543,13 @@ var SettingsManager = class _SettingsManager {
382
543
  constructor(settingsClass) {
383
544
  this.settingsMap = /* @__PURE__ */ new Map();
384
545
  this.defaultSessionId = "default";
546
+ this.operatorValues = {};
547
+ this.sessionSources = /* @__PURE__ */ new Map();
385
548
  this.settingsClass = settingsClass;
549
+ this.loggableKeys = debugLoggableKeys(settingsClass?.debugLoggableKeys ?? []);
550
+ this.basicKeys = keySet(settingsClass?.basicKeys ?? []);
551
+ this.headerAliases = aliasMap(settingsClass?.headerAliases);
552
+ this.operatorSettingsOverHttp = settingsClass?.operatorSettingsOverHttp !== false;
386
553
  }
387
554
  /**
388
555
  * Get settings for a specific session
@@ -409,12 +576,15 @@ var SettingsManager = class _SettingsManager {
409
576
  * Injects debug settings from source into settings object
410
577
  * @param settings The settings object to update
411
578
  * @param source Source object containing debug information
579
+ * @param scope 'process' when the operator set debug (CLI/env), which also
580
+ * updates the process-wide state; 'session' when a caller set it through a
581
+ * header, which affects only that session's settings
412
582
  * @private
413
583
  */
414
- injectDebug(settings, source) {
584
+ injectDebug(settings, source, scope) {
415
585
  const debug = source?.debug ?? _SettingsManager.globalDebugState ?? process.env.DEBUG;
416
586
  if (typeof debug !== "undefined") {
417
- if (source?.debug !== void 0) {
587
+ if (scope === "process" && source?.debug !== void 0) {
418
588
  _SettingsManager.globalDebugState = source.debug;
419
589
  }
420
590
  if (typeof settings.set === "function") {
@@ -423,8 +593,8 @@ var SettingsManager = class _SettingsManager {
423
593
  settings.debug = debug;
424
594
  }
425
595
  if (debug) {
426
- console.error("Debug enabled. Source object contents:");
427
- console.error(JSON.stringify(source, null, 2));
596
+ console.error("Debug enabled. Source:");
597
+ this.printDebugView(source);
428
598
  }
429
599
  }
430
600
  }
@@ -437,19 +607,21 @@ var SettingsManager = class _SettingsManager {
437
607
  if (typeof settings.get === "function") {
438
608
  console.error("Settings (using get method):");
439
609
  if (settings.data && typeof settings.data === "object") {
440
- Object.entries(settings.data).forEach(([key, value]) => {
441
- console.error(` ${key}: ${JSON.stringify(value)}`);
442
- });
610
+ this.printDebugView(settings.data);
443
611
  } else {
444
612
  console.error(" Unable to enumerate settings properties - no accessible data structure");
445
613
  }
446
614
  } else {
447
615
  console.error("Settings (plain object):");
448
- Object.entries(settings).forEach(([key, value]) => {
449
- console.error(` ${key}: ${JSON.stringify(value)}`);
450
- });
616
+ this.printDebugView(settings);
451
617
  }
452
618
  }
619
+ /** Print allowlisted values; everything else as "(hidden, N chars)". */
620
+ printDebugView(record) {
621
+ Object.entries(debugView(record, this.loggableKeys)).forEach(([key, value]) => {
622
+ console.error(` ${key}: ${value}`);
623
+ });
624
+ }
453
625
  /**
454
626
  * Create settings from command-line arguments
455
627
  * @param args Command line arguments
@@ -458,10 +630,19 @@ var SettingsManager = class _SettingsManager {
458
630
  */
459
631
  createFromArgs(args, sessionId) {
460
632
  const settings = this.settingsClass.fromArgs(args);
461
- this.injectDebug(settings, args);
633
+ this.injectDebug(settings, args, "process");
462
634
  this.setSettings(settings, sessionId);
463
635
  return settings;
464
636
  }
637
+ /**
638
+ * Record the operator's values (environment variables and parsed CLI
639
+ * options, keyed by environment-variable name). HTTP sessions take basic
640
+ * keys from them when the caller doesn't send its own, and their whole
641
+ * connection set when the caller sends none of it.
642
+ */
643
+ setOperatorValues(values) {
644
+ this.operatorValues = canonicalizeAliases(operatorHeaders(values), this.headerAliases);
645
+ }
465
646
  /**
466
647
  * Create settings from HTTP headers
467
648
  * @param headers HTTP headers
@@ -471,13 +652,13 @@ var SettingsManager = class _SettingsManager {
471
652
  createFromHeaders(headers, sessionId) {
472
653
  const verbose = _SettingsManager.globalDebugState;
473
654
  if (verbose) {
474
- console.error("[SettingsManager.createFromHeaders] Verbose: Starting with sessionId:", sessionId || "(default)");
655
+ console.error("[SettingsManager.createFromHeaders] Verbose: Starting with session:", sessionLogLabel(sessionId));
475
656
  console.error("[SettingsManager.createFromHeaders] Verbose: Incoming headers count:", Object.keys(headers).length);
476
657
  }
477
658
  if (_SettingsManager.globalDebugState) {
478
659
  console.error("=== createFromHeaders Debug Info ===");
479
660
  console.error("Incoming headers:");
480
- console.error(JSON.stringify(headers, null, 2));
661
+ this.printDebugView(headers);
481
662
  }
482
663
  if (verbose && (!headers || Object.keys(headers).length === 0)) {
483
664
  console.error("[SettingsManager.createFromHeaders] Verbose: WARNING - Headers object is empty or undefined");
@@ -494,26 +675,32 @@ var SettingsManager = class _SettingsManager {
494
675
  console.error("[SettingsManager.createFromHeaders] Verbose: Normalized headers count:", Object.keys(normalizedHeaders).length);
495
676
  }
496
677
  if (_SettingsManager.globalDebugState) {
497
- console.error("Normalized headers:");
498
- console.error(JSON.stringify(normalizedHeaders, null, 2));
678
+ console.error("Normalized header names:", Object.keys(normalizedHeaders));
499
679
  }
500
680
  const debugHeader = normalizedHeaders["debug"] || normalizedHeaders["Debug"] || normalizedHeaders["DEBUG"];
501
- const debugSource = { debug: debugHeader !== void 0 ? debugHeader : _SettingsManager.globalDebugState };
681
+ const requestedDebug = Array.isArray(debugHeader) ? debugHeader[0] : debugHeader;
682
+ const debugSource = {
683
+ debug: requestedDebug !== void 0 ? requestedDebug.toLowerCase() === "true" : _SettingsManager.globalDebugState
684
+ };
685
+ const callerHeaders = canonicalizeAliases(normalizedHeaders, this.headerAliases);
686
+ const source = this.connectionSource(callerHeaders, sessionId);
687
+ const inputHeaders = sessionHeaders(source, callerHeaders, this.operatorValues, this.basicKeys, this.operatorSettingsOverHttp);
502
688
  if (verbose) {
689
+ console.error("[SettingsManager.createFromHeaders] Verbose: Connection settings source:", source);
503
690
  console.error("[SettingsManager.createFromHeaders] Verbose: Calling settingsClass.fromHeaders");
504
691
  }
505
- const settings = this.settingsClass.fromHeaders(normalizedHeaders);
692
+ const settings = this.settingsClass.fromHeaders(inputHeaders);
506
693
  if (verbose) {
507
694
  console.error("[SettingsManager.createFromHeaders] Verbose: Settings object created");
508
695
  }
509
- this.injectDebug(settings, debugSource);
696
+ this.injectDebug(settings, debugSource, "session");
510
697
  if (_SettingsManager.globalDebugState) {
511
- console.error("Final settings object for session:", sessionId || "(default)");
698
+ console.error("Final settings object for session:", sessionLogLabel(sessionId));
512
699
  this.printSettingsDebug(settings);
513
700
  console.error("=== End createFromHeaders Debug Info ===");
514
701
  }
515
702
  if (verbose) {
516
- console.error("[SettingsManager.createFromHeaders] Verbose: Storing settings for session:", sessionId || "(default)");
703
+ console.error("[SettingsManager.createFromHeaders] Verbose: Storing settings for session:", sessionLogLabel(sessionId));
517
704
  }
518
705
  this.setSettings(settings, sessionId);
519
706
  return settings;
@@ -525,14 +712,28 @@ var SettingsManager = class _SettingsManager {
525
712
  clearSession(sessionId) {
526
713
  if (sessionId !== this.defaultSessionId) {
527
714
  this.settingsMap.delete(sessionId);
715
+ this.sessionSources.delete(sessionId);
528
716
  }
529
717
  }
718
+ /**
719
+ * The session's connection source: decided by the request that creates the
720
+ * session and kept for its lifetime, so a later request cannot switch it.
721
+ */
722
+ connectionSource(headers, sessionId) {
723
+ const known = sessionId ? this.sessionSources.get(sessionId) : void 0;
724
+ if (known)
725
+ return known;
726
+ const source = chooseConnectionSource(headersRead((h) => this.settingsClass.fromHeaders(h), headers), this.basicKeys);
727
+ if (sessionId)
728
+ this.sessionSources.set(sessionId, source);
729
+ return source;
730
+ }
530
731
  };
531
732
  SettingsManager.globalDebugState = void 0;
532
733
 
533
734
  // ../mcp-utils/dist/telemetry.js
534
735
  import machineIdPkg from "node-machine-id";
535
- import { createHash } from "crypto";
736
+ import { createHash as createHash2 } from "crypto";
536
737
  var { machineIdSync } = machineIdPkg;
537
738
  var machineId = null;
538
739
  var hashedMachineId = null;
@@ -547,7 +748,7 @@ function getTelemetryUrl() {
547
748
  return process.env.TELEMETRY_URL || "https://metrics.security.ai.checkpoint.com/api/v1/metrics/collect";
548
749
  }
549
750
  function hashString(input) {
550
- return createHash("sha256").update(input).digest("hex");
751
+ return createHash2("sha256").update(input).digest("hex");
551
752
  }
552
753
  function getMachineId() {
553
754
  if (!machineId) {
@@ -595,6 +796,21 @@ async function trackToolCall(mcpName, toolName, clientVersion, extra) {
595
796
  }
596
797
 
597
798
  // ../mcp-utils/dist/launcher.js
799
+ function mirrorBooleanFlagsToEnv(config, options) {
800
+ for (const option of config.options) {
801
+ if (option.type !== "boolean" || !option.env)
802
+ continue;
803
+ const derived = optionKey(option.flag);
804
+ if (!derived)
805
+ continue;
806
+ if (derived.negated) {
807
+ throw new Error(`Option '${option.flag}' in the configuration for '${config.name}' is a negatable flag paired with env var '${option.env}', which cannot be mirrored automatically: commander keys it as '${derived.key}' and inverts its meaning, so whether the flag being present should set '${option.env}' to 'true' or 'false' depends on how that variable is named. Mirror it explicitly in launchMCPServer instead, the way --no-telemetry does for TELEMETRY_DISABLED.`);
808
+ }
809
+ if (options[derived.key] === true) {
810
+ process.env[option.env] = "true";
811
+ }
812
+ }
813
+ }
598
814
  async function launchMCPServer(configPath, serverModule2) {
599
815
  const config = JSON.parse(fs.readFileSync(configPath, "utf8"));
600
816
  const program = new Command();
@@ -605,7 +821,7 @@ async function launchMCPServer(configPath, serverModule2) {
605
821
  const envValue = option.env ? process.env[option.env] : void 0;
606
822
  const defaultValue = envValue || option.default;
607
823
  if (option.type === "boolean") {
608
- const boolDefault = envValue === "true" || option.default === "true";
824
+ const boolDefault = booleanOptionDefault(envValue, option.default);
609
825
  program.option(option.flag, option.description, boolDefault);
610
826
  } else {
611
827
  program.option(option.flag, option.description, defaultValue);
@@ -629,17 +845,24 @@ async function launchMCPServer(configPath, serverModule2) {
629
845
  if (options.telemetryUrl) {
630
846
  process.env.TELEMETRY_URL = options.telemetryUrl;
631
847
  }
848
+ mirrorBooleanFlagsToEnv(config, options);
849
+ const transportType2 = resolveTransportType(options.transport);
850
+ process.env.MCP_TRANSPORT_TYPE = transportType2;
632
851
  if (!serverModule2.settingsManager) {
633
852
  throw new Error("ServerModule must have a settingsManager. Create it with createServerModule.");
634
853
  }
635
854
  const settings = serverModule2.settingsManager.createFromArgs(options);
855
+ serverModule2.settingsManager.setOperatorValues?.(operatorValues(config.options, options));
636
856
  if (typeof serverModule2.server.applyToolPolicy === "function") {
637
857
  console.error("[launchMCPServer] Applying tool policy before connecting");
638
858
  serverModule2.server.applyToolPolicy();
639
859
  }
640
- const transportType2 = (options.transport || process.env.MCP_TRANSPORT_TYPE || "stdio").toLowerCase() === "http" ? "http" : "stdio";
641
860
  const transportPort = options.transportPort ? parseInt(options.transportPort, 10) : process.env.MCP_TRANSPORT_PORT ? parseInt(process.env.MCP_TRANSPORT_PORT, 10) : 3e3;
642
861
  if (transportType2 === "http") {
862
+ const refusal = serverModule2.httpStartupCheck?.();
863
+ if (refusal) {
864
+ throw new Error(refusal);
865
+ }
643
866
  setTransportType("http");
644
867
  await launchHTTPServer(config, serverModule2, transportPort);
645
868
  } else {
@@ -688,7 +911,7 @@ async function launchHTTPServer(config, serverModule2, port) {
688
911
  if (sessionId && transports[sessionId]) {
689
912
  const verbose = SettingsManager.globalDebugState;
690
913
  if (verbose) {
691
- console.error("[launchHTTPServer] Verbose: Reusing existing transport for session:", sessionId);
914
+ console.error("[launchHTTPServer] Verbose: Reusing existing transport for session:", sessionLogLabel(sessionId));
692
915
  }
693
916
  transport = transports[sessionId];
694
917
  if (!serverModule2.settingsManager) {
@@ -708,7 +931,7 @@ async function launchHTTPServer(config, serverModule2, port) {
708
931
  onsessioninitialized: (sid) => {
709
932
  const verbose = SettingsManager.globalDebugState;
710
933
  if (verbose) {
711
- console.error("[launchHTTPServer] Verbose: New session initialized:", sid);
934
+ console.error("[launchHTTPServer] Verbose: New session initialized:", sessionLogLabel(sid));
712
935
  }
713
936
  transports[sid] = transport;
714
937
  if (verbose) {
@@ -911,7 +1134,6 @@ function headersToEnvVars(headers, config) {
911
1134
  }
912
1135
  if (verbose) {
913
1136
  console.error("[headersToEnvVars] Verbose: Final result keys:", Object.keys(result));
914
- console.error("[headersToEnvVars] Verbose: Final result (sanitized):", Object.fromEntries(Object.entries(result).map(([k, v]) => [k, k.toLowerCase().includes("key") || k.toLowerCase().includes("token") || k.toLowerCase().includes("password") ? "***" : v])));
915
1137
  }
916
1138
  return result;
917
1139
  }
@@ -938,7 +1160,7 @@ var APIManagerFactory = class {
938
1160
  const verbose = SettingsManager.globalDebugState;
939
1161
  const sid = sessionId || this.defaultSessionId;
940
1162
  if (verbose) {
941
- console.error("[APIManagerFactory.getAPIManager] Verbose: Getting API manager for session:", sid);
1163
+ console.error("[APIManagerFactory.getAPIManager] Verbose: Getting API manager for session:", sessionLogLabel(sid));
942
1164
  }
943
1165
  if (!this.apiManagerMap.has(sid)) {
944
1166
  if (verbose) {
@@ -1036,16 +1258,15 @@ var SessionContext = class {
1036
1258
  /**
1037
1259
  * Get the current session ID from the MCP extra context
1038
1260
  * @param extra Extra context passed to MCP tool callbacks
1039
- * @returns The session ID or DEFAULT_SESSION_ID if not available
1261
+ * @returns The session ID, or DEFAULT_SESSION_ID under stdio
1262
+ * @throws Under HTTP transport when the context carries no session ID
1040
1263
  */
1041
1264
  static getSessionId(extra) {
1042
- if (!extra)
1043
- return this.DEFAULT_SESSION_ID;
1044
- if (extra.transport && extra.transport.sessionId) {
1045
- return extra.transport.sessionId;
1046
- }
1047
- if (extra.sessionId) {
1048
- return extra.sessionId;
1265
+ const sessionId = extra?.transport?.sessionId || extra?.sessionId;
1266
+ if (sessionId)
1267
+ return sessionId;
1268
+ if (process.env.MCP_TRANSPORT_TYPE === "http") {
1269
+ throw new Error("No MCP session in this request context; HTTP transport requires one");
1049
1270
  }
1050
1271
  return this.DEFAULT_SESSION_ID;
1051
1272
  }
@@ -1162,10 +1383,10 @@ var SessionContext = class {
1162
1383
  }
1163
1384
  }
1164
1385
  }
1165
- console.error(`Using cached user data for session: ${sessionId}, key: ${options.cacheKey}`);
1386
+ console.error(`Using cached user data for ${sessionLogLabel(sessionId)}, key: ${options.cacheKey}`);
1166
1387
  return { cancelled: false, data: dataToReturn2 };
1167
1388
  }
1168
- console.error(`Prompting user for data for session: ${sessionId}, key: ${options.cacheKey}`);
1389
+ console.error(`Prompting user for data for ${sessionLogLabel(sessionId)}, key: ${options.cacheKey}`);
1169
1390
  let result;
1170
1391
  if (options.showLoginDialog) {
1171
1392
  const { showLoginDialog: showLoginDialog2 } = await Promise.resolve().then(() => (init_ui_dialog(), ui_dialog_exports));
@@ -1367,10 +1588,11 @@ function wrapServerWithTracking(server2, createServerFn) {
1367
1588
  newServer[reg.method](...reg.args);
1368
1589
  }
1369
1590
  }
1370
- if (typeof server2.setToolPolicy === "function" && typeof newServer.setToolPolicy === "function") {
1371
- const toolPolicy = server2._toolPolicyCallback;
1372
- if (toolPolicy) {
1373
- newServer.setToolPolicy(toolPolicy);
1591
+ const toolPolicy = typeof server2.getToolPolicy === "function" ? server2.getToolPolicy() : void 0;
1592
+ if (toolPolicy && typeof newServer.setToolPolicy === "function") {
1593
+ newServer.setToolPolicy(toolPolicy);
1594
+ if (typeof newServer.applyToolPolicy === "function") {
1595
+ newServer.applyToolPolicy();
1374
1596
  }
1375
1597
  }
1376
1598
  return newServer;
@@ -1426,35 +1648,61 @@ var CPMcpServer = class extends McpServer {
1426
1648
  console.error("[CPMcpServer] Setting tool policy callback");
1427
1649
  this.toolPolicyCallback = callback;
1428
1650
  }
1651
+ /**
1652
+ * Get the tool policy callback, if one was set.
1653
+ *
1654
+ * Needed so the policy can be carried onto the per-session server instances
1655
+ * created for HTTP transport. A typed accessor keeps that transfer honest:
1656
+ * callers do not have to reach into the private field, where a misspelling
1657
+ * would go unnoticed by the compiler.
1658
+ */
1659
+ getToolPolicy() {
1660
+ return this.toolPolicyCallback;
1661
+ }
1429
1662
  /**
1430
1663
  * Apply the tool policy by checking all registered tools
1431
1664
  * This should be called after all tools are registered but before connecting
1665
+ *
1666
+ * Per-tool tracing is behind the debug flag. This used to run once per process
1667
+ * on the template server, where a line per tool was a cheap startup banner.
1668
+ * It now also runs per session under HTTP transport, so unconditional tracing
1669
+ * would scale stderr with sessions x tools and bury the summary. The summary
1670
+ * line always prints — it is the fact worth having for every session.
1432
1671
  */
1433
1672
  applyToolPolicy() {
1673
+ const verbose = SettingsManager.globalDebugState === true || SettingsManager.globalDebugState === "true";
1434
1674
  if (!this.toolPolicyCallback) {
1435
- console.error("[CPMcpServer] No tool policy callback set, allowing all tools");
1675
+ if (verbose) {
1676
+ console.error("[CPMcpServer] No tool policy callback set, allowing all tools");
1677
+ }
1436
1678
  return;
1437
1679
  }
1438
- console.error("[CPMcpServer] Applying tool policy");
1439
1680
  const registeredTools = this._registeredTools;
1440
1681
  if (!registeredTools || typeof registeredTools !== "object") {
1441
1682
  console.error("[CPMcpServer] No tools registered yet");
1442
1683
  return;
1443
1684
  }
1444
1685
  const toolNames = Object.keys(registeredTools);
1445
- console.error(`[CPMcpServer] Found ${toolNames.length} registered tools:`, toolNames);
1686
+ if (verbose) {
1687
+ console.error(`[CPMcpServer] Applying tool policy`);
1688
+ console.error(`[CPMcpServer] Found ${toolNames.length} registered tools:`, toolNames);
1689
+ }
1446
1690
  let enabledCount = 0;
1447
1691
  let disabledCount = 0;
1448
1692
  for (const [toolName, tool] of Object.entries(registeredTools)) {
1449
1693
  const isAllowed = this.toolPolicyCallback(toolName);
1450
1694
  if (!isAllowed) {
1451
- console.error(`[CPMcpServer] Tool '${toolName}' is NOT allowed by policy - disabling`);
1695
+ if (verbose) {
1696
+ console.error(`[CPMcpServer] Tool '${toolName}' is NOT allowed by policy - disabling`);
1697
+ }
1452
1698
  if (typeof tool.disable === "function") {
1453
1699
  tool.disable();
1454
1700
  disabledCount++;
1455
1701
  }
1456
1702
  } else {
1457
- console.error(`[CPMcpServer] Tool '${toolName}' is allowed by policy`);
1703
+ if (verbose) {
1704
+ console.error(`[CPMcpServer] Tool '${toolName}' is allowed by policy`);
1705
+ }
1458
1706
  enabledCount++;
1459
1707
  }
1460
1708
  }
@@ -1792,7 +2040,7 @@ function unwrapSingleton(data, where) {
1792
2040
  }
1793
2041
 
1794
2042
  // src/core/providers/avanan.ts
1795
- import { createHash as createHash2 } from "node:crypto";
2043
+ import { createHash as createHash3 } from "node:crypto";
1796
2044
  var V1_PREFIX = "/v1.0";
1797
2045
  var AUTH_PATH = "/v1.0/auth";
1798
2046
  var REGION_HOSTS = {
@@ -1815,7 +2063,7 @@ function avananSignature(reqId, appId, date, secret, requestPath) {
1815
2063
  if (requestPath !== null) parts.push(requestPath);
1816
2064
  parts.push(secret);
1817
2065
  const base64 = Buffer.from(parts.join(""), "utf8").toString("base64");
1818
- return createHash2("sha256").update(base64, "utf8").digest("hex");
2066
+ return createHash3("sha256").update(base64, "utf8").digest("hex");
1819
2067
  }
1820
2068
  function formatAvananDate(now) {
1821
2069
  return `${now.toISOString().replace("Z", "")}000`;
@@ -2708,6 +2956,251 @@ function neverReachedUpstream(error) {
2708
2956
  return code !== null && PRE_FLIGHT_ERROR_CODES.has(code);
2709
2957
  }
2710
2958
 
2959
+ // src/tools/shared/schemas.ts
2960
+ import { z as z2 } from "zod";
2961
+ function orNull(value) {
2962
+ return value ?? null;
2963
+ }
2964
+ function compact(obj) {
2965
+ return Object.fromEntries(
2966
+ Object.entries(obj).filter(
2967
+ ([, value]) => value !== null && value !== void 0
2968
+ )
2969
+ );
2970
+ }
2971
+ var looseString = z2.unknown().transform((v) => typeof v === "string" ? v : null);
2972
+ var looseRecord = z2.unknown().transform(
2973
+ (v) => v !== null && typeof v === "object" && !Array.isArray(v) ? v : null
2974
+ );
2975
+ var SEVERITY_BY_DIGIT = {
2976
+ "1": "lowest",
2977
+ "2": "low",
2978
+ "3": "medium",
2979
+ "4": "high",
2980
+ "5": "critical"
2981
+ };
2982
+ function normalizeSeverity(value) {
2983
+ if (typeof value !== "string") return null;
2984
+ return SEVERITY_BY_DIGIT[value] ?? (value || null);
2985
+ }
2986
+ var eventSchema = z2.object({
2987
+ eventId: z2.unknown(),
2988
+ type: looseString,
2989
+ state: looseString,
2990
+ severity: z2.unknown(),
2991
+ saas: looseString,
2992
+ description: looseString,
2993
+ entityId: looseString,
2994
+ eventCreated: looseString,
2995
+ confidenceIndicator: looseString
2996
+ }).passthrough().transform((raw) => ({
2997
+ eventId: typeof raw.eventId === "string" ? raw.eventId : String(raw.eventId ?? ""),
2998
+ type: raw.type,
2999
+ state: raw.state,
3000
+ severity: normalizeSeverity(raw.severity),
3001
+ saas: raw.saas,
3002
+ description: raw.description,
3003
+ entityId: raw.entityId,
3004
+ eventCreated: raw.eventCreated,
3005
+ confidenceIndicator: raw.confidenceIndicator
3006
+ }));
3007
+ function toEvent(raw) {
3008
+ return eventSchema.parse(raw);
3009
+ }
3010
+ var sectoolResultSchema = z2.object({
3011
+ entityId: looseString,
3012
+ entityType: looseString,
3013
+ payload: looseRecord,
3014
+ score: looseString,
3015
+ securityResultEntityId: looseString,
3016
+ securityResultEntityType: looseString,
3017
+ statusCode: looseString,
3018
+ statusDescription: looseString,
3019
+ verdict: z2.unknown().transform((v) => typeof v === "string" ? v : "unknown")
3020
+ }).passthrough().transform((raw) => ({
3021
+ entityId: raw.entityId,
3022
+ entityType: raw.entityType,
3023
+ payload: raw.payload,
3024
+ score: raw.score,
3025
+ securityResultEntityId: raw.securityResultEntityId,
3026
+ securityResultEntityType: raw.securityResultEntityType,
3027
+ statusCode: raw.statusCode,
3028
+ statusDescription: raw.statusDescription,
3029
+ verdict: raw.verdict
3030
+ }));
3031
+ var engineResultsSchema = z2.array(sectoolResultSchema).nullish().transform((v) => orNull(v));
3032
+ var entitySecurityResultSchema = z2.object({
3033
+ combinedVerdict: looseRecord,
3034
+ ap: engineResultsSchema,
3035
+ dlp: engineResultsSchema,
3036
+ clicktimeProtection: engineResultsSchema,
3037
+ shadowIt: engineResultsSchema,
3038
+ av: engineResultsSchema
3039
+ }).passthrough().transform((raw) => ({
3040
+ combinedVerdict: raw.combinedVerdict,
3041
+ ap: raw.ap,
3042
+ dlp: raw.dlp,
3043
+ clicktimeProtection: raw.clicktimeProtection,
3044
+ shadowIt: raw.shadowIt,
3045
+ av: raw.av
3046
+ }));
3047
+ var entityActionSchema = z2.object({
3048
+ entityActionName: z2.string(),
3049
+ entityActionDate: looseString,
3050
+ entityActionResponseCode: looseString,
3051
+ entityActionResponseText: looseString,
3052
+ entityActionState: looseString
3053
+ }).passthrough().transform((raw) => ({
3054
+ entityActionName: raw.entityActionName,
3055
+ entityActionDate: raw.entityActionDate,
3056
+ entityActionResponseCode: raw.entityActionResponseCode,
3057
+ entityActionResponseText: raw.entityActionResponseText,
3058
+ entityActionState: raw.entityActionState
3059
+ }));
3060
+ var entityInfoSchema = z2.object({
3061
+ entityId: z2.string(),
3062
+ customerId: looseString,
3063
+ saas: looseString,
3064
+ saasEntityType: looseString,
3065
+ entityCreated: looseString,
3066
+ entityUpdated: looseString,
3067
+ entityActionState: looseString
3068
+ }).passthrough().transform((raw) => ({
3069
+ entityId: raw.entityId,
3070
+ customerId: raw.customerId,
3071
+ saas: raw.saas,
3072
+ saasEntityType: raw.saasEntityType,
3073
+ entityCreated: raw.entityCreated,
3074
+ entityUpdated: raw.entityUpdated,
3075
+ entityActionState: raw.entityActionState
3076
+ }));
3077
+ var entitySchema = z2.object({
3078
+ entityInfo: entityInfoSchema,
3079
+ saasInfo: looseRecord,
3080
+ entityPayload: looseRecord,
3081
+ entitySecurityResult: entitySecurityResultSchema.nullish().transform((v) => orNull(v)),
3082
+ entityActions: z2.array(entityActionSchema).nullish(),
3083
+ entityAvailableActions: z2.array(z2.string()).nullish()
3084
+ }).passthrough().transform((raw) => ({
3085
+ entityInfo: raw.entityInfo,
3086
+ saasInfo: raw.saasInfo,
3087
+ entityPayload: raw.entityPayload,
3088
+ entitySecurityResult: raw.entitySecurityResult,
3089
+ entityActions: raw.entityActions ?? [],
3090
+ entityAvailableActions: raw.entityAvailableActions ?? []
3091
+ }));
3092
+ function toEntity(raw) {
3093
+ return entitySchema.parse(raw);
3094
+ }
3095
+ function toEntitySummary(raw) {
3096
+ const {
3097
+ entityPayload: _payload,
3098
+ saasInfo: _saasInfo,
3099
+ ...rest
3100
+ } = toEntity(raw);
3101
+ return rest;
3102
+ }
3103
+ var TASK_STATUSES = [
3104
+ "init",
3105
+ "inprogress",
3106
+ "completed",
3107
+ "failed",
3108
+ "stopped",
3109
+ "paused"
3110
+ ];
3111
+ var taskSchema = z2.object({
3112
+ taskId: z2.string(),
3113
+ status: z2.enum(TASK_STATUSES),
3114
+ actor: looseString,
3115
+ startedAt: looseString,
3116
+ finishedAt: looseString
3117
+ }).passthrough().transform((raw) => ({
3118
+ taskId: raw.taskId,
3119
+ status: raw.status,
3120
+ actor: raw.actor,
3121
+ startedAt: raw.startedAt,
3122
+ finishedAt: raw.finishedAt
3123
+ }));
3124
+ function toTask(raw) {
3125
+ return taskSchema.parse(raw);
3126
+ }
3127
+ var EXCEPTION_TYPES = [
3128
+ "whitelist",
3129
+ "blacklist",
3130
+ "spam_whitelist"
3131
+ ];
3132
+ var AP_EXCEPTION_CRITERIA = [
3133
+ ["senderEmail", "senderEmailMatching"],
3134
+ ["senderName", "senderNameMatching"],
3135
+ ["senderDomain", "senderDomainMatching"],
3136
+ ["senderIp", null],
3137
+ ["senderClientIp", null],
3138
+ ["recipient", "recipientMatching"],
3139
+ ["domain", null],
3140
+ ["linkDomains", "linkDomainMatching"],
3141
+ ["subject", "subjectMatching"],
3142
+ ["attachmentMd5", null]
3143
+ ];
3144
+ var nonEmpty = (v) => typeof v === "string" && v !== "";
3145
+ function exceptionIdOf(row) {
3146
+ if (!row || typeof row !== "object" || Array.isArray(row)) return null;
3147
+ const { id, entityId } = row;
3148
+ for (const v of [id, entityId]) {
3149
+ if (nonEmpty(v)) return v;
3150
+ if (typeof v === "number") return String(v);
3151
+ }
3152
+ return null;
3153
+ }
3154
+ function withExceptionId(raw) {
3155
+ const id = exceptionIdOf(raw);
3156
+ return id === null ? raw : { ...raw, id };
3157
+ }
3158
+ var apExceptionRowSchema = z2.preprocess(
3159
+ withExceptionId,
3160
+ z2.object({
3161
+ id: z2.string(),
3162
+ comment: looseString,
3163
+ actionNeeded: looseString,
3164
+ ignoringSpfCheck: z2.unknown().transform((v) => {
3165
+ if (typeof v === "boolean") return v;
3166
+ if (v === 1 || v === "true") return true;
3167
+ if (v === 0 || v === "false") return false;
3168
+ return null;
3169
+ }),
3170
+ editedBy: looseString,
3171
+ addedBy: looseString,
3172
+ updateTime: looseString
3173
+ }).passthrough()
3174
+ );
3175
+ function criteriaOf(row) {
3176
+ const match = {};
3177
+ for (const [field, modeField] of AP_EXCEPTION_CRITERIA) {
3178
+ const value = row[field];
3179
+ if (value === null || value === void 0 || value === "" || Array.isArray(value) && value.length === 0) {
3180
+ continue;
3181
+ }
3182
+ const mode = modeField ? row[modeField] : null;
3183
+ match[field] = {
3184
+ value,
3185
+ matching: typeof mode === "string" ? mode : null
3186
+ };
3187
+ }
3188
+ return match;
3189
+ }
3190
+ function toApException(raw, type) {
3191
+ const row = apExceptionRowSchema.parse(raw);
3192
+ return {
3193
+ id: row.id,
3194
+ type,
3195
+ match: criteriaOf(row),
3196
+ comment: row.comment,
3197
+ actionNeeded: row.actionNeeded,
3198
+ ignoringSpfCheck: row.ignoringSpfCheck,
3199
+ actor: nonEmpty(row.editedBy) ? row.editedBy : row.addedBy,
3200
+ updatedAt: row.updateTime
3201
+ };
3202
+ }
3203
+
2711
3204
  // src/check.ts
2712
3205
  function maskId(value) {
2713
3206
  return value.length <= 8 ? `${value.slice(0, 2)}\u2026` : `${value.slice(0, 6)}\u2026`;
@@ -2732,8 +3225,8 @@ function firstExceptionId(whitelist, blacklist) {
2732
3225
  ["blacklist", blacklist]
2733
3226
  ];
2734
3227
  for (const [excType, rows] of candidates) {
2735
- const value = rows?.[0]?.id ?? rows?.[0]?.exceptionId;
2736
- if (typeof value === "string") return [excType, value];
3228
+ const value = exceptionIdOf(rows?.[0]);
3229
+ if (value !== null) return [excType, value];
2737
3230
  }
2738
3231
  return [null, null];
2739
3232
  }
@@ -3061,7 +3554,7 @@ query tools.
3061
3554
  `;
3062
3555
 
3063
3556
  // src/prompts/index.ts
3064
- import { z as z3 } from "zod";
3557
+ import { z as z4 } from "zod";
3065
3558
 
3066
3559
  // src/content/glossary.ts
3067
3560
  var GLOSSARY = {
@@ -3435,7 +3928,7 @@ var GLOSSARY = {
3435
3928
  };
3436
3929
 
3437
3930
  // src/tools/shared/result.ts
3438
- import { z as z2 } from "zod";
3931
+ import { z as z3 } from "zod";
3439
3932
  function jsonResult(payload) {
3440
3933
  return { content: [{ type: "text", text: JSON.stringify(payload) }] };
3441
3934
  }
@@ -3449,7 +3942,7 @@ function errorResult(tool, error) {
3449
3942
  isError: true
3450
3943
  };
3451
3944
  }
3452
- if (error instanceof z2.ZodError) {
3945
+ if (error instanceof z3.ZodError) {
3453
3946
  const detail = error.issues.map(
3454
3947
  (issue) => `${issue.path.join(".") || "input"}: ${issue.message}`
3455
3948
  ).join("; ");
@@ -3739,7 +4232,7 @@ function registerPrompts(server2) {
3739
4232
  "security_activity_summary",
3740
4233
  ACTIVITY_DESCRIPTION,
3741
4234
  {
3742
- time_window: z3.string().optional().describe(
4235
+ time_window: z4.string().optional().describe(
3743
4236
  'Rolling window, e.g. "last 24 hours" or "this week".'
3744
4237
  )
3745
4238
  },
@@ -3752,8 +4245,8 @@ function registerPrompts(server2) {
3752
4245
  {
3753
4246
  // Deliberately free-form, so model-invented synonyms ("open",
3754
4247
  // "active") still land on the pending branch.
3755
- mode: z3.string().optional().describe("pending, resolved, or trend."),
3756
- time_window: z3.string().optional().describe("Rolling window for the resolved and trend modes.")
4248
+ mode: z4.string().optional().describe("pending, resolved, or trend."),
4249
+ time_window: z4.string().optional().describe("Rolling window for the resolved and trend modes.")
3757
4250
  },
3758
4251
  (args) => promptMessage(
3759
4252
  restoreTemplate(
@@ -3767,7 +4260,7 @@ function registerPrompts(server2) {
3767
4260
  "single_email_investigation",
3768
4261
  SINGLE_DESCRIPTION,
3769
4262
  {
3770
- entity_id: z3.string().describe("The HEC entity id to investigate.")
4263
+ entity_id: z4.string().describe("The HEC entity id to investigate.")
3771
4264
  },
3772
4265
  (args) => promptMessage(singleTemplate(args.entity_id ?? ""))
3773
4266
  );
@@ -3854,48 +4347,19 @@ function firstOf(...values) {
3854
4347
  return values.find((v) => v !== void 0 && v !== "");
3855
4348
  }
3856
4349
  var Settings = class _Settings {
4350
+ // Takes only what it is given; fromArgs reads the environment.
3857
4351
  constructor({
3858
- domain = firstOf(
3859
- process.env.HEC_DOMAIN,
3860
- process.env.SMARTAPI_MCP_DOMAIN
3861
- ),
3862
- clientId = firstOf(
3863
- process.env.HEC_CLIENT_ID,
3864
- process.env.SMARTAPI_MCP_CLIENT_ID
3865
- ),
3866
- secret = firstOf(
3867
- process.env.HEC_SECRET,
3868
- process.env.SMARTAPI_MCP_SECRET
3869
- ),
3870
- authMethod = firstOf(
3871
- process.env.HEC_AUTH_METHOD,
3872
- process.env.SMARTAPI_MCP_AUTH_METHOD
3873
- ),
3874
- region = firstOf(
3875
- process.env.HEC_REGION,
3876
- process.env.SMARTAPI_MCP_REGION
3877
- ),
3878
- scope = firstOf(process.env.HEC_SCOPE, process.env.SMARTAPI_MCP_SCOPE),
3879
- requestTimeout = firstOf(
3880
- process.env.HEC_REQUEST_TIMEOUT,
3881
- process.env.SMARTAPI_MCP_REQUEST_TIMEOUT
3882
- ),
3883
- maxRetries = firstOf(
3884
- process.env.HEC_MAX_RETRIES,
3885
- process.env.SMARTAPI_MCP_MAX_RETRIES
3886
- ),
3887
- retryBaseSeconds = firstOf(
3888
- process.env.HEC_RETRY_BASE_SECONDS,
3889
- process.env.SMARTAPI_MCP_RETRY_BASE_SECONDS
3890
- ),
3891
- maxConcurrency = firstOf(
3892
- process.env.HEC_MAX_CONCURRENCY,
3893
- process.env.SMARTAPI_MCP_MAX_CONCURRENCY
3894
- ),
3895
- actionBatchLimit = firstOf(
3896
- process.env.HEC_ACTION_BATCH_LIMIT,
3897
- process.env.SMARTAPI_MCP_ACTION_BATCH_LIMIT
3898
- )
4352
+ domain,
4353
+ clientId,
4354
+ secret,
4355
+ authMethod,
4356
+ region,
4357
+ scope,
4358
+ requestTimeout,
4359
+ maxRetries,
4360
+ retryBaseSeconds,
4361
+ maxConcurrency,
4362
+ actionBatchLimit
3899
4363
  } = {}) {
3900
4364
  this.domain = "";
3901
4365
  this.clientId = "";
@@ -3920,6 +4384,67 @@ var Settings = class _Settings {
3920
4384
  "actionBatchLimit"
3921
4385
  );
3922
4386
  }
4387
+ static {
4388
+ /**
4389
+ * Keys whose values debug output may print (see debugView in mcp-utils).
4390
+ * Anything not listed, including every credential, is shown hidden.
4391
+ * Lists both the header/CLI names (hec-*) and the property names.
4392
+ */
4393
+ this.debugLoggableKeys = [
4394
+ "hec-domain",
4395
+ "domain",
4396
+ "hec-region",
4397
+ "region",
4398
+ "hec-scope",
4399
+ "scope",
4400
+ "hec-auth-method",
4401
+ "auth-method",
4402
+ "hec-request-timeout",
4403
+ "request-timeout",
4404
+ "hec-max-retries",
4405
+ "max-retries",
4406
+ "hec-retry-base-seconds",
4407
+ "retry-base-seconds",
4408
+ "hec-max-concurrency",
4409
+ "max-concurrency",
4410
+ "hec-action-batch-limit",
4411
+ "action-batch-limit"
4412
+ ];
4413
+ }
4414
+ static {
4415
+ /**
4416
+ * Headers an HTTP session may mix with the operator's values (see
4417
+ * connection-source in mcp-utils): the numeric tunables, which are
4418
+ * deployment-wide rather than per tenant, under both spellings.
4419
+ */
4420
+ this.basicKeys = [
4421
+ "HEC-REQUEST-TIMEOUT",
4422
+ "HEC-MAX-RETRIES",
4423
+ "HEC-RETRY-BASE-SECONDS",
4424
+ "HEC-MAX-CONCURRENCY",
4425
+ "HEC-ACTION-BATCH-LIMIT"
4426
+ ];
4427
+ }
4428
+ static {
4429
+ /**
4430
+ * The Python server's names for the tunables (alias -> canonical), so a
4431
+ * caller's SMARTAPI-MCP-* header overrides the operator's HEC_* value.
4432
+ */
4433
+ this.headerAliases = {
4434
+ "SMARTAPI-MCP-REQUEST-TIMEOUT": "HEC-REQUEST-TIMEOUT",
4435
+ "SMARTAPI-MCP-MAX-RETRIES": "HEC-MAX-RETRIES",
4436
+ "SMARTAPI-MCP-RETRY-BASE-SECONDS": "HEC-RETRY-BASE-SECONDS",
4437
+ "SMARTAPI-MCP-MAX-CONCURRENCY": "HEC-MAX-CONCURRENCY",
4438
+ "SMARTAPI-MCP-ACTION-BATCH-LIMIT": "HEC-ACTION-BATCH-LIMIT"
4439
+ };
4440
+ }
4441
+ static {
4442
+ /**
4443
+ * An HTTP session never uses the operator's tenant: a client must not be
4444
+ * able to act as whichever tenant the operator configured.
4445
+ */
4446
+ this.operatorSettingsOverHttp = false;
4447
+ }
3923
4448
  /**
3924
4449
  * Under HTTP transport credentials arrive per request as headers, so the
3925
4450
  * process starts with none and validating at boot would be wrong.
@@ -3936,30 +4461,27 @@ var Settings = class _Settings {
3936
4461
  return true;
3937
4462
  }
3938
4463
  static fromArgs(options) {
4464
+ const env = process.env;
3939
4465
  return new _Settings({
3940
- domain: options.hecDomain,
3941
- clientId: options.hecClientId,
3942
- secret: options.hecSecret,
3943
- authMethod: options.hecAuthMethod,
3944
- region: options.hecRegion,
3945
- scope: options.hecScope,
3946
- requestTimeout: options.hecRequestTimeout,
3947
- maxRetries: options.hecMaxRetries,
3948
- retryBaseSeconds: options.hecRetryBaseSeconds,
3949
- maxConcurrency: options.hecMaxConcurrency,
3950
- actionBatchLimit: options.hecActionBatchLimit
4466
+ domain: options.hecDomain ?? firstOf(env.HEC_DOMAIN, env.SMARTAPI_MCP_DOMAIN),
4467
+ clientId: options.hecClientId ?? firstOf(env.HEC_CLIENT_ID, env.SMARTAPI_MCP_CLIENT_ID),
4468
+ secret: options.hecSecret ?? firstOf(env.HEC_SECRET, env.SMARTAPI_MCP_SECRET),
4469
+ authMethod: options.hecAuthMethod ?? firstOf(env.HEC_AUTH_METHOD, env.SMARTAPI_MCP_AUTH_METHOD),
4470
+ region: options.hecRegion ?? firstOf(env.HEC_REGION, env.SMARTAPI_MCP_REGION),
4471
+ scope: options.hecScope ?? firstOf(env.HEC_SCOPE, env.SMARTAPI_MCP_SCOPE),
4472
+ requestTimeout: options.hecRequestTimeout ?? firstOf(env.HEC_REQUEST_TIMEOUT, env.SMARTAPI_MCP_REQUEST_TIMEOUT),
4473
+ maxRetries: options.hecMaxRetries ?? firstOf(env.HEC_MAX_RETRIES, env.SMARTAPI_MCP_MAX_RETRIES),
4474
+ retryBaseSeconds: options.hecRetryBaseSeconds ?? firstOf(env.HEC_RETRY_BASE_SECONDS, env.SMARTAPI_MCP_RETRY_BASE_SECONDS),
4475
+ maxConcurrency: options.hecMaxConcurrency ?? firstOf(env.HEC_MAX_CONCURRENCY, env.SMARTAPI_MCP_MAX_CONCURRENCY),
4476
+ actionBatchLimit: options.hecActionBatchLimit ?? firstOf(env.HEC_ACTION_BATCH_LIMIT, env.SMARTAPI_MCP_ACTION_BATCH_LIMIT)
3951
4477
  });
3952
4478
  }
3953
4479
  /**
3954
- * Per-request settings, built from headers alone.
3955
- *
3956
- * Every tenant-scoped field is passed as an explicit empty string when its
3957
- * header is absent, because `undefined` would hand the field back to the
3958
- * constructor's env default. Under HTTP transport that would let a request
3959
- * with no credentials inherit the server process's own tenant and act as
3960
- * it, so the empty string is what makes a header-less request fail closed.
3961
- * The numeric tunables are deployment-wide, not per tenant, so they keep
3962
- * their defaults.
4480
+ * Per-request settings, built from headers alone. A missing tenant-scoped
4481
+ * header leaves its field empty, so a request without credentials fails
4482
+ * closed. The numeric tunables are deployment-wide, not per tenant: they
4483
+ * are basic keys, so the operator's values apply when the caller sends
4484
+ * none.
3963
4485
  */
3964
4486
  static fromHeaders(headers) {
3965
4487
  return new _Settings({
@@ -3969,11 +4491,26 @@ var Settings = class _Settings {
3969
4491
  authMethod: getHeaderValue(headers, "HEC-AUTH-METHOD") ?? "",
3970
4492
  region: getHeaderValue(headers, "HEC-REGION") ?? "",
3971
4493
  scope: getHeaderValue(headers, "HEC-SCOPE") ?? "",
3972
- requestTimeout: getHeaderValue(headers, "HEC-REQUEST-TIMEOUT"),
3973
- maxRetries: getHeaderValue(headers, "HEC-MAX-RETRIES"),
3974
- retryBaseSeconds: getHeaderValue(headers, "HEC-RETRY-BASE-SECONDS"),
3975
- maxConcurrency: getHeaderValue(headers, "HEC-MAX-CONCURRENCY"),
3976
- actionBatchLimit: getHeaderValue(headers, "HEC-ACTION-BATCH-LIMIT")
4494
+ requestTimeout: firstOf(
4495
+ getHeaderValue(headers, "HEC-REQUEST-TIMEOUT"),
4496
+ getHeaderValue(headers, "SMARTAPI-MCP-REQUEST-TIMEOUT")
4497
+ ),
4498
+ maxRetries: firstOf(
4499
+ getHeaderValue(headers, "HEC-MAX-RETRIES"),
4500
+ getHeaderValue(headers, "SMARTAPI-MCP-MAX-RETRIES")
4501
+ ),
4502
+ retryBaseSeconds: firstOf(
4503
+ getHeaderValue(headers, "HEC-RETRY-BASE-SECONDS"),
4504
+ getHeaderValue(headers, "SMARTAPI-MCP-RETRY-BASE-SECONDS")
4505
+ ),
4506
+ maxConcurrency: firstOf(
4507
+ getHeaderValue(headers, "HEC-MAX-CONCURRENCY"),
4508
+ getHeaderValue(headers, "SMARTAPI-MCP-MAX-CONCURRENCY")
4509
+ ),
4510
+ actionBatchLimit: firstOf(
4511
+ getHeaderValue(headers, "HEC-ACTION-BATCH-LIMIT"),
4512
+ getHeaderValue(headers, "SMARTAPI-MCP-ACTION-BATCH-LIMIT")
4513
+ )
3977
4514
  });
3978
4515
  }
3979
4516
  };
@@ -3981,198 +4518,6 @@ var Settings = class _Settings {
3981
4518
  // src/tools/actions.ts
3982
4519
  import { z as z5 } from "zod";
3983
4520
 
3984
- // src/tools/shared/schemas.ts
3985
- import { z as z4 } from "zod";
3986
- function orNull(value) {
3987
- return value ?? null;
3988
- }
3989
- function compact(obj) {
3990
- return Object.fromEntries(
3991
- Object.entries(obj).filter(
3992
- ([, value]) => value !== null && value !== void 0
3993
- )
3994
- );
3995
- }
3996
- var looseString = z4.unknown().transform((v) => typeof v === "string" ? v : null);
3997
- var looseRecord = z4.unknown().transform(
3998
- (v) => v !== null && typeof v === "object" && !Array.isArray(v) ? v : null
3999
- );
4000
- var SEVERITY_BY_DIGIT = {
4001
- "1": "lowest",
4002
- "2": "low",
4003
- "3": "medium",
4004
- "4": "high",
4005
- "5": "critical"
4006
- };
4007
- function normalizeSeverity(value) {
4008
- if (typeof value !== "string") return null;
4009
- return SEVERITY_BY_DIGIT[value] ?? (value || null);
4010
- }
4011
- var eventSchema = z4.object({
4012
- eventId: z4.unknown(),
4013
- type: looseString,
4014
- state: looseString,
4015
- severity: z4.unknown(),
4016
- saas: looseString,
4017
- description: looseString,
4018
- entityId: looseString,
4019
- eventCreated: looseString,
4020
- confidenceIndicator: looseString
4021
- }).passthrough().transform((raw) => ({
4022
- eventId: typeof raw.eventId === "string" ? raw.eventId : String(raw.eventId ?? ""),
4023
- type: raw.type,
4024
- state: raw.state,
4025
- severity: normalizeSeverity(raw.severity),
4026
- saas: raw.saas,
4027
- description: raw.description,
4028
- entityId: raw.entityId,
4029
- eventCreated: raw.eventCreated,
4030
- confidenceIndicator: raw.confidenceIndicator
4031
- }));
4032
- function toEvent(raw) {
4033
- return eventSchema.parse(raw);
4034
- }
4035
- var sectoolResultSchema = z4.object({
4036
- entityId: looseString,
4037
- entityType: looseString,
4038
- payload: looseRecord,
4039
- score: looseString,
4040
- securityResultEntityId: looseString,
4041
- securityResultEntityType: looseString,
4042
- statusCode: looseString,
4043
- statusDescription: looseString,
4044
- verdict: z4.unknown().transform((v) => typeof v === "string" ? v : "unknown")
4045
- }).passthrough().transform((raw) => ({
4046
- entityId: raw.entityId,
4047
- entityType: raw.entityType,
4048
- payload: raw.payload,
4049
- score: raw.score,
4050
- securityResultEntityId: raw.securityResultEntityId,
4051
- securityResultEntityType: raw.securityResultEntityType,
4052
- statusCode: raw.statusCode,
4053
- statusDescription: raw.statusDescription,
4054
- verdict: raw.verdict
4055
- }));
4056
- var engineResultsSchema = z4.array(sectoolResultSchema).nullish().transform((v) => orNull(v));
4057
- var entitySecurityResultSchema = z4.object({
4058
- combinedVerdict: looseRecord,
4059
- ap: engineResultsSchema,
4060
- dlp: engineResultsSchema,
4061
- clicktimeProtection: engineResultsSchema,
4062
- shadowIt: engineResultsSchema,
4063
- av: engineResultsSchema
4064
- }).passthrough().transform((raw) => ({
4065
- combinedVerdict: raw.combinedVerdict,
4066
- ap: raw.ap,
4067
- dlp: raw.dlp,
4068
- clicktimeProtection: raw.clicktimeProtection,
4069
- shadowIt: raw.shadowIt,
4070
- av: raw.av
4071
- }));
4072
- var entityActionSchema = z4.object({
4073
- entityActionName: z4.string(),
4074
- entityActionDate: looseString,
4075
- entityActionResponseCode: looseString,
4076
- entityActionResponseText: looseString,
4077
- entityActionState: looseString
4078
- }).passthrough().transform((raw) => ({
4079
- entityActionName: raw.entityActionName,
4080
- entityActionDate: raw.entityActionDate,
4081
- entityActionResponseCode: raw.entityActionResponseCode,
4082
- entityActionResponseText: raw.entityActionResponseText,
4083
- entityActionState: raw.entityActionState
4084
- }));
4085
- var entityInfoSchema = z4.object({
4086
- entityId: z4.string(),
4087
- customerId: looseString,
4088
- saas: looseString,
4089
- saasEntityType: looseString,
4090
- entityCreated: looseString,
4091
- entityUpdated: looseString,
4092
- entityActionState: looseString
4093
- }).passthrough().transform((raw) => ({
4094
- entityId: raw.entityId,
4095
- customerId: raw.customerId,
4096
- saas: raw.saas,
4097
- saasEntityType: raw.saasEntityType,
4098
- entityCreated: raw.entityCreated,
4099
- entityUpdated: raw.entityUpdated,
4100
- entityActionState: raw.entityActionState
4101
- }));
4102
- var entitySchema = z4.object({
4103
- entityInfo: entityInfoSchema,
4104
- saasInfo: looseRecord,
4105
- entityPayload: looseRecord,
4106
- entitySecurityResult: entitySecurityResultSchema.nullish().transform((v) => orNull(v)),
4107
- entityActions: z4.array(entityActionSchema).nullish(),
4108
- entityAvailableActions: z4.array(z4.string()).nullish()
4109
- }).passthrough().transform((raw) => ({
4110
- entityInfo: raw.entityInfo,
4111
- saasInfo: raw.saasInfo,
4112
- entityPayload: raw.entityPayload,
4113
- entitySecurityResult: raw.entitySecurityResult,
4114
- entityActions: raw.entityActions ?? [],
4115
- entityAvailableActions: raw.entityAvailableActions ?? []
4116
- }));
4117
- function toEntity(raw) {
4118
- return entitySchema.parse(raw);
4119
- }
4120
- function toEntitySummary(raw) {
4121
- const {
4122
- entityPayload: _payload,
4123
- saasInfo: _saasInfo,
4124
- ...rest
4125
- } = toEntity(raw);
4126
- return rest;
4127
- }
4128
- var TASK_STATUSES = [
4129
- "init",
4130
- "inprogress",
4131
- "completed",
4132
- "failed",
4133
- "stopped",
4134
- "paused"
4135
- ];
4136
- var taskSchema = z4.object({
4137
- taskId: z4.string(),
4138
- status: z4.enum(TASK_STATUSES),
4139
- actor: looseString,
4140
- startedAt: looseString,
4141
- finishedAt: looseString
4142
- }).passthrough().transform((raw) => ({
4143
- taskId: raw.taskId,
4144
- status: raw.status,
4145
- actor: raw.actor,
4146
- startedAt: raw.startedAt,
4147
- finishedAt: raw.finishedAt
4148
- }));
4149
- function toTask(raw) {
4150
- return taskSchema.parse(raw);
4151
- }
4152
- var EXCEPTION_TYPES = [
4153
- "whitelist",
4154
- "blacklist",
4155
- "spam_whitelist"
4156
- ];
4157
- var apExceptionSchema = z4.object({
4158
- id: z4.string(),
4159
- type: z4.enum(EXCEPTION_TYPES).nullish().transform((v) => orNull(v)),
4160
- value: looseString,
4161
- actor: looseString,
4162
- createdAt: looseString,
4163
- updatedAt: looseString
4164
- }).passthrough().transform((raw) => ({
4165
- id: raw.id,
4166
- type: raw.type,
4167
- value: raw.value,
4168
- actor: raw.actor,
4169
- createdAt: raw.createdAt,
4170
- updatedAt: raw.updatedAt
4171
- }));
4172
- function toApException(raw) {
4173
- return apExceptionSchema.parse(raw);
4174
- }
4175
-
4176
4521
  // src/tools/shared/session.ts
4177
4522
  function apiFor(serverModule2, extra) {
4178
4523
  return SessionContext.getAPIManager(
@@ -5241,6 +5586,7 @@ WHEN TO USE:
5241
5586
  PURPOSE:
5242
5587
  - \`whitelist\` always allows, \`blacklist\` always blocks, \`spam_whitelist\` allows through the spam filter only
5243
5588
  - Each entry can match by sender email, domain or IP, recipient, subject, link domain, or attachment hash
5589
+ - Each entry returns \`id\`, \`type\`, \`match\` (only the criteria that are set, each as {value, matching}; matching is exact, contains, endswith...), \`comment\`, \`actionNeeded\` (the verdict the entry applies, e.g. phishing), \`ignoringSpfCheck\`, \`actor\` (who last edited it) and \`updatedAt\`
5244
5590
  - Read-only`;
5245
5591
  var GET_DESCRIPTION = `Fetch one Anti-Phishing exception entry by id.
5246
5592
 
@@ -5248,7 +5594,8 @@ WHEN TO USE:
5248
5594
  - Drilling into a specific entry surfaced by \`list_ap_exceptions\`
5249
5595
 
5250
5596
  PURPOSE:
5251
- - \`exc_id\` is the entry id from \`list_ap_exceptions\`; \`exc_type\` must match the list it came from
5597
+ - \`exc_id\` is the entry \`id\` from \`list_ap_exceptions\`; \`exc_type\` must match the list it came from
5598
+ - Returns the same fields as a \`list_ap_exceptions\` entry
5252
5599
  - Read-only`;
5253
5600
  var excTypeSchema = z11.enum(EXCEPTION_TYPES).describe(
5254
5601
  "Which exception list to read: whitelist, blacklist or spam_whitelist."
@@ -5266,7 +5613,11 @@ function registerExceptionTools(server2, serverModule2) {
5266
5613
  args.exc_type,
5267
5614
  args.scope
5268
5615
  );
5269
- return { exceptions: rows.map(toApException) };
5616
+ return {
5617
+ exceptions: rows.map(
5618
+ (row) => toApException(row, args.exc_type)
5619
+ )
5620
+ };
5270
5621
  })
5271
5622
  );
5272
5623
  registerTool(
@@ -5285,7 +5636,8 @@ function registerExceptionTools(server2, serverModule2) {
5285
5636
  args.exc_type,
5286
5637
  args.exc_id,
5287
5638
  args.scope
5288
- )
5639
+ ),
5640
+ args.exc_type
5289
5641
  );
5290
5642
  })
5291
5643
  );