imcp 0.0.14 → 0.0.16
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/core/ConfigurationProvider.d.ts +1 -0
- package/dist/core/ConfigurationProvider.js +15 -0
- package/dist/core/InstallationService.js +2 -7
- package/dist/core/MCPManager.d.ts +11 -2
- package/dist/core/MCPManager.js +24 -1
- package/dist/core/RequirementService.js +2 -8
- package/dist/core/installers/clients/BaseClientInstaller.d.ts +51 -0
- package/dist/core/installers/clients/BaseClientInstaller.js +160 -0
- package/dist/core/installers/clients/ClientInstaller.d.ts +16 -9
- package/dist/core/installers/clients/ClientInstaller.js +80 -527
- package/dist/core/installers/clients/ClientInstallerFactory.d.ts +20 -0
- package/dist/core/installers/clients/ClientInstallerFactory.js +37 -0
- package/dist/core/installers/clients/ClineInstaller.d.ts +18 -0
- package/dist/core/installers/clients/ClineInstaller.js +124 -0
- package/dist/core/installers/clients/GithubCopilotInstaller.d.ts +34 -0
- package/dist/core/installers/clients/GithubCopilotInstaller.js +162 -0
- package/dist/core/installers/clients/MSRooCodeInstaller.d.ts +15 -0
- package/dist/core/installers/clients/MSRooCodeInstaller.js +122 -0
- package/dist/core/installers/requirements/BaseInstaller.d.ts +11 -34
- package/dist/core/installers/requirements/BaseInstaller.js +5 -116
- package/dist/core/installers/requirements/CommandInstaller.d.ts +6 -1
- package/dist/core/installers/requirements/CommandInstaller.js +7 -0
- package/dist/core/installers/requirements/GeneralInstaller.d.ts +6 -1
- package/dist/core/installers/requirements/GeneralInstaller.js +9 -4
- package/dist/core/installers/requirements/NpmInstaller.d.ts +46 -7
- package/dist/core/installers/requirements/NpmInstaller.js +150 -58
- package/dist/core/installers/requirements/PipInstaller.d.ts +9 -0
- package/dist/core/installers/requirements/PipInstaller.js +66 -28
- package/dist/core/onboard/FeedOnboardService.d.ts +50 -13
- package/dist/core/onboard/FeedOnboardService.js +263 -88
- package/dist/core/onboard/OnboardProcessor.d.ts +79 -0
- package/dist/core/onboard/OnboardProcessor.js +290 -0
- package/dist/core/onboard/OnboardStatus.d.ts +49 -0
- package/dist/core/onboard/OnboardStatus.js +10 -0
- package/dist/core/onboard/OnboardStatusManager.d.ts +57 -0
- package/dist/core/onboard/OnboardStatusManager.js +176 -0
- package/dist/core/types.d.ts +4 -5
- package/dist/core/validators/FeedValidator.d.ts +8 -1
- package/dist/core/validators/FeedValidator.js +60 -7
- package/dist/core/validators/IServerValidator.d.ts +19 -0
- package/dist/core/validators/IServerValidator.js +2 -0
- package/dist/core/validators/SSEServerValidator.d.ts +15 -0
- package/dist/core/validators/SSEServerValidator.js +39 -0
- package/dist/core/validators/ServerValidatorFactory.d.ts +24 -0
- package/dist/core/validators/ServerValidatorFactory.js +45 -0
- package/dist/core/validators/StdioServerValidator.d.ts +46 -0
- package/dist/core/validators/StdioServerValidator.js +229 -0
- package/dist/services/InstallRequestValidator.d.ts +1 -1
- package/dist/services/ServerService.d.ts +9 -6
- package/dist/services/ServerService.js +18 -7
- package/dist/utils/adoUtils.d.ts +29 -0
- package/dist/utils/adoUtils.js +252 -0
- package/dist/utils/clientUtils.d.ts +0 -7
- package/dist/utils/clientUtils.js +0 -42
- package/dist/utils/githubUtils.d.ts +10 -0
- package/dist/utils/githubUtils.js +22 -0
- package/dist/utils/macroExpressionUtils.d.ts +38 -0
- package/dist/utils/macroExpressionUtils.js +116 -0
- package/dist/utils/osUtils.d.ts +4 -20
- package/dist/utils/osUtils.js +78 -23
- package/dist/web/contract/serverContract.d.ts +3 -1
- package/dist/web/public/css/notifications.css +48 -17
- package/dist/web/public/css/onboard.css +66 -3
- package/dist/web/public/index.html +84 -16
- package/dist/web/public/js/api.js +3 -6
- package/dist/web/public/js/flights/flights.js +127 -0
- package/dist/web/public/js/modal/installation.js +5 -5
- package/dist/web/public/js/modal/modalSetup.js +3 -2
- package/dist/web/public/js/notifications.js +66 -27
- package/dist/web/public/js/onboard/ONBOARDING_PAGE_DESIGN.md +338 -0
- package/dist/web/public/js/onboard/formProcessor.js +810 -255
- package/dist/web/public/js/onboard/index.js +328 -85
- package/dist/web/public/js/onboard/publishHandler.js +132 -0
- package/dist/web/public/js/onboard/state.js +61 -17
- package/dist/web/public/js/onboard/templates.js +217 -249
- package/dist/web/public/js/onboard/uiHandlers.js +679 -117
- package/dist/web/public/js/onboard/validationHandlers.js +378 -0
- package/dist/web/public/js/serverCategoryList.js +15 -2
- package/dist/web/public/onboard.html +191 -45
- package/dist/web/public/styles.css +91 -1
- package/dist/web/server.d.ts +0 -10
- package/dist/web/server.js +131 -22
- package/package.json +2 -2
- package/src/core/ConfigurationProvider.ts +15 -0
- package/src/core/InstallationService.ts +2 -7
- package/src/core/MCPManager.ts +26 -1
- package/src/core/RequirementService.ts +2 -9
- package/src/core/installers/clients/BaseClientInstaller.ts +196 -0
- package/src/core/installers/clients/ClientInstaller.ts +97 -608
- package/src/core/installers/clients/ClientInstallerFactory.ts +43 -0
- package/src/core/installers/clients/ClineInstaller.ts +135 -0
- package/src/core/installers/clients/GithubCopilotInstaller.ts +179 -0
- package/src/core/installers/clients/MSRooCodeInstaller.ts +133 -0
- package/src/core/installers/requirements/BaseInstaller.ts +13 -136
- package/src/core/installers/requirements/CommandInstaller.ts +9 -1
- package/src/core/installers/requirements/GeneralInstaller.ts +11 -4
- package/src/core/installers/requirements/NpmInstaller.ts +178 -61
- package/src/core/installers/requirements/PipInstaller.ts +68 -29
- package/src/core/onboard/FeedOnboardService.ts +346 -0
- package/src/core/onboard/OnboardProcessor.ts +305 -0
- package/src/core/onboard/OnboardStatus.ts +55 -0
- package/src/core/onboard/OnboardStatusManager.ts +188 -0
- package/src/core/types.ts +4 -5
- package/src/core/validators/FeedValidator.ts +79 -0
- package/src/core/validators/IServerValidator.ts +21 -0
- package/src/core/validators/SSEServerValidator.ts +43 -0
- package/src/core/validators/ServerValidatorFactory.ts +51 -0
- package/src/core/validators/StdioServerValidator.ts +259 -0
- package/src/services/InstallRequestValidator.ts +1 -1
- package/src/services/ServerService.ts +22 -7
- package/src/utils/adoUtils.ts +291 -0
- package/src/utils/clientUtils.ts +0 -44
- package/src/utils/githubUtils.ts +24 -0
- package/src/utils/macroExpressionUtils.ts +121 -0
- package/src/utils/osUtils.ts +89 -24
- package/src/web/contract/serverContract.ts +74 -0
- package/src/web/public/css/notifications.css +48 -17
- package/src/web/public/css/onboard.css +107 -0
- package/src/web/public/index.html +84 -16
- package/src/web/public/js/api.js +3 -6
- package/src/web/public/js/flights/flights.js +127 -0
- package/src/web/public/js/modal/installation.js +5 -5
- package/src/web/public/js/modal/modalSetup.js +3 -2
- package/src/web/public/js/notifications.js +66 -27
- package/src/web/public/js/onboard/ONBOARDING_PAGE_DESIGN.md +338 -0
- package/src/web/public/js/onboard/formProcessor.js +864 -0
- package/src/web/public/js/onboard/index.js +374 -0
- package/src/web/public/js/onboard/publishHandler.js +132 -0
- package/src/web/public/js/onboard/state.js +76 -0
- package/src/web/public/js/onboard/templates.js +343 -0
- package/src/web/public/js/onboard/uiHandlers.js +758 -0
- package/src/web/public/js/onboard/validationHandlers.js +378 -0
- package/src/web/public/js/serverCategoryList.js +15 -2
- package/src/web/public/onboard.html +296 -0
- package/src/web/public/styles.css +91 -1
- package/src/web/server.ts +167 -58
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { FeedConfiguration } from "../types.js";
|
|
2
|
+
|
|
3
|
+
export enum OnboardingProcessStatus {
|
|
4
|
+
VALIDATING = 'validating',
|
|
5
|
+
VALIDATED = 'validated',
|
|
6
|
+
PR_CREATING = 'prcreating',
|
|
7
|
+
FAILED = 'failed',
|
|
8
|
+
SUCCEEDED = 'succeeded',
|
|
9
|
+
PENDING = 'pending',
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export type OperationType = 'FULL_ONBOARDING' | 'VALIDATION_ONLY';
|
|
13
|
+
|
|
14
|
+
export interface ServerValidationResult {
|
|
15
|
+
serverName: string;
|
|
16
|
+
isValid: boolean;
|
|
17
|
+
message?: string;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface OnboardStatus {
|
|
21
|
+
onboardingId: string;
|
|
22
|
+
status: OnboardingProcessStatus;
|
|
23
|
+
currentStep?: string; // e.g., "Validating feed configuration", "Forking repository"
|
|
24
|
+
validationStatus?: {
|
|
25
|
+
isValid: boolean;
|
|
26
|
+
message?: string;
|
|
27
|
+
serverResults?: ServerValidationResult[];
|
|
28
|
+
};
|
|
29
|
+
prInfo?: {
|
|
30
|
+
url?: string;
|
|
31
|
+
branchName?: string;
|
|
32
|
+
};
|
|
33
|
+
result?: any; // Can store PR URL or other relevant result data
|
|
34
|
+
errorMessage?: string;
|
|
35
|
+
lastUpdated: string;
|
|
36
|
+
feedName: string;
|
|
37
|
+
serverName?: string; // Optional, if applicable
|
|
38
|
+
operationType?: OperationType;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export interface OperationStatus {
|
|
42
|
+
onboardingId: string;
|
|
43
|
+
status: OnboardingProcessStatus;
|
|
44
|
+
message?: string;
|
|
45
|
+
lastQueried?: string; // ISO date string of when the status was last queried by a client
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export interface ValidationOperationResult {
|
|
49
|
+
validationStatus: {
|
|
50
|
+
isValid: boolean;
|
|
51
|
+
message?: string;
|
|
52
|
+
serverResults?: ServerValidationResult[];
|
|
53
|
+
};
|
|
54
|
+
feedConfiguration?: FeedConfiguration;
|
|
55
|
+
}
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
import fs from 'fs/promises';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { SETTINGS_DIR } from '../constants.js';
|
|
4
|
+
import { FeedConfiguration } from '../types.js';
|
|
5
|
+
import { OnboardStatus, OnboardingProcessStatus, OperationType } from './OnboardStatus.js';
|
|
6
|
+
import { Logger } from '../../utils/logger.js';
|
|
7
|
+
|
|
8
|
+
const ONBOARD_STATUS_DIR = path.join(SETTINGS_DIR, 'onboard');
|
|
9
|
+
const CATEGORY_OPERATIONS_STATUS_FILE = path.join(ONBOARD_STATUS_DIR, 'category_operations_status.json');
|
|
10
|
+
const FEED_CONFIG_DIR = path.join(ONBOARD_STATUS_DIR, 'feed_configs'); // Staging for feed configs during operation
|
|
11
|
+
|
|
12
|
+
export class OnboardStatusManager {
|
|
13
|
+
private static instance: OnboardStatusManager;
|
|
14
|
+
private activeCategoryOperations: Record<string, OnboardStatus> = {};
|
|
15
|
+
private statusLock: Promise<void> = Promise.resolve();
|
|
16
|
+
|
|
17
|
+
private constructor() {
|
|
18
|
+
this.loadStatuses().catch(error => Logger.error('Failed to initialize OnboardStatusManager:', error));
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
public static getInstance(): OnboardStatusManager {
|
|
22
|
+
if (!OnboardStatusManager.instance) {
|
|
23
|
+
OnboardStatusManager.instance = new OnboardStatusManager();
|
|
24
|
+
}
|
|
25
|
+
return OnboardStatusManager.instance;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
private async withLock<T>(operation: () => Promise<T>): Promise<T> {
|
|
29
|
+
const current = this.statusLock;
|
|
30
|
+
let resolve: () => void;
|
|
31
|
+
this.statusLock = new Promise<void>(r => resolve = r);
|
|
32
|
+
try {
|
|
33
|
+
await current;
|
|
34
|
+
return await operation();
|
|
35
|
+
} finally {
|
|
36
|
+
resolve!();
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
private async loadStatuses(): Promise<void> {
|
|
41
|
+
await this.withLock(async () => {
|
|
42
|
+
try {
|
|
43
|
+
await fs.mkdir(ONBOARD_STATUS_DIR, { recursive: true });
|
|
44
|
+
await fs.mkdir(FEED_CONFIG_DIR, { recursive: true });
|
|
45
|
+
const data = await fs.readFile(CATEGORY_OPERATIONS_STATUS_FILE, 'utf-8');
|
|
46
|
+
this.activeCategoryOperations = JSON.parse(data);
|
|
47
|
+
} catch (error) {
|
|
48
|
+
if ((error as NodeJS.ErrnoException).code === 'ENOENT') {
|
|
49
|
+
this.activeCategoryOperations = {};
|
|
50
|
+
await this.saveStatuses(); // Create the file if it doesn't exist
|
|
51
|
+
} else {
|
|
52
|
+
Logger.error('Failed to load onboarding statuses:', error);
|
|
53
|
+
this.activeCategoryOperations = {}; // Initialize with empty statuses in case of other errors
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
private async saveStatuses(): Promise<void> {
|
|
60
|
+
await fs.mkdir(ONBOARD_STATUS_DIR, { recursive: true });
|
|
61
|
+
await fs.writeFile(CATEGORY_OPERATIONS_STATUS_FILE, JSON.stringify(this.activeCategoryOperations, null, 2));
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Saves the feed configuration associated with a category's active operation.
|
|
66
|
+
* The operation is identified by the categoryName.
|
|
67
|
+
* @param categoryName The name of the category (acting as operationId).
|
|
68
|
+
* @param config The FeedConfiguration to save.
|
|
69
|
+
*/
|
|
70
|
+
public async saveFeedConfiguration(categoryName: string, config: FeedConfiguration): Promise<void> {
|
|
71
|
+
await this.withLock(async () => {
|
|
72
|
+
try {
|
|
73
|
+
await fs.mkdir(FEED_CONFIG_DIR, { recursive: true });
|
|
74
|
+
// Suffix `_feed.json` to distinguish from potential status files if names overlap
|
|
75
|
+
const configPath = path.join(FEED_CONFIG_DIR, `${categoryName}_feed.json`);
|
|
76
|
+
await fs.writeFile(configPath, JSON.stringify(config, null, 2));
|
|
77
|
+
Logger.debug(`Saved feed configuration for category ${categoryName} to ${configPath}`);
|
|
78
|
+
} catch (error) {
|
|
79
|
+
Logger.error(`Failed to save feed configuration for category ${categoryName}:`, error);
|
|
80
|
+
throw error; // Re-throw to allow caller to handle
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Retrieves the feed configuration associated with a category's active operation.
|
|
87
|
+
* @param categoryName The name of the category (acting as operationId).
|
|
88
|
+
* @returns A promise that resolves to the FeedConfiguration, or undefined if not found.
|
|
89
|
+
*/
|
|
90
|
+
public async getFeedConfiguration(categoryName: string): Promise<FeedConfiguration | undefined> {
|
|
91
|
+
return await this.withLock(async () => {
|
|
92
|
+
try {
|
|
93
|
+
const configPath = path.join(FEED_CONFIG_DIR, `${categoryName}_feed.json`);
|
|
94
|
+
const data = await fs.readFile(configPath, 'utf-8');
|
|
95
|
+
return JSON.parse(data) as FeedConfiguration;
|
|
96
|
+
} catch (error) {
|
|
97
|
+
if ((error as NodeJS.ErrnoException).code === 'ENOENT') {
|
|
98
|
+
return undefined; // File not found, not an error state for this getter
|
|
99
|
+
}
|
|
100
|
+
// Log other errors but still return undefined as the config couldn't be retrieved.
|
|
101
|
+
Logger.error(`Failed to read feed configuration for category ${categoryName}:`, error);
|
|
102
|
+
return undefined; // Ensure undefined is returned on other errors too
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Retrieves the status of a category's active operation.
|
|
110
|
+
* @param categoryName The name of the category (acting as operationId).
|
|
111
|
+
* @returns A promise that resolves to the OnboardStatus, or undefined if no active operation.
|
|
112
|
+
*/
|
|
113
|
+
public async getStatus(categoryName: string): Promise<OnboardStatus | undefined> {
|
|
114
|
+
await this.loadStatuses(); // Ensure latest statuses are loaded (though constructor does it initially)
|
|
115
|
+
return this.activeCategoryOperations[categoryName];
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Retrieves all active category operation statuses.
|
|
120
|
+
* @returns A promise that resolves to a record of all active OnboardStatus objects, keyed by categoryName.
|
|
121
|
+
*/
|
|
122
|
+
public async getAllStatuses(): Promise<Record<string, OnboardStatus>> {
|
|
123
|
+
await this.loadStatuses();
|
|
124
|
+
return this.activeCategoryOperations;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Updates the status of a category's active operation.
|
|
129
|
+
* If no operation exists for the category, it might create one based on the updates,
|
|
130
|
+
* or fail if `onboardingId` (categoryName) is not present in `newStatus`.
|
|
131
|
+
* For clarity, it's better if `createInitialStatus` is called first.
|
|
132
|
+
* @param categoryName The name of the category (acting as operationId).
|
|
133
|
+
* @param updates Partial OnboardStatus object with fields to update.
|
|
134
|
+
* @returns A promise that resolves to the updated OnboardStatus.
|
|
135
|
+
*/
|
|
136
|
+
public async updateStatus(categoryName: string, updates: Partial<OnboardStatus>): Promise<OnboardStatus> {
|
|
137
|
+
return await this.withLock(async () => {
|
|
138
|
+
const existingStatus = this.activeCategoryOperations[categoryName] || {
|
|
139
|
+
onboardingId: categoryName, // Ensure onboardingId is set to categoryName if creating new
|
|
140
|
+
feedName: categoryName, // Ensure feedName is set to categoryName
|
|
141
|
+
status: OnboardingProcessStatus.PENDING, // Default status if creating new
|
|
142
|
+
lastUpdated: new Date().toISOString(),
|
|
143
|
+
operationType: updates.operationType || 'FULL_ONBOARDING', // Default operation type
|
|
144
|
+
...updates // Apply initial updates if creating new
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
const updatedStatus: OnboardStatus = {
|
|
148
|
+
...existingStatus,
|
|
149
|
+
...updates,
|
|
150
|
+
lastUpdated: new Date().toISOString(),
|
|
151
|
+
};
|
|
152
|
+
this.activeCategoryOperations[categoryName] = updatedStatus;
|
|
153
|
+
await this.saveStatuses();
|
|
154
|
+
return updatedStatus;
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Creates an initial status for a new category-wide operation.
|
|
160
|
+
* The operation is identified by the categoryName.
|
|
161
|
+
* @param categoryName The name of the category (also used as feedName and operationId).
|
|
162
|
+
* @param operationType The type of operation (e.g., FULL_ONBOARDING, VALIDATION_ONLY).
|
|
163
|
+
* @param serverName Optional: if the operation has a specific server context (e.g. validating a single new server in an existing category).
|
|
164
|
+
* However, the primary operation tracking is still by categoryName.
|
|
165
|
+
* @returns A promise that resolves to the initial OnboardStatus.
|
|
166
|
+
*/
|
|
167
|
+
public async createInitialStatus(categoryName: string, operationType: OperationType, serverName?: string): Promise<OnboardStatus> {
|
|
168
|
+
// The operationId is the categoryName itself for category-wide operations.
|
|
169
|
+
const operationId = categoryName;
|
|
170
|
+
|
|
171
|
+
const initialStatus: OnboardStatus = {
|
|
172
|
+
onboardingId: operationId, // This is the categoryName
|
|
173
|
+
feedName: categoryName, // Feed name is the category name
|
|
174
|
+
serverName, // Optional server context
|
|
175
|
+
status: OnboardingProcessStatus.PENDING,
|
|
176
|
+
currentStep: 'Initiated',
|
|
177
|
+
lastUpdated: new Date().toISOString(), // timestamp was an error, OnboardStatus uses lastUpdated
|
|
178
|
+
operationType,
|
|
179
|
+
errorMessage: undefined,
|
|
180
|
+
prInfo: undefined
|
|
181
|
+
};
|
|
182
|
+
// Use updateStatus to ensure it's saved correctly and lock is handled
|
|
183
|
+
return this.updateStatus(operationId, initialStatus);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// Export singleton instance
|
|
188
|
+
export const onboardStatusManager = OnboardStatusManager.getInstance();
|
package/src/core/types.ts
CHANGED
|
@@ -18,11 +18,13 @@ export interface RequirementStatus {
|
|
|
18
18
|
lastCheckTime?: string;
|
|
19
19
|
operationStatus?: OperationStatus;
|
|
20
20
|
pythonEnv?: string; // Store Python environment path for pip requirements
|
|
21
|
+
npmPath?: string; // Path to the installed npm package path
|
|
21
22
|
}
|
|
22
23
|
|
|
23
24
|
export interface MCPServerStatus {
|
|
24
25
|
installedStatus: Record<string, OperationStatus>; // client: installed
|
|
25
26
|
name: string;
|
|
27
|
+
tags?: string[];
|
|
26
28
|
error?: string;
|
|
27
29
|
}
|
|
28
30
|
|
|
@@ -46,6 +48,7 @@ export interface MCPServerCategory {
|
|
|
46
48
|
displayName: string;
|
|
47
49
|
description?: string;
|
|
48
50
|
type: 'local';
|
|
51
|
+
tags?: string[];
|
|
49
52
|
path?: string;
|
|
50
53
|
installationStatus?: InstallationStatus;
|
|
51
54
|
feedConfiguration?: FeedConfiguration;
|
|
@@ -131,12 +134,8 @@ export interface RegistryConfig {
|
|
|
131
134
|
};
|
|
132
135
|
artifacts?: {
|
|
133
136
|
registryUrl: string;
|
|
134
|
-
|
|
137
|
+
registryName: string; // Name of the registry
|
|
135
138
|
};
|
|
136
|
-
local?: {
|
|
137
|
-
localPath: string; // Local path for local registry
|
|
138
|
-
assetName?: string; // Template string like "ai-coder-tools-${latest}.tgz"
|
|
139
|
-
}
|
|
140
139
|
}
|
|
141
140
|
|
|
142
141
|
export interface RequirementConfig {
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { FeedConfiguration, McpConfig } from "../types.js";
|
|
2
|
+
import { serverValidatorFactory } from "./ServerValidatorFactory.js";
|
|
3
|
+
import { Logger } from "../../utils/logger.js";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Validates feed configurations to ensure they meet required criteria
|
|
7
|
+
*/
|
|
8
|
+
export class FeedValidator {
|
|
9
|
+
/**
|
|
10
|
+
* Validates a feed configuration
|
|
11
|
+
* @param config The feed configuration to validate
|
|
12
|
+
* @returns true if valid, throws error if invalid
|
|
13
|
+
*/
|
|
14
|
+
public validate(config: FeedConfiguration): boolean {
|
|
15
|
+
try {
|
|
16
|
+
Logger.debug(`Validating feed configuration: ${config.name}`);
|
|
17
|
+
|
|
18
|
+
// Validate required fields
|
|
19
|
+
if (!config.name) throw new Error('Feed name is required');
|
|
20
|
+
if (!config.displayName) throw new Error('Feed display name is required');
|
|
21
|
+
if (!config.description) throw new Error('Feed description is required');
|
|
22
|
+
|
|
23
|
+
// Validate MCP servers array
|
|
24
|
+
if (!Array.isArray(config.mcpServers) || config.mcpServers.length === 0) {
|
|
25
|
+
throw new Error('Feed must contain at least one MCP server');
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// Validate requirements if present
|
|
29
|
+
if (config.requirements) {
|
|
30
|
+
for (const req of config.requirements) {
|
|
31
|
+
if (!req.name) throw new Error('Requirement name is required');
|
|
32
|
+
if (!req.type) throw new Error('Requirement type is required');
|
|
33
|
+
if (!req.version) throw new Error('Requirement version is required');
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
Logger.debug(`Feed configuration validation successful: ${config.name}`);
|
|
38
|
+
return true;
|
|
39
|
+
} catch (error) {
|
|
40
|
+
const errorMsg = `Feed validation failed: ${error instanceof Error ? error.message : String(error)}`;
|
|
41
|
+
Logger.error(errorMsg);
|
|
42
|
+
throw new Error(errorMsg);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Validates a single MCP server configuration using the appropriate validator
|
|
48
|
+
* @param server The MCP server configuration to validate
|
|
49
|
+
* @param config The feed configuration containing shared requirements
|
|
50
|
+
* @returns true if valid, throws error if invalid
|
|
51
|
+
*/
|
|
52
|
+
public async validateServer(server: McpConfig, config: FeedConfiguration): Promise<boolean> {
|
|
53
|
+
try {
|
|
54
|
+
Logger.debug(`Validating server configuration: ${server.name}`);
|
|
55
|
+
|
|
56
|
+
// Validate basic required fields
|
|
57
|
+
if (!server.name) throw new Error('Server name is required');
|
|
58
|
+
if (!server.description) throw new Error('Server description is required');
|
|
59
|
+
if (!server.mode) throw new Error('Server mode is required');
|
|
60
|
+
if (!server.installation) throw new Error('Server installation configuration is required');
|
|
61
|
+
|
|
62
|
+
// Get the appropriate validator for this server type
|
|
63
|
+
const validator = serverValidatorFactory.getValidatorForServer(server);
|
|
64
|
+
|
|
65
|
+
// Perform mode-specific validation with feed config
|
|
66
|
+
await validator.validateServer(server, config);
|
|
67
|
+
|
|
68
|
+
Logger.debug(`Server configuration validation successful: ${server.name}`);
|
|
69
|
+
return true;
|
|
70
|
+
} catch (error) {
|
|
71
|
+
const errorMsg = `Server validation failed: ${error instanceof Error ? error.message : String(error)}`;
|
|
72
|
+
Logger.error(errorMsg);
|
|
73
|
+
throw new Error(errorMsg);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Export singleton instance
|
|
79
|
+
export const feedValidator = new FeedValidator();
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { McpConfig, FeedConfiguration } from "../types.js";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Interface for MCP server configuration validators
|
|
5
|
+
* Defines common validation operations for server configurations
|
|
6
|
+
*/
|
|
7
|
+
export interface IServerValidator {
|
|
8
|
+
/**
|
|
9
|
+
* Validates a specific MCP server configuration
|
|
10
|
+
* Implementation varies based on server mode (stdio/sse)
|
|
11
|
+
* @param server The MCP server configuration to validate
|
|
12
|
+
* @param config The feed configuration containing shared requirements
|
|
13
|
+
* @returns true if valid, throws error if invalid
|
|
14
|
+
*/
|
|
15
|
+
validateServer(server: McpConfig, config: FeedConfiguration): Promise<boolean>;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Valid validator types for MCP servers
|
|
20
|
+
*/
|
|
21
|
+
export type ValidatorType = 'stdio' | 'sse';
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { McpConfig, FeedConfiguration } from "../types.js";
|
|
2
|
+
import { IServerValidator } from "./IServerValidator.js";
|
|
3
|
+
import { Logger } from "../../utils/logger.js";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Validates MCP server configurations for SSE mode
|
|
7
|
+
*/
|
|
8
|
+
export class SSEServerValidator implements IServerValidator {
|
|
9
|
+
/**
|
|
10
|
+
* Validates SSE-specific MCP server configuration
|
|
11
|
+
* Checks installation URL, requirements, and environment variables
|
|
12
|
+
* @param server The MCP server configuration to validate
|
|
13
|
+
* @param config The feed configuration containing shared requirements
|
|
14
|
+
* @returns true if valid, throws error if invalid
|
|
15
|
+
*/
|
|
16
|
+
public async validateServer(server: McpConfig, config: FeedConfiguration): Promise<boolean> {
|
|
17
|
+
try {
|
|
18
|
+
Logger.debug(`Validating SSE server configuration: ${server.name}`);
|
|
19
|
+
|
|
20
|
+
if (!server.installation?.url) {
|
|
21
|
+
throw new Error('SSE server URL is required in installation configuration');
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
try {
|
|
25
|
+
new URL(server.installation.url);
|
|
26
|
+
} catch (e) {
|
|
27
|
+
throw new Error(`Invalid SSE server URL: ${server.installation.url}`);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Validate server mode
|
|
31
|
+
if (server.mode !== 'sse') {
|
|
32
|
+
throw new Error(`Invalid server mode for SSE validator: ${server.mode}`);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Additional SSE-specific validation will be implemented later
|
|
36
|
+
return true;
|
|
37
|
+
} catch (error) {
|
|
38
|
+
const errorMsg = `Server validation failed: ${error instanceof Error ? error.message : String(error)}`;
|
|
39
|
+
Logger.error(errorMsg);
|
|
40
|
+
throw new Error(errorMsg);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { McpConfig } from "../types.js";
|
|
2
|
+
import { IServerValidator, ValidatorType } from "./IServerValidator.js";
|
|
3
|
+
import { StdioServerValidator } from "./StdioServerValidator.js";
|
|
4
|
+
import { SSEServerValidator } from "./SSEServerValidator.js";
|
|
5
|
+
import { Logger } from "../../utils/logger.js";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Factory class for managing server validator instances
|
|
9
|
+
* Maintains a singleton map of validators by type
|
|
10
|
+
*/
|
|
11
|
+
export class ServerValidatorFactory {
|
|
12
|
+
private static validators = new Map<ValidatorType, IServerValidator>();
|
|
13
|
+
|
|
14
|
+
constructor() {
|
|
15
|
+
// Initialize validators if not already done
|
|
16
|
+
if (ServerValidatorFactory.validators.size === 0) {
|
|
17
|
+
Logger.debug('Initializing server validators');
|
|
18
|
+
ServerValidatorFactory.validators.set('stdio', new StdioServerValidator());
|
|
19
|
+
ServerValidatorFactory.validators.set('sse', new SSEServerValidator());
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Gets the appropriate validator for the given server mode
|
|
25
|
+
* @param mode The server mode to get validator for
|
|
26
|
+
* @returns IServerValidator instance for the specified mode
|
|
27
|
+
* @throws Error if server mode is not supported
|
|
28
|
+
*/
|
|
29
|
+
public getValidator(mode: ValidatorType): IServerValidator {
|
|
30
|
+
const validator = ServerValidatorFactory.validators.get(mode);
|
|
31
|
+
if (!validator) {
|
|
32
|
+
const error = `Unsupported MCP server mode: ${mode}`;
|
|
33
|
+
Logger.error(error);
|
|
34
|
+
throw new Error(error);
|
|
35
|
+
}
|
|
36
|
+
Logger.debug(`Retrieved ${mode} validator`);
|
|
37
|
+
return validator;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Gets the appropriate validator for the given server config
|
|
42
|
+
* @param serverConfig MCP server configuration
|
|
43
|
+
* @returns IServerValidator instance appropriate for the server mode
|
|
44
|
+
*/
|
|
45
|
+
public getValidatorForServer(serverConfig: McpConfig): IServerValidator {
|
|
46
|
+
return this.getValidator(serverConfig.mode);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Export singleton instance
|
|
51
|
+
export const serverValidatorFactory = new ServerValidatorFactory();
|