mcphosting-cli 0.1.1 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -24,9 +24,10 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
24
24
  ));
25
25
 
26
26
  // src/index.ts
27
- var import_commander7 = require("commander");
27
+ var import_commander17 = require("commander");
28
+ var import_chalk16 = __toESM(require("chalk"), 1);
28
29
 
29
- // src/commands/auth.ts
30
+ // src/commands/login.ts
30
31
  var import_commander = require("commander");
31
32
  var import_http = require("http");
32
33
  var import_open = __toESM(require("open"), 1);
@@ -47,8 +48,11 @@ var Config = class {
47
48
  defaults: { connections: [] }
48
49
  });
49
50
  }
51
+ /**
52
+ * Token resolution: env var MCPHOSTING_TOKEN takes priority over config file.
53
+ */
50
54
  get token() {
51
- return this.conf.get("token");
55
+ return process.env.MCPHOSTING_TOKEN || this.conf.get("token");
52
56
  }
53
57
  set token(value) {
54
58
  if (value) {
@@ -57,6 +61,16 @@ var Config = class {
57
61
  this.conf.delete("token");
58
62
  }
59
63
  }
64
+ get refreshToken() {
65
+ return this.conf.get("refresh_token");
66
+ }
67
+ set refreshToken(value) {
68
+ if (value) {
69
+ this.conf.set("refresh_token", value);
70
+ } else {
71
+ this.conf.delete("refresh_token");
72
+ }
73
+ }
60
74
  get user() {
61
75
  return this.conf.get("user");
62
76
  }
@@ -67,6 +81,12 @@ var Config = class {
67
81
  this.conf.delete("user");
68
82
  }
69
83
  }
84
+ /**
85
+ * Check if token came from environment variable.
86
+ */
87
+ get isEnvToken() {
88
+ return !!process.env.MCPHOSTING_TOKEN;
89
+ }
70
90
  get connections() {
71
91
  return this.connectionsConf.get("connections", []);
72
92
  }
@@ -107,20 +127,220 @@ var Config = class {
107
127
  };
108
128
 
109
129
  // src/lib/api.ts
130
+ var import_axios = __toESM(require("axios"), 1);
131
+ var API_BASE = process.env.MCPHOSTING_API_URL || "https://mcphosting.com";
110
132
  var MCPHostingAPI = class {
111
- baseUrl = "https://mcphosting.com/api";
112
- token;
133
+ token = null;
113
134
  constructor(token) {
135
+ this.token = token || null;
136
+ }
137
+ setToken(token) {
114
138
  this.token = token;
115
139
  }
116
- async request(endpoint, options = {}) {
117
- throw new Error(`API not available - using static fallback`);
140
+ get headers() {
141
+ const h = {
142
+ "Content-Type": "application/json",
143
+ "User-Agent": "mcphosting-cli/1.0.0"
144
+ };
145
+ if (this.token) {
146
+ h["Authorization"] = `Bearer ${this.token}`;
147
+ }
148
+ return h;
118
149
  }
119
- async searchMCPs(query) {
150
+ handleError(error) {
151
+ if (error instanceof import_axios.AxiosError) {
152
+ const status = error.response?.status;
153
+ const message = error.response?.data?.error || error.response?.data?.message || error.message;
154
+ if (status === 401) {
155
+ throw new Error("Authentication required. Run `mcphosting login` first.");
156
+ }
157
+ if (status === 403) {
158
+ throw new Error("Access denied. Check your permissions.");
159
+ }
160
+ if (status === 404) {
161
+ throw new Error("Not found. Check the server slug or ID.");
162
+ }
163
+ if (status === 429) {
164
+ throw new Error("Rate limited. Please wait a moment and try again.");
165
+ }
166
+ throw new Error(`API error (${status}): ${message}`);
167
+ }
168
+ throw error;
169
+ }
170
+ // --- Auth ---
171
+ async login(email, password) {
172
+ try {
173
+ const { data } = await import_axios.default.post(`${API_BASE}/api/auth/cli-login`, { email, password });
174
+ return data;
175
+ } catch (error) {
176
+ this.handleError(error);
177
+ }
178
+ }
179
+ async refreshToken(refreshToken) {
180
+ try {
181
+ const { data } = await import_axios.default.post(`${API_BASE}/api/auth/cli-login/refresh`, { refresh_token: refreshToken });
182
+ return data;
183
+ } catch (error) {
184
+ this.handleError(error);
185
+ }
186
+ }
187
+ async githubDeviceStart() {
188
+ try {
189
+ const { data } = await import_axios.default.get(`${API_BASE}/api/auth/github/device`);
190
+ return data;
191
+ } catch (error) {
192
+ this.handleError(error);
193
+ }
194
+ }
195
+ async githubDevicePoll(deviceCode) {
196
+ try {
197
+ const { data } = await import_axios.default.post(
198
+ `${API_BASE}/api/auth/github/device`,
199
+ { device_code: deviceCode },
200
+ { validateStatus: (status) => status < 500 }
201
+ // Don't throw on 202/400
202
+ );
203
+ return data;
204
+ } catch (error) {
205
+ this.handleError(error);
206
+ }
207
+ }
208
+ async whoami() {
209
+ if (!this.token) return null;
210
+ try {
211
+ const { data } = await import_axios.default.get(`${API_BASE}/api/auth/whoami`, { headers: this.headers });
212
+ return data.user || null;
213
+ } catch {
214
+ return null;
215
+ }
216
+ }
217
+ // --- Deploy ---
218
+ async deploy(params) {
219
+ try {
220
+ if (params.githubUrl) {
221
+ const { data: data2 } = await import_axios.default.post(`${API_BASE}/api/cli/github/deploy`, {
222
+ repo_url: params.githubUrl,
223
+ auto_detect: true
224
+ }, { headers: this.headers });
225
+ return data2;
226
+ }
227
+ const { data } = await import_axios.default.post(`${API_BASE}/api/cli/deploy`, params, { headers: this.headers });
228
+ return data;
229
+ } catch (error) {
230
+ this.handleError(error);
231
+ }
232
+ }
233
+ // --- Server Management ---
234
+ async listServers() {
235
+ try {
236
+ const { data } = await import_axios.default.get(`${API_BASE}/api/mcp/projects`, { headers: this.headers });
237
+ return data.projects || [];
238
+ } catch (error) {
239
+ this.handleError(error);
240
+ }
241
+ }
242
+ async getServer(idOrSlug) {
243
+ try {
244
+ const { data } = await import_axios.default.get(`${API_BASE}/api/mcp/projects/${idOrSlug}`, { headers: this.headers });
245
+ return data.project || data;
246
+ } catch (error) {
247
+ this.handleError(error);
248
+ }
249
+ }
250
+ async getServerLogs(idOrSlug, lines = 50) {
251
+ try {
252
+ const { data } = await import_axios.default.get(`${API_BASE}/api/mcp/projects/${idOrSlug}/logs?lines=${lines}`, { headers: this.headers });
253
+ return data.logs || [];
254
+ } catch (error) {
255
+ this.handleError(error);
256
+ }
257
+ }
258
+ // --- Connection Info ---
259
+ async connect(slug) {
260
+ try {
261
+ const { data } = await import_axios.default.post(`${API_BASE}/api/mcp/connect`, { slug }, { headers: this.headers });
262
+ return data;
263
+ } catch (error) {
264
+ this.handleError(error);
265
+ }
266
+ }
267
+ async getConnectionInfo(slug) {
268
+ try {
269
+ const { data } = await import_axios.default.get(`${API_BASE}/api/cli/info/${slug}`, { headers: this.headers });
270
+ return data;
271
+ } catch (error) {
272
+ this.handleError(error);
273
+ }
274
+ }
275
+ // --- Env Vars ---
276
+ async listEnvVars(projectId) {
277
+ try {
278
+ const { data } = await import_axios.default.get(`${API_BASE}/api/mcp/projects/${projectId}/env-vars`, { headers: this.headers });
279
+ return data.envVars || [];
280
+ } catch (error) {
281
+ this.handleError(error);
282
+ }
283
+ }
284
+ async addEnvVar(projectId, key, value) {
285
+ try {
286
+ await import_axios.default.post(
287
+ `${API_BASE}/api/mcp/projects/${projectId}/env-vars`,
288
+ { key, value },
289
+ { headers: this.headers }
290
+ );
291
+ } catch (error) {
292
+ this.handleError(error);
293
+ }
294
+ }
295
+ async removeEnvVar(projectId, key) {
296
+ try {
297
+ await import_axios.default.delete(
298
+ `${API_BASE}/api/mcp/projects/${projectId}/env-vars/${key}`,
299
+ { headers: this.headers }
300
+ );
301
+ } catch (error) {
302
+ this.handleError(error);
303
+ }
304
+ }
305
+ // --- One-Click Deploy ---
306
+ async oneClickDeploy(params) {
120
307
  try {
121
- const results = await this.request(`/marketplace/search?q=${encodeURIComponent(query)}`);
122
- return results.mcps || [];
308
+ const { data } = await import_axios.default.post(`${API_BASE}/api/cli/deploy/one-click`, params, { headers: this.headers });
309
+ return data;
123
310
  } catch (error) {
311
+ this.handleError(error);
312
+ }
313
+ }
314
+ // --- API Keys ---
315
+ async getAPIKeys() {
316
+ try {
317
+ const { data } = await import_axios.default.get(`${API_BASE}/api/keys`, { headers: this.headers });
318
+ return data.keys || [];
319
+ } catch (error) {
320
+ this.handleError(error);
321
+ }
322
+ }
323
+ async createAPIKey(name) {
324
+ try {
325
+ const { data } = await import_axios.default.post(`${API_BASE}/api/keys`, { name }, { headers: this.headers });
326
+ return data;
327
+ } catch (error) {
328
+ this.handleError(error);
329
+ }
330
+ }
331
+ async deleteAPIKey(id) {
332
+ try {
333
+ await import_axios.default.delete(`${API_BASE}/api/keys/${id}`, { headers: this.headers });
334
+ } catch (error) {
335
+ this.handleError(error);
336
+ }
337
+ }
338
+ // --- Marketplace (static fallback for MVP) ---
339
+ async searchMCPs(query) {
340
+ try {
341
+ const { data } = await import_axios.default.get(`${API_BASE}/api/marketplace/search?q=${encodeURIComponent(query)}`, { headers: this.headers });
342
+ return data.mcps || [];
343
+ } catch {
124
344
  const staticMCPs = this.getStaticMCPs();
125
345
  return staticMCPs.filter(
126
346
  (mcp) => mcp.name.toLowerCase().includes(query.toLowerCase()) || mcp.description.toLowerCase().includes(query.toLowerCase()) || mcp.tools.some((tool) => tool.toLowerCase().includes(query.toLowerCase()))
@@ -129,22 +349,13 @@ var MCPHostingAPI = class {
129
349
  }
130
350
  async getMCPInfo(slug) {
131
351
  try {
132
- const result = await this.request(`/marketplace/mcp/${slug}`);
133
- return result.mcp || null;
352
+ const { data } = await import_axios.default.get(`${API_BASE}/api/marketplace/mcp/${slug}`, { headers: this.headers });
353
+ return data.mcp || null;
134
354
  } catch {
135
355
  const staticMCPs = this.getStaticMCPs();
136
356
  return staticMCPs.find((mcp) => mcp.slug === slug) || null;
137
357
  }
138
358
  }
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
359
  getStaticMCPs() {
149
360
  return [
150
361
  {
@@ -208,26 +419,66 @@ var MCPHostingAPI = class {
208
419
  // src/lib/logger.ts
209
420
  var import_chalk = __toESM(require("chalk"), 1);
210
421
  var import_ora = __toESM(require("ora"), 1);
422
+ var _silent = false;
423
+ var _jsonMode = false;
211
424
  var Logger = class _Logger {
425
+ static get isSilent() {
426
+ return _silent;
427
+ }
428
+ static set silent(value) {
429
+ _silent = value;
430
+ }
431
+ static get isJsonMode() {
432
+ return _jsonMode;
433
+ }
434
+ static set jsonMode(value) {
435
+ _jsonMode = value;
436
+ }
212
437
  static info(message) {
438
+ if (_silent || _jsonMode) return;
213
439
  console.log(import_chalk.default.blue("\u2139"), message);
214
440
  }
215
441
  static success(message) {
442
+ if (_silent || _jsonMode) return;
216
443
  console.log(import_chalk.default.green("\u2713"), message);
217
444
  }
218
445
  static warning(message) {
446
+ if (_silent || _jsonMode) return;
219
447
  console.log(import_chalk.default.yellow("\u26A0"), message);
220
448
  }
221
449
  static error(message) {
450
+ if (_jsonMode) {
451
+ console.error(JSON.stringify({ success: false, error: message }));
452
+ return;
453
+ }
454
+ if (_silent) return;
222
455
  console.error(import_chalk.default.red("\u2717"), message);
223
456
  }
224
457
  static dim(message) {
458
+ if (_silent || _jsonMode) return;
225
459
  console.log(import_chalk.default.dim(message));
226
460
  }
227
461
  static bold(message) {
462
+ if (_silent || _jsonMode) return;
228
463
  console.log(import_chalk.default.bold(message));
229
464
  }
230
465
  static spinner(text) {
466
+ if (_silent || _jsonMode || !process.stderr.isTTY) {
467
+ return {
468
+ start: () => (0, import_ora.default)(text),
469
+ stop: () => {
470
+ },
471
+ succeed: () => {
472
+ },
473
+ fail: () => {
474
+ },
475
+ warn: () => {
476
+ },
477
+ info: () => {
478
+ },
479
+ text: ""
480
+ };
481
+ }
231
482
  return (0, import_ora.default)(text).start();
232
483
  }
233
484
  static table(data, headers) {
@@ -256,26 +507,196 @@ var Logger = class _Logger {
256
507
  }
257
508
  };
258
509
 
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) => {
510
+ // src/commands/login.ts
511
+ var import_chalk2 = __toESM(require("chalk"), 1);
512
+ var readline = __toESM(require("readline"), 1);
513
+ function prompt(question, hidden = false) {
514
+ return new Promise((resolve) => {
515
+ const rl = readline.createInterface({
516
+ input: process.stdin,
517
+ output: process.stdout
518
+ });
519
+ if (hidden) {
520
+ const originalWrite = process.stdout.write.bind(process.stdout);
521
+ process.stdout.write = ((str) => {
522
+ if (typeof str === "string" && str !== question && str !== "\n" && str !== "\r\n") {
523
+ return originalWrite("*");
524
+ }
525
+ return originalWrite(str);
526
+ });
527
+ rl.question(question, (answer) => {
528
+ process.stdout.write = originalWrite;
529
+ console.log();
530
+ rl.close();
531
+ resolve(answer);
532
+ });
533
+ } else {
534
+ rl.question(question, (answer) => {
535
+ rl.close();
536
+ resolve(answer);
537
+ });
538
+ }
539
+ });
540
+ }
541
+ function sleep(ms) {
542
+ return new Promise((resolve) => setTimeout(resolve, ms));
543
+ }
544
+ async function loginWithGitHub(config) {
545
+ const api = new MCPHostingAPI();
546
+ const spinner = Logger.spinner("Starting GitHub login...");
547
+ try {
548
+ const deviceFlow = await api.githubDeviceStart();
549
+ spinner.stop();
550
+ console.log("");
551
+ console.log(import_chalk2.default.bold(" \u{1F510} GitHub Login"));
552
+ console.log("");
553
+ console.log(` ${import_chalk2.default.dim("1.")} Open ${import_chalk2.default.cyan.underline(deviceFlow.verification_uri)}`);
554
+ console.log(` ${import_chalk2.default.dim("2.")} Enter code: ${import_chalk2.default.bold.yellow(deviceFlow.user_code)}`);
555
+ console.log("");
556
+ try {
557
+ await (0, import_open.default)(deviceFlow.verification_uri);
558
+ Logger.dim(" Browser opened automatically.");
559
+ } catch {
560
+ Logger.dim(" Open the URL above in your browser.");
561
+ }
562
+ console.log("");
563
+ const pollSpinner = Logger.spinner("Waiting for GitHub authorization...");
564
+ const pollInterval = (deviceFlow.interval || 5) * 1e3;
565
+ const maxAttempts = Math.ceil((deviceFlow.expires_in || 900) / (deviceFlow.interval || 5));
566
+ let currentInterval = pollInterval;
567
+ for (let attempt = 0; attempt < maxAttempts; attempt++) {
568
+ await sleep(currentInterval);
569
+ const result = await api.githubDevicePoll(deviceFlow.device_code);
570
+ if (result.token && result.user) {
571
+ config.token = result.token;
572
+ config.refreshToken = result.refresh_token;
573
+ config.user = { email: result.user.email, org: result.user.github_username };
574
+ pollSpinner.succeed(
575
+ result.is_new_user ? `Account created and logged in as ${import_chalk2.default.bold(result.user.github_username || result.user.email)}` : `Logged in as ${import_chalk2.default.bold(result.user.github_username || result.user.email)}`
576
+ );
577
+ console.log("");
578
+ Logger.info(`Token stored in ${import_chalk2.default.dim("~/.config/mcphosting/")}`);
579
+ Logger.info(`Run ${import_chalk2.default.cyan("mcphosting deploy")} to deploy your first MCP server!`);
580
+ return;
581
+ }
582
+ if (result.error === "authorization_pending") {
583
+ continue;
584
+ }
585
+ if (result.error === "slow_down") {
586
+ currentInterval = (result.interval || 10) * 1e3;
587
+ continue;
588
+ }
589
+ pollSpinner.fail("GitHub login failed");
590
+ Logger.error(result.error || "Unknown error during GitHub authorization");
591
+ process.exit(1);
592
+ }
593
+ pollSpinner.fail("GitHub login timed out");
594
+ Logger.error("Authorization expired. Please try again.");
595
+ process.exit(1);
596
+ } catch (error) {
597
+ spinner.fail("GitHub login failed");
598
+ Logger.error(error.message || "Failed to start GitHub device flow");
599
+ process.exit(1);
600
+ }
601
+ }
602
+ function createLoginCommand() {
603
+ return new import_commander.Command("login").description("Authenticate with MCPHosting").option("--email <email>", "Email address").option("--token <token>", "Provide API token directly").option("--github", "Login with GitHub (recommended)").option("--browser", "Use browser-based login").option("--json", "Output as JSON").action(async (options) => {
263
604
  const config = new Config();
605
+ const isJson = options.json || Logger.isJsonMode;
606
+ if (process.env.MCPHOSTING_TOKEN && !options.token && !options.github && !options.email) {
607
+ const api = new MCPHostingAPI(process.env.MCPHOSTING_TOKEN);
608
+ const user = await api.whoami();
609
+ if (isJson) {
610
+ console.log(JSON.stringify({
611
+ success: true,
612
+ email: user?.email || "unknown",
613
+ token_source: "environment"
614
+ }));
615
+ } else {
616
+ Logger.success(`Authenticated via MCPHOSTING_TOKEN env var`);
617
+ if (user) Logger.info(` User: ${import_chalk2.default.bold(user.email)}`);
618
+ }
619
+ return;
620
+ }
621
+ if (options.github) {
622
+ await loginWithGitHub(config);
623
+ return;
624
+ }
264
625
  if (options.token) {
265
626
  config.token = options.token;
266
627
  const api = new MCPHostingAPI(options.token);
267
628
  const user = await api.whoami();
268
629
  if (user) {
269
630
  config.user = user;
270
- Logger.success(`Logged in as ${user.email}${user.org ? ` (${user.org})` : ""}`);
631
+ if (isJson) {
632
+ console.log(JSON.stringify({ success: true, email: user.email }));
633
+ } else {
634
+ Logger.success(`Logged in as ${import_chalk2.default.bold(user.email)}`);
635
+ }
271
636
  } else {
272
- Logger.warning("Token saved, but could not verify user info");
637
+ if (isJson) {
638
+ console.log(JSON.stringify({ success: true, warning: "Token saved but could not verify identity" }));
639
+ } else {
640
+ Logger.warning("Token saved, but could not verify identity.");
641
+ }
642
+ }
643
+ return;
644
+ }
645
+ if (!process.stdin.isTTY && !options.email) {
646
+ if (isJson) {
647
+ console.log(JSON.stringify({
648
+ success: false,
649
+ error: "Interactive login requires a TTY. Use --token or MCPHOSTING_TOKEN env var for scripted usage."
650
+ }));
651
+ } else {
652
+ Logger.error("Interactive login requires a TTY.");
653
+ Logger.info("Use one of:");
654
+ Logger.dim(` ${import_chalk2.default.cyan("mcphosting login --token <token>")}`);
655
+ Logger.dim(` ${import_chalk2.default.cyan("export MCPHOSTING_TOKEN=<token>")}`);
656
+ }
657
+ process.exit(1);
658
+ }
659
+ if (options.email || !process.stdout.isTTY) {
660
+ const email = options.email || await prompt(import_chalk2.default.cyan("Email: "));
661
+ const password = await prompt(import_chalk2.default.cyan("Password: "), true);
662
+ if (!email || !password) {
663
+ Logger.error("Email and password are required.");
664
+ process.exit(1);
665
+ }
666
+ const spinner2 = Logger.spinner("Logging in...");
667
+ try {
668
+ const api = new MCPHostingAPI();
669
+ const result = await api.login(email, password);
670
+ config.token = result.token;
671
+ config.refreshToken = result.refresh_token;
672
+ config.user = result.user;
673
+ spinner2.succeed(`Logged in as ${import_chalk2.default.bold(result.user.email)}`);
674
+ console.log("");
675
+ Logger.info(`Token stored in ${import_chalk2.default.dim("~/.config/mcphosting/")}`);
676
+ Logger.info(`Run ${import_chalk2.default.cyan("mcphosting deploy")} to deploy your first MCP server!`);
677
+ } catch (error) {
678
+ spinner2.fail("Login failed");
679
+ Logger.error(error.message || "Invalid email or password.");
680
+ process.exit(1);
273
681
  }
274
682
  return;
275
683
  }
276
- const spinner = Logger.spinner("Starting login flow...");
684
+ if (process.stdout.isTTY) {
685
+ console.log("");
686
+ console.log(import_chalk2.default.bold(" \u{1F680} MCPHosting Login"));
687
+ console.log("");
688
+ console.log(` ${import_chalk2.default.cyan("mcphosting login --github")} ${import_chalk2.default.dim("Login with GitHub (recommended)")}`);
689
+ console.log(` ${import_chalk2.default.cyan("mcphosting login --email")} ${import_chalk2.default.dim("Login with email/password")}`);
690
+ console.log(` ${import_chalk2.default.cyan("mcphosting login --browser")} ${import_chalk2.default.dim("Login via browser")}`);
691
+ console.log(` ${import_chalk2.default.cyan("mcphosting login --token")} ${import_chalk2.default.dim("Use existing API token")}`);
692
+ console.log("");
693
+ Logger.info("Starting GitHub login (use --email for email/password)...");
694
+ console.log("");
695
+ await loginWithGitHub(config);
696
+ return;
697
+ }
698
+ const spinner = Logger.spinner("Opening browser for login...");
277
699
  let server;
278
- let resolved = false;
279
700
  try {
280
701
  const authPromise = new Promise((resolve, reject) => {
281
702
  server = (0, import_http.createServer)((req, res) => {
@@ -286,9 +707,9 @@ function createAuthCommands() {
286
707
  res.writeHead(200, { "Content-Type": "text/html" });
287
708
  res.end(`
288
709
  <html>
289
- <body style="font-family: system-ui; text-align: center; padding: 50px;">
710
+ <body style="font-family: system-ui; text-align: center; padding: 50px; background: #0a0a0a; color: #fff;">
290
711
  <h2>\u2705 Login Successful!</h2>
291
- <p>You can now close this window and return to your terminal.</p>
712
+ <p style="color: #888;">You can close this window and return to your terminal.</p>
292
713
  </body>
293
714
  </html>
294
715
  `);
@@ -297,9 +718,9 @@ function createAuthCommands() {
297
718
  res.writeHead(400, { "Content-Type": "text/html" });
298
719
  res.end(`
299
720
  <html>
300
- <body style="font-family: system-ui; text-align: center; padding: 50px;">
721
+ <body style="font-family: system-ui; text-align: center; padding: 50px; background: #0a0a0a; color: #fff;">
301
722
  <h2>\u274C Login Failed</h2>
302
- <p>No token received. Please try again.</p>
723
+ <p style="color: #888;">No token received. Please try again.</p>
303
724
  </body>
304
725
  </html>
305
726
  `);
@@ -314,29 +735,32 @@ function createAuthCommands() {
314
735
  const port = server.address().port;
315
736
  const callbackUrl = `http://localhost:${port}/callback`;
316
737
  const authUrl = `https://mcphosting.com/cli/auth?callback=${encodeURIComponent(callbackUrl)}`;
738
+ spinner.text = "Waiting for browser login...";
317
739
  setTimeout(() => {
318
- if (!resolved) {
319
- reject(new Error("Login timeout - please try again"));
320
- }
740
+ reject(new Error("Login timed out after 2 minutes. Try again."));
321
741
  }, 12e4);
322
742
  (0, import_open.default)(authUrl).catch(() => {
323
- Logger.warning(`Could not open browser automatically. Please visit: ${authUrl}`);
743
+ spinner.info("Could not open browser automatically.");
744
+ Logger.info(`Open this URL manually: ${import_chalk2.default.blue(authUrl)}`);
324
745
  });
325
746
  });
326
747
  });
327
748
  const token = await authPromise;
328
- resolved = true;
329
749
  spinner.succeed("Login successful!");
330
750
  config.token = token;
331
751
  const api = new MCPHostingAPI(token);
332
752
  const user = await api.whoami();
333
753
  if (user) {
334
754
  config.user = user;
335
- Logger.success(`Logged in as ${user.email}${user.org ? ` (${user.org})` : ""}`);
755
+ Logger.success(`Logged in as ${import_chalk2.default.bold(user.email)}`);
336
756
  }
757
+ console.log("");
758
+ Logger.info(`Run ${import_chalk2.default.cyan("mcphosting deploy")} to deploy your first MCP server!`);
337
759
  } catch (error) {
338
760
  spinner.fail("Login failed");
339
- Logger.error(`Login error: ${error}`);
761
+ Logger.error(error.message);
762
+ console.log("");
763
+ Logger.info(`Alternative: ${import_chalk2.default.cyan("mcphosting login --github")}`);
340
764
  process.exit(1);
341
765
  } finally {
342
766
  if (server) {
@@ -344,69 +768,184 @@ function createAuthCommands() {
344
768
  }
345
769
  }
346
770
  });
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 () => {
771
+ }
772
+
773
+ // src/commands/logout.ts
774
+ var import_commander2 = require("commander");
775
+ function createLogoutCommand() {
776
+ return new import_commander2.Command("logout").description("Log out and remove stored credentials").option("--json", "Output as JSON").action((options) => {
354
777
  const config = new Config();
355
- const token = config.token;
356
- if (!token) {
357
- Logger.warning("Not logged in. Use `mcphost auth login` to authenticate.");
778
+ const isJson = options.json || Logger.isJsonMode;
779
+ if (!config.token) {
780
+ if (isJson) {
781
+ console.log(JSON.stringify({ success: true, message: "Already logged out" }));
782
+ } else {
783
+ Logger.info("Already logged out.");
784
+ }
358
785
  return;
359
786
  }
360
- const user = config.user;
361
- if (user) {
362
- Logger.info(`Logged in as: ${user.email}${user.org ? ` (${user.org})` : ""}`);
787
+ const email = config.user?.email;
788
+ config.token = void 0;
789
+ config.user = void 0;
790
+ if (isJson) {
791
+ console.log(JSON.stringify({ success: true, email: email || void 0 }));
792
+ } else if (email) {
793
+ Logger.success(`Logged out from ${email}`);
363
794
  } else {
364
- Logger.warning("User info not available. Try logging in again.");
795
+ Logger.success("Logged out successfully.");
365
796
  }
366
797
  });
367
- return auth;
368
798
  }
369
- function createLegacyAuthCommands() {
799
+
800
+ // src/commands/deploy.ts
801
+ var import_commander3 = require("commander");
802
+ var import_promises4 = require("fs/promises");
803
+ var import_path3 = require("path");
804
+
805
+ // src/lib/auth.ts
806
+ function decodeJwtPayload(token) {
807
+ try {
808
+ const parts = token.split(".");
809
+ if (parts.length !== 3) return null;
810
+ const base64 = parts[1].replace(/-/g, "+").replace(/_/g, "/");
811
+ const json = Buffer.from(base64, "base64").toString("utf-8");
812
+ return JSON.parse(json);
813
+ } catch {
814
+ return null;
815
+ }
816
+ }
817
+ function isTokenExpiringSoon(token, thresholdSeconds = 300) {
818
+ const payload = decodeJwtPayload(token);
819
+ if (!payload || !payload.exp) return false;
820
+ const nowSeconds = Math.floor(Date.now() / 1e3);
821
+ return payload.exp - nowSeconds < thresholdSeconds;
822
+ }
823
+ function getAuthenticatedAPI() {
370
824
  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();
825
+ let token = config.token;
826
+ if (!token) {
827
+ Logger.error("Not logged in.");
828
+ Logger.info("Run `mcphosting login` to authenticate.");
829
+ process.exit(1);
830
+ }
831
+ if (isTokenExpiringSoon(token) && !config.isEnvToken) {
832
+ if (config.refreshToken) {
833
+ Logger.warning("Token expiring soon. Use async auth for auto-refresh, or run `mcphosting login`.");
834
+ } else {
835
+ Logger.warning("Token is expiring soon. Run `mcphosting login` to re-authenticate.");
388
836
  }
389
- });
390
- return [login2, logout2, whoami2];
837
+ }
838
+ const api = new MCPHostingAPI(token);
839
+ return { api, config };
840
+ }
841
+ function isAuthenticated() {
842
+ const config = new Config();
843
+ return !!config.token;
391
844
  }
392
845
 
393
- // src/commands/connect.ts
394
- var import_commander2 = require("commander");
395
-
396
- // src/lib/clients.ts
846
+ // src/lib/detector.ts
397
847
  var import_promises = require("fs/promises");
398
848
  var import_path = require("path");
399
- var import_os = require("os");
849
+ async function detectMCP(dir) {
850
+ try {
851
+ const files = await (0, import_promises.readdir)(dir);
852
+ const configFiles = files.filter(
853
+ (f) => ["mcp.json", "mcp.config.json", "mcp.yaml", ".mcprc", "mcp.config.ts", "mcp.config.js"].includes(f)
854
+ );
855
+ let packageJson = null;
856
+ let hasMcpSdk = false;
857
+ let runtime = "unknown";
858
+ let entryPoint;
859
+ if (files.includes("package.json")) {
860
+ try {
861
+ const content = await (0, import_promises.readFile)((0, import_path.join)(dir, "package.json"), "utf-8");
862
+ packageJson = JSON.parse(content);
863
+ hasMcpSdk = !!(packageJson?.dependencies?.["@modelcontextprotocol/sdk"] || packageJson?.devDependencies?.["@modelcontextprotocol/sdk"]);
864
+ if (hasMcpSdk || packageJson?.dependencies?.["@modelcontextprotocol/sdk"]) {
865
+ runtime = "node";
866
+ }
867
+ if (packageJson?.main) {
868
+ entryPoint = packageJson.main;
869
+ } else if (packageJson?.bin) {
870
+ const bins = typeof packageJson.bin === "string" ? packageJson.bin : Object.values(packageJson.bin)[0];
871
+ entryPoint = bins;
872
+ }
873
+ } catch {
874
+ }
875
+ }
876
+ if (files.includes("pyproject.toml") || files.includes("requirements.txt") || files.includes("setup.py")) {
877
+ try {
878
+ let pythonDeps = "";
879
+ if (files.includes("requirements.txt")) {
880
+ pythonDeps = await (0, import_promises.readFile)((0, import_path.join)(dir, "requirements.txt"), "utf-8");
881
+ } else if (files.includes("pyproject.toml")) {
882
+ pythonDeps = await (0, import_promises.readFile)((0, import_path.join)(dir, "pyproject.toml"), "utf-8");
883
+ }
884
+ if (pythonDeps.includes("mcp") || pythonDeps.includes("modelcontextprotocol")) {
885
+ hasMcpSdk = true;
886
+ runtime = "python";
887
+ }
888
+ } catch {
889
+ }
890
+ if (files.includes("server.py")) entryPoint = "server.py";
891
+ else if (files.includes("main.py")) entryPoint = "main.py";
892
+ else if (files.includes("app.py")) entryPoint = "app.py";
893
+ }
894
+ if (!hasMcpSdk && configFiles.length === 0) {
895
+ const sourceFiles = files.filter((f) => f.endsWith(".ts") || f.endsWith(".js"));
896
+ for (const sf of sourceFiles.slice(0, 5)) {
897
+ try {
898
+ const content = await (0, import_promises.readFile)((0, import_path.join)(dir, sf), "utf-8");
899
+ if (content.includes("@modelcontextprotocol/sdk") || content.includes("McpServer") || content.includes("mcp.server")) {
900
+ hasMcpSdk = true;
901
+ runtime = sf.endsWith(".py") ? "python" : "node";
902
+ if (!entryPoint) entryPoint = sf;
903
+ break;
904
+ }
905
+ } catch {
906
+ }
907
+ }
908
+ }
909
+ if (!hasMcpSdk && configFiles.length === 0) {
910
+ return null;
911
+ }
912
+ let mcpConfig = null;
913
+ if (configFiles.length > 0) {
914
+ try {
915
+ const configContent = await (0, import_promises.readFile)((0, import_path.join)(dir, configFiles[0]), "utf-8");
916
+ mcpConfig = JSON.parse(configContent);
917
+ } catch {
918
+ }
919
+ }
920
+ const name = packageJson?.name || (0, import_path.basename)(dir);
921
+ return {
922
+ name,
923
+ hasMcpSdk,
924
+ configFiles,
925
+ packageJson,
926
+ mcpConfig,
927
+ runtime,
928
+ entryPoint
929
+ };
930
+ } catch {
931
+ return null;
932
+ }
933
+ }
934
+
935
+ // src/lib/clients.ts
400
936
  var import_promises2 = require("fs/promises");
937
+ var import_path2 = require("path");
938
+ var import_os = require("os");
939
+ var import_promises3 = require("fs/promises");
401
940
  var ClientManager = class {
402
941
  static getClientPaths() {
403
942
  const home = (0, import_os.homedir)();
404
943
  const platform = process.platform;
405
944
  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"),
945
+ claude: platform === "win32" ? (0, import_path2.join)(process.env.APPDATA || "", "Claude", "claude_desktop_config.json") : (0, import_path2.join)(home, "Library", "Application Support", "Claude", "claude_desktop_config.json"),
946
+ cursor: (0, import_path2.join)(home, ".cursor", "mcp.json"),
947
+ vscode: (0, import_path2.join)(process.cwd(), ".vscode", "mcp.json"),
948
+ openclaw: (0, import_path2.join)(home, ".openclaw", "mcp.json"),
410
949
  chatgpt: ""
411
950
  // Web-based, no local config
412
951
  };
@@ -418,7 +957,7 @@ var ClientManager = class {
418
957
  for (const [name, path] of Object.entries(paths)) {
419
958
  if (name === "chatgpt") continue;
420
959
  try {
421
- await (0, import_promises.access)(path);
960
+ await (0, import_promises2.access)(path);
422
961
  clients.push({ name, configPath: path, exists: true });
423
962
  } catch {
424
963
  clients.push({ name, configPath: path, exists: false });
@@ -444,14 +983,14 @@ var ClientManager = class {
444
983
  }
445
984
  const serverEntry = {
446
985
  command: "npx",
447
- args: ["-y", "@mcphosting/cli", "proxy", url],
986
+ args: ["-y", "mcphosting-cli", "proxy", url],
448
987
  env: {}
449
988
  };
450
989
  try {
451
- await (0, import_promises2.mkdir)((0, import_path.dirname)(configPath), { recursive: true });
990
+ await (0, import_promises3.mkdir)((0, import_path2.dirname)(configPath), { recursive: true });
452
991
  let config = { mcpServers: {} };
453
992
  try {
454
- const configContent = await (0, import_promises.readFile)(configPath, "utf-8");
993
+ const configContent = await (0, import_promises2.readFile)(configPath, "utf-8");
455
994
  config = JSON.parse(configContent);
456
995
  if (!config.mcpServers) {
457
996
  config.mcpServers = {};
@@ -459,7 +998,7 @@ var ClientManager = class {
459
998
  } catch {
460
999
  }
461
1000
  config.mcpServers[slug] = serverEntry;
462
- await (0, import_promises.writeFile)(configPath, JSON.stringify(config, null, 2));
1001
+ await (0, import_promises2.writeFile)(configPath, JSON.stringify(config, null, 2));
463
1002
  return true;
464
1003
  } catch (error) {
465
1004
  Logger.error(`Failed to configure ${client}: ${error}`);
@@ -482,11 +1021,11 @@ var ClientManager = class {
482
1021
  throw new Error(`Unsupported client: ${client}`);
483
1022
  }
484
1023
  try {
485
- const configContent = await (0, import_promises.readFile)(configPath, "utf-8");
1024
+ const configContent = await (0, import_promises2.readFile)(configPath, "utf-8");
486
1025
  const config = JSON.parse(configContent);
487
1026
  if (config.mcpServers && config.mcpServers[slug]) {
488
1027
  delete config.mcpServers[slug];
489
- await (0, import_promises.writeFile)(configPath, JSON.stringify(config, null, 2));
1028
+ await (0, import_promises2.writeFile)(configPath, JSON.stringify(config, null, 2));
490
1029
  return true;
491
1030
  }
492
1031
  return false;
@@ -533,21 +1072,360 @@ var ClientManager = class {
533
1072
  }
534
1073
  };
535
1074
 
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;
1075
+ // src/commands/deploy.ts
1076
+ var import_chalk3 = __toESM(require("chalk"), 1);
1077
+ var TEMPLATES = ["weather", "crypto", "notion", "postgres", "blank"];
1078
+ function createDeployCommand() {
1079
+ return new import_commander3.Command("deploy").description("Deploy an MCP server to MCPHosting (one command)").option("--github <url>", "Deploy from a GitHub repository URL").option("--name <name>", "Server name (auto-detected if not provided)").option("--template <name>", "Deploy from a template (weather, crypto, notion, postgres, blank)").option("--api-url <url>", "Your MCP server's API URL (for external servers)").option("--auth <type>", "Auth type: none, api_key, or oauth", "none").option("--auto-key", "Automatically create an API key", true).option("--no-auto-key", "Skip automatic API key creation").option("--configure", "Automatically configure detected AI clients", false).option("--json", "Output result as JSON").option("--silent", "Suppress interactive output").action(async (options) => {
1080
+ const isJson = options.json || Logger.isJsonMode;
1081
+ const isSilent = options.silent || Logger.isSilent;
1082
+ const { api, config } = getAuthenticatedAPI();
1083
+ if (!isJson && !isSilent) {
1084
+ console.log("");
1085
+ console.log(import_chalk3.default.bold("\u{1F680} MCPHosting Deploy"));
1086
+ console.log(import_chalk3.default.dim(" One command. Your MCP server goes live."));
1087
+ console.log("");
1088
+ }
1089
+ if (options.template) {
1090
+ const template = options.template.toLowerCase();
1091
+ if (!TEMPLATES.includes(template)) {
1092
+ Logger.error(`Unknown template: ${template}`);
1093
+ console.log("");
1094
+ Logger.info("Available templates:");
1095
+ for (const t of TEMPLATES) {
1096
+ console.log(` ${import_chalk3.default.cyan(t)}`);
1097
+ }
1098
+ process.exit(1);
1099
+ }
1100
+ const name2 = options.name || `mcp-${template}`;
1101
+ const spinner2 = Logger.spinner(`Deploying template ${import_chalk3.default.cyan(template)}...`);
1102
+ try {
1103
+ const result = await api.oneClickDeploy({
1104
+ name: name2,
1105
+ template,
1106
+ authType: options.auth,
1107
+ autoKey: options.autoKey
1108
+ });
1109
+ spinner2.succeed(`Template ${import_chalk3.default.cyan(template)} deployed!`);
1110
+ if (isJson) {
1111
+ Logger.json(result);
1112
+ } else {
1113
+ printOneClickResult(result, options.configure);
1114
+ }
1115
+ if (options.configure && result.connectionUrl) {
1116
+ await autoConfigureClients(result.slug, result.connectionUrl);
1117
+ }
1118
+ } catch (error) {
1119
+ spinner2.fail("Deploy failed");
1120
+ Logger.error(error.message);
1121
+ process.exit(1);
1122
+ }
1123
+ return;
1124
+ }
1125
+ if (options.github) {
1126
+ const githubUrl2 = options.github;
1127
+ const name2 = options.name || extractRepoName(githubUrl2);
1128
+ const spinner2 = Logger.spinner(`Deploying from ${import_chalk3.default.cyan(githubUrl2)}...`);
1129
+ try {
1130
+ const result = await api.oneClickDeploy({
1131
+ name: name2,
1132
+ githubUrl: githubUrl2,
1133
+ authType: options.auth,
1134
+ autoKey: options.autoKey
1135
+ });
1136
+ spinner2.succeed("Deployed from GitHub!");
1137
+ if (isJson) {
1138
+ Logger.json(result);
1139
+ } else {
1140
+ printOneClickResult(result, options.configure);
1141
+ }
1142
+ if (options.configure && result.connectionUrl) {
1143
+ await autoConfigureClients(result.slug, result.connectionUrl);
1144
+ }
1145
+ } catch (error) {
1146
+ spinner2.fail("Deploy failed");
1147
+ Logger.error(error.message);
1148
+ process.exit(1);
1149
+ }
1150
+ return;
1151
+ }
1152
+ if (options.apiUrl) {
1153
+ const name2 = options.name || new URL(options.apiUrl).hostname;
1154
+ const spinner2 = Logger.spinner(`Registering ${import_chalk3.default.cyan(name2)}...`);
1155
+ try {
1156
+ const result = await api.oneClickDeploy({
1157
+ name: name2,
1158
+ baseApiUrl: options.apiUrl,
1159
+ authType: options.auth,
1160
+ autoKey: options.autoKey
1161
+ });
1162
+ spinner2.succeed("Registered successfully!");
1163
+ if (isJson) {
1164
+ Logger.json(result);
1165
+ } else {
1166
+ printOneClickResult(result, options.configure);
1167
+ }
1168
+ if (options.configure && result.connectionUrl) {
1169
+ await autoConfigureClients(result.slug, result.connectionUrl);
1170
+ }
1171
+ } catch (error) {
1172
+ spinner2.fail("Registration failed");
1173
+ Logger.error(error.message);
1174
+ process.exit(1);
1175
+ }
1176
+ return;
1177
+ }
1178
+ const dir = process.cwd();
1179
+ let mcphostingConfig = null;
1180
+ try {
1181
+ const configContent = await (0, import_promises4.readFile)((0, import_path3.join)(dir, "mcphosting.json"), "utf-8");
1182
+ mcphostingConfig = JSON.parse(configContent);
1183
+ } catch {
1184
+ }
1185
+ const spinner = Logger.spinner("Detecting MCP server...");
1186
+ const detected = await detectMCP(dir);
1187
+ if (!detected && !mcphostingConfig) {
1188
+ spinner.fail("No MCP server detected");
1189
+ console.log("");
1190
+ Logger.info("This directory doesn't look like an MCP server project.");
1191
+ console.log("");
1192
+ Logger.info(import_chalk3.default.bold("Quick options:"));
1193
+ console.log(` ${import_chalk3.default.cyan("mcphosting deploy --template weather")} Deploy a template`);
1194
+ console.log(` ${import_chalk3.default.cyan("mcphosting deploy --github <url>")} Deploy from GitHub`);
1195
+ console.log(` ${import_chalk3.default.cyan("mcphosting deploy --api-url <url>")} Register external server`);
1196
+ console.log(` ${import_chalk3.default.cyan("mcphosting init")} Create mcphosting.json`);
1197
+ console.log("");
1198
+ process.exit(1);
1199
+ }
1200
+ const name = options.name || mcphostingConfig?.name || detected?.name || "my-mcp-server";
1201
+ spinner.succeed(`Detected: ${import_chalk3.default.bold(name)}`);
1202
+ if (detected) {
1203
+ Logger.info(` SDK: ${detected.hasMcpSdk ? import_chalk3.default.green("\u2713") : import_chalk3.default.yellow("\u25CB")} @modelcontextprotocol/sdk`);
1204
+ if (detected.runtime !== "unknown") {
1205
+ Logger.info(` Runtime: ${detected.runtime}`);
1206
+ }
1207
+ }
1208
+ let githubUrl = mcphostingConfig?.github || null;
1209
+ if (!githubUrl) {
1210
+ try {
1211
+ const { execSync } = await import("child_process");
1212
+ const remoteUrl = execSync("git remote get-url origin", { cwd: dir, encoding: "utf-8" }).trim();
1213
+ if (remoteUrl.includes("github.com")) {
1214
+ githubUrl = remoteUrl.replace(/^git@github\.com:/, "https://github.com/").replace(/\.git$/, "");
1215
+ }
1216
+ } catch {
1217
+ }
1218
+ }
1219
+ if (githubUrl) {
1220
+ Logger.info(` GitHub: ${import_chalk3.default.dim(githubUrl)}`);
1221
+ }
1222
+ console.log("");
1223
+ const deploySpinner = Logger.spinner(`Deploying ${import_chalk3.default.bold(name)}...`);
1224
+ try {
1225
+ const result = await api.oneClickDeploy({
1226
+ name,
1227
+ slug: mcphostingConfig?.slug,
1228
+ githubUrl: githubUrl || void 0,
1229
+ authType: options.auth,
1230
+ autoKey: options.autoKey,
1231
+ vercelProjectUrl: mcphostingConfig?.vercelUrl
1232
+ });
1233
+ deploySpinner.succeed("Deployed successfully!");
1234
+ if (mcphostingConfig) {
1235
+ try {
1236
+ mcphostingConfig.connectionUrl = result.connectionUrl;
1237
+ mcphostingConfig.projectId = result.projectId;
1238
+ mcphostingConfig.slug = result.slug;
1239
+ await (0, import_promises4.writeFile)((0, import_path3.join)(dir, "mcphosting.json"), JSON.stringify(mcphostingConfig, null, 2) + "\n");
1240
+ } catch {
1241
+ }
1242
+ }
1243
+ if (isJson) {
1244
+ Logger.json(result);
1245
+ } else {
1246
+ printOneClickResult(result, options.configure);
1247
+ }
1248
+ if (options.configure && result.connectionUrl) {
1249
+ await autoConfigureClients(result.slug, result.connectionUrl);
1250
+ }
1251
+ } catch (error) {
1252
+ deploySpinner.fail("Deploy failed");
1253
+ Logger.error(error.message);
1254
+ if (error.message.includes("Authentication") || error.message.includes("Unauthorized")) {
1255
+ Logger.info(`Run ${import_chalk3.default.cyan("mcphosting login")} first.`);
1256
+ }
1257
+ process.exit(1);
1258
+ }
1259
+ });
1260
+ }
1261
+ function extractRepoName(url) {
1262
+ const parts = url.replace(/\.git$/, "").split("/");
1263
+ return parts[parts.length - 1] || "mcp-server";
1264
+ }
1265
+ function printOneClickResult(result, showConfigureHint = false) {
1266
+ console.log("");
1267
+ console.log(import_chalk3.default.green.bold(" \u2705 Your MCP server is live!"));
1268
+ console.log("");
1269
+ Logger.info(` ${import_chalk3.default.dim("Endpoint:")} ${import_chalk3.default.blue(result.connectionUrl)}`);
1270
+ if (result.slug) {
1271
+ Logger.info(` ${import_chalk3.default.dim("Slug:")} ${import_chalk3.default.cyan(result.slug)}`);
1272
+ }
1273
+ if (result.status) {
1274
+ const statusColor = result.status === "deployed" ? import_chalk3.default.green : import_chalk3.default.yellow;
1275
+ Logger.info(` ${import_chalk3.default.dim("Status:")} ${statusColor(result.status)}`);
1276
+ }
1277
+ if (result.apiKey?.key) {
1278
+ console.log("");
1279
+ console.log(import_chalk3.default.bold(" \u{1F511} API Key (save this \u2014 shown only once):"));
1280
+ console.log(` ${import_chalk3.default.green(result.apiKey.key)}`);
1281
+ }
1282
+ if (result.vercelDeployUrl) {
1283
+ console.log("");
1284
+ console.log(import_chalk3.default.bold(" \u{1F310} Hosted on mcphosting.com infrastructure"));
1285
+ console.log(import_chalk3.default.dim(" Your server is live and managed by MCPHosting."));
1286
+ }
1287
+ if (result.clientConfig) {
1288
+ console.log("");
1289
+ console.log(import_chalk3.default.bold(" \u{1F4CB} Add to your AI client:"));
1290
+ console.log("");
1291
+ console.log(import_chalk3.default.dim(" Claude Desktop (claude_desktop_config.json):"));
1292
+ console.log(import_chalk3.default.dim(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
1293
+ console.log(import_chalk3.default.white(` ${JSON.stringify(result.clientConfig.claude, null, 2).split("\n").join("\n ")}`));
1294
+ console.log("");
1295
+ console.log(import_chalk3.default.dim(" Cursor / VS Code (.cursor/mcp.json):"));
1296
+ console.log(import_chalk3.default.dim(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
1297
+ console.log(import_chalk3.default.white(` ${JSON.stringify(result.clientConfig.cursor, null, 2).split("\n").join("\n ")}`));
1298
+ }
1299
+ console.log("");
1300
+ console.log(import_chalk3.default.dim(" Quick connect:"));
1301
+ console.log(import_chalk3.default.cyan(` mcphosting connect ${result.slug}`));
1302
+ if (showConfigureHint) {
1303
+ console.log("");
1304
+ console.log(import_chalk3.default.dim(" Auto-configure AI clients:"));
1305
+ console.log(import_chalk3.default.cyan(` mcphosting deploy --configure`));
1306
+ }
1307
+ console.log("");
1308
+ Logger.info(`Manage at ${import_chalk3.default.blue(`https://mcphosting.com/dashboard/servers/${result.projectId || result.slug}`)}`);
1309
+ console.log("");
1310
+ }
1311
+ async function autoConfigureClients(slug, url) {
1312
+ const clients = await ClientManager.detectInstalledClients();
1313
+ const installed = clients.filter((c) => c.exists);
1314
+ if (installed.length === 0) {
1315
+ Logger.info("No AI clients detected. Add the config manually (shown above).");
1316
+ return;
1317
+ }
1318
+ for (const client of installed) {
1319
+ try {
1320
+ const success = await ClientManager.addToClient(client.name, slug, url);
1321
+ if (success) {
1322
+ Logger.success(`Configured ${import_chalk3.default.bold(client.name)}`);
1323
+ }
1324
+ } catch (error) {
1325
+ Logger.warning(`Failed to configure ${client.name}: ${error.message}`);
1326
+ }
1327
+ }
1328
+ }
1329
+
1330
+ // src/commands/init.ts
1331
+ var import_commander4 = require("commander");
1332
+ var import_promises5 = require("fs/promises");
1333
+ var import_path4 = require("path");
1334
+ var import_chalk4 = __toESM(require("chalk"), 1);
1335
+ var TEMPLATES2 = [
1336
+ { name: "weather", description: "Weather data MCP server", repo: "gorlomi-enzo/mcp-template-weather" },
1337
+ { name: "crypto", description: "Crypto prices & portfolio MCP", repo: "gorlomi-enzo/mcp-template-crypto" },
1338
+ { name: "notion", description: "Notion integration MCP", repo: "gorlomi-enzo/mcp-template-notion" },
1339
+ { name: "postgres", description: "PostgreSQL query MCP", repo: "gorlomi-enzo/mcp-template-postgres" },
1340
+ { name: "blank", description: "Minimal MCP server scaffold", repo: "gorlomi-enzo/mcp-template-blank" }
1341
+ ];
1342
+ function createInitCommand() {
1343
+ return new import_commander4.Command("init").description("Initialize mcphosting.json in the current directory").option("--name <name>", "Project name").option("--template <template>", "Initialize from a template").option("--list-templates", "List available templates").action(async (options) => {
1344
+ console.log("");
1345
+ console.log(import_chalk4.default.bold("\u{1F4E6} MCPHosting Init"));
1346
+ console.log("");
1347
+ if (options.listTemplates) {
1348
+ console.log(import_chalk4.default.bold("Available templates:"));
1349
+ console.log("");
1350
+ for (const t of TEMPLATES2) {
1351
+ console.log(` ${import_chalk4.default.cyan(t.name.padEnd(12))} ${import_chalk4.default.dim(t.description)}`);
1352
+ console.log(` ${import_chalk4.default.dim(` github.com/${t.repo}`)}`);
1353
+ console.log("");
1354
+ }
1355
+ console.log(import_chalk4.default.dim(`Usage: mcphosting init --template <name>`));
1356
+ return;
1357
+ }
1358
+ const dir = process.cwd();
1359
+ const configPath = (0, import_path4.join)(dir, "mcphosting.json");
1360
+ try {
1361
+ await (0, import_promises5.access)(configPath);
1362
+ Logger.warning("mcphosting.json already exists in this directory.");
1363
+ Logger.info(`Edit it manually or delete it and run ${import_chalk4.default.cyan("mcphosting init")} again.`);
1364
+ return;
1365
+ } catch {
1366
+ }
1367
+ const detected = await detectMCP(dir);
1368
+ const projectName = options.name || detected?.name || (0, import_path4.basename)(dir);
1369
+ const slug = projectName.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 60);
1370
+ let githubUrl = null;
1371
+ try {
1372
+ const { execSync } = await import("child_process");
1373
+ const remoteUrl = execSync("git remote get-url origin", { cwd: dir, encoding: "utf-8" }).trim();
1374
+ if (remoteUrl.includes("github.com")) {
1375
+ githubUrl = remoteUrl.replace(/^git@github\.com:/, "https://github.com/").replace(/\.git$/, "");
1376
+ }
1377
+ } catch {
1378
+ }
1379
+ const config = {
1380
+ $schema: "https://mcphosting.com/schema/mcphosting.json",
1381
+ name: projectName,
1382
+ slug,
1383
+ version: "1.0.0",
1384
+ server: {
1385
+ transport: "streamable-http",
1386
+ port: 3e3,
1387
+ path: "/mcp"
1388
+ }
1389
+ };
1390
+ if (githubUrl) {
1391
+ config.github = githubUrl;
1392
+ }
1393
+ if (options.template) {
1394
+ config.template = options.template;
1395
+ }
1396
+ await (0, import_promises5.writeFile)(configPath, JSON.stringify(config, null, 2) + "\n");
1397
+ Logger.success(`Created ${import_chalk4.default.bold("mcphosting.json")}`);
1398
+ console.log("");
1399
+ console.log(import_chalk4.default.dim(JSON.stringify(config, null, 2)));
1400
+ console.log("");
1401
+ if (detected?.hasMcpSdk) {
1402
+ Logger.info(`Detected MCP SDK (${detected.runtime})`);
1403
+ }
1404
+ if (githubUrl) {
1405
+ Logger.info(`GitHub: ${import_chalk4.default.blue(githubUrl)}`);
1406
+ }
1407
+ console.log("");
1408
+ Logger.info(`Next: ${import_chalk4.default.cyan("mcphosting deploy")} to deploy your MCP server`);
1409
+ console.log("");
1410
+ });
1411
+ }
1412
+
1413
+ // src/commands/connect.ts
1414
+ var import_commander5 = require("commander");
1415
+ var import_chalk5 = __toESM(require("chalk"), 1);
1416
+ function createConnectCommand() {
1417
+ return new import_commander5.Command("connect").description("Connect an MCP server to your AI clients").argument("<url-or-slug>", "MCP server URL or slug (e.g. github, notion, https://my-mcp.com)").option("--client <client>", "Target specific client: claude, cursor, vscode, openclaw, chatgpt").option("--name <name>", "Custom name for the connection").action(async (urlOrSlug, options) => {
1418
+ const config = new Config();
1419
+ const spinner = Logger.spinner("Setting up MCP connection...");
1420
+ try {
1421
+ const url = ClientManager.resolveUrl(urlOrSlug);
1422
+ const slug = ClientManager.extractSlug(url);
1423
+ const name = options.name || slug;
1424
+ const existing = config.findConnection(slug);
1425
+ if (existing) {
1426
+ spinner.warn(`Already connected to ${import_chalk5.default.cyan(slug)}`);
1427
+ Logger.info(`Use ${import_chalk5.default.cyan(`mcphosting disconnect ${slug}`)} to remove first`);
1428
+ return;
551
1429
  }
552
1430
  const clients = [];
553
1431
  if (options.client) {
@@ -555,7 +1433,7 @@ function createConnectCommands() {
555
1433
  const success = await ClientManager.addToClient(clientName, slug, url);
556
1434
  if (success) {
557
1435
  clients.push(clientName);
558
- spinner.succeed(`Connected to ${clientName}`);
1436
+ spinner.succeed(`Connected ${import_chalk5.default.bold(name)} to ${import_chalk5.default.green(clientName)}`);
559
1437
  } else {
560
1438
  spinner.fail(`Failed to connect to ${clientName}`);
561
1439
  process.exit(1);
@@ -564,9 +1442,9 @@ function createConnectCommands() {
564
1442
  const detectedClients = await ClientManager.detectInstalledClients();
565
1443
  const availableClients = detectedClients.filter((c) => c.exists);
566
1444
  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");
1445
+ spinner.warn("No supported AI clients detected");
1446
+ Logger.info("Supported: Claude Desktop, Cursor, VS Code, OpenClaw");
1447
+ Logger.info(`Use ${import_chalk5.default.cyan("--client chatgpt")} for web-based setup`);
570
1448
  return;
571
1449
  }
572
1450
  spinner.text = `Configuring ${availableClients.length} client${availableClients.length > 1 ? "s" : ""}...`;
@@ -584,86 +1462,470 @@ function createConnectCommands() {
584
1462
  Logger.warning(`Failed to configure ${client.name}: ${error}`);
585
1463
  }
586
1464
  }
587
- spinner.succeed(`Connected to ${clients.length} client${clients.length > 1 ? "s" : ""}`);
1465
+ spinner.succeed(`Connected ${import_chalk5.default.bold(name)} to ${clients.length} client${clients.length > 1 ? "s" : ""}`);
588
1466
  }
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"));
1467
+ config.addConnection({ slug, url, clients });
1468
+ console.log("");
1469
+ Logger.info(` URL: ${import_chalk5.default.dim(url)}`);
1470
+ Logger.info(` Clients: ${import_chalk5.default.dim(clients.join(", "))}`);
1471
+ console.log("");
1472
+ console.log(import_chalk5.default.green("\u{1F389} Share with your team:"));
1473
+ console.log(import_chalk5.default.cyan(` npx mcphosting-cli connect ${urlOrSlug}`));
600
1474
  console.log("");
601
1475
  } catch (error) {
602
1476
  spinner.fail("Connection failed");
603
- Logger.error(`Error: ${error}`);
1477
+ Logger.error(error.message);
604
1478
  process.exit(1);
605
1479
  }
606
1480
  });
607
- return connect;
608
1481
  }
609
1482
  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) => {
1483
+ return new import_commander5.Command("disconnect").description("Disconnect an MCP server").argument("<slug-or-id>", "MCP server slug or connection ID").action(async (slugOrId) => {
611
1484
  const config = new Config();
612
1485
  const connection = config.findConnection(slugOrId);
613
1486
  if (!connection) {
614
1487
  Logger.error(`Connection not found: ${slugOrId}`);
615
- Logger.info("Use `mcphost list` to see active connections");
1488
+ Logger.info(`Use ${import_chalk5.default.cyan("mcphosting list")} to see active connections`);
616
1489
  return;
617
1490
  }
618
1491
  const spinner = Logger.spinner(`Disconnecting ${connection.slug}...`);
619
1492
  try {
620
1493
  const removedFrom = await ClientManager.removeFromAllClients(connection.slug);
621
1494
  config.removeConnection(connection.id);
622
- spinner.succeed(`Disconnected ${connection.slug}`);
1495
+ spinner.succeed(`Disconnected ${import_chalk5.default.bold(connection.slug)}`);
623
1496
  if (removedFrom.length > 0) {
624
1497
  Logger.info(`Removed from: ${removedFrom.join(", ")}`);
625
1498
  }
626
1499
  } catch (error) {
627
1500
  spinner.fail("Disconnect failed");
628
- Logger.error(`Error: ${error}`);
1501
+ Logger.error(error.message);
629
1502
  }
630
1503
  });
631
1504
  }
1505
+
1506
+ // src/commands/list.ts
1507
+ var import_commander6 = require("commander");
1508
+ var import_chalk6 = __toESM(require("chalk"), 1);
632
1509
  function createListCommand() {
633
- return new import_commander2.Command("list").description("List connected MCP servers").option("--json", "Output as JSON").action(async (options) => {
1510
+ return new import_commander6.Command("list").description("List your MCP servers").option("--local", "Show locally connected servers only").option("--remote", "Show deployed servers only (requires login)").option("--json", "Output as JSON").action(async (options) => {
1511
+ const isJson = options.json || Logger.isJsonMode;
1512
+ const showLocal = options.local || !options.local && !options.remote;
1513
+ const showRemote = options.remote || !options.local && !options.remote;
1514
+ if (showLocal) {
1515
+ const config = new Config();
1516
+ const connections = config.connections;
1517
+ if (connections.length > 0) {
1518
+ if (isJson && !showRemote) {
1519
+ Logger.json(connections);
1520
+ return;
1521
+ }
1522
+ console.log("");
1523
+ Logger.bold(`\u{1F4CB} Connected MCP Servers (${connections.length})`);
1524
+ console.log("");
1525
+ const tableData = connections.map((conn) => ({
1526
+ Slug: conn.slug,
1527
+ URL: conn.url.length > 50 ? conn.url.slice(0, 47) + "..." : conn.url,
1528
+ Clients: conn.clients.join(", "),
1529
+ Added: new Date(conn.addedAt).toLocaleDateString()
1530
+ }));
1531
+ Logger.table(tableData);
1532
+ console.log("");
1533
+ } else if (!showRemote) {
1534
+ Logger.info("No local MCP connections.");
1535
+ Logger.dim(`Use ${import_chalk6.default.cyan("mcphosting connect <slug>")} to connect one.`);
1536
+ return;
1537
+ }
1538
+ }
1539
+ if (showRemote && isAuthenticated()) {
1540
+ const { api } = getAuthenticatedAPI();
1541
+ const spinner = Logger.spinner("Fetching deployed servers...");
1542
+ try {
1543
+ const servers = await api.listServers();
1544
+ spinner.stop();
1545
+ if (servers.length === 0) {
1546
+ if (!showLocal) {
1547
+ Logger.info("No deployed servers yet.");
1548
+ Logger.dim(`Use ${import_chalk6.default.cyan("mcphosting deploy")} to deploy your first MCP server.`);
1549
+ }
1550
+ return;
1551
+ }
1552
+ if (isJson) {
1553
+ Logger.json(servers);
1554
+ return;
1555
+ }
1556
+ Logger.bold(`\u{1F5A5}\uFE0F Deployed Servers (${servers.length})`);
1557
+ console.log("");
1558
+ const tableData = servers.map((s) => ({
1559
+ Name: s.name || s.slug,
1560
+ Status: s.status === "active" ? import_chalk6.default.green("\u25CF active") : s.status === "deploying" ? import_chalk6.default.yellow("\u25D0 deploying") : s.status === "stopped" ? import_chalk6.default.dim("\u25CB stopped") : import_chalk6.default.red("\u2717 " + s.status),
1561
+ Slug: s.slug,
1562
+ URL: (s.url || "").length > 45 ? (s.url || "").slice(0, 42) + "..." : s.url || ""
1563
+ }));
1564
+ Logger.table(tableData);
1565
+ console.log("");
1566
+ } catch (error) {
1567
+ spinner.fail("Failed to fetch servers");
1568
+ Logger.error(error.message);
1569
+ }
1570
+ } else if (showRemote && !isAuthenticated()) {
1571
+ Logger.dim(`Log in to see deployed servers: ${import_chalk6.default.cyan("mcphosting login")}`);
1572
+ console.log("");
1573
+ }
1574
+ });
1575
+ }
1576
+
1577
+ // src/commands/status.ts
1578
+ var import_commander7 = require("commander");
1579
+ var import_chalk7 = __toESM(require("chalk"), 1);
1580
+ function createStatusCommand() {
1581
+ return new import_commander7.Command("status").description("Check the status of a deployed MCP server").argument("<slug-or-id>", "Server slug or ID").option("--json", "Output as JSON").action(async (slugOrId, options) => {
1582
+ const isJson = options.json || Logger.isJsonMode;
1583
+ const { api } = getAuthenticatedAPI();
1584
+ const spinner = Logger.spinner(`Checking status of ${import_chalk7.default.cyan(slugOrId)}...`);
1585
+ try {
1586
+ const server = await api.getServer(slugOrId);
1587
+ spinner.stop();
1588
+ if (isJson) {
1589
+ Logger.json(server);
1590
+ return;
1591
+ }
1592
+ console.log("");
1593
+ console.log(import_chalk7.default.bold(`\u{1F4E1} ${server.name || server.slug}`));
1594
+ console.log("");
1595
+ const statusIcon = server.status === "active" ? import_chalk7.default.green("\u25CF Active") : server.status === "deploying" ? import_chalk7.default.yellow("\u25D0 Deploying") : server.status === "stopped" ? import_chalk7.default.dim("\u25CB Stopped") : import_chalk7.default.red("\u2717 " + server.status);
1596
+ const info = [
1597
+ { Property: "Status", Value: statusIcon },
1598
+ { Property: "Slug", Value: server.slug },
1599
+ { Property: "URL", Value: server.url || "\u2014" }
1600
+ ];
1601
+ if (server.sseUrl) {
1602
+ info.push({ Property: "SSE URL", Value: server.sseUrl });
1603
+ }
1604
+ if (server.streamableUrl) {
1605
+ info.push({ Property: "Streamable", Value: server.streamableUrl });
1606
+ }
1607
+ if (server.githubUrl) {
1608
+ info.push({ Property: "GitHub", Value: server.githubUrl });
1609
+ }
1610
+ info.push(
1611
+ { Property: "Created", Value: server.createdAt ? new Date(server.createdAt).toLocaleString() : "\u2014" },
1612
+ { Property: "Updated", Value: server.updatedAt ? new Date(server.updatedAt).toLocaleString() : "\u2014" }
1613
+ );
1614
+ Logger.table(info);
1615
+ if (server.tools && server.tools.length > 0) {
1616
+ console.log("");
1617
+ console.log(import_chalk7.default.yellow("\u{1F527} Tools:"));
1618
+ server.tools.forEach((tool) => {
1619
+ console.log(` \u2022 ${tool}`);
1620
+ });
1621
+ }
1622
+ if (server.envVars && server.envVars.length > 0) {
1623
+ console.log("");
1624
+ console.log(import_chalk7.default.yellow("\u{1F511} Environment Variables:"));
1625
+ server.envVars.forEach((env) => {
1626
+ console.log(` ${env.key} = ${env.masked ? "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022" : "(set)"}`);
1627
+ });
1628
+ }
1629
+ console.log("");
1630
+ } catch (error) {
1631
+ spinner.fail("Failed to get status");
1632
+ Logger.error(error.message);
1633
+ process.exit(1);
1634
+ }
1635
+ });
1636
+ }
1637
+
1638
+ // src/commands/logs.ts
1639
+ var import_commander8 = require("commander");
1640
+ var import_chalk8 = __toESM(require("chalk"), 1);
1641
+ function createLogsCommand() {
1642
+ return new import_commander8.Command("logs").description("View logs for a deployed MCP server").argument("<slug-or-id>", "Server slug or ID").option("-n, --lines <number>", "Number of log lines to show", "50").option("--json", "Output as JSON").action(async (slugOrId, options) => {
1643
+ const isJson = options.json || Logger.isJsonMode;
1644
+ const { api } = getAuthenticatedAPI();
1645
+ const lines = parseInt(options.lines);
1646
+ const spinner = Logger.spinner(`Fetching logs for ${import_chalk8.default.cyan(slugOrId)}...`);
1647
+ try {
1648
+ const logs = await api.getServerLogs(slugOrId, lines);
1649
+ spinner.stop();
1650
+ if (logs.length === 0) {
1651
+ if (isJson) {
1652
+ console.log(JSON.stringify({ success: true, logs: [] }));
1653
+ } else {
1654
+ Logger.info("No logs available yet.");
1655
+ }
1656
+ return;
1657
+ }
1658
+ if (isJson) {
1659
+ Logger.json(logs);
1660
+ return;
1661
+ }
1662
+ console.log("");
1663
+ console.log(import_chalk8.default.bold(`\u{1F4DC} Logs: ${slugOrId}`));
1664
+ console.log(import_chalk8.default.dim("\u2500".repeat(60)));
1665
+ console.log("");
1666
+ for (const entry of logs) {
1667
+ const time = import_chalk8.default.dim(new Date(entry.timestamp).toLocaleTimeString());
1668
+ const level = entry.level === "error" ? import_chalk8.default.red("ERR") : entry.level === "warn" ? import_chalk8.default.yellow("WRN") : entry.level === "debug" ? import_chalk8.default.dim("DBG") : import_chalk8.default.blue("INF");
1669
+ console.log(`${time} ${level} ${entry.message}`);
1670
+ }
1671
+ console.log("");
1672
+ console.log(import_chalk8.default.dim(`Showing last ${logs.length} entries`));
1673
+ console.log("");
1674
+ } catch (error) {
1675
+ spinner.fail("Failed to fetch logs");
1676
+ Logger.error(error.message);
1677
+ process.exit(1);
1678
+ }
1679
+ });
1680
+ }
1681
+
1682
+ // src/commands/env.ts
1683
+ var import_commander9 = require("commander");
1684
+ var import_chalk9 = __toESM(require("chalk"), 1);
1685
+ function createEnvCommand() {
1686
+ const env = new import_commander9.Command("env").description("Manage environment variables for a deployed MCP server");
1687
+ env.command("list").description("List environment variables").argument("<slug-or-id>", "Server slug or ID").option("--json", "Output as JSON").action(async (slugOrId, options) => {
1688
+ const { api } = getAuthenticatedAPI();
1689
+ const spinner = Logger.spinner("Fetching environment variables...");
1690
+ try {
1691
+ const envVars = await api.listEnvVars(slugOrId);
1692
+ spinner.stop();
1693
+ if (envVars.length === 0) {
1694
+ Logger.info("No environment variables set.");
1695
+ Logger.dim(`Add one: ${import_chalk9.default.cyan(`mcphosting env set ${slugOrId} KEY=value`)}`);
1696
+ return;
1697
+ }
1698
+ if (options.json) {
1699
+ Logger.json(envVars);
1700
+ return;
1701
+ }
1702
+ console.log("");
1703
+ Logger.bold(`\u{1F511} Environment Variables: ${slugOrId}`);
1704
+ console.log("");
1705
+ envVars.forEach((env2) => {
1706
+ console.log(` ${import_chalk9.default.cyan(env2.key)} = ${env2.masked ? import_chalk9.default.dim("\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022") : import_chalk9.default.dim("(set)")}`);
1707
+ });
1708
+ console.log("");
1709
+ } catch (error) {
1710
+ spinner.fail("Failed to fetch environment variables");
1711
+ Logger.error(error.message);
1712
+ process.exit(1);
1713
+ }
1714
+ });
1715
+ env.command("set").description("Set an environment variable (KEY=value)").argument("<slug-or-id>", "Server slug or ID").argument("<key-value>", "Environment variable in KEY=value format").action(async (slugOrId, keyValue) => {
1716
+ const eqIndex = keyValue.indexOf("=");
1717
+ if (eqIndex === -1) {
1718
+ Logger.error("Invalid format. Use KEY=value");
1719
+ Logger.dim(`Example: ${import_chalk9.default.cyan(`mcphosting env set ${slugOrId} API_KEY=sk-abc123`)}`);
1720
+ process.exit(1);
1721
+ }
1722
+ const key = keyValue.substring(0, eqIndex);
1723
+ const value = keyValue.substring(eqIndex + 1);
1724
+ if (!key) {
1725
+ Logger.error("Key cannot be empty.");
1726
+ process.exit(1);
1727
+ }
1728
+ const { api } = getAuthenticatedAPI();
1729
+ const spinner = Logger.spinner(`Setting ${import_chalk9.default.cyan(key)}...`);
1730
+ try {
1731
+ await api.addEnvVar(slugOrId, key, value);
1732
+ spinner.succeed(`Set ${import_chalk9.default.bold(key)} on ${import_chalk9.default.cyan(slugOrId)}`);
1733
+ } catch (error) {
1734
+ spinner.fail(`Failed to set ${key}`);
1735
+ Logger.error(error.message);
1736
+ process.exit(1);
1737
+ }
1738
+ });
1739
+ env.command("remove").description("Remove an environment variable").argument("<slug-or-id>", "Server slug or ID").argument("<key>", "Environment variable key to remove").action(async (slugOrId, key) => {
1740
+ const { api } = getAuthenticatedAPI();
1741
+ const spinner = Logger.spinner(`Removing ${import_chalk9.default.cyan(key)}...`);
1742
+ try {
1743
+ await api.removeEnvVar(slugOrId, key);
1744
+ spinner.succeed(`Removed ${import_chalk9.default.bold(key)} from ${import_chalk9.default.cyan(slugOrId)}`);
1745
+ } catch (error) {
1746
+ spinner.fail(`Failed to remove ${key}`);
1747
+ Logger.error(error.message);
1748
+ process.exit(1);
1749
+ }
1750
+ });
1751
+ return env;
1752
+ }
1753
+
1754
+ // src/commands/keys.ts
1755
+ var import_commander10 = require("commander");
1756
+ var import_chalk10 = __toESM(require("chalk"), 1);
1757
+ function createKeysCommand() {
1758
+ const keys = new import_commander10.Command("keys").description("Manage API keys");
1759
+ keys.command("list").description("List your API keys").option("--json", "Output as JSON").action(async (options) => {
1760
+ const { api } = getAuthenticatedAPI();
1761
+ const spinner = Logger.spinner("Fetching API keys...");
1762
+ try {
1763
+ const apiKeys = await api.getAPIKeys();
1764
+ spinner.stop();
1765
+ if (apiKeys.length === 0) {
1766
+ Logger.info("No API keys found.");
1767
+ Logger.dim(`Create one: ${import_chalk10.default.cyan('mcphosting keys create "My Key"')}`);
1768
+ return;
1769
+ }
1770
+ if (options.json) {
1771
+ Logger.json(apiKeys);
1772
+ return;
1773
+ }
1774
+ console.log("");
1775
+ Logger.bold(`\u{1F511} API Keys (${apiKeys.length})`);
1776
+ console.log("");
1777
+ const tableData = apiKeys.map((key) => ({
1778
+ Name: key.name,
1779
+ Prefix: key.prefix + "...",
1780
+ Created: new Date(key.createdAt).toLocaleDateString(),
1781
+ "Last Used": key.lastUsedAt ? new Date(key.lastUsedAt).toLocaleDateString() : "Never"
1782
+ }));
1783
+ Logger.table(tableData);
1784
+ console.log("");
1785
+ } catch (error) {
1786
+ spinner.fail("Failed to fetch API keys");
1787
+ Logger.error(error.message);
1788
+ process.exit(1);
1789
+ }
1790
+ });
1791
+ keys.command("create").description("Create a new API key").argument("<name>", "Name for the API key").action(async (name) => {
1792
+ const { api } = getAuthenticatedAPI();
1793
+ const spinner = Logger.spinner("Creating API key...");
1794
+ try {
1795
+ const key = await api.createAPIKey(name);
1796
+ spinner.succeed("API key created!");
1797
+ console.log("");
1798
+ Logger.bold("\u{1F511} New API Key");
1799
+ console.log("");
1800
+ Logger.info(` Name: ${import_chalk10.default.bold(key.name)}`);
1801
+ Logger.info(` Key: ${import_chalk10.default.green(key.key || key.prefix + "...")}`);
1802
+ Logger.info(` ID: ${import_chalk10.default.dim(key.id)}`);
1803
+ console.log("");
1804
+ Logger.warning("Save this key now \u2014 it won't be shown again!");
1805
+ console.log("");
1806
+ } catch (error) {
1807
+ spinner.fail("Failed to create API key");
1808
+ Logger.error(error.message);
1809
+ process.exit(1);
1810
+ }
1811
+ });
1812
+ keys.command("delete").description("Delete an API key").argument("<id>", "API key ID").action(async (id) => {
1813
+ const { api } = getAuthenticatedAPI();
1814
+ const spinner = Logger.spinner("Deleting API key...");
1815
+ try {
1816
+ await api.deleteAPIKey(id);
1817
+ spinner.succeed(`API key ${import_chalk10.default.dim(id)} deleted.`);
1818
+ } catch (error) {
1819
+ spinner.fail("Failed to delete API key");
1820
+ Logger.error(error.message);
1821
+ process.exit(1);
1822
+ }
1823
+ });
1824
+ return keys;
1825
+ }
1826
+
1827
+ // src/commands/search.ts
1828
+ var import_commander11 = require("commander");
1829
+ var import_chalk11 = __toESM(require("chalk"), 1);
1830
+ function createSearchCommand() {
1831
+ return new import_commander11.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) => {
634
1832
  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;
1833
+ const api = new MCPHostingAPI(config.token);
1834
+ const spinner = Logger.spinner(`Searching for "${query}"...`);
1835
+ try {
1836
+ const results = await api.searchMCPs(query);
1837
+ spinner.succeed(`Found ${results.length} MCP server(s)`);
1838
+ if (results.length === 0) {
1839
+ Logger.info("No MCP servers found.");
1840
+ Logger.dim("Try: github, slack, notion, stripe, postgres, filesystem");
1841
+ return;
1842
+ }
1843
+ const limit = parseInt(options.limit);
1844
+ const limitedResults = results.slice(0, limit);
1845
+ if (options.json) {
1846
+ Logger.json(limitedResults);
1847
+ return;
1848
+ }
1849
+ console.log("");
1850
+ Logger.bold("\u{1F50D} Marketplace Results");
1851
+ console.log("");
1852
+ limitedResults.forEach((server, index) => {
1853
+ console.log(import_chalk11.default.cyan(`${index + 1}. ${server.name}`));
1854
+ console.log(` ${import_chalk11.default.dim(server.description)}`);
1855
+ console.log(` ${import_chalk11.default.yellow("Slug:")} ${server.slug} | ${import_chalk11.default.yellow("Installs:")} ${server.installs.toLocaleString()} | ${import_chalk11.default.yellow("By:")} ${server.author}`);
1856
+ console.log(` ${import_chalk11.default.green("Tools:")} ${server.tools.join(", ")}`);
1857
+ console.log(` ${import_chalk11.default.blue("Connect:")} ${import_chalk11.default.dim(`mcphosting connect ${server.slug}`)}`);
1858
+ console.log("");
1859
+ });
1860
+ if (results.length > limit) {
1861
+ Logger.dim(`Showing ${limit} of ${results.length}. Use --limit for more.`);
1862
+ }
1863
+ } catch (error) {
1864
+ spinner.fail("Search failed");
1865
+ Logger.error(error.message);
1866
+ process.exit(1);
640
1867
  }
641
- if (options.json) {
642
- Logger.json(connections);
643
- return;
1868
+ });
1869
+ }
1870
+ function createInfoCommand() {
1871
+ return new import_commander11.Command("info").description("Show detailed information about an MCP server").argument("<slug>", "MCP server slug").option("--json", "Output as JSON").action(async (slug, options) => {
1872
+ const config = new Config();
1873
+ const api = new MCPHostingAPI(config.token);
1874
+ const spinner = Logger.spinner(`Getting info for ${slug}...`);
1875
+ try {
1876
+ const server = await api.getMCPInfo(slug);
1877
+ if (!server) {
1878
+ spinner.fail(`Not found: ${slug}`);
1879
+ Logger.info(`Use ${import_chalk11.default.cyan("mcphosting search <query>")} to find servers`);
1880
+ return;
1881
+ }
1882
+ spinner.stop();
1883
+ if (options.json) {
1884
+ Logger.json(server);
1885
+ return;
1886
+ }
1887
+ console.log("");
1888
+ console.log(import_chalk11.default.bold.cyan(`\u{1F4E6} ${server.name}`));
1889
+ console.log(import_chalk11.default.dim(server.description));
1890
+ console.log("");
1891
+ const info = [
1892
+ { Property: "Slug", Value: server.slug },
1893
+ { Property: "Author", Value: server.author },
1894
+ { Property: "Installs", Value: server.installs.toLocaleString() },
1895
+ { Property: "Tools", Value: server.tools.length.toString() }
1896
+ ];
1897
+ if (server.url) {
1898
+ info.push({ Property: "URL", Value: server.url });
1899
+ }
1900
+ Logger.table(info);
1901
+ console.log("");
1902
+ console.log(import_chalk11.default.yellow("\u{1F527} Available Tools:"));
1903
+ server.tools.forEach((tool) => console.log(` \u2022 ${tool}`));
1904
+ console.log("");
1905
+ console.log(import_chalk11.default.green("\u{1F4A1} Quick Start:"));
1906
+ console.log(import_chalk11.default.dim(` mcphosting connect ${server.slug}`));
1907
+ const connection = config.findConnection(slug);
1908
+ if (connection) {
1909
+ console.log("");
1910
+ console.log(import_chalk11.default.blue("\u2139\uFE0F Already connected to:"), connection.clients.join(", "));
1911
+ }
1912
+ console.log("");
1913
+ } catch (error) {
1914
+ spinner.fail("Failed to get info");
1915
+ Logger.error(error.message);
1916
+ process.exit(1);
644
1917
  }
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
1918
  });
657
1919
  }
658
1920
 
659
1921
  // src/commands/import.ts
660
- var import_commander3 = require("commander");
661
- var import_promises3 = require("fs/promises");
662
- var import_path2 = require("path");
1922
+ var import_commander12 = require("commander");
1923
+ var import_promises6 = require("fs/promises");
1924
+ var import_path5 = require("path");
663
1925
  var import_os2 = require("os");
664
- var import_chalk3 = __toESM(require("chalk"), 1);
1926
+ var import_chalk12 = __toESM(require("chalk"), 1);
665
1927
  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) => {
1928
+ return new import_commander12.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
1929
  if (options.from !== "smithery") {
668
1930
  Logger.error("Only Smithery import is currently supported");
669
1931
  Logger.info("Usage: mcphost import --from smithery");
@@ -678,16 +1940,16 @@ async function importFromSmitery(options) {
678
1940
  try {
679
1941
  const smitheryPaths = [
680
1942
  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")
1943
+ (0, import_path5.join)((0, import_os2.homedir)(), ".smithery", "config.json"),
1944
+ (0, import_path5.join)((0, import_os2.homedir)(), ".config", "smithery", "config.json"),
1945
+ (0, import_path5.join)((0, import_os2.homedir)(), "Library", "Application Support", "smithery", "config.json"),
1946
+ (0, import_path5.join)(process.cwd(), "smithery.json"),
1947
+ (0, import_path5.join)(process.cwd(), ".smithery.json")
686
1948
  ].filter(Boolean);
687
1949
  let smitheryConfigPath = null;
688
1950
  for (const path of smitheryPaths) {
689
1951
  try {
690
- await (0, import_promises3.access)(path);
1952
+ await (0, import_promises6.access)(path);
691
1953
  smitheryConfigPath = path;
692
1954
  break;
693
1955
  } catch {
@@ -703,7 +1965,7 @@ async function importFromSmitery(options) {
703
1965
  }
704
1966
  spinner.succeed(`Found Smithery config: ${smitheryConfigPath}`);
705
1967
  const loadSpinner = Logger.spinner("Reading Smithery config...");
706
- const configContent = await (0, import_promises3.readFile)(smitheryConfigPath, "utf-8");
1968
+ const configContent = await (0, import_promises6.readFile)(smitheryConfigPath, "utf-8");
707
1969
  const smitheryConfig = JSON.parse(configContent);
708
1970
  const servers = {
709
1971
  ...smitheryConfig.servers,
@@ -716,14 +1978,14 @@ async function importFromSmitery(options) {
716
1978
  const serverEntries = Object.entries(servers);
717
1979
  loadSpinner.succeed(`Found ${serverEntries.length} MCP server(s) in Smithery config`);
718
1980
  if (options.dryRun) {
719
- console.log("\n" + import_chalk3.default.bold("\u{1F50D} Preview: Would import the following MCP servers:"));
1981
+ console.log("\n" + import_chalk12.default.bold("\u{1F50D} Preview: Would import the following MCP servers:"));
720
1982
  console.log("");
721
1983
  serverEntries.forEach(([slug, serverConfig]) => {
722
1984
  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)}`);
1985
+ console.log(`\u2022 ${import_chalk12.default.cyan(slug)}`);
1986
+ console.log(` URL: ${import_chalk12.default.dim(url)}`);
725
1987
  if (serverConfig.command) {
726
- console.log(` Command: ${import_chalk3.default.dim(serverConfig.command + " " + (serverConfig.args?.join(" ") || ""))}`);
1988
+ console.log(` Command: ${import_chalk12.default.dim(serverConfig.command + " " + (serverConfig.args?.join(" ") || ""))}`);
727
1989
  }
728
1990
  console.log("");
729
1991
  });
@@ -743,7 +2005,7 @@ async function importFromSmitery(options) {
743
2005
  }
744
2006
  let url = serverConfig.url;
745
2007
  if (!url) {
746
- if (serverConfig.command === "npx" && serverConfig.args?.[0] === "@mcphosting/cli") {
2008
+ if (serverConfig.command === "npx" && serverConfig.args?.[0] === "mcphosting-cli") {
747
2009
  url = serverConfig.args[2];
748
2010
  } else {
749
2011
  url = `https://${slug}.mcphost.dev`;
@@ -788,9 +2050,9 @@ async function importFromSmitery(options) {
788
2050
  }
789
2051
  console.log("");
790
2052
  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"));
2053
+ console.log("\n" + import_chalk12.default.green("\u{1F389} Welcome to MCPHosting! Share with your team:"));
2054
+ console.log(import_chalk12.default.cyan("npx mcphosting-cli import --from smithery"));
2055
+ console.log("\n" + import_chalk12.default.yellow("\u2B50 Star us: ") + import_chalk12.default.blue("https://github.com/gorlomi-enzo/mcphosting-cli"));
794
2056
  console.log("");
795
2057
  } catch (error) {
796
2058
  spinner.fail("Import failed");
@@ -803,9 +2065,9 @@ async function importFromSmitery(options) {
803
2065
  }
804
2066
 
805
2067
  // src/commands/proxy.ts
806
- var import_commander4 = require("commander");
2068
+ var import_commander13 = require("commander");
807
2069
  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) => {
2070
+ return new import_commander13.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
2071
  if (!options.quiet) {
810
2072
  console.error("\u{1F517} Proxying via mcphosting.com");
811
2073
  console.error(`\u{1F4E1} Remote server: ${url}`);
@@ -872,7 +2134,7 @@ async function forwardMessage(url, message, quiet) {
872
2134
  headers: {
873
2135
  "Content-Type": "application/json",
874
2136
  "Accept": "application/json",
875
- "User-Agent": "@mcphosting/cli-proxy"
2137
+ "User-Agent": "mcphosting-cli-proxy"
876
2138
  },
877
2139
  body: message
878
2140
  });
@@ -902,279 +2164,478 @@ async function forwardMessage(url, message, quiet) {
902
2164
  }
903
2165
  }
904
2166
 
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) => {
2167
+ // src/commands/whoami.ts
2168
+ var import_commander14 = require("commander");
2169
+ var import_chalk13 = __toESM(require("chalk"), 1);
2170
+ function createWhoamiCommand() {
2171
+ return new import_commander14.Command("whoami").description("Show current logged-in user").option("--json", "Output as JSON").action(async (options) => {
910
2172
  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;
2173
+ const token = config.token;
2174
+ if (!token) {
2175
+ if (options.json || Logger.isJsonMode) {
2176
+ console.log(JSON.stringify({ success: false, logged_in: false, error: "Not logged in" }));
2177
+ } else {
2178
+ Logger.info("Not logged in.");
2179
+ Logger.dim(`Run ${import_chalk13.default.cyan("mcphosting login")} to authenticate.`);
920
2180
  }
921
- const limit = parseInt(options.limit);
922
- const limitedResults = results.slice(0, limit);
923
- if (options.json) {
924
- Logger.json(limitedResults);
925
- return;
2181
+ return;
2182
+ }
2183
+ const api = new MCPHostingAPI(token);
2184
+ const user = await api.whoami();
2185
+ if (!user && token) {
2186
+ if (options.json || Logger.isJsonMode) {
2187
+ console.log(JSON.stringify({
2188
+ success: false,
2189
+ logged_in: false,
2190
+ error: "Token expired",
2191
+ cached_email: config.user?.email
2192
+ }));
2193
+ } else {
2194
+ Logger.warning("Token expired. Run `mcphosting login` to re-authenticate.");
2195
+ if (config.user?.email) {
2196
+ Logger.dim(` Cached email: ${config.user.email}`);
2197
+ }
926
2198
  }
2199
+ return;
2200
+ }
2201
+ const result = {
2202
+ success: true,
2203
+ logged_in: true,
2204
+ email: user?.email || config.user?.email || "unknown",
2205
+ org: user?.org || config.user?.org || void 0,
2206
+ token_source: config.isEnvToken ? "environment" : "config"
2207
+ };
2208
+ if (options.json || Logger.isJsonMode) {
2209
+ console.log(JSON.stringify(result));
2210
+ } else {
927
2211
  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.`);
2212
+ Logger.success(`Logged in as ${import_chalk13.default.bold(result.email)}${result.org ? ` (${result.org})` : ""}`);
2213
+ if (config.isEnvToken) {
2214
+ Logger.dim(" Token source: MCPHOSTING_TOKEN environment variable");
942
2215
  }
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);
2216
+ console.log("");
949
2217
  }
950
2218
  });
951
2219
  }
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) => {
2220
+
2221
+ // src/commands/account-status.ts
2222
+ var import_commander15 = require("commander");
2223
+ var import_chalk14 = __toESM(require("chalk"), 1);
2224
+ function createAccountCommand() {
2225
+ return new import_commander15.Command("account").description("Show MCPHosting account status, plan, and servers").option("--json", "Output as JSON").action(async (options) => {
954
2226
  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;
2227
+ if (!isAuthenticated()) {
2228
+ if (options.json || Logger.isJsonMode) {
2229
+ console.log(JSON.stringify({
2230
+ success: false,
2231
+ logged_in: false,
2232
+ error: "Not logged in"
2233
+ }));
2234
+ } else {
2235
+ Logger.error("Not logged in.");
2236
+ Logger.info(`Run ${import_chalk14.default.cyan("mcphosting login")} to authenticate.`);
963
2237
  }
964
- spinner.succeed("Found server info");
965
- if (options.json) {
966
- Logger.json(server);
2238
+ return;
2239
+ }
2240
+ const { api } = getAuthenticatedAPI();
2241
+ try {
2242
+ const [user, servers] = await Promise.all([
2243
+ api.whoami(),
2244
+ api.listServers().catch(() => [])
2245
+ ]);
2246
+ const result = {
2247
+ success: true,
2248
+ logged_in: true,
2249
+ email: user?.email || config.user?.email || "unknown",
2250
+ org: user?.org || config.user?.org || void 0,
2251
+ plan: user?.plan || "free",
2252
+ token_source: config.isEnvToken ? "environment" : "config",
2253
+ servers: servers.map((s) => ({
2254
+ name: s.name || s.slug,
2255
+ slug: s.slug,
2256
+ url: s.url || "",
2257
+ status: s.status || "unknown"
2258
+ }))
2259
+ };
2260
+ if (options.json || Logger.isJsonMode) {
2261
+ console.log(JSON.stringify(result));
967
2262
  return;
968
2263
  }
969
2264
  console.log("");
970
- console.log(import_chalk4.default.bold.cyan(`\u{1F4E6} ${server.name}`));
971
- console.log(import_chalk4.default.dim(server.description));
2265
+ Logger.bold("\u{1F4CA} MCPHosting Account");
972
2266
  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 });
2267
+ Logger.info(` ${import_chalk14.default.dim("Email:")} ${import_chalk14.default.bold(result.email)}`);
2268
+ if (result.org) {
2269
+ Logger.info(` ${import_chalk14.default.dim("Org:")} ${result.org}`);
981
2270
  }
982
- Logger.table(infoTable);
2271
+ Logger.info(` ${import_chalk14.default.dim("Plan:")} ${import_chalk14.default.cyan(result.plan)}`);
2272
+ Logger.info(` ${import_chalk14.default.dim("Servers:")} ${result.servers.length}`);
983
2273
  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) {
2274
+ if (result.servers.length > 0) {
2275
+ Logger.bold("\u{1F5A5}\uFE0F Your MCP Servers");
993
2276
  console.log("");
994
- console.log(import_chalk4.default.blue("\u2139\uFE0F Already connected to:"), connection.clients.join(", "));
2277
+ const tableData = result.servers.map((s) => ({
2278
+ Name: s.name,
2279
+ Status: s.status === "active" ? import_chalk14.default.green("\u25CF active") : s.status === "deploying" ? import_chalk14.default.yellow("\u25D0 deploying") : s.status === "stopped" ? import_chalk14.default.dim("\u25CB stopped") : import_chalk14.default.red("\u2717 " + s.status),
2280
+ URL: s.url.length > 40 ? s.url.slice(0, 37) + "..." : s.url
2281
+ }));
2282
+ Logger.table(tableData);
2283
+ } else {
2284
+ Logger.dim(" No servers deployed yet.");
2285
+ Logger.dim(` Run ${import_chalk14.default.cyan("mcphosting deploy --template weather")} to get started.`);
995
2286
  }
2287
+ console.log("");
996
2288
  } catch (error) {
997
- spinner.fail("Failed to get server info");
998
- Logger.error(`Error: ${error}`);
999
- process.exit(1);
2289
+ if (options.json || Logger.isJsonMode) {
2290
+ console.log(JSON.stringify({ success: false, error: error.message }));
2291
+ } else {
2292
+ Logger.error(error.message);
2293
+ }
1000
2294
  }
1001
2295
  });
1002
2296
  }
1003
2297
 
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;
2298
+ // src/commands/completions.ts
2299
+ var import_commander16 = require("commander");
2300
+ var import_chalk15 = __toESM(require("chalk"), 1);
2301
+ var BASH_COMPLETIONS = `
2302
+ # mcphosting bash completions
2303
+ _mcphosting_completions() {
2304
+ local cur="\${COMP_WORDS[COMP_CWORD]}"
2305
+ local prev="\${COMP_WORDS[COMP_CWORD-1]}"
2306
+ local commands="login logout deploy init connect disconnect list status logs env keys search info import proxy whoami account completions"
2307
+ local templates="weather crypto notion postgres blank runescape reminder search"
2308
+ local global_opts="--help --version --json --silent"
2309
+
2310
+ case "\${prev}" in
2311
+ mcphosting|mcphost)
2312
+ COMPREPLY=( $(compgen -W "\${commands} \${global_opts}" -- "\${cur}") )
2313
+ return 0
2314
+ ;;
2315
+ deploy)
2316
+ COMPREPLY=( $(compgen -W "--template --github --api-url --name --auth --json --silent --configure --no-auto-key" -- "\${cur}") )
2317
+ return 0
2318
+ ;;
2319
+ --template)
2320
+ COMPREPLY=( $(compgen -W "\${templates}" -- "\${cur}") )
2321
+ return 0
2322
+ ;;
2323
+ --auth)
2324
+ COMPREPLY=( $(compgen -W "none api_key oauth" -- "\${cur}") )
2325
+ return 0
2326
+ ;;
2327
+ --shell)
2328
+ COMPREPLY=( $(compgen -W "bash zsh fish" -- "\${cur}") )
2329
+ return 0
2330
+ ;;
2331
+ env)
2332
+ COMPREPLY=( $(compgen -W "list set remove" -- "\${cur}") )
2333
+ return 0
2334
+ ;;
2335
+ keys)
2336
+ COMPREPLY=( $(compgen -W "list create delete" -- "\${cur}") )
2337
+ return 0
2338
+ ;;
2339
+ login)
2340
+ COMPREPLY=( $(compgen -W "--github --email --token --browser --json" -- "\${cur}") )
2341
+ return 0
2342
+ ;;
2343
+ list)
2344
+ COMPREPLY=( $(compgen -W "--local --remote --json" -- "\${cur}") )
2345
+ return 0
2346
+ ;;
2347
+ status|logs)
2348
+ COMPREPLY=( $(compgen -W "--json" -- "\${cur}") )
2349
+ return 0
2350
+ ;;
2351
+ *)
2352
+ COMPREPLY=( $(compgen -W "\${global_opts}" -- "\${cur}") )
2353
+ return 0
2354
+ ;;
2355
+ esac
1027
2356
  }
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;
2357
+ complete -F _mcphosting_completions mcphosting
2358
+ complete -F _mcphosting_completions mcphost
2359
+ `.trim();
2360
+ var ZSH_COMPLETIONS = `
2361
+ #compdef mcphosting mcphost
2362
+
2363
+ _mcphosting() {
2364
+ local -a commands templates global_opts
2365
+
2366
+ commands=(
2367
+ 'login:Authenticate with MCPHosting'
2368
+ 'logout:Log out and remove stored credentials'
2369
+ 'deploy:Deploy an MCP server to MCPHosting'
2370
+ 'init:Initialize mcphosting.json in the current directory'
2371
+ 'connect:Connect an MCP server to your AI clients'
2372
+ 'disconnect:Disconnect an MCP server'
2373
+ 'list:List your MCP servers'
2374
+ 'status:Check the status of a deployed MCP server'
2375
+ 'logs:View logs for a deployed MCP server'
2376
+ 'env:Manage environment variables'
2377
+ 'keys:Manage API keys'
2378
+ 'search:Search MCP servers in the marketplace'
2379
+ 'info:Show detailed information about an MCP server'
2380
+ 'import:Import MCP connections from other tools'
2381
+ 'proxy:Start a local STDIO MCP proxy'
2382
+ 'whoami:Show current logged-in user'
2383
+ 'account:Show MCPHosting account status'
2384
+ 'completions:Generate shell completions'
2385
+ )
2386
+
2387
+ templates=(weather crypto notion postgres blank runescape reminder search)
2388
+ global_opts=(--help --version --json --silent)
2389
+
2390
+ _arguments -C \\
2391
+ '1:command:->cmd' \\
2392
+ '*::arg:->args'
2393
+
2394
+ case "$state" in
2395
+ cmd)
2396
+ _describe -t commands 'mcphosting commands' commands
2397
+ _values 'global options' $global_opts
2398
+ ;;
2399
+ args)
2400
+ case "$words[1]" in
2401
+ deploy)
2402
+ _arguments \\
2403
+ '--template[Deploy from template]:template:(weather crypto notion postgres blank runescape reminder search)' \\
2404
+ '--github[Deploy from GitHub URL]:url:' \\
2405
+ '--api-url[External server URL]:url:' \\
2406
+ '--name[Server name]:name:' \\
2407
+ '--auth[Auth type]:auth:(none api_key oauth)' \\
2408
+ '--json[Output as JSON]' \\
2409
+ '--silent[Suppress interactive output]' \\
2410
+ '--configure[Auto-configure AI clients]' \\
2411
+ '--no-auto-key[Skip API key creation]'
2412
+ ;;
2413
+ completions)
2414
+ _arguments '--shell[Shell type]:shell:(bash zsh fish)'
2415
+ ;;
2416
+ login)
2417
+ _arguments '--github' '--email:email:' '--token:token:' '--browser' '--json'
2418
+ ;;
2419
+ *)
2420
+ _arguments '--json[Output as JSON]'
2421
+ ;;
2422
+ esac
2423
+ ;;
2424
+ esac
1062
2425
  }
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}`);
2426
+
2427
+ _mcphosting "$@"
2428
+ `.trim();
2429
+ var FISH_COMPLETIONS = `
2430
+ # mcphosting fish completions
2431
+ set -l commands login logout deploy init connect disconnect list status logs env keys search info import proxy whoami account completions
2432
+ set -l templates weather crypto notion postgres blank runescape reminder search
2433
+
2434
+ complete -c mcphosting -f
2435
+ complete -c mcphost -f
2436
+
2437
+ # Commands
2438
+ complete -c mcphosting -n "not __fish_seen_subcommand_from $commands" -a login -d "Authenticate with MCPHosting"
2439
+ complete -c mcphosting -n "not __fish_seen_subcommand_from $commands" -a logout -d "Log out"
2440
+ complete -c mcphosting -n "not __fish_seen_subcommand_from $commands" -a deploy -d "Deploy an MCP server"
2441
+ complete -c mcphosting -n "not __fish_seen_subcommand_from $commands" -a init -d "Initialize mcphosting.json"
2442
+ complete -c mcphosting -n "not __fish_seen_subcommand_from $commands" -a connect -d "Connect MCP to AI clients"
2443
+ complete -c mcphosting -n "not __fish_seen_subcommand_from $commands" -a disconnect -d "Disconnect MCP server"
2444
+ complete -c mcphosting -n "not __fish_seen_subcommand_from $commands" -a list -d "List servers"
2445
+ complete -c mcphosting -n "not __fish_seen_subcommand_from $commands" -a status -d "Check server status"
2446
+ complete -c mcphosting -n "not __fish_seen_subcommand_from $commands" -a logs -d "View logs"
2447
+ complete -c mcphosting -n "not __fish_seen_subcommand_from $commands" -a env -d "Manage env vars"
2448
+ complete -c mcphosting -n "not __fish_seen_subcommand_from $commands" -a keys -d "Manage API keys"
2449
+ complete -c mcphosting -n "not __fish_seen_subcommand_from $commands" -a search -d "Search marketplace"
2450
+ complete -c mcphosting -n "not __fish_seen_subcommand_from $commands" -a info -d "MCP server info"
2451
+ complete -c mcphosting -n "not __fish_seen_subcommand_from $commands" -a import -d "Import connections"
2452
+ complete -c mcphosting -n "not __fish_seen_subcommand_from $commands" -a proxy -d "STDIO proxy"
2453
+ complete -c mcphosting -n "not __fish_seen_subcommand_from $commands" -a whoami -d "Show current user"
2454
+ complete -c mcphosting -n "not __fish_seen_subcommand_from $commands" -a account -d "Account status"
2455
+ complete -c mcphosting -n "not __fish_seen_subcommand_from $commands" -a completions -d "Generate completions"
2456
+
2457
+ # Global
2458
+ complete -c mcphosting -l json -d "Output as JSON"
2459
+ complete -c mcphosting -l silent -d "Suppress interactive output"
2460
+ complete -c mcphosting -l help -d "Show help"
2461
+ complete -c mcphosting -l version -d "Show version"
2462
+
2463
+ # Deploy options
2464
+ complete -c mcphosting -n "__fish_seen_subcommand_from deploy" -l template -xa "$templates" -d "Template name"
2465
+ complete -c mcphosting -n "__fish_seen_subcommand_from deploy" -l github -d "GitHub URL"
2466
+ complete -c mcphosting -n "__fish_seen_subcommand_from deploy" -l api-url -d "API URL"
2467
+ complete -c mcphosting -n "__fish_seen_subcommand_from deploy" -l name -d "Server name"
2468
+ complete -c mcphosting -n "__fish_seen_subcommand_from deploy" -l auth -xa "none api_key oauth" -d "Auth type"
2469
+ complete -c mcphosting -n "__fish_seen_subcommand_from deploy" -l configure -d "Auto-configure clients"
2470
+
2471
+ # Completions options
2472
+ complete -c mcphosting -n "__fish_seen_subcommand_from completions" -l shell -xa "bash zsh fish" -d "Shell type"
2473
+ `.trim();
2474
+ function createCompletionsCommand() {
2475
+ return new import_commander16.Command("completions").description("Generate shell completions (bash, zsh, fish)").option("--shell <shell>", "Shell type: bash, zsh, or fish").action((options) => {
2476
+ const shell = options.shell?.toLowerCase() || detectShell();
2477
+ switch (shell) {
2478
+ case "bash":
2479
+ console.log(BASH_COMPLETIONS);
2480
+ if (!options.shell) {
2481
+ console.error("");
2482
+ console.error(import_chalk15.default.dim("# Add to ~/.bashrc:"));
2483
+ console.error(import_chalk15.default.cyan("# mcphosting completions --shell=bash >> ~/.bashrc"));
2484
+ }
2485
+ break;
2486
+ case "zsh":
2487
+ console.log(ZSH_COMPLETIONS);
2488
+ if (!options.shell) {
2489
+ console.error("");
2490
+ console.error(import_chalk15.default.dim("# Save to a file in your fpath:"));
2491
+ console.error(import_chalk15.default.cyan("# mcphosting completions --shell=zsh > ~/.zfunc/_mcphosting"));
2492
+ }
2493
+ break;
2494
+ case "fish":
2495
+ console.log(FISH_COMPLETIONS);
2496
+ if (!options.shell) {
2497
+ console.error("");
2498
+ console.error(import_chalk15.default.dim("# Save to completions dir:"));
2499
+ console.error(import_chalk15.default.cyan("# mcphosting completions --shell=fish > ~/.config/fish/completions/mcphosting.fish"));
2500
+ }
2501
+ break;
2502
+ default:
2503
+ Logger.error(`Unknown shell: ${shell}`);
2504
+ Logger.info("Supported shells: bash, zsh, fish");
2505
+ Logger.info("Usage: mcphosting completions --shell=bash");
2506
+ process.exit(1);
1088
2507
  }
1089
2508
  });
1090
2509
  }
2510
+ function detectShell() {
2511
+ const shell = process.env.SHELL || "";
2512
+ if (shell.includes("zsh")) return "zsh";
2513
+ if (shell.includes("fish")) return "fish";
2514
+ return "bash";
2515
+ }
1091
2516
 
1092
2517
  // 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({
2518
+ var program = new import_commander17.Command();
2519
+ program.name("mcphosting").description("Deploy and manage MCP servers from the terminal").version("1.0.0").option("--json", "Output all results as JSON (agent-friendly)").option("--silent", "Suppress interactive output and prompts").configureOutput({
1096
2520
  outputError: (str, write) => {
1097
- write(import_chalk5.default.red(str));
2521
+ if (Logger.isJsonMode) {
2522
+ write(JSON.stringify({ success: false, error: str.trim() }));
2523
+ } else {
2524
+ write(import_chalk16.default.red(str));
2525
+ }
2526
+ }
2527
+ }).hook("preAction", (thisCommand) => {
2528
+ const rootOpts = program.opts();
2529
+ if (rootOpts.json) {
2530
+ Logger.jsonMode = true;
2531
+ }
2532
+ if (rootOpts.silent) {
2533
+ Logger.silent = true;
1098
2534
  }
1099
2535
  });
1100
- program.addCommand(createConnectCommands());
2536
+ program.addCommand(createLoginCommand());
2537
+ program.addCommand(createLogoutCommand());
2538
+ program.addCommand(createDeployCommand());
2539
+ program.addCommand(createInitCommand());
2540
+ program.addCommand(createConnectCommand());
1101
2541
  program.addCommand(createDisconnectCommand());
1102
2542
  program.addCommand(createListCommand());
1103
- program.addCommand(createImportCommand());
1104
- program.addCommand(createProxyCommand());
2543
+ program.addCommand(createStatusCommand());
2544
+ program.addCommand(createLogsCommand());
2545
+ program.addCommand(createEnvCommand());
2546
+ program.addCommand(createKeysCommand());
1105
2547
  program.addCommand(createSearchCommand());
1106
2548
  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);
2549
+ program.addCommand(createWhoamiCommand());
2550
+ program.addCommand(createAccountCommand());
2551
+ program.addCommand(createImportCommand());
2552
+ program.addCommand(createProxyCommand());
2553
+ program.addCommand(createCompletionsCommand());
1115
2554
  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
- }
2555
+ subcommandTerm: (cmd) => import_chalk16.default.cyan(cmd.name()),
2556
+ commandUsage: (cmd) => import_chalk16.default.yellow(cmd.usage()),
2557
+ commandDescription: (cmd) => import_chalk16.default.dim(cmd.description())
1124
2558
  });
1125
2559
  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")}
2560
+ ${import_chalk16.default.bold("Quick Start (one command!):")}
2561
+ ${import_chalk16.default.dim("1.")} ${import_chalk16.default.cyan("mcphosting login")} ${import_chalk16.default.dim("Authenticate")}
2562
+ ${import_chalk16.default.dim("2.")} ${import_chalk16.default.cyan("mcphosting deploy --template weather")} ${import_chalk16.default.dim("Deploy a template")}
2563
+ ${import_chalk16.default.dim(" ")} ${import_chalk16.default.dim("Done! URL returned. API key created. Config ready.")}
1135
2564
 
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
2565
+ ${import_chalk16.default.bold("Global Flags:")}
2566
+ ${import_chalk16.default.cyan("--json")} ${import_chalk16.default.dim("Output as JSON (agent-friendly)")}
2567
+ ${import_chalk16.default.cyan("--silent")} ${import_chalk16.default.dim("Suppress interactive output")}
1142
2568
 
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")}
2569
+ ${import_chalk16.default.bold("Environment Variables:")}
2570
+ ${import_chalk16.default.cyan("MCPHOSTING_TOKEN")} ${import_chalk16.default.dim("Auth token (overrides config)")}
2571
+ ${import_chalk16.default.cyan("MCPHOSTING_API_URL")} ${import_chalk16.default.dim("API base URL (default: mcphosting.com)")}
1147
2572
 
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")}
2573
+ ${import_chalk16.default.bold("Deploy Options:")}
2574
+ ${import_chalk16.default.cyan("mcphosting deploy")} ${import_chalk16.default.dim("Deploy from current directory")}
2575
+ ${import_chalk16.default.cyan("mcphosting deploy --template crypto")} ${import_chalk16.default.dim("Deploy from template")}
2576
+ ${import_chalk16.default.cyan("mcphosting deploy --github <url>")} ${import_chalk16.default.dim("Deploy from GitHub repo")}
2577
+ ${import_chalk16.default.cyan("mcphosting deploy --api-url <url>")} ${import_chalk16.default.dim("Register external server")}
2578
+ ${import_chalk16.default.cyan("mcphosting deploy --configure")} ${import_chalk16.default.dim("Auto-configure AI clients")}
2579
+
2580
+ ${import_chalk16.default.bold("Templates:")}
2581
+ ${import_chalk16.default.cyan("weather")} ${import_chalk16.default.cyan("crypto")} ${import_chalk16.default.cyan("notion")} ${import_chalk16.default.cyan("postgres")} ${import_chalk16.default.cyan("blank")}
2582
+
2583
+ ${import_chalk16.default.bold("Agent Usage:")}
2584
+ ${import_chalk16.default.cyan("mcphosting deploy --template crypto --json")} ${import_chalk16.default.dim("Deploy + JSON output")}
2585
+ ${import_chalk16.default.cyan("mcphosting whoami --json")} ${import_chalk16.default.dim("Check auth status")}
2586
+ ${import_chalk16.default.cyan("mcphosting account --json")} ${import_chalk16.default.dim("Account + servers")}
2587
+ ${import_chalk16.default.cyan("mcphosting list --json")} ${import_chalk16.default.dim("List all servers")}
2588
+
2589
+ ${import_chalk16.default.bold("More Commands:")}
2590
+ ${import_chalk16.default.cyan("mcphosting init")} ${import_chalk16.default.dim("Create mcphosting.json")}
2591
+ ${import_chalk16.default.cyan("mcphosting connect <slug>")} ${import_chalk16.default.dim("Connect MCP to AI clients")}
2592
+ ${import_chalk16.default.cyan("mcphosting list")} ${import_chalk16.default.dim("List all servers")}
2593
+ ${import_chalk16.default.cyan("mcphosting status <server>")} ${import_chalk16.default.dim("Check server status")}
2594
+ ${import_chalk16.default.cyan('mcphosting keys create "Key Name"')} ${import_chalk16.default.dim("Create API key")}
2595
+ ${import_chalk16.default.cyan("mcphosting search <query>")} ${import_chalk16.default.dim("Search marketplace")}
2596
+ ${import_chalk16.default.cyan("mcphosting completions --shell=zsh")} ${import_chalk16.default.dim("Shell completions")}
2597
+
2598
+ ${import_chalk16.default.bold("Supported AI Clients:")}
2599
+ \u2022 ${import_chalk16.default.green("Claude Desktop")} \u2022 ${import_chalk16.default.green("Cursor")} \u2022 ${import_chalk16.default.green("VS Code")} \u2022 ${import_chalk16.default.green("OpenClaw")} \u2022 ${import_chalk16.default.green("ChatGPT")}
2600
+
2601
+ ${import_chalk16.default.yellow("\u{1F4DA} Docs:")} ${import_chalk16.default.blue("https://mcphosting.com/docs")}
2602
+ ${import_chalk16.default.yellow("\u2B50 GitHub:")} ${import_chalk16.default.blue("https://github.com/gorlomi-enzo/mcphosting-cli")}
1150
2603
  `);
1151
2604
  process.on("uncaughtException", (error) => {
1152
- Logger.error(`Uncaught error: ${error.message}`);
1153
- if (process.env.DEBUG) {
1154
- console.error(error.stack);
2605
+ if (Logger.isJsonMode) {
2606
+ console.error(JSON.stringify({ success: false, error: error.message }));
2607
+ } else {
2608
+ Logger.error(`Unexpected error: ${error.message}`);
1155
2609
  }
2610
+ if (process.env.DEBUG) console.error(error.stack);
1156
2611
  process.exit(1);
1157
2612
  });
1158
2613
  process.on("unhandledRejection", (reason) => {
1159
- Logger.error(`Unhandled rejection: ${reason}`);
1160
- if (process.env.DEBUG) {
1161
- console.error(reason);
2614
+ if (Logger.isJsonMode) {
2615
+ console.error(JSON.stringify({ success: false, error: String(reason) }));
2616
+ } else {
2617
+ Logger.error(`Unhandled rejection: ${reason}`);
1162
2618
  }
2619
+ if (process.env.DEBUG) console.error(reason);
1163
2620
  process.exit(1);
1164
2621
  });
1165
2622
  process.on("SIGINT", () => {
1166
- console.log("\n");
1167
- Logger.info("Goodbye! \u{1F44B}");
2623
+ if (!Logger.isSilent && !Logger.isJsonMode) {
2624
+ console.log("\n");
2625
+ Logger.info("Goodbye! \u{1F44B}");
2626
+ }
1168
2627
  process.exit(0);
1169
2628
  });
1170
2629
  async function main() {
1171
2630
  try {
1172
2631
  await program.parseAsync();
1173
2632
  } catch (error) {
1174
- Logger.error(`Command failed: ${error}`);
1175
- if (process.env.DEBUG) {
1176
- console.error(error);
2633
+ if (Logger.isJsonMode) {
2634
+ console.error(JSON.stringify({ success: false, error: error.message }));
2635
+ } else {
2636
+ Logger.error(`Command failed: ${error.message}`);
1177
2637
  }
2638
+ if (process.env.DEBUG) console.error(error);
1178
2639
  process.exit(1);
1179
2640
  }
1180
2641
  }