From cfc47b16a2850cef6262d8ee9685cbb38b0970c7 Mon Sep 17 00:00:00 2001 From: vritant24 Date: Tue, 1 Sep 2026 21:58:36 -0700 Subject: [PATCH] Fix MCP detail source for discovered servers Use the MCP collection origin when an installed server definition does not carry its own origin, while preserving definition-level precedence. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../browser/aiCustomization/mcpListWidget.ts | 8 +-- .../aiCustomization/mcpListWidget.test.ts | 53 ++++++++++++++++++- 2 files changed, 57 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/contrib/chat/browser/aiCustomization/mcpListWidget.ts b/src/vs/workbench/contrib/chat/browser/aiCustomization/mcpListWidget.ts index 5a5ae3ab13eda..28aec0ee2b992 100644 --- a/src/vs/workbench/contrib/chat/browser/aiCustomization/mcpListWidget.ts +++ b/src/vs/workbench/contrib/chat/browser/aiCustomization/mcpListWidget.ts @@ -987,15 +987,17 @@ function createBuiltinEntry(server: IMcpServer, activeSessionServer?: AgentHostM }; } -function createInstalledMcpServerDetailInput(entry: IMcpInstalledEntry): IMcpServerDetailInput { +export function createInstalledMcpServerDetailInput(entry: IMcpInstalledEntry): IMcpServerDetailInput { if (entry.type === 'server-item') { return createWorkbenchMcpServerDetailInput(entry.server); } const activeSessionServer = getActiveSessionServer(entry); const localServer = entry.type === 'session-server-item' ? undefined : entry.localServer; - const localDefinition = localServer?.readDefinitions().get().server; - const localSource = localDefinition?.presentation?.origin; + const localDefinitions = localServer?.readDefinitions().get(); + const localDefinition = localDefinitions?.server; + const collectionOrigin = localDefinitions?.collection?.presentation?.origin; + const localSource = localDefinition?.presentation?.origin ?? (collectionOrigin ? { uri: collectionOrigin } : undefined); const activeSessionSource = activeSessionServer?.sourceUri ? { uri: activeSessionServer.sourceUri, diff --git a/src/vs/workbench/contrib/chat/test/browser/aiCustomization/mcpListWidget.test.ts b/src/vs/workbench/contrib/chat/test/browser/aiCustomization/mcpListWidget.test.ts index 2697adc59ee27..7d19ecffcb23c 100644 --- a/src/vs/workbench/contrib/chat/test/browser/aiCustomization/mcpListWidget.test.ts +++ b/src/vs/workbench/contrib/chat/test/browser/aiCustomization/mcpListWidget.test.ts @@ -23,12 +23,13 @@ import { IAICustomizationWorkspaceService } from '../../../common/aiCustomizatio import { ICustomizationHarnessService } from '../../../common/customizationHarnessService.js'; import { IAgentHostCustomizationService } from '../../../browser/agentSessions/agentHost/agentHostCustomizationService.js'; import { IAgentPluginService } from '../../../common/plugins/agentPluginService.js'; -import { IMcpService, McpConnectionState } from '../../../../mcp/common/mcpTypes.js'; +import { IMcpServer, IMcpService, McpConnectionState, McpServerTransportType } from '../../../../mcp/common/mcpTypes.js'; import { DisableMcpServerForWorkspaceAction, DisableMcpServerGloballyAction, EnableMcpServerForWorkspaceAction, EnableMcpServerGloballyAction } from '../../../../mcp/browser/mcpServerActions.js'; import { AgentHostMcpServer, authenticateMcpServer, createBuiltinActiveSessionMcpEntries, + createInstalledMcpServerDetailInput, getActiveSessionServerLifecycleAction, getActiveSessionServerPresentation, getBuiltinMcpServerEnablementActions, @@ -96,6 +97,29 @@ function createMcpService(enablement: ContributionEnablementState): { service: I return { service, calls }; } +function createMcpDetailTestServer(definitionOrigin?: URI, collectionOrigin?: URI): IMcpServer { + const definitions = observableValue('definitions', { + server: { + id: 'server-1', + label: 'Server One', + launch: { + type: McpServerTransportType.Stdio, + command: 'server', + args: [], + env: {}, + }, + cacheNonce: 'server-1', + presentation: definitionOrigin ? { origin: { uri: definitionOrigin } } : undefined, + }, + collection: { + presentation: collectionOrigin ? { origin: collectionOrigin } : undefined, + }, + }); + return { + readDefinitions: () => definitions, + } as unknown as IMcpServer; +} + function runAction(action: IAction | undefined): void { assert.ok(action, 'expected an action to be defined'); void action.run(); @@ -193,6 +217,33 @@ suite('mcpListWidget', () => { }); }); + test('uses collection origin as the installed MCP detail fallback', () => { + const definitionOrigin = URI.file('/definition/mcp.json'); + const collectionOrigin = URI.file('/collection/mcp-config.json'); + const collectionFallback = createInstalledMcpServerDetailInput({ + type: 'builtin-item', + id: 'collection-origin', + label: 'Collection Origin', + description: '', + localServer: createMcpDetailTestServer(undefined, collectionOrigin), + }); + const definitionPrecedence = createInstalledMcpServerDetailInput({ + type: 'builtin-item', + id: 'definition-origin', + label: 'Definition Origin', + description: '', + localServer: createMcpDetailTestServer(definitionOrigin, collectionOrigin), + }); + + assert.deepStrictEqual({ + collectionFallback: collectionFallback.source, + definitionPrecedence: definitionPrecedence.source, + }, { + collectionFallback: { uri: collectionOrigin }, + definitionPrecedence: { uri: definitionOrigin }, + }); + }); + test('toggles MCP enablement without changing its scope', () => { assert.deepStrictEqual([ getToggledMcpEnablementState(ContributionEnablementState.EnabledProfile),