Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/workbench/contrib/chat/browser/aiCustomization/embeddedMcpServerDetail.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 * as DOM from '../../../../../base/browser/dom.js';
7
import { Disposable } from '../../../../../base/common/lifecycle.js';
8
import { ThemeIcon } from '../../../../../base/common/themables.js';
9
import { localize } from '../../../../../nls.js';
10
import { LocalMcpServerScope } from '../../../../services/mcp/common/mcpWorkbenchManagementService.js';
11
import { IMcpWorkbenchService, IWorkbenchMcpServer } from '../../../mcp/common/mcpTypes.js';
12
import { mcpServerIcon, userIcon, workspaceIcon } from './aiCustomizationIcons.js';
13
14
const $ = DOM.$;
15
16
/**
17
* Compact detail view for an MCP server inside the AI Customizations management editor's
18
* split-pane host. Renders identity (icon + name + scope) and description.
19
*
20
* Advanced actions (enable / disable / uninstall / configure) remain accessible via the
21
* row's existing context menu, so this component intentionally stays small.
22
*/
23
export class EmbeddedMcpServerDetail extends Disposable {
24
25
private readonly root: HTMLElement;
26
private readonly iconEl: HTMLElement;
27
private readonly nameEl: HTMLElement;
28
private readonly scopeEl: HTMLElement;
29
private readonly descriptionEl: HTMLElement;
30
private readonly emptyEl: HTMLElement;
31
32
private current: IWorkbenchMcpServer | undefined;
33
34
constructor(
35
parent: HTMLElement,
36
@IMcpWorkbenchService private readonly mcpWorkbenchService: IMcpWorkbenchService,
37
) {
38
super();
39
40
this.root = DOM.append(parent, $('.ai-customization-embedded-detail.embedded-mcp-detail'));
41
42
const header = DOM.append(this.root, $('.embedded-detail-header'));
43
this.iconEl = DOM.append(header, $('.embedded-detail-icon'));
44
const headerText = DOM.append(header, $('.embedded-detail-header-text'));
45
this.nameEl = DOM.append(headerText, $('h2.embedded-detail-name'));
46
this.nameEl.setAttribute('role', 'heading');
47
this.scopeEl = DOM.append(headerText, $('.embedded-detail-scope'));
48
49
this.descriptionEl = DOM.append(this.root, $('.embedded-detail-description'));
50
51
this.emptyEl = DOM.append(this.root, $('.embedded-detail-empty'));
52
this.emptyEl.textContent = localize('mcpDetailEmpty', "No MCP server selected.");
53
54
// Refresh when the underlying server changes (install state, enablement, etc.).
55
this._register(this.mcpWorkbenchService.onChange(server => {
56
if (this.current && server && server.id === this.current.id) {
57
this.current = server;
58
this.renderItem();
59
}
60
}));
61
62
this.renderItem();
63
}
64
65
get element(): HTMLElement {
66
return this.root;
67
}
68
69
setInput(server: IWorkbenchMcpServer): void {
70
this.current = server;
71
this.renderItem();
72
}
73
74
clearInput(): void {
75
this.current = undefined;
76
this.renderItem();
77
}
78
79
private renderItem(): void {
80
const server = this.current;
81
const hasItem = !!server;
82
this.emptyEl.style.display = hasItem ? 'none' : '';
83
this.root.classList.toggle('is-empty', !hasItem);
84
if (!server) {
85
this.nameEl.textContent = '';
86
this.scopeEl.textContent = '';
87
this.descriptionEl.textContent = '';
88
this.iconEl.className = 'embedded-detail-icon';
89
return;
90
}
91
92
this.nameEl.textContent = server.label || server.name;
93
94
// Icon: server.codicon (when set) is the full codicon class (e.g. "codicon-foo");
95
// fall back to the standard MCP server themed icon otherwise.
96
this.iconEl.className = `embedded-detail-icon ${server.codicon ? `codicon ${server.codicon}` : ThemeIcon.asClassName(mcpServerIcon)}`;
97
98
// Scope label
99
const scope = server.local?.scope;
100
const scopeInfo = describeMcpScope(scope);
101
if (scopeInfo) {
102
const scopeIcon = DOM.$(`span.codicon.codicon-${scopeInfo.icon.id}`);
103
this.scopeEl.replaceChildren(scopeIcon, document.createTextNode(' ' + scopeInfo.label));
104
this.scopeEl.style.display = '';
105
} else {
106
this.scopeEl.replaceChildren();
107
this.scopeEl.style.display = 'none';
108
}
109
110
// Description (single line, but allow wrapping in CSS)
111
const description = (server.description || '').trim();
112
this.descriptionEl.textContent = description;
113
this.descriptionEl.style.display = description ? '' : 'none';
114
}
115
}
116
117
function describeMcpScope(scope: LocalMcpServerScope | undefined): { label: string; icon: ThemeIcon } | undefined {
118
switch (scope) {
119
case LocalMcpServerScope.Workspace:
120
return { label: localize('mcpScopeWorkspace', "Workspace"), icon: workspaceIcon };
121
case LocalMcpServerScope.User:
122
case LocalMcpServerScope.RemoteUser:
123
return { label: localize('mcpScopeUser', "User"), icon: userIcon };
124
default:
125
return undefined;
126
}
127
}
128
129