@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.
Files changed (78) hide show
  1. package/README.md +40 -0
  2. package/lib/browser/ai-registry-frontend-module.d.ts +5 -0
  3. package/lib/browser/ai-registry-frontend-module.d.ts.map +1 -0
  4. package/lib/browser/ai-registry-frontend-module.js +45 -0
  5. package/lib/browser/ai-registry-frontend-module.js.map +1 -0
  6. package/lib/browser/ai-registry-toolbar-contribution.d.ts +9 -0
  7. package/lib/browser/ai-registry-toolbar-contribution.d.ts.map +1 -0
  8. package/lib/browser/ai-registry-toolbar-contribution.js +53 -0
  9. package/lib/browser/ai-registry-toolbar-contribution.js.map +1 -0
  10. package/lib/browser/mcp/mcp-entries.d.ts +40 -0
  11. package/lib/browser/mcp/mcp-entries.d.ts.map +1 -0
  12. package/lib/browser/mcp/mcp-entries.js +143 -0
  13. package/lib/browser/mcp/mcp-entries.js.map +1 -0
  14. package/lib/browser/mcp/mcp-extensions-contribution.d.ts +45 -0
  15. package/lib/browser/mcp/mcp-extensions-contribution.d.ts.map +1 -0
  16. package/lib/browser/mcp/mcp-extensions-contribution.js +198 -0
  17. package/lib/browser/mcp/mcp-extensions-contribution.js.map +1 -0
  18. package/lib/browser/mcp/mcp-extensions-contribution.spec.d.ts +2 -0
  19. package/lib/browser/mcp/mcp-extensions-contribution.spec.d.ts.map +1 -0
  20. package/lib/browser/mcp/mcp-extensions-contribution.spec.js +266 -0
  21. package/lib/browser/mcp/mcp-extensions-contribution.spec.js.map +1 -0
  22. package/lib/browser/mcp/mcp-install-service.d.ts +72 -0
  23. package/lib/browser/mcp/mcp-install-service.d.ts.map +1 -0
  24. package/lib/browser/mcp/mcp-install-service.js +255 -0
  25. package/lib/browser/mcp/mcp-install-service.js.map +1 -0
  26. package/lib/browser/mcp/mcp-install-service.spec.d.ts +2 -0
  27. package/lib/browser/mcp/mcp-install-service.spec.d.ts.map +1 -0
  28. package/lib/browser/mcp/mcp-install-service.spec.js +604 -0
  29. package/lib/browser/mcp/mcp-install-service.spec.js.map +1 -0
  30. package/lib/browser/mcp/mcp-registry-ui-bridge-impl.d.ts +27 -0
  31. package/lib/browser/mcp/mcp-registry-ui-bridge-impl.d.ts.map +1 -0
  32. package/lib/browser/mcp/mcp-registry-ui-bridge-impl.js +136 -0
  33. package/lib/browser/mcp/mcp-registry-ui-bridge-impl.js.map +1 -0
  34. package/lib/common/ai-registry-configuration.d.ts +28 -0
  35. package/lib/common/ai-registry-configuration.d.ts.map +1 -0
  36. package/lib/common/ai-registry-configuration.js +56 -0
  37. package/lib/common/ai-registry-configuration.js.map +1 -0
  38. package/lib/common/mcp/mcp-registry-entry-resolver.d.ts +12 -0
  39. package/lib/common/mcp/mcp-registry-entry-resolver.d.ts.map +1 -0
  40. package/lib/common/mcp/mcp-registry-entry-resolver.js +68 -0
  41. package/lib/common/mcp/mcp-registry-entry-resolver.js.map +1 -0
  42. package/lib/common/mcp/mcp-registry-entry-resolver.spec.d.ts +2 -0
  43. package/lib/common/mcp/mcp-registry-entry-resolver.spec.d.ts.map +1 -0
  44. package/lib/common/mcp/mcp-registry-entry-resolver.spec.js +230 -0
  45. package/lib/common/mcp/mcp-registry-entry-resolver.spec.js.map +1 -0
  46. package/lib/common/mcp/mcp-registry-types.d.ts +105 -0
  47. package/lib/common/mcp/mcp-registry-types.d.ts.map +1 -0
  48. package/lib/common/mcp/mcp-registry-types.js +18 -0
  49. package/lib/common/mcp/mcp-registry-types.js.map +1 -0
  50. package/lib/common/registry-fetch-service.d.ts +24 -0
  51. package/lib/common/registry-fetch-service.d.ts.map +1 -0
  52. package/lib/common/registry-fetch-service.js +72 -0
  53. package/lib/common/registry-fetch-service.js.map +1 -0
  54. package/lib/common/registry-fetch-service.spec.d.ts +2 -0
  55. package/lib/common/registry-fetch-service.spec.d.ts.map +1 -0
  56. package/lib/common/registry-fetch-service.spec.js +129 -0
  57. package/lib/common/registry-fetch-service.spec.js.map +1 -0
  58. package/lib/package.spec.d.ts +1 -0
  59. package/lib/package.spec.d.ts.map +1 -0
  60. package/lib/package.spec.js +26 -0
  61. package/lib/package.spec.js.map +1 -0
  62. package/package.json +50 -0
  63. package/src/browser/ai-registry-frontend-module.ts +48 -0
  64. package/src/browser/ai-registry-toolbar-contribution.ts +51 -0
  65. package/src/browser/mcp/mcp-entries.tsx +288 -0
  66. package/src/browser/mcp/mcp-extensions-contribution.spec.ts +294 -0
  67. package/src/browser/mcp/mcp-extensions-contribution.ts +199 -0
  68. package/src/browser/mcp/mcp-install-service.spec.ts +673 -0
  69. package/src/browser/mcp/mcp-install-service.ts +300 -0
  70. package/src/browser/mcp/mcp-registry-ui-bridge-impl.ts +130 -0
  71. package/src/browser/style/mcp-entries.css +56 -0
  72. package/src/common/ai-registry-configuration.ts +52 -0
  73. package/src/common/mcp/mcp-registry-entry-resolver.spec.ts +248 -0
  74. package/src/common/mcp/mcp-registry-entry-resolver.ts +68 -0
  75. package/src/common/mcp/mcp-registry-types.ts +119 -0
  76. package/src/common/registry-fetch-service.spec.ts +136 -0
  77. package/src/common/registry-fetch-service.ts +78 -0
  78. 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
+ }