@atlasharness/atlascode 0.1.80 → 0.1.81

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/cli.js +623 -188
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -19862,7 +19862,7 @@ var init_openai = __esm(() => {
19862
19862
  });
19863
19863
 
19864
19864
  // src/modelprovider/constants.ts
19865
- var API_IMAGE_MAX_BASE64_SIZE, IMAGE_TARGET_RAW_SIZE, PDF_TARGET_RAW_SIZE, API_PDF_MAX_PAGES = 100, LLM_TIMEOUT_DEFAULT_MS = 900000, LLM_TIMEOUT_CAP_MS = 1800000, LLM_TIMEOUT_UNIT_WARN_THRESHOLD_MS = 60000, STREAM_IDLE_TIMEOUT_MS = 300000, SUMMARIZE_CONNECTOR_TEXT_BETA_HEADER, AFK_MODE_BETA_HEADER;
19865
+ var API_IMAGE_MAX_BASE64_SIZE, IMAGE_TARGET_RAW_SIZE, PDF_TARGET_RAW_SIZE, API_PDF_MAX_PAGES = 100, LLM_TIMEOUT_DEFAULT_MS = 900000, LLM_TIMEOUT_CAP_MS = 1800000, LLM_TIMEOUT_UNIT_WARN_THRESHOLD_MS = 60000, STREAM_IDLE_TIMEOUT_MS = 300000, STREAM_IDLE_RETURN_GRACE_MS = 1000, SUMMARIZE_CONNECTOR_TEXT_BETA_HEADER, AFK_MODE_BETA_HEADER;
19866
19866
  var init_constants2 = __esm(() => {
19867
19867
  init_shared();
19868
19868
  API_IMAGE_MAX_BASE64_SIZE = 5 * 1024 * 1024;
@@ -19901,7 +19901,7 @@ var init_clients = __esm(() => {
19901
19901
  });
19902
19902
 
19903
19903
  // src/modelprovider/streamIdleWatchdog.ts
19904
- async function* withStreamIdleWatchdog(stream, timeoutMs = STREAM_IDLE_TIMEOUT_MS) {
19904
+ async function* withStreamIdleWatchdog(stream, timeoutMs = STREAM_IDLE_TIMEOUT_MS, returnGraceMs = STREAM_IDLE_RETURN_GRACE_MS) {
19905
19905
  if (!Number.isFinite(timeoutMs) || timeoutMs <= 0) {
19906
19906
  yield* stream;
19907
19907
  return;
@@ -19933,7 +19933,19 @@ async function* withStreamIdleWatchdog(stream, timeoutMs = STREAM_IDLE_TIMEOUT_M
19933
19933
  }
19934
19934
  } finally {
19935
19935
  try {
19936
- await iterator.return?.();
19936
+ const ret = iterator.return?.();
19937
+ if (ret) {
19938
+ let graceTimer;
19939
+ const grace = new Promise((resolve) => {
19940
+ graceTimer = setTimeout(resolve, returnGraceMs);
19941
+ if (typeof graceTimer === "object" && graceTimer !== null && "unref" in graceTimer) {
19942
+ graceTimer.unref();
19943
+ }
19944
+ });
19945
+ await Promise.race([ret.then(() => {
19946
+ return;
19947
+ }, () => {}), grace]);
19948
+ }
19937
19949
  } catch {}
19938
19950
  }
19939
19951
  }
@@ -27201,6 +27213,12 @@ function isQuotaExhaustedMessage(message) {
27201
27213
  }
27202
27214
  return false;
27203
27215
  }
27216
+ function parseToolSchemaRejectionError(err) {
27217
+ if (!(err instanceof APIError2) || err.status !== 400)
27218
+ return null;
27219
+ const m = /invalid schema for (?:tool|function) [`'"]?([A-Za-z0-9_.\-]+)[`'"]?/i.exec(err.message);
27220
+ return m ? m[1] : null;
27221
+ }
27204
27222
  function classifyAPIError(error) {
27205
27223
  if (error instanceof Error && error.message === "Request was aborted.") {
27206
27224
  return "aborted";
@@ -27341,6 +27359,8 @@ function shouldRetryModelError(err, signal) {
27341
27359
  return false;
27342
27360
  if (isClientRequestTimeout(err))
27343
27361
  return false;
27362
+ if (parseToolSchemaRejectionError(err))
27363
+ return false;
27344
27364
  return isRetryableError(err);
27345
27365
  }
27346
27366
  function llmTimeoutUnitHint(ms) {
@@ -29509,8 +29529,8 @@ var SettingsSchema = () => object({
29509
29529
  mcpServers: record(string2(), any()).optional(),
29510
29530
  allowedMcpServers: any().optional(),
29511
29531
  deniedMcpServers: any().optional(),
29512
- enabledMcpjsonServers: array(string2()).optional(),
29513
- disabledMcpjsonServers: array(string2()).optional(),
29532
+ enabledMcpjsonServers: union([string2(), array(string2())]).transform((v) => Array.isArray(v) ? v : [v]).optional(),
29533
+ disabledMcpjsonServers: union([string2(), array(string2())]).transform((v) => Array.isArray(v) ? v : [v]).optional(),
29514
29534
  sandbox: any().optional(),
29515
29535
  permissions: any().optional(),
29516
29536
  hooks: lazy(() => HooksSchema).optional(),
@@ -30144,7 +30164,7 @@ var init_filesystem = __esm(() => {
30144
30164
 
30145
30165
  // src/permissions/permissionRuleParser.ts
30146
30166
  function setLegacyToolNameAliases(map) {
30147
- legacyToolNameAliases = map;
30167
+ legacyToolNameAliases = Object.assign(Object.create(null), map);
30148
30168
  }
30149
30169
  function normalizeLegacyToolName(name) {
30150
30170
  return legacyToolNameAliases[name] ?? name;
@@ -30219,7 +30239,7 @@ function findLastUnescapedChar(str, char) {
30219
30239
  }
30220
30240
  var legacyToolNameAliases;
30221
30241
  var init_permissionRuleParser = __esm(() => {
30222
- legacyToolNameAliases = {};
30242
+ legacyToolNameAliases = Object.create(null);
30223
30243
  });
30224
30244
 
30225
30245
  // src/permissions/mcpRuleNames.ts
@@ -30228,6 +30248,9 @@ function normalizeNameForMCP(name) {
30228
30248
  const normalized = folded.replace(/_+/g, "_").replace(/^_|_$/g, "");
30229
30249
  return normalized === "" ? folded : normalized;
30230
30250
  }
30251
+ function foldMcpNameForCompare(name) {
30252
+ return name.replace(/[^a-zA-Z0-9_-]/g, "_").replace(/_+/g, "_");
30253
+ }
30231
30254
  function mcpInfoFromString(toolString) {
30232
30255
  const parts = toolString.split("__");
30233
30256
  const [mcpPart, serverName, ...toolNameParts] = parts;
@@ -30297,9 +30320,17 @@ function toolMatchesRule(tool, rule) {
30297
30320
  if (rule.ruleValue.toolName === nameForRuleMatch) {
30298
30321
  return true;
30299
30322
  }
30300
- const ruleInfo = mcpInfoFromString(rule.ruleValue.toolName);
30301
- const toolInfo = mcpInfoFromString(nameForRuleMatch);
30302
- return ruleInfo !== null && toolInfo !== null && (ruleInfo.toolName === undefined || ruleInfo.toolName === "*") && ruleInfo.serverName === toolInfo.serverName;
30323
+ const toolInfo = tool.mcpInfo ?? mcpInfoFromString(nameForRuleMatch);
30324
+ const ruleName = rule.ruleValue.toolName;
30325
+ if (!toolInfo || !ruleName.startsWith("mcp__")) {
30326
+ return false;
30327
+ }
30328
+ if (foldMcpNameForCompare(ruleName) === foldMcpNameForCompare(nameForRuleMatch)) {
30329
+ return true;
30330
+ }
30331
+ const rest = ruleName.slice("mcp__".length);
30332
+ const ruleServerRaw = rest.endsWith("__*") ? rest.slice(0, -3) : rest;
30333
+ return foldMcpNameForCompare(ruleServerRaw) === foldMcpNameForCompare(toolInfo.serverName);
30303
30334
  }
30304
30335
  function toolAlwaysAllowedRule(context, tool) {
30305
30336
  return getAllowRules(context).find((rule) => toolMatchesRule(tool, rule)) || null;
@@ -36851,6 +36882,15 @@ function filterToolsForAgent({
36851
36882
  function computeChildSpawnDepth(parentOptions) {
36852
36883
  return (parentOptions?.spawnDepth ?? 0) + 1;
36853
36884
  }
36885
+ function resolveMcpServerDomainTools(toolName, available) {
36886
+ if (typeof toolName !== "string")
36887
+ return null;
36888
+ const info = mcpInfoFromString(toolName);
36889
+ if (!info || info.toolName !== undefined)
36890
+ return null;
36891
+ const prefix = getMcpPrefix(info.serverName);
36892
+ return available.filter((t) => t.name.startsWith(prefix));
36893
+ }
36854
36894
  function resolveAgentTools(agentDefinition, availableTools, allowFanOut = false) {
36855
36895
  const { tools: agentTools, disallowedTools, source } = agentDefinition;
36856
36896
  const filtered = filterToolsForAgent({
@@ -36881,7 +36921,18 @@ function resolveAgentTools(agentDefinition, availableTools, allowFanOut = false)
36881
36921
  seen.add(tool);
36882
36922
  }
36883
36923
  } else {
36884
- invalidTools.push(spec);
36924
+ const domain = resolveMcpServerDomainTools(toolName, allowed);
36925
+ if (domain && domain.length > 0) {
36926
+ validTools.push(spec);
36927
+ for (const t of domain) {
36928
+ if (!seen.has(t)) {
36929
+ resolved.push(t);
36930
+ seen.add(t);
36931
+ }
36932
+ }
36933
+ } else {
36934
+ invalidTools.push(spec);
36935
+ }
36885
36936
  }
36886
36937
  }
36887
36938
  return { hasWildcard: false, validTools, invalidTools, resolvedTools: resolved };
@@ -78400,7 +78451,7 @@ function getMcpDiscoveryInput() {
78400
78451
  return discoveryInput;
78401
78452
  }
78402
78453
  async function buildMcpServerConfigs(input) {
78403
- const servers = {};
78454
+ const servers = Object.create(null);
78404
78455
  for (const [name, raw] of Object.entries(input.settingsServers ?? {})) {
78405
78456
  const parsed = parseMcpServerConfig(raw);
78406
78457
  if (parsed) {
@@ -78631,6 +78682,7 @@ async function terminateChild(name, child) {
78631
78682
  function createMcpConnectionManager() {
78632
78683
  const connections = new Map;
78633
78684
  const pending = new Set;
78685
+ const pendingConnects = new Map;
78634
78686
  const connectFailures = new Map;
78635
78687
  function fail(name, config, error, authFailure = false) {
78636
78688
  const conn = {
@@ -78643,6 +78695,17 @@ function createMcpConnectionManager() {
78643
78695
  connections.set(name, conn);
78644
78696
  return conn;
78645
78697
  }
78698
+ function markBlocked(name, config, reason) {
78699
+ const conn = {
78700
+ name,
78701
+ type: "failed",
78702
+ config,
78703
+ error: `MCP server "${name}" blocked by enterprise policy: ${reason}`,
78704
+ blockedByPolicy: true
78705
+ };
78706
+ connections.set(name, conn);
78707
+ return conn;
78708
+ }
78646
78709
  async function connectStdio(name, config, opts) {
78647
78710
  const finalCommand = process.env.ATLAS_SHELL_PREFIX || config.command;
78648
78711
  const finalArgs = process.env.ATLAS_SHELL_PREFIX ? [[config.command, ...config.args ?? []].join(" ")] : config.args ?? [];
@@ -78700,7 +78763,7 @@ function createMcpConnectionManager() {
78700
78763
  };
78701
78764
  client.onClose(closeOnDrop);
78702
78765
  const initPromise = client.request("initialize", {
78703
- protocolVersion: "2025-03-26",
78766
+ protocolVersion: "2026-07-28",
78704
78767
  capabilities: {
78705
78768
  elicitation: {},
78706
78769
  roots: {}
@@ -78727,6 +78790,7 @@ function createMcpConnectionManager() {
78727
78790
  version: init.serverInfo.version
78728
78791
  }
78729
78792
  } : {},
78793
+ ...init.protocolVersion ? { negotiatedProtocolVersion: init.protocolVersion } : {},
78730
78794
  ...init.instructions ? { instructions: init.instructions } : {},
78731
78795
  config,
78732
78796
  cleanup: async () => {
@@ -78753,6 +78817,21 @@ function createMcpConnectionManager() {
78753
78817
  }
78754
78818
  }
78755
78819
  async function connect(name, config, opts) {
78820
+ const existing = connections.get(name);
78821
+ if (existing?.type === "connected")
78822
+ return existing;
78823
+ const inflight = pendingConnects.get(name);
78824
+ if (inflight)
78825
+ return inflight;
78826
+ const attempt = connectOnce(name, config, opts);
78827
+ pendingConnects.set(name, attempt);
78828
+ try {
78829
+ return await attempt;
78830
+ } finally {
78831
+ pendingConnects.delete(name);
78832
+ }
78833
+ }
78834
+ async function connectOnce(name, config, opts) {
78756
78835
  const existing = connections.get(name);
78757
78836
  if (existing?.type === "connected")
78758
78837
  return existing;
@@ -78794,7 +78873,8 @@ function createMcpConnectionManager() {
78794
78873
  await manager.close(name);
78795
78874
  }
78796
78875
  },
78797
- getPendingServerNames: () => [...pending]
78876
+ getPendingServerNames: () => [...pending],
78877
+ markBlocked
78798
78878
  };
78799
78879
  return manager;
78800
78880
  }
@@ -78809,6 +78889,143 @@ var init_mcpConnectionManager = __esm(() => {
78809
78889
  init_mcpFetch();
78810
78890
  });
78811
78891
 
78892
+ // src/mcp/mcpPolicy.ts
78893
+ function getMcpPolicyFromSettings(settings) {
78894
+ if (typeof settings !== "object" || settings === null)
78895
+ return {};
78896
+ const rec = settings;
78897
+ return {
78898
+ ...rec.allowedMcpServers !== undefined ? { allowedMcpServers: rec.allowedMcpServers } : {},
78899
+ ...rec.deniedMcpServers !== undefined ? { deniedMcpServers: rec.deniedMcpServers } : {}
78900
+ };
78901
+ }
78902
+ function getMcpPolicyInput() {
78903
+ return policyInput;
78904
+ }
78905
+ function normalizePolicyEntries(raw) {
78906
+ if (!Array.isArray(raw))
78907
+ return [];
78908
+ const out = [];
78909
+ for (const entry of raw) {
78910
+ if (typeof entry === "string") {
78911
+ out.push({ kind: "name", value: entry });
78912
+ continue;
78913
+ }
78914
+ if (typeof entry !== "object" || entry === null)
78915
+ continue;
78916
+ const rec = entry;
78917
+ if (typeof rec.serverName === "string") {
78918
+ out.push({ kind: "name", value: rec.serverName });
78919
+ } else if (Array.isArray(rec.serverCommand) && rec.serverCommand.every((c) => typeof c === "string")) {
78920
+ out.push({ kind: "command", value: rec.serverCommand });
78921
+ } else if (typeof rec.serverUrl === "string") {
78922
+ out.push({ kind: "url", value: rec.serverUrl });
78923
+ }
78924
+ }
78925
+ return out;
78926
+ }
78927
+ function getServerCommandArray(config) {
78928
+ const t = config.type;
78929
+ if (t !== undefined && t !== "stdio") {
78930
+ return null;
78931
+ }
78932
+ const stdioConfig = config;
78933
+ return [stdioConfig.command, ...stdioConfig.args ?? []];
78934
+ }
78935
+ function commandArraysMatch(a, b) {
78936
+ if (a.length !== b.length) {
78937
+ return false;
78938
+ }
78939
+ return a.every((val, idx) => val === b[idx]);
78940
+ }
78941
+ function getServerUrl(config) {
78942
+ return "url" in config ? config.url : null;
78943
+ }
78944
+ function urlPatternToRegex(pattern) {
78945
+ const escaped = pattern.replace(/[.+?^${}()|[\]\\]/g, "\\$&");
78946
+ const regexStr = escaped.replace(/\*/g, ".*");
78947
+ return new RegExp(`^${regexStr}$`);
78948
+ }
78949
+ function urlMatchesPattern(url, pattern) {
78950
+ return urlPatternToRegex(pattern).test(url);
78951
+ }
78952
+ function isDeniedByEntries(serverName, config, entries) {
78953
+ for (const entry of entries) {
78954
+ if (entry.kind === "name" && entry.value === serverName)
78955
+ return true;
78956
+ }
78957
+ const serverCommand = getServerCommandArray(config);
78958
+ if (serverCommand) {
78959
+ for (const entry of entries) {
78960
+ if (entry.kind === "command" && commandArraysMatch(entry.value, serverCommand)) {
78961
+ return true;
78962
+ }
78963
+ }
78964
+ }
78965
+ const serverUrl = getServerUrl(config);
78966
+ if (serverUrl) {
78967
+ for (const entry of entries) {
78968
+ if (entry.kind === "url" && urlMatchesPattern(serverUrl, entry.value)) {
78969
+ return true;
78970
+ }
78971
+ }
78972
+ }
78973
+ return false;
78974
+ }
78975
+ function isAllowedByEntries(serverName, config, entries) {
78976
+ const hasCommandEntries = entries.some((e) => e.kind === "command");
78977
+ const hasUrlEntries = entries.some((e) => e.kind === "url");
78978
+ const serverCommand = getServerCommandArray(config);
78979
+ const serverUrl = getServerUrl(config);
78980
+ if (serverCommand) {
78981
+ if (hasCommandEntries) {
78982
+ return entries.some((e) => e.kind === "command" && commandArraysMatch(e.value, serverCommand));
78983
+ }
78984
+ return entries.some((e) => e.kind === "name" && e.value === serverName);
78985
+ }
78986
+ if (serverUrl) {
78987
+ if (hasUrlEntries) {
78988
+ return entries.some((e) => e.kind === "url" && urlMatchesPattern(serverUrl, e.value));
78989
+ }
78990
+ return entries.some((e) => e.kind === "name" && e.value === serverName);
78991
+ }
78992
+ return entries.some((e) => e.kind === "name" && e.value === serverName);
78993
+ }
78994
+ function checkMcpServerPolicy(serverName, config, policy) {
78995
+ const p = policy ?? getMcpPolicyInput() ?? {};
78996
+ const denied = normalizePolicyEntries(p.deniedMcpServers);
78997
+ if (isDeniedByEntries(serverName, config, denied)) {
78998
+ return { blocked: true, reason: "denied by policy (deniedMcpServers)" };
78999
+ }
79000
+ if (!Array.isArray(p.allowedMcpServers))
79001
+ return { blocked: false };
79002
+ if (p.allowedMcpServers.length === 0) {
79003
+ return {
79004
+ blocked: true,
79005
+ reason: "not in allowedMcpServers allowlist (empty allowlist blocks all servers)"
79006
+ };
79007
+ }
79008
+ const allowed = normalizePolicyEntries(p.allowedMcpServers);
79009
+ if (isAllowedByEntries(serverName, config, allowed)) {
79010
+ return { blocked: false };
79011
+ }
79012
+ return { blocked: true, reason: "not in allowedMcpServers allowlist" };
79013
+ }
79014
+ function filterMcpServersByPolicy(configs, policy) {
79015
+ const allowed = {};
79016
+ const blocked = [];
79017
+ for (const [name, config] of Object.entries(configs)) {
79018
+ const verdict = checkMcpServerPolicy(name, config, policy);
79019
+ if (verdict.blocked) {
79020
+ blocked.push({ name, reason: verdict.reason ?? "blocked by policy" });
79021
+ } else {
79022
+ allowed[name] = config;
79023
+ }
79024
+ }
79025
+ return { allowed, blocked };
79026
+ }
79027
+ var policyInput = null;
79028
+
78812
79029
  // src/mcp/index.ts
78813
79030
  var init_mcp3 = __esm(() => {
78814
79031
  init_types6();
@@ -79909,12 +80126,12 @@ var LEGACY_TOOL_NAME_ALIASES;
79909
80126
  var init_legacyToolNameAliases = __esm(() => {
79910
80127
  init_permissions3();
79911
80128
  init_toolNames();
79912
- LEGACY_TOOL_NAME_ALIASES = {
80129
+ LEGACY_TOOL_NAME_ALIASES = Object.assign(Object.create(null), {
79913
80130
  Task: AGENT_TOOL_NAME,
79914
80131
  KillShell: TASK_STOP_TOOL_NAME,
79915
80132
  AgentOutputTool: TASK_OUTPUT_TOOL_NAME,
79916
80133
  BashOutputTool: TASK_OUTPUT_TOOL_NAME
79917
- };
80134
+ });
79918
80135
  setLegacyToolNameAliases(LEGACY_TOOL_NAME_ALIASES);
79919
80136
  });
79920
80137
 
@@ -82800,6 +83017,8 @@ async function queryAgentLoop(deps, args) {
82800
83017
  let turns = 0;
82801
83018
  let terminated = false;
82802
83019
  let lastRound;
83020
+ let activeTools = tools;
83021
+ const degradedTools = new Set;
82803
83022
  const maxGoalTurns = args.context?.maxGoalTurns ?? DEFAULT_MAX_GOAL_TURNS;
82804
83023
  let goalTurns = 0;
82805
83024
  let deferredToolUse;
@@ -82842,6 +83061,24 @@ async function queryAgentLoop(deps, args) {
82842
83061
  deps.emit?.({ type: "compacted", turn: turns, messages });
82843
83062
  }
82844
83063
  }
83064
+ if (deps.refreshRoundContext) {
83065
+ try {
83066
+ const refresh = deps.refreshRoundContext(messages);
83067
+ if (refresh) {
83068
+ if (refresh.tools) {
83069
+ activeTools = degradedTools.size > 0 ? refresh.tools.filter((t) => !degradedTools.has(t.name)) : refresh.tools;
83070
+ }
83071
+ if (refresh.appendedMessages && refresh.appendedMessages.length > 0) {
83072
+ messages = [...messages, ...refresh.appendedMessages];
83073
+ if (deps.transcript) {
83074
+ await deps.transcript.record(messages);
83075
+ }
83076
+ }
83077
+ }
83078
+ } catch (refreshErr) {
83079
+ logForDebugging(`[round-context-refresh] failed: ${errorMessage(refreshErr)}`);
83080
+ }
83081
+ }
82845
83082
  deps.emit?.({ type: "round_start", turn: turns });
82846
83083
  let reactiveRetried = false;
82847
83084
  let effectiveDeps = deps;
@@ -82849,7 +83086,7 @@ async function queryAgentLoop(deps, args) {
82849
83086
  for (;; ) {
82850
83087
  const roundInputLen = messages.length;
82851
83088
  try {
82852
- lastRound = await queryOneRound(effectiveDeps, tools, messages);
83089
+ lastRound = await queryOneRound(effectiveDeps, activeTools, messages);
82853
83090
  } catch (err) {
82854
83091
  if (!reactiveRetried && deps.reactiveCompact !== undefined && isReactiveCompactEnabled() && isReactiveCompactRecoverableError(err)) {
82855
83092
  reactiveRetried = true;
@@ -82872,6 +83109,13 @@ async function queryAgentLoop(deps, args) {
82872
83109
  continue;
82873
83110
  }
82874
83111
  }
83112
+ const rejectedTool = parseToolSchemaRejectionError(err);
83113
+ if (rejectedTool && !degradedTools.has(rejectedTool) && activeTools.some((t) => t.name === rejectedTool)) {
83114
+ degradedTools.add(rejectedTool);
83115
+ activeTools = activeTools.filter((t) => t.name !== rejectedTool);
83116
+ logForDebugging(`[tool-schema-strip] 网关拒绝工具 '${rejectedTool}' 的 input schema,本会话剥除其参数后续跑(tools ${activeTools.length} 个)`);
83117
+ continue;
83118
+ }
82875
83119
  const nextRef = nextFallbackRef(deps.fallbackModels, triedFallbackRefs);
82876
83120
  if (nextRef && shouldRetryModelError(err, deps.signal)) {
82877
83121
  triedFallbackRefs.add(nextRef);
@@ -86960,7 +87204,12 @@ async function connectMcpStartup(args) {
86960
87204
  }) : {};
86961
87205
  const dynamicNames = new Set(Object.keys(dynamicConfigs));
86962
87206
  const allConfigs = strictMcpConfig ? dynamicConfigs : { ...baseConfigs, ...dynamicConfigs };
86963
- const connectAll = () => Promise.allSettled(Object.entries(allConfigs).map(([name, config]) => manager.connect(name, config, dynamicNames.has(name) ? { connectionTimeoutMs: 5000 } : undefined)));
87207
+ const policy = getMcpPolicyInput() ?? getMcpPolicyFromSettings(getSettingsWithErrors().settings);
87208
+ const { allowed, blocked } = filterMcpServersByPolicy(allConfigs, policy);
87209
+ for (const b of blocked) {
87210
+ manager.markBlocked(b.name, allConfigs[b.name], b.reason);
87211
+ }
87212
+ const connectAll = () => Promise.allSettled(Object.entries(allowed).map(([name, config]) => manager.connect(name, config, dynamicNames.has(name) ? { connectionTimeoutMs: 5000 } : undefined)));
86964
87213
  const nonBlocking = isMcpConnectionNonBlocking();
86965
87214
  if (nonBlocking) {
86966
87215
  connectAll();
@@ -88219,6 +88468,10 @@ async function mapConcurrent(items, fn, concurrency) {
88219
88468
  return results;
88220
88469
  }
88221
88470
  async function checkMcpServerHealth(manager, name, server) {
88471
+ const policy = getMcpPolicyInput() ?? getMcpPolicyFromSettings(getSettingsWithErrors().settings);
88472
+ if (checkMcpServerPolicy(name, server, policy).blocked) {
88473
+ return "✗ Blocked by enterprise policy";
88474
+ }
88222
88475
  try {
88223
88476
  const result = await manager.connect(name, server);
88224
88477
  if (result.type === "connected") {
@@ -88514,6 +88767,7 @@ Warning: The command "${actualCommand}" looks like a URL, but is being interpret
88514
88767
  }
88515
88768
  var init_mcp4 = __esm(() => {
88516
88769
  init_mcp3();
88770
+ init_engine();
88517
88771
  init_entryInit();
88518
88772
  init_mcpConfigWrite();
88519
88773
  init_setup();
@@ -124455,8 +124709,8 @@ var isMcpServerCommandEntry = () => ({}), isMcpServerNameEntry = () => ({}), isM
124455
124709
  mcpServers: record(string2(), any()).optional(),
124456
124710
  allowedMcpServers: any().optional(),
124457
124711
  deniedMcpServers: any().optional(),
124458
- enabledMcpjsonServers: array(string2()).optional(),
124459
- disabledMcpjsonServers: array(string2()).optional(),
124712
+ enabledMcpjsonServers: union([string2(), array(string2())]).transform((v) => Array.isArray(v) ? v : [v]).optional(),
124713
+ disabledMcpjsonServers: union([string2(), array(string2())]).transform((v) => Array.isArray(v) ? v : [v]).optional(),
124460
124714
  enabledPlugins: record(string2(), any()).optional(),
124461
124715
  pluginConfigs: any().optional(),
124462
124716
  extraKnownMarketplaces: any().optional(),
@@ -124797,12 +125051,12 @@ function findLastUnescapedChar2(str, char) {
124797
125051
  var LEGACY_TOOL_NAME_ALIASES2;
124798
125052
  var init_permissionRuleParser2 = __esm(() => {
124799
125053
  init_constants6();
124800
- LEGACY_TOOL_NAME_ALIASES2 = {
125054
+ LEGACY_TOOL_NAME_ALIASES2 = Object.assign(Object.create(null), {
124801
125055
  Task: AGENT_TOOL_NAME2,
124802
125056
  KillShell: TASK_STOP_TOOL_NAME2,
124803
125057
  AgentOutputTool: TASK_OUTPUT_TOOL_NAME2,
124804
125058
  BashOutputTool: TASK_OUTPUT_TOOL_NAME2
124805
- };
125059
+ });
124806
125060
  });
124807
125061
 
124808
125062
  // src/tui/utils/settings/toolValidationConfig.ts
@@ -188182,6 +188436,97 @@ var init_streamableHttp = __esm(() => {
188182
188436
  };
188183
188437
  });
188184
188438
 
188439
+ // src/tui/services/mcp/fetchTimeout.ts
188440
+ function getMcpConnectionTimeoutMs() {
188441
+ return parseInt(process.env.MCP_TIMEOUT || "", 10) || 30000;
188442
+ }
188443
+ function bindBodyReadDeadline(response) {
188444
+ const contentType = response.headers?.get("content-type") ?? "";
188445
+ if (contentType.includes("text/event-stream")) {
188446
+ return response;
188447
+ }
188448
+ const rawBody = response.body;
188449
+ if (!rawBody) {
188450
+ return response;
188451
+ }
188452
+ const reader = rawBody.getReader();
188453
+ let deadlineTimer;
188454
+ const deadline = new Promise((_, reject) => {
188455
+ deadlineTimer = setTimeout(() => reject(new DOMException("The operation timed out.", "TimeoutError")), getMcpConnectionTimeoutMs());
188456
+ deadlineTimer.unref?.();
188457
+ });
188458
+ deadline.catch(() => {});
188459
+ const clearDeadline = () => {
188460
+ if (deadlineTimer) {
188461
+ clearTimeout(deadlineTimer);
188462
+ }
188463
+ };
188464
+ const stream = new ReadableStream({
188465
+ async pull(ctrl) {
188466
+ try {
188467
+ const { done, value } = await Promise.race([reader.read(), deadline]);
188468
+ if (done) {
188469
+ clearDeadline();
188470
+ ctrl.close();
188471
+ } else {
188472
+ ctrl.enqueue(value);
188473
+ }
188474
+ } catch (error) {
188475
+ clearDeadline();
188476
+ reader.cancel().catch(() => {});
188477
+ ctrl.error(error);
188478
+ }
188479
+ },
188480
+ cancel(reason) {
188481
+ clearDeadline();
188482
+ return reader.cancel(reason);
188483
+ }
188484
+ });
188485
+ return new Response(stream, {
188486
+ status: response.status,
188487
+ statusText: response.statusText,
188488
+ headers: response.headers
188489
+ });
188490
+ }
188491
+ function wrapFetchWithTimeout(baseFetch) {
188492
+ return async (url, init) => {
188493
+ const method = (init?.method ?? "GET").toUpperCase();
188494
+ if (method === "GET") {
188495
+ return baseFetch(url, init);
188496
+ }
188497
+ const headers = new Headers(init?.headers);
188498
+ if (!headers.has("accept")) {
188499
+ headers.set("accept", MCP_STREAMABLE_HTTP_ACCEPT);
188500
+ }
188501
+ const controller = new AbortController;
188502
+ const timer = setTimeout((c) => c.abort(new DOMException("The operation timed out.", "TimeoutError")), MCP_REQUEST_TIMEOUT_MS, controller);
188503
+ timer.unref?.();
188504
+ const parentSignal = init?.signal;
188505
+ const abort = () => controller.abort(parentSignal?.reason);
188506
+ parentSignal?.addEventListener("abort", abort);
188507
+ if (parentSignal?.aborted) {
188508
+ controller.abort(parentSignal.reason);
188509
+ }
188510
+ const cleanup = () => {
188511
+ clearTimeout(timer);
188512
+ parentSignal?.removeEventListener("abort", abort);
188513
+ };
188514
+ try {
188515
+ const response = await baseFetch(url, {
188516
+ ...init,
188517
+ headers,
188518
+ signal: controller.signal
188519
+ });
188520
+ cleanup();
188521
+ return bindBodyReadDeadline(response);
188522
+ } catch (error) {
188523
+ cleanup();
188524
+ throw error;
188525
+ }
188526
+ };
188527
+ }
188528
+ var MCP_REQUEST_TIMEOUT_MS = 60000, MCP_STREAMABLE_HTTP_ACCEPT = "application/json, text/event-stream";
188529
+
188185
188530
  // node_modules/lodash-es/_baseZipObject.js
188186
188531
  function baseZipObject(props, values, assignFunc) {
188187
188532
  var index = -1, length = props.length, valsLength = values.length, result = {};
@@ -193521,6 +193866,73 @@ var init_browser = __esm(() => {
193521
193866
  init_execFileNoThrow2();
193522
193867
  });
193523
193868
 
193869
+ // src/tui/services/mcp/headersHelper.ts
193870
+ function isMcpServerFromProjectOrLocalSettings(config) {
193871
+ return config.scope === "project" || config.scope === "local";
193872
+ }
193873
+ async function getMcpHeadersFromHelper(serverName, config) {
193874
+ if (!config.headersHelper) {
193875
+ return null;
193876
+ }
193877
+ if ("scope" in config && isMcpServerFromProjectOrLocalSettings(config)) {
193878
+ const isNonInteractive = getIsNonInteractiveSession();
193879
+ const explicitTrust = process.env.ATLAS_TRUST_WORKSPACE === "true";
193880
+ const hasTrust = checkHasTrustDialogAccepted() || isNonInteractive && explicitTrust;
193881
+ if (!hasTrust) {
193882
+ const error = new Error(`Security: headersHelper for MCP server '${serverName}' executed before workspace trust is confirmed. If you see this message, post in ${MACRO.FEEDBACK_CHANNEL}.`);
193883
+ logAntError("MCP headersHelper invoked before trust check", error);
193884
+ return null;
193885
+ }
193886
+ }
193887
+ try {
193888
+ logMCPDebug(serverName, "Executing headersHelper to get dynamic headers");
193889
+ const execResult = await execFileNoThrowWithCwd2("sh", ["-c", config.headersHelper], {
193890
+ timeout: 1e4,
193891
+ env: {
193892
+ ...process.env,
193893
+ ATLAS_MCP_SERVER_NAME: serverName,
193894
+ ATLAS_MCP_SERVER_URL: config.url
193895
+ }
193896
+ });
193897
+ if (execResult.code !== 0 || !execResult.stdout) {
193898
+ throw new Error(`headersHelper for MCP server '${serverName}' did not return a valid value`);
193899
+ }
193900
+ const result = execResult.stdout.trim();
193901
+ const headers = jsonParse2(result);
193902
+ if (typeof headers !== "object" || headers === null || Array.isArray(headers)) {
193903
+ throw new Error(`headersHelper for MCP server '${serverName}' must return a JSON object with string key-value pairs`);
193904
+ }
193905
+ for (const [key, value] of Object.entries(headers)) {
193906
+ if (typeof value !== "string") {
193907
+ throw new Error(`headersHelper for MCP server '${serverName}' returned non-string value for key "${key}": ${typeof value}`);
193908
+ }
193909
+ }
193910
+ logMCPDebug(serverName, `Successfully retrieved ${Object.keys(headers).length} headers from headersHelper`);
193911
+ return headers;
193912
+ } catch (error) {
193913
+ logMCPError(serverName, `Error getting headers from headersHelper: ${errorMessage2(error)}`);
193914
+ logError3(new Error(`Error getting MCP headers from headersHelper for server '${serverName}': ${errorMessage2(error)}`));
193915
+ return null;
193916
+ }
193917
+ }
193918
+ async function getMcpServerHeaders(serverName, config) {
193919
+ const staticHeaders = config.headers || {};
193920
+ const dynamicHeaders = await getMcpHeadersFromHelper(serverName, config) || {};
193921
+ return {
193922
+ ...staticHeaders,
193923
+ ...dynamicHeaders
193924
+ };
193925
+ }
193926
+ var init_headersHelper = __esm(() => {
193927
+ init_bootstrap();
193928
+ init_config6();
193929
+ init_debug();
193930
+ init_errors4();
193931
+ init_execFileNoThrow2();
193932
+ init_log5();
193933
+ init_slowOperations();
193934
+ });
193935
+
193524
193936
  // src/tui/services/mcp/oauthPort.ts
193525
193937
  import { createServer } from "http";
193526
193938
  function buildRedirectUri(port = REDIRECT_PORT_FALLBACK) {
@@ -194028,20 +194440,20 @@ async function writeMcpjsonFile2(config) {
194028
194440
  throw e;
194029
194441
  }
194030
194442
  }
194031
- function getServerCommandArray(config) {
194443
+ function getServerCommandArray2(config) {
194032
194444
  if (config.type !== undefined && config.type !== "stdio") {
194033
194445
  return null;
194034
194446
  }
194035
194447
  const stdioConfig = config;
194036
194448
  return [stdioConfig.command, ...stdioConfig.args ?? []];
194037
194449
  }
194038
- function commandArraysMatch(a, b) {
194450
+ function commandArraysMatch2(a, b) {
194039
194451
  if (a.length !== b.length) {
194040
194452
  return false;
194041
194453
  }
194042
194454
  return a.every((val, idx) => val === b[idx]);
194043
194455
  }
194044
- function getServerUrl(config) {
194456
+ function getServerUrl2(config) {
194045
194457
  return "url" in config ? config.url : null;
194046
194458
  }
194047
194459
  function unwrapCcrProxyUrl(url) {
@@ -194057,11 +194469,11 @@ function unwrapCcrProxyUrl(url) {
194057
194469
  }
194058
194470
  }
194059
194471
  function getMcpServerSignature(config) {
194060
- const cmd = getServerCommandArray(config);
194472
+ const cmd = getServerCommandArray2(config);
194061
194473
  if (cmd) {
194062
194474
  return `stdio:${jsonStringify5(cmd)}`;
194063
194475
  }
194064
- const url = getServerUrl(config);
194476
+ const url = getServerUrl2(config);
194065
194477
  if (url) {
194066
194478
  return `url:${unwrapCcrProxyUrl(url)}`;
194067
194479
  }
@@ -194123,13 +194535,13 @@ function dedupManagedMcpServers(claudeAiServers, manualServers) {
194123
194535
  }
194124
194536
  return { servers, suppressed };
194125
194537
  }
194126
- function urlPatternToRegex(pattern) {
194538
+ function urlPatternToRegex2(pattern) {
194127
194539
  const escaped = pattern.replace(/[.+?^${}()|[\]\\]/g, "\\$&");
194128
194540
  const regexStr = escaped.replace(/\*/g, ".*");
194129
194541
  return new RegExp(`^${regexStr}$`);
194130
194542
  }
194131
- function urlMatchesPattern(url, pattern) {
194132
- const regex = urlPatternToRegex(pattern);
194543
+ function urlMatchesPattern2(url, pattern) {
194544
+ const regex = urlPatternToRegex2(pattern);
194133
194545
  return regex.test(url);
194134
194546
  }
194135
194547
  function getMcpAllowlistSettings() {
@@ -194152,18 +194564,18 @@ function isMcpServerDenied(serverName, config) {
194152
194564
  }
194153
194565
  }
194154
194566
  if (config) {
194155
- const serverCommand = getServerCommandArray(config);
194567
+ const serverCommand = getServerCommandArray2(config);
194156
194568
  if (serverCommand) {
194157
194569
  for (const entry of settings.deniedMcpServers) {
194158
- if (isMcpServerCommandEntry(entry) && commandArraysMatch(entry.serverCommand, serverCommand)) {
194570
+ if (isMcpServerCommandEntry(entry) && commandArraysMatch2(entry.serverCommand, serverCommand)) {
194159
194571
  return true;
194160
194572
  }
194161
194573
  }
194162
194574
  }
194163
- const serverUrl = getServerUrl(config);
194575
+ const serverUrl = getServerUrl2(config);
194164
194576
  if (serverUrl) {
194165
194577
  for (const entry of settings.deniedMcpServers) {
194166
- if (isMcpServerUrlEntry(entry) && urlMatchesPattern(serverUrl, entry.serverUrl)) {
194578
+ if (isMcpServerUrlEntry(entry) && urlMatchesPattern2(serverUrl, entry.serverUrl)) {
194167
194579
  return true;
194168
194580
  }
194169
194581
  }
@@ -194185,12 +194597,12 @@ function isMcpServerAllowedByPolicy(serverName, config) {
194185
194597
  const hasCommandEntries = settings.allowedMcpServers.some(isMcpServerCommandEntry);
194186
194598
  const hasUrlEntries = settings.allowedMcpServers.some(isMcpServerUrlEntry);
194187
194599
  if (config) {
194188
- const serverCommand = getServerCommandArray(config);
194189
- const serverUrl = getServerUrl(config);
194600
+ const serverCommand = getServerCommandArray2(config);
194601
+ const serverUrl = getServerUrl2(config);
194190
194602
  if (serverCommand) {
194191
194603
  if (hasCommandEntries) {
194192
194604
  for (const entry of settings.allowedMcpServers) {
194193
- if (isMcpServerCommandEntry(entry) && commandArraysMatch(entry.serverCommand, serverCommand)) {
194605
+ if (isMcpServerCommandEntry(entry) && commandArraysMatch2(entry.serverCommand, serverCommand)) {
194194
194606
  return true;
194195
194607
  }
194196
194608
  }
@@ -194206,7 +194618,7 @@ function isMcpServerAllowedByPolicy(serverName, config) {
194206
194618
  } else if (serverUrl) {
194207
194619
  if (hasUrlEntries) {
194208
194620
  for (const entry of settings.allowedMcpServers) {
194209
- if (isMcpServerUrlEntry(entry) && urlMatchesPattern(serverUrl, entry.serverUrl)) {
194621
+ if (isMcpServerUrlEntry(entry) && urlMatchesPattern2(serverUrl, entry.serverUrl)) {
194210
194622
  return true;
194211
194623
  }
194212
194624
  }
@@ -194235,7 +194647,7 @@ function isMcpServerAllowedByPolicy(serverName, config) {
194235
194647
  }
194236
194648
  return false;
194237
194649
  }
194238
- function filterMcpServersByPolicy(configs) {
194650
+ function filterMcpServersByPolicy2(configs) {
194239
194651
  const allowed = {};
194240
194652
  const blocked = [];
194241
194653
  for (const [name, config] of Object.entries(configs)) {
@@ -194687,7 +195099,7 @@ async function getAllMcpConfigs2() {
194687
195099
  }
194688
195100
  const claudeaiPromise = Promise.resolve({});
194689
195101
  const { servers: claudeCodeServers, errors } = await getMcpConfigs({}, claudeaiPromise);
194690
- const { allowed: claudeaiMcpServers } = filterMcpServersByPolicy(await claudeaiPromise);
195102
+ const { allowed: claudeaiMcpServers } = filterMcpServersByPolicy2(await claudeaiPromise);
194691
195103
  const { servers: dedupedClaudeAi } = dedupManagedMcpServers(claudeaiMcpServers, claudeCodeServers);
194692
195104
  const servers = Object.assign({}, dedupedClaudeAi, claudeCodeServers);
194693
195105
  return { servers, errors };
@@ -194792,6 +195204,9 @@ function parseMcpConfigFromFilePath(params) {
194792
195204
  ]
194793
195205
  };
194794
195206
  }
195207
+ if (!configContent.trim()) {
195208
+ return { config: { mcpServers: {} }, errors: [] };
195209
+ }
194795
195210
  const parsedJson = safeParseJSON3(configContent);
194796
195211
  if (!parsedJson) {
194797
195212
  logForDebugging3(`MCP config is not valid JSON: ${filePath} (scope=${scope}, length=${configContent.length}, first100=${jsonStringify5(configContent.slice(0, 100))})`, { level: "error" });
@@ -194811,6 +195226,9 @@ function parseMcpConfigFromFilePath(params) {
194811
195226
  ]
194812
195227
  };
194813
195228
  }
195229
+ if (typeof parsedJson === "object" && parsedJson !== null && !Array.isArray(parsedJson) && Object.keys(parsedJson).length === 0) {
195230
+ return { config: { mcpServers: {} }, errors: [] };
195231
+ }
194814
195232
  return parseMcpConfig({
194815
195233
  configObject: parsedJson,
194816
195234
  expandVars,
@@ -194941,11 +195359,11 @@ function hashMcpConfig(config) {
194941
195359
  });
194942
195360
  return createHash4("sha256").update(stable).digest("hex").slice(0, 16);
194943
195361
  }
194944
- function excludeStalePluginClients(mcp, configs) {
195362
+ function excludeStalePluginClients(mcp, configs, opts) {
194945
195363
  const stale = mcp.clients.filter((c) => {
194946
195364
  const fresh = configs[c.name];
194947
195365
  if (!fresh)
194948
- return c.config.scope === "dynamic";
195366
+ return opts?.absentIsStale || c.config.scope === "dynamic";
194949
195367
  return hashMcpConfig(c.config) !== hashMcpConfig(fresh);
194950
195368
  });
194951
195369
  if (stale.length === 0) {
@@ -195741,6 +196159,53 @@ function createAuthFetch() {
195741
196159
  }
195742
196160
  };
195743
196161
  }
196162
+ async function resolveConfiguredMcpHeaders(serverName, serverConfig) {
196163
+ try {
196164
+ return await getMcpServerHeaders(serverName, serverConfig);
196165
+ } catch (error) {
196166
+ logMCPDebug(serverName, `Failed to resolve configured headers for OAuth: ${errorMessage2(error)}`);
196167
+ return {};
196168
+ }
196169
+ }
196170
+ function headersToRecord2(headers) {
196171
+ const out = {};
196172
+ if (!headers)
196173
+ return out;
196174
+ if (headers instanceof Headers) {
196175
+ headers.forEach((v, k) => {
196176
+ out[k] = v;
196177
+ });
196178
+ return out;
196179
+ }
196180
+ if (Array.isArray(headers)) {
196181
+ for (const [k, v] of headers)
196182
+ out[k] = v;
196183
+ return out;
196184
+ }
196185
+ if (typeof headers === "object") {
196186
+ for (const [k, v] of Object.entries(headers)) {
196187
+ out[k] = String(v);
196188
+ }
196189
+ }
196190
+ return out;
196191
+ }
196192
+ function mergeConfiguredHeaders(fetchLike, configured) {
196193
+ if (Object.keys(configured).length === 0)
196194
+ return fetchLike;
196195
+ return (url, init) => {
196196
+ const requestHeaders = headersToRecord2(init?.headers);
196197
+ const requestKeys = new Set(Object.keys(requestHeaders).map((k) => k.toLowerCase()));
196198
+ const configuredNotOverridden = Object.fromEntries(Object.entries(configured).filter(([k]) => !requestKeys.has(k.toLowerCase())));
196199
+ return fetchLike(url, {
196200
+ ...init,
196201
+ headers: { ...configuredNotOverridden, ...requestHeaders }
196202
+ });
196203
+ };
196204
+ }
196205
+ async function withConfiguredHeaders(fetchLike, serverName, serverConfig) {
196206
+ const configured = await resolveConfiguredMcpHeaders(serverName, serverConfig);
196207
+ return mergeConfiguredHeaders(fetchLike, configured);
196208
+ }
195744
196209
  async function fetchAuthServerMetadata(serverName, serverUrl, configuredMetadataUrl, fetchFn, resourceMetadataUrl) {
195745
196210
  if (configuredMetadataUrl) {
195746
196211
  if (!configuredMetadataUrl.startsWith("https://")) {
@@ -195799,12 +196264,14 @@ async function revokeToken({
195799
196264
  clientId,
195800
196265
  clientSecret,
195801
196266
  accessToken,
195802
- authMethod = "client_secret_basic"
196267
+ authMethod = "client_secret_basic",
196268
+ extraHeaders
195803
196269
  }) {
195804
196270
  const params = new URLSearchParams;
195805
196271
  params.set("token", token);
195806
196272
  params.set("token_type_hint", tokenTypeHint);
195807
196273
  const headers = {
196274
+ ...extraHeaders ?? {},
195808
196275
  "Content-Type": "application/x-www-form-urlencoded"
195809
196276
  };
195810
196277
  if (clientId && clientSecret) {
@@ -195847,7 +196314,8 @@ async function revokeServerTokens(serverName, serverConfig, { preserveStepUpStat
195847
196314
  if (tokenData?.accessToken || tokenData?.refreshToken) {
195848
196315
  try {
195849
196316
  const asUrl = tokenData.discoveryState?.authorizationServerUrl ?? serverConfig.url;
195850
- const metadata = await fetchAuthServerMetadata(serverName, asUrl, serverConfig.oauth?.authServerMetadataUrl);
196317
+ const configuredHeaders = await resolveConfiguredMcpHeaders(serverName, serverConfig);
196318
+ const metadata = await fetchAuthServerMetadata(serverName, asUrl, serverConfig.oauth?.authServerMetadataUrl, mergeConfiguredHeaders(createAuthFetch(), configuredHeaders));
195851
196319
  if (!metadata) {
195852
196320
  logMCPDebug(serverName, "No OAuth metadata found");
195853
196321
  } else {
@@ -195869,7 +196337,8 @@ async function revokeServerTokens(serverName, serverConfig, { preserveStepUpStat
195869
196337
  clientId: tokenData.clientId,
195870
196338
  clientSecret: tokenData.clientSecret,
195871
196339
  accessToken: tokenData.accessToken,
195872
- authMethod
196340
+ authMethod,
196341
+ extraHeaders: configuredHeaders
195873
196342
  });
195874
196343
  } catch (error) {
195875
196344
  logMCPDebug(serverName, `Failed to revoke refresh token: ${errorMessage2(error)}`);
@@ -195885,7 +196354,8 @@ async function revokeServerTokens(serverName, serverConfig, { preserveStepUpStat
195885
196354
  clientId: tokenData.clientId,
195886
196355
  clientSecret: tokenData.clientSecret,
195887
196356
  accessToken: tokenData.accessToken,
195888
- authMethod
196357
+ authMethod,
196358
+ extraHeaders: configuredHeaders
195889
196359
  });
195890
196360
  } catch (error) {
195891
196361
  logMCPDebug(serverName, `Failed to revoke access token: ${errorMessage2(error)}`);
@@ -196078,8 +196548,9 @@ async function performMCPOAuthFlow(serverName, serverConfig, onAuthorizationUrl,
196078
196548
  const redirectUri = buildRedirectUri(port);
196079
196549
  logMCPDebug(serverName, `Using redirect port: ${port}${configuredCallbackPort ? " (from config)" : ""}`);
196080
196550
  const provider = new ClaudeAuthProvider(serverName, serverConfig, redirectUri, true, onAuthorizationUrl, options?.skipBrowserOpen);
196551
+ const oauthFetch = await withConfiguredHeaders(createAuthFetch(), serverName, serverConfig);
196081
196552
  try {
196082
- const metadata = await fetchAuthServerMetadata(serverName, serverConfig.url, serverConfig.oauth?.authServerMetadataUrl, undefined, wwwAuthParams.resourceMetadataUrl);
196553
+ const metadata = await fetchAuthServerMetadata(serverName, serverConfig.url, serverConfig.oauth?.authServerMetadataUrl, oauthFetch, wwwAuthParams.resourceMetadataUrl);
196083
196554
  if (metadata) {
196084
196555
  provider.setMetadata(metadata);
196085
196556
  logMCPDebug(serverName, `Fetched OAuth metadata with scope: ${getScopeFromMetadata(metadata) || "NONE"}`);
@@ -196215,7 +196686,8 @@ async function performMCPOAuthFlow(serverName, serverConfig, onAuthorizationUrl,
196215
196686
  const result = await auth(provider, {
196216
196687
  serverUrl: serverConfig.url,
196217
196688
  scope: wwwAuthParams.scope,
196218
- resourceMetadataUrl: wwwAuthParams.resourceMetadataUrl
196689
+ resourceMetadataUrl: wwwAuthParams.resourceMetadataUrl,
196690
+ fetchFn: oauthFetch
196219
196691
  });
196220
196692
  logMCPDebug(serverName, `Initial auth result: ${result}`);
196221
196693
  if (result !== "REDIRECT") {
@@ -196239,7 +196711,8 @@ async function performMCPOAuthFlow(serverName, serverConfig, onAuthorizationUrl,
196239
196711
  const result = await auth(provider, {
196240
196712
  serverUrl: serverConfig.url,
196241
196713
  authorizationCode,
196242
- resourceMetadataUrl: wwwAuthParams.resourceMetadataUrl
196714
+ resourceMetadataUrl: wwwAuthParams.resourceMetadataUrl,
196715
+ fetchFn: oauthFetch
196243
196716
  });
196244
196717
  logMCPDebug(serverName, `Auth result: ${result}`);
196245
196718
  if (result === "AUTHORIZED") {
@@ -196746,7 +197219,7 @@ class ClaudeAuthProvider {
196746
197219
  if (metadataUrl) {
196747
197220
  logMCPDebug(this.serverName, `Fetching metadata from configured URL: ${metadataUrl}`);
196748
197221
  try {
196749
- const metadata = await fetchAuthServerMetadata(this.serverName, this.serverConfig.url, metadataUrl);
197222
+ const metadata = await fetchAuthServerMetadata(this.serverName, this.serverConfig.url, metadataUrl, await withConfiguredHeaders(createAuthFetch(), this.serverName, this.serverConfig));
196750
197223
  if (metadata) {
196751
197224
  return {
196752
197225
  authorizationServerUrl: metadata.issuer,
@@ -196831,7 +197304,7 @@ class ClaudeAuthProvider {
196831
197304
  for (let attempt = 1;attempt <= MAX_ATTEMPTS; attempt++) {
196832
197305
  try {
196833
197306
  logMCPDebug(this.serverName, `Starting token refresh`);
196834
- const authFetch = createAuthFetch();
197307
+ const authFetch = await withConfiguredHeaders(createAuthFetch(), this.serverName, this.serverConfig);
196835
197308
  let metadata = this._metadata;
196836
197309
  if (!metadata) {
196837
197310
  const cached = await this.discoveryState();
@@ -197007,6 +197480,7 @@ var init_auth3 = __esm(() => {
197007
197480
  init_secureStorage();
197008
197481
  init_macOsKeychainHelpers();
197009
197482
  init_slowOperations();
197483
+ init_headersHelper();
197010
197484
  init_oauthPort();
197011
197485
  init_utils6();
197012
197486
  init_xaa();
@@ -201002,10 +201476,10 @@ function parseProxyResponse(socket) {
201002
201476
  read();
201003
201477
  });
201004
201478
  }
201005
- var import_debug52, debug;
201479
+ var import_debug53, debug;
201006
201480
  var init_parse_proxy_response = __esm(() => {
201007
- import_debug52 = __toESM(require_src(), 1);
201008
- debug = import_debug52.default("https-proxy-agent:parse-proxy-response");
201481
+ import_debug53 = __toESM(require_src(), 1);
201482
+ debug = import_debug53.default("https-proxy-agent:parse-proxy-response");
201009
201483
  });
201010
201484
 
201011
201485
  // node_modules/proxy-agent-negotiate/dist/index.js
@@ -201058,7 +201532,7 @@ function omit2(obj, ...keys) {
201058
201532
  }
201059
201533
  return ret;
201060
201534
  }
201061
- var import_debug53, debug2, setServernameFromNonIpHost = (options) => {
201535
+ var import_debug54, debug2, setServernameFromNonIpHost = (options) => {
201062
201536
  if (options.servername === undefined && options.host && !net2.isIP(options.host)) {
201063
201537
  return {
201064
201538
  ...options,
@@ -201070,8 +201544,8 @@ var import_debug53, debug2, setServernameFromNonIpHost = (options) => {
201070
201544
  var init_dist7 = __esm(() => {
201071
201545
  init_dist6();
201072
201546
  init_parse_proxy_response();
201073
- import_debug53 = __toESM(require_src(), 1);
201074
- debug2 = import_debug53.default("https-proxy-agent");
201547
+ import_debug54 = __toESM(require_src(), 1);
201548
+ debug2 = import_debug54.default("https-proxy-agent");
201075
201549
  HttpsProxyAgent2 = class HttpsProxyAgent2 extends Agent2 {
201076
201550
  constructor(proxy, opts) {
201077
201551
  super(opts);
@@ -202132,73 +202606,6 @@ var init_classifyForCollapse = __esm(() => {
202132
202606
  "resources_list"
202133
202607
  ]);
202134
202608
  });
202135
-
202136
- // src/tui/services/mcp/headersHelper.ts
202137
- function isMcpServerFromProjectOrLocalSettings(config) {
202138
- return config.scope === "project" || config.scope === "local";
202139
- }
202140
- async function getMcpHeadersFromHelper(serverName, config) {
202141
- if (!config.headersHelper) {
202142
- return null;
202143
- }
202144
- if ("scope" in config && isMcpServerFromProjectOrLocalSettings(config)) {
202145
- const isNonInteractive = getIsNonInteractiveSession();
202146
- const explicitTrust = process.env.ATLAS_TRUST_WORKSPACE === "true";
202147
- const hasTrust = checkHasTrustDialogAccepted() || isNonInteractive && explicitTrust;
202148
- if (!hasTrust) {
202149
- const error = new Error(`Security: headersHelper for MCP server '${serverName}' executed before workspace trust is confirmed. If you see this message, post in ${MACRO.FEEDBACK_CHANNEL}.`);
202150
- logAntError("MCP headersHelper invoked before trust check", error);
202151
- return null;
202152
- }
202153
- }
202154
- try {
202155
- logMCPDebug(serverName, "Executing headersHelper to get dynamic headers");
202156
- const execResult = await execFileNoThrowWithCwd2("sh", ["-c", config.headersHelper], {
202157
- timeout: 1e4,
202158
- env: {
202159
- ...process.env,
202160
- ATLAS_MCP_SERVER_NAME: serverName,
202161
- ATLAS_MCP_SERVER_URL: config.url
202162
- }
202163
- });
202164
- if (execResult.code !== 0 || !execResult.stdout) {
202165
- throw new Error(`headersHelper for MCP server '${serverName}' did not return a valid value`);
202166
- }
202167
- const result = execResult.stdout.trim();
202168
- const headers = jsonParse2(result);
202169
- if (typeof headers !== "object" || headers === null || Array.isArray(headers)) {
202170
- throw new Error(`headersHelper for MCP server '${serverName}' must return a JSON object with string key-value pairs`);
202171
- }
202172
- for (const [key, value] of Object.entries(headers)) {
202173
- if (typeof value !== "string") {
202174
- throw new Error(`headersHelper for MCP server '${serverName}' returned non-string value for key "${key}": ${typeof value}`);
202175
- }
202176
- }
202177
- logMCPDebug(serverName, `Successfully retrieved ${Object.keys(headers).length} headers from headersHelper`);
202178
- return headers;
202179
- } catch (error) {
202180
- logMCPError(serverName, `Error getting headers from headersHelper: ${errorMessage2(error)}`);
202181
- logError3(new Error(`Error getting MCP headers from headersHelper for server '${serverName}': ${errorMessage2(error)}`));
202182
- return null;
202183
- }
202184
- }
202185
- async function getMcpServerHeaders(serverName, config) {
202186
- const staticHeaders = config.headers || {};
202187
- const dynamicHeaders = await getMcpHeadersFromHelper(serverName, config) || {};
202188
- return {
202189
- ...staticHeaders,
202190
- ...dynamicHeaders
202191
- };
202192
- }
202193
- var init_headersHelper = __esm(() => {
202194
- init_bootstrap();
202195
- init_config6();
202196
- init_debug();
202197
- init_errors4();
202198
- init_execFileNoThrow2();
202199
- init_log5();
202200
- init_slowOperations();
202201
- });
202202
202609
  // src/tui/skills/mcpSkills.ts
202203
202610
  var exports_mcpSkills = {};
202204
202611
  __export(exports_mcpSkills, {
@@ -209645,6 +210052,11 @@ function isMcpSessionExpiredError(error) {
209645
210052
  }
209646
210053
  return error.message.includes('"code":-32001') || error.message.includes('"code": -32001');
209647
210054
  }
210055
+ function truncateMcpDescriptionText(text) {
210056
+ if (!text)
210057
+ return text;
210058
+ return text.length > MAX_MCP_DESCRIPTION_LENGTH ? text.slice(0, MAX_MCP_DESCRIPTION_LENGTH) + "… [truncated]" : text;
210059
+ }
209648
210060
  function getMcpToolTimeoutMs() {
209649
210061
  return parseInt(process.env.MCP_TOOL_TIMEOUT || "", 10) || DEFAULT_MCP_TOOL_TIMEOUT_MS;
209650
210062
  }
@@ -209693,46 +210105,6 @@ async function createNodeWsClient(url, options) {
209693
210105
  const WS = wrapper_default;
209694
210106
  return new WS(url, ["mcp"], options);
209695
210107
  }
209696
- function getConnectionTimeoutMs2() {
209697
- return parseInt(process.env.MCP_TIMEOUT || "", 10) || 30000;
209698
- }
209699
- function wrapFetchWithTimeout(baseFetch) {
209700
- return async (url, init) => {
209701
- const method = (init?.method ?? "GET").toUpperCase();
209702
- if (method === "GET") {
209703
- return baseFetch(url, init);
209704
- }
209705
- const headers = new Headers(init?.headers);
209706
- if (!headers.has("accept")) {
209707
- headers.set("accept", MCP_STREAMABLE_HTTP_ACCEPT);
209708
- }
209709
- const controller = new AbortController;
209710
- const timer = setTimeout((c) => c.abort(new DOMException("The operation timed out.", "TimeoutError")), MCP_REQUEST_TIMEOUT_MS, controller);
209711
- timer.unref?.();
209712
- const parentSignal = init?.signal;
209713
- const abort = () => controller.abort(parentSignal?.reason);
209714
- parentSignal?.addEventListener("abort", abort);
209715
- if (parentSignal?.aborted) {
209716
- controller.abort(parentSignal.reason);
209717
- }
209718
- const cleanup = () => {
209719
- clearTimeout(timer);
209720
- parentSignal?.removeEventListener("abort", abort);
209721
- };
209722
- try {
209723
- const response = await baseFetch(url, {
209724
- ...init,
209725
- headers,
209726
- signal: controller.signal
209727
- });
209728
- cleanup();
209729
- return response;
209730
- } catch (error) {
209731
- cleanup();
209732
- throw error;
209733
- }
209734
- };
209735
- }
209736
210108
  function getMcpServerConnectionBatchSize() {
209737
210109
  return parseInt(process.env.MCP_SERVER_CONNECTION_BATCH_SIZE || "", 10) || 3;
209738
210110
  }
@@ -210382,7 +210754,7 @@ function extractToolUseId(message) {
210382
210754
  }
210383
210755
  return message.message.content[0].id;
210384
210756
  }
210385
- var fetchMcpSkillsForClient2, McpAuthError, McpSessionExpiredError, McpToolCallError_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, DEFAULT_MCP_TOOL_TIMEOUT_MS = 1e8, MAX_MCP_DESCRIPTION_LENGTH = 2048, computerUseWrapper, isComputerUseMCPServer2, MCP_AUTH_CACHE_TTL_MS, authCachePromise = null, writeChain, IMAGE_MIME_TYPES, MCP_REQUEST_TIMEOUT_MS = 60000, MCP_STREAMABLE_HTTP_ACCEPT = "application/json, text/event-stream", ALLOWED_IDE_TOOLS, connectToServer, MCP_FETCH_CACHE_SIZE2 = 20, fetchToolsForClient2, fetchResourcesForClient2, fetchCommandsForClient2;
210757
+ var fetchMcpSkillsForClient2, McpAuthError, McpSessionExpiredError, McpToolCallError_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, DEFAULT_MCP_TOOL_TIMEOUT_MS = 1e8, MAX_MCP_DESCRIPTION_LENGTH = 4096, computerUseWrapper, isComputerUseMCPServer2, MCP_AUTH_CACHE_TTL_MS, authCachePromise = null, writeChain, IMAGE_MIME_TYPES, ALLOWED_IDE_TOOLS, connectToServer, MCP_FETCH_CACHE_SIZE2 = 20, fetchToolsForClient2, fetchResourcesForClient2, fetchCommandsForClient2;
210386
210758
  var init_client4 = __esm(() => {
210387
210759
  init_shared();
210388
210760
  init_client3();
@@ -210674,7 +211046,7 @@ var init_client4 = __esm(() => {
210674
211046
  ]
210675
211047
  };
210676
211048
  });
210677
- logMCPDebug(name, `Starting connection with timeout of ${getConnectionTimeoutMs2()}ms`);
211049
+ logMCPDebug(name, `Starting connection with timeout of ${getMcpConnectionTimeoutMs()}ms`);
210678
211050
  if (serverRef.type === "http") {
210679
211051
  logMCPDebug(name, `Testing basic HTTP connectivity to ${serverRef.url}`);
210680
211052
  try {
@@ -210691,13 +211063,13 @@ var init_client4 = __esm(() => {
210691
211063
  const timeoutPromise = new Promise((_, reject) => {
210692
211064
  const timeoutId = setTimeout(() => {
210693
211065
  const elapsed = Date.now() - connectStartTime;
210694
- logMCPDebug(name, `Connection timeout triggered after ${elapsed}ms (limit: ${getConnectionTimeoutMs2()}ms)`);
211066
+ logMCPDebug(name, `Connection timeout triggered after ${elapsed}ms (limit: ${getMcpConnectionTimeoutMs()}ms)`);
210695
211067
  if (inProcessServer) {
210696
211068
  inProcessServer.close().catch(() => {});
210697
211069
  }
210698
211070
  transport.close().catch(() => {});
210699
- reject(new TelemetrySafeError_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS(`MCP server "${name}" connection timed out after ${getConnectionTimeoutMs2()}ms`, "MCP connection timeout"));
210700
- }, getConnectionTimeoutMs2());
211071
+ reject(new TelemetrySafeError_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS(`MCP server "${name}" connection timed out after ${getMcpConnectionTimeoutMs()}ms`, "MCP connection timeout"));
211072
+ }, getMcpConnectionTimeoutMs());
210701
211073
  connectPromise.then(() => {
210702
211074
  clearTimeout(timeoutId);
210703
211075
  }, (_error) => {
@@ -210745,9 +211117,8 @@ var init_client4 = __esm(() => {
210745
211117
  const capabilities = client.getServerCapabilities();
210746
211118
  const serverVersion = client.getServerVersion();
210747
211119
  const rawInstructions = client.getInstructions();
210748
- let instructions = rawInstructions;
210749
- if (rawInstructions && rawInstructions.length > MAX_MCP_DESCRIPTION_LENGTH) {
210750
- instructions = rawInstructions.slice(0, MAX_MCP_DESCRIPTION_LENGTH) + "… [truncated]";
211120
+ const instructions = truncateMcpDescriptionText(rawInstructions);
211121
+ if (instructions !== rawInstructions) {
210751
211122
  logMCPDebug(name, `Server instructions truncated from ${rawInstructions.length} to ${MAX_MCP_DESCRIPTION_LENGTH} chars`);
210752
211123
  }
210753
211124
  logMCPDebug(name, `Connection established with capabilities: ${jsonStringify5({
@@ -211035,8 +211406,7 @@ var init_client4 = __esm(() => {
211035
211406
  return tool.description ?? "";
211036
211407
  },
211037
211408
  async prompt() {
211038
- const desc = tool.description ?? "";
211039
- return desc.length > MAX_MCP_DESCRIPTION_LENGTH ? desc.slice(0, MAX_MCP_DESCRIPTION_LENGTH) + "… [truncated]" : desc;
211409
+ return truncateMcpDescriptionText(tool.description ?? "");
211040
211410
  },
211041
211411
  isConcurrencySafe() {
211042
211412
  return tool.annotations?.readOnlyHint ?? false;
@@ -276339,6 +276709,15 @@ function filterToolsForAgent2({
276339
276709
  function computeChildSpawnDepth2(parentOptions) {
276340
276710
  return (parentOptions?.spawnDepth ?? 0) + 1;
276341
276711
  }
276712
+ function resolveMcpServerDomainTools2(toolName, available) {
276713
+ if (typeof toolName !== "string")
276714
+ return null;
276715
+ const info = mcpInfoFromString3(toolName);
276716
+ if (!info || info.toolName !== undefined)
276717
+ return null;
276718
+ const prefix = getMcpPrefix3(info.serverName);
276719
+ return available.filter((t) => t.name.startsWith(prefix));
276720
+ }
276342
276721
  function resolveAgentTools2(agentDefinition, availableTools, isAsync = false, isMainThread = false, allowFanOut = false) {
276343
276722
  const {
276344
276723
  tools: agentTools,
@@ -276395,7 +276774,18 @@ function resolveAgentTools2(agentDefinition, availableTools, isAsync = false, is
276395
276774
  resolvedToolsSet.add(tool);
276396
276775
  }
276397
276776
  } else {
276398
- invalidTools.push(toolSpec);
276777
+ const domain = resolveMcpServerDomainTools2(toolName, allowedAvailableTools);
276778
+ if (domain && domain.length > 0) {
276779
+ validTools.push(toolSpec);
276780
+ for (const t of domain) {
276781
+ if (!resolvedToolsSet.has(t)) {
276782
+ resolved.push(t);
276783
+ resolvedToolsSet.add(t);
276784
+ }
276785
+ }
276786
+ } else {
276787
+ invalidTools.push(toolSpec);
276788
+ }
276399
276789
  }
276400
276790
  }
276401
276791
  return {
@@ -276652,6 +277042,7 @@ var init_agentToolUtils2 = __esm(() => {
276652
277042
  init_errors4();
276653
277043
  init_messages3();
276654
277044
  init_permissionRuleParser2();
277045
+ init_mcpStringUtils();
276655
277046
  init_yoloClassifier();
276656
277047
  init_sdkProgress();
276657
277048
  init_teammateContext3();
@@ -321769,7 +322160,8 @@ function buildAgentLoopParams(m) {
321769
322160
  }
321770
322161
  },
321771
322162
  systemPrompt: asSystemPrompt(appendSystemContext(m.systemPrompt, systemContext)),
321772
- effortValue: m.effort !== undefined ? String(m.effort) : undefined
322163
+ effortValue: m.effort !== undefined ? String(m.effort) : undefined,
322164
+ refreshRoundContext: m.refreshRoundContext
321773
322165
  };
321774
322166
  const unboundedTurns = m.unboundedTurns ?? m.maxTurns === undefined;
321775
322167
  const args = {
@@ -322338,7 +322730,8 @@ function getDefaultAppState() {
322338
322730
  tools: [],
322339
322731
  commands: [],
322340
322732
  resources: {},
322341
- pluginReconnectKey: 0
322733
+ pluginReconnectKey: 0,
322734
+ cwdReconnectKey: 0
322342
322735
  },
322343
322736
  plugins: {
322344
322737
  enabled: [],
@@ -348079,7 +348472,8 @@ async function clearConversation({
348079
348472
  tools: [],
348080
348473
  commands: [],
348081
348474
  resources: {},
348082
- pluginReconnectKey: prev.mcp.pluginReconnectKey
348475
+ pluginReconnectKey: prev.mcp.pluginReconnectKey,
348476
+ cwdReconnectKey: prev.mcp.cwdReconnectKey
348083
348477
  }
348084
348478
  };
348085
348479
  });
@@ -369468,7 +369862,9 @@ function useManageMCPConnections(dynamicMcpConfig, isStrictMcpConfig = false) {
369468
369862
  const store = useAppStateStore();
369469
369863
  const _authVersion = useAppState((s) => s.authVersion);
369470
369864
  const _pluginReconnectKey = useAppState((s) => s.mcp.pluginReconnectKey);
369865
+ const _cwdReconnectKey = useAppState((s) => s.mcp.cwdReconnectKey);
369471
369866
  const setAppState = useSetAppState();
369867
+ const lastCwdRef = import_react110.useRef(null);
369472
369868
  const reconnectTimersRef = import_react110.useRef(new Map);
369473
369869
  const mcpFailureTrackerRef = import_react110.useRef(null);
369474
369870
  const notifyMcpConnectionFailure = import_react110.useCallback((serverName, error) => {
@@ -369695,9 +370091,12 @@ function useManageMCPConnections(dynamicMcpConfig, isStrictMcpConfig = false) {
369695
370091
  async function initializeServersAsPending() {
369696
370092
  const { servers: existingConfigs, errors: mcpErrors } = isStrictMcpConfig ? { servers: {}, errors: [] } : await getMcpConfigs(dynamicMcpConfig);
369697
370093
  const configs = { ...existingConfigs, ...dynamicMcpConfig };
370094
+ const currentCwd = getCwd();
370095
+ const cwdChanged = lastCwdRef.current !== null && lastCwdRef.current !== currentCwd;
370096
+ lastCwdRef.current = currentCwd;
369698
370097
  addErrorsToAppState(setAppState, mcpErrors);
369699
370098
  setAppState((prevState) => {
369700
- const { stale, ...mcpWithoutStale } = excludeStalePluginClients(prevState.mcp, configs);
370099
+ const { stale, ...mcpWithoutStale } = excludeStalePluginClients(prevState.mcp, configs, { absentIsStale: cwdChanged });
369701
370100
  for (const s of stale) {
369702
370101
  const timer = reconnectTimersRef.current.get(s.name);
369703
370102
  if (timer) {
@@ -369736,7 +370135,8 @@ function useManageMCPConnections(dynamicMcpConfig, isStrictMcpConfig = false) {
369736
370135
  dynamicMcpConfig,
369737
370136
  setAppState,
369738
370137
  sessionId,
369739
- _pluginReconnectKey
370138
+ _pluginReconnectKey,
370139
+ _cwdReconnectKey
369740
370140
  ]);
369741
370141
  import_react110.useEffect(() => {
369742
370142
  let cancelled = false;
@@ -369753,7 +370153,7 @@ function useManageMCPConnections(dynamicMcpConfig, isStrictMcpConfig = false) {
369753
370153
  });
369754
370154
  let claudeaiConfigs = {};
369755
370155
  if (!isStrictMcpConfig) {
369756
- claudeaiConfigs = filterMcpServersByPolicy(await claudeaiPromise).allowed;
370156
+ claudeaiConfigs = filterMcpServersByPolicy2(await claudeaiPromise).allowed;
369757
370157
  if (cancelled)
369758
370158
  return;
369759
370159
  if (Object.keys(claudeaiConfigs).length > 0) {
@@ -369819,7 +370219,8 @@ function useManageMCPConnections(dynamicMcpConfig, isStrictMcpConfig = false) {
369819
370219
  setAppState,
369820
370220
  _authVersion,
369821
370221
  sessionId,
369822
- _pluginReconnectKey
370222
+ _pluginReconnectKey,
370223
+ _cwdReconnectKey
369823
370224
  ]);
369824
370225
  import_react110.useEffect(() => {
369825
370226
  const timers = reconnectTimersRef.current;
@@ -412095,7 +412496,7 @@ var exports_cd = {};
412095
412496
  __export(exports_cd, {
412096
412497
  call: () => call46
412097
412498
  });
412098
- var USAGE2 = "Usage: /cd [path] (no argument = show current session cwd)", call46 = async (_args, _context) => {
412499
+ var USAGE2 = "Usage: /cd [path] (no argument = show current session cwd)", call46 = async (_args, context) => {
412099
412500
  const arg = (_args ?? "").trim();
412100
412501
  if (arg === "") {
412101
412502
  return {
@@ -412106,6 +412507,10 @@ ${USAGE2}`
412106
412507
  }
412107
412508
  try {
412108
412509
  setCwd2(arg, pwd());
412510
+ context.setAppState((prev) => ({
412511
+ ...prev,
412512
+ mcp: { ...prev.mcp, cwdReconnectKey: prev.mcp.cwdReconnectKey + 1 }
412513
+ }));
412109
412514
  return {
412110
412515
  type: "text",
412111
412516
  value: `Session working directory changed to: ${pwd()}`
@@ -428686,7 +429091,7 @@ function getHttpHookPolicy() {
428686
429091
  allowedEnvVars: settings.httpHookAllowedEnvVars
428687
429092
  };
428688
429093
  }
428689
- function urlMatchesPattern2(url, pattern) {
429094
+ function urlMatchesPattern3(url, pattern) {
428690
429095
  const escaped = pattern.replace(/[.+?^${}()|[\]\\]/g, "\\$&");
428691
429096
  const regexStr = escaped.replace(/\*/g, ".*");
428692
429097
  return new RegExp(`^${regexStr}$`).test(url);
@@ -428708,7 +429113,7 @@ function interpolateEnvVars(value, allowedEnvVars) {
428708
429113
  async function execHttpHook(hook, _hookEvent, jsonInput, signal) {
428709
429114
  const policy = getHttpHookPolicy();
428710
429115
  if (policy.allowedUrls !== undefined) {
428711
- const matched = policy.allowedUrls.some((p) => urlMatchesPattern2(hook.url, p));
429116
+ const matched = policy.allowedUrls.some((p) => urlMatchesPattern3(hook.url, p));
428712
429117
  if (!matched) {
428713
429118
  const msg = `HTTP hook blocked: ${hook.url} does not match any pattern in allowedHttpHookUrls`;
428714
429119
  logForDebugging3(msg, { level: "warn" });
@@ -470300,6 +470705,32 @@ var init_useMailboxBridge = __esm(() => {
470300
470705
 
470301
470706
  // src/tui/replLoopDeps.ts
470302
470707
  function buildReplLoopParams(m) {
470708
+ let prevPoolSig = (m.tools ?? []).map((t) => t.name).join(",");
470709
+ const refreshRoundContext = (messages) => {
470710
+ const appState = m.toolUseContext?.getAppState?.();
470711
+ const liveMcpTools = appState?.mcp?.tools;
470712
+ if (!liveMcpTools)
470713
+ return null;
470714
+ const pool = assembleToolPool(m.toolPermissionContext, liveMcpTools);
470715
+ const sig = pool.map((t) => t.name).join(",");
470716
+ const tuiMessages = messages;
470717
+ const atts = [
470718
+ ...getDeferredToolsDeltaAttachment(pool, m.mainLoopModel, tuiMessages, {
470719
+ callSite: "round_refresh",
470720
+ querySource: m.querySource
470721
+ }),
470722
+ ...getMcpInstructionsDeltaAttachment(m.toolUseContext.options.mcpClients ?? [], pool, m.mainLoopModel, tuiMessages)
470723
+ ];
470724
+ if (sig === prevPoolSig && atts.length === 0)
470725
+ return null;
470726
+ prevPoolSig = sig;
470727
+ return {
470728
+ tools: pool,
470729
+ ...atts.length > 0 ? {
470730
+ appendedMessages: atts.map((att) => createAttachmentMessage(att))
470731
+ } : {}
470732
+ };
470733
+ };
470303
470734
  return buildAgentLoopParams({
470304
470735
  messages: m.messages,
470305
470736
  userContext: m.userContext,
@@ -470315,11 +470746,14 @@ function buildReplLoopParams(m) {
470315
470746
  toolUseContext: m.toolUseContext,
470316
470747
  querySource: m.querySource,
470317
470748
  unboundedTurns: true,
470318
- maxGoalTurns: m.maxGoalTurns
470749
+ maxGoalTurns: m.maxGoalTurns,
470750
+ refreshRoundContext
470319
470751
  });
470320
470752
  }
470321
470753
  var init_replLoopDeps = __esm(() => {
470322
470754
  init_agentLoopDeps();
470755
+ init_tools3();
470756
+ init_attachments();
470323
470757
  });
470324
470758
 
470325
470759
  // src/tui/hooks/useMergedClients.ts
@@ -493526,7 +493960,7 @@ ${formattedErrors}
493526
493960
  const {
493527
493961
  allowed,
493528
493962
  blocked
493529
- } = filterMcpServersByPolicy(scopedConfigs);
493963
+ } = filterMcpServersByPolicy2(scopedConfigs);
493530
493964
  if (blocked.length > 0) {
493531
493965
  process.stderr.write(`Warning: MCP ${plural2(blocked.length, "server")} blocked by enterprise policy: ${blocked.join(", ")}
493532
493966
  `);
@@ -494207,7 +494641,8 @@ ${inputPrompt}` : mergePrompt;
494207
494641
  tools: [],
494208
494642
  commands: [],
494209
494643
  resources: {},
494210
- pluginReconnectKey: 0
494644
+ pluginReconnectKey: 0,
494645
+ cwdReconnectKey: 0
494211
494646
  },
494212
494647
  plugins: {
494213
494648
  enabled: [],