imcp 0.0.3 → 0.0.5
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 +5 -6
- package/dist/cli/commands/install.js +2 -0
- package/dist/cli/commands/list.js +1 -0
- package/dist/cli/index.js +1 -2
- package/dist/core/ConfigurationLoader.d.ts +32 -0
- package/dist/core/ConfigurationLoader.js +213 -0
- package/dist/core/ConfigurationProvider.d.ts +2 -3
- package/dist/core/ConfigurationProvider.js +13 -182
- package/dist/core/InstallationService.d.ts +8 -0
- package/dist/core/InstallationService.js +124 -96
- package/dist/core/RequirementService.d.ts +1 -1
- package/dist/core/RequirementService.js +5 -9
- package/dist/core/constants.js +14 -1
- package/dist/core/installers/BaseInstaller.d.ts +5 -4
- package/dist/core/installers/BaseInstaller.js +17 -28
- package/dist/core/installers/ClientInstaller.js +159 -39
- package/dist/core/installers/CommandInstaller.d.ts +1 -0
- package/dist/core/installers/CommandInstaller.js +3 -0
- package/dist/core/installers/GeneralInstaller.d.ts +1 -0
- package/dist/core/installers/GeneralInstaller.js +3 -0
- package/dist/core/installers/InstallerFactory.d.ts +9 -7
- package/dist/core/installers/InstallerFactory.js +10 -8
- package/dist/core/installers/NpmInstaller.d.ts +1 -0
- package/dist/core/installers/NpmInstaller.js +3 -0
- package/dist/core/installers/PipInstaller.d.ts +6 -3
- package/dist/core/installers/PipInstaller.js +21 -8
- package/dist/core/installers/RequirementInstaller.d.ts +4 -3
- package/dist/core/installers/clients/ClientInstaller.d.ts +23 -0
- package/dist/core/installers/clients/ClientInstaller.js +573 -0
- package/dist/core/installers/clients/ExtensionInstaller.d.ts +26 -0
- package/dist/core/installers/clients/ExtensionInstaller.js +149 -0
- package/dist/core/installers/index.d.ts +8 -6
- package/dist/core/installers/index.js +8 -6
- package/dist/core/installers/requirements/BaseInstaller.d.ts +59 -0
- package/dist/core/installers/requirements/BaseInstaller.js +168 -0
- package/dist/core/installers/requirements/CommandInstaller.d.ts +37 -0
- package/dist/core/installers/requirements/CommandInstaller.js +173 -0
- package/dist/core/installers/requirements/GeneralInstaller.d.ts +33 -0
- package/dist/core/installers/requirements/GeneralInstaller.js +86 -0
- package/dist/core/installers/requirements/InstallerFactory.d.ts +54 -0
- package/dist/core/installers/requirements/InstallerFactory.js +97 -0
- package/dist/core/installers/requirements/NpmInstaller.d.ts +26 -0
- package/dist/core/installers/requirements/NpmInstaller.js +128 -0
- package/dist/core/installers/requirements/PipInstaller.d.ts +28 -0
- package/dist/core/installers/requirements/PipInstaller.js +128 -0
- package/{src/core/installers/RequirementInstaller.ts → dist/core/installers/requirements/RequirementInstaller.d.ts} +33 -38
- package/dist/core/installers/requirements/RequirementInstaller.js +3 -0
- package/dist/core/types.d.ts +4 -1
- package/dist/services/ServerService.js +1 -1
- package/dist/utils/clientUtils.d.ts +0 -6
- package/dist/utils/clientUtils.js +3 -2
- package/dist/utils/githubUtils.d.ts +11 -0
- package/dist/utils/githubUtils.js +88 -0
- package/dist/utils/osUtils.d.ts +17 -0
- package/dist/utils/osUtils.js +184 -0
- package/dist/web/public/css/modal.css +97 -3
- package/dist/web/public/index.html +21 -2
- package/dist/web/public/js/modal.js +177 -28
- package/dist/web/public/js/serverCategoryDetails.js +12 -10
- package/dist/web/public/js/serverCategoryList.js +20 -5
- package/dist/web/public/modal.html +27 -13
- package/dist/web/server.js +1 -1
- package/package.json +2 -1
- package/src/cli/commands/install.ts +4 -2
- package/src/cli/commands/list.ts +1 -0
- package/src/cli/index.ts +1 -1
- package/src/core/ConfigurationLoader.ts +251 -0
- package/src/core/ConfigurationProvider.ts +13 -195
- package/src/core/InstallationService.ts +140 -106
- package/src/core/RequirementService.ts +5 -10
- package/src/core/constants.ts +15 -1
- package/src/core/installers/{ClientInstaller.ts → clients/ClientInstaller.ts} +185 -46
- package/src/core/installers/clients/ExtensionInstaller.ts +162 -0
- package/src/core/installers/index.ts +9 -7
- package/src/core/installers/{BaseInstaller.ts → requirements/BaseInstaller.ts} +10 -118
- package/src/core/installers/{CommandInstaller.ts → requirements/CommandInstaller.ts} +7 -3
- package/src/core/installers/{GeneralInstaller.ts → requirements/GeneralInstaller.ts} +6 -2
- package/src/core/installers/{InstallerFactory.ts → requirements/InstallerFactory.ts} +11 -9
- package/src/core/installers/{NpmInstaller.ts → requirements/NpmInstaller.ts} +7 -4
- package/src/core/installers/{PipInstaller.ts → requirements/PipInstaller.ts} +26 -10
- package/src/core/installers/requirements/RequirementInstaller.ts +41 -0
- package/src/core/types.ts +4 -1
- package/src/services/ServerService.ts +1 -1
- package/src/utils/clientUtils.ts +4 -2
- package/src/utils/githubUtils.ts +103 -0
- package/src/utils/osUtils.ts +206 -15
- package/src/web/public/css/modal.css +97 -3
- package/src/web/public/index.html +21 -2
- package/src/web/public/js/modal.js +177 -28
- package/src/web/public/js/serverCategoryDetails.js +12 -10
- package/src/web/public/js/serverCategoryList.js +20 -5
- package/src/web/public/modal.html +27 -13
- package/src/web/server.ts +1 -1
|
@@ -169,8 +169,17 @@ function hideInstallLoadingModal() {
|
|
|
169
169
|
const loadingModal = document.getElementById('installLoadingModal');
|
|
170
170
|
if (loadingModal) {
|
|
171
171
|
loadingModal.style.display = 'none';
|
|
172
|
+
|
|
173
|
+
// Get the last selected category
|
|
174
|
+
const lastSelected = localStorage.getItem('lastSelectedCategory');
|
|
175
|
+
|
|
176
|
+
// Refresh page while maintaining category selection
|
|
172
177
|
setTimeout(() => {
|
|
173
|
-
|
|
178
|
+
if (lastSelected) {
|
|
179
|
+
window.location.href = window.location.pathname + '?category=' + encodeURIComponent(lastSelected);
|
|
180
|
+
} else {
|
|
181
|
+
location.reload();
|
|
182
|
+
}
|
|
174
183
|
}, 100);
|
|
175
184
|
} else {
|
|
176
185
|
console.error('[LoadingModal] loading modal DOM not found');
|
|
@@ -194,12 +203,13 @@ async function showInstallModal(categoryName, serverName, callback) {
|
|
|
194
203
|
const envInputsDiv = modal.querySelector('#modalEnvInputs');
|
|
195
204
|
const targetDiv = modal.querySelector('#modalTargets');
|
|
196
205
|
const modalRequirements = modal.querySelector('#modalRequirements');
|
|
206
|
+
const modalArguments = modal.querySelector('#modalArguments');
|
|
197
207
|
|
|
198
208
|
// Global array to track selected clients
|
|
199
209
|
window.selectedClients = [];
|
|
200
210
|
|
|
201
211
|
// Verify all required modal elements exist
|
|
202
|
-
if (!title || !envInputsDiv || !targetDiv || !modalRequirements) {
|
|
212
|
+
if (!title || !envInputsDiv || !targetDiv || !modalRequirements || !modalArguments) {
|
|
203
213
|
console.error('Required modal elements not found');
|
|
204
214
|
return;
|
|
205
215
|
}
|
|
@@ -208,6 +218,7 @@ async function showInstallModal(categoryName, serverName, callback) {
|
|
|
208
218
|
envInputsDiv.innerHTML = ''; // Clear previous inputs
|
|
209
219
|
targetDiv.innerHTML = ''; // Clear previous targets
|
|
210
220
|
modalRequirements.innerHTML = ''; // Clear previous requirements
|
|
221
|
+
modalArguments.innerHTML = ''; // Clear previous arguments
|
|
211
222
|
|
|
212
223
|
try {
|
|
213
224
|
// Fetch both targets and server data simultaneously
|
|
@@ -353,6 +364,106 @@ async function showInstallModal(categoryName, serverName, callback) {
|
|
|
353
364
|
envInputsDiv.insertBefore(envTitle, envInputsDiv.firstChild);
|
|
354
365
|
}
|
|
355
366
|
|
|
367
|
+
// Handle installation arguments section
|
|
368
|
+
const installation = mcpServer.installation;
|
|
369
|
+
if (installation) {
|
|
370
|
+
// Add section title
|
|
371
|
+
const argsTitle = document.createElement('h3');
|
|
372
|
+
argsTitle.className = 'section-title text-lg font-semibold text-gray-700 mb-3';
|
|
373
|
+
argsTitle.textContent = 'Arguments';
|
|
374
|
+
modalArguments.appendChild(argsTitle);
|
|
375
|
+
|
|
376
|
+
const argsContainer = document.createElement('div');
|
|
377
|
+
argsContainer.className = 'args-container mb-3';
|
|
378
|
+
|
|
379
|
+
// Add button to add new argument
|
|
380
|
+
const addButton = document.createElement('button');
|
|
381
|
+
addButton.type = 'button';
|
|
382
|
+
addButton.className = 'add-arg-button px-3 py-1 text-sm text-blue-600 hover:text-blue-800 border border-blue-600 hover:border-blue-800 rounded-md mb-2';
|
|
383
|
+
addButton.innerHTML = '<i class="bx bx-plus"></i> Add Argument';
|
|
384
|
+
argsContainer.appendChild(addButton);
|
|
385
|
+
|
|
386
|
+
// Function to create new argument input
|
|
387
|
+
const createArgInput = (value = '') => {
|
|
388
|
+
const argWrapper = document.createElement('div');
|
|
389
|
+
argWrapper.className = 'arg-wrapper flex items-center gap-2 mb-2';
|
|
390
|
+
|
|
391
|
+
const input = document.createElement('input');
|
|
392
|
+
input.type = 'text';
|
|
393
|
+
input.className = 'arg-input flex-grow';
|
|
394
|
+
input.value = value;
|
|
395
|
+
input.placeholder = 'Enter argument value';
|
|
396
|
+
|
|
397
|
+
const removeButton = document.createElement('button');
|
|
398
|
+
removeButton.type = 'button';
|
|
399
|
+
removeButton.className = 'remove-arg-button text-red-600 hover:text-red-800';
|
|
400
|
+
removeButton.innerHTML = '<i class="bx bx-trash"></i>';
|
|
401
|
+
removeButton.onclick = () => argWrapper.remove();
|
|
402
|
+
|
|
403
|
+
argWrapper.appendChild(input);
|
|
404
|
+
argWrapper.appendChild(removeButton);
|
|
405
|
+
return argWrapper;
|
|
406
|
+
};
|
|
407
|
+
|
|
408
|
+
// Add click handler for add button
|
|
409
|
+
addButton.onclick = () => {
|
|
410
|
+
argsContainer.appendChild(createArgInput());
|
|
411
|
+
};
|
|
412
|
+
|
|
413
|
+
// Create wrapper for args section
|
|
414
|
+
const argsWrapper = document.createElement('div');
|
|
415
|
+
argsWrapper.className = 'mb-3';
|
|
416
|
+
|
|
417
|
+
// Set default values from installation args if available
|
|
418
|
+
if (installation.args && Array.isArray(installation.args)) {
|
|
419
|
+
installation.args.forEach(arg => {
|
|
420
|
+
argsContainer.appendChild(createArgInput(arg));
|
|
421
|
+
});
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
// Add initial empty input if no args
|
|
425
|
+
if (!installation.args || installation.args.length === 0) {
|
|
426
|
+
argsContainer.appendChild(createArgInput());
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
argsWrapper.appendChild(argsContainer);
|
|
430
|
+
|
|
431
|
+
// Add description
|
|
432
|
+
const description = document.createElement('p');
|
|
433
|
+
description.className = 'text-xs text-gray-500 mt-1';
|
|
434
|
+
description.textContent = 'Specify installation arguments, one per line. These will be used when installing the server.';
|
|
435
|
+
argsWrapper.appendChild(description);
|
|
436
|
+
|
|
437
|
+
modalArguments.appendChild(argsWrapper);
|
|
438
|
+
|
|
439
|
+
// If command type is python, add python environment input
|
|
440
|
+
if (installation.command === 'python' || installation.command.includes('python')) {
|
|
441
|
+
const pythonEnvWrapper = document.createElement('div');
|
|
442
|
+
pythonEnvWrapper.className = 'mt-4';
|
|
443
|
+
|
|
444
|
+
const pythonEnvLabel = document.createElement('label');
|
|
445
|
+
pythonEnvLabel.htmlFor = 'python_env';
|
|
446
|
+
pythonEnvLabel.className = 'block text-sm font-medium text-gray-700 mb-1';
|
|
447
|
+
pythonEnvLabel.textContent = 'Python Environment';
|
|
448
|
+
|
|
449
|
+
const pythonEnvInput = document.createElement('input');
|
|
450
|
+
pythonEnvInput.type = 'text';
|
|
451
|
+
pythonEnvInput.id = 'python_env';
|
|
452
|
+
pythonEnvInput.className = 'input-field';
|
|
453
|
+
pythonEnvInput.placeholder = 'Enter Python environment path (optional)';
|
|
454
|
+
|
|
455
|
+
pythonEnvWrapper.appendChild(pythonEnvLabel);
|
|
456
|
+
pythonEnvWrapper.appendChild(pythonEnvInput);
|
|
457
|
+
|
|
458
|
+
const envDescription = document.createElement('p');
|
|
459
|
+
envDescription.className = 'text-xs text-gray-500 mt-1';
|
|
460
|
+
envDescription.textContent = 'Specify the Python executable file(e.g. C:/python312/python) to use for installation. Leave empty to use system default.';
|
|
461
|
+
pythonEnvWrapper.appendChild(envDescription);
|
|
462
|
+
|
|
463
|
+
modalArguments.appendChild(pythonEnvWrapper);
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
|
|
356
467
|
if (Object.keys(envRequirements).length === 0) {
|
|
357
468
|
const noEnvMessage = document.createElement('p');
|
|
358
469
|
noEnvMessage.className = 'text-gray-600';
|
|
@@ -375,7 +486,7 @@ async function showInstallModal(categoryName, serverName, callback) {
|
|
|
375
486
|
label.innerHTML = `${key} ${req.Required ? '<span class="text-red-500">*</span>' : ''}`;
|
|
376
487
|
|
|
377
488
|
const input = document.createElement('input');
|
|
378
|
-
input.type = 'text';
|
|
489
|
+
input.type = req.isSecret ? 'password' : 'text';
|
|
379
490
|
input.id = inputId;
|
|
380
491
|
input.name = key;
|
|
381
492
|
input.placeholder = req.Description || key;
|
|
@@ -501,7 +612,7 @@ async function showInstallModal(categoryName, serverName, callback) {
|
|
|
501
612
|
installForm.onsubmit = (e) => {
|
|
502
613
|
e.preventDefault();
|
|
503
614
|
|
|
504
|
-
// Get all environment variables
|
|
615
|
+
// Get all environment variables and arguments
|
|
505
616
|
const envVars = {};
|
|
506
617
|
const inputs = envInputsDiv.querySelectorAll('input');
|
|
507
618
|
inputs.forEach(input => {
|
|
@@ -510,6 +621,16 @@ async function showInstallModal(categoryName, serverName, callback) {
|
|
|
510
621
|
}
|
|
511
622
|
});
|
|
512
623
|
|
|
624
|
+
// Get installation arguments from individual input fields
|
|
625
|
+
const argInputs = modalArguments.querySelectorAll('.arg-input');
|
|
626
|
+
const args = Array.from(argInputs)
|
|
627
|
+
.map(input => input.value.trim())
|
|
628
|
+
.filter(val => val !== '');
|
|
629
|
+
|
|
630
|
+
// Get Python environment if available
|
|
631
|
+
const pythonEnvInput = document.getElementById('python_env');
|
|
632
|
+
const pythonEnv = pythonEnvInput?.value?.trim();
|
|
633
|
+
|
|
513
634
|
// Check for enabled update toggles and collect requirements to update
|
|
514
635
|
const requirementsToUpdate = [];
|
|
515
636
|
const updateToggles = modalRequirements.querySelectorAll('.toggle-update:checked');
|
|
@@ -548,7 +669,9 @@ async function showInstallModal(categoryName, serverName, callback) {
|
|
|
548
669
|
// Add requirements to update to serverInstallOptions if any
|
|
549
670
|
const serverInstallOptions = {
|
|
550
671
|
targetClients: selectedTargets,
|
|
551
|
-
env: envVars
|
|
672
|
+
env: envVars,
|
|
673
|
+
args: args,
|
|
674
|
+
settings: pythonEnv ? { pythonEnv } : undefined
|
|
552
675
|
};
|
|
553
676
|
|
|
554
677
|
// Only add requirements if we have any to update
|
|
@@ -635,31 +758,38 @@ async function handleBulkClientInstall(categoryName, serverName, targets, envVar
|
|
|
635
758
|
// Optionally, you can refresh modal data here or trigger a callback
|
|
636
759
|
// Start polling for install status, pass requirements if available
|
|
637
760
|
const requirements = serverInstallOptions?.requirements || [];
|
|
638
|
-
pollInstallStatus(categoryName, serverName, targets, 2000,
|
|
761
|
+
pollInstallStatus(categoryName, serverName, targets, 2000, requirements);
|
|
639
762
|
} catch (error) {
|
|
640
763
|
console.error('[LoadingModal] Error applying configuration:', error);
|
|
641
764
|
delayedAppendInstallLoadingMessage(`<span style="color:red;">Error: ${error.message}</span>`);
|
|
642
|
-
setTimeout(() => {
|
|
643
|
-
hideInstallLoadingModal();
|
|
644
|
-
setTimeout(() => {
|
|
645
|
-
location.reload();
|
|
646
|
-
}, 200);
|
|
647
|
-
}, 3200);
|
|
648
765
|
}
|
|
649
766
|
}
|
|
650
767
|
|
|
651
768
|
// Poll install status for the given server/targets and optional requirements
|
|
652
|
-
async function pollInstallStatus(categoryName, serverName, targets, interval = 2000,
|
|
653
|
-
let tries = 0;
|
|
769
|
+
async function pollInstallStatus(categoryName, serverName, targets, interval = 2000, requirements = []) {
|
|
654
770
|
let lastMessages = {};
|
|
655
771
|
let lastRequirementMessages = {};
|
|
772
|
+
let lastReqirementErrorMessages = {};
|
|
656
773
|
let completionMessageSent = false;
|
|
657
774
|
|
|
658
|
-
|
|
775
|
+
const startTime = Date.now();
|
|
776
|
+
const maxTimeout = 10 * 60 * 1000; // 10 minutes in milliseconds
|
|
777
|
+
|
|
778
|
+
while (Date.now() - startTime < maxTimeout) {
|
|
659
779
|
try {
|
|
660
780
|
const resp = await fetch(`/api/categories/${categoryName}`);
|
|
661
781
|
if (resp.ok) {
|
|
662
782
|
const data = await resp.json();
|
|
783
|
+
// If requirements is empty and we have targets, get requirements from server's dependencies
|
|
784
|
+
if (requirements.length === 0 && targets?.length > 0) {
|
|
785
|
+
const feedConfig = data?.data?.feedConfiguration;
|
|
786
|
+
if (feedConfig?.mcpServers) {
|
|
787
|
+
const server = feedConfig.mcpServers.find(s => s.name === serverName);
|
|
788
|
+
if (server?.dependencies?.requirements) {
|
|
789
|
+
requirements = server.dependencies.requirements;
|
|
790
|
+
}
|
|
791
|
+
}
|
|
792
|
+
}
|
|
663
793
|
const installationStatus = data?.data?.installationStatus || {};
|
|
664
794
|
const serverStatuses = installationStatus.serversStatus || {};
|
|
665
795
|
const requirementsStatus = installationStatus.requirementsStatus || {};
|
|
@@ -671,6 +801,14 @@ async function pollInstallStatus(categoryName, serverName, targets, interval = 2
|
|
|
671
801
|
|
|
672
802
|
for (const req of requirements) {
|
|
673
803
|
const reqStatus = requirementsStatus[req.name] || {};
|
|
804
|
+
if (reqStatus.installed === true && !reqStatus.operationStatus) {
|
|
805
|
+
const msg = `Requirement [${req.name}] already installed.`;
|
|
806
|
+
if (msg && lastRequirementMessages[req.name] !== msg) {
|
|
807
|
+
delayedAppendInstallLoadingMessage(msg);
|
|
808
|
+
lastRequirementMessages[req.name] = msg;
|
|
809
|
+
}
|
|
810
|
+
continue;
|
|
811
|
+
}
|
|
674
812
|
const opStatus = reqStatus.operationStatus || {};
|
|
675
813
|
const msg = opStatus.message;
|
|
676
814
|
const status = opStatus.status;
|
|
@@ -687,7 +825,13 @@ async function pollInstallStatus(categoryName, serverName, targets, interval = 2
|
|
|
687
825
|
|
|
688
826
|
// If a requirement failed, show an error
|
|
689
827
|
if (status === "failed") {
|
|
690
|
-
|
|
828
|
+
if (lastReqirementErrorMessages[req.name] !== reqStatus.error) {
|
|
829
|
+
delayedAppendInstallLoadingMessage(`${req.name} failed: ${reqStatus.error}`);
|
|
830
|
+
lastReqirementErrorMessages[req.name] = reqStatus.error;
|
|
831
|
+
}
|
|
832
|
+
if (lastRequirementMessages[req.name] !== msg) {
|
|
833
|
+
delayedAppendInstallLoadingMessage(`<span style="color:#f59e0b;">${req.name}: Update failed - ${msg || 'Unknown error'}</span>`);
|
|
834
|
+
}
|
|
691
835
|
}
|
|
692
836
|
}
|
|
693
837
|
|
|
@@ -735,10 +879,12 @@ async function pollInstallStatus(categoryName, serverName, targets, interval = 2
|
|
|
735
879
|
} catch (error) {
|
|
736
880
|
console.error('[LoadingModal] Error polling status:', error);
|
|
737
881
|
}
|
|
738
|
-
await new Promise(resolve => setTimeout(resolve, interval));
|
|
739
|
-
tries++;
|
|
882
|
+
await new Promise(resolve => setTimeout(resolve, interval)); // 2 second interval
|
|
740
883
|
}
|
|
741
884
|
|
|
885
|
+
// If we reach here, we've timed out
|
|
886
|
+
console.error('[LoadingModal] Operation timed out after 10 minutes');
|
|
887
|
+
|
|
742
888
|
// On timeout, show timeout message but don't auto-close
|
|
743
889
|
if (!completionMessageSent) {
|
|
744
890
|
delayedAppendInstallLoadingMessage(`<span style="color:#f59e0b;">Operation timed out - Please refresh the page</span>`, true);
|
|
@@ -786,30 +932,33 @@ async function uninstallTools(categoryName, serverList, targets) {
|
|
|
786
932
|
}
|
|
787
933
|
}
|
|
788
934
|
|
|
789
|
-
// Close modal and
|
|
935
|
+
// Close modal and ensure selected category persists
|
|
790
936
|
function closeModal() {
|
|
791
937
|
document.getElementById('installModal').style.display = "none";
|
|
792
|
-
|
|
938
|
+
|
|
939
|
+
// Get the last selected category from localStorage
|
|
940
|
+
const lastSelected = localStorage.getItem('lastSelectedCategory');
|
|
941
|
+
|
|
942
|
+
// Refresh page and restore selection
|
|
943
|
+
if (lastSelected) {
|
|
944
|
+
setTimeout(() => {
|
|
945
|
+
window.location.href = window.location.pathname + '?category=' + encodeURIComponent(lastSelected);
|
|
946
|
+
}, 100);
|
|
947
|
+
} else {
|
|
948
|
+
location.reload();
|
|
949
|
+
}
|
|
793
950
|
}
|
|
794
951
|
|
|
795
952
|
// Close modal if clicked outside content
|
|
796
953
|
function setupModalOutsideClick() {
|
|
797
954
|
window.onclick = function (event) {
|
|
798
955
|
const installModal = document.getElementById('installModal');
|
|
799
|
-
const loadingModal = document.getElementById('installLoadingModal');
|
|
800
|
-
|
|
801
956
|
if (event.target == installModal) {
|
|
802
957
|
closeModal();
|
|
803
|
-
} else if (event.target == loadingModal) {
|
|
804
|
-
hideInstallLoadingModal();
|
|
805
958
|
}
|
|
806
959
|
};
|
|
807
960
|
}
|
|
808
961
|
|
|
809
|
-
// Compatibility function for individual client installation
|
|
810
|
-
async function handleClientInstall(categoryName, serverName, target, envVars = {}) {
|
|
811
|
-
return handleBulkClientInstall(categoryName, serverName, [target], envVars);
|
|
812
|
-
}
|
|
813
962
|
window.showInstallLoadingModal = showInstallLoadingModal;
|
|
814
963
|
window.appendInstallLoadingMessage = appendInstallLoadingMessage;
|
|
815
964
|
window.hideInstallLoadingModal = hideInstallLoadingModal;
|
|
@@ -36,6 +36,8 @@ function startRefreshTimer(serverName) {
|
|
|
36
36
|
// Show server details
|
|
37
37
|
async function showServerDetails(serverName) {
|
|
38
38
|
console.log("Showing details for:", serverName);
|
|
39
|
+
// Save the selected category in localStorage
|
|
40
|
+
localStorage.setItem('lastSelectedCategory', serverName);
|
|
39
41
|
const server = allServerCategoriesData.find(s => s.name === serverName);
|
|
40
42
|
const detailsDiv = document.getElementById('serverCategoryDetails');
|
|
41
43
|
|
|
@@ -78,7 +80,7 @@ async function showServerDetails(serverName) {
|
|
|
78
80
|
document.getElementById('toolMcpsList').innerHTML = '<p class="text-red-500">Error loading server details.</p>';
|
|
79
81
|
showToast(`Error loading server details: ${error.message}`, 'error');
|
|
80
82
|
}
|
|
81
|
-
|
|
83
|
+
|
|
82
84
|
// Start monitoring installation status
|
|
83
85
|
startRefreshTimer(serverName);
|
|
84
86
|
}
|
|
@@ -119,20 +121,20 @@ async function renderServersList(serverCategory) {
|
|
|
119
121
|
<span class="text-xs font-semibold mb-2">Client Status:</span>
|
|
120
122
|
<div class="flex flex-wrap gap-2">
|
|
121
123
|
${availableTargets.map(client => {
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
124
|
+
const status = (serverCategory.installationStatus?.serversStatus[mcpServer.name]?.installedStatus || {})[client];
|
|
125
|
+
const isInstalled = status?.status === 'completed';
|
|
126
|
+
return `
|
|
125
127
|
<span class="text-xs flex items-center ${isInstalled ? 'text-green-600 bg-green-50' : 'text-gray-600 bg-gray-50'} px-3 py-1 rounded-full shadow">
|
|
126
128
|
<svg class="w-3 h-3 mr-1" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg">
|
|
127
129
|
${isInstalled
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
130
|
+
? '<path fill-rule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clip-rule="evenodd"></path>'
|
|
131
|
+
: '<path d="M7 3.5A1.5 1.5 0 018.5 2h3.879a1.5 1.5 0 011.06.44l3.122 3.12A1.5 1.5 0 0117 6.622V12.5a1.5 1.5 0 01-1.5 1.5h-1v-3.379a1.5 1.5 0 00-.44-1.06L10.94 6.44A1.5 1.5 0 009.879 6H7V3.5z M6 6h2.879A1.5 1.5 0 0110 7.5v1.379a1.5 1.5 0 01-.44 1.06L6.44 13.06A1.5 1.5 0 015.379 13H4.5A1.5 1.5 0 013 11.5V7.5A1.5 1.5 0 014.5 6H6z"></path>'
|
|
132
|
+
}
|
|
131
133
|
</svg>
|
|
132
134
|
${client}
|
|
133
135
|
</span>
|
|
134
136
|
`;
|
|
135
|
-
|
|
137
|
+
}).join('')}
|
|
136
138
|
</div>
|
|
137
139
|
</div>
|
|
138
140
|
${hasEnvRequirements ? `<div class="mt-1 text-xs text-blue-600">
|
|
@@ -164,7 +166,7 @@ async function renderServersList(serverCategory) {
|
|
|
164
166
|
}
|
|
165
167
|
|
|
166
168
|
// Uninstall tools
|
|
167
|
-
window.uninstallTools = async function(serverName, toolList) {
|
|
169
|
+
window.uninstallTools = async function (serverName, toolList) {
|
|
168
170
|
const confirmed = await showConfirm(`Are you sure you want to uninstall ${toolList.length} tool(s)?`);
|
|
169
171
|
if (!confirmed) {
|
|
170
172
|
return;
|
|
@@ -197,7 +199,7 @@ window.uninstallTools = async function(serverName, toolList) {
|
|
|
197
199
|
}
|
|
198
200
|
|
|
199
201
|
// Make showServerDetails available in window scope and handle async
|
|
200
|
-
window.showServerDetails = async function(serverName) {
|
|
202
|
+
window.showServerDetails = async function (serverName) {
|
|
201
203
|
try {
|
|
202
204
|
await showServerDetails(serverName);
|
|
203
205
|
} catch (error) {
|
|
@@ -2,6 +2,18 @@ import { allServerCategoriesData, fetchServerCategories } from './api.js';
|
|
|
2
2
|
import { showServerDetails } from './serverCategoryDetails.js';
|
|
3
3
|
import { showToast } from './notifications.js';
|
|
4
4
|
|
|
5
|
+
// Function to show the last selected category on page load
|
|
6
|
+
async function loadLastSelectedCategory() {
|
|
7
|
+
const lastSelected = localStorage.getItem('lastSelectedCategory');
|
|
8
|
+
if (lastSelected && allServerCategoriesData) {
|
|
9
|
+
// Check if the category still exists
|
|
10
|
+
const categoryExists = allServerCategoriesData.some(server => server.name === lastSelected);
|
|
11
|
+
if (categoryExists) {
|
|
12
|
+
await showServerDetails(lastSelected);
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
5
17
|
// Function to render server list (used by fetchServerCategories and search)
|
|
6
18
|
function renderServerCategoryList(servers) {
|
|
7
19
|
const serverCategoryList = document.getElementById('serverCategoryList');
|
|
@@ -14,19 +26,19 @@ function renderServerCategoryList(servers) {
|
|
|
14
26
|
|
|
15
27
|
serverCategoryList.innerHTML = servers.map(server => {
|
|
16
28
|
let statusHtml = '';
|
|
17
|
-
|
|
29
|
+
|
|
18
30
|
// Add tool status summary if available
|
|
19
31
|
if (server.installationStatus && server.installationStatus.serversStatus) {
|
|
20
32
|
const totalTools = Object.keys(server.installationStatus.serversStatus).length;
|
|
21
33
|
const installedTools = Object.values(server.installationStatus.serversStatus)
|
|
22
|
-
.filter(serverStatus =>
|
|
34
|
+
.filter(serverStatus =>
|
|
23
35
|
Object.values(serverStatus.installedStatus || {})
|
|
24
36
|
.some(opStatus => opStatus.status === 'completed' && opStatus.type === 'install')
|
|
25
37
|
).length;
|
|
26
38
|
|
|
27
39
|
if (totalTools > 0) {
|
|
28
40
|
let colorClass, icon, statusText;
|
|
29
|
-
|
|
41
|
+
|
|
30
42
|
if (installedTools === totalTools) {
|
|
31
43
|
// All tools installed
|
|
32
44
|
colorClass = "text-green-600 bg-green-50";
|
|
@@ -43,7 +55,7 @@ function renderServerCategoryList(servers) {
|
|
|
43
55
|
icon = '<svg class="w-5 h-5 mr-1" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z" clip-rule="evenodd"></path></svg>';
|
|
44
56
|
statusText = "Not Configured";
|
|
45
57
|
}
|
|
46
|
-
|
|
58
|
+
|
|
47
59
|
statusHtml = `<span class="${colorClass} inline-flex items-center px-2 py-1 rounded-full text-xs" style="width: fit-content; max-width: 100%;">
|
|
48
60
|
${icon}<span class="truncate">${statusText} (${installedTools}/${totalTools})</span>
|
|
49
61
|
</span>`;
|
|
@@ -59,6 +71,9 @@ function renderServerCategoryList(servers) {
|
|
|
59
71
|
</div>
|
|
60
72
|
`;
|
|
61
73
|
}).join('');
|
|
74
|
+
|
|
75
|
+
// After rendering, try to load the last selected category
|
|
76
|
+
loadLastSelectedCategory();
|
|
62
77
|
}
|
|
63
78
|
|
|
64
79
|
// Setup search functionality
|
|
@@ -79,4 +94,4 @@ function setupSearch() {
|
|
|
79
94
|
}
|
|
80
95
|
|
|
81
96
|
// Export functions
|
|
82
|
-
export { renderServerCategoryList, setupSearch };
|
|
97
|
+
export { renderServerCategoryList, setupSearch, loadLastSelectedCategory };
|
|
@@ -17,24 +17,38 @@
|
|
|
17
17
|
<div id="modalTargets" class="client-grid"></div>
|
|
18
18
|
</div>
|
|
19
19
|
|
|
20
|
-
<!-- Environment Variables Section -->
|
|
21
20
|
<div class="section-container">
|
|
22
|
-
<div id="modalEnvInputs" class="space-y-4"
|
|
21
|
+
<div id="modalEnvInputs" class="space-y-4">
|
|
22
|
+
<!-- Environment Variables will be injected here -->
|
|
23
|
+
</div>
|
|
23
24
|
</div>
|
|
24
|
-
</div>
|
|
25
25
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
Cancel
|
|
31
|
-
</button>
|
|
32
|
-
<button type="submit" class="submit-button">
|
|
33
|
-
Apply
|
|
34
|
-
</button>
|
|
26
|
+
<div class="section-container">
|
|
27
|
+
<div id="modalArguments" class="space-y-4">
|
|
28
|
+
<!-- Arguments will be injected here -->
|
|
29
|
+
</div>
|
|
35
30
|
</div>
|
|
36
|
-
</
|
|
31
|
+
</div>
|
|
37
32
|
</div>
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
<form id="installForm" class="mt-8">
|
|
36
|
+
<!-- Arguments Section -->
|
|
37
|
+
<div id="modalArguments" class="mb-6 space-y-4">
|
|
38
|
+
<!-- Arguments will be injected here -->
|
|
39
|
+
</div>
|
|
40
|
+
|
|
41
|
+
<div class="flex justify-end space-x-4">
|
|
42
|
+
<button type="button" onclick="closeModal()"
|
|
43
|
+
class="px-6 py-2.5 text-gray-600 hover:text-gray-800 font-medium rounded-lg hover:bg-gray-100 transition-colors">
|
|
44
|
+
Cancel
|
|
45
|
+
</button>
|
|
46
|
+
<button type="submit" class="submit-button">
|
|
47
|
+
Apply
|
|
48
|
+
</button>
|
|
49
|
+
</div>
|
|
50
|
+
</form>
|
|
51
|
+
</div>
|
|
38
52
|
</div>
|
|
39
53
|
|
|
40
54
|
<!-- Loading Modal -->
|
package/dist/web/server.js
CHANGED
|
@@ -9,7 +9,7 @@ import { configProvider } from '../core/ConfigurationProvider.js';
|
|
|
9
9
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
10
10
|
const app = express();
|
|
11
11
|
// Middleware
|
|
12
|
-
app.use(express.static(path.join(__dirname, 'public')));
|
|
12
|
+
app.use('/', express.static(path.join(__dirname, '..', '..', 'src', 'web', 'public')));
|
|
13
13
|
app.use(express.json());
|
|
14
14
|
// Get available targets
|
|
15
15
|
app.get('/api/targets', async (req, res) => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "imcp",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.5",
|
|
4
4
|
"description": "Node.js SDK for Model Context Protocol (MCP)",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -30,6 +30,7 @@
|
|
|
30
30
|
"commander": "^11.1.0",
|
|
31
31
|
"express": "^4.18.2",
|
|
32
32
|
"extract-zip": "^2.0.1",
|
|
33
|
+
"strip-json-comments": "^5.0.1",
|
|
33
34
|
"unzipper": "^0.10.14"
|
|
34
35
|
},
|
|
35
36
|
"devDependencies": {
|
|
@@ -4,6 +4,7 @@ import { Logger } from '../../utils/logger.js';
|
|
|
4
4
|
import { hasLocalFeeds } from '../../utils/feedUtils.js';
|
|
5
5
|
import { ServerInstallOptions } from '../../core/types.js';
|
|
6
6
|
import { SUPPORTED_CLIENT_NAMES } from '../../core/constants.js';
|
|
7
|
+
import { mcpManager } from '../../core/MCPManager.js';
|
|
7
8
|
|
|
8
9
|
export function createInstallCommand(): Command {
|
|
9
10
|
return new Command('install')
|
|
@@ -49,10 +50,11 @@ Examples:
|
|
|
49
50
|
Logger.log('Local feeds not found, syncing from remote...');
|
|
50
51
|
await serverService.syncFeeds();
|
|
51
52
|
}
|
|
53
|
+
await mcpManager.initialize();
|
|
52
54
|
const { category, name, verbose, clients, envs } = options;
|
|
53
|
-
|
|
55
|
+
|
|
54
56
|
Logger.debug(`Install options: ${JSON.stringify({ category, name, verbose, clients, envs })}`);
|
|
55
|
-
|
|
57
|
+
|
|
56
58
|
const serverName = name.trim();
|
|
57
59
|
Logger.debug(`Server name: ${serverName}`);
|
|
58
60
|
|
package/src/cli/commands/list.ts
CHANGED
package/src/cli/index.ts
CHANGED