@mcp-use/cli 2.10.2 → 2.10.3

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.js CHANGED
@@ -1114,6 +1114,7 @@ var open_default = open;
1114
1114
  import { toJSONSchema } from "zod";
1115
1115
 
1116
1116
  // src/commands/auth.ts
1117
+ import crypto from "crypto";
1117
1118
  import {
1118
1119
  createServer
1119
1120
  } from "http";
@@ -1446,6 +1447,27 @@ var McpUseAPI = class _McpUseAPI {
1446
1447
  );
1447
1448
  return response.data.logs;
1448
1449
  }
1450
+ /**
1451
+ * Get GitHub connection status
1452
+ */
1453
+ async getGitHubConnectionStatus() {
1454
+ return this.request("/github/connection");
1455
+ }
1456
+ /**
1457
+ * Get GitHub app name
1458
+ */
1459
+ async getGitHubAppName() {
1460
+ const response = await this.request("/github/appname");
1461
+ return response.app_name;
1462
+ }
1463
+ /**
1464
+ * Get accessible GitHub repositories
1465
+ */
1466
+ async getGitHubRepos(refresh = false) {
1467
+ return this.request(
1468
+ `/github/repos${refresh ? "?refresh=true" : ""}`
1469
+ );
1470
+ }
1449
1471
  };
1450
1472
 
1451
1473
  // src/commands/auth.ts
@@ -1469,7 +1491,7 @@ async function findAvailablePort(startPort = 8765) {
1469
1491
  }
1470
1492
  throw new Error("No available ports found");
1471
1493
  }
1472
- async function startCallbackServer(port) {
1494
+ async function startCallbackServer(port, expectedState) {
1473
1495
  return new Promise((resolve, reject) => {
1474
1496
  let tokenResolver = null;
1475
1497
  const tokenPromise = new Promise((res) => {
@@ -1479,6 +1501,49 @@ async function startCallbackServer(port) {
1479
1501
  if (req.url?.startsWith("/callback")) {
1480
1502
  const url = new URL(req.url, `http://localhost:${port}`);
1481
1503
  const token = url.searchParams.get("token");
1504
+ const state = url.searchParams.get("state");
1505
+ if (state !== expectedState) {
1506
+ res.writeHead(400, { "Content-Type": "text/html" });
1507
+ res.end(`
1508
+ <!DOCTYPE html>
1509
+ <html>
1510
+ <head>
1511
+ <title>Security Error</title>
1512
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
1513
+ <style>
1514
+ body {
1515
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
1516
+ display: flex;
1517
+ justify-content: center;
1518
+ align-items: center;
1519
+ min-height: 100vh;
1520
+ background: #000;
1521
+ padding: 1rem;
1522
+ margin: 0;
1523
+ }
1524
+ .container {
1525
+ max-width: 28rem;
1526
+ padding: 3rem;
1527
+ text-align: center;
1528
+ background: rgba(255, 255, 255, 0.1);
1529
+ backdrop-filter: blur(40px);
1530
+ border: 1px solid rgba(255, 255, 255, 0.2);
1531
+ border-radius: 1.5rem;
1532
+ }
1533
+ h1 { color: #fff; font-size: 2rem; margin-bottom: 1rem; }
1534
+ p { color: rgba(255, 255, 255, 0.8); font-size: 1rem; }
1535
+ </style>
1536
+ </head>
1537
+ <body>
1538
+ <div class="container">
1539
+ <h1>Security Error</h1>
1540
+ <p>Invalid state parameter. Please try logging in again.</p>
1541
+ </div>
1542
+ </body>
1543
+ </html>
1544
+ `);
1545
+ return;
1546
+ }
1482
1547
  if (token && tokenResolver) {
1483
1548
  res.writeHead(200, { "Content-Type": "text/html" });
1484
1549
  res.end(`
@@ -1681,12 +1746,13 @@ async function loginCommand(options) {
1681
1746
  return;
1682
1747
  }
1683
1748
  console.log(source_default.cyan.bold("\u{1F510} Logging in to mcp-use cloud...\n"));
1749
+ const state = crypto.randomBytes(32).toString("hex");
1684
1750
  const port = await findAvailablePort();
1685
1751
  const redirectUri = `http://localhost:${port}/callback`;
1686
1752
  console.log(source_default.gray(`Starting local server on port ${port}...`));
1687
- const { server, token } = await startCallbackServer(port);
1753
+ const { server, token } = await startCallbackServer(port, state);
1688
1754
  const webUrl = await getWebUrl();
1689
- const loginUrl = `${webUrl}/auth/cli?redirect_uri=${encodeURIComponent(redirectUri)}`;
1755
+ const loginUrl = `${webUrl}/auth/cli?redirect_uri=${encodeURIComponent(redirectUri)}&state=${state}`;
1690
1756
  console.log(source_default.gray(`Opening browser to ${webUrl}/auth/cli...
1691
1757
  `));
1692
1758
  console.log(
@@ -3251,33 +3317,78 @@ async function displayDeploymentProgress(api, deployment) {
3251
3317
  lastDisplayedLogLength = finalDeployment.buildLogs.length;
3252
3318
  }
3253
3319
  if (finalDeployment.status === "running") {
3254
- const mcpUrl = `https://${finalDeployment.domain}/mcp`;
3255
- const inspectorUrl = `https://inspector.mcp-use.com/inspector?autoConnect=${encodeURIComponent(mcpUrl)}`;
3320
+ let mcpServerUrl;
3321
+ let dashboardUrl = null;
3322
+ if (finalDeployment.customDomain) {
3323
+ mcpServerUrl = `https://${finalDeployment.customDomain}/mcp`;
3324
+ if (finalDeployment.serverSlug) {
3325
+ dashboardUrl = `https://mcp-use.com/cloud/servers/${finalDeployment.serverSlug}`;
3326
+ }
3327
+ } else if (finalDeployment.serverSlug) {
3328
+ mcpServerUrl = `https://${finalDeployment.serverSlug}.mcp-use.run/mcp`;
3329
+ dashboardUrl = `https://mcp-use.com/cloud/servers/${finalDeployment.serverSlug}`;
3330
+ } else if (finalDeployment.serverId) {
3331
+ mcpServerUrl = `https://${finalDeployment.serverId}.mcp-use.run/mcp`;
3332
+ dashboardUrl = `https://mcp-use.com/cloud/servers/${finalDeployment.serverId}`;
3333
+ } else {
3334
+ mcpServerUrl = `https://${finalDeployment.domain}/mcp`;
3335
+ }
3336
+ const inspectorUrl = `https://inspector.mcp-use.com/inspector?autoConnect=${encodeURIComponent(
3337
+ mcpServerUrl
3338
+ )}`;
3256
3339
  console.log(source_default.green.bold("\u2713 Deployment successful!\n"));
3257
3340
  console.log(source_default.white("\u{1F310} MCP Server URL:"));
3258
- console.log(source_default.cyan.bold(` ${mcpUrl}
3341
+ console.log(source_default.cyan.bold(` ${mcpServerUrl}
3259
3342
  `));
3343
+ if (dashboardUrl) {
3344
+ console.log(source_default.white("\u{1F4CA} Dashboard:"));
3345
+ console.log(source_default.cyan.bold(` ${dashboardUrl}
3346
+ `));
3347
+ }
3260
3348
  console.log(source_default.white("\u{1F50D} Inspector URL:"));
3261
3349
  console.log(source_default.cyan.bold(` ${inspectorUrl}
3262
3350
  `));
3263
- if (finalDeployment.customDomain) {
3264
- const customMcpUrl = `https://${finalDeployment.customDomain}/mcp`;
3265
- const customInspectorUrl = `https://inspector.mcp-use.com/inspect?autoConnect=${encodeURIComponent(customMcpUrl)}`;
3266
- console.log(source_default.white("\u{1F517} Custom Domain:"));
3267
- console.log(source_default.cyan.bold(` ${customMcpUrl}
3268
- `));
3269
- console.log(source_default.white("\u{1F50D} Custom Inspector:"));
3270
- console.log(source_default.cyan.bold(` ${customInspectorUrl}
3271
- `));
3272
- }
3273
3351
  console.log(
3274
3352
  source_default.gray("Deployment ID: ") + source_default.white(finalDeployment.id)
3275
3353
  );
3276
3354
  return;
3277
3355
  } else if (finalDeployment.status === "failed") {
3356
+ stopSpinner();
3278
3357
  console.log(source_default.red.bold("\u2717 Deployment failed\n"));
3279
3358
  if (finalDeployment.error) {
3280
3359
  console.log(source_default.red("Error: ") + finalDeployment.error);
3360
+ if (finalDeployment.error.includes("No GitHub installations found")) {
3361
+ console.log();
3362
+ const retry = await promptGitHubInstallation(api, "not_connected");
3363
+ if (retry) {
3364
+ console.log(source_default.cyan("\n\u{1F504} Retrying deployment...\n"));
3365
+ const newDeployment = await api.redeployDeployment(deployment.id);
3366
+ await displayDeploymentProgress(api, newDeployment);
3367
+ return;
3368
+ }
3369
+ } else if (finalDeployment.error.includes("Authenticated git clone failed")) {
3370
+ let repoName;
3371
+ const repoMatch = finalDeployment.error.match(
3372
+ /github\.com\/([^/]+\/[^/\s]+)/
3373
+ );
3374
+ if (repoMatch) {
3375
+ repoName = repoMatch[1].replace(/\.git$/, "");
3376
+ } else if (finalDeployment.source.type === "github") {
3377
+ repoName = finalDeployment.source.repo;
3378
+ }
3379
+ console.log();
3380
+ const retry = await promptGitHubInstallation(
3381
+ api,
3382
+ "no_access",
3383
+ repoName
3384
+ );
3385
+ if (retry) {
3386
+ console.log(source_default.cyan("\n\u{1F504} Retrying deployment...\n"));
3387
+ const newDeployment = await api.redeployDeployment(deployment.id);
3388
+ await displayDeploymentProgress(api, newDeployment);
3389
+ return;
3390
+ }
3391
+ }
3281
3392
  }
3282
3393
  if (finalDeployment.buildLogs) {
3283
3394
  console.log(source_default.gray("\nBuild logs:"));
@@ -3315,6 +3426,137 @@ async function displayDeploymentProgress(api, deployment) {
3315
3426
  source_default.gray("Check status with: ") + source_default.white(`mcp-use status ${deployment.id}`)
3316
3427
  );
3317
3428
  }
3429
+ async function checkRepoAccess(api, owner, repo) {
3430
+ try {
3431
+ const reposResponse = await api.getGitHubRepos(true);
3432
+ const repoFullName = `${owner}/${repo}`;
3433
+ return reposResponse.repos.some((r) => r.full_name === repoFullName);
3434
+ } catch (error) {
3435
+ console.log(source_default.gray("Could not verify repository access"));
3436
+ return false;
3437
+ }
3438
+ }
3439
+ async function promptGitHubInstallation(api, reason, repoName) {
3440
+ console.log();
3441
+ if (reason === "not_connected") {
3442
+ console.log(source_default.yellow("\u26A0\uFE0F GitHub account not connected"));
3443
+ console.log(
3444
+ source_default.white("Deployments require a connected GitHub account.\n")
3445
+ );
3446
+ } else {
3447
+ console.log(
3448
+ source_default.yellow("\u26A0\uFE0F GitHub App doesn't have access to this repository")
3449
+ );
3450
+ console.log(
3451
+ source_default.white(
3452
+ `The GitHub App needs permission to access ${source_default.cyan(repoName || "this repository")}.
3453
+ `
3454
+ )
3455
+ );
3456
+ }
3457
+ const shouldInstall = await prompt(
3458
+ source_default.white(
3459
+ `Would you like to ${reason === "not_connected" ? "connect" : "configure"} GitHub now? (Y/n): `
3460
+ ),
3461
+ "y"
3462
+ );
3463
+ if (!shouldInstall) {
3464
+ return false;
3465
+ }
3466
+ try {
3467
+ const appName = process.env.MCP_GITHUB_APP_NAME || "mcp-use";
3468
+ const installUrl = reason === "not_connected" ? `https://github.com/apps/${appName}/installations/new` : `https://github.com/settings/installations`;
3469
+ console.log(
3470
+ source_default.cyan(
3471
+ `
3472
+ Opening browser to ${reason === "not_connected" ? "install" : "configure"} GitHub App...`
3473
+ )
3474
+ );
3475
+ console.log(source_default.gray(`URL: ${installUrl}
3476
+ `));
3477
+ if (reason === "no_access") {
3478
+ console.log(source_default.white("Please:"));
3479
+ console.log(
3480
+ source_default.cyan(" 1. Find the 'mcp-use' (or similar) GitHub App")
3481
+ );
3482
+ console.log(source_default.cyan(" 2. Click 'Configure'"));
3483
+ console.log(
3484
+ source_default.cyan(
3485
+ ` 3. Grant access to ${source_default.bold(repoName || "your repository")}`
3486
+ )
3487
+ );
3488
+ console.log(source_default.cyan(" 4. Save your changes"));
3489
+ console.log(source_default.cyan(" 5. Return here when done\n"));
3490
+ } else {
3491
+ console.log(source_default.white("Please:"));
3492
+ console.log(source_default.cyan(" 1. Select the repositories to grant access"));
3493
+ if (repoName) {
3494
+ console.log(
3495
+ source_default.cyan(` 2. Make sure to include ${source_default.bold(repoName)}`)
3496
+ );
3497
+ console.log(source_default.cyan(" 3. Complete the installation"));
3498
+ } else {
3499
+ console.log(source_default.cyan(" 2. Complete the installation"));
3500
+ }
3501
+ console.log();
3502
+ }
3503
+ await open_default(installUrl);
3504
+ console.log(source_default.gray("Waiting for GitHub configuration..."));
3505
+ await prompt(
3506
+ source_default.white("Press Enter when you've completed the GitHub setup..."),
3507
+ "y"
3508
+ );
3509
+ console.log(source_default.gray("Verifying GitHub connection..."));
3510
+ let verified = false;
3511
+ try {
3512
+ const status = await api.getGitHubConnectionStatus();
3513
+ if (!status.is_connected) {
3514
+ console.log(source_default.yellow("\u26A0\uFE0F GitHub connection not detected."));
3515
+ } else if (repoName) {
3516
+ const [owner, repo] = repoName.split("/");
3517
+ console.log(source_default.gray(`Checking access to ${repoName}...`));
3518
+ const hasAccess = await checkRepoAccess(api, owner, repo);
3519
+ if (!hasAccess) {
3520
+ console.log(
3521
+ source_default.yellow(
3522
+ `\u26A0\uFE0F The GitHub App may not have access to ${source_default.cyan(repoName)} yet`
3523
+ )
3524
+ );
3525
+ } else {
3526
+ console.log(source_default.green(`\u2713 Repository ${repoName} is accessible!
3527
+ `));
3528
+ verified = true;
3529
+ }
3530
+ } else {
3531
+ console.log(source_default.green("\u2713 GitHub connected successfully!\n"));
3532
+ verified = true;
3533
+ }
3534
+ } catch (error) {
3535
+ console.log(
3536
+ source_default.yellow("\u26A0\uFE0F Could not verify GitHub connection (API issue)")
3537
+ );
3538
+ }
3539
+ if (!verified) {
3540
+ console.log(
3541
+ source_default.gray(
3542
+ "\nNote: If you completed the GitHub setup, the deployment may work now.\n"
3543
+ )
3544
+ );
3545
+ }
3546
+ return true;
3547
+ } catch (error) {
3548
+ console.log(
3549
+ source_default.yellow("\n\u26A0\uFE0F Unable to open GitHub installation automatically")
3550
+ );
3551
+ console.log(
3552
+ source_default.white("Please visit: ") + source_default.cyan("https://cloud.mcp-use.com/cloud/settings")
3553
+ );
3554
+ console.log(
3555
+ source_default.gray("Then connect your GitHub account and try again.\n")
3556
+ );
3557
+ return false;
3558
+ }
3559
+ }
3318
3560
  async function deployCommand(options) {
3319
3561
  try {
3320
3562
  const cwd = process.cwd();
@@ -3437,8 +3679,9 @@ async function deployCommand(options) {
3437
3679
  console.log();
3438
3680
  const shouldDeploy = await prompt(
3439
3681
  source_default.white(
3440
- `Deploy from GitHub repository ${gitInfo.owner}/${gitInfo.repo}? (y/n): `
3441
- )
3682
+ `Deploy from GitHub repository ${gitInfo.owner}/${gitInfo.repo}? (Y/n): `
3683
+ ),
3684
+ "y"
3442
3685
  );
3443
3686
  if (!shouldDeploy) {
3444
3687
  console.log(source_default.gray("Deployment cancelled."));
@@ -3471,6 +3714,111 @@ async function deployCommand(options) {
3471
3714
  }
3472
3715
  console.log();
3473
3716
  const api = await McpUseAPI.create();
3717
+ let githubVerified = false;
3718
+ try {
3719
+ console.log(source_default.gray(`[DEBUG] API URL: ${api.baseUrl}`));
3720
+ const connectionStatus = await api.getGitHubConnectionStatus();
3721
+ if (!connectionStatus.is_connected) {
3722
+ const repoFullName = `${gitInfo.owner}/${gitInfo.repo}`;
3723
+ const installed = await promptGitHubInstallation(
3724
+ api,
3725
+ "not_connected",
3726
+ repoFullName
3727
+ );
3728
+ if (!installed) {
3729
+ console.log(source_default.gray("Deployment cancelled."));
3730
+ process.exit(0);
3731
+ }
3732
+ const retryStatus = await api.getGitHubConnectionStatus();
3733
+ if (!retryStatus.is_connected) {
3734
+ console.log(
3735
+ source_default.red("\n\u2717 GitHub connection could not be verified.")
3736
+ );
3737
+ console.log(
3738
+ source_default.gray("Please try connecting GitHub from the web UI:")
3739
+ );
3740
+ console.log(
3741
+ source_default.cyan(" https://cloud.mcp-use.com/cloud/settings\n")
3742
+ );
3743
+ process.exit(1);
3744
+ }
3745
+ githubVerified = true;
3746
+ } else if (gitInfo.owner && gitInfo.repo) {
3747
+ console.log(source_default.gray("Checking repository access..."));
3748
+ const hasAccess = await checkRepoAccess(
3749
+ api,
3750
+ gitInfo.owner,
3751
+ gitInfo.repo
3752
+ );
3753
+ if (!hasAccess) {
3754
+ const repoFullName = `${gitInfo.owner}/${gitInfo.repo}`;
3755
+ console.log(
3756
+ source_default.yellow(
3757
+ `\u26A0\uFE0F GitHub App doesn't have access to ${source_default.cyan(repoFullName)}`
3758
+ )
3759
+ );
3760
+ const configured = await promptGitHubInstallation(
3761
+ api,
3762
+ "no_access",
3763
+ repoFullName
3764
+ );
3765
+ if (!configured) {
3766
+ console.log(source_default.gray("Deployment cancelled."));
3767
+ process.exit(0);
3768
+ }
3769
+ const hasAccessRetry = await checkRepoAccess(
3770
+ api,
3771
+ gitInfo.owner,
3772
+ gitInfo.repo
3773
+ );
3774
+ if (!hasAccessRetry) {
3775
+ console.log(
3776
+ source_default.red(
3777
+ `
3778
+ \u2717 Repository ${source_default.cyan(repoFullName)} is still not accessible.`
3779
+ )
3780
+ );
3781
+ console.log(
3782
+ source_default.gray(
3783
+ "Please make sure the GitHub App has access to this repository."
3784
+ )
3785
+ );
3786
+ console.log(
3787
+ source_default.cyan(" https://github.com/settings/installations\n")
3788
+ );
3789
+ process.exit(1);
3790
+ }
3791
+ githubVerified = true;
3792
+ } else {
3793
+ console.log(source_default.green("\u2713 Repository access confirmed"));
3794
+ githubVerified = true;
3795
+ }
3796
+ }
3797
+ } catch (error) {
3798
+ console.log(source_default.red("\u2717 Could not verify GitHub connection"));
3799
+ console.log(
3800
+ source_default.gray(
3801
+ "Error: " + (error instanceof Error ? error.message : "Unknown error")
3802
+ )
3803
+ );
3804
+ console.log(source_default.gray("\nPlease ensure:"));
3805
+ console.log(
3806
+ source_default.cyan(
3807
+ " 1. You have connected GitHub at https://cloud.mcp-use.com/cloud/settings"
3808
+ )
3809
+ );
3810
+ console.log(
3811
+ source_default.cyan(" 2. The GitHub App has access to your repository")
3812
+ );
3813
+ console.log(source_default.cyan(" 3. Your internet connection is stable\n"));
3814
+ process.exit(1);
3815
+ }
3816
+ if (!githubVerified) {
3817
+ console.log(
3818
+ source_default.red("\n\u2717 GitHub verification required for this deployment")
3819
+ );
3820
+ process.exit(1);
3821
+ }
3474
3822
  const existingLink = !options.new ? await getProjectLink(cwd) : null;
3475
3823
  if (existingLink) {
3476
3824
  try {