@@ -12,11 +12,10 @@ import { Action2 } from '../../../../../platform/actions/common/actions.js';
12
12
import { ICommandService } from '../../../../../platform/commands/common/commands.js' ;
13
13
import { IDialogService } from '../../../../../platform/dialogs/common/dialogs.js' ;
14
14
import { IInstantiationService , ServicesAccessor } from '../../../../../platform/instantiation/common/instantiation.js' ;
15
- import { IProductService } from '../../../../../platform/product/common/productService.js' ;
16
15
import { IQuickInputService , IQuickPickItem , IQuickPickSeparator } from '../../../../../platform/quickinput/common/quickInput.js' ;
17
- import { AllowedMcpServer , IAuthenticationMcpAccessService } from '../../../../services/authentication/browser/authenticationMcpAccessService.js' ;
18
- import { IAuthenticationMcpUsageService } from '../../../../services/authentication/browser/authenticationMcpUsageService.js' ;
16
+ import { AllowedMcpServer } from '../../../../services/authentication/browser/authenticationMcpAccessService.js' ;
19
17
import { IAuthenticationService } from '../../../../services/authentication/common/authentication.js' ;
18
+ import { IAuthenticationQueryService , IAccountQuery } from '../../../../services/authentication/common/authenticationQuery.js' ;
20
19
import { IMcpService } from '../../../mcp/common/mcpTypes.js' ;
21
20
22
21
export class ManageTrustedMcpServersForAccountAction extends Action2 {
@@ -42,135 +41,101 @@ interface TrustedMcpServersQuickPickItem extends IQuickPickItem {
42
41
43
42
class ManageTrustedMcpServersForAccountActionImpl {
44
43
constructor (
45
- @IProductService private readonly _productService : IProductService ,
46
44
@IMcpService private readonly _mcpServerService : IMcpService ,
47
45
@IDialogService private readonly _dialogService : IDialogService ,
48
46
@IQuickInputService private readonly _quickInputService : IQuickInputService ,
49
47
@IAuthenticationService private readonly _mcpServerAuthenticationService : IAuthenticationService ,
50
- @IAuthenticationMcpUsageService private readonly _mcpServerAuthenticationUsageService : IAuthenticationMcpUsageService ,
51
- @IAuthenticationMcpAccessService private readonly _mcpServerAuthenticationAccessService : IAuthenticationMcpAccessService ,
48
+ @IAuthenticationQueryService private readonly _authenticationQueryService : IAuthenticationQueryService ,
52
49
@ICommandService private readonly _commandService : ICommandService
53
50
) { }
54
51
55
52
async run ( options ?: { providerId : string ; accountLabel : string } ) {
56
- const { providerId , accountLabel } = await this . _resolveProviderAndAccountLabel ( options ?. providerId , options ?. accountLabel ) ;
57
- if ( ! providerId || ! accountLabel ) {
53
+ const accountQuery = await this . _resolveAccountQuery ( options ?. providerId , options ?. accountLabel ) ;
54
+ if ( ! accountQuery ) {
58
55
return ;
59
56
}
60
57
61
- const items = await this . _getItems ( providerId , accountLabel ) ;
58
+ const items = await this . _getItems ( accountQuery ) ;
62
59
if ( ! items . length ) {
63
60
return ;
64
61
}
65
- const disposables = new DisposableStore ( ) ;
66
- const picker = this . _createQuickPick ( disposables , providerId , accountLabel ) ;
62
+ const picker = this . _createQuickPick ( accountQuery ) ;
67
63
picker . items = items ;
68
64
picker . selectedItems = items . filter ( ( i ) : i is TrustedMcpServersQuickPickItem => i . type !== 'separator' && ! ! i . picked ) ;
69
65
picker . show ( ) ;
70
66
}
71
67
72
- private async _resolveProviderAndAccountLabel ( providerId : string | undefined , accountLabel : string | undefined ) {
73
- if ( ! providerId || ! accountLabel ) {
74
- const accounts = new Array < { providerId : string ; providerLabel : string ; accountLabel : string } > ( ) ;
75
- for ( const id of this . _mcpServerAuthenticationService . getProviderIds ( ) ) {
76
- const providerLabel = this . _mcpServerAuthenticationService . getProvider ( id ) . label ;
77
- const sessions = await this . _mcpServerAuthenticationService . getSessions ( id ) ;
78
- const uniqueAccountLabels = new Set < string > ( ) ;
79
- for ( const session of sessions ) {
80
- if ( ! uniqueAccountLabels . has ( session . account . label ) ) {
81
- uniqueAccountLabels . add ( session . account . label ) ;
82
- accounts . push ( { providerId : id , providerLabel, accountLabel : session . account . label } ) ;
83
- }
84
- }
85
- }
86
-
87
- const pick = await this . _quickInputService . pick (
88
- accounts . map ( account => ( {
89
- providerId : account . providerId ,
90
- label : account . accountLabel ,
91
- description : account . providerLabel
92
- } ) ) ,
93
- {
94
- placeHolder : localize ( 'pickAccount' , "Pick an account to manage trusted MCP servers for" ) ,
95
- matchOnDescription : true ,
96
- }
97
- ) ;
68
+ //#region Account Query Resolution
98
69
99
- if ( pick ) {
100
- providerId = pick . providerId ;
101
- accountLabel = pick . label ;
102
- } else {
103
- return { providerId : undefined , accountLabel : undefined } ;
104
- }
70
+ private async _resolveAccountQuery ( providerId : string | undefined , accountLabel : string | undefined ) : Promise < IAccountQuery | undefined > {
71
+ if ( providerId && accountLabel ) {
72
+ return this . _authenticationQueryService . provider ( providerId ) . account ( accountLabel ) ;
105
73
}
106
- return { providerId, accountLabel } ;
74
+
75
+ const accounts = await this . _getAllAvailableAccounts ( ) ;
76
+ const pick = await this . _quickInputService . pick ( accounts , {
77
+ placeHolder : localize ( 'pickAccount' , "Pick an account to manage trusted MCP servers for" ) ,
78
+ matchOnDescription : true ,
79
+ } ) ;
80
+
81
+ return pick ? this . _authenticationQueryService . provider ( pick . providerId ) . account ( pick . label ) : undefined ;
107
82
}
108
83
109
- private async _getItems ( providerId : string , accountLabel : string ) {
110
- let allowedMcpServers = this . _mcpServerAuthenticationAccessService . readAllowedMcpServers ( providerId , accountLabel ) ;
111
- // only include MCP servers that are installed
112
- // TODO: improve?
113
- const resolvedMcpServers = await Promise . all ( allowedMcpServers . map ( server => this . _mcpServerService . servers . get ( ) . find ( s => s . definition . id === server . id ) ) ) ;
114
- allowedMcpServers = resolvedMcpServers
115
- . map ( ( server , i ) => server ? allowedMcpServers [ i ] : undefined )
116
- . filter ( server => ! ! server ) ;
117
- const trustedMcpServerAuthAccess = this . _productService . trustedMcpAuthAccess ;
118
- const trustedMcpServerIds =
119
- // Case 1: trustedMcpServerAuthAccess is an array
120
- Array . isArray ( trustedMcpServerAuthAccess )
121
- ? trustedMcpServerAuthAccess
122
- // Case 2: trustedMcpServerAuthAccess is an object
123
- : typeof trustedMcpServerAuthAccess === 'object'
124
- ? trustedMcpServerAuthAccess [ providerId ] ?? [ ]
125
- : [ ] ;
126
- for ( const mcpServerId of trustedMcpServerIds ) {
127
- const allowedMcpServer = allowedMcpServers . find ( server => server . id === mcpServerId ) ;
128
- if ( ! allowedMcpServer ) {
129
- // Add the MCP server to the allowedMcpServers list
130
- // TODO: improve?
131
- const mcpServer = this . _mcpServerService . servers . get ( ) . find ( s => s . definition . id === mcpServerId ) ;
132
- if ( mcpServer ) {
133
- allowedMcpServers . push ( {
134
- id : mcpServerId ,
135
- name : mcpServer . definition . label ,
136
- allowed : true ,
137
- trusted : true
84
+ private async _getAllAvailableAccounts ( ) {
85
+ const accounts = [ ] ;
86
+ for ( const providerId of this . _mcpServerAuthenticationService . getProviderIds ( ) ) {
87
+ const provider = this . _mcpServerAuthenticationService . getProvider ( providerId ) ;
88
+ const sessions = await this . _mcpServerAuthenticationService . getSessions ( providerId ) ;
89
+ const uniqueLabels = new Set < string > ( ) ;
90
+
91
+ for ( const session of sessions ) {
92
+ if ( ! uniqueLabels . has ( session . account . label ) ) {
93
+ uniqueLabels . add ( session . account . label ) ;
94
+ accounts . push ( {
95
+ providerId,
96
+ label : session . account . label ,
97
+ description : provider . label
138
98
} ) ;
139
99
}
140
- } else {
141
- // Update the MCP server to be allowed
142
- allowedMcpServer . allowed = true ;
143
- allowedMcpServer . trusted = true ;
144
100
}
145
101
}
102
+ return accounts ;
103
+ }
104
+
105
+ //#endregion
106
+
107
+ //#region Item Retrieval and Quick Pick Creation
108
+
109
+ private async _getItems ( accountQuery : IAccountQuery ) {
110
+ const allowedMcpServers = accountQuery . mcpServers ( ) . getAllowedMcpServers ( ) ;
111
+ const serverIdToLabel = new Map < string , string > ( this . _mcpServerService . servers . get ( ) . map ( s => [ s . definition . id , s . definition . label ] ) ) ;
112
+ const filteredMcpServers = allowedMcpServers
113
+ // Filter out MCP servers that are not in the current list of servers
114
+ . filter ( server => serverIdToLabel . has ( server . id ) )
115
+ . map ( server => {
116
+ const usage = accountQuery . mcpServer ( server . id ) . getUsage ( ) ;
117
+ return {
118
+ ...server ,
119
+ // Use the server name from the MCP service
120
+ name : serverIdToLabel . get ( server . id ) ! ,
121
+ lastUsed : usage . length > 0 ? Math . max ( ...usage . map ( u => u . lastUsed ) ) : server . lastUsed
122
+ } ;
123
+ } ) ;
146
124
147
- if ( ! allowedMcpServers . length ) {
125
+ if ( ! filteredMcpServers . length ) {
148
126
this . _dialogService . info ( localize ( 'noTrustedMcpServers' , "This account has not been used by any MCP servers." ) ) ;
149
127
return [ ] ;
150
128
}
151
129
152
- const usages = this . _mcpServerAuthenticationUsageService . readAccountUsages ( providerId , accountLabel ) ;
153
- const trustedMcpServers = [ ] ;
154
- const otherMcpServers = [ ] ;
155
- for ( const mcpServer of allowedMcpServers ) {
156
- const usage = usages . find ( usage => mcpServer . id === usage . mcpServerId ) ;
157
- mcpServer . lastUsed = usage ?. lastUsed ;
158
- if ( mcpServer . trusted ) {
159
- trustedMcpServers . push ( mcpServer ) ;
160
- } else {
161
- otherMcpServers . push ( mcpServer ) ;
162
- }
163
- }
164
-
130
+ const trustedServers = filteredMcpServers . filter ( s => s . trusted ) ;
131
+ const otherServers = filteredMcpServers . filter ( s => ! s . trusted ) ;
165
132
const sortByLastUsed = ( a : AllowedMcpServer , b : AllowedMcpServer ) => ( b . lastUsed || 0 ) - ( a . lastUsed || 0 ) ;
166
133
167
- const items = [
168
- ...otherMcpServers . sort ( sortByLastUsed ) . map ( this . _toQuickPickItem ) ,
134
+ return [
135
+ ...otherServers . sort ( sortByLastUsed ) . map ( this . _toQuickPickItem ) ,
169
136
{ type : 'separator' , label : localize ( 'trustedMcpServers' , "Trusted by Microsoft" ) } satisfies IQuickPickSeparator ,
170
- ...trustedMcpServers . sort ( sortByLastUsed ) . map ( this . _toQuickPickItem )
137
+ ...trustedServers . sort ( sortByLastUsed ) . map ( this . _toQuickPickItem )
171
138
] ;
172
-
173
- return items ;
174
139
}
175
140
176
141
private _toQuickPickItem ( mcpServer : AllowedMcpServer ) : TrustedMcpServersQuickPickItem {
@@ -198,38 +163,39 @@ class ManageTrustedMcpServersForAccountActionImpl {
198
163
} ;
199
164
}
200
165
201
- private _createQuickPick ( disposableStore : DisposableStore , providerId : string , accountLabel : string ) {
166
+ private _createQuickPick ( accountQuery : IAccountQuery ) {
167
+ const disposableStore = new DisposableStore ( ) ;
202
168
const quickPick = disposableStore . add ( this . _quickInputService . createQuickPick < TrustedMcpServersQuickPickItem > ( { useSeparators : true } ) ) ;
169
+
170
+ // Configure quick pick
203
171
quickPick . canSelectMany = true ;
204
172
quickPick . customButton = true ;
205
173
quickPick . customLabel = localize ( 'manageTrustedMcpServers.cancel' , 'Cancel' ) ;
206
-
207
174
quickPick . title = localize ( 'manageTrustedMcpServers' , "Manage Trusted MCP Servers" ) ;
208
175
quickPick . placeholder = localize ( 'manageMcpServers' , "Choose which MCP servers can access this account" ) ;
209
176
177
+ // Set up event handlers
210
178
disposableStore . add ( quickPick . onDidAccept ( ( ) => {
211
- const updatedAllowedList = quickPick . items
212
- . filter ( ( item ) : item is TrustedMcpServersQuickPickItem => item . type !== 'separator' )
213
- . map ( i => i . mcpServer ) ;
214
-
215
- const allowedMcpServersSet = new Set ( quickPick . selectedItems . map ( i => i . mcpServer ) ) ;
216
- updatedAllowedList . forEach ( mcpServer => {
217
- mcpServer . allowed = allowedMcpServersSet . has ( mcpServer ) ;
218
- } ) ;
219
- this . _mcpServerAuthenticationAccessService . updateAllowedMcpServers ( providerId , accountLabel , updatedAllowedList ) ;
220
179
quickPick . hide ( ) ;
221
- } ) ) ;
180
+ const allServers = quickPick . items
181
+ . filter ( ( item : any ) : item is TrustedMcpServersQuickPickItem => item . type !== 'separator' )
182
+ . map ( ( i : any ) => i . mcpServer ) ;
222
183
223
- disposableStore . add ( quickPick . onDidHide ( ( ) => {
224
- disposableStore . dispose ( ) ;
225
- } ) ) ;
184
+ const selectedServers = new Set ( quickPick . selectedItems . map ( ( i : any ) => i . mcpServer ) ) ;
226
185
227
- disposableStore . add ( quickPick . onDidCustom ( ( ) => {
228
- quickPick . hide ( ) ;
186
+ for ( const mcpServer of allServers ) {
187
+ const isAllowed = selectedServers . has ( mcpServer ) ;
188
+ accountQuery . mcpServer ( mcpServer . id ) . setAccessAllowed ( isAllowed , mcpServer . name ) ;
189
+ }
229
190
} ) ) ;
230
- disposableStore . add ( quickPick . onDidTriggerItemButton ( e =>
231
- this . _commandService . executeCommand ( '_manageAccountPreferencesForMcpServer' , e . item . mcpServer . id , providerId )
191
+ disposableStore . add ( quickPick . onDidHide ( ( ) => disposableStore . dispose ( ) ) ) ;
192
+ disposableStore . add ( quickPick . onDidCustom ( ( ) => quickPick . hide ( ) ) ) ;
193
+ disposableStore . add ( quickPick . onDidTriggerItemButton ( ( e : any ) =>
194
+ this . _commandService . executeCommand ( '_manageAccountPreferencesForMcpServer' , e . item . mcpServer . id , accountQuery . providerId )
232
195
) ) ;
196
+
233
197
return quickPick ;
234
198
}
199
+
200
+ //#endregion
235
201
}
0 commit comments