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.
Files changed (93) hide show
  1. package/README.md +5 -6
  2. package/dist/cli/commands/install.js +2 -0
  3. package/dist/cli/commands/list.js +1 -0
  4. package/dist/cli/index.js +1 -2
  5. package/dist/core/ConfigurationLoader.d.ts +32 -0
  6. package/dist/core/ConfigurationLoader.js +213 -0
  7. package/dist/core/ConfigurationProvider.d.ts +2 -3
  8. package/dist/core/ConfigurationProvider.js +13 -182
  9. package/dist/core/InstallationService.d.ts +8 -0
  10. package/dist/core/InstallationService.js +124 -96
  11. package/dist/core/RequirementService.d.ts +1 -1
  12. package/dist/core/RequirementService.js +5 -9
  13. package/dist/core/constants.js +14 -1
  14. package/dist/core/installers/BaseInstaller.d.ts +5 -4
  15. package/dist/core/installers/BaseInstaller.js +17 -28
  16. package/dist/core/installers/ClientInstaller.js +159 -39
  17. package/dist/core/installers/CommandInstaller.d.ts +1 -0
  18. package/dist/core/installers/CommandInstaller.js +3 -0
  19. package/dist/core/installers/GeneralInstaller.d.ts +1 -0
  20. package/dist/core/installers/GeneralInstaller.js +3 -0
  21. package/dist/core/installers/InstallerFactory.d.ts +9 -7
  22. package/dist/core/installers/InstallerFactory.js +10 -8
  23. package/dist/core/installers/NpmInstaller.d.ts +1 -0
  24. package/dist/core/installers/NpmInstaller.js +3 -0
  25. package/dist/core/installers/PipInstaller.d.ts +6 -3
  26. package/dist/core/installers/PipInstaller.js +21 -8
  27. package/dist/core/installers/RequirementInstaller.d.ts +4 -3
  28. package/dist/core/installers/clients/ClientInstaller.d.ts +23 -0
  29. package/dist/core/installers/clients/ClientInstaller.js +573 -0
  30. package/dist/core/installers/clients/ExtensionInstaller.d.ts +26 -0
  31. package/dist/core/installers/clients/ExtensionInstaller.js +149 -0
  32. package/dist/core/installers/index.d.ts +8 -6
  33. package/dist/core/installers/index.js +8 -6
  34. package/dist/core/installers/requirements/BaseInstaller.d.ts +59 -0
  35. package/dist/core/installers/requirements/BaseInstaller.js +168 -0
  36. package/dist/core/installers/requirements/CommandInstaller.d.ts +37 -0
  37. package/dist/core/installers/requirements/CommandInstaller.js +173 -0
  38. package/dist/core/installers/requirements/GeneralInstaller.d.ts +33 -0
  39. package/dist/core/installers/requirements/GeneralInstaller.js +86 -0
  40. package/dist/core/installers/requirements/InstallerFactory.d.ts +54 -0
  41. package/dist/core/installers/requirements/InstallerFactory.js +97 -0
  42. package/dist/core/installers/requirements/NpmInstaller.d.ts +26 -0
  43. package/dist/core/installers/requirements/NpmInstaller.js +128 -0
  44. package/dist/core/installers/requirements/PipInstaller.d.ts +28 -0
  45. package/dist/core/installers/requirements/PipInstaller.js +128 -0
  46. package/{src/core/installers/RequirementInstaller.ts → dist/core/installers/requirements/RequirementInstaller.d.ts} +33 -38
  47. package/dist/core/installers/requirements/RequirementInstaller.js +3 -0
  48. package/dist/core/types.d.ts +4 -1
  49. package/dist/services/ServerService.js +1 -1
  50. package/dist/utils/clientUtils.d.ts +0 -6
  51. package/dist/utils/clientUtils.js +3 -2
  52. package/dist/utils/githubUtils.d.ts +11 -0
  53. package/dist/utils/githubUtils.js +88 -0
  54. package/dist/utils/osUtils.d.ts +17 -0
  55. package/dist/utils/osUtils.js +184 -0
  56. package/dist/web/public/css/modal.css +97 -3
  57. package/dist/web/public/index.html +21 -2
  58. package/dist/web/public/js/modal.js +177 -28
  59. package/dist/web/public/js/serverCategoryDetails.js +12 -10
  60. package/dist/web/public/js/serverCategoryList.js +20 -5
  61. package/dist/web/public/modal.html +27 -13
  62. package/dist/web/server.js +1 -1
  63. package/package.json +2 -1
  64. package/src/cli/commands/install.ts +4 -2
  65. package/src/cli/commands/list.ts +1 -0
  66. package/src/cli/index.ts +1 -1
  67. package/src/core/ConfigurationLoader.ts +251 -0
  68. package/src/core/ConfigurationProvider.ts +13 -195
  69. package/src/core/InstallationService.ts +140 -106
  70. package/src/core/RequirementService.ts +5 -10
  71. package/src/core/constants.ts +15 -1
  72. package/src/core/installers/{ClientInstaller.ts → clients/ClientInstaller.ts} +185 -46
  73. package/src/core/installers/clients/ExtensionInstaller.ts +162 -0
  74. package/src/core/installers/index.ts +9 -7
  75. package/src/core/installers/{BaseInstaller.ts → requirements/BaseInstaller.ts} +10 -118
  76. package/src/core/installers/{CommandInstaller.ts → requirements/CommandInstaller.ts} +7 -3
  77. package/src/core/installers/{GeneralInstaller.ts → requirements/GeneralInstaller.ts} +6 -2
  78. package/src/core/installers/{InstallerFactory.ts → requirements/InstallerFactory.ts} +11 -9
  79. package/src/core/installers/{NpmInstaller.ts → requirements/NpmInstaller.ts} +7 -4
  80. package/src/core/installers/{PipInstaller.ts → requirements/PipInstaller.ts} +26 -10
  81. package/src/core/installers/requirements/RequirementInstaller.ts +41 -0
  82. package/src/core/types.ts +4 -1
  83. package/src/services/ServerService.ts +1 -1
  84. package/src/utils/clientUtils.ts +4 -2
  85. package/src/utils/githubUtils.ts +103 -0
  86. package/src/utils/osUtils.ts +206 -15
  87. package/src/web/public/css/modal.css +97 -3
  88. package/src/web/public/index.html +21 -2
  89. package/src/web/public/js/modal.js +177 -28
  90. package/src/web/public/js/serverCategoryDetails.js +12 -10
  91. package/src/web/public/js/serverCategoryList.js +20 -5
  92. package/src/web/public/modal.html +27 -13
  93. 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
- location.reload();
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, 60, requirements);
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, maxTries = 60, requirements = []) {
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
- while (tries < maxTries) {
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
- delayedAppendInstallLoadingMessage(`<span style="color:#f59e0b;">${req.name}: Update failed - ${msg || 'Unknown error'}</span>`);
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 reload
935
+ // Close modal and ensure selected category persists
790
936
  function closeModal() {
791
937
  document.getElementById('installModal').style.display = "none";
792
- location.reload();
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
- const status = (serverCategory.installationStatus?.serversStatus[mcpServer.name]?.installedStatus|| {})[client];
123
- const isInstalled = status?.status === 'completed';
124
- return `
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
- ? '<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>'
129
- : '<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>'
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
- }).join('')}
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"></div>
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
- <form id="installForm" class="mt-8">
27
- <div class="flex justify-end space-x-4">
28
- <button type="button" onclick="closeModal()"
29
- class="px-6 py-2.5 text-gray-600 hover:text-gray-800 font-medium rounded-lg hover:bg-gray-100 transition-colors">
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
- </form>
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 -->
@@ -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",
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
 
@@ -36,6 +36,7 @@ export function createListCommand(): Command {
36
36
  Logger.error('Failed to sync feeds, falling back to local feeds', syncError);
37
37
  }
38
38
  }
39
+ await mcpManager.initialize();
39
40
 
40
41
  Logger.debug({
41
42
  action: 'ensuring_feeds_directory',
package/src/cli/index.ts CHANGED
@@ -27,7 +27,7 @@ interface CommanderError extends Error {
27
27
 
28
28
  async function main(): Promise<void> {
29
29
  // Initialize the MCP manager
30
- await mcpManager.initialize();
30
+ // await mcpManager.initialize();
31
31
 
32
32
  const program = new Command();
33
33
  program