@codingame/monaco-vscode-mcp-service-override 36.2.7 → 37.1.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.
Files changed (49) hide show
  1. package/package.json +2 -2
  2. package/vscode/src/vs/base/common/jsonRpcProtocol.d.ts +2 -2
  3. package/vscode/src/vs/platform/mcp/common/allowedMcpServers.d.ts +52 -0
  4. package/vscode/src/vs/platform/mcp/common/allowedMcpServers.js +180 -0
  5. package/vscode/src/vs/platform/mcp/common/allowedMcpServersService.d.ts +4 -0
  6. package/vscode/src/vs/platform/mcp/common/allowedMcpServersService.js +68 -12
  7. package/vscode/src/vs/platform/mcp/common/mcpGalleryService.d.ts +5 -1
  8. package/vscode/src/vs/platform/mcp/common/mcpGalleryService.js +200 -92
  9. package/vscode/src/vs/platform/mcp/common/mcpResourceScannerService.js +3 -2
  10. package/vscode/src/vs/platform/mcp/common/nativeMcpDiscoveryHelper.d.ts +1 -0
  11. package/vscode/src/vs/workbench/contrib/mcp/browser/mcp.contribution.js +3 -3
  12. package/vscode/src/vs/workbench/contrib/mcp/browser/mcpAddContextContribution.js +2 -2
  13. package/vscode/src/vs/workbench/contrib/mcp/browser/mcpCommands.d.ts +10 -3
  14. package/vscode/src/vs/workbench/contrib/mcp/browser/mcpCommands.js +357 -121
  15. package/vscode/src/vs/workbench/contrib/mcp/browser/mcpElicitationService.js +25 -25
  16. package/vscode/src/vs/workbench/contrib/mcp/browser/mcpLanguageFeatures.d.ts +11 -1
  17. package/vscode/src/vs/workbench/contrib/mcp/browser/mcpLanguageFeatures.js +409 -145
  18. package/vscode/src/vs/workbench/contrib/mcp/browser/mcpMigration.js +4 -4
  19. package/vscode/src/vs/workbench/contrib/mcp/browser/mcpResourceQuickAccess.js +8 -8
  20. package/vscode/src/vs/workbench/contrib/mcp/browser/mcpServerEditor.js +37 -37
  21. package/vscode/src/vs/workbench/contrib/mcp/browser/mcpServerEditorInput.js +2 -2
  22. package/vscode/src/vs/workbench/contrib/mcp/browser/mcpServerIcons.js +5 -5
  23. package/vscode/src/vs/workbench/contrib/mcp/browser/mcpServerWidgets.js +5 -5
  24. package/vscode/src/vs/workbench/contrib/mcp/browser/mcpServersView.js +18 -18
  25. package/vscode/src/vs/workbench/contrib/mcp/browser/mcpWorkbenchService.d.ts +12 -0
  26. package/vscode/src/vs/workbench/contrib/mcp/browser/mcpWorkbenchService.js +279 -72
  27. package/vscode/src/vs/workbench/contrib/mcp/browser/media/mcpServersView.css +1 -1
  28. package/vscode/src/vs/workbench/contrib/mcp/common/discovery/extensionMcpDiscovery.js +6 -5
  29. package/vscode/src/vs/workbench/contrib/mcp/common/discovery/installedMcpServersDiscovery.js +9 -16
  30. package/vscode/src/vs/workbench/contrib/mcp/common/discovery/nativeMcpDiscoveryAbstract.d.ts +4 -4
  31. package/vscode/src/vs/workbench/contrib/mcp/common/discovery/nativeMcpDiscoveryAbstract.js +7 -3
  32. package/vscode/src/vs/workbench/contrib/mcp/common/discovery/nativeMcpDiscoveryAdapters.d.ts +14 -6
  33. package/vscode/src/vs/workbench/contrib/mcp/common/discovery/nativeMcpDiscoveryAdapters.js +32 -9
  34. package/vscode/src/vs/workbench/contrib/mcp/common/discovery/workspaceDotMcpDiscovery.d.ts +2 -2
  35. package/vscode/src/vs/workbench/contrib/mcp/common/discovery/workspaceDotMcpDiscovery.js +20 -6
  36. package/vscode/src/vs/workbench/contrib/mcp/common/discovery/workspaceMcpDiscoveryAdapter.js +9 -5
  37. package/vscode/src/vs/workbench/contrib/mcp/common/mcpContextKeys.js +4 -4
  38. package/vscode/src/vs/workbench/contrib/mcp/common/mcpEditorAffordanceState.d.ts +13 -0
  39. package/vscode/src/vs/workbench/contrib/mcp/common/mcpEditorAffordanceState.js +36 -0
  40. package/vscode/src/vs/workbench/contrib/mcp/common/mcpLanguageModelToolContribution.js +6 -6
  41. package/vscode/src/vs/workbench/contrib/mcp/common/mcpRegistry.d.ts +2 -0
  42. package/vscode/src/vs/workbench/contrib/mcp/common/mcpRegistry.js +51 -19
  43. package/vscode/src/vs/workbench/contrib/mcp/common/mcpSamplingLog.js +1 -1
  44. package/vscode/src/vs/workbench/contrib/mcp/common/mcpSamplingService.js +11 -11
  45. package/vscode/src/vs/workbench/contrib/mcp/common/mcpSandboxService.d.ts +4 -0
  46. package/vscode/src/vs/workbench/contrib/mcp/common/mcpSandboxService.js +42 -6
  47. package/vscode/src/vs/workbench/contrib/mcp/common/mcpServerConnection.js +3 -3
  48. package/vscode/src/vs/workbench/contrib/mcp/common/mcpService.js +6 -3
  49. package/vscode/src/vs/workbench/services/authentication/browser/authenticationMcpService.js +9 -9
@@ -5,6 +5,7 @@ import { MarkdownString, createCommandUri } from '@codingame/monaco-vscode-api/v
5
5
  import { Disposable } from '@codingame/monaco-vscode-api/vscode/vs/base/common/lifecycle';
6
6
  import { Schemas } from '@codingame/monaco-vscode-api/vscode/vs/base/common/network';
7
7
  import { basename } from '@codingame/monaco-vscode-api/vscode/vs/base/common/resources';
8
+ import { isObject, isString, isBoolean, isStringArray, isNumber } from '@codingame/monaco-vscode-api/vscode/vs/base/common/types';
8
9
  import { URI } from '@codingame/monaco-vscode-api/vscode/vs/base/common/uri';
9
10
  import { localize } from '@codingame/monaco-vscode-api/vscode/vs/nls';
10
11
  import { ConfigurationTarget } from '@codingame/monaco-vscode-api/vscode/vs/platform/configuration/common/configuration';
@@ -14,7 +15,7 @@ import { IFileService } from '@codingame/monaco-vscode-api/vscode/vs/platform/fi
14
15
  import { IInstantiationService } from '@codingame/monaco-vscode-api/vscode/vs/platform/instantiation/common/instantiation';
15
16
  import { ILabelService } from '@codingame/monaco-vscode-api/vscode/vs/platform/label/common/label.service';
16
17
  import { ILogService } from '@codingame/monaco-vscode-api/vscode/vs/platform/log/common/log.service';
17
- import { mcpAccessConfig, McpAccessValue } from '@codingame/monaco-vscode-api/vscode/vs/platform/mcp/common/mcpManagement';
18
+ import { mcpAccessConfig, McpGalleryResolveStatus, McpAccessValue } from '@codingame/monaco-vscode-api/vscode/vs/platform/mcp/common/mcpManagement';
18
19
  import { IMcpGalleryService, IAllowedMcpServersService } from '@codingame/monaco-vscode-api/vscode/vs/platform/mcp/common/mcpManagement.service';
19
20
  import { ITelemetryService } from '@codingame/monaco-vscode-api/vscode/vs/platform/telemetry/common/telemetry.service';
20
21
  import { McpServerType } from '@codingame/monaco-vscode-api/vscode/vs/platform/mcp/common/mcpPlatformTypes';
@@ -40,10 +41,153 @@ import { IMcpGalleryManifestService } from '@codingame/monaco-vscode-api/vscode/
40
41
  import { IExtensionsWorkbenchService } from '@codingame/monaco-vscode-api/vscode/vs/workbench/contrib/extensions/common/extensions.service';
41
42
  import '@codingame/monaco-vscode-api/vscode/vs/base/common/observableInternal/index';
42
43
  import Severity from '@codingame/monaco-vscode-api/vscode/vs/base/common/severity';
43
- import { Queue } from '@codingame/monaco-vscode-api/vscode/vs/base/common/async';
44
+ import { ThrottledDelayer } from '@codingame/monaco-vscode-api/vscode/vs/base/common/async';
44
45
  import { runOnChange } from '@codingame/monaco-vscode-api/vscode/vs/base/common/observableInternal/utils/runOnChange';
45
46
  import { autorun } from '@codingame/monaco-vscode-api/vscode/vs/base/common/observableInternal/reactions/autorun';
46
47
 
48
+ function parseMcpInstallUriPayload(query) {
49
+ let parsed;
50
+ try {
51
+ parsed = JSON.parse(decodeURIComponent(query));
52
+ } catch {
53
+ return undefined;
54
+ }
55
+ if (!isObject(parsed)) {
56
+ return undefined;
57
+ }
58
+ const payload = parsed;
59
+ if (!isString(payload.name) || !payload.name) {
60
+ return undefined;
61
+ }
62
+ const config = sanitizeMcpServerConfiguration(payload);
63
+ if (!config) {
64
+ return undefined;
65
+ }
66
+ return {
67
+ name: payload.name,
68
+ config,
69
+ inputs: Array.isArray(payload.inputs) ? payload.inputs : undefined
70
+ };
71
+ }
72
+ function sanitizeMcpServerConfiguration(payload) {
73
+ const type = payload.type === McpServerType.LOCAL || payload.type === McpServerType.REMOTE ? payload.type : isString(payload.command) ? McpServerType.LOCAL : McpServerType.REMOTE;
74
+ const dev = sanitizeMcpDevModeConfig(payload.dev);
75
+ const common = {
76
+ ...(isString(payload.version) ? {
77
+ version: payload.version
78
+ } : {}),
79
+ ...(isBoolean(payload.gallery) || isString(payload.gallery) ? {
80
+ gallery: payload.gallery
81
+ } : {}),
82
+ ...(dev ? {
83
+ dev
84
+ } : {})
85
+ };
86
+ if (type === McpServerType.LOCAL) {
87
+ if (!isString(payload.command)) {
88
+ return undefined;
89
+ }
90
+ const env = sanitizeMcpEnvironment(payload.env);
91
+ return {
92
+ type,
93
+ command: payload.command,
94
+ ...common,
95
+ ...(isStringArray(payload.args) ? {
96
+ args: payload.args
97
+ } : {}),
98
+ ...(env ? {
99
+ env
100
+ } : {}),
101
+ ...(isString(payload.envFile) ? {
102
+ envFile: payload.envFile
103
+ } : {}),
104
+ ...(isString(payload.cwd) ? {
105
+ cwd: payload.cwd
106
+ } : {}),
107
+ ...(isBoolean(payload.sandboxEnabled) ? {
108
+ sandboxEnabled: payload.sandboxEnabled
109
+ } : {})
110
+ };
111
+ }
112
+ if (!isString(payload.url)) {
113
+ return undefined;
114
+ }
115
+ const headers = sanitizeStringRecord(payload.headers);
116
+ const oauth = sanitizeMcpOAuthConfiguration(payload.oauth);
117
+ return {
118
+ type,
119
+ url: payload.url,
120
+ ...common,
121
+ ...(payload.transport === "http" || payload.transport === "sse" ? {
122
+ transport: payload.transport
123
+ } : {}),
124
+ ...(headers ? {
125
+ headers
126
+ } : {}),
127
+ ...(oauth ? {
128
+ oauth
129
+ } : {})
130
+ };
131
+ }
132
+ function sanitizeMcpDevModeConfig(value) {
133
+ if (!isObject(value)) {
134
+ return undefined;
135
+ }
136
+ const payload = value;
137
+ const debug = sanitizeMcpDevModeDebugConfiguration(payload.debug);
138
+ if (!isString(payload.watch) && !isStringArray(payload.watch) && !debug) {
139
+ return undefined;
140
+ }
141
+ return {
142
+ ...(isString(payload.watch) || isStringArray(payload.watch) ? {
143
+ watch: payload.watch
144
+ } : {}),
145
+ ...(debug ? {
146
+ debug
147
+ } : {})
148
+ };
149
+ }
150
+ function sanitizeMcpDevModeDebugConfiguration(value) {
151
+ if (!isObject(value)) {
152
+ return undefined;
153
+ }
154
+ const payload = value;
155
+ if (payload.type === "node") {
156
+ return {
157
+ type: "node"
158
+ };
159
+ }
160
+ if (payload.type === "debugpy") {
161
+ return {
162
+ type: "debugpy",
163
+ ...(isString(payload.debugpyPath) ? {
164
+ debugpyPath: payload.debugpyPath
165
+ } : {})
166
+ };
167
+ }
168
+ return undefined;
169
+ }
170
+ function sanitizeMcpEnvironment(value) {
171
+ return sanitizeRecord(value, entry => entry === null || isString(entry) || isNumber(entry));
172
+ }
173
+ function sanitizeStringRecord(value) {
174
+ return sanitizeRecord(value, isString);
175
+ }
176
+ function sanitizeRecord(value, isValidValue) {
177
+ if (!isObject(value)) {
178
+ return undefined;
179
+ }
180
+ return Object.fromEntries(Object.entries(value).filter(entry => isValidValue(entry[1])));
181
+ }
182
+ function sanitizeMcpOAuthConfiguration(value) {
183
+ if (!isObject(value)) {
184
+ return undefined;
185
+ }
186
+ const payload = value;
187
+ return isString(payload.clientId) ? {
188
+ clientId: payload.clientId
189
+ } : undefined;
190
+ }
47
191
  let McpWorkbenchServer = class McpWorkbenchServer {
48
192
  constructor(
49
193
  installStateProvider,
@@ -178,6 +322,12 @@ let McpWorkbenchService = class McpWorkbenchService extends Disposable {
178
322
  this.installing = [];
179
323
  this.uninstalling = [];
180
324
  this._local = [];
325
+ this.registrySyncGeneration = 0;
326
+ this.registryGeneration = 0;
327
+ this.localQueryGeneration = 0;
328
+ this.profileChangeGeneration = 0;
329
+ this.gallerySourceGenerations = ( new WeakMap());
330
+ this.registrySyncDelayer = this._register(( new ThrottledDelayer(0)));
181
331
  this._onChange = this._register(( new Emitter()));
182
332
  this.onChange = this._onChange.event;
183
333
  this._onReset = this._register(( new Emitter()));
@@ -194,16 +344,16 @@ let McpWorkbenchService = class McpWorkbenchService extends Disposable {
194
344
  this._register(
195
345
  this.mcpManagementService.onDidChangeProfile(e => this.onDidChangeProfile())
196
346
  );
197
- this.queryLocal().then(() => {
347
+ this.whenInitialLocalMcpServersLoaded = this.queryLocal().then(() => {
198
348
  if (this._store.isDisposed) {
199
349
  return;
200
350
  }
201
- const queue = this._register(( new Queue()));
202
- this._register(
203
- mcpGalleryManifestService.onDidChangeMcpGalleryManifest(e => queue.queue(() => this.syncInstalledMcpServers()))
204
- );
205
- queue.queue(() => this.syncInstalledMcpServers());
206
- });
351
+ this._register(mcpGalleryManifestService.onDidChangeMcpGalleryManifest(() => {
352
+ this.invalidateRegistryVerification();
353
+ this.scheduleRegistrySync();
354
+ }));
355
+ this.scheduleRegistrySync();
356
+ }, error => this.logService.error(error));
207
357
  urlService.registerHandler(this);
208
358
  this._register(this.configurationService.onDidChangeConfiguration(e => {
209
359
  if (e.affectsConfiguration(mcpAccessConfig)) {
@@ -226,9 +376,23 @@ let McpWorkbenchService = class McpWorkbenchService extends Disposable {
226
376
  }));
227
377
  }
228
378
  async onDidChangeProfile() {
229
- await this.queryLocal();
230
- this._onChange.fire(undefined);
379
+ const profileChangeGeneration = ++this.profileChangeGeneration;
380
+ const generation = ++this.localQueryGeneration;
381
+ this.invalidateRegistryVerification();
382
+ await this.queryLocalForGeneration(generation);
383
+ if (profileChangeGeneration !== this.profileChangeGeneration) {
384
+ return;
385
+ }
231
386
  this._onReset.fire();
387
+ this.scheduleRegistrySync();
388
+ }
389
+ invalidateRegistryVerification() {
390
+ this.registryGeneration++;
391
+ this.registrySyncGeneration++;
392
+ for (const server of this._local) {
393
+ server.gallery = undefined;
394
+ }
395
+ this._onChange.fire(undefined);
232
396
  }
233
397
  areSameMcpServers(a, b) {
234
398
  if (a === b) {
@@ -250,17 +414,18 @@ let McpWorkbenchService = class McpWorkbenchService extends Disposable {
250
414
  }
251
415
  }
252
416
  onDidInstallMcpServers(e) {
253
- const servers = [];
417
+ let needsRegistrySync = false;
254
418
  for (const {
255
419
  local,
256
- source,
257
- name
420
+ name,
421
+ source
258
422
  } of e) {
259
423
  let server = this.installing.find(
260
424
  server => server.local && local ? this.areSameMcpServers(server.local, local) : server.name === name
261
425
  );
262
426
  this.installing = server ? this.installing.filter(e => e !== server) : this.installing;
263
427
  if (local) {
428
+ const trustedGallery = this.getTrustedGallerySource(source) ?? this.getTrustedGallerySource(server?.gallery);
264
429
  if (server) {
265
430
  server.local = local;
266
431
  } else {
@@ -269,23 +434,23 @@ let McpWorkbenchService = class McpWorkbenchService extends Disposable {
269
434
  e => this.getInstallState(e),
270
435
  e => this.getRuntimeStatus(e),
271
436
  local,
272
- source,
437
+ undefined,
273
438
  undefined
274
439
  );
275
440
  }
276
- if (!local.galleryUrl) {
277
- server.gallery = undefined;
278
- }
441
+ server.gallery = trustedGallery?.name === local.name ? trustedGallery : undefined;
442
+ needsRegistrySync = true;
279
443
  this._local = this._local.filter(server => !this.areSameMcpServers(server.local, local));
280
444
  this.addServer(server);
281
445
  }
282
446
  this._onChange.fire(server);
283
447
  }
284
- if (( servers.some(server => server.local?.galleryUrl && !server.gallery))) {
285
- this.syncInstalledMcpServers();
448
+ if (needsRegistrySync) {
449
+ this.scheduleRegistrySync();
286
450
  }
287
451
  }
288
452
  onDidUpdateMcpServers(e) {
453
+ let needsRegistrySync = false;
289
454
  for (const result of e) {
290
455
  if (!result.local) {
291
456
  continue;
@@ -301,61 +466,89 @@ let McpWorkbenchService = class McpWorkbenchService extends Disposable {
301
466
  e => this.getInstallState(e),
302
467
  e => this.getRuntimeStatus(e),
303
468
  result.local,
304
- result.source,
469
+ undefined,
305
470
  undefined
306
471
  );
307
472
  this.addServer(server);
308
473
  }
474
+ const trustedGallery = this.getTrustedGallerySource(result.source) ?? this.getTrustedGallerySource(server.gallery);
475
+ server.gallery = trustedGallery?.name === result.local.name ? trustedGallery : undefined;
476
+ needsRegistrySync = true;
309
477
  this._onChange.fire(server);
310
478
  }
479
+ if (needsRegistrySync) {
480
+ this.scheduleRegistrySync();
481
+ }
311
482
  }
312
- fromGallery(gallery) {
483
+ fromGallery(gallery, registryGeneration) {
484
+ this.rememberGallerySource(gallery, registryGeneration);
313
485
  for (const local of this._local) {
314
486
  if (local.name === gallery.name) {
315
- local.gallery = gallery;
316
487
  return local;
317
488
  }
318
489
  }
319
490
  return undefined;
320
491
  }
321
- async syncInstalledMcpServers() {
322
- const infos = [];
323
- for (const installed of this.local) {
324
- if (installed.local?.source !== "gallery") {
325
- continue;
326
- }
327
- if (installed.local.galleryUrl) {
328
- infos.push({
329
- name: installed.local.name,
330
- id: installed.local.galleryId
492
+ scheduleRegistrySync() {
493
+ const generation = ++this.registrySyncGeneration;
494
+ void this.registrySyncDelayer.trigger(() => this.syncInstalledMcpServers(generation)).catch(error => this.logService.error(error));
495
+ }
496
+ async syncInstalledMcpServers(generation) {
497
+ if (!this.mcpGalleryService.isEnabled()) {
498
+ return;
499
+ }
500
+ const servers = this.local.flatMap(server => server.local ? [{
501
+ server,
502
+ local: server.local
503
+ }] : []);
504
+ const infosByName = ( new Map());
505
+ for (const {
506
+ local
507
+ } of servers) {
508
+ const existing = infosByName.get(local.name);
509
+ if (!existing || (!existing.id && local.galleryId)) {
510
+ infosByName.set(local.name, {
511
+ name: local.name,
512
+ id: local.galleryId
331
513
  });
332
514
  }
333
515
  }
334
- if (infos.length) {
335
- const galleryServers = await this.mcpGalleryService.getMcpServersFromGallery(infos);
336
- await this.syncInstalledMcpServersWithGallery(galleryServers);
516
+ const infos = [...( infosByName.values())];
517
+ if (!infos.length) {
518
+ return;
519
+ }
520
+ const resolved = await this.mcpGalleryService.resolveMcpServersFromGallery(infos);
521
+ if (generation !== this.registrySyncGeneration) {
522
+ return;
337
523
  }
524
+ this.syncInstalledMcpServersWithGallery(resolved, servers, generation);
338
525
  }
339
- async syncInstalledMcpServersWithGallery(gallery) {
340
- const galleryMap = ( new Map(( gallery.map(server => [server.name, server]))));
341
- for (const mcpServer of this.local) {
342
- if (!mcpServer.local) {
526
+ syncInstalledMcpServersWithGallery(resolved, servers, generation) {
527
+ for (const {
528
+ server: mcpServer,
529
+ local
530
+ } of servers) {
531
+ if (generation !== this.registrySyncGeneration || !this._local.includes(mcpServer) || mcpServer.local !== local) {
343
532
  continue;
344
533
  }
345
- const key = mcpServer.local.name;
346
- const gallery = key ? galleryMap.get(key) : undefined;
347
- if (!gallery || gallery.galleryUrl !== mcpServer.local.galleryUrl) {
534
+ const result = resolved.get(local.name);
535
+ if (!result || result.status === McpGalleryResolveStatus.Failed) {
536
+ continue;
537
+ }
538
+ if (result.status === McpGalleryResolveStatus.NotFound) {
348
539
  if (mcpServer.gallery) {
349
540
  mcpServer.gallery = undefined;
350
541
  this._onChange.fire(mcpServer);
351
542
  }
352
543
  continue;
353
544
  }
545
+ const gallery = result.server;
546
+ const changed = mcpServer.gallery !== gallery;
547
+ this.rememberGallerySource(gallery);
354
548
  mcpServer.gallery = gallery;
355
- if (!mcpServer.local.manifest) {
356
- mcpServer.local = await this.mcpManagementService.updateMetadata(mcpServer.local, gallery);
549
+ if (changed) {
550
+ this._onChange.fire(mcpServer);
357
551
  }
358
- this._onChange.fire(mcpServer);
359
552
  }
360
553
  }
361
554
  async queryGallery(options, token) {
@@ -371,10 +564,11 @@ let McpWorkbenchService = class McpWorkbenchService extends Disposable {
371
564
  })
372
565
  };
373
566
  }
567
+ const registryGeneration = this.registryGeneration;
374
568
  const pager = await this.mcpGalleryService.query(options, token);
375
569
  const mapPage = page => ({
376
570
  items: ( page.items.map(
377
- gallery => this.fromGallery(gallery) ?? this.instantiationService.createInstance(
571
+ gallery => this.fromGallery(gallery, registryGeneration) ?? this.instantiationService.createInstance(
378
572
  McpWorkbenchServer,
379
573
  e => this.getInstallState(e),
380
574
  e => this.getRuntimeStatus(e),
@@ -394,7 +588,14 @@ let McpWorkbenchService = class McpWorkbenchService extends Disposable {
394
588
  };
395
589
  }
396
590
  async queryLocal() {
591
+ await this.queryLocalForGeneration(++this.localQueryGeneration);
592
+ return [...this.local];
593
+ }
594
+ async queryLocalForGeneration(generation) {
397
595
  const installed = await this.mcpManagementService.getInstalled();
596
+ if (generation !== this.localQueryGeneration) {
597
+ return false;
598
+ }
398
599
  this._local = this.sort(( installed.map(i => {
399
600
  const existing = this._local.find(local => local.id === i.id);
400
601
  const local = existing ?? this.instantiationService.createInstance(
@@ -409,7 +610,15 @@ let McpWorkbenchService = class McpWorkbenchService extends Disposable {
409
610
  return local;
410
611
  })));
411
612
  this._onChange.fire(undefined);
412
- return [...this.local];
613
+ return true;
614
+ }
615
+ rememberGallerySource(gallery, registryGeneration = this.registryGeneration) {
616
+ if (registryGeneration === this.registryGeneration) {
617
+ this.gallerySourceGenerations.set(gallery, registryGeneration);
618
+ }
619
+ }
620
+ getTrustedGallerySource(gallery) {
621
+ return gallery && this.gallerySourceGenerations.get(gallery) === this.registryGeneration ? gallery : undefined;
413
622
  }
414
623
  addServer(server) {
415
624
  this._local.push(server);
@@ -418,13 +627,12 @@ let McpWorkbenchService = class McpWorkbenchService extends Disposable {
418
627
  sort(local) {
419
628
  return local.sort((a, b) => {
420
629
  if (a.name === b.name) {
421
- if (!a.runtimeStatus || a.runtimeStatus.state === McpServerEnablementState.Enabled) {
422
- return -1;
630
+ const aEnabled = !a.runtimeStatus || a.runtimeStatus.state === McpServerEnablementState.Enabled;
631
+ const bEnabled = !b.runtimeStatus || b.runtimeStatus.state === McpServerEnablementState.Enabled;
632
+ if (aEnabled !== bEnabled) {
633
+ return aEnabled ? -1 : 1;
423
634
  }
424
- if (!b.runtimeStatus || b.runtimeStatus.state === McpServerEnablementState.Enabled) {
425
- return 1;
426
- }
427
- return 0;
635
+ return a.id.localeCompare(b.id);
428
636
  }
429
637
  return a.name.localeCompare(b.name);
430
638
  });
@@ -450,7 +658,7 @@ let McpWorkbenchService = class McpWorkbenchService extends Disposable {
450
658
  const existing = result.get(server.name);
451
659
  if (existing) {
452
660
  this.logService.warn(( localize(
453
- 12076,
661
+ 14052,
454
662
  "Overwriting mcp server '{0}' from {1} with {2}.",
455
663
  server.name,
456
664
  server.mcpResource.path,
@@ -463,7 +671,7 @@ let McpWorkbenchService = class McpWorkbenchService extends Disposable {
463
671
  const existing = result.get(server.name);
464
672
  if (existing) {
465
673
  this.logService.warn(( localize(
466
- 12076,
674
+ 14052,
467
675
  "Overwriting mcp server '{0}' from {1} with {2}.",
468
676
  server.name,
469
677
  server.mcpResource.path,
@@ -476,7 +684,7 @@ let McpWorkbenchService = class McpWorkbenchService extends Disposable {
476
684
  }
477
685
  canInstall(mcpServer) {
478
686
  if (!(mcpServer instanceof McpWorkbenchServer)) {
479
- return ( new MarkdownString()).appendText(( localize(12077, "The provided object is not an mcp server.")));
687
+ return ( new MarkdownString()).appendText(( localize(14053, "The provided object is not an mcp server.")));
480
688
  }
481
689
  if (mcpServer.gallery) {
482
690
  const result = this.mcpManagementService.canInstall(mcpServer.gallery);
@@ -493,7 +701,7 @@ let McpWorkbenchService = class McpWorkbenchService extends Disposable {
493
701
  return result;
494
702
  }
495
703
  return ( new MarkdownString()).appendText(( localize(
496
- 12078,
704
+ 14054,
497
705
  "Cannot install the '{0}' MCP Server because it is not available in this setup.",
498
706
  mcpServer.label
499
707
  )));
@@ -600,7 +808,7 @@ let McpWorkbenchService = class McpWorkbenchService extends Disposable {
600
808
  id: USER_CONFIG_ID,
601
809
  key: "userLocalValue",
602
810
  target: ConfigurationTarget.USER_LOCAL,
603
- label: ( localize(12079, "Global in {0}", this.productService.nameShort)),
811
+ label: ( localize(14055, "Global in {0}", this.productService.nameShort)),
604
812
  scope: StorageScope.PROFILE,
605
813
  order: McpCollectionSortOrder.User,
606
814
  uri: mcpResource,
@@ -676,24 +884,24 @@ let McpWorkbenchService = class McpWorkbenchService extends Disposable {
676
884
  return false;
677
885
  }
678
886
  async handleMcpInstallUri(uri) {
679
- let parsed;
680
- try {
681
- parsed = JSON.parse(decodeURIComponent(uri.query));
682
- } catch (e) {
887
+ const parsed = parseMcpInstallUriPayload(uri.query);
888
+ if (!parsed) {
683
889
  return false;
684
890
  }
685
891
  try {
686
892
  const {
687
893
  name,
688
894
  inputs,
689
- ...config
895
+ config
690
896
  } = parsed;
691
897
  if (config.gallery && this.mcpGalleryService.isEnabled()) {
692
898
  try {
899
+ const registryGeneration = this.registryGeneration;
693
900
  const [galleryServer] = await this.mcpGalleryService.getMcpServersFromGallery([{
694
901
  name
695
902
  }]);
696
903
  if (galleryServer) {
904
+ this.rememberGallerySource(galleryServer, registryGeneration);
697
905
  const local = this.local.find(e => e.name === galleryServer.name) ?? this.instantiationService.createInstance(
698
906
  McpWorkbenchServer,
699
907
  e => this.getInstallState(e),
@@ -712,9 +920,6 @@ let McpWorkbenchService = class McpWorkbenchService extends Disposable {
712
920
  );
713
921
  }
714
922
  }
715
- if (config.type === undefined) {
716
- config.type = parsed.command ? McpServerType.LOCAL : McpServerType.REMOTE;
717
- }
718
923
  this.open(this.instantiationService.createInstance(
719
924
  McpWorkbenchServer,
720
925
  e => this.getInstallState(e),
@@ -753,6 +958,7 @@ let McpWorkbenchService = class McpWorkbenchService extends Disposable {
753
958
  }
754
959
  async handleMcpServerByName(name) {
755
960
  try {
961
+ const registryGeneration = this.registryGeneration;
756
962
  const [gallery] = await this.mcpGalleryService.getMcpServersFromGallery([{
757
963
  name
758
964
  }]);
@@ -760,6 +966,7 @@ let McpWorkbenchService = class McpWorkbenchService extends Disposable {
760
966
  this.logService.info(`MCP server '${name}' not found`);
761
967
  return true;
762
968
  }
969
+ this.rememberGallerySource(gallery, registryGeneration);
763
970
  const local = this.local.find(e => e.name === gallery.name) ?? this.instantiationService.createInstance(
764
971
  McpWorkbenchServer,
765
972
  e => this.getInstallState(e),
@@ -812,7 +1019,7 @@ let McpWorkbenchService = class McpWorkbenchService extends Disposable {
812
1019
  state: McpServerEnablementState.DisabledProfile,
813
1020
  message: {
814
1021
  severity: Severity.Info,
815
- text: ( new MarkdownString(( localize(12080, "This MCP server is disabled."))))
1022
+ text: ( new MarkdownString(( localize(14056, "This MCP server is disabled."))))
816
1023
  }
817
1024
  };
818
1025
  }
@@ -821,7 +1028,7 @@ let McpWorkbenchService = class McpWorkbenchService extends Disposable {
821
1028
  state: McpServerEnablementState.DisabledWorkspace,
822
1029
  message: {
823
1030
  severity: Severity.Info,
824
- text: ( new MarkdownString(( localize(12081, "This MCP server is disabled for this workspace."))))
1031
+ text: ( new MarkdownString(( localize(14057, "This MCP server is disabled for this workspace."))))
825
1032
  }
826
1033
  };
827
1034
  }
@@ -841,7 +1048,7 @@ let McpWorkbenchService = class McpWorkbenchService extends Disposable {
841
1048
  message: {
842
1049
  severity: Severity.Warning,
843
1050
  text: ( new MarkdownString(( localize(
844
- 12082,
1051
+ 14058,
845
1052
  "This MCP Server is disabled because MCP servers are configured to be disabled in the Editor. Please check your [settings]({0}).",
846
1053
  settingsCommandLink
847
1054
  ))))
@@ -855,7 +1062,7 @@ let McpWorkbenchService = class McpWorkbenchService extends Disposable {
855
1062
  message: {
856
1063
  severity: Severity.Warning,
857
1064
  text: ( new MarkdownString(( localize(
858
- 12083,
1065
+ 14059,
859
1066
  "This MCP Server is disabled because it is configured to be disabled in the Editor. Please check your [settings]({0}).",
860
1067
  settingsCommandLink
861
1068
  ))))
@@ -869,7 +1076,7 @@ let McpWorkbenchService = class McpWorkbenchService extends Disposable {
869
1076
  message: {
870
1077
  severity: Severity.Warning,
871
1078
  text: ( new MarkdownString(( localize(
872
- 12083,
1079
+ 14059,
873
1080
  "This MCP Server is disabled because it is configured to be disabled in the Editor. Please check your [settings]({0}).",
874
1081
  settingsCommandLink
875
1082
  ))))
@@ -29,7 +29,7 @@
29
29
  .mcp-welcome-title {
30
30
  font-size: 24px;
31
31
  margin-top: 5px;
32
- font-weight: 500;
32
+ font-weight: var(--vscode-fontWeight-semiBold);
33
33
  line-height: normal;
34
34
  }
35
35
 
@@ -13,7 +13,7 @@ import { IExtensionService } from '@codingame/monaco-vscode-api/vscode/vs/workbe
13
13
  import { ExtensionsRegistry } from '@codingame/monaco-vscode-api/vscode/vs/workbench/services/extensions/common/extensionsRegistry';
14
14
  import { mcpContributionPoint, mcpActivationEvent } from '@codingame/monaco-vscode-api/vscode/vs/workbench/contrib/mcp/common/mcpConfiguration';
15
15
  import { IMcpRegistry } from '@codingame/monaco-vscode-api/vscode/vs/workbench/contrib/mcp/common/mcpRegistryTypes.service';
16
- import { McpServerDefinition, extensionPrefixedIdentifier, McpCollectionSortOrder, McpServerTrust } from '@codingame/monaco-vscode-api/vscode/vs/workbench/contrib/mcp/common/mcpTypes';
16
+ import { McpServerDefinition, extensionPrefixedIdentifier, McpCollectionSortOrder, McpServerTrust, McpCollectionProvenance } from '@codingame/monaco-vscode-api/vscode/vs/workbench/contrib/mcp/common/mcpTypes';
17
17
  import { observableValue } from '@codingame/monaco-vscode-api/vscode/vs/base/common/observableInternal/observables/observableValue';
18
18
 
19
19
  var ExtensionMcpDiscovery_1;
@@ -97,6 +97,7 @@ let ExtensionMcpDiscovery = ExtensionMcpDiscovery_1 = class ExtensionMcpDiscover
97
97
  const serverDefs = this.cachedServers.hasOwnProperty(id) ? this.cachedServers[id].servers : undefined;
98
98
  const dispo = this._mcpRegistry.registerCollection({
99
99
  id,
100
+ provenance: McpCollectionProvenance.Extension,
100
101
  label: coll.label,
101
102
  remoteAuthority: null,
102
103
  trustBehavior: McpServerTrust.Kind.Trusted,
@@ -142,20 +143,20 @@ let ExtensionMcpDiscovery = ExtensionMcpDiscovery_1 = class ExtensionMcpDiscover
142
143
  }
143
144
  static _validate(user) {
144
145
  if (!Array.isArray(user.value)) {
145
- user.collector.error(( localize(12084, "Expected an array of MCP collections")));
146
+ user.collector.error(( localize(14060, "Expected an array of MCP collections")));
146
147
  return false;
147
148
  }
148
149
  for (const contribution of user.value) {
149
150
  if (typeof contribution.id !== "string" || isFalsyOrWhitespace(contribution.id)) {
150
- user.collector.error(( localize(12085, "Expected 'id' to be a non-empty string.")));
151
+ user.collector.error(( localize(14061, "Expected 'id' to be a non-empty string.")));
151
152
  return false;
152
153
  }
153
154
  if (typeof contribution.label !== "string" || isFalsyOrWhitespace(contribution.label)) {
154
- user.collector.error(( localize(12086, "Expected 'label' to be a non-empty string.")));
155
+ user.collector.error(( localize(14062, "Expected 'label' to be a non-empty string.")));
155
156
  return false;
156
157
  }
157
158
  if (contribution.when !== undefined && (typeof contribution.when !== "string" || isFalsyOrWhitespace(contribution.when))) {
158
- user.collector.error(( localize(12087, "Expected 'when' to be a non-empty string.")));
159
+ user.collector.error(( localize(14063, "Expected 'when' to be a non-empty string.")));
159
160
  return false;
160
161
  }
161
162
  }