imcp 0.0.18 → 0.1.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.
- package/.roo/rules-code/rules.md +88 -0
- package/dist/cli/index.js +0 -0
- package/dist/core/metadatas/constants.d.ts +7 -0
- package/dist/core/metadatas/constants.js +7 -0
- package/dist/core/onboard/FeedOnboardService.d.ts +7 -3
- package/dist/core/onboard/FeedOnboardService.js +52 -5
- package/dist/core/onboard/OnboardProcessor.js +22 -22
- package/dist/services/MCPManager.js +66 -6
- package/dist/services/TelemetryService.d.ts +15 -0
- package/dist/services/TelemetryService.js +54 -0
- package/dist/utils/githubAuth.js +65 -0
- package/dist/utils/logger.d.ts +16 -0
- package/dist/utils/logger.js +78 -1
- package/dist/utils/versionUtils.d.ts +1 -0
- package/dist/utils/versionUtils.js +29 -0
- package/dist/web/public/css/serverCategoryList.css +120 -0
- package/dist/web/public/index.html +6 -3
- package/dist/web/public/js/flights/flights.js +0 -1
- package/dist/web/public/js/onboard/formProcessor.js +18 -11
- package/dist/web/public/js/onboard/publishHandler.js +30 -0
- package/dist/web/public/js/onboard/templates.js +5 -1
- package/dist/web/public/js/onboard/uiHandlers.js +266 -39
- package/dist/web/public/js/onboard/validationHandlers.js +71 -39
- package/dist/web/public/js/serverCategoryList.js +91 -7
- package/dist/web/public/onboard.html +2 -2
- package/dist/web/server.js +11 -1
- package/{src/web/public/js/onboard → docs}/ONBOARDING_PAGE_DESIGN.md +15 -125
- package/docs/Telemetry.md +136 -0
- package/memory-bank/activeContext.md +14 -0
- package/memory-bank/decisionLog.md +28 -0
- package/memory-bank/productContext.md +41 -0
- package/memory-bank/progress.md +5 -0
- package/memory-bank/systemPatterns.md +3 -0
- package/package.json +2 -1
- package/src/core/metadatas/constants.ts +9 -0
- package/src/core/onboard/FeedOnboardService.ts +59 -5
- package/src/core/onboard/OnboardProcessor.ts +25 -23
- package/src/services/MCPManager.ts +78 -8
- package/src/services/TelemetryService.ts +59 -0
- package/src/utils/githubAuth.ts +84 -1
- package/src/utils/logger.ts +83 -1
- package/src/utils/versionUtils.ts +33 -0
- package/src/web/public/css/serverCategoryList.css +120 -0
- package/src/web/public/index.html +6 -3
- package/src/web/public/js/onboard/formProcessor.js +18 -11
- package/src/web/public/js/onboard/publishHandler.js +30 -0
- package/src/web/public/js/onboard/templates.js +5 -1
- package/src/web/public/js/onboard/uiHandlers.js +266 -39
- package/src/web/public/js/onboard/validationHandlers.js +71 -39
- package/src/web/public/js/serverCategoryList.js +91 -7
- package/src/web/public/onboard.html +2 -2
- package/src/web/server.ts +11 -1
- package/dist/cli/commands/start.d.ts +0 -2
- package/dist/cli/commands/start.js +0 -32
- package/dist/cli/commands/sync.d.ts +0 -2
- package/dist/cli/commands/sync.js +0 -17
- package/dist/core/ConfigurationLoader.d.ts +0 -32
- package/dist/core/ConfigurationLoader.js +0 -236
- package/dist/core/ConfigurationProvider.d.ts +0 -35
- package/dist/core/ConfigurationProvider.js +0 -375
- package/dist/core/InstallationService.d.ts +0 -50
- package/dist/core/InstallationService.js +0 -350
- package/dist/core/MCPManager.d.ts +0 -28
- package/dist/core/MCPManager.js +0 -188
- package/dist/core/RequirementService.d.ts +0 -40
- package/dist/core/RequirementService.js +0 -110
- package/dist/core/ServerSchemaLoader.d.ts +0 -11
- package/dist/core/ServerSchemaLoader.js +0 -43
- package/dist/core/ServerSchemaProvider.d.ts +0 -17
- package/dist/core/ServerSchemaProvider.js +0 -120
- package/dist/core/constants.d.ts +0 -47
- package/dist/core/constants.js +0 -94
- package/dist/core/installers/BaseInstaller.d.ts +0 -74
- package/dist/core/installers/BaseInstaller.js +0 -253
- package/dist/core/installers/ClientInstaller.d.ts +0 -23
- package/dist/core/installers/ClientInstaller.js +0 -564
- package/dist/core/installers/CommandInstaller.d.ts +0 -37
- package/dist/core/installers/CommandInstaller.js +0 -173
- package/dist/core/installers/GeneralInstaller.d.ts +0 -33
- package/dist/core/installers/GeneralInstaller.js +0 -85
- package/dist/core/installers/InstallerFactory.d.ts +0 -54
- package/dist/core/installers/InstallerFactory.js +0 -97
- package/dist/core/installers/NpmInstaller.d.ts +0 -26
- package/dist/core/installers/NpmInstaller.js +0 -127
- package/dist/core/installers/PipInstaller.d.ts +0 -28
- package/dist/core/installers/PipInstaller.js +0 -127
- package/dist/core/installers/RequirementInstaller.d.ts +0 -33
- package/dist/core/installers/RequirementInstaller.js +0 -3
- package/dist/core/types.d.ts +0 -166
- package/dist/core/types.js +0 -16
- package/dist/services/InstallRequestValidator.d.ts +0 -21
- package/dist/services/InstallRequestValidator.js +0 -99
- package/dist/web/public/js/modal/installHandler.js +0 -227
- package/dist/web/public/js/modal/loadingUI.js +0 -74
- package/dist/web/public/js/modal/modalUI.js +0 -214
- package/dist/web/public/js/modal/version.js +0 -20
|
@@ -3,6 +3,9 @@ import { showServerDetails } from './serverCategoryDetails.js'; // Still needed
|
|
|
3
3
|
import { showToast } from './notifications.js';
|
|
4
4
|
import { buildUrlWithFlights } from './flights/flights.js';
|
|
5
5
|
|
|
6
|
+
// Store state for categories
|
|
7
|
+
let pinnedCategories = {};
|
|
8
|
+
|
|
6
9
|
// Wait for data to be loaded
|
|
7
10
|
async function waitForData() {
|
|
8
11
|
if (allServerCategoriesData && allServerCategoriesData.length > 0) {
|
|
@@ -12,6 +15,54 @@ async function waitForData() {
|
|
|
12
15
|
return allServerCategoriesData && allServerCategoriesData.length > 0;
|
|
13
16
|
}
|
|
14
17
|
|
|
18
|
+
// Load pinned state from localStorage
|
|
19
|
+
function loadPinnedState() {
|
|
20
|
+
const saved = localStorage.getItem('pinnedCategories');
|
|
21
|
+
if (saved) {
|
|
22
|
+
try {
|
|
23
|
+
pinnedCategories = JSON.parse(saved);
|
|
24
|
+
} catch (e) {
|
|
25
|
+
console.error('Error parsing pinned categories from localStorage:', e);
|
|
26
|
+
pinnedCategories = {};
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Save pinned state to localStorage
|
|
32
|
+
function savePinnedState() {
|
|
33
|
+
localStorage.setItem('pinnedCategories', JSON.stringify(pinnedCategories));
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Toggle pin status for a category
|
|
37
|
+
function togglePinCategory(categoryName, event) {
|
|
38
|
+
// Stop propagation to prevent navigation
|
|
39
|
+
event.stopPropagation();
|
|
40
|
+
|
|
41
|
+
// Toggle pin status
|
|
42
|
+
if (pinnedCategories[categoryName]) {
|
|
43
|
+
delete pinnedCategories[categoryName];
|
|
44
|
+
} else {
|
|
45
|
+
pinnedCategories[categoryName] = true;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Save to localStorage
|
|
49
|
+
savePinnedState();
|
|
50
|
+
|
|
51
|
+
// Find the server item element and add a temporary animation class
|
|
52
|
+
const serverItem = event.target.closest('.server-item');
|
|
53
|
+
if (serverItem) {
|
|
54
|
+
serverItem.classList.add('pin-animation');
|
|
55
|
+
setTimeout(() => {
|
|
56
|
+
serverItem.classList.remove('pin-animation');
|
|
57
|
+
}, 300); // Animation duration
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Re-render list with updated pin status
|
|
61
|
+
if (allServerCategoriesData && allServerCategoriesData.length > 0) {
|
|
62
|
+
renderServerCategoryList(allServerCategoriesData);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
15
66
|
// Function to show the last selected category on page load
|
|
16
67
|
async function loadLastSelectedCategory() {
|
|
17
68
|
const lastSelected = localStorage.getItem('lastSelectedCategory');
|
|
@@ -33,8 +84,24 @@ function renderServerCategoryList(servers) {
|
|
|
33
84
|
document.getElementById('serverCategoryDetails').innerHTML = '<p>Select a server from the list to see details.</p>'; // Clear details
|
|
34
85
|
return;
|
|
35
86
|
}
|
|
87
|
+
|
|
88
|
+
// Load pinned state
|
|
89
|
+
loadPinnedState();
|
|
90
|
+
|
|
91
|
+
// Create a copy of the servers array to avoid modifying the original
|
|
92
|
+
const sortedServers = [...servers];
|
|
93
|
+
|
|
94
|
+
// Sort servers with pinned ones at the top
|
|
95
|
+
sortedServers.sort((a, b) => {
|
|
96
|
+
const isPinnedA = pinnedCategories[a.name] === true;
|
|
97
|
+
const isPinnedB = pinnedCategories[b.name] === true;
|
|
98
|
+
|
|
99
|
+
if (isPinnedA && !isPinnedB) return -1;
|
|
100
|
+
if (!isPinnedA && isPinnedB) return 1;
|
|
101
|
+
return 0; // Keep original order for items with same pin status
|
|
102
|
+
});
|
|
36
103
|
|
|
37
|
-
serverCategoryList.innerHTML =
|
|
104
|
+
serverCategoryList.innerHTML = sortedServers.map(server => {
|
|
38
105
|
let statusHtml = '';
|
|
39
106
|
|
|
40
107
|
// Add tool status summary if available
|
|
@@ -80,13 +147,27 @@ function renderServerCategoryList(servers) {
|
|
|
80
147
|
}
|
|
81
148
|
systemTagsHtml += '</div>';
|
|
82
149
|
}
|
|
150
|
+
// Check if this server is pinned
|
|
151
|
+
const isPinned = pinnedCategories[server.name] === true;
|
|
152
|
+
const pinnedClass = isPinned ? 'pinned' : '';
|
|
153
|
+
|
|
154
|
+
// Pin/unpin button with appropriate icon
|
|
155
|
+
const pinIcon = isPinned
|
|
156
|
+
? '<i class="bx bxs-pin"></i>' // Solid pin icon for pinned items
|
|
157
|
+
: '<i class="bx bx-pin"></i>'; // Outline pin icon for unpinned items
|
|
83
158
|
|
|
84
159
|
return `
|
|
85
|
-
<div class="server-item border border-gray-200 p-3 rounded hover:bg-gray-50 cursor-pointer transition duration-150 ease-in-out"
|
|
86
|
-
onclick="navigateToCategory('${server.name}')"
|
|
160
|
+
<div class="server-item border border-gray-200 p-3 rounded hover:bg-gray-50 cursor-pointer transition duration-150 ease-in-out ${pinnedClass}"
|
|
87
161
|
data-server-name="${server.name}">
|
|
88
|
-
<
|
|
89
|
-
|
|
162
|
+
<div class="flex justify-between items-center">
|
|
163
|
+
<h3 class="font-semibold text-gray-800" onclick="navigateToCategory('${server.name}')">${server.displayName || server.name}</h3>
|
|
164
|
+
<div class="flex items-center">
|
|
165
|
+
<div class="pin-button ${pinnedClass}" onclick="togglePinCategoryItem('${server.name}', event)" title="${isPinned ? 'Unpin' : 'Pin'} this category">
|
|
166
|
+
${pinIcon}
|
|
167
|
+
</div>
|
|
168
|
+
</div>
|
|
169
|
+
</div>
|
|
170
|
+
<div class="text-sm text-gray-500 flex items-center mt-1" onclick="navigateToCategory('${server.name}')">
|
|
90
171
|
${statusHtml}
|
|
91
172
|
${systemTagsHtml}
|
|
92
173
|
</div>
|
|
@@ -101,6 +182,9 @@ function renderServerCategoryList(servers) {
|
|
|
101
182
|
// Setup search functionality
|
|
102
183
|
function setupSearch() {
|
|
103
184
|
const searchBox = document.getElementById('searchBox');
|
|
185
|
+
|
|
186
|
+
// Load pinned state on page load
|
|
187
|
+
loadPinnedState();
|
|
104
188
|
|
|
105
189
|
searchBox.addEventListener('input', async function () {
|
|
106
190
|
const searchTerm = this.value.toLowerCase();
|
|
@@ -151,7 +235,7 @@ function navigateToCategory(categoryName) {
|
|
|
151
235
|
// Alternatively, attach event listeners dynamically after rendering the list.
|
|
152
236
|
// For simplicity with current structure, we'll make it global.
|
|
153
237
|
window.navigateToCategory = navigateToCategory;
|
|
154
|
-
|
|
238
|
+
window.togglePinCategoryItem = togglePinCategory;
|
|
155
239
|
|
|
156
240
|
// Export functions
|
|
157
|
-
export { renderServerCategoryList, setupSearch, loadLastSelectedCategory };
|
|
241
|
+
export { renderServerCategoryList, setupSearch, loadLastSelectedCategory, togglePinCategory };
|
|
@@ -106,8 +106,8 @@
|
|
|
106
106
|
placeholder="e.g., Coder Tools">
|
|
107
107
|
</div>
|
|
108
108
|
<div class="md:col-span-2">
|
|
109
|
-
<label class="block text-sm font-medium text-gray-700 mb-1">Description
|
|
110
|
-
<textarea name="description" rows="3"
|
|
109
|
+
<label class="block text-sm font-medium text-gray-700 mb-1">Description*</label>
|
|
110
|
+
<textarea name="description" rows="3" required
|
|
111
111
|
class="w-full px-3 py-1.5 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
|
|
112
112
|
placeholder="Describe the purpose and capabilities of this server category"></textarea>
|
|
113
113
|
</div>
|
package/src/web/server.ts
CHANGED
|
@@ -25,7 +25,7 @@ import { SUPPORTED_CLIENT_NAMES } from '../core/metadatas/constants.js';
|
|
|
25
25
|
import { serverService } from '../services/ServerService.js';
|
|
26
26
|
import { feedOnboardService } from '../core/onboard/FeedOnboardService.js';
|
|
27
27
|
import { openBrowser } from '../utils/osUtils.js';
|
|
28
|
-
import { Logger } from '../utils/logger.js';
|
|
28
|
+
import { Logger, EventType, EventStatus } from '../utils/logger.js';
|
|
29
29
|
import { configProvider } from '../core/loaders/ConfigurationProvider.js';
|
|
30
30
|
import { onboardStatusManager } from '../core/onboard/OnboardStatusManager.js';
|
|
31
31
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
@@ -368,10 +368,20 @@ export async function startWebServer(port = 3000): Promise<void> {
|
|
|
368
368
|
console.warn(`Failed to open browser: ${err.message}`);
|
|
369
369
|
});
|
|
370
370
|
|
|
371
|
+
// Track IMCP serve event
|
|
372
|
+
Logger.trackEvent(EventType.IMCP_SERVE, {
|
|
373
|
+
status: EventStatus.SUCCESS,
|
|
374
|
+
port: port,
|
|
375
|
+
});
|
|
371
376
|
resolve();
|
|
372
377
|
});
|
|
373
378
|
|
|
374
379
|
server.on('error', (error) => {
|
|
380
|
+
Logger.trackEvent(EventType.IMCP_SERVE, {
|
|
381
|
+
status: EventStatus.FAILED,
|
|
382
|
+
errorMessage: error instanceof Error ? error.message : String(error),
|
|
383
|
+
port: port
|
|
384
|
+
});
|
|
375
385
|
reject(error);
|
|
376
386
|
});
|
|
377
387
|
});
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
import { Command } from 'commander';
|
|
2
|
-
import { startWebServer } from '../../web/server.js';
|
|
3
|
-
import { mcpManager } from '../../core/MCPManager.js';
|
|
4
|
-
export function createStartCommand() {
|
|
5
|
-
return new Command('start')
|
|
6
|
-
.description('Start local web interface')
|
|
7
|
-
.option('-p, --port <port>', 'Port to run the server on', '3000')
|
|
8
|
-
.action(async (options) => {
|
|
9
|
-
try {
|
|
10
|
-
// Sync feeds before start the local UI
|
|
11
|
-
await mcpManager.syncFeeds();
|
|
12
|
-
// Ensure MCP manager is initialized before starting the web server
|
|
13
|
-
await mcpManager.initialize();
|
|
14
|
-
const port = parseInt(options.port, 10);
|
|
15
|
-
if (isNaN(port) || port < 1 || port > 65535) {
|
|
16
|
-
throw new Error('Invalid port number');
|
|
17
|
-
}
|
|
18
|
-
await startWebServer(port);
|
|
19
|
-
// The server is running, keep the process alive
|
|
20
|
-
process.on('SIGINT', () => {
|
|
21
|
-
console.log('\nShutting down server...');
|
|
22
|
-
process.exit(0);
|
|
23
|
-
});
|
|
24
|
-
}
|
|
25
|
-
catch (error) {
|
|
26
|
-
const message = error instanceof Error ? error.message : 'Unknown error';
|
|
27
|
-
console.error('Failed to start web server:', message);
|
|
28
|
-
process.exit(1);
|
|
29
|
-
}
|
|
30
|
-
});
|
|
31
|
-
}
|
|
32
|
-
//# sourceMappingURL=start.js.map
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import { Command } from 'commander';
|
|
2
|
-
import { serverService } from '../../services/ServerService.js';
|
|
3
|
-
export function createSyncCommand() {
|
|
4
|
-
return new Command('sync')
|
|
5
|
-
.description('Sync MCP server configurations from remote feed source')
|
|
6
|
-
.action(async () => {
|
|
7
|
-
try {
|
|
8
|
-
await serverService.syncFeeds();
|
|
9
|
-
}
|
|
10
|
-
catch (error) {
|
|
11
|
-
const message = error instanceof Error ? error.message : 'Unknown error';
|
|
12
|
-
console.error('Error syncing configurations:', message);
|
|
13
|
-
process.exit(1);
|
|
14
|
-
}
|
|
15
|
-
});
|
|
16
|
-
}
|
|
17
|
-
//# sourceMappingURL=sync.js.map
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
import { MCPConfiguration } from './types.js';
|
|
2
|
-
export declare class ConfigurationLoader {
|
|
3
|
-
/**
|
|
4
|
-
* Updates the installed status for a server and client combination
|
|
5
|
-
*/
|
|
6
|
-
private static updateServerInstalledStatus;
|
|
7
|
-
/**
|
|
8
|
-
* Removes a client's status from a server
|
|
9
|
-
*/
|
|
10
|
-
private static removeClientStatus;
|
|
11
|
-
/**
|
|
12
|
-
* Synchronizes server categories with client MCP settings.
|
|
13
|
-
* Uses clientMCPSettings as source of truth for installation status.
|
|
14
|
-
*/
|
|
15
|
-
static syncServerCategoriesWithClientSettings(configuration: MCPConfiguration): MCPConfiguration;
|
|
16
|
-
/**
|
|
17
|
-
* Initializes installation status for a feed configuration
|
|
18
|
-
*/
|
|
19
|
-
private static initializeInstallationStatus;
|
|
20
|
-
/**
|
|
21
|
-
* Synchronizes server categories with feeds
|
|
22
|
-
*/
|
|
23
|
-
private static syncServerCategoriesWithFeeds;
|
|
24
|
-
/**
|
|
25
|
-
* Loads feed configurations into the MCP configuration
|
|
26
|
-
*/
|
|
27
|
-
static loadFeedsIntoConfiguration(configuration: MCPConfiguration, feedFile?: string): Promise<MCPConfiguration>;
|
|
28
|
-
/**
|
|
29
|
-
* Loads MCP client settings into the configuration
|
|
30
|
-
*/
|
|
31
|
-
static loadClientMCPSettings(configuration: MCPConfiguration): Promise<MCPConfiguration>;
|
|
32
|
-
}
|
|
@@ -1,236 +0,0 @@
|
|
|
1
|
-
import fs from 'fs/promises';
|
|
2
|
-
import path from 'path';
|
|
3
|
-
import { LOCAL_FEEDS_DIR, SUPPORTED_CLIENTS } from './constants.js';
|
|
4
|
-
import { Logger } from '../utils/logger.js';
|
|
5
|
-
import { readJsonFile } from '../utils/clientUtils.js';
|
|
6
|
-
export class ConfigurationLoader {
|
|
7
|
-
/**
|
|
8
|
-
* Updates the installed status for a server and client combination
|
|
9
|
-
*/
|
|
10
|
-
static updateServerInstalledStatus(serverStatus, clientName, operationStatus) {
|
|
11
|
-
if (!serverStatus.installedStatus) {
|
|
12
|
-
serverStatus.installedStatus = {};
|
|
13
|
-
}
|
|
14
|
-
serverStatus.installedStatus[clientName] = operationStatus;
|
|
15
|
-
}
|
|
16
|
-
/**
|
|
17
|
-
* Removes a client's status from a server
|
|
18
|
-
*/
|
|
19
|
-
static removeClientStatus(serverStatus, clientName) {
|
|
20
|
-
if (serverStatus.installedStatus && serverStatus.installedStatus[clientName]) {
|
|
21
|
-
delete serverStatus.installedStatus[clientName];
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
/**
|
|
25
|
-
* Synchronizes server categories with client MCP settings.
|
|
26
|
-
* Uses clientMCPSettings as source of truth for installation status.
|
|
27
|
-
*/
|
|
28
|
-
static syncServerCategoriesWithClientSettings(configuration) {
|
|
29
|
-
if (!configuration.clientMCPSettings) {
|
|
30
|
-
return configuration;
|
|
31
|
-
}
|
|
32
|
-
configuration.localServerCategories = configuration.localServerCategories.map(category => {
|
|
33
|
-
if (!category.installationStatus?.serversStatus) {
|
|
34
|
-
return category;
|
|
35
|
-
}
|
|
36
|
-
const updatedServerStatus = { ...category.installationStatus.serversStatus };
|
|
37
|
-
const clientSettings = configuration.clientMCPSettings;
|
|
38
|
-
for (const [clientName, settings] of Object.entries(clientSettings)) {
|
|
39
|
-
const clientServers = clientName === 'GithubCopilot'
|
|
40
|
-
? settings.servers || {}
|
|
41
|
-
: settings.mcpServers || {};
|
|
42
|
-
Object.keys(updatedServerStatus).forEach(serverName => {
|
|
43
|
-
if (clientServers[serverName]) {
|
|
44
|
-
if (!updatedServerStatus[serverName].installedStatus[clientName]) {
|
|
45
|
-
const operationStatus = {
|
|
46
|
-
status: 'completed',
|
|
47
|
-
type: 'install',
|
|
48
|
-
target: 'server',
|
|
49
|
-
message: `Server ${serverName} is configured for client ${clientName}`
|
|
50
|
-
};
|
|
51
|
-
ConfigurationLoader.updateServerInstalledStatus(updatedServerStatus[serverName], clientName, operationStatus);
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
else {
|
|
55
|
-
ConfigurationLoader.removeClientStatus(updatedServerStatus[serverName], clientName);
|
|
56
|
-
}
|
|
57
|
-
});
|
|
58
|
-
}
|
|
59
|
-
return {
|
|
60
|
-
...category,
|
|
61
|
-
installationStatus: {
|
|
62
|
-
...category.installationStatus,
|
|
63
|
-
serversStatus: updatedServerStatus,
|
|
64
|
-
lastUpdated: new Date().toISOString()
|
|
65
|
-
}
|
|
66
|
-
};
|
|
67
|
-
});
|
|
68
|
-
return configuration;
|
|
69
|
-
}
|
|
70
|
-
/**
|
|
71
|
-
* Initializes installation status for a feed configuration
|
|
72
|
-
*/
|
|
73
|
-
static initializeInstallationStatus(feedConfig) {
|
|
74
|
-
const requirementsStatus = {};
|
|
75
|
-
const serversStatus = {};
|
|
76
|
-
if (feedConfig) {
|
|
77
|
-
if (feedConfig.requirements) {
|
|
78
|
-
for (const req of feedConfig.requirements) {
|
|
79
|
-
requirementsStatus[req.name] = {
|
|
80
|
-
name: req.name,
|
|
81
|
-
type: req.type,
|
|
82
|
-
installed: false,
|
|
83
|
-
version: req.version,
|
|
84
|
-
error: undefined
|
|
85
|
-
};
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
if (feedConfig.mcpServers) {
|
|
89
|
-
for (const mcp of feedConfig.mcpServers) {
|
|
90
|
-
serversStatus[mcp.name] = {
|
|
91
|
-
name: mcp.name,
|
|
92
|
-
error: undefined,
|
|
93
|
-
installedStatus: {}
|
|
94
|
-
};
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
return {
|
|
99
|
-
requirementsStatus,
|
|
100
|
-
serversStatus,
|
|
101
|
-
lastUpdated: new Date().toISOString()
|
|
102
|
-
};
|
|
103
|
-
}
|
|
104
|
-
/**
|
|
105
|
-
* Synchronizes server categories with feeds
|
|
106
|
-
*/
|
|
107
|
-
static async syncServerCategoriesWithFeeds(configuration) {
|
|
108
|
-
// Filter out categories that don't have corresponding feeds and update existing ones
|
|
109
|
-
configuration.localServerCategories = configuration.localServerCategories
|
|
110
|
-
.filter(server => configuration.feeds[server.name])
|
|
111
|
-
.map(server => {
|
|
112
|
-
server.feedConfiguration = configuration.feeds[server.name];
|
|
113
|
-
if (!server.installationStatus ||
|
|
114
|
-
!server.installationStatus.requirementsStatus ||
|
|
115
|
-
Object.keys(server.installationStatus.requirementsStatus).length === 0 ||
|
|
116
|
-
!server.installationStatus.serversStatus ||
|
|
117
|
-
Object.keys(server.installationStatus.serversStatus).length < Object.keys(server.feedConfiguration?.mcpServers || []).length) {
|
|
118
|
-
server.installationStatus = ConfigurationLoader.initializeInstallationStatus(server.feedConfiguration);
|
|
119
|
-
}
|
|
120
|
-
return server;
|
|
121
|
-
});
|
|
122
|
-
// Add new categories for feeds that don't have a corresponding category
|
|
123
|
-
const existingServerNames = new Set(configuration.localServerCategories.map(category => category.name));
|
|
124
|
-
for (const [feedName, feedConfig] of Object.entries(configuration.feeds)) {
|
|
125
|
-
if (!existingServerNames.has(feedName)) {
|
|
126
|
-
const newServerCategory = {
|
|
127
|
-
name: feedName,
|
|
128
|
-
displayName: feedConfig.displayName || feedName,
|
|
129
|
-
type: 'local',
|
|
130
|
-
description: feedConfig.description || `Local MCP server category: ${feedName}`,
|
|
131
|
-
installationStatus: ConfigurationLoader.initializeInstallationStatus(feedConfig),
|
|
132
|
-
feedConfiguration: feedConfig
|
|
133
|
-
};
|
|
134
|
-
configuration.localServerCategories.push(newServerCategory);
|
|
135
|
-
console.log(`Created new local server entry for feed: ${feedName}`);
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
return configuration;
|
|
139
|
-
}
|
|
140
|
-
/**
|
|
141
|
-
* Loads feed configurations into the MCP configuration
|
|
142
|
-
*/
|
|
143
|
-
static async loadFeedsIntoConfiguration(configuration, feedFile) {
|
|
144
|
-
try {
|
|
145
|
-
await fs.mkdir(LOCAL_FEEDS_DIR, { recursive: true });
|
|
146
|
-
const feeds = {};
|
|
147
|
-
// Load provided feed file if specified
|
|
148
|
-
if (feedFile) {
|
|
149
|
-
try {
|
|
150
|
-
const content = await fs.readFile(feedFile, 'utf8');
|
|
151
|
-
const config = JSON.parse(content);
|
|
152
|
-
if (config && config.name) {
|
|
153
|
-
feeds[config.name] = config;
|
|
154
|
-
console.log(`Loaded feed configuration from provided file: ${feedFile}`);
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
catch (error) {
|
|
158
|
-
console.log(`Error loading feed configuration from provided file ${feedFile}:`, error);
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
// Load feeds from LOCAL_FEEDS_DIR
|
|
162
|
-
const files = await fs.readdir(LOCAL_FEEDS_DIR);
|
|
163
|
-
const jsonFiles = files.filter(file => file.endsWith('.json'));
|
|
164
|
-
if (jsonFiles.length === 0 && !feedFile) {
|
|
165
|
-
console.log(`No feed configuration files found in ${LOCAL_FEEDS_DIR}`);
|
|
166
|
-
return configuration;
|
|
167
|
-
}
|
|
168
|
-
for (const file of jsonFiles) {
|
|
169
|
-
try {
|
|
170
|
-
const filePath = path.join(LOCAL_FEEDS_DIR, file);
|
|
171
|
-
const content = await fs.readFile(filePath, 'utf8');
|
|
172
|
-
const config = JSON.parse(content);
|
|
173
|
-
if (config && config.name) {
|
|
174
|
-
// If feed exists from provided file, skip the local one
|
|
175
|
-
if (!feeds[config.name]) {
|
|
176
|
-
feeds[config.name] = config;
|
|
177
|
-
}
|
|
178
|
-
else {
|
|
179
|
-
console.log(`Skipping local feed ${config.name} as it was provided via --feed-file`);
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
catch (error) {
|
|
184
|
-
console.warn(`Error loading feed configuration from ${file}:`, error);
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
configuration.feeds = feeds;
|
|
188
|
-
return await ConfigurationLoader.syncServerCategoriesWithFeeds(configuration);
|
|
189
|
-
}
|
|
190
|
-
catch (error) {
|
|
191
|
-
console.error("Error loading feed configurations:", error);
|
|
192
|
-
throw error;
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
/**
|
|
196
|
-
* Loads MCP client settings into the configuration
|
|
197
|
-
*/
|
|
198
|
-
static async loadClientMCPSettings(configuration) {
|
|
199
|
-
try {
|
|
200
|
-
Logger.debug('Starting to load MCP client settings...');
|
|
201
|
-
const settings = {};
|
|
202
|
-
for (const [clientName, clientSettings] of Object.entries(SUPPORTED_CLIENTS)) {
|
|
203
|
-
const settingPath = process.env.CODE_INSIDERS
|
|
204
|
-
? clientSettings.codeInsiderSettingPath
|
|
205
|
-
: clientSettings.codeSettingPath;
|
|
206
|
-
try {
|
|
207
|
-
let content = await readJsonFile(settingPath, true);
|
|
208
|
-
if (clientName === 'GithubCopilot') {
|
|
209
|
-
if (!content.mcp) {
|
|
210
|
-
content = {
|
|
211
|
-
servers: {},
|
|
212
|
-
inputs: []
|
|
213
|
-
};
|
|
214
|
-
}
|
|
215
|
-
else {
|
|
216
|
-
content = content.mcp;
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
settings[clientName] = content;
|
|
220
|
-
Logger.debug(`Successfully loaded MCP settings for ${clientName}`);
|
|
221
|
-
}
|
|
222
|
-
catch (error) {
|
|
223
|
-
Logger.debug(`Warning: Could not load MCP settings for client ${clientName}: ${error instanceof Error ? error.message : String(error)}`);
|
|
224
|
-
settings[clientName] = {};
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
configuration.clientMCPSettings = settings;
|
|
228
|
-
return ConfigurationLoader.syncServerCategoriesWithClientSettings(configuration);
|
|
229
|
-
}
|
|
230
|
-
catch (error) {
|
|
231
|
-
Logger.error('Error loading client MCP settings:', error);
|
|
232
|
-
throw error;
|
|
233
|
-
}
|
|
234
|
-
}
|
|
235
|
-
}
|
|
236
|
-
//# sourceMappingURL=ConfigurationLoader.js.map
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
import { MCPServerCategory, FeedConfiguration, InstallationStatus, RequirementStatus, MCPServerStatus, OperationStatus, McpConfig } from './types.js';
|
|
2
|
-
export declare class ConfigurationProvider {
|
|
3
|
-
private static instance;
|
|
4
|
-
private configPath;
|
|
5
|
-
private configuration;
|
|
6
|
-
private configLock;
|
|
7
|
-
private tempDir;
|
|
8
|
-
private constructor();
|
|
9
|
-
static getInstance(): ConfigurationProvider;
|
|
10
|
-
private withLock;
|
|
11
|
-
initialize(feedFile?: string): Promise<void>;
|
|
12
|
-
private saveConfiguration;
|
|
13
|
-
getServerCategories(): Promise<MCPServerCategory[]>;
|
|
14
|
-
getServerCategory(categoryName: string): Promise<MCPServerCategory | undefined>;
|
|
15
|
-
getClientMcpSettings(): Promise<Record<string, Record<string, any>> | undefined>;
|
|
16
|
-
getFeedConfiguration(categoryName: string): Promise<FeedConfiguration | undefined>;
|
|
17
|
-
getServerMcpConfig(categoryName: string, serverName: string): Promise<McpConfig | undefined>;
|
|
18
|
-
getInstallationStatus(categoryName: string): Promise<InstallationStatus | undefined>;
|
|
19
|
-
getServerStatus(categoryName: string, serverName: string): Promise<MCPServerStatus | undefined>;
|
|
20
|
-
getRequirementStatus(categoryName: string, requirementName: string): Promise<RequirementStatus | undefined>;
|
|
21
|
-
updateInstallationStatus(categoryName: string, requirementStatus: Record<string, RequirementStatus>, serverStatus: Record<string, MCPServerStatus>): Promise<boolean>;
|
|
22
|
-
updateRequirementStatus(categoryName: string, requirementName: string, status: RequirementStatus): Promise<boolean>;
|
|
23
|
-
updateRequirementOperationStatus(categoryName: string, requirementName: string, operationStatus: OperationStatus): Promise<boolean>;
|
|
24
|
-
updateServerStatus(categoryName: string, serverName: string, status: MCPServerStatus): Promise<boolean>;
|
|
25
|
-
updateServerOperationStatus(categoryName: string, serverName: string, clientName: string, operationStatus: OperationStatus): Promise<boolean>;
|
|
26
|
-
isRequirementsReady(categoryName: string, serverName: string): Promise<boolean>;
|
|
27
|
-
GetServerRequirementStatus(categoryName: string, serverName: string): Promise<RequirementStatus[]>;
|
|
28
|
-
isServerReady(categoryName: string, serverName: string, clients: string[]): Promise<boolean>;
|
|
29
|
-
syncFeeds(): Promise<void>;
|
|
30
|
-
private loadFeedsIntoConfiguration;
|
|
31
|
-
private loadClientMCPSettings;
|
|
32
|
-
reloadClientMCPSettings(): Promise<void>;
|
|
33
|
-
removeServerFromClientMCPSettings(serverName: string, target?: string): Promise<void>;
|
|
34
|
-
}
|
|
35
|
-
export declare const configProvider: ConfigurationProvider;
|