imcp 0.0.17 → 0.0.18
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.
- package/dist/cli/commands/serve.js +2 -1
- package/dist/core/installers/clients/BaseClientInstaller.d.ts +25 -2
- package/dist/core/installers/clients/BaseClientInstaller.js +121 -0
- package/dist/core/installers/clients/ClineInstaller.d.ts +1 -6
- package/dist/core/installers/clients/ClineInstaller.js +1 -94
- package/dist/core/installers/clients/GithubCopilotInstaller.d.ts +1 -6
- package/dist/core/installers/clients/GithubCopilotInstaller.js +1 -94
- package/dist/core/installers/clients/MSRooCodeInstaller.d.ts +1 -5
- package/dist/core/installers/clients/MSRooCodeInstaller.js +1 -94
- package/dist/core/loaders/ConfigurationLoader.d.ts +4 -1
- package/dist/core/loaders/ConfigurationLoader.js +24 -3
- package/dist/core/loaders/ConfigurationProvider.d.ts +4 -1
- package/dist/core/loaders/ConfigurationProvider.js +13 -4
- package/dist/core/loaders/ServerSchemaLoader.d.ts +15 -4
- package/dist/core/loaders/ServerSchemaLoader.js +86 -20
- package/dist/core/loaders/ServerSchemaProvider.d.ts +2 -5
- package/dist/core/loaders/ServerSchemaProvider.js +32 -62
- package/dist/core/metadatas/types.d.ts +3 -2
- package/dist/core/onboard/FeedOnboardService.d.ts +14 -7
- package/dist/core/onboard/FeedOnboardService.js +214 -129
- package/dist/core/onboard/OnboardProcessor.d.ts +7 -1
- package/dist/core/onboard/OnboardProcessor.js +52 -8
- package/dist/core/onboard/OnboardStatus.d.ts +6 -1
- package/dist/core/onboard/OnboardStatusManager.d.ts +70 -24
- package/dist/core/onboard/OnboardStatusManager.js +230 -46
- package/dist/core/validators/FeedValidator.d.ts +7 -2
- package/dist/core/validators/FeedValidator.js +61 -7
- package/dist/core/validators/StdioServerValidator.js +84 -32
- package/dist/services/MCPManager.d.ts +2 -1
- package/dist/services/MCPManager.js +5 -1
- package/dist/services/ServerService.js +2 -2
- package/dist/utils/logger.d.ts +2 -0
- package/dist/utils/logger.js +10 -0
- package/dist/web/public/js/modal/installation.js +1 -1
- package/dist/web/public/js/onboard/ONBOARDING_PAGE_DESIGN.md +41 -9
- package/dist/web/public/js/onboard/formProcessor.js +200 -34
- package/dist/web/public/js/onboard/index.js +2 -2
- package/dist/web/public/js/onboard/publishHandler.js +30 -22
- package/dist/web/public/js/onboard/templates.js +34 -40
- package/dist/web/public/js/onboard/uiHandlers.js +175 -84
- package/dist/web/public/js/onboard/validationHandlers.js +147 -64
- package/dist/web/public/js/serverCategoryDetails.js +19 -4
- package/dist/web/public/js/serverCategoryList.js +13 -1
- package/dist/web/public/onboard.html +1 -1
- package/dist/web/server.js +19 -6
- package/package.json +1 -1
|
@@ -1,5 +1,8 @@
|
|
|
1
|
+
import fs from 'fs/promises';
|
|
1
2
|
import { serverValidatorFactory } from "./ServerValidatorFactory.js";
|
|
2
3
|
import { Logger } from "../../utils/logger.js";
|
|
4
|
+
import { onboardStatusManager } from "../onboard/OnboardStatusManager.js";
|
|
5
|
+
import { OnboardingProcessStatus } from "../onboard/OnboardStatus.js";
|
|
3
6
|
/**
|
|
4
7
|
* Validates feed configurations to ensure they meet required criteria
|
|
5
8
|
*/
|
|
@@ -7,12 +10,16 @@ export class FeedValidator {
|
|
|
7
10
|
/**
|
|
8
11
|
* Validates a feed configuration
|
|
9
12
|
* @param config The feed configuration to validate
|
|
13
|
+
* @param categoryName The name of the category (feed name) for status updates
|
|
14
|
+
* @param operationType The type of operation for status updates
|
|
10
15
|
* @returns true if valid, throws error if invalid
|
|
11
16
|
*/
|
|
12
|
-
validate(config) {
|
|
17
|
+
async validate(config, categoryName, operationType) {
|
|
13
18
|
try {
|
|
19
|
+
await onboardStatusManager.recordStep(categoryName, operationType, `Starting feed validation for '${config.name}'`);
|
|
14
20
|
Logger.debug(`Validating feed configuration: ${config.name}`);
|
|
15
21
|
// Validate required fields
|
|
22
|
+
await onboardStatusManager.recordStep(categoryName, operationType, `Validating required fields for '${config.name}'`);
|
|
16
23
|
if (!config.name)
|
|
17
24
|
throw new Error('Feed name is required');
|
|
18
25
|
if (!config.displayName)
|
|
@@ -20,11 +27,13 @@ export class FeedValidator {
|
|
|
20
27
|
if (!config.description)
|
|
21
28
|
throw new Error('Feed description is required');
|
|
22
29
|
// Validate MCP servers array
|
|
30
|
+
await onboardStatusManager.recordStep(categoryName, operationType, `Validating MCP servers array for '${config.name}'`);
|
|
23
31
|
if (!Array.isArray(config.mcpServers) || config.mcpServers.length === 0) {
|
|
24
32
|
throw new Error('Feed must contain at least one MCP server');
|
|
25
33
|
}
|
|
26
34
|
// Validate requirements if present
|
|
27
35
|
if (config.requirements) {
|
|
36
|
+
await onboardStatusManager.recordStep(categoryName, operationType, `Validating requirements for '${config.name}'`);
|
|
28
37
|
for (const req of config.requirements) {
|
|
29
38
|
if (!req.name)
|
|
30
39
|
throw new Error('Requirement name is required');
|
|
@@ -34,12 +43,14 @@ export class FeedValidator {
|
|
|
34
43
|
throw new Error('Requirement version is required');
|
|
35
44
|
}
|
|
36
45
|
}
|
|
46
|
+
await onboardStatusManager.recordStep(categoryName, operationType, `Feed validation successful for '${config.name}'`, undefined, OnboardingProcessStatus.VALIDATING); // Still in progress until all servers validated
|
|
37
47
|
Logger.debug(`Feed configuration validation successful: ${config.name}`);
|
|
38
48
|
return true;
|
|
39
49
|
}
|
|
40
50
|
catch (error) {
|
|
41
|
-
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)}`;
|
|
42
52
|
Logger.error(errorMsg);
|
|
53
|
+
await onboardStatusManager.recordStep(categoryName, operationType, `Feed validation failed for '${config.name}'`, undefined, OnboardingProcessStatus.FAILED, errorMsg);
|
|
43
54
|
throw new Error(errorMsg);
|
|
44
55
|
}
|
|
45
56
|
}
|
|
@@ -47,12 +58,18 @@ export class FeedValidator {
|
|
|
47
58
|
* Validates a single MCP server configuration using the appropriate validator
|
|
48
59
|
* @param server The MCP server configuration to validate
|
|
49
60
|
* @param config The feed configuration containing shared requirements
|
|
61
|
+
* @param categoryName The name of the category (feed name) for status updates
|
|
62
|
+
* @param operationType The type of operation for status updates
|
|
50
63
|
* @returns true if valid, throws error if invalid
|
|
51
64
|
*/
|
|
52
|
-
async validateServer(server, config) {
|
|
65
|
+
async validateServer(server, config, categoryName, operationType) {
|
|
66
|
+
const feedName = config.name; // categoryName is the feedName in this context
|
|
67
|
+
const serverDisplayName = server.name;
|
|
53
68
|
try {
|
|
54
|
-
|
|
69
|
+
await onboardStatusManager.recordStep(categoryName, operationType, `Starting server validation for '${serverDisplayName}' in feed '${feedName}'`, server.name);
|
|
70
|
+
Logger.debug(`Validating server configuration: ${serverDisplayName} in feed ${feedName}`);
|
|
55
71
|
// Validate basic required fields
|
|
72
|
+
await onboardStatusManager.recordStep(categoryName, operationType, `Validating basic fields for server '${serverDisplayName}'`, server.name);
|
|
56
73
|
if (!server.name)
|
|
57
74
|
throw new Error('Server name is required');
|
|
58
75
|
if (!server.description)
|
|
@@ -61,16 +78,53 @@ export class FeedValidator {
|
|
|
61
78
|
throw new Error('Server mode is required');
|
|
62
79
|
if (!server.installation)
|
|
63
80
|
throw new Error('Server installation configuration is required');
|
|
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
|
+
}
|
|
110
|
+
catch (fileAccessError) {
|
|
111
|
+
throw new Error(`Schema file '${server.schemas}' not found for server '${serverDisplayName}'.`);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
64
114
|
// Get the appropriate validator for this server type
|
|
115
|
+
await onboardStatusManager.recordStep(categoryName, operationType, `Obtaining specific validator for server '${serverDisplayName}'`, server.name);
|
|
65
116
|
const validator = serverValidatorFactory.getValidatorForServer(server);
|
|
66
117
|
// Perform mode-specific validation with feed config
|
|
67
|
-
await
|
|
68
|
-
|
|
118
|
+
await onboardStatusManager.recordStep(categoryName, operationType, `Performing mode-specific validation for server '${serverDisplayName}'`, server.name);
|
|
119
|
+
await validator.validateServer(server, config); // This internal call won't have status updates unless modified too
|
|
120
|
+
await onboardStatusManager.recordStep(categoryName, operationType, `Server validation successful for '${serverDisplayName}'`, server.name, OnboardingProcessStatus.VALIDATING); // Still in progress until all servers validated
|
|
121
|
+
Logger.debug(`Server configuration validation successful: ${serverDisplayName}`);
|
|
69
122
|
return true;
|
|
70
123
|
}
|
|
71
124
|
catch (error) {
|
|
72
|
-
const errorMsg = `Server validation failed: ${error instanceof Error ? error.message : String(error)}`;
|
|
125
|
+
const errorMsg = `Server validation failed for '${serverDisplayName}' in feed '${feedName}': ${error instanceof Error ? error.message : String(error)}`;
|
|
73
126
|
Logger.error(errorMsg);
|
|
127
|
+
await onboardStatusManager.recordStep(categoryName, operationType, `Server validation failed for '${serverDisplayName}'`, server.name, OnboardingProcessStatus.FAILED, errorMsg);
|
|
74
128
|
throw new Error(errorMsg);
|
|
75
129
|
}
|
|
76
130
|
}
|
|
@@ -91,7 +91,9 @@ export class StdioServerValidator {
|
|
|
91
91
|
// Resolve npm module paths in arguments
|
|
92
92
|
Logger.debug('Resolving npm module paths in arguments');
|
|
93
93
|
const npmPath = requirement ? this._getRequirementFolderPath(requirement) : undefined;
|
|
94
|
-
finalArgs = args.map(arg => arg
|
|
94
|
+
finalArgs = args.map(arg => arg
|
|
95
|
+
.replace(MACRO_EXPRESSIONS.NPMPATH, resolveNpmModulePath(npmPath))
|
|
96
|
+
.replace(/\\/g, '/'));
|
|
95
97
|
Logger.debug(`Resolved npm arguments: ${finalArgs.join(' ')}`);
|
|
96
98
|
}
|
|
97
99
|
else if (command === 'python' || command === 'python3') {
|
|
@@ -109,54 +111,104 @@ export class StdioServerValidator {
|
|
|
109
111
|
}
|
|
110
112
|
}
|
|
111
113
|
return await new Promise((resolve, reject) => {
|
|
112
|
-
Logger.debug(`Starting process with command: ${command} ${finalArgs.join(' ')}`);
|
|
113
|
-
//
|
|
114
|
-
const timeout = setTimeout(() => {
|
|
115
|
-
const msg = 'Server startup test timed out after 10 seconds';
|
|
116
|
-
Logger.error(msg);
|
|
117
|
-
serverProcess.kill();
|
|
118
|
-
reject(new Error(msg));
|
|
119
|
-
}, 20000);
|
|
120
|
-
// Start the server process
|
|
114
|
+
Logger.debug(`Starting process for server test with command: ${command} ${finalArgs.join(' ')}`);
|
|
115
|
+
const timeoutDuration = 20000; // 20 seconds for server startup test
|
|
121
116
|
const serverProcess = spawn(command, finalArgs, {
|
|
122
|
-
stdio: ['ignore', 'pipe', 'pipe'],
|
|
117
|
+
stdio: ['ignore', 'pipe', 'pipe'], // stdin, stdout, stderr
|
|
123
118
|
shell: true
|
|
124
119
|
});
|
|
125
|
-
let
|
|
126
|
-
let
|
|
127
|
-
|
|
120
|
+
let stdoutData = '';
|
|
121
|
+
let stderrData = '';
|
|
122
|
+
let settled = false;
|
|
123
|
+
const cleanupAndResolve = (value) => {
|
|
124
|
+
if (settled)
|
|
125
|
+
return;
|
|
126
|
+
settled = true;
|
|
127
|
+
clearTimeout(timeoutId);
|
|
128
|
+
serverProcess.stdout.removeAllListeners();
|
|
129
|
+
serverProcess.stderr.removeAllListeners();
|
|
130
|
+
serverProcess.removeAllListeners('exit');
|
|
131
|
+
serverProcess.removeAllListeners('error');
|
|
132
|
+
if (serverProcess.exitCode === null && !serverProcess.killed) {
|
|
133
|
+
serverProcess.kill();
|
|
134
|
+
}
|
|
135
|
+
resolve(value);
|
|
136
|
+
};
|
|
137
|
+
const cleanupAndReject = (err) => {
|
|
138
|
+
if (settled)
|
|
139
|
+
return;
|
|
140
|
+
settled = true;
|
|
141
|
+
clearTimeout(timeoutId);
|
|
142
|
+
serverProcess.stdout.removeAllListeners();
|
|
143
|
+
serverProcess.stderr.removeAllListeners();
|
|
144
|
+
serverProcess.removeAllListeners('exit');
|
|
145
|
+
serverProcess.removeAllListeners('error');
|
|
146
|
+
if (serverProcess.exitCode === null && !serverProcess.killed) {
|
|
147
|
+
serverProcess.kill();
|
|
148
|
+
}
|
|
149
|
+
reject(err);
|
|
150
|
+
};
|
|
151
|
+
const timeoutId = setTimeout(() => {
|
|
152
|
+
if (settled)
|
|
153
|
+
return;
|
|
154
|
+
if (serverProcess.exitCode === null) { // Process is still running
|
|
155
|
+
Logger.debug(`Server startup test: Process still running after ${timeoutDuration / 1000} seconds. Considering it successful.`);
|
|
156
|
+
Logger.debug(`Collected stdout:\n${stdoutData}`);
|
|
157
|
+
Logger.debug(`Collected stderr:\n${stderrData}`);
|
|
158
|
+
cleanupAndResolve(true);
|
|
159
|
+
}
|
|
160
|
+
else {
|
|
161
|
+
// Process exited before timeout, 'exit' handler should have caught it.
|
|
162
|
+
// This is a fallback or race condition handling.
|
|
163
|
+
const msg = `Server startup test: Process exited with code ${serverProcess.exitCode} before timeout completed.`;
|
|
164
|
+
Logger.error(msg);
|
|
165
|
+
Logger.debug(`Collected stdout:\n${stdoutData}`);
|
|
166
|
+
Logger.error(`Collected stderr:\n${stderrData}`); // Log stderr as error here
|
|
167
|
+
cleanupAndReject(new Error(msg + ` Stderr: ${stderrData}`));
|
|
168
|
+
}
|
|
169
|
+
}, timeoutDuration);
|
|
128
170
|
serverProcess.stdout.on('data', (data) => {
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
serverProcess.kill();
|
|
133
|
-
resolve(true);
|
|
171
|
+
const messageChunk = data.toString();
|
|
172
|
+
stdoutData += messageChunk;
|
|
173
|
+
Logger.debug(`Server stdout: ${messageChunk.trim()}`);
|
|
134
174
|
});
|
|
135
|
-
// Collect stderr
|
|
136
175
|
serverProcess.stderr.on('data', (data) => {
|
|
137
|
-
|
|
176
|
+
const messageChunk = data.toString();
|
|
177
|
+
stderrData += messageChunk;
|
|
178
|
+
// Log stderr, but it doesn't automatically mean failure.
|
|
179
|
+
// The exit code or an 'error' event will determine failure.
|
|
180
|
+
Logger.debug(`Server stderr: ${messageChunk.trim()}`);
|
|
138
181
|
});
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
182
|
+
serverProcess.on('exit', (code, signal) => {
|
|
183
|
+
if (settled)
|
|
184
|
+
return;
|
|
185
|
+
Logger.debug(`Server process exited with code ${code}, signal: ${signal}.`);
|
|
186
|
+
Logger.debug(`Final stdout:\n${stdoutData}`);
|
|
187
|
+
Logger.debug(`Final stderr:\n${stderrData}`);
|
|
188
|
+
if (code === 0) {
|
|
189
|
+
cleanupAndResolve(true);
|
|
190
|
+
}
|
|
191
|
+
else {
|
|
192
|
+
const msg = `Server process exited with non-zero code ${code} or signal ${signal}. Stderr: ${stderrData.trim()}`;
|
|
144
193
|
Logger.error(msg);
|
|
145
|
-
|
|
194
|
+
cleanupAndReject(new Error(msg));
|
|
146
195
|
}
|
|
147
196
|
});
|
|
148
|
-
// Handle process error
|
|
149
197
|
serverProcess.on('error', (error) => {
|
|
150
|
-
|
|
151
|
-
|
|
198
|
+
if (settled)
|
|
199
|
+
return;
|
|
200
|
+
const msg = `Server process failed to start or encountered an error: ${error.message}.`;
|
|
152
201
|
Logger.error(msg);
|
|
153
|
-
|
|
202
|
+
Logger.debug(`Stdout at error:\n${stdoutData}`);
|
|
203
|
+
Logger.error(`Stderr at error:\n${stderrData}`);
|
|
204
|
+
cleanupAndReject(new Error(`${msg} Stderr: ${stderrData.trim()}`));
|
|
154
205
|
});
|
|
155
206
|
});
|
|
156
207
|
}
|
|
157
208
|
catch (error) {
|
|
158
|
-
const msg = `Failed to test server startup: ${error instanceof Error ? error.message : String(error)}`;
|
|
209
|
+
const msg = `Failed to test server startup (outer catch): ${error instanceof Error ? error.message : String(error)}`;
|
|
159
210
|
Logger.error(msg);
|
|
211
|
+
// Ensure the error thrown is an instance of Error
|
|
160
212
|
throw error instanceof Error ? error : new Error(msg);
|
|
161
213
|
}
|
|
162
214
|
}
|
|
@@ -5,9 +5,10 @@ export declare class MCPManager extends EventEmitter {
|
|
|
5
5
|
private installationService;
|
|
6
6
|
private configProvider;
|
|
7
7
|
private feedOnboardService;
|
|
8
|
+
private schemaProvider;
|
|
8
9
|
constructor();
|
|
9
10
|
syncFeeds(): Promise<void>;
|
|
10
|
-
initialize(feedFile?: string): Promise<void>;
|
|
11
|
+
initialize(feedFile?: string, schemasDirectory?: string): Promise<void>;
|
|
11
12
|
listServerCategories(options?: ServerCategoryListOptions): Promise<MCPServerCategory[]>;
|
|
12
13
|
getFeedConfiguration(categoryName: string): Promise<FeedConfiguration | undefined>;
|
|
13
14
|
getServerMcpConfig(categoryName: string, serverName: string): Promise<import("../core/metadatas/types.js").McpConfig | undefined>;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { EventEmitter } from 'events';
|
|
2
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 { MCPEvent, } from '../core/metadatas/types.js';
|
|
5
6
|
import { Logger } from '../utils/logger.js';
|
|
@@ -8,18 +9,21 @@ export class MCPManager extends EventEmitter {
|
|
|
8
9
|
installationService;
|
|
9
10
|
configProvider;
|
|
10
11
|
feedOnboardService;
|
|
12
|
+
schemaProvider;
|
|
11
13
|
constructor() {
|
|
12
14
|
super();
|
|
13
15
|
this.configProvider = ConfigurationProvider.getInstance();
|
|
16
|
+
this.schemaProvider = ServerSchemaProvider.getInstance();
|
|
14
17
|
this.installationService = new InstallationService();
|
|
15
18
|
this.feedOnboardService = new FeedOnboardService();
|
|
16
19
|
}
|
|
17
20
|
async syncFeeds() {
|
|
18
21
|
await this.configProvider.syncFeeds();
|
|
19
22
|
}
|
|
20
|
-
async initialize(feedFile) {
|
|
23
|
+
async initialize(feedFile, schemasDirectory) {
|
|
21
24
|
try {
|
|
22
25
|
await this.configProvider.initialize(feedFile);
|
|
26
|
+
await this.schemaProvider.initialize(schemasDirectory);
|
|
23
27
|
}
|
|
24
28
|
catch (error) {
|
|
25
29
|
console.error("Error during MCPManager initialization:", error);
|
|
@@ -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 {
|
|
4
|
+
import { ServerSchemaProvider } from '../core/loaders/ServerSchemaProvider.js';
|
|
5
5
|
import { mcpManager } from './MCPManager.js';
|
|
6
6
|
import { UPDATE_CHECK_INTERVAL_MS } from '../core/metadatas/constants.js';
|
|
7
7
|
import { updateCheckTracker } from '../utils/UpdateCheckTracker.js';
|
|
@@ -102,7 +102,7 @@ export class ServerService {
|
|
|
102
102
|
*/
|
|
103
103
|
async getServerSchema(categoryName, serverName) {
|
|
104
104
|
try {
|
|
105
|
-
const provider =
|
|
105
|
+
const provider = ServerSchemaProvider.getInstance();
|
|
106
106
|
const serverMcpConfig = await mcpManager.getServerMcpConfig(categoryName, serverName);
|
|
107
107
|
return await provider.getSchema(categoryName, serverMcpConfig?.schemas || `${serverName}.json`);
|
|
108
108
|
}
|
package/dist/utils/logger.d.ts
CHANGED
|
@@ -9,6 +9,8 @@ export declare class Logger {
|
|
|
9
9
|
private static getTimestamp;
|
|
10
10
|
private static writeToLogFile;
|
|
11
11
|
static log(message: string): Promise<void>;
|
|
12
|
+
static info(message: string): Promise<void>;
|
|
13
|
+
static warn(message: string): Promise<void>;
|
|
12
14
|
static debug(message: string | object): Promise<void>;
|
|
13
15
|
static error(message: string, error?: unknown): Promise<void>;
|
|
14
16
|
}
|
package/dist/utils/logger.js
CHANGED
|
@@ -48,6 +48,16 @@ export class Logger {
|
|
|
48
48
|
console.log(message);
|
|
49
49
|
await this.writeToLogFile('INFO', message);
|
|
50
50
|
}
|
|
51
|
+
static async info(message) {
|
|
52
|
+
console.info(message);
|
|
53
|
+
await this.writeToLogFile('INFO', message);
|
|
54
|
+
}
|
|
55
|
+
static async warn(message) {
|
|
56
|
+
const yellowColor = '\x1b[33m';
|
|
57
|
+
const resetColor = '\x1b[0m';
|
|
58
|
+
console.warn(`${yellowColor}${message}${resetColor}`);
|
|
59
|
+
await this.writeToLogFile('WARN', message);
|
|
60
|
+
}
|
|
51
61
|
static async debug(message) {
|
|
52
62
|
let formattedMessage;
|
|
53
63
|
if (typeof message === 'object') {
|
|
@@ -175,7 +175,7 @@ async function pollInstallStatus(categoryName, serverName, targets, interval = 2
|
|
|
175
175
|
delayedAppendInstallLoadingMessage(`${target}: ${msg}`);
|
|
176
176
|
lastMessages[target] = msg;
|
|
177
177
|
}
|
|
178
|
-
if (status !== "completed") {
|
|
178
|
+
if (status !== "completed" && status !== "failed") {
|
|
179
179
|
allTargetsCompleted = false;
|
|
180
180
|
}
|
|
181
181
|
}
|
|
@@ -168,9 +168,9 @@ A toggle switch allows users to switch between the standard form view and a raw
|
|
|
168
168
|
- Calls `getFormData()` (from `formProcessor.js`) to get the current form data as `FeedConfiguration`.
|
|
169
169
|
- Sends an initial POST request to `/api/categories/onboard` with the `FeedConfiguration` object and an `isUpdate` flag.
|
|
170
170
|
- The "Publish" button shows a spinning loader icon and its text changes to "Publishing...". Both "Validate" and "Publish" buttons are disabled.
|
|
171
|
-
- **Polling for Status**: If the initial publish request is successful and the operation is not immediately completed or failed, `handlePublish` initiates polling
|
|
172
|
-
- **Displaying Results**: The shared `updateOperationDisplay` function (from `validationHandlers.js`) formats and displays
|
|
173
|
-
- **Completion/Failure**: Polling stops
|
|
171
|
+
- **Polling for Status**: If the initial publish request is successful and the operation is not immediately completed or failed, `handlePublish` initiates polling. The shared `pollOperationStatus` function (from `validationHandlers.js`) is called, which periodically queries `GET /api/categories/:categoryName/onboard/status?operationType=FULL_ONBOARDING`. `pollOperationStatus` now returns a boolean to manage polling continuation.
|
|
172
|
+
- **Displaying Results**: The shared `updateOperationDisplay` function (from `validationHandlers.js`) formats and displays detailed progress, including individual steps, from both the initial publish call and subsequent status polls.
|
|
173
|
+
- **Completion/Failure**: Polling stops based on the status returned by `pollOperationStatus`. Upon completion or failure, buttons are reset, and `showToast()` displays messages. For detailed information on recent changes to polling and status display, see Section 9: "Enhanced Operation Status Polling and Display".
|
|
174
174
|
- **Validation (`handleValidation` in `validationHandlers.js`)**:
|
|
175
175
|
- **Validation (`handleValidation` in `validationHandlers.js`)**:
|
|
176
176
|
- The `validationStatusPanel` (unique per tab) is located under the "MCP Servers" section (or equivalent) and above the main action buttons ("Validate", "Publish"). It is a foldable panel.
|
|
@@ -179,14 +179,14 @@ A toggle switch allows users to switch between the standard form view and a raw
|
|
|
179
179
|
- Sends an initial POST request to `/api/categories/onboard/validate`.
|
|
180
180
|
- The "Validate" button shows a spinning loader icon and its text changes to "Validating...". Both "Validate" and "Publish" buttons are disabled.
|
|
181
181
|
- **Polling for Status**:
|
|
182
|
-
- If the initial validation request is successful and the operation is not immediately completed or failed, `handleValidation` initiates polling
|
|
183
|
-
-
|
|
182
|
+
- If the initial validation request is successful and the operation is not immediately completed or failed, `handleValidation` initiates polling.
|
|
183
|
+
- The shared `pollOperationStatus` function (from `validationHandlers.js`) is called, which periodically queries `GET /api/categories/:categoryName/onboard/status?operationType=VALIDATION_ONLY` (using the category name from the form). `pollOperationStatus` now returns a boolean to manage polling continuation.
|
|
184
184
|
- **Displaying Results**:
|
|
185
|
-
- The shared `updateOperationDisplay` function (from `validationHandlers.js`) formats and displays
|
|
186
|
-
- It handles different structures of the `validationStatus` object
|
|
185
|
+
- The shared `updateOperationDisplay` function (from `validationHandlers.js`) formats and displays detailed progress, including individual steps, from both the initial validation call and subsequent status polls.
|
|
186
|
+
- It handles different structures of the `validationStatus` object and overall operation status.
|
|
187
187
|
- **Completion/Failure**:
|
|
188
|
-
- Polling stops
|
|
189
|
-
- Upon completion or failure,
|
|
188
|
+
- Polling stops based on the status returned by `pollOperationStatus`.
|
|
189
|
+
- Upon completion or failure, buttons are reset. For detailed information on recent changes to polling and status display, see Section 9: "Enhanced Operation Status Polling and Display".
|
|
190
190
|
- **Copy JSON (`copyJsonToClipboard` in `uiHandlers.js`)**: Copies the content of `jsonEditorTextarea` to the clipboard. Uses `showToast()` for feedback.
|
|
191
191
|
- **Global Function Exposure**: Necessary UI handler functions (like `addServer`, `removeServer`, `toggleSectionContent`, etc.) are attached to the `window` object so they can be called from `onclick` attributes in the HTML templates.
|
|
192
192
|
- **Custom Notifications**: Standard browser `alert()` calls have been replaced with a custom toast notification system (`showToast()` from `../notifications.js`). This system requires an `.alert-container` div in the host HTML (`onboard.html`) and uses CSS from `css/notifications.css` for styling. The `notifications.js` module handles the creation, display, and dismissal (manual and automatic) of these toasts without relying on Bootstrap's JavaScript.
|
|
@@ -290,6 +290,38 @@ This section documents significant fixes and behavior changes implemented recent
|
|
|
290
290
|
- **Outcome:** The "Publish" and "Validate" buttons now reliably revert to their default active states when an operation (validation or publish) concludes with a failure, regardless of the casing of the status string from the backend or whether the failure occurs immediately or during polling.
|
|
291
291
|
- **Overall Outcome:** These changes improve the reliability of data submission and provide more accurate UI feedback for asynchronous operations on the "Create Server in Existing Category" tab.
|
|
292
292
|
|
|
293
|
+
- **Enhanced Operation Status Polling and Display (May 2025 - Current Session):**
|
|
294
|
+
- **Context:** Significant improvements were made to the handling of asynchronous operations (validation and publishing), including more detailed progress display, robust polling, and clearer API interactions.
|
|
295
|
+
- **Issues Addressed & Solutions:**
|
|
296
|
+
1. **Server-Side API (`server.ts` - `/api/categories/:categoryName/onboard/status`):**
|
|
297
|
+
- The endpoint now requires an `operationType` query parameter (e.g., `VALIDATION_ONLY`, `FULL_ONBOARDING`).
|
|
298
|
+
- The response payload (`OperationStatus`) was enhanced to include:
|
|
299
|
+
- `steps`: An array of objects detailing each step of the operation (name, serverName, timestamp, status, errorMessage).
|
|
300
|
+
- `operationType`: Reflects the type of operation being tracked, taken from the status object.
|
|
301
|
+
- `feedConfiguration`: Conditionally included for successful `VALIDATION_ONLY` operations if present in the result.
|
|
302
|
+
- The `onboardingId` in the response is `categoryName_operationType`.
|
|
303
|
+
- The `message` field in the response now prioritizes the last step's name or an overall error message.
|
|
304
|
+
2. **Client-Side Polling Logic (`validationHandlers.js` - `pollOperationStatus`):**
|
|
305
|
+
- The function now accepts an `operationType` parameter, which is passed to the API.
|
|
306
|
+
- It returns a boolean promise (`Promise<boolean>`) indicating whether polling should continue, allowing the caller (`handleValidation` or `handlePublish`) to manage `clearInterval`.
|
|
307
|
+
- Calls `ensureProgressToggleListener` to ensure the UI for displaying progress steps is interactive.
|
|
308
|
+
- Status comparisons (`COMPLETED`, `FAILED`, `SUCCEEDED`) are now case-insensitive.
|
|
309
|
+
3. **Operation Initiation (`publishHandler.js` - `handlePublish`, `validationHandlers.js` - `handleValidation`):**
|
|
310
|
+
- When initiating polling, these functions now pass the correct `operationType` (`FULL_ONBOARDING` for publish, `VALIDATION_ONLY` for validate) and use the original `finalFeedConfiguration.name` as the `categoryName` for the polling request.
|
|
311
|
+
- They now manage `clearInterval` based on the boolean returned by `pollOperationStatus`.
|
|
312
|
+
- Both handlers call `ensureProgressToggleListener` at the beginning to prepare the UI for status updates.
|
|
313
|
+
- `handlePublish` includes improved error handling for the initial publish request, ensuring UI elements are correctly updated even if the initial call fails or returns a terminal status.
|
|
314
|
+
4. **UI Display (`validationHandlers.js` - `updateOperationDisplay`):**
|
|
315
|
+
- A new collapsible "Progress" section is displayed, showing detailed `steps` from the API response. Each step includes:
|
|
316
|
+
- A status icon (error, in-progress spinner, success).
|
|
317
|
+
- The step name, server name (if applicable), and timestamp.
|
|
318
|
+
- An error message for the step, if any.
|
|
319
|
+
- The visibility of the "Progress" section is managed by `ensureProgressToggleListener` (which attaches a click handler to the section header) and the `toggleSectionContent` utility.
|
|
320
|
+
- The `operationType` is now displayed.
|
|
321
|
+
- The logic for displaying the overall status spinner has been updated to cover more in-progress states (e.g., `PENDING`, `VALIDATING`, `PRCREATING`, `PUBLISHING`) and uses case-insensitive comparisons.
|
|
322
|
+
5. **Progress Section Toggling (`validationHandlers.js` - `ensureProgressToggleListener`):**
|
|
323
|
+
- A new exported utility function, `ensureProgressToggleListener`, was added. It attaches a click event listener to the status content element. This listener delegates to `toggleSectionContent` (from `uiHandlers.js`) when the progress section header is clicked, allowing users to expand or collapse the detailed list of progress steps. The listener is attached only once per status content element.
|
|
324
|
+
- **Outcome:** These changes provide users with more granular and interactive feedback on long-running operations, improve the robustness and accuracy of status polling, and align client-server communication for tracking asynchronous onboarding tasks.
|
|
293
325
|
---
|
|
294
326
|
|
|
295
327
|
## 10. Server Validation System
|