@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.
- package/CHANGELOG.md +25 -1
- package/OAUTH.md +3 -3
- package/README.md +153 -85
- package/app-bridge.bundle.js +75 -33
- package/commands.ts +9 -6
- package/direct-tools.ts +25 -358
- package/dist/app-bridge.bundle.js +75 -33
- package/dist/commands.js +9 -6
- package/dist/direct-tools.d.ts +2 -4
- package/dist/direct-tools.js +21 -319
- package/dist/gateway-arguments.d.ts +33 -0
- package/dist/gateway-arguments.js +63 -0
- package/dist/index.js +181 -276
- package/dist/init.js +6 -20
- package/dist/mcp-code.d.ts +2 -1
- package/dist/mcp-code.js +87 -41
- package/dist/mcp-output-guard.d.ts +57 -2
- package/dist/mcp-output-guard.js +129 -24
- package/dist/mcp-panel.d.ts +1 -1
- package/dist/mcp-panel.js +34 -97
- package/dist/mcp-script-worker.mjs +5 -2
- package/dist/mcp-status.js +2 -1
- package/dist/metadata-cache.d.ts +1 -1
- package/dist/metadata-cache.js +25 -88
- package/dist/prompts.d.ts +1 -1
- package/dist/prompts.js +1 -1
- package/dist/proxy-modes.d.ts +40 -6
- package/dist/proxy-modes.js +270 -485
- package/dist/resource-tools.d.ts +15 -0
- package/dist/resource-tools.js +26 -1
- package/dist/sampling-handler.js +4 -1
- package/dist/search-ranking.d.ts +0 -2
- package/dist/search-ranking.js +66 -137
- package/dist/state.d.ts +1 -1
- package/dist/tool-loader.d.ts +32 -0
- package/dist/tool-loader.js +152 -0
- package/dist/tool-metadata.d.ts +67 -0
- package/dist/tool-metadata.js +23 -17
- package/dist/tool-registrar.d.ts +4 -4
- package/dist/tool-registrar.js +18 -13
- package/dist/tool-result-renderer.d.ts +3 -0
- package/dist/tool-result-renderer.js +8 -3
- package/dist/types.d.ts +11 -36
- package/dist/utils.js +2 -4
- package/gateway-arguments.ts +73 -0
- package/index.ts +166 -299
- package/init.ts +6 -20
- package/mcp-code.ts +71 -45
- package/mcp-output-guard.ts +145 -27
- package/mcp-panel.ts +38 -109
- package/mcp-script-worker.mjs +5 -2
- package/mcp-status.ts +2 -1
- package/metadata-cache.ts +31 -93
- package/package.json +14 -9
- package/prompts.ts +1 -1
- package/proxy-modes.ts +265 -517
- package/resource-tools.ts +36 -1
- package/sampling-handler.ts +4 -1
- package/scripts/build-app-bridge.mjs +15 -0
- package/scripts/build.mjs +3 -2
- package/scripts/prepare.mjs +1 -0
- package/search-ranking.ts +71 -135
- package/skills/mcp-scripting/SKILL.md +67 -20
- package/state.ts +1 -1
- package/tool-loader.ts +153 -0
- package/tool-metadata.ts +22 -17
- package/tool-registrar.ts +18 -13
- package/tool-result-renderer.ts +10 -2
- package/types.ts +15 -40
- package/utils.ts +2 -4
- package/dist/ts-shape.d.ts +0 -2
- package/dist/ts-shape.js +0 -147
- package/ts-shape.ts +0 -145
package/mcp-panel.ts
CHANGED
|
@@ -1,11 +1,9 @@
|
|
|
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, type PanelKeybindings, type PanelKeys } from "./panel-keys.ts";
|
|
4
|
-
import {
|
|
5
|
-
import type { McpConfig, McpPanelCallbacks, McpPanelResult, ServerProvenance, ToolPrefix } from "./types.ts";
|
|
6
|
-
import { resourceNameToToolName } from "./resource-tools.ts";
|
|
4
|
+
import type { McpConfig, McpPanelCallbacks, McpPanelResult, ServerEntry, ServerProvenance, ToolPrefix } from "./types.ts";
|
|
7
5
|
import { sanitizeTerminalText, stripOscSequences } from "./utils.ts";
|
|
8
|
-
import type
|
|
6
|
+
import { reconstructToolMetadata, type MetadataCache, type ServerCacheEntry, type CachedTool } from "./metadata-cache.ts";
|
|
9
7
|
|
|
10
8
|
interface PanelTheme {
|
|
11
9
|
border: string;
|
|
@@ -130,9 +128,9 @@ interface ServerState {
|
|
|
130
128
|
expanded: boolean;
|
|
131
129
|
source: "user" | "project" | "import";
|
|
132
130
|
importKind?: string;
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
131
|
+
definition: ServerEntry;
|
|
132
|
+
toolFilter: true | string[] | false;
|
|
133
|
+
resourceCount: number;
|
|
136
134
|
connectionStatus: ConnectionStatus;
|
|
137
135
|
failureMessage?: string | null;
|
|
138
136
|
tools: ToolState[];
|
|
@@ -197,61 +195,24 @@ class McpPanel {
|
|
|
197
195
|
toolFilter = globalDirect;
|
|
198
196
|
}
|
|
199
197
|
|
|
200
|
-
const tools: ToolState[] = [];
|
|
201
|
-
if (serverCache && !this.authOnly && !isServerDisabled(definition)) {
|
|
202
|
-
for (const tool of serverCache.tools ?? []) {
|
|
203
|
-
if (!isToolAllowed(tool.name, serverName, this.prefix, definition.includeTools, definition.excludeTools)) {
|
|
204
|
-
continue;
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
const isDirect = toolFilter === true || (Array.isArray(toolFilter) && toolFilter.includes(tool.name));
|
|
208
|
-
tools.push({
|
|
209
|
-
name: tool.name,
|
|
210
|
-
description: tool.description ?? "",
|
|
211
|
-
isDirect,
|
|
212
|
-
wasDirect: isDirect,
|
|
213
|
-
estimatedTokens: estimateTokens(tool),
|
|
214
|
-
});
|
|
215
|
-
}
|
|
216
|
-
if (definition.exposeResources !== false) {
|
|
217
|
-
for (const resource of serverCache.resources ?? []) {
|
|
218
|
-
const baseName = `read_${resourceNameToToolName(resource.name)}`;
|
|
219
|
-
if (!isToolAllowed(baseName, serverName, this.prefix, definition.includeTools, definition.excludeTools)) {
|
|
220
|
-
continue;
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
const isDirect = toolFilter === true || (Array.isArray(toolFilter) && toolFilter.includes(baseName));
|
|
224
|
-
const ct: CachedTool = {
|
|
225
|
-
name: baseName,
|
|
226
|
-
...(resource.description !== undefined ? { description: resource.description } : {}),
|
|
227
|
-
};
|
|
228
|
-
tools.push({
|
|
229
|
-
name: baseName,
|
|
230
|
-
description: resource.description ?? `Read resource: ${resource.uri}`,
|
|
231
|
-
isDirect,
|
|
232
|
-
wasDirect: isDirect,
|
|
233
|
-
estimatedTokens: estimateTokens(ct),
|
|
234
|
-
});
|
|
235
|
-
}
|
|
236
|
-
}
|
|
237
|
-
}
|
|
238
|
-
|
|
239
198
|
const status = callbacks.getConnectionStatus(serverName);
|
|
240
199
|
const failureMessage = callbacks.getFailureMessage?.(serverName) ?? null;
|
|
241
200
|
|
|
242
|
-
|
|
201
|
+
const server: ServerState = {
|
|
243
202
|
name: serverName,
|
|
244
203
|
expanded: false,
|
|
245
204
|
source: prov?.kind ?? "user",
|
|
246
205
|
...(prov?.importKind !== undefined ? { importKind: prov.importKind } : {}),
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
206
|
+
definition,
|
|
207
|
+
toolFilter,
|
|
208
|
+
resourceCount: 0,
|
|
250
209
|
connectionStatus: status,
|
|
251
210
|
failureMessage,
|
|
252
|
-
tools,
|
|
211
|
+
tools: [],
|
|
253
212
|
hasCachedData: !!serverCache,
|
|
254
|
-
}
|
|
213
|
+
};
|
|
214
|
+
this.servers.push(server);
|
|
215
|
+
if (serverCache && !this.authOnly) this.rebuildServerTools(server, serverCache);
|
|
255
216
|
}
|
|
256
217
|
|
|
257
218
|
this.rebuildVisibleItems();
|
|
@@ -565,7 +526,6 @@ class McpPanel {
|
|
|
565
526
|
if (entry) {
|
|
566
527
|
this.rebuildServerTools(server, entry);
|
|
567
528
|
}
|
|
568
|
-
server.hasCachedData = true;
|
|
569
529
|
}
|
|
570
530
|
if (options.afterAuth) {
|
|
571
531
|
this.authNotice = connected && server.connectionStatus === "connected"
|
|
@@ -637,50 +597,22 @@ class McpPanel {
|
|
|
637
597
|
}
|
|
638
598
|
|
|
639
599
|
private rebuildServerTools(server: ServerState, entry: ServerCacheEntry): void {
|
|
640
|
-
const existingState = new Map
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
name: tool.name,
|
|
653
|
-
description: tool.description ?? "",
|
|
654
|
-
isDirect,
|
|
655
|
-
wasDirect: prev !== undefined ? server.tools.find((t) => t.name === tool.name)?.wasDirect ?? false : false,
|
|
600
|
+
const existingState = new Map(server.tools.map(tool => [tool.name, tool]));
|
|
601
|
+
const metadata = reconstructToolMetadata(server.name, entry, this.prefix, server.definition);
|
|
602
|
+
server.resourceCount = metadata.filter(tool => tool.resourceUri !== undefined).length;
|
|
603
|
+
server.tools = metadata.filter(tool => tool.resourceUri === undefined).map(tool => {
|
|
604
|
+
const previous = existingState.get(tool.originalName);
|
|
605
|
+
const pinned = server.toolFilter === true
|
|
606
|
+
|| (Array.isArray(server.toolFilter) && server.toolFilter.includes(tool.originalName));
|
|
607
|
+
return {
|
|
608
|
+
name: tool.originalName,
|
|
609
|
+
description: tool.description,
|
|
610
|
+
isDirect: previous?.isDirect ?? pinned,
|
|
611
|
+
wasDirect: previous?.wasDirect ?? pinned,
|
|
656
612
|
estimatedTokens: estimateTokens(tool),
|
|
657
|
-
}
|
|
658
|
-
}
|
|
659
|
-
|
|
660
|
-
if (server.exposeResources) {
|
|
661
|
-
for (const resource of entry.resources ?? []) {
|
|
662
|
-
const baseName = `read_${resourceNameToToolName(resource.name)}`;
|
|
663
|
-
if (!isToolAllowed(baseName, server.name, this.prefix, server.includeTools, server.excludeTools)) {
|
|
664
|
-
continue;
|
|
665
|
-
}
|
|
666
|
-
|
|
667
|
-
const prev = existingState.get(baseName);
|
|
668
|
-
const isDirect = prev !== undefined ? prev : false;
|
|
669
|
-
const ct: CachedTool = {
|
|
670
|
-
name: baseName,
|
|
671
|
-
...(resource.description !== undefined ? { description: resource.description } : {}),
|
|
672
|
-
};
|
|
673
|
-
newTools.push({
|
|
674
|
-
name: baseName,
|
|
675
|
-
description: resource.description ?? `Read resource: ${resource.uri}`,
|
|
676
|
-
isDirect,
|
|
677
|
-
wasDirect: prev !== undefined ? server.tools.find((t) => t.name === baseName)?.wasDirect ?? false : false,
|
|
678
|
-
estimatedTokens: estimateTokens(ct),
|
|
679
|
-
});
|
|
680
|
-
}
|
|
681
|
-
}
|
|
682
|
-
|
|
683
|
-
server.tools = newTools;
|
|
613
|
+
};
|
|
614
|
+
});
|
|
615
|
+
server.hasCachedData = true;
|
|
684
616
|
this.rebuildVisibleItems();
|
|
685
617
|
this.updateDirty();
|
|
686
618
|
}
|
|
@@ -746,6 +678,9 @@ class McpPanel {
|
|
|
746
678
|
|
|
747
679
|
if (item.type === "server") {
|
|
748
680
|
lines.push(row(this.renderServerRow(server, isCursor)));
|
|
681
|
+
if (isCursor && !this.authOnly && server.resourceCount > 0) {
|
|
682
|
+
lines.push(row(fg(t.hint, " Resources: mcp actions resources / read-resource")));
|
|
683
|
+
}
|
|
749
684
|
if (isCursor && server.connectionStatus === "failed" && server.failureMessage) {
|
|
750
685
|
for (const line of this.wrapText(sanitizeDisplayText(server.failureMessage), innerW - 6)) {
|
|
751
686
|
lines.push(row(` ${fg(t.cancel, line)}`));
|
|
@@ -796,8 +731,9 @@ class McpPanel {
|
|
|
796
731
|
0,
|
|
797
732
|
);
|
|
798
733
|
const stats =
|
|
799
|
-
directCount > 0 ? `${directCount}
|
|
734
|
+
directCount > 0 ? `${directCount} pinned ~${totalTokens.toLocaleString()} tokens` : "no pinned tools";
|
|
800
735
|
lines.push(row(fg(t.description, stats + (this.dirty ? fg(t.needsAuth, " (unsaved)") : ""))));
|
|
736
|
+
lines.push(row(fg(t.hint, "Pins load at startup; discover more with mcp_search.")));
|
|
801
737
|
}
|
|
802
738
|
}
|
|
803
739
|
|
|
@@ -812,7 +748,7 @@ class McpPanel {
|
|
|
812
748
|
]
|
|
813
749
|
: [
|
|
814
750
|
italic("↑↓") + " navigate",
|
|
815
|
-
italic("space") + "
|
|
751
|
+
italic("space") + " pin/unpin",
|
|
816
752
|
italic("⏎") + " expand/auth",
|
|
817
753
|
italic("ctrl+a") + " auth",
|
|
818
754
|
italic("ctrl+r") + " reconnect",
|
|
@@ -860,7 +796,7 @@ class McpPanel {
|
|
|
860
796
|
const statusLabel = this.renderConnectionStatus(server);
|
|
861
797
|
|
|
862
798
|
if (!server.hasCachedData && !this.authOnly) {
|
|
863
|
-
return `${prefix} ${nameStr}${importLabel} ${fg(t.description, "(
|
|
799
|
+
return `${prefix} ${nameStr}${importLabel}${statusLabel} ${fg(t.description, "(undiscovered)")}`;
|
|
864
800
|
}
|
|
865
801
|
|
|
866
802
|
const directCount = server.tools.filter((t) => t.isDirect).length;
|
|
@@ -872,17 +808,10 @@ class McpPanel {
|
|
|
872
808
|
toggleIcon = fg(t.needsAuth, "◐");
|
|
873
809
|
}
|
|
874
810
|
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
toolInfo = `${directCount}/${totalCount}`;
|
|
878
|
-
if (directCount > 0) {
|
|
879
|
-
const tokens = server.tools.filter((t) => t.isDirect).reduce((s, t) => s + t.estimatedTokens, 0);
|
|
880
|
-
toolInfo += ` ~${tokens.toLocaleString()}`;
|
|
881
|
-
}
|
|
882
|
-
toolInfo = fg(t.description, toolInfo);
|
|
883
|
-
}
|
|
811
|
+
const toolInfo = this.authOnly ? "" : fg(t.description,
|
|
812
|
+
`${totalCount} tool${totalCount === 1 ? "" : "s"}, ${directCount} pinned${server.resourceCount > 0 ? `, ${server.resourceCount} resource${server.resourceCount === 1 ? "" : "s"}` : ""}`);
|
|
884
813
|
|
|
885
|
-
return `${prefix} ${toggleIcon} ${nameStr}${importLabel}
|
|
814
|
+
return `${prefix} ${toggleIcon} ${nameStr}${importLabel}${statusLabel} ${toolInfo}`;
|
|
886
815
|
}
|
|
887
816
|
|
|
888
817
|
private selectedServerHasFailureMessage(): boolean {
|
package/mcp-script-worker.mjs
CHANGED
|
@@ -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
|
-
|
|
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
|
}
|
package/mcp-status.ts
CHANGED
|
@@ -33,7 +33,7 @@ export function createMcpStatusSnapshot(state: McpExtensionState): McpStatusSnap
|
|
|
33
33
|
const disabled = definition?.disabled === true;
|
|
34
34
|
const connection = disabled ? undefined : state.manager.getConnection(name);
|
|
35
35
|
const metadata = disabled ? undefined : state.toolMetadata.get(name);
|
|
36
|
-
const toolCount = metadata?.length ?? (connection?.status === "connected" ? connection.tools.length : 0);
|
|
36
|
+
const toolCount = metadata?.filter(tool => !tool.resourceUri).length ?? (connection?.status === "connected" ? connection.tools.length : 0);
|
|
37
37
|
const resourceCount = disabled
|
|
38
38
|
? undefined
|
|
39
39
|
: state.resourceCounts?.get(name) ?? (connection?.status === "connected" ? connection.resources.length : undefined);
|
|
@@ -60,6 +60,7 @@ export function createMcpStatusSnapshot(state: McpExtensionState): McpStatusSnap
|
|
|
60
60
|
name,
|
|
61
61
|
status,
|
|
62
62
|
toolCount,
|
|
63
|
+
catalogKnown: metadata !== undefined || connection?.status === "connected",
|
|
63
64
|
...(resourceCount !== undefined ? { resourceCount } : {}),
|
|
64
65
|
...(status === "failed" && failedAgoSeconds !== undefined ? { failedAgoSeconds } : {}),
|
|
65
66
|
disabled,
|
package/metadata-cache.ts
CHANGED
|
@@ -3,7 +3,6 @@ import { existsSync, readFileSync, writeFileSync, renameSync, mkdirSync } from "
|
|
|
3
3
|
import { dirname } from "node:path";
|
|
4
4
|
import { getAdapterPath } from "./agent-dir.ts";
|
|
5
5
|
import { createHash } from "node:crypto";
|
|
6
|
-
import { getToolUiResourceUri } from "@modelcontextprotocol/ext-apps/app-bridge";
|
|
7
6
|
import type {
|
|
8
7
|
CachedPrompt,
|
|
9
8
|
CachedResource,
|
|
@@ -19,18 +18,17 @@ import type {
|
|
|
19
18
|
ToolMetadata,
|
|
20
19
|
PromptMetadata,
|
|
21
20
|
} from "./types.ts";
|
|
22
|
-
import { formatPromptCommandName,
|
|
23
|
-
import {
|
|
21
|
+
import { formatPromptCommandName, isServerDisabled, resolveToolPrefix, type ToolPrefix } from "./types.ts";
|
|
22
|
+
import { buildToolMetadata } from "./tool-metadata.ts";
|
|
24
23
|
import {
|
|
25
|
-
extractToolUiStreamMode,
|
|
26
24
|
interpolateEnvRecord,
|
|
27
25
|
resolveBearerToken,
|
|
28
26
|
resolveConfigPath,
|
|
29
27
|
resolveServerUrl,
|
|
30
28
|
} from "./utils.ts";
|
|
31
|
-
import { extractUiToolVisibility, isUiToolVisibleToModel } from "./ui-tool-visibility.ts";
|
|
32
29
|
|
|
33
|
-
|
|
30
|
+
// Version 2 stores raw descriptors; version 1 readers cannot enforce raw UI visibility.
|
|
31
|
+
const CACHE_VERSION = 2;
|
|
34
32
|
const CACHE_MAX_AGE_MS = 7 * 24 * 60 * 60 * 1000;
|
|
35
33
|
|
|
36
34
|
export type { CachedPrompt, CachedResource, CachedTool, MetadataCache, ServerCacheEntry } from "./types.ts";
|
|
@@ -46,7 +44,7 @@ export function loadMetadataCache(enabled = true): MetadataCache | null {
|
|
|
46
44
|
try {
|
|
47
45
|
const raw = JSON.parse(readFileSync(cachePath, "utf-8"));
|
|
48
46
|
if (!raw || typeof raw !== "object") return null;
|
|
49
|
-
if (raw.version !== CACHE_VERSION) return null;
|
|
47
|
+
if (raw.version !== 1 && raw.version !== CACHE_VERSION) return null;
|
|
50
48
|
if (!raw.servers || typeof raw.servers !== "object") return null;
|
|
51
49
|
return raw as MetadataCache;
|
|
52
50
|
} catch {
|
|
@@ -64,7 +62,7 @@ export function saveMetadataCache(cache: MetadataCache, enabled = true): void {
|
|
|
64
62
|
try {
|
|
65
63
|
if (existsSync(cachePath)) {
|
|
66
64
|
const existing = JSON.parse(readFileSync(cachePath, "utf-8")) as MetadataCache;
|
|
67
|
-
if (existing && existing.version === CACHE_VERSION && existing.servers) {
|
|
65
|
+
if (existing && (existing.version === 1 || existing.version === CACHE_VERSION) && existing.servers) {
|
|
68
66
|
merged.servers = { ...existing.servers };
|
|
69
67
|
}
|
|
70
68
|
}
|
|
@@ -178,92 +176,40 @@ export function reconstructToolMetadata(
|
|
|
178
176
|
serverName: string,
|
|
179
177
|
entry: ServerCacheEntry,
|
|
180
178
|
prefix: ToolPrefix,
|
|
181
|
-
definition: Pick<ServerEntry, "exposeResources" | "includeTools" | "excludeTools" | "toolPrefix">
|
|
179
|
+
definition: Pick<ServerEntry, "exposeResources" | "includeTools" | "excludeTools" | "toolPrefix" | "disabled">
|
|
182
180
|
): ToolMetadata[] {
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
}
|
|
192
|
-
if (!isToolAllowed(tool.name, serverName, effectivePrefix, definition.includeTools, definition.excludeTools)) {
|
|
193
|
-
continue;
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
const name = formatToolName(tool.name, serverName, effectivePrefix);
|
|
197
|
-
if (seenNames.has(name)) {
|
|
198
|
-
continue;
|
|
199
|
-
}
|
|
200
|
-
seenNames.add(name);
|
|
201
|
-
|
|
202
|
-
metadata.push({
|
|
203
|
-
name,
|
|
204
|
-
originalName: tool.name,
|
|
205
|
-
description: tool.description ?? "",
|
|
206
|
-
...(tool.inputSchema !== undefined ? { inputSchema: tool.inputSchema } : {}),
|
|
207
|
-
...(tool.annotations !== undefined ? { annotations: tool.annotations } : {}),
|
|
208
|
-
...(tool.uiResourceUri !== undefined ? { uiResourceUri: tool.uiResourceUri } : {}),
|
|
209
|
-
...(tool.uiVisibility !== undefined ? { uiVisibility: tool.uiVisibility } : {}),
|
|
210
|
-
...(tool.uiStreamMode !== undefined ? { uiStreamMode: tool.uiStreamMode } : {}),
|
|
211
|
-
});
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
if (definition.exposeResources !== false) {
|
|
215
|
-
for (const resource of entry.resources ?? []) {
|
|
216
|
-
if (!resource?.name || !resource?.uri) continue;
|
|
217
|
-
const baseName = `read_${resourceNameToToolName(resource.name)}`;
|
|
218
|
-
if (!isToolAllowed(baseName, serverName, effectivePrefix, definition.includeTools, definition.excludeTools)) {
|
|
219
|
-
continue;
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
const name = formatToolName(baseName, serverName, effectivePrefix);
|
|
223
|
-
if (seenNames.has(name)) {
|
|
224
|
-
continue;
|
|
225
|
-
}
|
|
226
|
-
seenNames.add(name);
|
|
181
|
+
return buildToolMetadata(
|
|
182
|
+
(entry.tools ?? []).filter(tool => tool?.name).map(restoreCachedTool),
|
|
183
|
+
entry.resources ?? [],
|
|
184
|
+
definition,
|
|
185
|
+
serverName,
|
|
186
|
+
prefix,
|
|
187
|
+
).metadata;
|
|
188
|
+
}
|
|
227
189
|
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
resourceUri: resource.uri,
|
|
233
|
-
});
|
|
234
|
-
}
|
|
190
|
+
function restoreCachedTool(tool: CachedTool): McpTool {
|
|
191
|
+
const { uiResourceUri, uiVisibility, uiStreamMode, ...descriptor } = tool;
|
|
192
|
+
if (descriptor._meta !== undefined || (uiResourceUri === undefined && uiVisibility === undefined && uiStreamMode === undefined)) {
|
|
193
|
+
return descriptor;
|
|
235
194
|
}
|
|
236
|
-
|
|
237
|
-
|
|
195
|
+
return {
|
|
196
|
+
...descriptor,
|
|
197
|
+
_meta: {
|
|
198
|
+
ui: {
|
|
199
|
+
...(uiResourceUri !== undefined ? { resourceUri: uiResourceUri } : {}),
|
|
200
|
+
...(uiVisibility !== undefined ? { visibility: uiVisibility } : {}),
|
|
201
|
+
...(uiStreamMode !== undefined ? { "pi-mcp-adapter.streamMode": uiStreamMode } : {}),
|
|
202
|
+
},
|
|
203
|
+
},
|
|
204
|
+
};
|
|
238
205
|
}
|
|
239
206
|
|
|
240
207
|
export function serializeTools(tools: McpTool[]): CachedTool[] {
|
|
241
|
-
return tools
|
|
242
|
-
.filter(t => t?.name)
|
|
243
|
-
.map(t => {
|
|
244
|
-
const uiResourceUri = tryGetToolUiResourceUri(t);
|
|
245
|
-
const uiVisibility = extractUiToolVisibility(t._meta);
|
|
246
|
-
const uiStreamMode = extractToolUiStreamMode(t._meta);
|
|
247
|
-
return {
|
|
248
|
-
name: t.name,
|
|
249
|
-
...(t.description !== undefined ? { description: t.description } : {}),
|
|
250
|
-
...(t.inputSchema !== undefined ? { inputSchema: t.inputSchema } : {}),
|
|
251
|
-
...(t.annotations !== undefined ? { annotations: t.annotations } : {}),
|
|
252
|
-
...(uiResourceUri !== undefined ? { uiResourceUri } : {}),
|
|
253
|
-
...(uiVisibility !== undefined ? { uiVisibility } : {}),
|
|
254
|
-
...(uiStreamMode !== undefined ? { uiStreamMode } : {}),
|
|
255
|
-
};
|
|
256
|
-
});
|
|
208
|
+
return tools.filter(tool => tool?.name);
|
|
257
209
|
}
|
|
258
210
|
|
|
259
211
|
export function serializeResources(resources: McpResource[]): CachedResource[] {
|
|
260
|
-
return resources
|
|
261
|
-
.filter(r => r?.name && r?.uri)
|
|
262
|
-
.map(r => ({
|
|
263
|
-
uri: r.uri,
|
|
264
|
-
name: r.name,
|
|
265
|
-
...(r.description !== undefined ? { description: r.description } : {}),
|
|
266
|
-
}));
|
|
212
|
+
return resources.filter(resource => resource?.name && resource?.uri);
|
|
267
213
|
}
|
|
268
214
|
|
|
269
215
|
export function serializePrompts(prompts: McpPrompt[]): CachedPrompt[] {
|
|
@@ -323,11 +269,3 @@ function stableStringify(value: unknown): string {
|
|
|
323
269
|
const keys = Object.keys(obj).sort();
|
|
324
270
|
return `{${keys.map(k => `${JSON.stringify(k)}:${stableStringify(obj[k])}`).join(",")}}`;
|
|
325
271
|
}
|
|
326
|
-
|
|
327
|
-
function tryGetToolUiResourceUri(tool: McpTool): string | undefined {
|
|
328
|
-
try {
|
|
329
|
-
return getToolUiResourceUri({ _meta: tool._meta });
|
|
330
|
-
} catch {
|
|
331
|
-
return undefined;
|
|
332
|
-
}
|
|
333
|
-
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fitchmultz/pi-mcp-adapter",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "6.0.0",
|
|
4
4
|
"description": "Fitch's independently maintained MCP adapter for Pi coding agent",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
@@ -30,7 +30,9 @@
|
|
|
30
30
|
},
|
|
31
31
|
"scripts": {
|
|
32
32
|
"build": "node ./scripts/build.mjs",
|
|
33
|
+
"build:bridge": "node ./scripts/build-app-bridge.mjs",
|
|
33
34
|
"prepare": "node ./scripts/prepare.mjs",
|
|
35
|
+
"check:compat": "node scripts/compat-host.mjs && npm run build && npm run typecheck && vitest run --exclude __tests__/interactive-visualizer-server.test.ts && npm run test:oauth && node scripts/compat-package-smoke.mjs",
|
|
34
36
|
"typecheck": "tsc --noEmit",
|
|
35
37
|
"test": "vitest run",
|
|
36
38
|
"test:watch": "vitest",
|
|
@@ -67,6 +69,7 @@
|
|
|
67
69
|
"dist",
|
|
68
70
|
"docs/client-metadata.json",
|
|
69
71
|
"scripts/build.mjs",
|
|
72
|
+
"scripts/build-app-bridge.mjs",
|
|
70
73
|
"scripts/prepare.mjs",
|
|
71
74
|
"tsconfig.build.json",
|
|
72
75
|
"tsconfig.json",
|
|
@@ -74,6 +77,8 @@
|
|
|
74
77
|
"agent-dir.ts",
|
|
75
78
|
"legacy-migration.ts",
|
|
76
79
|
"index.ts",
|
|
80
|
+
"tool-loader.ts",
|
|
81
|
+
"gateway-arguments.ts",
|
|
77
82
|
"error-signal.ts",
|
|
78
83
|
"state.ts",
|
|
79
84
|
"utils.ts",
|
|
@@ -81,7 +86,6 @@
|
|
|
81
86
|
"runtime-owner.ts",
|
|
82
87
|
"checkpoint.ts",
|
|
83
88
|
"tool-metadata.ts",
|
|
84
|
-
"ts-shape.ts",
|
|
85
89
|
"search-ranking.ts",
|
|
86
90
|
"tool-approval.ts",
|
|
87
91
|
"init.ts",
|
|
@@ -136,21 +140,21 @@
|
|
|
136
140
|
"dependencies": {
|
|
137
141
|
"@modelcontextprotocol/client": "2.0.0",
|
|
138
142
|
"@modelcontextprotocol/core": "2.0.0",
|
|
139
|
-
"@modelcontextprotocol/ext-apps": "
|
|
140
|
-
"@modelcontextprotocol/sdk": "^1.30.0",
|
|
143
|
+
"@modelcontextprotocol/ext-apps": "2.0.0",
|
|
141
144
|
"@napi-rs/keyring": "^1.3.0",
|
|
142
145
|
"cross-spawn": "^7.0.6",
|
|
146
|
+
"minisearch": "^7.2.0",
|
|
143
147
|
"open": "^10.2.0",
|
|
144
148
|
"smol-toml": "^1.8.0",
|
|
145
149
|
"strip-json-comments": "^5.0.3",
|
|
146
|
-
"zod": "^
|
|
150
|
+
"zod": "^4.6.5"
|
|
147
151
|
},
|
|
148
152
|
"peerDependencies": {
|
|
149
153
|
"@earendil-works/pi-ai": "*",
|
|
150
154
|
"@earendil-works/pi-coding-agent": "*",
|
|
151
155
|
"@earendil-works/pi-tui": "*",
|
|
152
156
|
"typebox": "*",
|
|
153
|
-
"zod": "^
|
|
157
|
+
"zod": "^4.6.5"
|
|
154
158
|
},
|
|
155
159
|
"peerDependenciesMeta": {
|
|
156
160
|
"@earendil-works/pi-ai": {
|
|
@@ -167,13 +171,14 @@
|
|
|
167
171
|
}
|
|
168
172
|
},
|
|
169
173
|
"devDependencies": {
|
|
170
|
-
"@earendil-works/pi-ai": "0.
|
|
171
|
-
"@earendil-works/pi-coding-agent": "0.
|
|
172
|
-
"@earendil-works/pi-tui": "0.
|
|
174
|
+
"@earendil-works/pi-ai": "0.87.0",
|
|
175
|
+
"@earendil-works/pi-coding-agent": "0.87.0",
|
|
176
|
+
"@earendil-works/pi-tui": "0.87.0",
|
|
173
177
|
"@modelcontextprotocol/conformance": "0.1.16",
|
|
174
178
|
"@modelcontextprotocol/server": "2.0.0",
|
|
175
179
|
"@types/cross-spawn": "6.0.6",
|
|
176
180
|
"@types/node": "20.19.43",
|
|
181
|
+
"esbuild": "^0.28.2",
|
|
177
182
|
"tsx": "^4.21.0",
|
|
178
183
|
"typebox": "1.3.7",
|
|
179
184
|
"typescript": "5.9.3",
|
package/prompts.ts
CHANGED
|
@@ -12,7 +12,7 @@ import { truncateAtWord } from "./utils.ts";
|
|
|
12
12
|
|
|
13
13
|
/**
|
|
14
14
|
* Resolve prompt metadata for slash-command registration at extension load
|
|
15
|
-
* time.
|
|
15
|
+
* time. Like typed tool registration, reads the persistent metadata cache so
|
|
16
16
|
* commands are available before any server connects.
|
|
17
17
|
*/
|
|
18
18
|
export function resolveCachedPrompts(config: McpConfig, cacheEnabled = true): PromptMetadata[] {
|