imcp 0.0.1

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 (124) hide show
  1. package/.github/ISSUE_TEMPLATE/JitAccess.yml +28 -0
  2. package/.github/acl/access.yml +20 -0
  3. package/.github/compliance/inventory.yml +5 -0
  4. package/.github/policies/jit.yml +19 -0
  5. package/README.md +137 -0
  6. package/dist/cli/commands/install.d.ts +2 -0
  7. package/dist/cli/commands/install.js +105 -0
  8. package/dist/cli/commands/list.d.ts +2 -0
  9. package/dist/cli/commands/list.js +90 -0
  10. package/dist/cli/commands/pull.d.ts +2 -0
  11. package/dist/cli/commands/pull.js +17 -0
  12. package/dist/cli/commands/serve.d.ts +2 -0
  13. package/dist/cli/commands/serve.js +32 -0
  14. package/dist/cli/commands/start.d.ts +2 -0
  15. package/dist/cli/commands/start.js +32 -0
  16. package/dist/cli/commands/sync.d.ts +2 -0
  17. package/dist/cli/commands/sync.js +17 -0
  18. package/dist/cli/commands/uninstall.d.ts +2 -0
  19. package/dist/cli/commands/uninstall.js +39 -0
  20. package/dist/cli/index.d.ts +2 -0
  21. package/dist/cli/index.js +114 -0
  22. package/dist/core/ConfigurationProvider.d.ts +31 -0
  23. package/dist/core/ConfigurationProvider.js +416 -0
  24. package/dist/core/InstallationService.d.ts +17 -0
  25. package/dist/core/InstallationService.js +144 -0
  26. package/dist/core/MCPManager.d.ts +17 -0
  27. package/dist/core/MCPManager.js +98 -0
  28. package/dist/core/RequirementService.d.ts +45 -0
  29. package/dist/core/RequirementService.js +123 -0
  30. package/dist/core/constants.d.ts +29 -0
  31. package/dist/core/constants.js +55 -0
  32. package/dist/core/installers/BaseInstaller.d.ts +73 -0
  33. package/dist/core/installers/BaseInstaller.js +247 -0
  34. package/dist/core/installers/ClientInstaller.d.ts +17 -0
  35. package/dist/core/installers/ClientInstaller.js +307 -0
  36. package/dist/core/installers/CommandInstaller.d.ts +36 -0
  37. package/dist/core/installers/CommandInstaller.js +170 -0
  38. package/dist/core/installers/GeneralInstaller.d.ts +32 -0
  39. package/dist/core/installers/GeneralInstaller.js +87 -0
  40. package/dist/core/installers/InstallerFactory.d.ts +52 -0
  41. package/dist/core/installers/InstallerFactory.js +95 -0
  42. package/dist/core/installers/NpmInstaller.d.ts +25 -0
  43. package/dist/core/installers/NpmInstaller.js +123 -0
  44. package/dist/core/installers/PipInstaller.d.ts +25 -0
  45. package/dist/core/installers/PipInstaller.js +114 -0
  46. package/dist/core/installers/RequirementInstaller.d.ts +32 -0
  47. package/dist/core/installers/RequirementInstaller.js +3 -0
  48. package/dist/core/installers/index.d.ts +6 -0
  49. package/dist/core/installers/index.js +7 -0
  50. package/dist/core/types.d.ts +152 -0
  51. package/dist/core/types.js +16 -0
  52. package/dist/index.d.ts +11 -0
  53. package/dist/index.js +19 -0
  54. package/dist/services/InstallRequestValidator.d.ts +21 -0
  55. package/dist/services/InstallRequestValidator.js +99 -0
  56. package/dist/services/ServerService.d.ts +47 -0
  57. package/dist/services/ServerService.js +145 -0
  58. package/dist/utils/UpdateCheckTracker.d.ts +39 -0
  59. package/dist/utils/UpdateCheckTracker.js +80 -0
  60. package/dist/utils/clientUtils.d.ts +29 -0
  61. package/dist/utils/clientUtils.js +105 -0
  62. package/dist/utils/feedUtils.d.ts +5 -0
  63. package/dist/utils/feedUtils.js +29 -0
  64. package/dist/utils/githubAuth.d.ts +1 -0
  65. package/dist/utils/githubAuth.js +123 -0
  66. package/dist/utils/logger.d.ts +14 -0
  67. package/dist/utils/logger.js +90 -0
  68. package/dist/utils/osUtils.d.ts +16 -0
  69. package/dist/utils/osUtils.js +235 -0
  70. package/dist/web/public/css/modal.css +250 -0
  71. package/dist/web/public/css/notifications.css +70 -0
  72. package/dist/web/public/index.html +157 -0
  73. package/dist/web/public/js/api.js +213 -0
  74. package/dist/web/public/js/modal.js +572 -0
  75. package/dist/web/public/js/notifications.js +99 -0
  76. package/dist/web/public/js/serverCategoryDetails.js +210 -0
  77. package/dist/web/public/js/serverCategoryList.js +82 -0
  78. package/dist/web/public/modal.html +61 -0
  79. package/dist/web/public/styles.css +155 -0
  80. package/dist/web/server.d.ts +5 -0
  81. package/dist/web/server.js +150 -0
  82. package/package.json +53 -0
  83. package/src/cli/commands/install.ts +140 -0
  84. package/src/cli/commands/list.ts +112 -0
  85. package/src/cli/commands/pull.ts +16 -0
  86. package/src/cli/commands/serve.ts +37 -0
  87. package/src/cli/commands/uninstall.ts +54 -0
  88. package/src/cli/index.ts +127 -0
  89. package/src/core/ConfigurationProvider.ts +489 -0
  90. package/src/core/InstallationService.ts +173 -0
  91. package/src/core/MCPManager.ts +134 -0
  92. package/src/core/RequirementService.ts +147 -0
  93. package/src/core/constants.ts +61 -0
  94. package/src/core/installers/BaseInstaller.ts +292 -0
  95. package/src/core/installers/ClientInstaller.ts +423 -0
  96. package/src/core/installers/CommandInstaller.ts +185 -0
  97. package/src/core/installers/GeneralInstaller.ts +89 -0
  98. package/src/core/installers/InstallerFactory.ts +109 -0
  99. package/src/core/installers/NpmInstaller.ts +128 -0
  100. package/src/core/installers/PipInstaller.ts +121 -0
  101. package/src/core/installers/RequirementInstaller.ts +38 -0
  102. package/src/core/installers/index.ts +9 -0
  103. package/src/core/types.ts +163 -0
  104. package/src/index.ts +44 -0
  105. package/src/services/InstallRequestValidator.ts +112 -0
  106. package/src/services/ServerService.ts +181 -0
  107. package/src/utils/UpdateCheckTracker.ts +86 -0
  108. package/src/utils/clientUtils.ts +112 -0
  109. package/src/utils/feedUtils.ts +31 -0
  110. package/src/utils/githubAuth.ts +142 -0
  111. package/src/utils/logger.ts +101 -0
  112. package/src/utils/osUtils.ts +250 -0
  113. package/src/web/public/css/modal.css +250 -0
  114. package/src/web/public/css/notifications.css +70 -0
  115. package/src/web/public/index.html +157 -0
  116. package/src/web/public/js/api.js +213 -0
  117. package/src/web/public/js/modal.js +572 -0
  118. package/src/web/public/js/notifications.js +99 -0
  119. package/src/web/public/js/serverCategoryDetails.js +210 -0
  120. package/src/web/public/js/serverCategoryList.js +82 -0
  121. package/src/web/public/modal.html +61 -0
  122. package/src/web/public/styles.css +155 -0
  123. package/src/web/server.ts +195 -0
  124. package/tsconfig.json +18 -0
@@ -0,0 +1,210 @@
1
+ import { allServerCategoriesData, fetchServerCategories } from './api.js';
2
+ import { showInstallModal } from './modal.js';
3
+ import { showToast, showConfirm } from './notifications.js';
4
+
5
+ const REFRESH_INTERVAL = 2000; // 2 seconds
6
+ let refreshTimer = null;
7
+
8
+ // Start refresh timer for installation status
9
+ function startRefreshTimer(serverName) {
10
+ // Clear existing timer if any
11
+ if (refreshTimer) {
12
+ clearInterval(refreshTimer);
13
+ }
14
+
15
+ // Setup new refresh timer
16
+ refreshTimer = setInterval(async () => {
17
+ const server = allServerCategoriesData.find(s => s.name === serverName);
18
+ if (!server?.installationStatus?.serversStatus) return;
19
+
20
+ // Check if any installation is pending
21
+ const hasPendingInstallation = Object.values(server.installationStatus.serversStatus).some(serverStatus =>
22
+ Object.values(serverStatus.installedStatus || {}).some(status =>
23
+ status.status === 'pending' || status.status === 'in-progress'
24
+ )
25
+ );
26
+
27
+ if (hasPendingInstallation) {
28
+ await fetchServerCategories(); // Refresh data
29
+ await showServerDetails(serverName); // Update UI
30
+ } else {
31
+ clearInterval(refreshTimer); // Stop refreshing if no pending installations
32
+ }
33
+ }, REFRESH_INTERVAL);
34
+ }
35
+
36
+ // Show server details
37
+ async function showServerDetails(serverName) {
38
+ console.log("Showing details for:", serverName);
39
+ const server = allServerCategoriesData.find(s => s.name === serverName);
40
+ const detailsDiv = document.getElementById('serverCategoryDetails');
41
+
42
+ if (!server) {
43
+ detailsDiv.innerHTML = '<p class="text-red-500">Error: Server data not found.</p>';
44
+ return;
45
+ }
46
+
47
+ // Highlight selected server
48
+ document.querySelectorAll('.server-item').forEach(item => {
49
+ item.classList.remove('bg-blue-100', 'border-blue-300');
50
+ if (item.dataset.serverName === serverName) {
51
+ item.classList.add('bg-blue-100', 'border-blue-300');
52
+ }
53
+ });
54
+
55
+ // Initial render with loading state
56
+ detailsDiv.innerHTML = `
57
+ <h3 class="text-xl font-semibold mb-2 text-gray-800">${server.displayName || server.name}</h3>
58
+ <p class="mb-4"><span class="font-semibold">Description:</span> ${server.description || 'N/A'}</p>
59
+ <div id="toolMcpsList" class="mt-4">
60
+ <div class="animate-pulse flex space-x-4">
61
+ <div class="flex-1 space-y-4 py-1">
62
+ <div class="h-4 bg-gray-200 rounded w-3/4"></div>
63
+ <div class="space-y-2">
64
+ <div class="h-4 bg-gray-200 rounded"></div>
65
+ <div class="h-4 bg-gray-200 rounded w-5/6"></div>
66
+ </div>
67
+ </div>
68
+ </div>
69
+ </div>
70
+ `;
71
+
72
+ // Asynchronously load and render the servers list
73
+ try {
74
+ const serversListHtml = await renderServersList(server);
75
+ document.getElementById('toolMcpsList').innerHTML = serversListHtml;
76
+ } catch (error) {
77
+ console.error('Error rendering servers list:', error);
78
+ document.getElementById('toolMcpsList').innerHTML = '<p class="text-red-500">Error loading server details.</p>';
79
+ showToast(`Error loading server details: ${error.message}`, 'error');
80
+ }
81
+
82
+ // Start monitoring installation status
83
+ startRefreshTimer(serverName);
84
+ }
85
+
86
+ // Render tools list
87
+ async function renderServersList(serverCategory) {
88
+ // Fetch available targets
89
+ const targetResponse = await fetch('/api/targets');
90
+ const targetData = await targetResponse.json();
91
+ const availableTargets = targetData.success ? targetData.data : [];
92
+ if (!serverCategory.feedConfiguration?.mcpServers) {
93
+ return '<p class="text-gray-500">No MCP Servers found for this server.</p>';
94
+ }
95
+
96
+ const mcpServers = serverCategory.feedConfiguration.mcpServers;
97
+ if (mcpServers.length === 0) {
98
+ return '<p class="text-gray-500">No MCP Servers found for this server.</p>';
99
+ }
100
+
101
+ let toolsHtml = `
102
+ <div class="flex justify-between items-center mb-4">
103
+ <h2 class="text-lg font-semibold text-gray-600">MCP Servers</h2>
104
+ </div>`;
105
+
106
+ mcpServers.forEach(mcpServer => {
107
+ const isInstalled = mcpServer.installed;
108
+ const envRequirements = mcpServer.installation?.['env:'] || mcpServer.installation?.env || {};
109
+ const hasEnvRequirements = Object.keys(envRequirements).length > 0;
110
+
111
+ toolsHtml += `
112
+ <div class="border border-gray-200 p-3 rounded mb-2 flex justify-between items-center hover:bg-gray-50 transition-all duration-150 ${!isInstalled ? 'bg-white' : 'bg-gray-50'}"
113
+ id="tool-item-${mcpServer.name}">
114
+ <div class="flex items-center flex-1">
115
+ <div>
116
+ <h5 class="font-semibold text-gray-800">${mcpServer.displayName || mcpServer.name}</h5>
117
+ <p class="text-sm text-gray-600 mb-1">${mcpServer.description || 'No description'}</p>
118
+ <div class="flex flex-col">
119
+ <span class="text-xs font-semibold mb-2">Client Status:</span>
120
+ <div class="flex flex-wrap gap-2">
121
+ ${availableTargets.map(client => {
122
+ const status = (serverCategory.installationStatus?.serversStatus[mcpServer.name]?.installedStatus|| {})[client];
123
+ const isInstalled = status?.status === 'completed';
124
+ return `
125
+ <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
+ <svg class="w-3 h-3 mr-1" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg">
127
+ ${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
+ }
131
+ </svg>
132
+ ${client}
133
+ </span>
134
+ `;
135
+ }).join('')}
136
+ </div>
137
+ </div>
138
+ ${hasEnvRequirements ? `<div class="mt-1 text-xs text-blue-600">
139
+ <svg class="w-3 h-3 inline mr-1" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg">
140
+ <path fill-rule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a1 1 0 000 2v3a1 1 0 001 1h1a1 1 0 100-2v-3a1 1 0 00-1-1H9z" clip-rule="evenodd"></path>
141
+ </svg>
142
+ Requires environment configuration
143
+ </div>` : ''}
144
+ </div>
145
+ </div>
146
+ ${!isInstalled ? `
147
+ <button onclick="event.stopPropagation(); window.showInstallModal('${serverCategory.name}', '${mcpServer.name}')"
148
+ class="bg-blue-500 hover:bg-blue-700 text-white text-sm font-bold py-1 px-3 rounded-full shadow-sm transition duration-150 ease-in-out">
149
+ Install
150
+ </button>
151
+ ` : `
152
+ <div class="flex items-center gap-2">
153
+ <button onclick="event.stopPropagation(); window.uninstallTools('${serverCategory.name}', ['${mcpServer.name}'])"
154
+ class="bg-red-500 hover:bg-red-700 text-white text-sm font-bold py-1 px-3 rounded transition duration-150 ease-in-out">
155
+ Uninstall
156
+ </button>
157
+ </div>
158
+ `}
159
+ </div>
160
+ `;
161
+ });
162
+
163
+ return toolsHtml;
164
+ }
165
+
166
+ // Uninstall tools
167
+ window.uninstallTools = async function(serverName, toolList) {
168
+ const confirmed = await showConfirm(`Are you sure you want to uninstall ${toolList.length} tool(s)?`);
169
+ if (!confirmed) {
170
+ return;
171
+ }
172
+
173
+ try {
174
+ const response = await fetch(`/api/categories/${serverName}/uninstall`, {
175
+ method: 'POST',
176
+ headers: { 'Content-Type': 'application/json' },
177
+ body: JSON.stringify({ toolList })
178
+ });
179
+
180
+ if (!response.ok) {
181
+ const errorData = await response.text();
182
+ throw new Error(`Uninstallation failed: ${errorData || response.statusText}`);
183
+ }
184
+
185
+ const result = await response.json();
186
+ if (!result.success) {
187
+ throw new Error(result.error || 'Uninstallation failed');
188
+ }
189
+
190
+ showToast('Tools uninstalled successfully!', 'success');
191
+ await fetchServerCategories(); // Refresh data
192
+ await showServerDetails(serverName); // Refresh UI
193
+ } catch (error) {
194
+ console.error('Error uninstalling tools:', error);
195
+ showToast(`Error uninstalling tools: ${error.message}`, 'error');
196
+ }
197
+ }
198
+
199
+ // Make showServerDetails available in window scope and handle async
200
+ window.showServerDetails = async function(serverName) {
201
+ try {
202
+ await showServerDetails(serverName);
203
+ } catch (error) {
204
+ console.error('Error showing server details:', error);
205
+ document.getElementById('serverCategoryDetails').innerHTML = '<p class="text-red-500">Error loading server details.</p>';
206
+ showToast(`Error loading server details: ${error.message}`, 'error');
207
+ }
208
+ };
209
+
210
+ export { showServerDetails };
@@ -0,0 +1,82 @@
1
+ import { allServerCategoriesData, fetchServerCategories } from './api.js';
2
+ import { showServerDetails } from './serverCategoryDetails.js';
3
+ import { showToast } from './notifications.js';
4
+
5
+ // Function to render server list (used by fetchServerCategories and search)
6
+ function renderServerCategoryList(servers) {
7
+ const serverCategoryList = document.getElementById('serverCategoryList');
8
+
9
+ if (servers.length === 0) {
10
+ serverCategoryList.innerHTML = '<p class="text-gray-500">No servers found matching search.</p>';
11
+ document.getElementById('serverCategoryDetails').innerHTML = '<p>Select a server from the list to see details.</p>'; // Clear details
12
+ return;
13
+ }
14
+
15
+ serverCategoryList.innerHTML = servers.map(server => {
16
+ let statusHtml = '';
17
+
18
+ // Add tool status summary if available
19
+ if (server.installationStatus && server.installationStatus.serversStatus) {
20
+ const totalTools = Object.keys(server.installationStatus.serversStatus).length;
21
+ const installedTools = Object.values(server.installationStatus.serversStatus)
22
+ .filter(serverStatus =>
23
+ Object.values(serverStatus.installedStatus || {})
24
+ .some(opStatus => opStatus.status === 'completed' && opStatus.type === 'install')
25
+ ).length;
26
+
27
+ if (totalTools > 0) {
28
+ let colorClass, icon, statusText;
29
+
30
+ if (installedTools === totalTools) {
31
+ // All tools installed
32
+ colorClass = "text-green-600 bg-green-50";
33
+ 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 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clip-rule="evenodd"></path></svg>';
34
+ statusText = "Fully Configured";
35
+ } else if (installedTools > 0) {
36
+ // Some tools installed
37
+ colorClass = "text-green-600 bg-green-50";
38
+ 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 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clip-rule="evenodd"></path></svg>';
39
+ statusText = "Partial Configured";
40
+ } else {
41
+ // No tools installed
42
+ colorClass = "text-red-600 bg-red-50";
43
+ 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
+ statusText = "Not Configured";
45
+ }
46
+
47
+ statusHtml = `<span class="${colorClass} inline-flex items-center px-2 py-1 rounded-full text-xs" style="width: fit-content; max-width: 100%;">
48
+ ${icon}<span class="truncate">${statusText} (${installedTools}/${totalTools})</span>
49
+ </span>`;
50
+ }
51
+ }
52
+
53
+ return `
54
+ <div class="server-item border border-gray-200 p-3 rounded hover:bg-gray-50 cursor-pointer transition duration-150 ease-in-out"
55
+ onclick="window.showServerDetails('${server.name}')"
56
+ data-server-name="${server.name}">
57
+ <h3 class="font-semibold text-gray-800">${server.displayName || server.name}</h3>
58
+ <p class="text-sm text-gray-500">${statusHtml}</p>
59
+ </div>
60
+ `;
61
+ }).join('');
62
+ }
63
+
64
+ // Setup search functionality
65
+ function setupSearch() {
66
+ document.getElementById('searchBox').addEventListener('input', function () {
67
+ const searchTerm = this.value.toLowerCase();
68
+
69
+ // Filter the servers list based on search
70
+ if (allServerCategorisData && allServerCategorisData.length > 0) {
71
+ const filteredServers = allServerCategorisData.filter(server =>
72
+ (server.displayName || server.name).toLowerCase().includes(searchTerm) ||
73
+ (server.description || '').toLowerCase().includes(searchTerm)
74
+ );
75
+
76
+ renderServerCategoryList(filteredServers);
77
+ }
78
+ });
79
+ }
80
+
81
+ // Export functions
82
+ export { renderServerCategoryList, setupSearch };
@@ -0,0 +1,61 @@
1
+ <div id="installModal" class="modal">
2
+ <div class="modal-content">
3
+ <span class="close" onclick="closeModal()">&times;</span>
4
+ <div class="modal-header">
5
+ <h2 id="modalTitle" class="text-2xl font-bold text-gray-800"></h2>
6
+ <p class="text-gray-600 mt-2">Configure your installation settings below</p>
7
+ </div>
8
+
9
+ <div class="modal-sections">
10
+ <!-- Requirements Section -->
11
+ <div class="section-container">
12
+ <div id="modalRequirements"></div>
13
+ </div>
14
+
15
+ <!-- Client Status Section -->
16
+ <div class="section-container">
17
+ <div id="modalTargets" class="client-grid"></div>
18
+ </div>
19
+
20
+ <!-- Environment Variables Section -->
21
+ <div class="section-container">
22
+ <div id="modalEnvInputs" class="space-y-4"></div>
23
+ </div>
24
+ </div>
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>
35
+ </div>
36
+ </form>
37
+ </div>
38
+ </div>
39
+
40
+ <!-- Loading Modal -->
41
+ <div id="installLoadingModal" class="modal" style="display:none; z-index:2000;">
42
+ <div class="modal-content" style="display: flex; flex-direction: column; align-items: center; justify-content: center; min-height: 340px; pointer-events:auto;">
43
+ <div style="width: 100%;">
44
+ <div style="display: flex; flex-direction: column; align-items: center; width: 100%;">
45
+ <div class="loading-icon" style="margin-bottom:8px;">
46
+ <svg width="48" height="48" viewBox="0 0 48 48" fill="none" style="display: block; margin-left: auto; margin-right: auto;">
47
+ <circle cx="24" cy="24" r="20" stroke="#888" stroke-width="4" opacity="0.2"/>
48
+ <circle cx="24" cy="24" r="20" stroke="#3498db" stroke-width="4" stroke-linecap="round" stroke-dasharray="100" stroke-dashoffset="60">
49
+ <animateTransform attributeName="transform" type="rotate" from="0 24 24" to="360 24 24" dur="1s" repeatCount="indefinite"/>
50
+ </circle>
51
+ </svg>
52
+ </div>
53
+ <div class="loading-title" style="font-size:1.5rem; font-weight:bold; margin-bottom:8px; text-align:center;">Installing...</div>
54
+ <div id="installLoadingMessage" style="min-height:48px; max-height:160px; overflow:auto; background:#f8f8f8; border-radius:6px; padding:12px; font-size:1rem; color:#444; text-align:left; width:100%;"></div>
55
+ </div>
56
+ </div>
57
+ </div>
58
+ </div>
59
+
60
+ <link rel="stylesheet" href="/css/modal.css">
61
+ <link rel="stylesheet" href="/css/modal.css">
@@ -0,0 +1,155 @@
1
+ /* Import Inter font */
2
+ @import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap');
3
+
4
+ /* Base styles */
5
+ body {
6
+ font-family: 'Inter', system-ui, -apple-system, sans-serif;
7
+ }
8
+
9
+ /* Enhanced modal styling */
10
+ .modal {
11
+ display: none;
12
+ position: fixed;
13
+ z-index: 50;
14
+ left: 0;
15
+ top: 0;
16
+ width: 100%;
17
+ height: 100%;
18
+ overflow: auto;
19
+ background-color: rgba(0, 0, 0, 0.5);
20
+ backdrop-filter: blur(4px);
21
+ }
22
+
23
+ .modal-content {
24
+ background-color: #ffffff;
25
+ margin: 10% auto;
26
+ padding: 2rem;
27
+ border: none;
28
+ border-radius: 1rem;
29
+ width: 90%;
30
+ max-width: 500px;
31
+ box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
32
+ transform: translateY(0);
33
+ transition: all 0.3s ease-out;
34
+ }
35
+
36
+ .close-button {
37
+ color: #6b7280;
38
+ float: right;
39
+ font-size: 24px;
40
+ font-weight: 600;
41
+ transition: color 0.2s ease;
42
+ }
43
+
44
+ .close-button:hover,
45
+ .close-button:focus {
46
+ color: #111827;
47
+ text-decoration: none;
48
+ cursor: pointer;
49
+ }
50
+
51
+ /* Server list styles */
52
+ .server-list {
53
+ border-radius: 0.75rem;
54
+ }
55
+
56
+ .server-item {
57
+ padding: 0.75rem 1rem;
58
+ margin-bottom: 0.5rem;
59
+ border-radius: 0.5rem;
60
+ border: 1px solid #e5e7eb;
61
+ transition: all 0.2s ease;
62
+ }
63
+
64
+ .server-item:hover {
65
+ background-color: #f3f4f6;
66
+ transform: translateY(-1px);
67
+ box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
68
+ }
69
+
70
+ /* Tool card styles */
71
+ .tool-card {
72
+ border: 1px solid #e5e7eb;
73
+ border-radius: 0.75rem;
74
+ padding: 1.25rem;
75
+ margin-bottom: 1rem;
76
+ background-color: #ffffff;
77
+ transition: all 0.2s ease;
78
+ }
79
+
80
+ .tool-card:hover {
81
+ box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1);
82
+ }
83
+
84
+ /* Status badges */
85
+ .status-badge {
86
+ display: inline-flex;
87
+ align-items: center;
88
+ padding: 0.25rem 0.75rem;
89
+ border-radius: 9999px;
90
+ font-size: 0.875rem;
91
+ font-weight: 500;
92
+ }
93
+
94
+ .status-badge.installed {
95
+ background-color: #dcfce7;
96
+ color: #166534;
97
+ }
98
+
99
+ .status-badge.not-installed {
100
+ background-color: #fee2e2;
101
+ color: #991b1b;
102
+ }
103
+
104
+ /* Button styles */
105
+ .btn {
106
+ display: inline-flex;
107
+ align-items: center;
108
+ justify-content: center;
109
+ padding: 0.5rem 1rem;
110
+ border-radius: 0.5rem;
111
+ font-weight: 500;
112
+ transition: all 0.2s ease;
113
+ }
114
+
115
+ .btn-install {
116
+ background-color: #2563eb;
117
+ color: white;
118
+ }
119
+
120
+ .btn-install:hover {
121
+ background-color: #1d4ed8;
122
+ transform: translateY(-1px);
123
+ }
124
+
125
+ .btn-uninstall {
126
+ background-color: #dc2626;
127
+ color: white;
128
+ }
129
+
130
+ .btn-uninstall:hover {
131
+ background-color: #b91c1c;
132
+ transform: translateY(-1px);
133
+ }
134
+
135
+ /* Icon styles */
136
+ .icon {
137
+ width: 1.25rem;
138
+ height: 1.25rem;
139
+ margin-right: 0.5rem;
140
+ }
141
+
142
+ /* Input styles */
143
+ .search-input {
144
+ width: 100%;
145
+ padding: 0.75rem 1rem 0.75rem 2.5rem;
146
+ border: 1px solid #e5e7eb;
147
+ border-radius: 0.5rem;
148
+ transition: all 0.2s ease;
149
+ }
150
+
151
+ .search-input:focus {
152
+ outline: none;
153
+ border-color: #2563eb;
154
+ box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.1);
155
+ }
@@ -0,0 +1,5 @@
1
+ import { ServerInstallOptions } from '../core/types.js';
2
+ export interface InstallServersRequestBody {
3
+ serverList: Record<string, ServerInstallOptions>;
4
+ }
5
+ export declare function startWebServer(port?: number): Promise<void>;
@@ -0,0 +1,150 @@
1
+ import express from 'express';
2
+ import path from 'path';
3
+ import { fileURLToPath } from 'url';
4
+ import { SUPPORTED_CLIENT_NAMES } from '../core/constants.js';
5
+ import { serverService } from '../services/ServerService.js';
6
+ import { openBrowser } from '../utils/osUtils.js';
7
+ import { Logger } from '../utils/logger.js';
8
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
9
+ const app = express();
10
+ // Middleware
11
+ app.use(express.static(path.join(__dirname, 'public')));
12
+ app.use(express.json());
13
+ // Get available targets
14
+ app.get('/api/targets', async (req, res) => {
15
+ try {
16
+ const response = {
17
+ success: true,
18
+ data: SUPPORTED_CLIENT_NAMES
19
+ };
20
+ res.json(response);
21
+ }
22
+ catch (error) {
23
+ const message = error instanceof Error ? error.message : 'Unknown error';
24
+ res.status(500).json({
25
+ success: false,
26
+ error: message
27
+ });
28
+ }
29
+ });
30
+ // List server categories
31
+ app.get('/api/categories', async (req, res) => {
32
+ try {
33
+ const { local } = req.query;
34
+ const servers = await serverService.listServerCategories({
35
+ local: local !== 'false'
36
+ });
37
+ const response = {
38
+ success: true,
39
+ data: servers
40
+ };
41
+ res.json(response);
42
+ }
43
+ catch (error) {
44
+ const message = error instanceof Error ? error.message : 'Unknown error';
45
+ res.status(500).json({
46
+ success: false,
47
+ error: message
48
+ });
49
+ }
50
+ });
51
+ // Get categories data (including feed configuration)
52
+ app.get('/api/categories/:categoryName', async (req, res) => {
53
+ try {
54
+ const { categoryName } = req.params;
55
+ const serverData = await serverService.getServerCategory(categoryName);
56
+ if (!serverData) {
57
+ return res.status(404).json({
58
+ success: false,
59
+ error: `Server category ${categoryName} not found`
60
+ });
61
+ }
62
+ const response = {
63
+ success: true,
64
+ data: serverData
65
+ };
66
+ res.json(response);
67
+ }
68
+ catch (error) {
69
+ const message = error instanceof Error ? error.message : 'Unknown error';
70
+ res.status(500).json({
71
+ success: false,
72
+ error: `Failed to get server category data for ${req.params.categoryName}: ${message}`
73
+ });
74
+ }
75
+ });
76
+ // Install servers for a category
77
+ app.post('/api/categories/:categoryName/install', async (req, res) => {
78
+ try {
79
+ const { categoryName } = req.params;
80
+ const { serverList } = req.body;
81
+ if (!serverList || Object.keys(serverList).length === 0) {
82
+ return res.status(400).json({
83
+ success: false,
84
+ error: 'Invalid server list provided'
85
+ });
86
+ }
87
+ const results = await Promise.all(Object.entries(serverList).map(([serverName, options]) => serverService.installMcpServer(categoryName, serverName, options)));
88
+ const { success, messages } = serverService.formatOperationResults(results);
89
+ const response = {
90
+ success,
91
+ data: { messages }
92
+ };
93
+ res.json(response);
94
+ }
95
+ catch (error) {
96
+ const message = error instanceof Error ? error.message : 'Unknown error';
97
+ res.status(500).json({
98
+ success: false,
99
+ error: `Failed to install server for ${req.params.categoryName}: ${message}`
100
+ });
101
+ }
102
+ });
103
+ // Uninstall tools from a server
104
+ app.post('/api/categories/:categoryName/uninstall', async (req, res) => {
105
+ try {
106
+ const { categoryName } = req.params;
107
+ const { serverList } = req.body;
108
+ if (!Array.isArray(serverList) || serverList.length === 0) {
109
+ return res.status(400).json({
110
+ success: false,
111
+ error: 'Invalid tool list provided'
112
+ });
113
+ }
114
+ const results = await Promise.all(serverList.map(serverName => serverService.uninstallMcpServer(categoryName, serverName)));
115
+ const { success, messages } = serverService.formatOperationResults(results);
116
+ const response = {
117
+ success,
118
+ data: { messages }
119
+ };
120
+ res.json(response);
121
+ }
122
+ catch (error) {
123
+ const message = error instanceof Error ? error.message : 'Unknown error';
124
+ res.status(500).json({
125
+ success: false,
126
+ error: `Failed to uninstall servers from ${req.params.categoryName}: ${message}`
127
+ });
128
+ }
129
+ });
130
+ export async function startWebServer(port = 3000) {
131
+ return new Promise((resolve, reject) => {
132
+ const server = app.listen(port, () => {
133
+ const url = `http://localhost:${port}`;
134
+ Logger.log(`IMCP web interface running at ${url}`);
135
+ // Open the URL in the default browser
136
+ openBrowser(url).catch(err => {
137
+ console.warn(`Failed to open browser: ${err.message}`);
138
+ });
139
+ resolve();
140
+ });
141
+ server.on('error', (error) => {
142
+ reject(error);
143
+ });
144
+ });
145
+ }
146
+ // Allow running directly
147
+ if (import.meta.url === `file://${process.argv[1]}`) {
148
+ startWebServer().catch(console.error);
149
+ }
150
+ //# sourceMappingURL=server.js.map