@@ -18,10 +18,13 @@ import { ICommandService } from '../../../../../platform/commands/common/command
18
18
import { ContextKeyExpr } from '../../../../../platform/contextkey/common/contextkey.js' ;
19
19
import { ExtensionIdentifier } from '../../../../../platform/extensions/common/extensions.js' ;
20
20
import { KeybindingWeight } from '../../../../../platform/keybinding/common/keybindingsRegistry.js' ;
21
- import { IQuickInputService , IQuickPickItem , IQuickPickSeparator } from '../../../../../platform/quickinput/common/quickInput.js' ;
21
+ import { IOpenerService } from '../../../../../platform/opener/common/opener.js' ;
22
+ import { IQuickInputButton , IQuickInputService , IQuickPickItem , IQuickPickSeparator } from '../../../../../platform/quickinput/common/quickInput.js' ;
22
23
import { ITelemetryService } from '../../../../../platform/telemetry/common/telemetry.js' ;
24
+ import { IEditorService } from '../../../../services/editor/common/editorService.js' ;
23
25
import { IExtensionsWorkbenchService } from '../../../extensions/common/extensions.js' ;
24
26
import { AddConfigurationAction } from '../../../mcp/browser/mcpCommands.js' ;
27
+ import { IMcpRegistry } from '../../../mcp/common/mcpRegistryTypes.js' ;
25
28
import { IMcpService , IMcpServer , McpConnectionState } from '../../../mcp/common/mcpTypes.js' ;
26
29
import { ChatContextKeys } from '../../common/chatContextKeys.js' ;
27
30
import { IChatToolInvocation } from '../../common/chatService.js' ;
@@ -109,11 +112,13 @@ export class AttachToolsAction extends Action2 {
109
112
110
113
const quickPickService = accessor . get ( IQuickInputService ) ;
111
114
const mcpService = accessor . get ( IMcpService ) ;
115
+ const mcpRegistry = accessor . get ( IMcpRegistry ) ;
112
116
const toolsService = accessor . get ( ILanguageModelToolsService ) ;
113
117
const chatWidgetService = accessor . get ( IChatWidgetService ) ;
114
118
const telemetryService = accessor . get ( ITelemetryService ) ;
115
119
const commandService = accessor . get ( ICommandService ) ;
116
120
const extensionWorkbenchService = accessor . get ( IExtensionsWorkbenchService ) ;
121
+ const editorService = accessor . get ( IEditorService ) ;
117
122
118
123
let widget = chatWidgetService . lastFocusedWidget ;
119
124
if ( ! widget ) {
@@ -143,6 +148,7 @@ export class AttachToolsAction extends Action2 {
143
148
type ToolPick = IQuickPickItem & { picked : boolean ; tool : IToolData ; parent : BucketPick } ;
144
149
type AddPick = IQuickPickItem & { pickable : false ; run : ( ) => void } ;
145
150
type MyPick = ToolPick | BucketPick | AddPick ;
151
+ type ActionableButton = IQuickInputButton & { action : ( ) => void } ;
146
152
147
153
const addMcpPick : AddPick = { type : 'item' , label : localize ( 'addServer' , "Add MCP Server..." ) , iconClass : ThemeIcon . asClassName ( Codicon . add ) , pickable : false , run : ( ) => commandService . executeCommand ( AddConfigurationAction . ID ) } ;
148
154
const addExpPick : AddPick = { type : 'item' , label : localize ( 'addExtension' , "Install Extension..." ) , iconClass : ThemeIcon . asClassName ( Codicon . add ) , pickable : false , run : ( ) => extensionWorkbenchService . openSearch ( '@tag:language-model-tools' ) } ;
@@ -184,16 +190,44 @@ export class AttachToolsAction extends Action2 {
184
190
continue ;
185
191
}
186
192
const key = tool . source . type + mcpServer . definition . id ;
187
- bucket = toolBuckets . get ( key ) ?? {
188
- type : 'item' ,
189
- label : localize ( 'mcplabel' , "MCP Server: {0}" , mcpServer ?. definition . label ) ,
190
- status : localize ( 'mcpstatus' , "From {0} ({1})" , mcpServer . collection . label , McpConnectionState . toString ( mcpServer . connectionState . get ( ) ) ) ,
191
- ordinal : BucketOrdinal . Mcp ,
192
- source : tool . source ,
193
- picked : false ,
194
- children : [ ]
195
- } ;
196
- toolBuckets . set ( key , bucket ) ;
193
+ const existingBucket = toolBuckets . get ( key ) ;
194
+ if ( existingBucket ) {
195
+ bucket = existingBucket ;
196
+ } else {
197
+
198
+ const collection = mcpRegistry . collections . get ( ) . find ( c => c . id === mcpServer . collection . id ) ;
199
+ const buttons : ActionableButton [ ] = [ ] ;
200
+ if ( collection ?. presentation ?. origin ) {
201
+ buttons . push ( {
202
+ iconClass : ThemeIcon . asClassName ( Codicon . settingsGear ) ,
203
+ tooltip : localize ( 'configMcpCol' , "Configure {0}" , collection . label ) ,
204
+ alwaysVisible : true ,
205
+ action : ( ) => editorService . openEditor ( {
206
+ resource : collection ! . presentation ! . origin ,
207
+ } ) ,
208
+ } ) ;
209
+ }
210
+ if ( mcpServer . connectionState . get ( ) . state === McpConnectionState . Kind . Error ) {
211
+ buttons . push ( {
212
+ iconClass : ThemeIcon . asClassName ( Codicon . warning ) ,
213
+ tooltip : localize ( 'mcpShowOutput' , "Show Output" ) ,
214
+ alwaysVisible : true ,
215
+ action : ( ) => mcpServer . showOutput ( ) ,
216
+ } ) ;
217
+ }
218
+
219
+ bucket = {
220
+ type : 'item' ,
221
+ label : localize ( 'mcplabel' , "MCP Server: {0}" , mcpServer ?. definition . label ) ,
222
+ status : localize ( 'mcpstatus' , "from {0}" , mcpServer . collection . label ) ,
223
+ ordinal : BucketOrdinal . Mcp ,
224
+ source : tool . source ,
225
+ picked : false ,
226
+ children : [ ] ,
227
+ buttons,
228
+ } ;
229
+ toolBuckets . set ( key , bucket ) ;
230
+ }
197
231
} else if ( tool . source . type === 'extension' ) {
198
232
const key = tool . source . type + ExtensionIdentifier . toKey ( tool . source . extensionId ) ;
199
233
@@ -238,6 +272,9 @@ export class AttachToolsAction extends Action2 {
238
272
function isAddPick ( obj : any ) : obj is AddPick {
239
273
return Boolean ( ( obj as AddPick ) . run ) ;
240
274
}
275
+ function isActionableButton ( obj : IQuickInputButton ) : obj is ActionableButton {
276
+ return typeof ( obj as ActionableButton ) . action === 'function' ;
277
+ }
241
278
242
279
const store = new DisposableStore ( ) ;
243
280
@@ -306,6 +343,13 @@ export class AttachToolsAction extends Action2 {
306
343
picker . items = picks ;
307
344
picker . show ( ) ;
308
345
346
+ store . add ( picker . onDidTriggerItemButton ( e => {
347
+ if ( isActionableButton ( e . button ) ) {
348
+ e . button . action ( ) ;
349
+ store . dispose ( ) ;
350
+ }
351
+ } ) ) ;
352
+
309
353
store . add ( picker . onDidChangeSelection ( selectedPicks => {
310
354
if ( ignoreEvent ) {
311
355
return ;
0 commit comments