@earendil-works/pi-coding-agent 1.0.3 → 1.1.0
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 +75 -0
- package/dist/bundle/chunks/{anthropic-messages-Y22YDKSW.js → anthropic-messages-KT2ID7DZ.js} +1 -1
- package/dist/bundle/chunks/anthropic.js +1 -1
- package/dist/bundle/chunks/{azure-openai-responses-YJDJIIDQ.js → azure-openai-responses-6A4PNZTH.js} +1 -1
- package/dist/bundle/chunks/bedrock-converse-stream.js +2 -2
- package/dist/bundle/chunks/chunk-33CRHCAK.js +2 -0
- package/dist/bundle/chunks/{chunk-UBHCV62G.js → chunk-BK6FEHYX.js} +3 -3
- package/dist/bundle/chunks/chunk-CH4ZBH45.js +11 -0
- package/dist/bundle/chunks/{chunk-6DBOMCBS.js → chunk-GXLAGZ3Z.js} +1 -1
- package/dist/bundle/chunks/{chunk-J52J6TFP.js → chunk-HKYL362W.js} +1 -1
- package/dist/bundle/chunks/chunk-LPXNMA2W.js +2 -0
- package/dist/bundle/chunks/{chunk-TAOL3JFA.js → chunk-LTSARG5Q.js} +1 -1
- package/dist/bundle/chunks/{chunk-ZSQEXT6X.js → chunk-MXTZIDYW.js} +4 -4
- package/dist/bundle/chunks/chunk-O7YIWAPD.js +2 -0
- package/dist/bundle/chunks/{chunk-BFNE7BHG.js → chunk-OIM2DMFI.js} +83 -72
- package/dist/bundle/chunks/{chunk-AXOERKKW.js → chunk-OTL6MGLJ.js} +6 -4
- package/dist/bundle/chunks/{chunk-LD2P6SFZ.js → chunk-PMRZO46Y.js} +1 -1
- package/dist/bundle/chunks/{chunk-7TP2J6F2.js → chunk-SYCGMWFH.js} +1 -1
- package/dist/bundle/chunks/{chunk-2VBQLN7Y.js → chunk-UH5J7I7X.js} +9 -6
- package/dist/bundle/chunks/{chunk-WE4MXUQS.js → chunk-VLRCF7MR.js} +1 -1
- package/dist/bundle/chunks/chunk-W5MPVSHA.js +82 -0
- package/dist/bundle/chunks/chunk-Y4NYF362.js +2 -0
- package/dist/bundle/chunks/{cli-HTJP6F6Z.js → cli-OQYLWLA3.js} +2 -2
- package/dist/bundle/chunks/cloudflare-workers-ai-system-one-U3AH4ODQ.js +2 -0
- package/dist/bundle/chunks/codemode-worker.js +101 -5
- package/dist/bundle/chunks/{easter-egg-3d-6YCNESG3.js → easter-egg-3d-TQFQS342.js} +1 -1
- package/dist/bundle/chunks/{execute-JRUOLYDA.js → execute-7IRB5TUX.js} +110 -8
- package/dist/bundle/chunks/github-copilot.js +1 -1
- package/dist/bundle/chunks/{google-generative-ai-XMFHGQC5.js → google-generative-ai-X2VFFS4I.js} +1 -1
- package/dist/bundle/chunks/{google-vertex-4XRRNP74.js → google-vertex-KMGHFTSP.js} +1 -1
- package/dist/bundle/chunks/image-resize-worker.js +1 -1
- package/dist/bundle/chunks/{llama-cpp-classify-XT44PI3C.js → llama-cpp-classify-FYN623RH.js} +2 -2
- package/dist/bundle/chunks/{mistral-conversations-Q22M3IBG.js → mistral-conversations-PRZLM26U.js} +2 -2
- package/dist/bundle/chunks/openai-chatgpt.js +1 -1
- package/dist/bundle/chunks/{openai-codex-responses-4MYQSO5E.js → openai-codex-responses-ZSLJZYOE.js} +2 -2
- package/dist/bundle/chunks/openai-codex.js +1 -1
- package/dist/bundle/chunks/{openai-completions-OSRCWAAA.js → openai-completions-WWQW7S3V.js} +1 -1
- package/dist/bundle/chunks/openai-decisions-QLZKZLAK.js +5 -0
- package/dist/bundle/chunks/{openai-responses-UHF3DHNG.js → openai-responses-KV7QHJY6.js} +1 -1
- package/dist/bundle/chunks/{openrouter-images-ZVXWCSIM.js → openrouter-images-DUPG5DZO.js} +1 -1
- package/dist/bundle/chunks/{pi-messages-TM5SOOIW.js → pi-messages-YHKPQIHC.js} +1 -1
- package/dist/bundle/chunks/{runtime-NW7GPWKS.js → runtime-ROGZ2EXI.js} +1 -1
- package/dist/bundle/chunks/typesafe-system-one-B4NNEG67.js +2 -0
- package/dist/bundle/chunks/{virtual-modules-CFHJO5SY.js → virtual-modules-4YN2GNVW.js} +1 -1
- package/dist/bundle/cli-runtime.js +1 -1
- package/dist/bundle/index.js +1 -1
- package/dist/bundle/rpc-entry.js +1 -1
- package/dist/cli/args.d.ts +2 -1
- package/dist/cli/args.d.ts.map +1 -1
- package/dist/cli/args.js +24 -5
- package/dist/cli/args.js.map +1 -1
- package/dist/core/agent-session.d.ts +33 -4
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +59 -21
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/bash-executor.d.ts.map +1 -1
- package/dist/core/bash-executor.js +40 -45
- package/dist/core/bash-executor.js.map +1 -1
- package/dist/core/export-html/tool-renderer.d.ts.map +1 -1
- package/dist/core/export-html/tool-renderer.js +2 -0
- package/dist/core/export-html/tool-renderer.js.map +1 -1
- package/dist/core/extensions/types.d.ts +13 -0
- package/dist/core/extensions/types.d.ts.map +1 -1
- package/dist/core/extensions/types.js.map +1 -1
- package/dist/core/mcp-servers.d.ts +8 -0
- package/dist/core/mcp-servers.d.ts.map +1 -1
- package/dist/core/mcp-servers.js +30 -7
- package/dist/core/mcp-servers.js.map +1 -1
- package/dist/core/model-runtime.d.ts.map +1 -1
- package/dist/core/model-runtime.js +2 -1
- package/dist/core/model-runtime.js.map +1 -1
- package/dist/core/nested-tool-calls.d.ts +1 -0
- package/dist/core/nested-tool-calls.d.ts.map +1 -1
- package/dist/core/nested-tool-calls.js +1 -0
- package/dist/core/nested-tool-calls.js.map +1 -1
- package/dist/core/resource-loader.d.ts +3 -0
- package/dist/core/resource-loader.d.ts.map +1 -1
- package/dist/core/resource-loader.js +4 -3
- package/dist/core/resource-loader.js.map +1 -1
- package/dist/core/sdk.d.ts +13 -4
- package/dist/core/sdk.d.ts.map +1 -1
- package/dist/core/sdk.js +19 -6
- package/dist/core/sdk.js.map +1 -1
- package/dist/core/settings-manager.d.ts +13 -0
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js +37 -13
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/core/skills.d.ts +4 -1
- package/dist/core/skills.d.ts.map +1 -1
- package/dist/core/skills.js +6 -1
- package/dist/core/skills.js.map +1 -1
- package/dist/core/system-prompt.d.ts +6 -0
- package/dist/core/system-prompt.d.ts.map +1 -1
- package/dist/core/system-prompt.js +9 -4
- package/dist/core/system-prompt.js.map +1 -1
- package/dist/core/tools/read.d.ts +13 -0
- package/dist/core/tools/read.d.ts.map +1 -1
- package/dist/core/tools/read.js +18 -1
- package/dist/core/tools/read.js.map +1 -1
- package/dist/core/tools/renderers/bash.d.ts.map +1 -1
- package/dist/core/tools/renderers/bash.js +8 -3
- package/dist/core/tools/renderers/bash.js.map +1 -1
- package/dist/core/tools/renderers/edit.d.ts.map +1 -1
- package/dist/core/tools/renderers/edit.js +5 -4
- package/dist/core/tools/renderers/edit.js.map +1 -1
- package/dist/extensions/codemode/execute.d.ts.map +1 -1
- package/dist/extensions/codemode/execute.js +65 -8
- package/dist/extensions/codemode/execute.js.map +1 -1
- package/dist/extensions/codemode/index.d.ts.map +1 -1
- package/dist/extensions/codemode/index.js +1 -0
- package/dist/extensions/codemode/index.js.map +1 -1
- package/dist/extensions/codemode/tool.d.ts +9 -2
- package/dist/extensions/codemode/tool.d.ts.map +1 -1
- package/dist/extensions/codemode/tool.js +14 -6
- package/dist/extensions/codemode/tool.js.map +1 -1
- package/dist/extensions/llama/provider.d.ts.map +1 -1
- package/dist/extensions/llama/provider.js +34 -11
- package/dist/extensions/llama/provider.js.map +1 -1
- package/dist/extensions/mcp/cli.js +17 -23
- package/dist/extensions/mcp/cli.js.map +1 -1
- package/dist/extensions/mcp/index.d.ts.map +1 -1
- package/dist/extensions/mcp/index.js +165 -64
- package/dist/extensions/mcp/index.js.map +1 -1
- package/dist/extensions/mcp/oauth.d.ts +5 -3
- package/dist/extensions/mcp/oauth.d.ts.map +1 -1
- package/dist/extensions/mcp/oauth.js +36 -10
- package/dist/extensions/mcp/oauth.js.map +1 -1
- package/dist/extensions/mcp/resources.d.ts +3 -4
- package/dist/extensions/mcp/resources.d.ts.map +1 -1
- package/dist/extensions/mcp/resources.js +3 -4
- package/dist/extensions/mcp/resources.js.map +1 -1
- package/dist/extensions/mcp/runtime.d.ts +4 -1
- package/dist/extensions/mcp/runtime.d.ts.map +1 -1
- package/dist/extensions/mcp/runtime.js +22 -5
- package/dist/extensions/mcp/runtime.js.map +1 -1
- package/dist/extensions/mcp/tools.d.ts +2 -1
- package/dist/extensions/mcp/tools.d.ts.map +1 -1
- package/dist/extensions/mcp/tools.js +2 -1
- package/dist/extensions/mcp/tools.js.map +1 -1
- package/dist/extensions/mcp/ui.d.ts +3 -3
- package/dist/extensions/mcp/ui.d.ts.map +1 -1
- package/dist/extensions/mcp/ui.js +10 -2
- package/dist/extensions/mcp/ui.js.map +1 -1
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +1 -0
- package/dist/main.js.map +1 -1
- package/dist/modes/interactive/components/bash-execution.d.ts +5 -1
- package/dist/modes/interactive/components/bash-execution.d.ts.map +1 -1
- package/dist/modes/interactive/components/bash-execution.js +17 -14
- package/dist/modes/interactive/components/bash-execution.js.map +1 -1
- package/dist/modes/interactive/components/branch-summary-message.d.ts +2 -1
- package/dist/modes/interactive/components/branch-summary-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/branch-summary-message.js +5 -2
- package/dist/modes/interactive/components/branch-summary-message.js.map +1 -1
- package/dist/modes/interactive/components/compaction-summary-message.d.ts +2 -1
- package/dist/modes/interactive/components/compaction-summary-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/compaction-summary-message.js +5 -2
- package/dist/modes/interactive/components/compaction-summary-message.js.map +1 -1
- package/dist/modes/interactive/components/custom-entry.d.ts +3 -1
- package/dist/modes/interactive/components/custom-entry.d.ts.map +1 -1
- package/dist/modes/interactive/components/custom-entry.js +8 -2
- package/dist/modes/interactive/components/custom-entry.js.map +1 -1
- package/dist/modes/interactive/components/custom-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/custom-message.js +1 -0
- package/dist/modes/interactive/components/custom-message.js.map +1 -1
- package/dist/modes/interactive/components/settings-selector.js +1 -1
- package/dist/modes/interactive/components/settings-selector.js.map +1 -1
- package/dist/modes/interactive/components/skill-invocation-message.d.ts +2 -1
- package/dist/modes/interactive/components/skill-invocation-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/skill-invocation-message.js +5 -2
- package/dist/modes/interactive/components/skill-invocation-message.js.map +1 -1
- package/dist/modes/interactive/components/tool-execution.d.ts +5 -0
- package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
- package/dist/modes/interactive/components/tool-execution.js +10 -0
- package/dist/modes/interactive/components/tool-execution.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts +2 -0
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +50 -29
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/modes/interactive/program-status-reporter.d.ts +34 -0
- package/dist/modes/interactive/program-status-reporter.d.ts.map +1 -0
- package/dist/modes/interactive/program-status-reporter.js +110 -0
- package/dist/modes/interactive/program-status-reporter.js.map +1 -0
- package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
- package/dist/modes/interactive/theme/theme.js +1 -0
- package/dist/modes/interactive/theme/theme.js.map +1 -1
- package/dist/package-manager-cli.d.ts.map +1 -1
- package/dist/package-manager-cli.js +24 -0
- package/dist/package-manager-cli.js.map +1 -1
- package/dist/utils/ansi.d.ts +8 -0
- package/dist/utils/ansi.d.ts.map +1 -1
- package/dist/utils/ansi.js +30 -11
- package/dist/utils/ansi.js.map +1 -1
- package/dist/utils/clipboard.d.ts.map +1 -1
- package/dist/utils/clipboard.js +12 -11
- package/dist/utils/clipboard.js.map +1 -1
- package/dist/utils/image-resize-core.d.ts +12 -0
- package/dist/utils/image-resize-core.d.ts.map +1 -1
- package/dist/utils/image-resize-core.js +5 -0
- package/dist/utils/image-resize-core.js.map +1 -1
- package/dist/utils/image-resize-worker.js +3 -2
- package/dist/utils/image-resize-worker.js.map +1 -1
- package/dist/utils/image-resize.d.ts.map +1 -1
- package/dist/utils/image-resize.js +9 -10
- package/dist/utils/image-resize.js.map +1 -1
- package/dist/utils/syntax-highlight.d.ts.map +1 -1
- package/dist/utils/syntax-highlight.js +6 -1
- package/dist/utils/syntax-highlight.js.map +1 -1
- package/docs/cli.md +18 -6
- package/docs/codemode.md +26 -5
- package/docs/environment-variables.md +1 -0
- package/docs/extensions.md +1 -1
- package/docs/json.md +3 -3
- package/docs/llama-cpp.md +8 -1
- package/docs/mcp.md +3 -1
- package/docs/message-types.md +22 -4
- package/docs/models.md +4 -1
- package/docs/providers.md +27 -0
- package/docs/settings.md +3 -3
- package/docs/terminal-setup.md +14 -0
- package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
- package/examples/extensions/custom-provider-anthropic/package.json +1 -1
- package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
- package/examples/extensions/gondolin/package-lock.json +2 -2
- package/examples/extensions/gondolin/package.json +1 -1
- package/examples/extensions/sandbox/package-lock.json +2 -2
- package/examples/extensions/sandbox/package.json +1 -1
- package/examples/extensions/with-deps/package-lock.json +2 -2
- package/examples/extensions/with-deps/package.json +1 -1
- package/package.json +12 -12
- package/dist/bundle/chunks/chunk-A3JYRWB6.js +0 -2
- package/dist/bundle/chunks/chunk-C47CLQZK.js +0 -2
- package/dist/bundle/chunks/chunk-HQHD6PZI.js +0 -2
- package/dist/bundle/chunks/chunk-KJUDFE6Y.js +0 -11
- package/dist/bundle/chunks/chunk-PSQJVYXD.js +0 -82
- package/dist/bundle/chunks/cloudflare-workers-ai-system-one-NM3JRRWQ.js +0 -2
- package/dist/bundle/chunks/typesafe-system-one-PRR5K6CI.js +0 -2
|
@@ -26,8 +26,11 @@ const CLIENT_METADATA_BASE_URL = "https://pi.dev/oauth";
|
|
|
26
26
|
const FALLBACK_REDIRECT_URL = `http://${CALLBACK_HOST}${CALLBACK_PATH}`;
|
|
27
27
|
/** Access tokens this close to expiry are refreshed before they are sent. */
|
|
28
28
|
const REFRESH_SKEW_MS = 30_000;
|
|
29
|
-
/**
|
|
30
|
-
|
|
29
|
+
/**
|
|
30
|
+
* Bounds each request to the authorization server, so an unresponsive one cannot hold the refresh lock,
|
|
31
|
+
* delay shutdown, or keep a sign-in waiting for long.
|
|
32
|
+
*/
|
|
33
|
+
const OAUTH_REQUEST_TIMEOUT_MS = 15_000;
|
|
31
34
|
/** A refresh lock that its holder stopped renewing (the process was killed) is taken over after this. */
|
|
32
35
|
const REFRESH_LOCK_STALE_MS = 20_000;
|
|
33
36
|
/** How long to wait for another process's refresh: longer than a stale lock lives. */
|
|
@@ -157,6 +160,13 @@ export class McpOAuthCredentialStore {
|
|
|
157
160
|
});
|
|
158
161
|
}
|
|
159
162
|
}
|
|
163
|
+
/** `fetch` with `OAUTH_REQUEST_TIMEOUT_MS` per request, on top of the request's own signal. */
|
|
164
|
+
function timedFetch(fetch = globalThis.fetch) {
|
|
165
|
+
return (input, init) => {
|
|
166
|
+
const timeout = AbortSignal.timeout(OAUTH_REQUEST_TIMEOUT_MS);
|
|
167
|
+
return fetch(input, { ...init, signal: init?.signal ? AbortSignal.any([init.signal, timeout]) : timeout });
|
|
168
|
+
};
|
|
169
|
+
}
|
|
160
170
|
function registeredRedirectUrls(client) {
|
|
161
171
|
return client && "redirect_uris" in client ? client.redirect_uris : [];
|
|
162
172
|
}
|
|
@@ -229,13 +239,14 @@ export function createMcpAuthProvider(options) {
|
|
|
229
239
|
registeredRedirectUrls(state.clientInformation)[0] ??
|
|
230
240
|
FALLBACK_REDIRECT_URL;
|
|
231
241
|
const provider = createProvider(serverUrl, store, settings, redirectUrl, () => { });
|
|
232
|
-
// Refreshes the tokens, or reports that a new sign-in is needed.
|
|
242
|
+
// Refreshes the tokens, or reports that a new sign-in is needed. Not cancellable: a refresh the
|
|
243
|
+
// server answered may have rotated the refresh token, so its answer must be saved.
|
|
233
244
|
const result = await authorizeMcp(provider, {
|
|
234
245
|
serverUrl,
|
|
235
246
|
resourceMetadataUrl: challenge?.resourceMetadataUrl,
|
|
236
247
|
authorizationServerMetadataUrl: settings.authServerMetadataUrl,
|
|
237
248
|
scope: challenge?.scope,
|
|
238
|
-
fetch: (
|
|
249
|
+
fetch: timedFetch(fetch),
|
|
239
250
|
});
|
|
240
251
|
if (result === "REDIRECT")
|
|
241
252
|
throw new McpOAuthAuthorizationRequiredError();
|
|
@@ -298,11 +309,15 @@ function responseFromRedirectUrl(input, state, redirectUrl) {
|
|
|
298
309
|
throw new Error("The redirect URL does not contain an authorization code");
|
|
299
310
|
return { code, iss: url.searchParams.get("iss") ?? undefined };
|
|
300
311
|
}
|
|
301
|
-
/**
|
|
302
|
-
|
|
312
|
+
/**
|
|
313
|
+
* Wait for the browser callback or a pasted redirect URL, whichever comes first. Aborting `signal`
|
|
314
|
+
* aborts the prompt, which then cancels the sign-in.
|
|
315
|
+
*/
|
|
316
|
+
async function waitForAuthorizationResponse(callback, state, redirectUrl, prompt, signal) {
|
|
303
317
|
const controller = new AbortController();
|
|
318
|
+
const promptSignal = signal ? AbortSignal.any([controller.signal, signal]) : controller.signal;
|
|
304
319
|
const fromBrowser = callback.waitForCallback(state, redirectUrl.pathname);
|
|
305
|
-
const fromUser = prompt.promptForRedirectUrl(
|
|
320
|
+
const fromUser = prompt.promptForRedirectUrl(promptSignal).then((input) => {
|
|
306
321
|
if (!input?.trim())
|
|
307
322
|
throw new McpSignInCancelledError();
|
|
308
323
|
return responseFromRedirectUrl(input, state, redirectUrl);
|
|
@@ -339,10 +354,13 @@ async function listenForCallback(settings, extraPaths, port, required) {
|
|
|
339
354
|
}
|
|
340
355
|
/**
|
|
341
356
|
* Sign in to an MCP server. Uses the stored refresh token when possible; otherwise runs the browser
|
|
342
|
-
* authorization code flow. Tokens are saved to `store`.
|
|
357
|
+
* authorization code flow. Tokens are saved to `store`. Aborting `signal` stops the sign-in at any
|
|
358
|
+
* step with `McpSignInCancelledError`; requests to the authorization server are also time-limited.
|
|
343
359
|
*/
|
|
344
360
|
export async function signInMcpServer(options) {
|
|
345
|
-
const { serverUrl, store, settings } = options;
|
|
361
|
+
const { serverUrl, store, settings, signal } = options;
|
|
362
|
+
if (signal?.aborted)
|
|
363
|
+
throw new McpSignInCancelledError();
|
|
346
364
|
const stored = await store.load();
|
|
347
365
|
const stepUp = options.challenge?.error === "insufficient_scope";
|
|
348
366
|
const callbackOptions = callbackSettings(settings);
|
|
@@ -376,6 +394,8 @@ export async function signInMcpServer(options) {
|
|
|
376
394
|
});
|
|
377
395
|
const flow = {
|
|
378
396
|
serverUrl,
|
|
397
|
+
fetch: timedFetch(),
|
|
398
|
+
signal,
|
|
379
399
|
resourceMetadataUrl: options.challenge?.resourceMetadataUrl,
|
|
380
400
|
authorizationServerMetadataUrl: settings.authServerMetadataUrl,
|
|
381
401
|
// A server asking for more scope gets it on top of the configured scope and, since the challenge
|
|
@@ -392,9 +412,15 @@ export async function signInMcpServer(options) {
|
|
|
392
412
|
// The flow picks the redirect URI, which may be specific to the MCP server.
|
|
393
413
|
const authorizationRedirectUrl = new URL(authorizationUrl.searchParams.get("redirect_uri") ?? redirectUrl);
|
|
394
414
|
options.prompt.showAuthorizationUrl(authorizationUrl);
|
|
395
|
-
const { code, iss } = await waitForAuthorizationResponse(callback, state, authorizationRedirectUrl, options.prompt);
|
|
415
|
+
const { code, iss } = await waitForAuthorizationResponse(callback, state, authorizationRedirectUrl, options.prompt, signal);
|
|
396
416
|
await authorizeMcp(provider, { ...flow, authorizationCode: code, iss });
|
|
397
417
|
}
|
|
418
|
+
catch (error) {
|
|
419
|
+
// Aborted requests fail with the signal's reason; report them as the cancellation they are.
|
|
420
|
+
if (signal?.aborted)
|
|
421
|
+
throw new McpSignInCancelledError();
|
|
422
|
+
throw error;
|
|
423
|
+
}
|
|
398
424
|
finally {
|
|
399
425
|
await callback.close();
|
|
400
426
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"oauth.js","sourceRoot":"","sources":["../../../src/extensions/mcp/oauth.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * OAuth sign-in for remote MCP servers.\n *\n * Connections never start a browser flow on their own. They send the stored access token and, after\n * a 401, try the stored refresh token. When that is not possible they fail with\n * `McpOAuthAuthorizationRequiredError`, and the user signs in through `/mcp`, which runs\n * the authorization code flow (PKCE, a Client ID Metadata Document or dynamic client registration)\n * against a loopback callback.\n *\n * Credentials live in `<agent-dir>/mcp-auth.json`, keyed by server name and URL.\n */\n\nimport { createHash } from \"node:crypto\";\nimport { mkdirSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { oauthErrorHtml, oauthSuccessHtml } from \"@earendil-works/pi-ai/utils/oauth-page\";\nimport type { AuthProvider, McpFetch } from \"@earendil-works/pi-mcp\";\nimport {\n\ttype AuthorizationServerMetadata,\n\tauthorizeMcp,\n\tMcpOAuthAuthorizationRequiredError,\n\tMcpOAuthProvider,\n\ttype McpOAuthState,\n\ttype McpOAuthStateStore,\n\ttype OAuthCallback,\n\ttype OAuthCallbackPage,\n\tOAuthCallbackServer,\n\ttype OAuthChallenge,\n\ttype OAuthClientInformationMixed,\n\ttype OAuthClientMetadataDocument,\n\tparseWwwAuthenticate,\n\tstepUpScope,\n} from \"@earendil-works/pi-mcp/oauth\";\nimport lockfile from \"proper-lockfile\";\nimport { APP_NAME, getAgentDir } from \"../../config.ts\";\nimport { type AuthStorageBackend, FileAuthStorageBackend } from \"../../core/auth-storage.ts\";\nimport { mcpNamespace } from \"../../core/mcp-servers.ts\";\n\nconst CALLBACK_HOST = \"127.0.0.1\";\nconst CALLBACK_PATH = \"/callback\";\n/** Where pi.dev serves pi's Client ID Metadata Documents: `client.json` and `<callback ID>/client.json`. */\nconst CLIENT_METADATA_BASE_URL = \"https://pi.dev/oauth\";\n/** Redirect URI for refreshes when none is stored. Refreshing never redirects the user. */\nconst FALLBACK_REDIRECT_URL = `http://${CALLBACK_HOST}${CALLBACK_PATH}`;\n/** Access tokens this close to expiry are refreshed before they are sent. */\nconst REFRESH_SKEW_MS = 30_000;\n/** Bounds each request of a refresh, so it cannot hold the refresh lock or delay shutdown for long. */\nconst REFRESH_REQUEST_TIMEOUT_MS = 15_000;\n/** A refresh lock that its holder stopped renewing (the process was killed) is taken over after this. */\nconst REFRESH_LOCK_STALE_MS = 20_000;\n/** How long to wait for another process's refresh: longer than a stale lock lives. */\nconst REFRESH_LOCK_WAIT_MS = 25_000;\nconst REFRESH_LOCK_RETRY_MS = 100;\n\nexport interface McpOAuthSettings {\n\tclientId?: string;\n\t/** Already resolved. */\n\tclientSecret?: string;\n\tcallbackPort?: number;\n\t/** Loopback redirect URI; see `McpOAuthConfig.callbackUrl`. */\n\tcallbackUrl?: string;\n\t/** Scopes to request, separated by spaces. */\n\tscope?: string;\n\t/** `client_name` for dynamic client registration. Default: `APP_NAME`. */\n\tclientName?: string;\n\t/** See `McpOAuthConfig.clientRegistration`. */\n\tclientRegistration?: \"dcr\" | \"cimd\";\n\t/** See `McpOAuthConfig.authServerMetadataUrl`. */\n\tauthServerMetadataUrl?: URL;\n}\n\n/** Where the loopback callback server listens and the redirect URI it serves. */\ninterface CallbackSettings {\n\t/** Address to listen on. */\n\thost: string;\n\t/** Host name in the redirect URI. */\n\tredirectHost: string;\n\tport: number | undefined;\n\tpath: string;\n\t/** The exact redirect URI, when the port is fixed. */\n\tfixedRedirectUrl: string | undefined;\n}\n\nfunction callbackSettings(settings: McpOAuthSettings): CallbackSettings {\n\tconst url = new URL(settings.callbackUrl ?? `http://${CALLBACK_HOST}${CALLBACK_PATH}`);\n\tconst address = url.hostname.replace(/^\\[|\\]$/g, \"\");\n\tconst port = url.port ? Number(url.port) : settings.callbackPort;\n\tlet fixedRedirectUrl: string | undefined;\n\t// A configured URI with a port is sent exactly as written, since servers compare it as a string.\n\tif (url.port) fixedRedirectUrl = settings.callbackUrl;\n\telse if (port !== undefined) {\n\t\turl.port = String(port);\n\t\tfixedRedirectUrl = url.href;\n\t}\n\treturn {\n\t\t// `localhost` is served on 127.0.0.1; browsers fall back to it when ::1 refuses.\n\t\thost: address === \"localhost\" ? CALLBACK_HOST : address,\n\t\tredirectHost: address,\n\t\tport,\n\t\tpath: url.pathname,\n\t\tfixedRedirectUrl,\n\t};\n}\n\n/** Scopes of both lists, each once. */\nfunction mergeScopes(...scopes: (string | undefined)[]): string | undefined {\n\tconst merged = [...new Set(scopes.flatMap((scope) => scope?.split(/\\s+/).filter(Boolean) ?? []))];\n\treturn merged.length > 0 ? merged.join(\" \") : undefined;\n}\n\ntype StoredStates = Record<string, McpOAuthState>;\n\nfunction parseStates(content: string | undefined): StoredStates {\n\tif (!content?.trim()) return {};\n\tconst parsed: unknown = JSON.parse(content);\n\treturn typeof parsed === \"object\" && parsed !== null && !Array.isArray(parsed) ? (parsed as StoredStates) : {};\n}\n\nfunction serializeStates(states: StoredStates): string {\n\treturn `${JSON.stringify(states, null, 2)}\\n`;\n}\n\n/**\n * Keys of a server's state: by name and URL, so servers sharing a URL keep separate accounts, and the\n * legacy key by URL alone, written by older versions.\n */\nfunction storeKeys(name: string, serverUrl: string): { key: string; legacyKey: string } {\n\tconst legacyKey = String(new URL(serverUrl));\n\treturn { key: `${mcpNamespace(name)}|${legacyKey}`, legacyKey };\n}\n\nexport interface McpOAuthServerStore extends McpOAuthStateStore {\n\t/** Run `fn` while no other process refreshes the server's tokens. */\n\twithRefreshLock<T>(fn: () => Promise<T>): Promise<T>;\n}\n\n/** Per-server OAuth state (client registration, tokens, pending PKCE verifier) in `mcp-auth.json`. */\nexport class McpOAuthCredentialStore {\n\tprivate readonly backend: AuthStorageBackend;\n\t/** Directory for the refresh lock files. Without one, refreshes are only serialized in this process. */\n\tprivate readonly lockDir: string | undefined;\n\n\tconstructor(backend?: AuthStorageBackend, lockDir?: string) {\n\t\tthis.backend = backend ?? new FileAuthStorageBackend(join(getAgentDir(), \"mcp-auth.json\"));\n\t\tthis.lockDir = backend ? lockDir : getAgentDir();\n\t}\n\n\tforServer(name: string, serverUrl: string): McpOAuthServerStore {\n\t\tconst { key, legacyKey } = storeKeys(name, serverUrl);\n\t\treturn {\n\t\t\t// The first server to load legacy state takes it over; others with the same URL sign in again.\n\t\t\tload: () =>\n\t\t\t\tthis.backend.withLock((current) => {\n\t\t\t\t\tconst states = parseStates(current);\n\t\t\t\t\tif (states[key] || !states[legacyKey]) return { result: states[key] };\n\t\t\t\t\tstates[key] = states[legacyKey];\n\t\t\t\t\tdelete states[legacyKey];\n\t\t\t\t\treturn { result: states[key], next: serializeStates(states) };\n\t\t\t\t}),\n\t\t\tsave: (state) =>\n\t\t\t\tthis.write((states) => {\n\t\t\t\t\tstates[key] = state;\n\t\t\t\t}),\n\t\t\twithRefreshLock: (fn) => this.withRefreshLock(key, fn),\n\t\t};\n\t}\n\n\t/**\n\t * A lock file per server. When the process exits, proper-lockfile removes the locks it holds; when\n\t * it is killed, the lock goes stale because it is no longer renewed, and the next process takes it over.\n\t */\n\tprivate async withRefreshLock<T>(key: string, fn: () => Promise<T>): Promise<T> {\n\t\tif (!this.lockDir) return fn();\n\t\tmkdirSync(this.lockDir, { recursive: true, mode: 0o700 });\n\t\tconst hash = createHash(\"sha256\").update(key).digest(\"hex\").slice(0, 16);\n\t\tconst release = await lockfile.lock(join(this.lockDir, `mcp-auth-refresh-${hash}`), {\n\t\t\trealpath: false,\n\t\t\tstale: REFRESH_LOCK_STALE_MS,\n\t\t\tretries: {\n\t\t\t\tretries: REFRESH_LOCK_WAIT_MS / REFRESH_LOCK_RETRY_MS,\n\t\t\t\tfactor: 1,\n\t\t\t\tminTimeout: REFRESH_LOCK_RETRY_MS,\n\t\t\t\tmaxTimeout: REFRESH_LOCK_RETRY_MS,\n\t\t\t},\n\t\t\t// The default throws from a timer. A lost lock at worst lets two refreshes overlap.\n\t\t\tonCompromised: () => {},\n\t\t});\n\t\ttry {\n\t\t\treturn await fn();\n\t\t} finally {\n\t\t\tawait release().catch(() => undefined);\n\t\t}\n\t}\n\n\t/** The stored tokens of a server, for noticing sign-ins done by another process. Does not take over legacy state. */\n\ttokens(name: string, serverUrl: string): McpOAuthState[\"tokens\"] {\n\t\tconst { key, legacyKey } = storeKeys(name, serverUrl);\n\t\tconst states = this.backend.withLock((current) => ({ result: parseStates(current) }));\n\t\treturn (states[key] ?? states[legacyKey])?.tokens;\n\t}\n\n\t/** Returns whether credentials were stored for the server. Removes legacy state the server would take over. */\n\tremove(name: string, serverUrl: string): boolean {\n\t\tconst { key, legacyKey } = storeKeys(name, serverUrl);\n\t\treturn this.backend.withLock((current) => {\n\t\t\tconst states = parseStates(current);\n\t\t\tconst stored = key in states ? key : legacyKey in states ? legacyKey : undefined;\n\t\t\tif (!stored) return { result: false };\n\t\t\tdelete states[stored];\n\t\t\treturn { result: true, next: serializeStates(states) };\n\t\t});\n\t}\n\n\tprivate write(update: (states: StoredStates) => void): void {\n\t\tthis.backend.withLock((current) => {\n\t\t\tconst states = parseStates(current);\n\t\t\tupdate(states);\n\t\t\treturn { result: undefined, next: serializeStates(states) };\n\t\t});\n\t}\n}\n\nfunction registeredRedirectUrls(client: OAuthClientInformationMixed | undefined): string[] {\n\treturn client && \"redirect_uris\" in client ? client.redirect_uris : [];\n}\n\n/** 12 characters identifying an MCP server URL in callback paths, computed like Codex does. */\nfunction callbackId(serverUrl: string): string {\n\tconst url = new URL(serverUrl);\n\turl.hash = \"\";\n\treturn createHash(\"sha256\").update(url.href).digest().subarray(0, 9).toString(\"base64url\");\n}\n\n/**\n * pi's Client ID Metadata Document, for `clientRegistration: \"cimd\"`, chosen like Codex chooses its own.\n * The configuration ensures the default callback path. Without the `iss` parameter in authorization\n * responses (RFC 9207), the redirect URI and the document are specific to the MCP server, so a response\n * cannot be mixed up with one from another authorization server (RFC 9700 section 4.4.2.2).\n */\nfunction clientMetadataDocument(\n\tserverUrl: string,\n\tredirectUrl: string,\n\tmetadata: AuthorizationServerMetadata | undefined,\n): OAuthClientMetadataDocument {\n\tif (\n\t\t!metadata?.client_id_metadata_document_supported ||\n\t\t!metadata.token_endpoint_auth_methods_supported?.includes(\"none\")\n\t) {\n\t\tthrow new Error(\n\t\t\t'The authorization server does not support Client ID Metadata Documents for public clients; remove oauth.clientRegistration \"cimd\"',\n\t\t);\n\t}\n\tif (metadata.authorization_response_iss_parameter_supported) {\n\t\treturn { url: `${CLIENT_METADATA_BASE_URL}/client.json`, redirectUrl };\n\t}\n\tconst id = callbackId(serverUrl);\n\tconst redirect = new URL(redirectUrl);\n\tredirect.pathname = `${CALLBACK_PATH}/${id}`;\n\treturn { url: `${CLIENT_METADATA_BASE_URL}/${id}/client.json`, redirectUrl: redirect.href };\n}\n\nfunction createProvider(\n\tserverUrl: string,\n\tstore: McpOAuthStateStore,\n\tsettings: McpOAuthSettings,\n\tredirectUrl: string,\n\tonRedirect: (url: URL) => void,\n): McpOAuthProvider {\n\treturn new McpOAuthProvider({\n\t\tserverUrl,\n\t\tredirectUrl,\n\t\tclientMetadata: { client_name: settings.clientName ?? APP_NAME },\n\t\tclientMetadataDocument:\n\t\t\tsettings.clientRegistration === \"cimd\"\n\t\t\t\t? (metadata) => clientMetadataDocument(serverUrl, redirectUrl, metadata)\n\t\t\t\t: undefined,\n\t\tclientId: settings.clientId,\n\t\tclientSecret: settings.clientSecret,\n\t\tstore,\n\t\tonRedirect,\n\t});\n}\n\nexport interface McpAuthProvider extends AuthProvider {\n\t/** Resolves when no refresh is running, so shutdown does not drop rotated tokens before they are saved. */\n\tsettled(): Promise<void>;\n}\n\n/**\n * Auth provider for MCP connections: sends the stored access token and refreshes it when it is about\n * to expire or after a 401. Throws `McpOAuthAuthorizationRequiredError` when the user has to sign in,\n * including when the server asks for more scope (`insufficient_scope`). `onChallenge` receives the\n * server's `WWW-Authenticate` challenge so sign-in can use its resource metadata URL and scope.\n * `settings` is only called when a refresh is needed, so a secret that fails to resolve fails the\n * refresh instead of the whole connection setup.\n *\n * Many servers rotate refresh tokens, so two refreshes with the same refresh token lose the grant.\n * Requests in this process share one refresh, and other processes are kept out by the store's\n * refresh lock, held from reading the tokens to saving new ones. Tokens that changed meanwhile\n * (another process refreshed them, or the user signed in) are used without refreshing.\n */\nexport function createMcpAuthProvider(options: {\n\tserverUrl: string;\n\tstore: McpOAuthServerStore;\n\tsettings: () => McpOAuthSettings;\n\tonChallenge: (challenge: OAuthChallenge) => void;\n}): McpAuthProvider {\n\tconst { serverUrl, store } = options;\n\tlet refreshing: Promise<void> | undefined;\n\n\t/** Replace `staleToken`, the access token that expired or was rejected. */\n\tconst refresh = (staleToken: string | undefined, fetch: McpFetch = globalThis.fetch, challenge?: OAuthChallenge) => {\n\t\trefreshing ??= store\n\t\t\t.withRefreshLock(async () => {\n\t\t\t\tconst state = await store.load();\n\t\t\t\tif (state?.tokens?.access_token !== staleToken) return;\n\t\t\t\tif (!state?.tokens?.refresh_token) throw new McpOAuthAuthorizationRequiredError();\n\t\t\t\tconst settings = options.settings();\n\t\t\t\tconst redirectUrl =\n\t\t\t\t\tcallbackSettings(settings).fixedRedirectUrl ??\n\t\t\t\t\tregisteredRedirectUrls(state.clientInformation)[0] ??\n\t\t\t\t\tFALLBACK_REDIRECT_URL;\n\t\t\t\tconst provider = createProvider(serverUrl, store, settings, redirectUrl, () => {});\n\t\t\t\t// Refreshes the tokens, or reports that a new sign-in is needed.\n\t\t\t\tconst result = await authorizeMcp(provider, {\n\t\t\t\t\tserverUrl,\n\t\t\t\t\tresourceMetadataUrl: challenge?.resourceMetadataUrl,\n\t\t\t\t\tauthorizationServerMetadataUrl: settings.authServerMetadataUrl,\n\t\t\t\t\tscope: challenge?.scope,\n\t\t\t\t\tfetch: (input, init) =>\n\t\t\t\t\t\tfetch(input, { ...init, signal: AbortSignal.timeout(REFRESH_REQUEST_TIMEOUT_MS) }),\n\t\t\t\t});\n\t\t\t\tif (result === \"REDIRECT\") throw new McpOAuthAuthorizationRequiredError();\n\t\t\t})\n\t\t\t.finally(() => {\n\t\t\t\trefreshing = undefined;\n\t\t\t});\n\t\treturn refreshing;\n\t};\n\n\treturn {\n\t\ttoken: async () => {\n\t\t\tawait refreshing?.catch(() => undefined);\n\t\t\tconst state = await store.load();\n\t\t\tconst token = state?.tokens?.access_token;\n\t\t\tconst expired = state?.tokensExpireAt !== undefined && state.tokensExpireAt - REFRESH_SKEW_MS <= Date.now();\n\t\t\tif (!expired || !state?.tokens?.refresh_token) return token;\n\t\t\t// Failures fall through: the request goes out with the old token and a 401 decides what happens.\n\t\t\tawait refresh(token).catch(() => undefined);\n\t\t\treturn (await store.load())?.tokens?.access_token;\n\t\t},\n\t\tonUnauthorized: async (context) => {\n\t\t\tconst challenge = parseWwwAuthenticate(context.response.headers.get(\"www-authenticate\"));\n\t\t\toptions.onChallenge(challenge);\n\t\t\t// A refresh keeps the granted scope, so more scope needs a new sign-in.\n\t\t\tif (challenge.error === \"insufficient_scope\") throw new McpOAuthAuthorizationRequiredError();\n\t\t\tawait refresh(context.token, context.fetch, challenge);\n\t\t},\n\t\tsettled: async () => {\n\t\t\tawait refreshing?.catch(() => undefined);\n\t\t},\n\t};\n}\n\nexport interface McpSignInPrompt {\n\t/** Show the authorization URL to the user and open it in a browser. */\n\tshowAuthorizationUrl(url: URL): void;\n\t/**\n\t * Ask for the redirect URL from the browser address bar, for when the browser cannot reach the\n\t * loopback callback (for example over SSH). Aborted once the callback arrives. Resolves to\n\t * `undefined` or an empty string when the user cancels.\n\t */\n\tpromptForRedirectUrl(signal: AbortSignal): Promise<string | undefined>;\n}\n\nexport class McpSignInCancelledError extends Error {\n\tconstructor() {\n\t\tsuper(\"Sign-in cancelled\");\n\t\tthis.name = \"McpSignInCancelledError\";\n\t}\n}\n\ntype AuthorizationResponse = Pick<OAuthCallback, \"code\" | \"iss\">;\n\nfunction responseFromRedirectUrl(input: string, state: string, redirectUrl: URL): AuthorizationResponse {\n\tlet url: URL;\n\ttry {\n\t\turl = new URL(input.trim());\n\t} catch {\n\t\tthrow new Error(\"Expected the full redirect URL from the browser address bar\");\n\t}\n\t// A server-specific redirect URI tells authorization servers apart, so it must match exactly.\n\tif (url.origin !== redirectUrl.origin || url.pathname !== redirectUrl.pathname) {\n\t\tthrow new Error(\"The redirect URL does not match this sign-in's redirect URI\");\n\t}\n\tconst error = url.searchParams.get(\"error\");\n\tif (error) throw new Error(url.searchParams.get(\"error_description\") ?? error);\n\tif (url.searchParams.get(\"state\") !== state) throw new Error(\"The redirect URL belongs to a different sign-in\");\n\tconst code = url.searchParams.get(\"code\");\n\tif (!code) throw new Error(\"The redirect URL does not contain an authorization code\");\n\treturn { code, iss: url.searchParams.get(\"iss\") ?? undefined };\n}\n\n/** Wait for the browser callback or a pasted redirect URL, whichever comes first. */\nasync function waitForAuthorizationResponse(\n\tcallback: OAuthCallbackServer,\n\tstate: string,\n\tredirectUrl: URL,\n\tprompt: McpSignInPrompt,\n): Promise<AuthorizationResponse> {\n\tconst controller = new AbortController();\n\tconst fromBrowser = callback.waitForCallback(state, redirectUrl.pathname);\n\tconst fromUser = prompt.promptForRedirectUrl(controller.signal).then((input) => {\n\t\tif (!input?.trim()) throw new McpSignInCancelledError();\n\t\treturn responseFromRedirectUrl(input, state, redirectUrl);\n\t});\n\ttry {\n\t\treturn await Promise.race([fromBrowser, fromUser]);\n\t} finally {\n\t\t// The losing side rejects once the prompt is aborted or the callback server closes.\n\t\tcontroller.abort();\n\t\tfromBrowser.catch(() => undefined);\n\t\tfromUser.catch(() => undefined);\n\t}\n}\n\n/** Listen on `port`, or on a free port when it is taken and not `required`. */\nasync function listenForCallback(\n\tsettings: CallbackSettings,\n\textraPaths: string[],\n\tport: number | undefined,\n\trequired: boolean,\n): Promise<OAuthCallbackServer> {\n\tconst options = {\n\t\thost: settings.host,\n\t\tredirectHost: settings.redirectHost,\n\t\tpath: settings.path,\n\t\textraPaths,\n\t\trenderPage: (page: OAuthCallbackPage) =>\n\t\t\tpage.ok\n\t\t\t\t? oauthSuccessHtml(\"Signed in to the MCP server. You may now close this page.\")\n\t\t\t\t: oauthErrorHtml(page.message, page.details),\n\t};\n\ttry {\n\t\treturn await OAuthCallbackServer.listen({ ...options, port: port ?? 0 });\n\t} catch (error) {\n\t\tif (required || port === undefined) throw error;\n\t\treturn OAuthCallbackServer.listen(options);\n\t}\n}\n\n/**\n * Sign in to an MCP server. Uses the stored refresh token when possible; otherwise runs the browser\n * authorization code flow. Tokens are saved to `store`.\n */\nexport async function signInMcpServer(options: {\n\tserverUrl: string;\n\tstore: McpOAuthStateStore;\n\tsettings: McpOAuthSettings;\n\tchallenge?: OAuthChallenge;\n\tprompt: McpSignInPrompt;\n}): Promise<void> {\n\tconst { serverUrl, store, settings } = options;\n\tconst stored = await store.load();\n\tconst stepUp = options.challenge?.error === \"insufficient_scope\";\n\tconst callbackOptions = callbackSettings(settings);\n\t// Reuse the port of the registered redirect URI so the registered client stays valid.\n\tconst registered = registeredRedirectUrls(stored?.clientInformation)[0];\n\tconst preferredPort =\n\t\tcallbackOptions.port ?? (registered ? Number(new URL(registered).port) || undefined : undefined);\n\tconst cimd = settings.clientRegistration === \"cimd\";\n\tconst callback = await listenForCallback(\n\t\tcallbackOptions,\n\t\t// The redirect URI of a server-specific Client ID Metadata Document.\n\t\tcimd ? [`${CALLBACK_PATH}/${callbackId(serverUrl)}`] : [],\n\t\tpreferredPort,\n\t\tcallbackOptions.port !== undefined,\n\t);\n\tconst redirectUrl = callbackOptions.fixedRedirectUrl ?? callback.redirectUrl;\n\ttry {\n\t\tif (stored) {\n\t\t\tconst next: McpOAuthState = { ...stored };\n\t\t\t// Every sign-in gets a fresh `state` parameter.\n\t\t\tdelete next.oauthState;\n\t\t\t// A registered client cannot use another redirect URI, and its tokens belong to it. A Client ID\n\t\t\t// Metadata Document is not stored, so with one, a stored client was registered before and is replaced.\n\t\t\tconst keepClient =\n\t\t\t\tsettings.clientId ||\n\t\t\t\t(cimd ? !stored.clientInformation : registeredRedirectUrls(stored.clientInformation).includes(redirectUrl));\n\t\t\tif (!keepClient) {\n\t\t\t\tdelete next.clientInformation;\n\t\t\t\tdelete next.tokens;\n\t\t\t\tdelete next.tokensExpireAt;\n\t\t\t}\n\t\t\tawait store.save(next);\n\t\t}\n\n\t\tlet authorizationUrl: URL | undefined;\n\t\tconst provider = createProvider(serverUrl, store, settings, redirectUrl, (url) => {\n\t\t\tauthorizationUrl = url;\n\t\t});\n\t\tconst flow = {\n\t\t\tserverUrl,\n\t\t\tresourceMetadataUrl: options.challenge?.resourceMetadataUrl,\n\t\t\tauthorizationServerMetadataUrl: settings.authServerMetadataUrl,\n\t\t\t// A server asking for more scope gets it on top of the configured scope and, since the challenge\n\t\t\t// may list only the missing scopes, on top of the scope granted so far.\n\t\t\tscope: mergeScopes(\n\t\t\t\tsettings.scope,\n\t\t\t\tstepUp ? stepUpScope(stored?.tokens?.scope, options.challenge?.scope) : options.challenge?.scope,\n\t\t\t),\n\t\t};\n\t\t// A refresh keeps the granted scope; a server asking for more needs the browser flow.\n\t\tconst skipRefresh = stepUp;\n\t\tif ((await authorizeMcp(provider, { ...flow, skipRefresh })) === \"AUTHORIZED\") return;\n\t\tif (!authorizationUrl) throw new Error(\"OAuth flow did not produce an authorization URL\");\n\n\t\tconst state = await provider.state();\n\t\t// The flow picks the redirect URI, which may be specific to the MCP server.\n\t\tconst authorizationRedirectUrl = new URL(authorizationUrl.searchParams.get(\"redirect_uri\") ?? redirectUrl);\n\t\toptions.prompt.showAuthorizationUrl(authorizationUrl);\n\t\tconst { code, iss } = await waitForAuthorizationResponse(\n\t\t\tcallback,\n\t\t\tstate,\n\t\t\tauthorizationRedirectUrl,\n\t\t\toptions.prompt,\n\t\t);\n\t\tawait authorizeMcp(provider, { ...flow, authorizationCode: code, iss });\n\t} finally {\n\t\tawait callback.close();\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"file":"oauth.js","sourceRoot":"","sources":["../../../src/extensions/mcp/oauth.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * OAuth sign-in for remote MCP servers.\n *\n * Connections never start a browser flow on their own. They send the stored access token and, after\n * a 401, try the stored refresh token. When that is not possible they fail with\n * `McpOAuthAuthorizationRequiredError`, and the user signs in through `/mcp`, which runs\n * the authorization code flow (PKCE, a Client ID Metadata Document or dynamic client registration)\n * against a loopback callback.\n *\n * Credentials live in `<agent-dir>/mcp-auth.json`, keyed by server name and URL.\n */\n\nimport { createHash } from \"node:crypto\";\nimport { mkdirSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { oauthErrorHtml, oauthSuccessHtml } from \"@earendil-works/pi-ai/utils/oauth-page\";\nimport type { AuthProvider, McpFetch } from \"@earendil-works/pi-mcp\";\nimport {\n\ttype AuthorizationServerMetadata,\n\tauthorizeMcp,\n\tMcpOAuthAuthorizationRequiredError,\n\tMcpOAuthProvider,\n\ttype McpOAuthState,\n\ttype McpOAuthStateStore,\n\ttype OAuthCallback,\n\ttype OAuthCallbackPage,\n\tOAuthCallbackServer,\n\ttype OAuthChallenge,\n\ttype OAuthClientInformationMixed,\n\ttype OAuthClientMetadataDocument,\n\tparseWwwAuthenticate,\n\tstepUpScope,\n} from \"@earendil-works/pi-mcp/oauth\";\nimport lockfile from \"proper-lockfile\";\nimport { APP_NAME, getAgentDir } from \"../../config.ts\";\nimport { type AuthStorageBackend, FileAuthStorageBackend } from \"../../core/auth-storage.ts\";\nimport { mcpNamespace } from \"../../core/mcp-servers.ts\";\n\nconst CALLBACK_HOST = \"127.0.0.1\";\nconst CALLBACK_PATH = \"/callback\";\n/** Where pi.dev serves pi's Client ID Metadata Documents: `client.json` and `<callback ID>/client.json`. */\nconst CLIENT_METADATA_BASE_URL = \"https://pi.dev/oauth\";\n/** Redirect URI for refreshes when none is stored. Refreshing never redirects the user. */\nconst FALLBACK_REDIRECT_URL = `http://${CALLBACK_HOST}${CALLBACK_PATH}`;\n/** Access tokens this close to expiry are refreshed before they are sent. */\nconst REFRESH_SKEW_MS = 30_000;\n/**\n * Bounds each request to the authorization server, so an unresponsive one cannot hold the refresh lock,\n * delay shutdown, or keep a sign-in waiting for long.\n */\nconst OAUTH_REQUEST_TIMEOUT_MS = 15_000;\n/** A refresh lock that its holder stopped renewing (the process was killed) is taken over after this. */\nconst REFRESH_LOCK_STALE_MS = 20_000;\n/** How long to wait for another process's refresh: longer than a stale lock lives. */\nconst REFRESH_LOCK_WAIT_MS = 25_000;\nconst REFRESH_LOCK_RETRY_MS = 100;\n\nexport interface McpOAuthSettings {\n\tclientId?: string;\n\t/** Already resolved. */\n\tclientSecret?: string;\n\tcallbackPort?: number;\n\t/** Loopback redirect URI; see `McpOAuthConfig.callbackUrl`. */\n\tcallbackUrl?: string;\n\t/** Scopes to request, separated by spaces. */\n\tscope?: string;\n\t/** `client_name` for dynamic client registration. Default: `APP_NAME`. */\n\tclientName?: string;\n\t/** See `McpOAuthConfig.clientRegistration`. */\n\tclientRegistration?: \"dcr\" | \"cimd\";\n\t/** See `McpOAuthConfig.authServerMetadataUrl`. */\n\tauthServerMetadataUrl?: URL;\n}\n\n/** Where the loopback callback server listens and the redirect URI it serves. */\ninterface CallbackSettings {\n\t/** Address to listen on. */\n\thost: string;\n\t/** Host name in the redirect URI. */\n\tredirectHost: string;\n\tport: number | undefined;\n\tpath: string;\n\t/** The exact redirect URI, when the port is fixed. */\n\tfixedRedirectUrl: string | undefined;\n}\n\nfunction callbackSettings(settings: McpOAuthSettings): CallbackSettings {\n\tconst url = new URL(settings.callbackUrl ?? `http://${CALLBACK_HOST}${CALLBACK_PATH}`);\n\tconst address = url.hostname.replace(/^\\[|\\]$/g, \"\");\n\tconst port = url.port ? Number(url.port) : settings.callbackPort;\n\tlet fixedRedirectUrl: string | undefined;\n\t// A configured URI with a port is sent exactly as written, since servers compare it as a string.\n\tif (url.port) fixedRedirectUrl = settings.callbackUrl;\n\telse if (port !== undefined) {\n\t\turl.port = String(port);\n\t\tfixedRedirectUrl = url.href;\n\t}\n\treturn {\n\t\t// `localhost` is served on 127.0.0.1; browsers fall back to it when ::1 refuses.\n\t\thost: address === \"localhost\" ? CALLBACK_HOST : address,\n\t\tredirectHost: address,\n\t\tport,\n\t\tpath: url.pathname,\n\t\tfixedRedirectUrl,\n\t};\n}\n\n/** Scopes of both lists, each once. */\nfunction mergeScopes(...scopes: (string | undefined)[]): string | undefined {\n\tconst merged = [...new Set(scopes.flatMap((scope) => scope?.split(/\\s+/).filter(Boolean) ?? []))];\n\treturn merged.length > 0 ? merged.join(\" \") : undefined;\n}\n\ntype StoredStates = Record<string, McpOAuthState>;\n\nfunction parseStates(content: string | undefined): StoredStates {\n\tif (!content?.trim()) return {};\n\tconst parsed: unknown = JSON.parse(content);\n\treturn typeof parsed === \"object\" && parsed !== null && !Array.isArray(parsed) ? (parsed as StoredStates) : {};\n}\n\nfunction serializeStates(states: StoredStates): string {\n\treturn `${JSON.stringify(states, null, 2)}\\n`;\n}\n\n/**\n * Keys of a server's state: by name and URL, so servers sharing a URL keep separate accounts, and the\n * legacy key by URL alone, written by older versions.\n */\nfunction storeKeys(name: string, serverUrl: string): { key: string; legacyKey: string } {\n\tconst legacyKey = String(new URL(serverUrl));\n\treturn { key: `${mcpNamespace(name)}|${legacyKey}`, legacyKey };\n}\n\nexport interface McpOAuthServerStore extends McpOAuthStateStore {\n\t/** Run `fn` while no other process refreshes the server's tokens. */\n\twithRefreshLock<T>(fn: () => Promise<T>): Promise<T>;\n}\n\n/** Per-server OAuth state (client registration, tokens, pending PKCE verifier) in `mcp-auth.json`. */\nexport class McpOAuthCredentialStore {\n\tprivate readonly backend: AuthStorageBackend;\n\t/** Directory for the refresh lock files. Without one, refreshes are only serialized in this process. */\n\tprivate readonly lockDir: string | undefined;\n\n\tconstructor(backend?: AuthStorageBackend, lockDir?: string) {\n\t\tthis.backend = backend ?? new FileAuthStorageBackend(join(getAgentDir(), \"mcp-auth.json\"));\n\t\tthis.lockDir = backend ? lockDir : getAgentDir();\n\t}\n\n\tforServer(name: string, serverUrl: string): McpOAuthServerStore {\n\t\tconst { key, legacyKey } = storeKeys(name, serverUrl);\n\t\treturn {\n\t\t\t// The first server to load legacy state takes it over; others with the same URL sign in again.\n\t\t\tload: () =>\n\t\t\t\tthis.backend.withLock((current) => {\n\t\t\t\t\tconst states = parseStates(current);\n\t\t\t\t\tif (states[key] || !states[legacyKey]) return { result: states[key] };\n\t\t\t\t\tstates[key] = states[legacyKey];\n\t\t\t\t\tdelete states[legacyKey];\n\t\t\t\t\treturn { result: states[key], next: serializeStates(states) };\n\t\t\t\t}),\n\t\t\tsave: (state) =>\n\t\t\t\tthis.write((states) => {\n\t\t\t\t\tstates[key] = state;\n\t\t\t\t}),\n\t\t\twithRefreshLock: (fn) => this.withRefreshLock(key, fn),\n\t\t};\n\t}\n\n\t/**\n\t * A lock file per server. When the process exits, proper-lockfile removes the locks it holds; when\n\t * it is killed, the lock goes stale because it is no longer renewed, and the next process takes it over.\n\t */\n\tprivate async withRefreshLock<T>(key: string, fn: () => Promise<T>): Promise<T> {\n\t\tif (!this.lockDir) return fn();\n\t\tmkdirSync(this.lockDir, { recursive: true, mode: 0o700 });\n\t\tconst hash = createHash(\"sha256\").update(key).digest(\"hex\").slice(0, 16);\n\t\tconst release = await lockfile.lock(join(this.lockDir, `mcp-auth-refresh-${hash}`), {\n\t\t\trealpath: false,\n\t\t\tstale: REFRESH_LOCK_STALE_MS,\n\t\t\tretries: {\n\t\t\t\tretries: REFRESH_LOCK_WAIT_MS / REFRESH_LOCK_RETRY_MS,\n\t\t\t\tfactor: 1,\n\t\t\t\tminTimeout: REFRESH_LOCK_RETRY_MS,\n\t\t\t\tmaxTimeout: REFRESH_LOCK_RETRY_MS,\n\t\t\t},\n\t\t\t// The default throws from a timer. A lost lock at worst lets two refreshes overlap.\n\t\t\tonCompromised: () => {},\n\t\t});\n\t\ttry {\n\t\t\treturn await fn();\n\t\t} finally {\n\t\t\tawait release().catch(() => undefined);\n\t\t}\n\t}\n\n\t/** The stored tokens of a server, for noticing sign-ins done by another process. Does not take over legacy state. */\n\ttokens(name: string, serverUrl: string): McpOAuthState[\"tokens\"] {\n\t\tconst { key, legacyKey } = storeKeys(name, serverUrl);\n\t\tconst states = this.backend.withLock((current) => ({ result: parseStates(current) }));\n\t\treturn (states[key] ?? states[legacyKey])?.tokens;\n\t}\n\n\t/** Returns whether credentials were stored for the server. Removes legacy state the server would take over. */\n\tremove(name: string, serverUrl: string): boolean {\n\t\tconst { key, legacyKey } = storeKeys(name, serverUrl);\n\t\treturn this.backend.withLock((current) => {\n\t\t\tconst states = parseStates(current);\n\t\t\tconst stored = key in states ? key : legacyKey in states ? legacyKey : undefined;\n\t\t\tif (!stored) return { result: false };\n\t\t\tdelete states[stored];\n\t\t\treturn { result: true, next: serializeStates(states) };\n\t\t});\n\t}\n\n\tprivate write(update: (states: StoredStates) => void): void {\n\t\tthis.backend.withLock((current) => {\n\t\t\tconst states = parseStates(current);\n\t\t\tupdate(states);\n\t\t\treturn { result: undefined, next: serializeStates(states) };\n\t\t});\n\t}\n}\n\n/** `fetch` with `OAUTH_REQUEST_TIMEOUT_MS` per request, on top of the request's own signal. */\nfunction timedFetch(fetch: McpFetch = globalThis.fetch): McpFetch {\n\treturn (input, init) => {\n\t\tconst timeout = AbortSignal.timeout(OAUTH_REQUEST_TIMEOUT_MS);\n\t\treturn fetch(input, { ...init, signal: init?.signal ? AbortSignal.any([init.signal, timeout]) : timeout });\n\t};\n}\n\nfunction registeredRedirectUrls(client: OAuthClientInformationMixed | undefined): string[] {\n\treturn client && \"redirect_uris\" in client ? client.redirect_uris : [];\n}\n\n/** 12 characters identifying an MCP server URL in callback paths, computed like Codex does. */\nfunction callbackId(serverUrl: string): string {\n\tconst url = new URL(serverUrl);\n\turl.hash = \"\";\n\treturn createHash(\"sha256\").update(url.href).digest().subarray(0, 9).toString(\"base64url\");\n}\n\n/**\n * pi's Client ID Metadata Document, for `clientRegistration: \"cimd\"`, chosen like Codex chooses its own.\n * The configuration ensures the default callback path. Without the `iss` parameter in authorization\n * responses (RFC 9207), the redirect URI and the document are specific to the MCP server, so a response\n * cannot be mixed up with one from another authorization server (RFC 9700 section 4.4.2.2).\n */\nfunction clientMetadataDocument(\n\tserverUrl: string,\n\tredirectUrl: string,\n\tmetadata: AuthorizationServerMetadata | undefined,\n): OAuthClientMetadataDocument {\n\tif (\n\t\t!metadata?.client_id_metadata_document_supported ||\n\t\t!metadata.token_endpoint_auth_methods_supported?.includes(\"none\")\n\t) {\n\t\tthrow new Error(\n\t\t\t'The authorization server does not support Client ID Metadata Documents for public clients; remove oauth.clientRegistration \"cimd\"',\n\t\t);\n\t}\n\tif (metadata.authorization_response_iss_parameter_supported) {\n\t\treturn { url: `${CLIENT_METADATA_BASE_URL}/client.json`, redirectUrl };\n\t}\n\tconst id = callbackId(serverUrl);\n\tconst redirect = new URL(redirectUrl);\n\tredirect.pathname = `${CALLBACK_PATH}/${id}`;\n\treturn { url: `${CLIENT_METADATA_BASE_URL}/${id}/client.json`, redirectUrl: redirect.href };\n}\n\nfunction createProvider(\n\tserverUrl: string,\n\tstore: McpOAuthStateStore,\n\tsettings: McpOAuthSettings,\n\tredirectUrl: string,\n\tonRedirect: (url: URL) => void,\n): McpOAuthProvider {\n\treturn new McpOAuthProvider({\n\t\tserverUrl,\n\t\tredirectUrl,\n\t\tclientMetadata: { client_name: settings.clientName ?? APP_NAME },\n\t\tclientMetadataDocument:\n\t\t\tsettings.clientRegistration === \"cimd\"\n\t\t\t\t? (metadata) => clientMetadataDocument(serverUrl, redirectUrl, metadata)\n\t\t\t\t: undefined,\n\t\tclientId: settings.clientId,\n\t\tclientSecret: settings.clientSecret,\n\t\tstore,\n\t\tonRedirect,\n\t});\n}\n\nexport interface McpAuthProvider extends AuthProvider {\n\t/** Resolves when no refresh is running, so shutdown does not drop rotated tokens before they are saved. */\n\tsettled(): Promise<void>;\n}\n\n/**\n * Auth provider for MCP connections: sends the stored access token and refreshes it when it is about\n * to expire or after a 401. Throws `McpOAuthAuthorizationRequiredError` when the user has to sign in,\n * including when the server asks for more scope (`insufficient_scope`). `onChallenge` receives the\n * server's `WWW-Authenticate` challenge so sign-in can use its resource metadata URL and scope.\n * `settings` is only called when a refresh is needed, so a secret that fails to resolve fails the\n * refresh instead of the whole connection setup.\n *\n * Many servers rotate refresh tokens, so two refreshes with the same refresh token lose the grant.\n * Requests in this process share one refresh, and other processes are kept out by the store's\n * refresh lock, held from reading the tokens to saving new ones. Tokens that changed meanwhile\n * (another process refreshed them, or the user signed in) are used without refreshing.\n */\nexport function createMcpAuthProvider(options: {\n\tserverUrl: string;\n\tstore: McpOAuthServerStore;\n\tsettings: () => McpOAuthSettings;\n\tonChallenge: (challenge: OAuthChallenge) => void;\n}): McpAuthProvider {\n\tconst { serverUrl, store } = options;\n\tlet refreshing: Promise<void> | undefined;\n\n\t/** Replace `staleToken`, the access token that expired or was rejected. */\n\tconst refresh = (staleToken: string | undefined, fetch: McpFetch = globalThis.fetch, challenge?: OAuthChallenge) => {\n\t\trefreshing ??= store\n\t\t\t.withRefreshLock(async () => {\n\t\t\t\tconst state = await store.load();\n\t\t\t\tif (state?.tokens?.access_token !== staleToken) return;\n\t\t\t\tif (!state?.tokens?.refresh_token) throw new McpOAuthAuthorizationRequiredError();\n\t\t\t\tconst settings = options.settings();\n\t\t\t\tconst redirectUrl =\n\t\t\t\t\tcallbackSettings(settings).fixedRedirectUrl ??\n\t\t\t\t\tregisteredRedirectUrls(state.clientInformation)[0] ??\n\t\t\t\t\tFALLBACK_REDIRECT_URL;\n\t\t\t\tconst provider = createProvider(serverUrl, store, settings, redirectUrl, () => {});\n\t\t\t\t// Refreshes the tokens, or reports that a new sign-in is needed. Not cancellable: a refresh the\n\t\t\t\t// server answered may have rotated the refresh token, so its answer must be saved.\n\t\t\t\tconst result = await authorizeMcp(provider, {\n\t\t\t\t\tserverUrl,\n\t\t\t\t\tresourceMetadataUrl: challenge?.resourceMetadataUrl,\n\t\t\t\t\tauthorizationServerMetadataUrl: settings.authServerMetadataUrl,\n\t\t\t\t\tscope: challenge?.scope,\n\t\t\t\t\tfetch: timedFetch(fetch),\n\t\t\t\t});\n\t\t\t\tif (result === \"REDIRECT\") throw new McpOAuthAuthorizationRequiredError();\n\t\t\t})\n\t\t\t.finally(() => {\n\t\t\t\trefreshing = undefined;\n\t\t\t});\n\t\treturn refreshing;\n\t};\n\n\treturn {\n\t\ttoken: async () => {\n\t\t\tawait refreshing?.catch(() => undefined);\n\t\t\tconst state = await store.load();\n\t\t\tconst token = state?.tokens?.access_token;\n\t\t\tconst expired = state?.tokensExpireAt !== undefined && state.tokensExpireAt - REFRESH_SKEW_MS <= Date.now();\n\t\t\tif (!expired || !state?.tokens?.refresh_token) return token;\n\t\t\t// Failures fall through: the request goes out with the old token and a 401 decides what happens.\n\t\t\tawait refresh(token).catch(() => undefined);\n\t\t\treturn (await store.load())?.tokens?.access_token;\n\t\t},\n\t\tonUnauthorized: async (context) => {\n\t\t\tconst challenge = parseWwwAuthenticate(context.response.headers.get(\"www-authenticate\"));\n\t\t\toptions.onChallenge(challenge);\n\t\t\t// A refresh keeps the granted scope, so more scope needs a new sign-in.\n\t\t\tif (challenge.error === \"insufficient_scope\") throw new McpOAuthAuthorizationRequiredError();\n\t\t\tawait refresh(context.token, context.fetch, challenge);\n\t\t},\n\t\tsettled: async () => {\n\t\t\tawait refreshing?.catch(() => undefined);\n\t\t},\n\t};\n}\n\nexport interface McpSignInPrompt {\n\t/** Show the authorization URL to the user and open it in a browser. */\n\tshowAuthorizationUrl(url: URL): void;\n\t/**\n\t * Ask for the redirect URL from the browser address bar, for when the browser cannot reach the\n\t * loopback callback (for example over SSH). Aborted once the callback arrives or the sign-in is\n\t * cancelled. Resolves to `undefined` or an empty string when the user cancels.\n\t */\n\tpromptForRedirectUrl(signal: AbortSignal): Promise<string | undefined>;\n}\n\nexport class McpSignInCancelledError extends Error {\n\tconstructor() {\n\t\tsuper(\"Sign-in cancelled\");\n\t\tthis.name = \"McpSignInCancelledError\";\n\t}\n}\n\ntype AuthorizationResponse = Pick<OAuthCallback, \"code\" | \"iss\">;\n\nfunction responseFromRedirectUrl(input: string, state: string, redirectUrl: URL): AuthorizationResponse {\n\tlet url: URL;\n\ttry {\n\t\turl = new URL(input.trim());\n\t} catch {\n\t\tthrow new Error(\"Expected the full redirect URL from the browser address bar\");\n\t}\n\t// A server-specific redirect URI tells authorization servers apart, so it must match exactly.\n\tif (url.origin !== redirectUrl.origin || url.pathname !== redirectUrl.pathname) {\n\t\tthrow new Error(\"The redirect URL does not match this sign-in's redirect URI\");\n\t}\n\tconst error = url.searchParams.get(\"error\");\n\tif (error) throw new Error(url.searchParams.get(\"error_description\") ?? error);\n\tif (url.searchParams.get(\"state\") !== state) throw new Error(\"The redirect URL belongs to a different sign-in\");\n\tconst code = url.searchParams.get(\"code\");\n\tif (!code) throw new Error(\"The redirect URL does not contain an authorization code\");\n\treturn { code, iss: url.searchParams.get(\"iss\") ?? undefined };\n}\n\n/**\n * Wait for the browser callback or a pasted redirect URL, whichever comes first. Aborting `signal`\n * aborts the prompt, which then cancels the sign-in.\n */\nasync function waitForAuthorizationResponse(\n\tcallback: OAuthCallbackServer,\n\tstate: string,\n\tredirectUrl: URL,\n\tprompt: McpSignInPrompt,\n\tsignal: AbortSignal | undefined,\n): Promise<AuthorizationResponse> {\n\tconst controller = new AbortController();\n\tconst promptSignal = signal ? AbortSignal.any([controller.signal, signal]) : controller.signal;\n\tconst fromBrowser = callback.waitForCallback(state, redirectUrl.pathname);\n\tconst fromUser = prompt.promptForRedirectUrl(promptSignal).then((input) => {\n\t\tif (!input?.trim()) throw new McpSignInCancelledError();\n\t\treturn responseFromRedirectUrl(input, state, redirectUrl);\n\t});\n\ttry {\n\t\treturn await Promise.race([fromBrowser, fromUser]);\n\t} finally {\n\t\t// The losing side rejects once the prompt is aborted or the callback server closes.\n\t\tcontroller.abort();\n\t\tfromBrowser.catch(() => undefined);\n\t\tfromUser.catch(() => undefined);\n\t}\n}\n\n/** Listen on `port`, or on a free port when it is taken and not `required`. */\nasync function listenForCallback(\n\tsettings: CallbackSettings,\n\textraPaths: string[],\n\tport: number | undefined,\n\trequired: boolean,\n): Promise<OAuthCallbackServer> {\n\tconst options = {\n\t\thost: settings.host,\n\t\tredirectHost: settings.redirectHost,\n\t\tpath: settings.path,\n\t\textraPaths,\n\t\trenderPage: (page: OAuthCallbackPage) =>\n\t\t\tpage.ok\n\t\t\t\t? oauthSuccessHtml(\"Signed in to the MCP server. You may now close this page.\")\n\t\t\t\t: oauthErrorHtml(page.message, page.details),\n\t};\n\ttry {\n\t\treturn await OAuthCallbackServer.listen({ ...options, port: port ?? 0 });\n\t} catch (error) {\n\t\tif (required || port === undefined) throw error;\n\t\treturn OAuthCallbackServer.listen(options);\n\t}\n}\n\n/**\n * Sign in to an MCP server. Uses the stored refresh token when possible; otherwise runs the browser\n * authorization code flow. Tokens are saved to `store`. Aborting `signal` stops the sign-in at any\n * step with `McpSignInCancelledError`; requests to the authorization server are also time-limited.\n */\nexport async function signInMcpServer(options: {\n\tserverUrl: string;\n\tstore: McpOAuthStateStore;\n\tsettings: McpOAuthSettings;\n\tchallenge?: OAuthChallenge;\n\tprompt: McpSignInPrompt;\n\tsignal?: AbortSignal;\n}): Promise<void> {\n\tconst { serverUrl, store, settings, signal } = options;\n\tif (signal?.aborted) throw new McpSignInCancelledError();\n\tconst stored = await store.load();\n\tconst stepUp = options.challenge?.error === \"insufficient_scope\";\n\tconst callbackOptions = callbackSettings(settings);\n\t// Reuse the port of the registered redirect URI so the registered client stays valid.\n\tconst registered = registeredRedirectUrls(stored?.clientInformation)[0];\n\tconst preferredPort =\n\t\tcallbackOptions.port ?? (registered ? Number(new URL(registered).port) || undefined : undefined);\n\tconst cimd = settings.clientRegistration === \"cimd\";\n\tconst callback = await listenForCallback(\n\t\tcallbackOptions,\n\t\t// The redirect URI of a server-specific Client ID Metadata Document.\n\t\tcimd ? [`${CALLBACK_PATH}/${callbackId(serverUrl)}`] : [],\n\t\tpreferredPort,\n\t\tcallbackOptions.port !== undefined,\n\t);\n\tconst redirectUrl = callbackOptions.fixedRedirectUrl ?? callback.redirectUrl;\n\ttry {\n\t\tif (stored) {\n\t\t\tconst next: McpOAuthState = { ...stored };\n\t\t\t// Every sign-in gets a fresh `state` parameter.\n\t\t\tdelete next.oauthState;\n\t\t\t// A registered client cannot use another redirect URI, and its tokens belong to it. A Client ID\n\t\t\t// Metadata Document is not stored, so with one, a stored client was registered before and is replaced.\n\t\t\tconst keepClient =\n\t\t\t\tsettings.clientId ||\n\t\t\t\t(cimd ? !stored.clientInformation : registeredRedirectUrls(stored.clientInformation).includes(redirectUrl));\n\t\t\tif (!keepClient) {\n\t\t\t\tdelete next.clientInformation;\n\t\t\t\tdelete next.tokens;\n\t\t\t\tdelete next.tokensExpireAt;\n\t\t\t}\n\t\t\tawait store.save(next);\n\t\t}\n\n\t\tlet authorizationUrl: URL | undefined;\n\t\tconst provider = createProvider(serverUrl, store, settings, redirectUrl, (url) => {\n\t\t\tauthorizationUrl = url;\n\t\t});\n\t\tconst flow = {\n\t\t\tserverUrl,\n\t\t\tfetch: timedFetch(),\n\t\t\tsignal,\n\t\t\tresourceMetadataUrl: options.challenge?.resourceMetadataUrl,\n\t\t\tauthorizationServerMetadataUrl: settings.authServerMetadataUrl,\n\t\t\t// A server asking for more scope gets it on top of the configured scope and, since the challenge\n\t\t\t// may list only the missing scopes, on top of the scope granted so far.\n\t\t\tscope: mergeScopes(\n\t\t\t\tsettings.scope,\n\t\t\t\tstepUp ? stepUpScope(stored?.tokens?.scope, options.challenge?.scope) : options.challenge?.scope,\n\t\t\t),\n\t\t};\n\t\t// A refresh keeps the granted scope; a server asking for more needs the browser flow.\n\t\tconst skipRefresh = stepUp;\n\t\tif ((await authorizeMcp(provider, { ...flow, skipRefresh })) === \"AUTHORIZED\") return;\n\t\tif (!authorizationUrl) throw new Error(\"OAuth flow did not produce an authorization URL\");\n\n\t\tconst state = await provider.state();\n\t\t// The flow picks the redirect URI, which may be specific to the MCP server.\n\t\tconst authorizationRedirectUrl = new URL(authorizationUrl.searchParams.get(\"redirect_uri\") ?? redirectUrl);\n\t\toptions.prompt.showAuthorizationUrl(authorizationUrl);\n\t\tconst { code, iss } = await waitForAuthorizationResponse(\n\t\t\tcallback,\n\t\t\tstate,\n\t\t\tauthorizationRedirectUrl,\n\t\t\toptions.prompt,\n\t\t\tsignal,\n\t\t);\n\t\tawait authorizeMcp(provider, { ...flow, authorizationCode: code, iss });\n\t} catch (error) {\n\t\t// Aborted requests fail with the signal's reason; report them as the cancellation they are.\n\t\tif (signal?.aborted) throw new McpSignInCancelledError();\n\t\tthrow error;\n\t} finally {\n\t\tawait callback.close();\n\t}\n}\n"]}
|
|
@@ -12,11 +12,10 @@
|
|
|
12
12
|
import type { ListResourcesResult, ListResourceTemplatesResult, McpRequestOptions, ReadResourceResult, Resource, ResourceTemplate } from "@earendil-works/pi-mcp";
|
|
13
13
|
import type { TSchema } from "typebox";
|
|
14
14
|
import type { ToolDefinition } from "../../core/extensions/types.ts";
|
|
15
|
+
import { LIST_MCP_RESOURCE_TEMPLATES_TOOL, LIST_MCP_RESOURCES_TOOL, READ_MCP_RESOURCE_TOOL } from "../../core/mcp-servers.ts";
|
|
15
16
|
import type { McpExposure } from "./config.ts";
|
|
16
|
-
import { type McpToolDetails
|
|
17
|
-
export
|
|
18
|
-
export declare const LIST_MCP_RESOURCE_TEMPLATES_TOOL = "list_mcp_resource_templates";
|
|
19
|
-
export { READ_MCP_RESOURCE_TOOL };
|
|
17
|
+
import { type McpToolDetails } from "./tools.ts";
|
|
18
|
+
export { LIST_MCP_RESOURCE_TEMPLATES_TOOL, LIST_MCP_RESOURCES_TOOL, READ_MCP_RESOURCE_TOOL };
|
|
20
19
|
/** A connected server that offers resources. */
|
|
21
20
|
export interface McpResourceServer {
|
|
22
21
|
name: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"resources.d.ts","sourceRoot":"","sources":["../../../src/extensions/mcp/resources.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAGH,OAAO,KAAK,EAEX,mBAAmB,EACnB,2BAA2B,EAC3B,iBAAiB,EACjB,kBAAkB,EAClB,QAAQ,EACR,gBAAgB,EAChB,MAAM,wBAAwB,CAAC;AAChC,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,KAAK,EAAmB,cAAc,EAAE,MAAM,gCAAgC,CAAC;AACtF,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC/C,OAAO,
|
|
1
|
+
{"version":3,"file":"resources.d.ts","sourceRoot":"","sources":["../../../src/extensions/mcp/resources.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAGH,OAAO,KAAK,EAEX,mBAAmB,EACnB,2BAA2B,EAC3B,iBAAiB,EACjB,kBAAkB,EAClB,QAAQ,EACR,gBAAgB,EAChB,MAAM,wBAAwB,CAAC;AAChC,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,KAAK,EAAmB,cAAc,EAAE,MAAM,gCAAgC,CAAC;AACtF,OAAO,EACN,gCAAgC,EAChC,uBAAuB,EACvB,sBAAsB,EACtB,MAAM,2BAA2B,CAAC;AACnC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC/C,OAAO,EAAmB,KAAK,cAAc,EAAkC,MAAM,YAAY,CAAC;AAElG,OAAO,EAAE,gCAAgC,EAAE,uBAAuB,EAAE,sBAAsB,EAAE,CAAC;AAE7F,gDAAgD;AAChD,MAAM,WAAW,iBAAiB;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,EAAE,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAAC;IACpG,qBAAqB,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,EAAE,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,2BAA2B,CAAC,CAAC;IACpH,YAAY,CAAC,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC,CAAC;IAC9D,oBAAoB,CAAC,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,gBAAgB,EAAE,CAAC,CAAC;IAC9E,YAAY,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAAC;CACnF;AAED,0EAA0E;AAC1E,wBAAgB,gBAAgB,CAAC,IAAI,EAAE;IAAE,GAAG,CAAC,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,OAAO,CAGzG;AA6ID;;GAEG;AACH,wBAAgB,gCAAgC,CAAC,OAAO,EAAE;IACzD,QAAQ,EAAE,WAAW,CAAC;IACtB,OAAO,EAAE,MAAM,SAAS,iBAAiB,EAAE,CAAC;CAC5C,GAAG,cAAc,CAAC,OAAO,EAAE,cAAc,CAAC,EAAE,CA8I5C"}
|
|
@@ -9,10 +9,9 @@
|
|
|
9
9
|
* interfaces for hosts that render them, and so are icons. Read resources become text and images for
|
|
10
10
|
* the model; binary resources are saved to temp files. Scripts get the JSON payloads.
|
|
11
11
|
*/
|
|
12
|
-
import {
|
|
13
|
-
|
|
14
|
-
export
|
|
15
|
-
export { READ_MCP_RESOURCE_TOOL };
|
|
12
|
+
import { LIST_MCP_RESOURCE_TEMPLATES_TOOL, LIST_MCP_RESOURCES_TOOL, READ_MCP_RESOURCE_TOOL, } from "../../core/mcp-servers.js";
|
|
13
|
+
import { limitMcpContent, toModelContent, toToolExposure } from "./tools.js";
|
|
14
|
+
export { LIST_MCP_RESOURCE_TEMPLATES_TOOL, LIST_MCP_RESOURCES_TOOL, READ_MCP_RESOURCE_TOOL };
|
|
16
15
|
/** MCP App user interfaces, which only hosts that render them can use. */
|
|
17
16
|
export function isMcpAppResource(item) {
|
|
18
17
|
const uri = item.uri ?? item.uriTemplate ?? "";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"resources.js","sourceRoot":"","sources":["../../../src/extensions/mcp/resources.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * MCP resources, through the tools Codex and opencode use: `list_mcp_resources`,\n * `list_mcp_resource_templates`, and `read_mcp_resource`. They take a `server` argument and cover\n * every connected server with resources, so models trained on those tools use them unchanged.\n *\n * Listings are JSON, as in Codex: `{ server?, resources: [{ server, ...resource }], nextCursor? }`.\n * With a `server`, one page is listed and `cursor` continues it; without, every page of every server.\n * MCP App resources (`ui://` URIs and `profile=mcp-app` HTML) are left out, since they are user\n * interfaces for hosts that render them, and so are icons. Read resources become text and images for\n * the model; binary resources are saved to temp files. Scripts get the JSON payloads.\n */\n\nimport type { ImageContent, JsonValue, TextContent } from \"@earendil-works/pi-ai\";\nimport type {\n\tContentBlock,\n\tListResourcesResult,\n\tListResourceTemplatesResult,\n\tMcpRequestOptions,\n\tReadResourceResult,\n\tResource,\n\tResourceTemplate,\n} from \"@earendil-works/pi-mcp\";\nimport type { TSchema } from \"typebox\";\nimport type { ToolAnnotations, ToolDefinition } from \"../../core/extensions/types.ts\";\nimport type { McpExposure } from \"./config.ts\";\nimport {\n\tlimitMcpContent,\n\ttype McpToolDetails,\n\tREAD_MCP_RESOURCE_TOOL,\n\ttoModelContent,\n\ttoToolExposure,\n} from \"./tools.ts\";\n\nexport const LIST_MCP_RESOURCES_TOOL = \"list_mcp_resources\";\nexport const LIST_MCP_RESOURCE_TEMPLATES_TOOL = \"list_mcp_resource_templates\";\nexport { READ_MCP_RESOURCE_TOOL };\n\n/** A connected server that offers resources. */\nexport interface McpResourceServer {\n\tname: string;\n\ttimeoutMs: number;\n\tresourcesPage(cursor: string | undefined, options: McpRequestOptions): Promise<ListResourcesResult>;\n\tresourceTemplatesPage(cursor: string | undefined, options: McpRequestOptions): Promise<ListResourceTemplatesResult>;\n\tallResources(options: McpRequestOptions): Promise<Resource[]>;\n\tallResourceTemplates(options: McpRequestOptions): Promise<ResourceTemplate[]>;\n\treadResource(uri: string, options: McpRequestOptions): Promise<ReadResourceResult>;\n}\n\n/** MCP App user interfaces, which only hosts that render them can use. */\nexport function isMcpAppResource(item: { uri?: string; uriTemplate?: string; mimeType?: string }): boolean {\n\tconst uri = item.uri ?? item.uriTemplate ?? \"\";\n\treturn uri.startsWith(\"ui://\") || /;\\s*profile\\s*=\\s*\"?mcp-app\"?/i.test(item.mimeType ?? \"\");\n}\n\n/** A listed resource or template without `_meta` and icons, tagged with its server. */\nfunction listed<T extends { _meta?: unknown }>(server: string, item: T): Record<string, unknown> {\n\tconst { _meta, icons: _icons, ...rest } = item as T & { icons?: unknown };\n\treturn { server, ...rest };\n}\n\nconst stringProperty = (description: string) => ({ type: \"string\", description });\nconst SERVER_FILTER = stringProperty(\"MCP server name. Omit to list every server with resources.\");\nconst CURSOR = stringProperty(\"Opaque cursor from a previous call with the same server; omit for the first page.\");\nconst LIST_PARAMETERS = {\n\ttype: \"object\",\n\tproperties: { server: SERVER_FILTER, cursor: CURSOR },\n\tadditionalProperties: false,\n};\nconst READ_PARAMETERS = {\n\ttype: \"object\",\n\tproperties: {\n\t\tserver: stringProperty(\n\t\t\t\"MCP server name exactly as configured. Must match the 'server' field returned by list_mcp_resources.\",\n\t\t),\n\t\turi: stringProperty(\"Resource URI to read. Must be one of the URIs returned by list_mcp_resources.\"),\n\t},\n\trequired: [\"server\", \"uri\"],\n\tadditionalProperties: false,\n};\n\nconst optionalString = { type: \"string\" };\nconst LISTING_ERRORS = {\n\ttype: \"array\",\n\tdescription: \"Servers that could not be listed\",\n\titems: {\n\t\ttype: \"object\",\n\t\tproperties: { server: { type: \"string\" }, error: { type: \"string\" } },\n\t\trequired: [\"server\", \"error\"],\n\t},\n};\nconst LIST_OUTPUT_SCHEMA = {\n\ttype: \"object\",\n\tproperties: {\n\t\tserver: optionalString,\n\t\tresources: {\n\t\t\ttype: \"array\",\n\t\t\titems: {\n\t\t\t\ttype: \"object\",\n\t\t\t\tproperties: {\n\t\t\t\t\tserver: { type: \"string\" },\n\t\t\t\t\turi: { type: \"string\" },\n\t\t\t\t\tname: { type: \"string\" },\n\t\t\t\t\ttitle: optionalString,\n\t\t\t\t\tdescription: optionalString,\n\t\t\t\t\tmimeType: optionalString,\n\t\t\t\t\tsize: { type: \"number\" },\n\t\t\t\t},\n\t\t\t\trequired: [\"server\", \"uri\", \"name\"],\n\t\t\t},\n\t\t},\n\t\tnextCursor: optionalString,\n\t\terrors: LISTING_ERRORS,\n\t},\n\trequired: [\"resources\"],\n};\nconst LIST_TEMPLATES_OUTPUT_SCHEMA = {\n\ttype: \"object\",\n\tproperties: {\n\t\tserver: optionalString,\n\t\tresourceTemplates: {\n\t\t\ttype: \"array\",\n\t\t\titems: {\n\t\t\t\ttype: \"object\",\n\t\t\t\tproperties: {\n\t\t\t\t\tserver: { type: \"string\" },\n\t\t\t\t\turiTemplate: { type: \"string\", description: \"RFC 6570 URI template\" },\n\t\t\t\t\tname: { type: \"string\" },\n\t\t\t\t\ttitle: optionalString,\n\t\t\t\t\tdescription: optionalString,\n\t\t\t\t\tmimeType: optionalString,\n\t\t\t\t},\n\t\t\t\trequired: [\"server\", \"uriTemplate\", \"name\"],\n\t\t\t},\n\t\t},\n\t\tnextCursor: optionalString,\n\t\terrors: LISTING_ERRORS,\n\t},\n\trequired: [\"resourceTemplates\"],\n};\nconst READ_OUTPUT_SCHEMA = {\n\ttype: \"object\",\n\tproperties: {\n\t\tserver: { type: \"string\" },\n\t\turi: { type: \"string\" },\n\t\tcontents: {\n\t\t\ttype: \"array\",\n\t\t\titems: {\n\t\t\t\tanyOf: [\n\t\t\t\t\t{\n\t\t\t\t\t\ttype: \"object\",\n\t\t\t\t\t\tproperties: { uri: { type: \"string\" }, mimeType: optionalString, text: { type: \"string\" } },\n\t\t\t\t\t\trequired: [\"uri\", \"text\"],\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\ttype: \"object\",\n\t\t\t\t\t\tproperties: {\n\t\t\t\t\t\t\turi: { type: \"string\" },\n\t\t\t\t\t\t\tmimeType: optionalString,\n\t\t\t\t\t\t\tblob: { type: \"string\", description: \"base64\" },\n\t\t\t\t\t\t},\n\t\t\t\t\t\trequired: [\"uri\", \"blob\"],\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t},\n\t\t},\n\t},\n\trequired: [\"server\", \"uri\", \"contents\"],\n};\n\nfunction stringArgument(params: unknown, key: string): string | undefined {\n\tconst value = (params as Record<string, unknown> | undefined)?.[key];\n\tif (value === undefined || value === null) return undefined;\n\tif (typeof value !== \"string\") throw new Error(`${key} must be a string`);\n\treturn value.trim() || undefined;\n}\n\nfunction errorMessage(error: unknown): string {\n\treturn error instanceof Error ? error.message : String(error);\n}\n\nasync function jsonResult(\n\ttool: string,\n\tserver: string | undefined,\n\tpayload: Record<string, unknown>,\n): Promise<{ content: (TextContent | ImageContent)[]; details: McpToolDetails; structuredContent: JsonValue }> {\n\tconst { content, fullOutputPath } = await limitMcpContent([{ type: \"text\", text: JSON.stringify(payload) }]);\n\treturn {\n\t\tcontent,\n\t\tdetails: { server: server ?? \"\", tool, ...(fullOutputPath ? { fullOutputPath } : {}) },\n\t\tstructuredContent: payload as unknown as JsonValue,\n\t};\n}\n\n/**\n * The three resource tools. `servers` returns the servers whose resources they reach, at call time.\n */\nexport function createMcpResourceToolDefinitions(options: {\n\texposure: McpExposure;\n\tservers: () => readonly McpResourceServer[];\n}): ToolDefinition<TSchema, McpToolDetails>[] {\n\tconst readOnly: ToolAnnotations = { readOnlyHint: true };\n\n\tconst findServer = (name: string): McpResourceServer => {\n\t\tconst servers = options.servers();\n\t\tconst server = servers.find((candidate) => candidate.name === name);\n\t\tif (server) return server;\n\t\tconst available = servers.map((candidate) => candidate.name).join(\", \");\n\t\tthrow new Error(\n\t\t\t`MCP server \"${name}\" has no resources${available ? `. Servers with resources: ${available}` : \"\"}`,\n\t\t);\n\t};\n\n\t/** One page of one server, or every page of every server. */\n\tconst list = async <T extends { _meta?: unknown }>(\n\t\tparams: unknown,\n\t\tsignal: AbortSignal | undefined,\n\t\tkey: \"resources\" | \"resourceTemplates\",\n\t\tpage: (\n\t\t\tserver: McpResourceServer,\n\t\t\tcursor: string | undefined,\n\t\t\toptions: McpRequestOptions,\n\t\t) => Promise<{\n\t\t\titems: T[];\n\t\t\tnextCursor?: string;\n\t\t}>,\n\t\tall: (server: McpResourceServer, options: McpRequestOptions) => Promise<T[]>,\n\t): Promise<Record<string, unknown>> => {\n\t\tconst serverName = stringArgument(params, \"server\");\n\t\tconst cursor = stringArgument(params, \"cursor\");\n\t\tconst visible = (item: T) => !isMcpAppResource(item as { uri?: string; uriTemplate?: string; mimeType?: string });\n\t\tif (serverName) {\n\t\t\tconst server = findServer(serverName);\n\t\t\tconst result = await page(server, cursor, { signal, timeoutMs: server.timeoutMs });\n\t\t\treturn {\n\t\t\t\tserver: server.name,\n\t\t\t\t[key]: result.items.filter(visible).map((item) => listed(server.name, item)),\n\t\t\t\t...(result.nextCursor === undefined ? {} : { nextCursor: result.nextCursor }),\n\t\t\t};\n\t\t}\n\t\tif (cursor) throw new Error(\"cursor can only be used when a server is specified\");\n\t\tconst servers = [...options.servers()].sort((a, b) => a.name.localeCompare(b.name));\n\t\tconst results = await Promise.allSettled(\n\t\t\tservers.map((server) => all(server, { signal, timeoutMs: server.timeoutMs })),\n\t\t);\n\t\tconst items: Record<string, unknown>[] = [];\n\t\tconst errors: { server: string; error: string }[] = [];\n\t\tresults.forEach((result, index) => {\n\t\t\tconst server = servers[index].name;\n\t\t\tif (result.status === \"fulfilled\")\n\t\t\t\titems.push(...result.value.filter(visible).map((item) => listed(server, item)));\n\t\t\telse errors.push({ server, error: errorMessage(result.reason) });\n\t\t});\n\t\treturn { [key]: items, ...(errors.length > 0 ? { errors } : {}) };\n\t};\n\n\tconst listResources: ToolDefinition<TSchema, McpToolDetails> = {\n\t\tname: LIST_MCP_RESOURCES_TOOL,\n\t\tlabel: LIST_MCP_RESOURCES_TOOL,\n\t\tdescription:\n\t\t\t\"Lists resources provided by MCP servers. Resources allow servers to share data that provides context to language models, such as files, database schemas, or application-specific information. Prefer resources over web search when possible.\",\n\t\tparameters: LIST_PARAMETERS as unknown as TSchema,\n\t\toutputSchema: LIST_OUTPUT_SCHEMA as unknown as TSchema,\n\t\texposure: toToolExposure(options.exposure),\n\t\tannotations: readOnly,\n\t\tasync execute(_toolCallId, params, signal) {\n\t\t\tconst payload = await list(\n\t\t\t\tparams,\n\t\t\t\tsignal,\n\t\t\t\t\"resources\",\n\t\t\t\tasync (server, cursor, requestOptions) => {\n\t\t\t\t\tconst result = await server.resourcesPage(cursor, requestOptions);\n\t\t\t\t\treturn { items: result.resources, nextCursor: result.nextCursor };\n\t\t\t\t},\n\t\t\t\t(server, requestOptions) => server.allResources(requestOptions),\n\t\t\t);\n\t\t\treturn jsonResult(LIST_MCP_RESOURCES_TOOL, stringArgument(params, \"server\"), payload);\n\t\t},\n\t};\n\n\tconst listTemplates: ToolDefinition<TSchema, McpToolDetails> = {\n\t\tname: LIST_MCP_RESOURCE_TEMPLATES_TOOL,\n\t\tlabel: LIST_MCP_RESOURCE_TEMPLATES_TOOL,\n\t\tdescription:\n\t\t\t\"Lists resource templates provided by MCP servers. Parameterized resource templates allow servers to share data that takes parameters and provides context to language models, such as files, database schemas, or application-specific information. Prefer resource templates over web search when possible.\",\n\t\tparameters: LIST_PARAMETERS as unknown as TSchema,\n\t\toutputSchema: LIST_TEMPLATES_OUTPUT_SCHEMA as unknown as TSchema,\n\t\texposure: toToolExposure(options.exposure),\n\t\tannotations: readOnly,\n\t\tasync execute(_toolCallId, params, signal) {\n\t\t\tconst payload = await list(\n\t\t\t\tparams,\n\t\t\t\tsignal,\n\t\t\t\t\"resourceTemplates\",\n\t\t\t\tasync (server, cursor, requestOptions) => {\n\t\t\t\t\tconst result = await server.resourceTemplatesPage(cursor, requestOptions);\n\t\t\t\t\treturn { items: result.resourceTemplates, nextCursor: result.nextCursor };\n\t\t\t\t},\n\t\t\t\t(server, requestOptions) => server.allResourceTemplates(requestOptions),\n\t\t\t);\n\t\t\treturn jsonResult(LIST_MCP_RESOURCE_TEMPLATES_TOOL, stringArgument(params, \"server\"), payload);\n\t\t},\n\t};\n\n\tconst readResource: ToolDefinition<TSchema, McpToolDetails> = {\n\t\tname: READ_MCP_RESOURCE_TOOL,\n\t\tlabel: READ_MCP_RESOURCE_TOOL,\n\t\tdescription: \"Read a specific resource from an MCP server given the server name and resource URI.\",\n\t\tparameters: READ_PARAMETERS as unknown as TSchema,\n\t\toutputSchema: READ_OUTPUT_SCHEMA as unknown as TSchema,\n\t\texposure: toToolExposure(options.exposure),\n\t\tannotations: readOnly,\n\t\tasync execute(_toolCallId, params, signal) {\n\t\t\tconst serverName = stringArgument(params, \"server\");\n\t\t\tconst uri = stringArgument(params, \"uri\");\n\t\t\tif (!serverName) throw new Error(\"server must be provided\");\n\t\t\tif (!uri) throw new Error(\"uri must be provided\");\n\t\t\tconst server = findServer(serverName);\n\t\t\tconst result = await server.readResource(uri, { signal, timeoutMs: server.timeoutMs });\n\t\t\t// Several contents (for example a directory) are labeled with their URIs.\n\t\t\tconst blocks: ContentBlock[] = result.contents.flatMap((contents) => [\n\t\t\t\t...(result.contents.length > 1 ? [{ type: \"text\" as const, text: `${contents.uri}:` }] : []),\n\t\t\t\t{ type: \"resource\" as const, resource: contents },\n\t\t\t]);\n\t\t\tconst converted = await toModelContent(server.name, blocks);\n\t\t\tconst { content, fullOutputPath } = await limitMcpContent(\n\t\t\t\tconverted.length > 0 ? converted : [{ type: \"text\", text: `Resource ${uri} is empty.` }],\n\t\t\t);\n\t\t\tconst contents = result.contents.map(({ _meta: _ignored, ...rest }) => rest);\n\t\t\treturn {\n\t\t\t\tcontent,\n\t\t\t\tdetails: {\n\t\t\t\t\tserver: server.name,\n\t\t\t\t\ttool: READ_MCP_RESOURCE_TOOL,\n\t\t\t\t\t...(fullOutputPath ? { fullOutputPath } : {}),\n\t\t\t\t},\n\t\t\t\tstructuredContent: { server: server.name, uri, contents } as unknown as JsonValue,\n\t\t\t};\n\t\t},\n\t};\n\n\treturn [listResources, listTemplates, readResource];\n}\n"]}
|
|
1
|
+
{"version":3,"file":"resources.js","sourceRoot":"","sources":["../../../src/extensions/mcp/resources.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * MCP resources, through the tools Codex and opencode use: `list_mcp_resources`,\n * `list_mcp_resource_templates`, and `read_mcp_resource`. They take a `server` argument and cover\n * every connected server with resources, so models trained on those tools use them unchanged.\n *\n * Listings are JSON, as in Codex: `{ server?, resources: [{ server, ...resource }], nextCursor? }`.\n * With a `server`, one page is listed and `cursor` continues it; without, every page of every server.\n * MCP App resources (`ui://` URIs and `profile=mcp-app` HTML) are left out, since they are user\n * interfaces for hosts that render them, and so are icons. Read resources become text and images for\n * the model; binary resources are saved to temp files. Scripts get the JSON payloads.\n */\n\nimport type { ImageContent, JsonValue, TextContent } from \"@earendil-works/pi-ai\";\nimport type {\n\tContentBlock,\n\tListResourcesResult,\n\tListResourceTemplatesResult,\n\tMcpRequestOptions,\n\tReadResourceResult,\n\tResource,\n\tResourceTemplate,\n} from \"@earendil-works/pi-mcp\";\nimport type { TSchema } from \"typebox\";\nimport type { ToolAnnotations, ToolDefinition } from \"../../core/extensions/types.ts\";\nimport {\n\tLIST_MCP_RESOURCE_TEMPLATES_TOOL,\n\tLIST_MCP_RESOURCES_TOOL,\n\tREAD_MCP_RESOURCE_TOOL,\n} from \"../../core/mcp-servers.ts\";\nimport type { McpExposure } from \"./config.ts\";\nimport { limitMcpContent, type McpToolDetails, toModelContent, toToolExposure } from \"./tools.ts\";\n\nexport { LIST_MCP_RESOURCE_TEMPLATES_TOOL, LIST_MCP_RESOURCES_TOOL, READ_MCP_RESOURCE_TOOL };\n\n/** A connected server that offers resources. */\nexport interface McpResourceServer {\n\tname: string;\n\ttimeoutMs: number;\n\tresourcesPage(cursor: string | undefined, options: McpRequestOptions): Promise<ListResourcesResult>;\n\tresourceTemplatesPage(cursor: string | undefined, options: McpRequestOptions): Promise<ListResourceTemplatesResult>;\n\tallResources(options: McpRequestOptions): Promise<Resource[]>;\n\tallResourceTemplates(options: McpRequestOptions): Promise<ResourceTemplate[]>;\n\treadResource(uri: string, options: McpRequestOptions): Promise<ReadResourceResult>;\n}\n\n/** MCP App user interfaces, which only hosts that render them can use. */\nexport function isMcpAppResource(item: { uri?: string; uriTemplate?: string; mimeType?: string }): boolean {\n\tconst uri = item.uri ?? item.uriTemplate ?? \"\";\n\treturn uri.startsWith(\"ui://\") || /;\\s*profile\\s*=\\s*\"?mcp-app\"?/i.test(item.mimeType ?? \"\");\n}\n\n/** A listed resource or template without `_meta` and icons, tagged with its server. */\nfunction listed<T extends { _meta?: unknown }>(server: string, item: T): Record<string, unknown> {\n\tconst { _meta, icons: _icons, ...rest } = item as T & { icons?: unknown };\n\treturn { server, ...rest };\n}\n\nconst stringProperty = (description: string) => ({ type: \"string\", description });\nconst SERVER_FILTER = stringProperty(\"MCP server name. Omit to list every server with resources.\");\nconst CURSOR = stringProperty(\"Opaque cursor from a previous call with the same server; omit for the first page.\");\nconst LIST_PARAMETERS = {\n\ttype: \"object\",\n\tproperties: { server: SERVER_FILTER, cursor: CURSOR },\n\tadditionalProperties: false,\n};\nconst READ_PARAMETERS = {\n\ttype: \"object\",\n\tproperties: {\n\t\tserver: stringProperty(\n\t\t\t\"MCP server name exactly as configured. Must match the 'server' field returned by list_mcp_resources.\",\n\t\t),\n\t\turi: stringProperty(\"Resource URI to read. Must be one of the URIs returned by list_mcp_resources.\"),\n\t},\n\trequired: [\"server\", \"uri\"],\n\tadditionalProperties: false,\n};\n\nconst optionalString = { type: \"string\" };\nconst LISTING_ERRORS = {\n\ttype: \"array\",\n\tdescription: \"Servers that could not be listed\",\n\titems: {\n\t\ttype: \"object\",\n\t\tproperties: { server: { type: \"string\" }, error: { type: \"string\" } },\n\t\trequired: [\"server\", \"error\"],\n\t},\n};\nconst LIST_OUTPUT_SCHEMA = {\n\ttype: \"object\",\n\tproperties: {\n\t\tserver: optionalString,\n\t\tresources: {\n\t\t\ttype: \"array\",\n\t\t\titems: {\n\t\t\t\ttype: \"object\",\n\t\t\t\tproperties: {\n\t\t\t\t\tserver: { type: \"string\" },\n\t\t\t\t\turi: { type: \"string\" },\n\t\t\t\t\tname: { type: \"string\" },\n\t\t\t\t\ttitle: optionalString,\n\t\t\t\t\tdescription: optionalString,\n\t\t\t\t\tmimeType: optionalString,\n\t\t\t\t\tsize: { type: \"number\" },\n\t\t\t\t},\n\t\t\t\trequired: [\"server\", \"uri\", \"name\"],\n\t\t\t},\n\t\t},\n\t\tnextCursor: optionalString,\n\t\terrors: LISTING_ERRORS,\n\t},\n\trequired: [\"resources\"],\n};\nconst LIST_TEMPLATES_OUTPUT_SCHEMA = {\n\ttype: \"object\",\n\tproperties: {\n\t\tserver: optionalString,\n\t\tresourceTemplates: {\n\t\t\ttype: \"array\",\n\t\t\titems: {\n\t\t\t\ttype: \"object\",\n\t\t\t\tproperties: {\n\t\t\t\t\tserver: { type: \"string\" },\n\t\t\t\t\turiTemplate: { type: \"string\", description: \"RFC 6570 URI template\" },\n\t\t\t\t\tname: { type: \"string\" },\n\t\t\t\t\ttitle: optionalString,\n\t\t\t\t\tdescription: optionalString,\n\t\t\t\t\tmimeType: optionalString,\n\t\t\t\t},\n\t\t\t\trequired: [\"server\", \"uriTemplate\", \"name\"],\n\t\t\t},\n\t\t},\n\t\tnextCursor: optionalString,\n\t\terrors: LISTING_ERRORS,\n\t},\n\trequired: [\"resourceTemplates\"],\n};\nconst READ_OUTPUT_SCHEMA = {\n\ttype: \"object\",\n\tproperties: {\n\t\tserver: { type: \"string\" },\n\t\turi: { type: \"string\" },\n\t\tcontents: {\n\t\t\ttype: \"array\",\n\t\t\titems: {\n\t\t\t\tanyOf: [\n\t\t\t\t\t{\n\t\t\t\t\t\ttype: \"object\",\n\t\t\t\t\t\tproperties: { uri: { type: \"string\" }, mimeType: optionalString, text: { type: \"string\" } },\n\t\t\t\t\t\trequired: [\"uri\", \"text\"],\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\ttype: \"object\",\n\t\t\t\t\t\tproperties: {\n\t\t\t\t\t\t\turi: { type: \"string\" },\n\t\t\t\t\t\t\tmimeType: optionalString,\n\t\t\t\t\t\t\tblob: { type: \"string\", description: \"base64\" },\n\t\t\t\t\t\t},\n\t\t\t\t\t\trequired: [\"uri\", \"blob\"],\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t},\n\t\t},\n\t},\n\trequired: [\"server\", \"uri\", \"contents\"],\n};\n\nfunction stringArgument(params: unknown, key: string): string | undefined {\n\tconst value = (params as Record<string, unknown> | undefined)?.[key];\n\tif (value === undefined || value === null) return undefined;\n\tif (typeof value !== \"string\") throw new Error(`${key} must be a string`);\n\treturn value.trim() || undefined;\n}\n\nfunction errorMessage(error: unknown): string {\n\treturn error instanceof Error ? error.message : String(error);\n}\n\nasync function jsonResult(\n\ttool: string,\n\tserver: string | undefined,\n\tpayload: Record<string, unknown>,\n): Promise<{ content: (TextContent | ImageContent)[]; details: McpToolDetails; structuredContent: JsonValue }> {\n\tconst { content, fullOutputPath } = await limitMcpContent([{ type: \"text\", text: JSON.stringify(payload) }]);\n\treturn {\n\t\tcontent,\n\t\tdetails: { server: server ?? \"\", tool, ...(fullOutputPath ? { fullOutputPath } : {}) },\n\t\tstructuredContent: payload as unknown as JsonValue,\n\t};\n}\n\n/**\n * The three resource tools. `servers` returns the servers whose resources they reach, at call time.\n */\nexport function createMcpResourceToolDefinitions(options: {\n\texposure: McpExposure;\n\tservers: () => readonly McpResourceServer[];\n}): ToolDefinition<TSchema, McpToolDetails>[] {\n\tconst readOnly: ToolAnnotations = { readOnlyHint: true };\n\n\tconst findServer = (name: string): McpResourceServer => {\n\t\tconst servers = options.servers();\n\t\tconst server = servers.find((candidate) => candidate.name === name);\n\t\tif (server) return server;\n\t\tconst available = servers.map((candidate) => candidate.name).join(\", \");\n\t\tthrow new Error(\n\t\t\t`MCP server \"${name}\" has no resources${available ? `. Servers with resources: ${available}` : \"\"}`,\n\t\t);\n\t};\n\n\t/** One page of one server, or every page of every server. */\n\tconst list = async <T extends { _meta?: unknown }>(\n\t\tparams: unknown,\n\t\tsignal: AbortSignal | undefined,\n\t\tkey: \"resources\" | \"resourceTemplates\",\n\t\tpage: (\n\t\t\tserver: McpResourceServer,\n\t\t\tcursor: string | undefined,\n\t\t\toptions: McpRequestOptions,\n\t\t) => Promise<{\n\t\t\titems: T[];\n\t\t\tnextCursor?: string;\n\t\t}>,\n\t\tall: (server: McpResourceServer, options: McpRequestOptions) => Promise<T[]>,\n\t): Promise<Record<string, unknown>> => {\n\t\tconst serverName = stringArgument(params, \"server\");\n\t\tconst cursor = stringArgument(params, \"cursor\");\n\t\tconst visible = (item: T) => !isMcpAppResource(item as { uri?: string; uriTemplate?: string; mimeType?: string });\n\t\tif (serverName) {\n\t\t\tconst server = findServer(serverName);\n\t\t\tconst result = await page(server, cursor, { signal, timeoutMs: server.timeoutMs });\n\t\t\treturn {\n\t\t\t\tserver: server.name,\n\t\t\t\t[key]: result.items.filter(visible).map((item) => listed(server.name, item)),\n\t\t\t\t...(result.nextCursor === undefined ? {} : { nextCursor: result.nextCursor }),\n\t\t\t};\n\t\t}\n\t\tif (cursor) throw new Error(\"cursor can only be used when a server is specified\");\n\t\tconst servers = [...options.servers()].sort((a, b) => a.name.localeCompare(b.name));\n\t\tconst results = await Promise.allSettled(\n\t\t\tservers.map((server) => all(server, { signal, timeoutMs: server.timeoutMs })),\n\t\t);\n\t\tconst items: Record<string, unknown>[] = [];\n\t\tconst errors: { server: string; error: string }[] = [];\n\t\tresults.forEach((result, index) => {\n\t\t\tconst server = servers[index].name;\n\t\t\tif (result.status === \"fulfilled\")\n\t\t\t\titems.push(...result.value.filter(visible).map((item) => listed(server, item)));\n\t\t\telse errors.push({ server, error: errorMessage(result.reason) });\n\t\t});\n\t\treturn { [key]: items, ...(errors.length > 0 ? { errors } : {}) };\n\t};\n\n\tconst listResources: ToolDefinition<TSchema, McpToolDetails> = {\n\t\tname: LIST_MCP_RESOURCES_TOOL,\n\t\tlabel: LIST_MCP_RESOURCES_TOOL,\n\t\tdescription:\n\t\t\t\"Lists resources provided by MCP servers. Resources allow servers to share data that provides context to language models, such as files, database schemas, or application-specific information. Prefer resources over web search when possible.\",\n\t\tparameters: LIST_PARAMETERS as unknown as TSchema,\n\t\toutputSchema: LIST_OUTPUT_SCHEMA as unknown as TSchema,\n\t\texposure: toToolExposure(options.exposure),\n\t\tannotations: readOnly,\n\t\tasync execute(_toolCallId, params, signal) {\n\t\t\tconst payload = await list(\n\t\t\t\tparams,\n\t\t\t\tsignal,\n\t\t\t\t\"resources\",\n\t\t\t\tasync (server, cursor, requestOptions) => {\n\t\t\t\t\tconst result = await server.resourcesPage(cursor, requestOptions);\n\t\t\t\t\treturn { items: result.resources, nextCursor: result.nextCursor };\n\t\t\t\t},\n\t\t\t\t(server, requestOptions) => server.allResources(requestOptions),\n\t\t\t);\n\t\t\treturn jsonResult(LIST_MCP_RESOURCES_TOOL, stringArgument(params, \"server\"), payload);\n\t\t},\n\t};\n\n\tconst listTemplates: ToolDefinition<TSchema, McpToolDetails> = {\n\t\tname: LIST_MCP_RESOURCE_TEMPLATES_TOOL,\n\t\tlabel: LIST_MCP_RESOURCE_TEMPLATES_TOOL,\n\t\tdescription:\n\t\t\t\"Lists resource templates provided by MCP servers. Parameterized resource templates allow servers to share data that takes parameters and provides context to language models, such as files, database schemas, or application-specific information. Prefer resource templates over web search when possible.\",\n\t\tparameters: LIST_PARAMETERS as unknown as TSchema,\n\t\toutputSchema: LIST_TEMPLATES_OUTPUT_SCHEMA as unknown as TSchema,\n\t\texposure: toToolExposure(options.exposure),\n\t\tannotations: readOnly,\n\t\tasync execute(_toolCallId, params, signal) {\n\t\t\tconst payload = await list(\n\t\t\t\tparams,\n\t\t\t\tsignal,\n\t\t\t\t\"resourceTemplates\",\n\t\t\t\tasync (server, cursor, requestOptions) => {\n\t\t\t\t\tconst result = await server.resourceTemplatesPage(cursor, requestOptions);\n\t\t\t\t\treturn { items: result.resourceTemplates, nextCursor: result.nextCursor };\n\t\t\t\t},\n\t\t\t\t(server, requestOptions) => server.allResourceTemplates(requestOptions),\n\t\t\t);\n\t\t\treturn jsonResult(LIST_MCP_RESOURCE_TEMPLATES_TOOL, stringArgument(params, \"server\"), payload);\n\t\t},\n\t};\n\n\tconst readResource: ToolDefinition<TSchema, McpToolDetails> = {\n\t\tname: READ_MCP_RESOURCE_TOOL,\n\t\tlabel: READ_MCP_RESOURCE_TOOL,\n\t\tdescription: \"Read a specific resource from an MCP server given the server name and resource URI.\",\n\t\tparameters: READ_PARAMETERS as unknown as TSchema,\n\t\toutputSchema: READ_OUTPUT_SCHEMA as unknown as TSchema,\n\t\texposure: toToolExposure(options.exposure),\n\t\tannotations: readOnly,\n\t\tasync execute(_toolCallId, params, signal) {\n\t\t\tconst serverName = stringArgument(params, \"server\");\n\t\t\tconst uri = stringArgument(params, \"uri\");\n\t\t\tif (!serverName) throw new Error(\"server must be provided\");\n\t\t\tif (!uri) throw new Error(\"uri must be provided\");\n\t\t\tconst server = findServer(serverName);\n\t\t\tconst result = await server.readResource(uri, { signal, timeoutMs: server.timeoutMs });\n\t\t\t// Several contents (for example a directory) are labeled with their URIs.\n\t\t\tconst blocks: ContentBlock[] = result.contents.flatMap((contents) => [\n\t\t\t\t...(result.contents.length > 1 ? [{ type: \"text\" as const, text: `${contents.uri}:` }] : []),\n\t\t\t\t{ type: \"resource\" as const, resource: contents },\n\t\t\t]);\n\t\t\tconst converted = await toModelContent(server.name, blocks);\n\t\t\tconst { content, fullOutputPath } = await limitMcpContent(\n\t\t\t\tconverted.length > 0 ? converted : [{ type: \"text\", text: `Resource ${uri} is empty.` }],\n\t\t\t);\n\t\t\tconst contents = result.contents.map(({ _meta: _ignored, ...rest }) => rest);\n\t\t\treturn {\n\t\t\t\tcontent,\n\t\t\t\tdetails: {\n\t\t\t\t\tserver: server.name,\n\t\t\t\t\ttool: READ_MCP_RESOURCE_TOOL,\n\t\t\t\t\t...(fullOutputPath ? { fullOutputPath } : {}),\n\t\t\t\t},\n\t\t\t\tstructuredContent: { server: server.name, uri, contents } as unknown as JsonValue,\n\t\t\t};\n\t\t},\n\t};\n\n\treturn [listResources, listTemplates, readResource];\n}\n"]}
|
|
@@ -38,7 +38,8 @@ export declare class McpServerConnection implements McpToolCaller, McpResourceSe
|
|
|
38
38
|
challenge: OAuthChallenge | undefined;
|
|
39
39
|
private client;
|
|
40
40
|
private opening;
|
|
41
|
-
|
|
41
|
+
/** Aborted by `close()`; cancels a connect in progress, including the wait between retries. */
|
|
42
|
+
private readonly shutdown;
|
|
42
43
|
/** Stderr of the last stdio server that failed to connect. */
|
|
43
44
|
private stderrTail;
|
|
44
45
|
private readonly cwd;
|
|
@@ -61,6 +62,7 @@ export declare class McpServerConnection implements McpToolCaller, McpResourceSe
|
|
|
61
62
|
log?: McpServerLog;
|
|
62
63
|
});
|
|
63
64
|
get name(): string;
|
|
65
|
+
private get closed();
|
|
64
66
|
get timeoutMs(): number;
|
|
65
67
|
/** Server URL when the server authenticates with OAuth. */
|
|
66
68
|
get oauthUrl(): string | undefined;
|
|
@@ -93,6 +95,7 @@ export declare class McpServerConnection implements McpToolCaller, McpResourceSe
|
|
|
93
95
|
private handleClientClose;
|
|
94
96
|
private refreshTools;
|
|
95
97
|
private refreshResources;
|
|
98
|
+
/** Resolves once no transport of this server is open, including one that was still connecting. */
|
|
96
99
|
close(): Promise<void>;
|
|
97
100
|
}
|
|
98
101
|
//# sourceMappingURL=runtime.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../../../src/extensions/mcp/runtime.ts"],"names":[],"mappings":"AAAA;;;;GAIG;
|
|
1
|
+
{"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../../../src/extensions/mcp/runtime.ts"],"names":[],"mappings":"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"}
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
import { homedir } from "node:os";
|
|
7
7
|
import { basename, join, resolve } from "node:path";
|
|
8
|
+
import { setTimeout as sleep } from "node:timers/promises";
|
|
8
9
|
import { pathToFileURL } from "node:url";
|
|
9
10
|
import { JSON_RPC_ERROR_CODES, McpAuthRequiredError, McpClient, McpError, McpHttpError, McpSessionExpiredError, StdioTransport, StreamableHttpTransport, } from "@earendil-works/pi-mcp";
|
|
10
11
|
import { McpOAuthAuthorizationRequiredError } from "@earendil-works/pi-mcp/oauth";
|
|
@@ -116,7 +117,8 @@ export class McpServerConnection {
|
|
|
116
117
|
challenge;
|
|
117
118
|
client;
|
|
118
119
|
opening;
|
|
119
|
-
|
|
120
|
+
/** Aborted by `close()`; cancels a connect in progress, including the wait between retries. */
|
|
121
|
+
shutdown = new AbortController();
|
|
120
122
|
/** Stderr of the last stdio server that failed to connect. */
|
|
121
123
|
stderrTail;
|
|
122
124
|
cwd;
|
|
@@ -151,6 +153,9 @@ export class McpServerConnection {
|
|
|
151
153
|
get name() {
|
|
152
154
|
return this.entry.name;
|
|
153
155
|
}
|
|
156
|
+
get closed() {
|
|
157
|
+
return this.shutdown.signal.aborted;
|
|
158
|
+
}
|
|
154
159
|
get timeoutMs() {
|
|
155
160
|
return (this.entry.config.timeout ?? DEFAULT_TIMEOUT_SECONDS) * 1000;
|
|
156
161
|
}
|
|
@@ -281,7 +286,7 @@ export class McpServerConnection {
|
|
|
281
286
|
if (this.closed || delay === undefined || !isTransientError(error)) {
|
|
282
287
|
throw this.connectFailed(error);
|
|
283
288
|
}
|
|
284
|
-
await
|
|
289
|
+
await sleep(delay, undefined, { signal: this.shutdown.signal }).catch(() => undefined);
|
|
285
290
|
if (this.closed)
|
|
286
291
|
throw this.connectFailed(error);
|
|
287
292
|
}
|
|
@@ -297,6 +302,14 @@ export class McpServerConnection {
|
|
|
297
302
|
const log = this.log;
|
|
298
303
|
if (log)
|
|
299
304
|
client.onNotification("notifications/message", (params) => log.write(this.entry.name, params));
|
|
305
|
+
// Closing the client fails its pending initialize or discovery requests and closes the transport.
|
|
306
|
+
// Memoized: a second `client.close()` returns before the first finished closing the transport.
|
|
307
|
+
let closing;
|
|
308
|
+
const closeClient = () => {
|
|
309
|
+
closing ??= client.close().catch(() => undefined);
|
|
310
|
+
return closing;
|
|
311
|
+
};
|
|
312
|
+
this.shutdown.signal.addEventListener("abort", closeClient, { once: true });
|
|
300
313
|
let transport;
|
|
301
314
|
try {
|
|
302
315
|
transport = this.createTransport(this.entry, this.cwd, this.authProvider);
|
|
@@ -332,12 +345,15 @@ export class McpServerConnection {
|
|
|
332
345
|
return client;
|
|
333
346
|
}
|
|
334
347
|
catch (error) {
|
|
335
|
-
await
|
|
348
|
+
await closeClient();
|
|
336
349
|
if (transport instanceof StdioTransport) {
|
|
337
350
|
this.stderrTail = transport.stderr.trim().slice(-STDERR_TAIL_CHARS) || undefined;
|
|
338
351
|
}
|
|
339
352
|
throw error;
|
|
340
353
|
}
|
|
354
|
+
finally {
|
|
355
|
+
this.shutdown.signal.removeEventListener("abort", closeClient);
|
|
356
|
+
}
|
|
341
357
|
}
|
|
342
358
|
connectFailed(error) {
|
|
343
359
|
if (this.needsSignIn(error) && !this.closed) {
|
|
@@ -381,13 +397,14 @@ export class McpServerConnection {
|
|
|
381
397
|
this.onTools(this);
|
|
382
398
|
this.changed();
|
|
383
399
|
}
|
|
400
|
+
/** Resolves once no transport of this server is open, including one that was still connecting. */
|
|
384
401
|
async close() {
|
|
385
|
-
this.
|
|
402
|
+
this.shutdown.abort();
|
|
386
403
|
this.state = "closed";
|
|
387
404
|
this.changed();
|
|
388
405
|
const client = this.client;
|
|
389
406
|
this.client = undefined;
|
|
390
|
-
await client?.close().catch(() => undefined);
|
|
407
|
+
await Promise.all([client?.close().catch(() => undefined), this.opening?.catch(() => undefined)]);
|
|
391
408
|
// A refresh the server already answered may have rotated the refresh token; exiting before the
|
|
392
409
|
// new tokens are saved would lose the grant.
|
|
393
410
|
await this.authProvider?.settled();
|