@fitchmultz/pi-mcp-adapter 8.0.0 → 8.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +36 -7
- package/LICENSE +1 -0
- package/OAUTH.md +3 -3
- package/README.md +43 -858
- package/dist/app-bridge.bundle.js +14 -14
- package/dist/client-operations.d.ts +5 -0
- package/dist/client-operations.js +19 -0
- package/dist/commands.d.ts +1 -1
- package/dist/commands.js +3 -1
- package/dist/config.js +1 -1
- package/dist/connection-runtime.d.ts +503 -0
- package/dist/connection-runtime.js +98 -0
- package/dist/direct-tools.js +1 -1
- package/dist/host-html-template.js +24 -12
- package/dist/index.js +36 -29
- package/dist/init.d.ts +1 -1
- package/dist/init.js +66 -38
- package/dist/lifecycle.d.ts +1 -1
- package/dist/lifecycle.js +1 -1
- package/dist/mcp-auth-flow.d.ts +2 -43
- package/dist/mcp-auth-flow.js +3 -138
- package/dist/mcp-callback-server.js +1 -1
- package/dist/mcp-code.d.ts +0 -1
- package/dist/mcp-code.js +20 -7
- package/dist/mcp-oauth-callback-config.d.ts +8 -0
- package/dist/mcp-oauth-callback-config.js +28 -0
- package/dist/mcp-oauth-provider.d.ts +1 -8
- package/dist/mcp-oauth-provider.js +2 -28
- package/dist/mcp-oauth-runtime.d.ts +83 -0
- package/dist/mcp-oauth-runtime.js +145 -0
- package/dist/mcp-output-guard.d.ts +1 -1
- package/dist/mcp-output-guard.js +5 -108
- package/dist/mcp-output-preview.d.ts +29 -0
- package/dist/mcp-output-preview.js +220 -0
- package/dist/mcp-panel.js +1 -2
- package/dist/mcp-script-worker.mjs +14 -2
- package/dist/mcp-setup-panel.js +1 -1
- package/dist/metadata-cache.d.ts +1 -1
- package/dist/metadata-cache.js +56 -14
- package/dist/plain-result-reader.d.ts +7 -0
- package/dist/plain-result-reader.js +201 -0
- package/dist/prompts.js +1 -1
- package/dist/proxy-modes.d.ts +1 -1
- package/dist/proxy-modes.js +14 -5
- package/dist/public-types.d.ts +2 -0
- package/dist/public-types.js +2 -0
- package/dist/resource-tools.d.ts +0 -1
- package/dist/resource-tools.js +0 -13
- package/dist/server-manager.d.ts +4 -4
- package/dist/server-manager.js +25 -125
- package/dist/tool-loader.js +10 -4
- package/dist/tool-metadata.js +20 -3
- package/dist/tool-result-renderer.d.ts +1 -1
- package/dist/tool-result-renderer.js +10 -3
- package/dist/types.d.ts +1 -1
- package/dist/types.js +0 -3
- package/dist/ui-message-history.js +3 -2
- package/dist/ui-resource-handler.d.ts +2 -1
- package/dist/ui-resource-handler.js +4 -3
- package/dist/ui-server.js +2 -1
- package/dist/ui-session.js +2 -1
- package/dist/utils.d.ts +1 -0
- package/dist/utils.js +1 -0
- package/package.json +10 -13
package/dist/direct-tools.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { abortable, throwIfAborted } from "./abort.js";
|
|
2
2
|
import { parseDirectToolSelectors } from "./metadata-cache.js";
|
|
3
3
|
export { getMissingConfiguredDirectToolServers } from "./metadata-cache.js";
|
|
4
|
-
import { executeCall } from "./proxy-modes.js";
|
|
5
4
|
import { isServerDisabled } from "./types.js";
|
|
6
5
|
import { nativeMcpResult } from "./tool-registrar.js";
|
|
7
6
|
export function directToolSelection(config, serverName, envOverride) {
|
|
@@ -46,6 +45,7 @@ export function createDirectToolExecutor(getState, getInitPromise, spec, beforeE
|
|
|
46
45
|
details: { error: "not_initialized" },
|
|
47
46
|
}, !!spec.outputSchema);
|
|
48
47
|
}
|
|
48
|
+
const { executeCall } = await import("./proxy-modes.js");
|
|
49
49
|
const result = await executeCall(state, spec.originalName, params, spec.serverName, undefined, signal, { toolCallId }, beforeExecute ? (callSignal, operation) => beforeExecute(toolCallId, { ...ctx, signal: callSignal }, operation) : undefined, { exactOriginalName: true, ...(spec.resourceUri ? { resourceUri: spec.resourceUri } : {}) });
|
|
50
50
|
const { mode: _mode, ...details } = result.details;
|
|
51
51
|
return nativeMcpResult({ ...result, details }, !!spec.outputSchema);
|
|
@@ -332,11 +332,6 @@ export function buildHostHtmlTemplate(input) {
|
|
|
332
332
|
bridge.setHostContext(JSON.parse(event.data));
|
|
333
333
|
} catch {}
|
|
334
334
|
});
|
|
335
|
-
eventSource.addEventListener("session-complete", async () => {
|
|
336
|
-
await bridge.teardownResource({}).catch(() => {});
|
|
337
|
-
eventSource.close();
|
|
338
|
-
closeOrShowDone();
|
|
339
|
-
});
|
|
340
335
|
eventSource.onerror = () => {
|
|
341
336
|
setStatus("Connection lost", true);
|
|
342
337
|
};
|
|
@@ -345,18 +340,35 @@ export function buildHostHtmlTemplate(input) {
|
|
|
345
340
|
post("/proxy/ui/heartbeat", {}).catch(() => {});
|
|
346
341
|
}, 10000);
|
|
347
342
|
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
await bridge.teardownResource({});
|
|
354
|
-
} catch {}
|
|
343
|
+
let completionRequested = false;
|
|
344
|
+
let finished = false;
|
|
345
|
+
const finish = async () => {
|
|
346
|
+
if (finished) return;
|
|
347
|
+
finished = true;
|
|
355
348
|
clearInterval(heartbeat);
|
|
356
349
|
eventSource.close();
|
|
350
|
+
|
|
351
|
+
try {
|
|
352
|
+
await bridge.teardownResource({});
|
|
353
|
+
} catch (error) {
|
|
354
|
+
// Apps may omit the optional teardown handler.
|
|
355
|
+
if (error?.code !== -32601) console.error("[host] Resource teardown failed:", error);
|
|
356
|
+
}
|
|
357
357
|
closeOrShowDone();
|
|
358
358
|
};
|
|
359
359
|
|
|
360
|
+
const complete = async (reason) => {
|
|
361
|
+
if (completionRequested || finished) return;
|
|
362
|
+
completionRequested = true;
|
|
363
|
+
try {
|
|
364
|
+
await post("/proxy/ui/complete", { reason });
|
|
365
|
+
} catch (error) {
|
|
366
|
+
console.error("[host] Session completion failed:", error);
|
|
367
|
+
}
|
|
368
|
+
await finish();
|
|
369
|
+
};
|
|
370
|
+
|
|
371
|
+
eventSource.addEventListener("session-complete", finish);
|
|
360
372
|
doneBtn.addEventListener("click", () => complete("done"));
|
|
361
373
|
cancelBtn.addEventListener("click", () => complete("cancel"));
|
|
362
374
|
window.addEventListener("keydown", (event) => {
|
package/dist/index.js
CHANGED
|
@@ -1,22 +1,17 @@
|
|
|
1
1
|
import { Type } from "typebox";
|
|
2
|
-
import { showStatus, showTools, showPrompts, reconnectServer, reconnectServers, authenticateServer, logoutServer, openMcpAuthPanel, openMcpPanel, openMcpSetup } from "./commands.js";
|
|
3
2
|
import { cloneMcpConfig, isPathInsideProject, loadMcpConfig, writeProjectServerDisabledOverride } from "./config.js";
|
|
4
3
|
import { buildProxyDescription, directToolSelection, getMissingConfiguredDirectToolServers } from "./direct-tools.js";
|
|
5
|
-
import { flushMetadataCache, initializeMcp, lazyConnect, updateStatusBar } from "./init.js";
|
|
6
4
|
import { getMetadataCachePath, isServerCacheValid, loadMetadataCache, reconstructToolMetadata } from "./metadata-cache.js";
|
|
7
5
|
import { createPromptCommand, resolveCachedPrompts } from "./prompts.js";
|
|
8
6
|
import { logger } from "./logger.js";
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import { createOAuthRuntime, shutdownOAuth } from "./mcp-auth-flow.js";
|
|
7
|
+
import { DEFAULT_MCP_SCRIPT_TIMEOUT_MS, formatTerminalError } from "./utils.js";
|
|
8
|
+
import { createOAuthRuntime, shutdownOAuth } from "./mcp-oauth-runtime.js";
|
|
12
9
|
import { renderMcpProxyToolCall, renderMcpToolResult } from "./tool-result-renderer.js";
|
|
13
10
|
import { createMcpRuntimeOwner, createOwnedUi, isAbortError } from "./runtime-owner.js";
|
|
14
11
|
import { publishMcpStatusShutdown } from "./mcp-status.js";
|
|
15
|
-
import { DEFAULT_MCP_SCRIPT_TIMEOUT_MS, runMcpScript } from "./mcp-code.js";
|
|
16
12
|
import { MAX_PAGE_SIZE, paginate, rankToolMatches } from "./search-ranking.js";
|
|
17
13
|
import { createToolLoader } from "./tool-loader.js";
|
|
18
14
|
import { gatewayParameters, prepareGatewayArguments } from "./gateway-arguments.js";
|
|
19
|
-
import { executeResourceList, executeResourceRead } from "./resource-tools.js";
|
|
20
15
|
import { guardMcpOutput, guardedMcpDetails, readMcpResult, resolveMcpOutputGuardOptions } from "./mcp-output-guard.js";
|
|
21
16
|
import { abortable, throwIfAborted } from "./abort.js";
|
|
22
17
|
export { MCP_STATUS_EVENT, MCP_STATUS_SNAPSHOT_VERSION, } from "./types.js";
|
|
@@ -47,6 +42,9 @@ function installMcpAdapter(pi, options) {
|
|
|
47
42
|
let currentOAuthRuntime = null;
|
|
48
43
|
let lifecycleGeneration = 0;
|
|
49
44
|
let currentConfigPath;
|
|
45
|
+
// init loads during session_start, after the cached surface registers, keeping it off the extension-load path.
|
|
46
|
+
// Command, panel, gateway, script and resource modules load on first use.
|
|
47
|
+
let initModule;
|
|
50
48
|
async function shutdownState(currentState, reason) {
|
|
51
49
|
if (!currentState) {
|
|
52
50
|
publishMcpStatusShutdown(pi.events);
|
|
@@ -67,7 +65,7 @@ function installMcpAdapter(pi, options) {
|
|
|
67
65
|
}
|
|
68
66
|
currentState.uiServer = null;
|
|
69
67
|
try {
|
|
70
|
-
flushMetadataCache(currentState);
|
|
68
|
+
initModule?.flushMetadataCache(currentState);
|
|
71
69
|
}
|
|
72
70
|
catch (error) {
|
|
73
71
|
failures.push(error);
|
|
@@ -132,8 +130,8 @@ function installMcpAdapter(pi, options) {
|
|
|
132
130
|
description: "Path to MCP config file",
|
|
133
131
|
type: "string",
|
|
134
132
|
});
|
|
135
|
-
function startInitialization(ctx, owner, oauthRuntime, runtimeConfig, generation) {
|
|
136
|
-
const promise = initializeMcp(pi, ctx, owner, {
|
|
133
|
+
function startInitialization(init, ctx, owner, oauthRuntime, runtimeConfig, generation) {
|
|
134
|
+
const promise = init.initializeMcp(pi, ctx, owner, {
|
|
137
135
|
...(programmaticConfig ? { config: runtimeConfig } : { resolvedConfig: runtimeConfig }),
|
|
138
136
|
...(options.outputDirectory !== undefined ? { outputDirectory: options.outputDirectory } : {}),
|
|
139
137
|
oauthRuntime,
|
|
@@ -164,12 +162,12 @@ function installMcpAdapter(pi, options) {
|
|
|
164
162
|
};
|
|
165
163
|
for (const server of toolLoader.selectedServers()) {
|
|
166
164
|
if (!nextState.toolMetadata.has(server) && nextState.config.mcpServers[server])
|
|
167
|
-
await lazyConnect(nextState, server, owner.signal);
|
|
165
|
+
await init.lazyConnect(nextState, server, owner.signal);
|
|
168
166
|
}
|
|
169
167
|
owner.throwIfInactive();
|
|
170
168
|
syncPromptCommands();
|
|
171
169
|
syncToolSurface(ctx);
|
|
172
|
-
updateStatusBar(nextState);
|
|
170
|
+
init.updateStatusBar(nextState);
|
|
173
171
|
initPromise = null;
|
|
174
172
|
if (nextState.config.settings?.freezeDirectTools === true) {
|
|
175
173
|
directToolsFrozen = true;
|
|
@@ -246,7 +244,11 @@ function installMcpAdapter(pi, options) {
|
|
|
246
244
|
registerPromptCommands(resolveCachedPrompts(runtimeConfig, metadataCacheEnabled));
|
|
247
245
|
const runtimeCache = loadMetadataCache(metadataCacheEnabled);
|
|
248
246
|
toolLoader.sync(runtimeConfig, runtimeCache, envRaw === "__none__" ? null : envDirectToolOverride, specs => syncGateways(runtimeConfig, runtimeCache, specs));
|
|
249
|
-
const
|
|
247
|
+
const init = await import("./init.js");
|
|
248
|
+
initModule = init;
|
|
249
|
+
if (generation !== lifecycleGeneration || !owner.isActive())
|
|
250
|
+
return;
|
|
251
|
+
const initialization = startInitialization(init, ctx, owner, oauthRuntime, runtimeConfig, generation);
|
|
250
252
|
if (envRaw !== undefined && envRaw !== "__none__") {
|
|
251
253
|
const missingEnvDirectTools = getMissingConfiguredDirectToolServers(runtimeConfig, runtimeCache, envDirectToolOverride);
|
|
252
254
|
if (missingEnvDirectTools.length > 0) {
|
|
@@ -358,6 +360,7 @@ function installMcpAdapter(pi, options) {
|
|
|
358
360
|
const commandState = await ensureCommandState(commandOwner, commandCtx);
|
|
359
361
|
if (!commandState)
|
|
360
362
|
return;
|
|
363
|
+
const { showStatus, showTools, showPrompts, reconnectServers, logoutServer, openMcpPanel, openMcpSetup } = await import("./commands.js");
|
|
361
364
|
const parts = args?.trim()?.split(/\s+/) ?? [];
|
|
362
365
|
const subcommand = parts[0] ?? "";
|
|
363
366
|
const targetServer = parts[1];
|
|
@@ -467,6 +470,7 @@ function installMcpAdapter(pi, options) {
|
|
|
467
470
|
const commandState = await ensureCommandState(commandOwner, commandCtx);
|
|
468
471
|
if (!commandState)
|
|
469
472
|
return;
|
|
473
|
+
const { authenticateServer, openMcpAuthPanel, reconnectServer } = await import("./commands.js");
|
|
470
474
|
if (!serverName) {
|
|
471
475
|
if (programmaticConfig) {
|
|
472
476
|
commandCtx.ui?.notify("Use /mcp-auth <server> to authenticate a server from the in-memory SDK config.", "info");
|
|
@@ -519,8 +523,9 @@ function installMcpAdapter(pi, options) {
|
|
|
519
523
|
const ready = await getToolState(signal);
|
|
520
524
|
if ("content" in ready)
|
|
521
525
|
return { ...ready, tools: [] };
|
|
526
|
+
const proxy = await import("./proxy-modes.js");
|
|
522
527
|
if (params.server && !ready.toolMetadata.has(params.server)) {
|
|
523
|
-
const discovery = await executeConnect(ready, params.server, signal);
|
|
528
|
+
const discovery = await proxy.executeConnect(ready, params.server, signal);
|
|
524
529
|
if (discovery.details.error)
|
|
525
530
|
return { ...discovery, tools: [] };
|
|
526
531
|
}
|
|
@@ -531,7 +536,7 @@ function installMcpAdapter(pi, options) {
|
|
|
531
536
|
const tools = toolLoader.activate(page.items, params.enable);
|
|
532
537
|
const unavailable = unavailableFeatures(params.enable, tools);
|
|
533
538
|
const typedCount = tools.filter(tool => tool.name !== "mcp" && tool.name !== "mcp_script").length;
|
|
534
|
-
const result = executeSearch(ready, params.query, params.server, typedCount < page.items.length, params.limit ?? 5, params.offset, "mcp_search");
|
|
539
|
+
const result = proxy.executeSearch(ready, params.query, params.server, typedCount < page.items.length, params.limit ?? 5, params.offset, "mcp_search");
|
|
535
540
|
const guarded = await guardMcpOutput([
|
|
536
541
|
...result.content,
|
|
537
542
|
{ type: "text", text: tools.length ? `Loaded for the next request: ${JSON.stringify(tools)}` : 'No direct tools loaded. Enable gateway or script with mcp_search({ enable: ["gateway"] }) or mcp_search({ enable: ["script"] }), then use mcp action call or tools.call with the returned exact names.' },
|
|
@@ -548,7 +553,7 @@ function installMcpAdapter(pi, options) {
|
|
|
548
553
|
server: Type.Optional(Type.String()),
|
|
549
554
|
limit: Type.Optional(Type.Integer({ minimum: 1, maximum: MAX_PAGE_SIZE, description: "Tools to load (default 5)." })),
|
|
550
555
|
offset: Type.Optional(Type.Integer({ minimum: 0 })),
|
|
551
|
-
}, { anyOf: [{ required: ["query"] }, { required: ["enable"] }] }),
|
|
556
|
+
}, { anyOf: [{ type: "object", required: ["query"] }, { type: "object", required: ["enable"] }] }),
|
|
552
557
|
renderResult: renderMcpToolResult,
|
|
553
558
|
async execute(toolCallId, params, signal, _onUpdate, ctx) {
|
|
554
559
|
if (beforeExecute) {
|
|
@@ -582,6 +587,7 @@ function installMcpAdapter(pi, options) {
|
|
|
582
587
|
const ready = await getToolState(signal);
|
|
583
588
|
if ("content" in ready)
|
|
584
589
|
return ready;
|
|
590
|
+
const { runMcpScript } = await import("./mcp-code.js");
|
|
585
591
|
return runMcpScript(ready, params.code, params.timeoutMs ?? options.defaultScriptTimeoutMs, getPiTools, signal, toolCallId, beforeExecute ? (callSignal, operation) => beforeExecute(toolCallId, { ...ctx, signal: callSignal }, operation) : undefined);
|
|
586
592
|
},
|
|
587
593
|
});
|
|
@@ -607,6 +613,7 @@ function installMcpAdapter(pi, options) {
|
|
|
607
613
|
const ready = await getToolState(signal);
|
|
608
614
|
if ("content" in ready)
|
|
609
615
|
return ready;
|
|
616
|
+
const proxy = await import("./proxy-modes.js");
|
|
610
617
|
const present = async (result) => {
|
|
611
618
|
const guarded = await guardMcpOutput(result.content, resolveMcpOutputGuardOptions(ready.config.settings, ready.outputDirectory));
|
|
612
619
|
return { content: guarded.content, details: { ...result.details, ...guardedMcpDetails(guarded) } };
|
|
@@ -615,34 +622,34 @@ function installMcpAdapter(pi, options) {
|
|
|
615
622
|
? (callSignal, operation) => beforeExecute(toolCallId, { ...ctx, signal: callSignal }, operation)
|
|
616
623
|
: undefined;
|
|
617
624
|
if (params.server && ["list", "search", "describe", "instructions"].includes(params.action) && !ready.toolMetadata.has(params.server)) {
|
|
618
|
-
const discovery = await executeConnect(ready, params.server, signal);
|
|
625
|
+
const discovery = await proxy.executeConnect(ready, params.server, signal);
|
|
619
626
|
if (discovery.details.error)
|
|
620
627
|
return discovery;
|
|
621
628
|
syncToolSurface(ctx);
|
|
622
629
|
}
|
|
623
630
|
switch (params.action) {
|
|
624
|
-
case "ui-messages": return executeUiMessages(ready);
|
|
625
|
-
case "auth-start": return executeAuthStart(ready, params.server, signal);
|
|
631
|
+
case "ui-messages": return proxy.executeUiMessages(ready);
|
|
632
|
+
case "auth-start": return proxy.executeAuthStart(ready, params.server, signal);
|
|
626
633
|
case "auth-complete": {
|
|
627
634
|
const input = params.args?.redirectUrl ?? params.args?.code ?? params.args?.input;
|
|
628
635
|
if (input !== undefined && (typeof input !== "string" || input.trim().length === 0))
|
|
629
636
|
throw new Error("auth-complete requires a non-empty redirectUrl, code, or input");
|
|
630
|
-
return executeAuthComplete(ready, params.server, input, signal);
|
|
637
|
+
return proxy.executeAuthComplete(ready, params.server, input, signal);
|
|
631
638
|
}
|
|
632
|
-
case "call": return executeCall(ready, params.tool, params.args, params.server, getPiTools, signal, { toolCallId }, beforeDispatch);
|
|
639
|
+
case "call": return proxy.executeCall(ready, params.tool, params.args, params.server, getPiTools, signal, { toolCallId }, beforeDispatch);
|
|
633
640
|
case "connect": {
|
|
634
|
-
const result = await executeConnect(ready, params.server, signal, params.limit, params.offset);
|
|
641
|
+
const result = await proxy.executeConnect(ready, params.server, signal, params.limit, params.offset);
|
|
635
642
|
syncToolSurface(ctx);
|
|
636
643
|
return present(result);
|
|
637
644
|
}
|
|
638
|
-
case "describe": return present(executeDescribe(ready, params.tool, params.server));
|
|
639
|
-
case "instructions": return present(executeInstructions(ready, params.server));
|
|
640
|
-
case "search": return present(executeSearch(ready, params.query, params.server, params.includeSchemas, params.limit, params.offset));
|
|
641
|
-
case "list": return present(executeList(ready, params.server, params.limit, params.offset));
|
|
642
|
-
case "resources": return present(await executeResourceList(ready, params.server, params.limit, params.offset, signal));
|
|
643
|
-
case "read-resource": return executeResourceRead(ready, params.server, params.uri, signal, { toolCallId }, beforeDispatch);
|
|
645
|
+
case "describe": return present(proxy.executeDescribe(ready, params.tool, params.server));
|
|
646
|
+
case "instructions": return present(proxy.executeInstructions(ready, params.server));
|
|
647
|
+
case "search": return present(proxy.executeSearch(ready, params.query, params.server, params.includeSchemas, params.limit, params.offset));
|
|
648
|
+
case "list": return present(proxy.executeList(ready, params.server, params.limit, params.offset));
|
|
649
|
+
case "resources": return present(await (await import("./resource-tools.js")).executeResourceList(ready, params.server, params.limit, params.offset, signal));
|
|
650
|
+
case "read-resource": return (await import("./resource-tools.js")).executeResourceRead(ready, params.server, params.uri, signal, { toolCallId }, beforeDispatch);
|
|
644
651
|
case "read-result": return readMcpResult({ ref: params.ref, ...(params.path !== undefined ? { path: params.path } : {}), ...(params.fields ? { fields: params.fields } : {}), ...(params.offset !== undefined ? { offset: params.offset } : {}), ...(params.limit !== undefined ? { limit: params.limit } : {}) }, resolveMcpOutputGuardOptions(ready.config.settings, ready.outputDirectory));
|
|
645
|
-
case "status": return present(executeStatus(ready));
|
|
652
|
+
case "status": return present(proxy.executeStatus(ready));
|
|
646
653
|
}
|
|
647
654
|
},
|
|
648
655
|
});
|
package/dist/init.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
2
2
|
import type { McpExtensionState } from "./state.ts";
|
|
3
3
|
import { type McpAdapterOptions, type McpConfig } from "./types.ts";
|
|
4
|
-
import { type McpOAuthRuntime } from "./mcp-
|
|
4
|
+
import { type McpOAuthRuntime } from "./mcp-oauth-runtime.ts";
|
|
5
5
|
import { type McpRuntimeOwner } from "./runtime-owner.ts";
|
|
6
6
|
export declare function clearFailure(state: McpExtensionState, serverName: string): void;
|
|
7
7
|
export declare function recordFailure(state: McpExtensionState, serverName: string, message: string): void;
|
package/dist/init.js
CHANGED
|
@@ -10,7 +10,7 @@ import { formatMcpStatus, openUrl, parallelLimit, sanitizeTerminalText } from ".
|
|
|
10
10
|
import { logger } from "./logger.js";
|
|
11
11
|
import { throwIfAborted } from "./abort.js";
|
|
12
12
|
import { getAuthStorageOptions } from "./mcp-auth.js";
|
|
13
|
-
import { createOAuthRuntime, hasPendingAuth, shutdownOAuth } from "./mcp-
|
|
13
|
+
import { createOAuthRuntime, hasPendingAuth, shutdownOAuth } from "./mcp-oauth-runtime.js";
|
|
14
14
|
import { combineAbortSignals, createMcpRuntimeOwner, createOwnedUi, isAbortError, } from "./runtime-owner.js";
|
|
15
15
|
import { publishMcpStatusSnapshot } from "./mcp-status.js";
|
|
16
16
|
const FAILURE_BACKOFF_MS = 60 * 1000;
|
|
@@ -229,40 +229,48 @@ export async function initializeMcp(pi, ctx, owner = createMcpRuntimeOwner(), op
|
|
|
229
229
|
if (initialSignal?.aborted)
|
|
230
230
|
return state;
|
|
231
231
|
owner.throwIfInactive();
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
232
|
+
const startupCache = { entries: new Map() };
|
|
233
|
+
try {
|
|
234
|
+
for (const { name, definition, connection, error } of results) {
|
|
235
|
+
owner.throwIfInactive();
|
|
236
|
+
if (error || !connection) {
|
|
237
|
+
if (initialSignal?.aborted)
|
|
238
|
+
continue;
|
|
239
|
+
saveMetadataCacheUpdates(state, startupCache);
|
|
240
|
+
if (error)
|
|
241
|
+
recordFailure(state, name, error);
|
|
242
|
+
const displayError = sanitizeTerminalText(error ?? "Unknown connection failure");
|
|
243
|
+
if (ui) {
|
|
244
|
+
ui.notify(`MCP: Failed to connect to ${name}: ${displayError}`, "error");
|
|
245
|
+
}
|
|
246
|
+
console.error(`MCP: Failed to connect to ${name}: ${displayError}`);
|
|
236
247
|
continue;
|
|
237
|
-
if (error)
|
|
238
|
-
recordFailure(state, name, error);
|
|
239
|
-
const displayError = sanitizeTerminalText(error ?? "Unknown connection failure");
|
|
240
|
-
if (ui) {
|
|
241
|
-
ui.notify(`MCP: Failed to connect to ${name}: ${displayError}`, "error");
|
|
242
248
|
}
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
ui.notify(`MCP: ${name} - ${failedTools.length} tools skipped`, "warning");
|
|
249
|
+
const { metadata, failedTools } = buildToolMetadata(connection.tools, connection.resources, definition, name, prefix);
|
|
250
|
+
toolMetadata.set(name, metadata);
|
|
251
|
+
resourceCounts.set(name, connection.resources.length);
|
|
252
|
+
if (!connection.promptDiscoveryFailed) {
|
|
253
|
+
promptMetadata.set(name, reconstructPromptMetadata(name, connection.prompts ?? [], prefix, definition));
|
|
254
|
+
promptMetadataLive.add(name);
|
|
255
|
+
}
|
|
256
|
+
if (connection.instructions) {
|
|
257
|
+
serverInstructions.set(name, connection.instructions);
|
|
258
|
+
}
|
|
259
|
+
else {
|
|
260
|
+
serverInstructions.delete(name);
|
|
261
|
+
}
|
|
262
|
+
collectMetadataCacheUpdate(state, name, startupCache);
|
|
263
|
+
notifyToolMetadataUpdated(state, name, "startup");
|
|
264
|
+
markKeepAliveAfterConnect(state, name);
|
|
265
|
+
if (failedTools.length > 0 && ui) {
|
|
266
|
+
saveMetadataCacheUpdates(state, startupCache);
|
|
267
|
+
ui.notify(`MCP: ${name} - ${failedTools.length} tools skipped`, "warning");
|
|
268
|
+
}
|
|
264
269
|
}
|
|
265
270
|
}
|
|
271
|
+
finally {
|
|
272
|
+
saveMetadataCacheUpdates(state, startupCache);
|
|
273
|
+
}
|
|
266
274
|
const connectedCount = results.filter(r => r.connection).length;
|
|
267
275
|
const failedCount = results.filter(r => r.error).length;
|
|
268
276
|
if (ui && connectedCount > 0) {
|
|
@@ -382,7 +390,7 @@ export function updateServerMetadata(state, serverName) {
|
|
|
382
390
|
state.serverInstructions?.delete(serverName);
|
|
383
391
|
}
|
|
384
392
|
}
|
|
385
|
-
|
|
393
|
+
function collectMetadataCacheUpdate(state, serverName, updates, options = {}) {
|
|
386
394
|
const connection = state.manager.getConnection(serverName);
|
|
387
395
|
if (!connection || connection.status !== "connected")
|
|
388
396
|
return;
|
|
@@ -390,8 +398,9 @@ export function updateMetadataCache(state, serverName, options = {}) {
|
|
|
390
398
|
if (!definition || isServerDisabled(definition))
|
|
391
399
|
return;
|
|
392
400
|
const configHash = computeServerHash(definition);
|
|
393
|
-
|
|
394
|
-
|
|
401
|
+
if (updates.existing === undefined)
|
|
402
|
+
updates.existing = loadMetadataCache(state.metadataCacheEnabled);
|
|
403
|
+
const existingEntry = updates.existing?.servers?.[serverName];
|
|
395
404
|
const tools = serializeTools(connection.tools);
|
|
396
405
|
let resources = definition.exposeResources === false ? [] : serializeResources(connection.resources);
|
|
397
406
|
const prompts = connection.promptDiscoveryFailed
|
|
@@ -412,7 +421,20 @@ export function updateMetadataCache(state, serverName, options = {}) {
|
|
|
412
421
|
...(connection.instructions !== undefined ? { instructions: connection.instructions } : {}),
|
|
413
422
|
cachedAt: Date.now(),
|
|
414
423
|
};
|
|
415
|
-
|
|
424
|
+
updates.entries.set(serverName, entry);
|
|
425
|
+
}
|
|
426
|
+
function saveMetadataCacheUpdates(state, updates) {
|
|
427
|
+
if (updates.entries.size === 0)
|
|
428
|
+
return;
|
|
429
|
+
const servers = Object.fromEntries(updates.entries);
|
|
430
|
+
updates.entries.clear();
|
|
431
|
+
delete updates.existing;
|
|
432
|
+
saveMetadataCache({ version: 1, servers }, state.metadataCacheEnabled, Object.keys(state.config.mcpServers));
|
|
433
|
+
}
|
|
434
|
+
export function updateMetadataCache(state, serverName, options = {}) {
|
|
435
|
+
const updates = { entries: new Map() };
|
|
436
|
+
collectMetadataCacheUpdate(state, serverName, updates, options);
|
|
437
|
+
saveMetadataCacheUpdates(state, updates);
|
|
416
438
|
}
|
|
417
439
|
export function notifyToolMetadataUpdated(state, serverName, reason) {
|
|
418
440
|
try {
|
|
@@ -430,11 +452,17 @@ export function notifyToolMetadataUpdated(state, serverName, reason) {
|
|
|
430
452
|
}
|
|
431
453
|
}
|
|
432
454
|
export function flushMetadataCache(state) {
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
455
|
+
const updates = { entries: new Map() };
|
|
456
|
+
try {
|
|
457
|
+
for (const [name, connection] of state.manager.getAllConnections()) {
|
|
458
|
+
if (connection.status === "connected") {
|
|
459
|
+
collectMetadataCacheUpdate(state, name, updates);
|
|
460
|
+
}
|
|
436
461
|
}
|
|
437
462
|
}
|
|
463
|
+
finally {
|
|
464
|
+
saveMetadataCacheUpdates(state, updates);
|
|
465
|
+
}
|
|
438
466
|
}
|
|
439
467
|
export function updateStatusBar(state) {
|
|
440
468
|
publishMcpStatusSnapshot(state);
|
package/dist/lifecycle.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { type ServerDefinition } from "./types.ts";
|
|
2
2
|
import type { McpServerManager } from "./server-manager.ts";
|
|
3
|
-
import { hasPendingAuth } from "./mcp-
|
|
3
|
+
import { hasPendingAuth } from "./mcp-oauth-runtime.ts";
|
|
4
4
|
export type ReconnectCallback = (serverName: string) => void;
|
|
5
5
|
export type ReconnectFailureCallback = (serverName: string, error: unknown) => void;
|
|
6
6
|
export declare class McpLifecycleManager {
|
package/dist/lifecycle.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { isServerDisabled } from "./types.js";
|
|
2
|
-
import { hasPendingAuth } from "./mcp-
|
|
2
|
+
import { hasPendingAuth } from "./mcp-oauth-runtime.js";
|
|
3
3
|
import { logger } from "./logger.js";
|
|
4
4
|
import { formatTerminalError, sanitizeTerminalText } from "./utils.js";
|
|
5
5
|
export class McpLifecycleManager {
|
package/dist/mcp-auth-flow.d.ts
CHANGED
|
@@ -5,36 +5,14 @@
|
|
|
5
5
|
*/
|
|
6
6
|
import { UnauthorizedError, InsufficientScopeError, SdkHttpError } from "@modelcontextprotocol/client";
|
|
7
7
|
import { type McpOAuthConfig } from "./mcp-oauth-provider.ts";
|
|
8
|
-
import { type AuthStorageOptions } from "./mcp-auth.ts";
|
|
9
8
|
import { type ServerEntry } from "./types.ts";
|
|
10
|
-
|
|
11
|
-
export type AuthStatus
|
|
12
|
-
export interface McpOAuthRuntime {
|
|
13
|
-
readonly signal: AbortSignal;
|
|
14
|
-
}
|
|
15
|
-
export interface AuthenticateOptions {
|
|
16
|
-
onAuthorizationUrl?: (authorizationUrl: string) => void | Promise<void>;
|
|
17
|
-
authStorageOptions?: AuthStorageOptions;
|
|
18
|
-
signal?: AbortSignal;
|
|
19
|
-
runtime?: McpOAuthRuntime;
|
|
20
|
-
}
|
|
21
|
-
export declare function createOAuthRuntime(signal?: AbortSignal): McpOAuthRuntime;
|
|
22
|
-
type OAuthRequest = {
|
|
23
|
-
serverName: string;
|
|
24
|
-
serverUrl: string;
|
|
25
|
-
requestedScope?: string;
|
|
26
|
-
challenge?: {
|
|
27
|
-
requiredScope?: string;
|
|
28
|
-
resourceMetadataUrl?: URL;
|
|
29
|
-
};
|
|
30
|
-
issuer?: string;
|
|
31
|
-
};
|
|
9
|
+
import { type AuthStatus, type AuthenticateOptions, type McpOAuthRuntime, type OAuthRequest } from "./mcp-oauth-runtime.ts";
|
|
10
|
+
export { createOAuthRuntime, hasPendingAuth, initializeOAuth, shutdownOAuth, supportsOAuth, type AuthStatus, type AuthenticateOptions, type McpOAuthRuntime, } from "./mcp-oauth-runtime.ts";
|
|
32
11
|
export declare function isOAuthChallenge(error: unknown): error is InsufficientScopeError | UnauthorizedError | SdkHttpError;
|
|
33
12
|
export declare function getOAuthRequest(serverName: string, serverUrl: string, storageBase: string, runtime?: McpOAuthRuntime): OAuthRequest | undefined;
|
|
34
13
|
/** Observe native challenges only; no credential reads or auth work here. */
|
|
35
14
|
export declare function recordOAuthChallenge(serverName: string, serverUrl: string, storageBase: string, error: unknown, runtime?: McpOAuthRuntime): void;
|
|
36
15
|
export declare function bindOAuthRequestIssuer(serverName: string, serverUrl: string, storageBase: string, issuer: string, runtime?: McpOAuthRuntime): void;
|
|
37
|
-
export declare function hasPendingAuth(serverName: string, options?: AuthStorageOptions, runtime?: McpOAuthRuntime): boolean;
|
|
38
16
|
/**
|
|
39
17
|
* Extract OAuth configuration from a ServerEntry.
|
|
40
18
|
*/
|
|
@@ -86,22 +64,3 @@ export declare function authenticate(serverName: string, serverUrl: string, defi
|
|
|
86
64
|
* @param serverName - The name of the MCP server
|
|
87
65
|
*/
|
|
88
66
|
export declare function removeAuth(serverName: string, options?: AuthenticateOptions): Promise<void>;
|
|
89
|
-
/**
|
|
90
|
-
* Check if OAuth is supported for a server configuration.
|
|
91
|
-
* OAuth is supported for HTTP servers unless explicitly disabled.
|
|
92
|
-
*
|
|
93
|
-
* @param definition - The server definition
|
|
94
|
-
* @returns True if OAuth is supported
|
|
95
|
-
*/
|
|
96
|
-
export declare function supportsOAuth(definition: ServerEntry): boolean;
|
|
97
|
-
/**
|
|
98
|
-
* Initialize the OAuth system on startup.
|
|
99
|
-
* OAuth callback binding is lazy and starts from startAuth() only.
|
|
100
|
-
*/
|
|
101
|
-
export declare function initializeOAuth(runtimeOrSignal?: McpOAuthRuntime | AbortSignal): Promise<McpOAuthRuntime>;
|
|
102
|
-
/**
|
|
103
|
-
* Shutdown one OAuth runtime. The callback server remains process-shared while
|
|
104
|
-
* another runtime has pending/reserved callback state or is still active.
|
|
105
|
-
*/
|
|
106
|
-
export declare function shutdownOAuth(runtime?: McpOAuthRuntime): Promise<void>;
|
|
107
|
-
export {};
|