mcphosting-cli 0.1.2 → 0.4.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.
package/dist/index.cjs DELETED
@@ -1,1182 +0,0 @@
1
- #!/usr/bin/env node
2
- "use strict";
3
- var __create = Object.create;
4
- var __defProp = Object.defineProperty;
5
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
- var __getOwnPropNames = Object.getOwnPropertyNames;
7
- var __getProtoOf = Object.getPrototypeOf;
8
- var __hasOwnProp = Object.prototype.hasOwnProperty;
9
- var __copyProps = (to, from, except, desc) => {
10
- if (from && typeof from === "object" || typeof from === "function") {
11
- for (let key of __getOwnPropNames(from))
12
- if (!__hasOwnProp.call(to, key) && key !== except)
13
- __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
14
- }
15
- return to;
16
- };
17
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
18
- // If the importer is in node compatibility mode or this is not an ESM
19
- // file that has been converted to a CommonJS file using a Babel-
20
- // compatible transform (i.e. "__esModule" has not been set), then set
21
- // "default" to the CommonJS "module.exports" for node compatibility.
22
- isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
23
- mod
24
- ));
25
-
26
- // src/index.ts
27
- var import_commander7 = require("commander");
28
-
29
- // src/commands/auth.ts
30
- var import_commander = require("commander");
31
- var import_http = require("http");
32
- var import_open = __toESM(require("open"), 1);
33
-
34
- // src/lib/config.ts
35
- var import_conf = __toESM(require("conf"), 1);
36
- var Config = class {
37
- conf;
38
- connectionsConf;
39
- constructor() {
40
- this.conf = new import_conf.default({
41
- projectName: "mcphosting",
42
- defaults: {}
43
- });
44
- this.connectionsConf = new import_conf.default({
45
- projectName: "mcphosting",
46
- configName: "connections",
47
- defaults: { connections: [] }
48
- });
49
- }
50
- get token() {
51
- return this.conf.get("token");
52
- }
53
- set token(value) {
54
- if (value) {
55
- this.conf.set("token", value);
56
- } else {
57
- this.conf.delete("token");
58
- }
59
- }
60
- get user() {
61
- return this.conf.get("user");
62
- }
63
- set user(value) {
64
- if (value) {
65
- this.conf.set("user", value);
66
- } else {
67
- this.conf.delete("user");
68
- }
69
- }
70
- get connections() {
71
- return this.connectionsConf.get("connections", []);
72
- }
73
- addConnection(connection) {
74
- const connections = this.connections;
75
- const newConnection = {
76
- ...connection,
77
- id: Math.random().toString(36).substr(2, 9),
78
- addedAt: Date.now()
79
- };
80
- connections.push(newConnection);
81
- this.connectionsConf.set("connections", connections);
82
- return newConnection;
83
- }
84
- removeConnection(id) {
85
- const connections = this.connections;
86
- const index = connections.findIndex((c) => c.id === id || c.slug === id);
87
- if (index === -1) return false;
88
- connections.splice(index, 1);
89
- this.connectionsConf.set("connections", connections);
90
- return true;
91
- }
92
- findConnection(slugOrId) {
93
- return this.connections.find((c) => c.id === slugOrId || c.slug === slugOrId);
94
- }
95
- updateConnection(id, updates) {
96
- const connections = this.connections;
97
- const index = connections.findIndex((c) => c.id === id);
98
- if (index === -1) return false;
99
- connections[index] = { ...connections[index], ...updates };
100
- this.connectionsConf.set("connections", connections);
101
- return true;
102
- }
103
- clear() {
104
- this.conf.clear();
105
- this.connectionsConf.clear();
106
- }
107
- };
108
-
109
- // src/lib/api.ts
110
- var MCPHostingAPI = class {
111
- baseUrl = "https://mcphosting.com/api";
112
- token;
113
- constructor(token) {
114
- this.token = token;
115
- }
116
- async request(endpoint, options = {}) {
117
- throw new Error(`API not available - using static fallback`);
118
- }
119
- async searchMCPs(query) {
120
- try {
121
- const results = await this.request(`/marketplace/search?q=${encodeURIComponent(query)}`);
122
- return results.mcps || [];
123
- } catch (error) {
124
- const staticMCPs = this.getStaticMCPs();
125
- return staticMCPs.filter(
126
- (mcp) => mcp.name.toLowerCase().includes(query.toLowerCase()) || mcp.description.toLowerCase().includes(query.toLowerCase()) || mcp.tools.some((tool) => tool.toLowerCase().includes(query.toLowerCase()))
127
- );
128
- }
129
- }
130
- async getMCPInfo(slug) {
131
- try {
132
- const result = await this.request(`/marketplace/mcp/${slug}`);
133
- return result.mcp || null;
134
- } catch {
135
- const staticMCPs = this.getStaticMCPs();
136
- return staticMCPs.find((mcp) => mcp.slug === slug) || null;
137
- }
138
- }
139
- async whoami() {
140
- if (!this.token) return null;
141
- try {
142
- const result = await this.request("/auth/whoami");
143
- return result.user;
144
- } catch {
145
- return null;
146
- }
147
- }
148
- getStaticMCPs() {
149
- return [
150
- {
151
- slug: "github",
152
- name: "GitHub MCP",
153
- description: "Access GitHub repositories, issues, and pull requests",
154
- url: "https://github.mcphost.dev",
155
- tools: ["read_file", "list_repos", "create_issue", "list_issues"],
156
- installs: 15420,
157
- author: "MCPHosting"
158
- },
159
- {
160
- slug: "slack",
161
- name: "Slack MCP",
162
- description: "Send messages and manage Slack workspaces",
163
- url: "https://slack.mcphost.dev",
164
- tools: ["send_message", "list_channels", "get_history"],
165
- installs: 8930,
166
- author: "MCPHosting"
167
- },
168
- {
169
- slug: "notion",
170
- name: "Notion MCP",
171
- description: "Read and write Notion pages and databases",
172
- url: "https://notion.mcphost.dev",
173
- tools: ["read_page", "create_page", "query_database"],
174
- installs: 12450,
175
- author: "MCPHosting"
176
- },
177
- {
178
- slug: "stripe",
179
- name: "Stripe MCP",
180
- description: "Access Stripe payment and customer data",
181
- url: "https://stripe.mcphost.dev",
182
- tools: ["get_customer", "list_payments", "create_invoice"],
183
- installs: 6720,
184
- author: "MCPHosting"
185
- },
186
- {
187
- slug: "postgres",
188
- name: "PostgreSQL MCP",
189
- description: "Query PostgreSQL databases safely",
190
- url: "https://postgres.mcphost.dev",
191
- tools: ["query", "describe_table", "list_tables"],
192
- installs: 9180,
193
- author: "MCPHosting"
194
- },
195
- {
196
- slug: "filesystem",
197
- name: "Filesystem MCP",
198
- description: "Read and write files securely",
199
- url: "https://filesystem.mcphost.dev",
200
- tools: ["read_file", "write_file", "list_directory"],
201
- installs: 18950,
202
- author: "MCPHosting"
203
- }
204
- ];
205
- }
206
- };
207
-
208
- // src/lib/logger.ts
209
- var import_chalk = __toESM(require("chalk"), 1);
210
- var import_ora = __toESM(require("ora"), 1);
211
- var Logger = class _Logger {
212
- static info(message) {
213
- console.log(import_chalk.default.blue("\u2139"), message);
214
- }
215
- static success(message) {
216
- console.log(import_chalk.default.green("\u2713"), message);
217
- }
218
- static warning(message) {
219
- console.log(import_chalk.default.yellow("\u26A0"), message);
220
- }
221
- static error(message) {
222
- console.error(import_chalk.default.red("\u2717"), message);
223
- }
224
- static dim(message) {
225
- console.log(import_chalk.default.dim(message));
226
- }
227
- static bold(message) {
228
- console.log(import_chalk.default.bold(message));
229
- }
230
- static spinner(text) {
231
- return (0, import_ora.default)(text).start();
232
- }
233
- static table(data, headers) {
234
- if (data.length === 0) {
235
- _Logger.dim("No data to display");
236
- return;
237
- }
238
- const keys = headers || Object.keys(data[0]);
239
- const maxWidths = keys.map(
240
- (key) => Math.max(key.length, ...data.map((row) => String(row[key] || "").length))
241
- );
242
- const headerRow = keys.map(
243
- (key, i) => import_chalk.default.bold(key.padEnd(maxWidths[i]))
244
- ).join(" | ");
245
- console.log(headerRow);
246
- console.log(keys.map((_, i) => "-".repeat(maxWidths[i])).join("-|-"));
247
- data.forEach((row) => {
248
- const dataRow = keys.map(
249
- (key, i) => String(row[key] || "").padEnd(maxWidths[i])
250
- ).join(" | ");
251
- console.log(dataRow);
252
- });
253
- }
254
- static json(data) {
255
- console.log(JSON.stringify(data, null, 2));
256
- }
257
- };
258
-
259
- // src/commands/auth.ts
260
- function createAuthCommands() {
261
- const auth = new import_commander.Command("auth");
262
- auth.command("login").description("Authenticate with MCPHosting").option("--token <token>", "Provide API token directly").action(async (options) => {
263
- const config = new Config();
264
- if (options.token) {
265
- config.token = options.token;
266
- const api = new MCPHostingAPI(options.token);
267
- const user = await api.whoami();
268
- if (user) {
269
- config.user = user;
270
- Logger.success(`Logged in as ${user.email}${user.org ? ` (${user.org})` : ""}`);
271
- } else {
272
- Logger.warning("Token saved, but could not verify user info");
273
- }
274
- return;
275
- }
276
- const spinner = Logger.spinner("Starting login flow...");
277
- let server;
278
- let resolved = false;
279
- try {
280
- const authPromise = new Promise((resolve, reject) => {
281
- server = (0, import_http.createServer)((req, res) => {
282
- if (req.url?.startsWith("/callback")) {
283
- const url = new URL(req.url, "http://localhost");
284
- const token2 = url.searchParams.get("token");
285
- if (token2) {
286
- res.writeHead(200, { "Content-Type": "text/html" });
287
- res.end(`
288
- <html>
289
- <body style="font-family: system-ui; text-align: center; padding: 50px;">
290
- <h2>\u2705 Login Successful!</h2>
291
- <p>You can now close this window and return to your terminal.</p>
292
- </body>
293
- </html>
294
- `);
295
- resolve(token2);
296
- } else {
297
- res.writeHead(400, { "Content-Type": "text/html" });
298
- res.end(`
299
- <html>
300
- <body style="font-family: system-ui; text-align: center; padding: 50px;">
301
- <h2>\u274C Login Failed</h2>
302
- <p>No token received. Please try again.</p>
303
- </body>
304
- </html>
305
- `);
306
- reject(new Error("No token received"));
307
- }
308
- } else {
309
- res.writeHead(404);
310
- res.end("Not found");
311
- }
312
- });
313
- server.listen(0, () => {
314
- const port = server.address().port;
315
- const callbackUrl = `http://localhost:${port}/callback`;
316
- const authUrl = `https://mcphosting.com/cli/auth?callback=${encodeURIComponent(callbackUrl)}`;
317
- setTimeout(() => {
318
- if (!resolved) {
319
- reject(new Error("Login timeout - please try again"));
320
- }
321
- }, 12e4);
322
- (0, import_open.default)(authUrl).catch(() => {
323
- Logger.warning(`Could not open browser automatically. Please visit: ${authUrl}`);
324
- });
325
- });
326
- });
327
- const token = await authPromise;
328
- resolved = true;
329
- spinner.succeed("Login successful!");
330
- config.token = token;
331
- const api = new MCPHostingAPI(token);
332
- const user = await api.whoami();
333
- if (user) {
334
- config.user = user;
335
- Logger.success(`Logged in as ${user.email}${user.org ? ` (${user.org})` : ""}`);
336
- }
337
- } catch (error) {
338
- spinner.fail("Login failed");
339
- Logger.error(`Login error: ${error}`);
340
- process.exit(1);
341
- } finally {
342
- if (server) {
343
- server.close();
344
- }
345
- }
346
- });
347
- auth.command("logout").description("Remove stored authentication").action(() => {
348
- const config = new Config();
349
- config.token = void 0;
350
- config.user = void 0;
351
- Logger.success("Logged out successfully");
352
- });
353
- auth.command("whoami").description("Show current user information").action(async () => {
354
- const config = new Config();
355
- const token = config.token;
356
- if (!token) {
357
- Logger.warning("Not logged in. Use `mcphost auth login` to authenticate.");
358
- return;
359
- }
360
- const user = config.user;
361
- if (user) {
362
- Logger.info(`Logged in as: ${user.email}${user.org ? ` (${user.org})` : ""}`);
363
- } else {
364
- Logger.warning("User info not available. Try logging in again.");
365
- }
366
- });
367
- return auth;
368
- }
369
- function createLegacyAuthCommands() {
370
- const config = new Config();
371
- const login2 = new import_commander.Command("login").description("Authenticate with MCPHosting").option("--token <token>", "Provide API token directly").action(async (options) => {
372
- const authCmd = createAuthCommands();
373
- const loginCmd = authCmd.commands.find((cmd) => cmd.name() === "login");
374
- if (loginCmd) {
375
- await loginCmd.action(options);
376
- }
377
- });
378
- const logout2 = new import_commander.Command("logout").description("Remove stored authentication").action(() => {
379
- config.token = void 0;
380
- config.user = void 0;
381
- Logger.success("Logged out successfully");
382
- });
383
- const whoami2 = new import_commander.Command("whoami").description("Show current user information").action(async () => {
384
- const authCmd = createAuthCommands();
385
- const whoamiCmd = authCmd.commands.find((cmd) => cmd.name() === "whoami");
386
- if (whoamiCmd) {
387
- await whoamiCmd.action();
388
- }
389
- });
390
- return [login2, logout2, whoami2];
391
- }
392
-
393
- // src/commands/connect.ts
394
- var import_commander2 = require("commander");
395
-
396
- // src/lib/clients.ts
397
- var import_promises = require("fs/promises");
398
- var import_path = require("path");
399
- var import_os = require("os");
400
- var import_promises2 = require("fs/promises");
401
- var ClientManager = class {
402
- static getClientPaths() {
403
- const home = (0, import_os.homedir)();
404
- const platform = process.platform;
405
- const paths = {
406
- claude: platform === "win32" ? (0, import_path.join)(process.env.APPDATA || "", "Claude", "claude_desktop_config.json") : (0, import_path.join)(home, "Library", "Application Support", "Claude", "claude_desktop_config.json"),
407
- cursor: (0, import_path.join)(home, ".cursor", "mcp.json"),
408
- vscode: (0, import_path.join)(process.cwd(), ".vscode", "mcp.json"),
409
- openclaw: (0, import_path.join)(home, ".openclaw", "mcp.json"),
410
- chatgpt: ""
411
- // Web-based, no local config
412
- };
413
- return paths;
414
- }
415
- static async detectInstalledClients() {
416
- const paths = this.getClientPaths();
417
- const clients = [];
418
- for (const [name, path] of Object.entries(paths)) {
419
- if (name === "chatgpt") continue;
420
- try {
421
- await (0, import_promises.access)(path);
422
- clients.push({ name, configPath: path, exists: true });
423
- } catch {
424
- clients.push({ name, configPath: path, exists: false });
425
- }
426
- }
427
- return clients;
428
- }
429
- static async addToClient(client, slug, url) {
430
- if (client === "chatgpt") {
431
- Logger.info("ChatGPT Setup Instructions:");
432
- console.log(`
433
- 1. Open ChatGPT in your browser
434
- 2. Go to Settings \u2192 Apps \u2192 Connect an app
435
- 3. Enter this URL: ${url}
436
- 4. Follow the prompts to authorize the MCP server
437
- `);
438
- return true;
439
- }
440
- const paths = this.getClientPaths();
441
- const configPath = paths[client];
442
- if (!configPath) {
443
- throw new Error(`Unsupported client: ${client}`);
444
- }
445
- const serverEntry = {
446
- command: "npx",
447
- args: ["-y", "mcphosting-cli", "proxy", url],
448
- env: {}
449
- };
450
- try {
451
- await (0, import_promises2.mkdir)((0, import_path.dirname)(configPath), { recursive: true });
452
- let config = { mcpServers: {} };
453
- try {
454
- const configContent = await (0, import_promises.readFile)(configPath, "utf-8");
455
- config = JSON.parse(configContent);
456
- if (!config.mcpServers) {
457
- config.mcpServers = {};
458
- }
459
- } catch {
460
- }
461
- config.mcpServers[slug] = serverEntry;
462
- await (0, import_promises.writeFile)(configPath, JSON.stringify(config, null, 2));
463
- return true;
464
- } catch (error) {
465
- Logger.error(`Failed to configure ${client}: ${error}`);
466
- return false;
467
- }
468
- }
469
- static async removeFromClient(client, slug) {
470
- if (client === "chatgpt") {
471
- Logger.info("To remove from ChatGPT:");
472
- console.log(`
473
- 1. Open ChatGPT in your browser
474
- 2. Go to Settings \u2192 Apps
475
- 3. Find and disconnect the MCP server: ${slug}
476
- `);
477
- return true;
478
- }
479
- const paths = this.getClientPaths();
480
- const configPath = paths[client];
481
- if (!configPath) {
482
- throw new Error(`Unsupported client: ${client}`);
483
- }
484
- try {
485
- const configContent = await (0, import_promises.readFile)(configPath, "utf-8");
486
- const config = JSON.parse(configContent);
487
- if (config.mcpServers && config.mcpServers[slug]) {
488
- delete config.mcpServers[slug];
489
- await (0, import_promises.writeFile)(configPath, JSON.stringify(config, null, 2));
490
- return true;
491
- }
492
- return false;
493
- } catch (error) {
494
- Logger.error(`Failed to remove from ${client}: ${error}`);
495
- return false;
496
- }
497
- }
498
- static async removeFromAllClients(slug) {
499
- const clients = await this.detectInstalledClients();
500
- const removed = [];
501
- for (const client of clients) {
502
- if (!client.exists) continue;
503
- try {
504
- const success = await this.removeFromClient(client.name, slug);
505
- if (success) {
506
- removed.push(client.name);
507
- }
508
- } catch (error) {
509
- Logger.warning(`Failed to remove from ${client.name}: ${error}`);
510
- }
511
- }
512
- return removed;
513
- }
514
- static resolveUrl(urlOrSlug) {
515
- if (urlOrSlug.startsWith("http://") || urlOrSlug.startsWith("https://")) {
516
- return urlOrSlug;
517
- }
518
- return `https://${urlOrSlug}.mcphost.dev`;
519
- }
520
- static extractSlug(url) {
521
- if (url.includes(".mcphost.dev")) {
522
- const match = url.match(/https?:\/\/([^.]+)\.mcphost\.dev/);
523
- if (match) {
524
- return match[1];
525
- }
526
- }
527
- try {
528
- const parsed = new URL(url);
529
- return parsed.hostname.replace(/\./g, "-");
530
- } catch {
531
- return url.replace(/[^a-zA-Z0-9]/g, "-").slice(0, 20);
532
- }
533
- }
534
- };
535
-
536
- // src/commands/connect.ts
537
- var import_chalk2 = __toESM(require("chalk"), 1);
538
- function createConnectCommands() {
539
- const connect = new import_commander2.Command("connect").description("Connect an MCP server to AI clients").argument("<url-or-slug>", "MCP server URL or slug").option("--client <client>", "Target specific client (claude, cursor, vscode, openclaw, chatgpt)").option("--name <name>", "Custom name for the connection").action(async (urlOrSlug, options) => {
540
- const config = new Config();
541
- const spinner = Logger.spinner("Setting up MCP connection...");
542
- try {
543
- const url = ClientManager.resolveUrl(urlOrSlug);
544
- const slug = ClientManager.extractSlug(url);
545
- const name = options.name || slug;
546
- const existing = config.findConnection(slug);
547
- if (existing) {
548
- spinner.warn(`Already connected to ${slug}`);
549
- Logger.info(`Use \`mcphost disconnect ${slug}\` to remove first`);
550
- return;
551
- }
552
- const clients = [];
553
- if (options.client) {
554
- const clientName = options.client;
555
- const success = await ClientManager.addToClient(clientName, slug, url);
556
- if (success) {
557
- clients.push(clientName);
558
- spinner.succeed(`Connected to ${clientName}`);
559
- } else {
560
- spinner.fail(`Failed to connect to ${clientName}`);
561
- process.exit(1);
562
- }
563
- } else {
564
- const detectedClients = await ClientManager.detectInstalledClients();
565
- const availableClients = detectedClients.filter((c) => c.exists);
566
- if (availableClients.length === 0) {
567
- spinner.warn("No supported clients found");
568
- Logger.info("Supported clients: Claude Desktop, Cursor, VS Code, OpenClaw");
569
- Logger.info("Install a client and try again, or use --client chatgpt for web setup");
570
- return;
571
- }
572
- spinner.text = `Configuring ${availableClients.length} client${availableClients.length > 1 ? "s" : ""}...`;
573
- for (const client of availableClients) {
574
- try {
575
- const success = await ClientManager.addToClient(
576
- client.name,
577
- slug,
578
- url
579
- );
580
- if (success) {
581
- clients.push(client.name);
582
- }
583
- } catch (error) {
584
- Logger.warning(`Failed to configure ${client.name}: ${error}`);
585
- }
586
- }
587
- spinner.succeed(`Connected to ${clients.length} client${clients.length > 1 ? "s" : ""}`);
588
- }
589
- config.addConnection({
590
- slug,
591
- url,
592
- clients
593
- });
594
- Logger.success(`\u{1F517} ${name} connected successfully!`);
595
- Logger.dim(` URL: ${url}`);
596
- Logger.dim(` Clients: ${clients.join(", ")}`);
597
- console.log("\n" + import_chalk2.default.green("\u{1F389} Connected! Share with your team:"));
598
- console.log(import_chalk2.default.cyan(`npx mcphosting-cli connect ${urlOrSlug}`));
599
- console.log("\n" + import_chalk2.default.yellow("\u2B50 Star us: ") + import_chalk2.default.blue("https://github.com/gorlomi-enzo/mcphosting-cli"));
600
- console.log("");
601
- } catch (error) {
602
- spinner.fail("Connection failed");
603
- Logger.error(`Error: ${error}`);
604
- process.exit(1);
605
- }
606
- });
607
- return connect;
608
- }
609
- function createDisconnectCommand() {
610
- return new import_commander2.Command("disconnect").description("Disconnect an MCP server").argument("<slug-or-id>", "MCP server slug or connection ID").action(async (slugOrId) => {
611
- const config = new Config();
612
- const connection = config.findConnection(slugOrId);
613
- if (!connection) {
614
- Logger.error(`Connection not found: ${slugOrId}`);
615
- Logger.info("Use `mcphost list` to see active connections");
616
- return;
617
- }
618
- const spinner = Logger.spinner(`Disconnecting ${connection.slug}...`);
619
- try {
620
- const removedFrom = await ClientManager.removeFromAllClients(connection.slug);
621
- config.removeConnection(connection.id);
622
- spinner.succeed(`Disconnected ${connection.slug}`);
623
- if (removedFrom.length > 0) {
624
- Logger.info(`Removed from: ${removedFrom.join(", ")}`);
625
- }
626
- } catch (error) {
627
- spinner.fail("Disconnect failed");
628
- Logger.error(`Error: ${error}`);
629
- }
630
- });
631
- }
632
- function createListCommand() {
633
- return new import_commander2.Command("list").description("List connected MCP servers").option("--json", "Output as JSON").action(async (options) => {
634
- const config = new Config();
635
- const connections = config.connections;
636
- if (connections.length === 0) {
637
- Logger.info("No MCP connections found");
638
- Logger.dim("Use `mcphost connect <url>` to add one");
639
- return;
640
- }
641
- if (options.json) {
642
- Logger.json(connections);
643
- return;
644
- }
645
- Logger.bold(`\u{1F4CB} Connected MCP Servers (${connections.length})`);
646
- console.log("");
647
- const tableData = connections.map((conn) => ({
648
- Slug: conn.slug,
649
- URL: conn.url.length > 50 ? conn.url.slice(0, 47) + "..." : conn.url,
650
- Clients: conn.clients.join(", "),
651
- Added: new Date(conn.addedAt).toLocaleDateString()
652
- }));
653
- Logger.table(tableData);
654
- console.log("");
655
- Logger.dim("Use `mcphost disconnect <slug>` to remove a connection");
656
- });
657
- }
658
-
659
- // src/commands/import.ts
660
- var import_commander3 = require("commander");
661
- var import_promises3 = require("fs/promises");
662
- var import_path2 = require("path");
663
- var import_os2 = require("os");
664
- var import_chalk3 = __toESM(require("chalk"), 1);
665
- function createImportCommand() {
666
- return new import_commander3.Command("import").description("Import MCP connections from other tools").option("--from <tool>", "Import from: smithery", "smithery").option("--dry-run", "Show what would be imported without actually importing").option("--config-path <path>", "Custom config file path").action(async (options) => {
667
- if (options.from !== "smithery") {
668
- Logger.error("Only Smithery import is currently supported");
669
- Logger.info("Usage: mcphost import --from smithery");
670
- return;
671
- }
672
- await importFromSmitery(options);
673
- });
674
- }
675
- async function importFromSmitery(options) {
676
- const config = new Config();
677
- const spinner = Logger.spinner("Looking for Smithery config...");
678
- try {
679
- const smitheryPaths = [
680
- options.configPath,
681
- (0, import_path2.join)((0, import_os2.homedir)(), ".smithery", "config.json"),
682
- (0, import_path2.join)((0, import_os2.homedir)(), ".config", "smithery", "config.json"),
683
- (0, import_path2.join)((0, import_os2.homedir)(), "Library", "Application Support", "smithery", "config.json"),
684
- (0, import_path2.join)(process.cwd(), "smithery.json"),
685
- (0, import_path2.join)(process.cwd(), ".smithery.json")
686
- ].filter(Boolean);
687
- let smitheryConfigPath = null;
688
- for (const path of smitheryPaths) {
689
- try {
690
- await (0, import_promises3.access)(path);
691
- smitheryConfigPath = path;
692
- break;
693
- } catch {
694
- }
695
- }
696
- if (!smitheryConfigPath) {
697
- spinner.fail("Smithery config not found");
698
- Logger.warning("Searched locations:");
699
- smitheryPaths.forEach((path) => Logger.dim(` ${path}`));
700
- Logger.info("\nIf Smithery is installed, you can specify the config path:");
701
- Logger.info(" mcphost import --from smithery --config-path /path/to/smithery/config.json");
702
- return;
703
- }
704
- spinner.succeed(`Found Smithery config: ${smitheryConfigPath}`);
705
- const loadSpinner = Logger.spinner("Reading Smithery config...");
706
- const configContent = await (0, import_promises3.readFile)(smitheryConfigPath, "utf-8");
707
- const smitheryConfig = JSON.parse(configContent);
708
- const servers = {
709
- ...smitheryConfig.servers,
710
- ...smitheryConfig.mcpServers
711
- };
712
- if (!servers || Object.keys(servers).length === 0) {
713
- loadSpinner.warn("No MCP servers found in Smithery config");
714
- return;
715
- }
716
- const serverEntries = Object.entries(servers);
717
- loadSpinner.succeed(`Found ${serverEntries.length} MCP server(s) in Smithery config`);
718
- if (options.dryRun) {
719
- console.log("\n" + import_chalk3.default.bold("\u{1F50D} Preview: Would import the following MCP servers:"));
720
- console.log("");
721
- serverEntries.forEach(([slug, serverConfig]) => {
722
- const url = serverConfig.url || `https://${slug}.mcphost.dev`;
723
- console.log(`\u2022 ${import_chalk3.default.cyan(slug)}`);
724
- console.log(` URL: ${import_chalk3.default.dim(url)}`);
725
- if (serverConfig.command) {
726
- console.log(` Command: ${import_chalk3.default.dim(serverConfig.command + " " + (serverConfig.args?.join(" ") || ""))}`);
727
- }
728
- console.log("");
729
- });
730
- Logger.info("Run without --dry-run to perform the import");
731
- return;
732
- }
733
- const importSpinner = Logger.spinner("Importing MCP servers...");
734
- let imported = 0;
735
- let skipped = 0;
736
- for (const [slug, serverConfig] of serverEntries) {
737
- try {
738
- const existing = config.findConnection(slug);
739
- if (existing) {
740
- Logger.warning(`Skipping ${slug} - already connected`);
741
- skipped++;
742
- continue;
743
- }
744
- let url = serverConfig.url;
745
- if (!url) {
746
- if (serverConfig.command === "npx" && serverConfig.args?.[0] === "mcphosting-cli") {
747
- url = serverConfig.args[2];
748
- } else {
749
- url = `https://${slug}.mcphost.dev`;
750
- }
751
- }
752
- if (!url) {
753
- Logger.warning(`Skipping ${slug} - no URL found`);
754
- skipped++;
755
- continue;
756
- }
757
- const detectedClients = await ClientManager.detectInstalledClients();
758
- const availableClients = detectedClients.filter((c) => c.exists);
759
- const connectedClients = [];
760
- for (const client of availableClients) {
761
- try {
762
- const success = await ClientManager.addToClient(
763
- client.name,
764
- slug,
765
- url
766
- );
767
- if (success) {
768
- connectedClients.push(client.name);
769
- }
770
- } catch (error) {
771
- }
772
- }
773
- config.addConnection({
774
- slug,
775
- url,
776
- clients: connectedClients
777
- });
778
- imported++;
779
- } catch (error) {
780
- Logger.warning(`Failed to import ${slug}: ${error}`);
781
- skipped++;
782
- }
783
- }
784
- importSpinner.succeed("Import completed");
785
- Logger.success(`\u2705 Imported ${imported} MCP server(s)`);
786
- if (skipped > 0) {
787
- Logger.warning(`\u26A0\uFE0F Skipped ${skipped} server(s)`);
788
- }
789
- console.log("");
790
- Logger.info("Use `mcphost list` to see your imported connections");
791
- console.log("\n" + import_chalk3.default.green("\u{1F389} Welcome to MCPHosting! Share with your team:"));
792
- console.log(import_chalk3.default.cyan("npx mcphosting-cli import --from smithery"));
793
- console.log("\n" + import_chalk3.default.yellow("\u2B50 Star us: ") + import_chalk3.default.blue("https://github.com/gorlomi-enzo/mcphosting-cli"));
794
- console.log("");
795
- } catch (error) {
796
- spinner.fail("Import failed");
797
- Logger.error(`Error: ${error}`);
798
- if (error instanceof SyntaxError) {
799
- Logger.warning("Invalid JSON in Smithery config file");
800
- }
801
- process.exit(1);
802
- }
803
- }
804
-
805
- // src/commands/proxy.ts
806
- var import_commander4 = require("commander");
807
- function createProxyCommand() {
808
- return new import_commander4.Command("proxy").description("Start a local STDIO MCP proxy to a remote server").argument("<url>", "Remote MCP server URL").option("--quiet", "Suppress startup messages").action(async (url, options) => {
809
- if (!options.quiet) {
810
- console.error("\u{1F517} Proxying via mcphosting.com");
811
- console.error(`\u{1F4E1} Remote server: ${url}`);
812
- }
813
- try {
814
- await startProxy(url, options.quiet);
815
- } catch (error) {
816
- if (!options.quiet) {
817
- console.error(`\u274C Proxy error: ${error}`);
818
- }
819
- process.exit(1);
820
- }
821
- });
822
- }
823
- async function startProxy(url, quiet = false) {
824
- let buffer = "";
825
- process.stdin.setEncoding("utf8");
826
- process.stdin.on("data", async (chunk) => {
827
- buffer += chunk;
828
- const lines = buffer.split("\n");
829
- buffer = lines.pop() || "";
830
- for (const line of lines) {
831
- if (line.trim()) {
832
- try {
833
- await forwardMessage(url, line, quiet);
834
- } catch (error) {
835
- if (!quiet) {
836
- console.error(`Proxy error: ${error}`);
837
- }
838
- const errorResponse = {
839
- jsonrpc: "2.0",
840
- id: null,
841
- error: {
842
- code: -32603,
843
- message: "Proxy error",
844
- data: error.toString()
845
- }
846
- };
847
- process.stdout.write(JSON.stringify(errorResponse) + "\n");
848
- }
849
- }
850
- }
851
- });
852
- process.stdin.on("end", () => {
853
- if (!quiet) {
854
- console.error("\u{1F4F4} Proxy connection closed");
855
- }
856
- process.exit(0);
857
- });
858
- process.stdin.on("error", (error) => {
859
- if (!quiet) {
860
- console.error(`Stdin error: ${error}`);
861
- }
862
- process.exit(1);
863
- });
864
- await new Promise(() => {
865
- });
866
- }
867
- async function forwardMessage(url, message, quiet) {
868
- try {
869
- const jsonrpcMessage = JSON.parse(message);
870
- const response = await fetch(url, {
871
- method: "POST",
872
- headers: {
873
- "Content-Type": "application/json",
874
- "Accept": "application/json",
875
- "User-Agent": "mcphosting-cli-proxy"
876
- },
877
- body: message
878
- });
879
- if (!response.ok) {
880
- throw new Error(`HTTP ${response.status}: ${response.statusText}`);
881
- }
882
- const responseData = await response.text();
883
- process.stdout.write(responseData);
884
- if (!responseData.endsWith("\n")) {
885
- process.stdout.write("\n");
886
- }
887
- } catch (error) {
888
- if (error instanceof SyntaxError) {
889
- const errorResponse = {
890
- jsonrpc: "2.0",
891
- id: null,
892
- error: {
893
- code: -32700,
894
- message: "Parse error",
895
- data: "Invalid JSON-RPC message"
896
- }
897
- };
898
- process.stdout.write(JSON.stringify(errorResponse) + "\n");
899
- } else {
900
- throw error;
901
- }
902
- }
903
- }
904
-
905
- // src/commands/search.ts
906
- var import_commander5 = require("commander");
907
- var import_chalk4 = __toESM(require("chalk"), 1);
908
- function createSearchCommand() {
909
- return new import_commander5.Command("search").description("Search MCP servers in the marketplace").argument("<query>", "Search query").option("--limit <number>", "Maximum number of results", "10").option("--json", "Output as JSON").action(async (query, options) => {
910
- const config = new Config();
911
- const api = new MCPHostingAPI(config.token);
912
- const spinner = Logger.spinner(`Searching for "${query}"...`);
913
- try {
914
- const results = await api.searchMCPs(query);
915
- spinner.succeed(`Found ${results.length} MCP server(s)`);
916
- if (results.length === 0) {
917
- Logger.info("No MCP servers found matching your query");
918
- Logger.dim("Try searching for: github, slack, notion, stripe, postgres, filesystem");
919
- return;
920
- }
921
- const limit = parseInt(options.limit);
922
- const limitedResults = results.slice(0, limit);
923
- if (options.json) {
924
- Logger.json(limitedResults);
925
- return;
926
- }
927
- console.log("");
928
- Logger.bold(`\u{1F50D} MCP Marketplace Search Results`);
929
- console.log("");
930
- limitedResults.forEach((server, index) => {
931
- console.log(import_chalk4.default.cyan(`${index + 1}. ${server.name}`));
932
- console.log(` ${import_chalk4.default.dim(server.description)}`);
933
- console.log(` ${import_chalk4.default.yellow("Slug:")} ${server.slug} | ${import_chalk4.default.yellow("Installs:")} ${server.installs.toLocaleString()} | ${import_chalk4.default.yellow("Author:")} ${server.author}`);
934
- console.log(` ${import_chalk4.default.green("Tools:")} ${server.tools.join(", ")}`);
935
- if (server.url) {
936
- console.log(` ${import_chalk4.default.blue("Connect:")} ${import_chalk4.default.dim(`mcphost connect ${server.slug}`)}`);
937
- }
938
- console.log("");
939
- });
940
- if (results.length > limit) {
941
- Logger.dim(`Showing ${limit} of ${results.length} results. Use --limit to see more.`);
942
- }
943
- Logger.info("Use `mcphost info <slug>` for detailed information");
944
- Logger.info("Use `mcphost connect <slug>` to install");
945
- } catch (error) {
946
- spinner.fail("Search failed");
947
- Logger.error(`Error: ${error}`);
948
- process.exit(1);
949
- }
950
- });
951
- }
952
- function createInfoCommand() {
953
- return new import_commander5.Command("info").description("Show detailed information about an MCP server").argument("<slug>", "MCP server slug").option("--json", "Output as JSON").action(async (slug, options) => {
954
- const config = new Config();
955
- const api = new MCPHostingAPI(config.token);
956
- const spinner = Logger.spinner(`Getting info for ${slug}...`);
957
- try {
958
- const server = await api.getMCPInfo(slug);
959
- if (!server) {
960
- spinner.fail(`MCP server not found: ${slug}`);
961
- Logger.info("Use `mcphost search <query>` to find servers");
962
- return;
963
- }
964
- spinner.succeed("Found server info");
965
- if (options.json) {
966
- Logger.json(server);
967
- return;
968
- }
969
- console.log("");
970
- console.log(import_chalk4.default.bold.cyan(`\u{1F4E6} ${server.name}`));
971
- console.log(import_chalk4.default.dim(server.description));
972
- console.log("");
973
- const infoTable = [
974
- { Property: "Slug", Value: server.slug },
975
- { Property: "Author", Value: server.author },
976
- { Property: "Installs", Value: server.installs.toLocaleString() },
977
- { Property: "Tools", Value: server.tools.length.toString() }
978
- ];
979
- if (server.url) {
980
- infoTable.push({ Property: "URL", Value: server.url });
981
- }
982
- Logger.table(infoTable);
983
- console.log("");
984
- console.log(import_chalk4.default.yellow("\u{1F527} Available Tools:"));
985
- server.tools.forEach((tool) => {
986
- console.log(` \u2022 ${tool}`);
987
- });
988
- console.log("");
989
- console.log(import_chalk4.default.green("\u{1F4A1} Quick Start:"));
990
- console.log(import_chalk4.default.dim(` mcphost connect ${server.slug}`));
991
- const connection = config.findConnection(slug);
992
- if (connection) {
993
- console.log("");
994
- console.log(import_chalk4.default.blue("\u2139\uFE0F Already connected to:"), connection.clients.join(", "));
995
- }
996
- } catch (error) {
997
- spinner.fail("Failed to get server info");
998
- Logger.error(`Error: ${error}`);
999
- process.exit(1);
1000
- }
1001
- });
1002
- }
1003
-
1004
- // src/commands/servers.ts
1005
- var import_commander6 = require("commander");
1006
- function createServersCommand() {
1007
- const servers = new import_commander6.Command("servers");
1008
- servers.description("Manage your hosted MCP servers");
1009
- servers.command("list").description("List your MCP servers").action(async () => {
1010
- const config = new Config();
1011
- if (!config.token) {
1012
- Logger.warning("Authentication required");
1013
- Logger.info("Use `mcphost login` to authenticate with MCPHosting");
1014
- return;
1015
- }
1016
- Logger.info("\u{1F6A7} Server management coming soon!");
1017
- console.log("");
1018
- console.log("This feature will let you:");
1019
- console.log("\u2022 List your hosted MCP servers");
1020
- console.log("\u2022 View usage analytics");
1021
- console.log("\u2022 Manage API keys");
1022
- console.log("\u2022 Publish to marketplace");
1023
- console.log("");
1024
- Logger.info("Visit https://mcphosting.com to manage servers in the web dashboard");
1025
- });
1026
- return servers;
1027
- }
1028
- function createKeysCommand() {
1029
- const keys = new import_commander6.Command("keys");
1030
- keys.description("Manage API keys for your MCP servers");
1031
- keys.command("list").argument("[server-id]", "Server ID").description("List API keys").action(async (serverId) => {
1032
- const config = new Config();
1033
- if (!config.token) {
1034
- Logger.warning("Authentication required");
1035
- Logger.info("Use `mcphost login` to authenticate");
1036
- return;
1037
- }
1038
- Logger.info("\u{1F6A7} API key management coming soon!");
1039
- Logger.info("Visit https://mcphosting.com to manage keys in the web dashboard");
1040
- });
1041
- keys.command("create").argument("<server-id>", "Server ID").option("--name <name>", "Key name", "CLI Key").description("Create a new API key").action(async (serverId, options) => {
1042
- const config = new Config();
1043
- if (!config.token) {
1044
- Logger.warning("Authentication required");
1045
- Logger.info("Use `mcphost login` to authenticate");
1046
- return;
1047
- }
1048
- Logger.info("\u{1F6A7} API key creation coming soon!");
1049
- Logger.info("Visit https://mcphosting.com to create keys in the web dashboard");
1050
- });
1051
- keys.command("revoke").argument("<key-id>", "API key ID").description("Revoke an API key").action(async (keyId) => {
1052
- const config = new Config();
1053
- if (!config.token) {
1054
- Logger.warning("Authentication required");
1055
- Logger.info("Use `mcphost login` to authenticate");
1056
- return;
1057
- }
1058
- Logger.info("\u{1F6A7} API key revocation coming soon!");
1059
- Logger.info("Visit https://mcphosting.com to revoke keys in the web dashboard");
1060
- });
1061
- return keys;
1062
- }
1063
- function createPublishCommand() {
1064
- return new import_commander6.Command("publish").description("Publish MCP server to marketplace").argument("[server-id]", "Server ID to publish").option("--private", "Keep server private").option("--description <desc>", "Server description").option("--tags <tags>", "Comma-separated tags").action(async (serverId, options = {}) => {
1065
- const config = new Config();
1066
- if (!config.token) {
1067
- Logger.warning("Authentication required");
1068
- Logger.info("Use `mcphost login` to authenticate");
1069
- return;
1070
- }
1071
- Logger.info("\u{1F6A7} Marketplace publishing coming soon!");
1072
- console.log("");
1073
- console.log("This feature will let you:");
1074
- console.log("\u2022 Publish your MCP servers to the marketplace");
1075
- console.log("\u2022 Set descriptions and tags");
1076
- console.log("\u2022 Configure public/private visibility");
1077
- console.log("\u2022 Track usage analytics");
1078
- console.log("");
1079
- Logger.info("Visit https://mcphosting.com to publish servers via the web dashboard");
1080
- if (serverId) {
1081
- Logger.dim(`Would publish server: ${serverId}`);
1082
- }
1083
- if (options.description) {
1084
- Logger.dim(`Description: ${options.description}`);
1085
- }
1086
- if (options.tags) {
1087
- Logger.dim(`Tags: ${options.tags}`);
1088
- }
1089
- });
1090
- }
1091
-
1092
- // src/index.ts
1093
- var import_chalk5 = __toESM(require("chalk"), 1);
1094
- var program = new import_commander7.Command();
1095
- program.name("mcphost").description("Connect AI agents to MCP servers. Browse, install, and manage Model Context Protocol servers.").version("0.1.0").configureOutput({
1096
- outputError: (str, write) => {
1097
- write(import_chalk5.default.red(str));
1098
- }
1099
- });
1100
- program.addCommand(createConnectCommands());
1101
- program.addCommand(createDisconnectCommand());
1102
- program.addCommand(createListCommand());
1103
- program.addCommand(createImportCommand());
1104
- program.addCommand(createProxyCommand());
1105
- program.addCommand(createSearchCommand());
1106
- program.addCommand(createInfoCommand());
1107
- program.addCommand(createServersCommand());
1108
- program.addCommand(createKeysCommand());
1109
- program.addCommand(createPublishCommand());
1110
- program.addCommand(createAuthCommands());
1111
- var [login, logout, whoami] = createLegacyAuthCommands();
1112
- program.addCommand(login);
1113
- program.addCommand(logout);
1114
- program.addCommand(whoami);
1115
- program.configureHelp({
1116
- subcommandTerm: (cmd) => import_chalk5.default.cyan(cmd.name()),
1117
- commandUsage: (cmd) => {
1118
- const usage = cmd.usage();
1119
- return import_chalk5.default.yellow(usage);
1120
- },
1121
- commandDescription: (cmd) => {
1122
- return import_chalk5.default.dim(cmd.description());
1123
- }
1124
- });
1125
- program.addHelpText("after", `
1126
- ${import_chalk5.default.bold("Examples:")}
1127
- ${import_chalk5.default.cyan("mcphost connect github")} ${import_chalk5.default.dim("Connect to GitHub MCP server")}
1128
- ${import_chalk5.default.cyan("mcphost connect https://my-mcp.example.com")} ${import_chalk5.default.dim("Connect to custom MCP server")}
1129
- ${import_chalk5.default.cyan("mcphost connect slack --client claude")} ${import_chalk5.default.dim("Connect Slack MCP only to Claude")}
1130
- ${import_chalk5.default.cyan("mcphost list")} ${import_chalk5.default.dim("List all connected MCP servers")}
1131
- ${import_chalk5.default.cyan("mcphost search github")} ${import_chalk5.default.dim("Search marketplace for MCP servers")}
1132
- ${import_chalk5.default.cyan("mcphost info notion")} ${import_chalk5.default.dim("Get details about Notion MCP")}
1133
- ${import_chalk5.default.cyan("mcphost import --from smithery")} ${import_chalk5.default.dim("Import connections from Smithery")}
1134
- ${import_chalk5.default.cyan("mcphost disconnect github")} ${import_chalk5.default.dim("Remove GitHub MCP connection")}
1135
-
1136
- ${import_chalk5.default.bold("Supported AI Clients:")}
1137
- \u2022 ${import_chalk5.default.green("Claude Desktop")} - Auto-configured via claude_desktop_config.json
1138
- \u2022 ${import_chalk5.default.green("Cursor")} - Auto-configured via .cursor/mcp.json
1139
- \u2022 ${import_chalk5.default.green("VS Code")} - Configured via .vscode/mcp.json
1140
- \u2022 ${import_chalk5.default.green("OpenClaw")} - Auto-configured via ~/.openclaw/mcp.json
1141
- \u2022 ${import_chalk5.default.green("ChatGPT")} - Manual setup with web instructions
1142
-
1143
- ${import_chalk5.default.bold("Get Started:")}
1144
- ${import_chalk5.default.dim("1.")} ${import_chalk5.default.cyan("mcphost search <topic>")} ${import_chalk5.default.dim("Find MCP servers")}
1145
- ${import_chalk5.default.dim("2.")} ${import_chalk5.default.cyan("mcphost connect <slug>")} ${import_chalk5.default.dim("Connect to your AI clients")}
1146
- ${import_chalk5.default.dim("3.")} ${import_chalk5.default.cyan("mcphost list")} ${import_chalk5.default.dim("View your connections")}
1147
-
1148
- ${import_chalk5.default.yellow("\u2B50 Star us:")} ${import_chalk5.default.blue("https://github.com/gorlomi-enzo/mcphosting-cli")}
1149
- ${import_chalk5.default.yellow("\u{1F4DA} Docs:")} ${import_chalk5.default.blue("https://mcphosting.com/docs")}
1150
- `);
1151
- process.on("uncaughtException", (error) => {
1152
- Logger.error(`Uncaught error: ${error.message}`);
1153
- if (process.env.DEBUG) {
1154
- console.error(error.stack);
1155
- }
1156
- process.exit(1);
1157
- });
1158
- process.on("unhandledRejection", (reason) => {
1159
- Logger.error(`Unhandled rejection: ${reason}`);
1160
- if (process.env.DEBUG) {
1161
- console.error(reason);
1162
- }
1163
- process.exit(1);
1164
- });
1165
- process.on("SIGINT", () => {
1166
- console.log("\n");
1167
- Logger.info("Goodbye! \u{1F44B}");
1168
- process.exit(0);
1169
- });
1170
- async function main() {
1171
- try {
1172
- await program.parseAsync();
1173
- } catch (error) {
1174
- Logger.error(`Command failed: ${error}`);
1175
- if (process.env.DEBUG) {
1176
- console.error(error);
1177
- }
1178
- process.exit(1);
1179
- }
1180
- }
1181
- main();
1182
- //# sourceMappingURL=index.cjs.map