@theia/ai-registry 1.73.0-next.10
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/README.md +40 -0
- package/lib/browser/ai-registry-frontend-module.d.ts +5 -0
- package/lib/browser/ai-registry-frontend-module.d.ts.map +1 -0
- package/lib/browser/ai-registry-frontend-module.js +45 -0
- package/lib/browser/ai-registry-frontend-module.js.map +1 -0
- package/lib/browser/ai-registry-toolbar-contribution.d.ts +9 -0
- package/lib/browser/ai-registry-toolbar-contribution.d.ts.map +1 -0
- package/lib/browser/ai-registry-toolbar-contribution.js +53 -0
- package/lib/browser/ai-registry-toolbar-contribution.js.map +1 -0
- package/lib/browser/mcp/mcp-entries.d.ts +40 -0
- package/lib/browser/mcp/mcp-entries.d.ts.map +1 -0
- package/lib/browser/mcp/mcp-entries.js +143 -0
- package/lib/browser/mcp/mcp-entries.js.map +1 -0
- package/lib/browser/mcp/mcp-extensions-contribution.d.ts +45 -0
- package/lib/browser/mcp/mcp-extensions-contribution.d.ts.map +1 -0
- package/lib/browser/mcp/mcp-extensions-contribution.js +198 -0
- package/lib/browser/mcp/mcp-extensions-contribution.js.map +1 -0
- package/lib/browser/mcp/mcp-extensions-contribution.spec.d.ts +2 -0
- package/lib/browser/mcp/mcp-extensions-contribution.spec.d.ts.map +1 -0
- package/lib/browser/mcp/mcp-extensions-contribution.spec.js +266 -0
- package/lib/browser/mcp/mcp-extensions-contribution.spec.js.map +1 -0
- package/lib/browser/mcp/mcp-install-service.d.ts +72 -0
- package/lib/browser/mcp/mcp-install-service.d.ts.map +1 -0
- package/lib/browser/mcp/mcp-install-service.js +255 -0
- package/lib/browser/mcp/mcp-install-service.js.map +1 -0
- package/lib/browser/mcp/mcp-install-service.spec.d.ts +2 -0
- package/lib/browser/mcp/mcp-install-service.spec.d.ts.map +1 -0
- package/lib/browser/mcp/mcp-install-service.spec.js +604 -0
- package/lib/browser/mcp/mcp-install-service.spec.js.map +1 -0
- package/lib/browser/mcp/mcp-registry-ui-bridge-impl.d.ts +27 -0
- package/lib/browser/mcp/mcp-registry-ui-bridge-impl.d.ts.map +1 -0
- package/lib/browser/mcp/mcp-registry-ui-bridge-impl.js +136 -0
- package/lib/browser/mcp/mcp-registry-ui-bridge-impl.js.map +1 -0
- package/lib/common/ai-registry-configuration.d.ts +28 -0
- package/lib/common/ai-registry-configuration.d.ts.map +1 -0
- package/lib/common/ai-registry-configuration.js +56 -0
- package/lib/common/ai-registry-configuration.js.map +1 -0
- package/lib/common/mcp/mcp-registry-entry-resolver.d.ts +12 -0
- package/lib/common/mcp/mcp-registry-entry-resolver.d.ts.map +1 -0
- package/lib/common/mcp/mcp-registry-entry-resolver.js +68 -0
- package/lib/common/mcp/mcp-registry-entry-resolver.js.map +1 -0
- package/lib/common/mcp/mcp-registry-entry-resolver.spec.d.ts +2 -0
- package/lib/common/mcp/mcp-registry-entry-resolver.spec.d.ts.map +1 -0
- package/lib/common/mcp/mcp-registry-entry-resolver.spec.js +230 -0
- package/lib/common/mcp/mcp-registry-entry-resolver.spec.js.map +1 -0
- package/lib/common/mcp/mcp-registry-types.d.ts +105 -0
- package/lib/common/mcp/mcp-registry-types.d.ts.map +1 -0
- package/lib/common/mcp/mcp-registry-types.js +18 -0
- package/lib/common/mcp/mcp-registry-types.js.map +1 -0
- package/lib/common/registry-fetch-service.d.ts +24 -0
- package/lib/common/registry-fetch-service.d.ts.map +1 -0
- package/lib/common/registry-fetch-service.js +72 -0
- package/lib/common/registry-fetch-service.js.map +1 -0
- package/lib/common/registry-fetch-service.spec.d.ts +2 -0
- package/lib/common/registry-fetch-service.spec.d.ts.map +1 -0
- package/lib/common/registry-fetch-service.spec.js +129 -0
- package/lib/common/registry-fetch-service.spec.js.map +1 -0
- package/lib/package.spec.d.ts +1 -0
- package/lib/package.spec.d.ts.map +1 -0
- package/lib/package.spec.js +26 -0
- package/lib/package.spec.js.map +1 -0
- package/package.json +50 -0
- package/src/browser/ai-registry-frontend-module.ts +48 -0
- package/src/browser/ai-registry-toolbar-contribution.ts +51 -0
- package/src/browser/mcp/mcp-entries.tsx +288 -0
- package/src/browser/mcp/mcp-extensions-contribution.spec.ts +294 -0
- package/src/browser/mcp/mcp-extensions-contribution.ts +199 -0
- package/src/browser/mcp/mcp-install-service.spec.ts +673 -0
- package/src/browser/mcp/mcp-install-service.ts +300 -0
- package/src/browser/mcp/mcp-registry-ui-bridge-impl.ts +130 -0
- package/src/browser/style/mcp-entries.css +56 -0
- package/src/common/ai-registry-configuration.ts +52 -0
- package/src/common/mcp/mcp-registry-entry-resolver.spec.ts +248 -0
- package/src/common/mcp/mcp-registry-entry-resolver.ts +68 -0
- package/src/common/mcp/mcp-registry-types.ts +119 -0
- package/src/common/registry-fetch-service.spec.ts +136 -0
- package/src/common/registry-fetch-service.ts +78 -0
- package/src/package.spec.ts +28 -0
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
// *****************************************************************************
|
|
2
|
+
// Copyright (C) 2026 EclipseSource GmbH and others.
|
|
3
|
+
//
|
|
4
|
+
// This program and the accompanying materials are made available under the
|
|
5
|
+
// terms of the Eclipse Public License v. 2.0 which is available at
|
|
6
|
+
// http://www.eclipse.org/legal/epl-2.0.
|
|
7
|
+
//
|
|
8
|
+
// This Source Code may also be made available under the following Secondary
|
|
9
|
+
// Licenses when the conditions for such availability set forth in the Eclipse
|
|
10
|
+
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
|
|
11
|
+
// with the GNU Classpath Exception which is available at
|
|
12
|
+
// https://www.gnu.org/software/classpath/license.html.
|
|
13
|
+
//
|
|
14
|
+
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
|
|
15
|
+
// *****************************************************************************
|
|
16
|
+
/* note: this bogus test file is required so that
|
|
17
|
+
we are able to run mocha unit tests on this
|
|
18
|
+
package, without having any actual unit tests in it.
|
|
19
|
+
This way a coverage report will be generated,
|
|
20
|
+
showing 0% coverage, instead of no report.
|
|
21
|
+
This file can be removed once we have real unit
|
|
22
|
+
tests in place. */
|
|
23
|
+
describe('ai-registry package', () => {
|
|
24
|
+
it('support code coverage statistics', () => true);
|
|
25
|
+
});
|
|
26
|
+
//# sourceMappingURL=package.spec.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"package.spec.js","sourceRoot":"","sources":["../src/package.spec.ts"],"names":[],"mappings":"AAAA,gFAAgF;AAChF,oDAAoD;AACpD,EAAE;AACF,2EAA2E;AAC3E,mEAAmE;AACnE,wCAAwC;AACxC,EAAE;AACF,4EAA4E;AAC5E,8EAA8E;AAC9E,6EAA6E;AAC7E,yDAAyD;AACzD,uDAAuD;AACvD,EAAE;AACF,gFAAgF;AAChF,gFAAgF;AAEhF;;;;;;qBAMqB;AAErB,QAAQ,CAAC,qBAAqB,EAAE,GAAG,EAAE;IAEjC,EAAE,CAAC,kCAAkC,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC;AACvD,CAAC,CAAC,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@theia/ai-registry",
|
|
3
|
+
"version": "1.73.0-next.10+20335bd3c",
|
|
4
|
+
"description": "Theia - AI Registry Integration",
|
|
5
|
+
"dependencies": {
|
|
6
|
+
"@theia/ai-mcp": "1.73.0-next.10+20335bd3c",
|
|
7
|
+
"@theia/core": "1.73.0-next.10+20335bd3c",
|
|
8
|
+
"@theia/vsx-registry": "1.73.0-next.10+20335bd3c"
|
|
9
|
+
},
|
|
10
|
+
"publishConfig": {
|
|
11
|
+
"access": "public"
|
|
12
|
+
},
|
|
13
|
+
"main": "lib/common",
|
|
14
|
+
"theiaExtensions": [
|
|
15
|
+
{
|
|
16
|
+
"frontend": "lib/browser/ai-registry-frontend-module"
|
|
17
|
+
}
|
|
18
|
+
],
|
|
19
|
+
"keywords": [
|
|
20
|
+
"theia-extension"
|
|
21
|
+
],
|
|
22
|
+
"license": "EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0",
|
|
23
|
+
"repository": {
|
|
24
|
+
"type": "git",
|
|
25
|
+
"url": "https://github.com/eclipse-theia/theia.git"
|
|
26
|
+
},
|
|
27
|
+
"bugs": {
|
|
28
|
+
"url": "https://github.com/eclipse-theia/theia/issues"
|
|
29
|
+
},
|
|
30
|
+
"homepage": "https://github.com/eclipse-theia/theia",
|
|
31
|
+
"files": [
|
|
32
|
+
"lib",
|
|
33
|
+
"src"
|
|
34
|
+
],
|
|
35
|
+
"scripts": {
|
|
36
|
+
"build": "theiaext build",
|
|
37
|
+
"clean": "theiaext clean",
|
|
38
|
+
"compile": "theiaext compile",
|
|
39
|
+
"lint": "theiaext lint",
|
|
40
|
+
"test": "theiaext test",
|
|
41
|
+
"watch": "theiaext watch"
|
|
42
|
+
},
|
|
43
|
+
"devDependencies": {
|
|
44
|
+
"@theia/ext-scripts": "1.72.0"
|
|
45
|
+
},
|
|
46
|
+
"nyc": {
|
|
47
|
+
"extends": "../../configs/nyc.json"
|
|
48
|
+
},
|
|
49
|
+
"gitHead": "20335bd3c402d6b5e0f40890ede8ab7c2ae0b909"
|
|
50
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
// *****************************************************************************
|
|
2
|
+
// Copyright (C) 2026 EclipseSource GmbH.
|
|
3
|
+
//
|
|
4
|
+
// This program and the accompanying materials are made available under the
|
|
5
|
+
// terms of the Eclipse Public License v. 2.0 which is available at
|
|
6
|
+
// http://www.eclipse.org/legal/epl-2.0.
|
|
7
|
+
//
|
|
8
|
+
// This Source Code may also be made available under the following Secondary
|
|
9
|
+
// Licenses when the conditions for such availability set forth in the Eclipse
|
|
10
|
+
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
|
|
11
|
+
// with the GNU Classpath Exception which is available at
|
|
12
|
+
// https://www.gnu.org/software/classpath/license.html.
|
|
13
|
+
//
|
|
14
|
+
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
|
|
15
|
+
// *****************************************************************************
|
|
16
|
+
|
|
17
|
+
import '../../src/browser/style/mcp-entries.css';
|
|
18
|
+
|
|
19
|
+
import { ContainerModule } from '@theia/core/shared/inversify';
|
|
20
|
+
import { TabBarToolbarContribution } from '@theia/core/lib/browser/shell/tab-bar-toolbar';
|
|
21
|
+
import { ExtensionsSourceContribution } from '@theia/vsx-registry/lib/browser/extensions-source-contribution';
|
|
22
|
+
import { MCPRegistryUiBridge } from '@theia/ai-mcp/lib/browser/mcp-registry-ui-bridge';
|
|
23
|
+
import { AIRegistryConfiguration } from '../common/ai-registry-configuration';
|
|
24
|
+
import { MCPRegistryEntryResolver, MCPRegistryEntryResolverImpl } from '../common/mcp/mcp-registry-entry-resolver';
|
|
25
|
+
import { RegistryFetchService, RegistryFetchServiceImpl } from '../common/registry-fetch-service';
|
|
26
|
+
import { MCPInstallService, MCPInstallServiceImpl } from './mcp/mcp-install-service';
|
|
27
|
+
import { MCPExtensionsContribution } from './mcp/mcp-extensions-contribution';
|
|
28
|
+
import { MCPRegistryUiBridgeImpl } from './mcp/mcp-registry-ui-bridge-impl';
|
|
29
|
+
import { AIRegistryToolbarContribution } from './ai-registry-toolbar-contribution';
|
|
30
|
+
|
|
31
|
+
export default new ContainerModule(bind => {
|
|
32
|
+
bind(AIRegistryConfiguration).toSelf().inSingletonScope();
|
|
33
|
+
bind(MCPRegistryEntryResolverImpl).toSelf().inSingletonScope();
|
|
34
|
+
bind(MCPRegistryEntryResolver).toService(MCPRegistryEntryResolverImpl);
|
|
35
|
+
bind(RegistryFetchServiceImpl).toSelf().inSingletonScope();
|
|
36
|
+
bind(RegistryFetchService).toService(RegistryFetchServiceImpl);
|
|
37
|
+
bind(MCPInstallServiceImpl).toSelf().inSingletonScope();
|
|
38
|
+
bind(MCPInstallService).toService(MCPInstallServiceImpl);
|
|
39
|
+
|
|
40
|
+
bind(MCPExtensionsContribution).toSelf().inSingletonScope();
|
|
41
|
+
bind(ExtensionsSourceContribution).toService(MCPExtensionsContribution);
|
|
42
|
+
|
|
43
|
+
bind(MCPRegistryUiBridgeImpl).toSelf().inSingletonScope();
|
|
44
|
+
bind(MCPRegistryUiBridge).toService(MCPRegistryUiBridgeImpl);
|
|
45
|
+
|
|
46
|
+
bind(AIRegistryToolbarContribution).toSelf().inSingletonScope();
|
|
47
|
+
bind(TabBarToolbarContribution).toService(AIRegistryToolbarContribution);
|
|
48
|
+
});
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
// *****************************************************************************
|
|
2
|
+
// Copyright (C) 2026 EclipseSource GmbH.
|
|
3
|
+
//
|
|
4
|
+
// This program and the accompanying materials are made available under the
|
|
5
|
+
// terms of the Eclipse Public License v. 2.0 which is available at
|
|
6
|
+
// http://www.eclipse.org/legal/epl-2.0.
|
|
7
|
+
//
|
|
8
|
+
// This Source Code may also be made available under the following Secondary
|
|
9
|
+
// Licenses when the conditions for such availability set forth in the Eclipse
|
|
10
|
+
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
|
|
11
|
+
// with the GNU Classpath Exception which is available at
|
|
12
|
+
// https://www.gnu.org/software/classpath/license.html.
|
|
13
|
+
//
|
|
14
|
+
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
|
|
15
|
+
// *****************************************************************************
|
|
16
|
+
|
|
17
|
+
import { CommandRegistry } from '@theia/core';
|
|
18
|
+
import { Widget } from '@theia/core/lib/browser';
|
|
19
|
+
import { TabBarToolbarContribution, TabBarToolbarRegistry } from '@theia/core/lib/browser/shell/tab-bar-toolbar';
|
|
20
|
+
import { ADD_MCP_SERVER_COMMAND } from '@theia/ai-mcp/lib/browser/mcp-configuration-command-contribution';
|
|
21
|
+
import { VSXExtensionsViewContainer } from '@theia/vsx-registry/lib/browser/vsx-extensions-view-container';
|
|
22
|
+
import { VSXExtensionsWidget } from '@theia/vsx-registry/lib/browser/vsx-extensions-widget';
|
|
23
|
+
import { inject, injectable } from '@theia/core/shared/inversify';
|
|
24
|
+
|
|
25
|
+
@injectable()
|
|
26
|
+
export class AIRegistryToolbarContribution implements TabBarToolbarContribution {
|
|
27
|
+
|
|
28
|
+
@inject(CommandRegistry)
|
|
29
|
+
protected readonly commandRegistry: CommandRegistry;
|
|
30
|
+
|
|
31
|
+
registerToolbarItems(registry: TabBarToolbarRegistry): void {
|
|
32
|
+
registry.registerItem({
|
|
33
|
+
id: 'ai-registry.addMcpServerManually',
|
|
34
|
+
command: ADD_MCP_SERVER_COMMAND.id,
|
|
35
|
+
group: 'other_1',
|
|
36
|
+
// The view container's own in-container items use `widget === getTabBarDelegate()`,
|
|
37
|
+
// which resolves to either the container itself (multi-part mode) or one of its
|
|
38
|
+
// child parts (single-part modes like Installed / Search). A separate toolbar
|
|
39
|
+
// contribution cannot reach the container instance, so we match both shapes by id
|
|
40
|
+
// and by the part-id prefix produced by `generateExtensionWidgetId`.
|
|
41
|
+
isVisible: (widget: Widget) =>
|
|
42
|
+
this.isVsxExtensionsWidget(widget)
|
|
43
|
+
&& this.commandRegistry.getCommand(ADD_MCP_SERVER_COMMAND.id) !== undefined
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
protected isVsxExtensionsWidget(widget: Widget): boolean {
|
|
48
|
+
return widget.id === VSXExtensionsViewContainer.ID
|
|
49
|
+
|| widget.id.startsWith(VSXExtensionsWidget.ID + ':');
|
|
50
|
+
}
|
|
51
|
+
}
|
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
// *****************************************************************************
|
|
2
|
+
// Copyright (C) 2026 EclipseSource GmbH.
|
|
3
|
+
//
|
|
4
|
+
// This program and the accompanying materials are made available under the
|
|
5
|
+
// terms of the Eclipse Public License v. 2.0 which is available at
|
|
6
|
+
// http://www.eclipse.org/legal/epl-2.0.
|
|
7
|
+
//
|
|
8
|
+
// This Source Code may also be made available under the following Secondary
|
|
9
|
+
// Licenses when the conditions for such availability set forth in the Eclipse
|
|
10
|
+
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
|
|
11
|
+
// with the GNU Classpath Exception which is available at
|
|
12
|
+
// https://www.gnu.org/software/classpath/license.html.
|
|
13
|
+
//
|
|
14
|
+
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
|
|
15
|
+
// *****************************************************************************
|
|
16
|
+
|
|
17
|
+
import * as React from '@theia/core/shared/react';
|
|
18
|
+
import { nls } from '@theia/core';
|
|
19
|
+
import { HoverService } from '@theia/core/lib/browser';
|
|
20
|
+
import { MarkdownStringImpl } from '@theia/core/lib/common/markdown-rendering';
|
|
21
|
+
import { TreeElement } from '@theia/core/lib/browser/source-tree';
|
|
22
|
+
import { TypeBadge } from '@theia/vsx-registry/lib/browser/type-badge';
|
|
23
|
+
import { ExtensionCard, ExtensionCardTrust } from '@theia/vsx-registry/lib/browser/extension-card';
|
|
24
|
+
import { MCPServerDescription } from '@theia/ai-mcp/lib/common/mcp-server-manager';
|
|
25
|
+
import { ClassificationResult, ResolvedRegistryEntry } from '../../common/mcp/mcp-registry-types';
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Per-entry action callbacks provided by the contribution. Entries close over
|
|
29
|
+
* these so their card render functions can dispatch user actions without
|
|
30
|
+
* pulling the install service through dependency injection at the React level.
|
|
31
|
+
*/
|
|
32
|
+
export interface MCPEntryHandlers {
|
|
33
|
+
install(entry: ResolvedRegistryEntry): Promise<void>;
|
|
34
|
+
uninstall(serverKey: string): Promise<void>;
|
|
35
|
+
unlink(serverKey: string): Promise<void>;
|
|
36
|
+
update(entry: ResolvedRegistryEntry): Promise<void>;
|
|
37
|
+
link(entry: ResolvedRegistryEntry): Promise<void>;
|
|
38
|
+
fixConfig(entry: ResolvedRegistryEntry): Promise<void>;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** An entry surfacing a locally installed MCP server in the Installed section. */
|
|
42
|
+
export class MCPInstalledEntry implements TreeElement {
|
|
43
|
+
|
|
44
|
+
readonly id: string;
|
|
45
|
+
|
|
46
|
+
constructor(
|
|
47
|
+
readonly local: MCPServerDescription,
|
|
48
|
+
readonly matchedEntry: ResolvedRegistryEntry | undefined,
|
|
49
|
+
readonly state: ClassificationResult,
|
|
50
|
+
readonly handlers: MCPEntryHandlers,
|
|
51
|
+
readonly hoverService: HoverService
|
|
52
|
+
) {
|
|
53
|
+
this.id = `mcp-installed-${local.name}`;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
render(): React.ReactNode {
|
|
57
|
+
const localVersion = this.local.registryMetadata?.version;
|
|
58
|
+
return (
|
|
59
|
+
<MCPCard
|
|
60
|
+
// When the registry knows this server we prefer its display name / description;
|
|
61
|
+
// otherwise fall back to whatever the user-edited local entry has.
|
|
62
|
+
title={this.matchedEntry?.name ?? this.local.name}
|
|
63
|
+
description={this.matchedEntry?.description}
|
|
64
|
+
// Show what the user has actually installed; fall back to the registry range
|
|
65
|
+
// for the unusual case where the local entry is missing a recorded version.
|
|
66
|
+
version={localVersion ?? this.matchedEntry?.version}
|
|
67
|
+
identifier={this.matchedEntry?.serverId ?? this.local.registryMetadata?.serverId}
|
|
68
|
+
verified={this.matchedEntry?.mcpRegistryVerified}
|
|
69
|
+
hoverService={this.hoverService}
|
|
70
|
+
actions={renderActions(this.state, this.matchedEntry, this.local.name, this.handlers, localVersion)}
|
|
71
|
+
/>
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/** An entry surfacing a registry-resolved MCP server in the Search Results section. */
|
|
77
|
+
export class MCPSearchResultEntry implements TreeElement {
|
|
78
|
+
|
|
79
|
+
readonly id: string;
|
|
80
|
+
|
|
81
|
+
constructor(
|
|
82
|
+
readonly entry: ResolvedRegistryEntry,
|
|
83
|
+
readonly state: ClassificationResult,
|
|
84
|
+
readonly handlers: MCPEntryHandlers,
|
|
85
|
+
readonly hoverService: HoverService
|
|
86
|
+
) {
|
|
87
|
+
this.id = `mcp-search-${entry.serverId}`;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
render(): React.ReactNode {
|
|
91
|
+
return (
|
|
92
|
+
<MCPCard
|
|
93
|
+
title={this.entry.name}
|
|
94
|
+
description={this.entry.description}
|
|
95
|
+
version={this.entry.version}
|
|
96
|
+
identifier={this.entry.serverId}
|
|
97
|
+
verified={this.entry.mcpRegistryVerified}
|
|
98
|
+
hoverService={this.hoverService}
|
|
99
|
+
actions={renderActions(this.state, this.entry, this.entry.localName, this.handlers)}
|
|
100
|
+
/>
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
interface MCPCardProps {
|
|
106
|
+
title: string;
|
|
107
|
+
description?: string;
|
|
108
|
+
version?: string;
|
|
109
|
+
/** Stable identifier shown in the publisher-row slot - typically the registry serverId. */
|
|
110
|
+
identifier?: string;
|
|
111
|
+
/** Drives the trust icon next to `identifier`: verified -> filled check, otherwise question mark. */
|
|
112
|
+
verified?: boolean;
|
|
113
|
+
hoverService: HoverService;
|
|
114
|
+
actions?: React.ReactNode;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/** Build the markdown tooltip shown on hover, mirroring the depth of the VSX card's tooltip. */
|
|
118
|
+
function buildHoverContent(props: MCPCardProps): MarkdownStringImpl {
|
|
119
|
+
const lines = [`**${props.title}**`];
|
|
120
|
+
if (props.version) {
|
|
121
|
+
lines.push('', `_${props.version}_`);
|
|
122
|
+
}
|
|
123
|
+
if (props.description) {
|
|
124
|
+
lines.push('', props.description);
|
|
125
|
+
}
|
|
126
|
+
if (props.identifier) {
|
|
127
|
+
lines.push('', `_${props.identifier}_`);
|
|
128
|
+
}
|
|
129
|
+
return new MarkdownStringImpl(lines.join('\n'));
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* MCP entry card. Renders against the shared {@link ExtensionCard} shell so MCP entries
|
|
134
|
+
* sit visually alongside extensions in the unified Extensions view, sharing the layout,
|
|
135
|
+
* trust icon and hover tooltip. The MCP-specific bits are the codicon icon, the type
|
|
136
|
+
* badge, the derived trust state and the action set.
|
|
137
|
+
*
|
|
138
|
+
* The trailing invisible settings-gear element reserves the same layout space VSX cards
|
|
139
|
+
* use for their context-menu gear, so MCP and VSX action bars align across the view.
|
|
140
|
+
*/
|
|
141
|
+
const MCPCard: React.FC<MCPCardProps> = props => {
|
|
142
|
+
const trust: ExtensionCardTrust = props.verified === true ? 'verified' : 'unknown';
|
|
143
|
+
return (
|
|
144
|
+
<ExtensionCard
|
|
145
|
+
title={props.title}
|
|
146
|
+
version={props.version}
|
|
147
|
+
description={props.description}
|
|
148
|
+
icon={<i className="codicon codicon-mcp" />}
|
|
149
|
+
iconClassName="theia-mcp-extension-icon"
|
|
150
|
+
typeBadge={
|
|
151
|
+
<TypeBadge
|
|
152
|
+
icon={<i className="codicon codicon-mcp" />}
|
|
153
|
+
label={nls.localizeByDefault('MCP')}
|
|
154
|
+
variant="mcp"
|
|
155
|
+
/>
|
|
156
|
+
}
|
|
157
|
+
publisher={props.identifier}
|
|
158
|
+
publisherTitle={props.identifier}
|
|
159
|
+
trust={trust}
|
|
160
|
+
hover={{ content: buildHoverContent(props), hoverService: props.hoverService }}
|
|
161
|
+
actions={
|
|
162
|
+
<div className="theia-mcp-extension-actions">
|
|
163
|
+
{props.actions}
|
|
164
|
+
<div
|
|
165
|
+
className="codicon codicon-settings-gear action theia-mcp-extension-gear-placeholder"
|
|
166
|
+
aria-hidden="true"
|
|
167
|
+
/>
|
|
168
|
+
</div>
|
|
169
|
+
}
|
|
170
|
+
/>
|
|
171
|
+
);
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Buttons are state-driven and identical in the Installed and Search sections, mirroring
|
|
176
|
+
* how VSX renders extension actions. State conveys itself through which buttons appear:
|
|
177
|
+
*
|
|
178
|
+
* - `not-installed` -> [Install]
|
|
179
|
+
* - `installed-from-registry` -> [Update?] [Uninstall] (Update only when newer version is available)
|
|
180
|
+
* - `installed-manually` -> [Link to registry] (no Uninstall - user wanted only the link affordance)
|
|
181
|
+
* - `fix-config` -> warning [Fix config] [Uninstall]
|
|
182
|
+
* - `installed-link-stale` -> warning "Not in registry" [Unlink] [Uninstall]
|
|
183
|
+
* - `installed-user-added` -> filtered out before reaching this view
|
|
184
|
+
*/
|
|
185
|
+
function renderActions(
|
|
186
|
+
state: ClassificationResult,
|
|
187
|
+
registryEntry: ResolvedRegistryEntry | undefined,
|
|
188
|
+
localName: string,
|
|
189
|
+
handlers: MCPEntryHandlers,
|
|
190
|
+
localVersion?: string
|
|
191
|
+
): React.ReactNode {
|
|
192
|
+
switch (state.kind) {
|
|
193
|
+
case 'not-installed':
|
|
194
|
+
return registryEntry && (
|
|
195
|
+
<button className="theia-button prominent action" onClick={() => handlers.install(registryEntry)}>
|
|
196
|
+
{nls.localizeByDefault('Install')}
|
|
197
|
+
</button>
|
|
198
|
+
);
|
|
199
|
+
case 'installed-from-registry':
|
|
200
|
+
return (
|
|
201
|
+
<>
|
|
202
|
+
{state.updateAvailable && registryEntry && (
|
|
203
|
+
<button
|
|
204
|
+
className="theia-button prominent action"
|
|
205
|
+
title={
|
|
206
|
+
registryEntry.version && registryEntry.version !== localVersion
|
|
207
|
+
? nls.localize(
|
|
208
|
+
'theia/ai-registry/action/updateTooltip',
|
|
209
|
+
'Update from {0} to {1}',
|
|
210
|
+
localVersion ?? '?',
|
|
211
|
+
registryEntry.version
|
|
212
|
+
)
|
|
213
|
+
: nls.localizeByDefault('Update')
|
|
214
|
+
}
|
|
215
|
+
onClick={() => handlers.update(registryEntry)}
|
|
216
|
+
>
|
|
217
|
+
{nls.localizeByDefault('Update')}
|
|
218
|
+
</button>
|
|
219
|
+
)}
|
|
220
|
+
<button className="theia-button action" onClick={() => handlers.uninstall(localName)}>
|
|
221
|
+
{nls.localizeByDefault('Uninstall')}
|
|
222
|
+
</button>
|
|
223
|
+
</>
|
|
224
|
+
);
|
|
225
|
+
case 'installed-manually':
|
|
226
|
+
// No Uninstall here by design - the only meaningful action is to link
|
|
227
|
+
// the existing local server to the registry so future updates are tracked.
|
|
228
|
+
return registryEntry && (
|
|
229
|
+
<button className="theia-button action" onClick={() => handlers.link(registryEntry)}>
|
|
230
|
+
{nls.localize('theia/ai-registry/action/link', 'Link to registry')}
|
|
231
|
+
</button>
|
|
232
|
+
);
|
|
233
|
+
case 'fix-config':
|
|
234
|
+
return (
|
|
235
|
+
<>
|
|
236
|
+
<i
|
|
237
|
+
className="codicon codicon-warning theia-mcp-extension-warning"
|
|
238
|
+
title={nls.localize(
|
|
239
|
+
'theia/ai-registry/warning/fixConfig',
|
|
240
|
+
"This server's configuration differs from the registry. Click 'Fix config' to restore it."
|
|
241
|
+
)}
|
|
242
|
+
/>
|
|
243
|
+
{registryEntry && (
|
|
244
|
+
<button className="theia-button prominent action" onClick={() => handlers.fixConfig(registryEntry)}>
|
|
245
|
+
{nls.localize('theia/ai-registry/action/fix', 'Fix config')}
|
|
246
|
+
</button>
|
|
247
|
+
)}
|
|
248
|
+
<button className="theia-button action" onClick={() => handlers.uninstall(localName)}>
|
|
249
|
+
{nls.localizeByDefault('Uninstall')}
|
|
250
|
+
</button>
|
|
251
|
+
</>
|
|
252
|
+
);
|
|
253
|
+
case 'installed-link-stale': {
|
|
254
|
+
// The registry no longer lists the linked serverId. The server itself may
|
|
255
|
+
// still work, so we offer Unlink (drop the registry link, keep the config)
|
|
256
|
+
// as the soft option and keep Uninstall available for users who want the
|
|
257
|
+
// server gone. Wording stays cautious - we don't assert the server works,
|
|
258
|
+
// we let the user decide.
|
|
259
|
+
const tooltip = nls.localize(
|
|
260
|
+
'theia/ai-registry/warning/linkStale',
|
|
261
|
+
'This server was installed from the registry, but the registry no longer lists it. '
|
|
262
|
+
+ 'If you are sure you want to keep it and it still works for you, click Unlink to drop '
|
|
263
|
+
+ 'the registry link. Otherwise click Uninstall to remove the server.'
|
|
264
|
+
);
|
|
265
|
+
return (
|
|
266
|
+
<>
|
|
267
|
+
<span className="theia-mcp-extension-link-stale-message" title={tooltip}>
|
|
268
|
+
<i className="codicon codicon-warning theia-mcp-extension-warning" />
|
|
269
|
+
{nls.localize('theia/ai-registry/warning/notInRegistry', 'Not in registry')}
|
|
270
|
+
</span>
|
|
271
|
+
<button
|
|
272
|
+
className="theia-button action"
|
|
273
|
+
title={tooltip}
|
|
274
|
+
onClick={() => handlers.unlink(localName)}
|
|
275
|
+
>
|
|
276
|
+
{nls.localize('theia/ai-registry/action/unlink', 'Unlink')}
|
|
277
|
+
</button>
|
|
278
|
+
<button className="theia-button action" onClick={() => handlers.uninstall(localName)}>
|
|
279
|
+
{nls.localizeByDefault('Uninstall')}
|
|
280
|
+
</button>
|
|
281
|
+
</>
|
|
282
|
+
);
|
|
283
|
+
}
|
|
284
|
+
case 'installed-user-added':
|
|
285
|
+
// Filtered out of resolveInstalled and never returned from search; render nothing defensively.
|
|
286
|
+
return undefined;
|
|
287
|
+
}
|
|
288
|
+
}
|