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
@@ -14,8 +14,9 @@ import {
14
14
  } from './types.js';
15
15
  import { RequirementInstaller, InstallerFactory, createInstallerFactory } from './installers/index.js';
16
16
  import { SUPPORTED_CLIENTS } from './constants.js';
17
- import { ClientInstaller } from './installers/ClientInstaller.js';
17
+ import { ClientInstaller } from './installers/clients/ClientInstaller.js';
18
18
  import { ConfigurationProvider } from './ConfigurationProvider.js';
19
+ import { Logger } from '../utils/logger.js';
19
20
 
20
21
  const execPromise = util.promisify(exec);
21
22
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
@@ -23,7 +24,6 @@ const __dirname = path.dirname(fileURLToPath(import.meta.url));
23
24
  /**
24
25
  * Handles the actual installation process for an MCP server.
25
26
  */
26
-
27
27
  export class InstallationService {
28
28
  private activeInstallations: Map<string, OperationStatus> = new Map();
29
29
  private installerFactory: InstallerFactory;
@@ -36,7 +36,6 @@ export class InstallationService {
36
36
  return `install-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
37
37
  }
38
38
 
39
-
40
39
  /**
41
40
  * Installs a server based on the provided options and feed configuration.
42
41
  * @param serverName The name of the server to install.
@@ -75,101 +74,9 @@ export class InstallationService {
75
74
  // Create new ClientInstaller instance for handling installation
76
75
  const clientInstaller = new ClientInstaller(categoryName, serverName, clients);
77
76
 
78
- // Check requirements readiness
79
- const requirementsReady = await configProvider.isRequirementsReady(categoryName, serverName);
80
- if (!requirementsReady) {
81
- // Get feed configuration to get requirements
82
- const feedConfig = await configProvider.getFeedConfiguration(categoryName);
83
- if (!feedConfig) {
84
- return {
85
- success: false,
86
- message: 'Feed configuration not found',
87
- status: [{
88
- status: 'failed',
89
- type: 'install',
90
- target: 'server',
91
- message: 'Feed configuration not found'
92
- }]
93
- };
94
- }
95
-
96
- // Find server config
97
- const serverConfig = feedConfig.mcpServers.find((s: McpConfig) => s.name === serverName);
98
- if (!serverConfig?.dependencies?.requirements) {
99
- return {
100
- success: false,
101
- message: 'Server configuration or requirements not found',
102
- status: [{
103
- status: 'failed',
104
- type: 'install',
105
- target: 'server',
106
- message: 'Server configuration or requirements not found'
107
- }]
108
- };
109
- }
110
-
111
- // Install requirements asynchronously
112
- for (const requirement of serverConfig.dependencies.requirements) {
113
- // Create full RequirementConfig from dependency requirement
114
-
115
- const feeds = await configProvider.getFeedConfiguration(categoryName);
116
- const requirementConfig = feeds?.requirements?.find((r: RequirementConfig) => r.name === requirement.name) || {
117
- name: requirement.name,
118
- version: requirement.version,
119
- type: 'npm' // Default to npm, can be enhanced to determine from requirement or config
120
- };
121
-
122
- const installer = this.installerFactory.getInstaller(requirementConfig);
123
- if (!installer) {
124
- await configProvider.updateRequirementStatus(categoryName, requirement.name, {
125
- name: requirement.name,
126
- type: requirementConfig.type,
127
- installed: false,
128
- error: `No installer found for requirement type: ${requirementConfig.type}`,
129
- operationStatus: {
130
- status: 'failed',
131
- type: 'install',
132
- target: 'requirement',
133
- message: `No installer found for requirement type: ${requirementConfig.type}`,
134
- operationId: this.generateOperationId()
135
- }
136
- });
137
- continue;
138
- }
139
-
140
- // Create operation status for requirement
141
- const operationStatus: OperationStatus = {
142
- status: 'pending',
143
- type: 'install',
144
- target: 'requirement',
145
- message: `Installing requirement: ${requirement.name}`,
146
- operationId: this.generateOperationId()
147
- };
148
-
149
- // Update requirement status
150
- await configProvider.updateRequirementStatus(categoryName, requirement.name, {
151
- name: requirement.name,
152
- type: requirementConfig.type,
153
- installed: false,
154
- inProgress: true,
155
- operationStatus
156
- });
157
-
158
- // Start async installation
159
- installer.install(requirementConfig).then(async (installStatus) => {
160
- const status: RequirementStatus = {
161
- ...installStatus,
162
- operationStatus: {
163
- status: installStatus.installed ? 'completed' : 'failed',
164
- type: 'install',
165
- target: 'requirement',
166
- message: installStatus.installed ? `Requirement ${requirement.name} installed successfully` : `Failed to install ${requirement.name}`,
167
- operationId: operationStatus.operationId
168
- }
169
- };
170
- await configProvider.updateRequirementStatus(categoryName, requirement.name, status);
171
- });
172
- }
77
+ const requirementsResult = await this.checkAndInstallRequirements(categoryName, serverName, options);
78
+ if (requirementsResult) {
79
+ return requirementsResult;
173
80
  }
174
81
 
175
82
  // Process client installation regardless of requirements state
@@ -230,9 +137,9 @@ export class InstallationService {
230
137
  // Update requirement status to indicate update in progress
231
138
  await configProvider.updateRequirementStatus(categoryName, reqToUpdate.name, {
232
139
  ...currentStatus,
233
- name: reqToUpdate.name, // Ensure name is included
234
- type: currentStatus.type || 'unknown', // Ensure type is included
235
- installed: currentStatus.installed || false, // Ensure installed is included
140
+ name: reqToUpdate.name,
141
+ type: currentStatus.type || 'unknown',
142
+ installed: currentStatus.installed || false,
236
143
  inProgress: true,
237
144
  operationStatus: {
238
145
  status: 'in-progress',
@@ -254,9 +161,9 @@ export class InstallationService {
254
161
  // Update requirement status
255
162
  await configProvider.updateRequirementStatus(categoryName, reqToUpdate.name, {
256
163
  ...updatedStatus,
257
- name: reqToUpdate.name, // Ensure name is always included
258
- type: updatedStatus.type || currentStatus.type || 'unknown', // Ensure type is included
259
- installed: updatedStatus.installed, // This should be defined from updatedStatus
164
+ name: reqToUpdate.name,
165
+ type: updatedStatus.type || currentStatus.type || 'unknown',
166
+ installed: updatedStatus.installed,
260
167
  inProgress: false,
261
168
  operationStatus: {
262
169
  status: updatedStatus.installed ? 'completed' : 'failed',
@@ -266,7 +173,6 @@ export class InstallationService {
266
173
  ? `Successfully updated ${reqToUpdate.name} to version ${reqToUpdate.version}`
267
174
  : `Failed to update ${reqToUpdate.name} to version ${reqToUpdate.version}`
268
175
  },
269
- // Clear availableUpdate if update was successful
270
176
  availableUpdate: updatedStatus.installed ? undefined : currentStatus.availableUpdate
271
177
  });
272
178
 
@@ -298,7 +204,135 @@ export class InstallationService {
298
204
  await updateCheckTracker.endOperation(operationKey);
299
205
  }
300
206
  }
207
+
208
+ /**
209
+ * Checks and installs requirements for a server if needed
210
+ * @param categoryName The category name
211
+ * @param serverName The server name
212
+ * @param options The installation options
213
+ * @returns A failure result if requirements check fails, null if requirements are satisfied
214
+ */
215
+ private async checkAndInstallRequirements(categoryName: string, serverName: string, options: ServerInstallOptions): Promise<ServerOperationResult | null> {
216
+ const configProvider = ConfigurationProvider.getInstance();
217
+
218
+ // Get feed configuration to get requirements
219
+ const feedConfig = await configProvider.getFeedConfiguration(categoryName);
220
+ if (!feedConfig) {
221
+ return {
222
+ success: false,
223
+ message: 'Feed configuration not found',
224
+ status: [{
225
+ status: 'failed',
226
+ type: 'install',
227
+ target: 'server',
228
+ message: 'Feed configuration not found'
229
+ }]
230
+ };
231
+ }
232
+
233
+ // Find server config and verify requirements
234
+ const serverConfig = feedConfig.mcpServers.find((s: McpConfig) => s.name === serverName);
235
+ if (!serverConfig?.dependencies?.requirements) {
236
+ Logger.debug(`No requirements for ${serverName}`);
237
+ return null;
238
+ }
239
+
240
+ // Check all requirements installation status
241
+ const requirementStatuses = await Promise.all(
242
+ serverConfig.dependencies.requirements.map(async (req) => {
243
+ const reqConfig = feedConfig.requirements?.find((r: RequirementConfig) => r.name === req.name) || {
244
+ name: req.name,
245
+ version: req.version,
246
+ type: 'npm'
247
+ };
248
+ return await this.installerFactory.checkInstallation(reqConfig, options);
249
+ })
250
+ );
251
+
252
+ // If all requirements are installed and ready, no need to proceed with installation
253
+ if (requirementStatuses.every(status => status.installed)) {
254
+ // Check if requirements are ready via ConfigurationProvider
255
+ const requirementsReady = await configProvider.isRequirementsReady(categoryName, serverName);
256
+
257
+ // Update requirement status if not ready
258
+ if (!requirementsReady) {
259
+ for (const status of requirementStatuses) {
260
+ await configProvider.updateRequirementStatus(categoryName, status.name, status);
261
+ }
262
+ }
263
+ return null;
264
+ }
265
+
266
+ // Sort requirements by order for installation
267
+ const sortedRequirements = [...serverConfig.dependencies.requirements].sort((a, b) => {
268
+ const orderA = a.order ?? Infinity;
269
+ const orderB = b.order ?? Infinity;
270
+ return orderA - orderB;
271
+ });
272
+
273
+ // Chain installations in sequence while keeping them non-blocking
274
+ await sortedRequirements.reduce((chain, requirement) => {
275
+ return chain.then(async () => {
276
+ const feeds = await configProvider.getFeedConfiguration(categoryName);
277
+ const requirementConfig = feeds?.requirements?.find((r: RequirementConfig) => r.name === requirement.name) || {
278
+ name: requirement.name,
279
+ version: requirement.version,
280
+ type: 'npm'
281
+ };
282
+
283
+ const installer = this.installerFactory.getInstaller(requirementConfig);
284
+ if (!installer) {
285
+ await configProvider.updateRequirementStatus(categoryName, requirement.name, {
286
+ name: requirement.name,
287
+ type: requirementConfig.type,
288
+ installed: false,
289
+ error: `No installer found for requirement type: ${requirementConfig.type}`,
290
+ operationStatus: {
291
+ status: 'failed',
292
+ type: 'install',
293
+ target: 'requirement',
294
+ message: `No installer found for requirement type: ${requirementConfig.type}`,
295
+ operationId: this.generateOperationId()
296
+ }
297
+ });
298
+ return;
299
+ }
300
+
301
+ const operationStatus: OperationStatus = {
302
+ status: 'pending',
303
+ type: 'install',
304
+ target: 'requirement',
305
+ message: `Installing requirement: ${requirement.name}`,
306
+ operationId: this.generateOperationId()
307
+ };
308
+
309
+ await configProvider.updateRequirementStatus(categoryName, requirement.name, {
310
+ name: requirement.name,
311
+ type: requirementConfig.type,
312
+ installed: false,
313
+ inProgress: true,
314
+ operationStatus
315
+ });
316
+
317
+ return installer.install(requirementConfig, options).then(async (installStatus) => {
318
+ const status: RequirementStatus = {
319
+ ...installStatus,
320
+ operationStatus: {
321
+ status: installStatus.installed ? 'completed' : 'failed',
322
+ type: 'install',
323
+ target: 'requirement',
324
+ message: installStatus.installed ? `Requirement ${requirement.name} installed successfully` : `Failed to install ${requirement.name}`,
325
+ operationId: operationStatus.operationId
326
+ }
327
+ };
328
+ await configProvider.updateRequirementStatus(categoryName, requirement.name, status);
329
+ });
330
+ });
331
+ }, Promise.resolve());
332
+
333
+ return null;
334
+ }
301
335
  }
302
336
 
303
337
  // Export a singleton instance (optional)
304
- // export const installationService = new InstallationService();
338
+ // export const installationService = new InstallationService();
@@ -54,25 +54,20 @@ export class RequirementService {
54
54
  * @param requirement The requirement to check for updates
55
55
  * @returns Updated status with available updates information
56
56
  */
57
- public async checkRequirementForUpdates(requirement: RequirementConfig): Promise<RequirementStatus> {
57
+ public async checkRequirementForUpdates(requirement: RequirementConfig, currentStatus: RequirementStatus): Promise<RequirementStatus> {
58
58
  // Validate requirement
59
59
  this.validateRequirement(requirement);
60
60
 
61
61
  // Get current status
62
- const status = await this.checkRequirementStatus(requirement);
63
62
 
64
63
  // Check for updates using the appropriate installer
65
64
  const installer = this.installerFactory.getInstaller(requirement);
66
- if (!installer) {
67
- return status;
68
- }
69
-
70
- // If the installer supports update checking, use it
71
- if ('checkForUpdates' in installer) {
72
- return await (installer as any).checkForUpdates(requirement, status);
65
+ if (!installer || !installer.supportCheckUpdates()) {
66
+ return currentStatus;
73
67
  }
74
68
 
75
- return status;
69
+ const status = await this.checkRequirementStatus(requirement);
70
+ return await (installer as any).checkForUpdates(requirement, status);
76
71
  }
77
72
 
78
73
  /**
@@ -60,14 +60,28 @@ const CODE_INSIDER_STRORAGE_DIR = (() => {
60
60
  */
61
61
  export const SUPPORTED_CLIENTS: Record<string, any> = {
62
62
  'MSRooCode': { /* MSROO specific settings */
63
+
64
+ extension: {
65
+ extensionId: 'microsoftai.ms-roo-cline',
66
+ leastVersion: '0.0.8',
67
+ repository: 'ai-microsoft/roo-cline',
68
+ assetName: 'ms-roo-cline-${version}.vsix',
69
+ private: true
70
+ },
63
71
  codeSettingPath: path.join(CODE_STRORAGE_DIR, 'globalStorage', 'microsoftai.ms-roo-cline', 'settings', 'cline_mcp_settings.json'),
64
- codeInsiderSettingPath: path.join(CODE_INSIDER_STRORAGE_DIR, 'globalStorage', 'microsoftai.ms-roo-clinev', 'settings', 'cline_mcp_settings.json'),
72
+ codeInsiderSettingPath: path.join(CODE_INSIDER_STRORAGE_DIR, 'globalStorage', 'microsoftai.ms-roo-cline', 'settings', 'cline_mcp_settings.json'),
65
73
  },
66
74
  'Cline': { /* VS Code specific settings */
75
+ extension: {
76
+ extensionId: 'saoudrizwan.claude-dev',
77
+ },
67
78
  codeSettingPath: path.join(CODE_STRORAGE_DIR, 'globalStorage', 'saoudrizwan.claude-dev', 'settings', 'cline_mcp_settings.json'),
68
79
  codeInsiderSettingPath: path.join(CODE_INSIDER_STRORAGE_DIR, 'globalStorage', 'saoudrizwan.claude-dev', 'settings', 'cline_mcp_settings.json'),
69
80
  },
70
81
  'GithubCopilot': { /* GitHub Copilot specific settings */
82
+ extension: {
83
+ extensionId: 'github.copilot',
84
+ },
71
85
  codeSettingPath: path.join(CODE_STRORAGE_DIR, 'settings.json'),
72
86
  codeInsiderSettingPath: path.join(CODE_INSIDER_STRORAGE_DIR, 'settings.json'),
73
87
  },