imcp 0.0.9 → 0.0.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -93
- package/dist/cli/commands/serve.js +2 -1
- package/dist/cli/index.js +3 -6
- package/dist/core/ConfigurationLoader.d.ts +1 -1
- package/dist/core/ConfigurationLoader.js +25 -4
- package/dist/core/ConfigurationProvider.d.ts +1 -1
- package/dist/core/ConfigurationProvider.js +4 -4
- package/dist/core/MCPManager.d.ts +1 -1
- package/dist/core/MCPManager.js +3 -3
- package/dist/core/ServerSchemaLoader.d.ts +11 -0
- package/dist/core/ServerSchemaLoader.js +43 -0
- package/dist/core/ServerSchemaProvider.d.ts +17 -0
- package/dist/core/ServerSchemaProvider.js +115 -0
- package/dist/web/public/css/detailsWidget.css +103 -0
- package/dist/web/public/css/serverDetails.css +40 -29
- package/dist/web/public/js/detailsWidget.js +107 -6
- package/package.json +1 -1
- package/src/cli/commands/serve.ts +4 -3
- package/src/cli/index.ts +3 -3
- package/src/core/ConfigurationLoader.ts +25 -4
- package/src/core/ConfigurationProvider.ts +4 -4
- package/src/core/MCPManager.ts +2 -4
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# IMCP
|
|
2
2
|
|
|
3
|
-
IMCP is a
|
|
3
|
+
IMCP is a Node.js SDK for MCP servers, offering a streamlined, powerful experience of managing MCP servers to your agents (e.g. code agents such as Cline/Github Copilot/Roo), through a unified interface – currently we support coding, browser and Bing tools but can expand more. Let’s use it to weapon your agents
|
|
4
4
|
|
|
5
5
|
## Overview
|
|
6
6
|
|
|
@@ -10,7 +10,7 @@ IMCP allows you to:
|
|
|
10
10
|
- Run a local UI interface with simple click experience to manage MCP servers
|
|
11
11
|
- (in progress) Distribute your own MCP servers to others
|
|
12
12
|
|
|
13
|
-
##
|
|
13
|
+
## Get started
|
|
14
14
|
- Quick usage with latest version
|
|
15
15
|
```
|
|
16
16
|
npx -y imcp@latest serve
|
|
@@ -20,6 +20,9 @@ npx -y imcp@latest serve
|
|
|
20
20
|
```bash
|
|
21
21
|
npm install -g imcp
|
|
22
22
|
```
|
|
23
|
+
```
|
|
24
|
+
imcp serve
|
|
25
|
+
```
|
|
23
26
|
|
|
24
27
|
## Commands
|
|
25
28
|
|
|
@@ -37,105 +40,17 @@ imcp serve [options]
|
|
|
37
40
|
|
|
38
41
|
Options:
|
|
39
42
|
- `-p, --port <port>`: Port to run the server on (default: 3000)
|
|
43
|
+
- `-f, --feed-file <filepath>`: Path to a custom feed configuration file
|
|
40
44
|
|
|
41
45
|
Example:
|
|
42
46
|
```bash
|
|
43
47
|
# Start the web interface on port 8080
|
|
44
48
|
imcp serve --port 8080
|
|
45
|
-
```
|
|
46
|
-
|
|
47
|
-
### list
|
|
48
|
-
|
|
49
|
-
Lists all available MCP servers from configured feeds.
|
|
50
|
-
|
|
51
|
-
```bash
|
|
52
|
-
imcp list [options]
|
|
53
|
-
```
|
|
54
|
-
|
|
55
|
-
Options:
|
|
56
|
-
- `--pull`: Sync with remote feeds before listing
|
|
57
49
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
# List servers after syncing with remote feeds
|
|
61
|
-
imcp list --pull
|
|
50
|
+
# Start with a custom feed configuration file
|
|
51
|
+
imcp serve --feed-file ./custom-feed.json
|
|
62
52
|
```
|
|
63
53
|
|
|
64
|
-
### install
|
|
65
|
-
|
|
66
|
-
Installs specific MCP servers.
|
|
67
|
-
|
|
68
|
-
```bash
|
|
69
|
-
imcp install [options]
|
|
70
|
-
```
|
|
71
|
-
|
|
72
|
-
Options:
|
|
73
|
-
- `--category <category>`: Server category (required)
|
|
74
|
-
- `--name <name>`: Server name to install (required)
|
|
75
|
-
- `--force`: Force installation even if server already exists
|
|
76
|
-
- `--clients <clients>`: Target clients (semicolon separated). Supported values: Cline, MSRooCode, GithubCopilot
|
|
77
|
-
- `--envs <envs>`: Environment variables (semicolon separated key=value pairs)
|
|
78
|
-
|
|
79
|
-
Examples:
|
|
80
|
-
```bash
|
|
81
|
-
# Install a server
|
|
82
|
-
imcp install --category ai-coder-tools --name git-tools
|
|
83
|
-
|
|
84
|
-
# Install with specific client targets
|
|
85
|
-
imcp install --category ai-coder-tools --name git-tools --clients "MSRooCode;GithubCopilot"
|
|
86
|
-
|
|
87
|
-
# Install with environment variables
|
|
88
|
-
imcp install --category ai-coder-tools --name git-tools --envs "GITHUB_TOKEN=abc123;API_KEY=xyz789"
|
|
89
|
-
```
|
|
90
|
-
|
|
91
|
-
### pull
|
|
92
|
-
|
|
93
|
-
Pulls MCP server configurations from remote feed sources to update local feeds.
|
|
94
|
-
|
|
95
|
-
```bash
|
|
96
|
-
imcp pull
|
|
97
|
-
```
|
|
98
|
-
|
|
99
|
-
Example:
|
|
100
|
-
```bash
|
|
101
|
-
# Update local feeds with the latest server configurations
|
|
102
|
-
imcp pull
|
|
103
|
-
```
|
|
104
|
-
|
|
105
|
-
## Development
|
|
106
|
-
|
|
107
|
-
### Prerequisites
|
|
108
|
-
|
|
109
|
-
- Node.js
|
|
110
|
-
- npm
|
|
111
|
-
|
|
112
|
-
### Setup
|
|
113
|
-
|
|
114
|
-
```bash
|
|
115
|
-
# Clone the repository
|
|
116
|
-
git clone <repository-url>
|
|
117
|
-
cd imcp
|
|
118
|
-
|
|
119
|
-
# Install dependencies
|
|
120
|
-
npm install
|
|
121
|
-
|
|
122
|
-
# Build the project
|
|
123
|
-
npm run build
|
|
124
|
-
|
|
125
|
-
# Run in development mode
|
|
126
|
-
npm run dev
|
|
127
|
-
```
|
|
128
|
-
|
|
129
|
-
### Scripts
|
|
130
|
-
|
|
131
|
-
- `npm run build`: Builds the project and copies necessary files
|
|
132
|
-
- `npm run dev`: Runs TypeScript in watch mode
|
|
133
|
-
- `npm run start`: Starts the web server
|
|
134
|
-
- `npm run dev:server`: Starts the development server with hot reloading
|
|
135
|
-
- `npm run test`: Runs tests
|
|
136
|
-
- `npm run lint`: Lints the source code
|
|
137
|
-
- `npm run format`: Formats the source code
|
|
138
|
-
|
|
139
54
|
## License
|
|
140
55
|
|
|
141
56
|
MIT
|
|
@@ -5,12 +5,13 @@ export function createServeCommand() {
|
|
|
5
5
|
return new Command('serve')
|
|
6
6
|
.description('Serve local web interface')
|
|
7
7
|
.option('-p, --port <port>', 'Port to run the server on', '3000')
|
|
8
|
+
.option('-f, --feed-file <filepath>', 'Path to a custom feed configuration file')
|
|
8
9
|
.action(async (options) => {
|
|
9
10
|
try {
|
|
10
11
|
// Sync feeds before start the local UI
|
|
11
12
|
await mcpManager.syncFeeds();
|
|
12
13
|
// Ensure MCP manager is initialized before starting the web server
|
|
13
|
-
await mcpManager.initialize();
|
|
14
|
+
await mcpManager.initialize(options.feedFile);
|
|
14
15
|
const port = parseInt(options.port, 10);
|
|
15
16
|
if (isNaN(port) || port < 1 || port > 65535) {
|
|
16
17
|
throw new Error('Invalid port number');
|
package/dist/cli/index.js
CHANGED
|
@@ -1,9 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { Command } from 'commander';
|
|
3
3
|
import { createServeCommand } from './commands/serve.js';
|
|
4
|
-
import { createListCommand } from './commands/list.js';
|
|
5
|
-
import { createInstallCommand } from './commands/install.js';
|
|
6
|
-
import { createPullCommand } from './commands/pull.js';
|
|
7
4
|
import { Logger } from '../utils/logger.js';
|
|
8
5
|
import axios from 'axios';
|
|
9
6
|
import path from 'path';
|
|
@@ -30,10 +27,10 @@ async function main() {
|
|
|
30
27
|
Logger.setVerbose(!!opts.verbose);
|
|
31
28
|
// Add all commands
|
|
32
29
|
program.addCommand(createServeCommand());
|
|
33
|
-
program.addCommand(createListCommand());
|
|
34
|
-
program.addCommand(createInstallCommand());
|
|
30
|
+
// program.addCommand(createListCommand());
|
|
31
|
+
// program.addCommand(createInstallCommand());
|
|
35
32
|
// program.addCommand(createUninstallCommand());
|
|
36
|
-
program.addCommand(createPullCommand());
|
|
33
|
+
// program.addCommand(createPullCommand());
|
|
37
34
|
// Error handling for the entire CLI
|
|
38
35
|
program.exitOverride();
|
|
39
36
|
// Check for updates
|
|
@@ -24,7 +24,7 @@ export declare class ConfigurationLoader {
|
|
|
24
24
|
/**
|
|
25
25
|
* Loads feed configurations into the MCP configuration
|
|
26
26
|
*/
|
|
27
|
-
static loadFeedsIntoConfiguration(configuration: MCPConfiguration): Promise<MCPConfiguration>;
|
|
27
|
+
static loadFeedsIntoConfiguration(configuration: MCPConfiguration, feedFile?: string): Promise<MCPConfiguration>;
|
|
28
28
|
/**
|
|
29
29
|
* Loads MCP client settings into the configuration
|
|
30
30
|
*/
|
|
@@ -140,23 +140,44 @@ export class ConfigurationLoader {
|
|
|
140
140
|
/**
|
|
141
141
|
* Loads feed configurations into the MCP configuration
|
|
142
142
|
*/
|
|
143
|
-
static async loadFeedsIntoConfiguration(configuration) {
|
|
143
|
+
static async loadFeedsIntoConfiguration(configuration, feedFile) {
|
|
144
144
|
try {
|
|
145
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
|
|
146
162
|
const files = await fs.readdir(LOCAL_FEEDS_DIR);
|
|
147
163
|
const jsonFiles = files.filter(file => file.endsWith('.json'));
|
|
148
|
-
if (jsonFiles.length === 0) {
|
|
164
|
+
if (jsonFiles.length === 0 && !feedFile) {
|
|
149
165
|
console.log(`No feed configuration files found in ${LOCAL_FEEDS_DIR}`);
|
|
150
166
|
return configuration;
|
|
151
167
|
}
|
|
152
|
-
const feeds = {};
|
|
153
168
|
for (const file of jsonFiles) {
|
|
154
169
|
try {
|
|
155
170
|
const filePath = path.join(LOCAL_FEEDS_DIR, file);
|
|
156
171
|
const content = await fs.readFile(filePath, 'utf8');
|
|
157
172
|
const config = JSON.parse(content);
|
|
158
173
|
if (config && config.name) {
|
|
159
|
-
|
|
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
|
+
}
|
|
160
181
|
}
|
|
161
182
|
}
|
|
162
183
|
catch (error) {
|
|
@@ -8,7 +8,7 @@ export declare class ConfigurationProvider {
|
|
|
8
8
|
private constructor();
|
|
9
9
|
static getInstance(): ConfigurationProvider;
|
|
10
10
|
private withLock;
|
|
11
|
-
initialize(): Promise<void>;
|
|
11
|
+
initialize(feedFile?: string): Promise<void>;
|
|
12
12
|
private saveConfiguration;
|
|
13
13
|
getServerCategories(): Promise<MCPServerCategory[]>;
|
|
14
14
|
getServerCategory(categoryName: string): Promise<MCPServerCategory | undefined>;
|
|
@@ -43,7 +43,7 @@ export class ConfigurationProvider {
|
|
|
43
43
|
resolve();
|
|
44
44
|
}
|
|
45
45
|
}
|
|
46
|
-
async initialize() {
|
|
46
|
+
async initialize(feedFile) {
|
|
47
47
|
await this.withLock(async () => {
|
|
48
48
|
const configDir = path.dirname(this.configPath);
|
|
49
49
|
await fs.mkdir(configDir, { recursive: true });
|
|
@@ -60,7 +60,7 @@ export class ConfigurationProvider {
|
|
|
60
60
|
await this.saveConfiguration();
|
|
61
61
|
}
|
|
62
62
|
// Always load feeds and client settings, whether file existed or not
|
|
63
|
-
await this.loadFeedsIntoConfiguration();
|
|
63
|
+
await this.loadFeedsIntoConfiguration(feedFile);
|
|
64
64
|
await this.loadClientMCPSettings();
|
|
65
65
|
}
|
|
66
66
|
catch (error) {
|
|
@@ -280,8 +280,8 @@ export class ConfigurationProvider {
|
|
|
280
280
|
}
|
|
281
281
|
});
|
|
282
282
|
}
|
|
283
|
-
async loadFeedsIntoConfiguration() {
|
|
284
|
-
this.configuration = await ConfigurationLoader.loadFeedsIntoConfiguration(this.configuration);
|
|
283
|
+
async loadFeedsIntoConfiguration(feedFile) {
|
|
284
|
+
this.configuration = await ConfigurationLoader.loadFeedsIntoConfiguration(this.configuration, feedFile);
|
|
285
285
|
await this.saveConfiguration();
|
|
286
286
|
}
|
|
287
287
|
async loadClientMCPSettings() {
|
|
@@ -5,7 +5,7 @@ export declare class MCPManager extends EventEmitter {
|
|
|
5
5
|
private configProvider;
|
|
6
6
|
constructor();
|
|
7
7
|
syncFeeds(): Promise<void>;
|
|
8
|
-
initialize(): Promise<void>;
|
|
8
|
+
initialize(feedFile?: string): Promise<void>;
|
|
9
9
|
listServerCategories(options?: ServerCategoryListOptions): Promise<MCPServerCategory[]>;
|
|
10
10
|
getFeedConfiguration(categoryName: string): Promise<import("./types.js").FeedConfiguration | undefined>;
|
|
11
11
|
installServer(categoryName: string, serverName: string, requestOptions?: ServerInstallOptions): Promise<ServerOperationResult>;
|
package/dist/core/MCPManager.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { EventEmitter } from 'events';
|
|
2
2
|
import { ConfigurationProvider } from './ConfigurationProvider.js';
|
|
3
3
|
import { InstallationService } from './InstallationService.js';
|
|
4
|
-
import { MCPEvent } from './types.js';
|
|
4
|
+
import { MCPEvent, } from './types.js';
|
|
5
5
|
export class MCPManager extends EventEmitter {
|
|
6
6
|
installationService;
|
|
7
7
|
configProvider;
|
|
@@ -13,9 +13,9 @@ export class MCPManager extends EventEmitter {
|
|
|
13
13
|
async syncFeeds() {
|
|
14
14
|
await this.configProvider.syncFeeds();
|
|
15
15
|
}
|
|
16
|
-
async initialize() {
|
|
16
|
+
async initialize(feedFile) {
|
|
17
17
|
try {
|
|
18
|
-
await this.configProvider.initialize();
|
|
18
|
+
await this.configProvider.initialize(feedFile);
|
|
19
19
|
}
|
|
20
20
|
catch (error) {
|
|
21
21
|
console.error("Error during MCPManager initialization:", error);
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { ServerSchema } from './ServerSchemaProvider.js';
|
|
2
|
+
export declare class ServerSchemaLoader {
|
|
3
|
+
/**
|
|
4
|
+
* Load schema for a specific server in a category
|
|
5
|
+
*/
|
|
6
|
+
static loadSchema(categoryName: string, serverName: string): Promise<ServerSchema | undefined>;
|
|
7
|
+
/**
|
|
8
|
+
* Validate schema content against expected format
|
|
9
|
+
*/
|
|
10
|
+
static validateSchema(schema: any): boolean;
|
|
11
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import fs from 'fs/promises';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { LOCAL_FEEDS_DIR } from './constants.js';
|
|
4
|
+
import { Logger } from '../utils/logger.js';
|
|
5
|
+
export class ServerSchemaLoader {
|
|
6
|
+
/**
|
|
7
|
+
* Load schema for a specific server in a category
|
|
8
|
+
*/
|
|
9
|
+
static async loadSchema(categoryName, serverName) {
|
|
10
|
+
try {
|
|
11
|
+
const schemaPath = path.join(LOCAL_FEEDS_DIR, categoryName, `${serverName}.json`);
|
|
12
|
+
const content = await fs.readFile(schemaPath, 'utf8');
|
|
13
|
+
const schema = JSON.parse(content);
|
|
14
|
+
// Validate schema structure
|
|
15
|
+
if (!schema.version || !schema.schema) {
|
|
16
|
+
Logger.debug(`Invalid schema format for server ${serverName} in category ${categoryName}`);
|
|
17
|
+
return undefined;
|
|
18
|
+
}
|
|
19
|
+
return {
|
|
20
|
+
schema: schema
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
catch (error) {
|
|
24
|
+
if (error.code === 'ENOENT') {
|
|
25
|
+
Logger.debug(`No schema file found for server ${serverName} in category ${categoryName}`);
|
|
26
|
+
return undefined;
|
|
27
|
+
}
|
|
28
|
+
Logger.error(`Error loading schema for server ${serverName} in category ${categoryName}:`, error);
|
|
29
|
+
throw error;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Validate schema content against expected format
|
|
34
|
+
*/
|
|
35
|
+
static validateSchema(schema) {
|
|
36
|
+
return (typeof schema === 'object' &&
|
|
37
|
+
schema !== null &&
|
|
38
|
+
typeof schema.version === 'string' &&
|
|
39
|
+
typeof schema.schema === 'object' &&
|
|
40
|
+
schema.schema !== null);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
//# sourceMappingURL=ServerSchemaLoader.js.map
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export interface ServerSchema {
|
|
2
|
+
schema: Record<string, any>;
|
|
3
|
+
}
|
|
4
|
+
export declare class ServerSchemaProvider {
|
|
5
|
+
private static instance;
|
|
6
|
+
private schemaMap;
|
|
7
|
+
private schemaLock;
|
|
8
|
+
private constructor();
|
|
9
|
+
static getInstance(): Promise<ServerSchemaProvider>;
|
|
10
|
+
private withLock;
|
|
11
|
+
initialize(): Promise<void>;
|
|
12
|
+
private loadSchema;
|
|
13
|
+
private loadAllSchemas;
|
|
14
|
+
getSchema(categoryName: string, serverName: string): Promise<ServerSchema | undefined>;
|
|
15
|
+
reloadSchemas(): Promise<void>;
|
|
16
|
+
}
|
|
17
|
+
export declare function getServerSchemaProvider(): Promise<ServerSchemaProvider>;
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import fs from 'fs/promises';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { LOCAL_FEEDS_SCHEMA_DIR } from './constants.js';
|
|
4
|
+
import { Logger } from '../utils/logger.js';
|
|
5
|
+
export class ServerSchemaProvider {
|
|
6
|
+
static instance;
|
|
7
|
+
schemaMap;
|
|
8
|
+
schemaLock = Promise.resolve();
|
|
9
|
+
constructor() {
|
|
10
|
+
this.schemaMap = new Map();
|
|
11
|
+
}
|
|
12
|
+
static async getInstance() {
|
|
13
|
+
if (!ServerSchemaProvider.instance) {
|
|
14
|
+
ServerSchemaProvider.instance = new ServerSchemaProvider();
|
|
15
|
+
await ServerSchemaProvider.instance.initialize();
|
|
16
|
+
}
|
|
17
|
+
return ServerSchemaProvider.instance;
|
|
18
|
+
}
|
|
19
|
+
async withLock(operation) {
|
|
20
|
+
const current = this.schemaLock;
|
|
21
|
+
let resolve;
|
|
22
|
+
this.schemaLock = new Promise(r => resolve = r);
|
|
23
|
+
try {
|
|
24
|
+
await current;
|
|
25
|
+
return await operation();
|
|
26
|
+
}
|
|
27
|
+
finally {
|
|
28
|
+
resolve();
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
async initialize() {
|
|
32
|
+
await this.withLock(async () => {
|
|
33
|
+
try {
|
|
34
|
+
// Create feeds directory if it doesn't exist
|
|
35
|
+
await fs.mkdir(LOCAL_FEEDS_SCHEMA_DIR, { recursive: true });
|
|
36
|
+
// Load all schemas from the feeds directory
|
|
37
|
+
await this.loadAllSchemas();
|
|
38
|
+
}
|
|
39
|
+
catch (error) {
|
|
40
|
+
Logger.error('Error during schema initialization:', error);
|
|
41
|
+
throw error;
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
async loadSchema(categoryName, serverName) {
|
|
46
|
+
try {
|
|
47
|
+
const schemaPath = path.join(LOCAL_FEEDS_SCHEMA_DIR, categoryName, `${serverName}.json`);
|
|
48
|
+
const content = await fs.readFile(schemaPath, 'utf8');
|
|
49
|
+
const schema = JSON.parse(content);
|
|
50
|
+
return {
|
|
51
|
+
schema: schema
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
catch (error) {
|
|
55
|
+
if (error.code === 'ENOENT') {
|
|
56
|
+
Logger.debug(`No schema file found for server ${serverName} in category ${categoryName}`);
|
|
57
|
+
return undefined;
|
|
58
|
+
}
|
|
59
|
+
Logger.error(`Error loading schema for server ${serverName} in category ${categoryName}:`, error);
|
|
60
|
+
throw error;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
async loadAllSchemas() {
|
|
64
|
+
this.schemaMap.clear();
|
|
65
|
+
// Read server category directories
|
|
66
|
+
const categoryDirs = await fs.readdir(LOCAL_FEEDS_SCHEMA_DIR, { withFileTypes: true });
|
|
67
|
+
for (const categoryDir of categoryDirs) {
|
|
68
|
+
if (categoryDir.isDirectory()) {
|
|
69
|
+
const categoryPath = path.join(LOCAL_FEEDS_SCHEMA_DIR, categoryDir.name);
|
|
70
|
+
const serverFiles = await fs.readdir(categoryPath);
|
|
71
|
+
const serverSchemas = new Map();
|
|
72
|
+
for (const file of serverFiles) {
|
|
73
|
+
if (file.endsWith('.json')) {
|
|
74
|
+
const serverName = path.basename(file, '.json');
|
|
75
|
+
try {
|
|
76
|
+
const schema = await this.loadSchema(categoryDir.name, serverName);
|
|
77
|
+
if (schema) {
|
|
78
|
+
serverSchemas.set(serverName, schema);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
catch (error) {
|
|
82
|
+
Logger.error(`Error loading schema for server ${serverName} in category ${categoryDir.name}:`, error);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
if (serverSchemas.size > 0) {
|
|
87
|
+
this.schemaMap.set(categoryDir.name, serverSchemas);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
async getSchema(categoryName, serverName) {
|
|
93
|
+
return await this.withLock(async () => {
|
|
94
|
+
const categorySchemas = this.schemaMap.get(categoryName);
|
|
95
|
+
if (!categorySchemas) {
|
|
96
|
+
return undefined;
|
|
97
|
+
}
|
|
98
|
+
return categorySchemas.get(serverName);
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
async reloadSchemas() {
|
|
102
|
+
return await this.withLock(async () => {
|
|
103
|
+
await this.loadAllSchemas();
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
// Export a lazy initialized singleton instance getter
|
|
108
|
+
let initPromise = null;
|
|
109
|
+
export function getServerSchemaProvider() {
|
|
110
|
+
if (!initPromise) {
|
|
111
|
+
initPromise = ServerSchemaProvider.getInstance();
|
|
112
|
+
}
|
|
113
|
+
return initPromise;
|
|
114
|
+
}
|
|
115
|
+
//# sourceMappingURL=ServerSchemaProvider.js.map
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
.details-widget {
|
|
2
|
+
max-height: 0;
|
|
3
|
+
overflow: hidden;
|
|
4
|
+
transition: max-height 0.3s ease-out;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
.details-widget.expanded {
|
|
8
|
+
max-height: 2000px;
|
|
9
|
+
transition: max-height 0.5s ease-in;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
.details-widget-content {
|
|
13
|
+
padding: 1rem;
|
|
14
|
+
background-color: #f8fafc;
|
|
15
|
+
border-top: 1px solid #e2e8f0;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/* Schema specific styles */
|
|
19
|
+
.schema-content {
|
|
20
|
+
font-family: system-ui, -apple-system, sans-serif;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
.tool-section {
|
|
24
|
+
background-color: white;
|
|
25
|
+
border-radius: 0.5rem;
|
|
26
|
+
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1);
|
|
27
|
+
margin-bottom: 1.5rem;
|
|
28
|
+
transition: transform 0.2s;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
.tool-section:hover {
|
|
32
|
+
transform: translateY(-2px);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
.property-item {
|
|
36
|
+
padding: 0.75rem;
|
|
37
|
+
margin: 0.5rem 0;
|
|
38
|
+
background-color: white;
|
|
39
|
+
border-radius: 0.375rem;
|
|
40
|
+
transition: all 0.2s;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
.property-item:hover {
|
|
44
|
+
background-color: #f1f5f9;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
.property-item.required {
|
|
48
|
+
border-left: 3px solid #3b82f6;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
.property-name {
|
|
52
|
+
color: #1e40af;
|
|
53
|
+
font-weight: 600;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
.property-type {
|
|
57
|
+
color: #64748b;
|
|
58
|
+
font-size: 0.875rem;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
.property-desc {
|
|
62
|
+
color: #475569;
|
|
63
|
+
margin-top: 0.375rem;
|
|
64
|
+
line-height: 1.4;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
.property-default {
|
|
68
|
+
color: #94a3b8;
|
|
69
|
+
font-size: 0.875rem;
|
|
70
|
+
font-family: monospace;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
.required-badge {
|
|
74
|
+
background-color: #dbeafe;
|
|
75
|
+
color: #1e40af;
|
|
76
|
+
padding: 0.125rem 0.5rem;
|
|
77
|
+
border-radius: 9999px;
|
|
78
|
+
font-size: 0.75rem;
|
|
79
|
+
font-weight: 500;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
.required-fields {
|
|
83
|
+
background-color: #fef3c7;
|
|
84
|
+
color: #92400e;
|
|
85
|
+
padding: 0.5rem;
|
|
86
|
+
border-radius: 0.375rem;
|
|
87
|
+
margin-bottom: 1rem;
|
|
88
|
+
font-size: 0.875rem;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
.input-schema {
|
|
92
|
+
margin-top: 1rem;
|
|
93
|
+
padding: 1rem;
|
|
94
|
+
background-color: #f8fafc;
|
|
95
|
+
border-radius: 0.5rem;
|
|
96
|
+
border: 1px solid #e2e8f0;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
.properties-list {
|
|
100
|
+
display: flex;
|
|
101
|
+
flex-direction: column;
|
|
102
|
+
gap: 0.75rem;
|
|
103
|
+
}
|
|
@@ -6,16 +6,14 @@
|
|
|
6
6
|
}
|
|
7
7
|
|
|
8
8
|
.server-item-content {
|
|
9
|
+
position: relative;
|
|
9
10
|
border: 1px solid #e5e7eb;
|
|
10
11
|
border-radius: 0.5rem;
|
|
11
12
|
padding: 1rem;
|
|
13
|
+
padding-right: calc(120px + 3rem); /* Button width + spacing */
|
|
12
14
|
margin-bottom: 1rem;
|
|
13
15
|
background-color: #ffffff;
|
|
14
16
|
transition: all 0.2s ease;
|
|
15
|
-
position: relative;
|
|
16
|
-
display: flex;
|
|
17
|
-
justify-content: space-between;
|
|
18
|
-
align-items: bottom; /* Center items vertically */
|
|
19
17
|
}
|
|
20
18
|
|
|
21
19
|
.server-item:hover .server-item-content {
|
|
@@ -59,40 +57,53 @@
|
|
|
59
57
|
border-color: #3b82f6;
|
|
60
58
|
}
|
|
61
59
|
|
|
62
|
-
/* Server item
|
|
60
|
+
/* Server item layout */
|
|
63
61
|
.server-item-info {
|
|
64
|
-
|
|
65
|
-
padding-right: 1rem;
|
|
66
|
-
min-width: 0; /* Prevent content from overflowing */
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
/* Install/Uninstall buttons positioning */
|
|
70
|
-
.action-buttons {
|
|
71
|
-
flex-shrink: 0;
|
|
72
|
-
display: flex;
|
|
73
|
-
align-items: center;
|
|
74
|
-
margin-left: 1rem; /* Add some space between content and button */
|
|
62
|
+
width: 100%;
|
|
75
63
|
}
|
|
76
64
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
position: relative;
|
|
80
|
-
z-index: 2;
|
|
65
|
+
.server-item-header {
|
|
66
|
+
margin-bottom: 1rem;
|
|
81
67
|
}
|
|
82
68
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
white-space: nowrap;
|
|
86
|
-
padding: 0.5rem 1rem; /* Slightly larger padding for better visibility */
|
|
87
|
-
min-width: 80px; /* Ensure consistent button width */
|
|
88
|
-
text-align: center;
|
|
69
|
+
.server-item-header h5 {
|
|
70
|
+
margin-bottom: 0.5rem;
|
|
89
71
|
}
|
|
90
72
|
|
|
91
|
-
/*
|
|
92
|
-
.
|
|
73
|
+
/* Client status section */
|
|
74
|
+
.flex-wrap {
|
|
93
75
|
margin: -0.25rem; /* Negative margin to offset badge spacing */
|
|
94
76
|
}
|
|
95
77
|
|
|
96
|
-
.
|
|
78
|
+
.flex-wrap > * {
|
|
97
79
|
margin: 0.25rem; /* Even spacing between badges */
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/* Install/Uninstall button section */
|
|
83
|
+
.action-buttons {
|
|
84
|
+
position: absolute;
|
|
85
|
+
right: 1rem;
|
|
86
|
+
top: 50%;
|
|
87
|
+
transform: translateY(-50%);
|
|
88
|
+
margin: 0;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
.action-buttons button {
|
|
92
|
+
min-width: 120px;
|
|
93
|
+
padding: 0.5rem 1.5rem;
|
|
94
|
+
text-align: center;
|
|
95
|
+
font-weight: 600;
|
|
96
|
+
transition: all 0.15s ease;
|
|
97
|
+
white-space: nowrap;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/* Status badges */
|
|
101
|
+
.server-item-info .flex-wrap span {
|
|
102
|
+
display: inline-flex;
|
|
103
|
+
align-items: center;
|
|
104
|
+
padding: 0.375rem 0.75rem;
|
|
105
|
+
border-radius: 9999px;
|
|
106
|
+
font-size: 0.75rem;
|
|
107
|
+
line-height: 1;
|
|
108
|
+
white-space: nowrap;
|
|
98
109
|
}
|
|
@@ -15,12 +15,101 @@ export class DetailsWidget {
|
|
|
15
15
|
this.container.appendChild(this.widgetElement);
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
-
setContent(
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
18
|
+
setContent(content) {
|
|
19
|
+
if (typeof content === 'string') {
|
|
20
|
+
this.contentElement.innerHTML = `
|
|
21
|
+
<div class="description-text">
|
|
22
|
+
${content || 'No description available.'}
|
|
23
|
+
</div>
|
|
24
|
+
`;
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// If content is a schema object
|
|
29
|
+
if (content.schema) {
|
|
30
|
+
this.contentElement.innerHTML = this.renderSchema(content.schema);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
renderSchema(schema) {
|
|
35
|
+
if (!schema || typeof schema !== 'object') {
|
|
36
|
+
return '<p>Invalid schema format</p>';
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// The schema object from the API is wrapped in a "schema" property
|
|
40
|
+
const schemaContent = schema.schema || schema;
|
|
41
|
+
|
|
42
|
+
let html = '<div class="schema-content p-4">';
|
|
43
|
+
|
|
44
|
+
// Iterate through each tool in schema
|
|
45
|
+
Object.entries(schemaContent).forEach(([toolName, toolInfo]) => {
|
|
46
|
+
if (!toolInfo) return; // Skip if tool info is undefined
|
|
47
|
+
|
|
48
|
+
html += `
|
|
49
|
+
<div class="tool-section mb-6 border-b border-gray-200 pb-4">
|
|
50
|
+
<h3 class="text-lg font-semibold text-blue-600 mb-2">${toolInfo.name || toolName}</h3>
|
|
51
|
+
<p class="text-gray-700 mb-4">${toolInfo.description || 'No description available'}</p>
|
|
52
|
+
|
|
53
|
+
<div class="input-schema bg-gray-50 p-4 rounded-lg">
|
|
54
|
+
<h4 class="text-md font-semibold text-gray-700 mb-2">Input Schema</h4>
|
|
55
|
+
${this.renderInputSchema(toolInfo.inputSchema)}
|
|
56
|
+
</div>
|
|
57
|
+
</div>
|
|
58
|
+
`;
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
html += '</div>';
|
|
62
|
+
return html;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
renderInputSchema(schema) {
|
|
66
|
+
if (!schema || typeof schema !== 'object') {
|
|
67
|
+
return '<p>No input schema available</p>';
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Handle case where schema might be empty or have no properties
|
|
71
|
+
if (!schema.properties || Object.keys(schema.properties).length === 0) {
|
|
72
|
+
return '<p>No input properties defined</p>';
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
let html = '<div class="properties-list">';
|
|
76
|
+
|
|
77
|
+
try {
|
|
78
|
+
// Required fields banner if any
|
|
79
|
+
if (schema.required && Array.isArray(schema.required) && schema.required.length > 0) {
|
|
80
|
+
html += `
|
|
81
|
+
<div class="required-fields mb-3 bg-yellow-50 p-2 rounded">
|
|
82
|
+
<span class="text-sm font-medium text-yellow-800">Required fields: ${schema.required.join(', ')}</span>
|
|
83
|
+
</div>
|
|
84
|
+
`;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Render each property
|
|
88
|
+
Object.entries(schema.properties).forEach(([propName, propDetails]) => {
|
|
89
|
+
if (!propDetails) return; // Skip if property details are undefined
|
|
90
|
+
|
|
91
|
+
const isRequired = schema.required && Array.isArray(schema.required) && schema.required.includes(propName);
|
|
92
|
+
const type = propDetails.type || 'any';
|
|
93
|
+
|
|
94
|
+
html += `
|
|
95
|
+
<div class="property-item mb-3 ${isRequired ? 'required' : ''} border-l-2 ${isRequired ? 'border-blue-500' : 'border-gray-300'} pl-3">
|
|
96
|
+
<div class="property-header flex items-center gap-2">
|
|
97
|
+
<span class="property-name font-medium text-gray-900">${this.escapeHtml(propName)}</span>
|
|
98
|
+
<span class="property-type text-sm text-gray-500">(${this.escapeHtml(type)})</span>
|
|
99
|
+
${isRequired ? '<span class="required-badge text-xs bg-blue-100 text-blue-800 px-2 py-0.5 rounded">Required</span>' : ''}
|
|
100
|
+
</div>
|
|
101
|
+
${propDetails.description ? `<p class="property-desc text-sm text-gray-600 mt-1">${this.escapeHtml(propDetails.description)}</p>` : ''}
|
|
102
|
+
${propDetails.default !== undefined ? `<p class="property-default text-sm text-gray-500 mt-1">Default: ${this.escapeHtml(JSON.stringify(propDetails.default))}</p>` : ''}
|
|
103
|
+
</div>
|
|
104
|
+
`;
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
html += '</div>';
|
|
108
|
+
return html;
|
|
109
|
+
} catch (error) {
|
|
110
|
+
console.error('Error rendering input schema:', error);
|
|
111
|
+
return '<p>Error rendering input schema</p>';
|
|
112
|
+
}
|
|
24
113
|
}
|
|
25
114
|
|
|
26
115
|
toggle() {
|
|
@@ -37,6 +126,18 @@ export class DetailsWidget {
|
|
|
37
126
|
this.container.querySelector('.server-item-content').classList.add('expanded');
|
|
38
127
|
}
|
|
39
128
|
|
|
129
|
+
escapeHtml(unsafe) {
|
|
130
|
+
if (unsafe === undefined || unsafe === null) {
|
|
131
|
+
return '';
|
|
132
|
+
}
|
|
133
|
+
return String(unsafe)
|
|
134
|
+
.replace(/&/g, "&")
|
|
135
|
+
.replace(/</g, "<")
|
|
136
|
+
.replace(/>/g, ">")
|
|
137
|
+
.replace(/"/g, """)
|
|
138
|
+
.replace(/'/g, "'");
|
|
139
|
+
}
|
|
140
|
+
|
|
40
141
|
collapse() {
|
|
41
142
|
this.widgetElement.classList.remove('expanded');
|
|
42
143
|
this.container.querySelector('.server-item-content').classList.remove('expanded');
|
package/package.json
CHANGED
|
@@ -8,21 +8,22 @@ export function createServeCommand(): Command {
|
|
|
8
8
|
return new Command('serve')
|
|
9
9
|
.description('Serve local web interface')
|
|
10
10
|
.option('-p, --port <port>', 'Port to run the server on', '3000')
|
|
11
|
+
.option('-f, --feed-file <filepath>', 'Path to a custom feed configuration file')
|
|
11
12
|
.action(async (options) => {
|
|
12
13
|
try {
|
|
13
14
|
// Sync feeds before start the local UI
|
|
14
15
|
await mcpManager.syncFeeds();
|
|
15
16
|
|
|
16
17
|
// Ensure MCP manager is initialized before starting the web server
|
|
17
|
-
await mcpManager.initialize();
|
|
18
|
-
|
|
18
|
+
await mcpManager.initialize(options.feedFile);
|
|
19
|
+
|
|
19
20
|
const port = parseInt(options.port, 10);
|
|
20
21
|
if (isNaN(port) || port < 1 || port > 65535) {
|
|
21
22
|
throw new Error('Invalid port number');
|
|
22
23
|
}
|
|
23
24
|
|
|
24
25
|
await startWebServer(port);
|
|
25
|
-
|
|
26
|
+
|
|
26
27
|
// The server is running, keep the process alive
|
|
27
28
|
process.on('SIGINT', () => {
|
|
28
29
|
console.log('\nShutting down server...');
|
package/src/cli/index.ts
CHANGED
|
@@ -42,10 +42,10 @@ async function main(): Promise<void> {
|
|
|
42
42
|
|
|
43
43
|
// Add all commands
|
|
44
44
|
program.addCommand(createServeCommand());
|
|
45
|
-
program.addCommand(createListCommand());
|
|
46
|
-
program.addCommand(createInstallCommand());
|
|
45
|
+
// program.addCommand(createListCommand());
|
|
46
|
+
// program.addCommand(createInstallCommand());
|
|
47
47
|
// program.addCommand(createUninstallCommand());
|
|
48
|
-
program.addCommand(createPullCommand());
|
|
48
|
+
// program.addCommand(createPullCommand());
|
|
49
49
|
|
|
50
50
|
// Error handling for the entire CLI
|
|
51
51
|
program.exitOverride();
|
|
@@ -177,25 +177,46 @@ export class ConfigurationLoader {
|
|
|
177
177
|
/**
|
|
178
178
|
* Loads feed configurations into the MCP configuration
|
|
179
179
|
*/
|
|
180
|
-
static async loadFeedsIntoConfiguration(configuration: MCPConfiguration): Promise<MCPConfiguration> {
|
|
180
|
+
static async loadFeedsIntoConfiguration(configuration: MCPConfiguration, feedFile?: string): Promise<MCPConfiguration> {
|
|
181
181
|
try {
|
|
182
182
|
await fs.mkdir(LOCAL_FEEDS_DIR, { recursive: true });
|
|
183
|
+
const feeds: Record<string, FeedConfiguration> = {};
|
|
184
|
+
|
|
185
|
+
// Load provided feed file if specified
|
|
186
|
+
if (feedFile) {
|
|
187
|
+
try {
|
|
188
|
+
const content = await fs.readFile(feedFile, 'utf8');
|
|
189
|
+
const config = JSON.parse(content) as FeedConfiguration;
|
|
190
|
+
if (config && config.name) {
|
|
191
|
+
feeds[config.name] = config;
|
|
192
|
+
console.log(`Loaded feed configuration from provided file: ${feedFile}`);
|
|
193
|
+
}
|
|
194
|
+
} catch (error) {
|
|
195
|
+
console.log(`Error loading feed configuration from provided file ${feedFile}:`, error);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// Load feeds from LOCAL_FEEDS_DIR
|
|
183
200
|
const files = await fs.readdir(LOCAL_FEEDS_DIR);
|
|
184
201
|
const jsonFiles = files.filter(file => file.endsWith('.json'));
|
|
185
202
|
|
|
186
|
-
if (jsonFiles.length === 0) {
|
|
203
|
+
if (jsonFiles.length === 0 && !feedFile) {
|
|
187
204
|
console.log(`No feed configuration files found in ${LOCAL_FEEDS_DIR}`);
|
|
188
205
|
return configuration;
|
|
189
206
|
}
|
|
190
207
|
|
|
191
|
-
const feeds: Record<string, FeedConfiguration> = {};
|
|
192
208
|
for (const file of jsonFiles) {
|
|
193
209
|
try {
|
|
194
210
|
const filePath = path.join(LOCAL_FEEDS_DIR, file);
|
|
195
211
|
const content = await fs.readFile(filePath, 'utf8');
|
|
196
212
|
const config = JSON.parse(content) as FeedConfiguration;
|
|
197
213
|
if (config && config.name) {
|
|
198
|
-
|
|
214
|
+
// If feed exists from provided file, skip the local one
|
|
215
|
+
if (!feeds[config.name]) {
|
|
216
|
+
feeds[config.name] = config;
|
|
217
|
+
} else {
|
|
218
|
+
console.log(`Skipping local feed ${config.name} as it was provided via --feed-file`);
|
|
219
|
+
}
|
|
199
220
|
}
|
|
200
221
|
} catch (error) {
|
|
201
222
|
console.warn(`Error loading feed configuration from ${file}:`, error);
|
|
@@ -57,7 +57,7 @@ export class ConfigurationProvider {
|
|
|
57
57
|
}
|
|
58
58
|
}
|
|
59
59
|
|
|
60
|
-
async initialize(): Promise<void> {
|
|
60
|
+
async initialize(feedFile?: string): Promise<void> {
|
|
61
61
|
await this.withLock(async () => {
|
|
62
62
|
const configDir = path.dirname(this.configPath);
|
|
63
63
|
await fs.mkdir(configDir, { recursive: true });
|
|
@@ -75,7 +75,7 @@ export class ConfigurationProvider {
|
|
|
75
75
|
}
|
|
76
76
|
|
|
77
77
|
// Always load feeds and client settings, whether file existed or not
|
|
78
|
-
await this.loadFeedsIntoConfiguration();
|
|
78
|
+
await this.loadFeedsIntoConfiguration(feedFile);
|
|
79
79
|
await this.loadClientMCPSettings();
|
|
80
80
|
} catch (error) {
|
|
81
81
|
Logger.error('Error during initialization', error);
|
|
@@ -340,8 +340,8 @@ export class ConfigurationProvider {
|
|
|
340
340
|
});
|
|
341
341
|
}
|
|
342
342
|
|
|
343
|
-
private async loadFeedsIntoConfiguration(): Promise<void> {
|
|
344
|
-
this.configuration = await ConfigurationLoader.loadFeedsIntoConfiguration(this.configuration);
|
|
343
|
+
private async loadFeedsIntoConfiguration(feedFile?: string): Promise<void> {
|
|
344
|
+
this.configuration = await ConfigurationLoader.loadFeedsIntoConfiguration(this.configuration, feedFile);
|
|
345
345
|
await this.saveConfiguration();
|
|
346
346
|
}
|
|
347
347
|
|
package/src/core/MCPManager.ts
CHANGED
|
@@ -9,8 +9,6 @@ import {
|
|
|
9
9
|
ServerCategoryListOptions,
|
|
10
10
|
ServerOperationResult,
|
|
11
11
|
ServerUninstallOptions,
|
|
12
|
-
InstallationStatus,
|
|
13
|
-
UpdateRequirementOptions
|
|
14
12
|
} from './types.js';
|
|
15
13
|
import path from 'path';
|
|
16
14
|
|
|
@@ -28,9 +26,9 @@ export class MCPManager extends EventEmitter {
|
|
|
28
26
|
await this.configProvider.syncFeeds();
|
|
29
27
|
}
|
|
30
28
|
|
|
31
|
-
async initialize(): Promise<void> {
|
|
29
|
+
async initialize(feedFile?: string): Promise<void> {
|
|
32
30
|
try {
|
|
33
|
-
await this.configProvider.initialize();
|
|
31
|
+
await this.configProvider.initialize(feedFile);
|
|
34
32
|
} catch (error) {
|
|
35
33
|
console.error("Error during MCPManager initialization:", error);
|
|
36
34
|
throw error;
|