@duckmind/dm-windows-x64 0.63.4 → 0.63.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (121) hide show
  1. package/dm.exe +0 -0
  2. package/extensions/.dm-extensions.json +236 -13
  3. package/extensions/dm-context/package.json +1 -1
  4. package/extensions/dm-context/skills/context-management/SKILL.md +173 -223
  5. package/extensions/dm-context/skills/context-management/references/development-and-troubleshooting.md +75 -0
  6. package/extensions/dm-context/skills/context-management/references/interleaved-async-work.md +143 -0
  7. package/extensions/dm-context/skills/context-management/references/planning-and-execution.md +81 -0
  8. package/extensions/dm-context/skills/context-management/references/repeated-items-and-batch-work.md +69 -0
  9. package/extensions/dm-context/skills/context-management/references/retry-branch-and-pivot.md +80 -0
  10. package/extensions/dm-context/skills/context-management/references/search-research-and-reading.md +103 -0
  11. package/extensions/dm-context/skills/context-management/references/task-switching-and-cleanup.md +73 -0
  12. package/extensions/dm-context/src/context.js +3 -2
  13. package/extensions/dm-context/src/index.js +168 -84
  14. package/extensions/dm-mcp-adapter/OAUTH.md +3 -0
  15. package/extensions/dm-mcp-adapter/THIRD_PARTY_NOTICES.md +4 -0
  16. package/extensions/dm-mcp-adapter/abort.js +35 -0
  17. package/extensions/dm-mcp-adapter/agent-dir.js +45 -0
  18. package/extensions/dm-mcp-adapter/agent-plugin-loader.js +349 -0
  19. package/extensions/dm-mcp-adapter/app-bridge.bundle.js +5843 -0
  20. package/extensions/dm-mcp-adapter/cli.js +354 -0
  21. package/extensions/dm-mcp-adapter/commands.js +619 -0
  22. package/extensions/dm-mcp-adapter/config.js +1060 -0
  23. package/extensions/dm-mcp-adapter/consent-manager.js +60 -0
  24. package/extensions/dm-mcp-adapter/direct-tools.js +544 -0
  25. package/extensions/dm-mcp-adapter/dist/agent-dir.js +45 -0
  26. package/extensions/dm-mcp-adapter/dist/agent-plugin-loader.js +349 -0
  27. package/extensions/dm-mcp-adapter/dist/config.js +1060 -0
  28. package/extensions/dm-mcp-adapter/dist/mcp-bearer-store.js +311 -0
  29. package/extensions/dm-mcp-adapter/dist/metadata-cache.js +285 -0
  30. package/extensions/dm-mcp-adapter/dist/package-mcp-loader.js +144 -0
  31. package/extensions/dm-mcp-adapter/dist/resource-tools.js +7 -0
  32. package/extensions/dm-mcp-adapter/dist/types.js +247 -0
  33. package/extensions/dm-mcp-adapter/dist/ui-app-bridge-helpers.js +36 -0
  34. package/extensions/dm-mcp-adapter/dist/ui-stream-types.js +58 -0
  35. package/extensions/dm-mcp-adapter/dist/ui-tool-visibility.js +26 -0
  36. package/extensions/dm-mcp-adapter/dist/utils.js +307 -0
  37. package/extensions/dm-mcp-adapter/elicitation-handler.js +306 -0
  38. package/extensions/dm-mcp-adapter/error-signal.js +16 -0
  39. package/extensions/dm-mcp-adapter/errors.js +225 -0
  40. package/extensions/dm-mcp-adapter/failure-backoff.js +19 -0
  41. package/extensions/dm-mcp-adapter/glimpse-ui.js +61 -0
  42. package/extensions/dm-mcp-adapter/host-html-template.js +464 -0
  43. package/extensions/dm-mcp-adapter/index.js +1079 -0
  44. package/extensions/dm-mcp-adapter/init.js +624 -0
  45. package/extensions/dm-mcp-adapter/json-schema-validator.js +53 -0
  46. package/extensions/dm-mcp-adapter/lifecycle.js +385 -0
  47. package/extensions/dm-mcp-adapter/logger.js +115 -0
  48. package/extensions/dm-mcp-adapter/mcp-auth-flow.js +824 -0
  49. package/extensions/dm-mcp-adapter/mcp-auth.js +732 -0
  50. package/extensions/dm-mcp-adapter/mcp-bearer-store.js +311 -0
  51. package/extensions/dm-mcp-adapter/mcp-callback-server.js +388 -0
  52. package/extensions/dm-mcp-adapter/mcp-code.js +291 -0
  53. package/extensions/dm-mcp-adapter/mcp-keyring-helper.cjs +88 -0
  54. package/extensions/dm-mcp-adapter/mcp-oauth-provider.js +490 -0
  55. package/extensions/dm-mcp-adapter/mcp-output-guard.js +386 -0
  56. package/extensions/dm-mcp-adapter/mcp-panel.js +951 -0
  57. package/extensions/dm-mcp-adapter/mcp-probe.js +151 -0
  58. package/extensions/dm-mcp-adapter/mcp-references.js +263 -0
  59. package/extensions/dm-mcp-adapter/mcp-script-worker.mjs +126 -0
  60. package/extensions/dm-mcp-adapter/mcp-setup-panel.js +578 -0
  61. package/extensions/dm-mcp-adapter/mcp-status.js +80 -0
  62. package/extensions/dm-mcp-adapter/mcp-trace.js +215 -0
  63. package/extensions/dm-mcp-adapter/metadata-cache.js +292 -0
  64. package/extensions/dm-mcp-adapter/namespace-tools.js +155 -0
  65. package/extensions/dm-mcp-adapter/npx-resolver.js +465 -0
  66. package/extensions/dm-mcp-adapter/oauth-handler.js +17 -0
  67. package/extensions/dm-mcp-adapter/oauth.js +17 -0
  68. package/extensions/dm-mcp-adapter/onboarding-state.js +62 -0
  69. package/extensions/dm-mcp-adapter/package-mcp-loader.js +144 -0
  70. package/extensions/dm-mcp-adapter/package.json +68 -0
  71. package/extensions/dm-mcp-adapter/panel-keys.js +26 -0
  72. package/extensions/dm-mcp-adapter/prompts.js +258 -0
  73. package/extensions/dm-mcp-adapter/proxy-modes.js +1336 -0
  74. package/extensions/dm-mcp-adapter/request-headers-command.js +293 -0
  75. package/extensions/dm-mcp-adapter/resource-tools.js +7 -0
  76. package/extensions/dm-mcp-adapter/runtime-owner.js +79 -0
  77. package/extensions/dm-mcp-adapter/sampling-handler.js +222 -0
  78. package/extensions/dm-mcp-adapter/sandbox-proxy-template.js +190 -0
  79. package/extensions/dm-mcp-adapter/search-ranking.js +198 -0
  80. package/extensions/dm-mcp-adapter/server-manager.js +1258 -0
  81. package/extensions/dm-mcp-adapter/session-recovery.js +82 -0
  82. package/extensions/dm-mcp-adapter/skills/mcp-scripting/SKILL.md +39 -0
  83. package/extensions/dm-mcp-adapter/state.js +0 -0
  84. package/extensions/dm-mcp-adapter/test-runner.mjs +18 -0
  85. package/extensions/dm-mcp-adapter/tool-approval.js +151 -0
  86. package/extensions/dm-mcp-adapter/tool-metadata.js +247 -0
  87. package/extensions/dm-mcp-adapter/tool-registrar.js +212 -0
  88. package/extensions/dm-mcp-adapter/tool-result-renderer.js +355 -0
  89. package/extensions/dm-mcp-adapter/ts-shape.js +157 -0
  90. package/extensions/dm-mcp-adapter/types.js +264 -0
  91. package/extensions/dm-mcp-adapter/ui-app-bridge-helpers.js +36 -0
  92. package/extensions/dm-mcp-adapter/ui-resource-handler.js +209 -0
  93. package/extensions/dm-mcp-adapter/ui-server.js +815 -0
  94. package/extensions/dm-mcp-adapter/ui-session.js +455 -0
  95. package/extensions/dm-mcp-adapter/ui-stream-types.js +58 -0
  96. package/extensions/dm-mcp-adapter/ui-tool-visibility.js +26 -0
  97. package/extensions/dm-mcp-adapter/unix-socket-transport.js +83 -0
  98. package/extensions/dm-mcp-adapter/utils.js +307 -0
  99. package/extensions/dm-skills-manager/THIRD_PARTY_NOTICES.md +27 -0
  100. package/extensions/dm-skills-manager/extensions/skills-manager/components.js +265 -0
  101. package/extensions/dm-skills-manager/extensions/skills-manager/constants.js +31 -0
  102. package/extensions/dm-skills-manager/extensions/skills-manager/creation-fallback.js +20 -0
  103. package/extensions/dm-skills-manager/extensions/skills-manager/creation.js +145 -0
  104. package/extensions/dm-skills-manager/extensions/skills-manager/dialog.js +738 -0
  105. package/extensions/dm-skills-manager/extensions/skills-manager/dm-ai-compat.js +15 -0
  106. package/extensions/dm-skills-manager/extensions/skills-manager/format.js +125 -0
  107. package/extensions/dm-skills-manager/extensions/skills-manager/glyphs.js +142 -0
  108. package/extensions/dm-skills-manager/extensions/skills-manager/layout.js +93 -0
  109. package/extensions/dm-skills-manager/extensions/skills-manager/paths.js +76 -0
  110. package/extensions/dm-skills-manager/extensions/skills-manager/registry.js +95 -0
  111. package/extensions/dm-skills-manager/extensions/skills-manager/settings.js +93 -0
  112. package/extensions/dm-skills-manager/extensions/skills-manager/startup.js +32 -0
  113. package/extensions/dm-skills-manager/extensions/skills-manager/toggle.js +54 -0
  114. package/extensions/dm-skills-manager/extensions/skills-manager/types.js +14 -0
  115. package/extensions/dm-skills-manager/extensions/skills-manager/ui.js +121 -0
  116. package/extensions/dm-skills-manager/extensions/skills-manager.js +111 -0
  117. package/extensions/dm-skills-manager/package.json +121 -0
  118. package/package.json +13 -1
  119. package/extensions/dm-localllm-provider/detect.test.js +0 -1051
  120. package/extensions/dm-localllm-provider/index.test.js +0 -149
  121. package/extensions/dm-localllm-provider/keychain.test.js +0 -61
@@ -0,0 +1,215 @@
1
+ import { appendFile, mkdir, writeFile } from "node:fs/promises";
2
+ import { Buffer } from "node:buffer";
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 = 1e4;
7
+ function boundedPositiveInteger(value, fallback) {
8
+ if (!Number.isFinite(value) || value === undefined || value <= 0)
9
+ return fallback;
10
+ return Math.floor(value);
11
+ }
12
+ export function redactTraceText(value, maxLength = 160) {
13
+ if (/\b(?:token|secret|password|passwd|api[_-]?key|authorization|cookie)\b/i.test(value)) {
14
+ return "[REDACTED]";
15
+ }
16
+ let redacted = value.replace(/\b[a-z][a-z\d+.-]*:\/\/[^\s"'<>]+/gi, "[REDACTED_URL]").replace(/\b(?:bearer|basic)\s+[A-Za-z0-9._~+/=-]+/gi, "[REDACTED_AUTH]").replace(/\b(?:token|secret|password|passwd|api[_-]?key|authorization|cookie)\s*[:=]\s*[^\s,;]+/gi, "$1=[REDACTED]");
17
+ if (redacted.length > maxLength)
18
+ redacted = `${redacted.slice(0, maxLength - 1)}…`;
19
+ return redacted;
20
+ }
21
+ function messageKind(message) {
22
+ if ("method" in message)
23
+ return "id" in message ? "request" : "notification";
24
+ return "response";
25
+ }
26
+ function traceId(value) {
27
+ if (value === null)
28
+ return null;
29
+ if (typeof value === "number" && Number.isFinite(value))
30
+ return value;
31
+ if (typeof value === "string")
32
+ return "[REDACTED_ID]";
33
+ return;
34
+ }
35
+ function messageBytes(message) {
36
+ try {
37
+ return Buffer.byteLength(JSON.stringify(message), "utf8");
38
+ } catch {
39
+ return;
40
+ }
41
+ }
42
+ export function createMcpTraceEvent(direction, server, transport, message, status, options) {
43
+ const kind = messageKind(message);
44
+ const bytes = messageBytes(message);
45
+ const event = {
46
+ version: MCP_TRACE_SCHEMA_VERSION,
47
+ timestamp: new Date().toISOString(),
48
+ direction,
49
+ server: redactTraceText(server, 120),
50
+ transport,
51
+ kind,
52
+ status,
53
+ ...bytes !== undefined ? { bytes } : {}
54
+ };
55
+ if ("method" in message)
56
+ event.method = redactTraceText(message.method, 120);
57
+ if ("id" in message)
58
+ event.id = traceId(message.id) ?? null;
59
+ const relatedRequestId = traceId(options?.relatedRequestId);
60
+ if (relatedRequestId !== undefined && relatedRequestId !== null)
61
+ event.relatedRequestId = relatedRequestId;
62
+ if ("error" in message && message.error && typeof message.error.code === "number") {
63
+ event.errorCode = message.error.code;
64
+ }
65
+ if (options?.durationMs !== undefined && Number.isFinite(options.durationMs)) {
66
+ event.durationMs = Math.max(0, Math.round(options.durationMs * 100) / 100);
67
+ }
68
+ return event;
69
+ }
70
+
71
+ export class McpTraceWriter {
72
+ options;
73
+ maxBytes;
74
+ maxEvents;
75
+ append;
76
+ resetFile;
77
+ makeDirectory;
78
+ bytesWritten = 0;
79
+ eventsWritten = 0;
80
+ queue = Promise.resolve();
81
+ disabled = false;
82
+ initializationFailed = false;
83
+ fileReady;
84
+ constructor(options) {
85
+ this.options = options;
86
+ this.maxBytes = boundedPositiveInteger(options.maxBytes, DEFAULT_MCP_TRACE_MAX_BYTES);
87
+ this.maxEvents = boundedPositiveInteger(options.maxEvents, DEFAULT_MCP_TRACE_MAX_EVENTS);
88
+ this.append = options.appendFile ?? (async (path, data, appendOptions) => {
89
+ await appendFile(path, data, appendOptions);
90
+ });
91
+ this.resetFile = options.writeFile ?? (async (path, data, writeOptions) => {
92
+ await writeFile(path, data, writeOptions);
93
+ });
94
+ this.makeDirectory = options.mkdir ?? (async (path) => {
95
+ await mkdir(path, { recursive: true });
96
+ return;
97
+ });
98
+ this.fileReady = this.makeDirectory(dirname(this.options.filePath), { recursive: true }).then(() => this.resetFile(this.options.filePath, "", { encoding: "utf8" })).catch(() => {
99
+ this.initializationFailed = true;
100
+ this.disabled = true;
101
+ });
102
+ }
103
+ get filePath() {
104
+ return this.options.filePath;
105
+ }
106
+ get isDisabled() {
107
+ return this.disabled;
108
+ }
109
+ get stats() {
110
+ return { bytes: this.bytesWritten, events: this.eventsWritten };
111
+ }
112
+ write(event) {
113
+ if (this.disabled || this.eventsWritten >= this.maxEvents)
114
+ return;
115
+ let line;
116
+ try {
117
+ line = `${JSON.stringify(event)}
118
+ `;
119
+ } catch {
120
+ this.disabled = true;
121
+ return;
122
+ }
123
+ const bytes = Buffer.byteLength(line, "utf8");
124
+ if (bytes > this.maxBytes - this.bytesWritten) {
125
+ this.disabled = true;
126
+ return;
127
+ }
128
+ this.bytesWritten += bytes;
129
+ this.eventsWritten += 1;
130
+ this.queue = this.queue.then(async () => {
131
+ await this.fileReady;
132
+ if (this.initializationFailed)
133
+ return;
134
+ await this.append(this.options.filePath, line, { encoding: "utf8" });
135
+ }).catch(() => {
136
+ this.disabled = true;
137
+ });
138
+ }
139
+ async flush() {
140
+ await this.fileReady;
141
+ await this.queue;
142
+ }
143
+ }
144
+ export function createMcpTraceWriter(sessionCwd, settings = {}, randomSuffix = Math.random().toString(36).slice(2, 10)) {
145
+ const timestamp = new Date().toISOString().replace(/[:.]/g, "-");
146
+ const configuredPath = settings.file;
147
+ const filePath = configuredPath ? isAbsolute(configuredPath) ? configuredPath : resolve(sessionCwd ?? process.cwd(), configuredPath) : resolve(sessionCwd ?? process.cwd(), ".dm", "mcp-traces", `mcp-${timestamp}-${randomSuffix}.jsonl`);
148
+ return new McpTraceWriter({
149
+ filePath,
150
+ ...settings.maxBytes !== undefined ? { maxBytes: settings.maxBytes } : {},
151
+ ...settings.maxEvents !== undefined ? { maxEvents: settings.maxEvents } : {}
152
+ });
153
+ }
154
+ export function isMcpTraceEnabled(definition, settings) {
155
+ return definition.trace ?? settings?.enabled === true;
156
+ }
157
+ export function wrapTransportWithMcpTrace(transport, server, transportKind, observer) {
158
+ let messageHandler = transport.onmessage;
159
+ let tracedMessageHandler;
160
+ const originalSend = transport.send;
161
+ const record = (event) => {
162
+ try {
163
+ observer.record(event);
164
+ } catch {}
165
+ };
166
+ try {
167
+ Object.defineProperty(transport, "onmessage", {
168
+ configurable: true,
169
+ enumerable: true,
170
+ get() {
171
+ return tracedMessageHandler;
172
+ },
173
+ set(handler) {
174
+ messageHandler = handler;
175
+ tracedMessageHandler = handler ? (message, extra) => {
176
+ record(createMcpTraceEvent("inbound", server, transportKind, message, "received"));
177
+ handler(message, extra);
178
+ } : undefined;
179
+ }
180
+ });
181
+ transport.onmessage = messageHandler;
182
+ } catch {}
183
+ transport.send = async (message, options) => {
184
+ const started = performance.now();
185
+ const messages = Array.isArray(message) ? message : [message];
186
+ try {
187
+ await originalSend.call(transport, message, options);
188
+ for (const item of messages) {
189
+ record(createMcpTraceEvent("outbound", server, transportKind, item, "sent", {
190
+ durationMs: performance.now() - started
191
+ }));
192
+ }
193
+ } catch (error) {
194
+ for (const item of messages) {
195
+ record(createMcpTraceEvent("outbound", server, transportKind, item, "error", {
196
+ durationMs: performance.now() - started
197
+ }));
198
+ }
199
+ throw error;
200
+ }
201
+ };
202
+ return transport;
203
+ }
204
+ export function traceTransportKind(definition, transport) {
205
+ if (definition.command)
206
+ return "stdio";
207
+ if (definition.socket)
208
+ return "unix-socket";
209
+ const constructorName = transport.constructor?.name.toLowerCase() ?? "";
210
+ if (constructorName.includes("sse"))
211
+ return "sse";
212
+ if (constructorName.includes("streamable"))
213
+ return "streamable-http";
214
+ return definition.url ? "streamable-http" : "unknown";
215
+ }
@@ -0,0 +1,292 @@
1
+ import { existsSync, readFileSync, writeFileSync, renameSync, mkdirSync } from "node:fs";
2
+ import { dirname } from "node:path";
3
+ import { getAgentPath } from "./agent-dir.js";
4
+ import { createHash } from "node:crypto";
5
+ import { getToolUiResourceUri } from "./ui-app-bridge-helpers.js";
6
+ import { createToolSelectorCandidateIndex, formatPromptCommandName, formatToolName, getToolNameCandidates, isServerDisabled, isToolAllowed, resolveToolPrefix } from "./types.js";
7
+ import { resourceNameToToolName } from "./resource-tools.js";
8
+ import {
9
+ extractToolUiStreamMode,
10
+ interpolateEnvRecord,
11
+ interpolateEnvVars,
12
+ resolveBearerToken,
13
+ resolveConfigPath,
14
+ resolveServerUrl
15
+ } from "./utils.js";
16
+ import { extractUiToolVisibility, isUiToolVisibleToModel } from "./ui-tool-visibility.js";
17
+ const CACHE_VERSION = 1;
18
+ const CACHE_MAX_AGE_MS = 7 * 24 * 60 * 60 * 1000;
19
+ export function getMetadataCachePath() {
20
+ return getAgentPath("mcp-cache.json");
21
+ }
22
+ export function loadMetadataCache() {
23
+ const cachePath = getMetadataCachePath();
24
+ if (!existsSync(cachePath))
25
+ return null;
26
+ try {
27
+ const raw = JSON.parse(readFileSync(cachePath, "utf-8"));
28
+ if (!raw || typeof raw !== "object")
29
+ return null;
30
+ if (raw.version !== CACHE_VERSION)
31
+ return null;
32
+ if (!raw.servers || typeof raw.servers !== "object")
33
+ return null;
34
+ return raw;
35
+ } catch {
36
+ return null;
37
+ }
38
+ }
39
+ export function saveMetadataCache(cache) {
40
+ const cachePath = getMetadataCachePath();
41
+ const dir = dirname(cachePath);
42
+ mkdirSync(dir, { recursive: true });
43
+ let merged = { version: CACHE_VERSION, servers: {} };
44
+ try {
45
+ if (existsSync(cachePath)) {
46
+ const existing = JSON.parse(readFileSync(cachePath, "utf-8"));
47
+ if (existing && existing.version === CACHE_VERSION && existing.servers) {
48
+ merged.servers = { ...existing.servers };
49
+ }
50
+ }
51
+ } catch {}
52
+ merged.version = CACHE_VERSION;
53
+ merged.servers = { ...merged.servers, ...cache.servers };
54
+ const tmpPath = `${cachePath}.${process.pid}.tmp`;
55
+ writeFileSync(tmpPath, JSON.stringify(merged), "utf-8");
56
+ renameSync(tmpPath, cachePath);
57
+ }
58
+ export function computeServerHash(definition, environment = process.env) {
59
+ const identity = {
60
+ command: definition.command,
61
+ args: definition.args,
62
+ socket: resolveConfigPath(definition.socket, environment),
63
+ env: interpolateEnvRecord(definition.env, environment),
64
+ cwd: resolveConfigPath(definition.cwd, environment),
65
+ url: resolveServerUrl(definition, environment),
66
+ headers: interpolateEnvRecord(definition.headers, environment),
67
+ requestHeadersCommand: definition.requestHeadersCommand ? {
68
+ command: interpolateEnvVars(definition.requestHeadersCommand.command, environment),
69
+ args: definition.requestHeadersCommand.args?.map((argument) => interpolateEnvVars(argument, environment)),
70
+ env: interpolateEnvRecord(definition.requestHeadersCommand.env, environment),
71
+ timeoutMs: definition.requestHeadersCommand.timeoutMs
72
+ } : undefined,
73
+ auth: definition.auth,
74
+ protocolVersion: definition.protocolVersion,
75
+ bearerToken: resolveBearerToken(definition, environment),
76
+ bearerTokenEnv: definition.bearerTokenEnv,
77
+ exposeResources: definition.exposeResources,
78
+ includeTools: definition.includeTools,
79
+ excludeTools: definition.excludeTools
80
+ };
81
+ const normalized = stableStringify(identity);
82
+ return createHash("sha256").update(normalized).digest("hex");
83
+ }
84
+ export function isServerCacheValid(entry, definition, maxAgeMs = CACHE_MAX_AGE_MS, environment = process.env) {
85
+ let configHash;
86
+ try {
87
+ configHash = computeServerHash(definition, environment);
88
+ } catch {
89
+ return false;
90
+ }
91
+ if (!entry || entry.configHash !== configHash)
92
+ return false;
93
+ if (!entry.cachedAt || typeof entry.cachedAt !== "number")
94
+ return false;
95
+ const declaredTtlMs = entry.ttlMs;
96
+ if (typeof declaredTtlMs === "number" && Number.isSafeInteger(declaredTtlMs) && declaredTtlMs >= 0) {
97
+ if (declaredTtlMs === 0)
98
+ return false;
99
+ const ageMs = Date.now() - entry.cachedAt;
100
+ const effectiveMaxAge = maxAgeMs > 0 ? Math.min(maxAgeMs, declaredTtlMs) : declaredTtlMs;
101
+ return ageMs < effectiveMaxAge;
102
+ }
103
+ if (maxAgeMs > 0 && Date.now() - entry.cachedAt > maxAgeMs)
104
+ return false;
105
+ return true;
106
+ }
107
+ export function parseDirectToolSelectors(selectors) {
108
+ const servers = new Set;
109
+ const tools = new Map;
110
+ for (let selector of selectors) {
111
+ selector = selector.replace(/\/+$/, "");
112
+ if (selector.includes("/")) {
113
+ const [server, tool] = selector.split("/", 2);
114
+ if (server && tool) {
115
+ const serverTools = tools.get(server) ?? new Set;
116
+ serverTools.add(tool);
117
+ tools.set(server, serverTools);
118
+ } else if (server) {
119
+ servers.add(server);
120
+ }
121
+ } else if (selector) {
122
+ servers.add(selector);
123
+ }
124
+ }
125
+ return { servers, tools };
126
+ }
127
+ export function getMissingConfiguredDirectToolServers(config, cache, envOverride) {
128
+ const missing = [];
129
+ const globalDirect = config.settings?.directTools;
130
+ const envSelection = envOverride ? parseDirectToolSelectors(envOverride) : null;
131
+ for (const [serverName, definition] of Object.entries(config.mcpServers)) {
132
+ if (isServerDisabled(definition))
133
+ continue;
134
+ const hasDirectTools = envSelection ? envSelection.servers.has(serverName) || envSelection.tools.has(serverName) : definition.directTools !== undefined ? !!definition.directTools : !!globalDirect;
135
+ if (!hasDirectTools)
136
+ continue;
137
+ const serverCache = cache?.servers?.[serverName];
138
+ if (!serverCache || !isServerCacheValid(serverCache, definition)) {
139
+ missing.push(serverName);
140
+ }
141
+ }
142
+ return missing;
143
+ }
144
+ export function reconstructToolMetadata(serverName, entry, prefix, definition, configuredServers, cache, sharedSelectorCandidateIndex) {
145
+ const metadata = [];
146
+ const seenNames = new Set;
147
+ const effectivePrefix = resolveToolPrefix(definition, prefix);
148
+ const hasToolFilters = Array.isArray(definition.includeTools) && definition.includeTools.length > 0 || Array.isArray(definition.excludeTools) && definition.excludeTools.length > 0;
149
+ const selectorCandidateIndex = hasToolFilters ? sharedSelectorCandidateIndex ?? (configuredServers && cache ? createCachedToolSelectorCandidateIndex(configuredServers, cache, prefix) : undefined) : undefined;
150
+ for (const tool of entry.tools ?? []) {
151
+ if (!tool?.name)
152
+ continue;
153
+ if (!isUiToolVisibleToModel(tool.uiVisibility)) {
154
+ continue;
155
+ }
156
+ if (!isToolAllowed(tool.name, serverName, effectivePrefix, definition.includeTools, definition.excludeTools, selectorCandidateIndex)) {
157
+ continue;
158
+ }
159
+ const name = formatToolName(tool.name, serverName, effectivePrefix);
160
+ if (seenNames.has(name)) {
161
+ continue;
162
+ }
163
+ seenNames.add(name);
164
+ metadata.push({
165
+ name,
166
+ originalName: tool.name,
167
+ description: tool.description ?? "",
168
+ ...tool.inputSchema !== undefined ? { inputSchema: tool.inputSchema } : {},
169
+ ...tool.uiResourceUri !== undefined ? { uiResourceUri: tool.uiResourceUri } : {},
170
+ ...tool.uiVisibility !== undefined ? { uiVisibility: tool.uiVisibility } : {},
171
+ ...tool.uiStreamMode !== undefined ? { uiStreamMode: tool.uiStreamMode } : {}
172
+ });
173
+ }
174
+ if (definition.exposeResources !== false) {
175
+ for (const resource of entry.resources ?? []) {
176
+ if (!resource?.name || !resource?.uri)
177
+ continue;
178
+ const baseName = `read_${resourceNameToToolName(resource.name)}`;
179
+ if (!isToolAllowed(baseName, serverName, effectivePrefix, definition.includeTools, definition.excludeTools, selectorCandidateIndex)) {
180
+ continue;
181
+ }
182
+ const name = formatToolName(baseName, serverName, effectivePrefix);
183
+ if (seenNames.has(name)) {
184
+ continue;
185
+ }
186
+ seenNames.add(name);
187
+ metadata.push({
188
+ name,
189
+ originalName: baseName,
190
+ description: resource.description ?? `Read resource: ${resource.uri}`,
191
+ resourceUri: resource.uri
192
+ });
193
+ }
194
+ }
195
+ return metadata;
196
+ }
197
+ export function createCachedToolSelectorCandidateIndex(configuredServers, cache, prefix) {
198
+ const candidates = new Set;
199
+ for (const [serverName, definition] of Object.entries(configuredServers)) {
200
+ const entry = cache.servers[serverName];
201
+ if (!entry || !isServerCacheValid(entry, definition) || isServerDisabled(definition))
202
+ continue;
203
+ const effectivePrefix = resolveToolPrefix(definition, prefix);
204
+ for (const tool of entry.tools ?? []) {
205
+ if (!isUiToolVisibleToModel(tool.uiVisibility))
206
+ continue;
207
+ for (const candidate of getToolNameCandidates(tool.name, serverName, effectivePrefix, false))
208
+ candidates.add(candidate);
209
+ }
210
+ if (definition.exposeResources !== false) {
211
+ for (const resource of entry.resources ?? []) {
212
+ const baseName = `read_${resourceNameToToolName(resource.name)}`;
213
+ for (const candidate of getToolNameCandidates(baseName, serverName, effectivePrefix, false))
214
+ candidates.add(candidate);
215
+ }
216
+ }
217
+ }
218
+ return createToolSelectorCandidateIndex(candidates);
219
+ }
220
+ export function serializeTools(tools) {
221
+ return tools.filter((t) => t?.name).map((t) => {
222
+ const uiResourceUri = tryGetToolUiResourceUri(t);
223
+ const uiVisibility = extractUiToolVisibility(t._meta);
224
+ const uiStreamMode = extractToolUiStreamMode(t._meta);
225
+ return {
226
+ name: t.name,
227
+ ...t.description !== undefined ? { description: t.description } : {},
228
+ ...t.inputSchema !== undefined ? { inputSchema: t.inputSchema } : {},
229
+ ...uiResourceUri !== undefined ? { uiResourceUri } : {},
230
+ ...uiVisibility !== undefined ? { uiVisibility } : {},
231
+ ...uiStreamMode !== undefined ? { uiStreamMode } : {}
232
+ };
233
+ });
234
+ }
235
+ export function serializeResources(resources) {
236
+ return resources.filter((r) => r?.name && r?.uri).map((r) => ({
237
+ uri: r.uri,
238
+ name: r.name,
239
+ ...r.description !== undefined ? { description: r.description } : {}
240
+ }));
241
+ }
242
+ export function serializePrompts(prompts) {
243
+ return (prompts ?? []).filter((prompt) => prompt?.name).map((prompt) => ({
244
+ name: prompt.name,
245
+ ...prompt.title !== undefined ? { title: prompt.title } : {},
246
+ ...prompt.description !== undefined ? { description: prompt.description } : {},
247
+ ...Array.isArray(prompt.arguments) ? {
248
+ arguments: prompt.arguments.filter((argument) => argument?.name).map((argument) => ({
249
+ name: argument.name,
250
+ ...argument.description !== undefined ? { description: argument.description } : {},
251
+ ...argument.required !== undefined ? { required: argument.required } : {}
252
+ }))
253
+ } : {}
254
+ }));
255
+ }
256
+ export function reconstructPromptMetadata(serverName, prompts, prefix, definition) {
257
+ const effectivePrefix = resolveToolPrefix(definition, prefix);
258
+ return (prompts ?? []).filter((prompt) => prompt?.name).map((prompt) => {
259
+ const args = Array.isArray(prompt.arguments) ? prompt.arguments.filter((argument) => argument?.name).map((argument) => ({
260
+ name: argument.name,
261
+ ...argument.description !== undefined ? { description: argument.description } : {},
262
+ ...argument.required !== undefined ? { required: argument.required } : {}
263
+ })) : [];
264
+ return {
265
+ serverName,
266
+ originalName: prompt.name,
267
+ commandName: formatPromptCommandName(prompt.name, serverName, effectivePrefix),
268
+ ...prompt.title !== undefined ? { title: prompt.title } : {},
269
+ description: prompt.description ?? "",
270
+ arguments: args
271
+ };
272
+ });
273
+ }
274
+ function stableStringify(value) {
275
+ if (value === null || value === undefined || typeof value !== "object") {
276
+ const serialized = JSON.stringify(value);
277
+ return serialized === undefined ? "undefined" : serialized;
278
+ }
279
+ if (Array.isArray(value)) {
280
+ return `[${value.map((v) => stableStringify(v)).join(",")}]`;
281
+ }
282
+ const obj = value;
283
+ const keys = Object.keys(obj).sort();
284
+ return `{${keys.map((k) => `${JSON.stringify(k)}:${stableStringify(obj[k])}`).join(",")}}`;
285
+ }
286
+ function tryGetToolUiResourceUri(tool) {
287
+ try {
288
+ return getToolUiResourceUri({ _meta: tool._meta });
289
+ } catch {
290
+ return;
291
+ }
292
+ }
@@ -0,0 +1,155 @@
1
+ import { Type } from "typebox";
2
+ import { isServerDisabled } from "./types.js";
3
+ import { isServerCacheValid } from "./metadata-cache.js";
4
+ import { executeCall } from "./proxy-modes.js";
5
+ import { createMcpProxyToolCallRenderer, createMcpToolResultRenderer, resolveMcpToolRenderOptions } from "./tool-result-renderer.js";
6
+ export { namespaceProxyName } from "./mcp-references.js";
7
+ import { hasCallableCachedTargets, isMcpServerDirectlyRegistered, namespaceProxyName } from "./mcp-references.js";
8
+ function namespaceProxyCandidate(config, cache, envOverride, existingDirectNames, serverName) {
9
+ const definition = config.mcpServers[serverName];
10
+ if (!definition || isServerDisabled(definition))
11
+ return null;
12
+ if (isMcpServerDirectlyRegistered(definition, config.settings, serverName, envOverride))
13
+ return null;
14
+ const entry = cache.servers?.[serverName];
15
+ if (!entry || !isServerCacheValid(entry, definition) || !hasCallableCachedTargets(entry, definition))
16
+ return null;
17
+ const toolName = namespaceProxyName(serverName);
18
+ if (existingDirectNames.has(toolName))
19
+ return null;
20
+ return {
21
+ serverName,
22
+ toolName,
23
+ description: `Namespace-proxy for MCP server "${serverName}". ` + `Forwards \`{tool, args}\` through the adapter's executeCall, so it inherits ` + `the same auth / lifecycle / output-guard rules as the \`mcp\` proxy.`
24
+ };
25
+ }
26
+ function filterCollidingNamespaceProxyTools(candidates) {
27
+ const names = new Map;
28
+ for (const spec of candidates) {
29
+ const colliding = names.get(spec.toolName) ?? [];
30
+ colliding.push(spec);
31
+ names.set(spec.toolName, colliding);
32
+ }
33
+ return candidates.filter((spec) => {
34
+ const colliding = names.get(spec.toolName);
35
+ if (colliding.length === 1)
36
+ return true;
37
+ if (colliding[0] === spec) {
38
+ console.warn(`MCP: skipping namespace proxy "${spec.toolName}" because servers ${colliding.map(({ serverName }) => `"${serverName}"`).sort().join(", ")} normalize to the same name`);
39
+ }
40
+ return false;
41
+ });
42
+ }
43
+ function resolveNamespaceProxyTools(config, cache, envOverride, existingDirectNames, unavailableServers) {
44
+ if (!config || !cache)
45
+ return [];
46
+ return filterCollidingNamespaceProxyTools(Object.keys(config.mcpServers).map((serverName) => namespaceProxyCandidate(config, cache, envOverride, existingDirectNames, serverName)).filter((spec) => spec !== null)).filter((spec) => !unavailableServers.has(spec.serverName));
47
+ }
48
+ function namespaceExecute(getState, getInitPromise, serverName, getPiTools) {
49
+ return async (_toolCallId, params, signal) => {
50
+ if (typeof params.tool !== "string" || params.tool.length === 0) {
51
+ return {
52
+ content: [{ type: "text", text: `mcp__${serverName} requires a \`tool\` parameter naming the underlying MCP tool.` }],
53
+ details: { error: "missing_tool", server: serverName }
54
+ };
55
+ }
56
+ let state = getState();
57
+ if (!state) {
58
+ const initPromise = getInitPromise();
59
+ if (initPromise) {
60
+ try {
61
+ state = await initPromise;
62
+ } catch (error) {
63
+ const message = error instanceof Error ? error.message : String(error);
64
+ return {
65
+ content: [{ type: "text", text: `MCP initialization failed for ${serverName}: ${message}` }],
66
+ details: { error: "init_failed", server: serverName, message }
67
+ };
68
+ }
69
+ }
70
+ }
71
+ if (!state) {
72
+ return {
73
+ content: [{ type: "text", text: `MCP not initialized for ${serverName}.` }],
74
+ details: { error: "not_initialized", server: serverName }
75
+ };
76
+ }
77
+ return executeCall(state, params.tool, params.args ?? {}, serverName, getPiTools, signal, "proxy");
78
+ };
79
+ }
80
+ const parameters = Type.Object({
81
+ tool: Type.String({ description: "Underlying MCP tool name to call on this server." }),
82
+ args: Type.Optional(Type.Object({}, {
83
+ additionalProperties: true,
84
+ description: "Arguments for the underlying tool. The exact shape depends on the tool being called; use mcp({ describe: 'server/tool' }) to inspect."
85
+ }))
86
+ });
87
+ function createNamespaceRenderCall(renderOptions, serverName) {
88
+ const renderCall = createMcpProxyToolCallRenderer(renderOptions);
89
+ return (args, theme, context) => renderCall({
90
+ ...args.tool !== undefined ? { tool: args.tool } : {},
91
+ ...args.args !== undefined ? { args: args.args } : {},
92
+ server: serverName
93
+ }, theme, context);
94
+ }
95
+ function registerNamespaceProxyTool(input, spec, renderOptions, renderShell, renderResult) {
96
+ input.pi.registerTool({
97
+ name: spec.toolName,
98
+ label: `MCP: ${spec.serverName}`,
99
+ description: spec.description,
100
+ promptSnippet: `MCP namespace proxy for ${spec.serverName}`,
101
+ parameters,
102
+ renderShell,
103
+ renderCall: createNamespaceRenderCall(renderOptions, spec.serverName),
104
+ renderResult,
105
+ execute: namespaceExecute(input.getState, input.getInitPromise, spec.serverName, input.getPiTools)
106
+ });
107
+ }
108
+ function getActiveToolsForStaleCleanup(pi, staleNames) {
109
+ if (staleNames.length === 0)
110
+ return;
111
+ try {
112
+ return pi.getActiveTools?.();
113
+ } catch (error) {
114
+ if (error instanceof Error && error.message.includes("Action methods cannot be called during extension loading"))
115
+ return;
116
+ throw error;
117
+ }
118
+ }
119
+ function deactivateStaleNamespaceTools(input, nextNames) {
120
+ const activeDirectNames = input.activeDirectNames ?? new Set;
121
+ const staleNames = [...input.existingNamespaceNames].filter((name) => !nextNames.has(name) && !activeDirectNames.has(name));
122
+ const deactivated = [];
123
+ const unregisterTool = input.pi.unregisterTool;
124
+ for (const stale of staleNames) {
125
+ if (unregisterTool?.(stale))
126
+ deactivated.push(stale);
127
+ }
128
+ const activeTools = getActiveToolsForStaleCleanup(input.pi, staleNames);
129
+ if (!activeTools)
130
+ return deactivated;
131
+ const stale = new Set(staleNames);
132
+ const nextActiveTools = activeTools.filter((name) => !stale.has(name));
133
+ if (nextActiveTools.length === activeTools.length)
134
+ return deactivated;
135
+ input.pi.setActiveTools(nextActiveTools);
136
+ for (const name of staleNames) {
137
+ if (!deactivated.includes(name))
138
+ deactivated.push(name);
139
+ }
140
+ return deactivated;
141
+ }
142
+ export function syncNamespaceProxyTools(input) {
143
+ const specs = resolveNamespaceProxyTools(input.config, input.cache, input.envOverride, input.existingDirectNames, input.unavailableServers ?? new Set);
144
+ const nextNames = new Set(specs.map((s) => s.toolName));
145
+ const result = { specs, added: [], updated: [], deactivated: [] };
146
+ const renderOptions = input.renderOptions ?? resolveMcpToolRenderOptions();
147
+ const renderShell = input.renderShell ?? (renderOptions.resultRendering === "compact" ? "self" : "default");
148
+ const renderResult = input.renderResult ?? createMcpToolResultRenderer(renderOptions);
149
+ for (const spec of specs) {
150
+ registerNamespaceProxyTool(input, spec, renderOptions, renderShell, renderResult);
151
+ (input.existingNamespaceNames.has(spec.toolName) ? result.updated : result.added).push(spec.toolName);
152
+ }
153
+ result.deactivated.push(...deactivateStaleNamespaceTools(input, nextNames));
154
+ return result;
155
+ }