@fitchmultz/pi-mcp-adapter 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.
- package/CHANGELOG.md +750 -0
- package/LICENSE +21 -0
- package/OAUTH.md +462 -0
- package/README.md +820 -0
- package/abort.ts +34 -0
- package/agent-dir.ts +24 -0
- package/app-bridge.bundle.js +67 -0
- package/banner.png +0 -0
- package/checkpoint.ts +55 -0
- package/cli.js +222 -0
- package/commands.ts +619 -0
- package/config.ts +1204 -0
- package/consent-manager.ts +25 -0
- package/direct-tools.ts +408 -0
- package/dist/abort.d.ts +2 -0
- package/dist/abort.js +35 -0
- package/dist/agent-dir.d.ts +3 -0
- package/dist/agent-dir.js +21 -0
- package/dist/app-bridge.bundle.js +67 -0
- package/dist/checkpoint.d.ts +18 -0
- package/dist/checkpoint.js +53 -0
- package/dist/commands.d.ts +20 -0
- package/dist/commands.js +521 -0
- package/dist/config.d.ts +109 -0
- package/dist/config.js +1013 -0
- package/dist/consent-manager.d.ts +8 -0
- package/dist/consent-manager.js +21 -0
- package/dist/direct-tools.d.ts +10 -0
- package/dist/direct-tools.js +350 -0
- package/dist/docs/client-metadata.json +14 -0
- package/dist/elicitation-handler.d.ts +17 -0
- package/dist/elicitation-handler.js +308 -0
- package/dist/error-signal.d.ts +15 -0
- package/dist/error-signal.js +21 -0
- package/dist/glimpse-ui.d.ts +9 -0
- package/dist/glimpse-ui.js +75 -0
- package/dist/host-html-template.d.ts +15 -0
- package/dist/host-html-template.js +429 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +840 -0
- package/dist/init.d.ts +25 -0
- package/dist/init.js +562 -0
- package/dist/legacy-migration.d.ts +18 -0
- package/dist/legacy-migration.js +45 -0
- package/dist/lifecycle.d.ts +40 -0
- package/dist/lifecycle.js +167 -0
- package/dist/logger.d.ts +38 -0
- package/dist/logger.js +102 -0
- package/dist/mcp-auth-flow.d.ts +113 -0
- package/dist/mcp-auth-flow.js +834 -0
- package/dist/mcp-auth.d.ts +127 -0
- package/dist/mcp-auth.js +658 -0
- package/dist/mcp-callback-server.d.ts +56 -0
- package/dist/mcp-callback-server.js +371 -0
- package/dist/mcp-code.d.ts +51 -0
- package/dist/mcp-code.js +366 -0
- package/dist/mcp-keyring-helper.cjs +89 -0
- package/dist/mcp-oauth-provider.d.ts +154 -0
- package/dist/mcp-oauth-provider.js +672 -0
- package/dist/mcp-output-guard.d.ts +59 -0
- package/dist/mcp-output-guard.js +332 -0
- package/dist/mcp-panel.d.ts +66 -0
- package/dist/mcp-panel.js +889 -0
- package/dist/mcp-script-worker.mjs +128 -0
- package/dist/mcp-setup-panel.d.ts +84 -0
- package/dist/mcp-setup-panel.js +561 -0
- package/dist/mcp-status.d.ts +11 -0
- package/dist/mcp-status.js +96 -0
- package/dist/mcp-trace.d.ts +95 -0
- package/dist/mcp-trace.js +233 -0
- package/dist/metadata-cache.d.ts +18 -0
- package/dist/metadata-cache.js +277 -0
- package/dist/npx-resolver.d.ts +6 -0
- package/dist/npx-resolver.js +448 -0
- package/dist/onboarding-state.d.ts +11 -0
- package/dist/onboarding-state.js +47 -0
- package/dist/panel-keys.d.ts +16 -0
- package/dist/panel-keys.js +15 -0
- package/dist/prompts.d.ts +56 -0
- package/dist/prompts.js +284 -0
- package/dist/proxy-modes.d.ts +38 -0
- package/dist/proxy-modes.js +1235 -0
- package/dist/resource-tools.d.ts +1 -0
- package/dist/resource-tools.js +14 -0
- package/dist/runtime-owner.d.ts +18 -0
- package/dist/runtime-owner.js +117 -0
- package/dist/sampling-handler.d.ts +15 -0
- package/dist/sampling-handler.js +211 -0
- package/dist/search-ranking.d.ts +20 -0
- package/dist/search-ranking.js +180 -0
- package/dist/server-manager.d.ts +114 -0
- package/dist/server-manager.js +1175 -0
- package/dist/session-recovery.d.ts +31 -0
- package/dist/session-recovery.js +310 -0
- package/dist/state.d.ts +63 -0
- package/dist/state.js +1 -0
- package/dist/tool-approval.d.ts +10 -0
- package/dist/tool-approval.js +42 -0
- package/dist/tool-metadata.d.ts +9 -0
- package/dist/tool-metadata.js +193 -0
- package/dist/tool-registrar.d.ts +10 -0
- package/dist/tool-registrar.js +64 -0
- package/dist/tool-result-renderer.d.ts +45 -0
- package/dist/tool-result-renderer.js +224 -0
- package/dist/ts-shape.d.ts +2 -0
- package/dist/ts-shape.js +147 -0
- package/dist/types.d.ts +516 -0
- package/dist/types.js +168 -0
- package/dist/ui-resource-handler.d.ts +17 -0
- package/dist/ui-resource-handler.js +209 -0
- package/dist/ui-server.d.ts +51 -0
- package/dist/ui-server.js +669 -0
- package/dist/ui-session.d.ts +39 -0
- package/dist/ui-session.js +463 -0
- package/dist/ui-stream-types.d.ts +104 -0
- package/dist/ui-stream-types.js +58 -0
- package/dist/ui-tool-visibility.d.ts +4 -0
- package/dist/ui-tool-visibility.js +26 -0
- package/dist/unix-socket-transport.d.ts +15 -0
- package/dist/unix-socket-transport.js +84 -0
- package/dist/utils.d.ts +30 -0
- package/dist/utils.js +276 -0
- package/docs/client-metadata.json +14 -0
- package/elicitation-handler.ts +349 -0
- package/error-signal.ts +21 -0
- package/glimpse-ui.ts +80 -0
- package/host-html-template.ts +456 -0
- package/index.ts +900 -0
- package/init.ts +627 -0
- package/legacy-migration.ts +47 -0
- package/lifecycle.ts +165 -0
- package/logger.ts +127 -0
- package/mcp-auth-flow.ts +975 -0
- package/mcp-auth.ts +793 -0
- package/mcp-callback-server.ts +450 -0
- package/mcp-code.ts +386 -0
- package/mcp-keyring-helper.cjs +89 -0
- package/mcp-oauth-provider.ts +774 -0
- package/mcp-output-guard.ts +412 -0
- package/mcp-panel.ts +978 -0
- package/mcp-script-worker.mjs +128 -0
- package/mcp-setup-panel.ts +666 -0
- package/mcp-status.ts +109 -0
- package/mcp-trace.ts +295 -0
- package/metadata-cache.ts +333 -0
- package/npx-resolver.ts +488 -0
- package/onboarding-state.ts +58 -0
- package/package.json +182 -0
- package/panel-keys.ts +37 -0
- package/prompts.ts +337 -0
- package/proxy-modes.ts +1361 -0
- package/resource-tools.ts +17 -0
- package/runtime-owner.ts +123 -0
- package/sampling-handler.ts +273 -0
- package/scripts/build.mjs +131 -0
- package/scripts/prepare.mjs +102 -0
- package/search-ranking.ts +196 -0
- package/server-manager.ts +1320 -0
- package/session-recovery.ts +316 -0
- package/skills/mcp-scripting/SKILL.md +38 -0
- package/state.ts +64 -0
- package/tool-approval.ts +78 -0
- package/tool-metadata.ts +235 -0
- package/tool-registrar.ts +69 -0
- package/tool-result-renderer.ts +286 -0
- package/ts-shape.ts +145 -0
- package/tsconfig.build.json +13 -0
- package/tsconfig.json +16 -0
- package/types.ts +810 -0
- package/ui-resource-handler.ts +249 -0
- package/ui-server.ts +808 -0
- package/ui-session.ts +568 -0
- package/ui-stream-types.ts +89 -0
- package/ui-tool-visibility.ts +25 -0
- package/unix-socket-transport.ts +85 -0
- package/utils.ts +309 -0
|
@@ -0,0 +1,333 @@
|
|
|
1
|
+
// metadata-cache.ts - Persistent MCP metadata cache
|
|
2
|
+
import { existsSync, readFileSync, writeFileSync, renameSync, mkdirSync } from "node:fs";
|
|
3
|
+
import { dirname } from "node:path";
|
|
4
|
+
import { getAdapterPath } from "./agent-dir.ts";
|
|
5
|
+
import { createHash } from "node:crypto";
|
|
6
|
+
import { getToolUiResourceUri } from "@modelcontextprotocol/ext-apps/app-bridge";
|
|
7
|
+
import type {
|
|
8
|
+
CachedPrompt,
|
|
9
|
+
CachedResource,
|
|
10
|
+
CachedTool,
|
|
11
|
+
McpConfig,
|
|
12
|
+
McpTool,
|
|
13
|
+
McpResource,
|
|
14
|
+
McpPrompt,
|
|
15
|
+
McpPromptArgument,
|
|
16
|
+
MetadataCache,
|
|
17
|
+
ServerCacheEntry,
|
|
18
|
+
ServerEntry,
|
|
19
|
+
ToolMetadata,
|
|
20
|
+
PromptMetadata,
|
|
21
|
+
} from "./types.ts";
|
|
22
|
+
import { formatPromptCommandName, formatToolName, isServerDisabled, isToolAllowed, resolveToolPrefix, type ToolPrefix } from "./types.ts";
|
|
23
|
+
import { resourceNameToToolName } from "./resource-tools.ts";
|
|
24
|
+
import {
|
|
25
|
+
extractToolUiStreamMode,
|
|
26
|
+
interpolateEnvRecord,
|
|
27
|
+
resolveBearerToken,
|
|
28
|
+
resolveConfigPath,
|
|
29
|
+
resolveServerUrl,
|
|
30
|
+
} from "./utils.ts";
|
|
31
|
+
import { extractUiToolVisibility, isUiToolVisibleToModel } from "./ui-tool-visibility.ts";
|
|
32
|
+
|
|
33
|
+
const CACHE_VERSION = 1;
|
|
34
|
+
const CACHE_MAX_AGE_MS = 7 * 24 * 60 * 60 * 1000;
|
|
35
|
+
|
|
36
|
+
export type { CachedPrompt, CachedResource, CachedTool, MetadataCache, ServerCacheEntry } from "./types.ts";
|
|
37
|
+
|
|
38
|
+
export function getMetadataCachePath(): string {
|
|
39
|
+
return getAdapterPath("mcp-cache.json");
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function loadMetadataCache(enabled = true): MetadataCache | null {
|
|
43
|
+
if (!enabled) return null;
|
|
44
|
+
const cachePath = getMetadataCachePath();
|
|
45
|
+
if (!existsSync(cachePath)) return null;
|
|
46
|
+
try {
|
|
47
|
+
const raw = JSON.parse(readFileSync(cachePath, "utf-8"));
|
|
48
|
+
if (!raw || typeof raw !== "object") return null;
|
|
49
|
+
if (raw.version !== CACHE_VERSION) return null;
|
|
50
|
+
if (!raw.servers || typeof raw.servers !== "object") return null;
|
|
51
|
+
return raw as MetadataCache;
|
|
52
|
+
} catch {
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export function saveMetadataCache(cache: MetadataCache, enabled = true): void {
|
|
58
|
+
if (!enabled) return;
|
|
59
|
+
const cachePath = getMetadataCachePath();
|
|
60
|
+
const dir = dirname(cachePath);
|
|
61
|
+
mkdirSync(dir, { recursive: true });
|
|
62
|
+
|
|
63
|
+
let merged: MetadataCache = { version: CACHE_VERSION, servers: {} };
|
|
64
|
+
try {
|
|
65
|
+
if (existsSync(cachePath)) {
|
|
66
|
+
const existing = JSON.parse(readFileSync(cachePath, "utf-8")) as MetadataCache;
|
|
67
|
+
if (existing && existing.version === CACHE_VERSION && existing.servers) {
|
|
68
|
+
merged.servers = { ...existing.servers };
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
} catch {
|
|
72
|
+
// Ignore parse errors and proceed with empty cache
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
merged.version = CACHE_VERSION;
|
|
76
|
+
merged.servers = { ...merged.servers, ...cache.servers };
|
|
77
|
+
|
|
78
|
+
const tmpPath = `${cachePath}.${process.pid}.tmp`;
|
|
79
|
+
writeFileSync(tmpPath, JSON.stringify(merged, null, 2), "utf-8");
|
|
80
|
+
renameSync(tmpPath, cachePath);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export function computeServerHash(definition: ServerEntry): string {
|
|
84
|
+
// Hash only fields that affect server identity and tool/resource output.
|
|
85
|
+
// Exclude lifecycle, idleTimeout, requestTimeoutMs, debug — those are runtime behavior settings
|
|
86
|
+
// that don't change which tools a server exposes.
|
|
87
|
+
const identity: Record<string, unknown> = {
|
|
88
|
+
command: definition.command,
|
|
89
|
+
args: definition.args,
|
|
90
|
+
socket: resolveConfigPath(definition.socket),
|
|
91
|
+
env: interpolateEnvRecord(definition.env),
|
|
92
|
+
cwd: resolveConfigPath(definition.cwd),
|
|
93
|
+
url: resolveServerUrl(definition),
|
|
94
|
+
headers: interpolateEnvRecord(definition.headers),
|
|
95
|
+
auth: definition.auth,
|
|
96
|
+
bearerToken: resolveBearerToken(definition),
|
|
97
|
+
bearerTokenEnv: definition.bearerTokenEnv,
|
|
98
|
+
exposeResources: definition.exposeResources,
|
|
99
|
+
includeTools: definition.includeTools,
|
|
100
|
+
excludeTools: definition.excludeTools,
|
|
101
|
+
};
|
|
102
|
+
const normalized = stableStringify(identity);
|
|
103
|
+
return createHash("sha256").update(normalized).digest("hex");
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export function isServerCacheValid(
|
|
107
|
+
entry: ServerCacheEntry,
|
|
108
|
+
definition: ServerEntry,
|
|
109
|
+
maxAgeMs: number = CACHE_MAX_AGE_MS
|
|
110
|
+
): boolean {
|
|
111
|
+
let configHash: string;
|
|
112
|
+
try {
|
|
113
|
+
configHash = computeServerHash(definition);
|
|
114
|
+
} catch {
|
|
115
|
+
return false;
|
|
116
|
+
}
|
|
117
|
+
if (!entry || entry.configHash !== configHash) return false;
|
|
118
|
+
if (!entry.cachedAt || typeof entry.cachedAt !== "number") return false;
|
|
119
|
+
if (maxAgeMs > 0 && Date.now() - entry.cachedAt > maxAgeMs) return false;
|
|
120
|
+
return true;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
export function parseDirectToolSelectors(selectors: string[]): {
|
|
124
|
+
servers: Set<string>;
|
|
125
|
+
tools: Map<string, Set<string>>;
|
|
126
|
+
} {
|
|
127
|
+
const servers = new Set<string>();
|
|
128
|
+
const tools = new Map<string, Set<string>>();
|
|
129
|
+
|
|
130
|
+
for (let selector of selectors) {
|
|
131
|
+
selector = selector.replace(/\/+$/, "");
|
|
132
|
+
if (selector.includes("/")) {
|
|
133
|
+
const [server, tool] = selector.split("/", 2);
|
|
134
|
+
if (server && tool) {
|
|
135
|
+
const serverTools = tools.get(server) ?? new Set<string>();
|
|
136
|
+
serverTools.add(tool);
|
|
137
|
+
tools.set(server, serverTools);
|
|
138
|
+
} else if (server) {
|
|
139
|
+
servers.add(server);
|
|
140
|
+
}
|
|
141
|
+
} else if (selector) {
|
|
142
|
+
servers.add(selector);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
return { servers, tools };
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
export function getMissingConfiguredDirectToolServers(
|
|
150
|
+
config: McpConfig,
|
|
151
|
+
cache: MetadataCache | null,
|
|
152
|
+
envOverride?: string[],
|
|
153
|
+
): string[] {
|
|
154
|
+
const missing: string[] = [];
|
|
155
|
+
const globalDirect = config.settings?.directTools;
|
|
156
|
+
const envSelection = envOverride ? parseDirectToolSelectors(envOverride) : null;
|
|
157
|
+
|
|
158
|
+
for (const [serverName, definition] of Object.entries(config.mcpServers)) {
|
|
159
|
+
if (isServerDisabled(definition)) continue;
|
|
160
|
+
const hasDirectTools = envSelection
|
|
161
|
+
? envSelection.servers.has(serverName) || envSelection.tools.has(serverName)
|
|
162
|
+
: definition.directTools !== undefined
|
|
163
|
+
? !!definition.directTools
|
|
164
|
+
: !!globalDirect;
|
|
165
|
+
|
|
166
|
+
if (!hasDirectTools) continue;
|
|
167
|
+
|
|
168
|
+
const serverCache = cache?.servers?.[serverName];
|
|
169
|
+
if (!serverCache || !isServerCacheValid(serverCache, definition)) {
|
|
170
|
+
missing.push(serverName);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
return missing;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
export function reconstructToolMetadata(
|
|
178
|
+
serverName: string,
|
|
179
|
+
entry: ServerCacheEntry,
|
|
180
|
+
prefix: ToolPrefix,
|
|
181
|
+
definition: Pick<ServerEntry, "exposeResources" | "includeTools" | "excludeTools" | "toolPrefix">
|
|
182
|
+
): ToolMetadata[] {
|
|
183
|
+
const metadata: ToolMetadata[] = [];
|
|
184
|
+
const seenNames = new Set<string>();
|
|
185
|
+
const effectivePrefix = resolveToolPrefix(definition, prefix);
|
|
186
|
+
|
|
187
|
+
for (const tool of entry.tools ?? []) {
|
|
188
|
+
if (!tool?.name) continue;
|
|
189
|
+
if (!isUiToolVisibleToModel(tool.uiVisibility)) {
|
|
190
|
+
continue;
|
|
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);
|
|
227
|
+
|
|
228
|
+
metadata.push({
|
|
229
|
+
name,
|
|
230
|
+
originalName: baseName,
|
|
231
|
+
description: resource.description ?? `Read resource: ${resource.uri}`,
|
|
232
|
+
resourceUri: resource.uri,
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
return metadata;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
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
|
+
});
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
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
|
+
}));
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
export function serializePrompts(prompts: McpPrompt[]): CachedPrompt[] {
|
|
270
|
+
return (prompts ?? [])
|
|
271
|
+
.filter(prompt => prompt?.name)
|
|
272
|
+
.map(prompt => ({
|
|
273
|
+
name: prompt.name,
|
|
274
|
+
...(prompt.title !== undefined ? { title: prompt.title } : {}),
|
|
275
|
+
...(prompt.description !== undefined ? { description: prompt.description } : {}),
|
|
276
|
+
...(Array.isArray(prompt.arguments)
|
|
277
|
+
? {
|
|
278
|
+
arguments: prompt.arguments.filter(argument => argument?.name).map(argument => ({
|
|
279
|
+
name: argument.name,
|
|
280
|
+
...(argument.description !== undefined ? { description: argument.description } : {}),
|
|
281
|
+
...(argument.required !== undefined ? { required: argument.required } : {}),
|
|
282
|
+
})),
|
|
283
|
+
}
|
|
284
|
+
: {}),
|
|
285
|
+
}));
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
export function reconstructPromptMetadata(
|
|
289
|
+
serverName: string,
|
|
290
|
+
prompts: ReadonlyArray<McpPrompt | CachedPrompt>,
|
|
291
|
+
prefix: ToolPrefix,
|
|
292
|
+
definition?: Pick<ServerEntry, "toolPrefix">,
|
|
293
|
+
): PromptMetadata[] {
|
|
294
|
+
const effectivePrefix = resolveToolPrefix(definition, prefix);
|
|
295
|
+
return (prompts ?? []).filter(prompt => prompt?.name).map(prompt => {
|
|
296
|
+
const args: McpPromptArgument[] = Array.isArray(prompt.arguments)
|
|
297
|
+
? prompt.arguments.filter(argument => argument?.name).map(argument => ({
|
|
298
|
+
name: argument.name,
|
|
299
|
+
...(argument.description !== undefined ? { description: argument.description } : {}),
|
|
300
|
+
...(argument.required !== undefined ? { required: argument.required } : {}),
|
|
301
|
+
}))
|
|
302
|
+
: [];
|
|
303
|
+
return {
|
|
304
|
+
serverName,
|
|
305
|
+
originalName: prompt.name,
|
|
306
|
+
commandName: formatPromptCommandName(prompt.name, serverName, effectivePrefix),
|
|
307
|
+
...(prompt.title !== undefined ? { title: prompt.title } : {}),
|
|
308
|
+
description: prompt.description ?? "",
|
|
309
|
+
arguments: args,
|
|
310
|
+
};
|
|
311
|
+
});
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
function stableStringify(value: unknown): string {
|
|
315
|
+
if (value === null || value === undefined || typeof value !== "object") {
|
|
316
|
+
const serialized = JSON.stringify(value);
|
|
317
|
+
return serialized === undefined ? "undefined" : serialized;
|
|
318
|
+
}
|
|
319
|
+
if (Array.isArray(value)) {
|
|
320
|
+
return `[${value.map(v => stableStringify(v)).join(",")}]`;
|
|
321
|
+
}
|
|
322
|
+
const obj = value as Record<string, unknown>;
|
|
323
|
+
const keys = Object.keys(obj).sort();
|
|
324
|
+
return `{${keys.map(k => `${JSON.stringify(k)}:${stableStringify(obj[k])}`).join(",")}}`;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
function tryGetToolUiResourceUri(tool: McpTool): string | undefined {
|
|
328
|
+
try {
|
|
329
|
+
return getToolUiResourceUri({ _meta: tool._meta });
|
|
330
|
+
} catch {
|
|
331
|
+
return undefined;
|
|
332
|
+
}
|
|
333
|
+
}
|