@fitchmultz/pi-mcp-adapter 8.0.6 → 8.0.8

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 (87) hide show
  1. package/CHANGELOG.md +142 -8
  2. package/OAUTH.md +13 -16
  3. package/README.md +8 -8
  4. package/cli.js +67 -28
  5. package/dist/abort.js +18 -9
  6. package/dist/client-operations.js +4 -2
  7. package/dist/commands.js +77 -43
  8. package/dist/config.js +194 -105
  9. package/dist/connection-runtime.d.ts +2 -2
  10. package/dist/connection-runtime.js +29 -18
  11. package/dist/consent-manager.d.ts +2 -2
  12. package/dist/consent-manager.js +5 -2
  13. package/dist/direct-tools.d.ts +1 -1
  14. package/dist/direct-tools.js +13 -7
  15. package/dist/elicitation-handler.d.ts +2 -3
  16. package/dist/elicitation-handler.js +113 -58
  17. package/dist/gateway-arguments.d.ts +1 -1
  18. package/dist/gateway-arguments.js +90 -23
  19. package/dist/glimpse-ui.js +10 -5
  20. package/dist/host-html-template.d.ts +3 -1
  21. package/dist/host-html-template.js +9 -12
  22. package/dist/index.d.ts +1 -1
  23. package/dist/index.js +264 -119
  24. package/dist/init.d.ts +1 -3
  25. package/dist/init.js +91 -51
  26. package/dist/legacy-migration.d.ts +1 -1
  27. package/dist/legacy-migration.js +17 -12
  28. package/dist/lifecycle.d.ts +3 -3
  29. package/dist/lifecycle.js +33 -17
  30. package/dist/logger.d.ts +2 -2
  31. package/dist/logger.js +4 -2
  32. package/dist/mcp-auth-flow.js +150 -58
  33. package/dist/mcp-auth.d.ts +7 -7
  34. package/dist/mcp-auth.js +189 -138
  35. package/dist/mcp-callback-server.js +19 -8
  36. package/dist/mcp-code.js +168 -54
  37. package/dist/mcp-keyring-helper.cjs +57 -23
  38. package/dist/mcp-oauth-provider.d.ts +6 -7
  39. package/dist/mcp-oauth-provider.js +146 -72
  40. package/dist/mcp-oauth-runtime.js +29 -16
  41. package/dist/mcp-output-guard.js +193 -65
  42. package/dist/mcp-output-preview.js +41 -20
  43. package/dist/mcp-panel.d.ts +9 -9
  44. package/dist/mcp-panel.js +141 -70
  45. package/dist/mcp-script-worker.mjs +135 -43
  46. package/dist/mcp-setup-panel.d.ts +8 -8
  47. package/dist/mcp-setup-panel.js +130 -44
  48. package/dist/mcp-status.js +14 -7
  49. package/dist/mcp-trace.d.ts +10 -5
  50. package/dist/mcp-trace.js +127 -61
  51. package/dist/metadata-cache.d.ts +3 -4
  52. package/dist/metadata-cache.js +83 -43
  53. package/dist/npx-resolver.js +101 -60
  54. package/dist/onboarding-state.js +4 -2
  55. package/dist/plain-result-reader.js +78 -31
  56. package/dist/prompts.js +36 -20
  57. package/dist/proxy-modes.js +670 -193
  58. package/dist/resource-tools.js +30 -9
  59. package/dist/runtime-owner.js +36 -18
  60. package/dist/sampling-handler.d.ts +1 -2
  61. package/dist/sampling-handler.js +41 -21
  62. package/dist/search-ranking.d.ts +1 -1
  63. package/dist/search-ranking.js +67 -34
  64. package/dist/server-manager.d.ts +11 -11
  65. package/dist/server-manager.js +283 -166
  66. package/dist/session-recovery.js +113 -61
  67. package/dist/tool-approval.js +9 -3
  68. package/dist/tool-loader.js +154 -55
  69. package/dist/tool-metadata.d.ts +1 -1
  70. package/dist/tool-metadata.js +51 -23
  71. package/dist/tool-registrar.js +32 -10
  72. package/dist/tool-result-renderer.js +58 -33
  73. package/dist/types.d.ts +2 -2
  74. package/dist/types.js +39 -18
  75. package/dist/ui-message-history.js +33 -13
  76. package/dist/ui-resource-handler.d.ts +3 -3
  77. package/dist/ui-resource-handler.js +37 -21
  78. package/dist/ui-server.js +101 -43
  79. package/dist/ui-session.js +66 -29
  80. package/dist/ui-stream-types.js +14 -4
  81. package/dist/ui-tool-visibility.js +12 -6
  82. package/dist/unix-socket-transport.d.ts +1 -2
  83. package/dist/unix-socket-transport.js +19 -12
  84. package/dist/utils.d.ts +2 -0
  85. package/dist/utils.js +60 -35
  86. package/package.json +28 -2
  87. package/skills/mcp-scripting/SKILL.md +6 -6
package/dist/commands.js CHANGED
@@ -1,18 +1,19 @@
1
- import { isServerDisabled, isNonInteractiveOAuth } from "./types.js";
1
+ import { isServerDisabled, isNonInteractiveOAuth, } from "./types.js";
2
2
  import { ensureCompatibilityImports, getMcpDiscoverySummary, getProjectConfigPath, getServerProvenance, previewCompatibilityImports, previewSharedServerEntry, previewStarterProjectConfig, writeDirectToolsConfig, writeSharedServerEntry, writeStarterProjectConfig, } from "./config.js";
3
- import { MAX_CONCURRENT_SERVER_CONNECTS, markKeepAliveAfterConnect, notifyToolMetadataUpdated, updateMetadataCache, updateStatusBar, getFailureAgeSeconds, getFailureMessage, clearFailure, recordFailure } from "./init.js";
3
+ import { MAX_CONCURRENT_SERVER_CONNECTS, markKeepAliveAfterConnect, notifyToolMetadataUpdated, updateMetadataCache, updateStatusBar, getFailureAgeSeconds, getFailureMessage, clearFailure, recordFailure, } from "./init.js";
4
4
  import { loadMetadataCache, reconstructPromptMetadata } from "./metadata-cache.js";
5
5
  import { buildToolMetadata } from "./tool-metadata.js";
6
6
  import { supportsOAuth } from "./mcp-oauth-runtime.js";
7
7
  import { getAuthStorageOptions, inspectAuthForUrl } from "./mcp-auth.js";
8
- import { loadOnboardingState, markSetupCompleted as persistSetupCompleted, markSharedConfigHintShown } from "./onboarding-state.js";
8
+ import { loadOnboardingState, markSetupCompleted as persistSetupCompleted, markSharedConfigHintShown, } from "./onboarding-state.js";
9
9
  import { openPath, parallelLimit, resolveServerUrl, sanitizeTerminalText } from "./utils.js";
10
10
  import { isAbortError } from "./runtime-owner.js";
11
11
  import { createMcpPanel } from "./mcp-panel.js";
12
12
  import { createMcpSetupPanel } from "./mcp-setup-panel.js";
13
13
  export async function showStatus(state, ctx) {
14
- if (!ctx.hasUI)
14
+ if (!ctx.hasUI) {
15
15
  return;
16
+ }
16
17
  const lines = ["MCP Server Status:", ""];
17
18
  for (const name of Object.keys(state.config.mcpServers)) {
18
19
  const definition = state.config.mcpServers[name];
@@ -22,8 +23,8 @@ export async function showStatus(state, ctx) {
22
23
  }
23
24
  const connection = state.manager.getConnection(name);
24
25
  const metadata = state.toolMetadata.get(name);
25
- const toolCount = metadata?.filter(tool => tool.resourceUri === undefined).length ?? 0;
26
- const resourceCount = metadata?.filter(tool => tool.resourceUri !== undefined).length ?? 0;
26
+ const toolCount = metadata?.filter((tool) => tool.resourceUri === undefined).length ?? 0;
27
+ const resourceCount = metadata?.filter((tool) => tool.resourceUri !== undefined).length ?? 0;
27
28
  const failedAgo = getFailureAgeSeconds(state, name);
28
29
  let status = "not connected";
29
30
  let statusIcon = "○";
@@ -45,9 +46,11 @@ export async function showStatus(state, ctx) {
45
46
  else if (metadata !== undefined) {
46
47
  status = "cached";
47
48
  }
48
- const toolSuffix = failed ? "" : metadata === undefined
49
- ? " (undiscovered)"
50
- : ` (${toolCount} tool${toolCount === 1 ? "" : "s"}, ${resourceCount} resource${resourceCount === 1 ? "" : "s"}${status === "cached" ? ", cached" : ""})`;
49
+ const toolSuffix = failed
50
+ ? ""
51
+ : metadata === undefined
52
+ ? " (undiscovered)"
53
+ : ` (${toolCount} tool${toolCount === 1 ? "" : "s"}, ${resourceCount} resource${resourceCount === 1 ? "" : "s"}${status === "cached" ? ", cached" : ""})`;
51
54
  lines.push(`${statusIcon} ${name}: ${status}${toolSuffix}`);
52
55
  }
53
56
  if (Object.keys(state.config.mcpServers).length === 0) {
@@ -57,8 +60,9 @@ export async function showStatus(state, ctx) {
57
60
  ctx.ui.notify(lines.join("\n"), "info");
58
61
  }
59
62
  export async function showPrompts(state, ctx) {
60
- if (!ctx.hasUI)
63
+ if (!ctx.hasUI) {
61
64
  return;
65
+ }
62
66
  const allPrompts = [...(state.promptMetadata?.values() ?? [])].flat();
63
67
  const failedPromptServers = [...(state.manager.getAllConnections?.() ?? [])]
64
68
  .filter(([, connection]) => connection.status === "connected" && connection.promptDiscoveryFailed)
@@ -81,10 +85,13 @@ export async function showPrompts(state, ctx) {
81
85
  for (const [serverName, prompts] of [...grouped.entries()].sort(([a], [b]) => a.localeCompare(b))) {
82
86
  lines.push(`${serverName}:`);
83
87
  for (const prompt of prompts.sort((a, b) => a.commandName.localeCompare(b.commandName))) {
84
- const args = prompt.arguments.map(argument => argument.required ? `<${argument.name}>` : `[${argument.name}]`).join(" ");
88
+ const args = prompt.arguments
89
+ .map((argument) => (argument.required ? `<${argument.name}>` : `[${argument.name}]`))
90
+ .join(" ");
85
91
  lines.push(` /${prompt.commandName}${args ? ` ${args}` : ""}`);
86
- if (prompt.description)
92
+ if (prompt.description) {
87
93
  lines.push(` ${prompt.description}`);
94
+ }
88
95
  }
89
96
  lines.push("");
90
97
  }
@@ -95,11 +102,12 @@ export async function showPrompts(state, ctx) {
95
102
  ctx.ui.notify(lines.join("\n"), "info");
96
103
  }
97
104
  export async function showTools(state, ctx) {
98
- if (!ctx.hasUI)
105
+ if (!ctx.hasUI) {
99
106
  return;
107
+ }
100
108
  const allTools = [...state.toolMetadata.entries()]
101
109
  .filter(([serverName]) => !isServerDisabled(state.config.mcpServers[serverName]))
102
- .flatMap(([, metadata]) => metadata.filter(tool => tool.resourceUri === undefined).map(tool => tool.name));
110
+ .flatMap(([, metadata]) => metadata.filter((tool) => tool.resourceUri === undefined).map((tool) => tool.name));
103
111
  if (allTools.length === 0) {
104
112
  ctx.ui.notify("No MCP tools discovered. Use mcp_search to discover tools.", "info");
105
113
  return;
@@ -107,7 +115,7 @@ export async function showTools(state, ctx) {
107
115
  const lines = [
108
116
  "Discovered MCP Tools:",
109
117
  "",
110
- ...allTools.map(t => ` ${t}`),
118
+ ...allTools.map((t) => ` ${t}`),
111
119
  "",
112
120
  `Total: ${allTools.length} tool${allTools.length === 1 ? "" : "s"}`,
113
121
  ];
@@ -124,14 +132,16 @@ export async function reconnectServer(state, ctx, name, afterAuth = false) {
124
132
  return false;
125
133
  }
126
134
  if (isServerDisabled(definition)) {
127
- if (ui)
135
+ if (ui) {
128
136
  ui.notify(`MCP: ${name} is disabled. Run /mcp enable ${name}, then /reload.`, "warning");
137
+ }
129
138
  return false;
130
139
  }
131
140
  try {
132
141
  const previous = afterAuth ? state.manager.getConnection(name) : undefined;
133
- if (!afterAuth)
142
+ if (!afterAuth) {
134
143
  await state.manager.close(name);
144
+ }
135
145
  state.owner?.throwIfInactive();
136
146
  const connection = previous
137
147
  ? await state.manager.reconnect(name, definition, previous, signal)
@@ -174,8 +184,9 @@ export async function reconnectServer(state, ctx, name, afterAuth = false) {
174
184
  }
175
185
  catch (error) {
176
186
  const message = error instanceof Error ? error.message : String(error);
177
- if (isAbortError(error, signal))
187
+ if (isAbortError(error, signal)) {
178
188
  throw error;
189
+ }
179
190
  recordFailure(state, name, message);
180
191
  if (ui) {
181
192
  ui.notify(`MCP: Failed to reconnect to ${name}: ${sanitizeTerminalText(message)}`, "error");
@@ -192,15 +203,16 @@ export async function reconnectServers(state, ctx, targetServer) {
192
203
  return;
193
204
  }
194
205
  const names = targetServer ? [targetServer] : Object.keys(state.config.mcpServers);
195
- await parallelLimit(names, MAX_CONCURRENT_SERVER_CONNECTS, name => reconnectServer(state, ctx, name));
206
+ await parallelLimit(names, MAX_CONCURRENT_SERVER_CONNECTS, (name) => reconnectServer(state, ctx, name));
196
207
  updateStatusBar(state);
197
208
  }
198
209
  export async function authenticateServer(serverName, config, ctx, signal, runtime) {
199
210
  const ui = ctx.hasUI ? ctx.ui : undefined;
200
211
  const cwd = ctx.cwd;
201
212
  signal ??= ctx.signal;
202
- if (!ui)
213
+ if (!ui) {
203
214
  return { ok: false, message: "OAuth authentication requires an interactive session." };
215
+ }
204
216
  const definition = config.mcpServers[serverName];
205
217
  if (!definition) {
206
218
  const message = `Server "${serverName}" not found in config`;
@@ -237,8 +249,9 @@ export async function authenticateServer(serverName, config, ctx, signal, runtim
237
249
  ...(signal ? { signal } : {}),
238
250
  ...(runtime ? { runtime } : {}),
239
251
  });
240
- if (signal?.aborted)
252
+ if (signal?.aborted) {
241
253
  signal.throwIfAborted();
254
+ }
242
255
  if (status === "authenticated") {
243
256
  const message = `OAuth authentication successful for "${serverName}".`;
244
257
  ui.notify(message, "info");
@@ -249,15 +262,17 @@ export async function authenticateServer(serverName, config, ctx, signal, runtim
249
262
  return { ok: false, message };
250
263
  }
251
264
  catch (error) {
252
- if (signal?.aborted)
265
+ if (signal?.aborted) {
253
266
  throw error;
267
+ }
254
268
  const message = error instanceof Error ? error.message : String(error);
255
269
  ui.notify(`Failed to authenticate "${serverName}": ${message}`, "error");
256
270
  return { ok: false, message };
257
271
  }
258
272
  finally {
259
- if (!signal?.aborted)
273
+ if (!signal?.aborted) {
260
274
  ui.setStatus("mcp-auth", undefined);
275
+ }
261
276
  }
262
277
  }
263
278
  export async function logoutServer(serverName, state, ctx) {
@@ -265,18 +280,24 @@ export async function logoutServer(serverName, state, ctx) {
265
280
  const ui = ctx.hasUI ? ctx.ui : undefined;
266
281
  if (!definition) {
267
282
  const message = `Server "${serverName}" not found in config`;
268
- if (ui)
283
+ if (ui) {
269
284
  ui.notify(message, "error");
285
+ }
270
286
  return { ok: false, message };
271
287
  }
272
288
  const signal = state.owner?.signal;
273
289
  try {
274
290
  const { removeAuth } = await import("./mcp-auth-flow.js");
275
- await removeAuth(serverName, { authStorageOptions: state.authStorageOptions, signal, runtime: state.oauthRuntime });
291
+ await removeAuth(serverName, {
292
+ authStorageOptions: state.authStorageOptions,
293
+ signal,
294
+ runtime: state.oauthRuntime,
295
+ });
276
296
  }
277
297
  catch (error) {
278
- if (isAbortError(error, signal))
298
+ if (isAbortError(error, signal)) {
279
299
  throw error;
300
+ }
280
301
  const message = error instanceof Error ? error.message : String(error);
281
302
  if (ui) {
282
303
  ui.notify(`Failed to clear OAuth credentials for "${serverName}": ${sanitizeTerminalText(message)}`, "error");
@@ -288,8 +309,9 @@ export async function logoutServer(serverName, state, ctx) {
288
309
  await state.manager.close(serverName);
289
310
  }
290
311
  catch (error) {
291
- if (isAbortError(error, signal))
312
+ if (isAbortError(error, signal)) {
292
313
  throw error;
314
+ }
293
315
  const message = error instanceof Error ? error.message : String(error);
294
316
  if (ui) {
295
317
  ui.notify(`OAuth credentials were cleared for "${serverName}", but its connection could not be closed: ${sanitizeTerminalText(message)}`, "error");
@@ -299,15 +321,17 @@ export async function logoutServer(serverName, state, ctx) {
299
321
  state.owner?.throwIfInactive();
300
322
  updateStatusBar(state);
301
323
  const message = `OAuth credentials cleared for "${serverName}". Run /mcp-auth ${serverName} to authenticate again.`;
302
- if (ui)
324
+ if (ui) {
303
325
  ui.notify(message, "info");
326
+ }
304
327
  return { ok: true, message };
305
328
  }
306
329
  function buildSharedConfigNoticeLines(configOverridePath, cwd) {
307
330
  const discovery = getMcpDiscoverySummary(configOverridePath, cwd);
308
331
  const onboardingState = loadOnboardingState();
309
- if (!discovery.hasSharedServers || onboardingState.sharedConfigHintShown)
332
+ if (!discovery.hasSharedServers || onboardingState.sharedConfigHintShown) {
310
333
  return [];
334
+ }
311
335
  const sharedSources = discovery.sources.filter((source) => source.kind === "shared" && source.serverCount > 0);
312
336
  const sourceList = sharedSources.map((source) => source.path).join(", ");
313
337
  return [
@@ -316,8 +340,9 @@ function buildSharedConfigNoticeLines(configOverridePath, cwd) {
316
340
  ];
317
341
  }
318
342
  export async function openMcpSetup(state, pi, ctx, configOverridePath, mode = "setup") {
319
- if (!ctx.hasUI)
343
+ if (!ctx.hasUI) {
320
344
  return { configChanged: false };
345
+ }
321
346
  if (!ctx.isProjectTrusted()) {
322
347
  ctx.ui.notify("Project MCP setup is unavailable until this project is trusted.", "warning");
323
348
  return { configChanged: false };
@@ -338,15 +363,17 @@ export async function openMcpSetup(state, pi, ctx, configOverridePath, mode = "s
338
363
  previewStarterProject: () => previewStarterProjectConfig(ctx.cwd),
339
364
  previewRepoPrompt: () => {
340
365
  const repoPrompt = getMcpDiscoverySummary(configOverridePath, ctx.cwd).repoPrompt;
341
- if (!repoPrompt.entry || !repoPrompt.targetPath || !repoPrompt.serverName)
366
+ if (!repoPrompt.entry || !repoPrompt.targetPath || !repoPrompt.serverName) {
342
367
  return null;
368
+ }
343
369
  return previewSharedServerEntry(repoPrompt.targetPath, repoPrompt.serverName, repoPrompt.entry);
344
370
  },
345
371
  previewKnownServer: (preset) => previewSharedServerEntry(getProjectConfigPath(ctx.cwd), preset.id, preset.entry),
346
372
  adoptImports: async (imports) => {
347
373
  const result = ensureCompatibilityImports(imports, configOverridePath);
348
- if (result.added.length > 0)
374
+ if (result.added.length > 0) {
349
375
  configChanged = true;
376
+ }
350
377
  return result;
351
378
  },
352
379
  scaffoldProjectConfig: async () => {
@@ -397,8 +424,9 @@ function buildMcpPanelCallbacks(state, config, ctx) {
397
424
  getConnectionStatus: (serverName) => {
398
425
  authStatusFailures.delete(serverName);
399
426
  const definition = config.mcpServers[serverName];
400
- if (isServerDisabled(definition))
427
+ if (isServerDisabled(definition)) {
401
428
  return "disabled";
429
+ }
402
430
  const connection = state.manager.getConnection(serverName);
403
431
  let serverUrl;
404
432
  try {
@@ -407,10 +435,10 @@ function buildMcpPanelCallbacks(state, config, ctx) {
407
435
  catch {
408
436
  return "failed";
409
437
  }
410
- if (definition?.auth === "oauth"
411
- && serverUrl
412
- && definition.oauth !== false
413
- && !isNonInteractiveOAuth(definition.oauth)) {
438
+ if (definition?.auth === "oauth" &&
439
+ serverUrl &&
440
+ definition.oauth !== false &&
441
+ !isNonInteractiveOAuth(definition.oauth)) {
414
442
  const authStatus = inspectAuthForUrl(serverName, serverUrl, state.authStorageOptions);
415
443
  if (authStatus.status === "unavailable") {
416
444
  authStatusFailures.set(serverName, authStatus.message);
@@ -420,12 +448,15 @@ function buildMcpPanelCallbacks(state, config, ctx) {
420
448
  return "needs-auth";
421
449
  }
422
450
  }
423
- if (connection?.status === "needs-auth")
451
+ if (connection?.status === "needs-auth") {
424
452
  return "needs-auth";
425
- if (connection?.status === "connected")
453
+ }
454
+ if (connection?.status === "connected") {
426
455
  return "connected";
427
- if (getFailureAgeSeconds(state, serverName) !== null)
456
+ }
457
+ if (getFailureAgeSeconds(state, serverName) !== null) {
428
458
  return "failed";
459
+ }
429
460
  return "idle";
430
461
  },
431
462
  getFailureMessage: (serverName) => authStatusFailures.get(serverName) ?? getFailureMessage(state, serverName),
@@ -437,8 +468,9 @@ function buildMcpPanelCallbacks(state, config, ctx) {
437
468
  }
438
469
  export async function openMcpPanel(state, pi, ctx, configOverridePath, onDirectToolsConfigChanged) {
439
470
  if (!ctx.isProjectTrusted() || ctx.mode !== "tui") {
440
- if (ctx.hasUI)
471
+ if (ctx.hasUI) {
441
472
  await showStatus(state, ctx);
473
+ }
442
474
  return { configChanged: false };
443
475
  }
444
476
  if (state.programmaticConfig) {
@@ -479,13 +511,15 @@ export async function openMcpPanel(state, pi, ctx, configOverridePath, onDirectT
479
511
  }, { noticeLines, keybindings });
480
512
  }, { overlay: true, overlayOptions: { anchor: "center", width: 82 } });
481
513
  });
482
- if (noticeLines.length > 0)
514
+ if (noticeLines.length > 0) {
483
515
  markSharedConfigHintShown();
516
+ }
484
517
  return { configChanged };
485
518
  }
486
519
  export async function openMcpAuthPanel(state, ctx, configOverridePath) {
487
- if (!ctx.hasUI)
520
+ if (!ctx.hasUI) {
488
521
  return { configChanged: false };
522
+ }
489
523
  if (!ctx.isProjectTrusted()) {
490
524
  ctx.ui.notify("Use /mcp-auth <server> while this project is untrusted.", "info");
491
525
  return { configChanged: false };