imcp 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/ISSUE_TEMPLATE/JitAccess.yml +28 -0
- package/.github/acl/access.yml +20 -0
- package/.github/compliance/inventory.yml +5 -0
- package/.github/policies/jit.yml +19 -0
- package/README.md +137 -0
- package/dist/cli/commands/install.d.ts +2 -0
- package/dist/cli/commands/install.js +105 -0
- package/dist/cli/commands/list.d.ts +2 -0
- package/dist/cli/commands/list.js +90 -0
- package/dist/cli/commands/pull.d.ts +2 -0
- package/dist/cli/commands/pull.js +17 -0
- package/dist/cli/commands/serve.d.ts +2 -0
- package/dist/cli/commands/serve.js +32 -0
- package/dist/cli/commands/start.d.ts +2 -0
- package/dist/cli/commands/start.js +32 -0
- package/dist/cli/commands/sync.d.ts +2 -0
- package/dist/cli/commands/sync.js +17 -0
- package/dist/cli/commands/uninstall.d.ts +2 -0
- package/dist/cli/commands/uninstall.js +39 -0
- package/dist/cli/index.d.ts +2 -0
- package/dist/cli/index.js +114 -0
- package/dist/core/ConfigurationProvider.d.ts +31 -0
- package/dist/core/ConfigurationProvider.js +416 -0
- package/dist/core/InstallationService.d.ts +17 -0
- package/dist/core/InstallationService.js +144 -0
- package/dist/core/MCPManager.d.ts +17 -0
- package/dist/core/MCPManager.js +98 -0
- package/dist/core/RequirementService.d.ts +45 -0
- package/dist/core/RequirementService.js +123 -0
- package/dist/core/constants.d.ts +29 -0
- package/dist/core/constants.js +55 -0
- package/dist/core/installers/BaseInstaller.d.ts +73 -0
- package/dist/core/installers/BaseInstaller.js +247 -0
- package/dist/core/installers/ClientInstaller.d.ts +17 -0
- package/dist/core/installers/ClientInstaller.js +307 -0
- package/dist/core/installers/CommandInstaller.d.ts +36 -0
- package/dist/core/installers/CommandInstaller.js +170 -0
- package/dist/core/installers/GeneralInstaller.d.ts +32 -0
- package/dist/core/installers/GeneralInstaller.js +87 -0
- package/dist/core/installers/InstallerFactory.d.ts +52 -0
- package/dist/core/installers/InstallerFactory.js +95 -0
- package/dist/core/installers/NpmInstaller.d.ts +25 -0
- package/dist/core/installers/NpmInstaller.js +123 -0
- package/dist/core/installers/PipInstaller.d.ts +25 -0
- package/dist/core/installers/PipInstaller.js +114 -0
- package/dist/core/installers/RequirementInstaller.d.ts +32 -0
- package/dist/core/installers/RequirementInstaller.js +3 -0
- package/dist/core/installers/index.d.ts +6 -0
- package/dist/core/installers/index.js +7 -0
- package/dist/core/types.d.ts +152 -0
- package/dist/core/types.js +16 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.js +19 -0
- package/dist/services/InstallRequestValidator.d.ts +21 -0
- package/dist/services/InstallRequestValidator.js +99 -0
- package/dist/services/ServerService.d.ts +47 -0
- package/dist/services/ServerService.js +145 -0
- package/dist/utils/UpdateCheckTracker.d.ts +39 -0
- package/dist/utils/UpdateCheckTracker.js +80 -0
- package/dist/utils/clientUtils.d.ts +29 -0
- package/dist/utils/clientUtils.js +105 -0
- package/dist/utils/feedUtils.d.ts +5 -0
- package/dist/utils/feedUtils.js +29 -0
- package/dist/utils/githubAuth.d.ts +1 -0
- package/dist/utils/githubAuth.js +123 -0
- package/dist/utils/logger.d.ts +14 -0
- package/dist/utils/logger.js +90 -0
- package/dist/utils/osUtils.d.ts +16 -0
- package/dist/utils/osUtils.js +235 -0
- package/dist/web/public/css/modal.css +250 -0
- package/dist/web/public/css/notifications.css +70 -0
- package/dist/web/public/index.html +157 -0
- package/dist/web/public/js/api.js +213 -0
- package/dist/web/public/js/modal.js +572 -0
- package/dist/web/public/js/notifications.js +99 -0
- package/dist/web/public/js/serverCategoryDetails.js +210 -0
- package/dist/web/public/js/serverCategoryList.js +82 -0
- package/dist/web/public/modal.html +61 -0
- package/dist/web/public/styles.css +155 -0
- package/dist/web/server.d.ts +5 -0
- package/dist/web/server.js +150 -0
- package/package.json +53 -0
- package/src/cli/commands/install.ts +140 -0
- package/src/cli/commands/list.ts +112 -0
- package/src/cli/commands/pull.ts +16 -0
- package/src/cli/commands/serve.ts +37 -0
- package/src/cli/commands/uninstall.ts +54 -0
- package/src/cli/index.ts +127 -0
- package/src/core/ConfigurationProvider.ts +489 -0
- package/src/core/InstallationService.ts +173 -0
- package/src/core/MCPManager.ts +134 -0
- package/src/core/RequirementService.ts +147 -0
- package/src/core/constants.ts +61 -0
- package/src/core/installers/BaseInstaller.ts +292 -0
- package/src/core/installers/ClientInstaller.ts +423 -0
- package/src/core/installers/CommandInstaller.ts +185 -0
- package/src/core/installers/GeneralInstaller.ts +89 -0
- package/src/core/installers/InstallerFactory.ts +109 -0
- package/src/core/installers/NpmInstaller.ts +128 -0
- package/src/core/installers/PipInstaller.ts +121 -0
- package/src/core/installers/RequirementInstaller.ts +38 -0
- package/src/core/installers/index.ts +9 -0
- package/src/core/types.ts +163 -0
- package/src/index.ts +44 -0
- package/src/services/InstallRequestValidator.ts +112 -0
- package/src/services/ServerService.ts +181 -0
- package/src/utils/UpdateCheckTracker.ts +86 -0
- package/src/utils/clientUtils.ts +112 -0
- package/src/utils/feedUtils.ts +31 -0
- package/src/utils/githubAuth.ts +142 -0
- package/src/utils/logger.ts +101 -0
- package/src/utils/osUtils.ts +250 -0
- package/src/web/public/css/modal.css +250 -0
- package/src/web/public/css/notifications.css +70 -0
- package/src/web/public/index.html +157 -0
- package/src/web/public/js/api.js +213 -0
- package/src/web/public/js/modal.js +572 -0
- package/src/web/public/js/notifications.js +99 -0
- package/src/web/public/js/serverCategoryDetails.js +210 -0
- package/src/web/public/js/serverCategoryList.js +82 -0
- package/src/web/public/modal.html +61 -0
- package/src/web/public/styles.css +155 -0
- package/src/web/server.ts +195 -0
- package/tsconfig.json +18 -0
package/package.json
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "imcp",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "Node.js SDK for Model Context Protocol (MCP)",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"bin": {
|
|
8
|
+
"imcp": "./dist/cli/index.js"
|
|
9
|
+
},
|
|
10
|
+
"scripts": {
|
|
11
|
+
"build": "tsc && npm run copy-public && npm run copy-feeds",
|
|
12
|
+
"dev": "tsc -w",
|
|
13
|
+
"start": "node dist/web/server.js",
|
|
14
|
+
"dev:server": "ts-node-dev --respawn --transpile-only src/cli/index.ts start",
|
|
15
|
+
"test": "jest",
|
|
16
|
+
"lint": "eslint src --ext .ts",
|
|
17
|
+
"format": "prettier --write \"src/**/*.ts\"",
|
|
18
|
+
"copy-public": "xcopy /E /I /Y src\\web\\public dist\\web\\public",
|
|
19
|
+
"copy-feeds": "xcopy /E /I /Y src\\feeds dist\\feeds"
|
|
20
|
+
},
|
|
21
|
+
"keywords": [
|
|
22
|
+
"mcp",
|
|
23
|
+
"sdk",
|
|
24
|
+
"cli"
|
|
25
|
+
],
|
|
26
|
+
"author": "",
|
|
27
|
+
"license": "MIT",
|
|
28
|
+
"dependencies": {
|
|
29
|
+
"axios": "^1.6.2",
|
|
30
|
+
"commander": "^11.1.0",
|
|
31
|
+
"express": "^4.18.2",
|
|
32
|
+
"extract-zip": "^2.0.1",
|
|
33
|
+
"unzipper": "^0.10.14"
|
|
34
|
+
},
|
|
35
|
+
"devDependencies": {
|
|
36
|
+
"@types/express": "^4.17.21",
|
|
37
|
+
"@types/jest": "^29.5.11",
|
|
38
|
+
"@types/node": "^20.10.5",
|
|
39
|
+
"@typescript-eslint/eslint-plugin": "^6.15.0",
|
|
40
|
+
"@typescript-eslint/parser": "^6.15.0",
|
|
41
|
+
"eslint": "^8.56.0",
|
|
42
|
+
"jest": "^29.7.0",
|
|
43
|
+
"prettier": "^3.1.1",
|
|
44
|
+
"ts-jest": "^29.1.1",
|
|
45
|
+
"ts-node": "^10.9.2",
|
|
46
|
+
"ts-node-dev": "^2.0.0",
|
|
47
|
+
"typescript": "^5.3.3"
|
|
48
|
+
},
|
|
49
|
+
"publishConfig": {
|
|
50
|
+
"access": "public"
|
|
51
|
+
},
|
|
52
|
+
"type": "module"
|
|
53
|
+
}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import { serverService } from '../../services/ServerService.js';
|
|
3
|
+
import { Logger } from '../../utils/logger.js';
|
|
4
|
+
import { hasLocalFeeds } from '../../utils/feedUtils.js';
|
|
5
|
+
import { ServerInstallOptions } from '../../core/types.js';
|
|
6
|
+
import { SUPPORTED_CLIENT_NAMES } from '../../core/constants.js';
|
|
7
|
+
|
|
8
|
+
export function createInstallCommand(): Command {
|
|
9
|
+
return new Command('install')
|
|
10
|
+
.description('Install specific MCP servers')
|
|
11
|
+
.addHelpText('after', `
|
|
12
|
+
Examples:
|
|
13
|
+
# Install a server
|
|
14
|
+
$ imcp install --category ai-coder-tools --name github-tools
|
|
15
|
+
|
|
16
|
+
# Install with specific client targets (semicolon separated)
|
|
17
|
+
$ imcp install --category ai-coder-tools --name github-tools --clients "MSRooCode;GithubCopilot"
|
|
18
|
+
|
|
19
|
+
# Install with environment variables
|
|
20
|
+
$ imcp install --category ai-coder-tools --name github-tools --envs "GITHUB_TOKEN=abc123;API_KEY=xyz789"
|
|
21
|
+
`)
|
|
22
|
+
.requiredOption(
|
|
23
|
+
'--category <category>',
|
|
24
|
+
'Server category'
|
|
25
|
+
)
|
|
26
|
+
.requiredOption(
|
|
27
|
+
'--name <name>',
|
|
28
|
+
'Server name to install'
|
|
29
|
+
)
|
|
30
|
+
.option(
|
|
31
|
+
'--force',
|
|
32
|
+
'Force installation even if server already exists',
|
|
33
|
+
false
|
|
34
|
+
)
|
|
35
|
+
.option(
|
|
36
|
+
'--clients <clients>',
|
|
37
|
+
'Target clients (semicolon separated). Supported values: Cline, MSRooCode, GithubCopilot. If not specified, installs for all clients'
|
|
38
|
+
)
|
|
39
|
+
.option(
|
|
40
|
+
'--envs <envs>',
|
|
41
|
+
'Environment variables (semicolon separated key=value pairs)'
|
|
42
|
+
)
|
|
43
|
+
.action(async (options: {
|
|
44
|
+
category: string;
|
|
45
|
+
name: string;
|
|
46
|
+
force: boolean;
|
|
47
|
+
clients?: string;
|
|
48
|
+
envs?: string;
|
|
49
|
+
verbose?: boolean;
|
|
50
|
+
}) => {
|
|
51
|
+
try {
|
|
52
|
+
// Check for local feeds existence at the start
|
|
53
|
+
const feedsExist = await hasLocalFeeds();
|
|
54
|
+
if (!feedsExist) {
|
|
55
|
+
Logger.log('Local feeds not found, syncing from remote...');
|
|
56
|
+
await serverService.syncFeeds();
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const { category, name, verbose, force, clients, envs } = options;
|
|
60
|
+
|
|
61
|
+
Logger.debug(`Install options: ${JSON.stringify({ category, name, force, verbose, clients, envs })}`);
|
|
62
|
+
|
|
63
|
+
const serverName = name.trim();
|
|
64
|
+
Logger.debug(`Server name: ${serverName}`);
|
|
65
|
+
|
|
66
|
+
if (!await serverService.validateServerName(category, serverName)) {
|
|
67
|
+
Logger.error('Invalid server name or category provided', {
|
|
68
|
+
category,
|
|
69
|
+
serverName
|
|
70
|
+
});
|
|
71
|
+
process.exit(1);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Parse and validate clients
|
|
75
|
+
const parsedClients = clients ? clients.split(';')
|
|
76
|
+
.map((c: string) => c.trim())
|
|
77
|
+
.filter(Boolean)
|
|
78
|
+
.map((c: string) => {
|
|
79
|
+
const clientName = c.toLowerCase();
|
|
80
|
+
const validClient = SUPPORTED_CLIENT_NAMES.find((name: string) => name.toLowerCase() === clientName);
|
|
81
|
+
if (!validClient) {
|
|
82
|
+
Logger.error(`Invalid client name: ${c}`);
|
|
83
|
+
return null;
|
|
84
|
+
}
|
|
85
|
+
return validClient;
|
|
86
|
+
})
|
|
87
|
+
.filter((c: string | null): c is string => c !== null) : undefined;
|
|
88
|
+
|
|
89
|
+
if (parsedClients && parsedClients.length > 0) {
|
|
90
|
+
Logger.debug(`Target clients: ${JSON.stringify(parsedClients)}`);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Parse environment variables
|
|
94
|
+
const parsedEnvs: Record<string, string> = {};
|
|
95
|
+
if (envs) {
|
|
96
|
+
const pairs = envs.split(';');
|
|
97
|
+
for (const pair of pairs) {
|
|
98
|
+
const [key, value] = pair.split('=').map((s: string) => s.trim());
|
|
99
|
+
if (key && value) {
|
|
100
|
+
parsedEnvs[key] = value;
|
|
101
|
+
} else {
|
|
102
|
+
Logger.error(`Invalid environment variable format: ${pair}`);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
if (Object.keys(parsedEnvs).length > 0) {
|
|
108
|
+
Logger.debug(`Environment variables: ${JSON.stringify(parsedEnvs)}`);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
Logger.log(`Installing server: ${serverName}`);
|
|
112
|
+
|
|
113
|
+
const installOptions: ServerInstallOptions = {
|
|
114
|
+
force: options.force,
|
|
115
|
+
...(parsedClients?.length && { targetClients: parsedClients }),
|
|
116
|
+
...(Object.keys(parsedEnvs).length > 0 && { env: parsedEnvs })
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
const results = [await serverService.installMcpServer(category, serverName, installOptions)];
|
|
120
|
+
|
|
121
|
+
const { success, messages } = serverService.formatOperationResults(results);
|
|
122
|
+
|
|
123
|
+
messages.forEach((message: string) => {
|
|
124
|
+
if (success) {
|
|
125
|
+
Logger.log(`✓ ${message}`);
|
|
126
|
+
} else {
|
|
127
|
+
Logger.error(`✗ ${message}`);
|
|
128
|
+
}
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
if (!success) {
|
|
132
|
+
process.exit(1);
|
|
133
|
+
}
|
|
134
|
+
} catch (error: unknown) {
|
|
135
|
+
const message = error instanceof Error ? error.message : 'Unknown error';
|
|
136
|
+
Logger.error('Installation failed:', message);
|
|
137
|
+
process.exit(1);
|
|
138
|
+
}
|
|
139
|
+
});
|
|
140
|
+
}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import fs from 'fs/promises';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
import { LOCAL_FEEDS_DIR } from '../../core/constants.js';
|
|
5
|
+
import { mcpManager } from '../../core/MCPManager.js';
|
|
6
|
+
import { Logger } from '../../utils/logger.js';
|
|
7
|
+
|
|
8
|
+
interface SimplifiedServer {
|
|
9
|
+
name: string;
|
|
10
|
+
description: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
interface SimplifiedFeed {
|
|
14
|
+
name: string;
|
|
15
|
+
displayName: string;
|
|
16
|
+
description: string;
|
|
17
|
+
mcpServers: SimplifiedServer[];
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function createListCommand(): Command {
|
|
21
|
+
return new Command('list')
|
|
22
|
+
.description('List all available MCP servers from feeds')
|
|
23
|
+
.option('--pull', 'Sync with remote feeds before listing')
|
|
24
|
+
.action(async (options) => {
|
|
25
|
+
try {
|
|
26
|
+
Logger.debug({
|
|
27
|
+
action: 'list_command_started',
|
|
28
|
+
options
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
// If sync flag is provided, try to sync first
|
|
32
|
+
if (options.pull) {
|
|
33
|
+
try {
|
|
34
|
+
await mcpManager.syncFeeds();
|
|
35
|
+
} catch (syncError) {
|
|
36
|
+
Logger.error('Failed to sync feeds, falling back to local feeds', syncError);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
Logger.debug({
|
|
41
|
+
action: 'ensuring_feeds_directory',
|
|
42
|
+
path: LOCAL_FEEDS_DIR
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
// Ensure feeds directory exists
|
|
46
|
+
await fs.mkdir(LOCAL_FEEDS_DIR, { recursive: true });
|
|
47
|
+
|
|
48
|
+
// Read all json files from feeds directory
|
|
49
|
+
Logger.debug('Reading feed files from directory...');
|
|
50
|
+
const files = await fs.readdir(LOCAL_FEEDS_DIR);
|
|
51
|
+
const jsonFiles = files.filter(file => file.endsWith('.json'));
|
|
52
|
+
|
|
53
|
+
Logger.debug({
|
|
54
|
+
action: 'found_feed_files',
|
|
55
|
+
count: jsonFiles.length,
|
|
56
|
+
files: jsonFiles
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
const feeds: SimplifiedFeed[] = [];
|
|
60
|
+
|
|
61
|
+
// Process each json file
|
|
62
|
+
for (const file of jsonFiles) {
|
|
63
|
+
Logger.debug(`Processing feed file: ${file}`);
|
|
64
|
+
try {
|
|
65
|
+
const filePath = path.join(LOCAL_FEEDS_DIR, file);
|
|
66
|
+
const content = await fs.readFile(filePath, 'utf8');
|
|
67
|
+
const feedConfig = JSON.parse(content);
|
|
68
|
+
|
|
69
|
+
Logger.debug({
|
|
70
|
+
action: 'processing_feed',
|
|
71
|
+
file,
|
|
72
|
+
feedName: feedConfig.name,
|
|
73
|
+
serverCount: feedConfig.mcpServers?.length ?? 0
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
// Extract only needed information
|
|
77
|
+
const simplifiedFeed: SimplifiedFeed = {
|
|
78
|
+
name: feedConfig.name,
|
|
79
|
+
displayName: feedConfig.displayName,
|
|
80
|
+
description: feedConfig.description,
|
|
81
|
+
mcpServers: feedConfig.mcpServers.map((server: any) => ({
|
|
82
|
+
name: server.name,
|
|
83
|
+
description: server.description
|
|
84
|
+
}))
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
feeds.push(simplifiedFeed);
|
|
88
|
+
Logger.debug(`Successfully processed feed: ${feedConfig.name}`);
|
|
89
|
+
} catch (error) {
|
|
90
|
+
Logger.error(`Failed to process feed file: ${file}`, error);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
if (feeds.length === 0) {
|
|
95
|
+
Logger.log('No feeds found.');
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
Logger.debug({
|
|
100
|
+
action: 'completed_processing',
|
|
101
|
+
totalFeeds: feeds.length,
|
|
102
|
+
totalServers: feeds.reduce((acc, feed) => acc + feed.mcpServers.length, 0)
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
// Output as formatted JSON
|
|
106
|
+
console.log(JSON.stringify(feeds, null, 2));
|
|
107
|
+
} catch (error) {
|
|
108
|
+
Logger.error('Failed to list servers', error);
|
|
109
|
+
process.exit(1);
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import { serverService } from '../../services/ServerService.js';
|
|
3
|
+
|
|
4
|
+
export function createPullCommand(): Command {
|
|
5
|
+
return new Command('pull')
|
|
6
|
+
.description('Pull MCP server configurations from remote feed source')
|
|
7
|
+
.action(async () => {
|
|
8
|
+
try {
|
|
9
|
+
await serverService.syncFeeds();
|
|
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
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import { startWebServer } from '../../web/server.js';
|
|
3
|
+
import { mcpManager } from '../../core/MCPManager.js';
|
|
4
|
+
import { checkGithubAuth } from '../../utils/githubAuth.js';
|
|
5
|
+
import { Logger } from '../../utils/logger.js';
|
|
6
|
+
|
|
7
|
+
export function createServeCommand(): Command {
|
|
8
|
+
return new Command('serve')
|
|
9
|
+
.description('Serve local web interface')
|
|
10
|
+
.option('-p, --port <port>', 'Port to run the server on', '3000')
|
|
11
|
+
.action(async (options) => {
|
|
12
|
+
try {
|
|
13
|
+
// Sync feeds before start the local UI
|
|
14
|
+
await mcpManager.syncFeeds();
|
|
15
|
+
|
|
16
|
+
// Ensure MCP manager is initialized before starting the web server
|
|
17
|
+
await mcpManager.initialize();
|
|
18
|
+
|
|
19
|
+
const port = parseInt(options.port, 10);
|
|
20
|
+
if (isNaN(port) || port < 1 || port > 65535) {
|
|
21
|
+
throw new Error('Invalid port number');
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
await startWebServer(port);
|
|
25
|
+
|
|
26
|
+
// The server is running, keep the process alive
|
|
27
|
+
process.on('SIGINT', () => {
|
|
28
|
+
console.log('\nShutting down server...');
|
|
29
|
+
process.exit(0);
|
|
30
|
+
});
|
|
31
|
+
} catch (error) {
|
|
32
|
+
const message = error instanceof Error ? error.message : 'Unknown error';
|
|
33
|
+
console.error('Failed to start web server:', message);
|
|
34
|
+
process.exit(1);
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import { serverService } from '../../services/ServerService.js';
|
|
3
|
+
|
|
4
|
+
export function createUninstallCommand(): Command {
|
|
5
|
+
return new Command('uninstall')
|
|
6
|
+
.description('Uninstall specific MCP servers')
|
|
7
|
+
.requiredOption(
|
|
8
|
+
'--category <category>',
|
|
9
|
+
'--names <names>',
|
|
10
|
+
'Server names (semicolon separated)'
|
|
11
|
+
)
|
|
12
|
+
.option(
|
|
13
|
+
'--remove-data',
|
|
14
|
+
'Remove all associated data',
|
|
15
|
+
false
|
|
16
|
+
)
|
|
17
|
+
.action(async (options) => {
|
|
18
|
+
try {
|
|
19
|
+
const serverNames = options.names.split(';').map((name: string) => name.trim());
|
|
20
|
+
|
|
21
|
+
if (!serverService.validateServerName(options.category, serverNames)) {
|
|
22
|
+
console.error('Invalid server names provided');
|
|
23
|
+
process.exit(1);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
console.log(`Uninstalling servers: ${serverNames.join(', ')}`);
|
|
27
|
+
|
|
28
|
+
const results = await Promise.all(
|
|
29
|
+
serverNames.map((serverName: string) =>
|
|
30
|
+
serverService.uninstallMcpServer(options.category, serverName, {
|
|
31
|
+
removeData: options.removeData,
|
|
32
|
+
})
|
|
33
|
+
)
|
|
34
|
+
);
|
|
35
|
+
|
|
36
|
+
const { success, messages } = serverService.formatOperationResults(results);
|
|
37
|
+
messages.forEach(message => {
|
|
38
|
+
if (success) {
|
|
39
|
+
console.log(`✓ ${message}`);
|
|
40
|
+
} else {
|
|
41
|
+
console.error(`✗ ${message}`);
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
if (!success) {
|
|
46
|
+
process.exit(1);
|
|
47
|
+
}
|
|
48
|
+
} catch (error) {
|
|
49
|
+
const message = error instanceof Error ? error.message : 'Unknown error';
|
|
50
|
+
console.error('Uninstallation failed:', message);
|
|
51
|
+
process.exit(1);
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
}
|
package/src/cli/index.ts
ADDED
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { Command } from 'commander';
|
|
4
|
+
import { createServeCommand } from './commands/serve.js';
|
|
5
|
+
import { createListCommand } from './commands/list.js';
|
|
6
|
+
import { createInstallCommand } from './commands/install.js';
|
|
7
|
+
import { createUninstallCommand } from './commands/uninstall.js';
|
|
8
|
+
import { createPullCommand } from './commands/pull.js';
|
|
9
|
+
import { mcpManager } from '../core/MCPManager.js';
|
|
10
|
+
import { Logger } from '../utils/logger.js';
|
|
11
|
+
import axios from 'axios';
|
|
12
|
+
import path from 'path';
|
|
13
|
+
import { fileURLToPath } from 'url';
|
|
14
|
+
import fs from 'fs';
|
|
15
|
+
|
|
16
|
+
// Custom error interface for Commander.js errors
|
|
17
|
+
// ANSI color codes
|
|
18
|
+
const COLORS = {
|
|
19
|
+
reset: '\x1b[0m',
|
|
20
|
+
yellow: '\x1b[33m'
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
interface CommanderError extends Error {
|
|
24
|
+
code?: string;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
async function main(): Promise<void> {
|
|
28
|
+
// Initialize the MCP manager
|
|
29
|
+
await mcpManager.initialize();
|
|
30
|
+
|
|
31
|
+
const program = new Command();
|
|
32
|
+
program
|
|
33
|
+
.name('imcp')
|
|
34
|
+
.description('IMCP (Install Model Context Protocol) CLI')
|
|
35
|
+
.version('0.0.1')
|
|
36
|
+
.option('--verbose', 'Show detailed logs for all commands');
|
|
37
|
+
|
|
38
|
+
// Parse global options first
|
|
39
|
+
program.parseOptions(process.argv);
|
|
40
|
+
const opts = program.opts();
|
|
41
|
+
Logger.setVerbose(!!opts.verbose);
|
|
42
|
+
|
|
43
|
+
// Add all commands
|
|
44
|
+
program.addCommand(createServeCommand());
|
|
45
|
+
program.addCommand(createListCommand());
|
|
46
|
+
program.addCommand(createInstallCommand());
|
|
47
|
+
// program.addCommand(createUninstallCommand());
|
|
48
|
+
program.addCommand(createPullCommand());
|
|
49
|
+
|
|
50
|
+
// Error handling for the entire CLI
|
|
51
|
+
program.exitOverride();
|
|
52
|
+
|
|
53
|
+
// Check for updates
|
|
54
|
+
await checkForUpdates();
|
|
55
|
+
|
|
56
|
+
try {
|
|
57
|
+
await program.parseAsync(process.argv);
|
|
58
|
+
} catch (error) {
|
|
59
|
+
const commanderError = error as CommanderError;
|
|
60
|
+
|
|
61
|
+
if (commanderError.code === 'commander.help') {
|
|
62
|
+
// Help was displayed, exit normally
|
|
63
|
+
process.exit(0);
|
|
64
|
+
} else if (commanderError.code === 'commander.version') {
|
|
65
|
+
// Version was displayed, exit normally
|
|
66
|
+
process.exit(0);
|
|
67
|
+
} else {
|
|
68
|
+
console.error('Error:', commanderError.message || 'An unknown error occurred');
|
|
69
|
+
process.exit(1);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Handle unhandled promise rejections
|
|
75
|
+
process.on('unhandledRejection', (error: unknown) => {
|
|
76
|
+
if (error instanceof Error) {
|
|
77
|
+
console.error('Unhandled promise rejection:', error.message);
|
|
78
|
+
} else {
|
|
79
|
+
console.error('Unhandled promise rejection:', error);
|
|
80
|
+
}
|
|
81
|
+
process.exit(1);
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Check if there's a newer version of the package available
|
|
86
|
+
*/
|
|
87
|
+
async function checkForUpdates(): Promise<void> {
|
|
88
|
+
try {
|
|
89
|
+
// Get the current package version
|
|
90
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
91
|
+
const __dirname = path.dirname(__filename);
|
|
92
|
+
const packagePath = path.resolve(__dirname, '../../package.json');
|
|
93
|
+
const packageJson = JSON.parse(fs.readFileSync(packagePath, 'utf8'));
|
|
94
|
+
const currentVersion = packageJson.name && packageJson.version ? packageJson.version : '0.0.0';
|
|
95
|
+
const packageName = packageJson.name || 'imcp';
|
|
96
|
+
|
|
97
|
+
try {
|
|
98
|
+
// Get the latest version from npm registry (only for published packages)
|
|
99
|
+
const npmResponse = await axios.get(`https://registry.npmjs.org/${packageName}`);
|
|
100
|
+
|
|
101
|
+
if (npmResponse.data && npmResponse.data['dist-tags'] && npmResponse.data['dist-tags'].latest) {
|
|
102
|
+
const latestVersion = npmResponse.data['dist-tags'].latest;
|
|
103
|
+
|
|
104
|
+
if (latestVersion && latestVersion !== currentVersion) {
|
|
105
|
+
console.log(`${COLORS.yellow}Update available for ${packageName}: ${currentVersion} → ${latestVersion}${COLORS.reset}`);
|
|
106
|
+
console.log(`${COLORS.yellow}Run \`npm install -g ${packageName}@latest\` to update${COLORS.reset}`);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
} catch (npmError) {
|
|
110
|
+
// Log the npm error
|
|
111
|
+
Logger.debug(`Failed to check npm registry: ${npmError instanceof Error ? npmError.message : String(npmError)}`);
|
|
112
|
+
}
|
|
113
|
+
} catch (error) {
|
|
114
|
+
// Silently fail - don't interrupt the command if update check fails
|
|
115
|
+
Logger.debug(`Failed to check for updates: ${error instanceof Error ? error.message : String(error)}`);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Start the CLI
|
|
120
|
+
main().catch((error: unknown) => {
|
|
121
|
+
if (error instanceof Error) {
|
|
122
|
+
console.error('Fatal error:', error.message);
|
|
123
|
+
} else {
|
|
124
|
+
console.error('Fatal error:', error);
|
|
125
|
+
}
|
|
126
|
+
process.exit(1);
|
|
127
|
+
});
|