imcp 0.0.11 → 0.0.13

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 (57) hide show
  1. package/dist/cli/commands/uninstall.js +14 -6
  2. package/dist/core/ConfigurationProvider.d.ts +3 -1
  3. package/dist/core/ConfigurationProvider.js +85 -25
  4. package/dist/core/InstallationService.d.ts +17 -0
  5. package/dist/core/InstallationService.js +127 -61
  6. package/dist/core/MCPManager.d.ts +1 -0
  7. package/dist/core/MCPManager.js +30 -5
  8. package/dist/core/RequirementService.d.ts +4 -4
  9. package/dist/core/RequirementService.js +11 -7
  10. package/dist/core/ServerSchemaLoader.js +2 -2
  11. package/dist/core/ServerSchemaProvider.d.ts +1 -1
  12. package/dist/core/ServerSchemaProvider.js +15 -10
  13. package/dist/core/constants.d.ts +14 -1
  14. package/dist/core/constants.js +4 -1
  15. package/dist/core/installers/clients/ExtensionInstaller.js +3 -0
  16. package/dist/core/installers/requirements/PipInstaller.js +10 -5
  17. package/dist/core/types.d.ts +10 -0
  18. package/dist/services/ServerService.d.ts +12 -1
  19. package/dist/services/ServerService.js +39 -9
  20. package/dist/utils/githubAuth.js +0 -10
  21. package/dist/utils/githubUtils.d.ts +16 -0
  22. package/dist/utils/githubUtils.js +55 -39
  23. package/dist/utils/osUtils.js +1 -1
  24. package/dist/web/public/css/detailsWidget.css +189 -57
  25. package/dist/web/public/css/modal.css +42 -0
  26. package/dist/web/public/css/serverDetails.css +35 -18
  27. package/dist/web/public/index.html +2 -0
  28. package/dist/web/public/js/detailsWidget.js +175 -60
  29. package/dist/web/public/js/modal.js +93 -29
  30. package/dist/web/public/js/notifications.js +34 -35
  31. package/dist/web/public/js/serverCategoryDetails.js +182 -120
  32. package/dist/web/server.js +38 -2
  33. package/package.json +3 -4
  34. package/src/cli/commands/uninstall.ts +16 -6
  35. package/src/core/ConfigurationProvider.ts +102 -25
  36. package/src/core/InstallationService.ts +176 -62
  37. package/src/core/MCPManager.ts +36 -5
  38. package/src/core/RequirementService.ts +12 -8
  39. package/src/core/ServerSchemaLoader.ts +48 -0
  40. package/src/core/ServerSchemaProvider.ts +137 -0
  41. package/src/core/constants.ts +16 -3
  42. package/src/core/installers/clients/ExtensionInstaller.ts +3 -0
  43. package/src/core/installers/requirements/PipInstaller.ts +10 -5
  44. package/src/core/types.ts +11 -1
  45. package/src/services/ServerService.ts +41 -8
  46. package/src/utils/githubAuth.ts +14 -27
  47. package/src/utils/githubUtils.ts +84 -47
  48. package/src/utils/osUtils.ts +1 -1
  49. package/src/web/public/css/detailsWidget.css +235 -0
  50. package/src/web/public/css/modal.css +42 -0
  51. package/src/web/public/css/serverDetails.css +126 -0
  52. package/src/web/public/index.html +2 -0
  53. package/src/web/public/js/detailsWidget.js +264 -0
  54. package/src/web/public/js/modal.js +93 -29
  55. package/src/web/public/js/notifications.js +34 -35
  56. package/src/web/public/js/serverCategoryDetails.js +182 -120
  57. package/src/web/server.ts +52 -3
@@ -1,34 +1,38 @@
1
1
  import { allServerCategoriesData, fetchServerCategories } from './api.js';
2
2
  import { showInstallModal } from './modal.js';
3
3
  import { showToast, showConfirm } from './notifications.js';
4
+ import { DetailsWidget } from './detailsWidget.js';
4
5
 
5
6
  const REFRESH_INTERVAL = 2000; // 2 seconds
6
7
  let refreshTimer = null;
8
+ let activeDetailsWidget = null;
7
9
 
8
10
  // Start refresh timer for installation status
9
11
  function startRefreshTimer(serverName) {
10
- // Clear existing timer if any
11
12
  if (refreshTimer) {
12
13
  clearInterval(refreshTimer);
13
14
  }
14
15
 
15
- // Setup new refresh timer
16
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
17
+ try {
18
+ const server = allServerCategoriesData.find(s => s.name === serverName);
19
+ if (!server?.installationStatus?.serversStatus) return;
20
+
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();
29
+ await showServerDetails(serverName);
30
+ } else {
31
+ clearInterval(refreshTimer);
32
+ }
33
+ } catch (error) {
34
+ console.error('Error in refresh timer:', error);
35
+ clearInterval(refreshTimer);
32
36
  }
33
37
  }, REFRESH_INTERVAL);
34
38
  }
@@ -36,108 +40,177 @@ function startRefreshTimer(serverName) {
36
40
  // Show server details
37
41
  async function showServerDetails(serverName) {
38
42
  console.log("Showing details for:", serverName);
39
- // Save the selected category in localStorage
40
- localStorage.setItem('lastSelectedCategory', serverName);
41
- const server = allServerCategoriesData.find(s => s.name === serverName);
42
- const detailsDiv = document.getElementById('serverCategoryDetails');
43
-
44
- if (!server) {
45
- detailsDiv.innerHTML = '<p class="text-red-500">Error: Server data not found.</p>';
46
- return;
47
- }
43
+ try {
44
+ localStorage.setItem('lastSelectedCategory', serverName);
45
+ const server = allServerCategoriesData.find(s => s.name === serverName);
46
+ const detailsDiv = document.getElementById('serverCategoryDetails');
48
47
 
49
- // Highlight selected server
50
- document.querySelectorAll('.server-item').forEach(item => {
51
- item.classList.remove('bg-blue-100', 'border-blue-300');
52
- if (item.dataset.serverName === serverName) {
53
- item.classList.add('bg-blue-100', 'border-blue-300');
48
+ if (!server) {
49
+ throw new Error('Server data not found');
54
50
  }
55
- });
56
-
57
- // Initial render with loading state
58
- detailsDiv.innerHTML = `
59
- <h3 class="text-xl font-semibold mb-2 text-gray-800">${server.displayName || server.name}</h3>
60
- <p class="mb-4"><span class="font-semibold">Description:</span> ${server.description || 'N/A'}</p>
61
- <div id="toolMcpsList" class="mt-4">
62
- <div class="animate-pulse flex space-x-4">
63
- <div class="flex-1 space-y-4 py-1">
64
- <div class="h-4 bg-gray-200 rounded w-3/4"></div>
65
- <div class="space-y-2">
66
- <div class="h-4 bg-gray-200 rounded"></div>
67
- <div class="h-4 bg-gray-200 rounded w-5/6"></div>
51
+
52
+ detailsDiv.innerHTML = `
53
+ <h3 class="text-xl font-semibold mb-2 text-gray-800">${server.displayName || server.name}</h3>
54
+ <p class="mb-4"><span class="font-semibold">Description:</span> ${server.description || 'N/A'}</p>
55
+ <div id="toolMcpsList" class="mt-4">
56
+ <div class="animate-pulse flex space-x-4">
57
+ <div class="flex-1 space-y-4 py-1">
58
+ <div class="h-4 bg-gray-200 rounded w-3/4"></div>
59
+ <div class="space-y-2">
60
+ <div class="h-4 bg-gray-200 rounded"></div>
61
+ <div class="h-4 bg-gray-200 rounded w-5/6"></div>
62
+ </div>
68
63
  </div>
69
64
  </div>
70
65
  </div>
71
- </div>
72
- `;
66
+ `;
73
67
 
74
- // Asynchronously load and render the servers list
75
- try {
76
68
  const serversListHtml = await renderServersList(server);
77
- document.getElementById('toolMcpsList').innerHTML = serversListHtml;
69
+ const toolMcpsList = document.getElementById('toolMcpsList');
70
+ if (!toolMcpsList) {
71
+ throw new Error('toolMcpsList element not found');
72
+ }
73
+
74
+ toolMcpsList.innerHTML = serversListHtml;
75
+
76
+ // Setup server items click handlers
77
+ const serverItems = document.querySelectorAll('.server-item-content');
78
+ console.log(`Found ${serverItems.length} server items`);
79
+
80
+ serverItems.forEach(item => {
81
+ const mcpServerName = item.dataset.serverName;
82
+ console.log(`Setting up click handler for server: ${mcpServerName}`);
83
+
84
+ const mcpServer = server.feedConfiguration?.mcpServers?.find(
85
+ s => s.name === mcpServerName
86
+ );
87
+
88
+ if (mcpServer) {
89
+ const detailsWidget = new DetailsWidget(item);
90
+
91
+ // Initially set description
92
+ detailsWidget.setContent(mcpServer.description);
93
+
94
+ // Fetch and display schema when expanded
95
+ const fetchSchema = async () => {
96
+ try {
97
+ const response = await fetch(`/api/categories/${server.name}/servers/${mcpServerName}/schema`);
98
+ if (!response.ok) return;
99
+
100
+ const result = await response.json();
101
+ if (result.success && result.data) {
102
+ detailsWidget.setContent(result.data);
103
+ }
104
+ } catch (error) {
105
+ console.error('Error fetching schema:', error);
106
+ // Silently fail - don't show error in UI
107
+ }
108
+ };
109
+
110
+ item.addEventListener('click', async (event) => {
111
+ if (!event.target.closest('button')) {
112
+ if (detailsWidget === activeDetailsWidget) {
113
+ // Only toggle if clicking the currently active item
114
+ detailsWidget.toggle();
115
+ if (!detailsWidget.isVisible()) {
116
+ activeDetailsWidget = null;
117
+ }
118
+ } else {
119
+ // Expand the clicked item without collapsing others
120
+ detailsWidget.expand();
121
+ activeDetailsWidget = detailsWidget;
122
+ await fetchSchema();
123
+ }
124
+ }
125
+ });
126
+ }
127
+ });
128
+
129
+ startRefreshTimer(serverName);
78
130
  } catch (error) {
79
- console.error('Error rendering servers list:', error);
80
- document.getElementById('toolMcpsList').innerHTML = '<p class="text-red-500">Error loading server details.</p>';
131
+ console.error('Error in showServerDetails:', error);
132
+ const detailsDiv = document.getElementById('serverCategoryDetails');
133
+ if (detailsDiv) {
134
+ detailsDiv.innerHTML = `<p class="text-red-500">Error loading server details: ${error.message}</p>`;
135
+ }
81
136
  showToast(`Error loading server details: ${error.message}`, 'error');
82
137
  }
83
-
84
- // Start monitoring installation status
85
- startRefreshTimer(serverName);
86
138
  }
87
139
 
88
- // Render tools list
89
140
  async function renderServersList(serverCategory) {
90
- // Fetch available targets
91
- const targetResponse = await fetch('/api/targets');
92
- const targetData = await targetResponse.json();
93
- const availableTargets = targetData.success ? targetData.data : [];
94
- if (!serverCategory.feedConfiguration?.mcpServers) {
95
- return '<p class="text-gray-500">No MCP Servers found for this server.</p>';
96
- }
141
+ try {
142
+ const targetResponse = await fetch('/api/targets');
143
+ const targetData = await targetResponse.json();
144
+ const availableTargets = targetData.success ? targetData.data : [];
97
145
 
98
- const mcpServers = serverCategory.feedConfiguration.mcpServers;
99
- if (mcpServers.length === 0) {
100
- return '<p class="text-gray-500">No MCP Servers found for this server.</p>';
101
- }
146
+ if (!serverCategory.feedConfiguration?.mcpServers) {
147
+ return '<p class="text-gray-500">No MCP Servers found for this server.</p>';
148
+ }
149
+
150
+ const mcpServers = serverCategory.feedConfiguration.mcpServers;
151
+ if (mcpServers.length === 0) {
152
+ return '<p class="text-gray-500">No MCP Servers found for this server.</p>';
153
+ }
154
+
155
+ let toolsHtml = `
156
+ <div class="flex justify-between items-center mb-4">
157
+ <h2 class="text-lg font-semibold text-gray-600">MCP Servers</h2>
158
+ </div>`;
159
+
160
+ mcpServers.forEach(mcpServer => {
161
+ const isInstalled = mcpServer.installed;
162
+ const envRequirements = mcpServer.installation?.['env:'] || mcpServer.installation?.env || {};
163
+ const hasEnvRequirements = Object.keys(envRequirements).length > 0;
102
164
 
103
- let toolsHtml = `
104
- <div class="flex justify-between items-center mb-4">
105
- <h2 class="text-lg font-semibold text-gray-600">MCP Servers</h2>
106
- </div>`;
107
-
108
- mcpServers.forEach(mcpServer => {
109
- const isInstalled = mcpServer.installed;
110
- const envRequirements = mcpServer.installation?.['env:'] || mcpServer.installation?.env || {};
111
- const hasEnvRequirements = Object.keys(envRequirements).length > 0;
112
-
113
- toolsHtml += `
114
- <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'}"
115
- id="tool-item-${mcpServer.name}">
116
- <div class="flex items-center flex-1">
117
- <div>
118
- <h5 class="font-semibold text-gray-800">${mcpServer.displayName || mcpServer.name}</h5>
119
- <p class="text-sm text-gray-600 mb-1">${mcpServer.description || 'No description'}</p>
120
- <div class="flex flex-col">
165
+ toolsHtml += `
166
+ <div class="server-item-content" data-server-name="${mcpServer.name}">
167
+ <div class="server-item-info" style="width: 100%; box-sizing: border-box;">
168
+ <div class="server-item-header">
169
+ <div class="flex items-center">
170
+ <h5 class="font-semibold text-gray-800">${mcpServer.displayName || mcpServer.name}</h5>
171
+ ${mcpServer.repository || serverCategory.feedConfiguration?.repository ? `
172
+ <a href="${mcpServer.repository || serverCategory.feedConfiguration?.repository}" target="_blank" class="ml-2 text-blue-600 hover:text-blue-800">
173
+ <svg class="w-5 h-5 shadow-md rounded-full p-1 hover:shadow-lg transition-shadow duration-200" fill="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
174
+ <path fill-rule="evenodd" d="M12 2C6.477 2 2 6.484 2 12.017c0 4.425 2.865 8.18 6.839 9.504.5.092.682-.217.682-.483 0-.237-.008-.868-.013-1.703-2.782.605-3.369-1.343-3.369-1.343-.454-1.158-1.11-1.466-1.11-1.466-.908-.62.069-.608.069-.608 1.003.07 1.531 1.032 1.531 1.032.892 1.53 2.341 1.088 2.91.832.092-.647.35-1.088.636-1.338-2.22-.253-4.555-1.113-4.555-4.951 0-1.093.39-1.988 1.029-2.688-.103-.253-.446-1.272.098-2.65 0 0 .84-.27 2.75 1.026A9.564 9.564 0 0112 6.844c.85.004 1.705.115 2.504.337 1.909-1.296 2.747-1.027 2.747-1.027.546 1.379.202 2.398.1 2.651.64.7 1.028 1.595 1.028 2.688 0 3.848-2.339 4.695-4.566 4.943.359.309.678.92.678 1.855 0 1.338-.012 2.419-.012 2.747 0 .268.18.58.688.482A10.019 10.019 0 0022 12.017C22 6.484 17.522 2 12 2z" clip-rule="evenodd"/>
175
+ </svg>
176
+ </a>` : ''}
177
+ </div>
178
+ <p class="text-sm text-gray-600 mb-1">${mcpServer.description || 'No description'}</p>
179
+ </div>
180
+ <div class="flex flex-col mt-3">
121
181
  <span class="text-xs font-semibold mb-2">Client Status:</span>
122
182
  <div class="flex flex-wrap gap-2">
123
183
  ${availableTargets.map(client => {
124
- const status = (serverCategory.installationStatus?.serversStatus[mcpServer.name]?.installedStatus || {})[client];
125
- const isInstalled = status?.status === 'completed';
126
- return `
184
+ const status = (serverCategory.installationStatus?.serversStatus[mcpServer.name]?.installedStatus || {})[client];
185
+ const isInstalled = status?.status === 'completed';
186
+ return `
127
187
  <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">
128
188
  <svg class="w-3 h-3 mr-1" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg">
129
189
  ${isInstalled
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
- }
190
+ ? '<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>'
191
+ : '<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>'
192
+ }
133
193
  </svg>
134
194
  ${client}
135
195
  </span>
136
196
  `;
137
- }).join('')}
197
+ }).join('')}
138
198
  </div>
139
199
  </div>
140
- ${hasEnvRequirements ? `<div class="mt-1 text-xs text-blue-600">
200
+ <div class="action-buttons">
201
+ ${!isInstalled ? `
202
+ <button onclick="event.stopPropagation(); window.showInstallModal('${serverCategory.name}', '${mcpServer.name}')"
203
+ class="bg-blue-500 hover:bg-blue-700 text-white text-xs font-bold py-2 px-4 rounded-full shadow-sm transition duration-150 ease-in-out">
204
+ Setup
205
+ </button>
206
+ ` : `
207
+ <button onclick="event.stopPropagation(); window.uninstallTools('${serverCategory.name}', ['${mcpServer.name}'])"
208
+ class="bg-red-500 hover:bg-red-700 text-white text-sm font-bold py-2 px-4 rounded-full shadow-sm transition duration-150 ease-in-out">
209
+ Uninstall
210
+ </button>
211
+ `}
212
+ </div>
213
+ ${hasEnvRequirements ? `<div class="mt-3 text-xs text-blue-600">
141
214
  <svg class="w-3 h-3 inline mr-1" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg">
142
215
  <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>
143
216
  </svg>
@@ -145,34 +218,21 @@ async function renderServersList(serverCategory) {
145
218
  </div>` : ''}
146
219
  </div>
147
220
  </div>
148
- ${!isInstalled ? `
149
- <button onclick="event.stopPropagation(); window.showInstallModal('${serverCategory.name}', '${mcpServer.name}')"
150
- 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">
151
- Install
152
- </button>
153
- ` : `
154
- <div class="flex items-center gap-2">
155
- <button onclick="event.stopPropagation(); window.uninstallTools('${serverCategory.name}', ['${mcpServer.name}'])"
156
- 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">
157
- Uninstall
158
- </button>
159
- </div>
160
- `}
161
- </div>
162
- `;
163
- });
221
+ `;
222
+ });
164
223
 
165
- return toolsHtml;
224
+ return toolsHtml;
225
+ } catch (error) {
226
+ console.error('Error in renderServersList:', error);
227
+ throw error;
228
+ }
166
229
  }
167
230
 
168
- // Uninstall tools
169
231
  window.uninstallTools = async function (serverName, toolList) {
170
- const confirmed = await showConfirm(`Are you sure you want to uninstall ${toolList.length} tool(s)?`);
171
- if (!confirmed) {
172
- return;
173
- }
174
-
175
232
  try {
233
+ const confirmed = await showConfirm(`Are you sure you want to uninstall ${toolList.length} tool(s)?`);
234
+ if (!confirmed) return;
235
+
176
236
  const response = await fetch(`/api/categories/${serverName}/uninstall`, {
177
237
  method: 'POST',
178
238
  headers: { 'Content-Type': 'application/json' },
@@ -190,21 +250,23 @@ window.uninstallTools = async function (serverName, toolList) {
190
250
  }
191
251
 
192
252
  showToast('Tools uninstalled successfully!', 'success');
193
- await fetchServerCategories(); // Refresh data
194
- await showServerDetails(serverName); // Refresh UI
253
+ await fetchServerCategories();
254
+ await showServerDetails(serverName);
195
255
  } catch (error) {
196
256
  console.error('Error uninstalling tools:', error);
197
257
  showToast(`Error uninstalling tools: ${error.message}`, 'error');
198
258
  }
199
259
  }
200
260
 
201
- // Make showServerDetails available in window scope and handle async
202
261
  window.showServerDetails = async function (serverName) {
203
262
  try {
204
263
  await showServerDetails(serverName);
205
264
  } catch (error) {
206
- console.error('Error showing server details:', error);
207
- document.getElementById('serverCategoryDetails').innerHTML = '<p class="text-red-500">Error loading server details.</p>';
265
+ console.error('Error in window.showServerDetails:', error);
266
+ const detailsDiv = document.getElementById('serverCategoryDetails');
267
+ if (detailsDiv) {
268
+ detailsDiv.innerHTML = `<p class="text-red-500">Error loading server details: ${error.message}</p>`;
269
+ }
208
270
  showToast(`Error loading server details: ${error.message}`, 'error');
209
271
  }
210
272
  };
package/src/web/server.ts CHANGED
@@ -1,6 +1,8 @@
1
1
  import express, { Request, Response } from 'express';
2
2
  import path from 'path';
3
3
  import { fileURLToPath } from 'url';
4
+ import { ServerSchema } from '../core/ServerSchemaProvider.js';
5
+
4
6
  import { SUPPORTED_CLIENT_NAMES } from '../core/constants.js';
5
7
  import { serverService } from '../services/ServerService.js';
6
8
  import { openBrowser } from '../utils/osUtils.js';
@@ -32,7 +34,11 @@ export interface InstallServersRequestBody {
32
34
  }
33
35
 
34
36
  interface UninstallServersRequestBody {
35
- serverList: string[];
37
+ serverList: Record<string, { removeData?: boolean }>;
38
+ options: {
39
+ targets: string[];
40
+ removeData?: boolean;
41
+ };
36
42
  }
37
43
 
38
44
  interface ApiResponse<T> {
@@ -86,6 +92,35 @@ app.get('/api/categories', async (req: Request<{}, {}, {}, ListQueryParams>, res
86
92
  error: message
87
93
  });
88
94
  }
95
+
96
+ // Get server schema
97
+ app.get('/api/categories/:categoryName/servers/:serverName/schema', async (req: Request<{ categoryName: string; serverName: string }>, res: Response) => {
98
+ try {
99
+ const { categoryName, serverName } = req.params;
100
+ const schema = await serverService.getServerSchema(categoryName, serverName);
101
+
102
+ if (!schema) {
103
+ return res.status(404).json({
104
+ success: false,
105
+ error: `Schema not found for server ${serverName} in category ${categoryName}`
106
+ });
107
+ }
108
+
109
+ const response: ApiResponse<ServerSchema> = {
110
+ success: true,
111
+ data: schema
112
+ };
113
+
114
+ res.json(response);
115
+ } catch (error) {
116
+ const message = error instanceof Error ? error.message : 'Unknown error';
117
+ res.status(500).json({
118
+ success: false,
119
+ error: `Failed to get schema for server ${req.params.serverName} in category ${req.params.categoryName}: ${message}`
120
+ });
121
+ }
122
+ });
123
+
89
124
  });
90
125
 
91
126
  // Get categories data (including feed configuration)
@@ -155,15 +190,29 @@ app.post('/api/categories/:categoryName/uninstall', async (req: Request<{ catego
155
190
  const { categoryName } = req.params;
156
191
  const { serverList } = req.body;
157
192
 
158
- if (!Array.isArray(serverList) || serverList.length === 0) {
193
+ if (!serverList || Object.keys(serverList).length === 0) {
159
194
  return res.status(400).json({
160
195
  success: false,
161
196
  error: 'Invalid tool list provided'
162
197
  });
163
198
  }
164
199
 
200
+ const { options } = req.body;
201
+ if (!options?.targets || options.targets.length === 0) {
202
+ return res.status(400).json({
203
+ success: false,
204
+ error: 'No target clients specified'
205
+ });
206
+ }
207
+
165
208
  const results = await Promise.all(
166
- serverList.map(serverName => serverService.uninstallMcpServer(categoryName, serverName))
209
+ Object.entries(serverList).map(([serverName, serverOptions]) =>
210
+ serverService.uninstallMcpServer(categoryName, serverName, {
211
+ ...serverOptions,
212
+ targets: options.targets,
213
+ removeData: options.removeData ?? serverOptions.removeData
214
+ })
215
+ )
167
216
  );
168
217
 
169
218
  const { success, messages } = serverService.formatOperationResults(results);