1
>
/*---------------------------------------------------------------------------------------------
mcpConfiguration.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 { 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;
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) {