mcpmate 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +176 -0
  3. package/dist/cli.d.ts +3 -0
  4. package/dist/cli.d.ts.map +1 -0
  5. package/dist/cli.js +191 -0
  6. package/dist/cli.js.map +1 -0
  7. package/dist/commands/export.d.ts +5 -0
  8. package/dist/commands/export.d.ts.map +1 -0
  9. package/dist/commands/export.js +117 -0
  10. package/dist/commands/export.js.map +1 -0
  11. package/dist/commands/install.d.ts +4 -0
  12. package/dist/commands/install.d.ts.map +1 -0
  13. package/dist/commands/install.js +120 -0
  14. package/dist/commands/install.js.map +1 -0
  15. package/dist/commands/list.d.ts +4 -0
  16. package/dist/commands/list.d.ts.map +1 -0
  17. package/dist/commands/list.js +62 -0
  18. package/dist/commands/list.js.map +1 -0
  19. package/dist/commands/openclaw.d.ts +6 -0
  20. package/dist/commands/openclaw.d.ts.map +1 -0
  21. package/dist/commands/openclaw.js +163 -0
  22. package/dist/commands/openclaw.js.map +1 -0
  23. package/dist/commands/search.d.ts +4 -0
  24. package/dist/commands/search.d.ts.map +1 -0
  25. package/dist/commands/search.js +88 -0
  26. package/dist/commands/search.js.map +1 -0
  27. package/dist/commands/template.d.ts +5 -0
  28. package/dist/commands/template.d.ts.map +1 -0
  29. package/dist/commands/template.js +122 -0
  30. package/dist/commands/template.js.map +1 -0
  31. package/dist/commands/toggle.d.ts +3 -0
  32. package/dist/commands/toggle.d.ts.map +1 -0
  33. package/dist/commands/toggle.js +53 -0
  34. package/dist/commands/toggle.js.map +1 -0
  35. package/dist/commands/uninstall.d.ts +2 -0
  36. package/dist/commands/uninstall.d.ts.map +1 -0
  37. package/dist/commands/uninstall.js +48 -0
  38. package/dist/commands/uninstall.js.map +1 -0
  39. package/package.json +56 -0
  40. package/src/cli.ts +191 -0
  41. package/src/commands/export.ts +127 -0
  42. package/src/commands/install.ts +139 -0
  43. package/src/commands/list.ts +75 -0
  44. package/src/commands/openclaw.ts +186 -0
  45. package/src/commands/search.ts +99 -0
  46. package/src/commands/template.ts +135 -0
  47. package/src/commands/toggle.ts +58 -0
  48. package/src/commands/uninstall.ts +51 -0
  49. package/tsconfig.json +19 -0
@@ -0,0 +1,48 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.uninstallServer = uninstallServer;
7
+ const chalk_1 = __importDefault(require("chalk"));
8
+ const fs_extra_1 = __importDefault(require("fs-extra"));
9
+ const path_1 = __importDefault(require("path"));
10
+ const os_1 = __importDefault(require("os"));
11
+ const inquirer_1 = __importDefault(require("inquirer"));
12
+ const CONFIG_DIR = path_1.default.join(os_1.default.homedir(), '.mcpm');
13
+ const SERVERS_FILE = path_1.default.join(CONFIG_DIR, 'servers.json');
14
+ async function getInstalledServers() {
15
+ if (await fs_extra_1.default.pathExists(SERVERS_FILE)) {
16
+ return await fs_extra_1.default.readJson(SERVERS_FILE);
17
+ }
18
+ return {};
19
+ }
20
+ async function saveInstalledServers(servers) {
21
+ await fs_extra_1.default.ensureDir(CONFIG_DIR);
22
+ await fs_extra_1.default.writeJson(SERVERS_FILE, servers, { spaces: 2 });
23
+ }
24
+ async function uninstallServer(serverName) {
25
+ console.log(chalk_1.default.blue('πŸ—‘οΈ Uninstalling MCP server...'));
26
+ console.log(chalk_1.default.gray(`Server: ${serverName}`));
27
+ console.log();
28
+ const servers = await getInstalledServers();
29
+ if (!servers[serverName]) {
30
+ console.log(chalk_1.default.yellow(`⚠️ Server "${serverName}" is not installed.`));
31
+ return;
32
+ }
33
+ const { confirm } = await inquirer_1.default.prompt([{
34
+ type: 'confirm',
35
+ name: 'confirm',
36
+ message: `Are you sure you want to uninstall "${serverName}"?`,
37
+ default: false
38
+ }]);
39
+ if (!confirm) {
40
+ console.log(chalk_1.default.gray('Uninstallation cancelled.'));
41
+ return;
42
+ }
43
+ delete servers[serverName];
44
+ await saveInstalledServers(servers);
45
+ console.log();
46
+ console.log(chalk_1.default.green('βœ… Server uninstalled successfully!'));
47
+ }
48
+ //# sourceMappingURL=uninstall.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"uninstall.js","sourceRoot":"","sources":["../../src/commands/uninstall.ts"],"names":[],"mappings":";;;;;AAqBA,0CA6BC;AAlDD,kDAA0B;AAC1B,wDAA0B;AAC1B,gDAAwB;AACxB,4CAAoB;AACpB,wDAAgC;AAEhC,MAAM,UAAU,GAAG,cAAI,CAAC,IAAI,CAAC,YAAE,CAAC,OAAO,EAAE,EAAE,OAAO,CAAC,CAAC;AACpD,MAAM,YAAY,GAAG,cAAI,CAAC,IAAI,CAAC,UAAU,EAAE,cAAc,CAAC,CAAC;AAE3D,KAAK,UAAU,mBAAmB;IAChC,IAAI,MAAM,kBAAE,CAAC,UAAU,CAAC,YAAY,CAAC,EAAE,CAAC;QACtC,OAAO,MAAM,kBAAE,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAC;IACzC,CAAC;IACD,OAAO,EAAE,CAAC;AACZ,CAAC;AAED,KAAK,UAAU,oBAAoB,CAAC,OAA4B;IAC9D,MAAM,kBAAE,CAAC,SAAS,CAAC,UAAU,CAAC,CAAC;IAC/B,MAAM,kBAAE,CAAC,SAAS,CAAC,YAAY,EAAE,OAAO,EAAE,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC,CAAC;AAC3D,CAAC;AAEM,KAAK,UAAU,eAAe,CAAC,UAAkB;IACtD,OAAO,CAAC,GAAG,CAAC,eAAK,CAAC,IAAI,CAAC,iCAAiC,CAAC,CAAC,CAAC;IAC3D,OAAO,CAAC,GAAG,CAAC,eAAK,CAAC,IAAI,CAAC,WAAW,UAAU,EAAE,CAAC,CAAC,CAAC;IACjD,OAAO,CAAC,GAAG,EAAE,CAAC;IAEd,MAAM,OAAO,GAAG,MAAM,mBAAmB,EAAE,CAAC;IAE5C,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,EAAE,CAAC;QACzB,OAAO,CAAC,GAAG,CAAC,eAAK,CAAC,MAAM,CAAC,eAAe,UAAU,qBAAqB,CAAC,CAAC,CAAC;QAC1E,OAAO;IACT,CAAC;IAED,MAAM,EAAE,OAAO,EAAE,GAAG,MAAM,kBAAQ,CAAC,MAAM,CAAC,CAAC;YACzC,IAAI,EAAE,SAAS;YACf,IAAI,EAAE,SAAS;YACf,OAAO,EAAE,uCAAuC,UAAU,IAAI;YAC9D,OAAO,EAAE,KAAK;SACf,CAAC,CAAC,CAAC;IAEJ,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,OAAO,CAAC,GAAG,CAAC,eAAK,CAAC,IAAI,CAAC,2BAA2B,CAAC,CAAC,CAAC;QACrD,OAAO;IACT,CAAC;IAED,OAAO,OAAO,CAAC,UAAU,CAAC,CAAC;IAC3B,MAAM,oBAAoB,CAAC,OAAO,CAAC,CAAC;IAEpC,OAAO,CAAC,GAAG,EAAE,CAAC;IACd,OAAO,CAAC,GAAG,CAAC,eAAK,CAAC,KAAK,CAAC,oCAAoC,CAAC,CAAC,CAAC;AACjE,CAAC"}
package/package.json ADDED
@@ -0,0 +1,56 @@
1
+ {
2
+ "name": "mcpmate",
3
+ "version": "0.1.0",
4
+ "description": "MCP Mate - The ultimate MCP server manager for OpenClaw and Chinese developers",
5
+ "main": "dist/index.js",
6
+ "bin": {
7
+ "mcpmate": "dist/cli.js",
8
+ "mcm": "dist/cli.js"
9
+ },
10
+ "scripts": {
11
+ "build": "tsc",
12
+ "dev": "ts-node src/cli.ts",
13
+ "start": "node dist/cli.js",
14
+ "test": "jest"
15
+ },
16
+ "keywords": [
17
+ "mcp",
18
+ "model-context-protocol",
19
+ "cli",
20
+ "server-manager",
21
+ "openclaw",
22
+ "feishu",
23
+ "dingtalk",
24
+ "china"
25
+ ],
26
+ "author": "Clawton",
27
+ "license": "MIT",
28
+ "repository": {
29
+ "type": "git",
30
+ "url": "https://github.com/clawton/mcpmate.git"
31
+ },
32
+ "bugs": {
33
+ "url": "https://github.com/clawton/mcpmate/issues"
34
+ },
35
+ "homepage": "https://github.com/clawton/mcpmate#readme",
36
+ "dependencies": {
37
+ "commander": "^11.1.0",
38
+ "chalk": "^4.1.2",
39
+ "inquirer": "^8.2.6",
40
+ "axios": "^1.6.2",
41
+ "fs-extra": "^11.2.0",
42
+ "yaml": "^2.3.4"
43
+ },
44
+ "devDependencies": {
45
+ "@types/node": "^20.10.4",
46
+ "@types/fs-extra": "^11.0.4",
47
+ "@types/inquirer": "^9.0.7",
48
+ "typescript": "^5.3.3",
49
+ "ts-node": "^10.9.2",
50
+ "jest": "^29.7.0",
51
+ "@types/jest": "^29.5.11"
52
+ },
53
+ "engines": {
54
+ "node": ">=18.0.0"
55
+ }
56
+ }
package/src/cli.ts ADDED
@@ -0,0 +1,191 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { Command } from 'commander';
4
+ import chalk from 'chalk';
5
+ import { searchServers } from './commands/search';
6
+ import { installServer } from './commands/install';
7
+ import { listServers } from './commands/list';
8
+ import { uninstallServer } from './commands/uninstall';
9
+ import { enableServer, disableServer } from './commands/toggle';
10
+ import { exportConfig } from './commands/export';
11
+ import { openclawCommand } from './commands/openclaw';
12
+ import { templateCommand } from './commands/template';
13
+
14
+ const program = new Command();
15
+
16
+ program
17
+ .name('mcpmate')
18
+ .alias('mcm')
19
+ .description('MCP Mate - The ultimate MCP server manager for OpenClaw and Chinese developers')
20
+ .version('0.1.0');
21
+
22
+ program
23
+ .command('search')
24
+ .description('Search for MCP servers in the registry')
25
+ .argument('<query>', 'Search query')
26
+ .option('-c, --category <category>', 'Filter by category')
27
+ .action(async (query, options) => {
28
+ try {
29
+ await searchServers(query, options);
30
+ } catch (error) {
31
+ console.error(chalk.red('Error:'), error instanceof Error ? error.message : error);
32
+ process.exit(1);
33
+ }
34
+ });
35
+
36
+ program
37
+ .command('install')
38
+ .description('Install an MCP server')
39
+ .argument('<server>', 'Server name or package')
40
+ .option('-g, --global', 'Install globally')
41
+ .action(async (server, options) => {
42
+ try {
43
+ await installServer(server, options);
44
+ } catch (error) {
45
+ console.error(chalk.red('Error:'), error instanceof Error ? error.message : error);
46
+ process.exit(1);
47
+ }
48
+ });
49
+
50
+ program
51
+ .command('list')
52
+ .alias('ls')
53
+ .description('List installed MCP servers')
54
+ .option('-a, --all', 'Show all servers including disabled')
55
+ .action(async (options) => {
56
+ try {
57
+ await listServers(options);
58
+ } catch (error) {
59
+ console.error(chalk.red('Error:'), error instanceof Error ? error.message : error);
60
+ process.exit(1);
61
+ }
62
+ });
63
+
64
+ program
65
+ .command('uninstall')
66
+ .alias('remove')
67
+ .description('Uninstall an MCP server')
68
+ .argument('<server>', 'Server name')
69
+ .action(async (server) => {
70
+ try {
71
+ await uninstallServer(server);
72
+ } catch (error) {
73
+ console.error(chalk.red('Error:'), error instanceof Error ? error.message : error);
74
+ process.exit(1);
75
+ }
76
+ });
77
+
78
+ program
79
+ .command('enable')
80
+ .description('Enable an MCP server')
81
+ .argument('<server>', 'Server name')
82
+ .action(async (server) => {
83
+ try {
84
+ await enableServer(server);
85
+ } catch (error) {
86
+ console.error(chalk.red('Error:'), error instanceof Error ? error.message : error);
87
+ process.exit(1);
88
+ }
89
+ });
90
+
91
+ program
92
+ .command('disable')
93
+ .description('Disable an MCP server')
94
+ .argument('<server>', 'Server name')
95
+ .action(async (server) => {
96
+ try {
97
+ await disableServer(server);
98
+ } catch (error) {
99
+ console.error(chalk.red('Error:'), error instanceof Error ? error.message : error);
100
+ process.exit(1);
101
+ }
102
+ });
103
+
104
+ program
105
+ .command('export')
106
+ .description('Export MCP configuration')
107
+ .option('-f, --format <format>', 'Export format (claude|cursor|openclaw)', 'openclaw')
108
+ .option('-o, --output <path>', 'Output file path')
109
+ .action(async (options) => {
110
+ try {
111
+ await exportConfig(options);
112
+ } catch (error) {
113
+ console.error(chalk.red('Error:'), error instanceof Error ? error.message : error);
114
+ process.exit(1);
115
+ }
116
+ });
117
+
118
+ // Template commands (εœΊζ™―ζ¨‘ζΏ)
119
+ const template = program
120
+ .command('template')
121
+ .alias('t')
122
+ .description('Scene templates for quick setup');
123
+
124
+ template
125
+ .command('list')
126
+ .description('List available templates')
127
+ .action(async () => {
128
+ try {
129
+ await templateCommand.list();
130
+ } catch (error) {
131
+ console.error(chalk.red('Error:'), error instanceof Error ? error.message : error);
132
+ process.exit(1);
133
+ }
134
+ });
135
+
136
+ template
137
+ .command('use')
138
+ .description('Use a template')
139
+ .argument('<template>', 'Template name')
140
+ .action(async (templateName) => {
141
+ try {
142
+ await templateCommand.use(templateName);
143
+ } catch (error) {
144
+ console.error(chalk.red('Error:'), error instanceof Error ? error.message : error);
145
+ process.exit(1);
146
+ }
147
+ });
148
+
149
+ // OpenClaw integration commands
150
+ const openclaw = program
151
+ .command('openclaw')
152
+ .alias('oc')
153
+ .description('OpenClaw integration commands');
154
+
155
+ openclaw
156
+ .command('setup')
157
+ .description('Setup MCP servers for OpenClaw')
158
+ .action(async () => {
159
+ try {
160
+ await openclawCommand.setup();
161
+ } catch (error) {
162
+ console.error(chalk.red('Error:'), error instanceof Error ? error.message : error);
163
+ process.exit(1);
164
+ }
165
+ });
166
+
167
+ openclaw
168
+ .command('sync')
169
+ .description('Sync MCP configuration to OpenClaw')
170
+ .action(async () => {
171
+ try {
172
+ await openclawCommand.sync();
173
+ } catch (error) {
174
+ console.error(chalk.red('Error:'), error instanceof Error ? error.message : error);
175
+ process.exit(1);
176
+ }
177
+ });
178
+
179
+ openclaw
180
+ .command('status')
181
+ .description('Show OpenClaw MCP configuration status')
182
+ .action(async () => {
183
+ try {
184
+ await openclawCommand.status();
185
+ } catch (error) {
186
+ console.error(chalk.red('Error:'), error instanceof Error ? error.message : error);
187
+ process.exit(1);
188
+ }
189
+ });
190
+
191
+ program.parse();
@@ -0,0 +1,127 @@
1
+ import chalk from 'chalk';
2
+ import fs from 'fs-extra';
3
+ import path from 'path';
4
+ import os from 'os';
5
+ import YAML from 'yaml';
6
+
7
+ const CONFIG_DIR = path.join(os.homedir(), '.mcpm');
8
+ const SERVERS_FILE = path.join(CONFIG_DIR, 'servers.json');
9
+
10
+ async function getInstalledServers(): Promise<Record<string, any>> {
11
+ if (await fs.pathExists(SERVERS_FILE)) {
12
+ return await fs.readJson(SERVERS_FILE);
13
+ }
14
+ return {};
15
+ }
16
+
17
+ function generateClaudeConfig(servers: Record<string, any>): any {
18
+ const mcpServers: Record<string, any> = {};
19
+
20
+ Object.values(servers)
21
+ .filter((s: any) => s.enabled)
22
+ .forEach((server: any) => {
23
+ mcpServers[server.name] = {
24
+ command: server.command,
25
+ args: server.args,
26
+ env: server.env
27
+ };
28
+ });
29
+
30
+ return { mcpServers };
31
+ }
32
+
33
+ function generateCursorConfig(servers: Record<string, any>): any {
34
+ // Cursor uses a similar format to Claude
35
+ return generateClaudeConfig(servers);
36
+ }
37
+
38
+ function generateOpenClawConfig(servers: Record<string, any>): any {
39
+ // OpenClaw specific format
40
+ const mcpServers: Record<string, any> = {};
41
+
42
+ Object.values(servers)
43
+ .filter((s: any) => s.enabled)
44
+ .forEach((server: any) => {
45
+ mcpServers[server.name] = {
46
+ command: server.command,
47
+ args: server.args,
48
+ env: server.env,
49
+ enabled: true
50
+ };
51
+ });
52
+
53
+ return {
54
+ version: '1.0',
55
+ mcpServers,
56
+ metadata: {
57
+ exportedAt: new Date().toISOString(),
58
+ tool: 'mcp-server-manager',
59
+ version: '0.1.0'
60
+ }
61
+ };
62
+ }
63
+
64
+ export async function exportConfig(options: { format?: string; output?: string }): Promise<void> {
65
+ const format = options.format || 'openclaw';
66
+ const servers = await getInstalledServers();
67
+ const enabledServers = Object.values(servers).filter((s: any) => s.enabled);
68
+
69
+ if (enabledServers.length === 0) {
70
+ console.log(chalk.yellow('⚠️ No enabled servers to export.'));
71
+ console.log(chalk.gray('Enable servers with:'), chalk.cyan('mcpm enable <server>'));
72
+ return;
73
+ }
74
+
75
+ console.log(chalk.blue('πŸ“€ Exporting MCP configuration...'));
76
+ console.log(chalk.gray(`Format: ${format}`));
77
+ console.log(chalk.gray(`Servers: ${enabledServers.length}`));
78
+ console.log();
79
+
80
+ let config: any;
81
+ let ext: string;
82
+
83
+ switch (format) {
84
+ case 'claude':
85
+ config = generateClaudeConfig(servers);
86
+ ext = 'json';
87
+ break;
88
+ case 'cursor':
89
+ config = generateCursorConfig(servers);
90
+ ext = 'json';
91
+ break;
92
+ case 'openclaw':
93
+ default:
94
+ config = generateOpenClawConfig(servers);
95
+ ext = 'yaml';
96
+ break;
97
+ }
98
+
99
+ const outputPath = options.output || `mcp-config.${ext}`;
100
+
101
+ if (ext === 'json') {
102
+ await fs.writeJson(outputPath, config, { spaces: 2 });
103
+ } else {
104
+ await fs.writeFile(outputPath, YAML.stringify(config));
105
+ }
106
+
107
+ console.log(chalk.green('βœ… Configuration exported successfully!'));
108
+ console.log(chalk.gray(`Output: ${path.resolve(outputPath)}`));
109
+ console.log();
110
+ console.log(chalk.blue('πŸ’‘ Next steps:'));
111
+
112
+ switch (format) {
113
+ case 'claude':
114
+ console.log(chalk.gray(' 1. Copy the configuration to Claude Desktop settings'));
115
+ console.log(chalk.gray(' 2. Restart Claude Desktop'));
116
+ break;
117
+ case 'cursor':
118
+ console.log(chalk.gray(' 1. Copy the configuration to Cursor MCP settings'));
119
+ console.log(chalk.gray(' 2. Restart Cursor'));
120
+ break;
121
+ case 'openclaw':
122
+ default:
123
+ console.log(chalk.gray(' 1. Run:'), chalk.cyan('mcpm openclaw sync'));
124
+ console.log(chalk.gray(' 2. Or manually copy to OpenClaw MCP configuration'));
125
+ break;
126
+ }
127
+ }
@@ -0,0 +1,139 @@
1
+ import chalk from 'chalk';
2
+ import inquirer from 'inquirer';
3
+ import fs from 'fs-extra';
4
+ import path from 'path';
5
+ import os from 'os';
6
+
7
+ interface ServerConfig {
8
+ name: string;
9
+ command: string;
10
+ args: string[];
11
+ env?: Record<string, string>;
12
+ enabled: boolean;
13
+ installedAt: string;
14
+ }
15
+
16
+ const CONFIG_DIR = path.join(os.homedir(), '.mcpm');
17
+ const SERVERS_FILE = path.join(CONFIG_DIR, 'servers.json');
18
+
19
+ async function ensureConfigDir(): Promise<void> {
20
+ await fs.ensureDir(CONFIG_DIR);
21
+ }
22
+
23
+ async function getInstalledServers(): Promise<Record<string, ServerConfig>> {
24
+ await ensureConfigDir();
25
+ if (await fs.pathExists(SERVERS_FILE)) {
26
+ return await fs.readJson(SERVERS_FILE);
27
+ }
28
+ return {};
29
+ }
30
+
31
+ async function saveInstalledServers(servers: Record<string, ServerConfig>): Promise<void> {
32
+ await ensureConfigDir();
33
+ await fs.writeJson(SERVERS_FILE, servers, { spaces: 2 });
34
+ }
35
+
36
+ // Mock installation logic for MVP
37
+ const SERVER_TEMPLATES: Record<string, Partial<ServerConfig>> = {
38
+ '@modelcontextprotocol/server-postgres': {
39
+ command: 'docker',
40
+ args: ['run', '-i', '--rm', 'mcp/postgres'],
41
+ env: {
42
+ POSTGRES_HOST: 'localhost',
43
+ POSTGRES_PORT: '5432',
44
+ POSTGRES_DB: 'postgres',
45
+ POSTGRES_USER: 'postgres'
46
+ }
47
+ },
48
+ '@modelcontextprotocol/server-github': {
49
+ command: 'docker',
50
+ args: ['run', '-i', '--rm', 'mcp/github'],
51
+ env: {
52
+ GITHUB_PERSONAL_ACCESS_TOKEN: ''
53
+ }
54
+ },
55
+ '@modelcontextprotocol/server-filesystem': {
56
+ command: 'docker',
57
+ args: ['run', '-i', '--rm', '-v', '${HOME}:/home/user', 'mcp/filesystem'],
58
+ env: {}
59
+ }
60
+ };
61
+
62
+ export async function installServer(serverName: string, options: { global?: boolean }): Promise<void> {
63
+ console.log(chalk.blue('πŸ“¦ Installing MCP server...'));
64
+ console.log(chalk.gray(`Server: ${serverName}`));
65
+ console.log();
66
+
67
+ const installedServers = await getInstalledServers();
68
+
69
+ // Check if already installed
70
+ if (installedServers[serverName]) {
71
+ console.log(chalk.yellow(`⚠️ Server "${serverName}" is already installed.`));
72
+ console.log(chalk.gray('Use "mcpm update" to update or "mcpm uninstall" to remove.'));
73
+ return;
74
+ }
75
+
76
+ // Get server template
77
+ const template = SERVER_TEMPLATES[serverName];
78
+ if (!template) {
79
+ console.log(chalk.yellow(`⚠️ Server "${serverName}" not found in registry.`));
80
+ console.log(chalk.gray('Searching for similar servers...'));
81
+ // TODO: Implement fuzzy search
82
+ return;
83
+ }
84
+
85
+ // Interactive configuration
86
+ const config: ServerConfig = {
87
+ name: serverName,
88
+ command: template.command || 'docker',
89
+ args: template.args || [],
90
+ env: {},
91
+ enabled: true,
92
+ installedAt: new Date().toISOString()
93
+ };
94
+
95
+ // Prompt for environment variables
96
+ if (template.env) {
97
+ console.log(chalk.blue('πŸ”§ Configuration required:\n'));
98
+
99
+ for (const [key, defaultValue] of Object.entries(template.env)) {
100
+ const { value } = await inquirer.prompt([{
101
+ type: 'input',
102
+ name: 'value',
103
+ message: `${key}${defaultValue ? ` (default: ${defaultValue})` : ''}:`,
104
+ default: defaultValue || undefined
105
+ }]);
106
+
107
+ if (value) {
108
+ config.env = config.env || {};
109
+ config.env[key] = value;
110
+ }
111
+ }
112
+ }
113
+
114
+ // Confirm installation
115
+ const { confirm } = await inquirer.prompt([{
116
+ type: 'confirm',
117
+ name: 'confirm',
118
+ message: 'Install this server?',
119
+ default: true
120
+ }]);
121
+
122
+ if (!confirm) {
123
+ console.log(chalk.gray('Installation cancelled.'));
124
+ return;
125
+ }
126
+
127
+ // Save configuration
128
+ installedServers[serverName] = config;
129
+ await saveInstalledServers(installedServers);
130
+
131
+ console.log();
132
+ console.log(chalk.green('βœ… Server installed successfully!'));
133
+ console.log(chalk.gray(`Configuration saved to: ${SERVERS_FILE}`));
134
+ console.log();
135
+ console.log(chalk.blue('πŸ’‘ Next steps:'));
136
+ console.log(chalk.gray(' - Enable the server:'), chalk.cyan(`mcpm enable ${serverName}`));
137
+ console.log(chalk.gray(' - Export configuration:'), chalk.cyan('mcpm export'));
138
+ console.log(chalk.gray(' - View all servers:'), chalk.cyan('mcpm list'));
139
+ }
@@ -0,0 +1,75 @@
1
+ import chalk from 'chalk';
2
+ import fs from 'fs-extra';
3
+ import path from 'path';
4
+ import os from 'os';
5
+
6
+ interface ServerConfig {
7
+ name: string;
8
+ command: string;
9
+ args: string[];
10
+ env?: Record<string, string>;
11
+ enabled: boolean;
12
+ installedAt: string;
13
+ }
14
+
15
+ const CONFIG_DIR = path.join(os.homedir(), '.mcpm');
16
+ const SERVERS_FILE = path.join(CONFIG_DIR, 'servers.json');
17
+
18
+ async function getInstalledServers(): Promise<Record<string, ServerConfig>> {
19
+ if (await fs.pathExists(SERVERS_FILE)) {
20
+ return await fs.readJson(SERVERS_FILE);
21
+ }
22
+ return {};
23
+ }
24
+
25
+ export async function listServers(options: { all?: boolean }): Promise<void> {
26
+ console.log(chalk.blue('πŸ“‹ Installed MCP servers\n'));
27
+
28
+ const servers = await getInstalledServers();
29
+ const serverList = Object.values(servers);
30
+
31
+ if (serverList.length === 0) {
32
+ console.log(chalk.yellow('No servers installed yet.'));
33
+ console.log(chalk.gray('Search and install servers with:'), chalk.cyan('mcpm search <query>'));
34
+ return;
35
+ }
36
+
37
+ // Filter enabled/disabled
38
+ const filteredServers = options.all
39
+ ? serverList
40
+ : serverList.filter(s => s.enabled);
41
+
42
+ if (filteredServers.length === 0 && !options.all) {
43
+ console.log(chalk.yellow('No enabled servers.'));
44
+ console.log(chalk.gray('Use --all to see disabled servers.'));
45
+ return;
46
+ }
47
+
48
+ // Group by status
49
+ const enabledServers = filteredServers.filter(s => s.enabled);
50
+ const disabledServers = filteredServers.filter(s => !s.enabled);
51
+
52
+ if (enabledServers.length > 0) {
53
+ console.log(chalk.green(`βœ… Enabled (${enabledServers.length}):\n`));
54
+ enabledServers.forEach((server, index) => {
55
+ console.log(chalk.white(` ${index + 1}. ${chalk.bold(server.name)}`));
56
+ console.log(chalk.gray(` Command: ${server.command} ${server.args.join(' ')}`));
57
+ console.log(chalk.gray(` Installed: ${new Date(server.installedAt).toLocaleDateString()}`));
58
+ console.log();
59
+ });
60
+ }
61
+
62
+ if (disabledServers.length > 0) {
63
+ console.log(chalk.gray(`⏸️ Disabled (${disabledServers.length}):\n`));
64
+ disabledServers.forEach((server, index) => {
65
+ console.log(chalk.gray(` ${index + 1}. ${server.name}`));
66
+ console.log();
67
+ });
68
+ }
69
+
70
+ console.log(chalk.blue('πŸ’‘ Commands:'));
71
+ console.log(chalk.gray(' Enable:'), chalk.cyan('mcpm enable <server>'));
72
+ console.log(chalk.gray(' Disable:'), chalk.cyan('mcpm disable <server>'));
73
+ console.log(chalk.gray(' Uninstall:'), chalk.cyan('mcpm uninstall <server>'));
74
+ console.log(chalk.gray(' Export:'), chalk.cyan('mcpm export'));
75
+ }