imcp 0.0.10 → 0.0.12
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/README.md +4 -0
- package/dist/cli/commands/serve.js +2 -1
- package/dist/cli/commands/uninstall.js +14 -6
- package/dist/core/ConfigurationLoader.d.ts +1 -1
- package/dist/core/ConfigurationLoader.js +25 -4
- package/dist/core/ConfigurationProvider.d.ts +2 -1
- package/dist/core/ConfigurationProvider.js +69 -5
- package/dist/core/MCPManager.d.ts +1 -1
- package/dist/core/MCPManager.js +28 -6
- package/dist/core/ServerSchemaLoader.d.ts +11 -0
- package/dist/core/ServerSchemaLoader.js +43 -0
- package/dist/core/ServerSchemaProvider.d.ts +17 -0
- package/dist/core/ServerSchemaProvider.js +115 -0
- package/dist/core/constants.d.ts +11 -1
- package/dist/core/installers/clients/ExtensionInstaller.js +3 -0
- package/dist/core/types.d.ts +6 -0
- package/dist/services/ServerService.d.ts +7 -1
- package/dist/services/ServerService.js +24 -9
- package/dist/utils/osUtils.js +1 -1
- package/dist/web/public/css/detailsWidget.css +110 -0
- package/dist/web/public/css/modal.css +42 -0
- package/dist/web/public/css/serverDetails.css +42 -30
- package/dist/web/public/js/detailsWidget.js +225 -12
- package/dist/web/public/js/modal.js +93 -29
- package/dist/web/public/js/notifications.js +34 -35
- package/dist/web/server.js +13 -2
- package/package.json +1 -1
- package/src/cli/commands/serve.ts +4 -3
- package/src/cli/commands/uninstall.ts +16 -6
- package/src/core/ConfigurationLoader.ts +25 -4
- package/src/core/ConfigurationProvider.ts +75 -6
- package/src/core/MCPManager.ts +34 -9
- package/src/core/constants.ts +12 -2
- package/src/core/installers/clients/ExtensionInstaller.ts +3 -0
- package/src/core/types.ts +7 -1
- package/src/services/ServerService.ts +26 -8
- package/src/utils/osUtils.ts +1 -1
- package/src/web/public/css/modal.css +42 -0
- package/src/web/public/js/modal.js +93 -29
- package/src/web/public/js/notifications.js +34 -35
- package/src/web/server.ts +21 -3
package/src/core/constants.ts
CHANGED
|
@@ -58,8 +58,18 @@ const CODE_INSIDER_STRORAGE_DIR = (() => {
|
|
|
58
58
|
* Value: Client-specific settings or configuration details.
|
|
59
59
|
* TODO: Define actual client settings structure.
|
|
60
60
|
*/
|
|
61
|
-
export const SUPPORTED_CLIENTS: Record<string,
|
|
62
|
-
|
|
61
|
+
export const SUPPORTED_CLIENTS: Record<string, {
|
|
62
|
+
extension: {
|
|
63
|
+
extensionId: string;
|
|
64
|
+
leastVersion?: string;
|
|
65
|
+
repository?: string;
|
|
66
|
+
assetName?: string;
|
|
67
|
+
private?: boolean;
|
|
68
|
+
};
|
|
69
|
+
codeSettingPath: string;
|
|
70
|
+
codeInsiderSettingPath: string;
|
|
71
|
+
}> = {
|
|
72
|
+
'MSRooCode': {
|
|
63
73
|
|
|
64
74
|
extension: {
|
|
65
75
|
extensionId: 'microsoftai.ms-roo-cline',
|
|
@@ -140,6 +140,9 @@ export class ExtensionInstaller {
|
|
|
140
140
|
} else {
|
|
141
141
|
// Install private extension from GitHub release using latest version
|
|
142
142
|
try {
|
|
143
|
+
if (!repository || !assetName) {
|
|
144
|
+
throw new Error(`Missing repository or assetName for private extension ${extensionId}`);
|
|
145
|
+
}
|
|
143
146
|
const { resolvedPath } = await handleGitHubRelease(
|
|
144
147
|
{ name: extensionId, version: 'latest', type: 'extension' },
|
|
145
148
|
{ repository, assetName }
|
package/src/core/types.ts
CHANGED
|
@@ -84,6 +84,7 @@ export interface UpdateRequirementOptions {
|
|
|
84
84
|
|
|
85
85
|
export interface ServerUninstallOptions {
|
|
86
86
|
removeData?: boolean;
|
|
87
|
+
targets?: string[]; // List of client targets to uninstall from
|
|
87
88
|
}
|
|
88
89
|
|
|
89
90
|
// Types related to server feed configuration (e.g., ai-coder-tools.json)
|
|
@@ -150,6 +151,11 @@ export interface FeedConfiguration {
|
|
|
150
151
|
mcpServers: McpConfig[];
|
|
151
152
|
}
|
|
152
153
|
|
|
154
|
+
export interface ClientSettings {
|
|
155
|
+
codeSettingPath: string;
|
|
156
|
+
codeInsiderSettingPath: string;
|
|
157
|
+
}
|
|
158
|
+
|
|
153
159
|
// Events that can be emitted by the SDK
|
|
154
160
|
export enum MCPEvent {
|
|
155
161
|
SERVER_INSTALLED = 'server:installed',
|
|
@@ -161,7 +167,7 @@ export enum MCPEvent {
|
|
|
161
167
|
|
|
162
168
|
export interface MCPEventData {
|
|
163
169
|
[MCPEvent.SERVER_INSTALLED]: { server: MCPServerCategory };
|
|
164
|
-
[MCPEvent.SERVER_UNINSTALLED]: { serverName: string };
|
|
170
|
+
[MCPEvent.SERVER_UNINSTALLED]: { serverName: string; targets?: string[] };
|
|
165
171
|
[MCPEvent.SERVER_STARTED]: { server: MCPServerCategory };
|
|
166
172
|
[MCPEvent.SERVER_STOPPED]: { serverName: string };
|
|
167
173
|
[MCPEvent.CONFIG_CHANGED]: { configuration: MCPConfiguration };
|
|
@@ -147,19 +147,34 @@ export class ServerService {
|
|
|
147
147
|
* Installs a specific mcp tool for a server.
|
|
148
148
|
* TODO: This might require enhancing MCPManager to handle category-specific installs.
|
|
149
149
|
*/
|
|
150
|
+
/**
|
|
151
|
+
* Uninstall MCP server from specified client targets
|
|
152
|
+
* @param category The server category
|
|
153
|
+
* @param serverName The server name to uninstall
|
|
154
|
+
* @param options Uninstall options including target clients and data removal flags
|
|
155
|
+
*/
|
|
150
156
|
async uninstallMcpServer(
|
|
151
157
|
category: string,
|
|
152
158
|
serverName: string,
|
|
153
|
-
options: ServerUninstallOptions = {}
|
|
159
|
+
options: ServerUninstallOptions = {}
|
|
154
160
|
): Promise<ServerOperationResult> {
|
|
155
|
-
|
|
161
|
+
Logger.debug(`Uninstalling MCP server: ${JSON.stringify({ category, serverName, options })}`);
|
|
162
|
+
try {
|
|
163
|
+
const result = await mcpManager.uninstallServer(category, serverName, options);
|
|
164
|
+
Logger.debug(`Uninstallation result: ${JSON.stringify(result)}`);
|
|
165
|
+
return result;
|
|
166
|
+
} catch (error) {
|
|
167
|
+
Logger.error(`Failed to uninstall MCP server: ${serverName}`, error);
|
|
168
|
+
throw error;
|
|
169
|
+
}
|
|
156
170
|
}
|
|
157
171
|
|
|
158
172
|
/**
|
|
159
173
|
* Validates server names
|
|
160
174
|
*/
|
|
161
|
-
async validateServerName(category: string,
|
|
162
|
-
|
|
175
|
+
async validateServerName(category: string, names: string | string[]): Promise<boolean> {
|
|
176
|
+
const serverNames = Array.isArray(names) ? names : [names];
|
|
177
|
+
Logger.debug(`Validating server names: ${JSON.stringify({ category, names: serverNames })}`);
|
|
163
178
|
|
|
164
179
|
// Check if category exists in feeds
|
|
165
180
|
const feedConfig = await mcpManager.getFeedConfiguration(category);
|
|
@@ -168,10 +183,13 @@ export class ServerService {
|
|
|
168
183
|
return false;
|
|
169
184
|
}
|
|
170
185
|
|
|
171
|
-
// Check if
|
|
172
|
-
const
|
|
173
|
-
|
|
174
|
-
|
|
186
|
+
// Check if all servers exist in the category's mcpServers
|
|
187
|
+
const invalidServers = serverNames.filter(name =>
|
|
188
|
+
!feedConfig.mcpServers.some(server => server.name === name)
|
|
189
|
+
);
|
|
190
|
+
|
|
191
|
+
if (invalidServers.length > 0) {
|
|
192
|
+
Logger.debug(`Validation failed: Servers "${invalidServers.join(', ')}" not found in category "${category}"`);
|
|
175
193
|
return false;
|
|
176
194
|
}
|
|
177
195
|
|
package/src/utils/osUtils.ts
CHANGED
|
@@ -273,7 +273,7 @@ export function getPythonPackagePath(pythonExecutablePath: string): string {
|
|
|
273
273
|
// Virtual environment structure on Windows: <venv>/Scripts/python.exe
|
|
274
274
|
const venvRoot = path.dirname(dir);
|
|
275
275
|
return path.join(venvRoot, 'Lib', 'site-packages');
|
|
276
|
-
} else
|
|
276
|
+
} else {
|
|
277
277
|
// System Python or Conda on Windows
|
|
278
278
|
return path.join(dir, 'Lib', 'site-packages');
|
|
279
279
|
}
|
|
@@ -160,6 +160,15 @@ body {
|
|
|
160
160
|
transition: all 0.2s ease;
|
|
161
161
|
cursor: pointer;
|
|
162
162
|
user-select: none;
|
|
163
|
+
gap: 0.5rem;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/* Client actions container */
|
|
167
|
+
.client-actions {
|
|
168
|
+
display: flex;
|
|
169
|
+
align-items: center;
|
|
170
|
+
gap: 0.5rem;
|
|
171
|
+
margin-left: auto;
|
|
163
172
|
}
|
|
164
173
|
|
|
165
174
|
.client-item:hover {
|
|
@@ -180,6 +189,14 @@ body {
|
|
|
180
189
|
gap: 0.75rem;
|
|
181
190
|
}
|
|
182
191
|
|
|
192
|
+
/* Status container */
|
|
193
|
+
.status-container {
|
|
194
|
+
display: flex;
|
|
195
|
+
align-items: center;
|
|
196
|
+
gap: 0.5rem;
|
|
197
|
+
margin-left: auto;
|
|
198
|
+
}
|
|
199
|
+
|
|
183
200
|
/* Status badges */
|
|
184
201
|
.status-badge {
|
|
185
202
|
display: inline-flex;
|
|
@@ -227,6 +244,31 @@ body {
|
|
|
227
244
|
background-color: #fef3c7;
|
|
228
245
|
}
|
|
229
246
|
|
|
247
|
+
/* Uninstall button styling */
|
|
248
|
+
.uninstall-btn {
|
|
249
|
+
display: inline-flex;
|
|
250
|
+
align-items: center;
|
|
251
|
+
justify-content: center;
|
|
252
|
+
width: 28px;
|
|
253
|
+
height: 28px;
|
|
254
|
+
border-radius: 6px;
|
|
255
|
+
border: 1px solid transparent;
|
|
256
|
+
background: transparent;
|
|
257
|
+
cursor: pointer;
|
|
258
|
+
transition: all 0.2s ease;
|
|
259
|
+
padding: 0;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
.uninstall-btn:hover {
|
|
263
|
+
background-color: #fee2e2;
|
|
264
|
+
border-color: #ef4444;
|
|
265
|
+
transform: scale(1.05);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
.uninstall-btn i {
|
|
269
|
+
font-size: 1.25rem;
|
|
270
|
+
}
|
|
271
|
+
|
|
230
272
|
/* Environment variables section */
|
|
231
273
|
#modalEnvInputs input {
|
|
232
274
|
width: 100%;
|
|
@@ -141,14 +141,16 @@ function _appendInstallLoadingMessage(message) {
|
|
|
141
141
|
/**
|
|
142
142
|
* Display the installation loading modal and prepare it for messages.
|
|
143
143
|
*/
|
|
144
|
-
function showInstallLoadingModal() {
|
|
144
|
+
function showInstallLoadingModal(operation = 'Installing') {
|
|
145
145
|
const loadingModal = document.getElementById('installLoadingModal');
|
|
146
146
|
const loadingMsg = document.getElementById('installLoadingMessage');
|
|
147
|
-
|
|
147
|
+
const loadingTitle = document.querySelector('.loading-title');
|
|
148
|
+
if (loadingModal && loadingMsg && loadingTitle) {
|
|
148
149
|
loadingModal.style.display = 'block';
|
|
149
150
|
loadingMsg.innerHTML = '';
|
|
151
|
+
loadingTitle.textContent = `${operation}...`;
|
|
150
152
|
} else {
|
|
151
|
-
console.error('[LoadingModal] Required elements not found: installLoadingModal or
|
|
153
|
+
console.error('[LoadingModal] Required elements not found: installLoadingModal, installLoadingMessage, or loading-title');
|
|
152
154
|
}
|
|
153
155
|
}
|
|
154
156
|
|
|
@@ -330,17 +332,55 @@ async function showInstallModal(categoryName, serverName, callback) {
|
|
|
330
332
|
// Add elements to client info
|
|
331
333
|
clientInfo.appendChild(clientName);
|
|
332
334
|
|
|
333
|
-
// Add
|
|
335
|
+
// Add elements to client item
|
|
334
336
|
clientItem.appendChild(clientInfo);
|
|
335
337
|
|
|
338
|
+
// Status container for badge and uninstall button
|
|
339
|
+
const statusContainer = document.createElement('div');
|
|
340
|
+
statusContainer.className = 'status-container';
|
|
341
|
+
|
|
336
342
|
// Status badge - only show if we have status text
|
|
337
343
|
if (statusText) {
|
|
338
344
|
const statusBadge = document.createElement('span');
|
|
339
345
|
statusBadge.className = `status-badge ${statusClass}`;
|
|
340
346
|
statusBadge.textContent = statusText;
|
|
341
|
-
|
|
347
|
+
statusContainer.appendChild(statusBadge);
|
|
348
|
+
|
|
349
|
+
// Add uninstall button right after status badge if installed
|
|
350
|
+
if (operationStatus.status === 'completed' && operationStatus.type === 'install') {
|
|
351
|
+
const uninstallBtn = document.createElement('button');
|
|
352
|
+
uninstallBtn.className = 'uninstall-btn text-red-600 hover:text-red-800 ml-2';
|
|
353
|
+
uninstallBtn.innerHTML = '<i class="bx bx-trash"></i>';
|
|
354
|
+
uninstallBtn.title = 'Uninstall from this client';
|
|
355
|
+
uninstallBtn.onclick = async (e) => {
|
|
356
|
+
e.stopPropagation(); // Prevent item selection
|
|
357
|
+
e.preventDefault(); // Prevent form submission
|
|
358
|
+
const confirmed = await showConfirm('Uninstall Confirmation', `Are you sure you want to uninstall ${serverName} from ${target}?`);
|
|
359
|
+
if (confirmed) {
|
|
360
|
+
// Add target to selectedClients for uninstallation
|
|
361
|
+
window.selectedClients = [target];
|
|
362
|
+
showInstallLoadingModal('Uninstalling');
|
|
363
|
+
const serverList = {
|
|
364
|
+
[serverName]: {
|
|
365
|
+
removeData: true // Include removal of associated data
|
|
366
|
+
}
|
|
367
|
+
};
|
|
368
|
+
try {
|
|
369
|
+
delayedAppendInstallLoadingMessage(`Uninstalling ${serverName} from ${target}...`);
|
|
370
|
+
await uninstallTools(categoryName, serverList, [target]);
|
|
371
|
+
} catch (error) {
|
|
372
|
+
delayedAppendInstallLoadingMessage(`Error: ${error.message}`);
|
|
373
|
+
throw error; // Re-throw to trigger error handling in uninstallTools
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
return false; // Prevent form submission
|
|
377
|
+
};
|
|
378
|
+
statusContainer.appendChild(uninstallBtn);
|
|
379
|
+
}
|
|
342
380
|
}
|
|
343
381
|
|
|
382
|
+
clientItem.appendChild(statusContainer);
|
|
383
|
+
|
|
344
384
|
// Add client item to target div
|
|
345
385
|
targetDiv.appendChild(clientItem);
|
|
346
386
|
});
|
|
@@ -641,34 +681,40 @@ async function showInstallModal(categoryName, serverName, callback) {
|
|
|
641
681
|
});
|
|
642
682
|
});
|
|
643
683
|
|
|
644
|
-
// Get selected clients
|
|
645
|
-
const selectedTargets = window.selectedClients.length > 0 ?
|
|
646
|
-
window.selectedClients :
|
|
647
|
-
Array.from(document.querySelectorAll('.client-item.selected'))
|
|
648
|
-
.map(item => item.dataset.target);
|
|
649
|
-
|
|
650
684
|
// Check if we have any requirements selected for update
|
|
651
685
|
const hasRequirementsToUpdate = requirementsToUpdate.length > 0;
|
|
652
686
|
|
|
653
|
-
// Only
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
687
|
+
// Only proceed if this isn't an uninstall operation
|
|
688
|
+
const uninstallBtn = document.querySelector('.uninstall-btn');
|
|
689
|
+
if (!uninstallBtn || !uninstallBtn.matches(':active')) {
|
|
690
|
+
// Get selected clients
|
|
691
|
+
const selectedTargets = window.selectedClients.length > 0 ?
|
|
692
|
+
window.selectedClients :
|
|
693
|
+
Array.from(document.querySelectorAll('.client-item.selected'))
|
|
694
|
+
.map(item => item.dataset.target);
|
|
695
|
+
|
|
696
|
+
console.log('Selected targets:', selectedTargets);
|
|
697
|
+
console.log('Requirements to update:', requirementsToUpdate);
|
|
698
|
+
if (selectedTargets.length === 0 && !hasRequirementsToUpdate) {
|
|
699
|
+
showToast('Please select at least one client to configure.', 'error');
|
|
700
|
+
return;
|
|
701
|
+
}
|
|
702
|
+
window.selectedClients = selectedTargets;
|
|
657
703
|
}
|
|
658
704
|
|
|
659
705
|
// Call install function with selected targets
|
|
660
706
|
// Find installing message for the first selected target
|
|
661
707
|
let installingMessage = "Starting installation...";
|
|
662
708
|
const serverStatus = serverStatuses[serverName] || { installedStatus: {} };
|
|
663
|
-
if (
|
|
664
|
-
const target =
|
|
709
|
+
if (window.selectedClients.length > 0) {
|
|
710
|
+
const target = window.selectedClients[0];
|
|
665
711
|
const msg = serverStatus.installedStatus?.[target]?.message;
|
|
666
712
|
if (msg) installingMessage = msg;
|
|
667
713
|
}
|
|
668
714
|
|
|
669
715
|
// Add requirements to update to serverInstallOptions if any
|
|
670
716
|
const serverInstallOptions = {
|
|
671
|
-
targetClients:
|
|
717
|
+
targetClients: window.selectedClients,
|
|
672
718
|
env: envVars,
|
|
673
719
|
args: args,
|
|
674
720
|
settings: pythonEnv ? { pythonEnv } : undefined
|
|
@@ -679,7 +725,9 @@ async function showInstallModal(categoryName, serverName, callback) {
|
|
|
679
725
|
serverInstallOptions.requirements = requirementsToUpdate;
|
|
680
726
|
}
|
|
681
727
|
|
|
682
|
-
|
|
728
|
+
// For installation, use the selectedTargets from the validation above
|
|
729
|
+
const targetsToUse = document.querySelector('.uninstall-btn:hover') ? [] : window.selectedClients;
|
|
730
|
+
handleBulkClientInstall(categoryName, serverName, targetsToUse, envVars, installingMessage, serverData, serverInstallOptions);
|
|
683
731
|
};
|
|
684
732
|
|
|
685
733
|
} catch (error) {
|
|
@@ -893,23 +941,35 @@ async function pollInstallStatus(categoryName, serverName, targets, interval = 2
|
|
|
893
941
|
|
|
894
942
|
// Function to handle client uninstallation for multiple targets
|
|
895
943
|
async function uninstallTools(categoryName, serverList, targets) {
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
944
|
+
// Get selected targets from window.selectedClients or the provided targets
|
|
945
|
+
const selectedTargets = window.selectedClients || (Array.isArray(targets) ? targets : [targets]);
|
|
946
|
+
|
|
947
|
+
// Validate selected targets
|
|
948
|
+
if (!selectedTargets || selectedTargets.length === 0) {
|
|
949
|
+
showToast('Please select at least one client to uninstall.', 'error');
|
|
902
950
|
return;
|
|
903
951
|
}
|
|
904
952
|
|
|
905
953
|
try {
|
|
954
|
+
delayedAppendInstallLoadingMessage('Starting uninstallation...');
|
|
955
|
+
|
|
956
|
+
// Ensure serverList is an object where each key is a server name
|
|
957
|
+
if (Array.isArray(serverList)) {
|
|
958
|
+
const formattedServerList = {};
|
|
959
|
+
serverList.forEach(server => {
|
|
960
|
+
formattedServerList[server] = { removeData: true };
|
|
961
|
+
});
|
|
962
|
+
serverList = formattedServerList;
|
|
963
|
+
}
|
|
964
|
+
|
|
906
965
|
const response = await fetch(`/api/categories/${categoryName}/uninstall`, {
|
|
907
966
|
method: 'POST',
|
|
908
967
|
headers: { 'Content-Type': 'application/json' },
|
|
909
968
|
body: JSON.stringify({
|
|
910
|
-
|
|
969
|
+
serverList: serverList,
|
|
911
970
|
options: {
|
|
912
|
-
targets:
|
|
971
|
+
targets: selectedTargets,
|
|
972
|
+
removeData: true
|
|
913
973
|
}
|
|
914
974
|
})
|
|
915
975
|
});
|
|
@@ -924,10 +984,14 @@ async function uninstallTools(categoryName, serverList, targets) {
|
|
|
924
984
|
throw new Error(result.error || 'Uninstallation failed');
|
|
925
985
|
}
|
|
926
986
|
|
|
927
|
-
|
|
928
|
-
|
|
987
|
+
// Add completion message and trigger completion UI
|
|
988
|
+
delayedAppendInstallLoadingMessage(`Successfully uninstalled from ${selectedTargets.join(', ')}`, true);
|
|
989
|
+
|
|
990
|
+
// Clear selected clients after successful uninstall
|
|
991
|
+
window.selectedClients = [];
|
|
929
992
|
} catch (error) {
|
|
930
993
|
console.error('Error uninstalling tools:', error);
|
|
994
|
+
delayedAppendInstallLoadingMessage(`Error: ${error.message}`, true);
|
|
931
995
|
showToast(`Error uninstalling tools: ${error.message}`, 'error');
|
|
932
996
|
}
|
|
933
997
|
}
|
|
@@ -30,28 +30,26 @@ function showToast(message, type = 'success') {
|
|
|
30
30
|
});
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
-
// Function to show a
|
|
34
|
-
function showConfirm(message) {
|
|
33
|
+
// Function to show a confirmation dialog using custom modal system
|
|
34
|
+
function showConfirm(title, message) {
|
|
35
35
|
return new Promise((resolve) => {
|
|
36
36
|
const modalId = `confirm-modal-${Date.now()}`;
|
|
37
37
|
const modalHtml = `
|
|
38
|
-
<div
|
|
39
|
-
<div class="modal-
|
|
40
|
-
<div class="modal-
|
|
41
|
-
<
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
</button>
|
|
54
|
-
</div>
|
|
38
|
+
<div id="${modalId}" class="modal">
|
|
39
|
+
<div class="modal-content" style="max-width: 400px; margin: 15% auto;">
|
|
40
|
+
<div class="modal-header">
|
|
41
|
+
<h3 class="text-lg font-semibold text-gray-700">${title}</h3>
|
|
42
|
+
</div>
|
|
43
|
+
<div style="margin: 1rem 0;">
|
|
44
|
+
${message}
|
|
45
|
+
</div>
|
|
46
|
+
<div style="display: flex; justify-content: flex-end; gap: 0.5rem; margin-top: 1rem;">
|
|
47
|
+
<button type="button" class="cancel-button px-4 py-2 text-gray-600 hover:text-gray-800 font-medium rounded-lg hover:bg-gray-100 transition-colors">
|
|
48
|
+
Cancel
|
|
49
|
+
</button>
|
|
50
|
+
<button type="button" class="confirm-button submit-button">
|
|
51
|
+
Confirm
|
|
52
|
+
</button>
|
|
55
53
|
</div>
|
|
56
54
|
</div>
|
|
57
55
|
</div>
|
|
@@ -60,29 +58,30 @@ function showConfirm(message) {
|
|
|
60
58
|
document.body.insertAdjacentHTML('beforeend', modalHtml);
|
|
61
59
|
|
|
62
60
|
const modalElement = document.getElementById(modalId);
|
|
63
|
-
|
|
61
|
+
modalElement.style.display = 'block';
|
|
64
62
|
|
|
65
63
|
// Handle confirm button click
|
|
66
|
-
modalElement.querySelector('.confirm-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
spinner.classList.remove('d-none');
|
|
71
|
-
|
|
72
|
-
// Add small delay to show the loading state
|
|
73
|
-
setTimeout(() => {
|
|
74
|
-
modal.hide();
|
|
75
|
-
resolve(true);
|
|
76
|
-
}, 300);
|
|
64
|
+
modalElement.querySelector('.confirm-button').addEventListener('click', () => {
|
|
65
|
+
modalElement.style.display = 'none';
|
|
66
|
+
modalElement.remove();
|
|
67
|
+
resolve(true);
|
|
77
68
|
});
|
|
78
69
|
|
|
79
|
-
// Handle
|
|
80
|
-
modalElement.
|
|
70
|
+
// Handle cancel button click
|
|
71
|
+
modalElement.querySelector('.cancel-button').addEventListener('click', () => {
|
|
72
|
+
modalElement.style.display = 'none';
|
|
81
73
|
modalElement.remove();
|
|
82
74
|
resolve(false);
|
|
83
75
|
});
|
|
84
|
-
|
|
85
|
-
modal
|
|
76
|
+
|
|
77
|
+
// Handle click outside modal
|
|
78
|
+
modalElement.addEventListener('click', (event) => {
|
|
79
|
+
if (event.target === modalElement) {
|
|
80
|
+
modalElement.style.display = 'none';
|
|
81
|
+
modalElement.remove();
|
|
82
|
+
resolve(false);
|
|
83
|
+
}
|
|
84
|
+
});
|
|
86
85
|
});
|
|
87
86
|
}
|
|
88
87
|
|
package/src/web/server.ts
CHANGED
|
@@ -32,7 +32,11 @@ export interface InstallServersRequestBody {
|
|
|
32
32
|
}
|
|
33
33
|
|
|
34
34
|
interface UninstallServersRequestBody {
|
|
35
|
-
serverList: string
|
|
35
|
+
serverList: Record<string, { removeData?: boolean }>;
|
|
36
|
+
options: {
|
|
37
|
+
targets: string[];
|
|
38
|
+
removeData?: boolean;
|
|
39
|
+
};
|
|
36
40
|
}
|
|
37
41
|
|
|
38
42
|
interface ApiResponse<T> {
|
|
@@ -155,15 +159,29 @@ app.post('/api/categories/:categoryName/uninstall', async (req: Request<{ catego
|
|
|
155
159
|
const { categoryName } = req.params;
|
|
156
160
|
const { serverList } = req.body;
|
|
157
161
|
|
|
158
|
-
if (!
|
|
162
|
+
if (!serverList || Object.keys(serverList).length === 0) {
|
|
159
163
|
return res.status(400).json({
|
|
160
164
|
success: false,
|
|
161
165
|
error: 'Invalid tool list provided'
|
|
162
166
|
});
|
|
163
167
|
}
|
|
164
168
|
|
|
169
|
+
const { options } = req.body;
|
|
170
|
+
if (!options?.targets || options.targets.length === 0) {
|
|
171
|
+
return res.status(400).json({
|
|
172
|
+
success: false,
|
|
173
|
+
error: 'No target clients specified'
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
|
|
165
177
|
const results = await Promise.all(
|
|
166
|
-
serverList.map(serverName
|
|
178
|
+
Object.entries(serverList).map(([serverName, serverOptions]) =>
|
|
179
|
+
serverService.uninstallMcpServer(categoryName, serverName, {
|
|
180
|
+
...serverOptions,
|
|
181
|
+
targets: options.targets,
|
|
182
|
+
removeData: options.removeData ?? serverOptions.removeData
|
|
183
|
+
})
|
|
184
|
+
)
|
|
167
185
|
);
|
|
168
186
|
|
|
169
187
|
const { success, messages } = serverService.formatOperationResults(results);
|