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