@aexol/spectral 0.9.251 → 0.9.255
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/dist/extensions/dataset-uploader/analyzer.d.ts +103 -0
- package/dist/extensions/dataset-uploader/analyzer.d.ts.map +1 -0
- package/dist/extensions/dataset-uploader/analyzer.js +266 -0
- package/dist/extensions/dataset-uploader/cleaner.d.ts +59 -0
- package/dist/extensions/dataset-uploader/cleaner.d.ts.map +1 -0
- package/dist/extensions/dataset-uploader/cleaner.js +118 -0
- package/dist/extensions/dataset-uploader/exporter.d.ts +147 -0
- package/dist/extensions/dataset-uploader/exporter.d.ts.map +1 -0
- package/dist/extensions/dataset-uploader/exporter.js +313 -0
- package/dist/extensions/dataset-uploader/extra-values.d.ts +17 -0
- package/dist/extensions/dataset-uploader/extra-values.d.ts.map +1 -0
- package/dist/extensions/dataset-uploader/extra-values.js +51 -0
- package/dist/extensions/dataset-uploader/index.d.ts +22 -0
- package/dist/extensions/dataset-uploader/index.d.ts.map +1 -0
- package/dist/extensions/dataset-uploader/index.js +247 -0
- package/dist/extensions/dataset-uploader/scrubber.d.ts +34 -0
- package/dist/extensions/dataset-uploader/scrubber.d.ts.map +1 -0
- package/dist/extensions/dataset-uploader/scrubber.js +91 -0
- package/dist/extensions/dataset-uploader/upload.d.ts +105 -0
- package/dist/extensions/dataset-uploader/upload.d.ts.map +1 -0
- package/dist/extensions/dataset-uploader/upload.js +274 -0
- package/dist/generated/zeus/const.d.ts.map +1 -1
- package/dist/generated/zeus/const.js +80 -6
- package/dist/generated/zeus/index.d.ts +432 -36
- package/dist/generated/zeus/index.d.ts.map +1 -1
- package/dist/mcp/commands.d.ts.map +1 -1
- package/dist/mcp/commands.js +3 -0
- package/dist/mcp/direct-tools.d.ts.map +1 -1
- package/dist/mcp/direct-tools.js +4 -3
- package/dist/mcp/index.d.ts.map +1 -1
- package/dist/mcp/index.js +5 -1
- package/dist/mcp/mcp-auth-flow.d.ts +5 -1
- package/dist/mcp/mcp-auth-flow.d.ts.map +1 -1
- package/dist/mcp/mcp-auth-flow.js +20 -4
- package/dist/mcp/mcp-auth-start.d.ts +64 -0
- package/dist/mcp/mcp-auth-start.d.ts.map +1 -0
- package/dist/mcp/mcp-auth-start.js +136 -0
- package/dist/mcp/proxy-modes.d.ts +7 -0
- package/dist/mcp/proxy-modes.d.ts.map +1 -1
- package/dist/mcp/proxy-modes.js +71 -11
- package/dist/mcp/server-manager.d.ts +2 -1
- package/dist/mcp/server-manager.d.ts.map +1 -1
- package/dist/mcp/server-manager.js +8 -4
- package/dist/memory/commands/status.d.ts.map +1 -1
- package/dist/memory/commands/status.js +2 -1
- package/dist/memory/hooks/compaction-trigger.d.ts +14 -1
- package/dist/memory/hooks/compaction-trigger.d.ts.map +1 -1
- package/dist/memory/hooks/compaction-trigger.js +31 -4
- package/dist/relay/dispatcher.d.ts.map +1 -1
- package/dist/relay/dispatcher.js +4 -47
- package/dist/relay/routing.d.ts +1 -1
- package/dist/relay/routing.d.ts.map +1 -1
- package/dist/relay/routing.js +9 -0
- package/dist/sdk/ai/utils/image-source.d.ts +26 -11
- package/dist/sdk/ai/utils/image-source.d.ts.map +1 -1
- package/dist/sdk/ai/utils/image-source.js +184 -47
- package/dist/sdk/ai/utils/transient-error.d.ts +4 -0
- package/dist/sdk/ai/utils/transient-error.d.ts.map +1 -1
- package/dist/sdk/ai/utils/transient-error.js +15 -0
- package/dist/sdk/coding-agent/core/compaction/policy.d.ts +2 -0
- package/dist/sdk/coding-agent/core/compaction/policy.d.ts.map +1 -1
- package/dist/sdk/coding-agent/core/compaction/policy.js +2 -0
- package/dist/sdk/coding-agent/core/extensions/native-extensions.d.ts.map +1 -1
- package/dist/sdk/coding-agent/core/extensions/native-extensions.js +10 -0
- package/dist/sdk/coding-agent/core/system-prompt.d.ts.map +1 -1
- package/dist/sdk/coding-agent/core/system-prompt.js +4 -0
- package/dist/sdk/coding-agent/core/tools/read.d.ts.map +1 -1
- package/dist/sdk/coding-agent/core/tools/read.js +14 -7
- package/dist/sdk/coding-agent/index.d.ts +1 -1
- package/dist/sdk/coding-agent/index.d.ts.map +1 -1
- package/dist/sdk/coding-agent/index.js +1 -1
- package/dist/sdk/coding-agent/utils/image-resize.d.ts +25 -11
- package/dist/sdk/coding-agent/utils/image-resize.d.ts.map +1 -1
- package/dist/sdk/coding-agent/utils/image-resize.js +54 -36
- package/dist/sdk/coding-agent/utils/photon.d.ts.map +1 -1
- package/dist/sdk/coding-agent/utils/photon.js +11 -2
- package/dist/server/agent-bridge.d.ts +32 -0
- package/dist/server/agent-bridge.d.ts.map +1 -1
- package/dist/server/agent-bridge.js +59 -9
- package/dist/server/handlers/mcp-auth.d.ts +28 -0
- package/dist/server/handlers/mcp-auth.d.ts.map +1 -0
- package/dist/server/handlers/mcp-auth.js +41 -0
- package/dist/server/handlers/mcp-status.d.ts +8 -2
- package/dist/server/handlers/mcp-status.d.ts.map +1 -1
- package/dist/server/handlers/mcp-status.js +15 -3
- package/dist/server/preset-chain.d.ts +0 -13
- package/dist/server/preset-chain.d.ts.map +1 -1
- package/dist/server/preset-chain.js +0 -25
- package/dist/server/session-stream/memory.d.ts.map +1 -1
- package/dist/server/session-stream/memory.js +8 -1
- package/dist/server/session-stream.d.ts +15 -56
- package/dist/server/session-stream.d.ts.map +1 -1
- package/dist/server/session-stream.js +41 -271
- package/dist/server/wire.d.ts +18 -32
- package/dist/server/wire.d.ts.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"commands.d.ts","sourceRoot":"","sources":["../../src/mcp/commands.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AACnF,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AACpD,OAAO,KAAK,EAAE,SAAS,EAAe,MAAM,YAAY,CAAC;AASzD,wBAAsB,UAAU,CAAC,KAAK,EAAE,iBAAiB,EAAE,GAAG,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC,CAsC/F;AAED,wBAAsB,SAAS,CAAC,KAAK,EAAE,iBAAiB,EAAE,GAAG,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC,CAmB9F;AAED,wBAAsB,gBAAgB,CACpC,KAAK,EAAE,iBAAiB,EACxB,GAAG,EAAE,gBAAgB,EACrB,YAAY,CAAC,EAAE,MAAM,GACpB,OAAO,CAAC,IAAI,CAAC,CAiDf;AAED,wBAAsB,kBAAkB,CACtC,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,SAAS,EACjB,GAAG,EAAE,gBAAgB,GACpB,OAAO,CAAC,IAAI,CAAC,
|
|
1
|
+
{"version":3,"file":"commands.d.ts","sourceRoot":"","sources":["../../src/mcp/commands.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AACnF,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AACpD,OAAO,KAAK,EAAE,SAAS,EAAe,MAAM,YAAY,CAAC;AASzD,wBAAsB,UAAU,CAAC,KAAK,EAAE,iBAAiB,EAAE,GAAG,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC,CAsC/F;AAED,wBAAsB,SAAS,CAAC,KAAK,EAAE,iBAAiB,EAAE,GAAG,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC,CAmB9F;AAED,wBAAsB,gBAAgB,CACpC,KAAK,EAAE,iBAAiB,EACxB,GAAG,EAAE,gBAAgB,EACrB,YAAY,CAAC,EAAE,MAAM,GACpB,OAAO,CAAC,IAAI,CAAC,CAiDf;AAED,wBAAsB,kBAAkB,CACtC,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,SAAS,EACjB,GAAG,EAAE,gBAAgB,GACpB,OAAO,CAAC,IAAI,CAAC,CAkDf;AAED,wBAAsB,eAAe,CAAC,KAAK,EAAE,iBAAiB,EAAE,GAAG,EAAE,YAAY,EAAE,GAAG,EAAE,gBAAgB,EAAE,kBAAkB,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAqBpJ"}
|
package/dist/mcp/commands.js
CHANGED
|
@@ -122,6 +122,9 @@ export async function authenticateServer(serverName, config, ctx) {
|
|
|
122
122
|
ctx.ui.notify(`OAuth authentication successful for "${serverName}"!\n` +
|
|
123
123
|
`Run /mcp reconnect ${serverName} to connect with the new token.`, "info");
|
|
124
124
|
}
|
|
125
|
+
else if (status === "in_progress") {
|
|
126
|
+
ctx.ui.notify(`OAuth authentication for "${serverName}" is already in progress — deferring to the in-flight flow.`, "info");
|
|
127
|
+
}
|
|
125
128
|
else {
|
|
126
129
|
ctx.ui.notify(`OAuth authentication failed for "${serverName}".`, "error");
|
|
127
130
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"direct-tools.d.ts","sourceRoot":"","sources":["../../src/mcp/direct-tools.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AACnE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AACpD,OAAO,KAAK,EAAE,cAAc,EAAE,SAAS,EAAc,MAAM,YAAY,CAAC;AACxE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAwEzD,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,SAAS,EACjB,KAAK,EAAE,aAAa,GAAG,IAAI,EAC3B,MAAM,EAAE,QAAQ,GAAG,MAAM,GAAG,OAAO,EACnC,WAAW,CAAC,EAAE,MAAM,EAAE,GACrB,cAAc,EAAE,CAoGlB;AAED,wBAAgB,qCAAqC,CACnD,MAAM,EAAE,SAAS,EACjB,KAAK,EAAE,aAAa,GAAG,IAAI,GAC1B,MAAM,EAAE,CAkBV;AAED,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,SAAS,EACjB,KAAK,EAAE,aAAa,GAAG,IAAI,EAC3B,WAAW,EAAE,cAAc,EAAE,GAC5B,MAAM,
|
|
1
|
+
{"version":3,"file":"direct-tools.d.ts","sourceRoot":"","sources":["../../src/mcp/direct-tools.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AACnE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AACpD,OAAO,KAAK,EAAE,cAAc,EAAE,SAAS,EAAc,MAAM,YAAY,CAAC;AACxE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAwEzD,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,SAAS,EACjB,KAAK,EAAE,aAAa,GAAG,IAAI,EAC3B,MAAM,EAAE,QAAQ,GAAG,MAAM,GAAG,OAAO,EACnC,WAAW,CAAC,EAAE,MAAM,EAAE,GACrB,cAAc,EAAE,CAoGlB;AAED,wBAAgB,qCAAqC,CACnD,MAAM,EAAE,SAAS,EACjB,KAAK,EAAE,aAAa,GAAG,IAAI,GAC1B,MAAM,EAAE,CAkBV;AAED,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,SAAS,EACjB,KAAK,EAAE,aAAa,GAAG,IAAI,EAC3B,WAAW,EAAE,cAAc,EAAE,GAC5B,MAAM,CAqDR;AAED,KAAK,iBAAiB,GAAG,cAAc,CAAC,SAAS,CAAC,CAAC;AAEnD,wBAAgB,wBAAwB,CACtC,QAAQ,EAAE,MAAM,iBAAiB,GAAG,IAAI,EACxC,cAAc,EAAE,MAAM,OAAO,CAAC,iBAAiB,CAAC,GAAG,IAAI,EACvD,IAAI,EAAE,cAAc,GACnB,iBAAiB,CAwJnB"}
|
package/dist/mcp/direct-tools.js
CHANGED
|
@@ -8,7 +8,7 @@ import { resourceNameToToolName } from "./resource-tools.js";
|
|
|
8
8
|
import { authenticate, supportsOAuth } from "./mcp-auth-flow.js";
|
|
9
9
|
import { formatAuthRequiredMessage } from "./utils.js";
|
|
10
10
|
const BUILTIN_NAMES = new Set(["read", "bash", "edit", "write", "grep", "ls", "mcp"]);
|
|
11
|
-
function getDirectAuthRequiredMessage(state, serverName, defaultMessage = `MCP server "${serverName}" requires OAuth authentication. Run /mcp-auth ${serverName} first.`) {
|
|
11
|
+
function getDirectAuthRequiredMessage(state, serverName, defaultMessage = `MCP server "${serverName}" requires OAuth authentication. Run /mcp-auth ${serverName} first, or call mcp({ auth: "${serverName}" }) to start the OAuth flow (a browser opens on this machine).`) {
|
|
12
12
|
return formatAuthRequiredMessage(state.config, serverName, defaultMessage);
|
|
13
13
|
}
|
|
14
14
|
function getDirectAuthFailedMessage(state, serverName, message) {
|
|
@@ -16,7 +16,7 @@ function getDirectAuthFailedMessage(state, serverName, message) {
|
|
|
16
16
|
if (customGuidance) {
|
|
17
17
|
return `OAuth authentication failed for "${serverName}": ${message}. ${getDirectAuthRequiredMessage(state, serverName)}`;
|
|
18
18
|
}
|
|
19
|
-
return `OAuth authentication failed for "${serverName}": ${message}. Run /mcp-auth ${serverName} first.`;
|
|
19
|
+
return `OAuth authentication failed for "${serverName}": ${message}. Run /mcp-auth ${serverName} first, or call mcp({ auth: "${serverName}" }) to start the OAuth flow.`;
|
|
20
20
|
}
|
|
21
21
|
async function attemptDirectAutoAuth(state, serverName) {
|
|
22
22
|
if (state.config.settings?.autoAuth !== true) {
|
|
@@ -31,7 +31,7 @@ async function attemptDirectAutoAuth(state, serverName) {
|
|
|
31
31
|
if (!state.ui && grantType !== "client_credentials") {
|
|
32
32
|
return {
|
|
33
33
|
status: "failed",
|
|
34
|
-
message: getDirectAuthRequiredMessage(state, serverName, `MCP server "${serverName}" requires OAuth authentication.
|
|
34
|
+
message: getDirectAuthRequiredMessage(state, serverName, `MCP server "${serverName}" requires OAuth authentication. Call mcp({ auth: "${serverName}" }) to start the OAuth flow (a browser opens on this machine), or run /mcp-auth ${serverName} in an interactive session.`),
|
|
35
35
|
};
|
|
36
36
|
}
|
|
37
37
|
try {
|
|
@@ -206,6 +206,7 @@ export function buildProxyDescription(config, cache, directSpecs) {
|
|
|
206
206
|
desc += ` mcp({ search: "query" }) → Search MCP tools by name/description\n`;
|
|
207
207
|
desc += ` mcp({ describe: "tool_name" }) → Show tool details and parameters\n`;
|
|
208
208
|
desc += ` mcp({ connect: "server-name" }) → Connect to a server and refresh metadata\n`;
|
|
209
|
+
desc += ` mcp({ auth: "server-name" }) → Start OAuth for a server (non-blocking; opens a browser on this machine)\n`;
|
|
209
210
|
desc += ` mcp({ tool: "name", args: '{"key": "value"}' }) → Call a tool (args is JSON string)\n`;
|
|
210
211
|
desc += ` mcp({ action: "ui-messages" }) → Retrieve accumulated messages from completed UI sessions\n`;
|
|
211
212
|
desc += `\nMode: tool (call) > connect > describe > search > server (list) > action > nothing (status)`;
|
package/dist/mcp/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/mcp/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAY,MAAM,8BAA8B,CAAC;AAa3E,MAAM,CAAC,OAAO,UAAU,UAAU,CAAC,GAAG,EAAE,YAAY,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/mcp/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAY,MAAM,8BAA8B,CAAC;AAa3E,MAAM,CAAC,OAAO,UAAU,UAAU,CAAC,GAAG,EAAE,YAAY,QA0SnD"}
|
package/dist/mcp/index.js
CHANGED
|
@@ -5,7 +5,7 @@ import { loadMcpConfig } from "./config.js";
|
|
|
5
5
|
import { buildProxyDescription, createDirectToolExecutor, getMissingConfiguredDirectToolServers, resolveDirectTools } from "./direct-tools.js";
|
|
6
6
|
import { flushMetadataCache, initializeMcp, updateStatusBar } from "./init.js";
|
|
7
7
|
import { loadMetadataCache } from "./metadata-cache.js";
|
|
8
|
-
import { executeCall, executeConnect, executeDescribe, executeSearch, executeStatus, executeUiMessages } from "./proxy-modes.js";
|
|
8
|
+
import { executeAuth, executeCall, executeConnect, executeDescribe, executeSearch, executeStatus, executeUiMessages } from "./proxy-modes.js";
|
|
9
9
|
import { getConfigPathFromArgv, truncateAtWord } from "./utils.js";
|
|
10
10
|
import { initializeOAuth, shutdownOAuth } from "./mcp-auth-flow.js";
|
|
11
11
|
export default function mcpAdapter(ext) {
|
|
@@ -98,6 +98,7 @@ export default function mcpAdapter(ext) {
|
|
|
98
98
|
parameters: Type.Object({
|
|
99
99
|
tool: Type.Optional(Type.String({ description: "Tool name to call (e.g., 'xcodebuild_list_sims')" })),
|
|
100
100
|
args: Type.Optional(Type.String({ description: "Arguments as JSON string (e.g., '{\"key\": \"value\"}')" })),
|
|
101
|
+
auth: Type.Optional(Type.String({ description: "Server name to authenticate via OAuth. Starts a non-blocking OAuth flow and opens a browser on this machine. Use when a server reports needs-auth." })),
|
|
101
102
|
connect: Type.Optional(Type.String({ description: "Server name to connect (closes any stale connection first, forces fresh reconnect)" })),
|
|
102
103
|
describe: Type.Optional(Type.String({ description: "Tool name to describe (shows parameters)" })),
|
|
103
104
|
search: Type.Optional(Type.String({ description: "Search tools by name/description" })),
|
|
@@ -142,6 +143,9 @@ export default function mcpAdapter(ext) {
|
|
|
142
143
|
details: { error: "not_initialized" },
|
|
143
144
|
};
|
|
144
145
|
}
|
|
146
|
+
if (params.auth) {
|
|
147
|
+
return executeAuth(state, params.auth);
|
|
148
|
+
}
|
|
145
149
|
if (params.action === "ui-messages") {
|
|
146
150
|
return executeUiMessages(state);
|
|
147
151
|
}
|
|
@@ -6,13 +6,17 @@
|
|
|
6
6
|
import { type StoredTokens } from "./mcp-auth.js";
|
|
7
7
|
import type { ServerEntry } from "./types.js";
|
|
8
8
|
/** Auth status for a server */
|
|
9
|
-
export type AuthStatus = "authenticated" | "expired" | "not_authenticated";
|
|
9
|
+
export type AuthStatus = "authenticated" | "expired" | "not_authenticated" | "in_progress";
|
|
10
10
|
/**
|
|
11
11
|
* Start OAuth authentication flow for a server.
|
|
12
12
|
* Returns the authorization URL when browser authorization is required.
|
|
13
|
+
* Also returns the OAuth `state` parameter that was generated and persisted
|
|
14
|
+
* for this flow, so headless callers (e.g. the `/api/mcp-auth` handler) can
|
|
15
|
+
* wait for the callback without re-reading it from storage.
|
|
13
16
|
*/
|
|
14
17
|
export declare function startAuth(serverName: string, serverUrl: string, definition?: ServerEntry): Promise<{
|
|
15
18
|
authorizationUrl: string;
|
|
19
|
+
oauthState?: string;
|
|
16
20
|
}>;
|
|
17
21
|
/**
|
|
18
22
|
* Complete OAuth authentication with the authorization code.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mcp-auth-flow.d.ts","sourceRoot":"","sources":["../../src/mcp/mcp-auth-flow.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAeH,OAAO,EAQL,KAAK,YAAY,EAClB,MAAM,eAAe,CAAA;
|
|
1
|
+
{"version":3,"file":"mcp-auth-flow.d.ts","sourceRoot":"","sources":["../../src/mcp/mcp-auth-flow.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAeH,OAAO,EAQL,KAAK,YAAY,EAClB,MAAM,eAAe,CAAA;AAEtB,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;AAE7C,+BAA+B;AAC/B,MAAM,MAAM,UAAU,GAClB,eAAe,GACf,SAAS,GACT,mBAAmB,GACnB,aAAa,CAAA;AAkDjB;;;;;;GAMG;AACH,wBAAsB,SAAS,CAC7B,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,MAAM,EACjB,UAAU,CAAC,EAAE,WAAW,GACvB,OAAO,CAAC;IAAE,gBAAgB,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CAyD5D;AAED;;GAEG;AACH,wBAAsB,YAAY,CAChC,UAAU,EAAE,MAAM,EAClB,iBAAiB,EAAE,MAAM,GACxB,OAAO,CAAC,UAAU,CAAC,CAcrB;AAED;;;;;;;GAOG;AACH,wBAAsB,YAAY,CAChC,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,MAAM,EACjB,UAAU,CAAC,EAAE,WAAW,GACvB,OAAO,CAAC,UAAU,CAAC,CAqFrB;AAED;;;;;;GAMG;AACH,wBAAsB,aAAa,CACjC,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CA2C9B;AAED;;;;;GAKG;AACH,wBAAsB,aAAa,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CAM3E;AAED;;;;GAIG;AACH,wBAAsB,UAAU,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAalE;AAED;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,UAAU,EAAE,WAAW,GAAG,OAAO,CAU9D;AAED;;;GAGG;AACH,wBAAsB,eAAe,IAAI,OAAO,CAAC,IAAI,CAAC,CAErD;AAED;;;GAGG;AACH,wBAAsB,aAAa,IAAI,OAAO,CAAC,IAAI,CAAC,CAEnD"}
|
|
@@ -9,6 +9,7 @@ import open from "open";
|
|
|
9
9
|
import { McpOAuthProvider, setOAuthCallbackPort } from "./mcp-oauth-provider.js";
|
|
10
10
|
import { ensureCallbackServer, waitForCallback, cancelPendingCallback, stopCallbackServer, } from "./mcp-callback-server.js";
|
|
11
11
|
import { getAuthForUrl, isTokenExpired, hasStoredTokens, clearAllCredentials, updateOAuthState, getOAuthState, clearOAuthState, } from "./mcp-auth.js";
|
|
12
|
+
import { inFlightMcpAuths } from "./mcp-auth-start.js";
|
|
12
13
|
// Track pending transports for auth completion
|
|
13
14
|
const pendingTransports = new Map();
|
|
14
15
|
// Deduplicate concurrent authenticate() calls per server.
|
|
@@ -56,6 +57,9 @@ function extractOAuthConfig(definition) {
|
|
|
56
57
|
/**
|
|
57
58
|
* Start OAuth authentication flow for a server.
|
|
58
59
|
* Returns the authorization URL when browser authorization is required.
|
|
60
|
+
* Also returns the OAuth `state` parameter that was generated and persisted
|
|
61
|
+
* for this flow, so headless callers (e.g. the `/api/mcp-auth` handler) can
|
|
62
|
+
* wait for the callback without re-reading it from storage.
|
|
59
63
|
*/
|
|
60
64
|
export async function startAuth(serverName, serverUrl, definition) {
|
|
61
65
|
const config = definition ? extractOAuthConfig(definition) : {};
|
|
@@ -101,7 +105,7 @@ export async function startAuth(serverName, serverUrl, definition) {
|
|
|
101
105
|
throw new UnauthorizedError("OAuth authorization URL was not provided");
|
|
102
106
|
}
|
|
103
107
|
pendingTransports.set(serverName, new StreamableHTTPClientTransport(new URL(serverUrl), { authProvider }));
|
|
104
|
-
return { authorizationUrl: capturedUrl.toString() };
|
|
108
|
+
return { authorizationUrl: capturedUrl.toString(), oauthState };
|
|
105
109
|
}
|
|
106
110
|
catch (error) {
|
|
107
111
|
await clearOAuthState(serverName);
|
|
@@ -135,19 +139,30 @@ export async function completeAuth(serverName, authorizationCode) {
|
|
|
135
139
|
* @returns The final auth status
|
|
136
140
|
*/
|
|
137
141
|
export async function authenticate(serverName, serverUrl, definition) {
|
|
142
|
+
// Canonical cross-entry-point in-flight guard: a non-blocking flow
|
|
143
|
+
// (startMcpAuthFlow) may already be in flight for this server. Starting
|
|
144
|
+
// startAuth again would overwrite the persisted OAuth state and the
|
|
145
|
+
// pending transport the in-flight browser callback completes against,
|
|
146
|
+
// breaking the first flow. Defer to it instead.
|
|
147
|
+
if (inFlightMcpAuths.has(serverName)) {
|
|
148
|
+
return "in_progress";
|
|
149
|
+
}
|
|
138
150
|
const inFlight = pendingAuthentications.get(serverName);
|
|
139
151
|
if (inFlight) {
|
|
140
152
|
return inFlight;
|
|
141
153
|
}
|
|
154
|
+
// Track this blocking flow in the same canonical guard so a concurrent
|
|
155
|
+
// non-blocking start (relay / agent tool) defers to us as well.
|
|
156
|
+
inFlightMcpAuths.add(serverName);
|
|
142
157
|
const operation = (async () => {
|
|
143
158
|
// Start auth flow
|
|
144
|
-
const { authorizationUrl } = await startAuth(serverName, serverUrl, definition);
|
|
159
|
+
const { authorizationUrl, oauthState } = await startAuth(serverName, serverUrl, definition);
|
|
145
160
|
// If no auth URL needed, already authenticated
|
|
146
161
|
if (!authorizationUrl) {
|
|
147
162
|
return "authenticated";
|
|
148
163
|
}
|
|
149
|
-
//
|
|
150
|
-
|
|
164
|
+
// The state was generated + persisted inside startAuth() and is returned
|
|
165
|
+
// together with the authorization URL.
|
|
151
166
|
if (!oauthState) {
|
|
152
167
|
throw new Error("OAuth state not found - this should not happen");
|
|
153
168
|
}
|
|
@@ -194,6 +209,7 @@ export async function authenticate(serverName, serverUrl, definition) {
|
|
|
194
209
|
if (pendingAuthentications.get(serverName) === operation) {
|
|
195
210
|
pendingAuthentications.delete(serverName);
|
|
196
211
|
}
|
|
212
|
+
inFlightMcpAuths.delete(serverName);
|
|
197
213
|
}
|
|
198
214
|
}
|
|
199
215
|
/**
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP Auth Start — shared, non-blocking OAuth flow starter.
|
|
3
|
+
*
|
|
4
|
+
* Single code path used by:
|
|
5
|
+
* - the relay handler (`POST /api/mcp-auth`)
|
|
6
|
+
* - the agent-facing `mcp` tool (`auth` action) and `connect` auto-trigger
|
|
7
|
+
*
|
|
8
|
+
* The flow is non-blocking: `startAuth` captures the authorization URL, and
|
|
9
|
+
* the local callback server completes the exchange in the background
|
|
10
|
+
* (waitForCallback → completeAuth → reloadMcpConfig).
|
|
11
|
+
*/
|
|
12
|
+
import type { McpExtensionState } from "./state.js";
|
|
13
|
+
/**
|
|
14
|
+
* Validation/in-flight errors thrown by `startMcpAuthFlow`. The relay handler
|
|
15
|
+
* maps these to `BadRequestError` (HTTP 400); the agent tool surfaces the
|
|
16
|
+
* message as tool output.
|
|
17
|
+
*/
|
|
18
|
+
export declare class McpAuthStartError extends Error {
|
|
19
|
+
constructor(message: string);
|
|
20
|
+
}
|
|
21
|
+
export interface StartMcpAuthFlowOptions {
|
|
22
|
+
/** Best-effort: open the authorization URL in the system browser. */
|
|
23
|
+
openBrowser?: boolean;
|
|
24
|
+
}
|
|
25
|
+
export type StartMcpAuthFlowResult = {
|
|
26
|
+
status: "already-authenticated";
|
|
27
|
+
} | {
|
|
28
|
+
status: "auth_url_ready";
|
|
29
|
+
authUrl: string;
|
|
30
|
+
openedBrowser: boolean;
|
|
31
|
+
};
|
|
32
|
+
/**
|
|
33
|
+
* Per-server in-flight guard. `startAuth` keys its pending transports and
|
|
34
|
+
* persisted OAuth state by server name, so two concurrent flows for the same
|
|
35
|
+
* server would clobber each other. Reject the second start until the first
|
|
36
|
+
* flow's background chain finishes.
|
|
37
|
+
*/
|
|
38
|
+
export declare const inFlightMcpAuths: Set<string>;
|
|
39
|
+
/**
|
|
40
|
+
* An authorization URL may be opened in a real browser, so it must never be
|
|
41
|
+
* a `javascript:` / `data:` / malformed URL coming from a malicious MCP
|
|
42
|
+
* server's OAuth metadata. Defense in depth: the landing UI applies the
|
|
43
|
+
* same check before `window.open`.
|
|
44
|
+
*/
|
|
45
|
+
export declare function assertSafeAuthorizationUrl(authUrl: string): void;
|
|
46
|
+
/**
|
|
47
|
+
* Start a non-blocking OAuth flow for a configured MCP server.
|
|
48
|
+
*
|
|
49
|
+
* - Looks up the server definition (throws `McpAuthStartError` when missing,
|
|
50
|
+
* not OAuth-capable, or url-less).
|
|
51
|
+
* - Enforces the per-server in-flight guard.
|
|
52
|
+
* - Calls `startAuth`: an empty `authorizationUrl` means the server is
|
|
53
|
+
* already authenticated (or was via client_credentials).
|
|
54
|
+
* - Optionally opens the authorization URL in the system browser
|
|
55
|
+
* (best-effort — a failed `open` never fails the call).
|
|
56
|
+
* - Kicks off the background completion chain
|
|
57
|
+
* (waitForCallback → completeAuth → reloadMcpConfig) fire-and-forget.
|
|
58
|
+
*
|
|
59
|
+
* @param state - The live MCP extension state (config + reload target).
|
|
60
|
+
* @param name - Configured MCP server name.
|
|
61
|
+
* @param opts - `openBrowser` to launch the system browser on the auth URL.
|
|
62
|
+
*/
|
|
63
|
+
export declare function startMcpAuthFlow(state: McpExtensionState, name: string, opts?: StartMcpAuthFlowOptions): Promise<StartMcpAuthFlowResult>;
|
|
64
|
+
//# sourceMappingURL=mcp-auth-start.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcp-auth-start.d.ts","sourceRoot":"","sources":["../../src/mcp/mcp-auth-start.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAWH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAEpD;;;;GAIG;AACH,qBAAa,iBAAkB,SAAQ,KAAK;gBAC9B,OAAO,EAAE,MAAM;CAI5B;AAED,MAAM,WAAW,uBAAuB;IACtC,qEAAqE;IACrE,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAED,MAAM,MAAM,sBAAsB,GAC9B;IAAE,MAAM,EAAE,uBAAuB,CAAA;CAAE,GACnC;IAAE,MAAM,EAAE,gBAAgB,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,OAAO,CAAA;CAAE,CAAC;AAE1E;;;;;GAKG;AACH,eAAO,MAAM,gBAAgB,aAAoB,CAAC;AAElD;;;;;GAKG;AACH,wBAAgB,0BAA0B,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAUhE;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAsB,gBAAgB,CACpC,KAAK,EAAE,iBAAiB,EACxB,IAAI,EAAE,MAAM,EACZ,IAAI,GAAE,uBAA4B,GACjC,OAAO,CAAC,sBAAsB,CAAC,CA+EjC"}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP Auth Start — shared, non-blocking OAuth flow starter.
|
|
3
|
+
*
|
|
4
|
+
* Single code path used by:
|
|
5
|
+
* - the relay handler (`POST /api/mcp-auth`)
|
|
6
|
+
* - the agent-facing `mcp` tool (`auth` action) and `connect` auto-trigger
|
|
7
|
+
*
|
|
8
|
+
* The flow is non-blocking: `startAuth` captures the authorization URL, and
|
|
9
|
+
* the local callback server completes the exchange in the background
|
|
10
|
+
* (waitForCallback → completeAuth → reloadMcpConfig).
|
|
11
|
+
*/
|
|
12
|
+
import open from "open";
|
|
13
|
+
import { completeAuth, startAuth, supportsOAuth, } from "./mcp-auth-flow.js";
|
|
14
|
+
import { clearOAuthState } from "./mcp-auth.js";
|
|
15
|
+
import { waitForCallback } from "./mcp-callback-server.js";
|
|
16
|
+
import { reloadMcpConfig } from "./init.js";
|
|
17
|
+
/**
|
|
18
|
+
* Validation/in-flight errors thrown by `startMcpAuthFlow`. The relay handler
|
|
19
|
+
* maps these to `BadRequestError` (HTTP 400); the agent tool surfaces the
|
|
20
|
+
* message as tool output.
|
|
21
|
+
*/
|
|
22
|
+
export class McpAuthStartError extends Error {
|
|
23
|
+
constructor(message) {
|
|
24
|
+
super(message);
|
|
25
|
+
this.name = "McpAuthStartError";
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Per-server in-flight guard. `startAuth` keys its pending transports and
|
|
30
|
+
* persisted OAuth state by server name, so two concurrent flows for the same
|
|
31
|
+
* server would clobber each other. Reject the second start until the first
|
|
32
|
+
* flow's background chain finishes.
|
|
33
|
+
*/
|
|
34
|
+
export const inFlightMcpAuths = new Set();
|
|
35
|
+
/**
|
|
36
|
+
* An authorization URL may be opened in a real browser, so it must never be
|
|
37
|
+
* a `javascript:` / `data:` / malformed URL coming from a malicious MCP
|
|
38
|
+
* server's OAuth metadata. Defense in depth: the landing UI applies the
|
|
39
|
+
* same check before `window.open`.
|
|
40
|
+
*/
|
|
41
|
+
export function assertSafeAuthorizationUrl(authUrl) {
|
|
42
|
+
let parsed;
|
|
43
|
+
try {
|
|
44
|
+
parsed = new URL(authUrl);
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
throw new McpAuthStartError("Invalid authorization URL");
|
|
48
|
+
}
|
|
49
|
+
if (parsed.protocol !== "https:" && parsed.protocol !== "http:") {
|
|
50
|
+
throw new McpAuthStartError("Invalid authorization URL");
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Start a non-blocking OAuth flow for a configured MCP server.
|
|
55
|
+
*
|
|
56
|
+
* - Looks up the server definition (throws `McpAuthStartError` when missing,
|
|
57
|
+
* not OAuth-capable, or url-less).
|
|
58
|
+
* - Enforces the per-server in-flight guard.
|
|
59
|
+
* - Calls `startAuth`: an empty `authorizationUrl` means the server is
|
|
60
|
+
* already authenticated (or was via client_credentials).
|
|
61
|
+
* - Optionally opens the authorization URL in the system browser
|
|
62
|
+
* (best-effort — a failed `open` never fails the call).
|
|
63
|
+
* - Kicks off the background completion chain
|
|
64
|
+
* (waitForCallback → completeAuth → reloadMcpConfig) fire-and-forget.
|
|
65
|
+
*
|
|
66
|
+
* @param state - The live MCP extension state (config + reload target).
|
|
67
|
+
* @param name - Configured MCP server name.
|
|
68
|
+
* @param opts - `openBrowser` to launch the system browser on the auth URL.
|
|
69
|
+
*/
|
|
70
|
+
export async function startMcpAuthFlow(state, name, opts = {}) {
|
|
71
|
+
const definition = state.config.mcpServers[name];
|
|
72
|
+
if (!definition) {
|
|
73
|
+
throw new McpAuthStartError(`Unknown MCP server: ${name}`);
|
|
74
|
+
}
|
|
75
|
+
if (!definition.url || !supportsOAuth(definition)) {
|
|
76
|
+
throw new McpAuthStartError(`MCP server "${name}" does not support OAuth`);
|
|
77
|
+
}
|
|
78
|
+
if (inFlightMcpAuths.has(name)) {
|
|
79
|
+
throw new McpAuthStartError("OAuth authentication already in progress for this server");
|
|
80
|
+
}
|
|
81
|
+
inFlightMcpAuths.add(name);
|
|
82
|
+
let authorizationUrl;
|
|
83
|
+
let oauthState;
|
|
84
|
+
try {
|
|
85
|
+
({ authorizationUrl, oauthState } = await startAuth(name, definition.url, definition));
|
|
86
|
+
if (authorizationUrl === "") {
|
|
87
|
+
inFlightMcpAuths.delete(name);
|
|
88
|
+
return { status: "already-authenticated" };
|
|
89
|
+
}
|
|
90
|
+
// Fail loudly instead of skipping background completion — a flow without
|
|
91
|
+
// its persisted state could never be completed nor cleanly aborted.
|
|
92
|
+
if (!oauthState) {
|
|
93
|
+
throw new McpAuthStartError("OAuth state missing after authorization start");
|
|
94
|
+
}
|
|
95
|
+
assertSafeAuthorizationUrl(authorizationUrl);
|
|
96
|
+
}
|
|
97
|
+
catch (error) {
|
|
98
|
+
// Release the guard so a failed start does not wedge the server.
|
|
99
|
+
inFlightMcpAuths.delete(name);
|
|
100
|
+
throw error;
|
|
101
|
+
}
|
|
102
|
+
if (opts.openBrowser) {
|
|
103
|
+
// Best-effort: a failed `open` must not fail the call — the user can
|
|
104
|
+
// always open authUrl manually.
|
|
105
|
+
open(authorizationUrl).catch((error) => {
|
|
106
|
+
console.warn(`MCP Auth: failed to open browser for ${name}`, error);
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
// Complete the flow in the background from the local callback server:
|
|
110
|
+
// waitForCallback resolves with the authorization code, completeAuth
|
|
111
|
+
// exchanges it for tokens, and the MCP config is reloaded so the server
|
|
112
|
+
// comes up authenticated. Fire-and-forget — auth progress is observable
|
|
113
|
+
// via server status, so failures are logged, not surfaced. The finally
|
|
114
|
+
// block releases the per-server in-flight guard and clears the
|
|
115
|
+
// now-stale OAuth state (idempotent) on every path.
|
|
116
|
+
waitForCallback(oauthState)
|
|
117
|
+
.then((code) => completeAuth(name, code))
|
|
118
|
+
.then(() => reloadMcpConfig(state))
|
|
119
|
+
.catch((error) => {
|
|
120
|
+
console.warn(`MCP Auth: background completion failed for ${name}`, error);
|
|
121
|
+
})
|
|
122
|
+
.finally(() => {
|
|
123
|
+
inFlightMcpAuths.delete(name);
|
|
124
|
+
try {
|
|
125
|
+
clearOAuthState(name);
|
|
126
|
+
}
|
|
127
|
+
catch {
|
|
128
|
+
// Storage errors must not surface as unhandled rejections.
|
|
129
|
+
}
|
|
130
|
+
});
|
|
131
|
+
return {
|
|
132
|
+
status: "auth_url_ready",
|
|
133
|
+
authUrl: authorizationUrl,
|
|
134
|
+
openedBrowser: !!opts.openBrowser,
|
|
135
|
+
};
|
|
136
|
+
}
|
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
import type { AgentToolResult, ToolInfo } from "../sdk/coding-agent/index.js";
|
|
2
2
|
import type { McpExtensionState } from "./state.js";
|
|
3
3
|
type ProxyToolResult = AgentToolResult<Record<string, unknown>>;
|
|
4
|
+
/**
|
|
5
|
+
* Start the OAuth flow for a server (the `mcp({ auth: "<name>" })` action).
|
|
6
|
+
* Non-blocking: opens the browser on this machine and returns immediately;
|
|
7
|
+
* the local callback server completes the token exchange in the background
|
|
8
|
+
* and reloads the MCP config, so the next connect succeeds.
|
|
9
|
+
*/
|
|
10
|
+
export declare function executeAuth(state: McpExtensionState, serverName: string): Promise<ProxyToolResult>;
|
|
4
11
|
export declare function executeUiMessages(state: McpExtensionState): ProxyToolResult;
|
|
5
12
|
export declare function executeStatus(state: McpExtensionState): ProxyToolResult;
|
|
6
13
|
export declare function executeDescribe(state: McpExtensionState, toolName: string): ProxyToolResult;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"proxy-modes.d.ts","sourceRoot":"","sources":["../../src/mcp/proxy-modes.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,QAAQ,EAAE,MAAM,8BAA8B,CAAC;AAC9E,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"proxy-modes.d.ts","sourceRoot":"","sources":["../../src/mcp/proxy-modes.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,QAAQ,EAAE,MAAM,8BAA8B,CAAC;AAC9E,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAWpD,KAAK,eAAe,GAAG,eAAe,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;AAuBhE;;;;;GAKG;AACH,wBAAsB,WAAW,CAC/B,KAAK,EAAE,iBAAiB,EACxB,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,eAAe,CAAC,CAoB1B;AA+BD,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,iBAAiB,GAAG,eAAe,CA4E3E;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,iBAAiB,GAAG,eAAe,CAsDvE;AAED,wBAAgB,eAAe,CAAC,KAAK,EAAE,iBAAiB,EAAE,QAAQ,EAAE,MAAM,GAAG,eAAe,CAuC3F;AAED,wBAAgB,aAAa,CAC3B,KAAK,EAAE,iBAAiB,EACxB,KAAK,EAAE,MAAM,EACb,KAAK,CAAC,EAAE,OAAO,EACf,MAAM,CAAC,EAAE,MAAM,EACf,cAAc,CAAC,EAAE,OAAO,GACvB,eAAe,CAiFjB;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,iBAAiB,EAAE,MAAM,EAAE,MAAM,GAAG,eAAe,CAqDrF;AAED,wBAAsB,cAAc,CAAC,KAAK,EAAE,iBAAiB,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC,CAuF3G;AAED,wBAAsB,WAAW,CAC/B,KAAK,EAAE,iBAAiB,EACxB,QAAQ,EAAE,MAAM,EAChB,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC9B,cAAc,CAAC,EAAE,MAAM,EACvB,gBAAgB,CAAC,EAAE,MAAM,QAAQ,EAAE,GAClC,OAAO,CAAC,eAAe,CAAC,CAkW1B"}
|
package/dist/mcp/proxy-modes.js
CHANGED
|
@@ -5,7 +5,8 @@ import { transformMcpContent } from "./tool-registrar.js";
|
|
|
5
5
|
import { maybeStartUiSession } from "./ui-session.js";
|
|
6
6
|
import { formatAuthRequiredMessage, truncateAtWord } from "./utils.js";
|
|
7
7
|
import { authenticate, supportsOAuth } from "./mcp-auth-flow.js";
|
|
8
|
-
|
|
8
|
+
import { startMcpAuthFlow } from "./mcp-auth-start.js";
|
|
9
|
+
function getAuthRequiredMessage(state, serverName, defaultMessage = `Server "${serverName}" requires OAuth authentication. Run /mcp-auth ${serverName} first, or call mcp({ auth: "${serverName}" }) to start the OAuth flow (a browser opens on this machine).`) {
|
|
9
10
|
return formatAuthRequiredMessage(state.config, serverName, defaultMessage);
|
|
10
11
|
}
|
|
11
12
|
function getAuthFailedMessage(state, serverName, message) {
|
|
@@ -13,7 +14,35 @@ function getAuthFailedMessage(state, serverName, message) {
|
|
|
13
14
|
if (customGuidance) {
|
|
14
15
|
return `OAuth authentication failed for "${serverName}": ${message}. ${getAuthRequiredMessage(state, serverName)}`;
|
|
15
16
|
}
|
|
16
|
-
return `OAuth authentication failed for "${serverName}": ${message}. Run /mcp-auth ${serverName} first.`;
|
|
17
|
+
return `OAuth authentication failed for "${serverName}": ${message}. Run /mcp-auth ${serverName} first, or call mcp({ auth: "${serverName}" }) to start the OAuth flow.`;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Start the OAuth flow for a server (the `mcp({ auth: "<name>" })` action).
|
|
21
|
+
* Non-blocking: opens the browser on this machine and returns immediately;
|
|
22
|
+
* the local callback server completes the token exchange in the background
|
|
23
|
+
* and reloads the MCP config, so the next connect succeeds.
|
|
24
|
+
*/
|
|
25
|
+
export async function executeAuth(state, serverName) {
|
|
26
|
+
try {
|
|
27
|
+
const result = await startMcpAuthFlow(state, serverName, { openBrowser: true });
|
|
28
|
+
if (result.status === "already-authenticated") {
|
|
29
|
+
return {
|
|
30
|
+
content: [{ type: "text", text: `MCP server "${serverName}" is already authenticated.` }],
|
|
31
|
+
details: { mode: "auth", server: serverName, status: "already-authenticated" },
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
return {
|
|
35
|
+
content: [{ type: "text", text: `OAuth started for MCP server "${serverName}". A browser window should have opened on this machine — complete the sign-in there, then retry the connection.` }],
|
|
36
|
+
details: { mode: "auth", server: serverName, status: "auth_url_ready", authUrl: result.authUrl },
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
catch (error) {
|
|
40
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
41
|
+
return {
|
|
42
|
+
content: [{ type: "text", text: `Failed to start OAuth for "${serverName}": ${message}` }],
|
|
43
|
+
details: { mode: "auth", server: serverName, error: "auth_start_failed", message },
|
|
44
|
+
};
|
|
45
|
+
}
|
|
17
46
|
}
|
|
18
47
|
async function attemptAutoAuth(state, serverName) {
|
|
19
48
|
if (state.config.settings?.autoAuth !== true) {
|
|
@@ -340,16 +369,47 @@ export async function executeConnect(state, serverName) {
|
|
|
340
369
|
await state.manager.close(serverName);
|
|
341
370
|
let connection = await state.manager.connect(serverName, definition);
|
|
342
371
|
if (connection.status === "needs-auth") {
|
|
343
|
-
const
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
372
|
+
const autoAuthEnabled = state.config.settings?.autoAuth === true;
|
|
373
|
+
const oauthConfig = definition.oauth === false ? undefined : definition.oauth;
|
|
374
|
+
const isClientCredentials = oauthConfig?.grantType === "client_credentials";
|
|
375
|
+
// authorization-code + autoAuth: non-blocking OAuth — open the browser
|
|
376
|
+
// and return immediately (the callback server completes the exchange).
|
|
377
|
+
// client_credentials (no browser) keeps the blocking attemptAutoAuth
|
|
378
|
+
// path, as does autoAuth=false (attemptAutoAuth returns "skipped").
|
|
379
|
+
const useBrowserFlow = autoAuthEnabled && !isClientCredentials;
|
|
380
|
+
if (!useBrowserFlow) {
|
|
381
|
+
const autoAuth = await attemptAutoAuth(state, serverName);
|
|
382
|
+
if (autoAuth.status === "failed") {
|
|
383
|
+
return {
|
|
384
|
+
content: [{ type: "text", text: autoAuth.message }],
|
|
385
|
+
details: { mode: "connect", error: "auth_required", server: serverName, message: autoAuth.message },
|
|
386
|
+
};
|
|
387
|
+
}
|
|
388
|
+
if (autoAuth.status === "success") {
|
|
389
|
+
await state.manager.close(serverName);
|
|
390
|
+
connection = await state.manager.connect(serverName, definition);
|
|
391
|
+
}
|
|
349
392
|
}
|
|
350
|
-
if (
|
|
351
|
-
|
|
352
|
-
|
|
393
|
+
if (connection.status === "needs-auth" && useBrowserFlow) {
|
|
394
|
+
try {
|
|
395
|
+
const result = await startMcpAuthFlow(state, serverName, { openBrowser: true });
|
|
396
|
+
if (result.status === "auth_url_ready") {
|
|
397
|
+
return {
|
|
398
|
+
content: [{ type: "text", text: `OAuth started for MCP server "${serverName}". A browser window should have opened on this machine — complete the sign-in there, then retry the connection.` }],
|
|
399
|
+
details: { mode: "connect", error: "auth_started", server: serverName, authUrl: result.authUrl },
|
|
400
|
+
};
|
|
401
|
+
}
|
|
402
|
+
// already-authenticated (race) — reconnect below.
|
|
403
|
+
await state.manager.close(serverName);
|
|
404
|
+
connection = await state.manager.connect(serverName, definition);
|
|
405
|
+
}
|
|
406
|
+
catch (error) {
|
|
407
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
408
|
+
return {
|
|
409
|
+
content: [{ type: "text", text: getAuthFailedMessage(state, serverName, message) }],
|
|
410
|
+
details: { mode: "connect", error: "auth_failed", server: serverName, message },
|
|
411
|
+
};
|
|
412
|
+
}
|
|
353
413
|
}
|
|
354
414
|
if (connection.status === "needs-auth") {
|
|
355
415
|
const message = getAuthRequiredMessage(state, serverName);
|
|
@@ -4,7 +4,8 @@ import type { McpTool, McpResource, ServerDefinition, ServerStreamResultPatchNot
|
|
|
4
4
|
import { type ServerSamplingConfig } from "./sampling-handler.js";
|
|
5
5
|
interface ServerConnection {
|
|
6
6
|
client: Client;
|
|
7
|
-
transport
|
|
7
|
+
/** Undefined for needs-auth connections whose probe threw before a transport existed. */
|
|
8
|
+
transport?: Transport;
|
|
8
9
|
definition: ServerDefinition;
|
|
9
10
|
tools: McpTool[];
|
|
10
11
|
resources: McpResource[];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server-manager.d.ts","sourceRoot":"","sources":["../../src/mcp/server-manager.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AAKnE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,oCAAoC,CAAC;AAC7E,OAAO,KAAK,EACV,OAAO,EACP,WAAW,EACX,gBAAgB,EAChB,mCAAmC,EACnC,SAAS,EACV,MAAM,YAAY,CAAC;AAMpB,OAAO,EAA2B,KAAK,oBAAoB,EAAE,MAAM,uBAAuB,CAAC;AAI3F,UAAU,gBAAgB;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"server-manager.d.ts","sourceRoot":"","sources":["../../src/mcp/server-manager.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AAKnE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,oCAAoC,CAAC;AAC7E,OAAO,KAAK,EACV,OAAO,EACP,WAAW,EACX,gBAAgB,EAChB,mCAAmC,EACnC,SAAS,EACV,MAAM,YAAY,CAAC;AAMpB,OAAO,EAA2B,KAAK,oBAAoB,EAAE,MAAM,uBAAuB,CAAC;AAI3F,UAAU,gBAAgB;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,yFAAyF;IACzF,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB,UAAU,EAAE,gBAAgB,CAAC;IAC7B,KAAK,EAAE,OAAO,EAAE,CAAC;IACjB,SAAS,EAAE,WAAW,EAAE,CAAC;IACzB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,WAAW,GAAG,QAAQ,GAAG,YAAY,CAAC;CAC/C;AAED,KAAK,gBAAgB,GAAG,CAAC,UAAU,EAAE,MAAM,EAAE,YAAY,EAAE,mCAAmC,CAAC,QAAQ,CAAC,KAAK,IAAI,CAAC;AAElH,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,WAAW,CAAuC;IAC1D,OAAO,CAAC,eAAe,CAAgD;IACvE,OAAO,CAAC,iBAAiB,CAAuC;IAChE,OAAO,CAAC,cAAc,CAAmC;IAEzD,iBAAiB,CAAC,MAAM,EAAE,oBAAoB,GAAG,SAAS,GAAG,IAAI;IAI3D,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,gBAAgB,GAAG,OAAO,CAAC,gBAAgB,CAAC;YAyBtE,gBAAgB;IAuF9B,OAAO,CAAC,YAAY;YAWN,mBAAmB;YAyEnB,aAAa;YAab,iBAAiB;IAkB/B,OAAO,CAAC,iCAAiC;IAQzC,wBAAwB,CAAC,WAAW,EAAE,MAAM,EAAE,QAAQ,EAAE,gBAAgB,GAAG,IAAI;IAI/E,sBAAsB,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI;IAI3C,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,CAAC;IAgBpE,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAelC,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IAK/B,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,gBAAgB,GAAG,SAAS;IAIzD,iBAAiB,IAAI,GAAG,CAAC,MAAM,EAAE,gBAAgB,CAAC;IAIlD,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAOzB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAOrC,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAOrC,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO;CAMjD"}
|
|
@@ -95,14 +95,18 @@ export class McpServerManager {
|
|
|
95
95
|
if (transport) {
|
|
96
96
|
await transport.close().catch(() => { });
|
|
97
97
|
}
|
|
98
|
-
//
|
|
99
|
-
//
|
|
98
|
+
// OAuth-configured servers: classify ANY probe/connect failure as
|
|
99
|
+
// needs-auth, not only UnauthorizedError. OAuth discovery can fail
|
|
100
|
+
// with non-401 errors (missing/malformed WWW-Authenticate metadata,
|
|
101
|
+
// 405 from the SSE fallback, etc.) — surfacing "error" would wedge the
|
|
102
|
+
// server and hide the auth entry point. This can fire from
|
|
103
|
+
// createHttpTransport probe or client.connect.
|
|
100
104
|
// transport may be undefined if createHttpTransport threw during the probe
|
|
101
105
|
// before returning a transport; close() handles this safely.
|
|
102
|
-
if (
|
|
106
|
+
if (supportsOAuth(definition)) {
|
|
103
107
|
return {
|
|
104
108
|
client,
|
|
105
|
-
transport
|
|
109
|
+
transport,
|
|
106
110
|
definition,
|
|
107
111
|
tools: [],
|
|
108
112
|
resources: [],
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"status.d.ts","sourceRoot":"","sources":["../../../src/memory/commands/status.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iCAAiC,CAAC;
|
|
1
|
+
{"version":3,"file":"status.d.ts","sourceRoot":"","sources":["../../../src/memory/commands/status.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iCAAiC,CAAC;AAUpE,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAI7C,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,YAAY,EAAE,OAAO,EAAE,OAAO,GAAG,IAAI,CA8F/E"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { SettingsManager } from "../../sdk/coding-agent/index.js";
|
|
2
2
|
import { getMemoryState, rawTokensSinceLastBound, rawTokensSinceLastCompaction, } from "../branch.js";
|
|
3
|
+
import { getContextWindow, getPostRunCompactionThresholdTokens } from "../hooks/compaction-trigger.js";
|
|
3
4
|
import { observationPoolTokens as estimateObservationPoolTokens } from "../compaction.js";
|
|
4
5
|
import { countByRelevance, formatRelevanceHistogram } from "../relevance.js";
|
|
5
6
|
import { estimateStringTokens } from "../tokens.js";
|
|
@@ -24,7 +25,7 @@ export function registerStatusCommand(ext, runtime) {
|
|
|
24
25
|
const compactionPolicy = SettingsManager.create(ctx.cwd).getCompactionPolicy();
|
|
25
26
|
const keepRecentTokens = compactionPolicy.conversation.keepRecentTokens;
|
|
26
27
|
const obsThreshold = runtime.config.observationThresholdTokens;
|
|
27
|
-
const compThreshold =
|
|
28
|
+
const compThreshold = getPostRunCompactionThresholdTokens(runtime, compactionPolicy, getContextWindow(ctx));
|
|
28
29
|
const refThreshold = runtime.config.reflectionThresholdTokens;
|
|
29
30
|
// Approximation: the real compaction gate excludes pending obs whose coversFromId falls inside
|
|
30
31
|
// the new keep-recent tail (deferred to next cycle) and adds any sync-catch-up gap obs produced
|
|
@@ -1,4 +1,17 @@
|
|
|
1
|
-
import type { ExtensionAPI } from "../../sdk/coding-agent/index.js";
|
|
1
|
+
import type { ExtensionAPI, ExtensionContext } from "../../sdk/coding-agent/index.js";
|
|
2
|
+
import { type CompactionPolicy } from "../../sdk/coding-agent/core/compaction/index.js";
|
|
2
3
|
import type { Runtime } from "../runtime.js";
|
|
4
|
+
/**
|
|
5
|
+
* Resolve the active model's context window. Prefers the live usage report,
|
|
6
|
+
* falls back to the model definition; returns undefined when unknown.
|
|
7
|
+
*/
|
|
8
|
+
export declare function getContextWindow(ctx: Pick<ExtensionContext, "getContextUsage" | "model">): number | null | undefined;
|
|
9
|
+
/**
|
|
10
|
+
* Effective post-run compaction threshold: the configured/policy floor, capped
|
|
11
|
+
* at AUTO_COMPACTION_WINDOW_RATIO × the model context window when the window
|
|
12
|
+
* is known. An unknown window (0/undefined, e.g. synthetic/proxy models) keeps
|
|
13
|
+
* the uncapped formula.
|
|
14
|
+
*/
|
|
15
|
+
export declare function getPostRunCompactionThresholdTokens(runtime: Runtime, policy: CompactionPolicy, contextWindow?: number | null): number;
|
|
3
16
|
export declare function registerCompactionTrigger(ext: ExtensionAPI, runtime: Runtime): void;
|
|
4
17
|
//# sourceMappingURL=compaction-trigger.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"compaction-trigger.d.ts","sourceRoot":"","sources":["../../../src/memory/hooks/compaction-trigger.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,
|
|
1
|
+
{"version":3,"file":"compaction-trigger.d.ts","sourceRoot":"","sources":["../../../src/memory/hooks/compaction-trigger.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,iCAAiC,CAAC;AACtF,OAAO,EAGN,KAAK,gBAAgB,EACrB,MAAM,iDAAiD,CAAC;AAEzD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAc7C;;;GAGG;AACH,wBAAgB,gBAAgB,CAC/B,GAAG,EAAE,IAAI,CAAC,gBAAgB,EAAE,iBAAiB,GAAG,OAAO,CAAC,GACtD,MAAM,GAAG,IAAI,GAAG,SAAS,CAW3B;AAED;;;;;GAKG;AACH,wBAAgB,mCAAmC,CAClD,OAAO,EAAE,OAAO,EAChB,MAAM,EAAE,gBAAgB,EACxB,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,GAC3B,MAAM,CAMR;AACD,wBAAgB,yBAAyB,CAAC,GAAG,EAAE,YAAY,EAAE,OAAO,EAAE,OAAO,GAAG,IAAI,CAmGnF"}
|