mcphosting-cli 0.1.0 → 0.1.2
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/dist/index.cjs +5 -5
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +5 -5
- package/dist/index.js.map +1 -1
- package/package.json +11 -6
- package/CONTRIBUTING.md +0 -156
- package/src/commands/auth.ts +0 -186
- package/src/commands/connect.ts +0 -172
- package/src/commands/import.ts +0 -213
- package/src/commands/proxy.ts +0 -144
- package/src/commands/search.ts +0 -135
- package/src/commands/servers.ts +0 -131
- package/src/index.ts +0 -130
- package/src/lib/api.ts +0 -137
- package/src/lib/clients.ts +0 -188
- package/src/lib/config.ts +0 -90
- package/src/lib/logger.ts +0 -63
- package/src/types.ts +0 -47
- package/tsconfig.json +0 -26
- package/tsup.config.ts +0 -14
package/src/commands/auth.ts
DELETED
|
@@ -1,186 +0,0 @@
|
|
|
1
|
-
import { Command } from 'commander';
|
|
2
|
-
import { createServer } from 'http';
|
|
3
|
-
import open from 'open';
|
|
4
|
-
import { Config } from '../lib/config.js';
|
|
5
|
-
import { MCPHostingAPI } from '../lib/api.js';
|
|
6
|
-
import { Logger } from '../lib/logger.js';
|
|
7
|
-
|
|
8
|
-
export function createAuthCommands(): Command {
|
|
9
|
-
const auth = new Command('auth');
|
|
10
|
-
|
|
11
|
-
auth
|
|
12
|
-
.command('login')
|
|
13
|
-
.description('Authenticate with MCPHosting')
|
|
14
|
-
.option('--token <token>', 'Provide API token directly')
|
|
15
|
-
.action(async (options) => {
|
|
16
|
-
const config = new Config();
|
|
17
|
-
|
|
18
|
-
if (options.token) {
|
|
19
|
-
// Manual token auth
|
|
20
|
-
config.token = options.token;
|
|
21
|
-
|
|
22
|
-
const api = new MCPHostingAPI(options.token);
|
|
23
|
-
const user = await api.whoami();
|
|
24
|
-
|
|
25
|
-
if (user) {
|
|
26
|
-
config.user = user;
|
|
27
|
-
Logger.success(`Logged in as ${user.email}${user.org ? ` (${user.org})` : ''}`);
|
|
28
|
-
} else {
|
|
29
|
-
Logger.warning('Token saved, but could not verify user info');
|
|
30
|
-
}
|
|
31
|
-
return;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
// Browser OAuth flow
|
|
35
|
-
const spinner = Logger.spinner('Starting login flow...');
|
|
36
|
-
|
|
37
|
-
let server: any;
|
|
38
|
-
let resolved = false;
|
|
39
|
-
|
|
40
|
-
try {
|
|
41
|
-
const authPromise = new Promise<string>((resolve, reject) => {
|
|
42
|
-
server = createServer((req, res) => {
|
|
43
|
-
if (req.url?.startsWith('/callback')) {
|
|
44
|
-
const url = new URL(req.url, 'http://localhost');
|
|
45
|
-
const token = url.searchParams.get('token');
|
|
46
|
-
|
|
47
|
-
if (token) {
|
|
48
|
-
res.writeHead(200, { 'Content-Type': 'text/html' });
|
|
49
|
-
res.end(`
|
|
50
|
-
<html>
|
|
51
|
-
<body style="font-family: system-ui; text-align: center; padding: 50px;">
|
|
52
|
-
<h2>✅ Login Successful!</h2>
|
|
53
|
-
<p>You can now close this window and return to your terminal.</p>
|
|
54
|
-
</body>
|
|
55
|
-
</html>
|
|
56
|
-
`);
|
|
57
|
-
resolve(token);
|
|
58
|
-
} else {
|
|
59
|
-
res.writeHead(400, { 'Content-Type': 'text/html' });
|
|
60
|
-
res.end(`
|
|
61
|
-
<html>
|
|
62
|
-
<body style="font-family: system-ui; text-align: center; padding: 50px;">
|
|
63
|
-
<h2>❌ Login Failed</h2>
|
|
64
|
-
<p>No token received. Please try again.</p>
|
|
65
|
-
</body>
|
|
66
|
-
</html>
|
|
67
|
-
`);
|
|
68
|
-
reject(new Error('No token received'));
|
|
69
|
-
}
|
|
70
|
-
} else {
|
|
71
|
-
res.writeHead(404);
|
|
72
|
-
res.end('Not found');
|
|
73
|
-
}
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
server.listen(0, () => {
|
|
77
|
-
const port = (server.address() as any).port;
|
|
78
|
-
const callbackUrl = `http://localhost:${port}/callback`;
|
|
79
|
-
const authUrl = `https://mcphosting.com/cli/auth?callback=${encodeURIComponent(callbackUrl)}`;
|
|
80
|
-
|
|
81
|
-
setTimeout(() => {
|
|
82
|
-
if (!resolved) {
|
|
83
|
-
reject(new Error('Login timeout - please try again'));
|
|
84
|
-
}
|
|
85
|
-
}, 120000); // 2 minute timeout
|
|
86
|
-
|
|
87
|
-
open(authUrl).catch(() => {
|
|
88
|
-
Logger.warning(`Could not open browser automatically. Please visit: ${authUrl}`);
|
|
89
|
-
});
|
|
90
|
-
});
|
|
91
|
-
});
|
|
92
|
-
|
|
93
|
-
const token = await authPromise;
|
|
94
|
-
resolved = true;
|
|
95
|
-
|
|
96
|
-
spinner.succeed('Login successful!');
|
|
97
|
-
|
|
98
|
-
config.token = token;
|
|
99
|
-
|
|
100
|
-
const api = new MCPHostingAPI(token);
|
|
101
|
-
const user = await api.whoami();
|
|
102
|
-
|
|
103
|
-
if (user) {
|
|
104
|
-
config.user = user;
|
|
105
|
-
Logger.success(`Logged in as ${user.email}${user.org ? ` (${user.org})` : ''}`);
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
} catch (error) {
|
|
109
|
-
spinner.fail('Login failed');
|
|
110
|
-
Logger.error(`Login error: ${error}`);
|
|
111
|
-
process.exit(1);
|
|
112
|
-
} finally {
|
|
113
|
-
if (server) {
|
|
114
|
-
server.close();
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
});
|
|
118
|
-
|
|
119
|
-
auth
|
|
120
|
-
.command('logout')
|
|
121
|
-
.description('Remove stored authentication')
|
|
122
|
-
.action(() => {
|
|
123
|
-
const config = new Config();
|
|
124
|
-
config.token = undefined;
|
|
125
|
-
config.user = undefined;
|
|
126
|
-
Logger.success('Logged out successfully');
|
|
127
|
-
});
|
|
128
|
-
|
|
129
|
-
auth
|
|
130
|
-
.command('whoami')
|
|
131
|
-
.description('Show current user information')
|
|
132
|
-
.action(async () => {
|
|
133
|
-
const config = new Config();
|
|
134
|
-
const token = config.token;
|
|
135
|
-
|
|
136
|
-
if (!token) {
|
|
137
|
-
Logger.warning('Not logged in. Use `mcphost auth login` to authenticate.');
|
|
138
|
-
return;
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
const user = config.user;
|
|
142
|
-
if (user) {
|
|
143
|
-
Logger.info(`Logged in as: ${user.email}${user.org ? ` (${user.org})` : ''}`);
|
|
144
|
-
} else {
|
|
145
|
-
Logger.warning('User info not available. Try logging in again.');
|
|
146
|
-
}
|
|
147
|
-
});
|
|
148
|
-
|
|
149
|
-
return auth;
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
// Legacy commands (backwards compatibility)
|
|
153
|
-
export function createLegacyAuthCommands(): Command[] {
|
|
154
|
-
const config = new Config();
|
|
155
|
-
|
|
156
|
-
const login = new Command('login')
|
|
157
|
-
.description('Authenticate with MCPHosting')
|
|
158
|
-
.option('--token <token>', 'Provide API token directly')
|
|
159
|
-
.action(async (options) => {
|
|
160
|
-
const authCmd = createAuthCommands();
|
|
161
|
-
const loginCmd = authCmd.commands.find(cmd => cmd.name() === 'login');
|
|
162
|
-
if (loginCmd) {
|
|
163
|
-
await loginCmd.action(options);
|
|
164
|
-
}
|
|
165
|
-
});
|
|
166
|
-
|
|
167
|
-
const logout = new Command('logout')
|
|
168
|
-
.description('Remove stored authentication')
|
|
169
|
-
.action(() => {
|
|
170
|
-
config.token = undefined;
|
|
171
|
-
config.user = undefined;
|
|
172
|
-
Logger.success('Logged out successfully');
|
|
173
|
-
});
|
|
174
|
-
|
|
175
|
-
const whoami = new Command('whoami')
|
|
176
|
-
.description('Show current user information')
|
|
177
|
-
.action(async () => {
|
|
178
|
-
const authCmd = createAuthCommands();
|
|
179
|
-
const whoamiCmd = authCmd.commands.find(cmd => cmd.name() === 'whoami');
|
|
180
|
-
if (whoamiCmd) {
|
|
181
|
-
await whoamiCmd.action();
|
|
182
|
-
}
|
|
183
|
-
});
|
|
184
|
-
|
|
185
|
-
return [login, logout, whoami];
|
|
186
|
-
}
|
package/src/commands/connect.ts
DELETED
|
@@ -1,172 +0,0 @@
|
|
|
1
|
-
import { Command } from 'commander';
|
|
2
|
-
import { Config } from '../lib/config.js';
|
|
3
|
-
import { ClientManager } from '../lib/clients.js';
|
|
4
|
-
import { Logger } from '../lib/logger.js';
|
|
5
|
-
import { SupportedClient } from '../types.js';
|
|
6
|
-
import chalk from 'chalk';
|
|
7
|
-
|
|
8
|
-
export function createConnectCommands(): Command {
|
|
9
|
-
const connect = new Command('connect')
|
|
10
|
-
.description('Connect an MCP server to AI clients')
|
|
11
|
-
.argument('<url-or-slug>', 'MCP server URL or slug')
|
|
12
|
-
.option('--client <client>', 'Target specific client (claude, cursor, vscode, openclaw, chatgpt)')
|
|
13
|
-
.option('--name <name>', 'Custom name for the connection')
|
|
14
|
-
.action(async (urlOrSlug: string, options) => {
|
|
15
|
-
const config = new Config();
|
|
16
|
-
const spinner = Logger.spinner('Setting up MCP connection...');
|
|
17
|
-
|
|
18
|
-
try {
|
|
19
|
-
const url = ClientManager.resolveUrl(urlOrSlug);
|
|
20
|
-
const slug = ClientManager.extractSlug(url);
|
|
21
|
-
const name = options.name || slug;
|
|
22
|
-
|
|
23
|
-
// Check if already connected
|
|
24
|
-
const existing = config.findConnection(slug);
|
|
25
|
-
if (existing) {
|
|
26
|
-
spinner.warn(`Already connected to ${slug}`);
|
|
27
|
-
Logger.info(`Use \`mcphost disconnect ${slug}\` to remove first`);
|
|
28
|
-
return;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
const clients: string[] = [];
|
|
32
|
-
|
|
33
|
-
if (options.client) {
|
|
34
|
-
// Connect to specific client
|
|
35
|
-
const clientName = options.client as SupportedClient;
|
|
36
|
-
const success = await ClientManager.addToClient(clientName, slug, url);
|
|
37
|
-
|
|
38
|
-
if (success) {
|
|
39
|
-
clients.push(clientName);
|
|
40
|
-
spinner.succeed(`Connected to ${clientName}`);
|
|
41
|
-
} else {
|
|
42
|
-
spinner.fail(`Failed to connect to ${clientName}`);
|
|
43
|
-
process.exit(1);
|
|
44
|
-
}
|
|
45
|
-
} else {
|
|
46
|
-
// Auto-detect and connect to all available clients
|
|
47
|
-
const detectedClients = await ClientManager.detectInstalledClients();
|
|
48
|
-
const availableClients = detectedClients.filter(c => c.exists);
|
|
49
|
-
|
|
50
|
-
if (availableClients.length === 0) {
|
|
51
|
-
spinner.warn('No supported clients found');
|
|
52
|
-
Logger.info('Supported clients: Claude Desktop, Cursor, VS Code, OpenClaw');
|
|
53
|
-
Logger.info('Install a client and try again, or use --client chatgpt for web setup');
|
|
54
|
-
return;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
spinner.text = `Configuring ${availableClients.length} client${availableClients.length > 1 ? 's' : ''}...`;
|
|
58
|
-
|
|
59
|
-
for (const client of availableClients) {
|
|
60
|
-
try {
|
|
61
|
-
const success = await ClientManager.addToClient(
|
|
62
|
-
client.name as SupportedClient,
|
|
63
|
-
slug,
|
|
64
|
-
url
|
|
65
|
-
);
|
|
66
|
-
|
|
67
|
-
if (success) {
|
|
68
|
-
clients.push(client.name);
|
|
69
|
-
}
|
|
70
|
-
} catch (error) {
|
|
71
|
-
Logger.warning(`Failed to configure ${client.name}: ${error}`);
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
spinner.succeed(`Connected to ${clients.length} client${clients.length > 1 ? 's' : ''}`);
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
// Save connection to config
|
|
79
|
-
config.addConnection({
|
|
80
|
-
slug,
|
|
81
|
-
url,
|
|
82
|
-
clients
|
|
83
|
-
});
|
|
84
|
-
|
|
85
|
-
Logger.success(`🔗 ${name} connected successfully!`);
|
|
86
|
-
Logger.dim(` URL: ${url}`);
|
|
87
|
-
Logger.dim(` Clients: ${clients.join(', ')}`);
|
|
88
|
-
|
|
89
|
-
// Growth hacking: show sharing message after successful connection
|
|
90
|
-
console.log('\n' + chalk.green('🎉 Connected! Share with your team:'));
|
|
91
|
-
console.log(chalk.cyan(`npx @mcphosting/cli connect ${urlOrSlug}`));
|
|
92
|
-
console.log('\n' + chalk.yellow('⭐ Star us: ') + chalk.blue('https://github.com/gorlomi-enzo/mcphosting-cli'));
|
|
93
|
-
console.log('');
|
|
94
|
-
|
|
95
|
-
} catch (error) {
|
|
96
|
-
spinner.fail('Connection failed');
|
|
97
|
-
Logger.error(`Error: ${error}`);
|
|
98
|
-
process.exit(1);
|
|
99
|
-
}
|
|
100
|
-
});
|
|
101
|
-
|
|
102
|
-
return connect;
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
export function createDisconnectCommand(): Command {
|
|
106
|
-
return new Command('disconnect')
|
|
107
|
-
.description('Disconnect an MCP server')
|
|
108
|
-
.argument('<slug-or-id>', 'MCP server slug or connection ID')
|
|
109
|
-
.action(async (slugOrId: string) => {
|
|
110
|
-
const config = new Config();
|
|
111
|
-
const connection = config.findConnection(slugOrId);
|
|
112
|
-
|
|
113
|
-
if (!connection) {
|
|
114
|
-
Logger.error(`Connection not found: ${slugOrId}`);
|
|
115
|
-
Logger.info('Use `mcphost list` to see active connections');
|
|
116
|
-
return;
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
const spinner = Logger.spinner(`Disconnecting ${connection.slug}...`);
|
|
120
|
-
|
|
121
|
-
try {
|
|
122
|
-
const removedFrom = await ClientManager.removeFromAllClients(connection.slug);
|
|
123
|
-
config.removeConnection(connection.id);
|
|
124
|
-
|
|
125
|
-
spinner.succeed(`Disconnected ${connection.slug}`);
|
|
126
|
-
|
|
127
|
-
if (removedFrom.length > 0) {
|
|
128
|
-
Logger.info(`Removed from: ${removedFrom.join(', ')}`);
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
} catch (error) {
|
|
132
|
-
spinner.fail('Disconnect failed');
|
|
133
|
-
Logger.error(`Error: ${error}`);
|
|
134
|
-
}
|
|
135
|
-
});
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
export function createListCommand(): Command {
|
|
139
|
-
return new Command('list')
|
|
140
|
-
.description('List connected MCP servers')
|
|
141
|
-
.option('--json', 'Output as JSON')
|
|
142
|
-
.action(async (options) => {
|
|
143
|
-
const config = new Config();
|
|
144
|
-
const connections = config.connections;
|
|
145
|
-
|
|
146
|
-
if (connections.length === 0) {
|
|
147
|
-
Logger.info('No MCP connections found');
|
|
148
|
-
Logger.dim('Use `mcphost connect <url>` to add one');
|
|
149
|
-
return;
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
if (options.json) {
|
|
153
|
-
Logger.json(connections);
|
|
154
|
-
return;
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
Logger.bold(`📋 Connected MCP Servers (${connections.length})`);
|
|
158
|
-
console.log('');
|
|
159
|
-
|
|
160
|
-
const tableData = connections.map(conn => ({
|
|
161
|
-
Slug: conn.slug,
|
|
162
|
-
URL: conn.url.length > 50 ? conn.url.slice(0, 47) + '...' : conn.url,
|
|
163
|
-
Clients: conn.clients.join(', '),
|
|
164
|
-
Added: new Date(conn.addedAt).toLocaleDateString()
|
|
165
|
-
}));
|
|
166
|
-
|
|
167
|
-
Logger.table(tableData);
|
|
168
|
-
|
|
169
|
-
console.log('');
|
|
170
|
-
Logger.dim('Use `mcphost disconnect <slug>` to remove a connection');
|
|
171
|
-
});
|
|
172
|
-
}
|
package/src/commands/import.ts
DELETED
|
@@ -1,213 +0,0 @@
|
|
|
1
|
-
import { Command } from 'commander';
|
|
2
|
-
import { readFile, access } from 'fs/promises';
|
|
3
|
-
import { join } from 'path';
|
|
4
|
-
import { homedir } from 'os';
|
|
5
|
-
import { Config } from '../lib/config.js';
|
|
6
|
-
import { ClientManager } from '../lib/clients.js';
|
|
7
|
-
import { Logger } from '../lib/logger.js';
|
|
8
|
-
import chalk from 'chalk';
|
|
9
|
-
|
|
10
|
-
interface SmitheryConfig {
|
|
11
|
-
servers?: Record<string, {
|
|
12
|
-
url?: string;
|
|
13
|
-
command?: string;
|
|
14
|
-
args?: string[];
|
|
15
|
-
env?: Record<string, string>;
|
|
16
|
-
}>;
|
|
17
|
-
mcpServers?: Record<string, {
|
|
18
|
-
url?: string;
|
|
19
|
-
command?: string;
|
|
20
|
-
args?: string[];
|
|
21
|
-
env?: Record<string, string>;
|
|
22
|
-
}>;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
export function createImportCommand(): Command {
|
|
26
|
-
return new Command('import')
|
|
27
|
-
.description('Import MCP connections from other tools')
|
|
28
|
-
.option('--from <tool>', 'Import from: smithery', 'smithery')
|
|
29
|
-
.option('--dry-run', 'Show what would be imported without actually importing')
|
|
30
|
-
.option('--config-path <path>', 'Custom config file path')
|
|
31
|
-
.action(async (options) => {
|
|
32
|
-
if (options.from !== 'smithery') {
|
|
33
|
-
Logger.error('Only Smithery import is currently supported');
|
|
34
|
-
Logger.info('Usage: mcphost import --from smithery');
|
|
35
|
-
return;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
await importFromSmitery(options);
|
|
39
|
-
});
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
async function importFromSmitery(options: { dryRun?: boolean; configPath?: string }) {
|
|
43
|
-
const config = new Config();
|
|
44
|
-
const spinner = Logger.spinner('Looking for Smithery config...');
|
|
45
|
-
|
|
46
|
-
try {
|
|
47
|
-
// Try to find Smithery config
|
|
48
|
-
const smitheryPaths = [
|
|
49
|
-
options.configPath,
|
|
50
|
-
join(homedir(), '.smithery', 'config.json'),
|
|
51
|
-
join(homedir(), '.config', 'smithery', 'config.json'),
|
|
52
|
-
join(homedir(), 'Library', 'Application Support', 'smithery', 'config.json'),
|
|
53
|
-
join(process.cwd(), 'smithery.json'),
|
|
54
|
-
join(process.cwd(), '.smithery.json')
|
|
55
|
-
].filter(Boolean) as string[];
|
|
56
|
-
|
|
57
|
-
let smitheryConfigPath: string | null = null;
|
|
58
|
-
for (const path of smitheryPaths) {
|
|
59
|
-
try {
|
|
60
|
-
await access(path);
|
|
61
|
-
smitheryConfigPath = path;
|
|
62
|
-
break;
|
|
63
|
-
} catch {
|
|
64
|
-
// Continue to next path
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
if (!smitheryConfigPath) {
|
|
69
|
-
spinner.fail('Smithery config not found');
|
|
70
|
-
Logger.warning('Searched locations:');
|
|
71
|
-
smitheryPaths.forEach(path => Logger.dim(` ${path}`));
|
|
72
|
-
Logger.info('\nIf Smithery is installed, you can specify the config path:');
|
|
73
|
-
Logger.info(' mcphost import --from smithery --config-path /path/to/smithery/config.json');
|
|
74
|
-
return;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
spinner.succeed(`Found Smithery config: ${smitheryConfigPath}`);
|
|
78
|
-
|
|
79
|
-
// Read and parse Smithery config
|
|
80
|
-
const loadSpinner = Logger.spinner('Reading Smithery config...');
|
|
81
|
-
const configContent = await readFile(smitheryConfigPath, 'utf-8');
|
|
82
|
-
const smitheryConfig: SmitheryConfig = JSON.parse(configContent);
|
|
83
|
-
|
|
84
|
-
// Extract MCP servers from both possible locations
|
|
85
|
-
const servers = {
|
|
86
|
-
...smitheryConfig.servers,
|
|
87
|
-
...smitheryConfig.mcpServers
|
|
88
|
-
};
|
|
89
|
-
|
|
90
|
-
if (!servers || Object.keys(servers).length === 0) {
|
|
91
|
-
loadSpinner.warn('No MCP servers found in Smithery config');
|
|
92
|
-
return;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
const serverEntries = Object.entries(servers);
|
|
96
|
-
loadSpinner.succeed(`Found ${serverEntries.length} MCP server(s) in Smithery config`);
|
|
97
|
-
|
|
98
|
-
if (options.dryRun) {
|
|
99
|
-
console.log('\n' + chalk.bold('🔍 Preview: Would import the following MCP servers:'));
|
|
100
|
-
console.log('');
|
|
101
|
-
|
|
102
|
-
serverEntries.forEach(([slug, serverConfig]) => {
|
|
103
|
-
const url = serverConfig.url || `https://${slug}.mcphost.dev`;
|
|
104
|
-
console.log(`• ${chalk.cyan(slug)}`);
|
|
105
|
-
console.log(` URL: ${chalk.dim(url)}`);
|
|
106
|
-
|
|
107
|
-
if (serverConfig.command) {
|
|
108
|
-
console.log(` Command: ${chalk.dim(serverConfig.command + ' ' + (serverConfig.args?.join(' ') || ''))}`);
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
console.log('');
|
|
112
|
-
});
|
|
113
|
-
|
|
114
|
-
Logger.info('Run without --dry-run to perform the import');
|
|
115
|
-
return;
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
// Actually import the servers
|
|
119
|
-
const importSpinner = Logger.spinner('Importing MCP servers...');
|
|
120
|
-
let imported = 0;
|
|
121
|
-
let skipped = 0;
|
|
122
|
-
|
|
123
|
-
for (const [slug, serverConfig] of serverEntries) {
|
|
124
|
-
try {
|
|
125
|
-
// Check if already exists
|
|
126
|
-
const existing = config.findConnection(slug);
|
|
127
|
-
if (existing) {
|
|
128
|
-
Logger.warning(`Skipping ${slug} - already connected`);
|
|
129
|
-
skipped++;
|
|
130
|
-
continue;
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
// Determine URL
|
|
134
|
-
let url = serverConfig.url;
|
|
135
|
-
if (!url) {
|
|
136
|
-
// If no URL but has command, try to extract from args
|
|
137
|
-
if (serverConfig.command === 'npx' && serverConfig.args?.[0] === '@mcphosting/cli') {
|
|
138
|
-
url = serverConfig.args[2]; // Should be the URL after 'proxy'
|
|
139
|
-
} else {
|
|
140
|
-
// Default to mcphost.dev format
|
|
141
|
-
url = `https://${slug}.mcphost.dev`;
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
if (!url) {
|
|
146
|
-
Logger.warning(`Skipping ${slug} - no URL found`);
|
|
147
|
-
skipped++;
|
|
148
|
-
continue;
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
// Connect to available clients
|
|
152
|
-
const detectedClients = await ClientManager.detectInstalledClients();
|
|
153
|
-
const availableClients = detectedClients.filter(c => c.exists);
|
|
154
|
-
const connectedClients: string[] = [];
|
|
155
|
-
|
|
156
|
-
for (const client of availableClients) {
|
|
157
|
-
try {
|
|
158
|
-
const success = await ClientManager.addToClient(
|
|
159
|
-
client.name as any,
|
|
160
|
-
slug,
|
|
161
|
-
url
|
|
162
|
-
);
|
|
163
|
-
|
|
164
|
-
if (success) {
|
|
165
|
-
connectedClients.push(client.name);
|
|
166
|
-
}
|
|
167
|
-
} catch (error) {
|
|
168
|
-
// Continue with other clients
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
// Save connection
|
|
173
|
-
config.addConnection({
|
|
174
|
-
slug,
|
|
175
|
-
url,
|
|
176
|
-
clients: connectedClients
|
|
177
|
-
});
|
|
178
|
-
|
|
179
|
-
imported++;
|
|
180
|
-
|
|
181
|
-
} catch (error) {
|
|
182
|
-
Logger.warning(`Failed to import ${slug}: ${error}`);
|
|
183
|
-
skipped++;
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
importSpinner.succeed('Import completed');
|
|
188
|
-
|
|
189
|
-
Logger.success(`✅ Imported ${imported} MCP server(s)`);
|
|
190
|
-
if (skipped > 0) {
|
|
191
|
-
Logger.warning(`⚠️ Skipped ${skipped} server(s)`);
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
console.log('');
|
|
195
|
-
Logger.info('Use `mcphost list` to see your imported connections');
|
|
196
|
-
|
|
197
|
-
// Growth hacking message
|
|
198
|
-
console.log('\n' + chalk.green('🎉 Welcome to MCPHosting! Share with your team:'));
|
|
199
|
-
console.log(chalk.cyan('npx @mcphosting/cli import --from smithery'));
|
|
200
|
-
console.log('\n' + chalk.yellow('⭐ Star us: ') + chalk.blue('https://github.com/gorlomi-enzo/mcphosting-cli'));
|
|
201
|
-
console.log('');
|
|
202
|
-
|
|
203
|
-
} catch (error) {
|
|
204
|
-
spinner.fail('Import failed');
|
|
205
|
-
Logger.error(`Error: ${error}`);
|
|
206
|
-
|
|
207
|
-
if (error instanceof SyntaxError) {
|
|
208
|
-
Logger.warning('Invalid JSON in Smithery config file');
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
process.exit(1);
|
|
212
|
-
}
|
|
213
|
-
}
|