@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,1079 @@
1
+ import { Type } from "typebox";
2
+ import { showStatus, showTools, showPrompts, reconnectServer, reconnectServers, authenticateServer, logoutServer, manageBearerToken, openMcpAuthPanel, openMcpPanel, openMcpSetup } from "./commands.js";
3
+ import { cloneMcpConfig, loadMcpConfig, writeProjectServerDisabledOverride } from "./config.js";
4
+ import { buildProxyDescription, createDirectToolExecutor, getMissingConfiguredDirectToolServers, prepareDirectToolArguments, resolveDirectTools } from "./direct-tools.js";
5
+ import { flushMetadataCache, initializeMcp, updateStatusBar } from "./init.js";
6
+ import { isServerInActiveFailureBackoff } from "./failure-backoff.js";
7
+ import { loadMetadataCache, parseDirectToolSelectors } from "./metadata-cache.js";
8
+ import { createPromptCommand, resolveCachedPrompts } from "./prompts.js";
9
+ import { logger } from "./logger.js";
10
+ import { executeAuthComplete, executeAuthStart, executeCall, executeConnect, executeDescribe, executeInstructions, executeList, executeSearch, executeStatus, executeUiMessages } from "./proxy-modes.js";
11
+ import { formatTerminalError, getConfigPathFromArgv, normalizeDirectToolInputSchema, truncateAtWord } from "./utils.js";
12
+ import { createOAuthRuntime, shutdownOAuth } from "./mcp-auth-flow.js";
13
+ import { createMcpDirectToolCallRenderer, createMcpProxyToolCallRenderer, createMcpScriptToolCallRenderer, createMcpToolResultRenderer, resolveMcpToolRenderOptions } from "./tool-result-renderer.js";
14
+ import { toolErrorOverride } from "./error-signal.js";
15
+ import { createMcpRuntimeOwner, createOwnedUi, isAbortError } from "./runtime-owner.js";
16
+ import { publishMcpStatusShutdown } from "./mcp-status.js";
17
+ import { runMcpScript } from "./mcp-code.js";
18
+ import { cleanupMaterializedBinaryResources } from "./tool-registrar.js";
19
+ import { syncNamespaceProxyTools } from "./namespace-tools.js";
20
+ export {
21
+ namespaceProxyName,
22
+ parseMcpReference,
23
+ resolveMcpToolReferences
24
+ } from "./mcp-references.js";
25
+ export {
26
+ MCP_STATUS_EVENT,
27
+ MCP_STATUS_SNAPSHOT_VERSION,
28
+ MCP_TOOL_APPROVAL_REQUEST_EVENT
29
+ } from "./types.js";
30
+ const INIT_WAIT_TIMEOUT_MS = 30000;
31
+ const INIT_FAILURE_MESSAGE_MAX_CHARS = 1000;
32
+ const INIT_WAIT_TIMED_OUT = Symbol("init-wait-timed-out");
33
+ export const MCP_RUNTIME_REGISTER_EVENT = "dm-mcp-adapter:runtime-register:v1";
34
+ export const MCP_RUNTIME_REGISTER_VERSION = 1;
35
+ export const MCP_RUNTIME_SNAPSHOT_EVENT = "dm-mcp-adapter:runtime-snapshot:v1";
36
+ export const MCP_RUNTIME_SNAPSHOT_VERSION = 1;
37
+ const runtimeRegistrars = new WeakMap;
38
+ const runtimeSnapshotters = new WeakMap;
39
+ async function awaitWithTimeout(promise, timeoutMs) {
40
+ let timer;
41
+ try {
42
+ return await Promise.race([
43
+ promise,
44
+ new Promise((resolve) => {
45
+ timer = setTimeout(() => resolve(INIT_WAIT_TIMED_OUT), timeoutMs);
46
+ timer.unref?.();
47
+ })
48
+ ]);
49
+ } finally {
50
+ clearTimeout(timer);
51
+ }
52
+ }
53
+ function optionalNumber(options) {
54
+ const number = Type.Number;
55
+ return typeof number === "function" ? Type.Optional(number(options)) : { type: "number", ...options };
56
+ }
57
+ function parseEnvDirectToolOverride(raw) {
58
+ return raw?.split(",").map((s) => s.trim()).filter(Boolean);
59
+ }
60
+ function resolveNamespaceEnvOverride(raw, selectors) {
61
+ if (raw === "__none__")
62
+ return { servers: new Set, tools: new Map };
63
+ return selectors ? parseDirectToolSelectors(selectors) : null;
64
+ }
65
+ function installMcpAdapter(pi, options) {
66
+ const sessionConfig = options.config !== undefined ? cloneMcpConfig(options.config) : undefined;
67
+ const programmaticConfig = sessionConfig !== undefined;
68
+ let state = null;
69
+ let initPromise = null;
70
+ let currentOwner = null;
71
+ let currentOAuthRuntime = null;
72
+ let lifecycleGeneration = 0;
73
+ let retainedInitFailure = null;
74
+ function retainInitFailure(error) {
75
+ const message = truncateAtWord(formatTerminalError(error), INIT_FAILURE_MESSAGE_MAX_CHARS);
76
+ retainedInitFailure = message;
77
+ return message;
78
+ }
79
+ function clearRetainedInitFailure() {
80
+ retainedInitFailure = null;
81
+ }
82
+ function buildInitRetryInstruction(prefix, failure = retainedInitFailure) {
83
+ const retry = "Fix the MCP server configuration or startup failure, then call mcp(...) again to retry initialization.";
84
+ return failure ? `${prefix}: ${failure}. ${retry}` : `${prefix}. ${retry}`;
85
+ }
86
+ function isOwnerAbortError(error, owner) {
87
+ if (!owner.signal.aborted)
88
+ return isAbortError(error);
89
+ const reason = owner.signal.reason;
90
+ if (reason instanceof Error && reason.message === "MCP initialization failed" && error !== reason) {
91
+ return isAbortError(error);
92
+ }
93
+ return true;
94
+ }
95
+ function startGatewayRetryInitialization(ctx) {
96
+ const generation = ++lifecycleGeneration;
97
+ const owner = createMcpRuntimeOwner();
98
+ const oauthRuntime = createOAuthRuntime(owner.signal);
99
+ currentOwner = owner;
100
+ currentOAuthRuntime = oauthRuntime;
101
+ state = null;
102
+ startInitialization(ctx, owner, oauthRuntime, generation, "stale_gateway_retry_initialization");
103
+ }
104
+ async function shutdownState(currentState, reason) {
105
+ if (!currentState) {
106
+ publishMcpStatusShutdown(pi.events);
107
+ return;
108
+ }
109
+ publishMcpStatusShutdown(currentState.statusEvents);
110
+ if (currentState.uiServer) {
111
+ currentState.uiServer.close(reason);
112
+ currentState.uiServer = null;
113
+ }
114
+ let flushError;
115
+ try {
116
+ flushMetadataCache(currentState);
117
+ } catch (error) {
118
+ flushError = error;
119
+ }
120
+ try {
121
+ if (currentState.owner) {
122
+ await currentState.owner.stop(reason);
123
+ } else {
124
+ await currentState.lifecycle.gracefulShutdown();
125
+ }
126
+ } catch (error) {
127
+ if (flushError) {
128
+ console.error(`MCP: graceful shutdown failed after metadata flush error: ${formatTerminalError(error)}`);
129
+ } else {
130
+ throw error;
131
+ }
132
+ }
133
+ if (flushError) {
134
+ throw flushError;
135
+ }
136
+ }
137
+ const earlyConfigPath = programmaticConfig ? undefined : options.configPath ?? getConfigPathFromArgv();
138
+ const earlyConfig = programmaticConfig ? cloneMcpConfig(sessionConfig) : loadMcpConfig(earlyConfigPath);
139
+ const earlyCache = loadMetadataCache();
140
+ const envRaw = process.env.MCP_DIRECT_TOOLS;
141
+ const envDirectToolOverride = parseEnvDirectToolOverride(envRaw);
142
+ const namespaceEnvOverride = resolveNamespaceEnvOverride(envRaw, envDirectToolOverride);
143
+ const registeredDirectTools = new Map;
144
+ const registeredNamespaceProxyTools = new Set;
145
+ const fallbackDeactivatedTools = new Set;
146
+ const toolRenderOptions = resolveMcpToolRenderOptions(earlyConfig.settings);
147
+ const toolRenderShell = toolRenderOptions.resultRendering === "compact" ? "self" : "default";
148
+ const renderMcpToolResult = createMcpToolResultRenderer(toolRenderOptions);
149
+ let proxyToolRegistered = false;
150
+ let proxyToolDescription = null;
151
+ let directToolsFrozen = false;
152
+ const runtimeServers = new Map;
153
+ function attachRuntimeServerLifecycle(targetState, name, definition) {
154
+ const lifecycleMode = definition.lifecycle ?? "lazy";
155
+ const persistsAfterFirstSpawn = lifecycleMode === "eager" || lifecycleMode === "lazy-keep-alive";
156
+ const idleOverride = definition.idleTimeout ?? (persistsAfterFirstSpawn ? 0 : undefined);
157
+ targetState.lifecycle.registerServer(name, definition, idleOverride !== undefined ? { idleTimeout: idleOverride } : undefined);
158
+ if (lifecycleMode === "keep-alive")
159
+ targetState.lifecycle.markKeepAlive(name, definition);
160
+ }
161
+ const toToolParameters = (schema) => typeof Type.Unsafe === "function" ? Type.Unsafe(schema) : schema;
162
+ function directToolFingerprint(spec) {
163
+ return JSON.stringify({
164
+ serverName: spec.serverName,
165
+ originalName: spec.originalName,
166
+ prefixedName: spec.prefixedName,
167
+ description: spec.description,
168
+ inputSchema: spec.inputSchema,
169
+ resourceUri: spec.resourceUri,
170
+ uiResourceUri: spec.uiResourceUri,
171
+ uiStreamMode: spec.uiStreamMode
172
+ });
173
+ }
174
+ function registerDirectTool(spec, config) {
175
+ pi.registerTool({
176
+ name: spec.prefixedName,
177
+ label: `MCP: ${spec.originalName}`,
178
+ description: spec.description || "(no description)",
179
+ promptSnippet: truncateAtWord(spec.description, 100) || `MCP tool from ${spec.serverName}`,
180
+ parameters: toToolParameters(normalizeDirectToolInputSchema(spec.inputSchema)),
181
+ ...config.settings?.strictDirectToolArguments === true ? { prepareArguments: (args) => prepareDirectToolArguments(spec.inputSchema, args) } : {},
182
+ execute: createDirectToolExecutor(() => state, () => initPromise, spec),
183
+ renderShell: toolRenderShell,
184
+ renderCall: createMcpDirectToolCallRenderer(spec.prefixedName, toolRenderOptions),
185
+ renderResult: renderMcpToolResult
186
+ });
187
+ }
188
+ function activeFailureServers() {
189
+ const currentState = state;
190
+ if (!currentState)
191
+ return new Set;
192
+ return new Set(Object.keys(currentState.config.mcpServers).filter((serverName) => isServerInActiveFailureBackoff(currentState, serverName)));
193
+ }
194
+ function resolveCurrentDirectTools(config, cache, reservedNames) {
195
+ if (envRaw === "__none__")
196
+ return [];
197
+ const prefix = config.settings?.toolPrefix ?? "server";
198
+ return resolveDirectTools(config, cache, prefix, envDirectToolOverride, activeFailureServers(), reservedNames);
199
+ }
200
+ function getActiveToolsIfReady() {
201
+ try {
202
+ return pi.getActiveTools?.();
203
+ } catch (error) {
204
+ if (error instanceof Error && error.message.includes("Action methods cannot be called during extension loading"))
205
+ return;
206
+ throw error;
207
+ }
208
+ }
209
+ function deactivateTools(toolNames) {
210
+ if (toolNames.length === 0)
211
+ return [];
212
+ const unregisterTool = pi.unregisterTool;
213
+ const unregistered = toolNames.filter((toolName) => unregisterTool?.(toolName) === true);
214
+ const fallbackNames = toolNames.filter((toolName) => !unregistered.includes(toolName));
215
+ const remove = new Set(toolNames);
216
+ const activeTools = getActiveToolsIfReady();
217
+ if (!activeTools || activeTools.length === 0) {
218
+ for (const toolName of fallbackNames)
219
+ fallbackDeactivatedTools.add(toolName);
220
+ return unregistered;
221
+ }
222
+ const nextActiveTools = activeTools.filter((name) => !remove.has(name));
223
+ if (nextActiveTools.length !== activeTools.length) {
224
+ for (const toolName of fallbackNames)
225
+ fallbackDeactivatedTools.add(toolName);
226
+ pi.setActiveTools(nextActiveTools);
227
+ }
228
+ return unregistered;
229
+ }
230
+ function syncDirectTools(config, cache) {
231
+ const reservedDirectNames = new Set;
232
+ const specs = resolveCurrentDirectTools(config, cache, reservedDirectNames);
233
+ const nextNames = new Set(specs.map((spec) => spec.prefixedName));
234
+ const added = [];
235
+ const updated = [];
236
+ const deactivated = [];
237
+ for (const spec of specs) {
238
+ const fingerprint = directToolFingerprint(spec);
239
+ const previous = registeredDirectTools.get(spec.prefixedName);
240
+ if (previous !== fingerprint) {
241
+ registerDirectTool(spec, config);
242
+ registeredDirectTools.set(spec.prefixedName, fingerprint);
243
+ if (fallbackDeactivatedTools.delete(spec.prefixedName)) {
244
+ const activeTools = getActiveToolsIfReady();
245
+ if (activeTools && !activeTools.includes(spec.prefixedName)) {
246
+ pi.setActiveTools([...activeTools, spec.prefixedName]);
247
+ }
248
+ }
249
+ (previous ? updated : added).push(spec.prefixedName);
250
+ }
251
+ }
252
+ for (const toolName of [...registeredDirectTools.keys()]) {
253
+ if (nextNames.has(toolName))
254
+ continue;
255
+ registeredDirectTools.delete(toolName);
256
+ deactivated.push(toolName);
257
+ }
258
+ deactivateTools(deactivated);
259
+ return { specs, reservedDirectNames, activeDirectNames: nextNames, added, updated, deactivated };
260
+ }
261
+ function applyDirectToolConfigChanges(changes) {
262
+ if (!state)
263
+ return;
264
+ for (const [serverName, value] of changes) {
265
+ const definition = state.config.mcpServers[serverName];
266
+ if (!definition)
267
+ continue;
268
+ state.config.mcpServers[serverName] = { ...definition, directTools: value };
269
+ }
270
+ }
271
+ function syncToolSurface(ctx) {
272
+ const config = state?.config ?? earlyConfig;
273
+ const cache = loadMetadataCache();
274
+ const result = syncDirectTools(config, cache);
275
+ syncProxyTool(config, cache, result.specs);
276
+ syncNamespaceTools(config, cache, result.reservedDirectNames, result.activeDirectNames);
277
+ const changed = result.added.length + result.updated.length + result.deactivated.length;
278
+ if (changed > 0 && ctx?.hasUI) {
279
+ ctx.ui.notify(`MCP: direct tools refreshed (+${result.added.length}, ~${result.updated.length}, -${result.deactivated.length})`, "info");
280
+ }
281
+ }
282
+ function syncNamespaceTools(config, cache, reservedDirectNames = new Set(registeredDirectTools.keys()), activeDirectNames = new Set(registeredDirectTools.keys())) {
283
+ const result = syncNamespaceProxyTools({
284
+ config,
285
+ cache,
286
+ envOverride: namespaceEnvOverride,
287
+ existingDirectNames: reservedDirectNames,
288
+ activeDirectNames,
289
+ existingNamespaceNames: registeredNamespaceProxyTools,
290
+ unavailableServers: activeFailureServers(),
291
+ pi,
292
+ getState: () => state,
293
+ getInitPromise: () => initPromise,
294
+ getPiTools: () => pi.getAllTools(),
295
+ renderOptions: toolRenderOptions,
296
+ renderShell: toolRenderShell,
297
+ renderResult: renderMcpToolResult
298
+ });
299
+ for (const name of result.added)
300
+ registeredNamespaceProxyTools.add(name);
301
+ for (const name of result.deactivated)
302
+ registeredNamespaceProxyTools.delete(name);
303
+ }
304
+ const registeredPromptCommands = new Set;
305
+ function registerPromptCommands(specs) {
306
+ for (const spec of specs) {
307
+ if (registeredPromptCommands.has(spec.commandName)) {
308
+ logger.debug(`MCP: prompt "${spec.originalName}" on ${spec.serverName} skipped; /${spec.commandName} is already registered`);
309
+ continue;
310
+ }
311
+ registeredPromptCommands.add(spec.commandName);
312
+ pi.registerCommand(spec.commandName, createPromptCommand(pi, () => state, spec));
313
+ }
314
+ }
315
+ function syncPromptCommands() {
316
+ registerPromptCommands([...state?.promptMetadata?.values() ?? []].flat());
317
+ }
318
+ registerPromptCommands(resolveCachedPrompts(earlyConfig));
319
+ const registerRuntimeServer = (name, definition) => {
320
+ if (typeof name !== "string" || name.trim() === "") {
321
+ throw new Error("MCP server name must be a non-empty string");
322
+ }
323
+ if (typeof definition !== "object" || definition === null || Array.isArray(definition)) {
324
+ throw new Error(`MCP server definition for "${name}" must be an object`);
325
+ }
326
+ const effective = state?.config ?? earlyConfig;
327
+ if (runtimeServers.has(name) || Object.hasOwn(effective.mcpServers, name)) {
328
+ throw new Error(`MCP server "${name}" is already registered`);
329
+ }
330
+ const snapshotDefinition = structuredClone(definition);
331
+ const entry = { ...structuredClone(snapshotDefinition), directTools: false };
332
+ runtimeServers.set(name, { definition: snapshotDefinition, entry });
333
+ const registeredState = state;
334
+ if (registeredState) {
335
+ registeredState.config.mcpServers[name] = entry;
336
+ attachRuntimeServerLifecycle(registeredState, name, entry);
337
+ syncToolSurface();
338
+ updateStatusBar(registeredState);
339
+ }
340
+ let disposed = false;
341
+ return {
342
+ dispose: async () => {
343
+ if (disposed)
344
+ return;
345
+ disposed = true;
346
+ runtimeServers.delete(name);
347
+ const currentState = state;
348
+ if (!currentState || currentState.config.mcpServers[name] !== entry)
349
+ return;
350
+ delete currentState.config.mcpServers[name];
351
+ currentState.lifecycle.unregisterServer(name);
352
+ await currentState.manager.close(name);
353
+ syncToolSurface();
354
+ updateStatusBar(currentState);
355
+ }
356
+ };
357
+ };
358
+ const getRuntimeServerSnapshot = (name) => {
359
+ if (typeof name !== "string" || name.trim() === "") {
360
+ throw new Error("MCP runtime server name must be a non-empty string");
361
+ }
362
+ const runtimeServer = runtimeServers.get(name);
363
+ if (!runtimeServer) {
364
+ throw new Error(`MCP runtime server "${name}" is not registered or has been disposed`);
365
+ }
366
+ const activeState = state;
367
+ if (!activeState) {
368
+ throw new Error(`MCP runtime server "${name}" is unavailable because the adapter has no active state`);
369
+ }
370
+ const activeEntry = activeState.config.mcpServers[name];
371
+ if (Object.hasOwn(activeState.config.mcpServers, name) && activeEntry !== runtimeServer.entry) {
372
+ throw new Error(`MCP runtime server "${name}" is shadowed by a configured server`);
373
+ }
374
+ if (activeEntry !== runtimeServer.entry) {
375
+ throw new Error(`MCP runtime server "${name}" is unavailable in the active adapter state`);
376
+ }
377
+ return {
378
+ name,
379
+ definition: structuredClone(runtimeServer.definition),
380
+ runtime: true,
381
+ persisted: false
382
+ };
383
+ };
384
+ runtimeRegistrars.set(pi, registerRuntimeServer);
385
+ runtimeSnapshotters.set(pi, getRuntimeServerSnapshot);
386
+ pi.events.on(MCP_RUNTIME_REGISTER_EVENT, (rawRequest) => {
387
+ if (typeof rawRequest !== "object" || rawRequest === null || Array.isArray(rawRequest))
388
+ return;
389
+ const request = rawRequest;
390
+ if (request.result !== undefined)
391
+ return;
392
+ if (request.version !== MCP_RUNTIME_REGISTER_VERSION) {
393
+ request.result = { ok: false, error: new Error(`Unsupported MCP runtime registration version: ${String(request.version)}`) };
394
+ return;
395
+ }
396
+ try {
397
+ request.result = { ok: true, registration: registerRuntimeServer(request.name, request.definition) };
398
+ } catch (error) {
399
+ request.result = { ok: false, error: error instanceof Error ? error : new Error(String(error)) };
400
+ }
401
+ });
402
+ pi.events.on(MCP_RUNTIME_SNAPSHOT_EVENT, (rawRequest) => {
403
+ if (typeof rawRequest !== "object" || rawRequest === null || Array.isArray(rawRequest))
404
+ return;
405
+ const request = rawRequest;
406
+ if (request.result !== undefined)
407
+ return;
408
+ if (request.version !== MCP_RUNTIME_SNAPSHOT_VERSION) {
409
+ request.result = { ok: false, error: new Error(`Unsupported MCP runtime snapshot version: ${String(request.version)}`) };
410
+ return;
411
+ }
412
+ try {
413
+ request.result = { ok: true, snapshot: getRuntimeServerSnapshot(request.name) };
414
+ } catch (error) {
415
+ request.result = { ok: false, error: error instanceof Error ? error : new Error(String(error)) };
416
+ }
417
+ });
418
+ const getPiTools = () => pi.getAllTools();
419
+ pi.registerFlag("mcp-config", {
420
+ description: "Path to MCP config file",
421
+ type: "string"
422
+ });
423
+ function startInitialization(ctx, owner, oauthRuntime, generation, staleReason) {
424
+ owner.addCleanup(() => cleanupMaterializedBinaryResources(owner.signal));
425
+ const promise = initializeMcp(pi, ctx, owner, {
426
+ ...programmaticConfig || options.configPath !== undefined ? {
427
+ ...earlyConfigPath !== undefined ? { configPath: earlyConfigPath } : {},
428
+ ...sessionConfig !== undefined ? { config: sessionConfig } : {}
429
+ } : {},
430
+ oauthRuntime
431
+ });
432
+ initPromise = promise;
433
+ return promise.then(async (nextState) => {
434
+ if (!owner.isActive() || generation !== lifecycleGeneration || initPromise !== promise) {
435
+ try {
436
+ await shutdownState(nextState, staleReason);
437
+ } catch (error) {
438
+ console.error(`MCP: failed to clean stale initialization state: ${formatTerminalError(error)}`);
439
+ }
440
+ return;
441
+ }
442
+ state = nextState;
443
+ clearRetainedInitFailure();
444
+ for (const [name, { entry }] of runtimeServers) {
445
+ if (Object.hasOwn(nextState.config.mcpServers, name)) {
446
+ console.error(`MCP: runtime-registered server "${name}" now collides with a configured server; keeping the configured server`);
447
+ continue;
448
+ }
449
+ nextState.config.mcpServers[name] = entry;
450
+ attachRuntimeServerLifecycle(nextState, name, entry);
451
+ }
452
+ nextState.onToolMetadataUpdated = (_serverName, _reason) => {
453
+ if (state !== nextState || !owner.isActive())
454
+ return;
455
+ syncPromptCommands();
456
+ if (directToolsFrozen) {
457
+ logger.debug(`MCP: metadata update for ${_serverName} (${_reason}) skipped — directTools frozen`);
458
+ return;
459
+ }
460
+ syncToolSurface(ctx);
461
+ };
462
+ syncPromptCommands();
463
+ syncToolSurface(ctx);
464
+ nextState.statusEvents = pi.events;
465
+ updateStatusBar(nextState);
466
+ initPromise = null;
467
+ if (earlyConfig.settings?.freezeDirectTools === true) {
468
+ directToolsFrozen = true;
469
+ logger.info("MCP: direct tools frozen after initial sync — metadata can refresh without rebuilding the active tool surface");
470
+ }
471
+ }).catch(async (err) => {
472
+ if (!owner.isActive() || generation !== lifecycleGeneration) {
473
+ return;
474
+ }
475
+ if (initPromise !== promise && initPromise !== null) {
476
+ return;
477
+ }
478
+ const message = retainInitFailure(err);
479
+ console.error(`MCP initialization failed: ${message}`);
480
+ initPromise = null;
481
+ if (state)
482
+ return;
483
+ try {
484
+ await Promise.all([
485
+ owner.stop("MCP initialization failed"),
486
+ shutdownOAuth(oauthRuntime)
487
+ ]);
488
+ } catch (error) {
489
+ console.error(`MCP: failed to clean rejected initialization: ${formatTerminalError(error)}`);
490
+ }
491
+ });
492
+ }
493
+ function startLoadTimeInitialization() {
494
+ const hasStartupServer = Object.values(earlyConfig.mcpServers).some((definition) => {
495
+ if (definition.disabled === true)
496
+ return false;
497
+ return definition.lifecycle === "eager" || definition.lifecycle === "keep-alive";
498
+ });
499
+ if (!hasStartupServer)
500
+ return;
501
+ setImmediate(() => {
502
+ if (lifecycleGeneration !== 0 || state || initPromise)
503
+ return;
504
+ const generation = ++lifecycleGeneration;
505
+ const owner = createMcpRuntimeOwner();
506
+ const oauthRuntime = createOAuthRuntime(owner.signal);
507
+ currentOwner = owner;
508
+ currentOAuthRuntime = oauthRuntime;
509
+ startInitialization({
510
+ mode: "print",
511
+ hasUI: false,
512
+ cwd: process.cwd(),
513
+ model: undefined,
514
+ modelRegistry: undefined,
515
+ signal: undefined
516
+ }, owner, oauthRuntime, generation, "stale_load_time_initialization");
517
+ });
518
+ }
519
+ pi.on("session_start", async (_event, ctx) => {
520
+ const generation = ++lifecycleGeneration;
521
+ const previousState = state;
522
+ const previousOwner = currentOwner;
523
+ const previousOAuthRuntime = currentOAuthRuntime;
524
+ const owner = createMcpRuntimeOwner();
525
+ const oauthRuntime = createOAuthRuntime(owner.signal);
526
+ currentOwner = owner;
527
+ currentOAuthRuntime = oauthRuntime;
528
+ state = null;
529
+ initPromise = null;
530
+ clearRetainedInitFailure();
531
+ const stopPrevious = previousOwner?.stop("MCP extension session restarted") ?? Promise.resolve();
532
+ try {
533
+ await Promise.all([
534
+ stopPrevious,
535
+ shutdownState(previousState, "session_restart"),
536
+ previousOAuthRuntime ? shutdownOAuth(previousOAuthRuntime) : Promise.resolve()
537
+ ]);
538
+ } catch (error) {
539
+ console.error(`MCP: failed to shut down previous session state: ${formatTerminalError(error)}`);
540
+ }
541
+ if (generation !== lifecycleGeneration || !owner.isActive())
542
+ return;
543
+ const initialization = startInitialization(ctx, owner, oauthRuntime, generation, "stale_session_start");
544
+ if (envRaw !== undefined && envRaw !== "__none__") {
545
+ const missingEnvDirectTools = getMissingConfiguredDirectToolServers(earlyConfig, loadMetadataCache(), envDirectToolOverride);
546
+ if (missingEnvDirectTools.length > 0) {
547
+ await initialization;
548
+ }
549
+ }
550
+ });
551
+ pi.on("input", async () => {
552
+ const inputOwner = currentOwner;
553
+ if (!inputOwner?.isActive())
554
+ return;
555
+ if (!state && initPromise) {
556
+ try {
557
+ await awaitWithTimeout(initPromise, INIT_WAIT_TIMEOUT_MS);
558
+ } catch {
559
+ return;
560
+ }
561
+ }
562
+ const inputState = state;
563
+ if (!inputState || !inputOwner.isActive())
564
+ return;
565
+ try {
566
+ await inputState.lifecycle.ensureConverged(inputOwner.signal);
567
+ } catch (error) {
568
+ if (!isAbortError(error, inputOwner.signal)) {
569
+ logger.debug(`MCP: keep-alive convergence failed before input: ${formatTerminalError(error)}`);
570
+ }
571
+ }
572
+ });
573
+ pi.on("session_shutdown", async () => {
574
+ ++lifecycleGeneration;
575
+ const currentState = state;
576
+ const owner = currentOwner;
577
+ const oauthRuntime = currentOAuthRuntime;
578
+ currentOwner = null;
579
+ currentOAuthRuntime = null;
580
+ state = null;
581
+ initPromise = null;
582
+ clearRetainedInitFailure();
583
+ const stopOwner = owner?.stop("MCP extension session shutdown") ?? Promise.resolve();
584
+ try {
585
+ await Promise.all([
586
+ stopOwner,
587
+ shutdownState(currentState, "session_shutdown"),
588
+ oauthRuntime ? shutdownOAuth(oauthRuntime) : Promise.resolve()
589
+ ]);
590
+ } catch (error) {
591
+ console.error(`MCP: session shutdown cleanup failed: ${formatTerminalError(error)}`);
592
+ }
593
+ });
594
+ pi.on("tool_result", (event) => toolErrorOverride(event.details));
595
+ const registerMcpCommand = (commandName) => pi.registerCommand(commandName, {
596
+ description: "Show MCP server status",
597
+ getArgumentCompletions: (prefix) => {
598
+ const normalized = prefix.trimStart();
599
+ const argumentMatch = normalized.match(/^(\S+)\s+(.*)$/);
600
+ if (!argumentMatch) {
601
+ const subcommands = [
602
+ { value: "reconnect", label: "reconnect — Reconnect servers" },
603
+ { value: "tools", label: "tools — List all tools" },
604
+ { value: "prompts", label: "prompts — List all MCP prompts" },
605
+ { value: "setup", label: "setup — Configure MCP servers" },
606
+ { value: "logout", label: "logout — Clear server credentials" },
607
+ { value: "token", label: "token — Manage stored bearer tokens" },
608
+ { value: "disable", label: "disable — Disable a server" },
609
+ { value: "enable", label: "enable — Enable a server" },
610
+ { value: "status", label: "status — Show server status" }
611
+ ].filter(({ value }) => value.startsWith(normalized));
612
+ return subcommands.length > 0 ? subcommands : null;
613
+ }
614
+ const [, subcommand, argumentPrefix] = argumentMatch;
615
+ if (subcommand !== "reconnect" && subcommand !== "logout" && subcommand !== "disable" && subcommand !== "enable" && subcommand !== "token" || argumentPrefix === undefined || !state)
616
+ return null;
617
+ if (subcommand === "token") {
618
+ const tokenMatch = argumentPrefix.trimStart().match(/^(set|remove|status)\s+(.*)$/);
619
+ if (!tokenMatch) {
620
+ const actions = ["set", "remove", "status"].filter((action) => action.startsWith(argumentPrefix.trimStart())).map((action) => ({ value: `token ${action} `, label: `${action} — Bearer token ${action}` }));
621
+ return actions.length > 0 ? actions : null;
622
+ }
623
+ const action = tokenMatch[1] ?? "";
624
+ const serverPrefix = tokenMatch[2] ?? "";
625
+ const servers = Object.keys(state.config.mcpServers).filter((serverName) => serverName.startsWith(serverPrefix.trimStart())).map((serverName) => ({ value: `token ${action} ${serverName}`, label: serverName }));
626
+ return servers.length > 0 ? servers : null;
627
+ }
628
+ const servers = Object.keys(state.config.mcpServers).filter((serverName) => serverName.startsWith(argumentPrefix.trimStart())).map((serverName) => ({ value: `${subcommand} ${serverName}`, label: serverName }));
629
+ return servers.length > 0 ? servers : null;
630
+ },
631
+ handler: async (args, ctx) => {
632
+ const commandOwner = currentOwner;
633
+ const commandReload = typeof ctx.reload === "function" ? ctx.reload.bind(ctx) : async () => {};
634
+ const commandHasUI = ctx.hasUI;
635
+ const commandCtx = {
636
+ hasUI: commandHasUI,
637
+ ui: commandHasUI ? commandOwner ? createOwnedUi(ctx.ui, commandOwner) : ctx.ui : undefined,
638
+ cwd: ctx.cwd,
639
+ mode: ctx.mode,
640
+ signal: commandOwner?.signal ?? ctx.signal
641
+ };
642
+ if (!state && initPromise) {
643
+ try {
644
+ const initialized = await initPromise;
645
+ commandOwner?.throwIfInactive();
646
+ state = initialized;
647
+ } catch (error) {
648
+ const message = error instanceof Error ? error.message : String(error);
649
+ if (commandCtx.hasUI)
650
+ commandCtx.ui?.notify(`MCP initialization failed: ${message}`, "error");
651
+ return;
652
+ }
653
+ }
654
+ if (!state) {
655
+ if (commandCtx.hasUI)
656
+ commandCtx.ui?.notify("MCP not initialized", "error");
657
+ return;
658
+ }
659
+ const parts = args?.trim()?.split(/\s+/) ?? [];
660
+ const subcommand = parts[0] ?? "";
661
+ const targetServer = parts[1];
662
+ const rest = parts.slice(1).join(" ");
663
+ switch (subcommand) {
664
+ case "reconnect":
665
+ commandOwner?.throwIfInactive();
666
+ await reconnectServers(state, commandCtx, targetServer);
667
+ break;
668
+ case "tools":
669
+ await showTools(state, commandCtx);
670
+ break;
671
+ case "prompts":
672
+ await showPrompts(state, commandCtx);
673
+ break;
674
+ case "setup": {
675
+ commandOwner?.throwIfInactive();
676
+ if (programmaticConfig) {
677
+ commandCtx.ui?.notify("MCP setup is unavailable when config is supplied by createMcpAdapter().", "info");
678
+ break;
679
+ }
680
+ const result = await openMcpSetup(state, pi, commandCtx, earlyConfigPath, "setup");
681
+ if (result?.configChanged) {
682
+ commandOwner?.throwIfInactive();
683
+ await commandReload();
684
+ return;
685
+ }
686
+ break;
687
+ }
688
+ case "logout": {
689
+ const serverName = rest;
690
+ if (!serverName) {
691
+ if (commandCtx.hasUI)
692
+ commandCtx.ui?.notify("Usage: /mcp logout <server>", "error");
693
+ return;
694
+ }
695
+ commandOwner?.throwIfInactive();
696
+ await logoutServer(serverName, state, commandCtx);
697
+ break;
698
+ }
699
+ case "token": {
700
+ const action = parts[1];
701
+ const serverName = parts.slice(2).join(" ");
702
+ if (action !== "set" && action !== "remove" && action !== "status") {
703
+ if (commandCtx.hasUI)
704
+ commandCtx.ui?.notify("Usage: /mcp token set|remove|status <server>", "error");
705
+ return;
706
+ }
707
+ if (!serverName) {
708
+ if (commandCtx.hasUI)
709
+ commandCtx.ui?.notify("Usage: /mcp token set|remove|status <server>", "error");
710
+ return;
711
+ }
712
+ commandOwner?.throwIfInactive();
713
+ await manageBearerToken(action, serverName, state, commandCtx);
714
+ break;
715
+ }
716
+ case "disable":
717
+ case "enable": {
718
+ const serverName = rest;
719
+ if (programmaticConfig) {
720
+ commandCtx.ui?.notify(`/mcp ${subcommand} is unavailable when config is supplied by createMcpAdapter().`, "info");
721
+ break;
722
+ }
723
+ if (!serverName) {
724
+ commandCtx.ui?.notify(`Usage: /mcp ${subcommand} <server>`, "error");
725
+ break;
726
+ }
727
+ if (!state.config.mcpServers[serverName]) {
728
+ commandCtx.ui?.notify(`Server "${serverName}" not found in effective config`, "error");
729
+ break;
730
+ }
731
+ commandOwner?.throwIfInactive();
732
+ const result = writeProjectServerDisabledOverride(earlyConfigPath, commandCtx.cwd, serverName, subcommand === "disable");
733
+ if (result.changed) {
734
+ commandCtx.ui?.notify(`${subcommand === "disable" ? "Disabled" : "Enabled"} server "${serverName}" in ${result.path} — run /reload to apply`, "info");
735
+ } else {
736
+ commandCtx.ui?.notify(`Server "${serverName}" is already ${subcommand === "disable" ? "disabled" : "enabled"}`, "info");
737
+ }
738
+ break;
739
+ }
740
+ case "status":
741
+ case "":
742
+ default:
743
+ if (commandCtx.hasUI) {
744
+ commandOwner?.throwIfInactive();
745
+ if (programmaticConfig) {
746
+ commandCtx.ui?.notify("MCP status is shown from the in-memory SDK config; configuration discovery is unavailable.", "info");
747
+ await showStatus(state, commandCtx);
748
+ break;
749
+ }
750
+ const result = await openMcpPanel(state, pi, commandCtx, earlyConfigPath, (changes) => {
751
+ applyDirectToolConfigChanges(changes);
752
+ syncToolSurface(commandCtx);
753
+ });
754
+ if (result?.configChanged) {
755
+ commandOwner?.throwIfInactive();
756
+ await commandReload();
757
+ return;
758
+ }
759
+ } else {
760
+ await showStatus(state, commandCtx);
761
+ }
762
+ break;
763
+ }
764
+ }
765
+ });
766
+ registerMcpCommand("mcp");
767
+ registerMcpCommand("pi-mcp");
768
+ pi.registerCommand("mcp-auth", {
769
+ description: "Authenticate with an MCP server (OAuth)",
770
+ handler: async (args, ctx) => {
771
+ const commandOwner = currentOwner;
772
+ const commandHasUI = ctx.hasUI;
773
+ const commandCtx = {
774
+ hasUI: commandHasUI,
775
+ ui: commandHasUI ? commandOwner ? createOwnedUi(ctx.ui, commandOwner) : ctx.ui : undefined,
776
+ cwd: ctx.cwd,
777
+ mode: ctx.mode,
778
+ signal: commandOwner?.signal ?? ctx.signal
779
+ };
780
+ const serverName = args?.trim();
781
+ if (!serverName && !commandCtx.hasUI) {
782
+ return;
783
+ }
784
+ if (!state && initPromise) {
785
+ try {
786
+ const initialized = await initPromise;
787
+ commandOwner?.throwIfInactive();
788
+ state = initialized;
789
+ } catch (error) {
790
+ const message = error instanceof Error ? error.message : String(error);
791
+ if (commandCtx.hasUI)
792
+ commandCtx.ui?.notify(`MCP initialization failed: ${message}`, "error");
793
+ return;
794
+ }
795
+ }
796
+ if (!state) {
797
+ if (commandCtx.hasUI)
798
+ commandCtx.ui?.notify("MCP not initialized", "error");
799
+ return;
800
+ }
801
+ if (!serverName) {
802
+ if (programmaticConfig) {
803
+ commandCtx.ui?.notify("Use /mcp-auth <server> to authenticate a server from the in-memory SDK config.", "info");
804
+ return;
805
+ }
806
+ await openMcpAuthPanel(state, pi, commandCtx, earlyConfigPath);
807
+ return;
808
+ }
809
+ const result = await authenticateServer(serverName, state.config, commandCtx, commandCtx.signal, state.oauthRuntime);
810
+ if (result.ok) {
811
+ commandOwner?.throwIfInactive();
812
+ await reconnectServer(state, commandCtx, serverName);
813
+ }
814
+ }
815
+ });
816
+ if (earlyConfig.settings?.scriptMode !== false) {
817
+ pi.registerTool({
818
+ name: "mcpScript",
819
+ label: "MCP Script",
820
+ description: "Run trusted JavaScript that makes multiple MCP tool calls in one request — loop, filter, chain, or fan out between calls. For a single MCP call, search, describe, status check, or auth action, use the mcp tool instead. Discover with await tools.search({ query }) — resolves to { items: [{ path, name, server, description? }], total, hasMore, nextOffset }, not an { ok, data } envelope. Inspect with await tools.describe({ path }) — resolves to the tool descriptor with inputTypeScript, or { path, error: { code, message, suggestions } }. Then call tools.call(path, args) — resolves to { ok: true, data } or { ok: false, error: { code, message } } — or use direct flat calls when the name is already known; use emit(value) for user-visible output. Load the mcp-scripting skill for the full workflow guide.",
821
+ promptSnippet: "Batch multiple MCP tool calls in one JavaScript request (loop, filter, chain)",
822
+ parameters: Type.Object({
823
+ code: Type.String({ description: "Trusted JavaScript MCP script. Use tools.<prefixedToolName>(args) and emit(value)." }),
824
+ timeoutMs: optionalNumber({ minimum: 1, description: "Execution timeout in milliseconds (default: 30000)" })
825
+ }),
826
+ renderCall: createMcpScriptToolCallRenderer(toolRenderOptions),
827
+ renderResult: renderMcpToolResult,
828
+ async execute(_toolCallId, params, signal) {
829
+ const executeOwner = currentOwner;
830
+ if (!state && initPromise) {
831
+ try {
832
+ const initialized = await awaitWithTimeout(initPromise, INIT_WAIT_TIMEOUT_MS);
833
+ if (initialized === INIT_WAIT_TIMED_OUT) {
834
+ return {
835
+ content: [{ type: "text", text: "MCP initialization is still in progress. Try again shortly." }],
836
+ details: { mode: "script", error: "init_timeout", timeoutMs: INIT_WAIT_TIMEOUT_MS }
837
+ };
838
+ }
839
+ executeOwner?.throwIfInactive();
840
+ state = initialized;
841
+ } catch (error) {
842
+ if (executeOwner && isOwnerAbortError(error, executeOwner))
843
+ throw error;
844
+ const message = error instanceof Error ? error.message : String(error);
845
+ return {
846
+ content: [{ type: "text", text: `MCP initialization failed: ${message}` }],
847
+ details: { mode: "script", error: "init_failed", message }
848
+ };
849
+ }
850
+ }
851
+ if (!state) {
852
+ return {
853
+ content: [{ type: "text", text: retainedInitFailure ? buildInitRetryInstruction("MCP is not initialized after an earlier initialization failure") : "MCP not initialized" }],
854
+ details: { mode: "script", error: "not_initialized", ...retainedInitFailure ? { message: retainedInitFailure } : {} }
855
+ };
856
+ }
857
+ executeOwner?.throwIfInactive();
858
+ return runMcpScript(state, params.code, params.timeoutMs, getPiTools, signal);
859
+ }
860
+ });
861
+ }
862
+ function registerProxyTool(description) {
863
+ pi.registerTool({
864
+ name: "mcp",
865
+ label: "MCP",
866
+ description,
867
+ promptSnippet: "MCP gateway — status, search, describe, auth, and single MCP tool calls",
868
+ renderShell: toolRenderShell,
869
+ renderCall: createMcpProxyToolCallRenderer(toolRenderOptions),
870
+ parameters: Type.Object({
871
+ tool: Type.Optional(Type.String({ description: "Tool name to call (e.g., 'xcodebuild_list_sims')" })),
872
+ args: Type.Optional(Type.Union([
873
+ Type.String({ description: `Arguments as a JSON string (e.g., '{"key": "value"}')` }),
874
+ Type.Object({}, {
875
+ additionalProperties: true,
876
+ description: 'Arguments as a JSON object (e.g., { "key": "value" })'
877
+ })
878
+ ], { description: "Tool arguments as a JSON object, or as a JSON string encoding one" })),
879
+ connect: Type.Optional(Type.String({ description: "Server name to connect (lazy connect + metadata refresh)" })),
880
+ describe: Type.Optional(Type.String({ description: "Tool name to describe (shows parameters)" })),
881
+ instructions: Type.Optional(Type.String({ description: "Server name to show that server's usage instructions" })),
882
+ search: Type.Optional(Type.String({ description: "Search tools by name/description" })),
883
+ regex: Type.Optional(Type.Boolean({ description: "Treat search as regex (default: substring match)" })),
884
+ includeSchemas: Type.Optional(Type.Boolean({ description: "Include parameter schemas in search results (default: true)" })),
885
+ limit: optionalNumber({ minimum: 1, description: "Maximum search results to return (default: 12)" }),
886
+ offset: optionalNumber({ minimum: 0, description: "Search result offset (default: 0)" }),
887
+ server: Type.Optional(Type.String({ description: "Filter to specific server (also disambiguates tool calls)" })),
888
+ action: Type.Optional(Type.String({ description: "Action: 'ui-messages', 'auth-start', or 'auth-complete'" }))
889
+ }),
890
+ renderResult: renderMcpToolResult,
891
+ async execute(_toolCallId, params, signal, _onUpdate, _ctx) {
892
+ let executeOwner = currentOwner;
893
+ const parseArgs = (value) => {
894
+ if (value === undefined || value === "")
895
+ return;
896
+ let args;
897
+ if (typeof value === "string") {
898
+ try {
899
+ args = JSON.parse(value);
900
+ } catch (error) {
901
+ if (error instanceof SyntaxError) {
902
+ throw new Error(`Invalid args JSON: ${error.message}`, { cause: error });
903
+ }
904
+ throw error;
905
+ }
906
+ } else {
907
+ args = value;
908
+ }
909
+ if (typeof args !== "object" || args === null || Array.isArray(args)) {
910
+ const gotType = Array.isArray(args) ? "array" : args === null ? "null" : typeof args;
911
+ throw new Error(`Invalid args: expected a JSON object, got ${gotType}`);
912
+ }
913
+ return args;
914
+ };
915
+ const parsedArgs = parseArgs(params.args);
916
+ const hasGatewayMode = (value) => value.tool !== undefined || value.connect !== undefined || value.describe !== undefined || value.instructions !== undefined || value.search !== undefined || value.server !== undefined || value.action !== undefined;
917
+ if (!hasGatewayMode(params) && params.args !== undefined) {
918
+ throw new Error('Gateway params were nested inside `args`; pass them top-level (for example, mcp({ search: "..." }) or mcp({ tool: "...", args: {} })).');
919
+ }
920
+ if (!state && !initPromise && retainedInitFailure) {
921
+ startGatewayRetryInitialization(_ctx);
922
+ executeOwner = currentOwner;
923
+ }
924
+ if (!state && initPromise) {
925
+ try {
926
+ const initialized = await awaitWithTimeout(initPromise, INIT_WAIT_TIMEOUT_MS);
927
+ if (initialized === INIT_WAIT_TIMED_OUT) {
928
+ return {
929
+ content: [{ type: "text", text: "MCP initialization is still in progress. Try again shortly." }],
930
+ details: { error: "init_timeout", timeoutMs: INIT_WAIT_TIMEOUT_MS }
931
+ };
932
+ }
933
+ executeOwner?.throwIfInactive();
934
+ state = initialized;
935
+ } catch (error) {
936
+ if (executeOwner && isOwnerAbortError(error, executeOwner))
937
+ throw error;
938
+ const message = retainInitFailure(error);
939
+ return {
940
+ content: [{ type: "text", text: buildInitRetryInstruction("MCP initialization failed", message) }],
941
+ details: { error: "init_failed", message }
942
+ };
943
+ }
944
+ }
945
+ if (!state) {
946
+ return {
947
+ content: [{ type: "text", text: retainedInitFailure ? buildInitRetryInstruction("MCP is not initialized after an earlier initialization failure") : "MCP not initialized" }],
948
+ details: { error: "not_initialized", ...retainedInitFailure ? { message: retainedInitFailure } : {} }
949
+ };
950
+ }
951
+ executeOwner?.throwIfInactive();
952
+ if (params.action === "ui-messages") {
953
+ return executeUiMessages(state);
954
+ }
955
+ if (params.action === "auth-start") {
956
+ if (!params.server) {
957
+ return {
958
+ content: [{ type: "text", text: 'auth-start requires `server`. Example: mcp({ action: "auth-start", server: "linear-server" })' }],
959
+ details: { mode: "auth-start", error: "missing_server" }
960
+ };
961
+ }
962
+ return signal ? executeAuthStart(state, params.server, signal) : executeAuthStart(state, params.server);
963
+ }
964
+ if (params.action === "auth-complete") {
965
+ if (!params.server) {
966
+ return {
967
+ content: [{ type: "text", text: "auth-complete requires `server`." }],
968
+ details: { mode: "auth-complete", error: "missing_server" }
969
+ };
970
+ }
971
+ const input = parsedArgs?.redirectUrl ?? parsedArgs?.code ?? parsedArgs?.input;
972
+ if (typeof input !== "string" || input.trim().length === 0) {
973
+ return {
974
+ content: [{ type: "text", text: "auth-complete requires args with `redirectUrl`, `code`, or `input`." }],
975
+ details: { mode: "auth-complete", error: "missing_input" }
976
+ };
977
+ }
978
+ return signal ? executeAuthComplete(state, params.server, input, signal) : executeAuthComplete(state, params.server, input);
979
+ }
980
+ if (params.tool) {
981
+ return executeCall(state, params.tool, parsedArgs, params.server, getPiTools, signal);
982
+ }
983
+ if (params.connect) {
984
+ const result = await executeConnect(state, params.connect, signal);
985
+ if (!directToolsFrozen)
986
+ syncToolSurface(_ctx);
987
+ return result;
988
+ }
989
+ if (params.describe) {
990
+ return executeDescribe(state, params.describe);
991
+ }
992
+ if (params.instructions) {
993
+ return executeInstructions(state, params.instructions);
994
+ }
995
+ if (params.search !== undefined) {
996
+ return executeSearch(state, params.search, params.regex, params.server, params.includeSchemas, params.limit, params.offset);
997
+ }
998
+ if (params.server) {
999
+ return executeList(state, params.server);
1000
+ }
1001
+ return executeStatus(state);
1002
+ }
1003
+ });
1004
+ proxyToolRegistered = true;
1005
+ proxyToolDescription = description;
1006
+ }
1007
+ function syncProxyTool(config, cache, directSpecs) {
1008
+ const missingConfiguredDirectToolServers = getMissingConfiguredDirectToolServers(config, cache, envRaw === undefined || envRaw === "__none__" ? undefined : envDirectToolOverride);
1009
+ const shouldRegisterProxyTool = config.settings?.disableProxyTool !== true || directSpecs.length === 0 || missingConfiguredDirectToolServers.length > 0;
1010
+ if (shouldRegisterProxyTool) {
1011
+ const description = buildProxyDescription(config);
1012
+ if (!proxyToolRegistered || proxyToolDescription !== description) {
1013
+ registerProxyTool(description);
1014
+ return;
1015
+ }
1016
+ const activeTools = getActiveToolsIfReady();
1017
+ if (activeTools && !activeTools.includes("mcp")) {
1018
+ pi.setActiveTools([...activeTools, "mcp"]);
1019
+ }
1020
+ return;
1021
+ }
1022
+ if (proxyToolRegistered) {
1023
+ const unregistered = deactivateTools(["mcp"]);
1024
+ if (unregistered.includes("mcp")) {
1025
+ proxyToolRegistered = false;
1026
+ proxyToolDescription = null;
1027
+ }
1028
+ }
1029
+ }
1030
+ const initialDirectResult = syncDirectTools(earlyConfig, earlyCache);
1031
+ syncProxyTool(earlyConfig, earlyCache, initialDirectResult.specs);
1032
+ syncNamespaceTools(earlyConfig, earlyCache, initialDirectResult.reservedDirectNames, initialDirectResult.activeDirectNames);
1033
+ startLoadTimeInitialization();
1034
+ }
1035
+ export function createMcpAdapter(options = {}) {
1036
+ const factoryConfig = options.config !== undefined ? cloneMcpConfig(options.config) : undefined;
1037
+ return function mcpAdapter(pi) {
1038
+ installMcpAdapter(pi, {
1039
+ ...options.configPath !== undefined ? { configPath: options.configPath } : {},
1040
+ ...factoryConfig !== undefined ? { config: cloneMcpConfig(factoryConfig) } : {}
1041
+ });
1042
+ };
1043
+ }
1044
+ export function registerMcpServer(options) {
1045
+ const { pi, name, definition } = options;
1046
+ const register = runtimeRegistrars.get(pi);
1047
+ if (register)
1048
+ return register(name, definition);
1049
+ const request = {
1050
+ version: MCP_RUNTIME_REGISTER_VERSION,
1051
+ name,
1052
+ definition
1053
+ };
1054
+ pi.events.emit(MCP_RUNTIME_REGISTER_EVENT, request);
1055
+ if (!request.result) {
1056
+ throw new Error("dm-mcp-adapter is not installed for this DM instance");
1057
+ }
1058
+ if (!request.result.ok)
1059
+ throw request.result.error;
1060
+ return request.result.registration;
1061
+ }
1062
+ export function getRuntimeMcpServerSnapshot(options) {
1063
+ const { pi, name } = options;
1064
+ const getSnapshot = runtimeSnapshotters.get(pi);
1065
+ if (getSnapshot)
1066
+ return getSnapshot(name);
1067
+ const request = {
1068
+ version: MCP_RUNTIME_SNAPSHOT_VERSION,
1069
+ name
1070
+ };
1071
+ pi.events.emit(MCP_RUNTIME_SNAPSHOT_EVENT, request);
1072
+ if (!request.result) {
1073
+ throw new Error("dm-mcp-adapter is not installed for this DM instance");
1074
+ }
1075
+ if (!request.result.ok)
1076
+ throw request.result.error;
1077
+ return request.result.snapshot;
1078
+ }
1079
+ export default createMcpAdapter();