@aexol/spectral 0.9.254 → 0.9.256
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 +55 -0
- package/dist/generated/zeus/index.d.ts +264 -0
- 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 +20 -2
- package/dist/mcp/mcp-auth-flow.d.ts.map +1 -1
- package/dist/mcp/mcp-auth-flow.js +87 -7
- package/dist/mcp/mcp-auth-start.d.ts +63 -0
- package/dist/mcp/mcp-auth-start.d.ts.map +1 -0
- package/dist/mcp/mcp-auth-start.js +131 -0
- package/dist/mcp/mcp-oauth-provider.d.ts +24 -0
- package/dist/mcp/mcp-oauth-provider.d.ts.map +1 -1
- package/dist/mcp/mcp-oauth-provider.js +20 -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 +9 -4
- package/dist/mcp/types.d.ts +9 -0
- package/dist/mcp/types.d.ts.map +1 -1
- 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/server/handlers/mcp-auth.d.ts +7 -4
- package/dist/server/handlers/mcp-auth.d.ts.map +1 -1
- package/dist/server/handlers/mcp-auth.js +15 -80
- 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
|
}
|
|
@@ -5,8 +5,22 @@
|
|
|
5
5
|
*/
|
|
6
6
|
import { type StoredTokens } from "./mcp-auth.js";
|
|
7
7
|
import type { ServerEntry } from "./types.js";
|
|
8
|
+
/**
|
|
9
|
+
* Validation/startup errors thrown by the MCP OAuth entry points. The relay
|
|
10
|
+
* handler maps these to `BadRequestError` (HTTP 400); the agent tool surfaces
|
|
11
|
+
* the message as tool output.
|
|
12
|
+
*
|
|
13
|
+
* Defined here (rather than in mcp-auth-start.ts) because `startAuth` in this
|
|
14
|
+
* module throws it too — mcp-auth-start.ts re-exports it, so existing
|
|
15
|
+
* imports from mcp-auth-start.js keep working without a circular-import
|
|
16
|
+
* change (the two modules already reference each other via
|
|
17
|
+
* `inFlightMcpAuths`, and this re-export rides that existing edge).
|
|
18
|
+
*/
|
|
19
|
+
export declare class McpAuthStartError extends Error {
|
|
20
|
+
constructor(message: string, options?: ErrorOptions);
|
|
21
|
+
}
|
|
8
22
|
/** Auth status for a server */
|
|
9
|
-
export type AuthStatus = "authenticated" | "expired" | "not_authenticated";
|
|
23
|
+
export type AuthStatus = "authenticated" | "expired" | "not_authenticated" | "in_progress";
|
|
10
24
|
/**
|
|
11
25
|
* Start OAuth authentication flow for a server.
|
|
12
26
|
* Returns the authorization URL when browser authorization is required.
|
|
@@ -36,9 +50,13 @@ export declare function authenticate(serverName: string, serverUrl: string, defi
|
|
|
36
50
|
*
|
|
37
51
|
* @param serverName - The name of the MCP server
|
|
38
52
|
* @param serverUrl - The URL of the MCP server
|
|
53
|
+
* @param definition - The server definition (optional). When provided, the
|
|
54
|
+
* refresh provider is built from the real OAuth config so a
|
|
55
|
+
* pre-registered client (clientId/clientSecret/scope) is available
|
|
56
|
+
* during the refresh; without it, behavior is unchanged.
|
|
39
57
|
* @returns The valid tokens or null if not authenticated
|
|
40
58
|
*/
|
|
41
|
-
export declare function getValidToken(serverName: string, serverUrl: string): Promise<StoredTokens | null>;
|
|
59
|
+
export declare function getValidToken(serverName: string, serverUrl: string, definition?: ServerEntry): Promise<StoredTokens | null>;
|
|
42
60
|
/**
|
|
43
61
|
* Check the authentication status for a server.
|
|
44
62
|
*
|
|
@@ -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;;;;;;;;;;GAUG;AACH,qBAAa,iBAAkB,SAAQ,KAAK;gBAC9B,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,YAAY;CAIpD;AAED,+BAA+B;AAC/B,MAAM,MAAM,UAAU,GAClB,eAAe,GACf,SAAS,GACT,mBAAmB,GACnB,aAAa,CAAA;AA2FjB;;;;;;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,CAsE5D;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;;;;;;;;;;GAUG;AACH,wBAAsB,aAAa,CACjC,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,MAAM,EACjB,UAAU,CAAC,EAAE,WAAW,GACvB,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CA8C9B;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,10 +9,57 @@ 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";
|
|
13
|
+
/**
|
|
14
|
+
* Validation/startup errors thrown by the MCP OAuth entry points. The relay
|
|
15
|
+
* handler maps these to `BadRequestError` (HTTP 400); the agent tool surfaces
|
|
16
|
+
* the message as tool output.
|
|
17
|
+
*
|
|
18
|
+
* Defined here (rather than in mcp-auth-start.ts) because `startAuth` in this
|
|
19
|
+
* module throws it too — mcp-auth-start.ts re-exports it, so existing
|
|
20
|
+
* imports from mcp-auth-start.js keep working without a circular-import
|
|
21
|
+
* change (the two modules already reference each other via
|
|
22
|
+
* `inFlightMcpAuths`, and this re-export rides that existing edge).
|
|
23
|
+
*/
|
|
24
|
+
export class McpAuthStartError extends Error {
|
|
25
|
+
constructor(message, options) {
|
|
26
|
+
super(message, options);
|
|
27
|
+
this.name = "McpAuthStartError";
|
|
28
|
+
}
|
|
29
|
+
}
|
|
12
30
|
// Track pending transports for auth completion
|
|
13
31
|
const pendingTransports = new Map();
|
|
14
32
|
// Deduplicate concurrent authenticate() calls per server.
|
|
15
33
|
const pendingAuthentications = new Map();
|
|
34
|
+
/**
|
|
35
|
+
* The SDK's dynamic-registration failure message
|
|
36
|
+
* ("Incompatible auth server: does not support dynamic client registration").
|
|
37
|
+
*/
|
|
38
|
+
const DCR_FAILURE_RE = /does not support dynamic client registration/i;
|
|
39
|
+
/**
|
|
40
|
+
* Tolerant match: when no pre-registered client is configured, any
|
|
41
|
+
* registration-related failure points at the same missing configuration.
|
|
42
|
+
*/
|
|
43
|
+
const REGISTRATION_FAILURE_RE = /dynamic client registration|registration (request )?failed/i;
|
|
44
|
+
/**
|
|
45
|
+
* Wrap an SDK `auth()` failure: when the authorization server does not
|
|
46
|
+
* support dynamic client registration (and no pre-registered client is
|
|
47
|
+
* configured), rethrow a self-explanatory error pointing at mcp.json,
|
|
48
|
+
* preserving the original error as `cause`. Non-DCR failures pass through
|
|
49
|
+
* untouched.
|
|
50
|
+
*/
|
|
51
|
+
function rethrowDcrFailure(error, serverName, clientId) {
|
|
52
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
53
|
+
const isDcr = DCR_FAILURE_RE.test(message) ||
|
|
54
|
+
(!clientId && REGISTRATION_FAILURE_RE.test(message));
|
|
55
|
+
if (!isDcr) {
|
|
56
|
+
throw error;
|
|
57
|
+
}
|
|
58
|
+
throw new McpAuthStartError(`MCP server '${serverName}' OAuth failed: the provider does not support dynamic client registration and no pre-registered client is configured. ` +
|
|
59
|
+
`Add \`oauth: { clientId: '...', clientSecret?: '...' }\` to this server's config in mcp.json. ` +
|
|
60
|
+
`If the authorization server supports Client ID Metadata Documents (client_id_metadata_document_supported), ` +
|
|
61
|
+
`you can also use \`oauth: { clientMetadataUrl: 'https://.../client-metadata.json' }\` instead of a clientId.`, { cause: error instanceof Error ? error : new Error(message) });
|
|
62
|
+
}
|
|
16
63
|
/**
|
|
17
64
|
* Generate a cryptographically secure random state parameter.
|
|
18
65
|
*/
|
|
@@ -50,6 +97,7 @@ function extractOAuthConfig(definition) {
|
|
|
50
97
|
clientId: definition.oauth?.clientId,
|
|
51
98
|
clientSecret: definition.oauth?.clientSecret,
|
|
52
99
|
scope: definition.oauth?.scope,
|
|
100
|
+
clientMetadataUrl: definition.oauth?.clientMetadataUrl,
|
|
53
101
|
redirectUri: definition.oauth?.redirectUri,
|
|
54
102
|
};
|
|
55
103
|
}
|
|
@@ -68,7 +116,13 @@ export async function startAuth(serverName, serverUrl, definition) {
|
|
|
68
116
|
throw new Error("Browser redirect is not used for client_credentials flow");
|
|
69
117
|
},
|
|
70
118
|
});
|
|
71
|
-
|
|
119
|
+
let result;
|
|
120
|
+
try {
|
|
121
|
+
result = await runSdkAuth(authProvider, { serverUrl });
|
|
122
|
+
}
|
|
123
|
+
catch (error) {
|
|
124
|
+
rethrowDcrFailure(error, serverName, config.clientId);
|
|
125
|
+
}
|
|
72
126
|
if (result !== "AUTHORIZED") {
|
|
73
127
|
throw new UnauthorizedError("Failed to authorize");
|
|
74
128
|
}
|
|
@@ -77,13 +131,15 @@ export async function startAuth(serverName, serverUrl, definition) {
|
|
|
77
131
|
// Start the callback server.
|
|
78
132
|
// Pre-registered OAuth clients require an exact redirect URI, so enforce strict port binding.
|
|
79
133
|
// When a custom redirectUri is specified, also use strict port binding and set the port.
|
|
134
|
+
// CIMD clients (clientMetadataUrl) are the same: the redirect URI in the
|
|
135
|
+
// hosted metadata document must match, so the callback port is fixed.
|
|
80
136
|
const hasCustomRedirect = Boolean(config.redirectUri);
|
|
81
137
|
const redirectPort = hasCustomRedirect ? parseRedirectPort(config.redirectUri) : undefined;
|
|
82
138
|
if (redirectPort) {
|
|
83
139
|
setOAuthCallbackPort(redirectPort);
|
|
84
140
|
}
|
|
85
141
|
await ensureCallbackServer({
|
|
86
|
-
strictPort: Boolean(config.clientId) || hasCustomRedirect,
|
|
142
|
+
strictPort: Boolean(config.clientId || config.clientMetadataUrl) || hasCustomRedirect,
|
|
87
143
|
preferredPort: redirectPort,
|
|
88
144
|
});
|
|
89
145
|
const oauthState = generateState();
|
|
@@ -95,7 +151,12 @@ export async function startAuth(serverName, serverUrl, definition) {
|
|
|
95
151
|
},
|
|
96
152
|
});
|
|
97
153
|
try {
|
|
98
|
-
const result = await runSdkAuth(authProvider, {
|
|
154
|
+
const result = await runSdkAuth(authProvider, {
|
|
155
|
+
serverUrl,
|
|
156
|
+
// Thread the configured scope into the authorization_code flow;
|
|
157
|
+
// omitted (undefined) when unset so existing behavior is unchanged.
|
|
158
|
+
scope: config.scope,
|
|
159
|
+
});
|
|
99
160
|
if (result === "AUTHORIZED") {
|
|
100
161
|
await clearOAuthState(serverName);
|
|
101
162
|
return { authorizationUrl: "" };
|
|
@@ -108,7 +169,7 @@ export async function startAuth(serverName, serverUrl, definition) {
|
|
|
108
169
|
}
|
|
109
170
|
catch (error) {
|
|
110
171
|
await clearOAuthState(serverName);
|
|
111
|
-
|
|
172
|
+
rethrowDcrFailure(error, serverName, config.clientId);
|
|
112
173
|
}
|
|
113
174
|
}
|
|
114
175
|
/**
|
|
@@ -138,10 +199,21 @@ export async function completeAuth(serverName, authorizationCode) {
|
|
|
138
199
|
* @returns The final auth status
|
|
139
200
|
*/
|
|
140
201
|
export async function authenticate(serverName, serverUrl, definition) {
|
|
202
|
+
// Canonical cross-entry-point in-flight guard: a non-blocking flow
|
|
203
|
+
// (startMcpAuthFlow) may already be in flight for this server. Starting
|
|
204
|
+
// startAuth again would overwrite the persisted OAuth state and the
|
|
205
|
+
// pending transport the in-flight browser callback completes against,
|
|
206
|
+
// breaking the first flow. Defer to it instead.
|
|
207
|
+
if (inFlightMcpAuths.has(serverName)) {
|
|
208
|
+
return "in_progress";
|
|
209
|
+
}
|
|
141
210
|
const inFlight = pendingAuthentications.get(serverName);
|
|
142
211
|
if (inFlight) {
|
|
143
212
|
return inFlight;
|
|
144
213
|
}
|
|
214
|
+
// Track this blocking flow in the same canonical guard so a concurrent
|
|
215
|
+
// non-blocking start (relay / agent tool) defers to us as well.
|
|
216
|
+
inFlightMcpAuths.add(serverName);
|
|
145
217
|
const operation = (async () => {
|
|
146
218
|
// Start auth flow
|
|
147
219
|
const { authorizationUrl, oauthState } = await startAuth(serverName, serverUrl, definition);
|
|
@@ -197,6 +269,7 @@ export async function authenticate(serverName, serverUrl, definition) {
|
|
|
197
269
|
if (pendingAuthentications.get(serverName) === operation) {
|
|
198
270
|
pendingAuthentications.delete(serverName);
|
|
199
271
|
}
|
|
272
|
+
inFlightMcpAuths.delete(serverName);
|
|
200
273
|
}
|
|
201
274
|
}
|
|
202
275
|
/**
|
|
@@ -204,9 +277,13 @@ export async function authenticate(serverName, serverUrl, definition) {
|
|
|
204
277
|
*
|
|
205
278
|
* @param serverName - The name of the MCP server
|
|
206
279
|
* @param serverUrl - The URL of the MCP server
|
|
280
|
+
* @param definition - The server definition (optional). When provided, the
|
|
281
|
+
* refresh provider is built from the real OAuth config so a
|
|
282
|
+
* pre-registered client (clientId/clientSecret/scope) is available
|
|
283
|
+
* during the refresh; without it, behavior is unchanged.
|
|
207
284
|
* @returns The valid tokens or null if not authenticated
|
|
208
285
|
*/
|
|
209
|
-
export async function getValidToken(serverName, serverUrl) {
|
|
286
|
+
export async function getValidToken(serverName, serverUrl, definition) {
|
|
210
287
|
// Check if we have valid tokens
|
|
211
288
|
const entry = await getAuthForUrl(serverName, serverUrl);
|
|
212
289
|
if (!entry?.tokens) {
|
|
@@ -221,8 +298,11 @@ export async function getValidToken(serverName, serverUrl) {
|
|
|
221
298
|
// Token is expired, try to refresh
|
|
222
299
|
console.log(`MCP Auth: Token expired for ${serverName}, attempting refresh`);
|
|
223
300
|
try {
|
|
224
|
-
// Create auth provider for token refresh
|
|
225
|
-
|
|
301
|
+
// Create auth provider for token refresh. Use the real server config
|
|
302
|
+
// so a pre-registered client (clientId/clientSecret/scope) is
|
|
303
|
+
// available; an undefined definition keeps the legacy empty config.
|
|
304
|
+
const refreshConfig = definition ? extractOAuthConfig(definition) : {};
|
|
305
|
+
const authProvider = new McpOAuthProvider(serverName, serverUrl, refreshConfig, {
|
|
226
306
|
onRedirect: async () => { },
|
|
227
307
|
});
|
|
228
308
|
const clientInfo = await authProvider.clientInformation();
|
|
@@ -0,0 +1,63 @@
|
|
|
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 { McpAuthStartError } from "./mcp-auth-flow.js";
|
|
13
|
+
import type { McpExtensionState } from "./state.js";
|
|
14
|
+
/**
|
|
15
|
+
* Validation/in-flight errors thrown by `startMcpAuthFlow`. Defined in
|
|
16
|
+
* mcp-auth-flow.js (its `startAuth` throws the same type for DCR failures)
|
|
17
|
+
* and re-exported here so existing imports keep working.
|
|
18
|
+
*/
|
|
19
|
+
export { McpAuthStartError };
|
|
20
|
+
export interface StartMcpAuthFlowOptions {
|
|
21
|
+
/** Best-effort: open the authorization URL in the system browser. */
|
|
22
|
+
openBrowser?: boolean;
|
|
23
|
+
}
|
|
24
|
+
export type StartMcpAuthFlowResult = {
|
|
25
|
+
status: "already-authenticated";
|
|
26
|
+
} | {
|
|
27
|
+
status: "auth_url_ready";
|
|
28
|
+
authUrl: string;
|
|
29
|
+
openedBrowser: boolean;
|
|
30
|
+
};
|
|
31
|
+
/**
|
|
32
|
+
* Per-server in-flight guard. `startAuth` keys its pending transports and
|
|
33
|
+
* persisted OAuth state by server name, so two concurrent flows for the same
|
|
34
|
+
* server would clobber each other. Reject the second start until the first
|
|
35
|
+
* flow's background chain finishes.
|
|
36
|
+
*/
|
|
37
|
+
export declare const inFlightMcpAuths: Set<string>;
|
|
38
|
+
/**
|
|
39
|
+
* An authorization URL may be opened in a real browser, so it must never be
|
|
40
|
+
* a `javascript:` / `data:` / malformed URL coming from a malicious MCP
|
|
41
|
+
* server's OAuth metadata. Defense in depth: the landing UI applies the
|
|
42
|
+
* same check before `window.open`.
|
|
43
|
+
*/
|
|
44
|
+
export declare function assertSafeAuthorizationUrl(authUrl: string): void;
|
|
45
|
+
/**
|
|
46
|
+
* Start a non-blocking OAuth flow for a configured MCP server.
|
|
47
|
+
*
|
|
48
|
+
* - Looks up the server definition (throws `McpAuthStartError` when missing,
|
|
49
|
+
* not OAuth-capable, or url-less).
|
|
50
|
+
* - Enforces the per-server in-flight guard.
|
|
51
|
+
* - Calls `startAuth`: an empty `authorizationUrl` means the server is
|
|
52
|
+
* already authenticated (or was via client_credentials).
|
|
53
|
+
* - Optionally opens the authorization URL in the system browser
|
|
54
|
+
* (best-effort — a failed `open` never fails the call).
|
|
55
|
+
* - Kicks off the background completion chain
|
|
56
|
+
* (waitForCallback → completeAuth → reloadMcpConfig) fire-and-forget.
|
|
57
|
+
*
|
|
58
|
+
* @param state - The live MCP extension state (config + reload target).
|
|
59
|
+
* @param name - Configured MCP server name.
|
|
60
|
+
* @param opts - `openBrowser` to launch the system browser on the auth URL.
|
|
61
|
+
*/
|
|
62
|
+
export declare function startMcpAuthFlow(state: McpExtensionState, name: string, opts?: StartMcpAuthFlowOptions): Promise<StartMcpAuthFlowResult>;
|
|
63
|
+
//# 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;AAGH,OAAO,EAIL,iBAAiB,EAClB,MAAM,oBAAoB,CAAC;AAI5B,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAEpD;;;;GAIG;AACH,OAAO,EAAE,iBAAiB,EAAE,CAAC;AAE7B,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,131 @@
|
|
|
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, McpAuthStartError, } 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`. Defined in
|
|
19
|
+
* mcp-auth-flow.js (its `startAuth` throws the same type for DCR failures)
|
|
20
|
+
* and re-exported here so existing imports keep working.
|
|
21
|
+
*/
|
|
22
|
+
export { McpAuthStartError };
|
|
23
|
+
/**
|
|
24
|
+
* Per-server in-flight guard. `startAuth` keys its pending transports and
|
|
25
|
+
* persisted OAuth state by server name, so two concurrent flows for the same
|
|
26
|
+
* server would clobber each other. Reject the second start until the first
|
|
27
|
+
* flow's background chain finishes.
|
|
28
|
+
*/
|
|
29
|
+
export const inFlightMcpAuths = new Set();
|
|
30
|
+
/**
|
|
31
|
+
* An authorization URL may be opened in a real browser, so it must never be
|
|
32
|
+
* a `javascript:` / `data:` / malformed URL coming from a malicious MCP
|
|
33
|
+
* server's OAuth metadata. Defense in depth: the landing UI applies the
|
|
34
|
+
* same check before `window.open`.
|
|
35
|
+
*/
|
|
36
|
+
export function assertSafeAuthorizationUrl(authUrl) {
|
|
37
|
+
let parsed;
|
|
38
|
+
try {
|
|
39
|
+
parsed = new URL(authUrl);
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
throw new McpAuthStartError("Invalid authorization URL");
|
|
43
|
+
}
|
|
44
|
+
if (parsed.protocol !== "https:" && parsed.protocol !== "http:") {
|
|
45
|
+
throw new McpAuthStartError("Invalid authorization URL");
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Start a non-blocking OAuth flow for a configured MCP server.
|
|
50
|
+
*
|
|
51
|
+
* - Looks up the server definition (throws `McpAuthStartError` when missing,
|
|
52
|
+
* not OAuth-capable, or url-less).
|
|
53
|
+
* - Enforces the per-server in-flight guard.
|
|
54
|
+
* - Calls `startAuth`: an empty `authorizationUrl` means the server is
|
|
55
|
+
* already authenticated (or was via client_credentials).
|
|
56
|
+
* - Optionally opens the authorization URL in the system browser
|
|
57
|
+
* (best-effort — a failed `open` never fails the call).
|
|
58
|
+
* - Kicks off the background completion chain
|
|
59
|
+
* (waitForCallback → completeAuth → reloadMcpConfig) fire-and-forget.
|
|
60
|
+
*
|
|
61
|
+
* @param state - The live MCP extension state (config + reload target).
|
|
62
|
+
* @param name - Configured MCP server name.
|
|
63
|
+
* @param opts - `openBrowser` to launch the system browser on the auth URL.
|
|
64
|
+
*/
|
|
65
|
+
export async function startMcpAuthFlow(state, name, opts = {}) {
|
|
66
|
+
const definition = state.config.mcpServers[name];
|
|
67
|
+
if (!definition) {
|
|
68
|
+
throw new McpAuthStartError(`Unknown MCP server: ${name}`);
|
|
69
|
+
}
|
|
70
|
+
if (!definition.url || !supportsOAuth(definition)) {
|
|
71
|
+
throw new McpAuthStartError(`MCP server "${name}" does not support OAuth`);
|
|
72
|
+
}
|
|
73
|
+
if (inFlightMcpAuths.has(name)) {
|
|
74
|
+
throw new McpAuthStartError("OAuth authentication already in progress for this server");
|
|
75
|
+
}
|
|
76
|
+
inFlightMcpAuths.add(name);
|
|
77
|
+
let authorizationUrl;
|
|
78
|
+
let oauthState;
|
|
79
|
+
try {
|
|
80
|
+
({ authorizationUrl, oauthState } = await startAuth(name, definition.url, definition));
|
|
81
|
+
if (authorizationUrl === "") {
|
|
82
|
+
inFlightMcpAuths.delete(name);
|
|
83
|
+
return { status: "already-authenticated" };
|
|
84
|
+
}
|
|
85
|
+
// Fail loudly instead of skipping background completion — a flow without
|
|
86
|
+
// its persisted state could never be completed nor cleanly aborted.
|
|
87
|
+
if (!oauthState) {
|
|
88
|
+
throw new McpAuthStartError("OAuth state missing after authorization start");
|
|
89
|
+
}
|
|
90
|
+
assertSafeAuthorizationUrl(authorizationUrl);
|
|
91
|
+
}
|
|
92
|
+
catch (error) {
|
|
93
|
+
// Release the guard so a failed start does not wedge the server.
|
|
94
|
+
inFlightMcpAuths.delete(name);
|
|
95
|
+
throw error;
|
|
96
|
+
}
|
|
97
|
+
if (opts.openBrowser) {
|
|
98
|
+
// Best-effort: a failed `open` must not fail the call — the user can
|
|
99
|
+
// always open authUrl manually.
|
|
100
|
+
open(authorizationUrl).catch((error) => {
|
|
101
|
+
console.warn(`MCP Auth: failed to open browser for ${name}`, error);
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
// Complete the flow in the background from the local callback server:
|
|
105
|
+
// waitForCallback resolves with the authorization code, completeAuth
|
|
106
|
+
// exchanges it for tokens, and the MCP config is reloaded so the server
|
|
107
|
+
// comes up authenticated. Fire-and-forget — auth progress is observable
|
|
108
|
+
// via server status, so failures are logged, not surfaced. The finally
|
|
109
|
+
// block releases the per-server in-flight guard and clears the
|
|
110
|
+
// now-stale OAuth state (idempotent) on every path.
|
|
111
|
+
waitForCallback(oauthState)
|
|
112
|
+
.then((code) => completeAuth(name, code))
|
|
113
|
+
.then(() => reloadMcpConfig(state))
|
|
114
|
+
.catch((error) => {
|
|
115
|
+
console.warn(`MCP Auth: background completion failed for ${name}`, error);
|
|
116
|
+
})
|
|
117
|
+
.finally(() => {
|
|
118
|
+
inFlightMcpAuths.delete(name);
|
|
119
|
+
try {
|
|
120
|
+
clearOAuthState(name);
|
|
121
|
+
}
|
|
122
|
+
catch {
|
|
123
|
+
// Storage errors must not surface as unhandled rejections.
|
|
124
|
+
}
|
|
125
|
+
});
|
|
126
|
+
return {
|
|
127
|
+
status: "auth_url_ready",
|
|
128
|
+
authUrl: authorizationUrl,
|
|
129
|
+
openedBrowser: !!opts.openBrowser,
|
|
130
|
+
};
|
|
131
|
+
}
|
|
@@ -8,6 +8,15 @@ import type { OAuthClientProvider } from "@modelcontextprotocol/sdk/client/auth.
|
|
|
8
8
|
import type { OAuthClientMetadata, OAuthTokens, OAuthClientInformation, OAuthClientInformationFull } from "@modelcontextprotocol/sdk/shared/auth.js";
|
|
9
9
|
declare const DEFAULT_OAUTH_CALLBACK_PORT = 19876;
|
|
10
10
|
declare const OAUTH_CALLBACK_PATH = "/callback";
|
|
11
|
+
/**
|
|
12
|
+
* Default hosted Client ID Metadata Document (SEP-991 / CIMD), served by
|
|
13
|
+
* aexol.ai (landing/public/mcp-client-metadata.json). Used as the client_id
|
|
14
|
+
* when the AS supports client_id_metadata_document_supported, no DCR, and no
|
|
15
|
+
* explicit clientId / clientMetadataUrl is configured (e.g. Bubble).
|
|
16
|
+
* The callback port must stay 19876 — it is fixed in the document's
|
|
17
|
+
* redirect_uris (changing MCP_OAUTH_CALLBACK_PORT requires a custom document).
|
|
18
|
+
*/
|
|
19
|
+
export declare const DEFAULT_CIMD_METADATA_URL = "https://aexol.ai/mcp-client-metadata.json";
|
|
11
20
|
export declare function getConfiguredOAuthCallbackPort(): number;
|
|
12
21
|
export declare function getOAuthCallbackPort(): number;
|
|
13
22
|
export declare function setOAuthCallbackPort(port: number): void;
|
|
@@ -17,6 +26,12 @@ export interface McpOAuthConfig {
|
|
|
17
26
|
clientId?: string;
|
|
18
27
|
clientSecret?: string;
|
|
19
28
|
scope?: string;
|
|
29
|
+
/**
|
|
30
|
+
* HTTPS URL of a hosted Client ID Metadata Document (SEP-991 / CIMD).
|
|
31
|
+
* Used when the AS does not support DCR and no pre-registered clientId
|
|
32
|
+
* is configured; the SDK uses this URL as the client_id.
|
|
33
|
+
*/
|
|
34
|
+
clientMetadataUrl?: string;
|
|
20
35
|
redirectUri?: string;
|
|
21
36
|
}
|
|
22
37
|
/** Callbacks for OAuth flow interactions */
|
|
@@ -39,6 +54,15 @@ export declare class McpOAuthProvider implements OAuthClientProvider {
|
|
|
39
54
|
* Uses configured redirectUri if provided, otherwise falls back to default.
|
|
40
55
|
*/
|
|
41
56
|
get redirectUrl(): string | undefined;
|
|
57
|
+
/**
|
|
58
|
+
* External URL of the hosted Client ID Metadata Document (SEP-991).
|
|
59
|
+
* The MCP SDK uses this as the client_id when the AS signals
|
|
60
|
+
* `client_id_metadata_document_supported` and no pre-registered
|
|
61
|
+
* clientId is available (clientInformation() wins over this field).
|
|
62
|
+
* Falls back to the default aexol.ai document so CIMD-capable ASes
|
|
63
|
+
* (e.g. Bubble) work out-of-the-box.
|
|
64
|
+
*/
|
|
65
|
+
get clientMetadataUrl(): string | undefined;
|
|
42
66
|
/**
|
|
43
67
|
* Client metadata for dynamic registration.
|
|
44
68
|
* Describes this client to the OAuth authorization server.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mcp-oauth-provider.d.ts","sourceRoot":"","sources":["../../src/mcp/mcp-oauth-provider.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,0CAA0C,CAAA;AAEnF,OAAO,KAAK,EACV,mBAAmB,EACnB,WAAW,EACX,sBAAsB,EACtB,0BAA0B,EAC3B,MAAM,0CAA0C,CAAA;AAgBjD,QAAA,MAAM,2BAA2B,QAAQ,CAAA;AACzC,QAAA,MAAM,mBAAmB,cAAc,CAAA;
|
|
1
|
+
{"version":3,"file":"mcp-oauth-provider.d.ts","sourceRoot":"","sources":["../../src/mcp/mcp-oauth-provider.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,0CAA0C,CAAA;AAEnF,OAAO,KAAK,EACV,mBAAmB,EACnB,WAAW,EACX,sBAAsB,EACtB,0BAA0B,EAC3B,MAAM,0CAA0C,CAAA;AAgBjD,QAAA,MAAM,2BAA2B,QAAQ,CAAA;AACzC,QAAA,MAAM,mBAAmB,cAAc,CAAA;AAEvC;;;;;;;GAOG;AACH,eAAO,MAAM,yBAAyB,8CAA8C,CAAA;AAapF,wBAAgB,8BAA8B,IAAI,MAAM,CAEvD;AAED,wBAAgB,oBAAoB,IAAI,MAAM,CAE7C;AAED,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAEvD;AAED,sCAAsC;AACtC,MAAM,WAAW,cAAc;IAC7B,SAAS,CAAC,EAAE,oBAAoB,GAAG,oBAAoB,CAAA;IACvD,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd;;;;OAIG;IACH,iBAAiB,CAAC,EAAE,MAAM,CAAA;IAC1B,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB;AAED,4CAA4C;AAC5C,MAAM,WAAW,iBAAiB;IAChC,UAAU,EAAE,CAAC,GAAG,EAAE,GAAG,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;CAC/C;AAED;;;GAGG;AACH,qBAAa,gBAAiB,YAAW,mBAAmB;IAExD,OAAO,CAAC,UAAU;IAClB,OAAO,CAAC,SAAS;IACjB,OAAO,CAAC,MAAM;IACd,OAAO,CAAC,SAAS;gBAHT,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,cAAc,EACtB,SAAS,EAAE,iBAAiB;IAGtC,OAAO,KAAK,qBAAqB,GAEhC;IAED;;;OAGG;IACH,IAAI,WAAW,IAAI,MAAM,GAAG,SAAS,CAIpC;IAED;;;;;;;OAOG;IACH,IAAI,iBAAiB,IAAI,MAAM,GAAG,SAAS,CAE1C;IAED;;;OAGG;IACH,IAAI,cAAc,IAAI,mBAAmB,CAuBxC;IAED;;;OAGG;IACG,iBAAiB,IAAI,OAAO,CAAC,sBAAsB,GAAG,SAAS,CAAC;IA2BtE;;OAEG;IACG,qBAAqB,CAAC,IAAI,EAAE,0BAA0B,GAAG,OAAO,CAAC,IAAI,CAAC;IAU5E;;;OAGG;IACG,MAAM,IAAI,OAAO,CAAC,WAAW,GAAG,SAAS,CAAC;IAgBhD;;OAEG;IACG,UAAU,CAAC,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IAUpD;;;;;;;;OAQG;IACG,uBAAuB,CAAC,gBAAgB,EAAE,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC;IAenE;;OAEG;IACG,gBAAgB,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAI3D;;;OAGG;IACG,YAAY,IAAI,OAAO,CAAC,MAAM,CAAC;IAWrC;;OAEG;IACG,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAI7C;;;OAGG;IACG,KAAK,IAAI,OAAO,CAAC,MAAM,CAAC;IAa9B;;;OAGG;IACG,qBAAqB,CAAC,IAAI,EAAE,KAAK,GAAG,QAAQ,GAAG,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC;IAc7E,mBAAmB,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,eAAe,GAAG,SAAS;CAYjE;AAED,OAAO,EAAE,2BAA2B,EAAE,mBAAmB,EAAE,CAAA"}
|
|
@@ -9,6 +9,15 @@ import { getAuthEntry, getAuthForUrl, updateTokens, updateClientInfo, updateCode
|
|
|
9
9
|
// Callback server configuration
|
|
10
10
|
const DEFAULT_OAUTH_CALLBACK_PORT = 19876;
|
|
11
11
|
const OAUTH_CALLBACK_PATH = "/callback";
|
|
12
|
+
/**
|
|
13
|
+
* Default hosted Client ID Metadata Document (SEP-991 / CIMD), served by
|
|
14
|
+
* aexol.ai (landing/public/mcp-client-metadata.json). Used as the client_id
|
|
15
|
+
* when the AS supports client_id_metadata_document_supported, no DCR, and no
|
|
16
|
+
* explicit clientId / clientMetadataUrl is configured (e.g. Bubble).
|
|
17
|
+
* The callback port must stay 19876 — it is fixed in the document's
|
|
18
|
+
* redirect_uris (changing MCP_OAUTH_CALLBACK_PORT requires a custom document).
|
|
19
|
+
*/
|
|
20
|
+
export const DEFAULT_CIMD_METADATA_URL = "https://aexol.ai/mcp-client-metadata.json";
|
|
12
21
|
let configuredOAuthCallbackPort = DEFAULT_OAUTH_CALLBACK_PORT;
|
|
13
22
|
if (process.env.MCP_OAUTH_CALLBACK_PORT) {
|
|
14
23
|
const parsedPort = Number.parseInt(process.env.MCP_OAUTH_CALLBACK_PORT, 10);
|
|
@@ -55,6 +64,17 @@ export class McpOAuthProvider {
|
|
|
55
64
|
return this.config.redirectUri;
|
|
56
65
|
return `http://localhost:${getOAuthCallbackPort()}${OAUTH_CALLBACK_PATH}`;
|
|
57
66
|
}
|
|
67
|
+
/**
|
|
68
|
+
* External URL of the hosted Client ID Metadata Document (SEP-991).
|
|
69
|
+
* The MCP SDK uses this as the client_id when the AS signals
|
|
70
|
+
* `client_id_metadata_document_supported` and no pre-registered
|
|
71
|
+
* clientId is available (clientInformation() wins over this field).
|
|
72
|
+
* Falls back to the default aexol.ai document so CIMD-capable ASes
|
|
73
|
+
* (e.g. Bubble) work out-of-the-box.
|
|
74
|
+
*/
|
|
75
|
+
get clientMetadataUrl() {
|
|
76
|
+
return this.config.clientMetadataUrl ?? DEFAULT_CIMD_METADATA_URL;
|
|
77
|
+
}
|
|
58
78
|
/**
|
|
59
79
|
* Client metadata for dynamic registration.
|
|
60
80
|
* Describes this client to the OAuth authorization server.
|
|
@@ -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"}
|