@bastani/atomic 0.9.25-alpha.3 → 0.9.25-alpha.5
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 +34 -0
- package/dist/builtin/intercom/package.json +1 -1
- package/dist/builtin/mcp/CHANGELOG.md +30 -0
- package/dist/builtin/mcp/OAUTH.md +59 -269
- package/dist/builtin/mcp/README.md +58 -345
- package/dist/builtin/mcp/index.bundle.mjs +4 -28073
- package/dist/builtin/mcp/native-client-licenses/LICENSES/modelcontextprotocol-typescript-sdk.txt +21 -0
- package/dist/builtin/mcp/native-client-licenses/NOTICE +30 -0
- package/dist/builtin/mcp/package.json +4 -25
- package/dist/builtin/subagents/package.json +1 -1
- package/dist/builtin/web-access/package.json +1 -1
- package/dist/builtin/workflows/CHANGELOG.md +6 -0
- package/dist/builtin/workflows/package.json +1 -1
- package/dist/builtin/workflows/src/extension/index.bundle.mjs +18 -3
- package/dist/cli/args.d.ts.map +1 -1
- package/dist/cli/args.js +2 -1
- package/dist/cli/args.js.map +1 -1
- package/dist/core/agent-session-extension-bindings.d.ts.map +1 -1
- package/dist/core/agent-session-extension-bindings.js +87 -5
- package/dist/core/agent-session-extension-bindings.js.map +1 -1
- package/dist/core/agent-session-tool-registry.d.ts.map +1 -1
- package/dist/core/agent-session-tool-registry.js +6 -3
- package/dist/core/agent-session-tool-registry.js.map +1 -1
- package/dist/core/extensions/context-types.d.ts +3 -1
- package/dist/core/extensions/context-types.d.ts.map +1 -1
- package/dist/core/extensions/context-types.js.map +1 -1
- package/dist/core/extensions/loader-api.d.ts.map +1 -1
- package/dist/core/extensions/loader-api.js +4 -0
- package/dist/core/extensions/loader-api.js.map +1 -1
- package/dist/core/extensions/loader-virtual-modules.d.ts.map +1 -1
- package/dist/core/extensions/loader-virtual-modules.js +3 -0
- package/dist/core/extensions/loader-virtual-modules.js.map +1 -1
- package/dist/core/mcp-child-policy.d.ts +11 -0
- package/dist/core/mcp-child-policy.d.ts.map +1 -0
- package/dist/core/mcp-child-policy.js +31 -0
- package/dist/core/mcp-child-policy.js.map +1 -0
- package/dist/core/mcp-servers.d.ts +32 -26
- package/dist/core/mcp-servers.d.ts.map +1 -1
- package/dist/core/mcp-servers.js +148 -1
- package/dist/core/mcp-servers.js.map +1 -1
- package/dist/core/package-manager-manifest.js.map +1 -1
- package/dist/core/package-manager-mcp-servers.js +1 -1
- package/dist/core/package-manager-mcp-servers.js.map +1 -1
- package/dist/core/sdk.js +10 -1
- package/dist/core/sdk.js.map +1 -1
- package/dist/core/tools/truncate.d.ts +8 -0
- package/dist/core/tools/truncate.d.ts.map +1 -1
- package/dist/core/tools/truncate.js +24 -0
- package/dist/core/tools/truncate.js.map +1 -1
- package/dist/extensions/mcp/cli.d.ts +21 -0
- package/dist/extensions/mcp/cli.d.ts.map +1 -0
- package/dist/extensions/mcp/cli.js +500 -0
- package/dist/extensions/mcp/cli.js.map +1 -0
- package/dist/extensions/mcp/cli.lazy.d.ts +3 -0
- package/dist/extensions/mcp/cli.lazy.d.ts.map +1 -0
- package/dist/extensions/mcp/cli.lazy.js +3 -0
- package/dist/extensions/mcp/cli.lazy.js.map +1 -0
- package/dist/extensions/mcp/client/auth-provider.d.ts +15 -0
- package/dist/extensions/mcp/client/auth-provider.d.ts.map +1 -0
- package/dist/extensions/mcp/client/auth-provider.js +2 -0
- package/dist/extensions/mcp/client/auth-provider.js.map +1 -0
- package/dist/extensions/mcp/client/client.d.ts +89 -0
- package/dist/extensions/mcp/client/client.d.ts.map +1 -0
- package/dist/extensions/mcp/client/client.js +457 -0
- package/dist/extensions/mcp/client/client.js.map +1 -0
- package/dist/extensions/mcp/client/index.d.ts +9 -0
- package/dist/extensions/mcp/client/index.d.ts.map +1 -0
- package/dist/extensions/mcp/client/index.js +7 -0
- package/dist/extensions/mcp/client/index.js.map +1 -0
- package/dist/extensions/mcp/client/oauth/callback.d.ts +42 -0
- package/dist/extensions/mcp/client/oauth/callback.d.ts.map +1 -0
- package/dist/extensions/mcp/client/oauth/callback.js +106 -0
- package/dist/extensions/mcp/client/oauth/callback.js.map +1 -0
- package/dist/extensions/mcp/client/oauth/discovery.d.ts +27 -0
- package/dist/extensions/mcp/client/oauth/discovery.d.ts.map +1 -0
- package/dist/extensions/mcp/client/oauth/discovery.js +153 -0
- package/dist/extensions/mcp/client/oauth/discovery.js.map +1 -0
- package/dist/extensions/mcp/client/oauth/errors.d.ts +24 -0
- package/dist/extensions/mcp/client/oauth/errors.d.ts.map +1 -0
- package/dist/extensions/mcp/client/oauth/errors.js +46 -0
- package/dist/extensions/mcp/client/oauth/errors.js.map +1 -0
- package/dist/extensions/mcp/client/oauth/flow.d.ts +82 -0
- package/dist/extensions/mcp/client/oauth/flow.d.ts.map +1 -0
- package/dist/extensions/mcp/client/oauth/flow.js +292 -0
- package/dist/extensions/mcp/client/oauth/flow.js.map +1 -0
- package/dist/extensions/mcp/client/oauth/index.d.ts +7 -0
- package/dist/extensions/mcp/client/oauth/index.d.ts.map +1 -0
- package/dist/extensions/mcp/client/oauth/index.js +6 -0
- package/dist/extensions/mcp/client/oauth/index.js.map +1 -0
- package/dist/extensions/mcp/client/oauth/provider.d.ts +59 -0
- package/dist/extensions/mcp/client/oauth/provider.d.ts.map +1 -0
- package/dist/extensions/mcp/client/oauth/provider.js +116 -0
- package/dist/extensions/mcp/client/oauth/provider.js.map +1 -0
- package/dist/extensions/mcp/client/oauth/types.d.ts +77 -0
- package/dist/extensions/mcp/client/oauth/types.d.ts.map +1 -0
- package/dist/extensions/mcp/client/oauth/types.js +109 -0
- package/dist/extensions/mcp/client/oauth/types.js.map +1 -0
- package/dist/extensions/mcp/client/protocol/content.d.ts +82 -0
- package/dist/extensions/mcp/client/protocol/content.d.ts.map +1 -0
- package/dist/extensions/mcp/client/protocol/content.js +41 -0
- package/dist/extensions/mcp/client/protocol/content.js.map +1 -0
- package/dist/extensions/mcp/client/protocol/jsonrpc.d.ts +59 -0
- package/dist/extensions/mcp/client/protocol/jsonrpc.d.ts.map +1 -0
- package/dist/extensions/mcp/client/protocol/jsonrpc.js +67 -0
- package/dist/extensions/mcp/client/protocol/jsonrpc.js.map +1 -0
- package/dist/extensions/mcp/client/protocol/types.d.ts +123 -0
- package/dist/extensions/mcp/client/protocol/types.d.ts.map +1 -0
- package/dist/extensions/mcp/client/protocol/types.js +7 -0
- package/dist/extensions/mcp/client/protocol/types.js.map +1 -0
- package/dist/extensions/mcp/client/testing/index.d.ts +2 -0
- package/dist/extensions/mcp/client/testing/index.d.ts.map +1 -0
- package/dist/extensions/mcp/client/testing/index.js +2 -0
- package/dist/extensions/mcp/client/testing/index.js.map +1 -0
- package/dist/extensions/mcp/client/transports/in-memory.d.ts +19 -0
- package/dist/extensions/mcp/client/transports/in-memory.d.ts.map +1 -0
- package/dist/extensions/mcp/client/transports/in-memory.js +50 -0
- package/dist/extensions/mcp/client/transports/in-memory.js.map +1 -0
- package/dist/extensions/mcp/client/transports/stdio.d.ts +32 -0
- package/dist/extensions/mcp/client/transports/stdio.d.ts.map +1 -0
- package/dist/extensions/mcp/client/transports/stdio.js +201 -0
- package/dist/extensions/mcp/client/transports/stdio.js.map +1 -0
- package/dist/extensions/mcp/client/transports/streamable-http.d.ts +96 -0
- package/dist/extensions/mcp/client/transports/streamable-http.d.ts.map +1 -0
- package/dist/extensions/mcp/client/transports/streamable-http.js +456 -0
- package/dist/extensions/mcp/client/transports/streamable-http.js.map +1 -0
- package/dist/extensions/mcp/client/transports/transport.d.ts +28 -0
- package/dist/extensions/mcp/client/transports/transport.d.ts.map +1 -0
- package/dist/extensions/mcp/client/transports/transport.js +38 -0
- package/dist/extensions/mcp/client/transports/transport.js.map +1 -0
- package/dist/extensions/mcp/config.d.ts +72 -0
- package/dist/extensions/mcp/config.d.ts.map +1 -0
- package/dist/extensions/mcp/config.js +158 -0
- package/dist/extensions/mcp/config.js.map +1 -0
- package/dist/extensions/mcp/index.d.ts +76 -0
- package/dist/extensions/mcp/index.d.ts.map +1 -0
- package/dist/extensions/mcp/index.js +1119 -0
- package/dist/extensions/mcp/index.js.map +1 -0
- package/dist/extensions/mcp/log.d.ts +16 -0
- package/dist/extensions/mcp/log.d.ts.map +1 -0
- package/dist/extensions/mcp/log.js +66 -0
- package/dist/extensions/mcp/log.js.map +1 -0
- package/dist/extensions/mcp/oauth.d.ts +103 -0
- package/dist/extensions/mcp/oauth.d.ts.map +1 -0
- package/dist/extensions/mcp/oauth.js +382 -0
- package/dist/extensions/mcp/oauth.js.map +1 -0
- package/dist/extensions/mcp/resources.d.ts +43 -0
- package/dist/extensions/mcp/resources.d.ts.map +1 -0
- package/dist/extensions/mcp/resources.js +262 -0
- package/dist/extensions/mcp/resources.js.map +1 -0
- package/dist/extensions/mcp/runtime.d.ts +104 -0
- package/dist/extensions/mcp/runtime.d.ts.map +1 -0
- package/dist/extensions/mcp/runtime.js +546 -0
- package/dist/extensions/mcp/runtime.js.map +1 -0
- package/dist/extensions/mcp/runtime.lazy.d.ts +3 -0
- package/dist/extensions/mcp/runtime.lazy.d.ts.map +1 -0
- package/dist/extensions/mcp/runtime.lazy.js +3 -0
- package/dist/extensions/mcp/runtime.lazy.js.map +1 -0
- package/dist/extensions/mcp/tools.d.ts +84 -0
- package/dist/extensions/mcp/tools.d.ts.map +1 -0
- package/dist/extensions/mcp/tools.js +251 -0
- package/dist/extensions/mcp/tools.js.map +1 -0
- package/dist/extensions/mcp/ui.d.ts +60 -0
- package/dist/extensions/mcp/ui.d.ts.map +1 -0
- package/dist/extensions/mcp/ui.js +175 -0
- package/dist/extensions/mcp/ui.js.map +1 -0
- package/dist/index.d.ts +5 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -1
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +7 -0
- package/dist/main.js.map +1 -1
- package/dist/modes/interactive/interactive-slash-commands.js +23 -8
- package/dist/modes/interactive/interactive-slash-commands.js.map +1 -1
- package/docs/extensions/api-reference.md +3 -3
- package/docs/extensions.md +8 -8
- package/docs/mcp-servers.md +153 -79
- package/docs/packages/authoring.md +2 -2
- package/docs/sdk.md +1 -1
- package/docs/sessions.md +1 -1
- package/npm-shrinkwrap.json +32 -32
- package/package.json +3 -3
- package/dist/builtin/mcp/app-bridge.bundle.js +0 -67
- package/dist/builtin/mcp/xdg-open +0 -1267
|
@@ -0,0 +1,546 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The part of the MCP integration that talks to servers: connections, transports, and OAuth
|
|
3
|
+
* sign-in. It pulls in the MCP client, so index.ts loads it through runtime.lazy.ts only when a
|
|
4
|
+
* server is configured.
|
|
5
|
+
*/
|
|
6
|
+
import { homedir } from "node:os";
|
|
7
|
+
import { basename, join, resolve } from "node:path";
|
|
8
|
+
import { pathToFileURL } from "node:url";
|
|
9
|
+
import { VERSION } from "../../config.js";
|
|
10
|
+
import { resolveMcpServerUrl } from "../../core/mcp-servers.js";
|
|
11
|
+
import { resolveConfigValueOrThrow, resolveHeadersOrThrow } from "../../core/resolve-config-value.js";
|
|
12
|
+
import { JSON_RPC_ERROR_CODES, McpAuthRequiredError, McpClient, McpError, McpHttpError, McpSessionExpiredError, StdioTransport, StreamableHttpTransport, } from "./client/index.js";
|
|
13
|
+
import { McpOAuthAuthorizationRequiredError } from "./client/oauth/index.js";
|
|
14
|
+
import { createMcpAuthProvider, } from "./oauth.js";
|
|
15
|
+
import { isMcpAppResource } from "./resources.js";
|
|
16
|
+
export { McpServerLog } from "./log.js";
|
|
17
|
+
export { McpOAuthCredentialStore, McpSignInCancelledError, signInMcpServer } from "./oauth.js";
|
|
18
|
+
const DEFAULT_TIMEOUT_SECONDS = 60;
|
|
19
|
+
const STDERR_TAIL_CHARS = 2_000;
|
|
20
|
+
/** Delays between attempts to connect to an HTTP server that failed with a transient error. */
|
|
21
|
+
const CONNECT_RETRY_DELAYS_MS = [250, 1_000];
|
|
22
|
+
/** Give canceled setup time to retire, without letting a broken transport block session cleanup. */
|
|
23
|
+
const OPENING_RETIREMENT_TIMEOUT_MS = 1_000;
|
|
24
|
+
async function boundedRetirement(retirement) {
|
|
25
|
+
let timer;
|
|
26
|
+
try {
|
|
27
|
+
await Promise.race([
|
|
28
|
+
retirement.catch(() => undefined),
|
|
29
|
+
new Promise((resolve) => {
|
|
30
|
+
timer = setTimeout(resolve, OPENING_RETIREMENT_TIMEOUT_MS);
|
|
31
|
+
}),
|
|
32
|
+
]);
|
|
33
|
+
}
|
|
34
|
+
finally {
|
|
35
|
+
clearTimeout(timer);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
function errorMessage(error) {
|
|
39
|
+
return error instanceof Error ? error.message : String(error);
|
|
40
|
+
}
|
|
41
|
+
/** Network failures and overloaded or restarting servers, which are worth another attempt. */
|
|
42
|
+
function isTransientError(error) {
|
|
43
|
+
if (error instanceof McpHttpError) {
|
|
44
|
+
return error.status === 408 || error.status === 429 || (error.status >= 500 && error.status !== 501);
|
|
45
|
+
}
|
|
46
|
+
return error instanceof TypeError;
|
|
47
|
+
}
|
|
48
|
+
function providerAuthName(entry) {
|
|
49
|
+
return "url" in entry.config ? entry.config.auth?.provider : undefined;
|
|
50
|
+
}
|
|
51
|
+
function validateProviderDestination(serverUrl) {
|
|
52
|
+
const url = new URL(serverUrl);
|
|
53
|
+
if (url.protocol !== "https:" &&
|
|
54
|
+
!(url.protocol === "http:" && ["localhost", "127.0.0.1", "[::1]"].includes(url.hostname))) {
|
|
55
|
+
throw new Error("MCP provider credentials require HTTPS or exact loopback HTTP");
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
/** A provider token is granted to this configured origin, never to a redirect destination. */
|
|
59
|
+
function providerFetch(serverUrl) {
|
|
60
|
+
const origin = new URL(serverUrl).origin;
|
|
61
|
+
return async (input, init) => {
|
|
62
|
+
let url = new URL(input);
|
|
63
|
+
let request = { ...init, redirect: "manual" };
|
|
64
|
+
for (let redirects = 0;; redirects++) {
|
|
65
|
+
if (url.origin !== origin)
|
|
66
|
+
throw new Error("MCP provider request cannot cross origins");
|
|
67
|
+
const response = await fetch(url, request);
|
|
68
|
+
const location = response.headers.get("location");
|
|
69
|
+
if (![301, 302, 303, 307, 308].includes(response.status) || !location)
|
|
70
|
+
return response;
|
|
71
|
+
await response.body?.cancel();
|
|
72
|
+
if (redirects >= 10)
|
|
73
|
+
throw new Error("Too many MCP provider redirects");
|
|
74
|
+
url = new URL(location, url);
|
|
75
|
+
const method = request.method?.toUpperCase() ?? "GET";
|
|
76
|
+
if ((response.status === 303 && method !== "HEAD") ||
|
|
77
|
+
([301, 302].includes(response.status) && method === "POST")) {
|
|
78
|
+
const headers = new Headers(request.headers);
|
|
79
|
+
for (const header of [
|
|
80
|
+
"content-type",
|
|
81
|
+
"content-length",
|
|
82
|
+
"content-encoding",
|
|
83
|
+
"content-language",
|
|
84
|
+
"content-location",
|
|
85
|
+
])
|
|
86
|
+
headers.delete(header);
|
|
87
|
+
request = { ...request, method: "GET", body: undefined, headers };
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
function signInRequiredMessage(entry) {
|
|
93
|
+
const provider = providerAuthName(entry);
|
|
94
|
+
return `MCP server "${entry.name}" requires sign-in. Run ${provider ? `/login ${provider}` : "/mcp"} to sign in.`;
|
|
95
|
+
}
|
|
96
|
+
/** HTTP servers authenticate with OAuth unless they supply Authorization or a provider. */
|
|
97
|
+
function usesOAuth(entry) {
|
|
98
|
+
const { config } = entry;
|
|
99
|
+
if (!("url" in config) || config.auth)
|
|
100
|
+
return false;
|
|
101
|
+
return !Object.keys(config.headers ?? {}).some((header) => header.toLowerCase() === "authorization");
|
|
102
|
+
}
|
|
103
|
+
/** `~` and `~/…` (also `~\…` on Windows) name the home directory, like in a shell. */
|
|
104
|
+
function expandHome(value) {
|
|
105
|
+
if (value === "~")
|
|
106
|
+
return homedir();
|
|
107
|
+
if (value.startsWith("~/") || (process.platform === "win32" && value.startsWith("~\\"))) {
|
|
108
|
+
return join(homedir(), value.slice(2));
|
|
109
|
+
}
|
|
110
|
+
return value;
|
|
111
|
+
}
|
|
112
|
+
export function createDefaultTransport(entry, cwd, authProvider) {
|
|
113
|
+
const { config, name } = entry;
|
|
114
|
+
if ("url" in config) {
|
|
115
|
+
const url = resolveMcpServerUrl(config.url);
|
|
116
|
+
if (config.auth)
|
|
117
|
+
validateProviderDestination(url);
|
|
118
|
+
return new StreamableHttpTransport({
|
|
119
|
+
url,
|
|
120
|
+
headers: resolveHeadersOrThrow(config.headers, `MCP server "${name}"`),
|
|
121
|
+
authProvider,
|
|
122
|
+
fetch: config.auth ? providerFetch(url) : undefined,
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
if (!config.command)
|
|
126
|
+
throw new Error(`MCP server "${name}" has no command or url`);
|
|
127
|
+
const env = {};
|
|
128
|
+
for (const [key, value] of Object.entries(config.env ?? {})) {
|
|
129
|
+
env[key] = resolveConfigValueOrThrow(value, `MCP server "${name}" env "${key}"`);
|
|
130
|
+
}
|
|
131
|
+
return new StdioTransport({
|
|
132
|
+
command: expandHome(config.command),
|
|
133
|
+
args: config.args?.map(expandHome),
|
|
134
|
+
cwd: resolve(cwd, expandHome(config.cwd ?? ".")),
|
|
135
|
+
env,
|
|
136
|
+
stderr: "pipe",
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
/** Servers that do not implement `resources/templates/list` have no templates. */
|
|
140
|
+
async function withoutTemplates(list, empty) {
|
|
141
|
+
try {
|
|
142
|
+
return await list();
|
|
143
|
+
}
|
|
144
|
+
catch (error) {
|
|
145
|
+
if (error instanceof McpError && error.code === JSON_RPC_ERROR_CODES.methodNotFound)
|
|
146
|
+
return empty;
|
|
147
|
+
throw error;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
function listTemplates(client, options = {}) {
|
|
151
|
+
return withoutTemplates(() => client.listResourceTemplates(options), []);
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Resources and templates at connect time, for the counts in `/mcp` and `pi mcp list`. A server
|
|
155
|
+
* whose lists fail still connects: the resource tools list and read its resources on demand.
|
|
156
|
+
*/
|
|
157
|
+
async function fetchResources(client) {
|
|
158
|
+
const [resources, resourceTemplates] = await Promise.all([
|
|
159
|
+
client.listResources().catch(() => []),
|
|
160
|
+
listTemplates(client).catch(() => []),
|
|
161
|
+
]);
|
|
162
|
+
return {
|
|
163
|
+
resources: resources.filter((resource) => !isMcpAppResource(resource)),
|
|
164
|
+
resourceTemplates: resourceTemplates.filter((template) => !isMcpAppResource(template)),
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
/** One configured server. Reconnects lazily when a call finds the connection gone. */
|
|
168
|
+
export class McpServerConnection {
|
|
169
|
+
entry;
|
|
170
|
+
state = "connecting";
|
|
171
|
+
error;
|
|
172
|
+
tools = [];
|
|
173
|
+
/**
|
|
174
|
+
* Whether the server offers resources. The lists below are what it listed at the last connect or
|
|
175
|
+
* change, without MCP App resources.
|
|
176
|
+
*/
|
|
177
|
+
hasResources = false;
|
|
178
|
+
resources = [];
|
|
179
|
+
resourceTemplates = [];
|
|
180
|
+
/** Server instructions from `initialize`, describing its tools as a group. */
|
|
181
|
+
instructions;
|
|
182
|
+
/** Last OAuth challenge from the server; sign-in uses its resource metadata URL and scope. */
|
|
183
|
+
challenge;
|
|
184
|
+
client;
|
|
185
|
+
opening;
|
|
186
|
+
initializingClient;
|
|
187
|
+
initializingTransport;
|
|
188
|
+
closing;
|
|
189
|
+
activeRequests = new Map();
|
|
190
|
+
retiringClients = new Map();
|
|
191
|
+
closed = false;
|
|
192
|
+
/** Stderr of the last stdio server that failed to connect. */
|
|
193
|
+
stderrTail;
|
|
194
|
+
cwd;
|
|
195
|
+
createTransport;
|
|
196
|
+
authProvider;
|
|
197
|
+
onTools;
|
|
198
|
+
onChange;
|
|
199
|
+
log;
|
|
200
|
+
constructor(options) {
|
|
201
|
+
const config = options.entry.config;
|
|
202
|
+
// Resolve once so the credential key, destination check, and transport use the same URL.
|
|
203
|
+
this.entry =
|
|
204
|
+
"url" in config
|
|
205
|
+
? { ...options.entry, config: { ...config, url: resolveMcpServerUrl(config.url) } }
|
|
206
|
+
: options.entry;
|
|
207
|
+
this.cwd = options.cwd;
|
|
208
|
+
this.createTransport = options.createTransport;
|
|
209
|
+
this.onTools = options.onTools;
|
|
210
|
+
this.onChange = options.onChange;
|
|
211
|
+
this.log = options.log;
|
|
212
|
+
const url = this.oauthUrl;
|
|
213
|
+
const provider = providerAuthName(this.entry);
|
|
214
|
+
if (provider && "url" in this.entry.config)
|
|
215
|
+
validateProviderDestination(this.entry.config.url);
|
|
216
|
+
this.authProvider = url
|
|
217
|
+
? createMcpAuthProvider({
|
|
218
|
+
serverUrl: url,
|
|
219
|
+
store: options.credentials.forServer(url),
|
|
220
|
+
settings: () => this.oauthSettings(),
|
|
221
|
+
onChallenge: (challenge) => {
|
|
222
|
+
this.challenge = challenge;
|
|
223
|
+
},
|
|
224
|
+
})
|
|
225
|
+
: provider
|
|
226
|
+
? // Read on every request, so the provider's refreshes apply; MCP stores no copy.
|
|
227
|
+
{ token: async () => options.providerToken?.(provider), settled: async () => { } }
|
|
228
|
+
: undefined;
|
|
229
|
+
}
|
|
230
|
+
get name() {
|
|
231
|
+
return this.entry.name;
|
|
232
|
+
}
|
|
233
|
+
get timeoutMs() {
|
|
234
|
+
return (this.entry.config.timeout ?? DEFAULT_TIMEOUT_SECONDS) * 1000;
|
|
235
|
+
}
|
|
236
|
+
/** Server URL when the server authenticates with OAuth. */
|
|
237
|
+
get oauthUrl() {
|
|
238
|
+
return "url" in this.entry.config && usesOAuth(this.entry) ? this.entry.config.url : undefined;
|
|
239
|
+
}
|
|
240
|
+
oauthSettings() {
|
|
241
|
+
const oauth = "url" in this.entry.config ? this.entry.config.oauth : undefined;
|
|
242
|
+
if (!oauth)
|
|
243
|
+
return {};
|
|
244
|
+
return {
|
|
245
|
+
clientId: oauth.clientId,
|
|
246
|
+
clientSecret: oauth.clientSecret === undefined
|
|
247
|
+
? undefined
|
|
248
|
+
: resolveConfigValueOrThrow(oauth.clientSecret, `MCP server "${this.entry.name}" oauth.clientSecret`),
|
|
249
|
+
callbackPort: oauth.callbackPort,
|
|
250
|
+
callbackUrl: oauth.callbackUrl,
|
|
251
|
+
scope: oauth.scope,
|
|
252
|
+
clientName: oauth.clientName,
|
|
253
|
+
authServerMetadataUrl: oauth.authServerMetadataUrl ? new URL(oauth.authServerMetadataUrl) : undefined,
|
|
254
|
+
};
|
|
255
|
+
}
|
|
256
|
+
getClient() {
|
|
257
|
+
if (this.closed)
|
|
258
|
+
return Promise.reject(new Error(`MCP server "${this.entry.name}" is shut down`));
|
|
259
|
+
if (this.client?.connectionState === "connected")
|
|
260
|
+
return Promise.resolve(this.client);
|
|
261
|
+
this.opening ??= this.open().finally(() => {
|
|
262
|
+
this.opening = undefined;
|
|
263
|
+
});
|
|
264
|
+
return this.opening;
|
|
265
|
+
}
|
|
266
|
+
callTool(name, args, options) {
|
|
267
|
+
return this.withClient((client) => client.callTool(name, args, options));
|
|
268
|
+
}
|
|
269
|
+
readResource(uri, options) {
|
|
270
|
+
return this.withClient((client) => client.readResource(uri, options), true);
|
|
271
|
+
}
|
|
272
|
+
resourcesPage(cursor, options) {
|
|
273
|
+
return this.withClient((client) => client.listResourcesPage(cursor, options), true);
|
|
274
|
+
}
|
|
275
|
+
resourceTemplatesPage(cursor, options) {
|
|
276
|
+
return this.withClient((client) => withoutTemplates(() => client.listResourceTemplatesPage(cursor, options), { resourceTemplates: [] }), true);
|
|
277
|
+
}
|
|
278
|
+
allResources(options) {
|
|
279
|
+
return this.withClient((client) => client.listResources(options), true);
|
|
280
|
+
}
|
|
281
|
+
allResourceTemplates(options) {
|
|
282
|
+
return this.withClient((client) => listTemplates(client, options), true);
|
|
283
|
+
}
|
|
284
|
+
/**
|
|
285
|
+
* Run a request, reconnecting when needed. `readOnly` requests are retried once after a transient
|
|
286
|
+
* HTTP error; tool calls are not, since they may have run.
|
|
287
|
+
*/
|
|
288
|
+
async withClient(run, readOnly = false) {
|
|
289
|
+
for (let attempt = 1;; attempt++) {
|
|
290
|
+
const client = await this.getClient();
|
|
291
|
+
try {
|
|
292
|
+
return await this.runWithClient(client, run);
|
|
293
|
+
}
|
|
294
|
+
catch (error) {
|
|
295
|
+
if (readOnly && attempt === 1 && error instanceof McpHttpError && isTransientError(error)) {
|
|
296
|
+
await new Promise((resolve) => setTimeout(resolve, CONNECT_RETRY_DELAYS_MS[0]));
|
|
297
|
+
continue;
|
|
298
|
+
}
|
|
299
|
+
if (error instanceof McpSessionExpiredError && attempt === 1) {
|
|
300
|
+
if (this.closed)
|
|
301
|
+
throw error;
|
|
302
|
+
const retirement = this.dropClient(client, true);
|
|
303
|
+
if (!this.activeRequests.has(client))
|
|
304
|
+
await retirement;
|
|
305
|
+
if (this.closed)
|
|
306
|
+
throw error;
|
|
307
|
+
continue;
|
|
308
|
+
}
|
|
309
|
+
if (!this.needsSignIn(error))
|
|
310
|
+
throw error;
|
|
311
|
+
await this.dropClient(client);
|
|
312
|
+
this.markNeedsAuth();
|
|
313
|
+
throw new Error(signInRequiredMessage(this.entry));
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
async runWithClient(client, run) {
|
|
318
|
+
this.activeRequests.set(client, (this.activeRequests.get(client) ?? 0) + 1);
|
|
319
|
+
try {
|
|
320
|
+
return await run(client);
|
|
321
|
+
}
|
|
322
|
+
finally {
|
|
323
|
+
const remaining = (this.activeRequests.get(client) ?? 1) - 1;
|
|
324
|
+
if (remaining > 0) {
|
|
325
|
+
this.activeRequests.set(client, remaining);
|
|
326
|
+
}
|
|
327
|
+
else {
|
|
328
|
+
this.activeRequests.delete(client);
|
|
329
|
+
void this.retiringClients.get(client)?.close();
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
/** Connect again with fresh credentials, for example after signing in. */
|
|
334
|
+
async reconnect() {
|
|
335
|
+
await this.opening?.catch(() => undefined);
|
|
336
|
+
if (this.client)
|
|
337
|
+
await this.dropClient(this.client);
|
|
338
|
+
await this.getClient();
|
|
339
|
+
}
|
|
340
|
+
/** Disconnect after the stored credentials were removed. */
|
|
341
|
+
async signOut() {
|
|
342
|
+
await this.opening?.catch(() => undefined);
|
|
343
|
+
if (this.client)
|
|
344
|
+
await this.dropClient(this.client);
|
|
345
|
+
if (!this.closed)
|
|
346
|
+
this.markNeedsAuth();
|
|
347
|
+
}
|
|
348
|
+
/** OAuth servers that still reject the request after a refresh need the user to sign in again. */
|
|
349
|
+
needsSignIn(error) {
|
|
350
|
+
return (error instanceof McpOAuthAuthorizationRequiredError ||
|
|
351
|
+
(this.authProvider !== undefined && error instanceof McpAuthRequiredError));
|
|
352
|
+
}
|
|
353
|
+
markNeedsAuth() {
|
|
354
|
+
this.state = "needs-auth";
|
|
355
|
+
this.error = undefined;
|
|
356
|
+
this.changed();
|
|
357
|
+
}
|
|
358
|
+
changed() {
|
|
359
|
+
this.onChange?.(this);
|
|
360
|
+
}
|
|
361
|
+
dropClient(client, drainRequests = false) {
|
|
362
|
+
if (this.client === client)
|
|
363
|
+
this.client = undefined;
|
|
364
|
+
const pending = this.retiringClients.get(client);
|
|
365
|
+
if (pending) {
|
|
366
|
+
if (!drainRequests)
|
|
367
|
+
void pending.close();
|
|
368
|
+
return pending.promise;
|
|
369
|
+
}
|
|
370
|
+
let resolve;
|
|
371
|
+
const promise = new Promise((done) => {
|
|
372
|
+
resolve = done;
|
|
373
|
+
});
|
|
374
|
+
let closing;
|
|
375
|
+
const close = () => {
|
|
376
|
+
closing ??= client
|
|
377
|
+
.close()
|
|
378
|
+
.catch(() => undefined)
|
|
379
|
+
.finally(() => {
|
|
380
|
+
this.retiringClients.delete(client);
|
|
381
|
+
resolve();
|
|
382
|
+
});
|
|
383
|
+
return closing;
|
|
384
|
+
};
|
|
385
|
+
this.retiringClients.set(client, { promise, close });
|
|
386
|
+
if (!drainRequests || !this.activeRequests.has(client))
|
|
387
|
+
void close();
|
|
388
|
+
return promise;
|
|
389
|
+
}
|
|
390
|
+
async open() {
|
|
391
|
+
this.state = "connecting";
|
|
392
|
+
this.changed();
|
|
393
|
+
const retries = "url" in this.entry.config ? CONNECT_RETRY_DELAYS_MS : [];
|
|
394
|
+
for (let attempt = 0;; attempt++) {
|
|
395
|
+
this.stderrTail = undefined;
|
|
396
|
+
try {
|
|
397
|
+
return await this.connectOnce();
|
|
398
|
+
}
|
|
399
|
+
catch (error) {
|
|
400
|
+
const delay = retries[attempt];
|
|
401
|
+
if (this.closed || delay === undefined || !isTransientError(error)) {
|
|
402
|
+
throw this.connectFailed(error);
|
|
403
|
+
}
|
|
404
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
405
|
+
if (this.closed)
|
|
406
|
+
throw this.connectFailed(error);
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
async connectOnce() {
|
|
411
|
+
if (this.closed)
|
|
412
|
+
throw new Error("shut down while connecting");
|
|
413
|
+
const client = new McpClient({
|
|
414
|
+
name: "pi",
|
|
415
|
+
version: VERSION,
|
|
416
|
+
requestTimeoutMs: this.timeoutMs,
|
|
417
|
+
roots: [{ uri: pathToFileURL(this.cwd).href, name: basename(this.cwd) }],
|
|
418
|
+
});
|
|
419
|
+
this.initializingClient = client;
|
|
420
|
+
const log = this.log;
|
|
421
|
+
if (log)
|
|
422
|
+
client.onNotification("notifications/message", (params) => log.write(this.entry.name, params));
|
|
423
|
+
let transport;
|
|
424
|
+
try {
|
|
425
|
+
transport = this.createTransport(this.entry, this.cwd, this.authProvider);
|
|
426
|
+
this.initializingTransport = transport;
|
|
427
|
+
if (this.closed)
|
|
428
|
+
throw new Error("shut down while connecting");
|
|
429
|
+
await client.connect(transport);
|
|
430
|
+
client.onNotification("notifications/tools/list_changed", () => {
|
|
431
|
+
void this.refreshTools(client);
|
|
432
|
+
});
|
|
433
|
+
client.onNotification("notifications/resources/list_changed", () => {
|
|
434
|
+
void this.refreshResources(client);
|
|
435
|
+
});
|
|
436
|
+
const stdio = transport instanceof StdioTransport ? transport : undefined;
|
|
437
|
+
client.onClose(() => this.handleClientClose(client, stdio));
|
|
438
|
+
// Servers without the tools capability (prompts or resources only) do not answer tools/list.
|
|
439
|
+
const hasResources = client.serverCapabilities?.resources !== undefined;
|
|
440
|
+
const [tools, resources] = await Promise.all([
|
|
441
|
+
client.serverCapabilities?.tools ? client.listTools() : [],
|
|
442
|
+
hasResources ? fetchResources(client) : { resources: [], resourceTemplates: [] },
|
|
443
|
+
]);
|
|
444
|
+
if (this.closed)
|
|
445
|
+
throw new Error("shut down while connecting");
|
|
446
|
+
if (client.connectionState !== "connected")
|
|
447
|
+
throw new Error("connection closed during setup");
|
|
448
|
+
this.client = client;
|
|
449
|
+
this.tools = tools;
|
|
450
|
+
this.hasResources = hasResources;
|
|
451
|
+
this.resources = resources.resources;
|
|
452
|
+
this.resourceTemplates = resources.resourceTemplates;
|
|
453
|
+
this.instructions = client.instructions?.trim() || undefined;
|
|
454
|
+
this.state = "connected";
|
|
455
|
+
this.error = undefined;
|
|
456
|
+
this.onTools(this);
|
|
457
|
+
this.changed();
|
|
458
|
+
return client;
|
|
459
|
+
}
|
|
460
|
+
catch (error) {
|
|
461
|
+
await client.close().catch(() => undefined);
|
|
462
|
+
if (transport instanceof StdioTransport) {
|
|
463
|
+
this.stderrTail = transport.stderr.trim().slice(-STDERR_TAIL_CHARS) || undefined;
|
|
464
|
+
}
|
|
465
|
+
throw error;
|
|
466
|
+
}
|
|
467
|
+
finally {
|
|
468
|
+
// A delayed start may allocate resources after its first close. Retire them too.
|
|
469
|
+
if (this.closed)
|
|
470
|
+
await transport?.close().catch(() => undefined);
|
|
471
|
+
if (this.initializingClient === client) {
|
|
472
|
+
this.initializingClient = undefined;
|
|
473
|
+
this.initializingTransport = undefined;
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
connectFailed(error) {
|
|
478
|
+
if (this.needsSignIn(error) && !this.closed) {
|
|
479
|
+
this.markNeedsAuth();
|
|
480
|
+
return new Error(signInRequiredMessage(this.entry));
|
|
481
|
+
}
|
|
482
|
+
this.state = this.closed ? "closed" : "failed";
|
|
483
|
+
this.error = this.stderrTail ? `${errorMessage(error)}\n${this.stderrTail}` : errorMessage(error);
|
|
484
|
+
this.changed();
|
|
485
|
+
return new Error(`MCP server "${this.entry.name}" failed to connect: ${this.error}`);
|
|
486
|
+
}
|
|
487
|
+
/** The transport dropped. The next call reconnects; until then the status shows why. */
|
|
488
|
+
handleClientClose(client, stdio) {
|
|
489
|
+
if (this.client !== client || this.closed)
|
|
490
|
+
return;
|
|
491
|
+
this.client = undefined;
|
|
492
|
+
this.state = "disconnected";
|
|
493
|
+
const stderr = stdio?.stderr.trim().slice(-STDERR_TAIL_CHARS);
|
|
494
|
+
this.error = stderr ? `Connection closed\n${stderr}` : "Connection closed";
|
|
495
|
+
this.changed();
|
|
496
|
+
}
|
|
497
|
+
async refreshTools(client) {
|
|
498
|
+
try {
|
|
499
|
+
const tools = await this.runWithClient(client, (client) => client.listTools());
|
|
500
|
+
if (this.client !== client || this.closed)
|
|
501
|
+
return;
|
|
502
|
+
this.tools = tools;
|
|
503
|
+
this.onTools(this);
|
|
504
|
+
}
|
|
505
|
+
catch (error) {
|
|
506
|
+
this.error = `Failed to refresh tools: ${errorMessage(error)}`;
|
|
507
|
+
}
|
|
508
|
+
this.changed();
|
|
509
|
+
}
|
|
510
|
+
async refreshResources(client) {
|
|
511
|
+
const { resources, resourceTemplates } = await this.runWithClient(client, fetchResources);
|
|
512
|
+
if (this.client !== client || this.closed)
|
|
513
|
+
return;
|
|
514
|
+
this.resources = resources;
|
|
515
|
+
this.resourceTemplates = resourceTemplates;
|
|
516
|
+
this.onTools(this);
|
|
517
|
+
this.changed();
|
|
518
|
+
}
|
|
519
|
+
close() {
|
|
520
|
+
if (this.closing)
|
|
521
|
+
return this.closing;
|
|
522
|
+
this.closed = true;
|
|
523
|
+
this.state = "closed";
|
|
524
|
+
this.changed();
|
|
525
|
+
const client = this.client;
|
|
526
|
+
this.client = undefined;
|
|
527
|
+
const opening = this.opening;
|
|
528
|
+
const initializingClient = this.initializingClient;
|
|
529
|
+
const initializingTransport = this.initializingTransport;
|
|
530
|
+
// Close immediately, before waiting for setup: start itself may need close to unblock.
|
|
531
|
+
const retirement = Promise.allSettled([
|
|
532
|
+
client?.close(),
|
|
533
|
+
initializingClient?.close(),
|
|
534
|
+
initializingTransport?.close(),
|
|
535
|
+
opening,
|
|
536
|
+
...[...this.retiringClients.values()].map((retirement) => retirement.close()),
|
|
537
|
+
]);
|
|
538
|
+
this.closing = (async () => {
|
|
539
|
+
await boundedRetirement(retirement);
|
|
540
|
+
// Preserve an already-rotated refresh token before exiting.
|
|
541
|
+
await this.authProvider?.settled();
|
|
542
|
+
})();
|
|
543
|
+
return this.closing;
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
//# sourceMappingURL=runtime.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runtime.js","sourceRoot":"","sources":["../../../src/extensions/mcp/runtime.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The part of the MCP integration that talks to servers: connections, transports, and OAuth\n * sign-in. It pulls in the MCP client, so index.ts loads it through runtime.lazy.ts only when a\n * server is configured.\n */\n\nimport { homedir } from \"node:os\";\nimport { basename, join, resolve } from \"node:path\";\nimport { pathToFileURL } from \"node:url\";\nimport { VERSION } from \"../../config.js\";\nimport { resolveMcpServerUrl } from \"../../core/mcp-servers.ts\";\nimport { resolveConfigValueOrThrow, resolveHeadersOrThrow } from \"../../core/resolve-config-value.ts\";\nimport {\n\ttype AuthProvider,\n\ttype CallToolResult,\n\tJSON_RPC_ERROR_CODES,\n\ttype ListResourcesResult,\n\ttype ListResourceTemplatesResult,\n\tMcpAuthRequiredError,\n\tMcpClient,\n\tMcpError,\n\ttype McpFetch,\n\tMcpHttpError,\n\ttype McpRequestOptions,\n\tMcpSessionExpiredError,\n\ttype Tool as McpTool,\n\ttype McpTransport,\n\ttype ReadResourceResult,\n\ttype Resource,\n\ttype ResourceTemplate,\n\tStdioTransport,\n\tStreamableHttpTransport,\n} from \"./client/index.js\";\nimport { McpOAuthAuthorizationRequiredError, type OAuthChallenge } from \"./client/oauth/index.js\";\nimport type { McpServerEntry } from \"./config.ts\";\nimport type { McpServerLog } from \"./log.ts\";\nimport {\n\tcreateMcpAuthProvider,\n\ttype McpAuthProvider,\n\ttype McpOAuthCredentialStore,\n\ttype McpOAuthSettings,\n} from \"./oauth.ts\";\nimport { isMcpAppResource, type McpResourceServer } from \"./resources.ts\";\nimport type { McpToolCaller } from \"./tools.ts\";\n\nexport { McpServerLog } from \"./log.ts\";\nexport { McpOAuthCredentialStore, McpSignInCancelledError, signInMcpServer } from \"./oauth.ts\";\n\nconst DEFAULT_TIMEOUT_SECONDS = 60;\nconst STDERR_TAIL_CHARS = 2_000;\n/** Delays between attempts to connect to an HTTP server that failed with a transient error. */\nconst CONNECT_RETRY_DELAYS_MS = [250, 1_000];\n/** Give canceled setup time to retire, without letting a broken transport block session cleanup. */\nconst OPENING_RETIREMENT_TIMEOUT_MS = 1_000;\n\nasync function boundedRetirement(retirement: Promise<unknown>): Promise<void> {\n\tlet timer: ReturnType<typeof setTimeout> | undefined;\n\ttry {\n\t\tawait Promise.race([\n\t\t\tretirement.catch(() => undefined),\n\t\t\tnew Promise<void>((resolve) => {\n\t\t\t\ttimer = setTimeout(resolve, OPENING_RETIREMENT_TIMEOUT_MS);\n\t\t\t}),\n\t\t]);\n\t} finally {\n\t\tclearTimeout(timer);\n\t}\n}\n\n/**\n * `disconnected`: the connection dropped (for example the stdio server exited); the next call\n * reconnects.\n */\ntype ServerState = \"connecting\" | \"connected\" | \"disconnected\" | \"needs-auth\" | \"failed\" | \"closed\";\n\nexport type McpTransportFactory = (\n\tentry: McpServerEntry,\n\tcwd: string,\n\tauthProvider: AuthProvider | undefined,\n) => McpTransport;\n\nfunction errorMessage(error: unknown): string {\n\treturn error instanceof Error ? error.message : String(error);\n}\n\n/** Network failures and overloaded or restarting servers, which are worth another attempt. */\nfunction isTransientError(error: unknown): boolean {\n\tif (error instanceof McpHttpError) {\n\t\treturn error.status === 408 || error.status === 429 || (error.status >= 500 && error.status !== 501);\n\t}\n\treturn error instanceof TypeError;\n}\n\nfunction providerAuthName(entry: McpServerEntry): string | undefined {\n\treturn \"url\" in entry.config ? entry.config.auth?.provider : undefined;\n}\n\nfunction validateProviderDestination(serverUrl: string): void {\n\tconst url = new URL(serverUrl);\n\tif (\n\t\turl.protocol !== \"https:\" &&\n\t\t!(url.protocol === \"http:\" && [\"localhost\", \"127.0.0.1\", \"[::1]\"].includes(url.hostname))\n\t) {\n\t\tthrow new Error(\"MCP provider credentials require HTTPS or exact loopback HTTP\");\n\t}\n}\n\n/** A provider token is granted to this configured origin, never to a redirect destination. */\nfunction providerFetch(serverUrl: string): McpFetch {\n\tconst origin = new URL(serverUrl).origin;\n\treturn async (input, init) => {\n\t\tlet url = new URL(input);\n\t\tlet request = { ...init, redirect: \"manual\" as const };\n\t\tfor (let redirects = 0; ; redirects++) {\n\t\t\tif (url.origin !== origin) throw new Error(\"MCP provider request cannot cross origins\");\n\t\t\tconst response = await fetch(url, request);\n\t\t\tconst location = response.headers.get(\"location\");\n\t\t\tif (![301, 302, 303, 307, 308].includes(response.status) || !location) return response;\n\t\t\tawait response.body?.cancel();\n\t\t\tif (redirects >= 10) throw new Error(\"Too many MCP provider redirects\");\n\t\t\turl = new URL(location, url);\n\t\t\tconst method = request.method?.toUpperCase() ?? \"GET\";\n\t\t\tif (\n\t\t\t\t(response.status === 303 && method !== \"HEAD\") ||\n\t\t\t\t([301, 302].includes(response.status) && method === \"POST\")\n\t\t\t) {\n\t\t\t\tconst headers = new Headers(request.headers);\n\t\t\t\tfor (const header of [\n\t\t\t\t\t\"content-type\",\n\t\t\t\t\t\"content-length\",\n\t\t\t\t\t\"content-encoding\",\n\t\t\t\t\t\"content-language\",\n\t\t\t\t\t\"content-location\",\n\t\t\t\t])\n\t\t\t\t\theaders.delete(header);\n\t\t\t\trequest = { ...request, method: \"GET\", body: undefined, headers };\n\t\t\t}\n\t\t}\n\t};\n}\n\nfunction signInRequiredMessage(entry: McpServerEntry): string {\n\tconst provider = providerAuthName(entry);\n\treturn `MCP server \"${entry.name}\" requires sign-in. Run ${provider ? `/login ${provider}` : \"/mcp\"} to sign in.`;\n}\n\n/** HTTP servers authenticate with OAuth unless they supply Authorization or a provider. */\nfunction usesOAuth(entry: McpServerEntry): boolean {\n\tconst { config } = entry;\n\tif (!(\"url\" in config) || config.auth) return false;\n\treturn !Object.keys(config.headers ?? {}).some((header) => header.toLowerCase() === \"authorization\");\n}\n\n/** `~` and `~/…` (also `~\\…` on Windows) name the home directory, like in a shell. */\nfunction expandHome(value: string): string {\n\tif (value === \"~\") return homedir();\n\tif (value.startsWith(\"~/\") || (process.platform === \"win32\" && value.startsWith(\"~\\\\\"))) {\n\t\treturn join(homedir(), value.slice(2));\n\t}\n\treturn value;\n}\n\nexport function createDefaultTransport(\n\tentry: McpServerEntry,\n\tcwd: string,\n\tauthProvider: AuthProvider | undefined,\n): McpTransport {\n\tconst { config, name } = entry;\n\tif (\"url\" in config) {\n\t\tconst url = resolveMcpServerUrl(config.url);\n\t\tif (config.auth) validateProviderDestination(url);\n\t\treturn new StreamableHttpTransport({\n\t\t\turl,\n\t\t\theaders: resolveHeadersOrThrow(config.headers, `MCP server \"${name}\"`),\n\t\t\tauthProvider,\n\t\t\tfetch: config.auth ? providerFetch(url) : undefined,\n\t\t});\n\t}\n\tif (!config.command) throw new Error(`MCP server \"${name}\" has no command or url`);\n\tconst env: Record<string, string> = {};\n\tfor (const [key, value] of Object.entries(config.env ?? {})) {\n\t\tenv[key] = resolveConfigValueOrThrow(value, `MCP server \"${name}\" env \"${key}\"`);\n\t}\n\treturn new StdioTransport({\n\t\tcommand: expandHome(config.command),\n\t\targs: config.args?.map(expandHome),\n\t\tcwd: resolve(cwd, expandHome(config.cwd ?? \".\")),\n\t\tenv,\n\t\tstderr: \"pipe\",\n\t});\n}\n\n/** Servers that do not implement `resources/templates/list` have no templates. */\nasync function withoutTemplates<T>(list: () => Promise<T>, empty: T): Promise<T> {\n\ttry {\n\t\treturn await list();\n\t} catch (error) {\n\t\tif (error instanceof McpError && error.code === JSON_RPC_ERROR_CODES.methodNotFound) return empty;\n\t\tthrow error;\n\t}\n}\n\nfunction listTemplates(client: McpClient, options: McpRequestOptions = {}): Promise<ResourceTemplate[]> {\n\treturn withoutTemplates(() => client.listResourceTemplates(options), []);\n}\n\n/**\n * Resources and templates at connect time, for the counts in `/mcp` and `pi mcp list`. A server\n * whose lists fail still connects: the resource tools list and read its resources on demand.\n */\nasync function fetchResources(\n\tclient: McpClient,\n): Promise<{ resources: Resource[]; resourceTemplates: ResourceTemplate[] }> {\n\tconst [resources, resourceTemplates] = await Promise.all([\n\t\tclient.listResources().catch(() => []),\n\t\tlistTemplates(client).catch(() => []),\n\t]);\n\treturn {\n\t\tresources: resources.filter((resource) => !isMcpAppResource(resource)),\n\t\tresourceTemplates: resourceTemplates.filter((template) => !isMcpAppResource(template)),\n\t};\n}\n\n/** One configured server. Reconnects lazily when a call finds the connection gone. */\nexport class McpServerConnection implements McpToolCaller, McpResourceServer {\n\treadonly entry: McpServerEntry;\n\tstate: ServerState = \"connecting\";\n\terror: string | undefined;\n\ttools: McpTool[] = [];\n\t/**\n\t * Whether the server offers resources. The lists below are what it listed at the last connect or\n\t * change, without MCP App resources.\n\t */\n\thasResources = false;\n\tresources: Resource[] = [];\n\tresourceTemplates: ResourceTemplate[] = [];\n\t/** Server instructions from `initialize`, describing its tools as a group. */\n\tinstructions: string | undefined;\n\t/** Last OAuth challenge from the server; sign-in uses its resource metadata URL and scope. */\n\tchallenge: OAuthChallenge | undefined;\n\tprivate client: McpClient | undefined;\n\tprivate opening: Promise<McpClient> | undefined;\n\tprivate initializingClient: McpClient | undefined;\n\tprivate initializingTransport: McpTransport | undefined;\n\tprivate closing: Promise<void> | undefined;\n\tprivate readonly activeRequests = new Map<McpClient, number>();\n\tprivate readonly retiringClients = new Map<McpClient, { promise: Promise<void>; close: () => Promise<void> }>();\n\tprivate closed = false;\n\t/** Stderr of the last stdio server that failed to connect. */\n\tprivate stderrTail: string | undefined;\n\tprivate readonly cwd: string;\n\tprivate readonly createTransport: McpTransportFactory;\n\tprivate readonly authProvider: McpAuthProvider | undefined;\n\tprivate readonly onTools: (connection: McpServerConnection) => void;\n\tprivate readonly onChange: ((connection: McpServerConnection) => void) | undefined;\n\tprivate readonly log: McpServerLog | undefined;\n\n\tconstructor(options: {\n\t\tentry: McpServerEntry;\n\t\tcwd: string;\n\t\tcreateTransport: McpTransportFactory;\n\t\tcredentials: McpOAuthCredentialStore;\n\t\t/** The current token of a pi provider, for servers with `auth.provider`. */\n\t\tproviderToken?: (provider: string) => Promise<string | undefined>;\n\t\tonTools: (connection: McpServerConnection) => void;\n\t\t/** Called when `state`, `error`, or `tools` change. */\n\t\tonChange?: (connection: McpServerConnection) => void;\n\t\t/** Receives the server's log messages (`notifications/message`). */\n\t\tlog?: McpServerLog;\n\t}) {\n\t\tconst config = options.entry.config;\n\t\t// Resolve once so the credential key, destination check, and transport use the same URL.\n\t\tthis.entry =\n\t\t\t\"url\" in config\n\t\t\t\t? { ...options.entry, config: { ...config, url: resolveMcpServerUrl(config.url) } }\n\t\t\t\t: options.entry;\n\t\tthis.cwd = options.cwd;\n\t\tthis.createTransport = options.createTransport;\n\t\tthis.onTools = options.onTools;\n\t\tthis.onChange = options.onChange;\n\t\tthis.log = options.log;\n\t\tconst url = this.oauthUrl;\n\t\tconst provider = providerAuthName(this.entry);\n\t\tif (provider && \"url\" in this.entry.config) validateProviderDestination(this.entry.config.url);\n\t\tthis.authProvider = url\n\t\t\t? createMcpAuthProvider({\n\t\t\t\t\tserverUrl: url,\n\t\t\t\t\tstore: options.credentials.forServer(url),\n\t\t\t\t\tsettings: () => this.oauthSettings(),\n\t\t\t\t\tonChallenge: (challenge) => {\n\t\t\t\t\t\tthis.challenge = challenge;\n\t\t\t\t\t},\n\t\t\t\t})\n\t\t\t: provider\n\t\t\t\t? // Read on every request, so the provider's refreshes apply; MCP stores no copy.\n\t\t\t\t\t{ token: async () => options.providerToken?.(provider), settled: async () => {} }\n\t\t\t\t: undefined;\n\t}\n\n\tget name(): string {\n\t\treturn this.entry.name;\n\t}\n\n\tget timeoutMs(): number {\n\t\treturn (this.entry.config.timeout ?? DEFAULT_TIMEOUT_SECONDS) * 1000;\n\t}\n\n\t/** Server URL when the server authenticates with OAuth. */\n\tget oauthUrl(): string | undefined {\n\t\treturn \"url\" in this.entry.config && usesOAuth(this.entry) ? this.entry.config.url : undefined;\n\t}\n\n\toauthSettings(): McpOAuthSettings {\n\t\tconst oauth = \"url\" in this.entry.config ? this.entry.config.oauth : undefined;\n\t\tif (!oauth) return {};\n\t\treturn {\n\t\t\tclientId: oauth.clientId,\n\t\t\tclientSecret:\n\t\t\t\toauth.clientSecret === undefined\n\t\t\t\t\t? undefined\n\t\t\t\t\t: resolveConfigValueOrThrow(oauth.clientSecret, `MCP server \"${this.entry.name}\" oauth.clientSecret`),\n\t\t\tcallbackPort: oauth.callbackPort,\n\t\t\tcallbackUrl: oauth.callbackUrl,\n\t\t\tscope: oauth.scope,\n\t\t\tclientName: oauth.clientName,\n\t\t\tauthServerMetadataUrl: oauth.authServerMetadataUrl ? new URL(oauth.authServerMetadataUrl) : undefined,\n\t\t};\n\t}\n\n\tgetClient(): Promise<McpClient> {\n\t\tif (this.closed) return Promise.reject(new Error(`MCP server \"${this.entry.name}\" is shut down`));\n\t\tif (this.client?.connectionState === \"connected\") return Promise.resolve(this.client);\n\t\tthis.opening ??= this.open().finally(() => {\n\t\t\tthis.opening = undefined;\n\t\t});\n\t\treturn this.opening;\n\t}\n\n\tcallTool(name: string, args: Record<string, unknown>, options: McpRequestOptions): Promise<CallToolResult> {\n\t\treturn this.withClient((client) => client.callTool(name, args, options));\n\t}\n\n\treadResource(uri: string, options: McpRequestOptions): Promise<ReadResourceResult> {\n\t\treturn this.withClient((client) => client.readResource(uri, options), true);\n\t}\n\n\tresourcesPage(cursor: string | undefined, options: McpRequestOptions): Promise<ListResourcesResult> {\n\t\treturn this.withClient((client) => client.listResourcesPage(cursor, options), true);\n\t}\n\n\tresourceTemplatesPage(cursor: string | undefined, options: McpRequestOptions): Promise<ListResourceTemplatesResult> {\n\t\treturn this.withClient(\n\t\t\t(client) =>\n\t\t\t\twithoutTemplates(() => client.listResourceTemplatesPage(cursor, options), { resourceTemplates: [] }),\n\t\t\ttrue,\n\t\t);\n\t}\n\n\tallResources(options: McpRequestOptions): Promise<Resource[]> {\n\t\treturn this.withClient((client) => client.listResources(options), true);\n\t}\n\n\tallResourceTemplates(options: McpRequestOptions): Promise<ResourceTemplate[]> {\n\t\treturn this.withClient((client) => listTemplates(client, options), true);\n\t}\n\n\t/**\n\t * Run a request, reconnecting when needed. `readOnly` requests are retried once after a transient\n\t * HTTP error; tool calls are not, since they may have run.\n\t */\n\tprivate async withClient<T>(run: (client: McpClient) => Promise<T>, readOnly = false): Promise<T> {\n\t\tfor (let attempt = 1; ; attempt++) {\n\t\t\tconst client = await this.getClient();\n\t\t\ttry {\n\t\t\t\treturn await this.runWithClient(client, run);\n\t\t\t} catch (error) {\n\t\t\t\tif (readOnly && attempt === 1 && error instanceof McpHttpError && isTransientError(error)) {\n\t\t\t\t\tawait new Promise((resolve) => setTimeout(resolve, CONNECT_RETRY_DELAYS_MS[0]));\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tif (error instanceof McpSessionExpiredError && attempt === 1) {\n\t\t\t\t\tif (this.closed) throw error;\n\t\t\t\t\tconst retirement = this.dropClient(client, true);\n\t\t\t\t\tif (!this.activeRequests.has(client)) await retirement;\n\t\t\t\t\tif (this.closed) throw error;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tif (!this.needsSignIn(error)) throw error;\n\t\t\t\tawait this.dropClient(client);\n\t\t\t\tthis.markNeedsAuth();\n\t\t\t\tthrow new Error(signInRequiredMessage(this.entry));\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate async runWithClient<T>(client: McpClient, run: (client: McpClient) => Promise<T>): Promise<T> {\n\t\tthis.activeRequests.set(client, (this.activeRequests.get(client) ?? 0) + 1);\n\t\ttry {\n\t\t\treturn await run(client);\n\t\t} finally {\n\t\t\tconst remaining = (this.activeRequests.get(client) ?? 1) - 1;\n\t\t\tif (remaining > 0) {\n\t\t\t\tthis.activeRequests.set(client, remaining);\n\t\t\t} else {\n\t\t\t\tthis.activeRequests.delete(client);\n\t\t\t\tvoid this.retiringClients.get(client)?.close();\n\t\t\t}\n\t\t}\n\t}\n\n\t/** Connect again with fresh credentials, for example after signing in. */\n\tasync reconnect(): Promise<void> {\n\t\tawait this.opening?.catch(() => undefined);\n\t\tif (this.client) await this.dropClient(this.client);\n\t\tawait this.getClient();\n\t}\n\n\t/** Disconnect after the stored credentials were removed. */\n\tasync signOut(): Promise<void> {\n\t\tawait this.opening?.catch(() => undefined);\n\t\tif (this.client) await this.dropClient(this.client);\n\t\tif (!this.closed) this.markNeedsAuth();\n\t}\n\n\t/** OAuth servers that still reject the request after a refresh need the user to sign in again. */\n\tprivate needsSignIn(error: unknown): boolean {\n\t\treturn (\n\t\t\terror instanceof McpOAuthAuthorizationRequiredError ||\n\t\t\t(this.authProvider !== undefined && error instanceof McpAuthRequiredError)\n\t\t);\n\t}\n\n\tprivate markNeedsAuth(): void {\n\t\tthis.state = \"needs-auth\";\n\t\tthis.error = undefined;\n\t\tthis.changed();\n\t}\n\n\tprivate changed(): void {\n\t\tthis.onChange?.(this);\n\t}\n\n\tprivate dropClient(client: McpClient, drainRequests = false): Promise<void> {\n\t\tif (this.client === client) this.client = undefined;\n\t\tconst pending = this.retiringClients.get(client);\n\t\tif (pending) {\n\t\t\tif (!drainRequests) void pending.close();\n\t\t\treturn pending.promise;\n\t\t}\n\t\tlet resolve!: () => void;\n\t\tconst promise = new Promise<void>((done) => {\n\t\t\tresolve = done;\n\t\t});\n\t\tlet closing: Promise<void> | undefined;\n\t\tconst close = () => {\n\t\t\tclosing ??= client\n\t\t\t\t.close()\n\t\t\t\t.catch(() => undefined)\n\t\t\t\t.finally(() => {\n\t\t\t\t\tthis.retiringClients.delete(client);\n\t\t\t\t\tresolve();\n\t\t\t\t});\n\t\t\treturn closing;\n\t\t};\n\t\tthis.retiringClients.set(client, { promise, close });\n\t\tif (!drainRequests || !this.activeRequests.has(client)) void close();\n\t\treturn promise;\n\t}\n\n\tprivate async open(): Promise<McpClient> {\n\t\tthis.state = \"connecting\";\n\t\tthis.changed();\n\t\tconst retries = \"url\" in this.entry.config ? CONNECT_RETRY_DELAYS_MS : [];\n\t\tfor (let attempt = 0; ; attempt++) {\n\t\t\tthis.stderrTail = undefined;\n\t\t\ttry {\n\t\t\t\treturn await this.connectOnce();\n\t\t\t} catch (error) {\n\t\t\t\tconst delay = retries[attempt];\n\t\t\t\tif (this.closed || delay === undefined || !isTransientError(error)) {\n\t\t\t\t\tthrow this.connectFailed(error);\n\t\t\t\t}\n\t\t\t\tawait new Promise((resolve) => setTimeout(resolve, delay));\n\t\t\t\tif (this.closed) throw this.connectFailed(error);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate async connectOnce(): Promise<McpClient> {\n\t\tif (this.closed) throw new Error(\"shut down while connecting\");\n\t\tconst client = new McpClient({\n\t\t\tname: \"pi\",\n\t\t\tversion: VERSION,\n\t\t\trequestTimeoutMs: this.timeoutMs,\n\t\t\troots: [{ uri: pathToFileURL(this.cwd).href, name: basename(this.cwd) }],\n\t\t});\n\t\tthis.initializingClient = client;\n\t\tconst log = this.log;\n\t\tif (log) client.onNotification(\"notifications/message\", (params) => log.write(this.entry.name, params));\n\t\tlet transport: McpTransport | undefined;\n\t\ttry {\n\t\t\ttransport = this.createTransport(this.entry, this.cwd, this.authProvider);\n\t\t\tthis.initializingTransport = transport;\n\t\t\tif (this.closed) throw new Error(\"shut down while connecting\");\n\t\t\tawait client.connect(transport);\n\t\t\tclient.onNotification(\"notifications/tools/list_changed\", () => {\n\t\t\t\tvoid this.refreshTools(client);\n\t\t\t});\n\t\t\tclient.onNotification(\"notifications/resources/list_changed\", () => {\n\t\t\t\tvoid this.refreshResources(client);\n\t\t\t});\n\t\t\tconst stdio = transport instanceof StdioTransport ? transport : undefined;\n\t\t\tclient.onClose(() => this.handleClientClose(client, stdio));\n\t\t\t// Servers without the tools capability (prompts or resources only) do not answer tools/list.\n\t\t\tconst hasResources = client.serverCapabilities?.resources !== undefined;\n\t\t\tconst [tools, resources] = await Promise.all([\n\t\t\t\tclient.serverCapabilities?.tools ? client.listTools() : [],\n\t\t\t\thasResources ? fetchResources(client) : { resources: [], resourceTemplates: [] },\n\t\t\t]);\n\t\t\tif (this.closed) throw new Error(\"shut down while connecting\");\n\t\t\tif (client.connectionState !== \"connected\") throw new Error(\"connection closed during setup\");\n\t\t\tthis.client = client;\n\t\t\tthis.tools = tools;\n\t\t\tthis.hasResources = hasResources;\n\t\t\tthis.resources = resources.resources;\n\t\t\tthis.resourceTemplates = resources.resourceTemplates;\n\t\t\tthis.instructions = client.instructions?.trim() || undefined;\n\t\t\tthis.state = \"connected\";\n\t\t\tthis.error = undefined;\n\t\t\tthis.onTools(this);\n\t\t\tthis.changed();\n\t\t\treturn client;\n\t\t} catch (error) {\n\t\t\tawait client.close().catch(() => undefined);\n\t\t\tif (transport instanceof StdioTransport) {\n\t\t\t\tthis.stderrTail = transport.stderr.trim().slice(-STDERR_TAIL_CHARS) || undefined;\n\t\t\t}\n\t\t\tthrow error;\n\t\t} finally {\n\t\t\t// A delayed start may allocate resources after its first close. Retire them too.\n\t\t\tif (this.closed) await transport?.close().catch(() => undefined);\n\t\t\tif (this.initializingClient === client) {\n\t\t\t\tthis.initializingClient = undefined;\n\t\t\t\tthis.initializingTransport = undefined;\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate connectFailed(error: unknown): Error {\n\t\tif (this.needsSignIn(error) && !this.closed) {\n\t\t\tthis.markNeedsAuth();\n\t\t\treturn new Error(signInRequiredMessage(this.entry));\n\t\t}\n\t\tthis.state = this.closed ? \"closed\" : \"failed\";\n\t\tthis.error = this.stderrTail ? `${errorMessage(error)}\\n${this.stderrTail}` : errorMessage(error);\n\t\tthis.changed();\n\t\treturn new Error(`MCP server \"${this.entry.name}\" failed to connect: ${this.error}`);\n\t}\n\n\t/** The transport dropped. The next call reconnects; until then the status shows why. */\n\tprivate handleClientClose(client: McpClient, stdio: StdioTransport | undefined): void {\n\t\tif (this.client !== client || this.closed) return;\n\t\tthis.client = undefined;\n\t\tthis.state = \"disconnected\";\n\t\tconst stderr = stdio?.stderr.trim().slice(-STDERR_TAIL_CHARS);\n\t\tthis.error = stderr ? `Connection closed\\n${stderr}` : \"Connection closed\";\n\t\tthis.changed();\n\t}\n\n\tprivate async refreshTools(client: McpClient): Promise<void> {\n\t\ttry {\n\t\t\tconst tools = await this.runWithClient(client, (client) => client.listTools());\n\t\t\tif (this.client !== client || this.closed) return;\n\t\t\tthis.tools = tools;\n\t\t\tthis.onTools(this);\n\t\t} catch (error) {\n\t\t\tthis.error = `Failed to refresh tools: ${errorMessage(error)}`;\n\t\t}\n\t\tthis.changed();\n\t}\n\n\tprivate async refreshResources(client: McpClient): Promise<void> {\n\t\tconst { resources, resourceTemplates } = await this.runWithClient(client, fetchResources);\n\t\tif (this.client !== client || this.closed) return;\n\t\tthis.resources = resources;\n\t\tthis.resourceTemplates = resourceTemplates;\n\t\tthis.onTools(this);\n\t\tthis.changed();\n\t}\n\n\tclose(): Promise<void> {\n\t\tif (this.closing) return this.closing;\n\t\tthis.closed = true;\n\t\tthis.state = \"closed\";\n\t\tthis.changed();\n\t\tconst client = this.client;\n\t\tthis.client = undefined;\n\t\tconst opening = this.opening;\n\t\tconst initializingClient = this.initializingClient;\n\t\tconst initializingTransport = this.initializingTransport;\n\t\t// Close immediately, before waiting for setup: start itself may need close to unblock.\n\t\tconst retirement = Promise.allSettled([\n\t\t\tclient?.close(),\n\t\t\tinitializingClient?.close(),\n\t\t\tinitializingTransport?.close(),\n\t\t\topening,\n\t\t\t...[...this.retiringClients.values()].map((retirement) => retirement.close()),\n\t\t]);\n\t\tthis.closing = (async () => {\n\t\t\tawait boundedRetirement(retirement);\n\t\t\t// Preserve an already-rotated refresh token before exiting.\n\t\t\tawait this.authProvider?.settled();\n\t\t})();\n\t\treturn this.closing;\n\t}\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runtime.lazy.d.ts","sourceRoot":"","sources":["../../../src/extensions/mcp/runtime.lazy.ts"],"names":[],"mappings":"AAAA,yFAAyF;AACzF,eAAO,MAAM,cAAc,8CAA+B,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runtime.lazy.js","sourceRoot":"","sources":["../../../src/extensions/mcp/runtime.lazy.ts"],"names":[],"mappings":"AAAA,yFAAyF;AACzF,MAAM,CAAC,MAAM,cAAc,GAAG,GAAG,EAAE,CAAC,MAAM,CAAC,cAAc,CAAC,CAAC","sourcesContent":["/** Loads the MCP client, transports, and OAuth sign-in on first use (see runtime.ts). */\nexport const loadMcpRuntime = () => import(\"./runtime.ts\");\n"]}
|