@codingame/monaco-vscode-mcp-service-override 37.1.0 → 37.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/index.js CHANGED
@@ -29,6 +29,7 @@ import { IWorkbenchMcpGatewayService } from '@codingame/monaco-vscode-api/vscode
29
29
  import { BrowserMcpGatewayService } from './vscode/src/vs/workbench/contrib/mcp/browser/mcpGatewayService.js';
30
30
  import './vscode/src/vs/workbench/contrib/mcp/browser/mcp.contribution.js';
31
31
  import './vscode/src/vs/workbench/contrib/mcp/browser/mcp.view.contribution.js';
32
+ import './vscode/src/vs/workbench/api/browser/mainThreadMcp.js';
32
33
 
33
34
  function getServiceOverride() {
34
35
  return {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codingame/monaco-vscode-mcp-service-override",
3
- "version": "37.1.0",
3
+ "version": "37.2.0",
4
4
  "private": false,
5
5
  "description": "VSCode public API plugged on the monaco editor - mcp service-override",
6
6
  "keywords": [],
@@ -15,7 +15,7 @@
15
15
  },
16
16
  "type": "module",
17
17
  "dependencies": {
18
- "@codingame/monaco-vscode-api": "37.1.0"
18
+ "@codingame/monaco-vscode-api": "37.2.0"
19
19
  },
20
20
  "main": "index.js",
21
21
  "module": "index.js",
@@ -0,0 +1,108 @@
1
+ import { Disposable } from "@codingame/monaco-vscode-api/vscode/vs/base/common/lifecycle";
2
+ import { URI, UriComponents } from "@codingame/monaco-vscode-api/vscode/vs/base/common/uri";
3
+ import { IContextKeyService } from "@codingame/monaco-vscode-api/vscode/vs/platform/contextkey/common/contextkey.service";
4
+ import { IConfigurationService } from "@codingame/monaco-vscode-api/vscode/vs/platform/configuration/common/configuration.service";
5
+ import { IDialogService } from "@codingame/monaco-vscode-api/vscode/vs/platform/dialogs/common/dialogs.service";
6
+ import { LogLevel } from "@codingame/monaco-vscode-api/vscode/vs/platform/log/common/log";
7
+ import { ITelemetryService } from "@codingame/monaco-vscode-api/vscode/vs/platform/telemetry/common/telemetry.service";
8
+ import { ISecretStorageService } from "@codingame/monaco-vscode-api/vscode/vs/platform/secrets/common/secrets.service";
9
+ import { IWorkbenchMcpGatewayService } from "@codingame/monaco-vscode-api/vscode/vs/workbench/contrib/mcp/common/mcpGatewayService.service";
10
+ import { IMcpRegistry } from "@codingame/monaco-vscode-api/vscode/vs/workbench/contrib/mcp/common/mcpRegistryTypes.service";
11
+ import { McpCollectionDefinition, McpConnectionState, McpServerDefinition } from "@codingame/monaco-vscode-api/vscode/vs/workbench/contrib/mcp/common/mcpTypes";
12
+ import { IAuthenticationMcpAccessService } from "@codingame/monaco-vscode-api/vscode/vs/workbench/services/authentication/browser/authenticationMcpAccessService.service";
13
+ import { IAuthenticationMcpService } from "@codingame/monaco-vscode-api/vscode/vs/workbench/services/authentication/browser/authenticationMcpService.service";
14
+ import { IAuthenticationMcpUsageService } from "@codingame/monaco-vscode-api/vscode/vs/workbench/services/authentication/browser/authenticationMcpUsageService.service";
15
+ import { IAuthenticationService } from "@codingame/monaco-vscode-api/vscode/vs/workbench/services/authentication/common/authentication.service";
16
+ import { IDynamicAuthenticationProviderStorageService } from "@codingame/monaco-vscode-api/vscode/vs/workbench/services/authentication/common/dynamicAuthenticationProviderStorage.service";
17
+ import { IExtensionService } from "@codingame/monaco-vscode-api/vscode/vs/workbench/services/extensions/common/extensions.service";
18
+ import { IExtHostContext } from "@codingame/monaco-vscode-api/vscode/vs/workbench/services/extensions/common/extHostCustomers";
19
+ import { IMcpAuthenticationDetails, IMcpAuthenticationOptions, IAuthMetadataSource, MainThreadMcpShape } from "@codingame/monaco-vscode-api/vscode/vs/workbench/api/common/extHost.protocol";
20
+ export declare class MainThreadMcp extends Disposable implements MainThreadMcpShape {
21
+ private readonly _extHostContext;
22
+ private readonly _mcpRegistry;
23
+ private readonly dialogService;
24
+ private readonly _authenticationService;
25
+ private readonly authenticationMcpServersService;
26
+ private readonly authenticationMCPServerAccessService;
27
+ private readonly authenticationMCPServerUsageService;
28
+ private readonly _dynamicAuthenticationProviderStorageService;
29
+ private readonly _extensionService;
30
+ private readonly _contextKeyService;
31
+ private readonly _telemetryService;
32
+ private readonly _mcpGatewayService;
33
+ private readonly _configurationService;
34
+ private readonly _secretStorageService;
35
+ private _serverIdCounter;
36
+ private readonly _servers;
37
+ private readonly _serverDefinitions;
38
+ private readonly _serverAuthTracking;
39
+ private readonly _proxy;
40
+ private readonly _collectionDefinitions;
41
+ private readonly _gateways;
42
+ constructor(_extHostContext: IExtHostContext, _mcpRegistry: IMcpRegistry, dialogService: IDialogService, _authenticationService: IAuthenticationService, authenticationMcpServersService: IAuthenticationMcpService, authenticationMCPServerAccessService: IAuthenticationMcpAccessService, authenticationMCPServerUsageService: IAuthenticationMcpUsageService, _dynamicAuthenticationProviderStorageService: IDynamicAuthenticationProviderStorageService, _extensionService: IExtensionService, _contextKeyService: IContextKeyService, _telemetryService: ITelemetryService, _mcpGatewayService: IWorkbenchMcpGatewayService, _configurationService: IConfigurationService, _secretStorageService: ISecretStorageService);
43
+ private _publishServerDefinitions;
44
+ $upsertMcpCollection(collection: McpCollectionDefinition.FromExtHost, serversDto: McpServerDefinition.Serialized[]): void;
45
+ $deleteMcpCollection(collectionId: string): void;
46
+ $onDidChangeState(id: number, update: McpConnectionState): void;
47
+ $onDidPublishLog(id: number, level: LogLevel, log: string): void;
48
+ $onDidReceiveMessage(id: number, message: string): void;
49
+ $getTokenForProviderId(id: number, providerId: string, scopes: string[], options?: IMcpAuthenticationOptions): Promise<string | undefined>;
50
+ $getTokenFromServerMetadata(id: number, authDetails: IMcpAuthenticationDetails, { errorOnUserInteraction, forceNewRegistration, clientId }?: IMcpAuthenticationOptions): Promise<string | undefined>;
51
+ private _ensureXaaIssuer;
52
+ private _getSessionForProvider;
53
+ private continueWithIncorrectAccountPrompt;
54
+ private _onDidChangeAuthSessions;
55
+ $logMcpAuthSetup(data: IAuthMetadataSource): void;
56
+ $startMcpGateway(chatSessionResource?: UriComponents): Promise<{
57
+ servers: {
58
+ label: string;
59
+ address: URI;
60
+ }[];
61
+ gatewayId: string;
62
+ } | undefined>;
63
+ $disposeMcpGateway(gatewayId: string): void;
64
+ private loginPrompt;
65
+ dispose(): void;
66
+ }
67
+ /**
68
+ * The context needed to re-acquire a token for a tracked MCP server, captured when the
69
+ * session was first established. The tracker holds this opaquely and replays it verbatim on
70
+ * re-validation so the silent token request targets the same authority / resource / audience
71
+ * that the original sign-in used. Dropping the authorization server here would let the provider
72
+ * fall back to a default authority (e.g. the Microsoft provider's `organizations` tenant) and
73
+ * request a token against the wrong tenant.
74
+ */
75
+ export interface IMcpServerAuthContext {
76
+ readonly authorizationServer?: URI;
77
+ readonly clientId?: string;
78
+ readonly resource?: string;
79
+ readonly audience?: string;
80
+ }
81
+ /**
82
+ * Tracks which MCP servers are using which authentication providers.
83
+ * Organized by provider ID for efficient lookup when auth sessions change.
84
+ */
85
+ export declare class McpServerAuthTracker {
86
+ private readonly _tracking;
87
+ /**
88
+ * Track authentication for a server with a specific provider.
89
+ * Replaces any existing tracking for this server/provider combination.
90
+ */
91
+ track(providerId: string, serverId: number, scopes: string[], context: IMcpServerAuthContext): void;
92
+ /**
93
+ * Remove all authentication tracking for a server across all providers.
94
+ */
95
+ untrack(serverId: number): void;
96
+ /**
97
+ * Get all servers using a specific authentication provider.
98
+ */
99
+ get(providerId: string): ReadonlyArray<{
100
+ serverId: number;
101
+ scopes: string[];
102
+ context: IMcpServerAuthContext;
103
+ }> | undefined;
104
+ /**
105
+ * Clear all tracking data.
106
+ */
107
+ clear(): void;
108
+ }
@@ -0,0 +1,698 @@
1
+
2
+ import { __decorate, __param } from '@codingame/monaco-vscode-api/external/tslib/tslib.es6';
3
+ import { mapFindFirst } from '@codingame/monaco-vscode-api/vscode/vs/base/common/arraysFind';
4
+ import { RunOnceScheduler, disposableTimeout } from '@codingame/monaco-vscode-api/vscode/vs/base/common/async';
5
+ import { CancellationError } from '@codingame/monaco-vscode-api/vscode/vs/base/common/errors';
6
+ import { Emitter } from '@codingame/monaco-vscode-api/vscode/vs/base/common/event';
7
+ import { Disposable, DisposableMap, DisposableStore, MutableDisposable } from '@codingame/monaco-vscode-api/vscode/vs/base/common/lifecycle';
8
+ import '@codingame/monaco-vscode-api/vscode/vs/base/common/observableInternal/index';
9
+ import Severity from '@codingame/monaco-vscode-api/vscode/vs/base/common/severity';
10
+ import { URI } from '@codingame/monaco-vscode-api/vscode/vs/base/common/uri';
11
+ import { generateUuid } from '@codingame/monaco-vscode-api/vscode/vs/base/common/uuid';
12
+ import { localize } from '@codingame/monaco-vscode-api/vscode/vs/nls';
13
+ import { ContextKeyExpr } from '@codingame/monaco-vscode-api/vscode/vs/platform/contextkey/common/contextkey';
14
+ import { IContextKeyService } from '@codingame/monaco-vscode-api/vscode/vs/platform/contextkey/common/contextkey.service';
15
+ import { IConfigurationService } from '@codingame/monaco-vscode-api/vscode/vs/platform/configuration/common/configuration.service';
16
+ import { IDialogService } from '@codingame/monaco-vscode-api/vscode/vs/platform/dialogs/common/dialogs.service';
17
+ import { ExtensionIdentifier } from '@codingame/monaco-vscode-api/vscode/vs/platform/extensions/common/extensions';
18
+ import { LogLevel } from '@codingame/monaco-vscode-api/vscode/vs/platform/log/common/log';
19
+ import { ITelemetryService } from '@codingame/monaco-vscode-api/vscode/vs/platform/telemetry/common/telemetry.service';
20
+ import { ISecretStorageService } from '@codingame/monaco-vscode-api/vscode/vs/platform/secrets/common/secrets.service';
21
+ import { IWorkbenchMcpGatewayService } from '@codingame/monaco-vscode-api/vscode/vs/workbench/contrib/mcp/common/mcpGatewayService.service';
22
+ import { IMcpRegistry } from '@codingame/monaco-vscode-api/vscode/vs/workbench/contrib/mcp/common/mcpRegistryTypes.service';
23
+ import { McpServerLaunch, McpServerTransportType, McpServerDefinition, McpServerTrust, McpCollectionSortOrder, McpCollectionProvenance, extensionPrefixedIdentifier, McpConnectionState, mcpOAuthClientSecretStorageKey, UserInteractionRequiredError } from '@codingame/monaco-vscode-api/vscode/vs/workbench/contrib/mcp/common/mcpTypes';
24
+ import { mcpEnterpriseManagedAuthIdpSection } from '@codingame/monaco-vscode-api/vscode/vs/workbench/contrib/mcp/common/mcpConfiguration';
25
+ import { IAuthenticationMcpAccessService } from '@codingame/monaco-vscode-api/vscode/vs/workbench/services/authentication/browser/authenticationMcpAccessService.service';
26
+ import { IAuthenticationMcpService } from '@codingame/monaco-vscode-api/vscode/vs/workbench/services/authentication/browser/authenticationMcpService.service';
27
+ import { IAuthenticationMcpUsageService } from '@codingame/monaco-vscode-api/vscode/vs/workbench/services/authentication/browser/authenticationMcpUsageService.service';
28
+ import { IAuthenticationService } from '@codingame/monaco-vscode-api/vscode/vs/workbench/services/authentication/common/authentication.service';
29
+ import { IDynamicAuthenticationProviderStorageService } from '@codingame/monaco-vscode-api/vscode/vs/workbench/services/authentication/common/dynamicAuthenticationProviderStorage.service';
30
+ import { ExtensionHostKind, extensionHostKindToString } from '@codingame/monaco-vscode-api/vscode/vs/workbench/services/extensions/common/extensionHostKind';
31
+ import { IExtensionService } from '@codingame/monaco-vscode-api/vscode/vs/workbench/services/extensions/common/extensions.service';
32
+ import { extHostNamedCustomer } from '@codingame/monaco-vscode-api/vscode/vs/workbench/services/extensions/common/extHostCustomers';
33
+ import { ExtHostContext, MainContext } from '@codingame/monaco-vscode-api/vscode/vs/workbench/api/common/extHost.protocol';
34
+ import { autorun } from '@codingame/monaco-vscode-api/vscode/vs/base/common/observableInternal/reactions/autorun';
35
+ import { observableValue } from '@codingame/monaco-vscode-api/vscode/vs/base/common/observableInternal/observables/observableValue';
36
+
37
+ let MainThreadMcp = class MainThreadMcp extends Disposable {
38
+ constructor(
39
+ _extHostContext,
40
+ _mcpRegistry,
41
+ dialogService,
42
+ _authenticationService,
43
+ authenticationMcpServersService,
44
+ authenticationMCPServerAccessService,
45
+ authenticationMCPServerUsageService,
46
+ _dynamicAuthenticationProviderStorageService,
47
+ _extensionService,
48
+ _contextKeyService,
49
+ _telemetryService,
50
+ _mcpGatewayService,
51
+ _configurationService,
52
+ _secretStorageService
53
+ ) {
54
+ super();
55
+ this._extHostContext = _extHostContext;
56
+ this._mcpRegistry = _mcpRegistry;
57
+ this.dialogService = dialogService;
58
+ this._authenticationService = _authenticationService;
59
+ this.authenticationMcpServersService = authenticationMcpServersService;
60
+ this.authenticationMCPServerAccessService = authenticationMCPServerAccessService;
61
+ this.authenticationMCPServerUsageService = authenticationMCPServerUsageService;
62
+ this._dynamicAuthenticationProviderStorageService = _dynamicAuthenticationProviderStorageService;
63
+ this._extensionService = _extensionService;
64
+ this._contextKeyService = _contextKeyService;
65
+ this._telemetryService = _telemetryService;
66
+ this._mcpGatewayService = _mcpGatewayService;
67
+ this._configurationService = _configurationService;
68
+ this._secretStorageService = _secretStorageService;
69
+ this._serverIdCounter = 0;
70
+ this._servers = ( new Map());
71
+ this._serverDefinitions = ( new Map());
72
+ this._serverAuthTracking = ( new McpServerAuthTracker());
73
+ this._collectionDefinitions = this._register(( new DisposableMap()));
74
+ this._gateways = this._register(( new DisposableMap()));
75
+ this._register(
76
+ _authenticationService.onDidChangeSessions(e => this._onDidChangeAuthSessions(e.providerId, e.label))
77
+ );
78
+ const proxy = this._proxy = ( _extHostContext.getProxy(ExtHostContext.ExtHostMcp));
79
+ this._register(this._mcpRegistry.registerDelegate({
80
+ priority: _extHostContext.extensionHostKind === ExtensionHostKind.LocalWebWorker ? 0 : 1,
81
+ waitForInitialProviderPromises() {
82
+ return proxy.$waitForInitialCollectionProviders();
83
+ },
84
+ canStart(collection, serverDefinition) {
85
+ if (collection.remoteAuthority !== _extHostContext.remoteAuthority) {
86
+ return false;
87
+ }
88
+ if (serverDefinition.launch.type === McpServerTransportType.Stdio && _extHostContext.extensionHostKind === ExtensionHostKind.LocalWebWorker) {
89
+ return false;
90
+ }
91
+ return true;
92
+ },
93
+ async substituteVariables(serverDefinition, launch) {
94
+ const ser = await proxy.$substituteVariables(
95
+ serverDefinition.variableReplacement?.folder?.uri,
96
+ McpServerLaunch.toSerialized(launch)
97
+ );
98
+ return McpServerLaunch.fromSerialized(ser);
99
+ },
100
+ start: (_collection, serverDefiniton, resolveLaunch, options) => {
101
+ const id = ++this._serverIdCounter;
102
+ const launch = ( new ExtHostMcpServerLaunch(
103
+ _extHostContext.extensionHostKind,
104
+ () => proxy.$stopMcp(id),
105
+ msg => proxy.$sendMessage(id, JSON.stringify(msg))
106
+ ));
107
+ this._servers.set(id, launch);
108
+ this._serverDefinitions.set(id, serverDefiniton);
109
+ proxy.$startMcp(id, {
110
+ launch: resolveLaunch,
111
+ defaultCwd: serverDefiniton.defaultCwd ?? serverDefiniton.variableReplacement?.folder?.uri,
112
+ errorOnUserInteraction: options?.errorOnUserInteraction
113
+ });
114
+ return launch;
115
+ }
116
+ }));
117
+ const onDidChangeMcpServerDefinitionsTrigger = this._register(( new RunOnceScheduler(() => this._publishServerDefinitions(), 500)));
118
+ this._register(autorun(reader => {
119
+ const collections = this._mcpRegistry.collections.read(reader);
120
+ for (const collection of collections) {
121
+ collection.serverDefinitions.read(reader);
122
+ }
123
+ if (!onDidChangeMcpServerDefinitionsTrigger.isScheduled()) {
124
+ onDidChangeMcpServerDefinitionsTrigger.schedule();
125
+ }
126
+ }));
127
+ onDidChangeMcpServerDefinitionsTrigger.schedule();
128
+ }
129
+ _publishServerDefinitions() {
130
+ const collections = this._mcpRegistry.collections.get();
131
+ const allServers = [];
132
+ for (const collection of collections) {
133
+ const servers = collection.serverDefinitions.get();
134
+ for (const server of servers) {
135
+ allServers.push(McpServerDefinition.toSerialized(server));
136
+ }
137
+ }
138
+ this._proxy.$onDidChangeMcpServerDefinitions(allServers);
139
+ }
140
+ $upsertMcpCollection(collection, serversDto) {
141
+ const servers = ( serversDto.map(McpServerDefinition.fromSerialized));
142
+ const existing = this._collectionDefinitions.get(collection.id);
143
+ if (existing) {
144
+ existing.servers.set(servers, undefined);
145
+ } else {
146
+ const serverDefinitions = observableValue("mcpServers", servers);
147
+ const extensionId = ( new ExtensionIdentifier(collection.extensionId));
148
+ const store = ( new DisposableStore());
149
+ const handle = store.add(( new MutableDisposable()));
150
+ const register = () => {
151
+ handle.value ??= this._mcpRegistry.registerCollection({
152
+ ...collection,
153
+ provenance: McpCollectionProvenance.Extension,
154
+ source: extensionId,
155
+ order: McpCollectionSortOrder.Extension,
156
+ resolveServerLanch: collection.canResolveLaunch ? (async def => {
157
+ const r = await this._proxy.$resolveMcpLaunch(collection.id, def.label);
158
+ return r ? McpServerLaunch.fromSerialized(r) : undefined;
159
+ }) : undefined,
160
+ trustBehavior: collection.isTrustedByDefault ? McpServerTrust.Kind.Trusted : McpServerTrust.Kind.TrustedOnNonce,
161
+ remoteAuthority: this._extHostContext.remoteAuthority,
162
+ serverDefinitions
163
+ });
164
+ };
165
+ const whenClauseStr = mapFindFirst(
166
+ this._extensionService.extensions,
167
+ e => ExtensionIdentifier.equals(extensionId, e.identifier) ? e.contributes?.mcpServerDefinitionProviders?.find(p => extensionPrefixedIdentifier(extensionId, p.id) === collection.id)?.when : undefined
168
+ );
169
+ const whenClause = whenClauseStr && ContextKeyExpr.deserialize(whenClauseStr);
170
+ if (!whenClause) {
171
+ register();
172
+ } else {
173
+ const evaluate = () => {
174
+ if (this._contextKeyService.contextMatchesRules(whenClause)) {
175
+ register();
176
+ } else {
177
+ handle.clear();
178
+ }
179
+ };
180
+ store.add(this._contextKeyService.onDidChangeContext(evaluate));
181
+ evaluate();
182
+ }
183
+ this._collectionDefinitions.set(collection.id, {
184
+ servers: serverDefinitions,
185
+ dispose: () => store.dispose()
186
+ });
187
+ }
188
+ }
189
+ $deleteMcpCollection(collectionId) {
190
+ this._collectionDefinitions.deleteAndDispose(collectionId);
191
+ }
192
+ $onDidChangeState(id, update) {
193
+ const server = this._servers.get(id);
194
+ if (!server) {
195
+ return;
196
+ }
197
+ server.state.set(update, undefined);
198
+ if (!McpConnectionState.isRunning(update)) {
199
+ server.dispose();
200
+ this._servers.delete(id);
201
+ this._serverDefinitions.delete(id);
202
+ this._serverAuthTracking.untrack(id);
203
+ }
204
+ }
205
+ $onDidPublishLog(id, level, log) {
206
+ if (typeof level === "string") {
207
+ level = LogLevel.Info;
208
+ log = level;
209
+ }
210
+ this._servers.get(id)?.pushLog(level, log);
211
+ }
212
+ $onDidReceiveMessage(id, message) {
213
+ this._servers.get(id)?.pushMessage(message);
214
+ }
215
+ async $getTokenForProviderId(id, providerId, scopes, options = {}) {
216
+ const server = this._serverDefinitions.get(id);
217
+ if (!server) {
218
+ return undefined;
219
+ }
220
+ return this._getSessionForProvider(
221
+ id,
222
+ server,
223
+ providerId,
224
+ scopes,
225
+ undefined,
226
+ options.errorOnUserInteraction,
227
+ options.clientId
228
+ );
229
+ }
230
+ async $getTokenFromServerMetadata(
231
+ id,
232
+ authDetails,
233
+ {
234
+ errorOnUserInteraction,
235
+ forceNewRegistration,
236
+ clientId
237
+ } = {}
238
+ ) {
239
+ const server = this._serverDefinitions.get(id);
240
+ if (!server) {
241
+ return undefined;
242
+ }
243
+ const authorizationServer = URI.revive(authDetails.authorizationServer);
244
+ const resourceServer = authDetails.resourceMetadata?.resource ? ( URI.parse(authDetails.resourceMetadata.resource)) : undefined;
245
+ const resolvedScopes = authDetails.scopes ?? authDetails.resourceMetadata?.scopes_supported ?? authDetails.authorizationServerMetadata.scopes_supported ?? [];
246
+ if (authDetails.enterpriseManaged) {
247
+ const resource = authDetails.resourceMetadata?.resource;
248
+ if (!resource) {
249
+ throw ( new Error(( localize(
250
+ 3014,
251
+ "The enterprise-managed MCP server '{0}' did not advertise a protected-resource metadata document with a 'resource' identifier.",
252
+ server.label
253
+ ))));
254
+ }
255
+ const resourceAuthServers = authDetails.resourceMetadata?.authorization_servers ?? [];
256
+ const audience = resourceAuthServers[0];
257
+ if (!audience) {
258
+ throw ( new Error(( localize(
259
+ 3015,
260
+ "The enterprise-managed MCP server '{0}' did not advertise an `authorization_servers` entry in its protected-resource metadata.",
261
+ server.label
262
+ ))));
263
+ }
264
+ const xaaScopes = authDetails.scopes ?? authDetails.resourceMetadata?.scopes_supported ?? [];
265
+ const issuer = this._ensureXaaIssuer();
266
+ const xaaProviderId = await this._authenticationService.createOrGetXaaProvider(issuer);
267
+ if (!xaaProviderId) {
268
+ return undefined;
269
+ }
270
+ const resourceClientId = clientId ?? authDetails.clientId;
271
+ let resourceClientSecret;
272
+ if (resourceClientId) {
273
+ try {
274
+ resourceClientSecret = await this._secretStorageService.get(mcpOAuthClientSecretStorageKey(resource, resourceClientId));
275
+ } catch {}
276
+ }
277
+ return this._getSessionForProvider(
278
+ id,
279
+ server,
280
+ xaaProviderId,
281
+ xaaScopes,
282
+ issuer,
283
+ errorOnUserInteraction,
284
+ resourceClientId,
285
+ resource,
286
+ audience,
287
+ resourceClientSecret
288
+ );
289
+ }
290
+ let providerId = await this._authenticationService.getOrActivateProviderIdForServer(authorizationServer, resourceServer);
291
+ const resolvedClientId = clientId ?? authDetails.clientId;
292
+ const mcpServerUrl = server.launch.type === McpServerTransportType.HTTP ? ( server.launch.uri.toString(true)) : undefined;
293
+ let clientSecret;
294
+ let didLookupClientSecret = false;
295
+ if (resolvedClientId && mcpServerUrl) {
296
+ try {
297
+ clientSecret = await this._secretStorageService.get(mcpOAuthClientSecretStorageKey(mcpServerUrl, resolvedClientId));
298
+ didLookupClientSecret = true;
299
+ } catch {}
300
+ }
301
+ if (didLookupClientSecret && providerId && !forceNewRegistration && this._authenticationService.isDynamicAuthenticationProvider(providerId)) {
302
+ const registered = await this._dynamicAuthenticationProviderStorageService.getClientRegistration(providerId);
303
+ if (registered && registered.clientSecret !== clientSecret) {
304
+ forceNewRegistration = true;
305
+ }
306
+ }
307
+ if (forceNewRegistration && providerId) {
308
+ if (!this._authenticationService.isDynamicAuthenticationProvider(providerId)) {
309
+ throw ( new Error("Cannot force new registration for a non-dynamic authentication provider."));
310
+ }
311
+ this._authenticationService.unregisterAuthenticationProvider(providerId);
312
+ await this._dynamicAuthenticationProviderStorageService.removeDynamicProvider(providerId);
313
+ providerId = undefined;
314
+ }
315
+ if (!providerId) {
316
+ const provider = await this._authenticationService.createDynamicAuthenticationProvider(
317
+ authorizationServer,
318
+ authDetails.authorizationServerMetadata,
319
+ authDetails.resourceMetadata,
320
+ resolvedClientId,
321
+ clientSecret
322
+ );
323
+ if (!provider) {
324
+ return undefined;
325
+ }
326
+ providerId = provider.id;
327
+ }
328
+ return this._getSessionForProvider(
329
+ id,
330
+ server,
331
+ providerId,
332
+ resolvedScopes,
333
+ authorizationServer,
334
+ errorOnUserInteraction,
335
+ resolvedClientId,
336
+ authDetails.resourceMetadata?.resource,
337
+ undefined,
338
+ clientSecret
339
+ );
340
+ }
341
+ _ensureXaaIssuer() {
342
+ const config = this._configurationService.getValue(mcpEnterpriseManagedAuthIdpSection) ?? {};
343
+ const configuredIssuer = config.issuer?.trim();
344
+ if (!configuredIssuer) {
345
+ throw ( new Error(( localize(
346
+ 3016,
347
+ "Enterprise-managed MCP authentication requires `mcp.enterpriseManagedAuth.idp.issuer` to be configured. Set it via enterprise policy (Windows Group Policy / macOS managed preferences / Linux `/etc/vscode/policy.json`) or, for local testing, by hand-editing `settings.json`."
348
+ ))));
349
+ }
350
+ let parsed;
351
+ try {
352
+ parsed = ( URI.parse(configuredIssuer));
353
+ } catch {
354
+ throw ( new Error(( localize(
355
+ 3017,
356
+ "Enterprise-managed MCP authentication requires `mcp.enterpriseManagedAuth.idp.issuer` to be a valid URL; got '{0}'.",
357
+ configuredIssuer
358
+ ))));
359
+ }
360
+ if (parsed.scheme !== "https" && parsed.scheme !== "http") {
361
+ throw ( new Error(( localize(
362
+ 3018,
363
+ "Enterprise-managed MCP authentication requires `mcp.enterpriseManagedAuth.idp.issuer` to use the `https` or `http` scheme; got '{0}'.",
364
+ configuredIssuer
365
+ ))));
366
+ }
367
+ return parsed;
368
+ }
369
+ async _getSessionForProvider(
370
+ serverId,
371
+ server,
372
+ providerId,
373
+ scopes,
374
+ authorizationServer,
375
+ errorOnUserInteraction = false,
376
+ clientId,
377
+ resource,
378
+ audience,
379
+ clientSecret
380
+ ) {
381
+ const authContext = {
382
+ authorizationServer,
383
+ clientId,
384
+ resource,
385
+ audience
386
+ };
387
+ const sessions = await this._authenticationService.getSessions(providerId, scopes, {
388
+ authorizationServer,
389
+ clientId,
390
+ clientSecret,
391
+ resource,
392
+ audience
393
+ }, true);
394
+ if (server.launch.type !== McpServerTransportType.HTTP) {
395
+ return undefined;
396
+ }
397
+ const mcpServerUrl = ( server.launch.uri.toString(true));
398
+ const accountNamePreference = this.authenticationMcpServersService.getAccountPreference(server.id, providerId);
399
+ let matchingAccountPreferenceSession;
400
+ if (accountNamePreference) {
401
+ matchingAccountPreferenceSession = sessions.find(session => session.account.label === accountNamePreference);
402
+ }
403
+ const provider = this._authenticationService.getProvider(providerId);
404
+ let session;
405
+ if (sessions.length) {
406
+ if (matchingAccountPreferenceSession && this.authenticationMCPServerAccessService.isAccessAllowedForUrl(
407
+ providerId,
408
+ matchingAccountPreferenceSession.account.label,
409
+ server.id,
410
+ mcpServerUrl
411
+ )) {
412
+ this.authenticationMCPServerUsageService.addAccountUsage(
413
+ providerId,
414
+ matchingAccountPreferenceSession.account.label,
415
+ scopes,
416
+ server.id,
417
+ server.label
418
+ );
419
+ this._serverAuthTracking.track(providerId, serverId, scopes, authContext);
420
+ return matchingAccountPreferenceSession.accessToken;
421
+ }
422
+ if (!provider.supportsMultipleAccounts && this.authenticationMCPServerAccessService.isAccessAllowedForUrl(providerId, sessions[0].account.label, server.id, mcpServerUrl)) {
423
+ this.authenticationMCPServerUsageService.addAccountUsage(providerId, sessions[0].account.label, scopes, server.id, server.label);
424
+ this._serverAuthTracking.track(providerId, serverId, scopes, authContext);
425
+ return sessions[0].accessToken;
426
+ }
427
+ }
428
+ if (errorOnUserInteraction) {
429
+ throw ( new UserInteractionRequiredError("authentication"));
430
+ }
431
+ const isAllowed = await this.loginPrompt(server.label, provider.label, false);
432
+ if (!isAllowed) {
433
+ throw ( new Error("User did not consent to login."));
434
+ }
435
+ if (sessions.length) {
436
+ if (provider.supportsMultipleAccounts && errorOnUserInteraction) {
437
+ throw ( new UserInteractionRequiredError("authentication"));
438
+ }
439
+ session = provider.supportsMultipleAccounts ? await this.authenticationMcpServersService.selectSession(providerId, server.id, server.label, scopes, sessions) : sessions[0];
440
+ } else {
441
+ if (errorOnUserInteraction) {
442
+ throw ( new UserInteractionRequiredError("authentication"));
443
+ }
444
+ const accountToCreate = matchingAccountPreferenceSession?.account;
445
+ do {
446
+ session = await this._authenticationService.createSession(providerId, scopes, {
447
+ activateImmediate: true,
448
+ account: accountToCreate,
449
+ authorizationServer,
450
+ clientId,
451
+ clientSecret,
452
+ resource,
453
+ audience
454
+ });
455
+ } while (accountToCreate && accountToCreate.label !== session.account.label && !(await this.continueWithIncorrectAccountPrompt(session.account.label, accountToCreate.label)));
456
+ }
457
+ this.authenticationMCPServerAccessService.updateAllowedMcpServers(providerId, session.account.label, [{
458
+ id: server.id,
459
+ name: server.label,
460
+ allowed: true,
461
+ url: mcpServerUrl
462
+ }]);
463
+ this.authenticationMcpServersService.updateAccountPreference(server.id, providerId, session.account);
464
+ this.authenticationMCPServerUsageService.addAccountUsage(providerId, session.account.label, scopes, server.id, server.label);
465
+ this._serverAuthTracking.track(providerId, serverId, scopes, authContext);
466
+ return session.accessToken;
467
+ }
468
+ async continueWithIncorrectAccountPrompt(chosenAccountLabel, requestedAccountLabel) {
469
+ const result = await this.dialogService.prompt({
470
+ message: ( localize(3019, "Incorrect account detected")),
471
+ detail: ( localize(
472
+ 3020,
473
+ "The chosen account, {0}, does not match the requested account, {1}.",
474
+ chosenAccountLabel,
475
+ requestedAccountLabel
476
+ )),
477
+ type: Severity.Warning,
478
+ cancelButton: true,
479
+ buttons: [{
480
+ label: ( localize(3021, "Keep {0}", chosenAccountLabel)),
481
+ run: () => chosenAccountLabel
482
+ }, {
483
+ label: ( localize(3022, "Login with {0}", requestedAccountLabel)),
484
+ run: () => requestedAccountLabel
485
+ }]
486
+ });
487
+ if (!result.result) {
488
+ throw ( new CancellationError());
489
+ }
490
+ return result.result === chosenAccountLabel;
491
+ }
492
+ async _onDidChangeAuthSessions(providerId, providerLabel) {
493
+ const serversUsingProvider = this._serverAuthTracking.get(providerId);
494
+ if (!serversUsingProvider) {
495
+ return;
496
+ }
497
+ for (const {
498
+ serverId,
499
+ scopes,
500
+ context
501
+ } of serversUsingProvider) {
502
+ const server = this._servers.get(serverId);
503
+ const serverDefinition = this._serverDefinitions.get(serverId);
504
+ if (!server || !serverDefinition) {
505
+ continue;
506
+ }
507
+ const state = server.state.get();
508
+ if (state.state !== McpConnectionState.Kind.Running) {
509
+ continue;
510
+ }
511
+ try {
512
+ await this._getSessionForProvider(
513
+ serverId,
514
+ serverDefinition,
515
+ providerId,
516
+ scopes,
517
+ context.authorizationServer,
518
+ true,
519
+ context.clientId,
520
+ context.resource,
521
+ context.audience
522
+ );
523
+ } catch (e) {
524
+ if (UserInteractionRequiredError.is(e)) {
525
+ server.pushLog(LogLevel.Warning, ( localize(
526
+ 3023,
527
+ "Authentication session for {0} removed, stopping server",
528
+ providerLabel
529
+ )));
530
+ server.stop();
531
+ }
532
+ }
533
+ }
534
+ }
535
+ $logMcpAuthSetup(data) {
536
+ this._telemetryService.publicLog2("mcp/authSetup", data);
537
+ }
538
+ async $startMcpGateway(chatSessionResource) {
539
+ const result = await this._mcpGatewayService.createGateway(
540
+ this._extHostContext.extensionHostKind === ExtensionHostKind.Remote,
541
+ chatSessionResource ? URI.revive(chatSessionResource) : undefined
542
+ );
543
+ if (!result) {
544
+ return undefined;
545
+ }
546
+ if (this._store.isDisposed) {
547
+ result.dispose();
548
+ return undefined;
549
+ }
550
+ const gatewayId = generateUuid();
551
+ const store = ( new DisposableStore());
552
+ store.add(result);
553
+ store.add(result.onDidChangeServers(servers => {
554
+ this._proxy.$onDidChangeGatewayServers(gatewayId, ( servers.map(s => ({
555
+ label: s.label,
556
+ address: s.address
557
+ }))));
558
+ }));
559
+ this._gateways.set(gatewayId, store);
560
+ return {
561
+ servers: ( result.servers.map(s => ({
562
+ label: s.label,
563
+ address: s.address
564
+ }))),
565
+ gatewayId
566
+ };
567
+ }
568
+ $disposeMcpGateway(gatewayId) {
569
+ this._gateways.deleteAndDispose(gatewayId);
570
+ }
571
+ async loginPrompt(mcpLabel, providerLabel, recreatingSession) {
572
+ const message = recreatingSession ? ( localize(
573
+ 3024,
574
+ "The MCP Server Definition '{0}' wants you to authenticate to {1}.",
575
+ mcpLabel,
576
+ providerLabel
577
+ )) : ( localize(
578
+ 3025,
579
+ "The MCP Server Definition '{0}' wants to authenticate to {1}.",
580
+ mcpLabel,
581
+ providerLabel
582
+ ));
583
+ const buttons = [{
584
+ label: ( localize(3026, "&&Allow")),
585
+ run() {
586
+ return true;
587
+ }
588
+ }];
589
+ const {
590
+ result
591
+ } = await this.dialogService.prompt({
592
+ type: Severity.Info,
593
+ message,
594
+ buttons,
595
+ cancelButton: true
596
+ });
597
+ return result ?? false;
598
+ }
599
+ dispose() {
600
+ for (const server of ( this._servers.values())) {
601
+ server.extHostDispose();
602
+ }
603
+ this._servers.clear();
604
+ this._serverDefinitions.clear();
605
+ this._serverAuthTracking.clear();
606
+ super.dispose();
607
+ }
608
+ };
609
+ MainThreadMcp = __decorate([extHostNamedCustomer(MainContext.MainThreadMcp), ( __param(1, IMcpRegistry)), ( __param(2, IDialogService)), ( __param(3, IAuthenticationService)), ( __param(4, IAuthenticationMcpService)), ( __param(5, IAuthenticationMcpAccessService)), ( __param(6, IAuthenticationMcpUsageService)), ( __param(7, IDynamicAuthenticationProviderStorageService)), ( __param(8, IExtensionService)), ( __param(9, IContextKeyService)), ( __param(10, ITelemetryService)), ( __param(11, IWorkbenchMcpGatewayService)), ( __param(12, IConfigurationService)), ( __param(13, ISecretStorageService))], MainThreadMcp);
610
+ class ExtHostMcpServerLaunch extends Disposable {
611
+ pushLog(level, message) {
612
+ this._onDidLog.fire({
613
+ message,
614
+ level
615
+ });
616
+ }
617
+ pushMessage(message) {
618
+ let parsed;
619
+ try {
620
+ parsed = JSON.parse(message);
621
+ } catch (e) {
622
+ this.pushLog(LogLevel.Warning, `Failed to parse message: ${JSON.stringify(message)}`);
623
+ }
624
+ if (parsed) {
625
+ if (Array.isArray(parsed)) {
626
+ parsed.forEach(p => this._onDidReceiveMessage.fire(p));
627
+ } else {
628
+ this._onDidReceiveMessage.fire(parsed);
629
+ }
630
+ }
631
+ }
632
+ constructor(extHostKind, stop, send) {
633
+ super();
634
+ this.stop = stop;
635
+ this.send = send;
636
+ this.state = observableValue("mcpServerState", {
637
+ state: McpConnectionState.Kind.Starting
638
+ });
639
+ this._onDidLog = this._register(( new Emitter()));
640
+ this.onDidLog = this._onDidLog.event;
641
+ this._onDidReceiveMessage = this._register(( new Emitter()));
642
+ this.onDidReceiveMessage = this._onDidReceiveMessage.event;
643
+ this._register(disposableTimeout(() => {
644
+ this.pushLog(
645
+ LogLevel.Info,
646
+ `Starting server from ${extensionHostKindToString(extHostKind)} extension host`
647
+ );
648
+ }));
649
+ }
650
+ extHostDispose() {
651
+ if (McpConnectionState.isRunning(this.state.get())) {
652
+ this.pushLog(LogLevel.Warning, "Extension host shut down, server will stop.");
653
+ this.state.set({
654
+ state: McpConnectionState.Kind.Stopped
655
+ }, undefined);
656
+ }
657
+ this.dispose();
658
+ }
659
+ dispose() {
660
+ if (McpConnectionState.isRunning(this.state.get())) {
661
+ this.stop();
662
+ }
663
+ super.dispose();
664
+ }
665
+ }
666
+ class McpServerAuthTracker {
667
+ constructor() {
668
+ this._tracking = ( new Map());
669
+ }
670
+ track(providerId, serverId, scopes, context) {
671
+ const servers = this._tracking.get(providerId) || [];
672
+ const filtered = servers.filter(s => s.serverId !== serverId);
673
+ filtered.push({
674
+ serverId,
675
+ scopes,
676
+ context
677
+ });
678
+ this._tracking.set(providerId, filtered);
679
+ }
680
+ untrack(serverId) {
681
+ for (const [providerId, servers] of this._tracking.entries()) {
682
+ const filtered = servers.filter(s => s.serverId !== serverId);
683
+ if (filtered.length === 0) {
684
+ this._tracking.delete(providerId);
685
+ } else {
686
+ this._tracking.set(providerId, filtered);
687
+ }
688
+ }
689
+ }
690
+ get(providerId) {
691
+ return this._tracking.get(providerId);
692
+ }
693
+ clear() {
694
+ this._tracking.clear();
695
+ }
696
+ }
697
+
698
+ export { MainThreadMcp, McpServerAuthTracker };