@fitchmultz/pi-mcp-adapter 5.0.0 → 6.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 (73) hide show
  1. package/CHANGELOG.md +25 -1
  2. package/OAUTH.md +3 -3
  3. package/README.md +153 -85
  4. package/app-bridge.bundle.js +75 -33
  5. package/commands.ts +9 -6
  6. package/direct-tools.ts +25 -358
  7. package/dist/app-bridge.bundle.js +75 -33
  8. package/dist/commands.js +9 -6
  9. package/dist/direct-tools.d.ts +2 -4
  10. package/dist/direct-tools.js +21 -319
  11. package/dist/gateway-arguments.d.ts +33 -0
  12. package/dist/gateway-arguments.js +63 -0
  13. package/dist/index.js +181 -276
  14. package/dist/init.js +6 -20
  15. package/dist/mcp-code.d.ts +2 -1
  16. package/dist/mcp-code.js +87 -41
  17. package/dist/mcp-output-guard.d.ts +57 -2
  18. package/dist/mcp-output-guard.js +129 -24
  19. package/dist/mcp-panel.d.ts +1 -1
  20. package/dist/mcp-panel.js +34 -97
  21. package/dist/mcp-script-worker.mjs +5 -2
  22. package/dist/mcp-status.js +2 -1
  23. package/dist/metadata-cache.d.ts +1 -1
  24. package/dist/metadata-cache.js +25 -88
  25. package/dist/prompts.d.ts +1 -1
  26. package/dist/prompts.js +1 -1
  27. package/dist/proxy-modes.d.ts +40 -6
  28. package/dist/proxy-modes.js +270 -485
  29. package/dist/resource-tools.d.ts +15 -0
  30. package/dist/resource-tools.js +26 -1
  31. package/dist/sampling-handler.js +4 -1
  32. package/dist/search-ranking.d.ts +0 -2
  33. package/dist/search-ranking.js +66 -137
  34. package/dist/state.d.ts +1 -1
  35. package/dist/tool-loader.d.ts +32 -0
  36. package/dist/tool-loader.js +152 -0
  37. package/dist/tool-metadata.d.ts +67 -0
  38. package/dist/tool-metadata.js +23 -17
  39. package/dist/tool-registrar.d.ts +4 -4
  40. package/dist/tool-registrar.js +18 -13
  41. package/dist/tool-result-renderer.d.ts +3 -0
  42. package/dist/tool-result-renderer.js +8 -3
  43. package/dist/types.d.ts +11 -36
  44. package/dist/utils.js +2 -4
  45. package/gateway-arguments.ts +73 -0
  46. package/index.ts +166 -299
  47. package/init.ts +6 -20
  48. package/mcp-code.ts +71 -45
  49. package/mcp-output-guard.ts +145 -27
  50. package/mcp-panel.ts +38 -109
  51. package/mcp-script-worker.mjs +5 -2
  52. package/mcp-status.ts +2 -1
  53. package/metadata-cache.ts +31 -93
  54. package/package.json +14 -9
  55. package/prompts.ts +1 -1
  56. package/proxy-modes.ts +265 -517
  57. package/resource-tools.ts +36 -1
  58. package/sampling-handler.ts +4 -1
  59. package/scripts/build-app-bridge.mjs +15 -0
  60. package/scripts/build.mjs +3 -2
  61. package/scripts/prepare.mjs +1 -0
  62. package/search-ranking.ts +71 -135
  63. package/skills/mcp-scripting/SKILL.md +67 -20
  64. package/state.ts +1 -1
  65. package/tool-loader.ts +153 -0
  66. package/tool-metadata.ts +22 -17
  67. package/tool-registrar.ts +18 -13
  68. package/tool-result-renderer.ts +10 -2
  69. package/types.ts +15 -40
  70. package/utils.ts +2 -4
  71. package/dist/ts-shape.d.ts +0 -2
  72. package/dist/ts-shape.js +0 -147
  73. package/ts-shape.ts +0 -145
package/dist/mcp-panel.js CHANGED
@@ -1,9 +1,8 @@
1
1
  import { matchesKey, truncateToWidth, visibleWidth } from "@earendil-works/pi-tui";
2
2
  import { copyToClipboard } from "@earendil-works/pi-coding-agent";
3
3
  import { createPanelKeys } from "./panel-keys.js";
4
- import { isServerDisabled, isToolAllowed } from "./types.js";
5
- import { resourceNameToToolName } from "./resource-tools.js";
6
4
  import { sanitizeTerminalText, stripOscSequences } from "./utils.js";
5
+ import { reconstructToolMetadata } from "./metadata-cache.js";
7
6
  const DEFAULT_THEME = {
8
7
  border: "2",
9
8
  title: "2",
@@ -138,57 +137,24 @@ class McpPanel {
138
137
  else if (globalDirect) {
139
138
  toolFilter = globalDirect;
140
139
  }
141
- const tools = [];
142
- if (serverCache && !this.authOnly && !isServerDisabled(definition)) {
143
- for (const tool of serverCache.tools ?? []) {
144
- if (!isToolAllowed(tool.name, serverName, this.prefix, definition.includeTools, definition.excludeTools)) {
145
- continue;
146
- }
147
- const isDirect = toolFilter === true || (Array.isArray(toolFilter) && toolFilter.includes(tool.name));
148
- tools.push({
149
- name: tool.name,
150
- description: tool.description ?? "",
151
- isDirect,
152
- wasDirect: isDirect,
153
- estimatedTokens: estimateTokens(tool),
154
- });
155
- }
156
- if (definition.exposeResources !== false) {
157
- for (const resource of serverCache.resources ?? []) {
158
- const baseName = `read_${resourceNameToToolName(resource.name)}`;
159
- if (!isToolAllowed(baseName, serverName, this.prefix, definition.includeTools, definition.excludeTools)) {
160
- continue;
161
- }
162
- const isDirect = toolFilter === true || (Array.isArray(toolFilter) && toolFilter.includes(baseName));
163
- const ct = {
164
- name: baseName,
165
- ...(resource.description !== undefined ? { description: resource.description } : {}),
166
- };
167
- tools.push({
168
- name: baseName,
169
- description: resource.description ?? `Read resource: ${resource.uri}`,
170
- isDirect,
171
- wasDirect: isDirect,
172
- estimatedTokens: estimateTokens(ct),
173
- });
174
- }
175
- }
176
- }
177
140
  const status = callbacks.getConnectionStatus(serverName);
178
141
  const failureMessage = callbacks.getFailureMessage?.(serverName) ?? null;
179
- this.servers.push({
142
+ const server = {
180
143
  name: serverName,
181
144
  expanded: false,
182
145
  source: prov?.kind ?? "user",
183
146
  ...(prov?.importKind !== undefined ? { importKind: prov.importKind } : {}),
184
- ...(definition.includeTools !== undefined ? { includeTools: definition.includeTools } : {}),
185
- ...(definition.excludeTools !== undefined ? { excludeTools: definition.excludeTools } : {}),
186
- exposeResources: definition.exposeResources !== false,
147
+ definition,
148
+ toolFilter,
149
+ resourceCount: 0,
187
150
  connectionStatus: status,
188
151
  failureMessage,
189
- tools,
152
+ tools: [],
190
153
  hasCachedData: !!serverCache,
191
- });
154
+ };
155
+ this.servers.push(server);
156
+ if (serverCache && !this.authOnly)
157
+ this.rebuildServerTools(server, serverCache);
192
158
  }
193
159
  this.rebuildVisibleItems();
194
160
  this.resetInactivityTimeout();
@@ -503,7 +469,6 @@ class McpPanel {
503
469
  if (entry) {
504
470
  this.rebuildServerTools(server, entry);
505
471
  }
506
- server.hasCachedData = true;
507
472
  }
508
473
  if (options.afterAuth) {
509
474
  this.authNotice = connected && server.connectionStatus === "connected"
@@ -578,46 +543,22 @@ class McpPanel {
578
543
  this.cursorIndex = Math.max(0, Math.min(this.visibleItems.length - 1, this.cursorIndex + delta));
579
544
  }
580
545
  rebuildServerTools(server, entry) {
581
- const existingState = new Map();
582
- for (const t of server.tools)
583
- existingState.set(t.name, t.isDirect);
584
- const newTools = [];
585
- for (const tool of entry.tools ?? []) {
586
- if (!isToolAllowed(tool.name, server.name, this.prefix, server.includeTools, server.excludeTools)) {
587
- continue;
588
- }
589
- const prev = existingState.get(tool.name);
590
- const isDirect = prev !== undefined ? prev : false;
591
- newTools.push({
592
- name: tool.name,
593
- description: tool.description ?? "",
594
- isDirect,
595
- wasDirect: prev !== undefined ? server.tools.find((t) => t.name === tool.name)?.wasDirect ?? false : false,
546
+ const existingState = new Map(server.tools.map(tool => [tool.name, tool]));
547
+ const metadata = reconstructToolMetadata(server.name, entry, this.prefix, server.definition);
548
+ server.resourceCount = metadata.filter(tool => tool.resourceUri !== undefined).length;
549
+ server.tools = metadata.filter(tool => tool.resourceUri === undefined).map(tool => {
550
+ const previous = existingState.get(tool.originalName);
551
+ const pinned = server.toolFilter === true
552
+ || (Array.isArray(server.toolFilter) && server.toolFilter.includes(tool.originalName));
553
+ return {
554
+ name: tool.originalName,
555
+ description: tool.description,
556
+ isDirect: previous?.isDirect ?? pinned,
557
+ wasDirect: previous?.wasDirect ?? pinned,
596
558
  estimatedTokens: estimateTokens(tool),
597
- });
598
- }
599
- if (server.exposeResources) {
600
- for (const resource of entry.resources ?? []) {
601
- const baseName = `read_${resourceNameToToolName(resource.name)}`;
602
- if (!isToolAllowed(baseName, server.name, this.prefix, server.includeTools, server.excludeTools)) {
603
- continue;
604
- }
605
- const prev = existingState.get(baseName);
606
- const isDirect = prev !== undefined ? prev : false;
607
- const ct = {
608
- name: baseName,
609
- ...(resource.description !== undefined ? { description: resource.description } : {}),
610
- };
611
- newTools.push({
612
- name: baseName,
613
- description: resource.description ?? `Read resource: ${resource.uri}`,
614
- isDirect,
615
- wasDirect: prev !== undefined ? server.tools.find((t) => t.name === baseName)?.wasDirect ?? false : false,
616
- estimatedTokens: estimateTokens(ct),
617
- });
618
- }
619
- }
620
- server.tools = newTools;
559
+ };
560
+ });
561
+ server.hasCachedData = true;
621
562
  this.rebuildVisibleItems();
622
563
  this.updateDirty();
623
564
  }
@@ -677,6 +618,9 @@ class McpPanel {
677
618
  continue;
678
619
  if (item.type === "server") {
679
620
  lines.push(row(this.renderServerRow(server, isCursor)));
621
+ if (isCursor && !this.authOnly && server.resourceCount > 0) {
622
+ lines.push(row(fg(t.hint, " Resources: mcp actions resources / read-resource")));
623
+ }
680
624
  if (isCursor && server.connectionStatus === "failed" && server.failureMessage) {
681
625
  for (const line of this.wrapText(sanitizeDisplayText(server.failureMessage), innerW - 6)) {
682
626
  lines.push(row(` ${fg(t.cancel, line)}`));
@@ -722,8 +666,9 @@ class McpPanel {
722
666
  else {
723
667
  const directCount = this.servers.reduce((sum, s) => sum + s.tools.filter((t) => t.isDirect).length, 0);
724
668
  const totalTokens = this.servers.reduce((sum, s) => sum + s.tools.filter((t) => t.isDirect).reduce((ts, t) => ts + t.estimatedTokens, 0), 0);
725
- const stats = directCount > 0 ? `${directCount} direct ~${totalTokens.toLocaleString()} tokens` : "no direct tools";
669
+ const stats = directCount > 0 ? `${directCount} pinned ~${totalTokens.toLocaleString()} tokens` : "no pinned tools";
726
670
  lines.push(row(fg(t.description, stats + (this.dirty ? fg(t.needsAuth, " (unsaved)") : ""))));
671
+ lines.push(row(fg(t.hint, "Pins load at startup; discover more with mcp_search.")));
727
672
  }
728
673
  }
729
674
  lines.push(emptyRow());
@@ -737,7 +682,7 @@ class McpPanel {
737
682
  ]
738
683
  : [
739
684
  italic("↑↓") + " navigate",
740
- italic("space") + " toggle",
685
+ italic("space") + " pin/unpin",
741
686
  italic("⏎") + " expand/auth",
742
687
  italic("ctrl+a") + " auth",
743
688
  italic("ctrl+r") + " reconnect",
@@ -781,7 +726,7 @@ class McpPanel {
781
726
  const importLabel = server.source === "import" ? fg(t.description, ` (${importKind})`) : "";
782
727
  const statusLabel = this.renderConnectionStatus(server);
783
728
  if (!server.hasCachedData && !this.authOnly) {
784
- return `${prefix} ${nameStr}${importLabel} ${fg(t.description, "(not cached)")}${statusLabel}`;
729
+ return `${prefix} ${nameStr}${importLabel}${statusLabel} ${fg(t.description, "(undiscovered)")}`;
785
730
  }
786
731
  const directCount = server.tools.filter((t) => t.isDirect).length;
787
732
  const totalCount = server.tools.length;
@@ -792,16 +737,8 @@ class McpPanel {
792
737
  else if (directCount > 0) {
793
738
  toggleIcon = fg(t.needsAuth, "◐");
794
739
  }
795
- let toolInfo = "";
796
- if (totalCount > 0) {
797
- toolInfo = `${directCount}/${totalCount}`;
798
- if (directCount > 0) {
799
- const tokens = server.tools.filter((t) => t.isDirect).reduce((s, t) => s + t.estimatedTokens, 0);
800
- toolInfo += ` ~${tokens.toLocaleString()}`;
801
- }
802
- toolInfo = fg(t.description, toolInfo);
803
- }
804
- return `${prefix} ${toggleIcon} ${nameStr}${importLabel} ${toolInfo}${statusLabel}`;
740
+ const toolInfo = this.authOnly ? "" : fg(t.description, `${totalCount} tool${totalCount === 1 ? "" : "s"}, ${directCount} pinned${server.resourceCount > 0 ? `, ${server.resourceCount} resource${server.resourceCount === 1 ? "" : "s"}` : ""}`);
741
+ return `${prefix} ${toggleIcon} ${nameStr}${importLabel}${statusLabel} ${toolInfo}`;
805
742
  }
806
743
  selectedServerHasFailureMessage() {
807
744
  const item = this.visibleItems[this.cursorIndex];
@@ -69,7 +69,7 @@ const tools = new Proxy(Object.create(null), {
69
69
  return async (input) => request("search", { input });
70
70
  }
71
71
  if (property === "call") {
72
- return async (path, args) => {
72
+ return async (path, args, server) => {
73
73
  // Invalid paths never reach dispatch and therefore never appear in the call trace.
74
74
  if (typeof path !== "string" || path.trim() === "") {
75
75
  return {
@@ -80,9 +80,12 @@ const tools = new Proxy(Object.create(null), {
80
80
  },
81
81
  };
82
82
  }
83
- return request("call", { path, args });
83
+ if (server !== undefined && (typeof server !== "string" || !server)) return { ok: false, error: { code: "invalid_arguments", message: "server must be a non-empty string" } };
84
+ return request("call", { path, args, server });
84
85
  };
85
86
  }
87
+ if (property === "resources" || property === "listResources") return async (input) => request("resources", { input });
88
+ if (property === "readResource" || property === "readResult") return async (input) => request(property, { input });
86
89
  if (property === "describe") {
87
90
  return async (input) => request("describe", { input });
88
91
  }
@@ -21,7 +21,7 @@ export function createMcpStatusSnapshot(state) {
21
21
  const disabled = definition?.disabled === true;
22
22
  const connection = disabled ? undefined : state.manager.getConnection(name);
23
23
  const metadata = disabled ? undefined : state.toolMetadata.get(name);
24
- const toolCount = metadata?.length ?? (connection?.status === "connected" ? connection.tools.length : 0);
24
+ const toolCount = metadata?.filter(tool => !tool.resourceUri).length ?? (connection?.status === "connected" ? connection.tools.length : 0);
25
25
  const resourceCount = disabled
26
26
  ? undefined
27
27
  : state.resourceCounts?.get(name) ?? (connection?.status === "connected" ? connection.resources.length : undefined);
@@ -51,6 +51,7 @@ export function createMcpStatusSnapshot(state) {
51
51
  name,
52
52
  status,
53
53
  toolCount,
54
+ catalogKnown: metadata !== undefined || connection?.status === "connected",
54
55
  ...(resourceCount !== undefined ? { resourceCount } : {}),
55
56
  ...(status === "failed" && failedAgoSeconds !== undefined ? { failedAgoSeconds } : {}),
56
57
  disabled,
@@ -11,7 +11,7 @@ export declare function parseDirectToolSelectors(selectors: string[]): {
11
11
  tools: Map<string, Set<string>>;
12
12
  };
13
13
  export declare function getMissingConfiguredDirectToolServers(config: McpConfig, cache: MetadataCache | null, envOverride?: string[]): string[];
14
- export declare function reconstructToolMetadata(serverName: string, entry: ServerCacheEntry, prefix: ToolPrefix, definition: Pick<ServerEntry, "exposeResources" | "includeTools" | "excludeTools" | "toolPrefix">): ToolMetadata[];
14
+ export declare function reconstructToolMetadata(serverName: string, entry: ServerCacheEntry, prefix: ToolPrefix, definition: Pick<ServerEntry, "exposeResources" | "includeTools" | "excludeTools" | "toolPrefix" | "disabled">): ToolMetadata[];
15
15
  export declare function serializeTools(tools: McpTool[]): CachedTool[];
16
16
  export declare function serializeResources(resources: McpResource[]): CachedResource[];
17
17
  export declare function serializePrompts(prompts: McpPrompt[]): CachedPrompt[];
@@ -3,12 +3,11 @@ import { existsSync, readFileSync, writeFileSync, renameSync, mkdirSync } from "
3
3
  import { dirname } from "node:path";
4
4
  import { getAdapterPath } from "./agent-dir.js";
5
5
  import { createHash } from "node:crypto";
6
- import { getToolUiResourceUri } from "@modelcontextprotocol/ext-apps/app-bridge";
7
- import { formatPromptCommandName, formatToolName, isServerDisabled, isToolAllowed, resolveToolPrefix } from "./types.js";
8
- import { resourceNameToToolName } from "./resource-tools.js";
9
- import { extractToolUiStreamMode, interpolateEnvRecord, resolveBearerToken, resolveConfigPath, resolveServerUrl, } from "./utils.js";
10
- import { extractUiToolVisibility, isUiToolVisibleToModel } from "./ui-tool-visibility.js";
11
- const CACHE_VERSION = 1;
6
+ import { formatPromptCommandName, isServerDisabled, resolveToolPrefix } from "./types.js";
7
+ import { buildToolMetadata } from "./tool-metadata.js";
8
+ import { interpolateEnvRecord, resolveBearerToken, resolveConfigPath, resolveServerUrl, } from "./utils.js";
9
+ // Version 2 stores raw descriptors; version 1 readers cannot enforce raw UI visibility.
10
+ const CACHE_VERSION = 2;
12
11
  const CACHE_MAX_AGE_MS = 7 * 24 * 60 * 60 * 1000;
13
12
  export function getMetadataCachePath() {
14
13
  return getAdapterPath("mcp-cache.json");
@@ -23,7 +22,7 @@ export function loadMetadataCache(enabled = true) {
23
22
  const raw = JSON.parse(readFileSync(cachePath, "utf-8"));
24
23
  if (!raw || typeof raw !== "object")
25
24
  return null;
26
- if (raw.version !== CACHE_VERSION)
25
+ if (raw.version !== 1 && raw.version !== CACHE_VERSION)
27
26
  return null;
28
27
  if (!raw.servers || typeof raw.servers !== "object")
29
28
  return null;
@@ -43,7 +42,7 @@ export function saveMetadataCache(cache, enabled = true) {
43
42
  try {
44
43
  if (existsSync(cachePath)) {
45
44
  const existing = JSON.parse(readFileSync(cachePath, "utf-8"));
46
- if (existing && existing.version === CACHE_VERSION && existing.servers) {
45
+ if (existing && (existing.version === 1 || existing.version === CACHE_VERSION) && existing.servers) {
47
46
  merged.servers = { ...existing.servers };
48
47
  }
49
48
  }
@@ -139,83 +138,29 @@ export function getMissingConfiguredDirectToolServers(config, cache, envOverride
139
138
  return missing;
140
139
  }
141
140
  export function reconstructToolMetadata(serverName, entry, prefix, definition) {
142
- const metadata = [];
143
- const seenNames = new Set();
144
- const effectivePrefix = resolveToolPrefix(definition, prefix);
145
- for (const tool of entry.tools ?? []) {
146
- if (!tool?.name)
147
- continue;
148
- if (!isUiToolVisibleToModel(tool.uiVisibility)) {
149
- continue;
150
- }
151
- if (!isToolAllowed(tool.name, serverName, effectivePrefix, definition.includeTools, definition.excludeTools)) {
152
- continue;
153
- }
154
- const name = formatToolName(tool.name, serverName, effectivePrefix);
155
- if (seenNames.has(name)) {
156
- continue;
157
- }
158
- seenNames.add(name);
159
- metadata.push({
160
- name,
161
- originalName: tool.name,
162
- description: tool.description ?? "",
163
- ...(tool.inputSchema !== undefined ? { inputSchema: tool.inputSchema } : {}),
164
- ...(tool.annotations !== undefined ? { annotations: tool.annotations } : {}),
165
- ...(tool.uiResourceUri !== undefined ? { uiResourceUri: tool.uiResourceUri } : {}),
166
- ...(tool.uiVisibility !== undefined ? { uiVisibility: tool.uiVisibility } : {}),
167
- ...(tool.uiStreamMode !== undefined ? { uiStreamMode: tool.uiStreamMode } : {}),
168
- });
169
- }
170
- if (definition.exposeResources !== false) {
171
- for (const resource of entry.resources ?? []) {
172
- if (!resource?.name || !resource?.uri)
173
- continue;
174
- const baseName = `read_${resourceNameToToolName(resource.name)}`;
175
- if (!isToolAllowed(baseName, serverName, effectivePrefix, definition.includeTools, definition.excludeTools)) {
176
- continue;
177
- }
178
- const name = formatToolName(baseName, serverName, effectivePrefix);
179
- if (seenNames.has(name)) {
180
- continue;
181
- }
182
- seenNames.add(name);
183
- metadata.push({
184
- name,
185
- originalName: baseName,
186
- description: resource.description ?? `Read resource: ${resource.uri}`,
187
- resourceUri: resource.uri,
188
- });
189
- }
141
+ return buildToolMetadata((entry.tools ?? []).filter(tool => tool?.name).map(restoreCachedTool), entry.resources ?? [], definition, serverName, prefix).metadata;
142
+ }
143
+ function restoreCachedTool(tool) {
144
+ const { uiResourceUri, uiVisibility, uiStreamMode, ...descriptor } = tool;
145
+ if (descriptor._meta !== undefined || (uiResourceUri === undefined && uiVisibility === undefined && uiStreamMode === undefined)) {
146
+ return descriptor;
190
147
  }
191
- return metadata;
148
+ return {
149
+ ...descriptor,
150
+ _meta: {
151
+ ui: {
152
+ ...(uiResourceUri !== undefined ? { resourceUri: uiResourceUri } : {}),
153
+ ...(uiVisibility !== undefined ? { visibility: uiVisibility } : {}),
154
+ ...(uiStreamMode !== undefined ? { "pi-mcp-adapter.streamMode": uiStreamMode } : {}),
155
+ },
156
+ },
157
+ };
192
158
  }
193
159
  export function serializeTools(tools) {
194
- return tools
195
- .filter(t => t?.name)
196
- .map(t => {
197
- const uiResourceUri = tryGetToolUiResourceUri(t);
198
- const uiVisibility = extractUiToolVisibility(t._meta);
199
- const uiStreamMode = extractToolUiStreamMode(t._meta);
200
- return {
201
- name: t.name,
202
- ...(t.description !== undefined ? { description: t.description } : {}),
203
- ...(t.inputSchema !== undefined ? { inputSchema: t.inputSchema } : {}),
204
- ...(t.annotations !== undefined ? { annotations: t.annotations } : {}),
205
- ...(uiResourceUri !== undefined ? { uiResourceUri } : {}),
206
- ...(uiVisibility !== undefined ? { uiVisibility } : {}),
207
- ...(uiStreamMode !== undefined ? { uiStreamMode } : {}),
208
- };
209
- });
160
+ return tools.filter(tool => tool?.name);
210
161
  }
211
162
  export function serializeResources(resources) {
212
- return resources
213
- .filter(r => r?.name && r?.uri)
214
- .map(r => ({
215
- uri: r.uri,
216
- name: r.name,
217
- ...(r.description !== undefined ? { description: r.description } : {}),
218
- }));
163
+ return resources.filter(resource => resource?.name && resource?.uri);
219
164
  }
220
165
  export function serializePrompts(prompts) {
221
166
  return (prompts ?? [])
@@ -267,11 +212,3 @@ function stableStringify(value) {
267
212
  const keys = Object.keys(obj).sort();
268
213
  return `{${keys.map(k => `${JSON.stringify(k)}:${stableStringify(obj[k])}`).join(",")}}`;
269
214
  }
270
- function tryGetToolUiResourceUri(tool) {
271
- try {
272
- return getToolUiResourceUri({ _meta: tool._meta });
273
- }
274
- catch {
275
- return undefined;
276
- }
277
- }
package/dist/prompts.d.ts CHANGED
@@ -4,7 +4,7 @@ import type { McpExtensionState } from "./state.ts";
4
4
  import { type McpConfig, type PromptMetadata } from "./types.ts";
5
5
  /**
6
6
  * Resolve prompt metadata for slash-command registration at extension load
7
- * time. Mirrors `resolveDirectTools`: reads the persistent metadata cache so
7
+ * time. Like typed tool registration, reads the persistent metadata cache so
8
8
  * commands are available before any server connects.
9
9
  */
10
10
  export declare function resolveCachedPrompts(config: McpConfig, cacheEnabled?: boolean): PromptMetadata[];
package/dist/prompts.js CHANGED
@@ -5,7 +5,7 @@ import { logger } from "./logger.js";
5
5
  import { truncateAtWord } from "./utils.js";
6
6
  /**
7
7
  * Resolve prompt metadata for slash-command registration at extension load
8
- * time. Mirrors `resolveDirectTools`: reads the persistent metadata cache so
8
+ * time. Like typed tool registration, reads the persistent metadata cache so
9
9
  * commands are available before any server connects.
10
10
  */
11
11
  export function resolveCachedPrompts(config, cacheEnabled = true) {
@@ -1,5 +1,6 @@
1
1
  import type { AgentToolResult, ToolInfo } from "@earendil-works/pi-coding-agent";
2
2
  import type { McpExtensionState } from "./state.ts";
3
+ import type { ServerConnection } from "./server-manager.ts";
3
4
  import type { ToolMetadata, McpOperationContext, McpToolCallIdentity } from "./types.ts";
4
5
  import { type SessionRecoveryDeps } from "./session-recovery.ts";
5
6
  type ProxyToolResult = AgentToolResult<Record<string, unknown>>;
@@ -7,7 +8,7 @@ export declare function executeUiMessages(state: McpExtensionState): ProxyToolRe
7
8
  export declare function executeStatus(state: McpExtensionState): ProxyToolResult;
8
9
  export declare function executeAuthStart(state: McpExtensionState, serverName: string, signal?: AbortSignal): Promise<ProxyToolResult>;
9
10
  export declare function executeAuthComplete(state: McpExtensionState, serverName: string, input?: string, signal?: AbortSignal): Promise<ProxyToolResult>;
10
- export declare function executeDescribe(state: McpExtensionState, toolName: string): ProxyToolResult;
11
+ export declare function executeDescribe(state: McpExtensionState, toolName: string, serverFilter?: string): ProxyToolResult;
11
12
  export declare function executeSearch(state: McpExtensionState, query: string, server?: string, includeSchemas?: boolean, limit?: number, offset?: number): ProxyToolResult;
12
13
  export declare function executeList(state: McpExtensionState, server: string, limit?: number, offset?: number): ProxyToolResult;
13
14
  export declare function executeInstructions(state: McpExtensionState, server: string): ProxyToolResult;
@@ -24,15 +25,48 @@ interface ToolCallOptions extends McpToolCallIdentity {
24
25
  recoverAuthConnection: NonNullable<SessionRecoveryDeps["onNeedsAuth"]>;
25
26
  authRequiredMessage: () => string;
26
27
  autoAuthAttempted: () => boolean;
27
- onRawResult?: (result: unknown) => void;
28
+ raw?: boolean;
28
29
  }
29
30
  /**
30
31
  * Executes an already-approved tool call against a connected server.
31
32
  *
32
- * Both the `mcp` proxy and direct tools reach this with a resolved target and a
33
- * live connection; everything before this point (name resolution, connect and
34
- * auth strategy, approval) differs between them and stays with the caller.
33
+ * Direct, gateway, resource and script calls share executeCall preparation.
34
+ * Raw scripts skip content rendering but retain protocol results and artifacts.
35
35
  */
36
36
  export declare function runToolCall(state: McpExtensionState, serverName: string, target: ToolCallTarget, args: Record<string, unknown> | undefined, options: ToolCallOptions): Promise<ProxyToolResult>;
37
- export declare function executeCall(state: McpExtensionState, toolName: string, args?: Record<string, unknown>, serverOverride?: string, getPiTools?: () => ToolInfo[], signal?: AbortSignal, onRawResult?: (result: unknown) => void, identity?: McpToolCallIdentity, beforeDispatch?: (signal: AbortSignal | undefined, operation: McpOperationContext) => Promise<void>): Promise<ProxyToolResult>;
37
+ export interface ExecuteCallOptions {
38
+ exactOriginalName?: boolean;
39
+ resourceUri?: string;
40
+ /** Scripts receive the raw protocol result without model-facing content transformation. */
41
+ raw?: boolean;
42
+ }
43
+ /** Connect/auth once for calls and resource discovery. Recovery stays bounded by withSessionRecovery. */
44
+ export declare function prepareMcpConnection(state: McpExtensionState, serverName: string, signal?: AbortSignal): Promise<{
45
+ error: {
46
+ content: {
47
+ type: "text";
48
+ text: string;
49
+ }[];
50
+ details: {
51
+ mode: string;
52
+ error: string;
53
+ server: string;
54
+ message: string;
55
+ };
56
+ };
57
+ connection?: never;
58
+ recoverAuthConnection?: never;
59
+ autoAuthAttempted?: never;
60
+ } | {
61
+ error: ProxyToolResult;
62
+ connection?: never;
63
+ recoverAuthConnection?: never;
64
+ autoAuthAttempted?: never;
65
+ } | {
66
+ connection: ServerConnection;
67
+ recoverAuthConnection: (serverName: string, signal?: AbortSignal, challenge?: import("./session-recovery.ts").AuthChallengeContext) => Promise<ServerConnection | undefined>;
68
+ autoAuthAttempted: () => boolean;
69
+ error?: never;
70
+ }>;
71
+ export declare function executeCall(state: McpExtensionState, toolName: string, args?: Record<string, unknown>, serverOverride?: string, getPiTools?: () => ToolInfo[], signal?: AbortSignal, identity?: McpToolCallIdentity, beforeDispatch?: (signal: AbortSignal | undefined, operation: McpOperationContext) => Promise<void>, options?: ExecuteCallOptions): Promise<ProxyToolResult>;
38
72
  export {};