Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/workbench/contrib/mcp/browser/mcpCommandsAddConfiguration.ts
3296 views
1
/*---------------------------------------------------------------------------------------------
2
* Copyright (c) Microsoft Corporation. All rights reserved.
3
* Licensed under the MIT License. See License.txt in the project root for license information.
4
*--------------------------------------------------------------------------------------------*/
5
6
import { mapFindFirst } from '../../../../base/common/arraysFind.js';
7
import { assertNever } from '../../../../base/common/assert.js';
8
import { disposableTimeout } from '../../../../base/common/async.js';
9
import { parse as parseJsonc } from '../../../../base/common/jsonc.js';
10
import { DisposableStore } from '../../../../base/common/lifecycle.js';
11
import { Schemas } from '../../../../base/common/network.js';
12
import { autorun } from '../../../../base/common/observable.js';
13
import { basename } from '../../../../base/common/resources.js';
14
import { URI } from '../../../../base/common/uri.js';
15
import { generateUuid } from '../../../../base/common/uuid.js';
16
import { localize } from '../../../../nls.js';
17
import { ICommandService } from '../../../../platform/commands/common/commands.js';
18
import { ConfigurationTarget, IConfigurationService } from '../../../../platform/configuration/common/configuration.js';
19
import { IFileService } from '../../../../platform/files/common/files.js';
20
import { ILabelService } from '../../../../platform/label/common/label.js';
21
import { IGalleryMcpServerConfiguration, RegistryType } from '../../../../platform/mcp/common/mcpManagement.js';
22
import { IMcpRemoteServerConfiguration, IMcpServerConfiguration, IMcpServerVariable, IMcpStdioServerConfiguration, McpServerType } from '../../../../platform/mcp/common/mcpPlatformTypes.js';
23
import { INotificationService } from '../../../../platform/notification/common/notification.js';
24
import { IOpenerService } from '../../../../platform/opener/common/opener.js';
25
import { IQuickInputService, IQuickPickItem, QuickPickInput } from '../../../../platform/quickinput/common/quickInput.js';
26
import { ITelemetryService } from '../../../../platform/telemetry/common/telemetry.js';
27
import { isWorkspaceFolder, IWorkspaceContextService, IWorkspaceFolder, WorkbenchState } from '../../../../platform/workspace/common/workspace.js';
28
import { IEditorService } from '../../../services/editor/common/editorService.js';
29
import { IWorkbenchEnvironmentService } from '../../../services/environment/common/environmentService.js';
30
import { IWorkbenchMcpManagementService } from '../../../services/mcp/common/mcpWorkbenchManagementService.js';
31
import { McpCommandIds } from '../common/mcpCommandIds.js';
32
import { allDiscoverySources, DiscoverySource, mcpDiscoverySection, mcpStdioServerSchema } from '../common/mcpConfiguration.js';
33
import { IMcpRegistry } from '../common/mcpRegistryTypes.js';
34
import { IMcpService, McpConnectionState } from '../common/mcpTypes.js';
35
36
export const enum AddConfigurationType {
37
Stdio,
38
HTTP,
39
40
NpmPackage,
41
PipPackage,
42
NuGetPackage,
43
DockerImage,
44
}
45
46
type AssistedConfigurationType = AddConfigurationType.NpmPackage | AddConfigurationType.PipPackage | AddConfigurationType.NuGetPackage | AddConfigurationType.DockerImage;
47
48
export const AssistedTypes = {
49
[AddConfigurationType.NpmPackage]: {
50
title: localize('mcp.npm.title', "Enter NPM Package Name"),
51
placeholder: localize('mcp.npm.placeholder', "Package name (e.g., @org/package)"),
52
pickLabel: localize('mcp.serverType.npm', "NPM Package"),
53
pickDescription: localize('mcp.serverType.npm.description', "Install from an NPM package name"),
54
enabledConfigKey: null, // always enabled
55
},
56
[AddConfigurationType.PipPackage]: {
57
title: localize('mcp.pip.title', "Enter Pip Package Name"),
58
placeholder: localize('mcp.pip.placeholder', "Package name (e.g., package-name)"),
59
pickLabel: localize('mcp.serverType.pip', "Pip Package"),
60
pickDescription: localize('mcp.serverType.pip.description', "Install from a Pip package name"),
61
enabledConfigKey: null, // always enabled
62
},
63
[AddConfigurationType.NuGetPackage]: {
64
title: localize('mcp.nuget.title', "Enter NuGet Package Name"),
65
placeholder: localize('mcp.nuget.placeholder', "Package name (e.g., Package.Name)"),
66
pickLabel: localize('mcp.serverType.nuget', "NuGet Package"),
67
pickDescription: localize('mcp.serverType.nuget.description', "Install from a NuGet package name"),
68
enabledConfigKey: 'chat.mcp.assisted.nuget.enabled',
69
},
70
[AddConfigurationType.DockerImage]: {
71
title: localize('mcp.docker.title', "Enter Docker Image Name"),
72
placeholder: localize('mcp.docker.placeholder', "Image name (e.g., mcp/imagename)"),
73
pickLabel: localize('mcp.serverType.docker', "Docker Image"),
74
pickDescription: localize('mcp.serverType.docker.description', "Install from a Docker image"),
75
enabledConfigKey: null, // always enabled
76
},
77
};
78
79
const enum AddConfigurationCopilotCommand {
80
/** Returns whether MCP enhanced setup is enabled. */
81
IsSupported = 'github.copilot.chat.mcp.setup.check',
82
83
/** Takes an npm/pip package name, validates its owner. */
84
ValidatePackage = 'github.copilot.chat.mcp.setup.validatePackage',
85
86
/** Returns the resolved MCP configuration. */
87
StartFlow = 'github.copilot.chat.mcp.setup.flow',
88
}
89
90
type ValidatePackageResult =
91
{ state: 'ok'; publisher: string; name?: string; version?: string }
92
| { state: 'error'; error: string; helpUri?: string; helpUriLabel?: string };
93
94
type AddServerData = {
95
packageType: string;
96
};
97
type AddServerClassification = {
98
owner: 'digitarald';
99
comment: 'Generic details for adding a new MCP server';
100
packageType: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The type of MCP server package' };
101
};
102
type AddServerCompletedData = {
103
packageType: string;
104
serverType: string | undefined;
105
target: string;
106
};
107
type AddServerCompletedClassification = {
108
owner: 'digitarald';
109
comment: 'Generic details for successfully adding model-assisted MCP server';
110
packageType: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The type of MCP server package' };
111
serverType: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The type of MCP server' };
112
target: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The target of the MCP server configuration' };
113
};
114
115
type AssistedServerConfiguration = {
116
type?: 'vscode';
117
name?: string;
118
server: Omit<IMcpStdioServerConfiguration, 'type'>;
119
inputs?: IMcpServerVariable[];
120
inputValues?: Record<string, string>;
121
} | {
122
type: 'server.json';
123
name?: string;
124
server: IGalleryMcpServerConfiguration;
125
};
126
127
export class McpAddConfigurationCommand {
128
constructor(
129
private readonly workspaceFolder: IWorkspaceFolder | undefined,
130
@IQuickInputService private readonly _quickInputService: IQuickInputService,
131
@IWorkbenchMcpManagementService private readonly _mcpManagementService: IWorkbenchMcpManagementService,
132
@IWorkspaceContextService private readonly _workspaceService: IWorkspaceContextService,
133
@IWorkbenchEnvironmentService private readonly _environmentService: IWorkbenchEnvironmentService,
134
@ICommandService private readonly _commandService: ICommandService,
135
@IMcpRegistry private readonly _mcpRegistry: IMcpRegistry,
136
@IOpenerService private readonly _openerService: IOpenerService,
137
@IEditorService private readonly _editorService: IEditorService,
138
@IFileService private readonly _fileService: IFileService,
139
@INotificationService private readonly _notificationService: INotificationService,
140
@ITelemetryService private readonly _telemetryService: ITelemetryService,
141
@IMcpService private readonly _mcpService: IMcpService,
142
@ILabelService private readonly _label: ILabelService,
143
@IConfigurationService private readonly _configurationService: IConfigurationService,
144
) { }
145
146
private async getServerType(): Promise<AddConfigurationType | undefined> {
147
type TItem = { kind: AddConfigurationType | 'browse' | 'discovery' } & IQuickPickItem;
148
const items: QuickPickInput<TItem>[] = [
149
{ kind: AddConfigurationType.Stdio, label: localize('mcp.serverType.command', "Command (stdio)"), description: localize('mcp.serverType.command.description', "Run a local command that implements the MCP protocol") },
150
{ kind: AddConfigurationType.HTTP, label: localize('mcp.serverType.http', "HTTP (HTTP or Server-Sent Events)"), description: localize('mcp.serverType.http.description', "Connect to a remote HTTP server that implements the MCP protocol") }
151
];
152
153
let aiSupported: boolean | undefined;
154
try {
155
aiSupported = await this._commandService.executeCommand<boolean>(AddConfigurationCopilotCommand.IsSupported);
156
} catch {
157
// ignored
158
}
159
160
if (aiSupported) {
161
items.unshift({ type: 'separator', label: localize('mcp.serverType.manual', "Manual Install") });
162
163
const elligableTypes = Object.entries(AssistedTypes).map(([type, { pickLabel, pickDescription, enabledConfigKey }]) => {
164
if (enabledConfigKey) {
165
const enabled = this._configurationService.getValue<boolean>(enabledConfigKey) ?? false;
166
if (!enabled) {
167
return;
168
}
169
}
170
return {
171
kind: Number(type) as AddConfigurationType,
172
label: pickLabel,
173
description: pickDescription,
174
};
175
}).filter(x => !!x);
176
177
items.push(
178
{ type: 'separator', label: localize('mcp.serverType.copilot', "Model-Assisted") },
179
...elligableTypes
180
);
181
}
182
183
items.push({ type: 'separator' });
184
185
const discovery = this._configurationService.getValue<{ [K in DiscoverySource]: boolean }>(mcpDiscoverySection);
186
if (discovery && typeof discovery === 'object' && allDiscoverySources.some(d => !discovery[d])) {
187
items.push({
188
kind: 'discovery',
189
label: localize('mcp.servers.discovery', "Add from another application..."),
190
});
191
}
192
193
items.push({
194
kind: 'browse',
195
label: localize('mcp.servers.browse', "Browse MCP Servers..."),
196
});
197
198
const result = await this._quickInputService.pick<TItem>(items, {
199
placeHolder: localize('mcp.serverType.placeholder', "Choose the type of MCP server to add"),
200
});
201
202
if (result?.kind === 'browse') {
203
this._commandService.executeCommand(McpCommandIds.Browse);
204
return undefined;
205
}
206
207
if (result?.kind === 'discovery') {
208
this._commandService.executeCommand('workbench.action.openSettings', mcpDiscoverySection);
209
return undefined;
210
}
211
212
return result?.kind;
213
}
214
215
private async getStdioConfig(): Promise<IMcpStdioServerConfiguration | undefined> {
216
const command = await this._quickInputService.input({
217
title: localize('mcp.command.title', "Enter Command"),
218
placeHolder: localize('mcp.command.placeholder', "Command to run (with optional arguments)"),
219
ignoreFocusLost: true,
220
});
221
222
if (!command) {
223
return undefined;
224
}
225
226
this._telemetryService.publicLog2<AddServerData, AddServerClassification>('mcp.addserver', {
227
packageType: 'stdio'
228
});
229
230
// Split command into command and args, handling quotes
231
const parts = command.match(/(?:[^\s"]+|"[^"]*")+/g)!;
232
return {
233
type: McpServerType.LOCAL,
234
command: parts[0].replace(/"/g, ''),
235
236
args: parts.slice(1).map(arg => arg.replace(/"/g, ''))
237
};
238
}
239
240
private async getSSEConfig(): Promise<IMcpRemoteServerConfiguration | undefined> {
241
const url = await this._quickInputService.input({
242
title: localize('mcp.url.title', "Enter Server URL"),
243
placeHolder: localize('mcp.url.placeholder', "URL of the MCP server (e.g., http://localhost:3000)"),
244
ignoreFocusLost: true,
245
});
246
247
if (!url) {
248
return undefined;
249
}
250
251
this._telemetryService.publicLog2<AddServerData, AddServerClassification>('mcp.addserver', {
252
packageType: 'sse'
253
});
254
255
return { url, type: McpServerType.REMOTE };
256
}
257
258
private async getServerId(suggestion = `my-mcp-server-${generateUuid().split('-')[0]}`): Promise<string | undefined> {
259
const id = await this._quickInputService.input({
260
title: localize('mcp.serverId.title', "Enter Server ID"),
261
placeHolder: localize('mcp.serverId.placeholder', "Unique identifier for this server"),
262
value: suggestion,
263
ignoreFocusLost: true,
264
});
265
266
return id;
267
}
268
269
private async getConfigurationTarget(): Promise<ConfigurationTarget | IWorkspaceFolder | undefined> {
270
const options: (IQuickPickItem & { target?: ConfigurationTarget | IWorkspaceFolder })[] = [
271
{ target: ConfigurationTarget.USER_LOCAL, label: localize('mcp.target.user', "Global"), description: localize('mcp.target.user.description', "Available in all workspaces, runs locally") }
272
];
273
274
const raLabel = this._environmentService.remoteAuthority && this._label.getHostLabel(Schemas.vscodeRemote, this._environmentService.remoteAuthority);
275
if (raLabel) {
276
options.push({ target: ConfigurationTarget.USER_REMOTE, label: localize('mcp.target.remote', "Remote"), description: localize('mcp.target..remote.description', "Available on this remote machine, runs on {0}", raLabel) });
277
}
278
279
const workbenchState = this._workspaceService.getWorkbenchState();
280
if (workbenchState !== WorkbenchState.EMPTY) {
281
const target = workbenchState === WorkbenchState.FOLDER ? this._workspaceService.getWorkspace().folders[0] : ConfigurationTarget.WORKSPACE;
282
if (this._environmentService.remoteAuthority) {
283
options.push({ target, label: localize('mcp.target.workspace', "Workspace"), description: localize('mcp.target.workspace.description.remote', "Available in this workspace, runs on {0}", raLabel) });
284
} else {
285
options.push({ target, label: localize('mcp.target.workspace', "Workspace"), description: localize('mcp.target.workspace.description', "Available in this workspace, runs locally") });
286
}
287
}
288
289
if (options.length === 1) {
290
return options[0].target;
291
}
292
293
const targetPick = await this._quickInputService.pick(options, {
294
title: localize('mcp.target.title', "Choose where to install the MCP server"),
295
});
296
297
return targetPick?.target;
298
}
299
300
private async getAssistedConfig(type: AssistedConfigurationType): Promise<{ name?: string; server: Omit<IMcpStdioServerConfiguration, 'type'>; inputs?: IMcpServerVariable[]; inputValues?: Record<string, string> } | undefined> {
301
const packageName = await this._quickInputService.input({
302
ignoreFocusLost: true,
303
title: AssistedTypes[type].title,
304
placeHolder: AssistedTypes[type].placeholder,
305
});
306
307
if (!packageName) {
308
return undefined;
309
}
310
311
const enum LoadAction {
312
Retry = 'retry',
313
Cancel = 'cancel',
314
Allow = 'allow',
315
OpenUri = 'openUri',
316
}
317
318
const loadingQuickPickStore = new DisposableStore();
319
const loadingQuickPick = loadingQuickPickStore.add(this._quickInputService.createQuickPick<IQuickPickItem & { id: LoadAction; helpUri?: URI }>());
320
loadingQuickPick.title = localize('mcp.loading.title', "Loading package details...");
321
loadingQuickPick.busy = true;
322
loadingQuickPick.ignoreFocusOut = true;
323
324
const packageType = this.getPackageType(type);
325
326
this._telemetryService.publicLog2<AddServerData, AddServerClassification>('mcp.addserver', {
327
packageType: packageType!
328
});
329
330
this._commandService.executeCommand<ValidatePackageResult>(
331
AddConfigurationCopilotCommand.ValidatePackage,
332
{
333
type: packageType,
334
name: packageName,
335
targetConfig: {
336
...mcpStdioServerSchema,
337
properties: {
338
...mcpStdioServerSchema.properties,
339
name: {
340
type: 'string',
341
description: 'Suggested name of the server, alphanumeric and hyphen only',
342
}
343
},
344
required: [...(mcpStdioServerSchema.required || []), 'name'],
345
},
346
}
347
).then(result => {
348
if (!result || result.state === 'error') {
349
loadingQuickPick.title = result?.error || 'Unknown error loading package';
350
351
const items: Array<IQuickPickItem & { id: LoadAction; helpUri?: URI }> = [];
352
353
if (result?.helpUri) {
354
items.push({
355
id: LoadAction.OpenUri,
356
label: result.helpUriLabel ?? localize('mcp.error.openHelpUri', 'Open help URL'),
357
helpUri: URI.parse(result.helpUri),
358
});
359
}
360
361
items.push(
362
{ id: LoadAction.Retry, label: localize('mcp.error.retry', 'Try a different package') },
363
{ id: LoadAction.Cancel, label: localize('cancel', 'Cancel') },
364
);
365
366
loadingQuickPick.items = items;
367
} else {
368
loadingQuickPick.title = localize(
369
'mcp.confirmPublish', 'Install {0}{1} from {2}?',
370
result.name ?? packageName,
371
result.version ? `@${result.version}` : '',
372
result.publisher);
373
loadingQuickPick.items = [
374
{ id: LoadAction.Allow, label: localize('allow', "Allow") },
375
{ id: LoadAction.Cancel, label: localize('cancel', 'Cancel') }
376
];
377
}
378
loadingQuickPick.busy = false;
379
});
380
381
const loadingAction = await new Promise<{ id: LoadAction; helpUri?: URI } | undefined>(resolve => {
382
loadingQuickPick.onDidAccept(() => resolve(loadingQuickPick.selectedItems[0]));
383
loadingQuickPick.onDidHide(() => resolve(undefined));
384
loadingQuickPick.show();
385
}).finally(() => loadingQuickPick.dispose());
386
387
switch (loadingAction?.id) {
388
case LoadAction.Retry:
389
return this.getAssistedConfig(type);
390
case LoadAction.OpenUri:
391
if (loadingAction.helpUri) { this._openerService.open(loadingAction.helpUri); }
392
return undefined;
393
case LoadAction.Allow:
394
break;
395
case LoadAction.Cancel:
396
default:
397
return undefined;
398
}
399
400
const config = await this._commandService.executeCommand<AssistedServerConfiguration>(
401
AddConfigurationCopilotCommand.StartFlow,
402
{
403
name: packageName,
404
type: packageType
405
}
406
);
407
408
if (config?.type === 'server.json') {
409
const packageType = this.getPackageTypeEnum(type);
410
if (!packageType) {
411
throw new Error(`Unsupported assisted package type ${type}`);
412
}
413
const server = this._mcpManagementService.getMcpServerConfigurationFromManifest(config.server, packageType);
414
if (server.config.type !== McpServerType.LOCAL) {
415
throw new Error(`Unexpected server type ${server.config.type} for assisted configuration from server.json.`);
416
}
417
return {
418
name: config.name,
419
server: server.config,
420
inputs: server.inputs,
421
};
422
} else if (config?.type === 'vscode' || !config?.type) {
423
return config;
424
} else {
425
assertNever(config?.type);
426
}
427
}
428
429
/** Shows the location of a server config once it's discovered. */
430
private showOnceDiscovered(name: string) {
431
const store = new DisposableStore();
432
store.add(autorun(reader => {
433
const colls = this._mcpRegistry.collections.read(reader);
434
const servers = this._mcpService.servers.read(reader);
435
const match = mapFindFirst(colls, collection => mapFindFirst(collection.serverDefinitions.read(reader),
436
server => server.label === name ? { server, collection } : undefined));
437
const server = match && servers.find(s => s.definition.id === match.server.id);
438
439
440
if (match && server) {
441
if (match.collection.presentation?.origin) {
442
this._editorService.openEditor({
443
resource: match.collection.presentation.origin,
444
options: {
445
selection: match.server.presentation?.origin?.range,
446
preserveFocus: true,
447
}
448
});
449
} else {
450
this._commandService.executeCommand(McpCommandIds.ServerOptions, name);
451
}
452
453
server.start({ promptType: 'all-untrusted' }).then(state => {
454
if (state.state === McpConnectionState.Kind.Error) {
455
server.showOutput();
456
}
457
});
458
459
store.dispose();
460
}
461
}));
462
463
store.add(disposableTimeout(() => store.dispose(), 5000));
464
}
465
466
public async run(): Promise<void> {
467
// Step 1: Choose server type
468
const serverType = await this.getServerType();
469
if (serverType === undefined) {
470
return;
471
}
472
473
// Step 2: Get server details based on type
474
let config: IMcpServerConfiguration | undefined;
475
let suggestedName: string | undefined;
476
let inputs: IMcpServerVariable[] | undefined;
477
let inputValues: Record<string, string> | undefined;
478
switch (serverType) {
479
case AddConfigurationType.Stdio:
480
config = await this.getStdioConfig();
481
break;
482
case AddConfigurationType.HTTP:
483
config = await this.getSSEConfig();
484
break;
485
case AddConfigurationType.NpmPackage:
486
case AddConfigurationType.PipPackage:
487
case AddConfigurationType.NuGetPackage:
488
case AddConfigurationType.DockerImage: {
489
const r = await this.getAssistedConfig(serverType);
490
config = r?.server ? { ...r.server, type: McpServerType.LOCAL } : undefined;
491
suggestedName = r?.name;
492
inputs = r?.inputs;
493
inputValues = r?.inputValues;
494
break;
495
}
496
default:
497
assertNever(serverType);
498
}
499
500
if (!config) {
501
return;
502
}
503
504
// Step 3: Get server ID
505
const name = await this.getServerId(suggestedName);
506
if (!name) {
507
return;
508
}
509
510
// Step 4: Choose configuration target if no configUri provided
511
let target: ConfigurationTarget | IWorkspaceFolder | undefined = this.workspaceFolder;
512
if (!target) {
513
target = await this.getConfigurationTarget();
514
if (!target) {
515
return;
516
}
517
}
518
519
await this._mcpManagementService.install({ name, config, inputs }, { target });
520
521
if (inputValues) {
522
for (const [key, value] of Object.entries(inputValues)) {
523
await this._mcpRegistry.setSavedInput(key, (isWorkspaceFolder(target) ? ConfigurationTarget.WORKSPACE_FOLDER : target) ?? ConfigurationTarget.WORKSPACE, value);
524
}
525
}
526
527
const packageType = this.getPackageType(serverType);
528
if (packageType) {
529
this._telemetryService.publicLog2<AddServerCompletedData, AddServerCompletedClassification>('mcp.addserver.completed', {
530
packageType,
531
serverType: config.type,
532
target: target === ConfigurationTarget.WORKSPACE ? 'workspace' : 'user'
533
});
534
}
535
536
this.showOnceDiscovered(name);
537
}
538
539
public async pickForUrlHandler(resource: URI, showIsPrimary = false): Promise<void> {
540
const name = decodeURIComponent(basename(resource)).replace(/\.json$/, '');
541
const placeHolder = localize('install.title', 'Install MCP server {0}', name);
542
543
const items: IQuickPickItem[] = [
544
{ id: 'install', label: localize('install.start', 'Install Server') },
545
{ id: 'show', label: localize('install.show', 'Show Configuration', name) },
546
{ id: 'rename', label: localize('install.rename', 'Rename "{0}"', name) },
547
{ id: 'cancel', label: localize('cancel', 'Cancel') },
548
];
549
if (showIsPrimary) {
550
[items[0], items[1]] = [items[1], items[0]];
551
}
552
553
const pick = await this._quickInputService.pick(items, { placeHolder, ignoreFocusLost: true });
554
const getEditors = () => this._editorService.findEditors(resource);
555
556
switch (pick?.id) {
557
case 'show':
558
await this._editorService.openEditor({ resource });
559
break;
560
case 'install':
561
await this._editorService.save(getEditors());
562
try {
563
const contents = await this._fileService.readFile(resource);
564
const { inputs, ...config }: IMcpServerConfiguration & { inputs?: IMcpServerVariable[] } = parseJsonc(contents.value.toString());
565
await this._mcpManagementService.install({ name, config, inputs });
566
this._editorService.closeEditors(getEditors());
567
this.showOnceDiscovered(name);
568
} catch (e) {
569
this._notificationService.error(localize('install.error', 'Error installing MCP server {0}: {1}', name, e.message));
570
await this._editorService.openEditor({ resource });
571
}
572
break;
573
case 'rename': {
574
const newName = await this._quickInputService.input({ placeHolder: localize('install.newName', 'Enter new name'), value: name });
575
if (newName) {
576
const newURI = resource.with({ path: `/${encodeURIComponent(newName)}.json` });
577
await this._editorService.save(getEditors());
578
await this._fileService.move(resource, newURI);
579
return this.pickForUrlHandler(newURI, showIsPrimary);
580
}
581
break;
582
}
583
}
584
}
585
586
private getPackageTypeEnum(type: AddConfigurationType): RegistryType | undefined {
587
switch (type) {
588
case AddConfigurationType.NpmPackage:
589
return RegistryType.NODE;
590
case AddConfigurationType.PipPackage:
591
return RegistryType.PYTHON;
592
case AddConfigurationType.NuGetPackage:
593
return RegistryType.NUGET;
594
case AddConfigurationType.DockerImage:
595
return RegistryType.DOCKER;
596
default:
597
return undefined;
598
}
599
}
600
601
private getPackageType(serverType: AddConfigurationType): string | undefined {
602
switch (serverType) {
603
case AddConfigurationType.NpmPackage:
604
return 'npm';
605
case AddConfigurationType.PipPackage:
606
return 'pip';
607
case AddConfigurationType.NuGetPackage:
608
return 'nuget';
609
case AddConfigurationType.DockerImage:
610
return 'docker';
611
case AddConfigurationType.Stdio:
612
return 'stdio';
613
case AddConfigurationType.HTTP:
614
return 'sse';
615
default:
616
return undefined;
617
}
618
}
619
}
620
621