imcp 0.0.16 → 0.0.17

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 (135) hide show
  1. package/dist/cli/commands/install.js +2 -2
  2. package/dist/cli/commands/list.js +2 -2
  3. package/dist/cli/commands/serve.js +1 -1
  4. package/dist/core/RequirementService.d.ts +0 -12
  5. package/dist/core/RequirementService.js +0 -24
  6. package/dist/core/installers/clients/BaseClientInstaller.d.ts +1 -1
  7. package/dist/core/installers/clients/ClientInstaller.d.ts +1 -1
  8. package/dist/core/installers/clients/ClientInstaller.js +1 -1
  9. package/dist/core/installers/clients/ClientInstallerFactory.js +1 -1
  10. package/dist/core/installers/clients/ClineInstaller.d.ts +1 -1
  11. package/dist/core/installers/clients/ClineInstaller.js +1 -1
  12. package/dist/core/installers/clients/ExtensionInstaller.js +1 -1
  13. package/dist/core/installers/clients/GithubCopilotInstaller.d.ts +1 -1
  14. package/dist/core/installers/clients/GithubCopilotInstaller.js +1 -1
  15. package/dist/core/installers/clients/MSRooCodeInstaller.d.ts +1 -1
  16. package/dist/core/installers/clients/MSRooCodeInstaller.js +1 -1
  17. package/dist/core/installers/requirements/BaseInstaller.d.ts +1 -1
  18. package/dist/core/installers/requirements/BaseInstaller.js +1 -1
  19. package/dist/core/installers/requirements/CommandInstaller.d.ts +1 -1
  20. package/dist/core/installers/requirements/CommandInstaller.js +1 -1
  21. package/dist/core/installers/requirements/GeneralInstaller.d.ts +1 -1
  22. package/dist/core/installers/requirements/InstallerFactory.d.ts +1 -1
  23. package/dist/core/installers/requirements/NpmInstaller.d.ts +1 -1
  24. package/dist/core/installers/requirements/NpmInstaller.js +1 -1
  25. package/dist/core/installers/requirements/PipInstaller.d.ts +1 -1
  26. package/dist/core/installers/requirements/RequirementInstaller.d.ts +1 -1
  27. package/dist/core/loaders/ConfigurationLoader.d.ts +32 -0
  28. package/dist/core/loaders/ConfigurationLoader.js +236 -0
  29. package/dist/core/loaders/ConfigurationProvider.d.ts +35 -0
  30. package/dist/core/loaders/ConfigurationProvider.js +375 -0
  31. package/dist/core/loaders/ServerSchemaLoader.d.ts +11 -0
  32. package/{src/core/ServerSchemaLoader.ts → dist/core/loaders/ServerSchemaLoader.js} +43 -48
  33. package/dist/core/loaders/ServerSchemaProvider.d.ts +17 -0
  34. package/{src/core/ServerSchemaProvider.ts → dist/core/loaders/ServerSchemaProvider.js} +120 -137
  35. package/dist/core/metadatas/constants.d.ts +47 -0
  36. package/dist/core/metadatas/constants.js +94 -0
  37. package/dist/core/metadatas/types.d.ts +166 -0
  38. package/dist/core/metadatas/types.js +16 -0
  39. package/dist/core/onboard/FeedOnboardService.d.ts +1 -1
  40. package/dist/core/onboard/FeedOnboardService.js +1 -1
  41. package/dist/core/onboard/OnboardProcessor.d.ts +1 -1
  42. package/dist/core/onboard/OnboardProcessor.js +1 -1
  43. package/dist/core/onboard/OnboardStatus.d.ts +1 -1
  44. package/dist/core/onboard/OnboardStatusManager.d.ts +1 -1
  45. package/dist/core/onboard/OnboardStatusManager.js +1 -1
  46. package/dist/core/validators/FeedValidator.d.ts +1 -1
  47. package/dist/core/validators/IServerValidator.d.ts +1 -1
  48. package/dist/core/validators/SSEServerValidator.d.ts +1 -1
  49. package/dist/core/validators/ServerValidatorFactory.d.ts +1 -1
  50. package/dist/core/validators/StdioServerValidator.d.ts +1 -1
  51. package/dist/core/validators/StdioServerValidator.js +1 -1
  52. package/dist/index.d.ts +3 -3
  53. package/dist/index.js +3 -3
  54. package/dist/services/InstallationService.d.ts +50 -0
  55. package/dist/services/InstallationService.js +350 -0
  56. package/dist/services/MCPManager.d.ts +28 -0
  57. package/dist/services/MCPManager.js +188 -0
  58. package/dist/services/RequirementService.d.ts +40 -0
  59. package/dist/services/RequirementService.js +110 -0
  60. package/dist/services/ServerService.d.ts +2 -2
  61. package/dist/services/ServerService.js +5 -5
  62. package/dist/utils/adoUtils.d.ts +2 -2
  63. package/dist/utils/adoUtils.js +1 -1
  64. package/dist/utils/feedUtils.js +1 -1
  65. package/dist/utils/githubUtils.d.ts +1 -1
  66. package/dist/utils/githubUtils.js +1 -1
  67. package/dist/utils/logger.js +1 -1
  68. package/dist/utils/macroExpressionUtils.d.ts +1 -1
  69. package/dist/utils/osUtils.d.ts +1 -1
  70. package/dist/utils/osUtils.js +1 -1
  71. package/dist/web/contract/serverContract.d.ts +1 -1
  72. package/dist/web/public/index.html +1 -3
  73. package/dist/web/public/js/api.js +2 -80
  74. package/dist/web/server.js +2 -2
  75. package/package.json +1 -1
  76. package/src/cli/commands/install.ts +3 -3
  77. package/src/cli/commands/list.ts +2 -2
  78. package/src/cli/commands/serve.ts +3 -2
  79. package/src/cli/index.ts +1 -1
  80. package/src/core/installers/clients/BaseClientInstaller.ts +134 -3
  81. package/src/core/installers/clients/ClientInstaller.ts +3 -3
  82. package/src/core/installers/clients/ClientInstallerFactory.ts +1 -1
  83. package/src/core/installers/clients/ClineInstaller.ts +1 -101
  84. package/src/core/installers/clients/ExtensionInstaller.ts +1 -1
  85. package/src/core/installers/clients/GithubCopilotInstaller.ts +1 -101
  86. package/src/core/installers/clients/MSRooCodeInstaller.ts +1 -102
  87. package/src/core/installers/requirements/BaseInstaller.ts +2 -2
  88. package/src/core/installers/requirements/CommandInstaller.ts +1 -1
  89. package/src/core/installers/requirements/GeneralInstaller.ts +1 -1
  90. package/src/core/installers/requirements/InstallerFactory.ts +1 -1
  91. package/src/core/installers/requirements/NpmInstaller.ts +12 -12
  92. package/src/core/installers/requirements/PipInstaller.ts +1 -1
  93. package/src/core/installers/requirements/RequirementInstaller.ts +1 -1
  94. package/src/core/{ConfigurationLoader.ts → loaders/ConfigurationLoader.ts} +31 -7
  95. package/src/core/{ConfigurationProvider.ts → loaders/ConfigurationProvider.ts} +18 -10
  96. package/src/core/loaders/ServerSchemaLoader.ts +117 -0
  97. package/src/core/loaders/ServerSchemaProvider.ts +99 -0
  98. package/src/core/{types.ts → metadatas/types.ts} +3 -2
  99. package/src/core/onboard/FeedOnboardService.ts +270 -146
  100. package/src/core/onboard/OnboardProcessor.ts +60 -11
  101. package/src/core/onboard/OnboardStatus.ts +7 -2
  102. package/src/core/onboard/OnboardStatusManager.ts +270 -43
  103. package/src/core/validators/FeedValidator.ts +65 -9
  104. package/src/core/validators/IServerValidator.ts +1 -1
  105. package/src/core/validators/SSEServerValidator.ts +2 -2
  106. package/src/core/validators/ServerValidatorFactory.ts +1 -1
  107. package/src/core/validators/StdioServerValidator.ts +86 -34
  108. package/src/index.ts +3 -3
  109. package/src/{core → services}/InstallationService.ts +5 -5
  110. package/src/{core → services}/MCPManager.ts +10 -5
  111. package/src/{core → services}/RequirementService.ts +2 -31
  112. package/src/services/ServerService.ts +7 -7
  113. package/src/utils/adoUtils.ts +3 -3
  114. package/src/utils/feedUtils.ts +2 -2
  115. package/src/utils/githubUtils.ts +2 -2
  116. package/src/utils/logger.ts +13 -1
  117. package/src/utils/macroExpressionUtils.ts +1 -1
  118. package/src/utils/osUtils.ts +4 -4
  119. package/src/web/contract/serverContract.ts +2 -2
  120. package/src/web/public/index.html +1 -3
  121. package/src/web/public/js/api.js +2 -80
  122. package/src/web/public/js/modal/installation.js +1 -1
  123. package/src/web/public/js/onboard/ONBOARDING_PAGE_DESIGN.md +41 -9
  124. package/src/web/public/js/onboard/formProcessor.js +200 -34
  125. package/src/web/public/js/onboard/index.js +2 -2
  126. package/src/web/public/js/onboard/publishHandler.js +30 -22
  127. package/src/web/public/js/onboard/templates.js +34 -40
  128. package/src/web/public/js/onboard/uiHandlers.js +175 -84
  129. package/src/web/public/js/onboard/validationHandlers.js +147 -64
  130. package/src/web/public/js/serverCategoryDetails.js +19 -4
  131. package/src/web/public/js/serverCategoryList.js +13 -1
  132. package/src/web/public/onboard.html +1 -1
  133. package/src/web/server.ts +30 -14
  134. package/src/services/InstallRequestValidator.ts +0 -112
  135. /package/src/core/{constants.ts → metadatas/constants.ts} +0 -0
@@ -1,6 +1,10 @@
1
- import { FeedConfiguration, McpConfig } from "../types.js";
1
+ import fs from 'fs/promises';
2
+ import path from 'path';
3
+ import { FeedConfiguration, McpConfig } from "../metadatas/types.js";
2
4
  import { serverValidatorFactory } from "./ServerValidatorFactory.js";
3
5
  import { Logger } from "../../utils/logger.js";
6
+ import { onboardStatusManager } from "../onboard/OnboardStatusManager.js";
7
+ import { OnboardingProcessStatus, OperationType } from "../onboard/OnboardStatus.js";
4
8
 
5
9
  /**
6
10
  * Validates feed configurations to ensure they meet required criteria
@@ -9,24 +13,30 @@ export class FeedValidator {
9
13
  /**
10
14
  * Validates a feed configuration
11
15
  * @param config The feed configuration to validate
16
+ * @param categoryName The name of the category (feed name) for status updates
17
+ * @param operationType The type of operation for status updates
12
18
  * @returns true if valid, throws error if invalid
13
19
  */
14
- public validate(config: FeedConfiguration): boolean {
20
+ public async validate(config: FeedConfiguration, categoryName: string, operationType: OperationType): Promise<boolean> {
15
21
  try {
22
+ await onboardStatusManager.recordStep(categoryName, operationType, `Starting feed validation for '${config.name}'`);
16
23
  Logger.debug(`Validating feed configuration: ${config.name}`);
17
24
 
18
25
  // Validate required fields
26
+ await onboardStatusManager.recordStep(categoryName, operationType, `Validating required fields for '${config.name}'`);
19
27
  if (!config.name) throw new Error('Feed name is required');
20
28
  if (!config.displayName) throw new Error('Feed display name is required');
21
29
  if (!config.description) throw new Error('Feed description is required');
22
30
 
23
31
  // Validate MCP servers array
32
+ await onboardStatusManager.recordStep(categoryName, operationType, `Validating MCP servers array for '${config.name}'`);
24
33
  if (!Array.isArray(config.mcpServers) || config.mcpServers.length === 0) {
25
34
  throw new Error('Feed must contain at least one MCP server');
26
35
  }
27
36
 
28
37
  // Validate requirements if present
29
38
  if (config.requirements) {
39
+ await onboardStatusManager.recordStep(categoryName, operationType, `Validating requirements for '${config.name}'`);
30
40
  for (const req of config.requirements) {
31
41
  if (!req.name) throw new Error('Requirement name is required');
32
42
  if (!req.type) throw new Error('Requirement type is required');
@@ -34,11 +44,13 @@ export class FeedValidator {
34
44
  }
35
45
  }
36
46
 
47
+ await onboardStatusManager.recordStep(categoryName, operationType, `Feed validation successful for '${config.name}'`, undefined, OnboardingProcessStatus.VALIDATING); // Still in progress until all servers validated
37
48
  Logger.debug(`Feed configuration validation successful: ${config.name}`);
38
49
  return true;
39
50
  } catch (error) {
40
- const errorMsg = `Feed validation failed: ${error instanceof Error ? error.message : String(error)}`;
51
+ const errorMsg = `Feed validation failed for '${config.name}': ${error instanceof Error ? error.message : String(error)}`;
41
52
  Logger.error(errorMsg);
53
+ await onboardStatusManager.recordStep(categoryName, operationType, `Feed validation failed for '${config.name}'`, undefined, OnboardingProcessStatus.FAILED, errorMsg);
42
54
  throw new Error(errorMsg);
43
55
  }
44
56
  }
@@ -47,29 +59,73 @@ export class FeedValidator {
47
59
  * Validates a single MCP server configuration using the appropriate validator
48
60
  * @param server The MCP server configuration to validate
49
61
  * @param config The feed configuration containing shared requirements
62
+ * @param categoryName The name of the category (feed name) for status updates
63
+ * @param operationType The type of operation for status updates
50
64
  * @returns true if valid, throws error if invalid
51
65
  */
52
- public async validateServer(server: McpConfig, config: FeedConfiguration): Promise<boolean> {
66
+ public async validateServer(server: McpConfig, config: FeedConfiguration, categoryName: string, operationType: OperationType): Promise<boolean> {
67
+ const feedName = config.name; // categoryName is the feedName in this context
68
+ const serverDisplayName = server.name;
69
+
53
70
  try {
54
- Logger.debug(`Validating server configuration: ${server.name}`);
71
+ await onboardStatusManager.recordStep(categoryName, operationType, `Starting server validation for '${serverDisplayName}' in feed '${feedName}'`, server.name);
72
+ Logger.debug(`Validating server configuration: ${serverDisplayName} in feed ${feedName}`);
55
73
 
56
74
  // Validate basic required fields
75
+ await onboardStatusManager.recordStep(categoryName, operationType, `Validating basic fields for server '${serverDisplayName}'`, server.name);
57
76
  if (!server.name) throw new Error('Server name is required');
58
77
  if (!server.description) throw new Error('Server description is required');
59
78
  if (!server.mode) throw new Error('Server mode is required');
60
79
  if (!server.installation) throw new Error('Server installation configuration is required');
61
80
 
81
+ // Validate schema file existence if schemas is defined
82
+ if (server.schemas) {
83
+ await onboardStatusManager.recordStep(categoryName, operationType, `Validating schema file for server '${serverDisplayName}'`, server.name);
84
+ try {
85
+ // Assuming server.schemas is a relative path from the project root or a path that needs to be resolved.
86
+ // For now, let's assume it's relative to the feed file's location or an absolute path.
87
+ // If it's relative to the feed file, we might need the feed file's path.
88
+ // For simplicity, let's assume it's a path that can be directly accessed.
89
+ // If the schema path is relative to the feed config file, this logic will need adjustment.
90
+ // For now, we'll treat it as a path that `fs.access` can check.
91
+ // It's more likely that schema paths are relative to the feed definition file itself.
92
+ // However, the task description implies `server.schemas` is a path to be checked.
93
+ // Let's assume for now it's a path that should exist.
94
+ // If the schemas are stored within the repo, their paths would be relative to the repo root.
95
+ // During validation, we might not have the repo context directly here.
96
+ // This needs clarification on how schema paths are resolved during validation.
97
+ // For now, we'll proceed with a direct check.
98
+ // If the schema is part of the feed package, its path might be relative to the package root.
99
+ // Let's assume the path is resolvable from the current working directory or is absolute.
100
+ // This part might need refinement based on where schema files are expected to be.
101
+ // The task implies `server.schemas` is a string path.
102
+ const schemaPath = server.schemas; // This path needs to be correctly resolved.
103
+ // If it's relative to the feed file, we need that context.
104
+ // For now, we'll assume it's a path that can be checked.
105
+ // This might be an issue if it's relative to a file not yet in the repo.
106
+ // Let's assume it's a path that should be resolvable.
107
+ await fs.access(schemaPath);
108
+ Logger.debug(`Schema file ${schemaPath} exists for server ${serverDisplayName}`);
109
+ } catch (fileAccessError) {
110
+ throw new Error(`Schema file '${server.schemas}' not found for server '${serverDisplayName}'.`);
111
+ }
112
+ }
113
+
62
114
  // Get the appropriate validator for this server type
115
+ await onboardStatusManager.recordStep(categoryName, operationType, `Obtaining specific validator for server '${serverDisplayName}'`, server.name);
63
116
  const validator = serverValidatorFactory.getValidatorForServer(server);
64
-
117
+
65
118
  // Perform mode-specific validation with feed config
66
- await validator.validateServer(server, config);
119
+ await onboardStatusManager.recordStep(categoryName, operationType, `Performing mode-specific validation for server '${serverDisplayName}'`, server.name);
120
+ await validator.validateServer(server, config); // This internal call won't have status updates unless modified too
67
121
 
68
- Logger.debug(`Server configuration validation successful: ${server.name}`);
122
+ await onboardStatusManager.recordStep(categoryName, operationType, `Server validation successful for '${serverDisplayName}'`, server.name, OnboardingProcessStatus.VALIDATING); // Still in progress until all servers validated
123
+ Logger.debug(`Server configuration validation successful: ${serverDisplayName}`);
69
124
  return true;
70
125
  } catch (error) {
71
- const errorMsg = `Server validation failed: ${error instanceof Error ? error.message : String(error)}`;
126
+ const errorMsg = `Server validation failed for '${serverDisplayName}' in feed '${feedName}': ${error instanceof Error ? error.message : String(error)}`;
72
127
  Logger.error(errorMsg);
128
+ await onboardStatusManager.recordStep(categoryName, operationType, `Server validation failed for '${serverDisplayName}'`, server.name, OnboardingProcessStatus.FAILED, errorMsg);
73
129
  throw new Error(errorMsg);
74
130
  }
75
131
  }
@@ -1,4 +1,4 @@
1
- import { McpConfig, FeedConfiguration } from "../types.js";
1
+ import { McpConfig, FeedConfiguration } from "../metadatas/types.js";
2
2
 
3
3
  /**
4
4
  * Interface for MCP server configuration validators
@@ -1,4 +1,4 @@
1
- import { McpConfig, FeedConfiguration } from "../types.js";
1
+ import { McpConfig, FeedConfiguration } from "../metadatas/types.js";
2
2
  import { IServerValidator } from "./IServerValidator.js";
3
3
  import { Logger } from "../../utils/logger.js";
4
4
 
@@ -16,7 +16,7 @@ export class SSEServerValidator implements IServerValidator {
16
16
  public async validateServer(server: McpConfig, config: FeedConfiguration): Promise<boolean> {
17
17
  try {
18
18
  Logger.debug(`Validating SSE server configuration: ${server.name}`);
19
-
19
+
20
20
  if (!server.installation?.url) {
21
21
  throw new Error('SSE server URL is required in installation configuration');
22
22
  }
@@ -1,4 +1,4 @@
1
- import { McpConfig } from "../types.js";
1
+ import { McpConfig } from "../metadatas/types.js";
2
2
  import { IServerValidator, ValidatorType } from "./IServerValidator.js";
3
3
  import { StdioServerValidator } from "./StdioServerValidator.js";
4
4
  import { SSEServerValidator } from "./SSEServerValidator.js";
@@ -1,4 +1,4 @@
1
- import { McpConfig, RequirementConfig, FeedConfiguration } from "../types.js";
1
+ import { McpConfig, RequirementConfig, FeedConfiguration } from "../metadatas/types.js";
2
2
  import { IServerValidator } from "./IServerValidator.js";
3
3
  import { Logger } from "../../utils/logger.js";
4
4
  import { createInstallerFactory } from "../installers/index.js";
@@ -6,7 +6,7 @@ import { exec, spawn } from 'child_process';
6
6
  import util from 'util';
7
7
  import { MACRO_EXPRESSIONS, resolveNpmModulePath } from "../../utils/macroExpressionUtils.js";
8
8
  import { getSystemPythonPackageDirectory } from "../../utils/osUtils.js";
9
- import { SETTINGS_DIR } from "../constants.js";
9
+ import { SETTINGS_DIR } from "../metadatas/constants.js";
10
10
  import path from "path";
11
11
 
12
12
  const execPromise = util.promisify(exec);
@@ -108,7 +108,10 @@ export class StdioServerValidator implements IServerValidator {
108
108
  // Resolve npm module paths in arguments
109
109
  Logger.debug('Resolving npm module paths in arguments');
110
110
  const npmPath = requirement ? this._getRequirementFolderPath(requirement) : undefined;
111
- finalArgs = args.map(arg => arg.replace(MACRO_EXPRESSIONS.NPMPATH, resolveNpmModulePath(npmPath)));
111
+ finalArgs = args.map(arg => arg
112
+ .replace(MACRO_EXPRESSIONS.NPMPATH, resolveNpmModulePath(npmPath))
113
+ .replace(/\\/g, '/')
114
+ );
112
115
  Logger.debug(`Resolved npm arguments: ${finalArgs.join(' ')}`);
113
116
  } else if (command === 'python' || command === 'python3') {
114
117
  // Resolve Python package paths in arguments
@@ -125,60 +128,109 @@ export class StdioServerValidator implements IServerValidator {
125
128
  }
126
129
 
127
130
  return await new Promise<boolean>((resolve, reject) => {
128
- Logger.debug(`Starting process with command: ${command} ${finalArgs.join(' ')}`);
131
+ Logger.debug(`Starting process for server test with command: ${command} ${finalArgs.join(' ')}`);
129
132
 
130
- // Set timeout for server startup test
131
- const timeout = setTimeout(() => {
132
- const msg = 'Server startup test timed out after 10 seconds';
133
- Logger.error(msg);
134
- serverProcess.kill();
135
- reject(new Error(msg));
136
- }, 20000);
133
+ const timeoutDuration = 20000; // 20 seconds for server startup test
137
134
 
138
- // Start the server process
139
135
  const serverProcess = spawn(command, finalArgs, {
140
- stdio: ['ignore', 'pipe', 'pipe'],
136
+ stdio: ['ignore', 'pipe', 'pipe'], // stdin, stdout, stderr
141
137
  shell: true
142
138
  });
143
139
 
144
- let output = '';
145
- let errorOutput = '';
140
+ let stdoutData = '';
141
+ let stderrData = '';
142
+ let settled = false;
143
+
144
+ const cleanupAndResolve = (value: boolean) => {
145
+ if (settled) return;
146
+ settled = true;
147
+ clearTimeout(timeoutId);
148
+ serverProcess.stdout.removeAllListeners();
149
+ serverProcess.stderr.removeAllListeners();
150
+ serverProcess.removeAllListeners('exit');
151
+ serverProcess.removeAllListeners('error');
152
+ if (serverProcess.exitCode === null && !serverProcess.killed) {
153
+ serverProcess.kill();
154
+ }
155
+ resolve(value);
156
+ };
157
+
158
+ const cleanupAndReject = (err: Error) => {
159
+ if (settled) return;
160
+ settled = true;
161
+ clearTimeout(timeoutId);
162
+ serverProcess.stdout.removeAllListeners();
163
+ serverProcess.stderr.removeAllListeners();
164
+ serverProcess.removeAllListeners('exit');
165
+ serverProcess.removeAllListeners('error');
166
+ if (serverProcess.exitCode === null && !serverProcess.killed) {
167
+ serverProcess.kill();
168
+ }
169
+ reject(err);
170
+ };
171
+
172
+ const timeoutId = setTimeout(() => {
173
+ if (settled) return;
174
+
175
+ if (serverProcess.exitCode === null) { // Process is still running
176
+ Logger.debug(`Server startup test: Process still running after ${timeoutDuration / 1000} seconds. Considering it successful.`);
177
+ Logger.debug(`Collected stdout:\n${stdoutData}`);
178
+ Logger.debug(`Collected stderr:\n${stderrData}`);
179
+ cleanupAndResolve(true);
180
+ } else {
181
+ // Process exited before timeout, 'exit' handler should have caught it.
182
+ // This is a fallback or race condition handling.
183
+ const msg = `Server startup test: Process exited with code ${serverProcess.exitCode} before timeout completed.`;
184
+ Logger.error(msg);
185
+ Logger.debug(`Collected stdout:\n${stdoutData}`);
186
+ Logger.error(`Collected stderr:\n${stderrData}`); // Log stderr as error here
187
+ cleanupAndReject(new Error(msg + ` Stderr: ${stderrData}`));
188
+ }
189
+ }, timeoutDuration);
146
190
 
147
- // Collect stdout
148
191
  serverProcess.stdout.on('data', (data: Buffer) => {
149
- output += data.toString();
150
- // If we see any output, consider it a success
151
- clearTimeout(timeout);
152
- serverProcess.kill();
153
- resolve(true);
192
+ const messageChunk = data.toString();
193
+ stdoutData += messageChunk;
194
+ Logger.debug(`Server stdout: ${messageChunk.trim()}`);
154
195
  });
155
196
 
156
- // Collect stderr
157
197
  serverProcess.stderr.on('data', (data: Buffer) => {
158
- errorOutput += data.toString();
198
+ const messageChunk = data.toString();
199
+ stderrData += messageChunk;
200
+ // Log stderr, but it doesn't automatically mean failure.
201
+ // The exit code or an 'error' event will determine failure.
202
+ Logger.debug(`Server stderr: ${messageChunk.trim()}`);
159
203
  });
160
204
 
161
- // Handle process exit
162
- serverProcess.on('exit', (code: number | null) => {
163
- clearTimeout(timeout);
164
- if (code !== null && code !== 0) {
165
- const msg = `Server startup failed with code ${code}: ${errorOutput}`;
205
+ serverProcess.on('exit', (code: number | null, signal: string | null) => {
206
+ if (settled) return;
207
+
208
+ Logger.debug(`Server process exited with code ${code}, signal: ${signal}.`);
209
+ Logger.debug(`Final stdout:\n${stdoutData}`);
210
+ Logger.debug(`Final stderr:\n${stderrData}`);
211
+
212
+ if (code === 0) {
213
+ cleanupAndResolve(true);
214
+ } else {
215
+ const msg = `Server process exited with non-zero code ${code} or signal ${signal}. Stderr: ${stderrData.trim()}`;
166
216
  Logger.error(msg);
167
- reject(new Error(msg));
217
+ cleanupAndReject(new Error(msg));
168
218
  }
169
219
  });
170
220
 
171
- // Handle process error
172
221
  serverProcess.on('error', (error: Error) => {
173
- clearTimeout(timeout);
174
- const msg = `Server startup error: ${error.message}`;
222
+ if (settled) return;
223
+ const msg = `Server process failed to start or encountered an error: ${error.message}.`;
175
224
  Logger.error(msg);
176
- reject(error);
225
+ Logger.debug(`Stdout at error:\n${stdoutData}`);
226
+ Logger.error(`Stderr at error:\n${stderrData}`);
227
+ cleanupAndReject(new Error(`${msg} Stderr: ${stderrData.trim()}`));
177
228
  });
178
229
  });
179
230
  } catch (error) {
180
- const msg = `Failed to test server startup: ${error instanceof Error ? error.message : String(error)}`;
231
+ const msg = `Failed to test server startup (outer catch): ${error instanceof Error ? error.message : String(error)}`;
181
232
  Logger.error(msg);
233
+ // Ensure the error thrown is an instance of Error
182
234
  throw error instanceof Error ? error : new Error(msg);
183
235
  }
184
236
  }
package/src/index.ts CHANGED
@@ -11,14 +11,14 @@ export {
11
11
  RequirementConfig,
12
12
  RequirementStatus,
13
13
  RegistryConfig
14
- } from './core/types.js';
14
+ } from './core/metadatas/types.js';
15
15
 
16
16
  // Core functionality
17
- export { MCPManager, mcpManager } from './core/MCPManager.js';
17
+ export { MCPManager, mcpManager } from './services/MCPManager.js';
18
18
 
19
19
  // Services
20
20
  export { ServerService, serverService } from './services/ServerService.js';
21
- export { RequirementService, requirementService } from './core/RequirementService.js';
21
+ export { RequirementService, requirementService } from './services/RequirementService.js';
22
22
 
23
23
  // Installer interfaces and implementations
24
24
  export {
@@ -11,11 +11,11 @@ import {
11
11
  OperationStatus,
12
12
  RequirementStatus,
13
13
  McpConfig
14
- } from './types.js';
15
- import { RequirementInstaller, InstallerFactory, createInstallerFactory } from './installers/index.js';
16
- import { SUPPORTED_CLIENTS } from './constants.js';
17
- import { ClientInstaller } from './installers/clients/ClientInstaller.js';
18
- import { ConfigurationProvider } from './ConfigurationProvider.js';
14
+ } from '../core/metadatas/types.js';
15
+ import { RequirementInstaller, InstallerFactory, createInstallerFactory } from '../core/installers/index.js';
16
+ import { SUPPORTED_CLIENTS } from '../core/metadatas/constants.js';
17
+ import { ClientInstaller } from '../core/installers/clients/ClientInstaller.js';
18
+ import { ConfigurationProvider } from '../core/loaders/ConfigurationProvider.js';
19
19
  import { Logger } from '../utils/logger.js';
20
20
 
21
21
  const execPromise = util.promisify(exec);
@@ -1,5 +1,6 @@
1
1
  import { EventEmitter } from 'events';
2
- import { ConfigurationProvider } from './ConfigurationProvider.js';
2
+ import { ConfigurationProvider } from '../core/loaders/ConfigurationProvider.js';
3
+ import { ServerSchemaProvider } from '../core/loaders/ServerSchemaProvider.js';
3
4
  import { InstallationService } from './InstallationService.js';
4
5
  import {
5
6
  MCPEvent,
@@ -11,20 +12,23 @@ import {
11
12
  ServerUninstallOptions,
12
13
  InstallationStatus,
13
14
  FeedConfiguration,
14
- } from './types.js';
15
- import { OperationStatus } from './onboard/OnboardStatus.js';
15
+ } from '../core/metadatas/types.js';
16
+ import { OperationStatus } from '../core/onboard/OnboardStatus.js';
16
17
  import { Logger } from '../utils/logger.js';
17
- import { FeedOnboardService } from './onboard/FeedOnboardService.js';
18
+ import { FeedOnboardService } from '../core/onboard/FeedOnboardService.js';
18
19
  import path from 'path';
20
+ import { Server } from 'http';
19
21
 
20
22
  export class MCPManager extends EventEmitter {
21
23
  private installationService: InstallationService;
22
24
  private configProvider: ConfigurationProvider;
23
25
  private feedOnboardService: FeedOnboardService;
26
+ private schemaProvider: ServerSchemaProvider;
24
27
 
25
28
  constructor() {
26
29
  super();
27
30
  this.configProvider = ConfigurationProvider.getInstance();
31
+ this.schemaProvider = ServerSchemaProvider.getInstance();
28
32
  this.installationService = new InstallationService();
29
33
  this.feedOnboardService = new FeedOnboardService();
30
34
  }
@@ -33,9 +37,10 @@ export class MCPManager extends EventEmitter {
33
37
  await this.configProvider.syncFeeds();
34
38
  }
35
39
 
36
- async initialize(feedFile?: string): Promise<void> {
40
+ async initialize(feedFile?: string, schemasDirectory?: string): Promise<void> {
37
41
  try {
38
42
  await this.configProvider.initialize(feedFile);
43
+ await this.schemaProvider.initialize(schemasDirectory);
39
44
  } catch (error) {
40
45
  console.error("Error during MCPManager initialization:", error);
41
46
  throw error;
@@ -1,5 +1,5 @@
1
- import { RequirementConfig, RequirementStatus, ServerInstallOptions } from './types.js';
2
- import { createInstallerFactory } from './installers/index.js';
1
+ import { RequirementConfig, RequirementStatus, ServerInstallOptions } from '../core/metadatas/types.js';
2
+ import { createInstallerFactory } from '../core/installers/index.js';
3
3
  import { exec } from 'child_process';
4
4
  import util from 'util';
5
5
 
@@ -23,19 +23,6 @@ export class RequirementService {
23
23
  return RequirementService.instance;
24
24
  }
25
25
 
26
- /**
27
- * Install a requirement
28
- * @param requirement The requirement to install
29
- * @returns The installation status
30
- */
31
- public async installRequirement(requirement: RequirementConfig, options?: ServerInstallOptions): Promise<RequirementStatus> {
32
- // Validate requirement
33
- this.validateRequirement(requirement);
34
-
35
- // Install the requirement
36
- return await this.installerFactory.install(requirement, options);
37
- }
38
-
39
26
  /**
40
27
  * Check the installation status of a requirement
41
28
  * @param requirement The requirement to check
@@ -94,22 +81,6 @@ export class RequirementService {
94
81
  return await this.installerFactory.install(updatedRequirement, options);
95
82
  }
96
83
 
97
- /**
98
- * Install multiple requirements
99
- * @param requirements The requirements to install
100
- * @returns A map of requirement names to their installation status
101
- */
102
- public async installRequirements(requirements: RequirementConfig[]): Promise<Record<string, RequirementStatus>> {
103
- const results: Record<string, RequirementStatus> = {};
104
-
105
- // Process each requirement sequentially to avoid conflicts
106
- for (const requirement of requirements) {
107
- results[requirement.name] = await this.installRequirement(requirement);
108
- }
109
-
110
- return results;
111
- }
112
-
113
84
  /**
114
85
  * Validate a requirement configuration
115
86
  * @param requirement The requirement to validate
@@ -1,7 +1,7 @@
1
1
  import path from 'path';
2
2
  import { fileURLToPath } from 'url';
3
3
  import { Logger } from '../utils/logger.js';
4
- import { getServerSchemaProvider, ServerSchema } from '../core/ServerSchemaProvider.js';
4
+ import { ServerSchema, ServerSchemaProvider } from '../core/loaders/ServerSchemaProvider.js';
5
5
  import {
6
6
  MCPServerCategory,
7
7
  ServerInstallOptions,
@@ -10,10 +10,10 @@ import {
10
10
  ServerUninstallOptions,
11
11
  FeedConfiguration,
12
12
  OperationStatus as CoreOperationStatus
13
- } from '../core/types.js';
14
- import { mcpManager } from '../core/MCPManager.js';
13
+ } from '../core/metadatas/types.js';
14
+ import { mcpManager } from './MCPManager.js';
15
15
  import { OperationStatus } from '../core/onboard/OnboardStatus.js';
16
- import { UPDATE_CHECK_INTERVAL_MS } from '../core/constants.js';
16
+ import { UPDATE_CHECK_INTERVAL_MS } from '../core/metadatas/constants.js';
17
17
  import { updateCheckTracker } from '../utils/UpdateCheckTracker.js';
18
18
 
19
19
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
@@ -70,8 +70,8 @@ export class ServerService {
70
70
  }
71
71
 
72
72
  try {
73
- const { requirementService } = await import('../core/RequirementService.js');
74
- const { configProvider } = await import('../core/ConfigurationProvider.js');
73
+ const { requirementService } = await import('./RequirementService.js');
74
+ const { configProvider } = await import('../core/loaders/ConfigurationProvider.js');
75
75
 
76
76
  for (const requirement of serverCategory.feedConfiguration.requirements) {
77
77
  if (requirement.version.includes('latest')) {
@@ -132,7 +132,7 @@ export class ServerService {
132
132
  */
133
133
  async getServerSchema(categoryName: string, serverName: string): Promise<ServerSchema | undefined> {
134
134
  try {
135
- const provider = await getServerSchemaProvider();
135
+ const provider = ServerSchemaProvider.getInstance();
136
136
  const serverMcpConfig = await mcpManager.getServerMcpConfig(categoryName, serverName);
137
137
  return await provider.getSchema(categoryName, serverMcpConfig?.schemas || `${serverName}.json`);
138
138
  } catch (error) {
@@ -1,5 +1,5 @@
1
- import { RequirementConfig, RegistryConfig } from '../core/types.js';
2
- import { SETTINGS_DIR } from '../core/constants.js';
1
+ import { RequirementConfig, RegistryConfig } from '../core/metadatas/types.js';
2
+ import { SETTINGS_DIR } from '../core/metadatas/constants.js';
3
3
  import { Logger } from './logger.js';
4
4
  import { exec, execSync } from 'child_process';
5
5
  import util from 'util';
@@ -183,7 +183,7 @@ export async function handleArtifact(
183
183
  export async function getArtifactLatestVersion(
184
184
  requirement: RequirementConfig,
185
185
  registry: RegistryConfig['artifacts'],
186
- options?: import('../core/types.js').ServerInstallOptions, // Added import for ServerInstallOptions
186
+ options?: import('../core/metadatas/types.js').ServerInstallOptions, // Added import for ServerInstallOptions
187
187
  targetDir?: string // Optional target directory for npm
188
188
  ): Promise<string | undefined> {
189
189
  if (!registry || !registry.registryUrl) {
@@ -1,6 +1,6 @@
1
1
  import fs from 'fs/promises';
2
2
  import { Logger } from './logger.js';
3
- import { LOCAL_FEEDS_DIR } from '../core/constants.js';
3
+ import { LOCAL_FEEDS_DIR } from '../core/metadatas/constants.js';
4
4
 
5
5
  /**
6
6
  * Checks if local feeds exist in the LOCAL_FEEDS_DIR
@@ -21,7 +21,7 @@ export async function hasLocalFeeds(): Promise<boolean> {
21
21
  // Check if directory contains any json files
22
22
  const files = await fs.readdir(LOCAL_FEEDS_DIR);
23
23
  const hasJsonFiles = files.some(file => file.endsWith('.json'));
24
-
24
+
25
25
  Logger.debug(`Local feeds directory ${hasJsonFiles ? 'contains' : 'does not contain'} JSON files`);
26
26
  return hasJsonFiles;
27
27
  } catch (error) {
@@ -1,11 +1,11 @@
1
- import { RegistryConfig, RequirementConfig } from '../core/types.js';
1
+ import { RegistryConfig, RequirementConfig } from '../core/metadatas/types.js';
2
2
  import { exec } from 'child_process';
3
3
  import util from 'util';
4
4
  import fs from 'fs/promises';
5
5
  import path from 'path';
6
6
  import { extractZipFile } from './clientUtils.js';
7
7
  import { Logger } from './logger.js';
8
- import { SETTINGS_DIR } from '../core/constants.js';
8
+ import { SETTINGS_DIR } from '../core/metadatas/constants.js';
9
9
 
10
10
  const execAsync = util.promisify(exec);
11
11
 
@@ -1,6 +1,6 @@
1
1
  import fs from 'fs';
2
2
  import path from 'path';
3
- import { SETTINGS_DIR } from '../core/constants.js';
3
+ import { SETTINGS_DIR } from '../core/metadatas/constants.js';
4
4
 
5
5
  export class Logger {
6
6
  private static verbose = false;
@@ -56,6 +56,18 @@ export class Logger {
56
56
  await this.writeToLogFile('INFO', message);
57
57
  }
58
58
 
59
+ static async info(message: string): Promise<void> {
60
+ console.info(message);
61
+ await this.writeToLogFile('INFO', message);
62
+ }
63
+
64
+ static async warn(message: string): Promise<void> {
65
+ const yellowColor = '\x1b[33m';
66
+ const resetColor = '\x1b[0m';
67
+ console.warn(`${yellowColor}${message}${resetColor}`);
68
+ await this.writeToLogFile('WARN', message);
69
+ }
70
+
59
71
  static async debug(message: string | object): Promise<void> {
60
72
  let formattedMessage: string;
61
73
 
@@ -1,6 +1,6 @@
1
1
  import { Logger } from './logger.js';
2
2
  import { getPythonPackagePath, getSystemPythonPackageDirectory, GetBrowserPath } from './osUtils.js';
3
- import { ServerInstallOptions } from '../core/types.js'; // Adjusted path
3
+ import { ServerInstallOptions } from '../core/metadatas/types.js'; // Adjusted path
4
4
  import * as fsSync from 'fs';
5
5
  import * as path from 'path';
6
6
  import { execSync } from 'child_process';
@@ -1,4 +1,4 @@
1
- import { OSType } from '../core/types.js';
1
+ import { OSType } from '../core/metadatas/types.js';
2
2
  import os from 'os';
3
3
  import { exec } from 'child_process';
4
4
  import util from 'util';
@@ -322,7 +322,7 @@ export function getPythonPackagePath(pythonExecutablePath: string): string {
322
322
  const minorVer = parts[3].split('_')[0];
323
323
  const version = majorVer + minorVer; // Combines "3" and "13" to "313"
324
324
  const localAppData = process.env.LOCALAPPDATA;
325
-
325
+
326
326
  if (localAppData) {
327
327
  const sitePkgsPath = path.join(
328
328
  localAppData,
@@ -333,12 +333,12 @@ export function getPythonPackagePath(pythonExecutablePath: string): string {
333
333
  'Python' + version,
334
334
  'site-packages'
335
335
  );
336
-
336
+
337
337
  Logger.debug(`Resolved Windows Store Python site-packages path: ${sitePkgsPath}`);
338
338
  return sitePkgsPath;
339
339
  }
340
340
  }
341
-
341
+
342
342
  Logger.debug('Could not resolve Windows Store Python site-packages path');
343
343
  // Fallback to user's site-packages
344
344
  return path.join(process.env.APPDATA || '', 'Python', 'Python3', 'site-packages');
@@ -1,5 +1,5 @@
1
- import { DependencyConfig, RegistryConfig, ServerInstallOptions, EnvVariableConfig } from '../../core/types.js';
2
- import { ServerSchema } from '../../core/ServerSchemaProvider.js';
1
+ import { DependencyConfig, RegistryConfig, ServerInstallOptions, EnvVariableConfig } from '../../core/metadatas/types.js';
2
+ import { ServerSchema } from '../../core/loaders/ServerSchemaProvider.js';
3
3
 
4
4
  export interface OnboardServerConfig {
5
5
  name: string;