pi-mcp-adapter 3.3.0 → 5.0.0

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 (77) hide show
  1. package/CHANGELOG.md +95 -1
  2. package/README.md +78 -974
  3. package/cli.js +210 -0
  4. package/commands.ts +55 -4
  5. package/config.ts +308 -33
  6. package/direct-tool-surface.ts +9 -7
  7. package/direct-tools.ts +43 -29
  8. package/dist/config.d.ts +15 -4
  9. package/dist/config.js +317 -30
  10. package/dist/config.js.map +1 -1
  11. package/dist/elicitation-handler.d.ts +4 -0
  12. package/dist/elicitation-handler.js +14 -1
  13. package/dist/elicitation-handler.js.map +1 -1
  14. package/dist/mcp-auth-flow.d.ts +2 -0
  15. package/dist/mcp-auth-flow.js +12 -3
  16. package/dist/mcp-auth-flow.js.map +1 -1
  17. package/dist/mcp-auth.js +3 -1
  18. package/dist/mcp-auth.js.map +1 -1
  19. package/dist/metadata-cache.d.ts +15 -1
  20. package/dist/metadata-cache.js +76 -8
  21. package/dist/metadata-cache.js.map +1 -1
  22. package/dist/output-shape.d.ts +18 -5
  23. package/dist/output-shape.js +162 -18
  24. package/dist/output-shape.js.map +1 -1
  25. package/dist/package-mcp-loader.js +9 -5
  26. package/dist/package-mcp-loader.js.map +1 -1
  27. package/dist/project-server-trust.d.ts +17 -0
  28. package/dist/project-server-trust.js +209 -0
  29. package/dist/project-server-trust.js.map +1 -0
  30. package/dist/server-manager.d.ts +5 -0
  31. package/dist/server-manager.js +98 -26
  32. package/dist/server-manager.js.map +1 -1
  33. package/dist/session-recovery.js +4 -3
  34. package/dist/session-recovery.js.map +1 -1
  35. package/dist/state.d.ts +2 -0
  36. package/dist/types.d.ts +29 -2
  37. package/dist/types.js.map +1 -1
  38. package/dist/utils.d.ts +5 -1
  39. package/dist/utils.js +16 -0
  40. package/dist/utils.js.map +1 -1
  41. package/{OAUTH.md → docs/auth.md} +66 -11
  42. package/docs/configuration.md +221 -0
  43. package/docs/extension-api.md +246 -0
  44. package/docs/pi-builtin-comparison.md +113 -0
  45. package/docs/prompts-and-ui.md +84 -0
  46. package/docs/scripting.md +131 -0
  47. package/docs/servers.md +142 -0
  48. package/docs/tools.md +259 -0
  49. package/elicitation-handler.ts +13 -1
  50. package/index.ts +268 -37
  51. package/init.ts +97 -46
  52. package/mcp-auth-flow.ts +13 -2
  53. package/mcp-auth.ts +3 -1
  54. package/mcp-code.ts +125 -77
  55. package/mcp-output-guard.ts +24 -5
  56. package/mcp-panel.ts +52 -4
  57. package/mcp-script-worker.mjs +6 -2
  58. package/mcp-setup-panel.ts +587 -303
  59. package/metadata-cache.ts +79 -8
  60. package/onboarding-state.ts +20 -0
  61. package/output-shape.ts +161 -22
  62. package/package-mcp-loader.ts +8 -5
  63. package/package.json +8 -6
  64. package/pi-builtin-mcp.ts +32 -0
  65. package/pi-signin-import.ts +147 -0
  66. package/project-server-trust.ts +8 -4
  67. package/prompts.ts +11 -3
  68. package/proxy-modes.ts +163 -138
  69. package/search-ranking.ts +12 -3
  70. package/server-manager.ts +101 -24
  71. package/session-recovery.ts +4 -3
  72. package/skills/mcp-scripting/SKILL.md +24 -30
  73. package/skills/mcp-scripting/references/jev.md +3 -0
  74. package/state.ts +2 -0
  75. package/types.ts +18 -2
  76. package/ui-server.ts +34 -32
  77. package/utils.ts +16 -1
package/direct-tools.ts CHANGED
@@ -8,7 +8,7 @@ import { describeFailure } from "./failure-backoff.ts";
8
8
  import { abortable, throwIfAborted } from "./abort.ts";
9
9
  import { formatSchema } from "./tool-metadata.ts";
10
10
  import { resolveMcpResultContent, transformMcpResourceContents } from "./tool-registrar.ts";
11
- import { guardMcpOutput, guardedMcpDetails, resolveMcpOutputGuardOptions } from "./mcp-output-guard.ts";
11
+ import { guardMcpOutput, guardedMcpDetails, resolveMcpOutputGuardOptions, scriptPipeHint } from "./mcp-output-guard.ts";
12
12
  import { maybeStartUiSession, summarizeUiSessionResult, type UiSessionRuntime } from "./ui-session.ts";
13
13
  import { isServerDisabled } from "./types.ts";
14
14
  import { authenticate, supportsOAuth } from "./mcp-auth-flow.ts";
@@ -17,8 +17,8 @@ import { SessionRecoveryAuthRequiredError, withSessionRecovery } from "./session
17
17
  import { combineAbortSignals, isAbortError } from "./runtime-owner.ts";
18
18
  import { callToolViaTaskSession } from "./mcp-tasks.ts";
19
19
  import { callToolPausingForElicitation } from "./elicitation-handler.ts";
20
- import { recordObservedOutput } from "./output-shape.ts";
21
- import { ensureToolCallApproved } from "./tool-approval.ts";
20
+ import { observedOutputRecorder } from "./output-shape.ts";
21
+ import { ensureToolCallApproved, type ToolCallApprovalResult } from "./tool-approval.ts";
22
22
  import { getInputRequiredNeedsUiDetails } from "./errors.ts";
23
23
 
24
24
  type ClientCallToolResult = Awaited<ReturnType<Client["callTool"]>>;
@@ -129,8 +129,13 @@ type DirectToolExecute = (
129
129
  export function createDirectToolExecutor(
130
130
  getState: () => McpExtensionState | null,
131
131
  getInitPromise: () => Promise<McpExtensionState> | null,
132
- spec: DirectToolSpec
132
+ spec: DirectToolSpec,
133
+ // Deferred tools (Pi 0.99+) keep the server's structuredContent.
134
+ structured = false,
133
135
  ): DirectToolExecute {
136
+ const structuredContentOf = (result: ClientCallToolResult) => structured && result.structuredContent !== undefined
137
+ ? { structuredContent: result.structuredContent as NonNullable<AgentToolResult["structuredContent"]> }
138
+ : {};
134
139
  return async function execute(toolCallId, params, signal) {
135
140
  throwIfAborted(signal);
136
141
  let state = getState();
@@ -213,30 +218,6 @@ export function createDirectToolExecutor(
213
218
  const annotations = spec.resourceUri
214
219
  ? undefined
215
220
  : state.toolMetadata.get(spec.serverName)?.find(tool => !tool.resourceUri && tool.originalName === spec.originalName)?.annotations;
216
- const approval = await ensureToolCallApproved(state, spec.serverName, {
217
- name: spec.prefixedName,
218
- originalName: spec.originalName,
219
- description: spec.description,
220
- ...(spec.inputSchema !== undefined ? { inputSchema: spec.inputSchema } : {}),
221
- ...(spec.resourceUri !== undefined ? { resourceUri: spec.resourceUri } : {}),
222
- ...(spec.uiResourceUri !== undefined ? { uiResourceUri: spec.uiResourceUri } : {}),
223
- ...(spec.uiStreamMode !== undefined ? { uiStreamMode: spec.uiStreamMode } : {}),
224
- ...(annotations !== undefined ? { annotations } : {}),
225
- }, normalizedParams, ownedSignal, spec.resourceUri ? "resource" : "direct");
226
- if (approval.ok === false) {
227
- const denied = approval.reason === "denied";
228
- const message = denied
229
- ? `The user declined approval to run MCP tool "${spec.originalName}" on server "${spec.serverName}".`
230
- : `MCP tool "${spec.originalName}" on server "${spec.serverName}" is approval-gated and requires an interactive session.`;
231
- return {
232
- content: [{ type: "text" as const, text: message }],
233
- details: {
234
- error: denied ? "approval_denied" : "approval_required",
235
- server: spec.serverName,
236
- tool: spec.originalName,
237
- },
238
- };
239
- }
240
221
 
241
222
  let uiSession: UiSessionRuntime | null = null;
242
223
  const requestOptions = state.manager.getRequestOptions?.(spec.serverName, ownedSignal) ?? (ownedSignal ? { signal: ownedSignal } : undefined);
@@ -266,9 +247,35 @@ export function createDirectToolExecutor(
266
247
  return state.manager.getConnection(spec.serverName);
267
248
  };
268
249
 
250
+ let approval: ToolCallApprovalResult | undefined;
269
251
  try {
252
+ // In flight from here so an idle check cannot close the server while the approval dialog is open.
270
253
  state.manager.touch(spec.serverName);
271
254
  state.manager.incrementInFlight(spec.serverName);
255
+ approval = await ensureToolCallApproved(state, spec.serverName, {
256
+ name: spec.prefixedName,
257
+ originalName: spec.originalName,
258
+ description: spec.description,
259
+ ...(spec.inputSchema !== undefined ? { inputSchema: spec.inputSchema } : {}),
260
+ ...(spec.resourceUri !== undefined ? { resourceUri: spec.resourceUri } : {}),
261
+ ...(spec.uiResourceUri !== undefined ? { uiResourceUri: spec.uiResourceUri } : {}),
262
+ ...(spec.uiStreamMode !== undefined ? { uiStreamMode: spec.uiStreamMode } : {}),
263
+ ...(annotations !== undefined ? { annotations } : {}),
264
+ }, normalizedParams, ownedSignal, spec.resourceUri ? "resource" : "direct");
265
+ if (approval.ok === false) {
266
+ const denied = approval.reason === "denied";
267
+ const message = denied
268
+ ? `The user declined approval to run MCP tool "${spec.originalName}" on server "${spec.serverName}".`
269
+ : `MCP tool "${spec.originalName}" on server "${spec.serverName}" is approval-gated and requires an interactive session.`;
270
+ return {
271
+ content: [{ type: "text" as const, text: message }],
272
+ details: {
273
+ error: denied ? "approval_denied" : "approval_required",
274
+ server: spec.serverName,
275
+ tool: spec.originalName,
276
+ },
277
+ };
278
+ }
272
279
 
273
280
  if (spec.resourceUri) {
274
281
  const result = await withSessionRecovery<ClientReadResourceResult>(
@@ -312,6 +319,7 @@ export function createDirectToolExecutor(
312
319
  : null;
313
320
 
314
321
  const requestMeta = withToolCallIdMeta(uiSession?.requestMeta, toolCallId);
322
+ const recordOutput = observedOutputRecorder(state, spec.serverName, spec.originalName);
315
323
  const result = await withSessionRecovery<ClientCallToolResult>(
316
324
  {
317
325
  manager: state.manager,
@@ -339,7 +347,7 @@ export function createDirectToolExecutor(
339
347
  },
340
348
  );
341
349
  uiSession?.sendToolResult(result as unknown as import("@modelcontextprotocol/client").CallToolResult);
342
- if (!result.isError) recordObservedOutput(state, spec.serverName, spec.originalName, result as Record<string, unknown>);
350
+ if (!result.isError) recordOutput(result as Record<string, unknown>);
343
351
 
344
352
  if (result.isError) {
345
353
  const content = resolveMcpResultContent(result as Record<string, unknown>, state.owner?.signal);
@@ -353,6 +361,7 @@ export function createDirectToolExecutor(
353
361
  return {
354
362
  content: guarded.content,
355
363
  details: { error: "tool_error", server: spec.serverName, ...guardedMcpDetails(guarded) },
364
+ ...structuredContentOf(result),
356
365
  };
357
366
  }
358
367
 
@@ -375,18 +384,23 @@ export function createDirectToolExecutor(
375
384
  uiUrl: uiSummary.uiUrl,
376
385
  ...guardedMcpDetails(guarded),
377
386
  },
387
+ ...structuredContentOf(result),
378
388
  };
379
389
  }
380
390
 
381
391
  const guarded = await guardMcpOutput(outputContent, {
382
392
  ...outputGuardOptions,
393
+ ...scriptPipeHint(state.scriptTool, outputContent),
383
394
  ...(state.config.settings?.directToolResultDetails === "bounded" ? { rawMcpResult: result } : {}),
384
395
  });
385
396
  return {
386
397
  content: guarded.content,
387
398
  details: { server: spec.serverName, tool: spec.originalName, ...guardedMcpDetails(guarded) },
399
+ ...structuredContentOf(result),
388
400
  };
389
401
  } catch (error) {
402
+ // Approval errors, such as an abort while the dialog is open, propagate unchanged.
403
+ if (!approval) throw error;
390
404
  if (error instanceof SessionRecoveryAuthRequiredError) {
391
405
  const message = error.authMessage ?? getDirectAuthRequiredMessage(state, spec.serverName);
392
406
  uiSession?.sendToolCancelled(message);
package/dist/config.d.ts CHANGED
@@ -5,10 +5,15 @@ export interface KnownServerPreset {
5
5
  name: string;
6
6
  summary: string;
7
7
  entry: ServerEntry;
8
+ /** Offered only when one of these app paths exists; the local server is probed after adding. */
9
+ desktopApp?: {
10
+ paths: readonly string[];
11
+ enableSteps: string;
12
+ };
8
13
  }
9
14
  export declare const KNOWN_SERVER_PRESETS: readonly KnownServerPreset[];
10
15
  interface ConfigSourceSpec {
11
- id: "shared-global" | "agents-global" | "agents-nested-global" | "pi-global" | "shared-project-ancestor" | "pi-project-ancestor" | "shared-project" | "pi-project";
16
+ id: "shared-global" | "agents-global" | "agents-nested-global" | "pi-mcp-global" | "pi-global" | "shared-project-ancestor" | "pi-project-ancestor" | "shared-project" | "pi-mcp-project" | "pi-project";
12
17
  label: string;
13
18
  readPath: string;
14
19
  writePath: string;
@@ -71,6 +76,7 @@ export interface McpDiscoverySummary {
71
76
  totalServerCount: number;
72
77
  fingerprint: string;
73
78
  repoPrompt: RepoPromptDiscovery;
79
+ knownServerPresets: readonly KnownServerPreset[];
74
80
  }
75
81
  export interface McpStandardConfigSummary {
76
82
  sources: ConfigDiscoverySource[];
@@ -87,12 +93,12 @@ export interface ConfigWritePreview {
87
93
  }
88
94
  export type SharedConfigTarget = "project" | "global";
89
95
  export declare function getPiGlobalConfigPath(overridePath?: string): string;
90
- export declare function getLegacyPiMcpGlobalConfigPath(): string;
96
+ export declare function getPiMcpGlobalConfigPath(): string;
91
97
  export declare function getGenericGlobalConfigPath(): string;
92
98
  export declare function getProjectConfigPath(cwd?: string): string;
93
99
  export declare function getProjectPiConfigPath(cwd?: string): string;
94
- export declare function getLegacyProjectPiMcpConfigPath(cwd?: string): string;
95
- export declare function getLegacyMcpMigrationNotices(cwd?: string, overridePath?: string, piOwnsServers?: boolean): string[];
100
+ export declare function getProjectPiMcpConfigPath(cwd?: string): string;
101
+ export declare function getLegacyMcpMigrationNotices(cwd?: string, overridePath?: string): string[];
96
102
  export declare function getSharedConfigPath(target: SharedConfigTarget, cwd?: string): string;
97
103
  export declare function getConfigDiscoveryPaths(overridePath?: string, cwd?: string): ConfigDiscoveryPath[];
98
104
  export declare function findAvailableImportConfigs(cwd?: string): DiscoveredImportConfig[];
@@ -114,6 +120,11 @@ export declare function loadMcpConfig(overridePath?: string, cwd?: string): McpC
114
120
  export declare function loadMcpConfigWithSources(overridePath?: string, cwd?: string): LoadedMcpConfig;
115
121
  export declare function resolveConfiguredClaudePluginMcp(config: McpConfig, cwd?: string): McpConfig;
116
122
  export declare function discoverConfiguredClaudePluginSkills(config: McpConfig, cwd?: string): string[];
123
+ /** Returns why the entry is skipped when Pi would reject it or the adapter can't run it. */
124
+ export declare function translatePiMcpServer(name: string, value: unknown): {
125
+ entry: ServerEntry;
126
+ ignored: string[];
127
+ } | string;
117
128
  export declare function writeSharedConfigText(filePath: string, text: string): void;
118
129
  export declare function writeJevSemanticSearchConfig(overridePath: string | undefined, cwd: string, allowedServers: string[], effectiveJev?: unknown): {
119
130
  path: string;