src/vs/workbench/contrib/mcp/common/mcpConfiguration.ts

393 LOC · 363 covered · 30 uncovered · 7 ranges · 113 concepts · 2 introducers · 75 tests

File neighbourhood

The centred file is linked to every concept that introduces one of its ranges, every test that runs code from the file, and the gray connector concepts standing between those tests and the file's own introducer concepts. Undirected links join concepts to every file where they introduce source and concepts to the tests they introduce; arrows show specialization between the displayed concepts and bridge only concepts omitted from this view. Concept colors match the source ranges below; connector concepts have no source color and are shown in gray.

Focused file, its introducer and connector concepts, their introduced files, and tests that run code from the file

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 related-file, concept, and source links on this page.

Graph controls are ready.

Interactive rendering requires JavaScript and WebGL. Use the related-file, concept, and source links on this page while the interactive map is unavailable.

1 > /*--------------------------------------------------------------------------------------------- mcpConfiguration.ts ×6
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 { MarkdownString } from '../../../../base/common/htmlContent.js';
7 > import { IJSONSchema, IJSONSchemaMap } from '../../../../base/common/jsonSchema.js';
8 > import { Disposable } from '../../../../base/common/lifecycle.js';
9 > import { localize } from '../../../../nls.js';
10 > import { IExtensionManifest, IMcpCollectionContribution } from '../../../../platform/extensions/common/extensions.js';
11 > import { SyncDescriptor } from '../../../../platform/instantiation/common/descriptors.js';
12 > import { Registry } from '../../../../platform/registry/common/platform.js';
13 > import { mcpSchemaId } from '../../../services/configuration/common/configuration.js';
14 > import { inputsSchema } from '../../../services/configurationResolver/common/configurationResolverSchema.js';
15 > import { Extensions, IExtensionFeaturesRegistry, IExtensionFeatureTableRenderer, IRenderedData, IRowData, ITableData } from '../../../services/extensionManagement/common/extensionFeatures.js';
16 > import { IExtensionPointDescriptor } from '../../../services/extensions/common/extensionsRegistry.js';
17 >
18 > const mcpActivationEventPrefix = 'onMcpCollection:';
19 >
20 > /**
21 > * note: `contributedCollectionId` is _not_ the collection ID. The collection
22 > * ID is formed by passing the contributed ID through `extensionPrefixedIdentifier`
23 > */
24 > export const mcpActivationEvent = (contributedCollectionId: string) =>
25 > mcpActivationEventPrefix + contributedCollectionId; mcpServer.ts ×36
27 > export const enum DiscoverySource {
28 > ClaudeDesktop = 'claude-desktop',
29 > Windsurf = 'windsurf',
30 > CursorGlobal = 'cursor-global',
31 > CursorWorkspace = 'cursor-workspace',
32 > }
33 >
34 > export const allDiscoverySources = Object.keys({
35 > [DiscoverySource.ClaudeDesktop]: true,
36 > [DiscoverySource.Windsurf]: true,
37 > [DiscoverySource.CursorGlobal]: true,
38 > [DiscoverySource.CursorWorkspace]: true,
39 > } satisfies Record<DiscoverySource, true>) as DiscoverySource[];
40 >
41 > export const discoverySourceLabel: Record<DiscoverySource, string> = {
42 > [DiscoverySource.ClaudeDesktop]: localize('mcp.discovery.source.claude-desktop', "Claude Desktop"),
43 > [DiscoverySource.Windsurf]: localize('mcp.discovery.source.windsurf', "Windsurf"),
44 > [DiscoverySource.CursorGlobal]: localize('mcp.discovery.source.cursor-global', "Cursor (Global)"),
45 > [DiscoverySource.CursorWorkspace]: localize('mcp.discovery.source.cursor-workspace', "Cursor (Workspace)"),
46 > };
47 > export const discoverySourceSettingsLabel: Record<DiscoverySource, string> = {
48 > [DiscoverySource.ClaudeDesktop]: localize('mcp.discovery.source.claude-desktop.config', "Claude Desktop configuration (`claude_desktop_config.json`)"),
49 > [DiscoverySource.Windsurf]: localize('mcp.discovery.source.windsurf.config', "Windsurf configurations (`~/.codeium/windsurf/mcp_config.json`)"),
50 > [DiscoverySource.CursorGlobal]: localize('mcp.discovery.source.cursor-global.config', "Cursor global configuration (`~/.cursor/mcp.json`)"),
51 > [DiscoverySource.CursorWorkspace]: localize('mcp.discovery.source.cursor-workspace.config', "Cursor workspace configuration (`.cursor/mcp.json`)"),
52 > };
53 >
54 > export const mcpConfigurationSection = 'mcp';
55 > export const mcpDiscoverySection = 'chat.mcp.discovery.enabled';
56 > export const mcpServerSamplingSection = 'chat.mcp.serverSampling';
57 > export const mcpServerCollisionBehaviorSection = 'chat.mcp.collisionBehavior';
58 > /**
59 > * Configuration key for the enterprise-managed MCP IdP bag. The setting is
60 > * registered with `included: false` so it is hidden from the Settings UI and
61 > * settings.json IntelliSense; it is intended to be delivered through enterprise
62 > * policy (Windows Group Policy / macOS managed preferences / Linux
63 > * `/etc/vscode/policy.json`), with hand-editing of `settings.json` as a
64 > * developer escape hatch.
65 > */
66 > export const mcpEnterpriseManagedAuthIdpSection = 'mcp.enterpriseManagedAuth.idp';
67 >
68 > /**
69 > * Shape of the {@link mcpEnterpriseManagedAuthIdpSection} setting. All fields
70 > * are optional so partial configurations (e.g. just the issuer) remain valid.
71 > */
72 > export interface IMcpEnterpriseManagedAuthIdpConfig {
73 > readonly issuer?: string;
74 > readonly clientId?: string;
75 > readonly clientSecret?: string;
76 > }
77 >
78 > export const enum McpCollisionBehavior {
79 > Disable = 'disable',
80 > Suffix = 'suffix',
81 > }
82 >
83 > export interface IMcpServerSamplingConfiguration {
84 > allowedDuringChat?: boolean;
85 > allowedOutsideChat?: boolean;
86 > allowedModels?: string[];
87 > }
88 >
89 > export const mcpSchemaExampleServers = {
90 > 'mcp-server-time': {
91 > command: 'python',
92 > args: ['-m', 'mcp_server_time', '--local-timezone=America/Los_Angeles'],
93 > env: {},
94 > }
95 > };
96 >
97 > const httpSchemaExamples = {
98 > 'my-mcp-server': {
99 > url: 'http://localhost:3001/mcp',
100 > headers: {},
101 > }
102 > };
103 >
104 > const mcpDevModeProps = (stdio: boolean): IJSONSchemaMap => ({
105 > dev: {
106 > type: 'object',
107 > markdownDescription: localize('app.mcp.dev', 'Enabled development mode for the server. When present, the server will be started eagerly and output will be included in its output. Properties inside the `dev` object can configure additional behavior.'),
108 > examples: [{ watch: 'src/**/*.ts', debug: { type: 'node' } }],
109 > properties: {
110 > watch: {
111 > description: localize('app.mcp.dev.watch', 'A glob pattern or list of glob patterns relative to the workspace folder to watch. The MCP server will be restarted when these files change.'),
112 > examples: ['src/**/*.ts'],
113 > oneOf: [{ type: 'string' }, { type: 'array', items: { type: 'string' } }],
114 > },
115 > ...(stdio && {
116 > debug: {
117 > markdownDescription: localize('app.mcp.dev.debug', 'If set, debugs the MCP server using the given runtime as it\'s started.'),
118 > oneOf: [
119 > {
120 > type: 'object',
121 > required: ['type'],
122 > properties: {
123 > type: {
124 > type: 'string',
125 > enum: ['node'],
126 > description: localize('app.mcp.dev.debug.type.node', "Debug the MCP server using Node.js.")
127 > }
128 > },
129 > additionalProperties: false
130 > },
131 > {
132 > type: 'object',
133 > required: ['type'],
134 > properties: {
135 > type: {
136 > type: 'string',
137 > enum: ['debugpy'],
138 > description: localize('app.mcp.dev.debug.type.python', "Debug the MCP server using Python and debugpy.")
139 > },
140 > debugpyPath: {
141 > type: 'string',
142 > description: localize('app.mcp.dev.debug.debugpyPath', "Path to the debugpy executable.")
143 > },
144 > },
145 > additionalProperties: false
146 > }
147 > ]
148 > }
149 > })
150 > }
151 > }
152 > });
153 >
154 > export const mcpStdioServerSchema: IJSONSchema = {
155 > type: 'object',
156 > additionalProperties: false,
157 > examples: [mcpSchemaExampleServers['mcp-server-time']],
158 > properties: {
159 > type: {
160 > type: 'string',
161 > enum: ['stdio'],
162 > description: localize('app.mcp.json.type', "The type of the server.")
163 > },
164 > sandboxEnabled: {
165 > type: 'boolean',
166 > default: false,
167 > description: localize('app.mcp.json.sandboxEnabled', "Whether to run the server in a sandboxed environment.")
168 > },
169 > command: {
170 > type: 'string',
171 > description: localize('app.mcp.json.command', "The command to run the server.")
172 > },
173 > cwd: {
174 > type: 'string',
175 > description: localize('app.mcp.json.cwd', "The working directory for the server command. Defaults to the workspace folder when run in a workspace."),
176 > examples: ['${workspaceFolder}'],
177 > },
178 > args: {
179 > type: 'array',
180 > description: localize('app.mcp.args.command', "Arguments passed to the server."),
181 > items: {
182 > type: 'string'
183 > },
184 > },
185 > envFile: {
186 > type: 'string',
187 > description: localize('app.mcp.envFile.command', "Path to a file containing environment variables for the server."),
188 > examples: ['${workspaceFolder}/.env'],
189 > },
190 > env: {
191 > description: localize('app.mcp.env.command', "Environment variables passed to the server."),
192 > additionalProperties: {
193 > anyOf: [
194 > { type: 'null' },
195 > { type: 'string' },
196 > { type: 'number' },
197 > ]
198 > }
199 > },
200 > ...mcpDevModeProps(true),
201 > }
202 > };
203 >
204 > export const mcpServerSchema: IJSONSchema = {
205 > id: mcpSchemaId,
206 > type: 'object',
207 > title: localize('app.mcp.json.title', "Model Context Protocol Servers"),
208 > allowTrailingCommas: true,
209 > allowComments: true,
210 > additionalProperties: false,
211 > properties: {
212 > sandbox: {
213 > description: localize('app.mcp.json.sandbox', "Sandbox config that determines file system and network access. Sandboxing is enabled when sandboxEnabled property is set at the server level on Mac OS and Linux only."),
214 > type: 'object',
215 > additionalProperties: false,
216 > properties: {
217 > network: {
218 > description: localize('app.mcp.json.sandbox.network', "Network access settings for the sandboxed server."),
219 > type: 'object',
220 > additionalProperties: false,
221 > properties: {
222 > allowedDomains: {
223 > description: localize('app.mcp.json.sandbox.network.allowedDomains', "List of domains that the server is allowed to access. Wildcards are supported, e.g. `*.example.com`."),
224 > type: 'array',
225 > items: { type: 'string' },
226 > default: []
227 > },
228 > deniedDomains: {
229 > description: localize('app.mcp.json.sandbox.network.deniedDomains', "List of domains that the server is not allowed to access. e.g. `invalid.example.com`."),
230 > type: 'array',
231 > items: { type: 'string' },
232 > default: []
233 > }
234 > }
235 > },
236 > filesystem: {
237 > description: localize('app.mcp.json.sandbox.filesystem', "Filesystem access settings for the sandboxed server. Glob patterns are supported for Mac OS only."),
238 > type: 'object',
239 > additionalProperties: false,
240 > properties: {
241 > denyRead: {
242 > description: localize('app.mcp.json.sandbox.filesystem.denyRead', "List of file paths that the server is not allowed to read. By default, all files are allowed to be read. e.g. `~/src/secrets`."),
243 > type: 'array',
244 > items: { type: 'string' },
245 > default: []
246 > },
247 > allowWrite: {
248 > description: localize('app.mcp.json.sandbox.filesystem.allowWrite', "List of file paths that the server is allowed to write to. e.g. `~/src/`."),
249 > type: 'array',
250 > items: { type: 'string' },
251 > default: []
252 > },
253 > denyWrite: {
254 > description: localize('app.mcp.json.sandbox.filesystem.denyWrite', "List of file paths that the server is not allowed to write to. e.g. `~/src/auth/`."),
255 > type: 'array',
256 > items: { type: 'string' },
257 > default: []
258 > }
259 > }
260 > }
261 > }
262 > },
263 > servers: {
264 > examples: [
265 > mcpSchemaExampleServers,
266 > httpSchemaExamples,
267 > ],
268 > additionalProperties: {
269 > oneOf: [
270 > mcpStdioServerSchema, {
271 > type: 'object',
272 > additionalProperties: false,
273 > required: ['url'],
274 > examples: [httpSchemaExamples['my-mcp-server']],
275 > properties: {
276 > type: {
277 > type: 'string',
278 > enum: ['http', 'sse'],
279 > description: localize('app.mcp.json.type', "The type of the server.")
280 > },
281 > url: {
282 > type: 'string',
283 > format: 'uri',
284 > pattern: '^https?:\\/\\/.+',
285 > patternErrorMessage: localize('app.mcp.json.url.pattern', "The URL must start with 'http://' or 'https://'."),
286 > description: localize('app.mcp.json.url', "The URL of the Streamable HTTP or SSE endpoint.")
287 > },
288 > headers: {
289 > type: 'object',
290 > description: localize('app.mcp.json.headers', "Additional headers sent to the server."),
291 > additionalProperties: { type: 'string' },
292 > },
293 > oauth: {
294 > type: 'object',
295 > description: localize('app.mcp.json.oauth', "OAuth configuration for authenticating with the server."),
296 > additionalProperties: false,
297 > minProperties: 1,
298 > properties: {
299 > clientId: {
300 > type: 'string',
301 > minLength: 1,
302 > markdownDescription: localize('app.mcp.json.oauth.clientId', "The OAuth client ID to use when authenticating with the server. When `enterpriseManaged` is `true`, this is the **resource** authorization server's client ID (the client trusted by the protected resource), not the IdP's. To set the matching client secret, use the *Set Client Secret* code lens above this field — secrets are stored in the OS secret store, not in this file.")
303 > },
304 > enterpriseManaged: {
305 > type: 'boolean',
306 > default: false,
307 > markdownDescription: localize('app.mcp.json.oauth.enterpriseManaged', "(Preview) When set to `true`, this MCP server authenticates through the SSO issuer configured by `#mcp.enterpriseManagedAuth.idp#` using OAuth Identity Assertion Authorization Grant (ID-JAG). After a one-time sign-in, subsequent enterprise-managed servers connect silently. The IdP issuer and client credentials are read from the `#mcp.enterpriseManagedAuth.idp#` setting; the `clientId` on this server entry is passed to the resource authorization server.")
308 > }
309 > }
310 > },
311 > ...mcpDevModeProps(false),
312 > }
313 > },
314 > ]
315 > }
316 > },
317 > inputs: inputsSchema.definitions!.inputs
318 > }
319 > };
320 >
321 > export const mcpContributionPoint: IExtensionPointDescriptor<IMcpCollectionContribution[]> = {
322 > extensionPoint: 'mcpServerDefinitionProviders',
323 > activationEventsGenerator: function* (contribs) {
324 for (const contrib of contribs) {
325 if (contrib.id) {
326 yield mcpActivationEvent(contrib.id);
327 }
328 }
329 },
330 > jsonSchema: { mcpConfiguration.ts ×6
331 > description: localize('vscode.extension.contributes.mcp', 'Contributes Model Context Protocol servers. Users of this should also use `vscode.lm.registerMcpServerDefinitionProvider`.'),
332 > type: 'array',
333 > defaultSnippets: [{ body: [{ id: '', label: '' }] }],
334 > items: {
335 > additionalProperties: false,
336 > type: 'object',
337 > defaultSnippets: [{ body: { id: '', label: '' } }],
338 > properties: {
339 > id: {
340 > description: localize('vscode.extension.contributes.mcp.id', "Unique ID for the collection."),
341 > type: 'string'
342 > },
343 > label: {
344 > description: localize('vscode.extension.contributes.mcp.label', "Display name for the collection."),
345 > type: 'string'
346 > },
347 > when: {
348 > description: localize('vscode.extension.contributes.mcp.when', "Condition which must be true to enable this collection."),
349 > type: 'string'
350 > }
351 > }
352 > }
353 > }
354 > };
355 >
356 class McpServerDefinitionsProviderRenderer extends Disposable implements IExtensionFeatureTableRenderer {
357
358 readonly type = 'table';
360 > shouldRender(manifest: IExtensionManifest): boolean {
361 return !!manifest.contributes?.mcpServerDefinitionProviders && Array.isArray(manifest.contributes.mcpServerDefinitionProviders) && manifest.contributes.mcpServerDefinitionProviders.length > 0;
362 }
364 > render(manifest: IExtensionManifest): IRenderedData<ITableData> {
365 const mcpServerDefinitionProviders = manifest.contributes?.mcpServerDefinitionProviders ?? [];
366 const headers = [localize('id', "ID"), localize('name', "Name")];
367 const rows: IRowData[][] = mcpServerDefinitionProviders
368 .map(mcpServerDefinitionProvider => {
369 return [
370 new MarkdownString().appendMarkdown(`\`${mcpServerDefinitionProvider.id}\``),
371 mcpServerDefinitionProvider.label
372 ];
373 });
374
375 return {
376 data: {
377 headers,
378 rows
379 },
380 dispose: () => { }
381 };
382 }
384 >
385 > Registry.as<IExtensionFeaturesRegistry>(Extensions.ExtensionFeaturesRegistry).registerExtensionFeature({
386 > id: mcpConfigurationSection,
387 > label: localize('mcpServerDefinitionProviders', "MCP Servers"),
388 > access: {
389 > canToggle: false
390 > },
391 > renderer: new SyncDescriptor(McpServerDefinitionsProviderRenderer),
392 > });
393