@akon-labs/nexus-cli 0.1.8 → 0.1.10
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 +27 -0
- package/LICENSE +1 -0
- package/README.md +3 -2
- package/dist/app-bridge.bundle.js +67 -0
- package/dist/cli.js +19934 -1869
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +1 -1
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/auto-model.d.ts +1 -1
- package/dist/core/auto-model.d.ts.map +1 -1
- package/dist/core/auto-model.js +1 -1
- package/dist/core/auto-model.js.map +1 -1
- package/dist/core/model-resolver.d.ts +2 -2
- package/dist/core/model-resolver.d.ts.map +1 -1
- package/dist/core/model-resolver.js +3 -3
- package/dist/core/model-resolver.js.map +1 -1
- package/dist/core/package-manager.d.ts +15 -0
- package/dist/core/package-manager.d.ts.map +1 -1
- package/dist/core/package-manager.js +50 -0
- package/dist/core/package-manager.js.map +1 -1
- package/dist/core/sdk.d.ts.map +1 -1
- package/dist/core/sdk.js +1 -1
- package/dist/core/sdk.js.map +1 -1
- package/dist/core/slash-commands.d.ts.map +1 -1
- package/dist/core/slash-commands.js +3 -3
- package/dist/core/slash-commands.js.map +1 -1
- package/dist/core/system-prompt.d.ts.map +1 -1
- package/dist/core/system-prompt.js +1 -1
- package/dist/core/system-prompt.js.map +1 -1
- package/dist/core/tools/index.d.ts +1 -1
- package/dist/core/tools/index.d.ts.map +1 -1
- package/dist/core/tools/index.js +5 -5
- package/dist/core/tools/index.js.map +1 -1
- package/dist/core/tools/websearch.d.ts.map +1 -1
- package/dist/core/tools/websearch.js +3 -3
- package/dist/core/tools/websearch.js.map +1 -1
- package/dist/extensions/gitnexus/gateway.d.ts +6 -6
- package/dist/extensions/gitnexus/gateway.d.ts.map +1 -1
- package/dist/extensions/gitnexus/gateway.js +4 -4
- package/dist/extensions/gitnexus/gateway.js.map +1 -1
- package/dist/extensions/gitnexus/index.d.ts.map +1 -1
- package/dist/extensions/gitnexus/index.js +4 -2
- package/dist/extensions/gitnexus/index.js.map +1 -1
- package/dist/extensions/mcp/abort.d.ts +3 -0
- package/dist/extensions/mcp/abort.d.ts.map +1 -0
- package/dist/extensions/mcp/abort.js +41 -0
- package/dist/extensions/mcp/abort.js.map +1 -0
- package/dist/extensions/mcp/agent-dir.d.ts +6 -0
- package/dist/extensions/mcp/agent-dir.d.ts.map +1 -0
- package/dist/extensions/mcp/agent-dir.js +18 -0
- package/dist/extensions/mcp/agent-dir.js.map +1 -0
- package/dist/extensions/mcp/agent-plugin-loader.d.ts +9 -0
- package/dist/extensions/mcp/agent-plugin-loader.d.ts.map +1 -0
- package/dist/extensions/mcp/agent-plugin-loader.js +384 -0
- package/dist/extensions/mcp/agent-plugin-loader.js.map +1 -0
- package/dist/extensions/mcp/app-bridge.bundle.js +67 -0
- package/dist/extensions/mcp/commands.d.ts +27 -0
- package/dist/extensions/mcp/commands.d.ts.map +1 -0
- package/dist/extensions/mcp/commands.js +629 -0
- package/dist/extensions/mcp/commands.js.map +1 -0
- package/dist/extensions/mcp/config.d.ts +121 -38
- package/dist/extensions/mcp/config.d.ts.map +1 -1
- package/dist/extensions/mcp/config.js +1030 -70
- package/dist/extensions/mcp/config.js.map +1 -1
- package/dist/extensions/mcp/consent-manager.d.ts +14 -0
- package/dist/extensions/mcp/consent-manager.d.ts.map +1 -0
- package/dist/extensions/mcp/consent-manager.js +60 -0
- package/dist/extensions/mcp/consent-manager.js.map +1 -0
- package/dist/extensions/mcp/direct-tools.d.ts +11 -0
- package/dist/extensions/mcp/direct-tools.d.ts.map +1 -0
- package/dist/extensions/mcp/direct-tools.js +562 -0
- package/dist/extensions/mcp/direct-tools.js.map +1 -0
- package/dist/extensions/mcp/elicitation-handler.d.ts +18 -0
- package/dist/extensions/mcp/elicitation-handler.d.ts.map +1 -0
- package/dist/extensions/mcp/elicitation-handler.js +320 -0
- package/dist/extensions/mcp/elicitation-handler.js.map +1 -0
- package/dist/extensions/mcp/error-signal.d.ts +16 -0
- package/dist/extensions/mcp/error-signal.d.ts.map +1 -0
- package/dist/extensions/mcp/error-signal.js +22 -0
- package/dist/extensions/mcp/error-signal.js.map +1 -0
- package/dist/extensions/mcp/errors.d.ts +104 -0
- package/dist/extensions/mcp/errors.d.ts.map +1 -0
- package/dist/extensions/mcp/errors.js +171 -0
- package/dist/extensions/mcp/errors.js.map +1 -0
- package/dist/extensions/mcp/glimpse-ui.d.ts +10 -0
- package/dist/extensions/mcp/glimpse-ui.d.ts.map +1 -0
- package/dist/extensions/mcp/glimpse-ui.js +67 -0
- package/dist/extensions/mcp/glimpse-ui.js.map +1 -0
- package/dist/extensions/mcp/host-html-template.d.ts +17 -0
- package/dist/extensions/mcp/host-html-template.d.ts.map +1 -0
- package/dist/extensions/mcp/host-html-template.js +442 -0
- package/dist/extensions/mcp/host-html-template.js.map +1 -0
- package/dist/extensions/mcp/index.d.ts +20 -15
- package/dist/extensions/mcp/index.d.ts.map +1 -1
- package/dist/extensions/mcp/index.js +1064 -82
- package/dist/extensions/mcp/index.js.map +1 -1
- package/dist/extensions/mcp/init.d.ts +28 -0
- package/dist/extensions/mcp/init.d.ts.map +1 -0
- package/dist/extensions/mcp/init.js +631 -0
- package/dist/extensions/mcp/init.js.map +1 -0
- package/dist/extensions/mcp/json-schema-validator.d.ts +3 -0
- package/dist/extensions/mcp/json-schema-validator.d.ts.map +1 -0
- package/dist/extensions/mcp/json-schema-validator.js +53 -0
- package/dist/extensions/mcp/json-schema-validator.js.map +1 -0
- package/dist/extensions/mcp/lifecycle.d.ts +56 -0
- package/dist/extensions/mcp/lifecycle.d.ts.map +1 -0
- package/dist/extensions/mcp/lifecycle.js +395 -0
- package/dist/extensions/mcp/lifecycle.js.map +1 -0
- package/dist/extensions/mcp/logger.d.ts +52 -0
- package/dist/extensions/mcp/logger.d.ts.map +1 -0
- package/dist/extensions/mcp/logger.js +134 -0
- package/dist/extensions/mcp/logger.js.map +1 -0
- package/dist/extensions/mcp/mcp-auth-flow.d.ts +117 -0
- package/dist/extensions/mcp/mcp-auth-flow.d.ts.map +1 -0
- package/dist/extensions/mcp/mcp-auth-flow.js +869 -0
- package/dist/extensions/mcp/mcp-auth-flow.js.map +1 -0
- package/dist/extensions/mcp/mcp-auth.d.ts +162 -0
- package/dist/extensions/mcp/mcp-auth.d.ts.map +1 -0
- package/dist/extensions/mcp/mcp-auth.js +897 -0
- package/dist/extensions/mcp/mcp-auth.js.map +1 -0
- package/dist/extensions/mcp/mcp-bearer-store.d.ts +25 -0
- package/dist/extensions/mcp/mcp-bearer-store.d.ts.map +1 -0
- package/dist/extensions/mcp/mcp-bearer-store.js +350 -0
- package/dist/extensions/mcp/mcp-bearer-store.js.map +1 -0
- package/dist/extensions/mcp/mcp-callback-server.d.ts +54 -0
- package/dist/extensions/mcp/mcp-callback-server.d.ts.map +1 -0
- package/dist/extensions/mcp/mcp-callback-server.js +454 -0
- package/dist/extensions/mcp/mcp-callback-server.js.map +1 -0
- package/dist/extensions/mcp/mcp-code.d.ts +48 -0
- package/dist/extensions/mcp/mcp-code.d.ts.map +1 -0
- package/dist/extensions/mcp/mcp-code.js +358 -0
- package/dist/extensions/mcp/mcp-code.js.map +1 -0
- package/dist/extensions/mcp/mcp-keyring-helper.cjs +89 -0
- package/dist/extensions/mcp/mcp-oauth-provider.d.ts +135 -0
- package/dist/extensions/mcp/mcp-oauth-provider.d.ts.map +1 -0
- package/dist/extensions/mcp/mcp-oauth-provider.js +491 -0
- package/dist/extensions/mcp/mcp-oauth-provider.js.map +1 -0
- package/dist/extensions/mcp/mcp-output-guard.d.ts +59 -0
- package/dist/extensions/mcp/mcp-output-guard.d.ts.map +1 -0
- package/dist/extensions/mcp/mcp-output-guard.js +343 -0
- package/dist/extensions/mcp/mcp-output-guard.js.map +1 -0
- package/dist/extensions/mcp/mcp-panel.d.ts +71 -0
- package/dist/extensions/mcp/mcp-panel.d.ts.map +1 -0
- package/dist/extensions/mcp/mcp-panel.js +973 -0
- package/dist/extensions/mcp/mcp-panel.js.map +1 -0
- package/dist/extensions/mcp/mcp-probe.d.ts +7 -0
- package/dist/extensions/mcp/mcp-probe.d.ts.map +1 -0
- package/dist/extensions/mcp/mcp-probe.js +155 -0
- package/dist/extensions/mcp/mcp-probe.js.map +1 -0
- package/dist/extensions/mcp/mcp-script-worker.mjs +130 -0
- package/dist/extensions/mcp/mcp-setup-panel.d.ts +85 -0
- package/dist/extensions/mcp/mcp-setup-panel.d.ts.map +1 -0
- package/dist/extensions/mcp/mcp-setup-panel.js +618 -0
- package/dist/extensions/mcp/mcp-setup-panel.js.map +1 -0
- package/dist/extensions/mcp/mcp-status.d.ts +12 -0
- package/dist/extensions/mcp/mcp-status.d.ts.map +1 -0
- package/dist/extensions/mcp/mcp-status.js +98 -0
- package/dist/extensions/mcp/mcp-status.js.map +1 -0
- package/dist/extensions/mcp/mcp-trace.d.ts +95 -0
- package/dist/extensions/mcp/mcp-trace.d.ts.map +1 -0
- package/dist/extensions/mcp/mcp-trace.js +252 -0
- package/dist/extensions/mcp/mcp-trace.js.map +1 -0
- package/dist/extensions/mcp/metadata-cache.d.ts +20 -0
- package/dist/extensions/mcp/metadata-cache.d.ts.map +1 -0
- package/dist/extensions/mcp/metadata-cache.js +318 -0
- package/dist/extensions/mcp/metadata-cache.js.map +1 -0
- package/dist/extensions/mcp/npx-resolver.d.ts +7 -0
- package/dist/extensions/mcp/npx-resolver.d.ts.map +1 -0
- package/dist/extensions/mcp/npx-resolver.js +505 -0
- package/dist/extensions/mcp/npx-resolver.js.map +1 -0
- package/dist/extensions/mcp/oauth.d.ts +21 -0
- package/dist/extensions/mcp/oauth.d.ts.map +1 -0
- package/dist/extensions/mcp/oauth.js +15 -0
- package/dist/extensions/mcp/oauth.js.map +1 -0
- package/dist/extensions/mcp/onboarding-state.d.ts +13 -0
- package/dist/extensions/mcp/onboarding-state.d.ts.map +1 -0
- package/dist/extensions/mcp/onboarding-state.js +65 -0
- package/dist/extensions/mcp/onboarding-state.js.map +1 -0
- package/dist/extensions/mcp/package-mcp-loader.d.ts +3 -0
- package/dist/extensions/mcp/package-mcp-loader.d.ts.map +1 -0
- package/dist/extensions/mcp/package-mcp-loader.js +170 -0
- package/dist/extensions/mcp/package-mcp-loader.js.map +1 -0
- package/dist/extensions/mcp/panel-keys.d.ts +21 -0
- package/dist/extensions/mcp/panel-keys.d.ts.map +1 -0
- package/dist/extensions/mcp/panel-keys.js +29 -0
- package/dist/extensions/mcp/panel-keys.js.map +1 -0
- package/dist/extensions/mcp/prompts.d.ts +63 -0
- package/dist/extensions/mcp/prompts.d.ts.map +1 -0
- package/dist/extensions/mcp/prompts.js +297 -0
- package/dist/extensions/mcp/prompts.js.map +1 -0
- package/dist/extensions/mcp/proxy-modes.d.ts +15 -0
- package/dist/extensions/mcp/proxy-modes.d.ts.map +1 -0
- package/dist/extensions/mcp/proxy-modes.js +1421 -0
- package/dist/extensions/mcp/proxy-modes.js.map +1 -0
- package/dist/extensions/mcp/request-headers-command.d.ts +11 -0
- package/dist/extensions/mcp/request-headers-command.d.ts.map +1 -0
- package/dist/extensions/mcp/request-headers-command.js +334 -0
- package/dist/extensions/mcp/request-headers-command.js.map +1 -0
- package/dist/extensions/mcp/resource-tools.d.ts +2 -0
- package/dist/extensions/mcp/resource-tools.d.ts.map +1 -0
- package/dist/extensions/mcp/resource-tools.js +15 -0
- package/dist/extensions/mcp/resource-tools.js.map +1 -0
- package/dist/extensions/mcp/runtime-owner.d.ts +14 -0
- package/dist/extensions/mcp/runtime-owner.d.ts.map +1 -0
- package/dist/extensions/mcp/runtime-owner.js +84 -0
- package/dist/extensions/mcp/runtime-owner.js.map +1 -0
- package/dist/extensions/mcp/sampling-handler.d.ts +18 -0
- package/dist/extensions/mcp/sampling-handler.d.ts.map +1 -0
- package/dist/extensions/mcp/sampling-handler.js +229 -0
- package/dist/extensions/mcp/sampling-handler.js.map +1 -0
- package/dist/extensions/mcp/search-ranking.d.ts +25 -0
- package/dist/extensions/mcp/search-ranking.d.ts.map +1 -0
- package/dist/extensions/mcp/search-ranking.js +234 -0
- package/dist/extensions/mcp/search-ranking.js.map +1 -0
- package/dist/extensions/mcp/server-manager.d.ts +116 -0
- package/dist/extensions/mcp/server-manager.d.ts.map +1 -0
- package/dist/extensions/mcp/server-manager.js +1052 -0
- package/dist/extensions/mcp/server-manager.js.map +1 -0
- package/dist/extensions/mcp/session-recovery.d.ts +27 -0
- package/dist/extensions/mcp/session-recovery.d.ts.map +1 -0
- package/dist/extensions/mcp/session-recovery.js +140 -0
- package/dist/extensions/mcp/session-recovery.js.map +1 -0
- package/dist/extensions/mcp/skills/mcp-scripting/SKILL.md +38 -0
- package/dist/extensions/mcp/state.d.ts +62 -0
- package/dist/extensions/mcp/state.d.ts.map +1 -0
- package/dist/extensions/mcp/state.js +2 -0
- package/dist/extensions/mcp/state.js.map +1 -0
- package/dist/extensions/mcp/tool-approval.d.ts +11 -0
- package/dist/extensions/mcp/tool-approval.d.ts.map +1 -0
- package/dist/extensions/mcp/tool-approval.js +156 -0
- package/dist/extensions/mcp/tool-approval.js.map +1 -0
- package/dist/extensions/mcp/tool-metadata.d.ts +11 -0
- package/dist/extensions/mcp/tool-metadata.d.ts.map +1 -0
- package/dist/extensions/mcp/tool-metadata.js +264 -0
- package/dist/extensions/mcp/tool-metadata.js.map +1 -0
- package/dist/extensions/mcp/tool-registrar.d.ts +21 -0
- package/dist/extensions/mcp/tool-registrar.d.ts.map +1 -0
- package/dist/extensions/mcp/tool-registrar.js +229 -0
- package/dist/extensions/mcp/tool-registrar.js.map +1 -0
- package/dist/extensions/mcp/tool-result-renderer.d.ts +83 -0
- package/dist/extensions/mcp/tool-result-renderer.d.ts.map +1 -0
- package/dist/extensions/mcp/tool-result-renderer.js +345 -0
- package/dist/extensions/mcp/tool-result-renderer.js.map +1 -0
- package/dist/extensions/mcp/ts-shape.d.ts +3 -0
- package/dist/extensions/mcp/ts-shape.d.ts.map +1 -0
- package/dist/extensions/mcp/ts-shape.js +160 -0
- package/dist/extensions/mcp/ts-shape.js.map +1 -0
- package/dist/extensions/mcp/types.d.ts +521 -0
- package/dist/extensions/mcp/types.d.ts.map +1 -0
- package/dist/extensions/mcp/types.js +294 -0
- package/dist/extensions/mcp/types.js.map +1 -0
- package/dist/extensions/mcp/ui-app-bridge-helpers.d.ts +7 -0
- package/dist/extensions/mcp/ui-app-bridge-helpers.d.ts.map +1 -0
- package/dist/extensions/mcp/ui-app-bridge-helpers.js +37 -0
- package/dist/extensions/mcp/ui-app-bridge-helpers.js.map +1 -0
- package/dist/extensions/mcp/ui-resource-handler.d.ts +18 -0
- package/dist/extensions/mcp/ui-resource-handler.d.ts.map +1 -0
- package/dist/extensions/mcp/ui-resource-handler.js +210 -0
- package/dist/extensions/mcp/ui-resource-handler.js.map +1 -0
- package/dist/extensions/mcp/ui-server.d.ts +52 -0
- package/dist/extensions/mcp/ui-server.d.ts.map +1 -0
- package/dist/extensions/mcp/ui-server.js +740 -0
- package/dist/extensions/mcp/ui-server.js.map +1 -0
- package/dist/extensions/mcp/ui-session.d.ts +40 -0
- package/dist/extensions/mcp/ui-session.d.ts.map +1 -0
- package/dist/extensions/mcp/ui-session.js +477 -0
- package/dist/extensions/mcp/ui-session.js.map +1 -0
- package/dist/extensions/mcp/ui-stream-types.d.ts +105 -0
- package/dist/extensions/mcp/ui-stream-types.d.ts.map +1 -0
- package/dist/extensions/mcp/ui-stream-types.js +61 -0
- package/dist/extensions/mcp/ui-stream-types.js.map +1 -0
- package/dist/extensions/mcp/ui-tool-visibility.d.ts +5 -0
- package/dist/extensions/mcp/ui-tool-visibility.d.ts.map +1 -0
- package/dist/extensions/mcp/ui-tool-visibility.js +27 -0
- package/dist/extensions/mcp/ui-tool-visibility.js.map +1 -0
- package/dist/extensions/mcp/unix-socket-transport.d.ts +15 -0
- package/dist/extensions/mcp/unix-socket-transport.d.ts.map +1 -0
- package/dist/extensions/mcp/unix-socket-transport.js +85 -0
- package/dist/extensions/mcp/unix-socket-transport.js.map +1 -0
- package/dist/extensions/mcp/utils.d.ts +32 -0
- package/dist/extensions/mcp/utils.d.ts.map +1 -0
- package/dist/extensions/mcp/utils.js +336 -0
- package/dist/extensions/mcp/utils.js.map +1 -0
- package/dist/extensions/subagent/index.d.ts +62 -3
- package/dist/extensions/subagent/index.d.ts.map +1 -1
- package/dist/extensions/subagent/index.js +193 -58
- package/dist/extensions/subagent/index.js.map +1 -1
- package/dist/extensions/web-access.d.ts.map +1 -1
- package/dist/extensions/web-access.js +1 -1
- package/dist/extensions/web-access.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +20074 -2004
- package/dist/index.js.map +1 -1
- package/dist/mcp-keyring-helper.cjs +89 -0
- package/dist/mcp-script-worker.mjs +130 -0
- package/dist/modes/interactive/components/model-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/model-selector.js +1 -1
- package/dist/modes/interactive/components/model-selector.js.map +1 -1
- package/dist/modes/interactive/components/response-separator.d.ts +1 -1
- package/dist/modes/interactive/components/response-separator.d.ts.map +1 -1
- package/dist/modes/interactive/components/response-separator.js +1 -1
- package/dist/modes/interactive/components/response-separator.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +7 -5
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/rpc-entry.js +19934 -1869
- package/dist/skills/mcp-scripting/SKILL.md +38 -0
- package/dist/vendor/ai.js +282 -207
- package/docs/mcp.md +178 -0
- package/docs/pi-subagents.md +75 -0
- package/docs/quickstart.md +1 -1
- package/docs/usage.md +2 -1
- package/examples/extensions/pi-subagents-worker-policy.ts +86 -0
- package/npm-shrinkwrap.json +763 -6
- package/package.json +15 -4
- package/dist/extensions/mcp/client.d.ts +0 -86
- package/dist/extensions/mcp/client.d.ts.map +0 -1
- package/dist/extensions/mcp/client.js +0 -275
- package/dist/extensions/mcp/client.js.map +0 -1
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
import { Buffer } from "node:buffer";
|
|
2
|
+
import { appendFile, mkdir, writeFile } from "node:fs/promises";
|
|
3
|
+
import { dirname, isAbsolute, resolve } from "node:path";
|
|
4
|
+
export const MCP_TRACE_SCHEMA_VERSION = 1;
|
|
5
|
+
export const DEFAULT_MCP_TRACE_MAX_BYTES = 256 * 1024;
|
|
6
|
+
export const DEFAULT_MCP_TRACE_MAX_EVENTS = 10_000;
|
|
7
|
+
function boundedPositiveInteger(value, fallback) {
|
|
8
|
+
if (!Number.isFinite(value) || value === undefined || value <= 0)
|
|
9
|
+
return fallback;
|
|
10
|
+
return Math.floor(value);
|
|
11
|
+
}
|
|
12
|
+
/** Redact strings before they can become durable trace metadata. */
|
|
13
|
+
export function redactTraceText(value, maxLength = 160) {
|
|
14
|
+
let redacted = value
|
|
15
|
+
.replace(/\b[a-z][a-z\d+.-]*:\/\/[^\s"'<>]+/gi, "[REDACTED_URL]")
|
|
16
|
+
.replace(/\b(?:bearer|basic)\s+[A-Za-z0-9._~+/=-]+/gi, "[REDACTED_AUTH]")
|
|
17
|
+
.replace(/\b(token|secret|password|passwd|api[_-]?key|authorization|cookie)\s*[:=]\s*[^\s,;]+/gi, "$1=[REDACTED]");
|
|
18
|
+
if (redacted === value && /\b(?:token|secret|password|passwd|api[_-]?key|authorization|cookie)\b/i.test(value)) {
|
|
19
|
+
return "[REDACTED]";
|
|
20
|
+
}
|
|
21
|
+
if (redacted.length > maxLength)
|
|
22
|
+
redacted = `${redacted.slice(0, maxLength - 1)}…`;
|
|
23
|
+
return redacted;
|
|
24
|
+
}
|
|
25
|
+
function messageKind(message) {
|
|
26
|
+
if ("method" in message)
|
|
27
|
+
return "id" in message ? "request" : "notification";
|
|
28
|
+
return "response";
|
|
29
|
+
}
|
|
30
|
+
function traceId(value) {
|
|
31
|
+
if (value === null)
|
|
32
|
+
return null;
|
|
33
|
+
if (typeof value === "number" && Number.isFinite(value))
|
|
34
|
+
return value;
|
|
35
|
+
if (typeof value === "string")
|
|
36
|
+
return "[REDACTED_ID]";
|
|
37
|
+
return undefined;
|
|
38
|
+
}
|
|
39
|
+
function messageBytes(message) {
|
|
40
|
+
try {
|
|
41
|
+
return Buffer.byteLength(JSON.stringify(message), "utf8");
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
return undefined;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
export function createMcpTraceEvent(direction, server, transport, message, status, options) {
|
|
48
|
+
const kind = messageKind(message);
|
|
49
|
+
const bytes = messageBytes(message);
|
|
50
|
+
const event = {
|
|
51
|
+
version: MCP_TRACE_SCHEMA_VERSION,
|
|
52
|
+
timestamp: new Date().toISOString(),
|
|
53
|
+
direction,
|
|
54
|
+
server: redactTraceText(server, 120),
|
|
55
|
+
transport,
|
|
56
|
+
kind,
|
|
57
|
+
status,
|
|
58
|
+
...(bytes !== undefined ? { bytes } : {}),
|
|
59
|
+
};
|
|
60
|
+
if ("method" in message)
|
|
61
|
+
event.method = redactTraceText(message.method, 120);
|
|
62
|
+
if ("id" in message)
|
|
63
|
+
event.id = traceId(message.id) ?? null;
|
|
64
|
+
const relatedRequestId = traceId(options?.relatedRequestId);
|
|
65
|
+
if (relatedRequestId !== undefined && relatedRequestId !== null)
|
|
66
|
+
event.relatedRequestId = relatedRequestId;
|
|
67
|
+
if ("error" in message && message.error && typeof message.error.code === "number") {
|
|
68
|
+
event.errorCode = message.error.code;
|
|
69
|
+
}
|
|
70
|
+
if (options?.durationMs !== undefined && Number.isFinite(options.durationMs)) {
|
|
71
|
+
event.durationMs = Math.max(0, Math.round(options.durationMs * 100) / 100);
|
|
72
|
+
}
|
|
73
|
+
return event;
|
|
74
|
+
}
|
|
75
|
+
export class McpTraceWriter {
|
|
76
|
+
maxBytes;
|
|
77
|
+
maxEvents;
|
|
78
|
+
append;
|
|
79
|
+
resetFile;
|
|
80
|
+
makeDirectory;
|
|
81
|
+
bytesWritten = 0;
|
|
82
|
+
eventsWritten = 0;
|
|
83
|
+
queue = Promise.resolve();
|
|
84
|
+
disabled = false;
|
|
85
|
+
initializationFailed = false;
|
|
86
|
+
fileReady;
|
|
87
|
+
options;
|
|
88
|
+
constructor(options) {
|
|
89
|
+
this.options = options;
|
|
90
|
+
this.maxBytes = boundedPositiveInteger(options.maxBytes, DEFAULT_MCP_TRACE_MAX_BYTES);
|
|
91
|
+
this.maxEvents = boundedPositiveInteger(options.maxEvents, DEFAULT_MCP_TRACE_MAX_EVENTS);
|
|
92
|
+
this.append =
|
|
93
|
+
options.appendFile ??
|
|
94
|
+
(async (path, data, appendOptions) => {
|
|
95
|
+
await appendFile(path, data, appendOptions);
|
|
96
|
+
});
|
|
97
|
+
this.resetFile =
|
|
98
|
+
options.writeFile ??
|
|
99
|
+
(async (path, data, writeOptions) => {
|
|
100
|
+
await writeFile(path, data, writeOptions);
|
|
101
|
+
});
|
|
102
|
+
this.makeDirectory =
|
|
103
|
+
options.mkdir ??
|
|
104
|
+
(async (path) => {
|
|
105
|
+
await mkdir(path, { recursive: true });
|
|
106
|
+
return undefined;
|
|
107
|
+
});
|
|
108
|
+
this.fileReady = this.makeDirectory(dirname(this.options.filePath), { recursive: true })
|
|
109
|
+
.then(() => this.resetFile(this.options.filePath, "", { encoding: "utf8" }))
|
|
110
|
+
.catch(() => {
|
|
111
|
+
// Tracing must never change MCP request/response behavior.
|
|
112
|
+
this.initializationFailed = true;
|
|
113
|
+
this.disabled = true;
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
get filePath() {
|
|
117
|
+
return this.options.filePath;
|
|
118
|
+
}
|
|
119
|
+
get isDisabled() {
|
|
120
|
+
return this.disabled;
|
|
121
|
+
}
|
|
122
|
+
get stats() {
|
|
123
|
+
return { bytes: this.bytesWritten, events: this.eventsWritten };
|
|
124
|
+
}
|
|
125
|
+
write(event) {
|
|
126
|
+
if (this.disabled || this.eventsWritten >= this.maxEvents)
|
|
127
|
+
return;
|
|
128
|
+
let line;
|
|
129
|
+
try {
|
|
130
|
+
line = `${JSON.stringify(event)}\n`;
|
|
131
|
+
}
|
|
132
|
+
catch {
|
|
133
|
+
this.disabled = true;
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
const bytes = Buffer.byteLength(line, "utf8");
|
|
137
|
+
if (bytes > this.maxBytes - this.bytesWritten) {
|
|
138
|
+
this.disabled = true;
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
this.bytesWritten += bytes;
|
|
142
|
+
this.eventsWritten += 1;
|
|
143
|
+
this.queue = this.queue
|
|
144
|
+
.then(async () => {
|
|
145
|
+
await this.fileReady;
|
|
146
|
+
if (this.initializationFailed)
|
|
147
|
+
return;
|
|
148
|
+
await this.append(this.options.filePath, line, { encoding: "utf8" });
|
|
149
|
+
})
|
|
150
|
+
.catch(() => {
|
|
151
|
+
// Tracing must never change MCP request/response behavior.
|
|
152
|
+
this.disabled = true;
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
async flush() {
|
|
156
|
+
await this.fileReady;
|
|
157
|
+
await this.queue;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
export function createMcpTraceWriter(sessionCwd, settings = {}, randomSuffix = Math.random().toString(36).slice(2, 10)) {
|
|
161
|
+
const timestamp = new Date().toISOString().replace(/[:.]/g, "-");
|
|
162
|
+
const configuredPath = settings.file;
|
|
163
|
+
const filePath = configuredPath
|
|
164
|
+
? isAbsolute(configuredPath)
|
|
165
|
+
? configuredPath
|
|
166
|
+
: resolve(sessionCwd ?? process.cwd(), configuredPath)
|
|
167
|
+
: resolve(sessionCwd ?? process.cwd(), ".nexus", "mcp-traces", `mcp-${timestamp}-${randomSuffix}.jsonl`);
|
|
168
|
+
return new McpTraceWriter({
|
|
169
|
+
filePath,
|
|
170
|
+
...(settings.maxBytes !== undefined ? { maxBytes: settings.maxBytes } : {}),
|
|
171
|
+
...(settings.maxEvents !== undefined ? { maxEvents: settings.maxEvents } : {}),
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
export function isMcpTraceEnabled(definition, settings) {
|
|
175
|
+
return definition.trace ?? settings?.enabled === true;
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Compose the SDK's transport callbacks in place instead of replacing the
|
|
179
|
+
* transport object. SDK v2 detects its base stdio transport before connect so
|
|
180
|
+
* it can run `server/discover` on a disposable sibling process; returning a
|
|
181
|
+
* wrapper hides that identity and makes tracing change negotiation behavior.
|
|
182
|
+
*/
|
|
183
|
+
export function wrapTransportWithMcpTrace(transport, server, transportKind, observer) {
|
|
184
|
+
let messageHandler = transport.onmessage;
|
|
185
|
+
let tracedMessageHandler;
|
|
186
|
+
const originalSend = transport.send;
|
|
187
|
+
const record = (event) => {
|
|
188
|
+
try {
|
|
189
|
+
observer.record(event);
|
|
190
|
+
}
|
|
191
|
+
catch {
|
|
192
|
+
// An observer failure must never alter SDK transport behavior.
|
|
193
|
+
}
|
|
194
|
+
};
|
|
195
|
+
try {
|
|
196
|
+
Object.defineProperty(transport, "onmessage", {
|
|
197
|
+
configurable: true,
|
|
198
|
+
enumerable: true,
|
|
199
|
+
get() {
|
|
200
|
+
return tracedMessageHandler;
|
|
201
|
+
},
|
|
202
|
+
set(handler) {
|
|
203
|
+
messageHandler = handler;
|
|
204
|
+
tracedMessageHandler = handler
|
|
205
|
+
? (message, extra) => {
|
|
206
|
+
record(createMcpTraceEvent("inbound", server, transportKind, message, "received"));
|
|
207
|
+
handler(message, extra);
|
|
208
|
+
}
|
|
209
|
+
: undefined;
|
|
210
|
+
},
|
|
211
|
+
});
|
|
212
|
+
transport.onmessage = messageHandler;
|
|
213
|
+
}
|
|
214
|
+
catch {
|
|
215
|
+
// Some future transport may make onmessage non-configurable. Keep tracing
|
|
216
|
+
// best-effort rather than changing connection behavior.
|
|
217
|
+
}
|
|
218
|
+
transport.send = async (message, options) => {
|
|
219
|
+
const started = performance.now();
|
|
220
|
+
const messages = Array.isArray(message) ? message : [message];
|
|
221
|
+
try {
|
|
222
|
+
await originalSend.call(transport, message, options);
|
|
223
|
+
for (const item of messages) {
|
|
224
|
+
record(createMcpTraceEvent("outbound", server, transportKind, item, "sent", {
|
|
225
|
+
durationMs: performance.now() - started,
|
|
226
|
+
}));
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
catch (error) {
|
|
230
|
+
for (const item of messages) {
|
|
231
|
+
record(createMcpTraceEvent("outbound", server, transportKind, item, "error", {
|
|
232
|
+
durationMs: performance.now() - started,
|
|
233
|
+
}));
|
|
234
|
+
}
|
|
235
|
+
throw error;
|
|
236
|
+
}
|
|
237
|
+
};
|
|
238
|
+
return transport;
|
|
239
|
+
}
|
|
240
|
+
export function traceTransportKind(definition, transport) {
|
|
241
|
+
if (definition.command)
|
|
242
|
+
return "stdio";
|
|
243
|
+
if (definition.socket)
|
|
244
|
+
return "unix-socket";
|
|
245
|
+
const constructorName = transport.constructor?.name.toLowerCase() ?? "";
|
|
246
|
+
if (constructorName.includes("sse"))
|
|
247
|
+
return "sse";
|
|
248
|
+
if (constructorName.includes("streamable"))
|
|
249
|
+
return "streamable-http";
|
|
250
|
+
return definition.url ? "streamable-http" : "unknown";
|
|
251
|
+
}
|
|
252
|
+
//# sourceMappingURL=mcp-trace.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcp-trace.js","sourceRoot":"","sources":["../../../src/extensions/mcp/mcp-trace.ts"],"names":[],"mappings":"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","sourcesContent":["import { Buffer } from \"node:buffer\";\nimport { appendFile, mkdir, writeFile } from \"node:fs/promises\";\nimport { dirname, isAbsolute, resolve } from \"node:path\";\nimport type { JSONRPCMessage, MessageExtraInfo, Transport } from \"@modelcontextprotocol/client\";\n\nexport const MCP_TRACE_SCHEMA_VERSION = 1;\nexport const DEFAULT_MCP_TRACE_MAX_BYTES = 256 * 1024;\nexport const DEFAULT_MCP_TRACE_MAX_EVENTS = 10_000;\n\nexport type McpTraceDirection = \"outbound\" | \"inbound\";\nexport type McpTraceTransport = \"stdio\" | \"unix-socket\" | \"sse\" | \"streamable-http\" | \"unknown\";\nexport type McpTraceMessageKind = \"request\" | \"response\" | \"notification\";\n\nexport interface McpTraceSettings {\n\t/** Enable metadata-only protocol tracing for all servers unless overridden. */\n\tenabled?: boolean;\n\t/** JSONL destination. Relative paths are resolved from the session cwd. */\n\tfile?: string;\n\t/** Maximum bytes retained in the per-session JSONL file. */\n\tmaxBytes?: number;\n\t/** Maximum events retained in the per-session JSONL file. */\n\tmaxEvents?: number;\n}\n\nexport interface McpTraceEvent {\n\tversion: typeof MCP_TRACE_SCHEMA_VERSION;\n\ttimestamp: string;\n\tdirection: McpTraceDirection;\n\tserver: string;\n\ttransport: McpTraceTransport;\n\tkind: McpTraceMessageKind;\n\tstatus: \"sent\" | \"received\" | \"error\";\n\tmethod?: string;\n\tid?: string | number | null;\n\trelatedRequestId?: string | number;\n\terrorCode?: number | string;\n\tbytes?: number;\n\tdurationMs?: number;\n}\n\nexport interface McpTraceWriterOptions {\n\tfilePath: string;\n\tmaxBytes?: number;\n\tmaxEvents?: number;\n\tappendFile?: (path: string, data: string, options: { encoding: \"utf8\" }) => Promise<void>;\n\twriteFile?: (path: string, data: string, options: { encoding: \"utf8\" }) => Promise<void>;\n\tmkdir?: (path: string, options: { recursive: true }) => Promise<string | undefined>;\n}\n\nfunction boundedPositiveInteger(value: number | undefined, fallback: number): number {\n\tif (!Number.isFinite(value) || value === undefined || value <= 0) return fallback;\n\treturn Math.floor(value);\n}\n\n/** Redact strings before they can become durable trace metadata. */\nexport function redactTraceText(value: string, maxLength = 160): string {\n\tlet redacted = value\n\t\t.replace(/\\b[a-z][a-z\\d+.-]*:\\/\\/[^\\s\"'<>]+/gi, \"[REDACTED_URL]\")\n\t\t.replace(/\\b(?:bearer|basic)\\s+[A-Za-z0-9._~+/=-]+/gi, \"[REDACTED_AUTH]\")\n\t\t.replace(\n\t\t\t/\\b(token|secret|password|passwd|api[_-]?key|authorization|cookie)\\s*[:=]\\s*[^\\s,;]+/gi,\n\t\t\t\"$1=[REDACTED]\",\n\t\t);\n\tif (redacted === value && /\\b(?:token|secret|password|passwd|api[_-]?key|authorization|cookie)\\b/i.test(value)) {\n\t\treturn \"[REDACTED]\";\n\t}\n\tif (redacted.length > maxLength) redacted = `${redacted.slice(0, maxLength - 1)}…`;\n\treturn redacted;\n}\n\nfunction messageKind(message: JSONRPCMessage): McpTraceMessageKind {\n\tif (\"method\" in message) return \"id\" in message ? \"request\" : \"notification\";\n\treturn \"response\";\n}\n\nfunction traceId(value: unknown): string | number | null | undefined {\n\tif (value === null) return null;\n\tif (typeof value === \"number\" && Number.isFinite(value)) return value;\n\tif (typeof value === \"string\") return \"[REDACTED_ID]\";\n\treturn undefined;\n}\n\nfunction messageBytes(message: JSONRPCMessage): number | undefined {\n\ttry {\n\t\treturn Buffer.byteLength(JSON.stringify(message), \"utf8\");\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\nexport function createMcpTraceEvent(\n\tdirection: McpTraceDirection,\n\tserver: string,\n\ttransport: McpTraceTransport,\n\tmessage: JSONRPCMessage,\n\tstatus: \"sent\" | \"received\" | \"error\",\n\toptions?: { relatedRequestId?: unknown; durationMs?: number },\n): McpTraceEvent {\n\tconst kind = messageKind(message);\n\tconst bytes = messageBytes(message);\n\tconst event: McpTraceEvent = {\n\t\tversion: MCP_TRACE_SCHEMA_VERSION,\n\t\ttimestamp: new Date().toISOString(),\n\t\tdirection,\n\t\tserver: redactTraceText(server, 120),\n\t\ttransport,\n\t\tkind,\n\t\tstatus,\n\t\t...(bytes !== undefined ? { bytes } : {}),\n\t};\n\tif (\"method\" in message) event.method = redactTraceText(message.method, 120);\n\tif (\"id\" in message) event.id = traceId(message.id) ?? null;\n\tconst relatedRequestId = traceId(options?.relatedRequestId);\n\tif (relatedRequestId !== undefined && relatedRequestId !== null) event.relatedRequestId = relatedRequestId;\n\tif (\"error\" in message && message.error && typeof message.error.code === \"number\") {\n\t\tevent.errorCode = message.error.code;\n\t}\n\tif (options?.durationMs !== undefined && Number.isFinite(options.durationMs)) {\n\t\tevent.durationMs = Math.max(0, Math.round(options.durationMs * 100) / 100);\n\t}\n\treturn event;\n}\n\nexport class McpTraceWriter {\n\tprivate readonly maxBytes: number;\n\tprivate readonly maxEvents: number;\n\tprivate readonly append: NonNullable<McpTraceWriterOptions[\"appendFile\"]>;\n\tprivate readonly resetFile: NonNullable<McpTraceWriterOptions[\"writeFile\"]>;\n\tprivate readonly makeDirectory: NonNullable<McpTraceWriterOptions[\"mkdir\"]>;\n\tprivate bytesWritten = 0;\n\tprivate eventsWritten = 0;\n\tprivate queue = Promise.resolve();\n\tprivate disabled = false;\n\tprivate initializationFailed = false;\n\tprivate readonly fileReady: Promise<void>;\n\tprivate readonly options: McpTraceWriterOptions;\n\n\tconstructor(options: McpTraceWriterOptions) {\n\t\tthis.options = options;\n\t\tthis.maxBytes = boundedPositiveInteger(options.maxBytes, DEFAULT_MCP_TRACE_MAX_BYTES);\n\t\tthis.maxEvents = boundedPositiveInteger(options.maxEvents, DEFAULT_MCP_TRACE_MAX_EVENTS);\n\t\tthis.append =\n\t\t\toptions.appendFile ??\n\t\t\t(async (path, data, appendOptions) => {\n\t\t\t\tawait appendFile(path, data, appendOptions);\n\t\t\t});\n\t\tthis.resetFile =\n\t\t\toptions.writeFile ??\n\t\t\t(async (path, data, writeOptions) => {\n\t\t\t\tawait writeFile(path, data, writeOptions);\n\t\t\t});\n\t\tthis.makeDirectory =\n\t\t\toptions.mkdir ??\n\t\t\t(async (path) => {\n\t\t\t\tawait mkdir(path, { recursive: true });\n\t\t\t\treturn undefined;\n\t\t\t});\n\t\tthis.fileReady = this.makeDirectory(dirname(this.options.filePath), { recursive: true })\n\t\t\t.then(() => this.resetFile(this.options.filePath, \"\", { encoding: \"utf8\" }))\n\t\t\t.catch(() => {\n\t\t\t\t// Tracing must never change MCP request/response behavior.\n\t\t\t\tthis.initializationFailed = true;\n\t\t\t\tthis.disabled = true;\n\t\t\t});\n\t}\n\n\tget filePath(): string {\n\t\treturn this.options.filePath;\n\t}\n\n\tget isDisabled(): boolean {\n\t\treturn this.disabled;\n\t}\n\n\tget stats(): { bytes: number; events: number } {\n\t\treturn { bytes: this.bytesWritten, events: this.eventsWritten };\n\t}\n\n\twrite(event: McpTraceEvent): void {\n\t\tif (this.disabled || this.eventsWritten >= this.maxEvents) return;\n\t\tlet line: string;\n\t\ttry {\n\t\t\tline = `${JSON.stringify(event)}\\n`;\n\t\t} catch {\n\t\t\tthis.disabled = true;\n\t\t\treturn;\n\t\t}\n\t\tconst bytes = Buffer.byteLength(line, \"utf8\");\n\t\tif (bytes > this.maxBytes - this.bytesWritten) {\n\t\t\tthis.disabled = true;\n\t\t\treturn;\n\t\t}\n\n\t\tthis.bytesWritten += bytes;\n\t\tthis.eventsWritten += 1;\n\t\tthis.queue = this.queue\n\t\t\t.then(async () => {\n\t\t\t\tawait this.fileReady;\n\t\t\t\tif (this.initializationFailed) return;\n\t\t\t\tawait this.append(this.options.filePath, line, { encoding: \"utf8\" });\n\t\t\t})\n\t\t\t.catch(() => {\n\t\t\t\t// Tracing must never change MCP request/response behavior.\n\t\t\t\tthis.disabled = true;\n\t\t\t});\n\t}\n\n\tasync flush(): Promise<void> {\n\t\tawait this.fileReady;\n\t\tawait this.queue;\n\t}\n}\n\nexport function createMcpTraceWriter(\n\tsessionCwd: string | undefined,\n\tsettings: McpTraceSettings = {},\n\trandomSuffix = Math.random().toString(36).slice(2, 10),\n): McpTraceWriter {\n\tconst timestamp = new Date().toISOString().replace(/[:.]/g, \"-\");\n\tconst configuredPath = settings.file;\n\tconst filePath = configuredPath\n\t\t? isAbsolute(configuredPath)\n\t\t\t? configuredPath\n\t\t\t: resolve(sessionCwd ?? process.cwd(), configuredPath)\n\t\t: resolve(sessionCwd ?? process.cwd(), \".nexus\", \"mcp-traces\", `mcp-${timestamp}-${randomSuffix}.jsonl`);\n\treturn new McpTraceWriter({\n\t\tfilePath,\n\t\t...(settings.maxBytes !== undefined ? { maxBytes: settings.maxBytes } : {}),\n\t\t...(settings.maxEvents !== undefined ? { maxEvents: settings.maxEvents } : {}),\n\t});\n}\n\nexport interface McpTraceObserver {\n\trecord(event: McpTraceEvent): void;\n}\n\nexport function isMcpTraceEnabled(definition: { trace?: boolean }, settings?: McpTraceSettings): boolean {\n\treturn definition.trace ?? settings?.enabled === true;\n}\n\n/**\n * Compose the SDK's transport callbacks in place instead of replacing the\n * transport object. SDK v2 detects its base stdio transport before connect so\n * it can run `server/discover` on a disposable sibling process; returning a\n * wrapper hides that identity and makes tracing change negotiation behavior.\n */\nexport function wrapTransportWithMcpTrace(\n\ttransport: Transport,\n\tserver: string,\n\ttransportKind: McpTraceTransport,\n\tobserver: McpTraceObserver,\n): Transport {\n\tlet messageHandler = transport.onmessage;\n\tlet tracedMessageHandler: Transport[\"onmessage\"];\n\tconst originalSend = transport.send;\n\tconst record = (event: McpTraceEvent): void => {\n\t\ttry {\n\t\t\tobserver.record(event);\n\t\t} catch {\n\t\t\t// An observer failure must never alter SDK transport behavior.\n\t\t}\n\t};\n\n\ttry {\n\t\tObject.defineProperty(transport, \"onmessage\", {\n\t\t\tconfigurable: true,\n\t\t\tenumerable: true,\n\t\t\tget() {\n\t\t\t\treturn tracedMessageHandler;\n\t\t\t},\n\t\t\tset(handler: Transport[\"onmessage\"]) {\n\t\t\t\tmessageHandler = handler;\n\t\t\t\ttracedMessageHandler = handler\n\t\t\t\t\t? (message: JSONRPCMessage, extra?: MessageExtraInfo) => {\n\t\t\t\t\t\t\trecord(createMcpTraceEvent(\"inbound\", server, transportKind, message, \"received\"));\n\t\t\t\t\t\t\thandler(message, extra);\n\t\t\t\t\t\t}\n\t\t\t\t\t: undefined;\n\t\t\t},\n\t\t});\n\t\ttransport.onmessage = messageHandler;\n\t} catch {\n\t\t// Some future transport may make onmessage non-configurable. Keep tracing\n\t\t// best-effort rather than changing connection behavior.\n\t}\n\n\ttransport.send = async (message: JSONRPCMessage, options?: Parameters<Transport[\"send\"]>[1]) => {\n\t\tconst started = performance.now();\n\t\tconst messages = Array.isArray(message) ? message : [message];\n\t\ttry {\n\t\t\tawait originalSend.call(transport, message, options);\n\t\t\tfor (const item of messages) {\n\t\t\t\trecord(\n\t\t\t\t\tcreateMcpTraceEvent(\"outbound\", server, transportKind, item, \"sent\", {\n\t\t\t\t\t\tdurationMs: performance.now() - started,\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tfor (const item of messages) {\n\t\t\t\trecord(\n\t\t\t\t\tcreateMcpTraceEvent(\"outbound\", server, transportKind, item, \"error\", {\n\t\t\t\t\t\tdurationMs: performance.now() - started,\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\t}\n\t\t\tthrow error;\n\t\t}\n\t};\n\n\treturn transport;\n}\n\nexport function traceTransportKind(\n\tdefinition: { command?: string; url?: string; socket?: string },\n\ttransport: Transport,\n): McpTraceTransport {\n\tif (definition.command) return \"stdio\";\n\tif (definition.socket) return \"unix-socket\";\n\tconst constructorName = transport.constructor?.name.toLowerCase() ?? \"\";\n\tif (constructorName.includes(\"sse\")) return \"sse\";\n\tif (constructorName.includes(\"streamable\")) return \"streamable-http\";\n\treturn definition.url ? \"streamable-http\" : \"unknown\";\n}\n"]}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { CachedPrompt, CachedResource, CachedTool, McpConfig, McpPrompt, McpResource, McpTool, MetadataCache, PromptMetadata, ServerCacheEntry, ServerEntry, ToolMetadata } from "./types.ts";
|
|
2
|
+
import { type ToolPrefix, type ToolSelectorCandidateIndex } from "./types.ts";
|
|
3
|
+
export type { CachedPrompt, CachedResource, CachedTool, MetadataCache, ServerCacheEntry } from "./types.ts";
|
|
4
|
+
export declare function getMetadataCachePath(): string;
|
|
5
|
+
export declare function loadMetadataCache(): MetadataCache | null;
|
|
6
|
+
export declare function saveMetadataCache(cache: MetadataCache): void;
|
|
7
|
+
export declare function computeServerHash(definition: ServerEntry): string;
|
|
8
|
+
export declare function isServerCacheValid(entry: ServerCacheEntry, definition: ServerEntry, maxAgeMs?: number): boolean;
|
|
9
|
+
export declare function parseDirectToolSelectors(selectors: string[]): {
|
|
10
|
+
servers: Set<string>;
|
|
11
|
+
tools: Map<string, Set<string>>;
|
|
12
|
+
};
|
|
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">, configuredServers?: Record<string, ServerEntry>, cache?: MetadataCache, sharedSelectorCandidateIndex?: ToolSelectorCandidateIndex): ToolMetadata[];
|
|
15
|
+
export declare function createCachedToolSelectorCandidateIndex(configuredServers: Record<string, ServerEntry>, cache: MetadataCache, prefix: ToolPrefix): ToolSelectorCandidateIndex;
|
|
16
|
+
export declare function serializeTools(tools: McpTool[]): CachedTool[];
|
|
17
|
+
export declare function serializeResources(resources: McpResource[]): CachedResource[];
|
|
18
|
+
export declare function serializePrompts(prompts: McpPrompt[]): CachedPrompt[];
|
|
19
|
+
export declare function reconstructPromptMetadata(serverName: string, prompts: ReadonlyArray<McpPrompt | CachedPrompt>, prefix: ToolPrefix, definition?: Pick<ServerEntry, "toolPrefix">): PromptMetadata[];
|
|
20
|
+
//# sourceMappingURL=metadata-cache.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"metadata-cache.d.ts","sourceRoot":"","sources":["../../../src/extensions/mcp/metadata-cache.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EACX,YAAY,EACZ,cAAc,EACd,UAAU,EACV,SAAS,EACT,SAAS,EAET,WAAW,EACX,OAAO,EACP,aAAa,EACb,cAAc,EACd,gBAAgB,EAChB,WAAW,EACX,YAAY,EACZ,MAAM,YAAY,CAAC;AACpB,OAAO,EAQN,KAAK,UAAU,EACf,KAAK,0BAA0B,EAC/B,MAAM,YAAY,CAAC;AAepB,YAAY,EAAE,YAAY,EAAE,cAAc,EAAE,UAAU,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAE5G,wBAAgB,oBAAoB,IAAI,MAAM,CAE7C;AAED,wBAAgB,iBAAiB,IAAI,aAAa,GAAG,IAAI,CAYxD;AAED,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,aAAa,GAAG,IAAI,CAuB5D;AAED,wBAAgB,iBAAiB,CAAC,UAAU,EAAE,WAAW,GAAG,MAAM,CA8BjE;AAED,wBAAgB,kBAAkB,CACjC,KAAK,EAAE,gBAAgB,EACvB,UAAU,EAAE,WAAW,EACvB,QAAQ,GAAE,MAAyB,GACjC,OAAO,CAWT;AAED,wBAAgB,wBAAwB,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG;IAC9D,OAAO,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IACrB,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC;CAChC,CAqBA;AAED,wBAAgB,qCAAqC,CACpD,MAAM,EAAE,SAAS,EACjB,KAAK,EAAE,aAAa,GAAG,IAAI,EAC3B,WAAW,CAAC,EAAE,MAAM,EAAE,GACpB,MAAM,EAAE,CAsBV;AAED,wBAAgB,uBAAuB,CACtC,UAAU,EAAE,MAAM,EAClB,KAAK,EAAE,gBAAgB,EACvB,MAAM,EAAE,UAAU,EAClB,UAAU,EAAE,IAAI,CAAC,WAAW,EAAE,iBAAiB,GAAG,cAAc,GAAG,cAAc,GAAG,YAAY,CAAC,EACjG,iBAAiB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,EAC/C,KAAK,CAAC,EAAE,aAAa,EACrB,4BAA4B,CAAC,EAAE,0BAA0B,GACvD,YAAY,EAAE,CAkFhB;AAED,wBAAgB,sCAAsC,CACrD,iBAAiB,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,EAC9C,KAAK,EAAE,aAAa,EACpB,MAAM,EAAE,UAAU,GAChB,0BAA0B,CAoB5B;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,EAAE,GAAG,UAAU,EAAE,CAgB7D;AAED,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,WAAW,EAAE,GAAG,cAAc,EAAE,CAQ7E;AAED,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,SAAS,EAAE,GAAG,YAAY,EAAE,CAmBrE;AAED,wBAAgB,yBAAyB,CACxC,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,aAAa,CAAC,SAAS,GAAG,YAAY,CAAC,EAChD,MAAM,EAAE,UAAU,EAClB,UAAU,CAAC,EAAE,IAAI,CAAC,WAAW,EAAE,YAAY,CAAC,GAC1C,cAAc,EAAE,CAuBlB","sourcesContent":["// metadata-cache.ts - Persistent MCP metadata cache\n\nimport { createHash } from \"node:crypto\";\nimport { existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from \"node:fs\";\nimport { dirname } from \"node:path\";\nimport { getAgentPath } from \"./agent-dir.ts\";\nimport { resourceNameToToolName } from \"./resource-tools.ts\";\nimport type {\n\tCachedPrompt,\n\tCachedResource,\n\tCachedTool,\n\tMcpConfig,\n\tMcpPrompt,\n\tMcpPromptArgument,\n\tMcpResource,\n\tMcpTool,\n\tMetadataCache,\n\tPromptMetadata,\n\tServerCacheEntry,\n\tServerEntry,\n\tToolMetadata,\n} from \"./types.ts\";\nimport {\n\tcreateToolSelectorCandidateIndex,\n\tformatPromptCommandName,\n\tformatToolName,\n\tgetToolNameCandidates,\n\tisServerDisabled,\n\tisToolAllowed,\n\tresolveToolPrefix,\n\ttype ToolPrefix,\n\ttype ToolSelectorCandidateIndex,\n} from \"./types.ts\";\nimport { getToolUiResourceUri } from \"./ui-app-bridge-helpers.ts\";\nimport { extractUiToolVisibility, isUiToolVisibleToModel } from \"./ui-tool-visibility.ts\";\nimport {\n\textractToolUiStreamMode,\n\tinterpolateEnvRecord,\n\tinterpolateEnvVars,\n\tresolveBearerToken,\n\tresolveConfigPath,\n\tresolveServerUrl,\n} from \"./utils.ts\";\n\nconst CACHE_VERSION = 1;\nconst CACHE_MAX_AGE_MS = 7 * 24 * 60 * 60 * 1000;\n\nexport type { CachedPrompt, CachedResource, CachedTool, MetadataCache, ServerCacheEntry } from \"./types.ts\";\n\nexport function getMetadataCachePath(): string {\n\treturn getAgentPath(\"mcp-cache.json\");\n}\n\nexport function loadMetadataCache(): MetadataCache | null {\n\tconst cachePath = getMetadataCachePath();\n\tif (!existsSync(cachePath)) return null;\n\ttry {\n\t\tconst raw = JSON.parse(readFileSync(cachePath, \"utf-8\"));\n\t\tif (!raw || typeof raw !== \"object\") return null;\n\t\tif (raw.version !== CACHE_VERSION) return null;\n\t\tif (!raw.servers || typeof raw.servers !== \"object\") return null;\n\t\treturn raw as MetadataCache;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nexport function saveMetadataCache(cache: MetadataCache): void {\n\tconst cachePath = getMetadataCachePath();\n\tconst dir = dirname(cachePath);\n\tmkdirSync(dir, { recursive: true });\n\n\tconst merged: MetadataCache = { version: CACHE_VERSION, servers: {} };\n\ttry {\n\t\tif (existsSync(cachePath)) {\n\t\t\tconst existing = JSON.parse(readFileSync(cachePath, \"utf-8\")) as MetadataCache;\n\t\t\tif (existing && existing.version === CACHE_VERSION && existing.servers) {\n\t\t\t\tmerged.servers = { ...existing.servers };\n\t\t\t}\n\t\t}\n\t} catch {\n\t\t// Ignore parse errors and proceed with empty cache\n\t}\n\n\tmerged.version = CACHE_VERSION;\n\tmerged.servers = { ...merged.servers, ...cache.servers };\n\n\tconst tmpPath = `${cachePath}.${process.pid}.tmp`;\n\twriteFileSync(tmpPath, JSON.stringify(merged), \"utf-8\");\n\trenameSync(tmpPath, cachePath);\n}\n\nexport function computeServerHash(definition: ServerEntry): string {\n\t// Hash only fields that affect server identity and tool/resource output.\n\t// Exclude lifecycle, idleTimeout, requestTimeoutMs, debug — those are runtime behavior settings\n\t// that don't change which tools a server exposes.\n\tconst identity: Record<string, unknown> = {\n\t\tcommand: definition.command,\n\t\targs: definition.args,\n\t\tsocket: resolveConfigPath(definition.socket),\n\t\tenv: interpolateEnvRecord(definition.env),\n\t\tcwd: resolveConfigPath(definition.cwd),\n\t\turl: resolveServerUrl(definition),\n\t\theaders: interpolateEnvRecord(definition.headers),\n\t\trequestHeadersCommand: definition.requestHeadersCommand\n\t\t\t? {\n\t\t\t\t\tcommand: interpolateEnvVars(definition.requestHeadersCommand.command),\n\t\t\t\t\targs: definition.requestHeadersCommand.args?.map(interpolateEnvVars),\n\t\t\t\t\tenv: interpolateEnvRecord(definition.requestHeadersCommand.env),\n\t\t\t\t\ttimeoutMs: definition.requestHeadersCommand.timeoutMs,\n\t\t\t\t}\n\t\t\t: undefined,\n\t\tauth: definition.auth,\n\t\tprotocolVersion: definition.protocolVersion,\n\t\tbearerToken: resolveBearerToken(definition),\n\t\tbearerTokenEnv: definition.bearerTokenEnv,\n\t\texposeResources: definition.exposeResources,\n\t\tincludeTools: definition.includeTools,\n\t\texcludeTools: definition.excludeTools,\n\t};\n\tconst normalized = stableStringify(identity);\n\treturn createHash(\"sha256\").update(normalized).digest(\"hex\");\n}\n\nexport function isServerCacheValid(\n\tentry: ServerCacheEntry,\n\tdefinition: ServerEntry,\n\tmaxAgeMs: number = CACHE_MAX_AGE_MS,\n): boolean {\n\tlet configHash: string;\n\ttry {\n\t\tconfigHash = computeServerHash(definition);\n\t} catch {\n\t\treturn false;\n\t}\n\tif (!entry || entry.configHash !== configHash) return false;\n\tif (!entry.cachedAt || typeof entry.cachedAt !== \"number\") return false;\n\tif (maxAgeMs > 0 && Date.now() - entry.cachedAt > maxAgeMs) return false;\n\treturn true;\n}\n\nexport function parseDirectToolSelectors(selectors: string[]): {\n\tservers: Set<string>;\n\ttools: Map<string, Set<string>>;\n} {\n\tconst servers = new Set<string>();\n\tconst tools = new Map<string, Set<string>>();\n\n\tfor (let selector of selectors) {\n\t\tselector = selector.replace(/\\/+$/, \"\");\n\t\tif (selector.includes(\"/\")) {\n\t\t\tconst [server, tool] = selector.split(\"/\", 2);\n\t\t\tif (server && tool) {\n\t\t\t\tconst serverTools = tools.get(server) ?? new Set<string>();\n\t\t\t\tserverTools.add(tool);\n\t\t\t\ttools.set(server, serverTools);\n\t\t\t} else if (server) {\n\t\t\t\tservers.add(server);\n\t\t\t}\n\t\t} else if (selector) {\n\t\t\tservers.add(selector);\n\t\t}\n\t}\n\n\treturn { servers, tools };\n}\n\nexport function getMissingConfiguredDirectToolServers(\n\tconfig: McpConfig,\n\tcache: MetadataCache | null,\n\tenvOverride?: string[],\n): string[] {\n\tconst missing: string[] = [];\n\tconst globalDirect = config.settings?.directTools;\n\tconst envSelection = envOverride ? parseDirectToolSelectors(envOverride) : null;\n\n\tfor (const [serverName, definition] of Object.entries(config.mcpServers)) {\n\t\tif (isServerDisabled(definition)) continue;\n\t\tconst hasDirectTools = envSelection\n\t\t\t? envSelection.servers.has(serverName) || envSelection.tools.has(serverName)\n\t\t\t: definition.directTools !== undefined\n\t\t\t\t? !!definition.directTools\n\t\t\t\t: !!globalDirect;\n\n\t\tif (!hasDirectTools) continue;\n\n\t\tconst serverCache = cache?.servers?.[serverName];\n\t\tif (!serverCache || !isServerCacheValid(serverCache, definition)) {\n\t\t\tmissing.push(serverName);\n\t\t}\n\t}\n\n\treturn missing;\n}\n\nexport function reconstructToolMetadata(\n\tserverName: string,\n\tentry: ServerCacheEntry,\n\tprefix: ToolPrefix,\n\tdefinition: Pick<ServerEntry, \"exposeResources\" | \"includeTools\" | \"excludeTools\" | \"toolPrefix\">,\n\tconfiguredServers?: Record<string, ServerEntry>,\n\tcache?: MetadataCache,\n\tsharedSelectorCandidateIndex?: ToolSelectorCandidateIndex,\n): ToolMetadata[] {\n\tconst metadata: ToolMetadata[] = [];\n\tconst seenNames = new Set<string>();\n\tconst effectivePrefix = resolveToolPrefix(definition, prefix);\n\tconst hasToolFilters =\n\t\t(Array.isArray(definition.includeTools) && definition.includeTools.length > 0) ||\n\t\t(Array.isArray(definition.excludeTools) && definition.excludeTools.length > 0);\n\tconst selectorCandidateIndex = hasToolFilters\n\t\t? (sharedSelectorCandidateIndex ??\n\t\t\t(configuredServers && cache\n\t\t\t\t? createCachedToolSelectorCandidateIndex(configuredServers, cache, prefix)\n\t\t\t\t: undefined))\n\t\t: undefined;\n\n\tfor (const tool of entry.tools ?? []) {\n\t\tif (!tool?.name) continue;\n\t\tif (!isUiToolVisibleToModel(tool.uiVisibility)) {\n\t\t\tcontinue;\n\t\t}\n\t\tif (\n\t\t\t!isToolAllowed(\n\t\t\t\ttool.name,\n\t\t\t\tserverName,\n\t\t\t\teffectivePrefix,\n\t\t\t\tdefinition.includeTools,\n\t\t\t\tdefinition.excludeTools,\n\t\t\t\tselectorCandidateIndex,\n\t\t\t)\n\t\t) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst name = formatToolName(tool.name, serverName, effectivePrefix);\n\t\tif (seenNames.has(name)) {\n\t\t\tcontinue;\n\t\t}\n\t\tseenNames.add(name);\n\n\t\tmetadata.push({\n\t\t\tname,\n\t\t\toriginalName: tool.name,\n\t\t\tdescription: tool.description ?? \"\",\n\t\t\t...(tool.inputSchema !== undefined ? { inputSchema: tool.inputSchema } : {}),\n\t\t\t...(tool.uiResourceUri !== undefined ? { uiResourceUri: tool.uiResourceUri } : {}),\n\t\t\t...(tool.uiVisibility !== undefined ? { uiVisibility: tool.uiVisibility } : {}),\n\t\t\t...(tool.uiStreamMode !== undefined ? { uiStreamMode: tool.uiStreamMode } : {}),\n\t\t});\n\t}\n\n\tif (definition.exposeResources !== false) {\n\t\tfor (const resource of entry.resources ?? []) {\n\t\t\tif (!resource?.name || !resource?.uri) continue;\n\t\t\tconst baseName = `read_${resourceNameToToolName(resource.name)}`;\n\t\t\tif (\n\t\t\t\t!isToolAllowed(\n\t\t\t\t\tbaseName,\n\t\t\t\t\tserverName,\n\t\t\t\t\teffectivePrefix,\n\t\t\t\t\tdefinition.includeTools,\n\t\t\t\t\tdefinition.excludeTools,\n\t\t\t\t\tselectorCandidateIndex,\n\t\t\t\t)\n\t\t\t) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tconst name = formatToolName(baseName, serverName, effectivePrefix);\n\t\t\tif (seenNames.has(name)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tseenNames.add(name);\n\n\t\t\tmetadata.push({\n\t\t\t\tname,\n\t\t\t\toriginalName: baseName,\n\t\t\t\tdescription: resource.description ?? `Read resource: ${resource.uri}`,\n\t\t\t\tresourceUri: resource.uri,\n\t\t\t});\n\t\t}\n\t}\n\n\treturn metadata;\n}\n\nexport function createCachedToolSelectorCandidateIndex(\n\tconfiguredServers: Record<string, ServerEntry>,\n\tcache: MetadataCache,\n\tprefix: ToolPrefix,\n): ToolSelectorCandidateIndex {\n\tconst candidates = new Set<string>();\n\tfor (const [serverName, definition] of Object.entries(configuredServers)) {\n\t\tconst entry = cache.servers[serverName];\n\t\tif (!entry || !isServerCacheValid(entry, definition) || isServerDisabled(definition)) continue;\n\t\tconst effectivePrefix = resolveToolPrefix(definition, prefix);\n\t\tfor (const tool of entry.tools ?? []) {\n\t\t\tif (!isUiToolVisibleToModel(tool.uiVisibility)) continue;\n\t\t\tfor (const candidate of getToolNameCandidates(tool.name, serverName, effectivePrefix, false))\n\t\t\t\tcandidates.add(candidate);\n\t\t}\n\t\tif (definition.exposeResources !== false) {\n\t\t\tfor (const resource of entry.resources ?? []) {\n\t\t\t\tconst baseName = `read_${resourceNameToToolName(resource.name)}`;\n\t\t\t\tfor (const candidate of getToolNameCandidates(baseName, serverName, effectivePrefix, false))\n\t\t\t\t\tcandidates.add(candidate);\n\t\t\t}\n\t\t}\n\t}\n\treturn createToolSelectorCandidateIndex(candidates);\n}\n\nexport function serializeTools(tools: McpTool[]): CachedTool[] {\n\treturn tools\n\t\t.filter((t) => t?.name)\n\t\t.map((t) => {\n\t\t\tconst uiResourceUri = tryGetToolUiResourceUri(t);\n\t\t\tconst uiVisibility = extractUiToolVisibility(t._meta);\n\t\t\tconst uiStreamMode = extractToolUiStreamMode(t._meta);\n\t\t\treturn {\n\t\t\t\tname: t.name,\n\t\t\t\t...(t.description !== undefined ? { description: t.description } : {}),\n\t\t\t\t...(t.inputSchema !== undefined ? { inputSchema: t.inputSchema } : {}),\n\t\t\t\t...(uiResourceUri !== undefined ? { uiResourceUri } : {}),\n\t\t\t\t...(uiVisibility !== undefined ? { uiVisibility } : {}),\n\t\t\t\t...(uiStreamMode !== undefined ? { uiStreamMode } : {}),\n\t\t\t};\n\t\t});\n}\n\nexport function serializeResources(resources: McpResource[]): CachedResource[] {\n\treturn resources\n\t\t.filter((r) => r?.name && r?.uri)\n\t\t.map((r) => ({\n\t\t\turi: r.uri,\n\t\t\tname: r.name,\n\t\t\t...(r.description !== undefined ? { description: r.description } : {}),\n\t\t}));\n}\n\nexport function serializePrompts(prompts: McpPrompt[]): CachedPrompt[] {\n\treturn (prompts ?? [])\n\t\t.filter((prompt) => prompt?.name)\n\t\t.map((prompt) => ({\n\t\t\tname: prompt.name,\n\t\t\t...(prompt.title !== undefined ? { title: prompt.title } : {}),\n\t\t\t...(prompt.description !== undefined ? { description: prompt.description } : {}),\n\t\t\t...(Array.isArray(prompt.arguments)\n\t\t\t\t? {\n\t\t\t\t\t\targuments: prompt.arguments\n\t\t\t\t\t\t\t.filter((argument) => argument?.name)\n\t\t\t\t\t\t\t.map((argument) => ({\n\t\t\t\t\t\t\t\tname: argument.name,\n\t\t\t\t\t\t\t\t...(argument.description !== undefined ? { description: argument.description } : {}),\n\t\t\t\t\t\t\t\t...(argument.required !== undefined ? { required: argument.required } : {}),\n\t\t\t\t\t\t\t})),\n\t\t\t\t\t}\n\t\t\t\t: {}),\n\t\t}));\n}\n\nexport function reconstructPromptMetadata(\n\tserverName: string,\n\tprompts: ReadonlyArray<McpPrompt | CachedPrompt>,\n\tprefix: ToolPrefix,\n\tdefinition?: Pick<ServerEntry, \"toolPrefix\">,\n): PromptMetadata[] {\n\tconst effectivePrefix = resolveToolPrefix(definition, prefix);\n\treturn (prompts ?? [])\n\t\t.filter((prompt) => prompt?.name)\n\t\t.map((prompt) => {\n\t\t\tconst args: McpPromptArgument[] = Array.isArray(prompt.arguments)\n\t\t\t\t? prompt.arguments\n\t\t\t\t\t\t.filter((argument) => argument?.name)\n\t\t\t\t\t\t.map((argument) => ({\n\t\t\t\t\t\t\tname: argument.name,\n\t\t\t\t\t\t\t...(argument.description !== undefined ? { description: argument.description } : {}),\n\t\t\t\t\t\t\t...(argument.required !== undefined ? { required: argument.required } : {}),\n\t\t\t\t\t\t}))\n\t\t\t\t: [];\n\t\t\treturn {\n\t\t\t\tserverName,\n\t\t\t\toriginalName: prompt.name,\n\t\t\t\tcommandName: formatPromptCommandName(prompt.name, serverName, effectivePrefix),\n\t\t\t\t...(prompt.title !== undefined ? { title: prompt.title } : {}),\n\t\t\t\tdescription: prompt.description ?? \"\",\n\t\t\t\targuments: args,\n\t\t\t};\n\t\t});\n}\n\nfunction stableStringify(value: unknown): string {\n\tif (value === null || value === undefined || typeof value !== \"object\") {\n\t\tconst serialized = JSON.stringify(value);\n\t\treturn serialized === undefined ? \"undefined\" : serialized;\n\t}\n\tif (Array.isArray(value)) {\n\t\treturn `[${value.map((v) => stableStringify(v)).join(\",\")}]`;\n\t}\n\tconst obj = value as Record<string, unknown>;\n\tconst keys = Object.keys(obj).sort();\n\treturn `{${keys.map((k) => `${JSON.stringify(k)}:${stableStringify(obj[k])}`).join(\",\")}}`;\n}\n\nfunction tryGetToolUiResourceUri(tool: McpTool): string | undefined {\n\ttry {\n\t\treturn getToolUiResourceUri({ _meta: tool._meta });\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n"]}
|