imcp 0.0.13 → 0.0.15
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 -8
- package/dist/core/installers/clients/ClientInstaller.js +77 -504
- package/dist/core/installers/clients/ClientInstallerFactory.d.ts +19 -0
- package/dist/core/installers/clients/ClientInstallerFactory.js +41 -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 +72 -0
- package/dist/core/onboard/FeedOnboardService.js +312 -0
- 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 +6 -6
- package/dist/core/validators/FeedValidator.d.ts +20 -0
- package/dist/core/validators/FeedValidator.js +80 -0
- 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 +66 -0
- package/dist/web/contract/serverContract.js +2 -0
- package/dist/web/public/css/notifications.css +48 -17
- package/dist/web/public/css/onboard.css +107 -0
- package/dist/web/public/index.html +90 -18
- 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/index.js +58 -0
- package/dist/web/public/js/modal/installHandler.js +227 -0
- package/dist/web/public/js/modal/installModal.js +163 -0
- package/dist/web/public/js/modal/installation.js +281 -0
- package/dist/web/public/js/modal/loadingModal.js +52 -0
- package/dist/web/public/js/modal/loadingUI.js +74 -0
- package/dist/web/public/js/modal/messageQueue.js +112 -0
- package/dist/web/public/js/modal/modalSetup.js +513 -0
- package/dist/web/public/js/modal/modalUI.js +214 -0
- package/dist/web/public/js/modal/modalUtils.js +49 -0
- package/dist/web/public/js/modal/version.js +20 -0
- package/dist/web/public/js/modal/versionUtils.js +20 -0
- package/dist/web/public/js/modal.js +25 -1041
- 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 +864 -0
- package/dist/web/public/js/onboard/index.js +374 -0
- package/dist/web/public/js/onboard/publishHandler.js +132 -0
- package/dist/web/public/js/onboard/state.js +76 -0
- package/dist/web/public/js/onboard/templates.js +343 -0
- package/dist/web/public/js/onboard/uiHandlers.js +758 -0
- package/dist/web/public/js/onboard/validationHandlers.js +378 -0
- package/dist/web/public/js/serverCategoryDetails.js +43 -17
- package/dist/web/public/js/serverCategoryList.js +15 -2
- package/dist/web/public/onboard.html +296 -0
- 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 -589
- package/src/core/installers/clients/ClientInstallerFactory.ts +46 -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 +6 -6
- 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 +90 -18
- 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/index.js +58 -0
- package/src/web/public/js/modal/installModal.js +163 -0
- package/src/web/public/js/modal/installation.js +281 -0
- package/src/web/public/js/modal/loadingModal.js +52 -0
- package/src/web/public/js/modal/messageQueue.js +112 -0
- package/src/web/public/js/modal/modalSetup.js +513 -0
- package/src/web/public/js/modal/modalUtils.js +49 -0
- package/src/web/public/js/modal/versionUtils.js +20 -0
- package/src/web/public/js/modal.js +25 -1041
- 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/serverCategoryDetails.js +43 -17
- 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
|
@@ -1,25 +1,19 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { ConfigurationProvider } from '../../ConfigurationProvider.js';
|
|
2
|
+
import { SUPPORTED_CLIENTS } from '../../constants.js';
|
|
2
3
|
import {
|
|
3
4
|
ServerOperationResult,
|
|
4
5
|
OperationStatus,
|
|
5
|
-
MCPServerStatus,
|
|
6
|
-
RequirementStatus,
|
|
7
6
|
ServerInstallOptions,
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
McpConfig,
|
|
8
|
+
RequirementStatus
|
|
10
9
|
} from '../../types.js';
|
|
11
|
-
import { GetBrowserPath } from '../../../utils/osUtils.js';
|
|
12
|
-
import { ConfigurationProvider } from '../../ConfigurationProvider.js';
|
|
13
|
-
import { SUPPORTED_CLIENTS } from '../../constants.js';
|
|
14
|
-
import { resolveNpmModulePath, readJsonFile, writeJsonFile } from '../../../utils/clientUtils.js';
|
|
15
|
-
import { exec } from 'child_process';
|
|
16
|
-
import { promisify } from 'util';
|
|
17
|
-
import * as path from 'path';
|
|
18
10
|
import { Logger } from '../../../utils/logger.js';
|
|
19
|
-
import {
|
|
20
|
-
|
|
21
|
-
const execAsync = promisify(exec); // Moved promisify here for reuse
|
|
11
|
+
import { ClientInstallerFactory } from './ClientInstallerFactory.js';
|
|
22
12
|
|
|
13
|
+
/**
|
|
14
|
+
* Main client installer class that orchestrates client installation process
|
|
15
|
+
* Handles requirements checking and delegates to specific client installers
|
|
16
|
+
*/
|
|
23
17
|
export class ClientInstaller {
|
|
24
18
|
private configProvider: ConfigurationProvider;
|
|
25
19
|
private operationStatuses: Map<string, OperationStatus>;
|
|
@@ -33,106 +27,87 @@ export class ClientInstaller {
|
|
|
33
27
|
this.operationStatuses = new Map();
|
|
34
28
|
}
|
|
35
29
|
|
|
30
|
+
/**
|
|
31
|
+
* Generate a unique operation ID for tracking installations
|
|
32
|
+
*/
|
|
36
33
|
private generateOperationId(): string {
|
|
37
34
|
return `install-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
|
38
35
|
}
|
|
39
36
|
|
|
40
|
-
private async getNpmPath(): Promise<string> {
|
|
41
|
-
try {
|
|
42
|
-
// Execute the get-command npm command to find the npm path
|
|
43
|
-
const { stdout } = await execAsync('powershell -Command "get-command npm | Select-Object -ExpandProperty Source"');
|
|
44
|
-
|
|
45
|
-
// Extract the directory from the full path (removing npm.cmd)
|
|
46
|
-
const npmPath = stdout.trim().replace(/\\npm\.cmd$/, '');
|
|
47
|
-
return npmPath;
|
|
48
|
-
} catch (error) {
|
|
49
|
-
console.error('Error getting npm path:', error);
|
|
50
|
-
// Return a default path if the command fails
|
|
51
|
-
return 'C:\\Program Files\\nodejs';
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
|
|
56
37
|
/**
|
|
57
|
-
* Check if
|
|
58
|
-
*
|
|
59
|
-
* @returns True if the command is available, false otherwise
|
|
38
|
+
* Check if server requirements are ready
|
|
39
|
+
* Waits for requirements to be ready with timeout
|
|
60
40
|
*/
|
|
61
|
-
private async
|
|
62
|
-
|
|
63
|
-
// For VS Code on macOS, check both command-line tool and app bundle
|
|
64
|
-
if (process.platform === 'darwin' && (command === 'code' || command === 'code-insiders')) {
|
|
65
|
-
try {
|
|
66
|
-
// Try which command first
|
|
67
|
-
await execAsync(`which ${command}`);
|
|
68
|
-
return true;
|
|
69
|
-
} catch (error) {
|
|
70
|
-
// If which fails, check application bundles
|
|
71
|
-
const systemVSCodePath = command === 'code' ?
|
|
72
|
-
'/Applications/Visual Studio Code.app' :
|
|
73
|
-
'/Applications/Visual Studio Code - Insiders.app';
|
|
41
|
+
private async checkRequirements(operationId: string, clientName: string, options: ServerInstallOptions): Promise<boolean> {
|
|
42
|
+
let requirementsReady = await this.configProvider.isRequirementsReady(this.categoryName, this.serverName);
|
|
74
43
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
const userVSCodePath = command === 'code' ?
|
|
84
|
-
`${homedir}/Applications/Visual Studio Code.app` :
|
|
85
|
-
`${homedir}/Applications/Visual Studio Code - Insiders.app`;
|
|
44
|
+
if (!requirementsReady) {
|
|
45
|
+
const pendingStatus: OperationStatus = {
|
|
46
|
+
status: 'pending',
|
|
47
|
+
type: 'install',
|
|
48
|
+
target: 'server',
|
|
49
|
+
message: `Waiting for requirements to be ready for client: ${clientName}`,
|
|
50
|
+
operationId
|
|
51
|
+
};
|
|
86
52
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
53
|
+
await this.configProvider.updateServerOperationStatus(
|
|
54
|
+
this.categoryName,
|
|
55
|
+
this.serverName,
|
|
56
|
+
clientName,
|
|
57
|
+
pendingStatus
|
|
58
|
+
);
|
|
59
|
+
|
|
60
|
+
// Set up periodic checking with timeout
|
|
61
|
+
const startTime = Date.now();
|
|
62
|
+
const timeoutMs = 5 * 60 * 1000; // 5 minutes
|
|
63
|
+
const intervalMs = 5 * 1000; // 5 seconds
|
|
98
64
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
await
|
|
102
|
-
return true;
|
|
65
|
+
while (!requirementsReady && (Date.now() - startTime) < timeoutMs) {
|
|
66
|
+
await new Promise(resolve => setTimeout(resolve, intervalMs));
|
|
67
|
+
requirementsReady = await this.configProvider.isRequirementsReady(this.categoryName, this.serverName);
|
|
103
68
|
}
|
|
69
|
+
}
|
|
104
70
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
71
|
+
var requirementsStatus: RequirementStatus[] = await this.configProvider.GetServerRequirementStatus(this.categoryName, this.serverName);
|
|
72
|
+
|
|
73
|
+
// Find first non-empty npmPath from requirements status
|
|
74
|
+
const npmPathRequirement = requirementsStatus.find(status => status.npmPath && status.npmPath.length > 0);
|
|
75
|
+
if (npmPathRequirement && npmPathRequirement.npmPath) {
|
|
76
|
+
options.settings = options.settings || {};
|
|
77
|
+
options.settings.npmPath = npmPathRequirement.npmPath;
|
|
110
78
|
}
|
|
79
|
+
|
|
80
|
+
return requirementsReady;
|
|
111
81
|
}
|
|
112
82
|
|
|
113
|
-
|
|
83
|
+
/**
|
|
84
|
+
* Install client with requirements checking
|
|
85
|
+
*/
|
|
114
86
|
private async installClient(clientName: string, options: ServerInstallOptions): Promise<OperationStatus> {
|
|
87
|
+
const operationId = this.generateOperationId();
|
|
88
|
+
|
|
115
89
|
// Check if client is supported
|
|
116
|
-
if (!
|
|
90
|
+
if (!ClientInstallerFactory.isClientSupported(clientName)) {
|
|
117
91
|
return {
|
|
118
92
|
status: 'failed',
|
|
119
93
|
type: 'install',
|
|
120
94
|
target: 'server',
|
|
121
95
|
message: `Unsupported client: ${clientName}`,
|
|
122
|
-
operationId
|
|
96
|
+
operationId
|
|
123
97
|
};
|
|
124
98
|
}
|
|
125
99
|
|
|
126
100
|
// Create initial operation status
|
|
127
|
-
const operationId = this.generateOperationId();
|
|
128
101
|
const initialStatus: OperationStatus = {
|
|
129
102
|
status: 'pending',
|
|
130
103
|
type: 'install',
|
|
131
104
|
target: 'server',
|
|
132
105
|
message: `Initializing installation for client: ${clientName}`,
|
|
133
|
-
operationId
|
|
106
|
+
operationId
|
|
134
107
|
};
|
|
135
108
|
|
|
109
|
+
this.processInstallation(clientName, operationId, options);
|
|
110
|
+
|
|
136
111
|
// Update server status with initial client installation status
|
|
137
112
|
await this.configProvider.updateServerOperationStatus(
|
|
138
113
|
this.categoryName,
|
|
@@ -140,73 +115,41 @@ export class ClientInstaller {
|
|
|
140
115
|
clientName,
|
|
141
116
|
initialStatus
|
|
142
117
|
);
|
|
143
|
-
|
|
144
|
-
// Start the asynchronous installation process without awaiting it
|
|
145
|
-
// Pass options down
|
|
146
|
-
this.processInstallation(clientName, operationId, options);
|
|
147
|
-
|
|
148
|
-
// Return the initial status immediately
|
|
149
118
|
return initialStatus;
|
|
150
119
|
}
|
|
151
120
|
|
|
152
|
-
// Modified to accept ServerInstallOptions
|
|
153
121
|
private async processInstallation(clientName: string, operationId: string, options: ServerInstallOptions): Promise<void> {
|
|
154
122
|
try {
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
// Check requirements before installation
|
|
158
|
-
let requirementsReady = await this.configProvider.isRequirementsReady(this.categoryName, this.serverName);
|
|
159
|
-
|
|
160
|
-
// If requirements are not ready, periodically check with timeout
|
|
123
|
+
// Check requirements
|
|
124
|
+
const requirementsReady = await this.checkRequirements(operationId, clientName, options);
|
|
161
125
|
if (!requirementsReady) {
|
|
162
|
-
const
|
|
163
|
-
status: '
|
|
126
|
+
const failedStatus: OperationStatus = {
|
|
127
|
+
status: 'failed',
|
|
164
128
|
type: 'install',
|
|
165
129
|
target: 'server',
|
|
166
|
-
message: `
|
|
167
|
-
operationId
|
|
130
|
+
message: `Requirements not ready for client: ${clientName} after timeout`,
|
|
131
|
+
operationId
|
|
168
132
|
};
|
|
169
133
|
|
|
170
|
-
// Update status to pending with reference to configProvider
|
|
171
134
|
await this.configProvider.updateServerOperationStatus(
|
|
172
135
|
this.categoryName,
|
|
173
136
|
this.serverName,
|
|
174
137
|
clientName,
|
|
175
|
-
|
|
138
|
+
failedStatus
|
|
176
139
|
);
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
177
142
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
const timeoutMs = 5 * 60 * 1000; // 5 minutes in milliseconds
|
|
181
|
-
const intervalMs = 5 * 1000; // 5 seconds in milliseconds
|
|
182
|
-
|
|
183
|
-
while (!requirementsReady && (Date.now() - startTime) < timeoutMs) {
|
|
184
|
-
// Wait for the interval
|
|
185
|
-
await new Promise(resolve => setTimeout(resolve, intervalMs));
|
|
186
|
-
|
|
187
|
-
// Check again
|
|
188
|
-
requirementsReady = await this.configProvider.isRequirementsReady(this.categoryName, this.serverName);
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
// If still not ready after timeout, update status as failed and exit
|
|
192
|
-
if (!requirementsReady) {
|
|
193
|
-
const failedStatus: OperationStatus = {
|
|
194
|
-
status: 'failed',
|
|
195
|
-
type: 'install',
|
|
196
|
-
target: 'server',
|
|
197
|
-
message: `Timed out waiting for requirements to be ready for client: ${clientName} after 5 minutes`,
|
|
198
|
-
operationId: operationId
|
|
199
|
-
};
|
|
143
|
+
// Create client-specific installer
|
|
144
|
+
const installer = ClientInstallerFactory.createInstaller(clientName);
|
|
200
145
|
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
clientName,
|
|
205
|
-
failedStatus
|
|
206
|
-
);
|
|
146
|
+
if (!installer) {
|
|
147
|
+
throw new Error(`Failed to create installer for client: ${clientName}`);
|
|
148
|
+
}
|
|
207
149
|
|
|
208
|
-
|
|
209
|
-
|
|
150
|
+
const serverConfig = await this.configProvider.getServerMcpConfig(this.categoryName, this.serverName);
|
|
151
|
+
if (!serverConfig) {
|
|
152
|
+
throw new Error(`Server configuration not found for category: ${this.categoryName}, server: ${this.serverName}`);
|
|
210
153
|
}
|
|
211
154
|
|
|
212
155
|
// If we've reached here, requirements are ready - update status to in-progress
|
|
@@ -224,101 +167,25 @@ export class ClientInstaller {
|
|
|
224
167
|
clientName,
|
|
225
168
|
inProgressStatus
|
|
226
169
|
);
|
|
170
|
+
// Install client
|
|
171
|
+
const status = await installer.install(serverConfig, options);
|
|
172
|
+
await this.configProvider.updateServerOperationStatus(
|
|
173
|
+
this.categoryName,
|
|
174
|
+
this.serverName,
|
|
175
|
+
clientName,
|
|
176
|
+
status
|
|
177
|
+
);
|
|
227
178
|
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
if (!feedConfiguration) {
|
|
231
|
-
const errorStatus: OperationStatus = {
|
|
232
|
-
status: 'failed',
|
|
233
|
-
type: 'install',
|
|
234
|
-
target: 'server',
|
|
235
|
-
message: `Failed to get feed configuration for category: ${this.categoryName}`,
|
|
236
|
-
operationId: operationId
|
|
237
|
-
};
|
|
238
|
-
await this.configProvider.updateServerOperationStatus(
|
|
239
|
-
this.categoryName,
|
|
240
|
-
this.serverName,
|
|
241
|
-
clientName,
|
|
242
|
-
errorStatus
|
|
243
|
-
);
|
|
244
|
-
return;
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
// Find the server config in the feed configuration
|
|
248
|
-
const serverConfig = feedConfiguration.mcpServers.find(s => s.name === this.serverName);
|
|
249
|
-
if (!serverConfig) {
|
|
250
|
-
const errorStatus: OperationStatus = {
|
|
251
|
-
status: 'failed',
|
|
252
|
-
type: 'install',
|
|
253
|
-
target: 'server',
|
|
254
|
-
message: `Server ${this.serverName} not found in feed configuration`,
|
|
255
|
-
operationId: operationId
|
|
256
|
-
};
|
|
257
|
-
await this.configProvider.updateServerOperationStatus(
|
|
258
|
-
this.categoryName,
|
|
259
|
-
this.serverName,
|
|
260
|
-
clientName,
|
|
261
|
-
errorStatus
|
|
262
|
-
);
|
|
263
|
-
return;
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
try {
|
|
267
|
-
// Install extension if available
|
|
268
|
-
if (SUPPORTED_CLIENTS[clientName]?.extension) {
|
|
269
|
-
const extensionResult = await ExtensionInstaller.installExtension(clientName);
|
|
270
|
-
if (!extensionResult) {
|
|
271
|
-
Logger.debug(`Failed to install extension for client ${clientName}`);
|
|
272
|
-
}
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
// Install client-specific configuration, passing options down
|
|
276
|
-
const result = await this.installClientConfig(clientName, options, serverConfig, feedConfiguration);
|
|
277
|
-
|
|
278
|
-
const finalStatus: OperationStatus = {
|
|
279
|
-
status: result.success ? 'completed' : 'failed',
|
|
280
|
-
type: 'install',
|
|
281
|
-
target: 'server',
|
|
282
|
-
message: result.message || `Installation for client ${clientName}: ${result.success ? 'successful' : 'failed'}`,
|
|
283
|
-
operationId: operationId,
|
|
284
|
-
error: result.success ? undefined : result.message
|
|
285
|
-
};
|
|
286
|
-
|
|
287
|
-
await this.configProvider.updateServerOperationStatus(
|
|
288
|
-
this.categoryName,
|
|
289
|
-
this.serverName,
|
|
290
|
-
clientName,
|
|
291
|
-
finalStatus
|
|
292
|
-
);
|
|
293
|
-
|
|
294
|
-
if (result.success) {
|
|
295
|
-
await this.configProvider.reloadClientMCPSettings();
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
} catch (error) {
|
|
299
|
-
const errorStatus: OperationStatus = {
|
|
300
|
-
status: 'failed',
|
|
301
|
-
type: 'install',
|
|
302
|
-
target: 'server',
|
|
303
|
-
message: `Failed to install client: ${clientName}. Error: ${error instanceof Error ? error.message : String(error)}`,
|
|
304
|
-
operationId: operationId,
|
|
305
|
-
error: error instanceof Error ? error.message : String(error)
|
|
306
|
-
};
|
|
307
|
-
|
|
308
|
-
await this.configProvider.updateServerOperationStatus(
|
|
309
|
-
this.categoryName,
|
|
310
|
-
this.serverName,
|
|
311
|
-
clientName,
|
|
312
|
-
errorStatus
|
|
313
|
-
);
|
|
179
|
+
if (status.status === 'completed') {
|
|
180
|
+
await this.configProvider.reloadClientMCPSettings();
|
|
314
181
|
}
|
|
315
182
|
} catch (error) {
|
|
316
183
|
const errorStatus: OperationStatus = {
|
|
317
184
|
status: 'failed',
|
|
318
185
|
type: 'install',
|
|
319
186
|
target: 'server',
|
|
320
|
-
message: `
|
|
321
|
-
operationId
|
|
187
|
+
message: `Error installing client ${clientName}: ${error instanceof Error ? error.message : String(error)}`,
|
|
188
|
+
operationId,
|
|
322
189
|
error: error instanceof Error ? error.message : String(error)
|
|
323
190
|
};
|
|
324
191
|
|
|
@@ -331,385 +198,26 @@ export class ClientInstaller {
|
|
|
331
198
|
}
|
|
332
199
|
}
|
|
333
200
|
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
options: ServerInstallOptions, // Use options directly
|
|
338
|
-
serverConfig: McpConfig,
|
|
339
|
-
feedConfig: FeedConfiguration
|
|
340
|
-
): Promise<{ success: boolean; message: string }> {
|
|
341
|
-
try {
|
|
342
|
-
if (!SUPPORTED_CLIENTS[clientName]) {
|
|
343
|
-
Logger.debug(`Client ${clientName} is not supported.`);
|
|
344
|
-
return { success: false, message: `Unsupported client: ${clientName}` };
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
const clientSettings = SUPPORTED_CLIENTS[clientName];
|
|
348
|
-
|
|
349
|
-
// Get both setting paths for VS Code and VS Code Insiders
|
|
350
|
-
const regularSettingPath = clientSettings.codeSettingPath;
|
|
351
|
-
const insidersSettingPath = clientSettings.codeInsiderSettingPath;
|
|
352
|
-
|
|
353
|
-
Logger.debug(`Starting installation of ${this.serverName} for client ${clientName}`);
|
|
354
|
-
Logger.debug(`VS Code settings path configured as: ${regularSettingPath}`);
|
|
355
|
-
Logger.debug(`VS Code Insiders settings path configured as: ${insidersSettingPath}`);
|
|
356
|
-
|
|
357
|
-
// Check if VS Code and VS Code Insiders are installed
|
|
358
|
-
const isVSCodeInstalled = await this.isCommandAvailable('code');
|
|
359
|
-
const isVSCodeInsidersInstalled = await this.isCommandAvailable('code-insiders');
|
|
360
|
-
Logger.debug(isVSCodeInstalled ? 'VS Code detected on system' : 'VS Code not detected on system');
|
|
361
|
-
Logger.debug(isVSCodeInsidersInstalled ? 'VS Code Insiders detected on system' : 'VS Code Insiders not detected on system');
|
|
362
|
-
|
|
363
|
-
// If neither is installed, we can't proceed
|
|
364
|
-
if (!isVSCodeInstalled && !isVSCodeInsidersInstalled) {
|
|
365
|
-
Logger.debug('No VS Code installations detected on system. Cannot update settings.');
|
|
366
|
-
return {
|
|
367
|
-
success: false,
|
|
368
|
-
message: `Neither VS Code nor VS Code Insiders are installed on this system. Cannot update settings for client: ${clientName}. Please install VS Code or VS Code Insiders and try again.`
|
|
369
|
-
};
|
|
370
|
-
}
|
|
371
|
-
|
|
372
|
-
// --- Start of new logic ---
|
|
373
|
-
|
|
374
|
-
// Clone the base installation configuration to avoid modifying the original serverConfig
|
|
375
|
-
const installConfig = JSON.parse(JSON.stringify(serverConfig.installation));
|
|
376
|
-
const pythonEnv = options.settings?.pythonEnv;
|
|
377
|
-
let pythonDir: string | null = null;
|
|
378
|
-
|
|
379
|
-
// 1. Determine which args to use and resolve npm paths
|
|
380
|
-
let finalArgs: string[] = [];
|
|
381
|
-
if (options.args && options.args.length > 0) {
|
|
382
|
-
Logger.debug(`Using args from ServerInstallOptions: ${options.args.join(' ')}`);
|
|
383
|
-
finalArgs = options.args.map(arg => resolveNpmModulePath(arg));
|
|
384
|
-
} else if (installConfig.args && installConfig.args.length > 0) {
|
|
385
|
-
Logger.debug(`Using args from serverConfig.installation: ${installConfig.args.join(' ')}`);
|
|
386
|
-
finalArgs = installConfig.args.map((arg: string) => resolveNpmModulePath(arg));
|
|
387
|
-
} else {
|
|
388
|
-
Logger.debug('No args found in options or serverConfig.installation.');
|
|
389
|
-
finalArgs = [];
|
|
390
|
-
}
|
|
391
|
-
|
|
392
|
-
// 2. Handle pythonEnv settings
|
|
393
|
-
if (pythonEnv) {
|
|
394
|
-
// 2.1 If pythonEnv is set
|
|
395
|
-
Logger.debug(`Python environment specified: ${pythonEnv}`);
|
|
396
|
-
pythonDir = getPythonPackagePath(pythonEnv);
|
|
397
|
-
|
|
398
|
-
// 2.1.1 Replace ${PYTHON_PACKAGE} in args
|
|
399
|
-
if (pythonDir) {
|
|
400
|
-
finalArgs = finalArgs.map(arg => arg.includes('${PYTHON_PACKAGE}') ? arg.replace('${PYTHON_PACKAGE}', pythonDir!) : arg);
|
|
401
|
-
Logger.debug(`Args after PYTHON_PACKAGE replacement (using pythonEnv): ${finalArgs.join(' ')}`);
|
|
402
|
-
} else {
|
|
403
|
-
Logger.debug(`Could not determine directory for pythonEnv: ${pythonEnv}`);
|
|
404
|
-
}
|
|
405
|
-
|
|
406
|
-
// 2.1.2 Replace command if it's 'python'
|
|
407
|
-
if (installConfig.command === 'python') {
|
|
408
|
-
Logger.debug(`Replacing command 'python' with specified pythonEnv: ${pythonEnv}`);
|
|
409
|
-
installConfig.command = pythonEnv;
|
|
410
|
-
}
|
|
411
|
-
} else {
|
|
412
|
-
// 2.2 If pythonEnv is not set
|
|
413
|
-
Logger.debug('No Python environment specified in settings.');
|
|
414
|
-
// 2.2.1 Replace ${PYTHON_PACKAGE} with system python directory if needed
|
|
415
|
-
if (finalArgs.some(arg => arg.includes('${PYTHON_PACKAGE}'))) {
|
|
416
|
-
Logger.debug('Attempting to find system Python directory for ${PYTHON_PACKAGE} replacement.');
|
|
417
|
-
pythonDir = await getSystemPythonPackageDirectory();
|
|
418
|
-
if (pythonDir) {
|
|
419
|
-
finalArgs = finalArgs.map(arg => arg.includes('${PYTHON_PACKAGE}') ? arg.replace('${PYTHON_PACKAGE}', pythonDir!) : arg);
|
|
420
|
-
Logger.debug(`Args after PYTHON_PACKAGE replacement (using system python): ${finalArgs.join(' ')}`);
|
|
421
|
-
} else {
|
|
422
|
-
Logger.debug('Could not find system Python directory. ${PYTHON_PACKAGE} replacement skipped.');
|
|
423
|
-
// Optionally, remove or handle the arg containing ${PYTHON_PACKAGE} if Python is required
|
|
424
|
-
// finalArgs = finalArgs.filter(arg => !arg.includes('${PYTHON_PACKAGE}'));
|
|
425
|
-
}
|
|
426
|
-
}
|
|
427
|
-
}
|
|
428
|
-
|
|
429
|
-
// Update installConfig with potentially modified args
|
|
430
|
-
installConfig.args = finalArgs;
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
// 3. Handle environment variables (merge default, serverConfig, and options.env)
|
|
434
|
-
const baseEnv = serverConfig.installation.env || {};
|
|
435
|
-
const defaultEnv: Record<string, string> = {};
|
|
436
|
-
for (const [key, config] of Object.entries(baseEnv)) {
|
|
437
|
-
const envConfig = config as any; // Type assertion
|
|
438
|
-
if (envConfig.Default) {
|
|
439
|
-
defaultEnv[key] = envConfig.Default;
|
|
440
|
-
}
|
|
441
|
-
}
|
|
442
|
-
// Merge: options.env overrides defaultEnv
|
|
443
|
-
installConfig.env = { ...defaultEnv, ...(options.env || {}) };
|
|
444
|
-
|
|
445
|
-
// Replace ${BROWSER_PATH} with actual browser path
|
|
446
|
-
const replacements = Object.entries(installConfig.env)
|
|
447
|
-
.filter(([_, value]) => typeof value === 'string' && value.includes('${BROWSER_PATH}'))
|
|
448
|
-
.map(async ([key, value]) => {
|
|
449
|
-
try {
|
|
450
|
-
const browserPath = await GetBrowserPath();
|
|
451
|
-
return [key, (value as string).replace('${BROWSER_PATH}', browserPath)] as [string, string];
|
|
452
|
-
} catch (error) {
|
|
453
|
-
Logger.error(`Failed to get system browser path for env var ${key}:`, error);
|
|
454
|
-
return [key, value] as [string, string];
|
|
455
|
-
}
|
|
456
|
-
});
|
|
457
|
-
|
|
458
|
-
// Wait for all replacements to complete
|
|
459
|
-
const replacedValues = await Promise.all(replacements);
|
|
460
|
-
replacedValues.forEach(([key, value]) => {
|
|
461
|
-
installConfig.env[key] = value;
|
|
462
|
-
});
|
|
463
|
-
|
|
464
|
-
Logger.debug(`Final environment variables: ${JSON.stringify(installConfig.env)}`);
|
|
465
|
-
|
|
466
|
-
// --- End of new logic ---
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
// Keep track of success for both installations
|
|
470
|
-
let regularSuccess = false;
|
|
471
|
-
let insidersSuccess = false;
|
|
472
|
-
let errorMessages: string[] = [];
|
|
473
|
-
|
|
474
|
-
// Update client-specific settings for both VS Code and VS Code Insiders
|
|
475
|
-
if (clientName === 'MSRooCode' || clientName === 'Cline') {
|
|
476
|
-
// Update VS Code settings if VS Code is installed
|
|
477
|
-
if (isVSCodeInstalled) {
|
|
478
|
-
try {
|
|
479
|
-
Logger.debug(`Updating VS Code settings for ${clientName} at path: ${regularSettingPath}`);
|
|
480
|
-
// Pass the modified installConfig
|
|
481
|
-
await this.updateClineOrMSRooSettings(regularSettingPath, this.serverName, installConfig, clientName);
|
|
482
|
-
regularSuccess = true;
|
|
483
|
-
Logger.debug(`Settings successfully updated to ${regularSettingPath} for ${clientName} (VS Code)`);
|
|
484
|
-
} catch (error) {
|
|
485
|
-
const errorMsg = `Error updating VS Code settings: ${error instanceof Error ? error.message : String(error)}`;
|
|
486
|
-
errorMessages.push(errorMsg);
|
|
487
|
-
console.error(errorMsg);
|
|
488
|
-
}
|
|
489
|
-
}
|
|
490
|
-
|
|
491
|
-
// Update VS Code Insiders settings if VS Code Insiders is installed
|
|
492
|
-
if (isVSCodeInsidersInstalled) {
|
|
493
|
-
try {
|
|
494
|
-
Logger.debug(`Updating VS Code Insiders settings for ${clientName} at path: ${insidersSettingPath}`);
|
|
495
|
-
// Pass the modified installConfig
|
|
496
|
-
await this.updateClineOrMSRooSettings(insidersSettingPath, this.serverName, installConfig, clientName);
|
|
497
|
-
insidersSuccess = true;
|
|
498
|
-
Logger.debug(`Settings successfully updated to ${insidersSettingPath} for ${clientName} (VS Code Insiders)`);
|
|
499
|
-
} catch (error) {
|
|
500
|
-
const errorMsg = `Error updating VS Code Insiders settings: ${error instanceof Error ? error.message : String(error)}`;
|
|
501
|
-
errorMessages.push(errorMsg);
|
|
502
|
-
console.error(errorMsg);
|
|
503
|
-
}
|
|
504
|
-
}
|
|
505
|
-
} else if (clientName === 'GithubCopilot') {
|
|
506
|
-
// Update VS Code settings if VS Code is installed
|
|
507
|
-
if (isVSCodeInstalled) {
|
|
508
|
-
try {
|
|
509
|
-
Logger.debug(`Updating VS Code settings for ${clientName} at path: ${regularSettingPath}`);
|
|
510
|
-
// Pass the modified installConfig
|
|
511
|
-
await this.updateGithubCopilotSettings(regularSettingPath, this.serverName, installConfig);
|
|
512
|
-
regularSuccess = true;
|
|
513
|
-
Logger.debug(`Settings successfully updated to ${regularSettingPath} for ${clientName} (VS Code)`);
|
|
514
|
-
} catch (error) {
|
|
515
|
-
const errorMsg = `Error updating VS Code settings: ${error instanceof Error ? error.message : String(error)}`;
|
|
516
|
-
errorMessages.push(errorMsg);
|
|
517
|
-
console.error(errorMsg);
|
|
518
|
-
}
|
|
519
|
-
}
|
|
520
|
-
|
|
521
|
-
// Update VS Code Insiders settings if VS Code Insiders is installed
|
|
522
|
-
if (isVSCodeInsidersInstalled) {
|
|
523
|
-
try {
|
|
524
|
-
Logger.debug(`Updating VS Code Insiders settings for ${clientName} at path: ${insidersSettingPath}`);
|
|
525
|
-
// Pass the modified installConfig
|
|
526
|
-
await this.updateGithubCopilotSettings(insidersSettingPath, this.serverName, installConfig);
|
|
527
|
-
insidersSuccess = true;
|
|
528
|
-
Logger.debug(`Settings successfully updated to ${insidersSettingPath} for ${clientName} (VS Code Insiders)`);
|
|
529
|
-
} catch (error) {
|
|
530
|
-
const errorMsg = `Error updating VS Code Insiders settings: ${error instanceof Error ? error.message : String(error)}`;
|
|
531
|
-
errorMessages.push(errorMsg);
|
|
532
|
-
console.error(errorMsg);
|
|
533
|
-
}
|
|
534
|
-
}
|
|
535
|
-
} else {
|
|
536
|
-
Logger.debug(`No implementation exists for updating settings for client: ${clientName}`);
|
|
537
|
-
return {
|
|
538
|
-
success: false,
|
|
539
|
-
message: `Client ${clientName} is supported but no implementation exists for updating its settings`
|
|
540
|
-
};
|
|
541
|
-
}
|
|
542
|
-
|
|
543
|
-
// Determine overall success status and message
|
|
544
|
-
const overallSuccess = regularSuccess || insidersSuccess;
|
|
545
|
-
let message = '';
|
|
546
|
-
|
|
547
|
-
if (overallSuccess) {
|
|
548
|
-
const successDetails = [];
|
|
549
|
-
if (regularSuccess) successDetails.push('VS Code');
|
|
550
|
-
if (insidersSuccess) successDetails.push('VS Code Insiders');
|
|
551
|
-
|
|
552
|
-
// Create a more compact success message with specific paths
|
|
553
|
-
const pathDetails = [];
|
|
554
|
-
if (regularSuccess) {
|
|
555
|
-
pathDetails.push(`\n[VS Code]: ${regularSettingPath}`);
|
|
556
|
-
}
|
|
557
|
-
if (insidersSuccess) {
|
|
558
|
-
pathDetails.push(`\n[VS Code Insiders]: ${insidersSettingPath}`);
|
|
559
|
-
}
|
|
560
|
-
message = `Successfully installed ${this.serverName} for client: ${clientName}. ${pathDetails.join(' ')}`;
|
|
561
|
-
|
|
562
|
-
// Add partial failure information if applicable
|
|
563
|
-
if (errorMessages.length > 0) {
|
|
564
|
-
message += `\nHowever, some errors occurred:\n${errorMessages.join('\n- ')}`;
|
|
565
|
-
}
|
|
566
|
-
Logger.debug(`Installation complete: ${message}`);
|
|
567
|
-
} else {
|
|
568
|
-
// Create a more detailed failure message that includes the detection results
|
|
569
|
-
const detectionInfo = [];
|
|
570
|
-
if (!isVSCodeInstalled) detectionInfo.push('VS Code not detected');
|
|
571
|
-
if (!isVSCodeInsidersInstalled) detectionInfo.push('VS Code Insiders not detected');
|
|
572
|
-
|
|
573
|
-
if (errorMessages.length > 0) {
|
|
574
|
-
message = `Failed to install ${this.serverName} for client: ${clientName}.\nDetection status: [${detectionInfo.join(', ')}].\nErrors:\n- ${errorMessages.join('\n- ')}`;
|
|
575
|
-
} else {
|
|
576
|
-
message = `Failed to install ${this.serverName} for client: ${clientName}.\nDetection status: [${detectionInfo.join(', ')}]`;
|
|
577
|
-
}
|
|
578
|
-
console.error(`Installation failed: ${message}`);
|
|
579
|
-
}
|
|
580
|
-
|
|
581
|
-
return {
|
|
582
|
-
success: overallSuccess,
|
|
583
|
-
message: message
|
|
584
|
-
};
|
|
585
|
-
} catch (error) {
|
|
586
|
-
const errorMsg = `Error installing client ${clientName}: ${error instanceof Error ? error.message : String(error)}`;
|
|
587
|
-
console.error(errorMsg);
|
|
588
|
-
return {
|
|
589
|
-
success: false,
|
|
590
|
-
message: errorMsg
|
|
591
|
-
};
|
|
592
|
-
}
|
|
593
|
-
}
|
|
594
|
-
|
|
595
|
-
private async updateClineOrMSRooSettings(
|
|
596
|
-
settingPath: string,
|
|
597
|
-
serverName: string,
|
|
598
|
-
installConfig: any, // Use the processed installConfig
|
|
599
|
-
clientName: string
|
|
600
|
-
): Promise<void> {
|
|
601
|
-
// Read the Cline/MSRoo settings file
|
|
602
|
-
const settings = await readJsonFile(settingPath, true);
|
|
603
|
-
|
|
604
|
-
// Initialize mcpServers section if it doesn't exist
|
|
605
|
-
if (!settings.mcpServers) {
|
|
606
|
-
settings.mcpServers = {};
|
|
607
|
-
}
|
|
608
|
-
|
|
609
|
-
// Special handling for Windows when command is npx for Cline and MSROO clients
|
|
610
|
-
// Use a copy to avoid modifying the passed installConfig directly if needed elsewhere
|
|
611
|
-
const serverConfigForClient = { ...installConfig };
|
|
612
|
-
if (process.platform === 'win32' &&
|
|
613
|
-
serverConfigForClient.command === 'npx' &&
|
|
614
|
-
(clientName === 'Cline' || clientName === 'MSRooCode' || clientName === 'MSROO')) {
|
|
615
|
-
// Update command to cmd
|
|
616
|
-
serverConfigForClient.command = 'cmd';
|
|
617
|
-
|
|
618
|
-
// Add /c and npx at the beginning of args
|
|
619
|
-
serverConfigForClient.args = ['/c', 'npx', ...serverConfigForClient.args];
|
|
620
|
-
|
|
621
|
-
// Add APPDATA environment variable pointing to npm directory
|
|
622
|
-
if (!serverConfigForClient.env) {
|
|
623
|
-
serverConfigForClient.env = {};
|
|
624
|
-
}
|
|
625
|
-
|
|
626
|
-
// Dynamically get npm path and set APPDATA to it
|
|
627
|
-
const npmPath = await this.getNpmPath();
|
|
628
|
-
serverConfigForClient.env['APPDATA'] = npmPath;
|
|
629
|
-
Logger.debug(`Windows npx fix: command=${serverConfigForClient.command}, args=${serverConfigForClient.args.join(' ')}, env=${JSON.stringify(serverConfigForClient.env)}`);
|
|
630
|
-
}
|
|
631
|
-
// Convert backslashes to forward slashes in args paths
|
|
632
|
-
if (serverConfigForClient.args) {
|
|
633
|
-
serverConfigForClient.args = serverConfigForClient.args.map((arg: string) =>
|
|
634
|
-
typeof arg === 'string' ? arg.replace(/\\/g, '/') : arg
|
|
635
|
-
);
|
|
636
|
-
}
|
|
637
|
-
|
|
638
|
-
// Add or update the server configuration
|
|
639
|
-
settings.mcpServers[serverName] = {
|
|
640
|
-
command: serverConfigForClient.command,
|
|
641
|
-
args: serverConfigForClient.args,
|
|
642
|
-
env: serverConfigForClient.env,
|
|
643
|
-
autoApprove: [],
|
|
644
|
-
disabled: false,
|
|
645
|
-
alwaysAllow: []
|
|
646
|
-
};
|
|
647
|
-
Logger.debug(`Updating ${settingPath} for ${serverName}: ${JSON.stringify(settings.mcpServers[serverName])}`);
|
|
648
|
-
|
|
649
|
-
// Write the updated settings back to the file
|
|
650
|
-
await writeJsonFile(settingPath, settings);
|
|
651
|
-
}
|
|
652
|
-
|
|
653
|
-
private async updateGithubCopilotSettings(
|
|
654
|
-
settingPath: string,
|
|
655
|
-
serverName: string,
|
|
656
|
-
installConfig: any // Use the processed installConfig
|
|
657
|
-
): Promise<void> {
|
|
658
|
-
// Read the VS Code settings.json file
|
|
659
|
-
const settings = await readJsonFile(settingPath, true);
|
|
660
|
-
|
|
661
|
-
// Initialize the mcp section if it doesn't exist
|
|
662
|
-
if (!settings.mcp) {
|
|
663
|
-
settings.mcp = {
|
|
664
|
-
servers: {},
|
|
665
|
-
inputs: []
|
|
666
|
-
};
|
|
667
|
-
}
|
|
668
|
-
|
|
669
|
-
if (!settings.mcp.servers) {
|
|
670
|
-
settings.mcp.servers = {};
|
|
671
|
-
}
|
|
672
|
-
|
|
673
|
-
// Use a copy to avoid modifying the passed installConfig directly
|
|
674
|
-
const serverConfigForClient = { ...installConfig };
|
|
675
|
-
|
|
676
|
-
// Convert backslashes to forward slashes in args paths
|
|
677
|
-
if (serverConfigForClient.args) {
|
|
678
|
-
serverConfigForClient.args = serverConfigForClient.args.map((arg: string) =>
|
|
679
|
-
typeof arg === 'string' ? arg.replace(/\\/g, '/') : arg
|
|
680
|
-
);
|
|
681
|
-
}
|
|
682
|
-
|
|
683
|
-
// Add or update the server configuration
|
|
684
|
-
settings.mcp.servers[serverName] = {
|
|
685
|
-
command: serverConfigForClient.command,
|
|
686
|
-
args: serverConfigForClient.args,
|
|
687
|
-
env: serverConfigForClient.env
|
|
688
|
-
};
|
|
689
|
-
Logger.debug(`Updating ${settingPath} for ${serverName}: ${JSON.stringify(settings.mcp.servers[serverName])}`);
|
|
690
|
-
|
|
691
|
-
// Write the updated settings back to the file
|
|
692
|
-
await writeJsonFile(settingPath, settings);
|
|
693
|
-
}
|
|
694
|
-
|
|
201
|
+
/**
|
|
202
|
+
* Install all specified clients
|
|
203
|
+
*/
|
|
695
204
|
async install(options: ServerInstallOptions): Promise<ServerOperationResult> {
|
|
696
205
|
const initialStatuses: OperationStatus[] = [];
|
|
697
206
|
|
|
698
|
-
// Start installation for each client asynchronously
|
|
699
|
-
// Pass options down to installClient
|
|
207
|
+
// Start installation for each client asynchronously
|
|
700
208
|
const installPromises = this.clients.map(async (clientName) => {
|
|
701
|
-
const
|
|
702
|
-
initialStatuses.push(
|
|
703
|
-
return
|
|
209
|
+
const status = await this.installClient(clientName, options);
|
|
210
|
+
initialStatuses.push(status);
|
|
211
|
+
return status;
|
|
704
212
|
});
|
|
705
213
|
|
|
706
|
-
// Wait for all
|
|
214
|
+
// Wait for all installations to complete
|
|
707
215
|
await Promise.all(installPromises);
|
|
708
216
|
|
|
709
|
-
// Return
|
|
217
|
+
// Return result
|
|
710
218
|
return {
|
|
711
219
|
success: true,
|
|
712
|
-
message: 'Client installations
|
|
220
|
+
message: 'Client installations completed',
|
|
713
221
|
status: initialStatuses
|
|
714
222
|
};
|
|
715
223
|
}
|