@@ -25,12 +25,17 @@ import { ContextKeyExpr } from '../../../../platform/contextkey/common/contextke
25
25
import { ExtensionsLocalizedLabel } from '../../../../platform/extensionManagement/common/extensionManagement.js' ;
26
26
import { IInstantiationService , ServicesAccessor } from '../../../../platform/instantiation/common/instantiation.js' ;
27
27
import { IMcpGalleryService } from '../../../../platform/mcp/common/mcpManagement.js' ;
28
+ import { IProductService } from '../../../../platform/product/common/productService.js' ;
28
29
import { IQuickInputService , IQuickPickItem , IQuickPickSeparator } from '../../../../platform/quickinput/common/quickInput.js' ;
29
30
import { StorageScope } from '../../../../platform/storage/common/storage.js' ;
30
31
import { spinningLoading } from '../../../../platform/theme/common/iconRegistry.js' ;
31
32
import { IWorkspaceContextService } from '../../../../platform/workspace/common/workspace.js' ;
32
33
import { ActiveEditorContext , ResourceContextKey } from '../../../common/contextkeys.js' ;
33
34
import { IWorkbenchContribution } from '../../../common/contributions.js' ;
35
+ import { IAuthenticationAccessService } from '../../../services/authentication/browser/authenticationAccessService.js' ;
36
+ import { IAuthenticationMcpAccessService } from '../../../services/authentication/browser/authenticationMcpAccessService.js' ;
37
+ import { IAuthenticationMcpService } from '../../../services/authentication/browser/authenticationMcpService.js' ;
38
+ import { IAuthenticationService } from '../../../services/authentication/common/authentication.js' ;
34
39
import { IEditorService } from '../../../services/editor/common/editorService.js' ;
35
40
import { IViewsService } from '../../../services/views/common/viewsService.js' ;
36
41
import { IChatWidgetService } from '../../chat/browser/chat.js' ;
@@ -142,6 +147,9 @@ export class ListMcpServerCommand extends Action2 {
142
147
}
143
148
}
144
149
150
+ interface ActionItem extends IQuickPickItem {
151
+ action : 'start' | 'stop' | 'restart' | 'disconnect' | 'signout' | 'showOutput' | 'config' | 'configSampling' | 'samplingLog' | 'resources' ;
152
+ }
145
153
146
154
export class McpServerOptionsCommand extends Action2 {
147
155
constructor ( ) {
@@ -160,6 +168,11 @@ export class McpServerOptionsCommand extends Action2 {
160
168
const editorService = accessor . get ( IEditorService ) ;
161
169
const commandService = accessor . get ( ICommandService ) ;
162
170
const samplingService = accessor . get ( IMcpSamplingService ) ;
171
+ const authenticationMcpService = accessor . get ( IAuthenticationMcpService ) ;
172
+ const authenticationMcpAccessService = accessor . get ( IAuthenticationMcpAccessService ) ;
173
+ const authenticationExtensionAccessService = accessor . get ( IAuthenticationAccessService ) ;
174
+ const authenticationService = accessor . get ( IAuthenticationService ) ;
175
+ const productService = accessor . get ( IProductService ) ;
163
176
const server = mcpService . servers . get ( ) . find ( s => s . definition . id === id ) ;
164
177
if ( ! server ) {
165
178
return ;
@@ -168,10 +181,6 @@ export class McpServerOptionsCommand extends Action2 {
168
181
const collection = mcpRegistry . collections . get ( ) . find ( c => c . id === server . collection . id ) ;
169
182
const serverDefinition = collection ?. serverDefinitions . get ( ) . find ( s => s . id === server . definition . id ) ;
170
183
171
- interface ActionItem extends IQuickPickItem {
172
- action : 'start' | 'stop' | 'restart' | 'showOutput' | 'config' | 'configSampling' | 'samplingLog' | 'resources' ;
173
- }
174
-
175
184
const items : ( ActionItem | IQuickPickSeparator ) [ ] = [ ] ;
176
185
const serverState = server . connectionState . get ( ) ;
177
186
@@ -194,6 +203,18 @@ export class McpServerOptionsCommand extends Action2 {
194
203
} ) ;
195
204
}
196
205
206
+ const item = this . _getAuthAction (
207
+ mcpRegistry ,
208
+ authenticationMcpService ,
209
+ authenticationMcpAccessService ,
210
+ authenticationExtensionAccessService ,
211
+ productService ,
212
+ server . definition . id
213
+ ) ;
214
+ if ( item ) {
215
+ items . push ( item ) ;
216
+ }
217
+
197
218
const configTarget = serverDefinition ?. presentation ?. origin || collection ?. presentation ?. origin ;
198
219
if ( configTarget ) {
199
220
items . push ( {
@@ -254,6 +275,26 @@ export class McpServerOptionsCommand extends Action2 {
254
275
await server . stop ( ) ;
255
276
await server . start ( { isFromInteraction : true } ) ;
256
277
break ;
278
+ case 'disconnect' :
279
+ await this . _handleAuth (
280
+ mcpRegistry ,
281
+ authenticationMcpService ,
282
+ authenticationMcpAccessService ,
283
+ authenticationService ,
284
+ server ,
285
+ false
286
+ ) ;
287
+ break ;
288
+ case 'signout' :
289
+ await this . _handleAuth (
290
+ mcpRegistry ,
291
+ authenticationMcpService ,
292
+ authenticationMcpAccessService ,
293
+ authenticationService ,
294
+ server ,
295
+ true
296
+ ) ;
297
+ break ;
257
298
case 'showOutput' :
258
299
server . showOutput ( ) ;
259
300
break ;
@@ -278,6 +319,107 @@ export class McpServerOptionsCommand extends Action2 {
278
319
assertNever ( pick . action ) ;
279
320
}
280
321
}
322
+
323
+ private _getAuthAction (
324
+ mcpRegistry : IMcpRegistry ,
325
+ authenticationMcpService : IAuthenticationMcpService ,
326
+ authenticationMcpAccessService : IAuthenticationMcpAccessService ,
327
+ authenticationAccessService : IAuthenticationAccessService ,
328
+ productService : IProductService ,
329
+ serverId : string
330
+ ) : ActionItem | undefined {
331
+ const providerId = mcpRegistry . getAuthenticationUsage ( serverId ) ;
332
+ if ( ! providerId ) {
333
+ return undefined ;
334
+ }
335
+ const preference = authenticationMcpService . getAccountPreference ( serverId , providerId ) ;
336
+ if ( ! preference ) {
337
+ return undefined ;
338
+ }
339
+ if ( ! authenticationMcpAccessService . isAccessAllowed ( providerId , preference , serverId ) ) {
340
+ return undefined ;
341
+ }
342
+ const allowedServers = this . _getAllAllowedItems (
343
+ authenticationMcpAccessService ,
344
+ authenticationAccessService ,
345
+ productService ,
346
+ providerId ,
347
+ preference
348
+ ) ;
349
+
350
+ // If there are multiple allowed servers/extensions, other things are using this provider
351
+ // so we show a disconnect action, otherwise we show a sign out action.
352
+ if ( allowedServers . length > 1 ) {
353
+ return {
354
+ action : 'disconnect' ,
355
+ label : localize ( 'mcp.disconnect' , 'Disconnect Account' ) ,
356
+ description : `(${ preference } )` ,
357
+ } ;
358
+ }
359
+ return {
360
+ action : 'signout' ,
361
+ label : localize ( 'mcp.signOut' , 'Sign Out' ) ,
362
+ description : `(${ preference } )`
363
+ } ;
364
+ }
365
+
366
+ // TODO@TylerLeonhardt : The fact that this function exists means that these classes could really use some refactoring...
367
+ private _getAllAllowedItems (
368
+ authenticationMcpAccessService : IAuthenticationMcpAccessService ,
369
+ authenticationAccessService : IAuthenticationAccessService ,
370
+ productService : IProductService ,
371
+ providerId : string ,
372
+ preference : string
373
+ ) {
374
+ const trustedExtensionAuth = Array . isArray ( productService . trustedExtensionAuthAccess ) || ! productService . trustedExtensionAuthAccess
375
+ ? [ ]
376
+ : productService . trustedExtensionAuthAccess [ providerId ] ?? [ ] ;
377
+ const trustedMcpAuth = Array . isArray ( productService . trustedMcpAuthAccess ) || ! productService . trustedMcpAuthAccess
378
+ ? [ ]
379
+ : productService . trustedMcpAuthAccess [ providerId ] ?? [ ] ;
380
+
381
+ return [
382
+ ...authenticationMcpAccessService . readAllowedMcpServers ( providerId , preference ) . filter ( s => ! s . trusted ) ,
383
+ ...authenticationAccessService . readAllowedExtensions ( providerId , preference ) . filter ( e => ! e . trusted ) ,
384
+ ...trustedExtensionAuth ,
385
+ ...trustedMcpAuth
386
+ ] ;
387
+ }
388
+
389
+ private async _handleAuth (
390
+ mcpRegistry : IMcpRegistry ,
391
+ authenticationMcpService : IAuthenticationMcpService ,
392
+ authenticationMcpAccessService : IAuthenticationMcpAccessService ,
393
+ authenticationService : IAuthenticationService ,
394
+ server : IMcpServer ,
395
+ signOut : boolean
396
+ ) {
397
+ const providerId = mcpRegistry . getAuthenticationUsage ( server . definition . id ) ;
398
+ if ( ! providerId ) {
399
+ return ;
400
+ }
401
+ const preference = authenticationMcpService . getAccountPreference ( server . definition . id , providerId ) ;
402
+ if ( ! preference ) {
403
+ return ;
404
+ }
405
+ authenticationMcpAccessService . updateAllowedMcpServers ( providerId , preference , [
406
+ {
407
+ id : server . definition . id ,
408
+ name : server . definition . label ,
409
+ allowed : false
410
+ }
411
+ ] ) ;
412
+ if ( signOut ) {
413
+ const accounts = await authenticationService . getAccounts ( providerId ) ;
414
+ const account = accounts . find ( a => a . label === preference ) ;
415
+ if ( account ) {
416
+ const sessions = await authenticationService . getSessions ( providerId , undefined , { account } ) ;
417
+ for ( const session of sessions ) {
418
+ await authenticationService . removeSession ( providerId , session . id ) ;
419
+ }
420
+ }
421
+ }
422
+ }
281
423
}
282
424
283
425
export class MCPServerActionRendering extends Disposable implements IWorkbenchContribution {
0 commit comments