@herbertgao/pi-extensions 2026.8.1 → 2026.8.2

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 (162) hide show
  1. package/README.md +5 -2
  2. package/node_modules/@czottmann/pi-automode/README.md +16 -2
  3. package/node_modules/@czottmann/pi-automode/docs/GLOSSARY.md +35 -0
  4. package/node_modules/@czottmann/pi-automode/docs/automode-classifier-flow.md +18 -9
  5. package/node_modules/@czottmann/pi-automode/docs/defaults.md +6 -2
  6. package/node_modules/@czottmann/pi-automode/examples/automode.local.json +6 -0
  7. package/node_modules/@czottmann/pi-automode/extensions/auto-mode/classifier.ts +11 -7
  8. package/node_modules/@czottmann/pi-automode/extensions/auto-mode/config.ts +100 -0
  9. package/node_modules/@czottmann/pi-automode/extensions/auto-mode/constants.ts +32 -0
  10. package/node_modules/@czottmann/pi-automode/extensions/auto-mode/extension.ts +78 -3
  11. package/node_modules/@czottmann/pi-automode/extensions/auto-mode/hard-deny.ts +16 -4
  12. package/node_modules/@czottmann/pi-automode/extensions/auto-mode/paths.ts +23 -1
  13. package/node_modules/@czottmann/pi-automode/extensions/auto-mode/permissions.ts +22 -1
  14. package/node_modules/@czottmann/pi-automode/extensions/auto-mode/types.ts +18 -2
  15. package/node_modules/@czottmann/pi-automode/package.json +4 -4
  16. package/node_modules/@dietrichgebert/ponytail/.opencode/command/ponytail-audit.md +5 -0
  17. package/node_modules/@dietrichgebert/ponytail/.opencode/command/ponytail-debt.md +5 -0
  18. package/node_modules/@dietrichgebert/ponytail/.opencode/command/ponytail-gain.md +5 -0
  19. package/node_modules/@dietrichgebert/ponytail/.opencode/command/ponytail-help.md +5 -0
  20. package/node_modules/@dietrichgebert/ponytail/.opencode/command/ponytail-review.md +5 -0
  21. package/node_modules/@dietrichgebert/ponytail/.opencode/command/ponytail.md +5 -0
  22. package/node_modules/@dietrichgebert/ponytail/.opencode/plugins/ponytail-frontmatter.cjs +23 -0
  23. package/node_modules/@dietrichgebert/ponytail/.opencode/plugins/ponytail.mjs +99 -0
  24. package/node_modules/@dietrichgebert/ponytail/.qoder/rules/ponytail.md +30 -0
  25. package/node_modules/@dietrichgebert/ponytail/.qoder-plugin/plugin.json +16 -0
  26. package/node_modules/@dietrichgebert/ponytail/AGENTS.md +32 -0
  27. package/node_modules/@dietrichgebert/ponytail/LICENSE +21 -0
  28. package/node_modules/@dietrichgebert/ponytail/README.es.md +298 -0
  29. package/node_modules/@dietrichgebert/ponytail/README.ko.md +315 -0
  30. package/node_modules/@dietrichgebert/ponytail/README.md +351 -0
  31. package/node_modules/@dietrichgebert/ponytail/assets/benchmark-3model.svg +21 -0
  32. package/node_modules/@dietrichgebert/ponytail/assets/benchmark-agentic.svg +62 -0
  33. package/node_modules/@dietrichgebert/ponytail/assets/logo-dark.png +0 -0
  34. package/node_modules/@dietrichgebert/ponytail/assets/logo-dark.svg +115 -0
  35. package/node_modules/@dietrichgebert/ponytail/assets/logo-greenpt-dark.svg +27 -0
  36. package/node_modules/@dietrichgebert/ponytail/assets/logo-greenpt.svg +27 -0
  37. package/node_modules/@dietrichgebert/ponytail/assets/logo.png +0 -0
  38. package/node_modules/@dietrichgebert/ponytail/assets/social-preview.png +0 -0
  39. package/node_modules/@dietrichgebert/ponytail/assets/waitlist-banner-es.png +0 -0
  40. package/node_modules/@dietrichgebert/ponytail/assets/waitlist-banner-ko.png +0 -0
  41. package/node_modules/@dietrichgebert/ponytail/assets/waitlist-banner.png +0 -0
  42. package/node_modules/@dietrichgebert/ponytail/hooks/claude-codex-hooks.json +44 -0
  43. package/node_modules/@dietrichgebert/ponytail/hooks/copilot-hooks.json +21 -0
  44. package/node_modules/@dietrichgebert/ponytail/hooks/ponytail-activate.js +96 -0
  45. package/node_modules/@dietrichgebert/ponytail/hooks/ponytail-config.js +169 -0
  46. package/node_modules/@dietrichgebert/ponytail/hooks/ponytail-instructions.js +98 -0
  47. package/node_modules/@dietrichgebert/ponytail/hooks/ponytail-mode-tracker.js +130 -0
  48. package/node_modules/@dietrichgebert/ponytail/hooks/ponytail-runtime.js +85 -0
  49. package/node_modules/@dietrichgebert/ponytail/hooks/ponytail-statusline.ps1 +24 -0
  50. package/node_modules/@dietrichgebert/ponytail/hooks/ponytail-statusline.sh +18 -0
  51. package/node_modules/@dietrichgebert/ponytail/hooks/ponytail-subagent.js +77 -0
  52. package/node_modules/@dietrichgebert/ponytail/hooks/qoder-hooks.json +26 -0
  53. package/node_modules/@dietrichgebert/ponytail/package.json +47 -0
  54. package/node_modules/@dietrichgebert/ponytail/pi-extension/index.js +211 -0
  55. package/node_modules/@dietrichgebert/ponytail/pi-extension/package.json +8 -0
  56. package/node_modules/@dietrichgebert/ponytail/scripts/uninstall.js +60 -0
  57. package/node_modules/@dietrichgebert/ponytail/skills/ponytail/SKILL.md +120 -0
  58. package/node_modules/@dietrichgebert/ponytail/skills/ponytail-audit/SKILL.md +41 -0
  59. package/node_modules/@dietrichgebert/ponytail/skills/ponytail-debt/SKILL.md +44 -0
  60. package/node_modules/@dietrichgebert/ponytail/skills/ponytail-gain/SKILL.md +50 -0
  61. package/node_modules/@dietrichgebert/ponytail/skills/ponytail-help/SKILL.md +71 -0
  62. package/node_modules/@dietrichgebert/ponytail/skills/ponytail-review/SKILL.md +57 -0
  63. package/node_modules/@herbertgao/pi-cc-extensions/README.en.md +1 -3
  64. package/node_modules/@herbertgao/pi-cc-extensions/README.md +1 -3
  65. package/node_modules/@herbertgao/pi-cc-extensions/extensions/config/panel.ts +10 -6
  66. package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/agent-summary.ts +6 -2
  67. package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/compact-thinking.ts +24 -83
  68. package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/context.ts +1 -1
  69. package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/markdown-enhance.ts +119 -0
  70. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/compact-mode.ts +73 -9
  71. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/index.ts +2 -2
  72. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse-hover.ts +130 -0
  73. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse-interaction.ts +65 -542
  74. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse-layout.ts +160 -0
  75. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse-packets.ts +143 -0
  76. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse-scroll.ts +184 -0
  77. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool-diff/diff-renderer.ts +2 -2
  78. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool-grouping.ts +9 -2
  79. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/transcript-refresh.ts +91 -0
  80. package/node_modules/@herbertgao/pi-cc-extensions/extensions/utils/agent-summary.ts +1 -1
  81. package/node_modules/@herbertgao/pi-cc-extensions/package.json +3 -3
  82. package/node_modules/@herbertgao/pi-cc-extensions/themes/cc-dark.json +1 -0
  83. package/node_modules/@herbertgao/pi-cc-extensions/themes/cc-light.json +2 -2
  84. package/node_modules/@herbertgao/pi-stash/README.md +9 -9
  85. package/node_modules/@herbertgao/pi-stash/package.json +1 -1
  86. package/node_modules/@herbertgao/pi-stash/src/index.ts +2 -1
  87. package/node_modules/@herbertgao/pi-subagents/CHANGELOG.md +14 -0
  88. package/node_modules/@herbertgao/pi-subagents/README.md +6 -2
  89. package/node_modules/@herbertgao/pi-subagents/package.json +3 -3
  90. package/node_modules/@herbertgao/pi-subagents/src/custom-agents.ts +85 -11
  91. package/node_modules/@herbertgao/pi-subagents/src/index.ts +28 -4
  92. package/node_modules/@herbertgao/pi-subagents/src/settings.ts +12 -0
  93. package/node_modules/@herbertgao/pi-subagents/src/types.ts +2 -0
  94. package/node_modules/@narumitw/pi-btw/LICENSE +21 -0
  95. package/node_modules/@narumitw/pi-btw/README.md +210 -0
  96. package/node_modules/@narumitw/pi-btw/package.json +53 -0
  97. package/node_modules/@narumitw/pi-btw/src/bring-to-main.ts +632 -0
  98. package/node_modules/@narumitw/pi-btw/src/btw.ts +996 -0
  99. package/node_modules/@narumitw/pi-btw/src/fullscreen-ui.ts +339 -0
  100. package/node_modules/@narumitw/pi-btw/src/index.ts +1 -0
  101. package/node_modules/@narumitw/pi-btw/src/menu.ts +296 -0
  102. package/node_modules/@narumitw/pi-btw/src/settings.ts +237 -0
  103. package/node_modules/@narumitw/pi-btw/src/side-thread.ts +238 -0
  104. package/node_modules/@narumitw/pi-btw/src/text.ts +10 -0
  105. package/node_modules/@narumitw/pi-btw/src/transcript-pager.ts +620 -0
  106. package/node_modules/@pi-plugins/fast-mode/LICENSE +21 -0
  107. package/node_modules/@pi-plugins/fast-mode/README.md +92 -0
  108. package/node_modules/@pi-plugins/fast-mode/dist/index.d.mts +6 -0
  109. package/node_modules/@pi-plugins/fast-mode/dist/index.d.mts.map +1 -0
  110. package/node_modules/@pi-plugins/fast-mode/dist/index.mjs +2026 -0
  111. package/node_modules/@pi-plugins/fast-mode/dist/index.mjs.map +1 -0
  112. package/node_modules/@pi-plugins/fast-mode/package.json +73 -0
  113. package/node_modules/pi-mcp-adapter/CHANGELOG.md +59 -0
  114. package/node_modules/pi-mcp-adapter/README.md +62 -13
  115. package/node_modules/pi-mcp-adapter/agent-plugin-loader.ts +377 -0
  116. package/node_modules/pi-mcp-adapter/commands.ts +9 -7
  117. package/node_modules/pi-mcp-adapter/config.ts +110 -49
  118. package/node_modules/pi-mcp-adapter/direct-tools.ts +34 -19
  119. package/node_modules/pi-mcp-adapter/elicitation-handler.ts +37 -36
  120. package/node_modules/pi-mcp-adapter/errors.ts +21 -17
  121. package/node_modules/pi-mcp-adapter/host-html-template.ts +13 -7
  122. package/node_modules/pi-mcp-adapter/index.ts +37 -14
  123. package/node_modules/pi-mcp-adapter/init.ts +9 -7
  124. package/node_modules/pi-mcp-adapter/json-schema-validator.ts +2 -2
  125. package/node_modules/pi-mcp-adapter/lifecycle.ts +8 -8
  126. package/node_modules/pi-mcp-adapter/logger.ts +1 -1
  127. package/node_modules/pi-mcp-adapter/mcp-auth-flow.ts +36 -22
  128. package/node_modules/pi-mcp-adapter/mcp-auth.ts +120 -2
  129. package/node_modules/pi-mcp-adapter/mcp-code.ts +13 -11
  130. package/node_modules/pi-mcp-adapter/mcp-oauth-provider.ts +19 -13
  131. package/node_modules/pi-mcp-adapter/mcp-output-guard.ts +15 -11
  132. package/node_modules/pi-mcp-adapter/mcp-panel.ts +34 -15
  133. package/node_modules/pi-mcp-adapter/mcp-probe.ts +131 -34
  134. package/node_modules/pi-mcp-adapter/mcp-script-worker.mjs +2 -2
  135. package/node_modules/pi-mcp-adapter/mcp-setup-panel.ts +7 -4
  136. package/node_modules/pi-mcp-adapter/mcp-trace.ts +59 -64
  137. package/node_modules/pi-mcp-adapter/metadata-cache.ts +34 -26
  138. package/node_modules/pi-mcp-adapter/npx-resolver.ts +6 -4
  139. package/node_modules/pi-mcp-adapter/oauth-handler.ts +1 -1
  140. package/node_modules/pi-mcp-adapter/onboarding-state.ts +19 -11
  141. package/node_modules/pi-mcp-adapter/package.json +13 -8
  142. package/node_modules/pi-mcp-adapter/panel-keys.ts +20 -4
  143. package/node_modules/pi-mcp-adapter/prompts.ts +2 -2
  144. package/node_modules/pi-mcp-adapter/proxy-modes.ts +48 -11
  145. package/node_modules/pi-mcp-adapter/sampling-handler.ts +22 -15
  146. package/node_modules/pi-mcp-adapter/search-ranking.ts +2 -1
  147. package/node_modules/pi-mcp-adapter/server-manager.ts +165 -113
  148. package/node_modules/pi-mcp-adapter/session-recovery.ts +11 -12
  149. package/node_modules/pi-mcp-adapter/skills/mcp-scripting/SKILL.md +3 -3
  150. package/node_modules/pi-mcp-adapter/state.ts +3 -1
  151. package/node_modules/pi-mcp-adapter/tool-approval.ts +67 -4
  152. package/node_modules/pi-mcp-adapter/tool-metadata.ts +6 -5
  153. package/node_modules/pi-mcp-adapter/tool-result-renderer.ts +14 -2
  154. package/node_modules/pi-mcp-adapter/ts-shape.ts +9 -2
  155. package/node_modules/pi-mcp-adapter/types.ts +109 -22
  156. package/node_modules/pi-mcp-adapter/ui-app-bridge-helpers.ts +40 -0
  157. package/node_modules/pi-mcp-adapter/ui-resource-handler.ts +28 -19
  158. package/node_modules/pi-mcp-adapter/ui-server.ts +90 -58
  159. package/node_modules/pi-mcp-adapter/ui-session.ts +16 -14
  160. package/node_modules/pi-mcp-adapter/unix-socket-transport.ts +4 -4
  161. package/node_modules/pi-mcp-adapter/utils.ts +23 -9
  162. package/package.json +22 -7
@@ -5,6 +5,7 @@ import { dirname, join, resolve } from "node:path";
5
5
  import { parse as parseToml } from "smol-toml";
6
6
  import stripJsonComments from "strip-json-comments";
7
7
  import { getAgentPath } from "./agent-dir.ts";
8
+ import { getAgentPluginSummaries, loadAgentPluginConfigs, type AgentPluginSummary } from "./agent-plugin-loader.ts";
8
9
  import { isServerDisabled, type HostConfigDiscovery, type McpConfig, type ServerEntry, type McpSettings, type ImportKind, type ServerProvenance } from "./types.ts";
9
10
  import { toStringRecord } from "./utils.ts";
10
11
 
@@ -32,31 +33,31 @@ export const KNOWN_SERVER_PRESETS: readonly KnownServerPreset[] = [
32
33
  id: "deepwiki",
33
34
  name: "DeepWiki",
34
35
  summary: "Ask questions about public GitHub repositories.",
35
- entry: { url: "https://mcp.deepwiki.com/mcp" },
36
+ entry: { url: "https://mcp.deepwiki.com/mcp", protocolVersion: "auto" },
36
37
  },
37
38
  {
38
39
  id: "context7",
39
40
  name: "Context7",
40
41
  summary: "Look up current library documentation and examples.",
41
- entry: { url: "https://mcp.context7.com/mcp" },
42
+ entry: { url: "https://mcp.context7.com/mcp", protocolVersion: "auto" },
42
43
  },
43
44
  {
44
45
  id: "notion",
45
46
  name: "Notion",
46
47
  summary: "Search and work with your Notion workspace.",
47
- entry: { url: "https://mcp.notion.com/mcp", auth: "oauth" },
48
+ entry: { url: "https://mcp.notion.com/mcp", auth: "oauth", protocolVersion: "auto" },
48
49
  },
49
50
  {
50
51
  id: "github",
51
52
  name: "GitHub",
52
53
  summary: "Work with GitHub through your Copilot account.",
53
- entry: { url: "https://api.githubcopilot.com/mcp", auth: "oauth" },
54
+ entry: { url: "https://api.githubcopilot.com/mcp", auth: "oauth", protocolVersion: "auto" },
54
55
  },
55
56
  {
56
57
  id: "chrome-devtools",
57
58
  name: "Chrome DevTools",
58
59
  summary: "Inspect and automate a local Chrome browser.",
59
- entry: { command: "npx", args: ["-y", "chrome-devtools-mcp@latest"] },
60
+ entry: { command: "npx", args: ["-y", "chrome-devtools-mcp@1.6.0"] },
60
61
  },
61
62
  ];
62
63
 
@@ -137,6 +138,7 @@ export interface McpDiscoverySummary {
137
138
  imports: ImportConfigSummary[];
138
139
  hostConfigs: HostConfigSummary[];
139
140
  hostConfigDiscovery: HostConfigDiscovery;
141
+ agentPlugins: AgentPluginSummary[];
140
142
  conflicts: McpConfigConflict[];
141
143
  hasAnyConfig: boolean;
142
144
  hasAnyDetectedPaths: boolean;
@@ -147,6 +149,12 @@ export interface McpDiscoverySummary {
147
149
  repoPrompt: RepoPromptDiscovery;
148
150
  }
149
151
 
152
+ export interface McpStandardConfigSummary {
153
+ sources: ConfigDiscoverySource[];
154
+ hasSharedServers: boolean;
155
+ fingerprint: string;
156
+ }
157
+
150
158
  export interface ConfigWritePreview {
151
159
  path: string;
152
160
  existed: boolean;
@@ -193,9 +201,8 @@ export function findAvailableImportConfigs(cwd = process.cwd()): DiscoveredImpor
193
201
  return discovered;
194
202
  }
195
203
 
196
- export function getMcpDiscoverySummary(overridePath?: string, cwd = process.cwd()): McpDiscoverySummary {
197
- const sourceSpecs = getConfigSources(overridePath, cwd);
198
- const sources = sourceSpecs.map((source) => {
204
+ function getConfigSourceSummaries(sourceSpecs: ConfigSourceSpec[]): ConfigDiscoverySource[] {
205
+ return sourceSpecs.map((source) => {
199
206
  const loaded = readValidatedConfig(source.readPath, `MCP config from ${source.readPath}`);
200
207
  return {
201
208
  id: source.id,
@@ -207,24 +214,47 @@ export function getMcpDiscoverySummary(overridePath?: string, cwd = process.cwd(
207
214
  serverCount: loaded ? Object.keys(loaded.mcpServers).length : 0,
208
215
  } satisfies ConfigDiscoverySource;
209
216
  });
217
+ }
210
218
 
211
- const imports = (Object.keys(IMPORT_PATHS) as ImportKind[])
212
- .map((kind) => {
213
- const imported = loadImportedConfig(kind, cwd, `Failed to inspect imported MCP config from ${kind}:`);
214
- if (!imported) return null;
215
- return {
216
- kind,
217
- path: imported.path,
218
- serverCount: Object.keys(extractServers(imported.value, kind)).length,
219
- } satisfies ImportConfigSummary;
220
- })
221
- .filter((value): value is ImportConfigSummary => value !== null);
219
+ export function getMcpStandardConfigSummary(overridePath?: string, cwd = process.cwd()): McpStandardConfigSummary {
220
+ const sources = getConfigSourceSummaries(getConfigSources(overridePath, cwd));
221
+ return {
222
+ sources,
223
+ hasSharedServers: sources.some((source) => source.kind === "shared" && source.serverCount > 0),
224
+ fingerprint: JSON.stringify({ sources: sources.map((source) => [source.id, source.exists, source.serverCount]) }),
225
+ };
226
+ }
227
+
228
+ export function getMcpDiscoverySummary(
229
+ overridePath?: string,
230
+ cwd = process.cwd(),
231
+ options: { includeHostConfigs?: boolean } = {},
232
+ ): McpDiscoverySummary {
233
+ const sourceSpecs = getConfigSources(overridePath, cwd);
234
+ const sources = getConfigSourceSummaries(sourceSpecs);
235
+ const includeHostConfigs = options.includeHostConfigs !== false;
236
+
237
+ const imports = includeHostConfigs
238
+ ? (Object.keys(IMPORT_PATHS) as ImportKind[])
239
+ .map((kind) => {
240
+ const imported = loadImportedConfig(kind, cwd, `Failed to inspect imported MCP config from ${kind}:`);
241
+ if (!imported) return null;
242
+ return {
243
+ kind,
244
+ path: imported.path,
245
+ serverCount: Object.keys(extractServers(imported.value, kind)).length,
246
+ } satisfies ImportConfigSummary;
247
+ })
248
+ .filter((value): value is ImportConfigSummary => value !== null)
249
+ : [];
222
250
  const hostConfigDiscovery = getConfiguredHostConfigDiscovery(overridePath, cwd);
223
251
  const hostConfigs = imports.map((entry) => ({ ...entry, active: hostConfigDiscovery === "on" }));
224
- const totalServerCount = sources.reduce((sum, source) => sum + source.serverCount, 0);
225
- const hasSharedServers = sources.some((source) => source.kind === "shared" && source.serverCount > 0);
252
+ const settings = getMergedSettings(overridePath, cwd);
253
+ const agentPlugins = getAgentPluginSummaries(settings?.agentPluginPaths, cwd);
254
+ const totalServerCount = sources.reduce((sum, source) => sum + source.serverCount, 0) + agentPlugins.reduce((sum, plugin) => sum + plugin.serverCount, 0);
255
+ const hasSharedServers = sources.some((source) => source.kind === "shared" && source.serverCount > 0) || agentPlugins.some(plugin => plugin.serverCount > 0);
226
256
  const hasPiOwnedServers = sources.some((source) => source.kind === "pi" && source.serverCount > 0);
227
- const hasAnyDetectedPaths = sources.some((source) => source.exists) || imports.length > 0;
257
+ const hasAnyDetectedPaths = sources.some((source) => source.exists) || imports.length > 0 || agentPlugins.length > 0;
228
258
  const hasAnyConfig = totalServerCount > 0 || imports.some((entry) => entry.serverCount > 0) || hasAnyDetectedPaths;
229
259
 
230
260
  const summaryWithoutRepoPrompt = {
@@ -232,6 +262,7 @@ export function getMcpDiscoverySummary(overridePath?: string, cwd = process.cwd(
232
262
  imports,
233
263
  hostConfigs,
234
264
  hostConfigDiscovery,
265
+ agentPlugins,
235
266
  conflicts: getConfigConflicts(sourceSpecs, imports, cwd),
236
267
  hasAnyConfig,
237
268
  hasAnyDetectedPaths,
@@ -243,6 +274,7 @@ export function getMcpDiscoverySummary(overridePath?: string, cwd = process.cwd(
243
274
  const fingerprint = JSON.stringify({
244
275
  sources: sources.map((source) => [source.id, source.exists, source.serverCount]),
245
276
  imports: imports.map((entry) => [entry.kind, entry.path, entry.serverCount]),
277
+ agentPlugins: agentPlugins.map((entry) => [entry.path, entry.name, entry.serverCount]),
246
278
  hostConfigDiscovery,
247
279
  conflicts: summaryWithoutRepoPrompt.conflicts,
248
280
  });
@@ -274,16 +306,24 @@ export function loadMcpConfig(overridePath?: string, cwd = process.cwd()): McpCo
274
306
  config = mergeConfigs(config, expandImports(loaded, cwd));
275
307
  }
276
308
 
277
- return config;
309
+ const pluginConfig = loadAgentPluginConfigs(config.settings?.agentPluginPaths, cwd);
310
+ return mergeConfigs(pluginConfig, config);
278
311
  }
279
312
 
280
- function getConfiguredHostConfigDiscovery(overridePath?: string, cwd = process.cwd()): HostConfigDiscovery {
281
- let configured: HostConfigDiscovery = "off";
313
+ function getMergedSettings(overridePath?: string, cwd = process.cwd()): McpSettings | undefined {
314
+ let settings: McpSettings | undefined;
282
315
  for (const source of getConfigSources(overridePath, cwd)) {
283
316
  const loaded = readValidatedConfig(source.readPath, `MCP config from ${source.readPath}`);
284
- const value = loaded?.settings?.hostConfigDiscovery;
285
- if (value === "off" || value === "prompt" || value === "on") configured = value;
317
+ if (loaded?.settings) settings = { ...settings, ...loaded.settings };
286
318
  }
319
+ return settings;
320
+ }
321
+
322
+ function getConfiguredHostConfigDiscovery(overridePath?: string, cwd = process.cwd()): HostConfigDiscovery {
323
+ let configured: HostConfigDiscovery = "off";
324
+ const settings = getMergedSettings(overridePath, cwd);
325
+ const value = settings?.hostConfigDiscovery;
326
+ if (value === "off" || value === "prompt" || value === "on") configured = value;
287
327
  return configured;
288
328
  }
289
329
 
@@ -417,10 +457,12 @@ function getConfigSources(overridePath?: string, cwd = process.cwd()): ConfigSou
417
457
  }
418
458
 
419
459
  function mergeConfigs(base: McpConfig, next: McpConfig): McpConfig {
460
+ const imports = mergeImports(base.imports, next.imports);
461
+ const settings = next.settings ? { ...base.settings, ...next.settings } : base.settings;
420
462
  return {
421
463
  mcpServers: mergeServerMaps(base.mcpServers, next.mcpServers),
422
- imports: mergeImports(base.imports, next.imports),
423
- settings: next.settings ? { ...base.settings, ...next.settings } : base.settings,
464
+ ...(imports !== undefined ? { imports } : {}),
465
+ ...(settings !== undefined ? { settings } : {}),
424
466
  };
425
467
  }
426
468
 
@@ -499,7 +541,7 @@ function expandImports(config: McpConfig, cwd = process.cwd()): McpConfig {
499
541
 
500
542
  return {
501
543
  imports: config.imports,
502
- settings: config.settings,
544
+ ...(config.settings !== undefined ? { settings: config.settings } : {}),
503
545
  mcpServers: mergeServerMaps(importedServers, config.mcpServers),
504
546
  };
505
547
  }
@@ -596,22 +638,32 @@ function readValidatedConfig(path: string, label: string): McpConfig | null {
596
638
  }
597
639
 
598
640
  function validateConfig(raw: unknown): McpConfig {
599
- if (!raw || typeof raw !== "object") {
641
+ if (!isRecord(raw)) {
600
642
  return { mcpServers: {} };
601
643
  }
602
644
 
603
- const obj = raw as Record<string, unknown>;
604
- const servers = obj.mcpServers ?? obj["mcp-servers"] ?? {};
645
+ return {
646
+ mcpServers: toServerEntries(raw.mcpServers ?? raw["mcp-servers"]),
647
+ ...(Array.isArray(raw.imports) ? { imports: raw.imports as ImportKind[] } : {}),
648
+ ...(raw.settings !== undefined ? { settings: raw.settings as McpSettings } : {}),
649
+ };
650
+ }
605
651
 
606
- if (typeof servers !== "object" || servers === null || Array.isArray(servers)) {
607
- return { mcpServers: {} };
652
+ function toServerEntries(servers: unknown): Record<string, ServerEntry> {
653
+ if (!isRecord(servers)) return {};
654
+ const entries: Record<string, ServerEntry> = {};
655
+ for (const [name, entry] of Object.entries(servers)) {
656
+ if (isServerEntry(entry)) entries[name] = entry;
608
657
  }
658
+ return entries;
659
+ }
609
660
 
610
- return {
611
- mcpServers: servers as Record<string, ServerEntry>,
612
- imports: Array.isArray(obj.imports) ? (obj.imports as ImportKind[]) : undefined,
613
- settings: obj.settings as McpSettings | undefined,
614
- };
661
+ function isServerEntry(value: unknown): value is ServerEntry {
662
+ return isRecord(value);
663
+ }
664
+
665
+ function isRecord(value: unknown): value is Record<string, unknown> {
666
+ return typeof value === "object" && value !== null && !Array.isArray(value);
615
667
  }
616
668
 
617
669
  function mergeOpenCodeConfigs(base: Record<string, unknown>, next: Record<string, unknown>): Record<string, unknown> {
@@ -708,8 +760,10 @@ function extractServers(config: unknown, kind: ImportKind): Record<string, Serve
708
760
 
709
761
  if (raw.type === "local" && Array.isArray(raw.command) && raw.command.length > 0 && raw.command.every((value): value is string => typeof value === "string")) {
710
762
  const env = toStringRecord(raw.environment);
763
+ const command = raw.command[0];
764
+ if (command === undefined) continue;
711
765
  const mapped: ServerEntry = {
712
- command: raw.command[0],
766
+ command,
713
767
  args: raw.command.slice(1),
714
768
  ...(env ? { env } : {}),
715
769
  ...(typeof raw.cwd === "string" ? { cwd: raw.cwd } : {}),
@@ -733,6 +787,9 @@ function extractServers(config: unknown, kind: ImportKind): Record<string, Serve
733
787
  ...(typeof oauth.clientId === "string" ? { clientId: oauth.clientId } : {}),
734
788
  ...(typeof oauth.clientSecret === "string" ? { clientSecret: oauth.clientSecret } : {}),
735
789
  ...(typeof oauth.scope === "string" ? { scope: oauth.scope } : {}),
790
+ ...(typeof oauth.skipIssuerMetadataValidation === "boolean"
791
+ ? { skipIssuerMetadataValidation: oauth.skipIssuerMetadataValidation }
792
+ : {}),
736
793
  };
737
794
  }
738
795
  mappedServers[name] = mapped;
@@ -740,12 +797,13 @@ function extractServers(config: unknown, kind: ImportKind): Record<string, Serve
740
797
  continue;
741
798
  }
742
799
 
743
- if (kind !== "codex" || !entry || typeof entry !== "object" || Array.isArray(entry)) {
744
- mappedServers[name] = entry as ServerEntry;
800
+ if (!isRecord(entry)) continue;
801
+ if (kind !== "codex") {
802
+ mappedServers[name] = entry;
745
803
  continue;
746
804
  }
747
805
 
748
- const mapped = { ...(entry as Record<string, unknown>) };
806
+ const mapped = { ...entry };
749
807
  const bearerTokenEnv = mapped.bearer_token_env_var;
750
808
  const httpHeaders = mapped.http_headers;
751
809
  const envHttpHeaders = mapped.env_http_headers;
@@ -789,9 +847,12 @@ function buildUnifiedDiff(beforeText: string, afterText: string): string {
789
847
 
790
848
  for (let i = rows - 1; i >= 0; i--) {
791
849
  for (let j = cols - 1; j >= 0; j--) {
792
- lcs[i][j] = before[i] === after[j]
793
- ? lcs[i + 1][j + 1] + 1
794
- : Math.max(lcs[i + 1][j], lcs[i][j + 1]);
850
+ const row = lcs[i];
851
+ const nextRow = lcs[i + 1];
852
+ if (!row || !nextRow) continue;
853
+ row[j] = before[i] === after[j]
854
+ ? (nextRow[j + 1] ?? 0) + 1
855
+ : Math.max(nextRow[j] ?? 0, row[j + 1] ?? 0);
795
856
  }
796
857
  }
797
858
 
@@ -805,7 +866,7 @@ function buildUnifiedDiff(beforeText: string, afterText: string): string {
805
866
  j++;
806
867
  continue;
807
868
  }
808
- if (j < cols && (i === rows || lcs[i][j + 1] >= lcs[i + 1][j])) {
869
+ if (j < cols && (i === rows || (lcs[i]?.[j + 1] ?? 0) >= (lcs[i + 1]?.[j] ?? 0))) {
809
870
  lines.push(`+ ${after[j]}`);
810
871
  j++;
811
872
  continue;
@@ -1108,7 +1169,7 @@ export function getServerProvenance(overridePath?: string, cwd = process.cwd()):
1108
1169
  provenance.set(name, {
1109
1170
  path: source.writePath,
1110
1171
  kind: source.kind,
1111
- importKind: source.importKind,
1172
+ ...(source.importKind !== undefined ? { importKind: source.importKind } : {}),
1112
1173
  });
1113
1174
  }
1114
1175
  }
@@ -1,5 +1,5 @@
1
1
  import type { AgentToolResult, AgentToolUpdateCallback, ExtensionContext } from "@earendil-works/pi-coding-agent";
2
- import { UrlElicitationRequiredError } from "@modelcontextprotocol/sdk/types.js";
2
+ import { UrlElicitationRequiredError, type Client } from "@modelcontextprotocol/client";
3
3
  import type { McpExtensionState } from "./state.ts";
4
4
  import type { DirectToolSpec, McpConfig, McpContent, ToolPrefix } from "./types.ts";
5
5
  import type { MetadataCache } from "./metadata-cache.ts";
@@ -19,6 +19,8 @@ import { SessionRecoveryAuthRequiredError, withSessionRecovery } from "./session
19
19
  import { combineAbortSignals, isAbortError } from "./runtime-owner.ts";
20
20
  import { ensureToolCallApproved } from "./tool-approval.ts";
21
21
 
22
+ type ClientCallToolResult = Awaited<ReturnType<Client["callTool"]>>;
23
+
22
24
  const BUILTIN_NAMES = new Set(["read", "bash", "edit", "write", "grep", "find", "ls", "mcp"]);
23
25
  const INSTRUCTIONS_SNIPPET_LENGTH = 150;
24
26
  export const DIRECT_TOOLS_ADVISORY_THRESHOLD = 75;
@@ -92,7 +94,10 @@ async function attemptDirectAutoAuth(
92
94
  : { authStorageOptions: state.authStorageOptions, runtime: state.oauthRuntime },
93
95
  );
94
96
  } else {
95
- await authenticate(serverName, serverUrl, definition, { signal, runtime: state.oauthRuntime });
97
+ await authenticate(serverName, serverUrl, definition, {
98
+ ...(signal ? { signal } : {}),
99
+ runtime: state.oauthRuntime,
100
+ });
96
101
  }
97
102
  return { status: "success" };
98
103
  } catch (error) {
@@ -162,9 +167,9 @@ export function resolveDirectTools(
162
167
  originalName: tool.name,
163
168
  prefixedName,
164
169
  description: tool.description ?? "",
165
- inputSchema: tool.inputSchema,
166
- uiResourceUri: tool.uiResourceUri,
167
- uiStreamMode: tool.uiStreamMode,
170
+ ...(tool.inputSchema !== undefined ? { inputSchema: tool.inputSchema } : {}),
171
+ ...(tool.uiResourceUri !== undefined ? { uiResourceUri: tool.uiResourceUri } : {}),
172
+ ...(tool.uiStreamMode !== undefined ? { uiStreamMode: tool.uiStreamMode } : {}),
168
173
  });
169
174
  }
170
175
 
@@ -207,7 +212,7 @@ export function buildProxyDescription(
207
212
  directSpecs: DirectToolSpec[],
208
213
  ): string {
209
214
  const prefix = config.settings?.toolPrefix ?? "server";
210
- let desc = `MCP gateway — server status, tool search/describe, auth, and single MCP tool calls. When one request needs several MCP calls with logic between them, use mcp_script. Non-MCP Pi tools should be called directly, not through mcp.\n`;
215
+ let desc = `MCP gateway — server status, tool search/describe, auth, and single MCP tool calls. When one request needs several MCP calls with logic between them, use mcpScript. Non-MCP Pi tools should be called directly, not through mcp.\n`;
211
216
 
212
217
  const directByServer = new Map<string, number>();
213
218
  for (const spec of directSpecs) {
@@ -223,7 +228,7 @@ export function buildProxyDescription(
223
228
  const serverSummaries: string[] = [];
224
229
  for (const serverName of Object.keys(config.mcpServers)) {
225
230
  const definition = config.mcpServers[serverName];
226
- if (isServerDisabled(definition)) continue;
231
+ if (!definition || isServerDisabled(definition)) continue;
227
232
  const entry = cache?.servers?.[serverName];
228
233
  const effectivePrefix = resolveToolPrefix(definition, prefix);
229
234
  const toolCount = (entry?.tools ?? []).filter(
@@ -377,11 +382,11 @@ export function createDirectToolExecutor(
377
382
  name: spec.prefixedName,
378
383
  originalName: spec.originalName,
379
384
  description: spec.description,
380
- inputSchema: spec.inputSchema,
381
- resourceUri: spec.resourceUri,
382
- uiResourceUri: spec.uiResourceUri,
383
- uiStreamMode: spec.uiStreamMode,
384
- }, params, ownedSignal);
385
+ ...(spec.inputSchema !== undefined ? { inputSchema: spec.inputSchema } : {}),
386
+ ...(spec.resourceUri !== undefined ? { resourceUri: spec.resourceUri } : {}),
387
+ ...(spec.uiResourceUri !== undefined ? { uiResourceUri: spec.uiResourceUri } : {}),
388
+ ...(spec.uiStreamMode !== undefined ? { uiStreamMode: spec.uiStreamMode } : {}),
389
+ }, params, ownedSignal, spec.resourceUri ? "resource" : "direct");
385
390
  if (approval.ok === false) {
386
391
  const denied = approval.reason === "denied";
387
392
  const message = denied
@@ -431,7 +436,12 @@ export function createDirectToolExecutor(
431
436
 
432
437
  if (spec.resourceUri) {
433
438
  const result = await withSessionRecovery(
434
- { manager: state.manager, config: state.config, signal: ownedSignal, onNeedsAuth: recoverAuthConnection },
439
+ {
440
+ manager: state.manager,
441
+ config: state.config,
442
+ ...(ownedSignal ? { signal: ownedSignal } : {}),
443
+ onNeedsAuth: recoverAuthConnection,
444
+ },
435
445
  spec.serverName,
436
446
  (conn) => conn.client.readResource({ uri: spec.resourceUri! }, requestOptions),
437
447
  );
@@ -453,22 +463,27 @@ export function createDirectToolExecutor(
453
463
  toolName: spec.originalName,
454
464
  toolArgs: params ?? {},
455
465
  uiResourceUri: spec.uiResourceUri!,
456
- streamMode: spec.uiStreamMode,
457
- signal,
466
+ ...(spec.uiStreamMode !== undefined ? { streamMode: spec.uiStreamMode } : {}),
467
+ ...(signal ? { signal } : {}),
458
468
  onNeedsAuth: recoverAuthConnection,
459
469
  })
460
470
  : null;
461
471
 
462
- const result = await withSessionRecovery(
463
- { manager: state.manager, config: state.config, signal: ownedSignal, onNeedsAuth: recoverAuthConnection },
472
+ const result = await withSessionRecovery<ClientCallToolResult>(
473
+ {
474
+ manager: state.manager,
475
+ config: state.config,
476
+ ...(ownedSignal ? { signal: ownedSignal } : {}),
477
+ onNeedsAuth: recoverAuthConnection,
478
+ },
464
479
  spec.serverName,
465
480
  (conn) => abortable(conn.client.callTool({
466
481
  name: spec.originalName,
467
482
  arguments: params ?? {},
468
483
  _meta: uiSession?.requestMeta,
469
- }, undefined, requestOptions), ownedSignal),
484
+ }, requestOptions), ownedSignal),
470
485
  );
471
- uiSession?.sendToolResult(result as unknown as import("@modelcontextprotocol/sdk/types.js").CallToolResult);
486
+ uiSession?.sendToolResult(result as unknown as import("@modelcontextprotocol/client").CallToolResult);
472
487
 
473
488
  if (result.isError) {
474
489
  const mcpContent = (result.content ?? []) as McpContent[];
@@ -1,22 +1,22 @@
1
1
  import type { ExtensionUIContext } from "@earendil-works/pi-coding-agent";
2
- import type { Client } from "@modelcontextprotocol/sdk/client/index.js";
2
+ import type { Client } from "@modelcontextprotocol/client";
3
3
  import {
4
- ElicitRequestSchema,
5
- ErrorCode,
6
- McpError,
4
+ ProtocolError,
5
+ ProtocolErrorCode,
7
6
  type ElicitRequest,
8
7
  type ElicitRequestFormParams,
9
8
  type ElicitRequestURLParams,
10
9
  type ElicitResult,
11
- } from "@modelcontextprotocol/sdk/types.js";
12
- import { AjvJsonSchemaValidator } from "@modelcontextprotocol/sdk/validation/ajv";
13
- import type { JsonSchemaType } from "@modelcontextprotocol/sdk/validation/types.js";
10
+ } from "@modelcontextprotocol/client";
11
+ import { AjvJsonSchemaValidator } from "@modelcontextprotocol/client/validators/ajv";
12
+ import type { JsonSchemaType } from "@modelcontextprotocol/client";
14
13
  import open from "open";
15
14
 
16
15
  export type ElicitationValue = string | number | boolean | string[] | undefined;
17
16
  type FormProperty = ElicitRequestFormParams["requestedSchema"]["properties"][string];
17
+ type FieldCollectionResult = { status: "cancelled" } | { status: "collected"; value: ElicitationValue };
18
18
 
19
- export type ElicitationUIContext = ExtensionUIContext;
19
+ export type ElicitationUIContext = Pick<ExtensionUIContext, "select" | "input" | "notify">;
20
20
 
21
21
  export interface ElicitationHandlerOptions {
22
22
  serverName: string;
@@ -28,7 +28,7 @@ export interface ElicitationHandlerOptions {
28
28
  export type ServerElicitationConfig = Omit<ElicitationHandlerOptions, "serverName" | "onUrlAccepted">;
29
29
 
30
30
  export function registerElicitationHandler(client: Client, options: ElicitationHandlerOptions): void {
31
- client.setRequestHandler(ElicitRequestSchema, request =>
31
+ client.setRequestHandler("elicitation/create", request =>
32
32
  handleElicitationRequest(options, request));
33
33
  }
34
34
 
@@ -45,18 +45,19 @@ export async function handleFormElicitation(
45
45
  options: ElicitationHandlerOptions,
46
46
  params: ElicitRequestFormParams,
47
47
  ): Promise<ElicitResult> {
48
+ const properties = Object.entries(params.requestedSchema.properties);
48
49
  const decision = await options.ui.select(
49
50
  `MCP Input Request\nServer: ${options.serverName}\n\n${params.message}`,
50
51
  ["Continue", "Decline"],
51
52
  );
52
53
  if (decision === undefined) return { action: "cancel" };
53
54
  if (decision === "Decline") return { action: "decline" };
55
+ if (properties.length === 0) return { action: "accept", content: {} };
54
56
 
55
57
  const values: Record<string, ElicitationValue> = {};
56
- const properties = Object.entries(params.requestedSchema.properties);
57
58
  for (const [name, schema] of properties) {
58
59
  const value = await collectValidField(options.ui, params, name, schema);
59
- if (!("value" in value)) return { action: "cancel" };
60
+ if (value.status === "cancelled") return { action: "cancel" };
60
61
  values[name] = value.value;
61
62
  }
62
63
 
@@ -64,7 +65,7 @@ export async function handleFormElicitation(
64
65
  const content = coerceAndValidateFormValues(params, values);
65
66
  const action = await options.ui.select(
66
67
  formatReview(options.serverName, properties, content),
67
- properties.length > 0 ? ["Submit", "Edit", "Decline"] : ["Submit", "Decline"],
68
+ ["Submit", "Edit", "Decline"],
68
69
  );
69
70
  if (action === undefined) return { action: "cancel" };
70
71
  if (action === "Decline") return { action: "decline" };
@@ -77,7 +78,7 @@ export async function handleFormElicitation(
77
78
  if (!property) continue;
78
79
  const [name, schema] = property;
79
80
  const value = await collectValidField(options.ui, params, name, schema, values[name]);
80
- if (!("value" in value)) return { action: "cancel" };
81
+ if (value.status === "cancelled") return { action: "cancel" };
81
82
  values[name] = value.value;
82
83
  }
83
84
  }
@@ -88,11 +89,11 @@ async function collectValidField(
88
89
  name: string,
89
90
  schema: FormProperty,
90
91
  current?: ElicitationValue,
91
- ): Promise<{ cancelled: true } | { cancelled: false; value: ElicitationValue }> {
92
+ ): Promise<FieldCollectionResult> {
92
93
  const required = params.requestedSchema.required?.includes(name) === true;
93
94
  while (true) {
94
95
  const result = await collectField(ui, params, name, schema, current);
95
- if (!("value" in result)) return result;
96
+ if (result.status === "cancelled") return result;
96
97
  try {
97
98
  coerceAndValidateFormValues({
98
99
  ...params,
@@ -116,7 +117,7 @@ async function collectField(
116
117
  name: string,
117
118
  schema: FormProperty,
118
119
  current?: ElicitationValue,
119
- ): Promise<{ cancelled: true } | { cancelled: false; value: ElicitationValue }> {
120
+ ): Promise<FieldCollectionResult> {
120
121
  const required = params.requestedSchema.required?.includes(name) === true;
121
122
  const title = [schema.title ?? humanizeName(name), required ? "(required)" : "", schema.description]
122
123
  .filter(Boolean)
@@ -136,10 +137,10 @@ async function collectField(
136
137
  const omit = required ? undefined : uniqueAction("Omit", actions);
137
138
  if (omit) actions.push(omit);
138
139
  const action = await ui.select(title, actions);
139
- if (action === undefined) return { cancelled: true };
140
- if (action === useDefault) return { cancelled: false, value: schema.default };
141
- if (action === omit) return { cancelled: false, value: undefined };
142
- return { cancelled: false, value: choices[displays.indexOf(action)]?.value };
140
+ if (action === undefined) return { status: "cancelled" };
141
+ if (action === useDefault) return { status: "collected", value: schema.default };
142
+ if (action === omit) return { status: "collected", value: undefined };
143
+ return { status: "collected", value: choices[displays.indexOf(action)]?.value };
143
144
  }
144
145
 
145
146
  if (schema.type === "boolean") {
@@ -147,10 +148,10 @@ async function collectField(
147
148
  if (schema.default !== undefined) actions.push("Use default");
148
149
  if (!required) actions.push("Omit");
149
150
  const action = await ui.select(title, actions);
150
- if (action === undefined) return { cancelled: true };
151
- if (action === "Use default") return { cancelled: false, value: schema.default };
152
- if (action === "Omit") return { cancelled: false, value: undefined };
153
- return { cancelled: false, value: action === "Yes" };
151
+ if (action === undefined) return { status: "cancelled" };
152
+ if (action === "Use default") return { status: "collected", value: schema.default };
153
+ if (action === "Omit") return { status: "collected", value: undefined };
154
+ return { status: "collected", value: action === "Yes" };
154
155
  }
155
156
 
156
157
  if (schema.type === "array") {
@@ -158,9 +159,9 @@ async function collectField(
158
159
  if (schema.default !== undefined) actions.push("Use default");
159
160
  if (!required) actions.push("Omit");
160
161
  const action = await ui.select(title, actions);
161
- if (action === undefined) return { cancelled: true };
162
- if (action === "Use default") return { cancelled: false, value: schema.default };
163
- if (action === "Omit") return { cancelled: false, value: undefined };
162
+ if (action === undefined) return { status: "cancelled" };
163
+ if (action === "Use default") return { status: "collected", value: schema.default };
164
+ if (action === "Omit") return { status: "collected", value: undefined };
164
165
 
165
166
  const choices = extractMultiSelectOptions(schema);
166
167
  const selected = new Set(Array.isArray(current) ? current : []);
@@ -168,8 +169,8 @@ async function collectField(
168
169
  const displays = uniqueLabels(choices.map(choice => selected.has(choice.value) ? `✓ ${choice.display}` : choice.display));
169
170
  const done = uniqueAction("Done", displays);
170
171
  const picked = await ui.select(title, [...displays, done]);
171
- if (picked === undefined) return { cancelled: true };
172
- if (picked === done) return { cancelled: false, value: [...selected] };
172
+ if (picked === undefined) return { status: "cancelled" };
173
+ if (picked === done) return { status: "collected", value: [...selected] };
173
174
  const choice = choices[displays.indexOf(picked)];
174
175
  if (!choice) continue;
175
176
  if (selected.has(choice.value)) selected.delete(choice.value);
@@ -181,11 +182,11 @@ async function collectField(
181
182
  if (schema.default !== undefined) actions.push("Use default");
182
183
  if (!required) actions.push("Omit");
183
184
  const action = await ui.select(title, actions);
184
- if (action === undefined) return { cancelled: true };
185
- if (action === "Use default") return { cancelled: false, value: schema.default };
186
- if (action === "Omit") return { cancelled: false, value: undefined };
185
+ if (action === undefined) return { status: "cancelled" };
186
+ if (action === "Use default") return { status: "collected", value: schema.default };
187
+ if (action === "Omit") return { status: "collected", value: undefined };
187
188
  const entered = await ui.input(title, current === undefined ? undefined : String(current));
188
- return entered === undefined ? { cancelled: true } : { cancelled: false, value: entered };
189
+ return entered === undefined ? { status: "cancelled" } : { status: "collected", value: entered };
189
190
  }
190
191
 
191
192
  export function coerceAndValidateFormValues(
@@ -305,16 +306,16 @@ export async function handleUrlElicitation(
305
306
  options: ElicitationHandlerOptions,
306
307
  params: ElicitRequestURLParams,
307
308
  ): Promise<ElicitResult> {
308
- if (!options.allowUrl) throw new McpError(ErrorCode.InvalidParams, "URL elicitation is not supported");
309
+ if (!options.allowUrl) throw new ProtocolError(ProtocolErrorCode.InvalidParams, "URL elicitation is not supported");
309
310
 
310
311
  let parsed: URL;
311
312
  try {
312
313
  parsed = new URL(params.url);
313
314
  } catch {
314
- throw new McpError(ErrorCode.InvalidParams, "URL elicitation supplied an invalid URL");
315
+ throw new ProtocolError(ProtocolErrorCode.InvalidParams, "URL elicitation supplied an invalid URL");
315
316
  }
316
317
  if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
317
- throw new McpError(ErrorCode.InvalidParams, "URL elicitation only supports HTTP and HTTPS URLs");
318
+ throw new ProtocolError(ProtocolErrorCode.InvalidParams, "URL elicitation only supports HTTP and HTTPS URLs");
318
319
  }
319
320
 
320
321
  const decision = await options.ui.select([