@codingame/monaco-vscode-mcp-service-override 32.0.0 → 32.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.js +1 -0
- package/package.json +2 -2
- package/vscode/src/vs/workbench/contrib/mcp/browser/mcp.contribution.js +3 -3
- package/vscode/src/vs/workbench/contrib/mcp/browser/mcp.view.contribution.d.ts +1 -0
- package/vscode/src/vs/workbench/contrib/mcp/browser/mcp.view.contribution.js +5 -0
- package/vscode/src/vs/workbench/contrib/mcp/browser/mcpAddContextContribution.js +2 -2
- package/vscode/src/vs/workbench/contrib/mcp/browser/mcpCommands.js +61 -61
- package/vscode/src/vs/workbench/contrib/mcp/browser/mcpElicitationService.js +25 -25
- package/vscode/src/vs/workbench/contrib/mcp/browser/mcpLanguageFeatures.js +22 -22
- package/vscode/src/vs/workbench/contrib/mcp/browser/mcpMigration.js +4 -4
- package/vscode/src/vs/workbench/contrib/mcp/browser/mcpResourceQuickAccess.js +8 -8
- package/vscode/src/vs/workbench/contrib/mcp/browser/mcpServerEditor.js +37 -37
- package/vscode/src/vs/workbench/contrib/mcp/browser/mcpServerEditorInput.js +2 -2
- package/vscode/src/vs/workbench/contrib/mcp/browser/mcpServerIcons.js +5 -5
- package/vscode/src/vs/workbench/contrib/mcp/browser/mcpServerWidgets.js +98 -6
- package/vscode/src/vs/workbench/contrib/mcp/browser/mcpServersView.d.ts +59 -0
- package/vscode/src/vs/workbench/contrib/mcp/browser/mcpServersView.js +576 -0
- package/vscode/src/vs/workbench/contrib/mcp/browser/mcpWorkbenchService.js +10 -10
- package/vscode/src/vs/workbench/contrib/mcp/browser/media/mcpServersView.css +53 -0
- package/vscode/src/vs/workbench/contrib/mcp/common/discovery/extensionMcpDiscovery.js +4 -4
- package/vscode/src/vs/workbench/contrib/mcp/common/discovery/nativeMcpDiscoveryAbstract.js +1 -1
- package/vscode/src/vs/workbench/contrib/mcp/common/mcpContextKeys.js +4 -4
- package/vscode/src/vs/workbench/contrib/mcp/common/mcpLanguageModelToolContribution.js +6 -6
- package/vscode/src/vs/workbench/contrib/mcp/common/mcpRegistry.js +13 -13
- package/vscode/src/vs/workbench/contrib/mcp/common/mcpSamplingLog.js +1 -1
- package/vscode/src/vs/workbench/contrib/mcp/common/mcpSamplingService.js +11 -11
- package/vscode/src/vs/workbench/contrib/mcp/common/mcpSandboxService.js +3 -3
- package/vscode/src/vs/workbench/contrib/mcp/common/mcpServerConnection.js +3 -3
- package/vscode/src/vs/workbench/services/authentication/browser/authenticationMcpService.js +9 -9
|
@@ -0,0 +1,576 @@
|
|
|
1
|
+
|
|
2
|
+
import { registerCss } from '@codingame/monaco-vscode-api/css';
|
|
3
|
+
import { __decorate, __param } from '@codingame/monaco-vscode-api/external/tslib/tslib.es6';
|
|
4
|
+
import * as mcpServersView from './media/mcpServersView.css';
|
|
5
|
+
import { append, $ } from '@codingame/monaco-vscode-api/vscode/vs/base/browser/dom';
|
|
6
|
+
import { ActionBar } from '@codingame/monaco-vscode-api/vscode/vs/base/browser/ui/actionbar/actionbar';
|
|
7
|
+
import { Event, Emitter } from '@codingame/monaco-vscode-api/vscode/vs/base/common/event';
|
|
8
|
+
import { createMarkdownCommandLink, MarkdownString } from '@codingame/monaco-vscode-api/vscode/vs/base/common/htmlContent';
|
|
9
|
+
import { MutableDisposable, DisposableStore, isDisposable, combinedDisposable, dispose, Disposable } from '@codingame/monaco-vscode-api/vscode/vs/base/common/lifecycle';
|
|
10
|
+
import { PagedModel, DelayedPagedModel, IterativePagedModel } from '@codingame/monaco-vscode-api/vscode/vs/base/common/paging';
|
|
11
|
+
import { localize, localize2 } from '@codingame/monaco-vscode-api/vscode/vs/nls';
|
|
12
|
+
import { IConfigurationService } from '@codingame/monaco-vscode-api/vscode/vs/platform/configuration/common/configuration.service';
|
|
13
|
+
import { ContextKeyExpr, ContextKeyDefinedExpr } from '@codingame/monaco-vscode-api/vscode/vs/platform/contextkey/common/contextkey';
|
|
14
|
+
import { IContextKeyService } from '@codingame/monaco-vscode-api/vscode/vs/platform/contextkey/common/contextkey.service';
|
|
15
|
+
import { IContextMenuService } from '@codingame/monaco-vscode-api/vscode/vs/platform/contextview/browser/contextView.service';
|
|
16
|
+
import { IDialogService } from '@codingame/monaco-vscode-api/vscode/vs/platform/dialogs/common/dialogs.service';
|
|
17
|
+
import { IHoverService } from '@codingame/monaco-vscode-api/vscode/vs/platform/hover/browser/hover.service';
|
|
18
|
+
import { IInstantiationService } from '@codingame/monaco-vscode-api/vscode/vs/platform/instantiation/common/instantiation';
|
|
19
|
+
import { IKeybindingService } from '@codingame/monaco-vscode-api/vscode/vs/platform/keybinding/common/keybinding.service';
|
|
20
|
+
import { WorkbenchPagedList } from '@codingame/monaco-vscode-api/vscode/vs/platform/list/browser/listService';
|
|
21
|
+
import { INotificationService } from '@codingame/monaco-vscode-api/vscode/vs/platform/notification/common/notification.service';
|
|
22
|
+
import { IOpenerService } from '@codingame/monaco-vscode-api/vscode/vs/platform/opener/common/opener.service';
|
|
23
|
+
import { IThemeService } from '@codingame/monaco-vscode-api/vscode/vs/platform/theme/common/themeService.service';
|
|
24
|
+
import { getLocationBasedViewColors } from '@codingame/monaco-vscode-api/vscode/vs/workbench/browser/parts/views/viewPane';
|
|
25
|
+
import { ViewContainerLocation, Extensions } from '@codingame/monaco-vscode-api/vscode/vs/workbench/common/views';
|
|
26
|
+
import { IViewDescriptorService } from '@codingame/monaco-vscode-api/vscode/vs/workbench/common/views.service';
|
|
27
|
+
import { McpServerContainers, McpServerEnablementState, InstalledMcpServersViewId, HasInstalledMcpServersContext, McpServersGalleryStatusContext } from '@codingame/monaco-vscode-api/vscode/vs/workbench/contrib/mcp/common/mcpTypes';
|
|
28
|
+
import { IMcpWorkbenchService } from '@codingame/monaco-vscode-api/vscode/vs/workbench/contrib/mcp/common/mcpTypes.service';
|
|
29
|
+
import { getContextMenuActions, DropDownAction, McpServerStatusAction, InstallAction, InstallingLabelAction, ManageMcpServerAction } from '@codingame/monaco-vscode-api/vscode/vs/workbench/contrib/mcp/browser/mcpServerActions';
|
|
30
|
+
import { McpServerIconWidget, PublisherWidget, StarredWidget, McpServerScopeBadgeWidget, McpServerHoverWidget } from './mcpServerWidgets.js';
|
|
31
|
+
import { ActionRunner, Separator } from '@codingame/monaco-vscode-api/vscode/vs/base/common/actions';
|
|
32
|
+
import { mcpGalleryServiceEnablementConfig, mcpGalleryServiceUrlConfig } from '@codingame/monaco-vscode-api/vscode/vs/platform/mcp/common/mcpManagement';
|
|
33
|
+
import { ThemeIcon } from '@codingame/monaco-vscode-api/vscode/vs/base/common/themables';
|
|
34
|
+
import { alert } from '@codingame/monaco-vscode-api/vscode/vs/base/browser/ui/aria/aria';
|
|
35
|
+
import { Registry } from '@codingame/monaco-vscode-api/vscode/vs/platform/registry/common/platform';
|
|
36
|
+
import { SyncDescriptor } from '@codingame/monaco-vscode-api/vscode/vs/platform/instantiation/common/descriptors';
|
|
37
|
+
import { DefaultViewsContext, SearchMcpServersContext } from '@codingame/monaco-vscode-api/vscode/vs/workbench/contrib/extensions/common/extensions';
|
|
38
|
+
import { VIEW_CONTAINER } from '@codingame/monaco-vscode-api/vscode/vs/workbench/contrib/extensions/browser/extensions.contribution';
|
|
39
|
+
import { ChatContextKeys } from '@codingame/monaco-vscode-api/vscode/vs/workbench/contrib/chat/common/actions/chatContextKeys';
|
|
40
|
+
import { Button } from '@codingame/monaco-vscode-api/vscode/vs/base/browser/ui/button/button';
|
|
41
|
+
import { defaultButtonStyles } from '@codingame/monaco-vscode-api/vscode/vs/platform/theme/browser/defaultStyles';
|
|
42
|
+
import { AbstractExtensionsListView } from '@codingame/monaco-vscode-api/vscode/vs/workbench/contrib/extensions/browser/extensionsViews';
|
|
43
|
+
import { HoverPosition } from '@codingame/monaco-vscode-api/vscode/vs/base/browser/ui/hover/hoverWidget';
|
|
44
|
+
import { Position } from '@codingame/monaco-vscode-api/vscode/vs/workbench/services/layout/browser/layoutService';
|
|
45
|
+
import { IWorkbenchLayoutService } from '@codingame/monaco-vscode-api/vscode/vs/workbench/services/layout/browser/layoutService.service';
|
|
46
|
+
import { mcpServerIcon } from './mcpServerIcons.js';
|
|
47
|
+
import { McpGalleryManifestStatus } from '@codingame/monaco-vscode-api/vscode/vs/platform/mcp/common/mcpGalleryManifest';
|
|
48
|
+
import { IMcpGalleryManifestService } from '@codingame/monaco-vscode-api/vscode/vs/platform/mcp/common/mcpGalleryManifest.service';
|
|
49
|
+
import { ProductQualityContext } from '@codingame/monaco-vscode-api/vscode/vs/platform/contextkey/common/contextkeys';
|
|
50
|
+
import { SeverityIcon } from '@codingame/monaco-vscode-api/vscode/vs/base/browser/ui/severityIcon/severityIcon';
|
|
51
|
+
import { IMarkdownRendererService } from '@codingame/monaco-vscode-api/vscode/vs/platform/markdown/browser/markdownRenderer.service';
|
|
52
|
+
import { ILogService } from '@codingame/monaco-vscode-api/vscode/vs/platform/log/common/log.service';
|
|
53
|
+
import { buildModalNavigationForPagedList } from '@codingame/monaco-vscode-api/vscode/vs/workbench/contrib/extensions/browser/extensionsViewer';
|
|
54
|
+
|
|
55
|
+
var McpServerRenderer_1;
|
|
56
|
+
registerCss(mcpServersView);
|
|
57
|
+
let McpServersListView = class McpServersListView extends AbstractExtensionsListView {
|
|
58
|
+
constructor(
|
|
59
|
+
mpcViewOptions,
|
|
60
|
+
options,
|
|
61
|
+
keybindingService,
|
|
62
|
+
contextMenuService,
|
|
63
|
+
instantiationService,
|
|
64
|
+
themeService,
|
|
65
|
+
hoverService,
|
|
66
|
+
configurationService,
|
|
67
|
+
contextKeyService,
|
|
68
|
+
viewDescriptorService,
|
|
69
|
+
openerService,
|
|
70
|
+
dialogService,
|
|
71
|
+
mcpWorkbenchService,
|
|
72
|
+
mcpGalleryManifestService,
|
|
73
|
+
layoutService,
|
|
74
|
+
markdownRendererService,
|
|
75
|
+
logService
|
|
76
|
+
) {
|
|
77
|
+
super(
|
|
78
|
+
options,
|
|
79
|
+
keybindingService,
|
|
80
|
+
contextMenuService,
|
|
81
|
+
configurationService,
|
|
82
|
+
contextKeyService,
|
|
83
|
+
viewDescriptorService,
|
|
84
|
+
instantiationService,
|
|
85
|
+
openerService,
|
|
86
|
+
themeService,
|
|
87
|
+
hoverService
|
|
88
|
+
);
|
|
89
|
+
this.mpcViewOptions = mpcViewOptions;
|
|
90
|
+
this.dialogService = dialogService;
|
|
91
|
+
this.mcpWorkbenchService = mcpWorkbenchService;
|
|
92
|
+
this.mcpGalleryManifestService = mcpGalleryManifestService;
|
|
93
|
+
this.layoutService = layoutService;
|
|
94
|
+
this.markdownRendererService = markdownRendererService;
|
|
95
|
+
this.logService = logService;
|
|
96
|
+
this.list = null;
|
|
97
|
+
this.listContainer = null;
|
|
98
|
+
this.welcomeContainer = null;
|
|
99
|
+
this.contextMenuActionRunner = this._register(( new ActionRunner()));
|
|
100
|
+
this.modalNavigationDisposable = this._register(( new MutableDisposable()));
|
|
101
|
+
}
|
|
102
|
+
renderBody(container) {
|
|
103
|
+
super.renderBody(container);
|
|
104
|
+
this.welcomeContainer = append(container, $(".mcp-welcome-container.hide"));
|
|
105
|
+
this.createWelcomeContent(this.welcomeContainer);
|
|
106
|
+
const messageContainer = append(container, $(".message-container"));
|
|
107
|
+
const messageSeverityIcon = append(messageContainer, $(""));
|
|
108
|
+
const messageBox = append(messageContainer, $(".message"));
|
|
109
|
+
const mcpServersList = $(".mcp-servers-list");
|
|
110
|
+
this.bodyTemplate = {
|
|
111
|
+
mcpServersList,
|
|
112
|
+
messageBox,
|
|
113
|
+
messageContainer,
|
|
114
|
+
messageSeverityIcon
|
|
115
|
+
};
|
|
116
|
+
this.listContainer = append(container, mcpServersList);
|
|
117
|
+
this.list = this._register(
|
|
118
|
+
this.instantiationService.createInstance(WorkbenchPagedList, `${this.id}-MCP-Servers`, this.listContainer, {
|
|
119
|
+
getHeight() {
|
|
120
|
+
return 72;
|
|
121
|
+
},
|
|
122
|
+
getTemplateId: () => McpServerRenderer.templateId
|
|
123
|
+
}, [this.instantiationService.createInstance(McpServerRenderer, {
|
|
124
|
+
hoverOptions: {
|
|
125
|
+
position: () => {
|
|
126
|
+
const viewLocation = this.viewDescriptorService.getViewLocationById(this.id);
|
|
127
|
+
if (viewLocation === ViewContainerLocation.Sidebar) {
|
|
128
|
+
return this.layoutService.getSideBarPosition() === Position.LEFT ? HoverPosition.RIGHT : HoverPosition.LEFT;
|
|
129
|
+
}
|
|
130
|
+
if (viewLocation === ViewContainerLocation.AuxiliaryBar) {
|
|
131
|
+
return this.layoutService.getSideBarPosition() === Position.LEFT ? HoverPosition.LEFT : HoverPosition.RIGHT;
|
|
132
|
+
}
|
|
133
|
+
return HoverPosition.RIGHT;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
})], {
|
|
137
|
+
multipleSelectionSupport: false,
|
|
138
|
+
setRowLineHeight: false,
|
|
139
|
+
horizontalScrolling: false,
|
|
140
|
+
accessibilityProvider: {
|
|
141
|
+
getAriaLabel(mcpServer) {
|
|
142
|
+
return mcpServer?.label ?? "";
|
|
143
|
+
},
|
|
144
|
+
getWidgetAriaLabel() {
|
|
145
|
+
return localize(11126, "MCP Servers");
|
|
146
|
+
}
|
|
147
|
+
},
|
|
148
|
+
overrideStyles: getLocationBasedViewColors(this.viewDescriptorService.getViewLocationById(this.id)).listOverrideStyles,
|
|
149
|
+
openOnSingleClick: true
|
|
150
|
+
})
|
|
151
|
+
);
|
|
152
|
+
this._register(Event.debounce(
|
|
153
|
+
Event.filter(this.list.onDidOpen, e => e.element !== null),
|
|
154
|
+
(_, event) => event,
|
|
155
|
+
75,
|
|
156
|
+
true
|
|
157
|
+
)(options => {
|
|
158
|
+
this.mcpWorkbenchService.open(options.element, {
|
|
159
|
+
...options.editorOptions,
|
|
160
|
+
modal: options.sideBySide ? undefined : buildModalNavigationForPagedList(
|
|
161
|
+
options.element,
|
|
162
|
+
() => this.list?.model,
|
|
163
|
+
(serverA, serverB) => serverA.id === serverB.id,
|
|
164
|
+
(server, modal) => this.mcpWorkbenchService.open(server, {
|
|
165
|
+
pinned: false,
|
|
166
|
+
modal
|
|
167
|
+
}),
|
|
168
|
+
this.modalNavigationDisposable,
|
|
169
|
+
this.logService
|
|
170
|
+
)
|
|
171
|
+
});
|
|
172
|
+
}));
|
|
173
|
+
this._register(this.list.onContextMenu(e => this.onContextMenu(e), this));
|
|
174
|
+
if (this.input) {
|
|
175
|
+
this.renderInput();
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
async onContextMenu(e) {
|
|
179
|
+
if (e.element) {
|
|
180
|
+
const disposables = ( new DisposableStore());
|
|
181
|
+
const mcpServer = e.element ? this.mcpWorkbenchService.local.find(local => local.id === e.element.id) || e.element : e.element;
|
|
182
|
+
const groups = getContextMenuActions(mcpServer, false, this.instantiationService);
|
|
183
|
+
const actions = [];
|
|
184
|
+
for (const menuActions of groups) {
|
|
185
|
+
for (const menuAction of menuActions) {
|
|
186
|
+
actions.push(menuAction);
|
|
187
|
+
if (isDisposable(menuAction)) {
|
|
188
|
+
disposables.add(menuAction);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
actions.push(( new Separator()));
|
|
192
|
+
}
|
|
193
|
+
actions.pop();
|
|
194
|
+
this.contextMenuService.showContextMenu({
|
|
195
|
+
getAnchor: () => e.anchor,
|
|
196
|
+
getActions: () => actions,
|
|
197
|
+
actionRunner: this.contextMenuActionRunner,
|
|
198
|
+
onHide: () => disposables.dispose()
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
layoutBody(height, width) {
|
|
203
|
+
super.layoutBody(height, width);
|
|
204
|
+
this.list?.layout(height, width);
|
|
205
|
+
}
|
|
206
|
+
async show(query) {
|
|
207
|
+
if (this.input) {
|
|
208
|
+
this.input.disposables.dispose();
|
|
209
|
+
this.input = undefined;
|
|
210
|
+
}
|
|
211
|
+
if (this.mpcViewOptions.showWelcome) {
|
|
212
|
+
this.input = {
|
|
213
|
+
model: ( new PagedModel([])),
|
|
214
|
+
disposables: ( new DisposableStore()),
|
|
215
|
+
showWelcomeContent: true
|
|
216
|
+
};
|
|
217
|
+
} else {
|
|
218
|
+
this.input = await this.query(query.trim());
|
|
219
|
+
}
|
|
220
|
+
this.renderInput();
|
|
221
|
+
if (this.input.onDidChangeModel) {
|
|
222
|
+
this.input.disposables.add(this.input.onDidChangeModel(model => {
|
|
223
|
+
if (!this.input) {
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
this.input.model = model;
|
|
227
|
+
this.renderInput();
|
|
228
|
+
}));
|
|
229
|
+
}
|
|
230
|
+
return this.input.model;
|
|
231
|
+
}
|
|
232
|
+
renderInput() {
|
|
233
|
+
if (!this.input) {
|
|
234
|
+
return;
|
|
235
|
+
}
|
|
236
|
+
if (this.list) {
|
|
237
|
+
this.list.model = ( new DelayedPagedModel(this.input.model));
|
|
238
|
+
}
|
|
239
|
+
this.showWelcomeContent(!!this.input.showWelcomeContent);
|
|
240
|
+
if (!this.input.showWelcomeContent) {
|
|
241
|
+
this.updateBody();
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
showWelcomeContent(show) {
|
|
245
|
+
this.welcomeContainer?.classList.toggle("hide", !show);
|
|
246
|
+
this.listContainer?.classList.toggle("hide", show);
|
|
247
|
+
}
|
|
248
|
+
createWelcomeContent(welcomeContainer) {
|
|
249
|
+
const welcomeContent = append(welcomeContainer, $(".mcp-welcome-content"));
|
|
250
|
+
const iconContainer = append(welcomeContent, $(".mcp-welcome-icon"));
|
|
251
|
+
const iconElement = append(iconContainer, $("span"));
|
|
252
|
+
iconElement.className = ThemeIcon.asClassName(mcpServerIcon);
|
|
253
|
+
const title = append(welcomeContent, $(".mcp-welcome-title"));
|
|
254
|
+
title.textContent = ( localize(11127, "MCP Servers"));
|
|
255
|
+
const settingsCommandLink = ( createMarkdownCommandLink({
|
|
256
|
+
id: "workbench.action.openSettings",
|
|
257
|
+
arguments: [`@id:${mcpGalleryServiceEnablementConfig}`],
|
|
258
|
+
text: mcpGalleryServiceEnablementConfig,
|
|
259
|
+
tooltip: ( localize(11128, "Open Settings"))
|
|
260
|
+
}).toString());
|
|
261
|
+
const description = append(welcomeContent, $(".mcp-welcome-description"));
|
|
262
|
+
const markdownResult = this._register(this.markdownRendererService.render(( new MarkdownString(( localize(
|
|
263
|
+
11129,
|
|
264
|
+
"Browse and install [Model Context Protocol (MCP) servers](https://code.visualstudio.com/docs/copilot/customization/mcp-servers) directly from VS Code to extend agent mode with extra tools for connecting to databases, invoking APIs and performing specialized tasks."
|
|
265
|
+
)), {
|
|
266
|
+
isTrusted: {
|
|
267
|
+
enabledCommands: ["workbench.action.openSettings"]
|
|
268
|
+
}
|
|
269
|
+
})).appendMarkdown("\n\n").appendMarkdown(( localize(
|
|
270
|
+
11130,
|
|
271
|
+
"This feature is currently in preview. You can disable it anytime using the setting {0}.",
|
|
272
|
+
settingsCommandLink
|
|
273
|
+
)))));
|
|
274
|
+
description.appendChild(markdownResult.element);
|
|
275
|
+
const buttonContainer = append(welcomeContent, $(".mcp-welcome-button-container"));
|
|
276
|
+
const button = this._register(( new Button(buttonContainer, {
|
|
277
|
+
title: ( localize(11131, "Enable MCP Servers Marketplace")),
|
|
278
|
+
...defaultButtonStyles
|
|
279
|
+
})));
|
|
280
|
+
button.label = ( localize(11131, "Enable MCP Servers Marketplace"));
|
|
281
|
+
this._register(button.onDidClick(async () => {
|
|
282
|
+
const {
|
|
283
|
+
result
|
|
284
|
+
} = await this.dialogService.prompt({
|
|
285
|
+
type: "info",
|
|
286
|
+
message: ( localize(11132, "Enable MCP Servers Marketplace?")),
|
|
287
|
+
custom: {
|
|
288
|
+
markdownDetails: [{
|
|
289
|
+
markdown: ( new MarkdownString(( localize(
|
|
290
|
+
11130,
|
|
291
|
+
"This feature is currently in preview. You can disable it anytime using the setting {0}.",
|
|
292
|
+
settingsCommandLink
|
|
293
|
+
)), {
|
|
294
|
+
isTrusted: true
|
|
295
|
+
}))
|
|
296
|
+
}]
|
|
297
|
+
},
|
|
298
|
+
buttons: [{
|
|
299
|
+
label: ( localize(11133, "Enable")),
|
|
300
|
+
run: () => true
|
|
301
|
+
}, {
|
|
302
|
+
label: ( localize(11134, "Cancel")),
|
|
303
|
+
run: () => false
|
|
304
|
+
}]
|
|
305
|
+
});
|
|
306
|
+
if (result) {
|
|
307
|
+
await this.configurationService.updateValue(mcpGalleryServiceEnablementConfig, true);
|
|
308
|
+
}
|
|
309
|
+
}));
|
|
310
|
+
}
|
|
311
|
+
updateBody(message) {
|
|
312
|
+
if (this.bodyTemplate) {
|
|
313
|
+
const count = this.input?.model.length ?? 0;
|
|
314
|
+
this.bodyTemplate.mcpServersList.classList.toggle("hidden", count === 0);
|
|
315
|
+
this.bodyTemplate.messageContainer.classList.toggle("hidden", !message && count > 0);
|
|
316
|
+
if (this.isBodyVisible()) {
|
|
317
|
+
if (message) {
|
|
318
|
+
this.bodyTemplate.messageSeverityIcon.className = SeverityIcon.className(message.severity);
|
|
319
|
+
this.bodyTemplate.messageBox.textContent = message.text;
|
|
320
|
+
} else if (count === 0) {
|
|
321
|
+
this.bodyTemplate.messageSeverityIcon.className = "";
|
|
322
|
+
this.bodyTemplate.messageBox.textContent = ( localize(11135, "No MCP Servers found."));
|
|
323
|
+
}
|
|
324
|
+
if (this.bodyTemplate.messageBox.textContent) {
|
|
325
|
+
alert(this.bodyTemplate.messageBox.textContent);
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
async query(query) {
|
|
331
|
+
const disposables = ( new DisposableStore());
|
|
332
|
+
if (query) {
|
|
333
|
+
const servers = await this.mcpWorkbenchService.queryGallery({
|
|
334
|
+
text: query.replace("@mcp", "")
|
|
335
|
+
});
|
|
336
|
+
const model = disposables.add(( new IterativePagedModel(servers)));
|
|
337
|
+
return {
|
|
338
|
+
model,
|
|
339
|
+
disposables
|
|
340
|
+
};
|
|
341
|
+
}
|
|
342
|
+
const onDidChangeModel = disposables.add(( new Emitter()));
|
|
343
|
+
let servers = await this.mcpWorkbenchService.queryLocal();
|
|
344
|
+
disposables.add(Event.debounce(this.mcpWorkbenchService.onChange, () => undefined)(() => {
|
|
345
|
+
const mergedMcpServers = this.mergeChangedMcpServers(servers, [...this.mcpWorkbenchService.local]);
|
|
346
|
+
if (mergedMcpServers) {
|
|
347
|
+
servers = mergedMcpServers;
|
|
348
|
+
onDidChangeModel.fire(( new PagedModel(servers)));
|
|
349
|
+
}
|
|
350
|
+
}));
|
|
351
|
+
disposables.add(
|
|
352
|
+
this.mcpWorkbenchService.onReset(() => onDidChangeModel.fire(( new PagedModel([...this.mcpWorkbenchService.local]))))
|
|
353
|
+
);
|
|
354
|
+
return {
|
|
355
|
+
model: ( new PagedModel(servers)),
|
|
356
|
+
onDidChangeModel: onDidChangeModel.event,
|
|
357
|
+
disposables
|
|
358
|
+
};
|
|
359
|
+
}
|
|
360
|
+
mergeChangedMcpServers(mcpServers, newMcpServers) {
|
|
361
|
+
const oldMcpServers = [...mcpServers];
|
|
362
|
+
const findPreviousMcpServerIndex = from => {
|
|
363
|
+
let index = -1;
|
|
364
|
+
const previousMcpServerInNew = newMcpServers[from];
|
|
365
|
+
if (previousMcpServerInNew) {
|
|
366
|
+
index = oldMcpServers.findIndex(e => e.id === previousMcpServerInNew.id);
|
|
367
|
+
if (index === -1) {
|
|
368
|
+
return findPreviousMcpServerIndex(from - 1);
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
return index;
|
|
372
|
+
};
|
|
373
|
+
let hasChanged = false;
|
|
374
|
+
for (let index = 0; index < newMcpServers.length; index++) {
|
|
375
|
+
const newMcpServer = newMcpServers[index];
|
|
376
|
+
if (mcpServers.every(r => r.id !== newMcpServer.id)) {
|
|
377
|
+
hasChanged = true;
|
|
378
|
+
mcpServers.splice(findPreviousMcpServerIndex(index - 1) + 1, 0, newMcpServer);
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
for (let index = mcpServers.length - 1; index >= 0; index--) {
|
|
382
|
+
const oldMcpServer = mcpServers[index];
|
|
383
|
+
if (newMcpServers.every(r => r.id !== oldMcpServer.id) && ( newMcpServers.some(r => r.name === oldMcpServer.name))) {
|
|
384
|
+
hasChanged = true;
|
|
385
|
+
mcpServers.splice(index, 1);
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
if (!hasChanged) {
|
|
389
|
+
if (mcpServers.length === newMcpServers.length) {
|
|
390
|
+
for (let index = 0; index < newMcpServers.length; index++) {
|
|
391
|
+
if (mcpServers[index]?.id !== newMcpServers[index]?.id) {
|
|
392
|
+
hasChanged = true;
|
|
393
|
+
mcpServers = newMcpServers;
|
|
394
|
+
break;
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
return hasChanged ? mcpServers : undefined;
|
|
400
|
+
}
|
|
401
|
+
};
|
|
402
|
+
McpServersListView = ( __decorate([( __param(2, IKeybindingService)), ( __param(3, IContextMenuService)), ( __param(4, IInstantiationService)), ( __param(5, IThemeService)), ( __param(6, IHoverService)), ( __param(7, IConfigurationService)), ( __param(8, IContextKeyService)), ( __param(9, IViewDescriptorService)), ( __param(10, IOpenerService)), ( __param(11, IDialogService)), ( __param(12, IMcpWorkbenchService)), ( __param(13, IMcpGalleryManifestService)), ( __param(14, IWorkbenchLayoutService)), ( __param(15, IMarkdownRendererService)), ( __param(16, ILogService))], McpServersListView));
|
|
403
|
+
let McpServerRenderer = class McpServerRenderer {
|
|
404
|
+
static {
|
|
405
|
+
McpServerRenderer_1 = this;
|
|
406
|
+
}
|
|
407
|
+
static {
|
|
408
|
+
this.templateId = "mcpServer";
|
|
409
|
+
}
|
|
410
|
+
constructor(options, instantiationService, mcpWorkbenchService, notificationService) {
|
|
411
|
+
this.options = options;
|
|
412
|
+
this.instantiationService = instantiationService;
|
|
413
|
+
this.mcpWorkbenchService = mcpWorkbenchService;
|
|
414
|
+
this.notificationService = notificationService;
|
|
415
|
+
this.templateId = McpServerRenderer_1.templateId;
|
|
416
|
+
}
|
|
417
|
+
renderTemplate(root) {
|
|
418
|
+
const element = append(root, $(".mcp-server-item.extension-list-item"));
|
|
419
|
+
const iconContainer = append(element, $(".icon-container"));
|
|
420
|
+
const iconWidget = this.instantiationService.createInstance(McpServerIconWidget, iconContainer);
|
|
421
|
+
const details = append(element, $(".details"));
|
|
422
|
+
const headerContainer = append(details, $(".header-container"));
|
|
423
|
+
const header = append(headerContainer, $(".header"));
|
|
424
|
+
const name = append(header, $("span.name"));
|
|
425
|
+
const starred = append(header, $("span.ratings"));
|
|
426
|
+
const description = append(details, $(".description.ellipsis"));
|
|
427
|
+
const footer = append(details, $(".footer"));
|
|
428
|
+
const publisherWidget = this.instantiationService.createInstance(PublisherWidget, append(footer, $(".publisher-container")), true);
|
|
429
|
+
const actionbar = ( new ActionBar(footer, {
|
|
430
|
+
actionViewItemProvider: (action, options) => {
|
|
431
|
+
if (action instanceof DropDownAction) {
|
|
432
|
+
return action.createActionViewItem(options);
|
|
433
|
+
}
|
|
434
|
+
return undefined;
|
|
435
|
+
},
|
|
436
|
+
focusOnlyEnabledItems: true
|
|
437
|
+
}));
|
|
438
|
+
actionbar.setFocusable(false);
|
|
439
|
+
const actionBarListener = actionbar.onDidRun((
|
|
440
|
+
{
|
|
441
|
+
error
|
|
442
|
+
}
|
|
443
|
+
) => error && this.notificationService.error(error));
|
|
444
|
+
const mcpServerStatusAction = this.instantiationService.createInstance(McpServerStatusAction);
|
|
445
|
+
const actions = [
|
|
446
|
+
this.instantiationService.createInstance(InstallAction, true),
|
|
447
|
+
this.instantiationService.createInstance(InstallingLabelAction),
|
|
448
|
+
this.instantiationService.createInstance(ManageMcpServerAction, false),
|
|
449
|
+
mcpServerStatusAction
|
|
450
|
+
];
|
|
451
|
+
const widgets = [
|
|
452
|
+
iconWidget,
|
|
453
|
+
publisherWidget,
|
|
454
|
+
this.instantiationService.createInstance(StarredWidget, starred, true),
|
|
455
|
+
this.instantiationService.createInstance(McpServerScopeBadgeWidget, iconContainer),
|
|
456
|
+
this.instantiationService.createInstance(McpServerHoverWidget, {
|
|
457
|
+
target: root,
|
|
458
|
+
position: this.options.hoverOptions.position
|
|
459
|
+
}, mcpServerStatusAction)
|
|
460
|
+
];
|
|
461
|
+
const extensionContainers = this.instantiationService.createInstance(McpServerContainers, [...actions, ...widgets]);
|
|
462
|
+
actionbar.push(actions, {
|
|
463
|
+
icon: true,
|
|
464
|
+
label: true
|
|
465
|
+
});
|
|
466
|
+
const disposable = combinedDisposable(...actions, ...widgets, actionbar, actionBarListener, extensionContainers);
|
|
467
|
+
return {
|
|
468
|
+
root,
|
|
469
|
+
element,
|
|
470
|
+
name,
|
|
471
|
+
description,
|
|
472
|
+
starred,
|
|
473
|
+
disposables: [disposable],
|
|
474
|
+
actionbar,
|
|
475
|
+
mcpServerDisposables: [],
|
|
476
|
+
set mcpServer(mcpServer) {
|
|
477
|
+
extensionContainers.mcpServer = mcpServer;
|
|
478
|
+
}
|
|
479
|
+
};
|
|
480
|
+
}
|
|
481
|
+
renderPlaceholder(index, data) {
|
|
482
|
+
data.element.classList.add("loading");
|
|
483
|
+
data.mcpServerDisposables = dispose(data.mcpServerDisposables);
|
|
484
|
+
data.name.textContent = "";
|
|
485
|
+
data.description.textContent = "";
|
|
486
|
+
data.starred.style.display = "none";
|
|
487
|
+
data.mcpServer = null;
|
|
488
|
+
}
|
|
489
|
+
renderElement(mcpServer, index, data) {
|
|
490
|
+
data.element.classList.remove("loading");
|
|
491
|
+
data.mcpServerDisposables = dispose(data.mcpServerDisposables);
|
|
492
|
+
data.root.setAttribute("data-mcp-server-id", mcpServer.id);
|
|
493
|
+
data.name.textContent = mcpServer.label;
|
|
494
|
+
data.description.textContent = mcpServer.description;
|
|
495
|
+
data.starred.style.display = "";
|
|
496
|
+
data.mcpServer = mcpServer;
|
|
497
|
+
const updateEnablement = () => data.root.classList.toggle(
|
|
498
|
+
"disabled",
|
|
499
|
+
!!mcpServer.runtimeStatus?.state && mcpServer.runtimeStatus.state !== McpServerEnablementState.Enabled
|
|
500
|
+
);
|
|
501
|
+
updateEnablement();
|
|
502
|
+
data.mcpServerDisposables.push(this.mcpWorkbenchService.onChange(e => {
|
|
503
|
+
if (!e || e.id === mcpServer.id) {
|
|
504
|
+
updateEnablement();
|
|
505
|
+
}
|
|
506
|
+
}));
|
|
507
|
+
}
|
|
508
|
+
disposeElement(mcpServer, index, data) {
|
|
509
|
+
data.mcpServerDisposables = dispose(data.mcpServerDisposables);
|
|
510
|
+
}
|
|
511
|
+
disposeTemplate(data) {
|
|
512
|
+
data.mcpServerDisposables = dispose(data.mcpServerDisposables);
|
|
513
|
+
data.disposables = dispose(data.disposables);
|
|
514
|
+
}
|
|
515
|
+
};
|
|
516
|
+
McpServerRenderer = McpServerRenderer_1 = ( __decorate([( __param(1, IInstantiationService)), ( __param(2, IMcpWorkbenchService)), ( __param(3, INotificationService))], McpServerRenderer));
|
|
517
|
+
class DefaultBrowseMcpServersView extends McpServersListView {
|
|
518
|
+
renderBody(container) {
|
|
519
|
+
super.renderBody(container);
|
|
520
|
+
this._register(
|
|
521
|
+
this.mcpGalleryManifestService.onDidChangeMcpGalleryManifest(() => this.show())
|
|
522
|
+
);
|
|
523
|
+
}
|
|
524
|
+
async show() {
|
|
525
|
+
return super.show("@mcp");
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
class McpServersViewsContribution extends Disposable {
|
|
529
|
+
static {
|
|
530
|
+
this.ID = "workbench.mcp.servers.views.contribution";
|
|
531
|
+
}
|
|
532
|
+
constructor() {
|
|
533
|
+
super();
|
|
534
|
+
( Registry.as(Extensions.ViewsRegistry)).registerViews([{
|
|
535
|
+
id: InstalledMcpServersViewId,
|
|
536
|
+
name: ( localize2(11136, "MCP Servers - Installed")),
|
|
537
|
+
ctorDescriptor: ( new SyncDescriptor(McpServersListView, [{}])),
|
|
538
|
+
when: ( ContextKeyExpr.and(DefaultViewsContext, HasInstalledMcpServersContext, ( ChatContextKeys.Setup.hidden.negate()), ( ChatContextKeys.Setup.disabledInWorkspace.negate()))),
|
|
539
|
+
weight: 40,
|
|
540
|
+
order: 4,
|
|
541
|
+
canToggleVisibility: true
|
|
542
|
+
}, {
|
|
543
|
+
id: "workbench.views.mcp.default.marketplace",
|
|
544
|
+
name: ( localize2(11137, "MCP Servers")),
|
|
545
|
+
ctorDescriptor: ( new SyncDescriptor(DefaultBrowseMcpServersView, [{}])),
|
|
546
|
+
when: ( ContextKeyExpr.and(DefaultViewsContext, ( HasInstalledMcpServersContext.toNegated()), ( ChatContextKeys.Setup.hidden.negate()), ( ChatContextKeys.Setup.disabledInWorkspace.negate()), ( McpServersGalleryStatusContext.isEqualTo(McpGalleryManifestStatus.Available)), ( ContextKeyExpr.or(( ContextKeyDefinedExpr.create(`config.${mcpGalleryServiceUrlConfig}`)), ( ProductQualityContext.notEqualsTo("stable")), ( ContextKeyDefinedExpr.create(`config.${mcpGalleryServiceEnablementConfig}`)))))),
|
|
547
|
+
weight: 40,
|
|
548
|
+
order: 4,
|
|
549
|
+
canToggleVisibility: true
|
|
550
|
+
}, {
|
|
551
|
+
id: "workbench.views.mcp.marketplace",
|
|
552
|
+
name: ( localize2(11137, "MCP Servers")),
|
|
553
|
+
ctorDescriptor: ( new SyncDescriptor(McpServersListView, [{}])),
|
|
554
|
+
when: ( ContextKeyExpr.and(SearchMcpServersContext, ( ChatContextKeys.Setup.hidden.negate()), ( ChatContextKeys.Setup.disabledInWorkspace.negate()), ( McpServersGalleryStatusContext.isEqualTo(McpGalleryManifestStatus.Available)), ( ContextKeyExpr.or(( ContextKeyDefinedExpr.create(`config.${mcpGalleryServiceUrlConfig}`)), ( ProductQualityContext.notEqualsTo("stable")), ( ContextKeyDefinedExpr.create(`config.${mcpGalleryServiceEnablementConfig}`))))))
|
|
555
|
+
}, {
|
|
556
|
+
id: "workbench.views.mcp.default.welcomeView",
|
|
557
|
+
name: ( localize2(11137, "MCP Servers")),
|
|
558
|
+
ctorDescriptor: ( new SyncDescriptor(DefaultBrowseMcpServersView, [{
|
|
559
|
+
showWelcome: true
|
|
560
|
+
}])),
|
|
561
|
+
when: ( ContextKeyExpr.and(DefaultViewsContext, ( HasInstalledMcpServersContext.toNegated()), ( ChatContextKeys.Setup.hidden.negate()), ( ChatContextKeys.Setup.disabledInWorkspace.negate()), ( McpServersGalleryStatusContext.isEqualTo(McpGalleryManifestStatus.Available)), ( ( ContextKeyDefinedExpr.create(`config.${mcpGalleryServiceUrlConfig}`)).negate()), ( ProductQualityContext.isEqualTo("stable")), ( ( ContextKeyDefinedExpr.create(`config.${mcpGalleryServiceEnablementConfig}`)).negate()))),
|
|
562
|
+
weight: 40,
|
|
563
|
+
order: 4,
|
|
564
|
+
canToggleVisibility: true
|
|
565
|
+
}, {
|
|
566
|
+
id: "workbench.views.mcp.welcomeView",
|
|
567
|
+
name: ( localize2(11137, "MCP Servers")),
|
|
568
|
+
ctorDescriptor: ( new SyncDescriptor(McpServersListView, [{
|
|
569
|
+
showWelcome: true
|
|
570
|
+
}])),
|
|
571
|
+
when: ( ContextKeyExpr.and(SearchMcpServersContext, ( ChatContextKeys.Setup.hidden.negate()), ( ChatContextKeys.Setup.disabledInWorkspace.negate()), ( McpServersGalleryStatusContext.isEqualTo(McpGalleryManifestStatus.Available)), ( ( ContextKeyDefinedExpr.create(`config.${mcpGalleryServiceUrlConfig}`)).negate()), ( ProductQualityContext.isEqualTo("stable")), ( ( ContextKeyDefinedExpr.create(`config.${mcpGalleryServiceEnablementConfig}`)).negate())))
|
|
572
|
+
}], VIEW_CONTAINER);
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
export { DefaultBrowseMcpServersView, McpServersListView, McpServersViewsContribution };
|
|
@@ -450,7 +450,7 @@ let McpWorkbenchService = class McpWorkbenchService extends Disposable {
|
|
|
450
450
|
const existing = result.get(server.name);
|
|
451
451
|
if (existing) {
|
|
452
452
|
this.logService.warn(( localize(
|
|
453
|
-
|
|
453
|
+
11138,
|
|
454
454
|
"Overwriting mcp server '{0}' from {1} with {2}.",
|
|
455
455
|
server.name,
|
|
456
456
|
server.mcpResource.path,
|
|
@@ -463,7 +463,7 @@ let McpWorkbenchService = class McpWorkbenchService extends Disposable {
|
|
|
463
463
|
const existing = result.get(server.name);
|
|
464
464
|
if (existing) {
|
|
465
465
|
this.logService.warn(( localize(
|
|
466
|
-
|
|
466
|
+
11138,
|
|
467
467
|
"Overwriting mcp server '{0}' from {1} with {2}.",
|
|
468
468
|
server.name,
|
|
469
469
|
server.mcpResource.path,
|
|
@@ -476,7 +476,7 @@ let McpWorkbenchService = class McpWorkbenchService extends Disposable {
|
|
|
476
476
|
}
|
|
477
477
|
canInstall(mcpServer) {
|
|
478
478
|
if (!(mcpServer instanceof McpWorkbenchServer)) {
|
|
479
|
-
return ( new MarkdownString()).appendText(( localize(
|
|
479
|
+
return ( new MarkdownString()).appendText(( localize(11139, "The provided object is not an mcp server.")));
|
|
480
480
|
}
|
|
481
481
|
if (mcpServer.gallery) {
|
|
482
482
|
const result = this.mcpManagementService.canInstall(mcpServer.gallery);
|
|
@@ -493,7 +493,7 @@ let McpWorkbenchService = class McpWorkbenchService extends Disposable {
|
|
|
493
493
|
return result;
|
|
494
494
|
}
|
|
495
495
|
return ( new MarkdownString()).appendText(( localize(
|
|
496
|
-
|
|
496
|
+
11140,
|
|
497
497
|
"Cannot install the '{0}' MCP Server because it is not available in this setup.",
|
|
498
498
|
mcpServer.label
|
|
499
499
|
)));
|
|
@@ -600,7 +600,7 @@ let McpWorkbenchService = class McpWorkbenchService extends Disposable {
|
|
|
600
600
|
id: USER_CONFIG_ID,
|
|
601
601
|
key: "userLocalValue",
|
|
602
602
|
target: ConfigurationTarget.USER_LOCAL,
|
|
603
|
-
label: ( localize(
|
|
603
|
+
label: ( localize(11141, "Global in {0}", this.productService.nameShort)),
|
|
604
604
|
scope: StorageScope.PROFILE,
|
|
605
605
|
order: McpCollectionSortOrder.User,
|
|
606
606
|
uri: mcpResource,
|
|
@@ -789,7 +789,7 @@ let McpWorkbenchService = class McpWorkbenchService extends Disposable {
|
|
|
789
789
|
state: McpServerEnablementState.DisabledProfile,
|
|
790
790
|
message: {
|
|
791
791
|
severity: Severity.Info,
|
|
792
|
-
text: ( new MarkdownString(( localize(
|
|
792
|
+
text: ( new MarkdownString(( localize(11142, "This MCP server is disabled."))))
|
|
793
793
|
}
|
|
794
794
|
};
|
|
795
795
|
}
|
|
@@ -798,7 +798,7 @@ let McpWorkbenchService = class McpWorkbenchService extends Disposable {
|
|
|
798
798
|
state: McpServerEnablementState.DisabledWorkspace,
|
|
799
799
|
message: {
|
|
800
800
|
severity: Severity.Info,
|
|
801
|
-
text: ( new MarkdownString(( localize(
|
|
801
|
+
text: ( new MarkdownString(( localize(11143, "This MCP server is disabled for this workspace."))))
|
|
802
802
|
}
|
|
803
803
|
};
|
|
804
804
|
}
|
|
@@ -818,7 +818,7 @@ let McpWorkbenchService = class McpWorkbenchService extends Disposable {
|
|
|
818
818
|
message: {
|
|
819
819
|
severity: Severity.Warning,
|
|
820
820
|
text: ( new MarkdownString(( localize(
|
|
821
|
-
|
|
821
|
+
11144,
|
|
822
822
|
"This MCP Server is disabled because MCP servers are configured to be disabled in the Editor. Please check your [settings]({0}).",
|
|
823
823
|
settingsCommandLink
|
|
824
824
|
))))
|
|
@@ -832,7 +832,7 @@ let McpWorkbenchService = class McpWorkbenchService extends Disposable {
|
|
|
832
832
|
message: {
|
|
833
833
|
severity: Severity.Warning,
|
|
834
834
|
text: ( new MarkdownString(( localize(
|
|
835
|
-
|
|
835
|
+
11145,
|
|
836
836
|
"This MCP Server is disabled because it is configured to be disabled in the Editor. Please check your [settings]({0}).",
|
|
837
837
|
settingsCommandLink
|
|
838
838
|
))))
|
|
@@ -846,7 +846,7 @@ let McpWorkbenchService = class McpWorkbenchService extends Disposable {
|
|
|
846
846
|
message: {
|
|
847
847
|
severity: Severity.Warning,
|
|
848
848
|
text: ( new MarkdownString(( localize(
|
|
849
|
-
|
|
849
|
+
11145,
|
|
850
850
|
"This MCP Server is disabled because it is configured to be disabled in the Editor. Please check your [settings]({0}).",
|
|
851
851
|
settingsCommandLink
|
|
852
852
|
))))
|