@bastani/atomic 0.9.25-alpha.1 → 0.9.25-alpha.3

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 (133) hide show
  1. package/CHANGELOG.md +36 -0
  2. package/dist/builtin/intercom/package.json +2 -2
  3. package/dist/builtin/mcp/CHANGELOG.md +13 -0
  4. package/dist/builtin/mcp/index.bundle.mjs +489 -208
  5. package/dist/builtin/mcp/package.json +2 -2
  6. package/dist/builtin/subagents/package.json +3 -3
  7. package/dist/builtin/web-access/package.json +2 -2
  8. package/dist/builtin/workflows/CHANGELOG.md +12 -0
  9. package/dist/builtin/workflows/builtin/{chunk-e1254zvd.js → chunk-kfsnh3w7.js} +40 -12
  10. package/dist/builtin/workflows/builtin/{chunk-cpw5gtzg.js → chunk-rqmasd3d.js} +1 -1
  11. package/dist/builtin/workflows/builtin/{chunk-qy6g0baf.js → chunk-wz783pvd.js} +1 -1
  12. package/dist/builtin/workflows/builtin/goal.js +2 -2
  13. package/dist/builtin/workflows/builtin/index.js +3 -3
  14. package/dist/builtin/workflows/builtin/ralph.js +2 -2
  15. package/dist/builtin/workflows/package.json +2 -2
  16. package/dist/builtin/workflows/src/extension/index.bundle.mjs +472 -88
  17. package/dist/builtin/workflows/src/index.js +65 -28
  18. package/dist/core/agent-session-accessors.d.ts.map +1 -1
  19. package/dist/core/agent-session-accessors.js +7 -0
  20. package/dist/core/agent-session-accessors.js.map +1 -1
  21. package/dist/core/agent-session-extension-bindings.d.ts.map +1 -1
  22. package/dist/core/agent-session-extension-bindings.js +16 -2
  23. package/dist/core/agent-session-extension-bindings.js.map +1 -1
  24. package/dist/core/agent-session-methods.d.ts +7 -1
  25. package/dist/core/agent-session-methods.d.ts.map +1 -1
  26. package/dist/core/agent-session-methods.js.map +1 -1
  27. package/dist/core/agent-session-state.d.ts.map +1 -1
  28. package/dist/core/agent-session-state.js +3 -2
  29. package/dist/core/agent-session-state.js.map +1 -1
  30. package/dist/core/agent-session-types.d.ts +1 -0
  31. package/dist/core/agent-session-types.d.ts.map +1 -1
  32. package/dist/core/agent-session-types.js.map +1 -1
  33. package/dist/core/agent-session.d.ts +3 -0
  34. package/dist/core/agent-session.d.ts.map +1 -1
  35. package/dist/core/agent-session.js +6 -0
  36. package/dist/core/agent-session.js.map +1 -1
  37. package/dist/core/builtin-resource-loader.d.ts.map +1 -1
  38. package/dist/core/builtin-resource-loader.js +13 -1
  39. package/dist/core/builtin-resource-loader.js.map +1 -1
  40. package/dist/core/extensions/api-types.d.ts +3 -0
  41. package/dist/core/extensions/api-types.d.ts.map +1 -1
  42. package/dist/core/extensions/api-types.js.map +1 -1
  43. package/dist/core/extensions/index.d.ts +2 -0
  44. package/dist/core/extensions/index.d.ts.map +1 -1
  45. package/dist/core/extensions/index.js +1 -0
  46. package/dist/core/extensions/index.js.map +1 -1
  47. package/dist/core/extensions/loader-api.d.ts.map +1 -1
  48. package/dist/core/extensions/loader-api.js +11 -0
  49. package/dist/core/extensions/loader-api.js.map +1 -1
  50. package/dist/core/extensions/loader-runtime.d.ts.map +1 -1
  51. package/dist/core/extensions/loader-runtime.js +3 -0
  52. package/dist/core/extensions/loader-runtime.js.map +1 -1
  53. package/dist/core/extensions/mcp-server-registry.d.ts.map +1 -1
  54. package/dist/core/extensions/mcp-server-registry.js +4 -0
  55. package/dist/core/extensions/mcp-server-registry.js.map +1 -1
  56. package/dist/core/extensions/runner.d.ts +3 -0
  57. package/dist/core/extensions/runner.d.ts.map +1 -1
  58. package/dist/core/extensions/runner.js +4 -0
  59. package/dist/core/extensions/runner.js.map +1 -1
  60. package/dist/core/extensions/runtime-types.d.ts +3 -0
  61. package/dist/core/extensions/runtime-types.d.ts.map +1 -1
  62. package/dist/core/extensions/runtime-types.js.map +1 -1
  63. package/dist/core/extensions/tool-types.d.ts +1 -0
  64. package/dist/core/extensions/tool-types.d.ts.map +1 -1
  65. package/dist/core/extensions/tool-types.js.map +1 -1
  66. package/dist/core/extensions/workflow-run-control-hub.d.ts +23 -0
  67. package/dist/core/extensions/workflow-run-control-hub.d.ts.map +1 -0
  68. package/dist/core/extensions/workflow-run-control-hub.js +57 -0
  69. package/dist/core/extensions/workflow-run-control-hub.js.map +1 -0
  70. package/dist/core/extensions/workflow-run-control.d.ts +163 -0
  71. package/dist/core/extensions/workflow-run-control.d.ts.map +1 -0
  72. package/dist/core/extensions/workflow-run-control.js +73 -0
  73. package/dist/core/extensions/workflow-run-control.js.map +1 -0
  74. package/dist/core/mandatory-resource-loader.d.ts.map +1 -1
  75. package/dist/core/mandatory-resource-loader.js +9 -0
  76. package/dist/core/mandatory-resource-loader.js.map +1 -1
  77. package/dist/core/mcp-servers.d.ts +4 -1
  78. package/dist/core/mcp-servers.d.ts.map +1 -1
  79. package/dist/core/mcp-servers.js.map +1 -1
  80. package/dist/core/oauth-provider-metadata.js +1 -1
  81. package/dist/core/oauth-provider-metadata.js.map +1 -1
  82. package/dist/core/remote-catalog-provider.d.ts.map +1 -1
  83. package/dist/core/remote-catalog-provider.js +4 -9
  84. package/dist/core/remote-catalog-provider.js.map +1 -1
  85. package/dist/core/resource-loader-types.d.ts +4 -0
  86. package/dist/core/resource-loader-types.d.ts.map +1 -1
  87. package/dist/core/resource-loader-types.js.map +1 -1
  88. package/dist/core/sdk.js +1 -0
  89. package/dist/core/sdk.js.map +1 -1
  90. package/dist/core/structured-output/index.d.ts.map +1 -1
  91. package/dist/core/structured-output/index.js +3 -1
  92. package/dist/core/structured-output/index.js.map +1 -1
  93. package/dist/core/tool-loadout.d.ts +1 -0
  94. package/dist/core/tool-loadout.d.ts.map +1 -1
  95. package/dist/core/tool-loadout.js +3 -0
  96. package/dist/core/tool-loadout.js.map +1 -1
  97. package/dist/core/tools/bash.d.ts.map +1 -1
  98. package/dist/core/tools/bash.js +11 -30
  99. package/dist/core/tools/bash.js.map +1 -1
  100. package/dist/extensions/codemode/execute.d.ts.map +1 -1
  101. package/dist/extensions/codemode/execute.js +26 -0
  102. package/dist/extensions/codemode/execute.js.map +1 -1
  103. package/dist/extensions/codemode/renderer.d.ts.map +1 -1
  104. package/dist/extensions/codemode/renderer.js +38 -23
  105. package/dist/extensions/codemode/renderer.js.map +1 -1
  106. package/dist/extensions/codemode/tool.d.ts.map +1 -1
  107. package/dist/extensions/codemode/tool.js +22 -10
  108. package/dist/extensions/codemode/tool.js.map +1 -1
  109. package/dist/extensions/tool-search/tool.d.ts +1 -1
  110. package/dist/extensions/tool-search/tool.d.ts.map +1 -1
  111. package/dist/extensions/tool-search/tool.js +3 -13
  112. package/dist/extensions/tool-search/tool.js.map +1 -1
  113. package/dist/index-extensions.d.ts +2 -1
  114. package/dist/index-extensions.d.ts.map +1 -1
  115. package/dist/index-extensions.js +1 -1
  116. package/dist/index-extensions.js.map +1 -1
  117. package/dist/modes/interactive/components/visual-truncate.d.ts +27 -3
  118. package/dist/modes/interactive/components/visual-truncate.d.ts.map +1 -1
  119. package/dist/modes/interactive/components/visual-truncate.js +39 -6
  120. package/dist/modes/interactive/components/visual-truncate.js.map +1 -1
  121. package/docs/extensions/events.md +2 -0
  122. package/docs/mcp-servers.md +47 -0
  123. package/docs/providers.md +9 -1
  124. package/docs/sdk/reference.md +22 -0
  125. package/docs/sdk/structured-decisions.md +2 -0
  126. package/docs/sdk.md +67 -1
  127. package/docs/settings.md +2 -0
  128. package/docs/tools.md +6 -0
  129. package/docs/workflows/operations.md +3 -1
  130. package/examples/extensions/built-in-tool-renderer.ts +16 -40
  131. package/examples/extensions/minimal-mode.ts +28 -108
  132. package/npm-shrinkwrap.json +69 -69
  133. package/package.json +8 -8
@@ -210,6 +210,90 @@ var init_state_lease = __esm(() => {
210
210
  };
211
211
  });
212
212
 
213
+ // dist/builtin/mcp/authorization-notice.ts
214
+ import { hyperlink } from "@earendil-works/pi-tui";
215
+ function authorizationNotice(url) {
216
+ const clickHint = process.platform === "darwin" ? "Cmd+click to open" : "Ctrl+click to open";
217
+ return `Approve access in your browser. If it did not open, visit:
218
+ ${hyperlink(url, url)}
219
+ ${hyperlink(clickHint, url)}`;
220
+ }
221
+ var init_authorization_notice = () => {};
222
+
223
+ // dist/builtin/mcp/provider-auth.ts
224
+ function isProviderAuth(auth) {
225
+ return typeof auth === "object" && auth !== null && typeof auth.provider === "string";
226
+ }
227
+ function providerAuthShapeError(auth) {
228
+ if (typeof auth !== "object" || auth === null)
229
+ return;
230
+ const provider = auth.provider;
231
+ return typeof provider === "string" && provider.trim() ? undefined : "auth.provider must be a provider name";
232
+ }
233
+ function providerAuthUrlError(value) {
234
+ if (!value)
235
+ return "auth.provider requires a url";
236
+ try {
237
+ const url = new URL(value);
238
+ if (url.protocol === "https:" || url.protocol === "http:" && LOOPBACK_HOSTS.has(url.hostname))
239
+ return;
240
+ } catch {}
241
+ return URL_REQUIREMENT;
242
+ }
243
+ function providerSignInMessage(serverName, provider) {
244
+ return `MCP server "${serverName}" requires sign-in. Run /login ${provider} to sign in.`;
245
+ }
246
+ function providerSignInGuidance(definition, serverName) {
247
+ return isProviderAuth(definition?.auth) ? providerSignInMessage(serverName, definition.auth.provider) : undefined;
248
+ }
249
+ function signInRequired(serverName, provider) {
250
+ return new McpProviderAuthError(providerSignInMessage(serverName, provider), provider);
251
+ }
252
+ function createProviderAuthFetch(options) {
253
+ const { serverName, provider, token } = options;
254
+ const origin = new URL(options.serverUrl).origin;
255
+ return async (input, init) => {
256
+ let url = new URL(input);
257
+ for (let redirects = 0;; redirects++) {
258
+ if (url.origin !== origin) {
259
+ throw new McpProviderAuthError(`MCP server "${serverName}" request refused: provider credentials are only sent to the configured server origin.`);
260
+ }
261
+ const value = await token?.(provider);
262
+ if (!value)
263
+ throw signInRequired(serverName, provider);
264
+ const headers = new Headers(init?.headers);
265
+ headers.set("Authorization", `Bearer ${value}`);
266
+ const response = await (options.fetch ?? globalThis.fetch)(url, { ...init, headers, redirect: "manual" });
267
+ if (response.status === 401) {
268
+ await response.body?.cancel().catch(() => {});
269
+ throw signInRequired(serverName, provider);
270
+ }
271
+ const location = response.status >= 300 && response.status < 400 ? response.headers.get("location") : null;
272
+ if (!location)
273
+ return response;
274
+ await response.body?.cancel().catch(() => {});
275
+ const target = new URL(location, url);
276
+ const samePreservingRedirect = response.status === 307 || response.status === 308;
277
+ if (!samePreservingRedirect || target.origin !== origin || redirects >= MAX_SAME_ORIGIN_REDIRECTS) {
278
+ throw new McpProviderAuthError(`MCP server "${serverName}" redirected the request; provider credentials are not sent across redirects.`);
279
+ }
280
+ url = target;
281
+ }
282
+ };
283
+ }
284
+ var LOOPBACK_HOSTS, MAX_SAME_ORIGIN_REDIRECTS = 3, URL_REQUIREMENT = "auth.provider requires an https URL, or http on localhost, 127.0.0.1, or [::1]", McpProviderAuthError;
285
+ var init_provider_auth = __esm(() => {
286
+ LOOPBACK_HOSTS = new Set(["localhost", "127.0.0.1", "[::1]"]);
287
+ McpProviderAuthError = class McpProviderAuthError extends Error {
288
+ provider;
289
+ constructor(message, provider) {
290
+ super(message);
291
+ this.provider = provider;
292
+ this.name = "McpProviderAuthError";
293
+ }
294
+ };
295
+ });
296
+
213
297
  // dist/builtin/mcp/agent-dir.ts
214
298
  import { homedir } from "node:os";
215
299
  import { join, resolve } from "node:path";
@@ -6453,10 +6537,141 @@ var init_tool_call_timeout = __esm(() => {
6453
6537
  };
6454
6538
  });
6455
6539
 
6540
+ // dist/builtin/mcp/utils.ts
6541
+ var exports_utils = {};
6542
+ __export(exports_utils, {
6543
+ extractToolUiStreamMode: () => extractToolUiStreamMode,
6544
+ formatAuthRequiredMessage: () => formatAuthRequiredMessage,
6545
+ getConfigPathFromArgv: () => getConfigPathFromArgv,
6546
+ interpolateEnvRecord: () => interpolateEnvRecord,
6547
+ interpolateEnvVars: () => interpolateEnvVars,
6548
+ openPath: () => openPath,
6549
+ openUrl: () => openUrl,
6550
+ parallelLimit: () => parallelLimit,
6551
+ resolveBearerToken: () => resolveBearerToken,
6552
+ resolveConfigPath: () => resolveConfigPath,
6553
+ resolveServerUrl: () => resolveServerUrl,
6554
+ truncateAtWord: () => truncateAtWord,
6555
+ unflattenToolArguments: () => unflattenToolArguments
6556
+ });
6557
+ import { unflattenArgumentsWithSchema } from "@bastani/atomic";
6558
+ import { homedir as homedir2, platform } from "node:os";
6559
+ import { join as join2 } from "node:path";
6560
+ async function execOpen(pi, target, browser) {
6561
+ const os = platform();
6562
+ if (os === "darwin") {
6563
+ return browser ? pi.exec("open", ["-a", browser, target]) : pi.exec("open", [target]);
6564
+ }
6565
+ if (os === "win32") {
6566
+ return browser ? pi.exec("cmd", ["/c", "start", "", browser, target]) : pi.exec("cmd", ["/c", "start", "", target]);
6567
+ }
6568
+ return browser ? pi.exec(browser, [target]) : pi.exec("xdg-open", [target]);
6569
+ }
6570
+ async function openUrl(pi, url, browser) {
6571
+ const result = await execOpen(pi, url, browser);
6572
+ if (result.code !== 0) {
6573
+ throw new Error(result.stderr || `Failed to open browser (exit code ${result.code})`);
6574
+ }
6575
+ }
6576
+ async function openPath(pi, targetPath) {
6577
+ const result = await execOpen(pi, targetPath);
6578
+ if (result.code !== 0) {
6579
+ throw new Error(result.stderr || `Failed to open path (exit code ${result.code})`);
6580
+ }
6581
+ }
6582
+ async function parallelLimit(items, limit, fn) {
6583
+ const results = [];
6584
+ let index = 0;
6585
+ async function worker() {
6586
+ while (index < items.length) {
6587
+ const i = index++;
6588
+ results[i] = await fn(items[i]);
6589
+ }
6590
+ }
6591
+ const workers = Array(Math.min(limit, items.length)).fill(null).map(() => worker());
6592
+ await Promise.all(workers);
6593
+ return results;
6594
+ }
6595
+ function getConfigPathFromArgv() {
6596
+ const idx = process.argv.indexOf("--mcp-config");
6597
+ if (idx >= 0 && idx + 1 < process.argv.length) {
6598
+ return process.argv[idx + 1];
6599
+ }
6600
+ return;
6601
+ }
6602
+ function interpolateEnvVars(value) {
6603
+ return value.replace(/\$\{(\w+)\}/g, (_, name) => process.env[name] ?? "").replace(/\$env:(\w+)/g, (_, name) => process.env[name] ?? "");
6604
+ }
6605
+ function resolveServerUrl(value) {
6606
+ const resolved = interpolateEnvVars(value);
6607
+ try {
6608
+ const url = new URL(resolved);
6609
+ if (url.protocol === "http:" || url.protocol === "https:")
6610
+ return resolved;
6611
+ } catch {}
6612
+ throw new Error("Invalid MCP server url: expected a non-empty HTTP(S) URL after environment variable interpolation.");
6613
+ }
6614
+ function interpolateEnvRecord(values) {
6615
+ if (!values)
6616
+ return;
6617
+ const resolved = {};
6618
+ for (const [key, value] of Object.entries(values)) {
6619
+ resolved[key] = interpolateEnvVars(value);
6620
+ }
6621
+ return resolved;
6622
+ }
6623
+ function resolveConfigPath(value) {
6624
+ if (value === undefined)
6625
+ return;
6626
+ const resolved = interpolateEnvVars(value);
6627
+ if (resolved === "~")
6628
+ return homedir2();
6629
+ if (resolved.startsWith("~/") || resolved.startsWith("~\\")) {
6630
+ return join2(homedir2(), resolved.slice(2));
6631
+ }
6632
+ return resolved;
6633
+ }
6634
+ function resolveBearerToken(definition) {
6635
+ if (definition.bearerToken !== undefined) {
6636
+ return interpolateEnvVars(definition.bearerToken);
6637
+ }
6638
+ return definition.bearerTokenEnv ? process.env[definition.bearerTokenEnv] : undefined;
6639
+ }
6640
+ function truncateAtWord(text, target) {
6641
+ if (!text || text.length <= target)
6642
+ return text;
6643
+ const truncated = text.slice(0, target);
6644
+ const lastSpace = truncated.lastIndexOf(" ");
6645
+ if (lastSpace > target * 0.6) {
6646
+ return truncated.slice(0, lastSpace) + "...";
6647
+ }
6648
+ return truncated + "...";
6649
+ }
6650
+ function formatAuthRequiredMessage(config, serverName, defaultMessage) {
6651
+ const template = config.settings?.authRequiredMessage;
6652
+ return template ? template.replaceAll("${server}", serverName) : defaultMessage;
6653
+ }
6654
+ function extractToolUiStreamMode(toolMeta) {
6655
+ const uiMeta = toolMeta?.ui;
6656
+ if (!uiMeta || typeof uiMeta !== "object")
6657
+ return;
6658
+ const streamMode = uiMeta["pi-mcp-adapter.streamMode"];
6659
+ if (streamMode === "eager" || streamMode === "stream-first") {
6660
+ return streamMode;
6661
+ }
6662
+ return;
6663
+ }
6664
+ function unflattenToolArguments(args, inputSchema) {
6665
+ if (args === null || args === undefined)
6666
+ return {};
6667
+ return unflattenArgumentsWithSchema(args, inputSchema);
6668
+ }
6669
+ var init_utils = () => {};
6670
+
6456
6671
  // dist/builtin/mcp/config.ts
6457
6672
  import { existsSync as existsSync2, readFileSync as readFileSync2 } from "node:fs";
6458
- import { homedir as homedir2 } from "node:os";
6459
- import { dirname as dirname2, join as join2, resolve as resolve2 } from "node:path";
6673
+ import { homedir as homedir3 } from "node:os";
6674
+ import { dirname as dirname2, join as join3, resolve as resolve2 } from "node:path";
6460
6675
  import { CONFIG_DIR_NAME as CONFIG_DIR_NAME2, getProjectConfigPaths } from "@bastani/atomic";
6461
6676
  function getPiGlobalConfigPath(overridePath) {
6462
6677
  return overridePath ? resolve2(overridePath) : getAgentPath("mcp.json");
@@ -6472,7 +6687,7 @@ function getProjectPiConfigPaths(cwd = process.cwd()) {
6472
6687
  }
6473
6688
  function getMcpDiscoverySummary(overridePath, cwd = process.cwd()) {
6474
6689
  const sources = getConfigSources(overridePath, cwd).map((source) => {
6475
- const loaded = readValidatedConfig(source.readPath, `MCP config from ${source.readPath}`);
6690
+ const loaded = readValidatedConfig(source.readPath, `MCP config from ${source.readPath}`, source.scope === "global");
6476
6691
  return {
6477
6692
  id: source.id,
6478
6693
  label: source.label,
@@ -6520,10 +6735,10 @@ function getMcpDiscoverySummary(overridePath, cwd = process.cwd()) {
6520
6735
  function loadMcpConfig(overridePath, cwd = process.cwd(), contributions = []) {
6521
6736
  let config = { mcpServers: validateContributedServers(contributions) };
6522
6737
  for (const source of getConfigSources(overridePath, cwd)) {
6523
- const loaded = readValidatedConfig(source.readPath, `MCP config from ${source.readPath}`);
6738
+ const loaded = readValidatedConfig(source.readPath, `MCP config from ${source.readPath}`, source.scope === "global");
6524
6739
  if (!loaded)
6525
6740
  continue;
6526
- config = mergeConfigs(config, expandImports(loaded, cwd));
6741
+ config = mergeConfigs(config, expandImports(loaded, cwd, source.scope === "global"));
6527
6742
  }
6528
6743
  return withoutDisabledServers(config);
6529
6744
  }
@@ -6547,7 +6762,7 @@ function validateContributedServers(contributions) {
6547
6762
  const servers = {};
6548
6763
  for (const contribution of contributions) {
6549
6764
  try {
6550
- Object.assign(servers, validateMcpServerTimeouts({ [contribution.name]: contribution.config }));
6765
+ Object.assign(servers, validateServerEntries({ [contribution.name]: contribution.config }, contribution.origin === "extension", describeMcpContributionSource(contribution)));
6551
6766
  } catch (error) {
6552
6767
  const reason = error instanceof Error ? error.message : String(error);
6553
6768
  const message = `Ignoring MCP server "${contribution.name}" from ${describeMcpContributionSource(contribution)}: ${reason}`;
@@ -6555,7 +6770,7 @@ function validateContributedServers(contributions) {
6555
6770
  console.warn(message);
6556
6771
  }
6557
6772
  }
6558
- return servers;
6773
+ return validateServerEntries(servers, true);
6559
6774
  }
6560
6775
  function withoutDisabledServers(config) {
6561
6776
  const enabled = Object.entries(config.mcpServers).filter(([, entry]) => entry.disabled !== true);
@@ -6619,8 +6834,16 @@ function getConfigSources(overridePath, cwd = process.cwd()) {
6619
6834
  return sources;
6620
6835
  }
6621
6836
  function mergeConfigs(base, next) {
6837
+ const servers = { ...base.mcpServers };
6838
+ for (const [name, entry] of Object.entries(next.mcpServers)) {
6839
+ for (const previous of Object.keys(servers)) {
6840
+ if (previous !== name && previous.replace(/-/g, "_") === name.replace(/-/g, "_"))
6841
+ delete servers[previous];
6842
+ }
6843
+ servers[name] = entry;
6844
+ }
6622
6845
  return {
6623
- mcpServers: { ...base.mcpServers, ...next.mcpServers },
6846
+ mcpServers: servers,
6624
6847
  imports: mergeImports(base.imports, next.imports),
6625
6848
  settings: next.settings ? { ...base.settings, ...next.settings } : base.settings
6626
6849
  };
@@ -6631,7 +6854,7 @@ function mergeImports(left, right) {
6631
6854
  return;
6632
6855
  return [...new Set(merged)];
6633
6856
  }
6634
- function expandImports(config, cwd = process.cwd()) {
6857
+ function expandImports(config, cwd = process.cwd(), allowProviderAuth = false) {
6635
6858
  if (!config.imports?.length)
6636
6859
  return config;
6637
6860
  const importedServers = {};
@@ -6641,7 +6864,7 @@ function expandImports(config, cwd = process.cwd()) {
6641
6864
  continue;
6642
6865
  try {
6643
6866
  const imported = JSON.parse(readFileSync2(importPath, "utf-8"));
6644
- const servers = extractServers(imported, importKind);
6867
+ const servers = extractServers(imported, importKind, allowProviderAuth && importKind !== "vscode");
6645
6868
  for (const [name, definition] of Object.entries(servers)) {
6646
6869
  if (!importedServers[name]) {
6647
6870
  importedServers[name] = definition;
@@ -6673,16 +6896,16 @@ function resolveImportPath(importKind, cwd = process.cwd()) {
6673
6896
  function getImportServerCount(importKind, path) {
6674
6897
  try {
6675
6898
  const raw = JSON.parse(readFileSync2(path, "utf-8"));
6676
- return Object.keys(extractServers(raw, importKind)).length;
6899
+ return Object.keys(extractServers(raw, importKind, importKind !== "vscode")).length;
6677
6900
  } catch {
6678
6901
  return 0;
6679
6902
  }
6680
6903
  }
6681
- function readValidatedConfig(path, label) {
6904
+ function readValidatedConfig(path, label, allowProviderAuth) {
6682
6905
  if (!existsSync2(path))
6683
6906
  return null;
6684
6907
  try {
6685
- return validateMcpConfig(JSON.parse(readFileSync2(path, "utf-8")));
6908
+ return validateMcpConfig(JSON.parse(readFileSync2(path, "utf-8")), allowProviderAuth);
6686
6909
  } catch (error) {
6687
6910
  if (error instanceof McpTimeoutConfigError)
6688
6911
  throw error;
@@ -6691,7 +6914,7 @@ function readValidatedConfig(path, label) {
6691
6914
  return null;
6692
6915
  }
6693
6916
  }
6694
- function validateMcpConfig(raw) {
6917
+ function validateMcpConfig(raw, allowProviderAuth = true) {
6695
6918
  if (!raw || typeof raw !== "object") {
6696
6919
  return { mcpServers: {} };
6697
6920
  }
@@ -6701,12 +6924,47 @@ function validateMcpConfig(raw) {
6701
6924
  return { mcpServers: {} };
6702
6925
  }
6703
6926
  return {
6704
- mcpServers: validateMcpServerTimeouts(servers),
6927
+ mcpServers: validateServerEntries(servers, allowProviderAuth),
6705
6928
  imports: Array.isArray(obj.imports) ? obj.imports : undefined,
6706
6929
  settings: obj.settings
6707
6930
  };
6708
6931
  }
6709
- function extractServers(config, kind) {
6932
+ function serverEntryError(entry, allowProviderAuth) {
6933
+ const oauth = entry?.oauth;
6934
+ if (oauth && oauth.clientName !== undefined && (typeof oauth.clientName !== "string" || !oauth.clientName.trim())) {
6935
+ return "oauth.clientName must be a non-empty string";
6936
+ }
6937
+ const auth = entry?.auth;
6938
+ if (typeof auth !== "object" || auth === null)
6939
+ return;
6940
+ return providerAuthShapeError(auth) ?? (allowProviderAuth ? undefined : "auth.provider is only allowed in the global mcp.json and from extensions") ?? providerAuthUrlError(typeof entry?.url === "string" ? interpolateEnvVars(entry.url) : undefined);
6941
+ }
6942
+ function validateServerEntries(servers, allowProviderAuth, origin) {
6943
+ const candidates = {};
6944
+ for (const [name, entry] of Object.entries(servers)) {
6945
+ const reason = serverEntryError(entry, allowProviderAuth);
6946
+ if (reason) {
6947
+ if (!reportOwnedMcpLog("warn")) {
6948
+ console.warn(`Ignoring MCP server "${name}"${origin ? ` from ${origin}` : ""}: ${reason}`);
6949
+ }
6950
+ continue;
6951
+ }
6952
+ candidates[name] = entry;
6953
+ }
6954
+ const validated = validateMcpServerTimeouts(candidates);
6955
+ const accepted = {};
6956
+ for (const [name, entry] of Object.entries(validated)) {
6957
+ const clash = Object.keys(accepted).find((other) => other !== name && other.replace(/-/g, "_") === name.replace(/-/g, "_"));
6958
+ if (clash) {
6959
+ if (!reportOwnedMcpLog("warn"))
6960
+ console.warn(`MCP server "${name}" conflicts with "${clash}"`);
6961
+ continue;
6962
+ }
6963
+ accepted[name] = entry;
6964
+ }
6965
+ return accepted;
6966
+ }
6967
+ function extractServers(config, kind, allowProviderAuth) {
6710
6968
  if (!config || typeof config !== "object")
6711
6969
  return {};
6712
6970
  const obj = config;
@@ -6727,7 +6985,7 @@ function extractServers(config, kind) {
6727
6985
  if (!servers || typeof servers !== "object" || Array.isArray(servers)) {
6728
6986
  return {};
6729
6987
  }
6730
- return validateMcpServerTimeouts(servers);
6988
+ return validateServerEntries(servers, allowProviderAuth);
6731
6989
  }
6732
6990
  function isRepoPromptServer(name, entry) {
6733
6991
  const normalizedName = name.toLowerCase();
@@ -6743,7 +7001,7 @@ function isRepoPromptServer(name, entry) {
6743
7001
  function findProjectRoot(cwd = process.cwd()) {
6744
7002
  let current = resolve2(cwd);
6745
7003
  while (true) {
6746
- if (existsSync2(join2(current, ".git")) || existsSync2(join2(current, "package.json")) || existsSync2(join2(current, PROJECT_CONFIG_NAME)) || existsSync2(join2(current, CONFIG_DIR_NAME2))) {
7004
+ if (existsSync2(join3(current, ".git")) || existsSync2(join3(current, "package.json")) || existsSync2(join3(current, PROJECT_CONFIG_NAME)) || existsSync2(join3(current, CONFIG_DIR_NAME2))) {
6747
7005
  return current;
6748
7006
  }
6749
7007
  const parent = dirname2(current);
@@ -6763,7 +7021,7 @@ function detectRepoPrompt(summary, cwd = process.cwd()) {
6763
7021
  for (const source of summary.sources) {
6764
7022
  if (source.kind !== "shared" || source.serverCount === 0)
6765
7023
  continue;
6766
- const config = readValidatedConfig(source.path, `MCP config from ${source.path}`);
7024
+ const config = readValidatedConfig(source.path, `MCP config from ${source.path}`, source.scope === "global");
6767
7025
  if (!config)
6768
7026
  continue;
6769
7027
  for (const [name, entry] of Object.entries(config.mcpServers)) {
@@ -6777,7 +7035,7 @@ function detectRepoPrompt(summary, cwd = process.cwd()) {
6777
7035
  return { configured: false };
6778
7036
  }
6779
7037
  const projectRoot = findProjectRoot(cwd);
6780
- const targetPath = projectRoot ? join2(projectRoot, PROJECT_CONFIG_NAME) : GENERIC_GLOBAL_CONFIG_PATH;
7038
+ const targetPath = projectRoot ? join3(projectRoot, PROJECT_CONFIG_NAME) : GENERIC_GLOBAL_CONFIG_PATH;
6781
7039
  return {
6782
7040
  configured: false,
6783
7041
  executablePath,
@@ -6853,7 +7111,7 @@ function getServerProvenance(overridePath, cwd = process.cwd(), contributions =
6853
7111
  });
6854
7112
  }
6855
7113
  for (const source of getConfigSources(overridePath, cwd)) {
6856
- const loaded = readValidatedConfig(source.readPath, `MCP config from ${source.readPath}`);
7114
+ const loaded = readValidatedConfig(source.readPath, `MCP config from ${source.readPath}`, source.scope === "global");
6857
7115
  if (!loaded)
6858
7116
  continue;
6859
7117
  if (loaded.imports?.length) {
@@ -6863,7 +7121,7 @@ function getServerProvenance(overridePath, cwd = process.cwd(), contributions =
6863
7121
  continue;
6864
7122
  try {
6865
7123
  const imported = JSON.parse(readFileSync2(importPath, "utf-8"));
6866
- const servers = extractServers(imported, importKind);
7124
+ const servers = extractServers(imported, importKind, source.scope === "global" && importKind !== "vscode");
6867
7125
  for (const name of Object.keys(servers)) {
6868
7126
  if (!provenance.has(name) || provenance.get(name)?.kind === "contributed") {
6869
7127
  provenance.set(name, { path: userPath, kind: "import", importKind });
@@ -6916,18 +7174,20 @@ var init_config = __esm(() => {
6916
7174
  init_agent_dir();
6917
7175
  init_config_write_utils();
6918
7176
  init_tool_call_timeout();
6919
- GENERIC_GLOBAL_CONFIG_PATH = join2(homedir2(), ".config", "mcp", "mcp.json");
7177
+ init_utils();
7178
+ init_provider_auth();
7179
+ GENERIC_GLOBAL_CONFIG_PATH = join3(homedir3(), ".config", "mcp", "mcp.json");
6920
7180
  PROJECT_PI_CONFIG_NAME = `${CONFIG_DIR_NAME2}/mcp.json`;
6921
- REPOPROMPT_BINARY_CANDIDATES = [join2(homedir2(), "RepoPrompt", "repoprompt_cli"), "/Applications/Repo Prompt.app/Contents/MacOS/repoprompt-mcp"];
7181
+ REPOPROMPT_BINARY_CANDIDATES = [join3(homedir3(), "RepoPrompt", "repoprompt_cli"), "/Applications/Repo Prompt.app/Contents/MacOS/repoprompt-mcp"];
6922
7182
  IMPORT_PATHS = {
6923
7183
  "claude-code": [
6924
- join2(homedir2(), ".claude", "mcp.json"),
6925
- join2(homedir2(), ".claude.json"),
6926
- join2(homedir2(), ".claude", "claude_desktop_config.json")
7184
+ join3(homedir3(), ".claude", "mcp.json"),
7185
+ join3(homedir3(), ".claude.json"),
7186
+ join3(homedir3(), ".claude", "claude_desktop_config.json")
6927
7187
  ],
6928
- "claude-desktop": [join2(homedir2(), "Library", "Application Support", "Claude", "claude_desktop_config.json")],
6929
- codex: [join2(homedir2(), ".codex", "config.json")],
6930
- windsurf: [join2(homedir2(), ".windsurf", "mcp.json")],
7188
+ "claude-desktop": [join3(homedir3(), "Library", "Application Support", "Claude", "claude_desktop_config.json")],
7189
+ codex: [join3(homedir3(), ".codex", "config.json")],
7190
+ windsurf: [join3(homedir3(), ".windsurf", "mcp.json")],
6931
7191
  vscode: [".vscode/mcp.json"]
6932
7192
  };
6933
7193
  });
@@ -9134,20 +9394,25 @@ function getServerPrefix(serverName, mode) {
9134
9394
  }
9135
9395
  function formatToolName(toolName, serverName, prefix) {
9136
9396
  const p = getServerPrefix(serverName, prefix);
9137
- return p ? `${p}_${toolName}` : toolName;
9397
+ const name = (p ? `${p}_${toolName}` : toolName).replace(/[^A-Za-z0-9_]/g, "_");
9398
+ return /^\d/.test(name) ? `_${name}` : name;
9138
9399
  }
9139
9400
  function normalizeToolName(value) {
9140
9401
  return value.replace(/-/g, "_");
9141
9402
  }
9403
+ function legacyToolName(toolName, serverName, prefix) {
9404
+ const p = getServerPrefix(serverName, prefix);
9405
+ return p ? `${p}_${toolName}` : toolName;
9406
+ }
9142
9407
  function isToolExcluded(toolName, serverName, prefix, excludeTools) {
9143
9408
  if (!Array.isArray(excludeTools) || excludeTools.length === 0)
9144
9409
  return false;
9145
- const candidates = new Set([
9146
- normalizeToolName(toolName),
9147
- normalizeToolName(formatToolName(toolName, serverName, prefix)),
9148
- normalizeToolName(formatToolName(toolName, serverName, "server")),
9149
- normalizeToolName(formatToolName(toolName, serverName, "short"))
9150
- ]);
9410
+ const modes = new Set([prefix, "server", "short"]);
9411
+ const candidates = new Set([normalizeToolName(toolName)]);
9412
+ for (const mode of modes) {
9413
+ candidates.add(normalizeToolName(formatToolName(toolName, serverName, mode)));
9414
+ candidates.add(normalizeToolName(legacyToolName(toolName, serverName, mode)));
9415
+ }
9151
9416
  for (const excluded of excludeTools) {
9152
9417
  if (typeof excluded !== "string")
9153
9418
  continue;
@@ -9161,6 +9426,27 @@ var init_types2 = __esm(() => {
9161
9426
  init_ui_stream_types();
9162
9427
  });
9163
9428
 
9429
+ // dist/builtin/mcp/tool-names.ts
9430
+ import { createHash } from "node:crypto";
9431
+ function assignToolNames(names, server, prefix) {
9432
+ const unique = [...new Set(names)];
9433
+ const counts = new Map;
9434
+ for (const name of unique) {
9435
+ const plain = formatToolName(name, server, prefix);
9436
+ counts.set(plain, (counts.get(plain) ?? 0) + 1);
9437
+ }
9438
+ return new Map(unique.map((name) => {
9439
+ const plain = formatToolName(name, server, prefix);
9440
+ if (plain.length <= 64 && counts.get(plain) === 1)
9441
+ return [name, plain];
9442
+ const hash = createHash("sha256").update(`${server}\x00${name}`).digest("hex").slice(0, 8);
9443
+ return [name, `${plain.slice(0, 55)}_${hash}`];
9444
+ }));
9445
+ }
9446
+ var init_tool_names = __esm(() => {
9447
+ init_types2();
9448
+ });
9449
+
9164
9450
  // dist/builtin/mcp/resource-tools.ts
9165
9451
  function resourceNameToToolName(name) {
9166
9452
  let result = name.replace(/[^a-zA-Z0-9]/g, "_").replace(/_+/g, "_").replace(/^_+/, "").replace(/_+$/, "").toLowerCase();
@@ -9170,141 +9456,10 @@ function resourceNameToToolName(name) {
9170
9456
  return result;
9171
9457
  }
9172
9458
 
9173
- // dist/builtin/mcp/utils.ts
9174
- var exports_utils = {};
9175
- __export(exports_utils, {
9176
- extractToolUiStreamMode: () => extractToolUiStreamMode,
9177
- formatAuthRequiredMessage: () => formatAuthRequiredMessage,
9178
- getConfigPathFromArgv: () => getConfigPathFromArgv,
9179
- interpolateEnvRecord: () => interpolateEnvRecord,
9180
- interpolateEnvVars: () => interpolateEnvVars,
9181
- openPath: () => openPath,
9182
- openUrl: () => openUrl,
9183
- parallelLimit: () => parallelLimit,
9184
- resolveBearerToken: () => resolveBearerToken,
9185
- resolveConfigPath: () => resolveConfigPath,
9186
- resolveServerUrl: () => resolveServerUrl,
9187
- truncateAtWord: () => truncateAtWord,
9188
- unflattenToolArguments: () => unflattenToolArguments
9189
- });
9190
- import { unflattenArgumentsWithSchema } from "@bastani/atomic";
9191
- import { homedir as homedir3, platform } from "node:os";
9192
- import { join as join3 } from "node:path";
9193
- async function execOpen(pi, target, browser) {
9194
- const os = platform();
9195
- if (os === "darwin") {
9196
- return browser ? pi.exec("open", ["-a", browser, target]) : pi.exec("open", [target]);
9197
- }
9198
- if (os === "win32") {
9199
- return browser ? pi.exec("cmd", ["/c", "start", "", browser, target]) : pi.exec("cmd", ["/c", "start", "", target]);
9200
- }
9201
- return browser ? pi.exec(browser, [target]) : pi.exec("xdg-open", [target]);
9202
- }
9203
- async function openUrl(pi, url, browser) {
9204
- const result = await execOpen(pi, url, browser);
9205
- if (result.code !== 0) {
9206
- throw new Error(result.stderr || `Failed to open browser (exit code ${result.code})`);
9207
- }
9208
- }
9209
- async function openPath(pi, targetPath) {
9210
- const result = await execOpen(pi, targetPath);
9211
- if (result.code !== 0) {
9212
- throw new Error(result.stderr || `Failed to open path (exit code ${result.code})`);
9213
- }
9214
- }
9215
- async function parallelLimit(items, limit, fn) {
9216
- const results = [];
9217
- let index = 0;
9218
- async function worker() {
9219
- while (index < items.length) {
9220
- const i = index++;
9221
- results[i] = await fn(items[i]);
9222
- }
9223
- }
9224
- const workers = Array(Math.min(limit, items.length)).fill(null).map(() => worker());
9225
- await Promise.all(workers);
9226
- return results;
9227
- }
9228
- function getConfigPathFromArgv() {
9229
- const idx = process.argv.indexOf("--mcp-config");
9230
- if (idx >= 0 && idx + 1 < process.argv.length) {
9231
- return process.argv[idx + 1];
9232
- }
9233
- return;
9234
- }
9235
- function interpolateEnvVars(value) {
9236
- return value.replace(/\$\{(\w+)\}/g, (_, name) => process.env[name] ?? "").replace(/\$env:(\w+)/g, (_, name) => process.env[name] ?? "");
9237
- }
9238
- function resolveServerUrl(value) {
9239
- const resolved = interpolateEnvVars(value);
9240
- try {
9241
- const url = new URL(resolved);
9242
- if (url.protocol === "http:" || url.protocol === "https:")
9243
- return resolved;
9244
- } catch {}
9245
- throw new Error("Invalid MCP server url: expected a non-empty HTTP(S) URL after environment variable interpolation.");
9246
- }
9247
- function interpolateEnvRecord(values) {
9248
- if (!values)
9249
- return;
9250
- const resolved = {};
9251
- for (const [key, value] of Object.entries(values)) {
9252
- resolved[key] = interpolateEnvVars(value);
9253
- }
9254
- return resolved;
9255
- }
9256
- function resolveConfigPath(value) {
9257
- if (value === undefined)
9258
- return;
9259
- const resolved = interpolateEnvVars(value);
9260
- if (resolved === "~")
9261
- return homedir3();
9262
- if (resolved.startsWith("~/") || resolved.startsWith("~\\")) {
9263
- return join3(homedir3(), resolved.slice(2));
9264
- }
9265
- return resolved;
9266
- }
9267
- function resolveBearerToken(definition) {
9268
- if (definition.bearerToken !== undefined) {
9269
- return interpolateEnvVars(definition.bearerToken);
9270
- }
9271
- return definition.bearerTokenEnv ? process.env[definition.bearerTokenEnv] : undefined;
9272
- }
9273
- function truncateAtWord(text, target) {
9274
- if (!text || text.length <= target)
9275
- return text;
9276
- const truncated = text.slice(0, target);
9277
- const lastSpace = truncated.lastIndexOf(" ");
9278
- if (lastSpace > target * 0.6) {
9279
- return truncated.slice(0, lastSpace) + "...";
9280
- }
9281
- return truncated + "...";
9282
- }
9283
- function formatAuthRequiredMessage(config, serverName, defaultMessage) {
9284
- const template = config.settings?.authRequiredMessage;
9285
- return template ? template.replaceAll("${server}", serverName) : defaultMessage;
9286
- }
9287
- function extractToolUiStreamMode(toolMeta) {
9288
- const uiMeta = toolMeta?.ui;
9289
- if (!uiMeta || typeof uiMeta !== "object")
9290
- return;
9291
- const streamMode = uiMeta["pi-mcp-adapter.streamMode"];
9292
- if (streamMode === "eager" || streamMode === "stream-first") {
9293
- return streamMode;
9294
- }
9295
- return;
9296
- }
9297
- function unflattenToolArguments(args, inputSchema) {
9298
- if (args === null || args === undefined)
9299
- return {};
9300
- return unflattenArgumentsWithSchema(args, inputSchema);
9301
- }
9302
- var init_utils = () => {};
9303
-
9304
9459
  // dist/builtin/mcp/metadata-cache.ts
9305
9460
  import { existsSync as existsSync3, readFileSync as readFileSync3, writeFileSync as writeFileSync2, renameSync as renameSync2, mkdirSync as mkdirSync2 } from "node:fs";
9306
9461
  import { dirname as dirname3 } from "node:path";
9307
- import { createHash } from "node:crypto";
9462
+ import { createHash as createHash2 } from "node:crypto";
9308
9463
  function getMetadataCachePath() {
9309
9464
  return getAgentPath("mcp-cache.json");
9310
9465
  }
@@ -9359,7 +9514,7 @@ function computeServerHash(definition) {
9359
9514
  excludeTools: definition.excludeTools
9360
9515
  };
9361
9516
  const normalized = stableStringify(identity);
9362
- return createHash("sha256").update(normalized).digest("hex");
9517
+ return createHash2("sha256").update(normalized).digest("hex");
9363
9518
  }
9364
9519
  function isServerCacheValid(entry, definition, maxAgeMs = CACHE_MAX_AGE_MS) {
9365
9520
  if (!entry || entry.configHash !== computeServerHash(definition))
@@ -9372,6 +9527,10 @@ function isServerCacheValid(entry, definition, maxAgeMs = CACHE_MAX_AGE_MS) {
9372
9527
  }
9373
9528
  function reconstructToolMetadata(serverName, entry, prefix, definition) {
9374
9529
  const metadata = [];
9530
+ const names = assignToolNames([
9531
+ ...(entry.tools ?? []).filter((tool) => tool?.name).map((tool) => tool.name),
9532
+ ...definition.exposeResources !== false ? (entry.resources ?? []).filter((resource) => resource?.name && resource?.uri).map((resource) => `get_${resourceNameToToolName(resource.name)}`) : []
9533
+ ], serverName, prefix);
9375
9534
  for (const tool of entry.tools ?? []) {
9376
9535
  if (!tool?.name)
9377
9536
  continue;
@@ -9379,7 +9538,7 @@ function reconstructToolMetadata(serverName, entry, prefix, definition) {
9379
9538
  continue;
9380
9539
  }
9381
9540
  metadata.push({
9382
- name: formatToolName(tool.name, serverName, prefix),
9541
+ name: names.get(tool.name),
9383
9542
  originalName: tool.name,
9384
9543
  description: tool.description ?? "",
9385
9544
  inputSchema: tool.inputSchema,
@@ -9396,7 +9555,7 @@ function reconstructToolMetadata(serverName, entry, prefix, definition) {
9396
9555
  continue;
9397
9556
  }
9398
9557
  metadata.push({
9399
- name: formatToolName(baseName, serverName, prefix),
9558
+ name: names.get(baseName),
9400
9559
  originalName: baseName,
9401
9560
  description: resource.description ?? `Read resource: ${resource.uri}`,
9402
9561
  resourceUri: resource.uri
@@ -9445,6 +9604,7 @@ var init_metadata_cache = __esm(() => {
9445
9604
  init_agent_dir();
9446
9605
  init_app_bridge();
9447
9606
  init_types2();
9607
+ init_tool_names();
9448
9608
  init_utils();
9449
9609
  CACHE_MAX_AGE_MS = 7 * 24 * 60 * 60 * 1000;
9450
9610
  });
@@ -20222,7 +20382,7 @@ class McpOAuthProvider {
20222
20382
  get clientMetadata() {
20223
20383
  if (this.usesClientCredentials) {
20224
20384
  return {
20225
- client_name: "Pi Coding Agent",
20385
+ client_name: this.config.clientName ?? "atomic",
20226
20386
  redirect_uris: [],
20227
20387
  grant_types: ["client_credentials"],
20228
20388
  token_endpoint_auth_method: this.config.clientSecret ? "client_secret_post" : "none"
@@ -20234,7 +20394,7 @@ class McpOAuthProvider {
20234
20394
  }
20235
20395
  return {
20236
20396
  redirect_uris: [redirectUrl],
20237
- client_name: "Pi Coding Agent",
20397
+ client_name: this.config.clientName ?? "atomic",
20238
20398
  client_uri: "https://github.com/nicobailon/pi-mcp-adapter",
20239
20399
  grant_types: ["authorization_code", "refresh_token"],
20240
20400
  response_types: ["code"],
@@ -21314,7 +21474,7 @@ function redactDiagnosticText(message, endpoint, preserveSafeUrls = false) {
21314
21474
  }
21315
21475
  function sanitizeRemoteError(error, endpoint) {
21316
21476
  const message = redactDiagnosticText(error instanceof Error ? error.message : String(error), endpoint);
21317
- const safe = error instanceof UnauthorizedError ? new UnauthorizedError(message) : new Error(message);
21477
+ const safe = error instanceof UnauthorizedError ? new UnauthorizedError(message) : error instanceof McpProviderAuthError ? new McpProviderAuthError(message, error.provider) : new Error(message);
21318
21478
  if (error instanceof Error && "code" in error && typeof error.code === "number") {
21319
21479
  Object.assign(safe, { code: error.code });
21320
21480
  }
@@ -21388,6 +21548,7 @@ function protectRemoteTransport(transport, endpoint) {
21388
21548
  var init_remote_diagnostics = __esm(() => {
21389
21549
  init_auth2();
21390
21550
  init_types();
21551
+ init_provider_auth();
21391
21552
  });
21392
21553
 
21393
21554
  // dist/builtin/mcp/mcp-auth-flow.ts
@@ -21419,7 +21580,8 @@ function extractOAuthConfig(definition) {
21419
21580
  grantType: definition.oauth?.grantType,
21420
21581
  clientId: definition.oauth?.clientId,
21421
21582
  clientSecret: definition.oauth?.clientSecret,
21422
- scope: definition.oauth?.scope
21583
+ scope: definition.oauth?.scope,
21584
+ clientName: definition.oauth?.clientName
21423
21585
  };
21424
21586
  }
21425
21587
  function assertActive(owner) {
@@ -21537,6 +21699,9 @@ async function performAuthentication(owner, serverUrl, definition) {
21537
21699
  }
21538
21700
  const callbackPromise = waitForCallback(started.oauthState);
21539
21701
  try {
21702
+ owner.authorizationUrl = started.authorizationUrl;
21703
+ for (const listener of owner.authorizationListeners)
21704
+ listener(started.authorizationUrl);
21540
21705
  if (!reportOwnedMcpLog("info"))
21541
21706
  console.log(`MCP Auth: Opening browser for ${serverName}`);
21542
21707
  try {
@@ -21564,11 +21729,17 @@ async function performAuthentication(owner, serverUrl, definition) {
21564
21729
  throw failure;
21565
21730
  }
21566
21731
  }
21567
- function authenticate(serverName, serverUrl, definition) {
21732
+ function authenticate(serverName, serverUrl, definition, onAuthorizationUrl) {
21568
21733
  const { pendingAuthentications, oauthCleanupBarrier } = oauthLifecycle();
21569
21734
  const inFlight = pendingAuthentications.get(serverName);
21570
- if (inFlight)
21735
+ if (inFlight) {
21736
+ if (onAuthorizationUrl) {
21737
+ inFlight.authorizationListeners.add(onAuthorizationUrl);
21738
+ if (inFlight.authorizationUrl)
21739
+ onAuthorizationUrl(inFlight.authorizationUrl);
21740
+ }
21571
21741
  return inFlight.result;
21742
+ }
21572
21743
  let resolve;
21573
21744
  let reject;
21574
21745
  const result = new Promise((resolveResult, rejectResult) => {
@@ -21579,7 +21750,7 @@ function authenticate(serverName, serverUrl, definition) {
21579
21750
  const inheritedCleanup = oauthCleanupBarrier.wait();
21580
21751
  let owner;
21581
21752
  const producer = inheritedCleanup.then(() => performAuthentication(owner, serverUrl, definition));
21582
- owner = { serverName, controller, result, producer, resolve, reject };
21753
+ owner = { serverName, controller, result, producer, resolve, reject, authorizationListeners: new Set(onAuthorizationUrl ? [onAuthorizationUrl] : []) };
21583
21754
  pendingAuthentications.set(serverName, owner);
21584
21755
  const removeOwner = () => {
21585
21756
  if (pendingAuthentications.get(serverName) === owner)
@@ -21886,9 +22057,13 @@ class McpServerManager {
21886
22057
  connectPromises = new Map;
21887
22058
  uiStreamListeners = new Map;
21888
22059
  samplingConfig;
22060
+ providerToken;
21889
22061
  setSamplingConfig(config) {
21890
22062
  this.samplingConfig = config;
21891
22063
  }
22064
+ setProviderTokenResolver(resolver) {
22065
+ this.providerToken = resolver;
22066
+ }
21892
22067
  async connect(name, definition) {
21893
22068
  if (this.connectPromises.has(name)) {
21894
22069
  return this.connectPromises.get(name);
@@ -21930,7 +22105,23 @@ class McpServerManager {
21930
22105
  stderr: definition.debug ? "inherit" : "ignore"
21931
22106
  });
21932
22107
  } else if (definition.url !== undefined) {
21933
- transport = await this.createHttpTransport(definition, name);
22108
+ try {
22109
+ transport = await this.createHttpTransport(definition, name);
22110
+ } catch (error) {
22111
+ if (error instanceof McpProviderAuthError && error.provider !== undefined) {
22112
+ return {
22113
+ client,
22114
+ transport: new StreamableHTTPClientTransport(new URL(resolveServerUrl(definition.url))),
22115
+ definition,
22116
+ tools: [],
22117
+ resources: [],
22118
+ lastUsedAt: Date.now(),
22119
+ inFlight: 0,
22120
+ status: "needs-auth"
22121
+ };
22122
+ }
22123
+ throw error;
22124
+ }
21934
22125
  } else {
21935
22126
  throw new Error(`Server ${name} has no command or url`);
21936
22127
  }
@@ -21952,7 +22143,7 @@ class McpServerManager {
21952
22143
  status: "connected"
21953
22144
  };
21954
22145
  } catch (error) {
21955
- if (error instanceof UnauthorizedError && supportsOAuth(definition)) {
22146
+ if (error instanceof UnauthorizedError && supportsOAuth(definition) || error instanceof McpProviderAuthError && error.provider !== undefined) {
21956
22147
  await client.close().catch(() => {});
21957
22148
  await transport.close().catch(() => {});
21958
22149
  return {
@@ -21995,12 +22186,25 @@ class McpServerManager {
21995
22186
  grantType: definition.oauth?.grantType,
21996
22187
  clientId: definition.oauth?.clientId,
21997
22188
  clientSecret: definition.oauth?.clientSecret,
21998
- scope: definition.oauth?.scope
22189
+ scope: definition.oauth?.scope,
22190
+ clientName: definition.oauth?.clientName
21999
22191
  };
22000
22192
  authProvider = new McpOAuthProvider(serverName, resolvedUrl, oauthConfig, {
22001
22193
  onRedirect: async (_authUrl) => {}
22002
22194
  });
22003
22195
  }
22196
+ let providerFetch;
22197
+ if (isProviderAuth(definition.auth)) {
22198
+ const urlError = providerAuthUrlError(resolvedUrl);
22199
+ if (urlError)
22200
+ throw new Error(`MCP server "${serverName}": ${urlError}`);
22201
+ providerFetch = createProviderAuthFetch({
22202
+ serverName,
22203
+ provider: definition.auth.provider,
22204
+ serverUrl: resolvedUrl,
22205
+ token: this.providerToken
22206
+ });
22207
+ }
22004
22208
  const protect = (transport) => {
22005
22209
  if (authProvider) {
22006
22210
  const close = transport.close.bind(transport);
@@ -22014,20 +22218,56 @@ class McpServerManager {
22014
22218
  const streamableTransport = protect(new StreamableHTTPClientTransport(url, {
22015
22219
  requestInit,
22016
22220
  authProvider,
22017
- fetch: authProvider?.fetch
22221
+ fetch: authProvider?.fetch ?? providerFetch
22018
22222
  }));
22019
22223
  try {
22020
22224
  const testClient = new Client({ name: "pi-mcp-probe", version: "2.1.2" });
22021
22225
  await testClient.connect(streamableTransport);
22022
22226
  await testClient.close().catch(() => {});
22023
22227
  await streamableTransport.close().catch(() => {});
22024
- return protect(new StreamableHTTPClientTransport(url, { requestInit, authProvider, fetch: authProvider?.fetch }));
22228
+ return protect(new StreamableHTTPClientTransport(url, { requestInit, authProvider, fetch: authProvider?.fetch ?? providerFetch }));
22025
22229
  } catch (error) {
22026
22230
  await streamableTransport.close().catch(() => {});
22027
- if (error instanceof UnauthorizedError) {
22231
+ if (error instanceof UnauthorizedError || error instanceof McpProviderAuthError) {
22028
22232
  throw error;
22029
22233
  }
22030
- return protect(new SSEClientTransport(url, { requestInit, authProvider, fetch: authProvider?.fetch }));
22234
+ let pendingStart;
22235
+ const sseTransport = new SSEClientTransport(url, {
22236
+ requestInit,
22237
+ authProvider,
22238
+ fetch: authProvider?.fetch ?? providerFetch,
22239
+ eventSourceInit: providerFetch ? {
22240
+ fetch: async (input, init) => {
22241
+ const attempt = pendingStart;
22242
+ if (attempt)
22243
+ attempt.error = undefined;
22244
+ try {
22245
+ return await providerFetch(input, init);
22246
+ } catch (error) {
22247
+ if (attempt && pendingStart === attempt && !init?.signal?.aborted && error instanceof McpProviderAuthError) {
22248
+ attempt.error = error;
22249
+ }
22250
+ throw error;
22251
+ }
22252
+ }
22253
+ } : undefined
22254
+ });
22255
+ if (providerFetch) {
22256
+ const start = sseTransport.start.bind(sseTransport);
22257
+ sseTransport.start = async () => {
22258
+ const attempt = {};
22259
+ pendingStart = attempt;
22260
+ try {
22261
+ await start();
22262
+ } catch (error) {
22263
+ throw attempt.error ?? error;
22264
+ } finally {
22265
+ if (pendingStart === attempt)
22266
+ pendingStart = undefined;
22267
+ }
22268
+ };
22269
+ }
22270
+ return protect(sseTransport);
22031
22271
  }
22032
22272
  }
22033
22273
  async fetchAllTools(client) {
@@ -22155,12 +22395,17 @@ var init_server_manager = __esm(() => {
22155
22395
  init_sampling_handler();
22156
22396
  init_utils();
22157
22397
  init_remote_diagnostics();
22398
+ init_provider_auth();
22158
22399
  });
22159
22400
 
22160
22401
  // dist/builtin/mcp/tool-metadata.ts
22161
22402
  function buildToolMetadata(tools, resources, definition, serverName, prefix) {
22162
22403
  const metadata = [];
22163
22404
  const failedTools = [];
22405
+ const names = assignToolNames([
22406
+ ...tools.filter((tool) => tool?.name).map((tool) => tool.name),
22407
+ ...definition.exposeResources !== false ? resources.filter((resource) => resource?.name && resource?.uri).map((resource) => `get_${resourceNameToToolName(resource.name)}`) : []
22408
+ ], serverName, prefix);
22164
22409
  for (const tool of tools) {
22165
22410
  if (!tool?.name) {
22166
22411
  failedTools.push("(unnamed)");
@@ -22176,7 +22421,7 @@ function buildToolMetadata(tools, resources, definition, serverName, prefix) {
22176
22421
  failedTools.push(tool.name);
22177
22422
  }
22178
22423
  metadata.push({
22179
- name: formatToolName(tool.name, serverName, prefix),
22424
+ name: names.get(tool.name),
22180
22425
  originalName: tool.name,
22181
22426
  description: tool.description ?? "",
22182
22427
  inputSchema: tool.inputSchema,
@@ -22186,12 +22431,14 @@ function buildToolMetadata(tools, resources, definition, serverName, prefix) {
22186
22431
  }
22187
22432
  if (definition.exposeResources !== false) {
22188
22433
  for (const resource of resources) {
22434
+ if (!resource?.name || !resource?.uri)
22435
+ continue;
22189
22436
  const baseName = `get_${resourceNameToToolName(resource.name)}`;
22190
22437
  if (isToolExcluded(baseName, serverName, prefix, definition.excludeTools)) {
22191
22438
  continue;
22192
22439
  }
22193
22440
  metadata.push({
22194
- name: formatToolName(baseName, serverName, prefix),
22441
+ name: names.get(baseName),
22195
22442
  originalName: baseName,
22196
22443
  description: resource.description ?? `Read resource: ${resource.uri}`,
22197
22444
  resourceUri: resource.uri
@@ -22282,6 +22529,7 @@ function formatProperty(name, schema, required, indent) {
22282
22529
  var init_tool_metadata = __esm(() => {
22283
22530
  init_app_bridge();
22284
22531
  init_types2();
22532
+ init_tool_names();
22285
22533
  init_utils();
22286
22534
  });
22287
22535
 
@@ -22420,6 +22668,7 @@ async function initializeMcp(pi, ctx) {
22420
22668
  const contributions = pi.getMcpServerContributions?.() ?? [];
22421
22669
  const config = loadMcpConfig(configPath, ctx.cwd, contributions);
22422
22670
  const manager = new McpServerManager;
22671
+ manager.setProviderTokenResolver((provider) => ctx.modelRegistry.getApiKeyForProvider(provider));
22423
22672
  const lifecycle = new McpLifecycleManager(manager);
22424
22673
  try {
22425
22674
  const samplingAutoApprove = config.settings?.samplingAutoApprove === true;
@@ -22482,7 +22731,12 @@ async function initializeMcp(pi, ctx) {
22482
22731
  try {
22483
22732
  const connection = await manager.connect(name, definition);
22484
22733
  if (connection.status === "needs-auth") {
22485
- return { name, definition, connection: null, error: `OAuth authentication required. Run /mcp-auth ${name}.` };
22734
+ return {
22735
+ name,
22736
+ definition,
22737
+ connection: null,
22738
+ error: providerSignInGuidance(definition, name) ?? `OAuth authentication required. Run /mcp-auth ${name}.`
22739
+ };
22486
22740
  }
22487
22741
  return { name, definition, connection, error: null };
22488
22742
  } catch (error) {
@@ -22655,7 +22909,9 @@ function getFailureAgeSeconds(state, serverName) {
22655
22909
  async function lazyConnect(state, serverName) {
22656
22910
  const connection = state.manager.getConnection(serverName);
22657
22911
  if (connection?.status === "needs-auth") {
22658
- return false;
22912
+ if (!isProviderAuth(state.config.mcpServers[serverName]?.auth))
22913
+ return false;
22914
+ await state.manager.close(serverName);
22659
22915
  }
22660
22916
  if (connection?.status === "connected") {
22661
22917
  updateServerMetadata(state, serverName);
@@ -22712,6 +22968,7 @@ var init_init = __esm(() => {
22712
22968
  init_tool_metadata();
22713
22969
  init_ui_resource_handler();
22714
22970
  init_utils();
22971
+ init_provider_auth();
22715
22972
  init_logger();
22716
22973
  FAILURE_BACKOFF_MS = 60 * 1000;
22717
22974
  });
@@ -23487,7 +23744,7 @@ var init_mcp_panel_types = __esm(() => {
23487
23744
  });
23488
23745
 
23489
23746
  // dist/builtin/mcp/mcp-panel-renderer.ts
23490
- import { truncateToWidth as truncateToWidth2, visibleWidth as visibleWidth2 } from "@earendil-works/pi-tui";
23747
+ import { Text, truncateToWidth as truncateToWidth2, visibleWidth as visibleWidth2 } from "@earendil-works/pi-tui";
23491
23748
  function bold(text) {
23492
23749
  return `\x1B[1m${text}\x1B[22m`;
23493
23750
  }
@@ -23662,7 +23919,8 @@ function renderMcpPanel(context) {
23662
23919
  lines.push(emptyRow());
23663
23920
  }
23664
23921
  if (context.authNotice) {
23665
- lines.push(row(fg2(t.needsAuth, italic(context.authNotice))));
23922
+ for (const line of new Text(fg2(t.needsAuth, context.authNotice), 0, 0).render(Math.max(1, innerW - 1)))
23923
+ lines.push(row(line));
23666
23924
  lines.push(emptyRow());
23667
23925
  }
23668
23926
  }
@@ -23930,7 +24188,10 @@ class McpPanel {
23930
24188
  this.authInFlight = server.name;
23931
24189
  this.authNotice = `Authenticating ${server.name}...`;
23932
24190
  this.tui.requestRender();
23933
- this.callbacks.authenticate(server.name).then((result) => {
24191
+ this.callbacks.authenticate(server.name, (url) => {
24192
+ this.authNotice = authorizationNotice(url);
24193
+ this.tui.requestRender();
24194
+ }).then((result) => {
23934
24195
  server.connectionStatus = this.callbacks.getConnectionStatus(server.name);
23935
24196
  this.authNotice = result.ok ? `OAuth finished for ${server.name}. Run reconnect if it is still idle.` : `OAuth failed for ${server.name}${result.message ? `: ${result.message}` : ". Check the notification for details."}`;
23936
24197
  this.authInFlight = null;
@@ -24033,6 +24294,7 @@ function createMcpPanel(config, cache, provenance, callbacks, tui, done, options
24033
24294
  return new McpPanel(config, cache, provenance, callbacks, tui, done, options ?? {});
24034
24295
  }
24035
24296
  var init_mcp_panel = __esm(() => {
24297
+ init_authorization_notice();
24036
24298
  init_mcp_panel_state();
24037
24299
  init_mcp_panel_renderer();
24038
24300
  init_mcp_panel_types();
@@ -24106,7 +24368,7 @@ async function reconnectServers(state, ctx, targetServer) {
24106
24368
  const connection = await state.manager.connect(name, definition);
24107
24369
  if (connection.status === "needs-auth") {
24108
24370
  if (ctx.hasUI) {
24109
- ctx.ui.notify(`MCP: ${name} requires OAuth. Run /mcp-auth ${name} first.`, "warning");
24371
+ ctx.ui.notify(providerSignInGuidance(definition, name) ?? `MCP: ${name} requires OAuth. Run /mcp-auth ${name} first.`, "warning");
24110
24372
  }
24111
24373
  continue;
24112
24374
  }
@@ -24131,7 +24393,7 @@ async function reconnectServers(state, ctx, targetServer) {
24131
24393
  }
24132
24394
  updateStatusBar(state);
24133
24395
  }
24134
- async function authenticateServer(serverName, config, ctx) {
24396
+ async function authenticateServer(serverName, config, ctx, onAuthorizationUrl) {
24135
24397
  if (!ctx.hasUI)
24136
24398
  return { ok: false, message: "OAuth authentication requires an interactive session." };
24137
24399
  const definition = config.mcpServers[serverName];
@@ -24153,7 +24415,12 @@ async function authenticateServer(serverName, config, ctx) {
24153
24415
  }
24154
24416
  try {
24155
24417
  ctx.ui.setStatus("mcp-auth", `Authenticating ${serverName}...`);
24156
- const status = await authenticate(serverName, definition.url, definition);
24418
+ const status = await authenticate(serverName, definition.url, definition, (url) => {
24419
+ if (onAuthorizationUrl)
24420
+ onAuthorizationUrl(url);
24421
+ else
24422
+ ctx.ui.notify(authorizationNotice(url), "info");
24423
+ });
24157
24424
  if (status === "authenticated") {
24158
24425
  const message = `OAuth authentication successful for "${serverName}"! Run /mcp reconnect ${serverName} to connect with the new token.`;
24159
24426
  ctx.ui.notify(`OAuth authentication successful for "${serverName}"!
@@ -24262,7 +24529,7 @@ function buildMcpPanelCallbacks(state, config, ctx) {
24262
24529
  const definition = config.mcpServers[serverName];
24263
24530
  return definition ? supportsOAuth(definition) : false;
24264
24531
  },
24265
- authenticate: (serverName) => authenticateServer(serverName, config, ctx),
24532
+ authenticate: (serverName, onAuthorizationUrl) => authenticateServer(serverName, config, ctx, onAuthorizationUrl),
24266
24533
  getConnectionStatus: (serverName) => {
24267
24534
  const definition = config.mcpServers[serverName];
24268
24535
  const connection = state.manager.getConnection(serverName);
@@ -24342,6 +24609,8 @@ async function openMcpAuthPanel(state, pi, ctx, configOverridePath) {
24342
24609
  return { configChanged: false };
24343
24610
  }
24344
24611
  var init_commands = __esm(() => {
24612
+ init_authorization_notice();
24613
+ init_provider_auth();
24345
24614
  init_config();
24346
24615
  init_init();
24347
24616
  init_metadata_cache();
@@ -25688,7 +25957,7 @@ function asCallToolResult(result) {
25688
25957
  }
25689
25958
 
25690
25959
  // dist/builtin/mcp/direct-tool-executor.ts
25691
- function getDirectAuthRequiredMessage(state, serverName, defaultMessage = `MCP server "${serverName}" requires OAuth authentication. Run /mcp-auth ${serverName} first.`) {
25960
+ function getDirectAuthRequiredMessage(state, serverName, defaultMessage = providerSignInGuidance(state.config.mcpServers[serverName], serverName) ?? `MCP server "${serverName}" requires OAuth authentication. Run /mcp-auth ${serverName} first.`) {
25692
25961
  return formatAuthRequiredMessage(state.config, serverName, defaultMessage);
25693
25962
  }
25694
25963
  function getDirectAuthFailedMessage(state, serverName, message) {
@@ -25927,6 +26196,7 @@ ${formatSchema(spec.inputSchema)}`;
25927
26196
  };
25928
26197
  }
25929
26198
  var init_direct_tool_executor = __esm(() => {
26199
+ init_provider_auth();
25930
26200
  init_init();
25931
26201
  init_tool_metadata();
25932
26202
  init_ui_session();
@@ -25981,6 +26251,10 @@ function resolveDirectTools(config, cache, prefix, envOverride) {
25981
26251
  const serverCache = cache.servers[serverName];
25982
26252
  if (!serverCache || !isServerCacheValid(serverCache, definition))
25983
26253
  continue;
26254
+ const names = assignToolNames([
26255
+ ...(serverCache.tools ?? []).filter((tool) => tool?.name).map((tool) => tool.name),
26256
+ ...definition.exposeResources !== false ? (serverCache.resources ?? []).filter((resource) => resource?.name && resource?.uri).map((resource) => `get_${resourceNameToToolName(resource.name)}`) : []
26257
+ ], serverName, prefix);
25984
26258
  let toolFilter = false;
25985
26259
  if (envOverride) {
25986
26260
  if (envSelection.servers.has(serverName)) {
@@ -25998,11 +26272,13 @@ function resolveDirectTools(config, cache, prefix, envOverride) {
25998
26272
  if (!toolFilter)
25999
26273
  continue;
26000
26274
  for (const tool of serverCache.tools ?? []) {
26275
+ if (!tool?.name)
26276
+ continue;
26001
26277
  if (toolFilter !== true && !toolFilter.includes(tool.name))
26002
26278
  continue;
26003
26279
  if (isToolExcluded(tool.name, serverName, prefix, definition.excludeTools))
26004
26280
  continue;
26005
- const prefixedName = formatToolName(tool.name, serverName, prefix);
26281
+ const prefixedName = names.get(tool.name);
26006
26282
  if (BUILTIN_NAMES.has(prefixedName)) {
26007
26283
  if (!reportOwnedMcpLog("warn"))
26008
26284
  console.warn(`MCP: skipping direct tool "${prefixedName}" (collides with builtin)`);
@@ -26026,12 +26302,14 @@ function resolveDirectTools(config, cache, prefix, envOverride) {
26026
26302
  }
26027
26303
  if (definition.exposeResources !== false) {
26028
26304
  for (const resource of serverCache.resources ?? []) {
26305
+ if (!resource?.name || !resource?.uri)
26306
+ continue;
26029
26307
  const baseName = `get_${resourceNameToToolName(resource.name)}`;
26030
26308
  if (toolFilter !== true && !toolFilter.includes(baseName))
26031
26309
  continue;
26032
26310
  if (isToolExcluded(baseName, serverName, prefix, definition.excludeTools))
26033
26311
  continue;
26034
- const prefixedName = formatToolName(baseName, serverName, prefix);
26312
+ const prefixedName = names.get(baseName);
26035
26313
  if (BUILTIN_NAMES.has(prefixedName)) {
26036
26314
  if (!reportOwnedMcpLog("warn"))
26037
26315
  console.warn(`MCP: skipping direct resource tool "${prefixedName}" (collides with builtin)`);
@@ -26076,6 +26354,7 @@ var init_direct_tools = __esm(() => {
26076
26354
  init_diagnostics();
26077
26355
  init_metadata_cache();
26078
26356
  init_types2();
26357
+ init_tool_names();
26079
26358
  init_direct_tool_executor();
26080
26359
  BUILTIN_NAMES = new Set(["read", "bash", "edit", "write", "grep", "find", "search", "ls", "mcp"]);
26081
26360
  });
@@ -26580,7 +26859,7 @@ var init_proxy_info_modes = __esm(() => {
26580
26859
  });
26581
26860
 
26582
26861
  // dist/builtin/mcp/proxy-auth.ts
26583
- function getAuthRequiredMessage(state, serverName, defaultMessage = `Server "${serverName}" requires OAuth authentication. Run /mcp-auth ${serverName} first.`) {
26862
+ function getAuthRequiredMessage(state, serverName, defaultMessage = providerSignInGuidance(state.config.mcpServers[serverName], serverName) ?? `Server "${serverName}" requires OAuth authentication. Run /mcp-auth ${serverName} first.`) {
26584
26863
  return formatAuthRequiredMessage(state.config, serverName, defaultMessage);
26585
26864
  }
26586
26865
  function getAuthFailedMessage(state, serverName, message) {
@@ -26619,6 +26898,7 @@ async function attemptAutoAuth(state, serverName) {
26619
26898
  var init_proxy_auth = __esm(() => {
26620
26899
  init_mcp_auth_flow();
26621
26900
  init_utils();
26901
+ init_provider_auth();
26622
26902
  });
26623
26903
 
26624
26904
  // dist/builtin/mcp/proxy-connect.ts
@@ -26719,7 +26999,7 @@ async function executeCall(state, toolName, args, serverOverride, getPiTools, si
26719
26999
  }
26720
27000
  }
26721
27001
  }
26722
- if (serverName && !toolMeta) {
27002
+ if (serverName && (!toolMeta || state.manager.getConnection(serverName)?.status === "needs-auth" && isProviderAuth(state.config.mcpServers[serverName]?.auth))) {
26723
27003
  const connected = await waitForCaller(() => lazyConnect(state, serverName), signal);
26724
27004
  signal?.throwIfAborted();
26725
27005
  assertMcpStateLease(assertActive);
@@ -27086,6 +27366,7 @@ var init_proxy_call = __esm(() => {
27086
27366
  init_apps_cancellation();
27087
27367
  init_state_lease();
27088
27368
  init_tool_call_timeout();
27369
+ init_provider_auth();
27089
27370
  });
27090
27371
 
27091
27372
  // dist/builtin/mcp/proxy-modes.ts
@@ -27197,7 +27478,7 @@ import { Type } from "typebox";
27197
27478
 
27198
27479
  // dist/builtin/mcp/tool-result-renderer.ts
27199
27480
  import { keyText } from "@bastani/atomic";
27200
- import { Text } from "@earendil-works/pi-tui";
27481
+ import { Text as Text2 } from "@earendil-works/pi-tui";
27201
27482
  function blockToLines(block) {
27202
27483
  if (block.type === "text") {
27203
27484
  return block.text.split(`
@@ -27218,7 +27499,7 @@ function formatMcpToolResultLines(result, expanded, maxCollapsedLines = 5) {
27218
27499
  }
27219
27500
  function renderMcpToolResult(result, options, theme, context) {
27220
27501
  if (options.isPartial) {
27221
- return new Text(theme.fg("warning", "Running MCP tool..."), 0, 0);
27502
+ return new Text2(theme.fg("warning", "Running MCP tool..."), 0, 0);
27222
27503
  }
27223
27504
  const hasErrorDetails = Boolean(result.details.error);
27224
27505
  const display = formatMcpToolResultLines(result, options.expanded || context?.isError === true || hasErrorDetails);
@@ -27227,14 +27508,14 @@ function renderMcpToolResult(result, options, theme, context) {
27227
27508
  const expandKey = keyText("app.tools.expand");
27228
27509
  const hint = display.truncated && !options.expanded && expandKey ? `
27229
27510
  ${theme.fg("muted", `(${expandKey} Expand)`)}` : "";
27230
- return new Text(`${output}${hint}`, 0, 0);
27511
+ return new Text2(`${output}${hint}`, 0, 0);
27231
27512
  }
27232
27513
 
27233
27514
  // dist/builtin/mcp/tool-call-renderer.ts
27234
27515
  init_tool_metadata();
27235
27516
  init_types2();
27236
27517
  import { formatToolCallWithArgs } from "@bastani/atomic";
27237
- import { stripTerminalSequences, Text as Text2 } from "@earendil-works/pi-tui";
27518
+ import { stripTerminalSequences, Text as Text3 } from "@earendil-works/pi-tui";
27238
27519
  function textArg(value) {
27239
27520
  return typeof value === "string" && value.length > 0 ? value : undefined;
27240
27521
  }
@@ -27259,8 +27540,8 @@ function redactArguments(value) {
27259
27540
  function header(theme, server, operation, args, expanded = false) {
27260
27541
  const clean = (value) => stripTerminalSequences(value).replace(/[\r\n\t]/g, " ");
27261
27542
  if (server && args && Object.keys(args).length > 0)
27262
- return new Text2(formatToolCallWithArgs(`MCP${server ? ` ${clean(server)}` : ""} · ${clean(operation)}`, redactArguments(args), { fg: (name, text) => theme.fg(name, text), bold: (text) => theme.bold?.(text) ?? text }, expanded), 0, 0);
27263
- return new Text2(theme.fg("toolTitle", `MCP${server ? ` ${clean(server)}` : ""}`) + theme.fg("muted", ` · ${clean(operation)}`), 0, 0);
27543
+ return new Text3(formatToolCallWithArgs(`MCP${server ? ` ${clean(server)}` : ""} · ${clean(operation)}`, redactArguments(args), { fg: (name, text) => theme.fg(name, text), bold: (text) => theme.bold?.(text) ?? text }, expanded), 0, 0);
27544
+ return new Text3(theme.fg("toolTitle", `MCP${server ? ` ${clean(server)}` : ""}`) + theme.fg("muted", ` · ${clean(operation)}`), 0, 0);
27264
27545
  }
27265
27546
  function renderMcpDirectToolCall(server, tool, theme, args, expanded = false) {
27266
27547
  return header(theme, server, tool, args, expanded);