Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/workbench/contrib/mcp/common/discovery/pluginMcpDiscovery.ts
13406 views
1
/*---------------------------------------------------------------------------------------------
2
* Copyright (c) Microsoft Corporation. All rights reserved.
3
* Licensed under the MIT License. See License.txt in the project root for license information.
4
*--------------------------------------------------------------------------------------------*/
5
6
import { hash } from '../../../../../base/common/hash.js';
7
import { Disposable, DisposableResourceMap } from '../../../../../base/common/lifecycle.js';
8
import { ResourceSet } from '../../../../../base/common/map.js';
9
import { Schemas } from '../../../../../base/common/network.js';
10
import { autorun } from '../../../../../base/common/observable.js';
11
import { isDefined } from '../../../../../base/common/types.js';
12
import { URI } from '../../../../../base/common/uri.js';
13
import { ConfigurationTarget } from '../../../../../platform/configuration/common/configuration.js';
14
import { IMcpServerConfiguration, McpServerType } from '../../../../../platform/mcp/common/mcpPlatformTypes.js';
15
import { StorageScope } from '../../../../../platform/storage/common/storage.js';
16
import {
17
IAgentPlugin,
18
IAgentPluginMcpServerDefinition,
19
IAgentPluginService
20
} from '../../../chat/common/plugins/agentPluginService.js';
21
import { isContributionEnabled } from '../../../chat/common/enablement.js';
22
import { IMcpRegistry } from '../mcpRegistryTypes.js';
23
import { McpCollectionSortOrder, McpServerDefinition, McpServerLaunch, McpServerTransportType, McpServerTrust } from '../mcpTypes.js';
24
import { IMcpDiscovery } from './mcpDiscovery.js';
25
26
export class PluginMcpDiscovery extends Disposable implements IMcpDiscovery {
27
readonly fromGallery = false;
28
29
private readonly _collections = this._register(new DisposableResourceMap());
30
31
constructor(
32
@IAgentPluginService private readonly _agentPluginService: IAgentPluginService,
33
@IMcpRegistry private readonly _mcpRegistry: IMcpRegistry,
34
) {
35
super();
36
}
37
38
public start(): void {
39
this._register(autorun(reader => {
40
const plugins = this._agentPluginService.plugins.read(reader);
41
const seen = new ResourceSet();
42
for (const plugin of plugins) {
43
if (!isContributionEnabled(plugin.enablement.read(reader))) {
44
continue;
45
}
46
const servers = plugin.mcpServerDefinitions.read(reader);
47
if (servers.length === 0) {
48
continue;
49
}
50
51
seen.add(plugin.uri);
52
53
let collectionState = this._collections.get(plugin.uri);
54
if (!collectionState) {
55
// note: all plugin servers are currently defined in the same file
56
collectionState = this.createCollectionState(plugin, servers[0].uri);
57
this._collections.set(plugin.uri, collectionState);
58
}
59
}
60
61
for (const [pluginUri] of this._collections) {
62
if (!seen.has(pluginUri)) {
63
this._collections.deleteAndDispose(pluginUri);
64
}
65
}
66
}));
67
}
68
69
private createCollectionState(plugin: IAgentPlugin, manifestURI: URI) {
70
const collectionId = `plugin.${plugin.uri}`;
71
return this._mcpRegistry.registerCollection({
72
id: collectionId,
73
label: `${plugin.label} (Agent Plugin)`,
74
remoteAuthority: plugin.uri.scheme === Schemas.vscodeRemote ? plugin.uri.authority : null,
75
configTarget: ConfigurationTarget.USER,
76
scope: StorageScope.PROFILE,
77
trustBehavior: McpServerTrust.Kind.Trusted,
78
serverDefinitions: plugin.mcpServerDefinitions.map(defs =>
79
defs.map(d => this._toServerDefinition(collectionId, d)).filter(isDefined)),
80
order: McpCollectionSortOrder.Plugin,
81
presentation: {
82
origin: manifestURI,
83
},
84
});
85
}
86
87
private _toServerDefinition(
88
collectionId: string,
89
{ name, configuration }: IAgentPluginMcpServerDefinition,
90
): McpServerDefinition | undefined {
91
const launch = this._toLaunch(configuration);
92
if (!launch) {
93
return undefined;
94
}
95
96
return {
97
id: `${collectionId}.${name}`,
98
label: name,
99
launch,
100
variableReplacement: { target: ConfigurationTarget.USER },
101
cacheNonce: String(hash(launch)),
102
};
103
}
104
105
private _toLaunch(config: IMcpServerConfiguration): McpServerLaunch | undefined {
106
if (config.type === McpServerType.LOCAL) {
107
return {
108
type: McpServerTransportType.Stdio,
109
command: config.command,
110
args: config.args ? [...config.args] : [],
111
env: config.env ? { ...config.env } : {},
112
envFile: config.envFile,
113
cwd: config.cwd,
114
sandbox: undefined,
115
};
116
}
117
118
try {
119
return {
120
type: McpServerTransportType.HTTP,
121
uri: URI.parse(config.url),
122
headers: Object.entries(config.headers ?? {}),
123
oauth: config.oauth,
124
};
125
} catch {
126
return undefined;
127
}
128
}
129
}
130
131