imcp 0.0.3 → 0.0.5

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 (93) hide show
  1. package/README.md +5 -6
  2. package/dist/cli/commands/install.js +2 -0
  3. package/dist/cli/commands/list.js +1 -0
  4. package/dist/cli/index.js +1 -2
  5. package/dist/core/ConfigurationLoader.d.ts +32 -0
  6. package/dist/core/ConfigurationLoader.js +213 -0
  7. package/dist/core/ConfigurationProvider.d.ts +2 -3
  8. package/dist/core/ConfigurationProvider.js +13 -182
  9. package/dist/core/InstallationService.d.ts +8 -0
  10. package/dist/core/InstallationService.js +124 -96
  11. package/dist/core/RequirementService.d.ts +1 -1
  12. package/dist/core/RequirementService.js +5 -9
  13. package/dist/core/constants.js +14 -1
  14. package/dist/core/installers/BaseInstaller.d.ts +5 -4
  15. package/dist/core/installers/BaseInstaller.js +17 -28
  16. package/dist/core/installers/ClientInstaller.js +159 -39
  17. package/dist/core/installers/CommandInstaller.d.ts +1 -0
  18. package/dist/core/installers/CommandInstaller.js +3 -0
  19. package/dist/core/installers/GeneralInstaller.d.ts +1 -0
  20. package/dist/core/installers/GeneralInstaller.js +3 -0
  21. package/dist/core/installers/InstallerFactory.d.ts +9 -7
  22. package/dist/core/installers/InstallerFactory.js +10 -8
  23. package/dist/core/installers/NpmInstaller.d.ts +1 -0
  24. package/dist/core/installers/NpmInstaller.js +3 -0
  25. package/dist/core/installers/PipInstaller.d.ts +6 -3
  26. package/dist/core/installers/PipInstaller.js +21 -8
  27. package/dist/core/installers/RequirementInstaller.d.ts +4 -3
  28. package/dist/core/installers/clients/ClientInstaller.d.ts +23 -0
  29. package/dist/core/installers/clients/ClientInstaller.js +573 -0
  30. package/dist/core/installers/clients/ExtensionInstaller.d.ts +26 -0
  31. package/dist/core/installers/clients/ExtensionInstaller.js +149 -0
  32. package/dist/core/installers/index.d.ts +8 -6
  33. package/dist/core/installers/index.js +8 -6
  34. package/dist/core/installers/requirements/BaseInstaller.d.ts +59 -0
  35. package/dist/core/installers/requirements/BaseInstaller.js +168 -0
  36. package/dist/core/installers/requirements/CommandInstaller.d.ts +37 -0
  37. package/dist/core/installers/requirements/CommandInstaller.js +173 -0
  38. package/dist/core/installers/requirements/GeneralInstaller.d.ts +33 -0
  39. package/dist/core/installers/requirements/GeneralInstaller.js +86 -0
  40. package/dist/core/installers/requirements/InstallerFactory.d.ts +54 -0
  41. package/dist/core/installers/requirements/InstallerFactory.js +97 -0
  42. package/dist/core/installers/requirements/NpmInstaller.d.ts +26 -0
  43. package/dist/core/installers/requirements/NpmInstaller.js +128 -0
  44. package/dist/core/installers/requirements/PipInstaller.d.ts +28 -0
  45. package/dist/core/installers/requirements/PipInstaller.js +128 -0
  46. package/{src/core/installers/RequirementInstaller.ts → dist/core/installers/requirements/RequirementInstaller.d.ts} +33 -38
  47. package/dist/core/installers/requirements/RequirementInstaller.js +3 -0
  48. package/dist/core/types.d.ts +4 -1
  49. package/dist/services/ServerService.js +1 -1
  50. package/dist/utils/clientUtils.d.ts +0 -6
  51. package/dist/utils/clientUtils.js +3 -2
  52. package/dist/utils/githubUtils.d.ts +11 -0
  53. package/dist/utils/githubUtils.js +88 -0
  54. package/dist/utils/osUtils.d.ts +17 -0
  55. package/dist/utils/osUtils.js +184 -0
  56. package/dist/web/public/css/modal.css +97 -3
  57. package/dist/web/public/index.html +21 -2
  58. package/dist/web/public/js/modal.js +177 -28
  59. package/dist/web/public/js/serverCategoryDetails.js +12 -10
  60. package/dist/web/public/js/serverCategoryList.js +20 -5
  61. package/dist/web/public/modal.html +27 -13
  62. package/dist/web/server.js +1 -1
  63. package/package.json +2 -1
  64. package/src/cli/commands/install.ts +4 -2
  65. package/src/cli/commands/list.ts +1 -0
  66. package/src/cli/index.ts +1 -1
  67. package/src/core/ConfigurationLoader.ts +251 -0
  68. package/src/core/ConfigurationProvider.ts +13 -195
  69. package/src/core/InstallationService.ts +140 -106
  70. package/src/core/RequirementService.ts +5 -10
  71. package/src/core/constants.ts +15 -1
  72. package/src/core/installers/{ClientInstaller.ts → clients/ClientInstaller.ts} +185 -46
  73. package/src/core/installers/clients/ExtensionInstaller.ts +162 -0
  74. package/src/core/installers/index.ts +9 -7
  75. package/src/core/installers/{BaseInstaller.ts → requirements/BaseInstaller.ts} +10 -118
  76. package/src/core/installers/{CommandInstaller.ts → requirements/CommandInstaller.ts} +7 -3
  77. package/src/core/installers/{GeneralInstaller.ts → requirements/GeneralInstaller.ts} +6 -2
  78. package/src/core/installers/{InstallerFactory.ts → requirements/InstallerFactory.ts} +11 -9
  79. package/src/core/installers/{NpmInstaller.ts → requirements/NpmInstaller.ts} +7 -4
  80. package/src/core/installers/{PipInstaller.ts → requirements/PipInstaller.ts} +26 -10
  81. package/src/core/installers/requirements/RequirementInstaller.ts +41 -0
  82. package/src/core/types.ts +4 -1
  83. package/src/services/ServerService.ts +1 -1
  84. package/src/utils/clientUtils.ts +4 -2
  85. package/src/utils/githubUtils.ts +103 -0
  86. package/src/utils/osUtils.ts +206 -15
  87. package/src/web/public/css/modal.css +97 -3
  88. package/src/web/public/index.html +21 -2
  89. package/src/web/public/js/modal.js +177 -28
  90. package/src/web/public/js/serverCategoryDetails.js +12 -10
  91. package/src/web/public/js/serverCategoryList.js +20 -5
  92. package/src/web/public/modal.html +27 -13
  93. package/src/web/server.ts +1 -1
package/README.md CHANGED
@@ -1,15 +1,14 @@
1
1
  # IMCP
2
2
 
3
- IMCP (Install Model Context Protocol) is a unified MCP management project that provides the ability to list, install, and manage MCP servers based on unified feeds.
3
+ IMCP is a single-line pull and push experience that makes onboarding MCP servers easy.
4
4
 
5
5
  ## Overview
6
6
 
7
- IMCP is a command-line tool that simplifies the management of Model Context Protocol (MCP) servers. It allows you to:
7
+ IMCP allows you to:
8
8
 
9
- - Discover available MCP servers from configurable feeds
10
- - Install servers with specific configurations
11
- - Manage server installations
12
- - Run a local web interface to manage your MCP servers
9
+ - Discover and install available MCP servers to your local agents
10
+ - Run a local UI interface with simple click experience to manage MCP servers
11
+ - (in progress) Distribute your own MCP servers to others
13
12
 
14
13
  ## Installation
15
14
 
@@ -3,6 +3,7 @@ import { serverService } from '../../services/ServerService.js';
3
3
  import { Logger } from '../../utils/logger.js';
4
4
  import { hasLocalFeeds } from '../../utils/feedUtils.js';
5
5
  import { SUPPORTED_CLIENT_NAMES } from '../../core/constants.js';
6
+ import { mcpManager } from '../../core/MCPManager.js';
6
7
  export function createInstallCommand() {
7
8
  return new Command('install')
8
9
  .description('Install specific MCP servers')
@@ -29,6 +30,7 @@ Examples:
29
30
  Logger.log('Local feeds not found, syncing from remote...');
30
31
  await serverService.syncFeeds();
31
32
  }
33
+ await mcpManager.initialize();
32
34
  const { category, name, verbose, clients, envs } = options;
33
35
  Logger.debug(`Install options: ${JSON.stringify({ category, name, verbose, clients, envs })}`);
34
36
  const serverName = name.trim();
@@ -23,6 +23,7 @@ export function createListCommand() {
23
23
  Logger.error('Failed to sync feeds, falling back to local feeds', syncError);
24
24
  }
25
25
  }
26
+ await mcpManager.initialize();
26
27
  Logger.debug({
27
28
  action: 'ensuring_feeds_directory',
28
29
  path: LOCAL_FEEDS_DIR
package/dist/cli/index.js CHANGED
@@ -4,7 +4,6 @@ import { createServeCommand } from './commands/serve.js';
4
4
  import { createListCommand } from './commands/list.js';
5
5
  import { createInstallCommand } from './commands/install.js';
6
6
  import { createPullCommand } from './commands/pull.js';
7
- import { mcpManager } from '../core/MCPManager.js';
8
7
  import { Logger } from '../utils/logger.js';
9
8
  import axios from 'axios';
10
9
  import path from 'path';
@@ -19,7 +18,7 @@ const COLORS = {
19
18
  };
20
19
  async function main() {
21
20
  // Initialize the MCP manager
22
- await mcpManager.initialize();
21
+ // await mcpManager.initialize();
23
22
  const program = new Command();
24
23
  program
25
24
  .name('imcp')
@@ -0,0 +1,32 @@
1
+ import { MCPConfiguration } from './types.js';
2
+ export declare class ConfigurationLoader {
3
+ /**
4
+ * Updates the installed status for a server and client combination
5
+ */
6
+ private static updateServerInstalledStatus;
7
+ /**
8
+ * Removes a client's status from a server
9
+ */
10
+ private static removeClientStatus;
11
+ /**
12
+ * Synchronizes server categories with client MCP settings.
13
+ * Uses clientMCPSettings as source of truth for installation status.
14
+ */
15
+ static syncServerCategoriesWithClientSettings(configuration: MCPConfiguration): MCPConfiguration;
16
+ /**
17
+ * Initializes installation status for a feed configuration
18
+ */
19
+ private static initializeInstallationStatus;
20
+ /**
21
+ * Synchronizes server categories with feeds
22
+ */
23
+ private static syncServerCategoriesWithFeeds;
24
+ /**
25
+ * Loads feed configurations into the MCP configuration
26
+ */
27
+ static loadFeedsIntoConfiguration(configuration: MCPConfiguration): Promise<MCPConfiguration>;
28
+ /**
29
+ * Loads MCP client settings into the configuration
30
+ */
31
+ static loadClientMCPSettings(configuration: MCPConfiguration): Promise<MCPConfiguration>;
32
+ }
@@ -0,0 +1,213 @@
1
+ import fs from 'fs/promises';
2
+ import path from 'path';
3
+ import { LOCAL_FEEDS_DIR, SUPPORTED_CLIENTS } from './constants.js';
4
+ import { Logger } from '../utils/logger.js';
5
+ import { readJsonFile } from '../utils/clientUtils.js';
6
+ export class ConfigurationLoader {
7
+ /**
8
+ * Updates the installed status for a server and client combination
9
+ */
10
+ static updateServerInstalledStatus(serverStatus, clientName, operationStatus) {
11
+ if (!serverStatus.installedStatus) {
12
+ serverStatus.installedStatus = {};
13
+ }
14
+ serverStatus.installedStatus[clientName] = operationStatus;
15
+ }
16
+ /**
17
+ * Removes a client's status from a server
18
+ */
19
+ static removeClientStatus(serverStatus, clientName) {
20
+ if (serverStatus.installedStatus && serverStatus.installedStatus[clientName]) {
21
+ delete serverStatus.installedStatus[clientName];
22
+ }
23
+ }
24
+ /**
25
+ * Synchronizes server categories with client MCP settings.
26
+ * Uses clientMCPSettings as source of truth for installation status.
27
+ */
28
+ static syncServerCategoriesWithClientSettings(configuration) {
29
+ if (!configuration.clientMCPSettings) {
30
+ return configuration;
31
+ }
32
+ configuration.localServerCategories = configuration.localServerCategories.map(category => {
33
+ if (!category.installationStatus?.serversStatus) {
34
+ return category;
35
+ }
36
+ const updatedServerStatus = { ...category.installationStatus.serversStatus };
37
+ const clientSettings = configuration.clientMCPSettings;
38
+ for (const [clientName, settings] of Object.entries(clientSettings)) {
39
+ const clientServers = clientName === 'GithubCopilot'
40
+ ? settings.servers || {}
41
+ : settings.mcpServers || {};
42
+ Object.keys(updatedServerStatus).forEach(serverName => {
43
+ if (clientServers[serverName]) {
44
+ if (!updatedServerStatus[serverName].installedStatus[clientName]) {
45
+ const operationStatus = {
46
+ status: 'completed',
47
+ type: 'install',
48
+ target: 'server',
49
+ message: `Server ${serverName} is configured for client ${clientName}`
50
+ };
51
+ ConfigurationLoader.updateServerInstalledStatus(updatedServerStatus[serverName], clientName, operationStatus);
52
+ }
53
+ }
54
+ else {
55
+ ConfigurationLoader.removeClientStatus(updatedServerStatus[serverName], clientName);
56
+ }
57
+ });
58
+ }
59
+ return {
60
+ ...category,
61
+ installationStatus: {
62
+ ...category.installationStatus,
63
+ serversStatus: updatedServerStatus,
64
+ lastUpdated: new Date().toISOString()
65
+ }
66
+ };
67
+ });
68
+ return configuration;
69
+ }
70
+ /**
71
+ * Initializes installation status for a feed configuration
72
+ */
73
+ static initializeInstallationStatus(feedConfig) {
74
+ const requirementsStatus = {};
75
+ const serversStatus = {};
76
+ if (feedConfig) {
77
+ if (feedConfig.requirements) {
78
+ for (const req of feedConfig.requirements) {
79
+ requirementsStatus[req.name] = {
80
+ name: req.name,
81
+ type: req.type,
82
+ installed: false,
83
+ version: req.version,
84
+ error: undefined
85
+ };
86
+ }
87
+ }
88
+ if (feedConfig.mcpServers) {
89
+ for (const mcp of feedConfig.mcpServers) {
90
+ serversStatus[mcp.name] = {
91
+ name: mcp.name,
92
+ error: undefined,
93
+ installedStatus: {}
94
+ };
95
+ }
96
+ }
97
+ }
98
+ return {
99
+ requirementsStatus,
100
+ serversStatus,
101
+ lastUpdated: new Date().toISOString()
102
+ };
103
+ }
104
+ /**
105
+ * Synchronizes server categories with feeds
106
+ */
107
+ static async syncServerCategoriesWithFeeds(configuration) {
108
+ configuration.localServerCategories = configuration.localServerCategories.map(server => {
109
+ if (configuration.feeds[server.name]) {
110
+ server.feedConfiguration = configuration.feeds[server.name];
111
+ }
112
+ if (!server.installationStatus ||
113
+ !server.installationStatus.requirementsStatus ||
114
+ Object.keys(server.installationStatus.requirementsStatus).length === 0 ||
115
+ !server.installationStatus.serversStatus ||
116
+ Object.keys(server.installationStatus.serversStatus).length === 0) {
117
+ server.installationStatus = ConfigurationLoader.initializeInstallationStatus(server.feedConfiguration);
118
+ }
119
+ return server;
120
+ });
121
+ const existingServerNames = new Set(configuration.localServerCategories.map(category => category.name));
122
+ for (const [feedName, feedConfig] of Object.entries(configuration.feeds)) {
123
+ if (!existingServerNames.has(feedName)) {
124
+ const newServerCategory = {
125
+ name: feedName,
126
+ displayName: feedConfig.displayName || feedName,
127
+ type: 'local',
128
+ description: feedConfig.description || `Local MCP server category: ${feedName}`,
129
+ installationStatus: ConfigurationLoader.initializeInstallationStatus(feedConfig),
130
+ feedConfiguration: feedConfig
131
+ };
132
+ configuration.localServerCategories.push(newServerCategory);
133
+ console.log(`Created new local server entry for feed: ${feedName}`);
134
+ }
135
+ }
136
+ return configuration;
137
+ }
138
+ /**
139
+ * Loads feed configurations into the MCP configuration
140
+ */
141
+ static async loadFeedsIntoConfiguration(configuration) {
142
+ try {
143
+ await fs.mkdir(LOCAL_FEEDS_DIR, { recursive: true });
144
+ const files = await fs.readdir(LOCAL_FEEDS_DIR);
145
+ const jsonFiles = files.filter(file => file.endsWith('.json'));
146
+ if (jsonFiles.length === 0) {
147
+ console.log(`No feed configuration files found in ${LOCAL_FEEDS_DIR}`);
148
+ return configuration;
149
+ }
150
+ const feeds = {};
151
+ for (const file of jsonFiles) {
152
+ try {
153
+ const filePath = path.join(LOCAL_FEEDS_DIR, file);
154
+ const content = await fs.readFile(filePath, 'utf8');
155
+ const config = JSON.parse(content);
156
+ if (config && config.name) {
157
+ feeds[config.name] = config;
158
+ }
159
+ }
160
+ catch (error) {
161
+ console.warn(`Error loading feed configuration from ${file}:`, error);
162
+ }
163
+ }
164
+ configuration.feeds = feeds;
165
+ return await ConfigurationLoader.syncServerCategoriesWithFeeds(configuration);
166
+ }
167
+ catch (error) {
168
+ console.error("Error loading feed configurations:", error);
169
+ throw error;
170
+ }
171
+ }
172
+ /**
173
+ * Loads MCP client settings into the configuration
174
+ */
175
+ static async loadClientMCPSettings(configuration) {
176
+ try {
177
+ Logger.debug('Starting to load MCP client settings...');
178
+ const settings = {};
179
+ for (const [clientName, clientSettings] of Object.entries(SUPPORTED_CLIENTS)) {
180
+ const settingPath = process.env.CODE_INSIDERS
181
+ ? clientSettings.codeInsiderSettingPath
182
+ : clientSettings.codeSettingPath;
183
+ try {
184
+ let content = await readJsonFile(settingPath, true);
185
+ if (clientName === 'GithubCopilot') {
186
+ if (!content.mcp) {
187
+ content = {
188
+ servers: {},
189
+ inputs: []
190
+ };
191
+ }
192
+ else {
193
+ content = content.mcp;
194
+ }
195
+ }
196
+ settings[clientName] = content;
197
+ Logger.debug(`Successfully loaded MCP settings for ${clientName}`);
198
+ }
199
+ catch (error) {
200
+ Logger.debug(`Warning: Could not load MCP settings for client ${clientName}: ${error instanceof Error ? error.message : String(error)}`);
201
+ settings[clientName] = {};
202
+ }
203
+ }
204
+ configuration.clientMCPSettings = settings;
205
+ return ConfigurationLoader.syncServerCategoriesWithClientSettings(configuration);
206
+ }
207
+ catch (error) {
208
+ Logger.error('Error loading client MCP settings:', error);
209
+ throw error;
210
+ }
211
+ }
212
+ }
213
+ //# sourceMappingURL=ConfigurationLoader.js.map
@@ -9,7 +9,6 @@ export declare class ConfigurationProvider {
9
9
  static getInstance(): ConfigurationProvider;
10
10
  private withLock;
11
11
  initialize(): Promise<void>;
12
- private loadClientMCPSettings;
13
12
  private saveConfiguration;
14
13
  getServerCategories(): Promise<MCPServerCategory[]>;
15
14
  getServerCategory(categoryName: string): Promise<MCPServerCategory | undefined>;
@@ -27,7 +26,7 @@ export declare class ConfigurationProvider {
27
26
  isServerReady(categoryName: string, serverName: string, clients: string[]): Promise<boolean>;
28
27
  syncFeeds(): Promise<void>;
29
28
  private loadFeedsIntoConfiguration;
30
- private syncServerCategoriesWithFeeds;
31
- syncWithFeed(feedConfiguration: Record<string, FeedConfiguration>): Promise<void>;
29
+ private loadClientMCPSettings;
30
+ reloadClientMCPSettings(): Promise<void>;
32
31
  }
33
32
  export declare const configProvider: ConfigurationProvider;
@@ -3,10 +3,10 @@ import path from 'path';
3
3
  import { exec } from 'child_process';
4
4
  import { promisify } from 'util';
5
5
  import { fileURLToPath } from 'url';
6
- import { GITHUB_REPO, LOCAL_FEEDS_DIR, SETTINGS_DIR, SUPPORTED_CLIENTS } from './constants.js';
6
+ import { GITHUB_REPO, LOCAL_FEEDS_DIR, SETTINGS_DIR } from './constants.js';
7
7
  import { Logger } from '../utils/logger.js';
8
8
  import { checkGithubAuth } from '../utils/githubAuth.js';
9
- import { readJsonFile } from '../utils/clientUtils.js';
9
+ import { ConfigurationLoader } from './ConfigurationLoader.js';
10
10
  const execAsync = promisify(exec);
11
11
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
12
12
  export class ConfigurationProvider {
@@ -62,45 +62,6 @@ export class ConfigurationProvider {
62
62
  }
63
63
  });
64
64
  }
65
- async loadClientMCPSettings() {
66
- try {
67
- Logger.debug('Starting to load MCP client settings...');
68
- const settings = {};
69
- for (const [clientName, clientSettings] of Object.entries(SUPPORTED_CLIENTS)) {
70
- const settingPath = process.env.CODE_INSIDERS
71
- ? clientSettings.codeInsiderSettingPath
72
- : clientSettings.codeSettingPath;
73
- try {
74
- let content = await readJsonFile(settingPath, true);
75
- if (clientName === 'GithubCopilot') {
76
- // Initialize the mcp section if it doesn't exist
77
- if (!content.mcp) {
78
- content = {
79
- servers: {},
80
- inputs: []
81
- };
82
- }
83
- else {
84
- content = content.mcp;
85
- }
86
- }
87
- settings[clientName] = content;
88
- Logger.debug(`Successfully loaded MCP settings for ${clientName}`);
89
- }
90
- catch (error) {
91
- Logger.debug(`Warning: Could not load MCP settings for client ${clientName}: ${error instanceof Error ? error.message : String(error)}`);
92
- settings[clientName] = {};
93
- }
94
- }
95
- this.configuration.clientMCPSettings = settings;
96
- await this.saveConfiguration();
97
- Logger.debug('Saved MCP client settings to configuration');
98
- }
99
- catch (error) {
100
- Logger.error('Error loading client MCP settings:', error);
101
- throw error;
102
- }
103
- }
104
65
  async saveConfiguration() {
105
66
  const configDir = path.dirname(this.configPath);
106
67
  await fs.mkdir(configDir, { recursive: true });
@@ -295,7 +256,7 @@ export class ConfigurationProvider {
295
256
  });
296
257
  }
297
258
  Logger.debug('Updating local feeds...');
298
- await fs.rm(LOCAL_FEEDS_DIR, { recursive: true, force: true });
259
+ // await fs.rm(LOCAL_FEEDS_DIR, { recursive: true, force: true });
299
260
  const sourceFeedsDir = path.join(this.tempDir, GITHUB_REPO.feedsPath);
300
261
  try {
301
262
  await fs.access(sourceFeedsDir);
@@ -303,10 +264,8 @@ export class ConfigurationProvider {
303
264
  catch (err) {
304
265
  throw new Error(`Could not find feeds directory in cloned repository: ${sourceFeedsDir}`);
305
266
  }
306
- await fs.cp(sourceFeedsDir, LOCAL_FEEDS_DIR, { recursive: true });
267
+ await fs.cp(sourceFeedsDir, LOCAL_FEEDS_DIR, { recursive: true, force: true });
307
268
  Logger.log('Successfully updated local feeds');
308
- // Update configuration with new feeds
309
- await this.loadFeedsIntoConfiguration();
310
269
  }
311
270
  catch (error) {
312
271
  Logger.error('Error during feed synchronization', error);
@@ -315,145 +274,17 @@ export class ConfigurationProvider {
315
274
  });
316
275
  }
317
276
  async loadFeedsIntoConfiguration() {
318
- try {
319
- await fs.mkdir(LOCAL_FEEDS_DIR, { recursive: true });
320
- const files = await fs.readdir(LOCAL_FEEDS_DIR);
321
- const jsonFiles = files.filter(file => file.endsWith('.json'));
322
- if (jsonFiles.length === 0) {
323
- console.log(`No feed configuration files found in ${LOCAL_FEEDS_DIR}`);
324
- return;
325
- }
326
- const feeds = {};
327
- for (const file of jsonFiles) {
328
- try {
329
- const filePath = path.join(LOCAL_FEEDS_DIR, file);
330
- const content = await fs.readFile(filePath, 'utf8');
331
- const config = JSON.parse(content);
332
- if (config && config.name) {
333
- feeds[config.name] = config;
334
- }
335
- }
336
- catch (error) {
337
- console.warn(`Error loading feed configuration from ${file}:`, error);
338
- }
339
- }
340
- this.configuration.feeds = feeds;
341
- await this.syncServerCategoriesWithFeeds(); // Sync categories after loading feeds
342
- await this.saveConfiguration();
343
- }
344
- catch (error) {
345
- console.error("Error loading feed configurations:", error);
346
- throw error;
347
- }
277
+ this.configuration = await ConfigurationLoader.loadFeedsIntoConfiguration(this.configuration);
278
+ await this.saveConfiguration();
348
279
  }
349
- async syncServerCategoriesWithFeeds() {
350
- let configUpdated = false;
351
- // 1. Process existing local servers - update their feed configurations
352
- for (const server of this.configuration.localServerCategories) {
353
- if (this.configuration.feeds[server.name]) {
354
- server.feedConfiguration = this.configuration.feeds[server.name];
355
- configUpdated = true;
356
- }
357
- // If server doesn't have installation status, initialize it
358
- const feedConfig = server.feedConfiguration;
359
- // If installationStatus is missing, or requirements/tools are empty, initialize from feed
360
- if (!server.installationStatus ||
361
- !server.installationStatus.requirementsStatus ||
362
- Object.keys(server.installationStatus.requirementsStatus).length === 0 ||
363
- !server.installationStatus.serversStatus ||
364
- Object.keys(server.installationStatus.serversStatus).length === 0) {
365
- const requirementsStatus = {};
366
- const serversStatus = {};
367
- if (feedConfig) {
368
- if (feedConfig.requirements) {
369
- for (const req of feedConfig.requirements) {
370
- requirementsStatus[req.name] = {
371
- name: req.name,
372
- type: req.type,
373
- installed: false,
374
- version: req.version,
375
- error: undefined
376
- };
377
- }
378
- }
379
- if (feedConfig.mcpServers) {
380
- for (const mcp of feedConfig.mcpServers) {
381
- serversStatus[mcp.name] = {
382
- name: mcp.name,
383
- error: undefined,
384
- installedStatus: {} // Add missing property
385
- };
386
- }
387
- }
388
- }
389
- server.installationStatus = {
390
- requirementsStatus,
391
- serversStatus,
392
- lastUpdated: new Date().toISOString()
393
- };
394
- configUpdated = true;
395
- }
396
- }
397
- // 2. Check for feeds that don't have a corresponding local server and create new entries
398
- const existingServerCategoryNames = new Set(this.configuration.localServerCategories.map(catetory => catetory.name));
399
- for (const feedName in this.configuration.feeds) {
400
- if (!existingServerCategoryNames.has(feedName)) {
401
- // This feed doesn't have a corresponding local server - create one with empty installation status
402
- const feedConfig = this.configuration.feeds[feedName];
403
- // Create new server with empty installation status
404
- const newServerCategory = {
405
- name: feedName,
406
- displayName: feedConfig.displayName || feedName,
407
- type: 'local',
408
- description: feedConfig.description || `Local MCP server category: ${feedName}`,
409
- installationStatus: (() => {
410
- const requirementsStatus = {};
411
- const serversStatus = {};
412
- if (feedConfig) {
413
- if (feedConfig.requirements) {
414
- for (const req of feedConfig.requirements) {
415
- requirementsStatus[req.name] = {
416
- name: req.name,
417
- type: req.type,
418
- installed: false,
419
- version: req.version,
420
- error: undefined
421
- };
422
- }
423
- }
424
- if (feedConfig.mcpServers) {
425
- for (const mcp of feedConfig.mcpServers) {
426
- serversStatus[mcp.name] = {
427
- name: mcp.name,
428
- error: undefined,
429
- installedStatus: {} // Add missing property
430
- };
431
- }
432
- }
433
- }
434
- return {
435
- requirementsStatus,
436
- serversStatus,
437
- lastUpdated: new Date().toISOString()
438
- };
439
- })(),
440
- feedConfiguration: feedConfig
441
- };
442
- // Add the new server to the configuration
443
- this.configuration.localServerCategories.push(newServerCategory);
444
- console.log(`Created new local server entry for feed: ${feedName}`);
445
- configUpdated = true;
446
- }
447
- }
448
- if (configUpdated) {
449
- await this.saveConfiguration();
450
- }
280
+ async loadClientMCPSettings() {
281
+ this.configuration = await ConfigurationLoader.loadClientMCPSettings(this.configuration);
282
+ await this.saveConfiguration();
451
283
  }
452
- async syncWithFeed(feedConfiguration) {
453
- await this.withLock(async () => {
454
- this.configuration.feeds = feedConfiguration;
455
- await this.syncServerCategoriesWithFeeds(); // Sync categories after direct update
456
- await this.saveConfiguration();
284
+ // Public method to reload client MCP settings
285
+ async reloadClientMCPSettings() {
286
+ return await this.withLock(async () => {
287
+ await this.loadClientMCPSettings();
457
288
  });
458
289
  }
459
290
  }
@@ -22,4 +22,12 @@ export declare class InstallationService {
22
22
  * @param requirements The requirements to update
23
23
  */
24
24
  private processRequirementUpdates;
25
+ /**
26
+ * Checks and installs requirements for a server if needed
27
+ * @param categoryName The category name
28
+ * @param serverName The server name
29
+ * @param options The installation options
30
+ * @returns A failure result if requirements check fails, null if requirements are satisfied
31
+ */
32
+ private checkAndInstallRequirements;
25
33
  }