@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 @@
1
+ {"version":3,"file":"mcp-extensions-contribution.js","sourceRoot":"","sources":["../../../src/browser/mcp/mcp-extensions-contribution.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=mcp-extensions-contribution.spec.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp-extensions-contribution.spec.d.ts","sourceRoot":"","sources":["../../../src/browser/mcp/mcp-extensions-contribution.spec.ts"],"names":[],"mappings":""}
@@ -0,0 +1,266 @@
1
+ "use strict";
2
+ // *****************************************************************************
3
+ // Copyright (C) 2026 EclipseSource GmbH.
4
+ //
5
+ // This program and the accompanying materials are made available under the
6
+ // terms of the Eclipse Public License v. 2.0 which is available at
7
+ // http://www.eclipse.org/legal/epl-2.0.
8
+ //
9
+ // This Source Code may also be made available under the following Secondary
10
+ // Licenses when the conditions for such availability set forth in the Eclipse
11
+ // Public License v. 2.0 are satisfied: GNU General Public License, version 2
12
+ // with the GNU Classpath Exception which is available at
13
+ // https://www.gnu.org/software/classpath/license.html.
14
+ //
15
+ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
16
+ // *****************************************************************************
17
+ Object.defineProperty(exports, "__esModule", { value: true });
18
+ const frontend_application_config_provider_1 = require("@theia/core/lib/browser/frontend-application-config-provider");
19
+ const jsdom_1 = require("@theia/core/lib/browser/test/jsdom");
20
+ // Entries render against the shared ExtensionCard, which pulls in browser-side modules,
21
+ // so a DOM is required at import time.
22
+ const disableJSDOM = (0, jsdom_1.enableJSDOM)();
23
+ try {
24
+ frontend_application_config_provider_1.FrontendApplicationConfigProvider.get();
25
+ }
26
+ catch {
27
+ frontend_application_config_provider_1.FrontendApplicationConfigProvider.set({});
28
+ }
29
+ const chai_1 = require("chai");
30
+ const inversify_1 = require("@theia/core/shared/inversify");
31
+ const core_1 = require("@theia/core");
32
+ const browser_1 = require("@theia/core/lib/browser");
33
+ const mcp_preferences_1 = require("@theia/ai-mcp/lib/common/mcp-preferences");
34
+ const mcp_server_manager_1 = require("@theia/ai-mcp/lib/common/mcp-server-manager");
35
+ const mcp_server_editor_1 = require("@theia/ai-mcp/lib/browser/mcp-server-editor");
36
+ const mcp_server_install_dialog_1 = require("@theia/ai-mcp/lib/browser/mcp-server-install-dialog");
37
+ const registry_fetch_service_1 = require("../../common/registry-fetch-service");
38
+ const mcp_registry_entry_resolver_1 = require("../../common/mcp/mcp-registry-entry-resolver");
39
+ const mcp_install_service_1 = require("./mcp-install-service");
40
+ const mcp_extensions_contribution_1 = require("./mcp-extensions-contribution");
41
+ after(() => disableJSDOM());
42
+ class FakePreferenceService {
43
+ constructor() {
44
+ this.store = new Map();
45
+ this.listeners = [];
46
+ }
47
+ get(key, defaultValue) {
48
+ return (this.store.has(key) ? this.store.get(key) : defaultValue);
49
+ }
50
+ async set(key, value) {
51
+ this.store.set(key, value);
52
+ for (const l of this.listeners) {
53
+ l({ preferenceName: key });
54
+ }
55
+ }
56
+ onPreferenceChanged(listener) {
57
+ this.listeners.push(listener);
58
+ return { dispose: () => { } };
59
+ }
60
+ }
61
+ class StubRegistryFetchService {
62
+ constructor(entries = []) {
63
+ this.entries = entries;
64
+ this.onDidChangeEmitter = new core_1.Emitter();
65
+ this.onDidChange = this.onDidChangeEmitter.event;
66
+ }
67
+ async getEntries() {
68
+ return this.entries;
69
+ }
70
+ }
71
+ function buildContainer(prefs, fetch) {
72
+ const container = new inversify_1.Container();
73
+ container.bind(core_1.PreferenceService).toConstantValue(prefs);
74
+ container.bind(registry_fetch_service_1.RegistryFetchService).toConstantValue(fetch);
75
+ container.bind(mcp_registry_entry_resolver_1.MCPRegistryEntryResolverImpl).toSelf().inSingletonScope();
76
+ container.bind(mcp_registry_entry_resolver_1.MCPRegistryEntryResolver).toService(mcp_registry_entry_resolver_1.MCPRegistryEntryResolverImpl);
77
+ // Editor dependencies — the contribution doesn't invoke the install path here, but
78
+ // MCPInstallService now injects MCPServerEditor so we stub its required services.
79
+ container.bind(core_1.MessageService).toConstantValue({ error: () => undefined });
80
+ container.bind(mcp_server_manager_1.MCPFrontendService).toConstantValue({});
81
+ container.bind(browser_1.HoverService).toConstantValue({ requestHover: () => undefined });
82
+ // The contribution doesn't open dialogs in these tests; bind factories that fail loudly if used.
83
+ container.bind(mcp_server_editor_1.MCPServerEditDialogFactory).toConstantValue(() => {
84
+ throw new Error('MCPServerEditDialogFactory should not be invoked in these tests');
85
+ });
86
+ container.bind(mcp_server_install_dialog_1.MCPServerInstallDialogFactory).toConstantValue(() => {
87
+ throw new Error('MCPServerInstallDialogFactory should not be invoked in these tests');
88
+ });
89
+ container.bind(mcp_server_editor_1.MCPServerEditorImpl).toSelf().inSingletonScope();
90
+ container.bind(mcp_server_editor_1.MCPServerEditor).toService(mcp_server_editor_1.MCPServerEditorImpl);
91
+ container.bind(mcp_install_service_1.MCPInstallServiceImpl).toSelf().inSingletonScope();
92
+ container.bind(mcp_install_service_1.MCPInstallService).toService(mcp_install_service_1.MCPInstallServiceImpl);
93
+ container.bind(mcp_extensions_contribution_1.MCPExtensionsContribution).toSelf().inSingletonScope();
94
+ return container;
95
+ }
96
+ const exampleRegistryEntry = {
97
+ serverId: 'io.github.example/example-mcp',
98
+ name: 'Example',
99
+ description: 'Example MCP server',
100
+ localName: 'example',
101
+ config: { command: 'npx', args: ['-y', 'example-mcp'] },
102
+ version: '^1.0.0',
103
+ configHash: 'hash-v1',
104
+ mcpRegistryVerified: true
105
+ };
106
+ describe('MCPExtensionsContribution.resolveInstalled', () => {
107
+ it('only surfaces servers tied to a registry entry; user-added entries are filtered out', async () => {
108
+ const prefs = new FakePreferenceService();
109
+ await prefs.set(mcp_preferences_1.MCP_SERVERS_PREF, {
110
+ // Linked: matches a registry entry by serverId.
111
+ example: {
112
+ command: 'npx',
113
+ args: ['-y', 'example-mcp'],
114
+ registryMetadata: {
115
+ serverId: exampleRegistryEntry.serverId,
116
+ version: exampleRegistryEntry.version,
117
+ configHash: exampleRegistryEntry.configHash
118
+ }
119
+ },
120
+ // User-added: no registry counterpart at all.
121
+ standalone: { command: 'node', args: ['srv.js'] }
122
+ });
123
+ const fetch = new StubRegistryFetchService([exampleRegistryEntry]);
124
+ const contribution = buildContainer(prefs, fetch).get(mcp_extensions_contribution_1.MCPExtensionsContribution);
125
+ const entries = [...await contribution.resolveInstalled()];
126
+ (0, chai_1.expect)(entries.map(e => e.local.name)).to.deep.equal(['example']);
127
+ (0, chai_1.expect)(entries[0].state).to.deep.equal({ kind: 'installed-from-registry', updateAvailable: false });
128
+ });
129
+ it('surfaces installed-link-stale entries so the view can render the warning + Unlink/Uninstall actions', async () => {
130
+ const prefs = new FakePreferenceService();
131
+ await prefs.set(mcp_preferences_1.MCP_SERVERS_PREF, {
132
+ stale: {
133
+ command: 'npx',
134
+ args: ['-y', 'gone-mcp'],
135
+ registryMetadata: {
136
+ serverId: 'io.github.example/gone-server',
137
+ version: '^1.0.0'
138
+ }
139
+ }
140
+ });
141
+ const fetch = new StubRegistryFetchService([exampleRegistryEntry]);
142
+ const contribution = buildContainer(prefs, fetch).get(mcp_extensions_contribution_1.MCPExtensionsContribution);
143
+ const entries = [...await contribution.resolveInstalled()];
144
+ (0, chai_1.expect)(entries.map(e => e.local.name)).to.deep.equal(['stale']);
145
+ (0, chai_1.expect)(entries[0].state).to.deep.equal({ kind: 'installed-link-stale' });
146
+ });
147
+ it('shows installed-link-stale (Unlink + Uninstall) when the local is linked to a missing id, even if the key still matches a registry entry', async () => {
148
+ // PR scenario: user installs from the registry, then manually rewrites
149
+ // `registryMetadata.serverId` to a value that no longer exists. The local key
150
+ // still matches a registry `localName`, but the broken id linkage must take
151
+ // precedence - the entry has to surface as link-stale so the user sees the
152
+ // Unlink + Uninstall affordances and the warning, not Link (which would suggest
153
+ // the entry is merely unlinked).
154
+ const prefs = new FakePreferenceService();
155
+ await prefs.set(mcp_preferences_1.MCP_SERVERS_PREF, {
156
+ example: {
157
+ command: 'npx',
158
+ args: ['-y', 'example-mcp'],
159
+ registryMetadata: {
160
+ serverId: 'io.example/gone',
161
+ version: '^1.0.0',
162
+ configHash: 'hash-v1'
163
+ }
164
+ }
165
+ });
166
+ const fetch = new StubRegistryFetchService([exampleRegistryEntry]);
167
+ const contribution = buildContainer(prefs, fetch).get(mcp_extensions_contribution_1.MCPExtensionsContribution);
168
+ const entries = [...await contribution.resolveInstalled()];
169
+ (0, chai_1.expect)(entries).to.have.length(1);
170
+ (0, chai_1.expect)(entries[0].state).to.deep.equal({ kind: 'installed-link-stale' });
171
+ });
172
+ it('skips malformed stored entries whose `command` is not a string — mere key presence is not enough', async () => {
173
+ const prefs = new FakePreferenceService();
174
+ await prefs.set(mcp_preferences_1.MCP_SERVERS_PREF, {
175
+ // `command` is present but typed wrong; the shared MCPServersPreference.isValue
176
+ // guard must reject this so it doesn't get cast to MCPServerDescription.
177
+ broken: { command: 42, args: ['-y', 'whatever'] },
178
+ example: {
179
+ command: 'npx',
180
+ args: ['-y', 'example-mcp'],
181
+ registryMetadata: {
182
+ serverId: exampleRegistryEntry.serverId,
183
+ version: exampleRegistryEntry.version,
184
+ configHash: exampleRegistryEntry.configHash
185
+ }
186
+ }
187
+ });
188
+ const fetch = new StubRegistryFetchService([exampleRegistryEntry]);
189
+ const contribution = buildContainer(prefs, fetch).get(mcp_extensions_contribution_1.MCPExtensionsContribution);
190
+ const entries = [...await contribution.resolveInstalled()];
191
+ (0, chai_1.expect)(entries.map(e => e.local.name)).to.deep.equal(['example']);
192
+ });
193
+ });
194
+ describe('MCPExtensionsContribution.resolveSearchResults', () => {
195
+ const githubEntry = {
196
+ serverId: 'io.github.github/github-mcp-server',
197
+ name: 'GitHub',
198
+ description: 'Connect AI assistants to GitHub repositories',
199
+ localName: 'github',
200
+ config: { command: 'docker', args: ['run', '-i', '--rm'] },
201
+ version: '^1.0.0',
202
+ configHash: 'hash-github',
203
+ mcpRegistryVerified: true
204
+ };
205
+ it('returns nothing for an empty or whitespace-only query', async () => {
206
+ const prefs = new FakePreferenceService();
207
+ const fetch = new StubRegistryFetchService([exampleRegistryEntry, githubEntry]);
208
+ const contribution = buildContainer(prefs, fetch).get(mcp_extensions_contribution_1.MCPExtensionsContribution);
209
+ const empty = [...await contribution.resolveSearchResults('', { verifiedOnly: false })];
210
+ const whitespace = [...await contribution.resolveSearchResults(' ', { verifiedOnly: false })];
211
+ (0, chai_1.expect)(empty).to.be.empty;
212
+ (0, chai_1.expect)(whitespace).to.be.empty;
213
+ });
214
+ it('returns all entries with searchableText covering name, serverId and description so the global fuzzy ranker can match any of them', async () => {
215
+ const prefs = new FakePreferenceService();
216
+ const fetch = new StubRegistryFetchService([exampleRegistryEntry, githubEntry]);
217
+ const contribution = buildContainer(prefs, fetch).get(mcp_extensions_contribution_1.MCPExtensionsContribution);
218
+ // The contribution intentionally does not filter by the query string: that's the
219
+ // view's job (`VSXExtensionsSource.collectSearchResults` runs `FuzzySearch.filter`
220
+ // across results from every contribution). The contribution's only responsibility
221
+ // is to supply candidates with rich `searchableText`.
222
+ const results = [...await contribution.resolveSearchResults('REPOSITORIES', { verifiedOnly: false })];
223
+ (0, chai_1.expect)(results).to.have.length(2);
224
+ const githubResult = results.find(r => r.element.entry.serverId === githubEntry.serverId);
225
+ (0, chai_1.expect)(githubResult.searchableText).to.contain(githubEntry.name);
226
+ (0, chai_1.expect)(githubResult.searchableText).to.contain(githubEntry.serverId);
227
+ (0, chai_1.expect)(githubResult.searchableText).to.contain(githubEntry.description);
228
+ });
229
+ it('classifies a registry entry as installed-link-stale when the matching-key local is linked to a server id missing from the registry', async () => {
230
+ // PR scenario: user installed `example` from the registry, then manually pointed
231
+ // its `registryMetadata.serverId` at an id not in the registry. In Search results
232
+ // the matching registry entry must show the **Unlink / Uninstall** affordances -
233
+ // mirroring what the Installed view shows - instead of **Link** (which would
234
+ // imply the local is merely unlinked rather than stale-linked).
235
+ const prefs = new FakePreferenceService();
236
+ await prefs.set(mcp_preferences_1.MCP_SERVERS_PREF, {
237
+ example: {
238
+ command: 'npx',
239
+ args: ['-y', 'example-mcp'],
240
+ registryMetadata: {
241
+ serverId: 'io.example/gone',
242
+ version: '^1.0.0',
243
+ configHash: 'hash-v1'
244
+ }
245
+ }
246
+ });
247
+ const fetch = new StubRegistryFetchService([exampleRegistryEntry]);
248
+ const contribution = buildContainer(prefs, fetch).get(mcp_extensions_contribution_1.MCPExtensionsContribution);
249
+ const results = [...await contribution.resolveSearchResults('example', { verifiedOnly: false })];
250
+ const exampleResult = results.find(r => r.element.entry.serverId === exampleRegistryEntry.serverId);
251
+ (0, chai_1.expect)(exampleResult, 'registry entry must surface in search results').to.not.be.undefined;
252
+ (0, chai_1.expect)(exampleResult.element.state).to.deep.equal({ kind: 'installed-link-stale' });
253
+ });
254
+ it('omits unverified entries when verifiedOnly is true and keeps verified ones', async () => {
255
+ const unverified = { ...exampleRegistryEntry, mcpRegistryVerified: false };
256
+ const prefs = new FakePreferenceService();
257
+ const fetch = new StubRegistryFetchService([unverified, githubEntry]);
258
+ const contribution = buildContainer(prefs, fetch).get(mcp_extensions_contribution_1.MCPExtensionsContribution);
259
+ const all = [...await contribution.resolveSearchResults('example', { verifiedOnly: false })];
260
+ const verifiedOnly = [...await contribution.resolveSearchResults('example', { verifiedOnly: true })];
261
+ (0, chai_1.expect)(all).to.have.length(2);
262
+ (0, chai_1.expect)(verifiedOnly).to.have.length(1);
263
+ (0, chai_1.expect)(verifiedOnly[0].element.entry.serverId).to.equal(githubEntry.serverId);
264
+ });
265
+ });
266
+ //# sourceMappingURL=mcp-extensions-contribution.spec.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp-extensions-contribution.spec.js","sourceRoot":"","sources":["../../../src/browser/mcp/mcp-extensions-contribution.spec.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,72 @@
1
+ import { PreferenceService } from '@theia/core';
2
+ import { MCPInstallEntryConfig, MCPRegistryMetadata, MCPServerDescription } from '@theia/ai-mcp/lib/common/mcp-server-manager';
3
+ import { MCPInstallOverrides, MCPServerEditor } from '@theia/ai-mcp/lib/browser/mcp-server-editor';
4
+ import { ClassificationResult, ResolvedRegistryEntry } from '../../common/mcp/mcp-registry-types';
5
+ export { MCPInstallOverrides };
6
+ type StoredEntry = MCPInstallEntryConfig & {
7
+ autostart?: boolean;
8
+ registryMetadata?: MCPRegistryMetadata;
9
+ };
10
+ type StoredServers = Record<string, StoredEntry>;
11
+ export declare const MCPInstallService: unique symbol;
12
+ export interface MCPInstallService {
13
+ /** Installs a registry entry, applying any user-supplied overrides collected by the install dialog. */
14
+ install(entry: ResolvedRegistryEntry, overrides?: MCPInstallOverrides): Promise<void>;
15
+ /** Restores a drifted entry's registry-owned config fields while preserving user-owned ones. */
16
+ fixConfig(entry: ResolvedRegistryEntry): Promise<void>;
17
+ /** Applies a newer registry approval to an installed entry, preserving user-supplied additions. */
18
+ update(entry: ResolvedRegistryEntry): Promise<void>;
19
+ /** Links an existing local server to a registry entry by stamping its registry metadata. */
20
+ link(entry: ResolvedRegistryEntry): Promise<void>;
21
+ /** Drops the registry link from a local server while keeping its config intact. */
22
+ unlink(name: string): Promise<void>;
23
+ /** Removes an installed server entry by its local preference key. */
24
+ uninstall(name: string): Promise<void>;
25
+ /** Classifies a locally stored server against the registry (for the Installed view). */
26
+ classifyLocalServer(local: MCPServerDescription, registryEntries: ResolvedRegistryEntry[]): ClassificationResult;
27
+ /** Classifies a registry entry against the locally stored servers (for the Search view). */
28
+ classifyRegistryEntry(entry: ResolvedRegistryEntry, locals: MCPServerDescription[], registryEntries: ResolvedRegistryEntry[]): ClassificationResult;
29
+ }
30
+ export declare class MCPInstallServiceImpl implements MCPInstallService {
31
+ protected readonly preferenceService: PreferenceService;
32
+ protected readonly editor: MCPServerEditor;
33
+ /** Delegates to the generic editor so both registry installs and `install-mcp` URL handlers go through the same code path. */
34
+ install(entry: ResolvedRegistryEntry, overrides?: MCPInstallOverrides): Promise<void>;
35
+ fixConfig(entry: ResolvedRegistryEntry): Promise<void>;
36
+ update(entry: ResolvedRegistryEntry): Promise<void>;
37
+ link(entry: ResolvedRegistryEntry): Promise<void>;
38
+ /**
39
+ * Drop the registry link from a local server while keeping its config intact.
40
+ * Used to convert a stale-linked entry (registry no longer lists the serverId)
41
+ * into a plain user-added entry without losing the user's running server config.
42
+ */
43
+ unlink(name: string): Promise<void>;
44
+ uninstall(name: string): Promise<void>;
45
+ protected readServers(): StoredServers;
46
+ protected writeServers(next: StoredServers): Promise<void>;
47
+ /** Registry-managed metadata block written onto every linked server entry. */
48
+ protected metadata(entry: ResolvedRegistryEntry): MCPRegistryMetadata;
49
+ classifyLocalServer(local: MCPServerDescription, registryEntries: ResolvedRegistryEntry[]): ClassificationResult;
50
+ classifyRegistryEntry(entry: ResolvedRegistryEntry, locals: MCPServerDescription[], registryEntries: ResolvedRegistryEntry[]): ClassificationResult;
51
+ /**
52
+ * Classify a local server that is already linked to a registry entry. Either the
53
+ * registry config matches (eligible for Update when the registry has published a new
54
+ * approval - detected via `configHash`) or it has drifted away (`fix-config`).
55
+ *
56
+ * Update detection uses `registryMetadata.configHash` exclusively.
57
+ * `registryMetadata.version` is kept on the local entry for display only; the
58
+ * registry may publish a new version without changing the install config, in which
59
+ * case we still want to offer Update.
60
+ */
61
+ protected classifyLinked(entry: ResolvedRegistryEntry, local: MCPServerDescription): ClassificationResult;
62
+ /**
63
+ * True when the registry's `configHash` differs from the locally stored
64
+ * `registryMetadata.configHash`. Returns false when the registry has no `configHash`
65
+ * to compare against (older payloads) - without a hash we cannot make a confident
66
+ * "update available" decision and prefer not to nag the user.
67
+ */
68
+ protected isUpdateAvailable(entry: ResolvedRegistryEntry, local: MCPServerDescription): boolean;
69
+ protected matchesByConfig(entry: ResolvedRegistryEntry, local: MCPServerDescription): boolean;
70
+ protected envMatches(registryEnv: Record<string, string> | undefined, localEnv: Record<string, string> | undefined): boolean;
71
+ }
72
+ //# sourceMappingURL=mcp-install-service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp-install-service.d.ts","sourceRoot":"","sources":["../../../src/browser/mcp/mcp-install-service.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,255 @@
1
+ "use strict";
2
+ // *****************************************************************************
3
+ // Copyright (C) 2026 EclipseSource GmbH.
4
+ //
5
+ // This program and the accompanying materials are made available under the
6
+ // terms of the Eclipse Public License v. 2.0 which is available at
7
+ // http://www.eclipse.org/legal/epl-2.0.
8
+ //
9
+ // This Source Code may also be made available under the following Secondary
10
+ // Licenses when the conditions for such availability set forth in the Eclipse
11
+ // Public License v. 2.0 are satisfied: GNU General Public License, version 2
12
+ // with the GNU Classpath Exception which is available at
13
+ // https://www.gnu.org/software/classpath/license.html.
14
+ //
15
+ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
16
+ // *****************************************************************************
17
+ Object.defineProperty(exports, "__esModule", { value: true });
18
+ exports.MCPInstallServiceImpl = exports.MCPInstallService = void 0;
19
+ const tslib_1 = require("tslib");
20
+ const inversify_1 = require("@theia/core/shared/inversify");
21
+ const core_1 = require("@theia/core");
22
+ const mcp_server_manager_1 = require("@theia/ai-mcp/lib/common/mcp-server-manager");
23
+ const mcp_preferences_1 = require("@theia/ai-mcp/lib/common/mcp-preferences");
24
+ const mcp_server_editor_1 = require("@theia/ai-mcp/lib/browser/mcp-server-editor");
25
+ exports.MCPInstallService = Symbol('MCPInstallService');
26
+ let MCPInstallServiceImpl = class MCPInstallServiceImpl {
27
+ /** Delegates to the generic editor so both registry installs and `install-mcp` URL handlers go through the same code path. */
28
+ async install(entry, overrides) {
29
+ await this.editor.installFromEntry(entry, overrides);
30
+ }
31
+ async fixConfig(entry) {
32
+ // Overwrite the registry-owned config fields, but carry forward user-owned ones
33
+ // (today: autostart). The registry has no opinion on `autostart`, so wiping it on
34
+ // fix-config would silently discard a user preference. Once the registry grows a
35
+ // formal notion of "user-configurable parameters", extend the forwarded set here.
36
+ const existing = this.readServers()[entry.localName];
37
+ const overrides = {};
38
+ if (existing?.autostart !== undefined) {
39
+ overrides.autostart = existing.autostart;
40
+ }
41
+ await this.install(entry, overrides);
42
+ }
43
+ async update(entry) {
44
+ const current = this.readServers();
45
+ const existing = current[entry.localName];
46
+ if (!existing) {
47
+ return;
48
+ }
49
+ // If the registry switched the entry's transport (e.g. local stdio -> remote URL),
50
+ // drop the previous side's fields so settings.json doesn't end up carrying both.
51
+ const sanitized = { ...existing };
52
+ if (entry.config.serverUrl !== undefined) {
53
+ delete sanitized.command;
54
+ delete sanitized.args;
55
+ delete sanitized.env;
56
+ }
57
+ else if (entry.config.command !== undefined) {
58
+ delete sanitized.serverUrl;
59
+ delete sanitized.serverAuthToken;
60
+ delete sanitized.serverAuthTokenHeader;
61
+ delete sanitized.headers;
62
+ }
63
+ // Preserve user-added env keys; registry values win for keys the registry sets.
64
+ // Asymmetry: keys the registry previously set and has since dropped are also
65
+ // preserved here, because we cannot distinguish them from user-added keys without
66
+ // tracking provenance. A registry approval that published e.g. `LOG_LEVEL` in v1
67
+ // and removes it in v2 will leave the stale key in the local entry. This will be
68
+ // addressed alongside the planned "user-configurable parameters" work, which
69
+ // introduces an explicit registry-set vs. user-set distinction.
70
+ const mergedEnv = (entry.config.env || sanitized.env)
71
+ ? { ...sanitized.env, ...(entry.config.env ?? {}) }
72
+ : undefined;
73
+ // Preserve user-supplied additions across updates. Today we only carry the auth
74
+ // token forward - registries should not ship secrets, so the new approval will
75
+ // either omit `serverAuthToken` entirely or carry it as an empty slot, both of
76
+ // which would otherwise wipe a token the user previously entered in the install
77
+ // dialog. A broader policy for user-additions belongs with the planned parameter
78
+ // configuration work.
79
+ const userAdditions = sanitized.serverAuthToken !== undefined
80
+ ? { serverAuthToken: sanitized.serverAuthToken }
81
+ : {};
82
+ const updated = {
83
+ ...sanitized,
84
+ ...entry.config,
85
+ ...userAdditions,
86
+ ...(mergedEnv && { env: mergedEnv }),
87
+ registryMetadata: this.metadata(entry)
88
+ };
89
+ await this.writeServers({ ...current, [entry.localName]: updated });
90
+ }
91
+ async link(entry) {
92
+ const current = this.readServers();
93
+ const existing = current[entry.localName];
94
+ if (!existing) {
95
+ return;
96
+ }
97
+ await this.writeServers({
98
+ ...current,
99
+ [entry.localName]: { ...existing, registryMetadata: this.metadata(entry) }
100
+ });
101
+ }
102
+ /**
103
+ * Drop the registry link from a local server while keeping its config intact.
104
+ * Used to convert a stale-linked entry (registry no longer lists the serverId)
105
+ * into a plain user-added entry without losing the user's running server config.
106
+ */
107
+ async unlink(name) {
108
+ const current = this.readServers();
109
+ const existing = current[name];
110
+ if (!existing || existing.registryMetadata === undefined) {
111
+ return;
112
+ }
113
+ const next = { ...existing };
114
+ delete next.registryMetadata;
115
+ await this.writeServers({ ...current, [name]: next });
116
+ }
117
+ async uninstall(name) {
118
+ const current = this.readServers();
119
+ if (!(name in current)) {
120
+ return;
121
+ }
122
+ const next = { ...current };
123
+ delete next[name];
124
+ await this.writeServers(next);
125
+ }
126
+ readServers() {
127
+ return this.preferenceService.get(mcp_preferences_1.MCP_SERVERS_PREF, {}) ?? {};
128
+ }
129
+ async writeServers(next) {
130
+ await this.preferenceService.set(mcp_preferences_1.MCP_SERVERS_PREF, next, core_1.PreferenceScope.User);
131
+ }
132
+ /** Registry-managed metadata block written onto every linked server entry. */
133
+ metadata(entry) {
134
+ return {
135
+ serverId: entry.serverId,
136
+ ...(entry.version !== undefined && { version: entry.version }),
137
+ ...(entry.configHash !== undefined && { configHash: entry.configHash })
138
+ };
139
+ }
140
+ classifyLocalServer(local, registryEntries) {
141
+ const linkedId = local.registryMetadata?.serverId;
142
+ if (linkedId) {
143
+ const byServerId = registryEntries.find(e => e.serverId === linkedId);
144
+ if (!byServerId) {
145
+ return { kind: 'installed-link-stale' };
146
+ }
147
+ return this.classifyLinked(byServerId, local);
148
+ }
149
+ const matchingEntry = registryEntries.find(e => e.localName === local.name);
150
+ if (!matchingEntry) {
151
+ return { kind: 'installed-user-added' };
152
+ }
153
+ // Unlinked + same key: always offer Link, regardless of config drift. Drift is only
154
+ // considered actionable (fix-config) once the user has opted in by linking.
155
+ return { kind: 'installed-manually' };
156
+ }
157
+ classifyRegistryEntry(entry, locals, registryEntries) {
158
+ const local = locals.find(l => l.name === entry.localName);
159
+ if (!local) {
160
+ return { kind: 'not-installed' };
161
+ }
162
+ const linkedId = local.registryMetadata?.serverId;
163
+ if (linkedId === entry.serverId) {
164
+ return this.classifyLinked(entry, local);
165
+ }
166
+ // Local links to a registry id that doesn't exist in the registry - the link is
167
+ // stale. Surface the same state Installed shows so the user sees the Unlink and
168
+ // Uninstall affordances in both views instead of a misleading Link button.
169
+ if (linkedId && !registryEntries.some(e => e.serverId === linkedId)) {
170
+ return { kind: 'installed-link-stale' };
171
+ }
172
+ // Same key but not linked (no registryMetadata, or pointing to a different valid
173
+ // id): offer Link before surfacing any drift - drift handling is a post-link concern.
174
+ return { kind: 'installed-manually' };
175
+ }
176
+ /**
177
+ * Classify a local server that is already linked to a registry entry. Either the
178
+ * registry config matches (eligible for Update when the registry has published a new
179
+ * approval - detected via `configHash`) or it has drifted away (`fix-config`).
180
+ *
181
+ * Update detection uses `registryMetadata.configHash` exclusively.
182
+ * `registryMetadata.version` is kept on the local entry for display only; the
183
+ * registry may publish a new version without changing the install config, in which
184
+ * case we still want to offer Update.
185
+ */
186
+ classifyLinked(entry, local) {
187
+ if (!this.matchesByConfig(entry, local)) {
188
+ return { kind: 'fix-config' };
189
+ }
190
+ const updateAvailable = this.isUpdateAvailable(entry, local);
191
+ return { kind: 'installed-from-registry', updateAvailable };
192
+ }
193
+ /**
194
+ * True when the registry's `configHash` differs from the locally stored
195
+ * `registryMetadata.configHash`. Returns false when the registry has no `configHash`
196
+ * to compare against (older payloads) - without a hash we cannot make a confident
197
+ * "update available" decision and prefer not to nag the user.
198
+ */
199
+ isUpdateAvailable(entry, local) {
200
+ if (entry.configHash === undefined) {
201
+ return false;
202
+ }
203
+ return local.registryMetadata?.configHash !== entry.configHash;
204
+ }
205
+ matchesByConfig(entry, local) {
206
+ if (entry.config.command !== undefined) {
207
+ if (!(0, mcp_server_manager_1.isLocalMCPServerDescription)(local)) {
208
+ return false;
209
+ }
210
+ if (local.command !== entry.config.command) {
211
+ return false;
212
+ }
213
+ const entryArgs = entry.config.args ?? [];
214
+ const localArgs = local.args ?? [];
215
+ if (entryArgs.length !== localArgs.length) {
216
+ return false;
217
+ }
218
+ if (!entryArgs.every((value, index) => value === localArgs[index])) {
219
+ return false;
220
+ }
221
+ return this.envMatches(entry.config.env, local.env);
222
+ }
223
+ if (entry.config.serverUrl !== undefined) {
224
+ if (!(0, mcp_server_manager_1.isRemoteMCPServerDescription)(local)) {
225
+ return false;
226
+ }
227
+ return local.serverUrl === entry.config.serverUrl;
228
+ }
229
+ return false;
230
+ }
231
+ envMatches(registryEnv, localEnv) {
232
+ if (!registryEnv) {
233
+ return true;
234
+ }
235
+ for (const key of Object.keys(registryEnv)) {
236
+ if (localEnv?.[key] !== registryEnv[key]) {
237
+ return false;
238
+ }
239
+ }
240
+ return true;
241
+ }
242
+ };
243
+ exports.MCPInstallServiceImpl = MCPInstallServiceImpl;
244
+ tslib_1.__decorate([
245
+ (0, inversify_1.inject)(core_1.PreferenceService),
246
+ tslib_1.__metadata("design:type", Object)
247
+ ], MCPInstallServiceImpl.prototype, "preferenceService", void 0);
248
+ tslib_1.__decorate([
249
+ (0, inversify_1.inject)(mcp_server_editor_1.MCPServerEditor),
250
+ tslib_1.__metadata("design:type", Object)
251
+ ], MCPInstallServiceImpl.prototype, "editor", void 0);
252
+ exports.MCPInstallServiceImpl = MCPInstallServiceImpl = tslib_1.__decorate([
253
+ (0, inversify_1.injectable)()
254
+ ], MCPInstallServiceImpl);
255
+ //# sourceMappingURL=mcp-install-service.js.map