@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,156 @@
|
|
|
1
|
+
import { createHash, randomUUID } from "node:crypto";
|
|
2
|
+
import { abortable } from "./abort.js";
|
|
3
|
+
import { combineAbortSignals } from "./runtime-owner.js";
|
|
4
|
+
import { getToolNameCandidates, MCP_TOOL_APPROVAL_REQUEST_EVENT, matchesToolPattern, resolveToolPrefix, } from "./types.js";
|
|
5
|
+
import { sanitizeTerminalText } from "./utils.js";
|
|
6
|
+
function stableStringify(value) {
|
|
7
|
+
if (value === null || value === undefined || typeof value !== "object") {
|
|
8
|
+
const serialized = JSON.stringify(value);
|
|
9
|
+
return serialized === undefined ? "undefined" : serialized;
|
|
10
|
+
}
|
|
11
|
+
if (Array.isArray(value)) {
|
|
12
|
+
return `[${value.map((item) => stableStringify(item)).join(",")}]`;
|
|
13
|
+
}
|
|
14
|
+
const object = value;
|
|
15
|
+
return `{${Object.keys(object)
|
|
16
|
+
.sort()
|
|
17
|
+
.map((key) => `${JSON.stringify(key)}:${stableStringify(object[key])}`)
|
|
18
|
+
.join(",")}}`;
|
|
19
|
+
}
|
|
20
|
+
export function isToolCallApprovalRequired(config, serverName, toolMeta, toolMetadata) {
|
|
21
|
+
const definition = config.mcpServers[serverName];
|
|
22
|
+
const serverApproval = definition?.approveTools;
|
|
23
|
+
const approval = serverApproval !== undefined ? serverApproval : config.settings?.approveTools;
|
|
24
|
+
if (approval === true)
|
|
25
|
+
return true;
|
|
26
|
+
if (!Array.isArray(approval) || approval.length === 0)
|
|
27
|
+
return false;
|
|
28
|
+
const prefix = resolveToolPrefix(definition, config.settings?.toolPrefix);
|
|
29
|
+
const currentCandidates = getToolNameCandidates(toolMeta.originalName, serverName, prefix, false);
|
|
30
|
+
if (serverApproval !== undefined) {
|
|
31
|
+
if (matchesToolPattern(currentCandidates, approval))
|
|
32
|
+
return true;
|
|
33
|
+
if (!toolMetadata)
|
|
34
|
+
return matchesToolPattern(getToolNameCandidates(toolMeta.originalName, serverName, prefix), approval);
|
|
35
|
+
const legacyCandidates = getToolNameCandidates(toolMeta.originalName, serverName, prefix);
|
|
36
|
+
const legacyEmittedName = [...currentCandidates]
|
|
37
|
+
.find((candidate) => candidate !== toolMeta.originalName)
|
|
38
|
+
?.replace(/-/g, "_");
|
|
39
|
+
if (legacyEmittedName)
|
|
40
|
+
legacyCandidates.add(legacyEmittedName);
|
|
41
|
+
for (const candidate of currentCandidates)
|
|
42
|
+
legacyCandidates.delete(candidate);
|
|
43
|
+
const otherCurrentCandidates = new Set();
|
|
44
|
+
for (const tool of toolMetadata.get(serverName) ?? []) {
|
|
45
|
+
for (const candidate of getToolNameCandidates(tool.originalName, serverName, prefix, false)) {
|
|
46
|
+
otherCurrentCandidates.add(candidate);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
for (const candidate of currentCandidates)
|
|
50
|
+
otherCurrentCandidates.delete(candidate);
|
|
51
|
+
return approval.some((pattern) => matchesToolPattern(legacyCandidates, [pattern]) && !matchesToolPattern(otherCurrentCandidates, [pattern]));
|
|
52
|
+
}
|
|
53
|
+
if (matchesToolPattern(currentCandidates, approval))
|
|
54
|
+
return true;
|
|
55
|
+
if (!toolMetadata)
|
|
56
|
+
return false;
|
|
57
|
+
const legacyCandidates = getToolNameCandidates(toolMeta.originalName, serverName, prefix);
|
|
58
|
+
const legacyEmittedName = [...currentCandidates]
|
|
59
|
+
.find((candidate) => candidate !== toolMeta.originalName)
|
|
60
|
+
?.replace(/-/g, "_");
|
|
61
|
+
if (legacyEmittedName)
|
|
62
|
+
legacyCandidates.add(legacyEmittedName);
|
|
63
|
+
for (const candidate of currentCandidates)
|
|
64
|
+
legacyCandidates.delete(candidate);
|
|
65
|
+
const otherCurrentCandidates = new Set();
|
|
66
|
+
for (const [name, metadata] of toolMetadata) {
|
|
67
|
+
const otherPrefix = resolveToolPrefix(config.mcpServers[name], config.settings?.toolPrefix);
|
|
68
|
+
for (const tool of metadata) {
|
|
69
|
+
for (const candidate of getToolNameCandidates(tool.originalName, name, otherPrefix, false)) {
|
|
70
|
+
otherCurrentCandidates.add(candidate);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
for (const candidate of currentCandidates)
|
|
75
|
+
otherCurrentCandidates.delete(candidate);
|
|
76
|
+
return approval.some((pattern) => matchesToolPattern(legacyCandidates, [pattern]) && !matchesToolPattern(otherCurrentCandidates, [pattern]));
|
|
77
|
+
}
|
|
78
|
+
function isMcpToolApprovalDecision(value) {
|
|
79
|
+
return value === "allow_once" || value === "allow_for_session" || value === "deny" || value === "abstain";
|
|
80
|
+
}
|
|
81
|
+
async function requestBrokerApproval(state, serverName, toolMeta, args, origin, signal) {
|
|
82
|
+
if (!state.approvalEvents)
|
|
83
|
+
return "abstain";
|
|
84
|
+
let acceptingClaim = true;
|
|
85
|
+
let handler;
|
|
86
|
+
const request = {
|
|
87
|
+
requestId: randomUUID(),
|
|
88
|
+
serverName,
|
|
89
|
+
originalToolName: toolMeta.originalName,
|
|
90
|
+
prefixedToolName: toolMeta.name,
|
|
91
|
+
args: args ?? {},
|
|
92
|
+
origin,
|
|
93
|
+
...(signal !== undefined ? { signal } : {}),
|
|
94
|
+
claim(candidate) {
|
|
95
|
+
if (!acceptingClaim || handler)
|
|
96
|
+
return false;
|
|
97
|
+
handler = candidate;
|
|
98
|
+
return true;
|
|
99
|
+
},
|
|
100
|
+
};
|
|
101
|
+
state.approvalEvents.emit(MCP_TOOL_APPROVAL_REQUEST_EVENT, request);
|
|
102
|
+
acceptingClaim = false;
|
|
103
|
+
if (!handler)
|
|
104
|
+
return "abstain";
|
|
105
|
+
try {
|
|
106
|
+
const decision = await abortable(Promise.resolve().then(handler), signal);
|
|
107
|
+
return isMcpToolApprovalDecision(decision) ? decision : "deny";
|
|
108
|
+
}
|
|
109
|
+
catch (error) {
|
|
110
|
+
if (signal?.aborted)
|
|
111
|
+
throw error;
|
|
112
|
+
return "deny";
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
export async function ensureToolCallApproved(state, serverName, toolMeta, args, signal, origin = toolMeta.resourceUri ? "resource" : "proxy", approvalMetadata) {
|
|
116
|
+
const argsHash = createHash("sha256")
|
|
117
|
+
.update(stableStringify(args ?? {}))
|
|
118
|
+
.digest("hex");
|
|
119
|
+
const cacheKey = `${serverName}\u0000${toolMeta.originalName}\u0000${argsHash}`;
|
|
120
|
+
if (!state.approvedToolCalls)
|
|
121
|
+
state.approvedToolCalls = new Map();
|
|
122
|
+
const approvedToolCalls = state.approvedToolCalls;
|
|
123
|
+
if (approvedToolCalls.has(cacheKey)) {
|
|
124
|
+
return { ok: true };
|
|
125
|
+
}
|
|
126
|
+
const brokerDecision = await requestBrokerApproval(state, serverName, toolMeta, args, origin, signal);
|
|
127
|
+
if (brokerDecision === "allow_once")
|
|
128
|
+
return { ok: true };
|
|
129
|
+
if (brokerDecision === "allow_for_session") {
|
|
130
|
+
approvedToolCalls.set(cacheKey, true);
|
|
131
|
+
return { ok: true };
|
|
132
|
+
}
|
|
133
|
+
if (brokerDecision === "deny")
|
|
134
|
+
return { ok: false, reason: "denied" };
|
|
135
|
+
if (!isToolCallApprovalRequired(state.config, serverName, toolMeta, approvalMetadata ?? state.toolMetadata)) {
|
|
136
|
+
return { ok: true };
|
|
137
|
+
}
|
|
138
|
+
if (!state.ui) {
|
|
139
|
+
return { ok: false, reason: "approval_required_headless" };
|
|
140
|
+
}
|
|
141
|
+
const json = JSON.stringify(args ?? {}, null, 2);
|
|
142
|
+
const sanitized = sanitizeTerminalText(json);
|
|
143
|
+
const preview = sanitized.length > 500 ? `${sanitized.slice(0, 500)}...` : sanitized;
|
|
144
|
+
const title = `MCP: ${sanitizeTerminalText(serverName)} wants to run ${sanitizeTerminalText(toolMeta.originalName)}`;
|
|
145
|
+
const ownedSignal = combineAbortSignals(state.owner?.signal, signal);
|
|
146
|
+
const decision = await abortable(state.ui.select(`${title}\n\nArguments:\n${preview}`, ["Allow once", "Allow for session", "Deny"]), ownedSignal);
|
|
147
|
+
if (decision === "Allow once") {
|
|
148
|
+
return { ok: true };
|
|
149
|
+
}
|
|
150
|
+
if (decision === "Allow for session") {
|
|
151
|
+
approvedToolCalls.set(cacheKey, true);
|
|
152
|
+
return { ok: true };
|
|
153
|
+
}
|
|
154
|
+
return { ok: false, reason: "denied" };
|
|
155
|
+
}
|
|
156
|
+
//# sourceMappingURL=tool-approval.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tool-approval.js","sourceRoot":"","sources":["../../../src/extensions/mcp/tool-approval.ts"],"names":[],"mappings":"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","sourcesContent":["import { createHash, randomUUID } from \"node:crypto\";\nimport { abortable } from \"./abort.ts\";\nimport { combineAbortSignals } from \"./runtime-owner.ts\";\nimport type { McpExtensionState } from \"./state.ts\";\nimport {\n\tgetToolNameCandidates,\n\tMCP_TOOL_APPROVAL_REQUEST_EVENT,\n\ttype McpConfig,\n\ttype McpToolApprovalDecision,\n\ttype McpToolApprovalHandler,\n\ttype McpToolApprovalOrigin,\n\ttype McpToolApprovalRequest,\n\tmatchesToolPattern,\n\tresolveToolPrefix,\n\ttype ToolMetadata,\n} from \"./types.ts\";\nimport { sanitizeTerminalText } from \"./utils.ts\";\n\nexport type ToolCallApprovalResult = { ok: true } | { ok: false; reason: \"denied\" | \"approval_required_headless\" };\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((item) => stableStringify(item)).join(\",\")}]`;\n\t}\n\tconst object = value as Record<string, unknown>;\n\treturn `{${Object.keys(object)\n\t\t.sort()\n\t\t.map((key) => `${JSON.stringify(key)}:${stableStringify(object[key])}`)\n\t\t.join(\",\")}}`;\n}\n\nexport function isToolCallApprovalRequired(\n\tconfig: McpConfig,\n\tserverName: string,\n\ttoolMeta: Pick<ToolMetadata, \"originalName\">,\n\ttoolMetadata?: ReadonlyMap<string, readonly ToolMetadata[]>,\n): boolean {\n\tconst definition = config.mcpServers[serverName];\n\tconst serverApproval = definition?.approveTools;\n\tconst approval = serverApproval !== undefined ? serverApproval : config.settings?.approveTools;\n\n\tif (approval === true) return true;\n\tif (!Array.isArray(approval) || approval.length === 0) return false;\n\n\tconst prefix = resolveToolPrefix(definition, config.settings?.toolPrefix);\n\tconst currentCandidates = getToolNameCandidates(toolMeta.originalName, serverName, prefix, false);\n\tif (serverApproval !== undefined) {\n\t\tif (matchesToolPattern(currentCandidates, approval)) return true;\n\t\tif (!toolMetadata)\n\t\t\treturn matchesToolPattern(getToolNameCandidates(toolMeta.originalName, serverName, prefix), approval);\n\t\tconst legacyCandidates = getToolNameCandidates(toolMeta.originalName, serverName, prefix);\n\t\tconst legacyEmittedName = [...currentCandidates]\n\t\t\t.find((candidate) => candidate !== toolMeta.originalName)\n\t\t\t?.replace(/-/g, \"_\");\n\t\tif (legacyEmittedName) legacyCandidates.add(legacyEmittedName);\n\t\tfor (const candidate of currentCandidates) legacyCandidates.delete(candidate);\n\t\tconst otherCurrentCandidates = new Set<string>();\n\t\tfor (const tool of toolMetadata.get(serverName) ?? []) {\n\t\t\tfor (const candidate of getToolNameCandidates(tool.originalName, serverName, prefix, false)) {\n\t\t\t\totherCurrentCandidates.add(candidate);\n\t\t\t}\n\t\t}\n\t\tfor (const candidate of currentCandidates) otherCurrentCandidates.delete(candidate);\n\t\treturn approval.some(\n\t\t\t(pattern) =>\n\t\t\t\tmatchesToolPattern(legacyCandidates, [pattern]) && !matchesToolPattern(otherCurrentCandidates, [pattern]),\n\t\t);\n\t}\n\n\tif (matchesToolPattern(currentCandidates, approval)) return true;\n\tif (!toolMetadata) return false;\n\n\tconst legacyCandidates = getToolNameCandidates(toolMeta.originalName, serverName, prefix);\n\tconst legacyEmittedName = [...currentCandidates]\n\t\t.find((candidate) => candidate !== toolMeta.originalName)\n\t\t?.replace(/-/g, \"_\");\n\tif (legacyEmittedName) legacyCandidates.add(legacyEmittedName);\n\tfor (const candidate of currentCandidates) legacyCandidates.delete(candidate);\n\tconst otherCurrentCandidates = new Set<string>();\n\tfor (const [name, metadata] of toolMetadata) {\n\t\tconst otherPrefix = resolveToolPrefix(config.mcpServers[name], config.settings?.toolPrefix);\n\t\tfor (const tool of metadata) {\n\t\t\tfor (const candidate of getToolNameCandidates(tool.originalName, name, otherPrefix, false)) {\n\t\t\t\totherCurrentCandidates.add(candidate);\n\t\t\t}\n\t\t}\n\t}\n\tfor (const candidate of currentCandidates) otherCurrentCandidates.delete(candidate);\n\n\treturn approval.some(\n\t\t(pattern) =>\n\t\t\tmatchesToolPattern(legacyCandidates, [pattern]) && !matchesToolPattern(otherCurrentCandidates, [pattern]),\n\t);\n}\n\nfunction isMcpToolApprovalDecision(value: unknown): value is McpToolApprovalDecision {\n\treturn value === \"allow_once\" || value === \"allow_for_session\" || value === \"deny\" || value === \"abstain\";\n}\n\nasync function requestBrokerApproval(\n\tstate: McpExtensionState,\n\tserverName: string,\n\ttoolMeta: ToolMetadata,\n\targs: Record<string, unknown> | undefined,\n\torigin: McpToolApprovalOrigin,\n\tsignal?: AbortSignal,\n): Promise<McpToolApprovalDecision> {\n\tif (!state.approvalEvents) return \"abstain\";\n\n\tlet acceptingClaim = true;\n\tlet handler: McpToolApprovalHandler | undefined;\n\tconst request: McpToolApprovalRequest = {\n\t\trequestId: randomUUID(),\n\t\tserverName,\n\t\toriginalToolName: toolMeta.originalName,\n\t\tprefixedToolName: toolMeta.name,\n\t\targs: args ?? {},\n\t\torigin,\n\t\t...(signal !== undefined ? { signal } : {}),\n\t\tclaim(candidate: McpToolApprovalHandler) {\n\t\t\tif (!acceptingClaim || handler) return false;\n\t\t\thandler = candidate;\n\t\t\treturn true;\n\t\t},\n\t};\n\n\tstate.approvalEvents.emit(MCP_TOOL_APPROVAL_REQUEST_EVENT, request);\n\tacceptingClaim = false;\n\tif (!handler) return \"abstain\";\n\n\ttry {\n\t\tconst decision = await abortable(Promise.resolve().then(handler), signal);\n\t\treturn isMcpToolApprovalDecision(decision) ? decision : \"deny\";\n\t} catch (error) {\n\t\tif (signal?.aborted) throw error;\n\t\treturn \"deny\";\n\t}\n}\n\nexport async function ensureToolCallApproved(\n\tstate: McpExtensionState,\n\tserverName: string,\n\ttoolMeta: ToolMetadata,\n\targs: Record<string, unknown> | undefined,\n\tsignal?: AbortSignal,\n\torigin: McpToolApprovalOrigin = toolMeta.resourceUri ? \"resource\" : \"proxy\",\n\tapprovalMetadata?: ReadonlyMap<string, readonly ToolMetadata[]>,\n): Promise<ToolCallApprovalResult> {\n\tconst argsHash = createHash(\"sha256\")\n\t\t.update(stableStringify(args ?? {}))\n\t\t.digest(\"hex\");\n\tconst cacheKey = `${serverName}\\u0000${toolMeta.originalName}\\u0000${argsHash}`;\n\tif (!state.approvedToolCalls) state.approvedToolCalls = new Map<string, true>();\n\tconst approvedToolCalls = state.approvedToolCalls;\n\tif (approvedToolCalls.has(cacheKey)) {\n\t\treturn { ok: true };\n\t}\n\n\tconst brokerDecision = await requestBrokerApproval(state, serverName, toolMeta, args, origin, signal);\n\tif (brokerDecision === \"allow_once\") return { ok: true };\n\tif (brokerDecision === \"allow_for_session\") {\n\t\tapprovedToolCalls.set(cacheKey, true);\n\t\treturn { ok: true };\n\t}\n\tif (brokerDecision === \"deny\") return { ok: false, reason: \"denied\" };\n\n\tif (!isToolCallApprovalRequired(state.config, serverName, toolMeta, approvalMetadata ?? state.toolMetadata)) {\n\t\treturn { ok: true };\n\t}\n\n\tif (!state.ui) {\n\t\treturn { ok: false, reason: \"approval_required_headless\" };\n\t}\n\n\tconst json = JSON.stringify(args ?? {}, null, 2);\n\tconst sanitized = sanitizeTerminalText(json);\n\tconst preview = sanitized.length > 500 ? `${sanitized.slice(0, 500)}...` : sanitized;\n\tconst title = `MCP: ${sanitizeTerminalText(serverName)} wants to run ${sanitizeTerminalText(toolMeta.originalName)}`;\n\tconst ownedSignal = combineAbortSignals(state.owner?.signal, signal);\n\tconst decision = await abortable(\n\t\tstate.ui.select(`${title}\\n\\nArguments:\\n${preview}`, [\"Allow once\", \"Allow for session\", \"Deny\"]),\n\t\townedSignal,\n\t);\n\n\tif (decision === \"Allow once\") {\n\t\treturn { ok: true };\n\t}\n\tif (decision === \"Allow for session\") {\n\t\tapprovedToolCalls.set(cacheKey, true);\n\t\treturn { ok: true };\n\t}\n\treturn { ok: false, reason: \"denied\" };\n}\n"]}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { McpExtensionState } from "./state.ts";
|
|
2
|
+
import type { McpResource, McpTool, ServerEntry, ToolMetadata, ToolPrefix } from "./types.ts";
|
|
3
|
+
export declare function buildToolMetadata(tools: McpTool[], resources: McpResource[], definition: ServerEntry, serverName: string, prefix: ToolPrefix, configuredServers?: Record<string, ServerEntry>, knownMetadata?: Map<string, ToolMetadata[]>, includeMissingConfiguredCandidates?: boolean): {
|
|
4
|
+
metadata: ToolMetadata[];
|
|
5
|
+
failedTools: string[];
|
|
6
|
+
};
|
|
7
|
+
export declare function getToolNames(state: McpExtensionState, serverName: string): string[];
|
|
8
|
+
export declare function totalToolCount(state: McpExtensionState): number;
|
|
9
|
+
export declare function findToolByName(metadata: ToolMetadata[] | undefined, toolName: string): ToolMetadata | undefined;
|
|
10
|
+
export declare function formatSchema(schema: unknown, indent?: string): string;
|
|
11
|
+
//# sourceMappingURL=tool-metadata.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tool-metadata.d.ts","sourceRoot":"","sources":["../../../src/extensions/mcp/tool-metadata.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AACpD,OAAO,KAAK,EAAE,WAAW,EAAE,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAY9F,wBAAgB,iBAAiB,CAChC,KAAK,EAAE,OAAO,EAAE,EAChB,SAAS,EAAE,WAAW,EAAE,EACxB,UAAU,EAAE,WAAW,EACvB,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,UAAU,EAClB,iBAAiB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,EAC/C,aAAa,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,YAAY,EAAE,CAAC,EAC3C,kCAAkC,UAAQ,GACxC;IAAE,QAAQ,EAAE,YAAY,EAAE,CAAC;IAAC,WAAW,EAAE,MAAM,EAAE,CAAA;CAAE,CA2JrD;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,iBAAiB,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,EAAE,CAEnF;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,iBAAiB,GAAG,MAAM,CAM/D;AAED,wBAAgB,cAAc,CAAC,QAAQ,EAAE,YAAY,EAAE,GAAG,SAAS,EAAE,QAAQ,EAAE,MAAM,GAAG,YAAY,GAAG,SAAS,CAM/G;AAED,wBAAgB,YAAY,CAAC,MAAM,EAAE,OAAO,EAAE,MAAM,SAAO,GAAG,MAAM,CAmCnE","sourcesContent":["import { resourceNameToToolName } from \"./resource-tools.ts\";\nimport type { McpExtensionState } from \"./state.ts\";\nimport type { McpResource, McpTool, ServerEntry, ToolMetadata, ToolPrefix } from \"./types.ts\";\nimport {\n\tcreateToolSelectorCandidateIndex,\n\tformatToolName,\n\tgetToolNameCandidates,\n\tisToolAllowed,\n\tresolveToolPrefix,\n} from \"./types.ts\";\nimport { getToolUiResourceUri } from \"./ui-app-bridge-helpers.ts\";\nimport { extractUiToolVisibility, isUiToolVisibleToModel } from \"./ui-tool-visibility.ts\";\nimport { extractToolUiStreamMode } from \"./utils.ts\";\n\nexport function buildToolMetadata(\n\ttools: McpTool[],\n\tresources: McpResource[],\n\tdefinition: ServerEntry,\n\tserverName: string,\n\tprefix: ToolPrefix,\n\tconfiguredServers?: Record<string, ServerEntry>,\n\tknownMetadata?: Map<string, ToolMetadata[]>,\n\tincludeMissingConfiguredCandidates = false,\n): { metadata: ToolMetadata[]; failedTools: string[] } {\n\tconst metadata: ToolMetadata[] = [];\n\tconst failedTools: string[] = [];\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 =\n\t\thasToolFilters && configuredServers\n\t\t\t? (() => {\n\t\t\t\t\tconst candidates = new Set<string>();\n\t\t\t\t\tconst additionalCandidatesByToolName = new Map<string, Set<string>>();\n\t\t\t\t\tconst evaluatedToolNames = new Set<string>();\n\t\t\t\t\tconst addCandidates = (\n\t\t\t\t\t\ttarget: Set<string>,\n\t\t\t\t\t\toriginalName: string,\n\t\t\t\t\t\tcandidateServerName: string,\n\t\t\t\t\t\tcandidatePrefix: ToolPrefix,\n\t\t\t\t\t) => {\n\t\t\t\t\t\tfor (const candidate of getToolNameCandidates(\n\t\t\t\t\t\t\toriginalName,\n\t\t\t\t\t\t\tcandidateServerName,\n\t\t\t\t\t\t\tcandidatePrefix,\n\t\t\t\t\t\t\tfalse,\n\t\t\t\t\t\t))\n\t\t\t\t\t\t\ttarget.add(candidate);\n\t\t\t\t\t};\n\n\t\t\t\t\tfor (const tool of tools) {\n\t\t\t\t\t\tif (!tool?.name) continue;\n\t\t\t\t\t\tevaluatedToolNames.add(tool.name);\n\t\t\t\t\t\taddCandidates(candidates, tool.name, serverName, effectivePrefix);\n\t\t\t\t\t}\n\t\t\t\t\tif (definition.exposeResources !== false) {\n\t\t\t\t\t\tfor (const resource of resources) {\n\t\t\t\t\t\t\tconst baseName = `read_${resourceNameToToolName(resource.name)}`;\n\t\t\t\t\t\t\tevaluatedToolNames.add(baseName);\n\t\t\t\t\t\t\tif (resource?.name && resource?.uri)\n\t\t\t\t\t\t\t\taddCandidates(candidates, baseName, serverName, effectivePrefix);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tfor (const [otherServerName, otherDefinition] of Object.entries(configuredServers)) {\n\t\t\t\t\t\tif (otherServerName === serverName) continue;\n\t\t\t\t\t\tconst otherPrefix = resolveToolPrefix(otherDefinition, prefix);\n\t\t\t\t\t\tconst knownTools = knownMetadata?.get(otherServerName);\n\t\t\t\t\t\tif (knownTools) {\n\t\t\t\t\t\t\tfor (const tool of knownTools) {\n\t\t\t\t\t\t\t\tcandidates.add(tool.name);\n\t\t\t\t\t\t\t\taddCandidates(candidates, tool.originalName, otherServerName, otherPrefix);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} else if (!knownMetadata || includeMissingConfiguredCandidates) {\n\t\t\t\t\t\t\tfor (const toolName of evaluatedToolNames) {\n\t\t\t\t\t\t\t\tlet additionalCandidates = additionalCandidatesByToolName.get(toolName);\n\t\t\t\t\t\t\t\tif (!additionalCandidates) {\n\t\t\t\t\t\t\t\t\tadditionalCandidates = new Set<string>();\n\t\t\t\t\t\t\t\t\tadditionalCandidatesByToolName.set(toolName, additionalCandidates);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\taddCandidates(additionalCandidates, toolName, otherServerName, otherPrefix);\n\t\t\t\t\t\t\t\tif (includeMissingConfiguredCandidates) {\n\t\t\t\t\t\t\t\t\tfor (const candidate of getToolNameCandidates(\n\t\t\t\t\t\t\t\t\t\ttoolName,\n\t\t\t\t\t\t\t\t\t\totherServerName,\n\t\t\t\t\t\t\t\t\t\totherPrefix,\n\t\t\t\t\t\t\t\t\t\tfalse,\n\t\t\t\t\t\t\t\t\t)) {\n\t\t\t\t\t\t\t\t\t\tadditionalCandidates.add(candidate.replace(/-/g, \"_\"));\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\treturn createToolSelectorCandidateIndex(candidates, additionalCandidatesByToolName);\n\t\t\t\t})()\n\t\t\t: undefined;\n\n\tfor (const tool of tools) {\n\t\tif (!tool?.name) {\n\t\t\tfailedTools.push(\"(unnamed)\");\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\n\t\tconst uiVisibility = extractUiToolVisibility(tool._meta);\n\t\tif (!isUiToolVisibleToModel(uiVisibility)) {\n\t\t\tcontinue;\n\t\t}\n\t\tseenNames.add(name);\n\n\t\tlet uiResourceUri: string | undefined;\n\t\ttry {\n\t\t\tuiResourceUri = getToolUiResourceUri({ _meta: tool._meta });\n\t\t} catch {\n\t\t\tfailedTools.push(tool.name);\n\t\t}\n\t\tconst uiStreamMode = extractToolUiStreamMode(tool._meta);\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...(uiResourceUri !== undefined ? { uiResourceUri } : {}),\n\t\t\t...(uiVisibility !== undefined ? { uiVisibility } : {}),\n\t\t\t...(uiStreamMode !== undefined ? { uiStreamMode } : {}),\n\t\t});\n\t}\n\n\tif (definition.exposeResources !== false) {\n\t\tfor (const resource of resources) {\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, failedTools };\n}\n\nexport function getToolNames(state: McpExtensionState, serverName: string): string[] {\n\treturn state.toolMetadata.get(serverName)?.map((m) => m.name) ?? [];\n}\n\nexport function totalToolCount(state: McpExtensionState): number {\n\tlet count = 0;\n\tfor (const metadata of state.toolMetadata.values()) {\n\t\tcount += metadata.length;\n\t}\n\treturn count;\n}\n\nexport function findToolByName(metadata: ToolMetadata[] | undefined, toolName: string): ToolMetadata | undefined {\n\tif (!metadata) return undefined;\n\tconst exact = metadata.find((m) => m.name === toolName);\n\tif (exact) return exact;\n\tconst normalized = toolName.replace(/-/g, \"_\");\n\treturn metadata.find((m) => m.name.replace(/-/g, \"_\") === normalized);\n}\n\nexport function formatSchema(schema: unknown, indent = \" \"): string {\n\tif (!schema || typeof schema !== \"object\" || Array.isArray(schema)) {\n\t\treturn `${indent}(no schema)`;\n\t}\n\n\tconst s = schema as Record<string, unknown>;\n\n\tif (s.type === \"object\" && s.properties && typeof s.properties === \"object\" && !Array.isArray(s.properties)) {\n\t\tconst props = s.properties as Record<string, unknown>;\n\t\tconst required = Array.isArray(s.required)\n\t\t\t? s.required.filter((name): name is string => typeof name === \"string\")\n\t\t\t: [];\n\n\t\tif (Object.keys(props).length === 0) {\n\t\t\treturn `${indent}(no parameters)`;\n\t\t}\n\n\t\tconst lines: string[] = [];\n\t\tfor (const [name, propSchema] of Object.entries(props)) {\n\t\t\tlines.push(...formatProperty(name, propSchema, required.includes(name), indent));\n\t\t}\n\t\treturn lines.join(\"\\n\");\n\t}\n\n\tconst lines = formatNestedSchema(s, indent);\n\tif (lines.length > 0) {\n\t\treturn lines.join(\"\\n\");\n\t}\n\n\tconst typeStr = formatType(s);\n\tif (typeStr) {\n\t\treturn `${indent}(${typeStr})`;\n\t}\n\n\treturn `${indent}(complex schema)`;\n}\n\nfunction formatProperty(name: string, schema: unknown, required: boolean, indent: string): string[] {\n\tif (!schema || typeof schema !== \"object\" || Array.isArray(schema)) {\n\t\treturn [`${indent}${name}${required ? \" *required*\" : \"\"}`];\n\t}\n\n\tconst s = schema as Record<string, unknown>;\n\tconst parts = [`${indent}${name}`];\n\tconst typeStr = formatType(s);\n\tif (typeStr) parts.push(`(${typeStr})`);\n\tif (required) parts.push(\"*required*\");\n\tappendSchemaAnnotations(parts, s);\n\n\treturn [parts.join(\" \"), ...formatNestedSchema(s, `${indent} `)];\n}\n\nfunction formatNestedSchema(schema: Record<string, unknown>, indent: string): string[] {\n\tconst lines: string[] = [];\n\n\tif (Array.isArray(schema.anyOf)) {\n\t\tlines.push(...formatVariants(\"anyOf\", schema.anyOf, indent));\n\t}\n\tif (Array.isArray(schema.oneOf)) {\n\t\tlines.push(...formatVariants(\"oneOf\", schema.oneOf, indent));\n\t}\n\tif (schema.items !== undefined) {\n\t\tlines.push(...formatProperty(\"items\", schema.items, false, indent));\n\t}\n\tif (schema.properties && typeof schema.properties === \"object\" && !Array.isArray(schema.properties)) {\n\t\tconst required = Array.isArray(schema.required)\n\t\t\t? schema.required.filter((name): name is string => typeof name === \"string\")\n\t\t\t: [];\n\t\tfor (const [name, propSchema] of Object.entries(schema.properties as Record<string, unknown>)) {\n\t\t\tlines.push(...formatProperty(name, propSchema, required.includes(name), indent));\n\t\t}\n\t}\n\n\treturn lines;\n}\n\nfunction formatVariants(keyword: \"anyOf\" | \"oneOf\", variants: unknown[], indent: string): string[] {\n\tconst lines = [`${indent}${keyword}:`];\n\n\tfor (const variant of variants) {\n\t\tif (!variant || typeof variant !== \"object\" || Array.isArray(variant)) {\n\t\t\tlines.push(`${indent} - ${JSON.stringify(variant)}`);\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst s = variant as Record<string, unknown>;\n\t\tconst typeStr = formatType(s) || \"schema\";\n\t\tconst parts = [`${indent} - ${typeStr}`];\n\t\tappendSchemaAnnotations(parts, s);\n\t\tlines.push(parts.join(\" \"));\n\t\tlines.push(...formatNestedSchema(s, `${indent} `));\n\t}\n\n\treturn lines;\n}\n\nfunction formatType(schema: Record<string, unknown>): string {\n\tif (Object.hasOwn(schema, \"const\")) {\n\t\treturn `const ${JSON.stringify(schema.const)}`;\n\t}\n\n\tif (Array.isArray(schema.enum)) {\n\t\treturn `enum: ${schema.enum.map((v) => JSON.stringify(v)).join(\", \")}`;\n\t}\n\n\tif (Array.isArray(schema.type)) {\n\t\treturn schema.type.map((type) => String(type)).join(\" | \");\n\t}\n\n\tif (schema.type) {\n\t\treturn String(schema.type);\n\t}\n\n\tif (schema.properties && typeof schema.properties === \"object\" && !Array.isArray(schema.properties)) {\n\t\treturn \"object\";\n\t}\n\n\tif (schema.items !== undefined) {\n\t\treturn \"array\";\n\t}\n\n\treturn \"\";\n}\n\nfunction appendSchemaAnnotations(parts: string[], schema: Record<string, unknown>): void {\n\tif (schema.description && typeof schema.description === \"string\") {\n\t\tparts.push(`- ${schema.description}`);\n\t}\n\n\tfor (const key of [\n\t\t\"minLength\",\n\t\t\"maxLength\",\n\t\t\"minimum\",\n\t\t\"maximum\",\n\t\t\"minItems\",\n\t\t\"maxItems\",\n\t\t\"format\",\n\t\t\"pattern\",\n\t] as const) {\n\t\tif (schema[key] !== undefined) {\n\t\t\tparts.push(`[${key}: ${JSON.stringify(schema[key])}]`);\n\t\t}\n\t}\n\n\tif (schema.default !== undefined) {\n\t\tparts.push(`[default: ${JSON.stringify(schema.default)}]`);\n\t}\n}\n"]}
|
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
import { resourceNameToToolName } from "./resource-tools.js";
|
|
2
|
+
import { createToolSelectorCandidateIndex, formatToolName, getToolNameCandidates, isToolAllowed, resolveToolPrefix, } from "./types.js";
|
|
3
|
+
import { getToolUiResourceUri } from "./ui-app-bridge-helpers.js";
|
|
4
|
+
import { extractUiToolVisibility, isUiToolVisibleToModel } from "./ui-tool-visibility.js";
|
|
5
|
+
import { extractToolUiStreamMode } from "./utils.js";
|
|
6
|
+
export function buildToolMetadata(tools, resources, definition, serverName, prefix, configuredServers, knownMetadata, includeMissingConfiguredCandidates = false) {
|
|
7
|
+
const metadata = [];
|
|
8
|
+
const failedTools = [];
|
|
9
|
+
const seenNames = new Set();
|
|
10
|
+
const effectivePrefix = resolveToolPrefix(definition, prefix);
|
|
11
|
+
const hasToolFilters = (Array.isArray(definition.includeTools) && definition.includeTools.length > 0) ||
|
|
12
|
+
(Array.isArray(definition.excludeTools) && definition.excludeTools.length > 0);
|
|
13
|
+
const selectorCandidateIndex = hasToolFilters && configuredServers
|
|
14
|
+
? (() => {
|
|
15
|
+
const candidates = new Set();
|
|
16
|
+
const additionalCandidatesByToolName = new Map();
|
|
17
|
+
const evaluatedToolNames = new Set();
|
|
18
|
+
const addCandidates = (target, originalName, candidateServerName, candidatePrefix) => {
|
|
19
|
+
for (const candidate of getToolNameCandidates(originalName, candidateServerName, candidatePrefix, false))
|
|
20
|
+
target.add(candidate);
|
|
21
|
+
};
|
|
22
|
+
for (const tool of tools) {
|
|
23
|
+
if (!tool?.name)
|
|
24
|
+
continue;
|
|
25
|
+
evaluatedToolNames.add(tool.name);
|
|
26
|
+
addCandidates(candidates, tool.name, serverName, effectivePrefix);
|
|
27
|
+
}
|
|
28
|
+
if (definition.exposeResources !== false) {
|
|
29
|
+
for (const resource of resources) {
|
|
30
|
+
const baseName = `read_${resourceNameToToolName(resource.name)}`;
|
|
31
|
+
evaluatedToolNames.add(baseName);
|
|
32
|
+
if (resource?.name && resource?.uri)
|
|
33
|
+
addCandidates(candidates, baseName, serverName, effectivePrefix);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
for (const [otherServerName, otherDefinition] of Object.entries(configuredServers)) {
|
|
37
|
+
if (otherServerName === serverName)
|
|
38
|
+
continue;
|
|
39
|
+
const otherPrefix = resolveToolPrefix(otherDefinition, prefix);
|
|
40
|
+
const knownTools = knownMetadata?.get(otherServerName);
|
|
41
|
+
if (knownTools) {
|
|
42
|
+
for (const tool of knownTools) {
|
|
43
|
+
candidates.add(tool.name);
|
|
44
|
+
addCandidates(candidates, tool.originalName, otherServerName, otherPrefix);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
else if (!knownMetadata || includeMissingConfiguredCandidates) {
|
|
48
|
+
for (const toolName of evaluatedToolNames) {
|
|
49
|
+
let additionalCandidates = additionalCandidatesByToolName.get(toolName);
|
|
50
|
+
if (!additionalCandidates) {
|
|
51
|
+
additionalCandidates = new Set();
|
|
52
|
+
additionalCandidatesByToolName.set(toolName, additionalCandidates);
|
|
53
|
+
}
|
|
54
|
+
addCandidates(additionalCandidates, toolName, otherServerName, otherPrefix);
|
|
55
|
+
if (includeMissingConfiguredCandidates) {
|
|
56
|
+
for (const candidate of getToolNameCandidates(toolName, otherServerName, otherPrefix, false)) {
|
|
57
|
+
additionalCandidates.add(candidate.replace(/-/g, "_"));
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return createToolSelectorCandidateIndex(candidates, additionalCandidatesByToolName);
|
|
64
|
+
})()
|
|
65
|
+
: undefined;
|
|
66
|
+
for (const tool of tools) {
|
|
67
|
+
if (!tool?.name) {
|
|
68
|
+
failedTools.push("(unnamed)");
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
if (!isToolAllowed(tool.name, serverName, effectivePrefix, definition.includeTools, definition.excludeTools, selectorCandidateIndex)) {
|
|
72
|
+
continue;
|
|
73
|
+
}
|
|
74
|
+
const name = formatToolName(tool.name, serverName, effectivePrefix);
|
|
75
|
+
if (seenNames.has(name)) {
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
78
|
+
const uiVisibility = extractUiToolVisibility(tool._meta);
|
|
79
|
+
if (!isUiToolVisibleToModel(uiVisibility)) {
|
|
80
|
+
continue;
|
|
81
|
+
}
|
|
82
|
+
seenNames.add(name);
|
|
83
|
+
let uiResourceUri;
|
|
84
|
+
try {
|
|
85
|
+
uiResourceUri = getToolUiResourceUri({ _meta: tool._meta });
|
|
86
|
+
}
|
|
87
|
+
catch {
|
|
88
|
+
failedTools.push(tool.name);
|
|
89
|
+
}
|
|
90
|
+
const uiStreamMode = extractToolUiStreamMode(tool._meta);
|
|
91
|
+
metadata.push({
|
|
92
|
+
name,
|
|
93
|
+
originalName: tool.name,
|
|
94
|
+
description: tool.description ?? "",
|
|
95
|
+
...(tool.inputSchema !== undefined ? { inputSchema: tool.inputSchema } : {}),
|
|
96
|
+
...(uiResourceUri !== undefined ? { uiResourceUri } : {}),
|
|
97
|
+
...(uiVisibility !== undefined ? { uiVisibility } : {}),
|
|
98
|
+
...(uiStreamMode !== undefined ? { uiStreamMode } : {}),
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
if (definition.exposeResources !== false) {
|
|
102
|
+
for (const resource of resources) {
|
|
103
|
+
const baseName = `read_${resourceNameToToolName(resource.name)}`;
|
|
104
|
+
if (!isToolAllowed(baseName, serverName, effectivePrefix, definition.includeTools, definition.excludeTools, selectorCandidateIndex)) {
|
|
105
|
+
continue;
|
|
106
|
+
}
|
|
107
|
+
const name = formatToolName(baseName, serverName, effectivePrefix);
|
|
108
|
+
if (seenNames.has(name)) {
|
|
109
|
+
continue;
|
|
110
|
+
}
|
|
111
|
+
seenNames.add(name);
|
|
112
|
+
metadata.push({
|
|
113
|
+
name,
|
|
114
|
+
originalName: baseName,
|
|
115
|
+
description: resource.description ?? `Read resource: ${resource.uri}`,
|
|
116
|
+
resourceUri: resource.uri,
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
return { metadata, failedTools };
|
|
121
|
+
}
|
|
122
|
+
export function getToolNames(state, serverName) {
|
|
123
|
+
return state.toolMetadata.get(serverName)?.map((m) => m.name) ?? [];
|
|
124
|
+
}
|
|
125
|
+
export function totalToolCount(state) {
|
|
126
|
+
let count = 0;
|
|
127
|
+
for (const metadata of state.toolMetadata.values()) {
|
|
128
|
+
count += metadata.length;
|
|
129
|
+
}
|
|
130
|
+
return count;
|
|
131
|
+
}
|
|
132
|
+
export function findToolByName(metadata, toolName) {
|
|
133
|
+
if (!metadata)
|
|
134
|
+
return undefined;
|
|
135
|
+
const exact = metadata.find((m) => m.name === toolName);
|
|
136
|
+
if (exact)
|
|
137
|
+
return exact;
|
|
138
|
+
const normalized = toolName.replace(/-/g, "_");
|
|
139
|
+
return metadata.find((m) => m.name.replace(/-/g, "_") === normalized);
|
|
140
|
+
}
|
|
141
|
+
export function formatSchema(schema, indent = " ") {
|
|
142
|
+
if (!schema || typeof schema !== "object" || Array.isArray(schema)) {
|
|
143
|
+
return `${indent}(no schema)`;
|
|
144
|
+
}
|
|
145
|
+
const s = schema;
|
|
146
|
+
if (s.type === "object" && s.properties && typeof s.properties === "object" && !Array.isArray(s.properties)) {
|
|
147
|
+
const props = s.properties;
|
|
148
|
+
const required = Array.isArray(s.required)
|
|
149
|
+
? s.required.filter((name) => typeof name === "string")
|
|
150
|
+
: [];
|
|
151
|
+
if (Object.keys(props).length === 0) {
|
|
152
|
+
return `${indent}(no parameters)`;
|
|
153
|
+
}
|
|
154
|
+
const lines = [];
|
|
155
|
+
for (const [name, propSchema] of Object.entries(props)) {
|
|
156
|
+
lines.push(...formatProperty(name, propSchema, required.includes(name), indent));
|
|
157
|
+
}
|
|
158
|
+
return lines.join("\n");
|
|
159
|
+
}
|
|
160
|
+
const lines = formatNestedSchema(s, indent);
|
|
161
|
+
if (lines.length > 0) {
|
|
162
|
+
return lines.join("\n");
|
|
163
|
+
}
|
|
164
|
+
const typeStr = formatType(s);
|
|
165
|
+
if (typeStr) {
|
|
166
|
+
return `${indent}(${typeStr})`;
|
|
167
|
+
}
|
|
168
|
+
return `${indent}(complex schema)`;
|
|
169
|
+
}
|
|
170
|
+
function formatProperty(name, schema, required, indent) {
|
|
171
|
+
if (!schema || typeof schema !== "object" || Array.isArray(schema)) {
|
|
172
|
+
return [`${indent}${name}${required ? " *required*" : ""}`];
|
|
173
|
+
}
|
|
174
|
+
const s = schema;
|
|
175
|
+
const parts = [`${indent}${name}`];
|
|
176
|
+
const typeStr = formatType(s);
|
|
177
|
+
if (typeStr)
|
|
178
|
+
parts.push(`(${typeStr})`);
|
|
179
|
+
if (required)
|
|
180
|
+
parts.push("*required*");
|
|
181
|
+
appendSchemaAnnotations(parts, s);
|
|
182
|
+
return [parts.join(" "), ...formatNestedSchema(s, `${indent} `)];
|
|
183
|
+
}
|
|
184
|
+
function formatNestedSchema(schema, indent) {
|
|
185
|
+
const lines = [];
|
|
186
|
+
if (Array.isArray(schema.anyOf)) {
|
|
187
|
+
lines.push(...formatVariants("anyOf", schema.anyOf, indent));
|
|
188
|
+
}
|
|
189
|
+
if (Array.isArray(schema.oneOf)) {
|
|
190
|
+
lines.push(...formatVariants("oneOf", schema.oneOf, indent));
|
|
191
|
+
}
|
|
192
|
+
if (schema.items !== undefined) {
|
|
193
|
+
lines.push(...formatProperty("items", schema.items, false, indent));
|
|
194
|
+
}
|
|
195
|
+
if (schema.properties && typeof schema.properties === "object" && !Array.isArray(schema.properties)) {
|
|
196
|
+
const required = Array.isArray(schema.required)
|
|
197
|
+
? schema.required.filter((name) => typeof name === "string")
|
|
198
|
+
: [];
|
|
199
|
+
for (const [name, propSchema] of Object.entries(schema.properties)) {
|
|
200
|
+
lines.push(...formatProperty(name, propSchema, required.includes(name), indent));
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
return lines;
|
|
204
|
+
}
|
|
205
|
+
function formatVariants(keyword, variants, indent) {
|
|
206
|
+
const lines = [`${indent}${keyword}:`];
|
|
207
|
+
for (const variant of variants) {
|
|
208
|
+
if (!variant || typeof variant !== "object" || Array.isArray(variant)) {
|
|
209
|
+
lines.push(`${indent} - ${JSON.stringify(variant)}`);
|
|
210
|
+
continue;
|
|
211
|
+
}
|
|
212
|
+
const s = variant;
|
|
213
|
+
const typeStr = formatType(s) || "schema";
|
|
214
|
+
const parts = [`${indent} - ${typeStr}`];
|
|
215
|
+
appendSchemaAnnotations(parts, s);
|
|
216
|
+
lines.push(parts.join(" "));
|
|
217
|
+
lines.push(...formatNestedSchema(s, `${indent} `));
|
|
218
|
+
}
|
|
219
|
+
return lines;
|
|
220
|
+
}
|
|
221
|
+
function formatType(schema) {
|
|
222
|
+
if (Object.hasOwn(schema, "const")) {
|
|
223
|
+
return `const ${JSON.stringify(schema.const)}`;
|
|
224
|
+
}
|
|
225
|
+
if (Array.isArray(schema.enum)) {
|
|
226
|
+
return `enum: ${schema.enum.map((v) => JSON.stringify(v)).join(", ")}`;
|
|
227
|
+
}
|
|
228
|
+
if (Array.isArray(schema.type)) {
|
|
229
|
+
return schema.type.map((type) => String(type)).join(" | ");
|
|
230
|
+
}
|
|
231
|
+
if (schema.type) {
|
|
232
|
+
return String(schema.type);
|
|
233
|
+
}
|
|
234
|
+
if (schema.properties && typeof schema.properties === "object" && !Array.isArray(schema.properties)) {
|
|
235
|
+
return "object";
|
|
236
|
+
}
|
|
237
|
+
if (schema.items !== undefined) {
|
|
238
|
+
return "array";
|
|
239
|
+
}
|
|
240
|
+
return "";
|
|
241
|
+
}
|
|
242
|
+
function appendSchemaAnnotations(parts, schema) {
|
|
243
|
+
if (schema.description && typeof schema.description === "string") {
|
|
244
|
+
parts.push(`- ${schema.description}`);
|
|
245
|
+
}
|
|
246
|
+
for (const key of [
|
|
247
|
+
"minLength",
|
|
248
|
+
"maxLength",
|
|
249
|
+
"minimum",
|
|
250
|
+
"maximum",
|
|
251
|
+
"minItems",
|
|
252
|
+
"maxItems",
|
|
253
|
+
"format",
|
|
254
|
+
"pattern",
|
|
255
|
+
]) {
|
|
256
|
+
if (schema[key] !== undefined) {
|
|
257
|
+
parts.push(`[${key}: ${JSON.stringify(schema[key])}]`);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
if (schema.default !== undefined) {
|
|
261
|
+
parts.push(`[default: ${JSON.stringify(schema.default)}]`);
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
//# sourceMappingURL=tool-metadata.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tool-metadata.js","sourceRoot":"","sources":["../../../src/extensions/mcp/tool-metadata.ts"],"names":[],"mappings":"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","sourcesContent":["import { resourceNameToToolName } from \"./resource-tools.ts\";\nimport type { McpExtensionState } from \"./state.ts\";\nimport type { McpResource, McpTool, ServerEntry, ToolMetadata, ToolPrefix } from \"./types.ts\";\nimport {\n\tcreateToolSelectorCandidateIndex,\n\tformatToolName,\n\tgetToolNameCandidates,\n\tisToolAllowed,\n\tresolveToolPrefix,\n} from \"./types.ts\";\nimport { getToolUiResourceUri } from \"./ui-app-bridge-helpers.ts\";\nimport { extractUiToolVisibility, isUiToolVisibleToModel } from \"./ui-tool-visibility.ts\";\nimport { extractToolUiStreamMode } from \"./utils.ts\";\n\nexport function buildToolMetadata(\n\ttools: McpTool[],\n\tresources: McpResource[],\n\tdefinition: ServerEntry,\n\tserverName: string,\n\tprefix: ToolPrefix,\n\tconfiguredServers?: Record<string, ServerEntry>,\n\tknownMetadata?: Map<string, ToolMetadata[]>,\n\tincludeMissingConfiguredCandidates = false,\n): { metadata: ToolMetadata[]; failedTools: string[] } {\n\tconst metadata: ToolMetadata[] = [];\n\tconst failedTools: string[] = [];\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 =\n\t\thasToolFilters && configuredServers\n\t\t\t? (() => {\n\t\t\t\t\tconst candidates = new Set<string>();\n\t\t\t\t\tconst additionalCandidatesByToolName = new Map<string, Set<string>>();\n\t\t\t\t\tconst evaluatedToolNames = new Set<string>();\n\t\t\t\t\tconst addCandidates = (\n\t\t\t\t\t\ttarget: Set<string>,\n\t\t\t\t\t\toriginalName: string,\n\t\t\t\t\t\tcandidateServerName: string,\n\t\t\t\t\t\tcandidatePrefix: ToolPrefix,\n\t\t\t\t\t) => {\n\t\t\t\t\t\tfor (const candidate of getToolNameCandidates(\n\t\t\t\t\t\t\toriginalName,\n\t\t\t\t\t\t\tcandidateServerName,\n\t\t\t\t\t\t\tcandidatePrefix,\n\t\t\t\t\t\t\tfalse,\n\t\t\t\t\t\t))\n\t\t\t\t\t\t\ttarget.add(candidate);\n\t\t\t\t\t};\n\n\t\t\t\t\tfor (const tool of tools) {\n\t\t\t\t\t\tif (!tool?.name) continue;\n\t\t\t\t\t\tevaluatedToolNames.add(tool.name);\n\t\t\t\t\t\taddCandidates(candidates, tool.name, serverName, effectivePrefix);\n\t\t\t\t\t}\n\t\t\t\t\tif (definition.exposeResources !== false) {\n\t\t\t\t\t\tfor (const resource of resources) {\n\t\t\t\t\t\t\tconst baseName = `read_${resourceNameToToolName(resource.name)}`;\n\t\t\t\t\t\t\tevaluatedToolNames.add(baseName);\n\t\t\t\t\t\t\tif (resource?.name && resource?.uri)\n\t\t\t\t\t\t\t\taddCandidates(candidates, baseName, serverName, effectivePrefix);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tfor (const [otherServerName, otherDefinition] of Object.entries(configuredServers)) {\n\t\t\t\t\t\tif (otherServerName === serverName) continue;\n\t\t\t\t\t\tconst otherPrefix = resolveToolPrefix(otherDefinition, prefix);\n\t\t\t\t\t\tconst knownTools = knownMetadata?.get(otherServerName);\n\t\t\t\t\t\tif (knownTools) {\n\t\t\t\t\t\t\tfor (const tool of knownTools) {\n\t\t\t\t\t\t\t\tcandidates.add(tool.name);\n\t\t\t\t\t\t\t\taddCandidates(candidates, tool.originalName, otherServerName, otherPrefix);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} else if (!knownMetadata || includeMissingConfiguredCandidates) {\n\t\t\t\t\t\t\tfor (const toolName of evaluatedToolNames) {\n\t\t\t\t\t\t\t\tlet additionalCandidates = additionalCandidatesByToolName.get(toolName);\n\t\t\t\t\t\t\t\tif (!additionalCandidates) {\n\t\t\t\t\t\t\t\t\tadditionalCandidates = new Set<string>();\n\t\t\t\t\t\t\t\t\tadditionalCandidatesByToolName.set(toolName, additionalCandidates);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\taddCandidates(additionalCandidates, toolName, otherServerName, otherPrefix);\n\t\t\t\t\t\t\t\tif (includeMissingConfiguredCandidates) {\n\t\t\t\t\t\t\t\t\tfor (const candidate of getToolNameCandidates(\n\t\t\t\t\t\t\t\t\t\ttoolName,\n\t\t\t\t\t\t\t\t\t\totherServerName,\n\t\t\t\t\t\t\t\t\t\totherPrefix,\n\t\t\t\t\t\t\t\t\t\tfalse,\n\t\t\t\t\t\t\t\t\t)) {\n\t\t\t\t\t\t\t\t\t\tadditionalCandidates.add(candidate.replace(/-/g, \"_\"));\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\treturn createToolSelectorCandidateIndex(candidates, additionalCandidatesByToolName);\n\t\t\t\t})()\n\t\t\t: undefined;\n\n\tfor (const tool of tools) {\n\t\tif (!tool?.name) {\n\t\t\tfailedTools.push(\"(unnamed)\");\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\n\t\tconst uiVisibility = extractUiToolVisibility(tool._meta);\n\t\tif (!isUiToolVisibleToModel(uiVisibility)) {\n\t\t\tcontinue;\n\t\t}\n\t\tseenNames.add(name);\n\n\t\tlet uiResourceUri: string | undefined;\n\t\ttry {\n\t\t\tuiResourceUri = getToolUiResourceUri({ _meta: tool._meta });\n\t\t} catch {\n\t\t\tfailedTools.push(tool.name);\n\t\t}\n\t\tconst uiStreamMode = extractToolUiStreamMode(tool._meta);\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...(uiResourceUri !== undefined ? { uiResourceUri } : {}),\n\t\t\t...(uiVisibility !== undefined ? { uiVisibility } : {}),\n\t\t\t...(uiStreamMode !== undefined ? { uiStreamMode } : {}),\n\t\t});\n\t}\n\n\tif (definition.exposeResources !== false) {\n\t\tfor (const resource of resources) {\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, failedTools };\n}\n\nexport function getToolNames(state: McpExtensionState, serverName: string): string[] {\n\treturn state.toolMetadata.get(serverName)?.map((m) => m.name) ?? [];\n}\n\nexport function totalToolCount(state: McpExtensionState): number {\n\tlet count = 0;\n\tfor (const metadata of state.toolMetadata.values()) {\n\t\tcount += metadata.length;\n\t}\n\treturn count;\n}\n\nexport function findToolByName(metadata: ToolMetadata[] | undefined, toolName: string): ToolMetadata | undefined {\n\tif (!metadata) return undefined;\n\tconst exact = metadata.find((m) => m.name === toolName);\n\tif (exact) return exact;\n\tconst normalized = toolName.replace(/-/g, \"_\");\n\treturn metadata.find((m) => m.name.replace(/-/g, \"_\") === normalized);\n}\n\nexport function formatSchema(schema: unknown, indent = \" \"): string {\n\tif (!schema || typeof schema !== \"object\" || Array.isArray(schema)) {\n\t\treturn `${indent}(no schema)`;\n\t}\n\n\tconst s = schema as Record<string, unknown>;\n\n\tif (s.type === \"object\" && s.properties && typeof s.properties === \"object\" && !Array.isArray(s.properties)) {\n\t\tconst props = s.properties as Record<string, unknown>;\n\t\tconst required = Array.isArray(s.required)\n\t\t\t? s.required.filter((name): name is string => typeof name === \"string\")\n\t\t\t: [];\n\n\t\tif (Object.keys(props).length === 0) {\n\t\t\treturn `${indent}(no parameters)`;\n\t\t}\n\n\t\tconst lines: string[] = [];\n\t\tfor (const [name, propSchema] of Object.entries(props)) {\n\t\t\tlines.push(...formatProperty(name, propSchema, required.includes(name), indent));\n\t\t}\n\t\treturn lines.join(\"\\n\");\n\t}\n\n\tconst lines = formatNestedSchema(s, indent);\n\tif (lines.length > 0) {\n\t\treturn lines.join(\"\\n\");\n\t}\n\n\tconst typeStr = formatType(s);\n\tif (typeStr) {\n\t\treturn `${indent}(${typeStr})`;\n\t}\n\n\treturn `${indent}(complex schema)`;\n}\n\nfunction formatProperty(name: string, schema: unknown, required: boolean, indent: string): string[] {\n\tif (!schema || typeof schema !== \"object\" || Array.isArray(schema)) {\n\t\treturn [`${indent}${name}${required ? \" *required*\" : \"\"}`];\n\t}\n\n\tconst s = schema as Record<string, unknown>;\n\tconst parts = [`${indent}${name}`];\n\tconst typeStr = formatType(s);\n\tif (typeStr) parts.push(`(${typeStr})`);\n\tif (required) parts.push(\"*required*\");\n\tappendSchemaAnnotations(parts, s);\n\n\treturn [parts.join(\" \"), ...formatNestedSchema(s, `${indent} `)];\n}\n\nfunction formatNestedSchema(schema: Record<string, unknown>, indent: string): string[] {\n\tconst lines: string[] = [];\n\n\tif (Array.isArray(schema.anyOf)) {\n\t\tlines.push(...formatVariants(\"anyOf\", schema.anyOf, indent));\n\t}\n\tif (Array.isArray(schema.oneOf)) {\n\t\tlines.push(...formatVariants(\"oneOf\", schema.oneOf, indent));\n\t}\n\tif (schema.items !== undefined) {\n\t\tlines.push(...formatProperty(\"items\", schema.items, false, indent));\n\t}\n\tif (schema.properties && typeof schema.properties === \"object\" && !Array.isArray(schema.properties)) {\n\t\tconst required = Array.isArray(schema.required)\n\t\t\t? schema.required.filter((name): name is string => typeof name === \"string\")\n\t\t\t: [];\n\t\tfor (const [name, propSchema] of Object.entries(schema.properties as Record<string, unknown>)) {\n\t\t\tlines.push(...formatProperty(name, propSchema, required.includes(name), indent));\n\t\t}\n\t}\n\n\treturn lines;\n}\n\nfunction formatVariants(keyword: \"anyOf\" | \"oneOf\", variants: unknown[], indent: string): string[] {\n\tconst lines = [`${indent}${keyword}:`];\n\n\tfor (const variant of variants) {\n\t\tif (!variant || typeof variant !== \"object\" || Array.isArray(variant)) {\n\t\t\tlines.push(`${indent} - ${JSON.stringify(variant)}`);\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst s = variant as Record<string, unknown>;\n\t\tconst typeStr = formatType(s) || \"schema\";\n\t\tconst parts = [`${indent} - ${typeStr}`];\n\t\tappendSchemaAnnotations(parts, s);\n\t\tlines.push(parts.join(\" \"));\n\t\tlines.push(...formatNestedSchema(s, `${indent} `));\n\t}\n\n\treturn lines;\n}\n\nfunction formatType(schema: Record<string, unknown>): string {\n\tif (Object.hasOwn(schema, \"const\")) {\n\t\treturn `const ${JSON.stringify(schema.const)}`;\n\t}\n\n\tif (Array.isArray(schema.enum)) {\n\t\treturn `enum: ${schema.enum.map((v) => JSON.stringify(v)).join(\", \")}`;\n\t}\n\n\tif (Array.isArray(schema.type)) {\n\t\treturn schema.type.map((type) => String(type)).join(\" | \");\n\t}\n\n\tif (schema.type) {\n\t\treturn String(schema.type);\n\t}\n\n\tif (schema.properties && typeof schema.properties === \"object\" && !Array.isArray(schema.properties)) {\n\t\treturn \"object\";\n\t}\n\n\tif (schema.items !== undefined) {\n\t\treturn \"array\";\n\t}\n\n\treturn \"\";\n}\n\nfunction appendSchemaAnnotations(parts: string[], schema: Record<string, unknown>): void {\n\tif (schema.description && typeof schema.description === \"string\") {\n\t\tparts.push(`- ${schema.description}`);\n\t}\n\n\tfor (const key of [\n\t\t\"minLength\",\n\t\t\"maxLength\",\n\t\t\"minimum\",\n\t\t\"maximum\",\n\t\t\"minItems\",\n\t\t\"maxItems\",\n\t\t\"format\",\n\t\t\"pattern\",\n\t] as const) {\n\t\tif (schema[key] !== undefined) {\n\t\t\tparts.push(`[${key}: ${JSON.stringify(schema[key])}]`);\n\t\t}\n\t}\n\n\tif (schema.default !== undefined) {\n\t\tparts.push(`[default: ${JSON.stringify(schema.default)}]`);\n\t}\n}\n"]}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { ContentBlock, McpContent } from "./types.ts";
|
|
2
|
+
type McpResourceContent = {
|
|
3
|
+
uri: string;
|
|
4
|
+
text?: string | undefined;
|
|
5
|
+
blob?: string | undefined;
|
|
6
|
+
mimeType?: string | undefined;
|
|
7
|
+
[key: string]: unknown;
|
|
8
|
+
};
|
|
9
|
+
export declare function cleanupMaterializedBinaryResources(scope?: object): void;
|
|
10
|
+
/**
|
|
11
|
+
* Transform MCP content types to Nexus content blocks.
|
|
12
|
+
*/
|
|
13
|
+
export declare function transformMcpResourceContents(contents: McpResourceContent[], scope?: object): ContentBlock[];
|
|
14
|
+
export declare function transformMcpContent(content: McpContent[], scope?: object): ContentBlock[];
|
|
15
|
+
/**
|
|
16
|
+
* Resolve a tool result's content blocks, falling back to structuredContent
|
|
17
|
+
* when content is empty.
|
|
18
|
+
*/
|
|
19
|
+
export declare function resolveMcpResultContent(result: Record<string, unknown>, scope?: object): ContentBlock[];
|
|
20
|
+
export {};
|
|
21
|
+
//# sourceMappingURL=tool-registrar.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tool-registrar.d.ts","sourceRoot":"","sources":["../../../src/extensions/mcp/tool-registrar.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAkD3D,KAAK,kBAAkB,GAAG;IACzB,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1B,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1B,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC9B,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACvB,CAAC;AA8CF,wBAAgB,kCAAkC,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAYvE;AA8DD;;GAEG;AACH,wBAAgB,4BAA4B,CAAC,QAAQ,EAAE,kBAAkB,EAAE,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,YAAY,EAAE,CAO3G;AAED,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,UAAU,EAAE,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,YAAY,EAAE,CA2CzF;AAED;;;GAGG;AACH,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,YAAY,EAAE,CASvG","sourcesContent":["// tool-registrar.ts - MCP content transformation\n// NOTE: Tools are NOT registered with Nexus - only the unified `mcp` proxy tool is registered.\n// This keeps the LLM context small (1 tool instead of 100s).\n\nimport { mkdtempSync, rmSync, writeFileSync } from \"node:fs\";\nimport { tmpdir } from \"node:os\";\nimport { join } from \"node:path\";\nimport type { ContentBlock, McpContent } from \"./types.ts\";\n\nconst MAX_BINARY_RESOURCE_BYTES = 10 * 1024 * 1024;\nconst MAX_SESSION_RESOURCE_BYTES = 100 * 1024 * 1024;\nconst MAX_SESSION_RESOURCE_FILES = 10_000; // Bounds metadata from empty or tiny resources.\nconst CLEANUP_RETRY_DELAY_MS = 30_000;\nconst MAX_CLEANUP_RETRY_ATTEMPTS = 3;\ntype MaterializedResourceSession = {\n\tdirectory: string | undefined;\n\tbytes: number;\n\tfiles: number;\n\tsequence: number;\n};\n\nfunction createMaterializedResourceSession(): MaterializedResourceSession {\n\treturn {\n\t\tdirectory: undefined,\n\t\tbytes: 0,\n\t\tfiles: 0,\n\t\tsequence: 0,\n\t};\n}\n\nconst defaultMaterializedResourceSession = createMaterializedResourceSession();\nconst scopedMaterializedResourceSessions = new WeakMap<object, MaterializedResourceSession>();\nconst pendingCleanupDirectories = new Set<string>();\nconst cleanupRetryAttempts = new Map<string, number>();\nlet pendingCleanupRetry: ReturnType<typeof setTimeout> | undefined;\n\nfunction isAbortedScope(scope: object | undefined): boolean {\n\treturn !!scope && \"aborted\" in scope && (scope as { aborted?: unknown }).aborted === true;\n}\n\nfunction getMaterializedResourceSession(scope?: object): MaterializedResourceSession | undefined {\n\tif (isAbortedScope(scope)) return undefined;\n\tif (!scope) return defaultMaterializedResourceSession;\n\tlet session = scopedMaterializedResourceSessions.get(scope);\n\tif (!session) {\n\t\tsession = createMaterializedResourceSession();\n\t\tscopedMaterializedResourceSessions.set(scope, session);\n\t}\n\treturn session;\n}\n\ntype BinaryResource = {\n\turi?: string | undefined;\n\ttext?: string | undefined;\n\tmimeType?: string | undefined;\n\tblob: string;\n};\ntype McpResourceContent = {\n\turi: string;\n\ttext?: string | undefined;\n\tblob?: string | undefined;\n\tmimeType?: string | undefined;\n\t[key: string]: unknown;\n};\n\nfunction hasRetryableCleanupDirectory(): boolean {\n\tfor (const directory of pendingCleanupDirectories) {\n\t\tif ((cleanupRetryAttempts.get(directory) ?? 0) < MAX_CLEANUP_RETRY_ATTEMPTS) return true;\n\t}\n\treturn false;\n}\n\nfunction schedulePendingCleanupRetry(): void {\n\tif (pendingCleanupRetry || !hasRetryableCleanupDirectory()) return;\n\tfor (const directory of pendingCleanupDirectories) {\n\t\tconst attempts = cleanupRetryAttempts.get(directory) ?? 0;\n\t\tif (attempts < MAX_CLEANUP_RETRY_ATTEMPTS) cleanupRetryAttempts.set(directory, attempts + 1);\n\t}\n\tpendingCleanupRetry = setTimeout(() => {\n\t\tpendingCleanupRetry = undefined;\n\t\ttry {\n\t\t\tdrainPendingCleanupDirectories();\n\t\t} catch {\n\t\t\t// drainPendingCleanupDirectories already retained the paths and rescheduled another retry.\n\t\t}\n\t}, CLEANUP_RETRY_DELAY_MS);\n}\n\nfunction drainPendingCleanupDirectories(): void {\n\tconst failures: unknown[] = [];\n\tfor (const directory of Array.from(pendingCleanupDirectories)) {\n\t\ttry {\n\t\t\trmSync(directory, { recursive: true, force: true });\n\t\t\tpendingCleanupDirectories.delete(directory);\n\t\t\tcleanupRetryAttempts.delete(directory);\n\t\t} catch (error) {\n\t\t\tfailures.push(error);\n\t\t}\n\t}\n\tif (pendingCleanupDirectories.size === 0 && pendingCleanupRetry) {\n\t\tclearTimeout(pendingCleanupRetry);\n\t\tpendingCleanupRetry = undefined;\n\t}\n\tif (failures.length > 0) {\n\t\tschedulePendingCleanupRetry();\n\t\tthrow new AggregateError(failures, \"Failed to clean materialized MCP resources\");\n\t}\n}\n\nexport function cleanupMaterializedBinaryResources(scope?: object): void {\n\tconst session = scope ? scopedMaterializedResourceSessions.get(scope) : defaultMaterializedResourceSession;\n\tif (session?.directory) pendingCleanupDirectories.add(session.directory);\n\tif (session) {\n\t\tsession.directory = undefined;\n\t\tsession.bytes = 0;\n\t\tsession.files = 0;\n\t\tsession.sequence = 0;\n\t\tif (scope) scopedMaterializedResourceSessions.delete(scope);\n\t}\n\n\tdrainPendingCleanupDirectories();\n}\n\nfunction replaceBlob(resource: BinaryResource, text: string): string {\n\tdelete (resource as Partial<BinaryResource>).blob;\n\tresource.text = text;\n\treturn text;\n}\n\nfunction omitBinaryResource(resource: BinaryResource, reason: string): string {\n\treturn replaceBlob(\n\t\tresource,\n\t\t[\n\t\t\t`[Resource: ${resource.uri ?? \"(no URI)\"}]`,\n\t\t\t`Binary content omitted: ${reason}`,\n\t\t\t`MIME type: ${resource.mimeType ?? \"application/octet-stream\"}`,\n\t\t].join(\"\\n\"),\n\t);\n}\n\nfunction materializeBinaryResource(resource: BinaryResource, scope?: object): string {\n\tconst session = getMaterializedResourceSession(scope);\n\tif (!session) return omitBinaryResource(resource, \"runtime stopped\");\n\tconst decodedBytes = Buffer.byteLength(resource.blob, \"base64\");\n\tif (decodedBytes > MAX_BINARY_RESOURCE_BYTES) {\n\t\treturn omitBinaryResource(resource, \"decoded size exceeds 10 MiB\");\n\t}\n\tif (session.bytes + decodedBytes > MAX_SESSION_RESOURCE_BYTES || session.files >= MAX_SESSION_RESOURCE_FILES) {\n\t\treturn omitBinaryResource(resource, \"session resource limit reached\");\n\t}\n\n\ttry {\n\t\tsession.directory ??= mkdtempSync(join(tmpdir(), \"nexus-mcp-resource-\"));\n\t} catch {\n\t\treturn omitBinaryResource(resource, \"could not be saved\");\n\t}\n\n\tconst filePath = join(session.directory, `resource-${++session.sequence}.bin`);\n\tsession.bytes += decodedBytes;\n\tsession.files += 1;\n\ttry {\n\t\twriteFileSync(filePath, Buffer.from(resource.blob, \"base64\"), { flag: \"wx\", mode: 0o600 });\n\t} catch {\n\t\ttry {\n\t\t\trmSync(filePath, { force: true });\n\t\t\tsession.bytes -= decodedBytes;\n\t\t\tsession.files -= 1;\n\t\t} catch {\n\t\t\t// Keep the reservation when a partial file cannot be removed.\n\t\t}\n\t\treturn omitBinaryResource(resource, \"could not be saved\");\n\t}\n\n\treturn replaceBlob(\n\t\tresource,\n\t\t[\n\t\t\t`[Resource: ${resource.uri ?? \"(no URI)\"}]`,\n\t\t\t`Binary content saved to ${filePath}`,\n\t\t\t`MIME type: ${resource.mimeType ?? \"application/octet-stream\"}`,\n\t\t].join(\"\\n\"),\n\t);\n}\n\n/**\n * Transform MCP content types to Nexus content blocks.\n */\nexport function transformMcpResourceContents(contents: McpResourceContent[], scope?: object): ContentBlock[] {\n\treturn contents.map((resource) => {\n\t\tif (typeof resource.text === \"string\") return { type: \"text\" as const, text: resource.text };\n\t\tif (typeof resource.blob === \"string\")\n\t\t\treturn { type: \"text\" as const, text: materializeBinaryResource(resource as BinaryResource, scope) };\n\t\treturn { type: \"text\" as const, text: JSON.stringify(resource) };\n\t});\n}\n\nexport function transformMcpContent(content: McpContent[], scope?: object): ContentBlock[] {\n\treturn content.map((c) => {\n\t\tif (c.type === \"text\") {\n\t\t\treturn { type: \"text\" as const, text: c.text ?? \"\" };\n\t\t}\n\t\tif (c.type === \"image\") {\n\t\t\treturn {\n\t\t\t\ttype: \"image\" as const,\n\t\t\t\tdata: c.data ?? \"\",\n\t\t\t\tmimeType: c.mimeType ?? \"image/png\",\n\t\t\t};\n\t\t}\n\t\tif (c.type === \"resource\") {\n\t\t\tconst resourceUri = c.resource?.uri ?? \"(no URI)\";\n\t\t\tif (c.resource && \"blob\" in c.resource && typeof c.resource.blob === \"string\") {\n\t\t\t\tconst binaryResource = c.resource as typeof c.resource & { mimeType?: string; blob: string };\n\t\t\t\treturn {\n\t\t\t\t\ttype: \"text\" as const,\n\t\t\t\t\ttext: materializeBinaryResource(binaryResource, scope),\n\t\t\t\t};\n\t\t\t}\n\t\t\tconst resourceContent = c.resource?.text ?? (c.resource ? JSON.stringify(c.resource) : \"(no content)\");\n\t\t\treturn {\n\t\t\t\ttype: \"text\" as const,\n\t\t\t\ttext: `[Resource: ${resourceUri}]\\n${resourceContent}`,\n\t\t\t};\n\t\t}\n\t\tif (c.type === \"resource_link\") {\n\t\t\tconst linkName = c.name ?? c.uri ?? \"unknown\";\n\t\t\tconst linkUri = c.uri ?? \"(no URI)\";\n\t\t\treturn {\n\t\t\t\ttype: \"text\" as const,\n\t\t\t\ttext: `[Resource Link: ${linkName}]\\nURI: ${linkUri}`,\n\t\t\t};\n\t\t}\n\t\tif (c.type === \"audio\") {\n\t\t\treturn {\n\t\t\t\ttype: \"text\" as const,\n\t\t\t\ttext: `[Audio content: ${c.mimeType ?? \"audio/*\"}]`,\n\t\t\t};\n\t\t}\n\t\treturn { type: \"text\" as const, text: JSON.stringify(c) };\n\t});\n}\n\n/**\n * Resolve a tool result's content blocks, falling back to structuredContent\n * when content is empty.\n */\nexport function resolveMcpResultContent(result: Record<string, unknown>, scope?: object): ContentBlock[] {\n\tconst blocks = transformMcpContent((Array.isArray(result.content) ? result.content : []) as McpContent[], scope);\n\tif (blocks.length > 0) return blocks;\n\n\tif (result.structuredContent !== undefined && result.structuredContent !== null) {\n\t\treturn [{ type: \"text\" as const, text: stringifyStructuredContent(result.structuredContent) }];\n\t}\n\n\treturn [];\n}\n\nfunction stringifyStructuredContent(value: unknown): string {\n\ttry {\n\t\treturn JSON.stringify(value, null, 2) ?? String(value);\n\t} catch {\n\t\treturn String(value);\n\t}\n}\n"]}
|