Path: blob/main/src/vs/workbench/contrib/chat/browser/aiCustomization/embeddedMcpServerDetail.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 * as DOM from '../../../../../base/browser/dom.js';6import { Disposable } from '../../../../../base/common/lifecycle.js';7import { ThemeIcon } from '../../../../../base/common/themables.js';8import { localize } from '../../../../../nls.js';9import { LocalMcpServerScope } from '../../../../services/mcp/common/mcpWorkbenchManagementService.js';10import { IMcpWorkbenchService, IWorkbenchMcpServer } from '../../../mcp/common/mcpTypes.js';11import { mcpServerIcon, userIcon, workspaceIcon } from './aiCustomizationIcons.js';1213const $ = DOM.$;1415/**16* Compact detail view for an MCP server inside the AI Customizations management editor's17* split-pane host. Renders identity (icon + name + scope) and description.18*19* Advanced actions (enable / disable / uninstall / configure) remain accessible via the20* row's existing context menu, so this component intentionally stays small.21*/22export class EmbeddedMcpServerDetail extends Disposable {2324private readonly root: HTMLElement;25private readonly iconEl: HTMLElement;26private readonly nameEl: HTMLElement;27private readonly scopeEl: HTMLElement;28private readonly descriptionEl: HTMLElement;29private readonly emptyEl: HTMLElement;3031private current: IWorkbenchMcpServer | undefined;3233constructor(34parent: HTMLElement,35@IMcpWorkbenchService private readonly mcpWorkbenchService: IMcpWorkbenchService,36) {37super();3839this.root = DOM.append(parent, $('.ai-customization-embedded-detail.embedded-mcp-detail'));4041const header = DOM.append(this.root, $('.embedded-detail-header'));42this.iconEl = DOM.append(header, $('.embedded-detail-icon'));43const headerText = DOM.append(header, $('.embedded-detail-header-text'));44this.nameEl = DOM.append(headerText, $('h2.embedded-detail-name'));45this.nameEl.setAttribute('role', 'heading');46this.scopeEl = DOM.append(headerText, $('.embedded-detail-scope'));4748this.descriptionEl = DOM.append(this.root, $('.embedded-detail-description'));4950this.emptyEl = DOM.append(this.root, $('.embedded-detail-empty'));51this.emptyEl.textContent = localize('mcpDetailEmpty', "No MCP server selected.");5253// Refresh when the underlying server changes (install state, enablement, etc.).54this._register(this.mcpWorkbenchService.onChange(server => {55if (this.current && server && server.id === this.current.id) {56this.current = server;57this.renderItem();58}59}));6061this.renderItem();62}6364get element(): HTMLElement {65return this.root;66}6768setInput(server: IWorkbenchMcpServer): void {69this.current = server;70this.renderItem();71}7273clearInput(): void {74this.current = undefined;75this.renderItem();76}7778private renderItem(): void {79const server = this.current;80const hasItem = !!server;81this.emptyEl.style.display = hasItem ? 'none' : '';82this.root.classList.toggle('is-empty', !hasItem);83if (!server) {84this.nameEl.textContent = '';85this.scopeEl.textContent = '';86this.descriptionEl.textContent = '';87this.iconEl.className = 'embedded-detail-icon';88return;89}9091this.nameEl.textContent = server.label || server.name;9293// Icon: server.codicon (when set) is the full codicon class (e.g. "codicon-foo");94// fall back to the standard MCP server themed icon otherwise.95this.iconEl.className = `embedded-detail-icon ${server.codicon ? `codicon ${server.codicon}` : ThemeIcon.asClassName(mcpServerIcon)}`;9697// Scope label98const scope = server.local?.scope;99const scopeInfo = describeMcpScope(scope);100if (scopeInfo) {101const scopeIcon = DOM.$(`span.codicon.codicon-${scopeInfo.icon.id}`);102this.scopeEl.replaceChildren(scopeIcon, document.createTextNode(' ' + scopeInfo.label));103this.scopeEl.style.display = '';104} else {105this.scopeEl.replaceChildren();106this.scopeEl.style.display = 'none';107}108109// Description (single line, but allow wrapping in CSS)110const description = (server.description || '').trim();111this.descriptionEl.textContent = description;112this.descriptionEl.style.display = description ? '' : 'none';113}114}115116function describeMcpScope(scope: LocalMcpServerScope | undefined): { label: string; icon: ThemeIcon } | undefined {117switch (scope) {118case LocalMcpServerScope.Workspace:119return { label: localize('mcpScopeWorkspace', "Workspace"), icon: workspaceIcon };120case LocalMcpServerScope.User:121case LocalMcpServerScope.RemoteUser:122return { label: localize('mcpScopeUser', "User"), icon: userIcon };123default:124return undefined;125}126}127128129