mcpServer.ts ×62

Frontier kind: Code frontier

unlabeled · c_d0f306e47d93

74 tests · 35454 LOC · 164 files · introduces 0 tests · 748 LOC · 5 files

Introduces — evidence that enters the hierarchy at this concept

Code
91 ranges748 lines · 5 files
Tests
0 tests

Contains — complete concept membership

All code (extent)
2982 ranges35454 lines · 164 files · Browse complete extent
All tests (intent)
74 testsBrowse complete intent

Neighbourhood graph

The orange circle is the focus. Violet and green circles are every ancestor and descendant, broader and narrower, at any distance; blue squares and pink diamonds are the introduced files and exact introduced tests of every visible concept, not only the focus's. Arrows point from broader to narrower concepts and bridge only concepts omitted from this view. Undirected links show source or test introduction. Concept and file size follows LOC; exact test nodes use test-count units.

Introduced files, introduced tests, and structurally relevant concept specialization

In the embedded map, ordinary wheel input scrolls the page; use the visible controls to zoom and drag to pan. Open the full-screen map for canvas navigation: wheel pans, Ctrl/Command plus wheel zooms, and arrow keys pan when this region is focused. On touch screens, open the full-screen map to pan or pinch. If JavaScript or WebGL is unavailable, use the native relationship evidence on this page.

Graph controls are ready.

Interactive rendering requires JavaScript and WebGL. Use the native relationship evidence on this page while the interactive map is unavailable.

Native relationship evidence

Every exact file and test below is linked only from the concept that introduces it.

Introduced tests

Every collected test enters the hierarchy at exactly one concept.

No tests are introduced at this concept. Its intent tests are introduced by other concepts.

Introduced code

Every collected source range enters the hierarchy at exactly one concept.

5 files ranked by introduced lines: 748 introduced LOC across 91 ranges. Expand a file to inspect source; the > gutter marks introduced lines.

src/vs/workbench/contrib/mcp/common/mcpServer.ts 469 introduced LOC · 62 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- mcpServer.ts
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 { AsyncIterableProducer, raceCancellationError, Sequencer } from '../../../../base/common/async.js';
7 > import { CancellationToken, CancellationTokenSource } from '../../../../base/common/cancellation.js';
8 > import { Iterable } from '../../../../base/common/iterator.js';
9 > import * as json from '../../../../base/common/json.js';
10 > import { normalizeDriveLetter } from '../../../../base/common/labels.js';
11 > import { Disposable, DisposableStore, IDisposable, IReference, MutableDisposable, toDisposable } from '../../../../base/common/lifecycle.js';
12 > import { LRUCache } from '../../../../base/common/map.js';
13 > import { Schemas } from '../../../../base/common/network.js';
14 > import { mapValues } from '../../../../base/common/objects.js';
15 > import { autorun, autorunSelfDisposable, derived, derivedDisposable, disposableObservableValue, IDerivedReader, IObservable, IReader, ITransaction, observableFromEvent, ObservablePromise, observableValue, transaction } from '../../../../base/common/observable.js';
16 > import { basename } from '../../../../base/common/resources.js';
17 > import { URI } from '../../../../base/common/uri.js';
18 > import { createURITransformer } from '../../../../base/common/uriTransformer.js';
19 > import { generateUuid } from '../../../../base/common/uuid.js';
20 > import { localize } from '../../../../nls.js';
21 > import { ICommandService } from '../../../../platform/commands/common/commands.js';
22 > import { IDialogService } from '../../../../platform/dialogs/common/dialogs.js';
23 > import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js';
24 > import { IMcpServerIdentity } from '../../../../platform/mcp/common/allowedMcpServers.js';
25 > import { IAllowedMcpServersService } from '../../../../platform/mcp/common/mcpManagement.js';
26 > import { ILogger, ILoggerService } from '../../../../platform/log/common/log.js';
27 > import { INotificationService, IPromptChoice, Severity } from '../../../../platform/notification/common/notification.js';
28 > import { IOpenerService } from '../../../../platform/opener/common/opener.js';
29 > import { IStorageService, StorageScope, StorageTarget } from '../../../../platform/storage/common/storage.js';
30 > import { ITelemetryService } from '../../../../platform/telemetry/common/telemetry.js';
31 > import { IWorkspaceContextService } from '../../../../platform/workspace/common/workspace.js';
32 > import { ConfigurationResolverExpression } from '../../../services/configurationResolver/common/configurationResolverExpression.js';
33 > import { IEditorService } from '../../../services/editor/common/editorService.js';
34 > import { IWorkbenchEnvironmentService } from '../../../services/environment/common/environmentService.js';
35 > import { IExtensionService } from '../../../services/extensions/common/extensions.js';
36 > import { IOutputService } from '../../../services/output/common/output.js';
37 > import { chatSessionResourceToId } from '../../chat/common/model/chatUri.js';
38 > import { ToolProgress } from '../../chat/common/tools/languageModelToolsService.js';
39 > import { mcpActivationEvent } from './mcpConfiguration.js';
40 > import { McpDevModeServerAttache } from './mcpDevMode.js';
41 > import { McpIcons, parseAndValidateMcpIcon, StoredMcpIcons } from './mcpIcons.js';
42 > import { IMcpRegistry } from './mcpRegistryTypes.js';
43 > import { IMcpSandboxService } from './mcpSandboxService.js';
44 > import { McpServerRequestHandler } from './mcpServerRequestHandler.js';
45 > import { McpTaskManager } from './mcpTaskManager.js';
46 > import { ElicitationKind, extensionMcpCollectionPrefix, IMcpElicitationService, IMcpIcons, IMcpPotentialSandboxBlock, IMcpPrompt, IMcpPromptMessage, IMcpResource, IMcpResourceTemplate, IMcpSamplingService, IMcpServer, IMcpServerConnection, IMcpServerStartOpts, IMcpTool, IMcpToolCallContext, McpCapability, McpCollectionDefinition, McpCollectionReference, McpConnectionFailedError, McpConnectionState, McpDefinitionReference, mcpPromptReplaceSpecialChars, McpResourceURI, McpServerCacheState, McpServerDefinition, McpServerLaunch, McpServerStaticToolAvailability, McpServerTransportType, McpToolName, McpToolVisibility, MpcResponseError, UserInteractionRequiredError } from './mcpTypes.js';
47 > import { ContributionEnablementState, IEnablementModel } from '../../chat/common/enablement.js';
48 > import { MCP } from './modelContextProtocol.js';
49 > import { McpApps } from './modelContextProtocolApps.js';
50 > import { UriTemplate } from '../../../../base/common/uriTemplate.js';
51 >
52 > type ServerBootData = {
53 > supportsLogging: boolean;
54 > supportsPrompts: boolean;
55 > supportsResources: boolean;
56 > toolCount: number;
57 > serverName: string;
58 > serverVersion: string;
59 > };
60 > type ServerBootClassification = {
61 > owner: 'connor4312';
62 > comment: 'Details the capabilities of the MCP server';
63 > supportsLogging: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; isMeasurement: true; comment: 'Whether the server supports logging' };
64 > supportsPrompts: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; isMeasurement: true; comment: 'Whether the server supports prompts' };
65 > supportsResources: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; isMeasurement: true; comment: 'Whether the server supports resource' };
66 > toolCount: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; isMeasurement: true; comment: 'The number of tools the server advertises' };
67 > serverName: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The name of the MCP server' };
68 > serverVersion: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The version of the MCP server' };
69 > };
70 >
71 > type ElicitationTelemetryData = {
72 > serverName: string;
73 > serverVersion: string;
74 > };
75 >
76 > type ElicitationTelemetryClassification = {
77 > owner: 'connor4312';
78 > comment: 'Triggered when elictation is requested';
79 > serverName: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The name of the MCP server' };
80 > serverVersion: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The version of the MCP server' };
81 > };
82 >
83 > export type McpServerInstallData = {
84 > serverName: string;
85 > source: 'gallery' | 'local';
86 > scope: string;
87 > success: boolean;
88 > error?: string;
89 > hasInputs: boolean;
90 > };
91 >
92 > export type McpServerInstallClassification = {
93 > owner: 'connor4312';
94 > comment: 'MCP server installation event tracking';
95 > serverName: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The name of the MCP server being installed' };
96 > source: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'Installation source (gallery or local)' };
97 > scope: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'Installation scope (user, workspace, etc.)' };
98 > success: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; isMeasurement: true; comment: 'Whether installation succeeded' };
99 > error?: { classification: 'CallstackOrException'; purpose: 'FeatureInsight'; comment: 'Error message if installation failed' };
100 > hasInputs: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; isMeasurement: true; comment: 'Whether the server requires input configuration' };
101 > };
102 >
103 > type ServerBootState = {
104 > state: string;
105 > time: number;
106 > };
107 > type ServerBootStateClassification = {
108 > owner: 'connor4312';
109 > comment: 'Details the capabilities of the MCP server';
110 > state: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The server outcome' };
111 > time: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; isMeasurement: true; comment: 'Duration in milliseconds to reach that state' };
112 > };
113 >
114 > type StoredMcpPrompt = MCP.Prompt & { _icons: StoredMcpIcons };
115 >
116 > interface IToolCacheEntry {
117 > readonly serverName: string | undefined;
118 > readonly serverInstructions: string | undefined;
119 > readonly serverIcons: StoredMcpIcons;
120 >
121 > readonly trustedAtNonce: string | undefined;
122 >
123 > readonly nonce: string | undefined;
124 > /** Cached tools so we can show what's available before it's started */
125 > readonly tools: readonly ValidatedMcpTool[];
126 > /** Cached prompts */
127 > readonly prompts: readonly StoredMcpPrompt[] | undefined;
128 > /** Cached capabilities */
129 > readonly capabilities: McpCapability | undefined;
130 > }
131 >
132 > const emptyToolEntry: IToolCacheEntry = {
133 > serverName: undefined,
134 > serverIcons: [],
135 > serverInstructions: undefined,
136 > trustedAtNonce: undefined,
137 > nonce: undefined,
138 > tools: [],
139 > prompts: undefined,
140 > capabilities: undefined,
141 > };
142 >
143 > interface IServerCacheEntry {
144 > readonly servers: readonly McpServerDefinition.Serialized[];
145 > }
146 >
147 > const toolInvalidCharRe = /[^a-z0-9_-]/gi;
148 >
149 > export class McpServerMetadataCache extends Disposable {
150 > private didChange = false;
151 > private readonly cache = new LRUCache<string, IToolCacheEntry>(128);
152 > private readonly extensionServers = new Map</* collection ID */string, IServerCacheEntry>();
153 >
154 > constructor(
155 scope: StorageScope,
156 @IStorageService storageService: IStorageService,
182 }
183 }
184 > mcpServer.ts
185 > /** Resets the cache for primitives and extension servers */
186 > reset() {
187 this.cache.clear();
188 this.extensionServers.clear();
189 this.didChange = true;
190 }
191 > mcpServer.ts
192 > /** Gets cached primitives for a server (used before a server is running) */
193 > get(definitionId: string) {
194 return this.cache.get(definitionId);
195 }
196 > mcpServer.ts
197 > /** Sets cached primitives for a server */
198 > store(definitionId: string, entry: Partial<IToolCacheEntry>): void {
199 const prev = this.get(definitionId) || emptyToolEntry;
200 this.cache.set(definitionId, { ...prev, ...entry });
201 this.didChange = true;
202 }
203 > mcpServer.ts
204 > /** Gets cached servers for a collection (used for extensions, before the extension activates) */
205 > getServers(collectionId: string) {
206 return this.extensionServers.get(collectionId);
207 }
208 > mcpServer.ts
209 > /** Sets cached servers for a collection */
210 > storeServers(collectionId: string, entry: IServerCacheEntry | undefined): void {
211 if (entry) {
212 this.extensionServers.set(collectionId, entry);
216 this.didChange = true;
217 }
218 > } mcpServer.ts
219 >
220 > /**
221 > * Shared across all {@link McpServer}s. Each server `take`s the name it wants
222 > * to base its tool prefix on (announced `serverInfo.title`/`name` when known,
223 > * otherwise the mcp.json key) and gets back a stable, collision-resolved prefix
224 > * observable. When a server's preferred name changes (e.g. after the live
225 > * `serverInfo` arrives), it simply takes again and disposes the previous
226 > * reference; other servers that share the name keep the suffix they were
227 > * already assigned. See #299749.
228 > */
229 > export class McpPrefixGenerator {
230 private readonly _buckets = new Map<string, { usedIndexes: Set<number>; size: number }>();
231 > mcpServer.ts
232 > take(name: string): IReference<string> {
233 const safeName = name.toLowerCase().replace(/[^a-z0-9_.-]+/g, '_').slice(0, McpToolName.MaxPrefixLen - McpToolName.Prefix.length - 1);
234 let bucket = this._buckets.get(safeName);
263 };
264 }
265 > } mcpServer.ts
266 >
267 > type ValidatedMcpTool = MCP.Tool & {
268 > _icons: StoredMcpIcons;
269 >
270 > /**
271 > * Tool name as published by the MCP server. This may
272 > * be different than the one in {@link definition} due to name normalization
273 > * in {@link McpServer._getValidatedTools}.
274 > */
275 > serverToolName: string;
276 >
277 > /**
278 > * Visibility of the tool, parsed from `_meta.ui.visibility`.
279 > * Defaults to Model | App if not specified.
280 > */
281 > visibility: McpToolVisibility;
282 >
283 > /**
284 > * UI resource URI if this tool has an associated MCP App UI.
285 > * Parsed from `_meta.ui.resourceUri`.
286 > */
287 > uiResourceUri?: string;
288 > };
289 >
290 > interface StoredServerMetadata {
291 > readonly serverName: string | undefined;
292 > readonly serverInstructions: string | undefined;
293 > readonly serverIcons: StoredMcpIcons | undefined;
294 > }
295 >
296 > interface ServerMetadata {
297 > readonly serverName: string | undefined;
298 > readonly serverInstructions: string | undefined;
299 > readonly icons: IMcpIcons;
300 > }
301 >
302 > class CachedPrimitive<T, C> {
303 > /**
304 > * @param _definitionId Server definition ID
305 > * @param _cache Metadata cache instance
306 > * @param _fromStaticDefinition Static definition that came with the server.
307 > * This should ONLY have a value if it should be used instead of whatever
308 > * is currently in the cache.
309 > * @param _fromCache Pull the value from the cache entry.
310 > * @param _toT Transform the value to the observable type.
311 > * @param defaultValue Default value if no cache entry.
312 > */
313 > constructor(
314 private readonly _definitionId: string,
315 private readonly _cache: McpServerMetadataCache,
341 return this._toT(definitions, reader);
342 });
343 > } mcpServer.ts
344 >
345 > export class McpServer extends Disposable implements IMcpServer {
346 > /** Shared task manager that survives reconnections */
347 > private readonly _taskManager = this._register(new McpTaskManager());
348 >
349 > /**
350 > * Helper function to call the function on the handler once it's online. The
351 > * connection started if it is not already.
352 > */
353 > public static async callOn<R>(server: IMcpServer, fn: (handler: McpServerRequestHandler, connection: IMcpServerConnection) => Promise<R>, token: CancellationToken = CancellationToken.None): Promise<R> {
354 > await server.start({ promptType: 'all-untrusted' }); // idempotent
355 >
356 > let ranOnce = false;
357 > let d: IDisposable;
358 >
359 > const callPromise = new Promise<R>((resolve, reject) => {
360 >
361 > d = autorun(reader => {
362 > if (ranOnce) {
363 return;
364 }
365 > mcpServer.ts
366 > const connection = server.connection.read(reader);
367 > if (!connection) {
368 // No live connection: the server may be blocked by policy (its connection is torn
369 // down while blocked) or stopped. Surface the terminal state instead of waiting forever.
376 return;
377 }
378 > mcpServer.ts
379 > const handler = connection.handler.read(reader);
380 > if (!handler) {
381 > const state = connection.state.read(reader);
382 > if (state.state === McpConnectionState.Kind.Error) {
383 reject(new McpConnectionFailedError(`MCP server could not be started: ${state.message}`));
384 return;
385 > } else if (state.state === McpConnectionState.Kind.Stopped) { mcpServer.ts
386 reject(new McpConnectionFailedError('MCP server has stopped'));
387 return;
388 > } else { mcpServer.ts
389 > // keep waiting for handler
390 > return;
391 > }
392 > }
393 >
394 > resolve(fn(handler, connection));
395 > ranOnce = true; // aggressive prevent multiple racey calls, don't dispose because autorun is sync
396 > });
397 > });
398 >
399 > return raceCancellationError(callPromise, token).finally(() => d.dispose());
400 > }
401 >
402 > public readonly collection: McpCollectionReference;
403 > private readonly _connectionSequencer = new Sequencer();
404 > private readonly _connection = this._register(disposableObservableValue<IMcpServerConnection | undefined>(this, undefined));
405 >
406 > public readonly connection = this._connection;
407 >
408 > /**
409 > * Reactively evaluates the `chat.mcp.allowedServers` / `chat.mcp.deniedServers` policy against
410 > * this server's identity. Holds an error state while blocked, `undefined` while allowed.
411 > *
412 > * Being a derived, it recomputes whenever the policy changes (via {@link _policyEpoch}), the
413 > * server definition changes, or a connection resolves — so it always evaluates the *resolved*
414 > * launch of a live connection and falls back to the definition otherwise. This also means a
415 > * blocked server surfaces the block at rest (before any start), which hides its cached tools
416 > * and prompts and lets the UI show the reason.
417 > *
418 > * Initialized in the constructor because it depends on the injected allowed-servers service.
419 > */
420 > private readonly _policyEpoch: IObservable<void>;
421 > private readonly _policyBlock: IObservable<McpConnectionState.Error | undefined>;
422 > public readonly connectionState: IObservable<McpConnectionState> = derived(reader => this._policyBlock.read(reader) ?? this._connection.read(reader)?.state.read(reader) ?? { state: McpConnectionState.Kind.Stopped });
423 >
424 >
425 > private readonly _capabilities: CachedPrimitive<number | undefined, number | undefined>;
426 > public get capabilities() {
427 return this._capabilities.value;
428 }
429 > mcpServer.ts
430 > private readonly _tools: CachedPrimitive<readonly IMcpTool[], readonly ValidatedMcpTool[]>;
431 > /** Cached tools are suppressed while the server is blocked by policy so they cannot be listed, referenced, or executed. */
432 > private readonly _gatedTools: IObservable<readonly IMcpTool[]> = derived(reader => this._policyBlock.read(reader) ? [] : this._tools.value.read(reader));
433 > public get tools() {
434 return this._gatedTools;
435 }
436 > mcpServer.ts
437 > private readonly _prompts: CachedPrimitive<readonly IMcpPrompt[], readonly StoredMcpPrompt[]>;
438 > /** Cached prompts are suppressed while the server is blocked by policy. */
439 > private readonly _gatedPrompts: IObservable<readonly IMcpPrompt[]> = derived(reader => this._policyBlock.read(reader) ? [] : this._prompts.value.read(reader));
440 > public get prompts() {
441 return this._gatedPrompts;
442 }
443 > mcpServer.ts
444 > private readonly _serverMetadata: CachedPrimitive<ServerMetadata, StoredServerMetadata | undefined>;
445 > public get serverMetadata() {
446 return this._serverMetadata.value;
447 }
448 > mcpServer.ts
449 > public get trustedAtNonce() {
450 return this._primitiveCache.get(this.definition.id)?.trustedAtNonce;
451 }
452 > mcpServer.ts
453 > public set trustedAtNonce(nonce: string | undefined) {
454 this._primitiveCache.store(this.definition.id, { trustedAtNonce: nonce });
455 }
456 > mcpServer.ts
457 > private readonly _fullDefinitions: IObservable<{
458 > server: McpServerDefinition | undefined;
459 > collection: McpCollectionDefinition | undefined;
460 > }>;
461 >
462 > public readonly cacheState = derived(reader => {
463 const currentNonce = () => this._fullDefinitions.read(reader)?.server?.cacheNonce;
464 const stateWhenServingFromCache = () => {
491
492 return fromServerResult.data?.nonce === currentNonce() ? McpServerCacheState.Live : McpServerCacheState.Outdated;
493 > }); mcpServer.ts
494 >
495 > public get logger(): ILogger {
496 return this._logger;
497 }
498 > mcpServer.ts
499 > private readonly _loggerId: string;
500 > private readonly _logger: ILogger;
501 > private _lastModeDebugged = false;
502 > private _isQuietStart = false;
503 > private _isSandboxSuggestionDialogVisible = false;
504 > private _potentialSandboxBlocks: IMcpPotentialSandboxBlock[] = [];
505 > private _potentialSandboxBlockListener = this._register(new MutableDisposable<IDisposable>());
506 > /** Count of running tool calls, used to detect if sampling is during an LM call */
507 > public runningToolCalls = new Set<IMcpToolCallContext>();
508 >
509 > public readonly enablement: IObservable<ContributionEnablementState>;
510 >
511 > constructor(
512 initialCollection: McpCollectionDefinition,
513 public readonly definition: McpDefinitionReference,
689 prefixRef.recomputeInitiallyAndOnChange(this._store);
690 }
691 > mcpServer.ts
692 > public readDefinitions(): IObservable<{ server: McpServerDefinition | undefined; collection: McpCollectionDefinition | undefined }> {
693 return this._fullDefinitions;
694 }
695 > mcpServer.ts
696 > public showOutput(preserveFocus?: boolean) {
697 this._loggerService.setVisibility(this._loggerId, true);
698 return this._outputService.showChannel(this._loggerId, preserveFocus);
699 }
700 > mcpServer.ts
701 > public resources(token?: CancellationToken): AsyncIterable<IMcpResource[]> {
702 const cts = new CancellationTokenSource(token);
703 return new AsyncIterableProducer<IMcpResource[]>(async emitter => {
712 }, () => cts.dispose(true));
713 }
714 > mcpServer.ts
715 > public resourceTemplates(token?: CancellationToken): Promise<IMcpResourceTemplate[]> {
716 return McpServer.callOn(this, async (handler) => {
717 const templates = await handler.listResourceTemplates({}, token);
719 }, token);
720 }
721 > mcpServer.ts
722 > private _identityFromLaunch(launch: McpServerLaunch | undefined): IMcpServerIdentity {
723 if (launch?.type === McpServerTransportType.HTTP) {
724 return { name: this.definition.label, url: launch.uri.toString(true) };
740 return { name: this.definition.label };
741 }
742 > mcpServer.ts
743 > private _evaluatePolicy(identity: IMcpServerIdentity): McpConnectionState.Error | undefined {
744 const allowed = this._allowedMcpServersService.isServerAllowed(identity);
745 return allowed === true ? undefined : { state: McpConnectionState.Kind.Error, message: allowed.value };
746 }
747 > mcpServer.ts
748 > /**
749 > * Whether the URL/command fields matched by the policy still contain unresolved `${...}`
750 > * configuration variables. When they do, matching against allow/deny URL or command rules is
751 > * unreliable, so the block is deferred until the launch is resolved. The server name is used
752 > * verbatim and is not considered here.
753 > */
754 > private static _hasUnresolvedVariables(identity: IMcpServerIdentity): boolean {
755 const variableMarker = ConfigurationResolverExpression.VARIABLE_LHS;
756 return !!identity.url?.includes(variableMarker) || !!identity.command?.some(arg => arg.includes(variableMarker));
757 }
758 > mcpServer.ts
759 > public start({ interaction, autoTrustChanges, promptType, debug, errorOnUserInteraction }: IMcpServerStartOpts = {}): Promise<McpConnectionState> {
760 interaction?.participants.set(this.definition.id, { s: 'unknown' });
761
907 });
908 }
909 > mcpServer.ts
910 > private showInteractiveError(cnx: IMcpServerConnection, error: McpConnectionState.Error, debug?: boolean) {
911 if (cnx.definition.sandboxEnabled) {
912 if (!this.showSandboxConfigSuggestionFromPotentialBlocks(cnx, this._potentialSandboxBlocks)) {
957 }
958 }
959 > mcpServer.ts
960 > public showSandboxConfigSuggestionFromPotentialBlocks(cnx: IMcpServerConnection, potentialBlocks: readonly IMcpPotentialSandboxBlock[]): boolean {
961 if (!cnx.definition.sandboxEnabled || !potentialBlocks.length || this._isSandboxSuggestionDialogVisible) {
962 return false;
977 return true;
978 }
979 > mcpServer.ts
980 > private _confirmAndApplySandboxConfigSuggestion(cnx: IMcpServerConnection, potentialBlocks: readonly IMcpPotentialSandboxBlock[], suggestion: NonNullable<ReturnType<IMcpSandboxService['getSandboxConfigSuggestionMessage']>>): void {
981 const mcpResource = cnx.definition.presentation?.origin?.uri ?? this.collection.presentation?.origin;
982 const configTarget = this._fullDefinitions.get().collection?.configTarget;
1012 });
1013 }
1014 > mcpServer.ts
1015 > public recordPotentialSandboxBlock(block: IMcpPotentialSandboxBlock): void {
1016 this._potentialSandboxBlocks.push(block);
1017 if (this._potentialSandboxBlocks.length > 200) {
1024 }
1025 }
1026 > mcpServer.ts
1027 > private _removePotentialSandboxBlocks(blocks: readonly IMcpPotentialSandboxBlock[]): void {
1028 if (!blocks.length || !this._potentialSandboxBlocks.length) {
1029 return;
1033 this._potentialSandboxBlocks = this._potentialSandboxBlocks.filter(block => !toRemove.has(block));
1034 }
1035 > mcpServer.ts
1036 > public stop(): Promise<void> {
1037 return this._connection.get()?.stop() || Promise.resolve();
1038 }
1039 > mcpServer.ts
1040 > /** Waits for any ongoing tools to be refreshed before resolving. */
1041 > public awaitToolRefresh() {
1042 return new Promise<void>(resolve => {
1043 autorunSelfDisposable(reader => {
1050 });
1051 }
1052 > mcpServer.ts
1053 > private resetLiveData() {
1054 transaction(tx => {
1055 this._tools.fromServerPromise.set(undefined, tx);
1057 });
1058 }
1059 > mcpServer.ts
1060 > private async _normalizeTool(originalTool: MCP.Tool): Promise<ValidatedMcpTool | { error: string[] }> {
1061 // Parse MCP Apps UI metadata from _meta.ui
1062 const uiMeta = originalTool._meta?.ui as McpApps.McpUiToolMeta | undefined;
1125 return { error: messages };
1126 }
1127 > mcpServer.ts
1128 > private async _getValidatedTools(tools: MCP.Tool[]): Promise<ValidatedMcpTool[]> {
1129 let error = '';
1130
1150 return validated;
1151 }
1152 > mcpServer.ts
1153 > /**
1154 > * Parses incoming MCP icons and returns the resulting 'stored' record. Note
1155 > * that this requires an active MCP server connection since we validate
1156 > * against some of that connection's data. The icons may however be stored
1157 > * and rehydrated later.
1158 > */
1159 > private _parseIcons(icons: MCP.Icons) {
1160 const cnx = this._connection.get();
1161 if (!cnx) {
1165 return parseAndValidateMcpIcon(icons, cnx.launchDefinition, this._logger);
1166 }
1167 > mcpServer.ts
1168 > private _setServerTools(nonce: string | undefined, toolsPromise: Promise<MCP.Tool[]>, tx: ITransaction | undefined) {
1169 const toolPromiseSafe = toolsPromise.then(async tools => {
1170 this._logger.info(`Discovered ${tools.length} tools`);
1176 return toolPromiseSafe;
1177 }
1178 > mcpServer.ts
1179 > private _setServerPrompts(nonce: string | undefined, promptsPromise: Promise<MCP.Prompt[]>, tx: ITransaction | undefined) {
1180 const promptsPromiseSafe = promptsPromise.then((result): { data: StoredMcpPrompt[]; nonce: string | undefined } => {
1181 const data: StoredMcpPrompt[] = result.map(prompt => ({
1190 return promptsPromiseSafe;
1191 }
1192 > mcpServer.ts
1193 > private _toStoredMetadata(serverInfo?: MCP.Implementation, instructions?: string): StoredServerMetadata {
1194 return {
1195 serverName: serverInfo ? serverInfo.title || serverInfo.name : undefined,
1198 };
1199 }
1200 > mcpServer.ts
1201 > private _setServerMetadata(
1202 nonce: string | undefined,
1203 { serverInfo, instructions, capabilities }: { serverInfo: MCP.Implementation; instructions: string | undefined; capabilities: MCP.ServerCapabilities },
1211 this._primitiveCache.store(this.definition.id, { ...serverMetadata, nonce, capabilities: capabilitiesEncoded });
1212 }
1213 > mcpServer.ts
1214 > private _populateLiveData(handler: McpServerRequestHandler, cacheNonce: string | undefined, store: DisposableStore) {
1215 const cts = new CancellationTokenSource();
1216 store.add(toDisposable(() => cts.dispose(true)));
1253 });
1254 }
1255 > } mcpServer.ts
1256 >
1257 > class McpPrompt implements IMcpPrompt {
1258 > readonly id: string;
1259 > readonly name: string;
1260 > readonly description?: string;
1261 > readonly title?: string;
1262 > readonly arguments: readonly MCP.PromptArgument[];
1263 > readonly icons: IMcpIcons;
1264 >
1265 > constructor(
1266 private readonly _server: McpServer,
1267 private readonly _definition: StoredMcpPrompt,
1274 this.icons = McpIcons.fromStored(this._definition._icons);
1275 }
1276 > mcpServer.ts
1277 > async resolve(args: Record<string, string>, token?: CancellationToken): Promise<IMcpPromptMessage[]> {
1278 const result = await McpServer.callOn(this._server, h => h.getPrompt({ name: this._definition.name, arguments: args }, token), token);
1279 return result.messages;
1280 }
1281 > mcpServer.ts
1282 > async complete(argument: string, prefix: string, alreadyResolved: Record<string, string>, token?: CancellationToken): Promise<string[]> {
1283 const result = await McpServer.callOn(this._server, h => h.complete({
1284 ref: { type: 'ref/prompt', name: this._definition.name },
1288 return result.completion.values;
1289 }
1290 > } mcpServer.ts
1291 >
1292 function encodeCapabilities(cap: MCP.ServerCapabilities): McpCapability {
1293 let out = 0;
1317 return out;
1318 }
1319 > mcpServer.ts
1320 > export class McpTool implements IMcpTool {
1321 >
1322 > readonly id: string;
1323 > readonly referenceName: string;
1324 > readonly icons: IMcpIcons;
1325 > readonly visibility: McpToolVisibility;
1326 >
1327 > public get definition(): MCP.Tool { return this._definition; }
1328 > public get uiResourceUri(): string | undefined { return this._definition.uiResourceUri; }
1329 >
1330 > constructor(
1331 private readonly _server: McpServer,
1332 idPrefix: string,
1339 this.visibility = _definition.visibility ?? (McpToolVisibility.Model | McpToolVisibility.App);
1340 }
1341 > mcpServer.ts
1342 > async call(params: Record<string, unknown>, context?: IMcpToolCallContext, token?: CancellationToken): Promise<MCP.CallToolResult> {
1343 if (context) { this._server.runningToolCalls.add(context); }
1344 try {
1348 }
1349 }
1350 > mcpServer.ts
1351 > async callWithProgress(params: Record<string, unknown>, progress: ToolProgress, context?: IMcpToolCallContext, token?: CancellationToken): Promise<MCP.CallToolResult> {
1352 if (context) { this._server.runningToolCalls.add(context); }
1353 try {
1357 }
1358 }
1359 > mcpServer.ts
1360 > _callWithProgress(params: Record<string, unknown>, progress: ToolProgress | undefined, context?: IMcpToolCallContext, token = CancellationToken.None, allowRetry = true): Promise<MCP.CallToolResult> {
1361 // serverToolName is always set now, but older cache entries (from 1.99-Insiders) may not have it.
1362 const name = this._definition.serverToolName ?? this._definition.name;
1426 }, token);
1427 }
1428 > mcpServer.ts
1429 > private async _handleElicitationErr(err: MpcResponseError, context: IMcpToolCallContext | undefined, token: CancellationToken) {
1430 const elicitations = (err.data as MCP.URLElicitationRequiredError['error']['data'])?.elicitations;
1431 if (Array.isArray(elicitations) && elicitations.length > 0) {
1447 }
1448 }
1449 > mcpServer.ts
1450 > compare(other: IMcpTool): number {
1451 return this._definition.name.localeCompare(other.definition.name);
1452 }
1453 > } mcpServer.ts
1454 >
1455 function warnInvalidTools(instaService: IInstantiationService, serverName: string, errorText: string) {
1456 instaService.invokeFunction((accessor) => {
1478 });
1479 }
1480 > mcpServer.ts
1481 > class McpResource implements IMcpResource {
1482 > readonly uri: URI;
1483 > readonly mcpUri: string;
1484 > readonly name: string;
1485 > readonly description: string | undefined;
1486 > readonly mimeType: string | undefined;
1487 > readonly sizeInBytes: number | undefined;
1488 > readonly title: string | undefined;
1489 >
1490 > constructor(
1491 server: McpServer,
1492 original: MCP.Resource,
1501 this.sizeInBytes = original.size;
1502 }
1503 > } mcpServer.ts
1504 >
1505 > class McpResourceTemplate implements IMcpResourceTemplate {
1506 > readonly name: string;
1507 > readonly title?: string | undefined;
1508 > readonly description?: string;
1509 > readonly mimeType?: string;
1510 > readonly template: UriTemplate;
1511 >
1512 > constructor(
1513 private readonly _server: McpServer,
1514 private readonly _definition: MCP.ResourceTemplate,
1521 this.template = UriTemplate.parse(_definition.uriTemplate);
1522 }
1523 > mcpServer.ts
1524 > public resolveURI(vars: Record<string, unknown>): URI {
1525 const serverUri = this.template.resolve(vars);
1526 return McpResourceURI.fromServer(this._server.definition, serverUri);
1527 }
1528 > mcpServer.ts
1529 > async complete(templatePart: string, prefix: string, alreadyResolved: Record<string, string | string[]>, token?: CancellationToken): Promise<string[]> {
1530 const result = await McpServer.callOn(this._server, h => h.complete({
1531 ref: { type: 'ref/resource', uri: this._definition.uriTemplate },
1537 return result.completion.values;
1538 }
1539 > } mcpServer.ts
src/vs/workbench/contrib/mcp/common/mcpSandboxService.ts 114 introduced LOC · 20 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- mcpSandboxService.ts
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 { VSBuffer } from '../../../../base/common/buffer.js';
7 > import { Disposable } from '../../../../base/common/lifecycle.js';
8 > import { FileAccess } from '../../../../base/common/network.js';
9 > import { dirname, posix, win32 } from '../../../../base/common/path.js';
10 > import { OperatingSystem, OS } from '../../../../base/common/platform.js';
11 > import { arch } from '../../../../base/common/process.js';
12 > import { URI } from '../../../../base/common/uri.js';
13 > import { generateUuid } from '../../../../base/common/uuid.js';
14 > import { localize } from '../../../../nls.js';
15 > import { ConfigurationTarget, ConfigurationTargetToString } from '../../../../platform/configuration/common/configuration.js';
16 > import { IEnvironmentService } from '../../../../platform/environment/common/environment.js';
17 > import { IFileService } from '../../../../platform/files/common/files.js';
18 > import { createDecorator } from '../../../../platform/instantiation/common/instantiation.js';
19 > import { ILogService } from '../../../../platform/log/common/log.js';
20 > import { IMcpResourceScannerService, McpResourceTarget } from '../../../../platform/mcp/common/mcpResourceScannerService.js';
21 > import { IRemoteAgentEnvironment } from '../../../../platform/remote/common/remoteAgentEnvironment.js';
22 > import { IRemoteAgentService } from '../../../services/remote/common/remoteAgentService.js';
23 > import { IMcpSandboxConfiguration } from '../../../../platform/mcp/common/mcpPlatformTypes.js';
24 > import { IMcpPotentialSandboxBlock, McpServerDefinition, McpServerLaunch, McpServerTransportStdio, McpServerTransportType } from './mcpTypes.js';
25 >
26 >
27 > export const IMcpSandboxService = createDecorator<IMcpSandboxService>('mcpSandboxService');
28 >
29 > export interface IMcpSandboxService {
30 > readonly _serviceBrand: undefined;
31 > launchInSandboxIfEnabled(serverDef: McpServerDefinition, launch: McpServerLaunch, remoteAuthority: string | undefined, configTarget: ConfigurationTarget): Promise<McpServerLaunch>;
32 > isEnabled(serverDef: McpServerDefinition, serverLabel?: string): Promise<boolean>;
33 > getSandboxConfigSuggestionMessage(serverLabel: string, potentialBlocks: readonly IMcpPotentialSandboxBlock[], existingSandboxConfig?: IMcpSandboxConfiguration): SandboxConfigSuggestionResult | undefined;
34 > applySandboxConfigSuggestion(serverDef: McpServerDefinition, mcpResource: URI, configTarget: ConfigurationTarget, potentialBlocks: readonly IMcpPotentialSandboxBlock[], suggestedSandboxConfig?: IMcpSandboxConfiguration): Promise<boolean>;
35 > }
36 >
37 > type SandboxConfigSuggestions = {
38 > allowWrite: readonly string[];
39 > allowedDomains: readonly string[];
40 > };
41 >
42 > type SandboxConfigSuggestionResult = {
43 > message: string;
44 > sandboxConfig: IMcpSandboxConfiguration;
45 > };
46 >
47 > type SandboxLaunchDetails = {
48 > execPath: string | undefined;
49 > srtPath: string | undefined;
50 > rgPath: string | undefined;
51 > sandboxConfigPath: string | undefined;
52 > tempDir: URI | undefined;
53 > };
54 >
55 > export class McpSandboxService extends Disposable implements IMcpSandboxService {
56 > readonly _serviceBrand: undefined;
57 >
58 > private _sandboxSettingsId: string | undefined;
59 > private _remoteEnvDetailsPromise: Promise<IRemoteAgentEnvironment | null>;
60 > private readonly _defaultAllowedDomains: readonly string[] = ['registry.npmjs.org']; // Default allowed domains that are commonly needed for MCP servers, even if the user doesn't specify them in their sandbox config
61 > private _defaultAllowWritePaths: string[] = ['~/.npm'];
62 > private _sandboxConfigPerConfigurationTarget: Map<string, string> = new Map();
63 >
64 > constructor(
65 @IFileService private readonly _fileService: IFileService,
66 @IEnvironmentService private readonly _environmentService: IEnvironmentService,
74
75 }
77 > public async isEnabled(serverDef: McpServerDefinition, remoteAuthority?: string): Promise<boolean> {
78 const os = await this._getOperatingSystem(remoteAuthority);
79 if (os === OperatingSystem.Windows) {
82 return !!serverDef.sandboxEnabled;
83 }
85 > public async launchInSandboxIfEnabled(serverDef: McpServerDefinition, launch: McpServerLaunch, remoteAuthority: string | undefined, configTarget: ConfigurationTarget): Promise<McpServerLaunch> {
86 if (launch.type !== McpServerTransportType.Stdio) {
87 return launch;
120 return launch;
121 }
123 > public getSandboxConfigSuggestionMessage(serverLabel: string, potentialBlocks: readonly IMcpPotentialSandboxBlock[], existingSandboxConfig?: IMcpSandboxConfiguration): SandboxConfigSuggestionResult | undefined {
124 const suggestions = this._getSandboxConfigSuggestions(potentialBlocks, existingSandboxConfig);
125 if (!suggestions) {
159 };
160 }
162 > public async applySandboxConfigSuggestion(serverDef: McpServerDefinition, mcpResource: URI, configTarget: ConfigurationTarget, potentialBlocks: readonly IMcpPotentialSandboxBlock[], suggestedSandboxConfig?: IMcpSandboxConfiguration): Promise<boolean> {
163 const scanTarget = this._toMcpResourceTarget(configTarget);
164 let didChange = false;
209 return didChange;
210 }
212 > private _getSandboxConfigSuggestions(potentialBlocks: readonly IMcpPotentialSandboxBlock[], existingSandboxConfig?: IMcpSandboxConfiguration): SandboxConfigSuggestions | undefined {
213 if (!potentialBlocks.length) {
214 return undefined;
239 };
240 }
242 > private _toMcpResourceTarget(configTarget: ConfigurationTarget): McpResourceTarget {
243 switch (configTarget) {
244 case ConfigurationTarget.USER:
254 }
255 }
257 > private async _resolveSandboxLaunchDetails(configTarget: ConfigurationTarget, remoteAuthority?: string, sandboxConfig?: IMcpSandboxConfiguration, launchCwd?: string): Promise<SandboxLaunchDetails> {
258 const os = await this._getOperatingSystem(remoteAuthority);
259 if (os === OperatingSystem.Windows) {
273 return { execPath, srtPath, rgPath, sandboxConfigPath, tempDir };
274 }
276 > private async _getExecPath(os: OperatingSystem, appRoot: string, remoteAuthority?: string): Promise<string | undefined> {
277 if (remoteAuthority) {
278 return this._pathJoin(os, appRoot, 'node');
280 return undefined; // Use Electron executable as the default exec path for local development, which will run the sandbox runtime wrapper with Electron in node mode. For remote, we need to specify the node executable to ensure it runs with Node.js.
281 }
283 > private async _getSandboxEnvVariables(baseEnv: McpServerTransportStdio['env'], tempDir: URI | undefined, rgPath: string | undefined, remoteAuthority?: string): Promise<McpServerTransportStdio['env']> {
284 let env: McpServerTransportStdio['env'] = { ...baseEnv };
285 if (tempDir) {
297 return env;
298 }
300 > private _getSandboxCommandArgs(command: string, args: readonly string[], sandboxConfigPath: string | undefined): string[] {
301 const result: string[] = [];
302 if (sandboxConfigPath) {
307 return result;
308 }
310 > private async _getRemoteEnv(remoteAuthority?: string): Promise<IRemoteAgentEnvironment | null> {
311 if (!remoteAuthority) {
312 return null;
314 return this._remoteEnvDetailsPromise;
315 }
317 > private async _getOperatingSystem(remoteAuthority?: string): Promise<OperatingSystem> {
318 const remoteEnv = await this._getRemoteEnv(remoteAuthority);
319 if (remoteEnv) {
322 return OS;
323 }
325 > private async _getAppRoot(remoteAuthority?: string): Promise<string> {
326 const remoteEnv = await this._getRemoteEnv(remoteAuthority);
327 if (remoteEnv) {
330 return dirname(FileAccess.asFileUri('').path);
331 }
333 > private async _getTempDir(remoteAuthority?: string): Promise<URI | undefined> {
334 const remoteEnv = await this._getRemoteEnv(remoteAuthority);
335 if (remoteEnv) {
343 return tempDir;
344 }
346 > private async _updateSandboxConfig(tempDir: URI, configTarget: ConfigurationTarget, sandboxConfig?: IMcpSandboxConfiguration, launchCwd?: string): Promise<string> {
347 const normalizedSandboxConfig = this._withDefaultSandboxConfig(sandboxConfig, launchCwd);
348 let configFileUri: URI;
357 return configFileUri.path;
358 }
360 > // this method merges the default allowWrite paths and allowedDomains with the ones provided in the sandbox config, to ensure that the default necessary paths and domains are always included in the sandbox config used for launching,
361 > // even if they are not explicitly specified in the config provided by the user or the MCP server config.
362 > private _withDefaultSandboxConfig(sandboxConfig?: IMcpSandboxConfiguration, launchCwd?: string): IMcpSandboxConfiguration {
363 const mergedAllowWrite = new Set(sandboxConfig?.filesystem?.allowWrite ?? []);
364 for (const defaultAllowWrite of this._getDefaultAllowWrite(launchCwd ? [launchCwd] : undefined)) {
388 };
389 }
391 > private _getDefaultAllowWrite(directories?: string[]): readonly string[] {
392 for (const launchCwd of directories ?? []) {
393 const trimmed = launchCwd.trim();
398 return this._defaultAllowWritePaths;
399 }
401 > private _pathJoin = (os: OperatingSystem, ...segments: string[]) => {
402 > const path = os === OperatingSystem.Windows ? win32 : posix;
403 > return path.join(...segments);
404 > };
405 >
406 > private _getPathDelimiter = async (remoteAuthority?: string) => {
407 > const os = await this._getOperatingSystem(remoteAuthority);
408 > return os === OperatingSystem.Windows ? win32.delimiter : posix.delimiter;
409 > };
410 >
411 > private _quoteShellArgument(value: string): string {
412 return `'${value.replace(/'/g, `'\\''`)}'`;
413 }
415 > }
src/vs/workbench/contrib/mcp/common/mcpRegistryTypes.ts 100 introduced LOC · 1 range

Open complete file

1 > /*--------------------------------------------------------------------------------------------- mcpRegistryTypes.ts
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 { Event } from '../../../../base/common/event.js';
7 > import { IDisposable } from '../../../../base/common/lifecycle.js';
8 > import { IObservable } from '../../../../base/common/observable.js';
9 > import { ConfigurationTarget } from '../../../../platform/configuration/common/configuration.js';
10 > import { createDecorator } from '../../../../platform/instantiation/common/instantiation.js';
11 > import { ILogger, LogLevel } from '../../../../platform/log/common/log.js';
12 > import { StorageScope } from '../../../../platform/storage/common/storage.js';
13 > import { IWorkspaceFolderData } from '../../../../platform/workspace/common/workspace.js';
14 > import { IResolvedValue } from '../../../services/configurationResolver/common/configurationResolverExpression.js';
15 > import { McpTaskManager } from './mcpTaskManager.js';
16 > import { IMcpServerConnection, LazyCollectionState, McpCollectionDefinition, McpCollectionReference, McpConnectionState, McpDefinitionReference, McpServerDefinition, McpServerLaunch, McpStartServerInteraction } from './mcpTypes.js';
17 > import { MCP } from './modelContextProtocol.js';
18 >
19 > export const IMcpRegistry = createDecorator<IMcpRegistry>('mcpRegistry');
20 >
21 > /** Message transport to a single MCP server. */
22 > export interface IMcpMessageTransport extends IDisposable {
23 > readonly state: IObservable<McpConnectionState>;
24 > readonly onDidLog: Event<{ level: LogLevel; message: string }>;
25 > readonly onDidReceiveMessage: Event<MCP.JSONRPCMessage>;
26 > send(message: MCP.JSONRPCMessage): void;
27 > stop(): void;
28 > }
29 >
30 > export interface IMcpHostDelegate {
31 > /** Priority for this delegate, delegates are tested in descending priority order */
32 > readonly priority: number;
33 > waitForInitialProviderPromises(): Promise<void>;
34 > canStart(collectionDefinition: McpCollectionDefinition, serverDefinition: McpServerDefinition): boolean;
35 > substituteVariables(serverDefinition: McpServerDefinition, launch: McpServerLaunch): Promise<McpServerLaunch>;
36 > start(collectionDefinition: McpCollectionDefinition, serverDefinition: McpServerDefinition, resolvedLaunch: McpServerLaunch, options?: { errorOnUserInteraction?: boolean }): IMcpMessageTransport;
37 > }
38 >
39 > export interface IMcpResolveConnectionOptions {
40 > logger: ILogger;
41 > interaction?: McpStartServerInteraction;
42 > collectionRef: McpCollectionReference;
43 > definitionRef: McpDefinitionReference;
44 >
45 > /** A reference (on the server) to its last nonce where trust was given. */
46 > trustNonceBearer: { trustedAtNonce: string | undefined };
47 > /**
48 > * When to trigger the trust prompt.
49 > * - only-new: only prompt for servers that are not previously explicitly untrusted (default)
50 > * - all-untrusted: prompt for all servers that are not trusted
51 > * - never: don't prompt, fail silently when trying to start an untrusted server
52 > */
53 > promptType?: 'only-new' | 'all-untrusted' | 'never';
54 > /**
55 > * Automatically trust if changed. This should ONLY be set for afforances that
56 > * ensure the user sees the config before it gets started (e.g. code lenses)
57 > */
58 > autoTrustChanges?: boolean;
59 >
60 > /** If set, try to launch with debugging when dev mode is configured */
61 > debug?: boolean;
62 >
63 > /** If true, throw an error if any user interaction would be required during startup. */
64 > errorOnUserInteraction?: boolean;
65 >
66 > /** Shared task manager for server-side MCP tasks (survives reconnections) */
67 > taskManager: McpTaskManager;
68 > }
69 >
70 > export interface IMcpRegistry {
71 > readonly _serviceBrand: undefined;
72 >
73 > /** Fired when the user provides more inputs when creating a connection. */
74 > readonly onDidChangeInputs: Event<void>;
75 >
76 > readonly collections: IObservable<readonly McpCollectionDefinition[]>;
77 > readonly delegates: IObservable<readonly IMcpHostDelegate[]>;
78 > /** Whether there are new collections that can be resolved with a discover() call */
79 > readonly lazyCollectionState: IObservable<{ state: LazyCollectionState; collections: McpCollectionDefinition[] }>;
80 >
81 > /** Helper function to observe a definition by its reference. */
82 > getServerDefinition(collectionRef: McpDefinitionReference, definitionRef: McpDefinitionReference): IObservable<{ server: McpServerDefinition | undefined; collection: McpCollectionDefinition | undefined }>;
83 >
84 > /** Discover new collections, returning newly-discovered ones. */
85 > discoverCollections(): Promise<McpCollectionDefinition[]>;
86 >
87 > registerDelegate(delegate: IMcpHostDelegate): IDisposable;
88 > registerCollection(collection: McpCollectionDefinition): IDisposable;
89 >
90 > /** Resets any saved inputs for the input, or globally. */
91 > clearSavedInputs(scope: StorageScope, inputId?: string): Promise<void>;
92 > /** Edits a previously-saved input. */
93 > editSavedInput(inputId: string, folderData: IWorkspaceFolderData | undefined, configSection: string, target: ConfigurationTarget): Promise<void>;
94 > /** Updates a saved input. */
95 > setSavedInput(inputId: string, target: ConfigurationTarget, value: string): Promise<void>;
96 > /** Gets saved inputs from storage. */
97 > getSavedInputs(scope: StorageScope): Promise<{ [id: string]: IResolvedValue }>;
98 > /** Creates a connection for the collection and definition. */
99 > resolveConnection(options: IMcpResolveConnectionOptions): Promise<IMcpServerConnection | undefined>;
100 > }
src/vs/workbench/contrib/mcp/common/mcpDevMode.ts 46 introduced LOC · 6 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- mcpDevMode.ts
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 { equals as arraysEqual } from '../../../../base/common/arrays.js';
7 > import { assertNever } from '../../../../base/common/assert.js';
8 > import { Throttler } from '../../../../base/common/async.js';
9 > import * as glob from '../../../../base/common/glob.js';
10 > import { Disposable } from '../../../../base/common/lifecycle.js';
11 > import { equals as objectsEqual } from '../../../../base/common/objects.js';
12 > import { autorun, autorunDelta, derivedOpts } from '../../../../base/common/observable.js';
13 > import { localize } from '../../../../nls.js';
14 > import { ICommandService } from '../../../../platform/commands/common/commands.js';
15 > import { FileSystemProviderCapabilities, IFileService } from '../../../../platform/files/common/files.js';
16 > import { createDecorator } from '../../../../platform/instantiation/common/instantiation.js';
17 > import { IWorkspaceContextService } from '../../../../platform/workspace/common/workspace.js';
18 > import { IConfig, IDebugService, IDebugSessionOptions } from '../../debug/common/debug.js';
19 > import { IMcpRegistry } from './mcpRegistryTypes.js';
20 > import { IMcpServer, McpServerDefinition, McpServerLaunch, McpServerTransportType } from './mcpTypes.js';
21 >
22 > export class McpDevModeServerAttache extends Disposable {
23 > constructor(
24 server: IMcpServer,
25 fwdRef: { lastModeDebugged: boolean },
104 }));
105 }
106 > } mcpDevMode.ts
107 >
108 > export interface IMcpDevModeDebugging {
109 > readonly _serviceBrand: undefined;
110 >
111 > transform(definition: McpServerDefinition, launch: McpServerLaunch): Promise<McpServerLaunch>;
112 > }
113 >
114 > export const IMcpDevModeDebugging = createDecorator<IMcpDevModeDebugging>('mcpDevModeDebugging');
115 >
116 > const DEBUG_HOST = '127.0.0.1';
117 >
118 > export class McpDevModeDebugging implements IMcpDevModeDebugging {
119 > declare readonly _serviceBrand: undefined;
120 >
121 > constructor(
122 @IDebugService private readonly _debugService: IDebugService,
123 @ICommandService private readonly _commandService: ICommandService,
124 ) { }
126 > public async transform(definition: McpServerDefinition, launch: McpServerLaunch): Promise<McpServerLaunch> {
127 if (!definition.devMode?.debug || launch.type !== McpServerTransportType.Stdio) {
128 return launch;
203 }
204 }
206 > protected ensureListeningOnPort(port: number): Promise<void> {
207 return Promise.resolve();
208 }
210 > protected getDebugPort() {
211 return Promise.resolve(9230);
212 }
213 > } mcpDevMode.ts
src/vs/base/common/uriTransformer.ts 19 introduced LOC · 2 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- uriTransformer.ts
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 { UriParts, IRawURITransformer, URITransformer, IURITransformer } from './uriIpc.js';
7 >
8 > /**
9 > * ```
10 > * --------------------------------
11 > * | UI SIDE | AGENT SIDE |
12 > * |---------------|--------------|
13 > * | vscode-remote | file |
14 > * | file | vscode-local |
15 > * --------------------------------
16 > * ```
17 > */
18 function createRawURITransformer(remoteAuthority: string): IRawURITransformer {
19 return {
46 };
47 }
49 > export function createURITransformer(remoteAuthority: string): IURITransformer {
50 return new URITransformer(createRawURITransformer(remoteAuthority));
51 }