@mcp-use/cli 2.21.5-canary.1 → 3.0.0-canary.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
@@ -1130,12 +1130,6 @@ var open_default = open;
1130
1130
  import { viteSingleFile } from "vite-plugin-singlefile";
1131
1131
  import { toJSONSchema } from "zod";
1132
1132
 
1133
- // src/commands/auth.ts
1134
- import crypto from "crypto";
1135
- import {
1136
- createServer
1137
- } from "http";
1138
-
1139
1133
  // src/utils/config.ts
1140
1134
  import { promises as fs7 } from "fs";
1141
1135
  import os3 from "os";
@@ -1187,6 +1181,10 @@ async function getProfileId() {
1187
1181
  async function getWebUrl() {
1188
1182
  return DEFAULT_WEB_URL;
1189
1183
  }
1184
+ async function getAuthBaseUrl() {
1185
+ const apiUrl = await getApiUrl();
1186
+ return apiUrl.replace(/\/api\/v1$/, "");
1187
+ }
1190
1188
 
1191
1189
  // src/utils/api.ts
1192
1190
  var McpUseAPI = class _McpUseAPI {
@@ -1238,6 +1236,13 @@ var McpUseAPI = class _McpUseAPI {
1238
1236
  signal: controller.signal
1239
1237
  });
1240
1238
  clearTimeout(timeoutId);
1239
+ if (response.status === 401) {
1240
+ const err = new Error(
1241
+ "Your session has expired or your API key is invalid."
1242
+ );
1243
+ err.status = 401;
1244
+ throw err;
1245
+ }
1241
1246
  if (!response.ok) {
1242
1247
  const error = await response.text();
1243
1248
  throw new Error(`API request failed: ${response.status} ${error}`);
@@ -1254,17 +1259,19 @@ var McpUseAPI = class _McpUseAPI {
1254
1259
  }
1255
1260
  }
1256
1261
  /**
1257
- * Create API key using JWT token
1262
+ * Create a persistent API key using a Better Auth access token.
1263
+ * Calls Better Auth's built-in POST /api/auth/api-key/create endpoint.
1258
1264
  */
1259
- async createApiKey(jwtToken, name = "CLI") {
1260
- const url = `${this.baseUrl}/api-key`;
1265
+ async createApiKeyWithAccessToken(accessToken, name = "CLI") {
1266
+ const authBase = await getAuthBaseUrl();
1267
+ const url = `${authBase}/api/auth/api-key/create`;
1261
1268
  const response = await fetch(url, {
1262
1269
  method: "POST",
1263
1270
  headers: {
1264
1271
  "Content-Type": "application/json",
1265
- Authorization: `Bearer ${jwtToken}`
1272
+ Authorization: `Bearer ${accessToken}`
1266
1273
  },
1267
- body: JSON.stringify({ name })
1274
+ body: JSON.stringify({ name, prefix: "mcp_" })
1268
1275
  });
1269
1276
  if (!response.ok) {
1270
1277
  const error = await response.text();
@@ -1525,273 +1532,65 @@ var McpUseAPI = class _McpUseAPI {
1525
1532
  };
1526
1533
 
1527
1534
  // src/commands/auth.ts
1528
- var LOGIN_TIMEOUT = 3e5;
1529
- async function findAvailablePort(startPort = 8765) {
1530
- for (let port = startPort; port < startPort + 100; port++) {
1531
- try {
1532
- await new Promise((resolve2, reject) => {
1533
- const server = createServer();
1534
- server.once("error", reject);
1535
- server.once("listening", () => {
1536
- server.close();
1537
- resolve2();
1538
- });
1539
- server.listen(port);
1540
- });
1541
- return port;
1542
- } catch {
1543
- continue;
1544
- }
1535
+ var DEVICE_CLIENT_ID = "mcp-use-cli";
1536
+ var DEVICE_POLL_TIMEOUT = 18e5;
1537
+ async function requestDeviceCode(authBaseUrl) {
1538
+ const url = `${authBaseUrl}/api/auth/device/code`;
1539
+ const response = await fetch(url, {
1540
+ method: "POST",
1541
+ headers: { "Content-Type": "application/json" },
1542
+ body: JSON.stringify({
1543
+ client_id: DEVICE_CLIENT_ID,
1544
+ scope: "openid profile email"
1545
+ })
1546
+ });
1547
+ if (!response.ok) {
1548
+ const error = await response.text();
1549
+ throw new Error(
1550
+ `Failed to request device code: ${response.status} ${error}`
1551
+ );
1545
1552
  }
1546
- throw new Error("No available ports found");
1553
+ return response.json();
1547
1554
  }
1548
- async function startCallbackServer(port, expectedState) {
1549
- return new Promise((resolve2, reject) => {
1550
- let tokenResolver = null;
1551
- const tokenPromise = new Promise((res) => {
1552
- tokenResolver = res;
1555
+ async function pollForDeviceToken(authBaseUrl, deviceCode, intervalSeconds) {
1556
+ let pollingInterval = intervalSeconds;
1557
+ const deadline = Date.now() + DEVICE_POLL_TIMEOUT;
1558
+ while (Date.now() < deadline) {
1559
+ const delayMs = pollingInterval * 1e3;
1560
+ await new Promise((r) => setTimeout(r, delayMs));
1561
+ const url = `${authBaseUrl}/api/auth/device/token`;
1562
+ const response = await fetch(url, {
1563
+ method: "POST",
1564
+ headers: { "Content-Type": "application/json" },
1565
+ body: JSON.stringify({
1566
+ grant_type: "urn:ietf:params:oauth:grant-type:device_code",
1567
+ device_code: deviceCode,
1568
+ client_id: DEVICE_CLIENT_ID
1569
+ })
1553
1570
  });
1554
- const server = createServer(
1555
- {
1556
- maxHeaderSize: 65536
1557
- // 64KB - handle very long JWT tokens in URL (increased from default 8192)
1558
- },
1559
- (req, res) => {
1560
- if (req.url?.startsWith("/callback")) {
1561
- const url = new URL(req.url, `http://localhost:${port}`);
1562
- const token = url.searchParams.get("token");
1563
- const state = url.searchParams.get("state");
1564
- if (state !== expectedState) {
1565
- res.writeHead(400, { "Content-Type": "text/html" });
1566
- res.end(`
1567
- <!DOCTYPE html>
1568
- <html>
1569
- <head>
1570
- <title>Security Error</title>
1571
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
1572
- <style>
1573
- body {
1574
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
1575
- display: flex;
1576
- justify-content: center;
1577
- align-items: center;
1578
- min-height: 100vh;
1579
- background: #000;
1580
- padding: 1rem;
1581
- margin: 0;
1582
- }
1583
- .container {
1584
- max-width: 28rem;
1585
- padding: 3rem;
1586
- text-align: center;
1587
- background: rgba(255, 255, 255, 0.1);
1588
- backdrop-filter: blur(40px);
1589
- border: 1px solid rgba(255, 255, 255, 0.2);
1590
- border-radius: 1.5rem;
1591
- }
1592
- h1 { color: #fff; font-size: 2rem; margin-bottom: 1rem; }
1593
- p { color: rgba(255, 255, 255, 0.8); font-size: 1rem; }
1594
- </style>
1595
- </head>
1596
- <body>
1597
- <div class="container">
1598
- <h1>Security Error</h1>
1599
- <p>Invalid state parameter. Please try logging in again.</p>
1600
- </div>
1601
- </body>
1602
- </html>
1603
- `);
1604
- return;
1605
- }
1606
- if (token && tokenResolver) {
1607
- res.writeHead(200, { "Content-Type": "text/html" });
1608
- res.end(`
1609
- <!DOCTYPE html>
1610
- <html>
1611
- <head>
1612
- <title>Login Successful</title>
1613
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
1614
- <style>
1615
- * {
1616
- margin: 0;
1617
- padding: 0;
1618
- box-sizing: border-box;
1619
- }
1620
- body {
1621
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
1622
- display: flex;
1623
- justify-content: center;
1624
- align-items: center;
1625
- min-height: 100vh;
1626
- background: #000;
1627
- padding: 1rem;
1628
- }
1629
- .container {
1630
- width: 100%;
1631
- max-width: 28rem;
1632
- padding: 3rem;
1633
- text-align: center;
1634
- -webkit-backdrop-filter: blur(40px);
1635
- border: 1px solid rgba(255, 255, 255, 0.2);
1636
- border-radius: 1.5rem;
1637
- box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.5);
1638
- }
1639
- .icon-container {
1640
- display: inline-flex;
1641
- align-items: center;
1642
- justify-content: center;
1643
- width: 6rem;
1644
- height: 6rem;
1645
- margin-bottom: 2rem;
1646
- background: rgba(255, 255, 255, 0.1);
1647
- backdrop-filter: blur(10px);
1648
- -webkit-backdrop-filter: blur(10px);
1649
- border-radius: 50%;
1650
- }
1651
- .checkmark {
1652
- font-size: 4rem;
1653
- color: #fff;
1654
- line-height: 1;
1655
- animation: scaleIn 0.5s ease-out;
1656
- }
1657
- @keyframes scaleIn {
1658
- from {
1659
- transform: scale(0);
1660
- opacity: 0;
1661
- }
1662
- to {
1663
- transform: scale(1);
1664
- opacity: 1;
1665
- }
1666
- }
1667
- h1 {
1668
- color: #fff;
1669
- margin: 0 0 1rem 0;
1670
- font-size: 2.5rem;
1671
- font-weight: 700;
1672
- letter-spacing: -0.025em;
1673
- }
1674
- p {
1675
- color: rgba(255, 255, 255, 0.8);
1676
- margin: 0 0 2rem 0;
1677
- font-size: 1.125rem;
1678
- line-height: 1.5;
1679
- }
1680
- .spinner {
1681
- display: inline-block;
1682
- width: 2rem;
1683
- height: 2rem;
1684
- border: 3px solid rgba(255, 255, 255, 0.3);
1685
- border-top-color: #fff;
1686
- border-radius: 50%;
1687
- animation: spin 0.8s linear infinite;
1688
- }
1689
- @keyframes spin {
1690
- to { transform: rotate(360deg); }
1691
- }
1692
- .footer {
1693
- margin-top: 2rem;
1694
- color: rgba(255, 255, 255, 0.6);
1695
- font-size: 0.875rem;
1696
- }
1697
- </style>
1698
- </head>
1699
- <body>
1700
- <div class="container">
1701
- <h1>Authentication Successful!</h1>
1702
- <p>You can now close this window and return to the CLI.</p>
1703
- </div>
1704
- </body>
1705
- </html>
1706
- `);
1707
- tokenResolver(token);
1708
- } else {
1709
- res.writeHead(400, { "Content-Type": "text/html" });
1710
- res.end(`
1711
- <!DOCTYPE html>
1712
- <html>
1713
- <head>
1714
- <title>Login Failed</title>
1715
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
1716
- <style>
1717
- * {
1718
- margin: 0;
1719
- padding: 0;
1720
- box-sizing: border-box;
1721
- }
1722
- body {
1723
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
1724
- display: flex;
1725
- justify-content: center;
1726
- align-items: center;
1727
- min-height: 100vh;
1728
- background: #000;
1729
- padding: 1rem;
1730
- }
1731
- .container {
1732
- width: 100%;
1733
- max-width: 28rem;
1734
- padding: 3rem;
1735
- text-align: center;
1736
- background: rgba(255, 255, 255, 0.1);
1737
- backdrop-filter: blur(40px);
1738
- -webkit-backdrop-filter: blur(40px);
1739
- border: 1px solid rgba(255, 255, 255, 0.2);
1740
- border-radius: 1.5rem;
1741
- box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.5);
1742
- }
1743
- .icon-container {
1744
- display: inline-flex;
1745
- align-items: center;
1746
- justify-content: center;
1747
- width: 6rem;
1748
- height: 6rem;
1749
- margin-bottom: 2rem;
1750
- background: rgba(255, 255, 255, 0.1);
1751
- backdrop-filter: blur(10px);
1752
- -webkit-backdrop-filter: blur(10px);
1753
- border-radius: 50%;
1754
- }
1755
- .cross {
1756
- font-size: 4rem;
1757
- color: #fff;
1758
- line-height: 1;
1759
- }
1760
- h1 {
1761
- color: #fff;
1762
- margin: 0 0 1rem 0;
1763
- font-size: 2.5rem;
1764
- font-weight: 700;
1765
- letter-spacing: -0.025em;
1766
- }
1767
- p {
1768
- color: rgba(255, 255, 255, 0.8);
1769
- margin: 0;
1770
- font-size: 1.125rem;
1771
- line-height: 1.5;
1772
- }
1773
- </style>
1774
- </head>
1775
- <body>
1776
- <div class="container">
1777
- <div class="icon-container">
1778
- <div class="cross">\u2717</div>
1779
- </div>
1780
- <h1>Login Failed</h1>
1781
- <p>No token received. Please try again.</p>
1782
- </div>
1783
- </body>
1784
- </html>
1785
- `);
1786
- }
1787
- }
1571
+ const data = await response.json();
1572
+ if (data.access_token) {
1573
+ return data.access_token;
1574
+ }
1575
+ if (data.error) {
1576
+ switch (data.error) {
1577
+ case "authorization_pending":
1578
+ break;
1579
+ case "slow_down":
1580
+ pollingInterval += 5;
1581
+ break;
1582
+ case "access_denied":
1583
+ throw new Error("Authorization was denied by the user.");
1584
+ case "expired_token":
1585
+ throw new Error("The device code has expired. Please try again.");
1586
+ default:
1587
+ throw new Error(
1588
+ data.error_description || `Device auth error: ${data.error}`
1589
+ );
1788
1590
  }
1789
- );
1790
- server.listen(port, () => {
1791
- resolve2({ server, token: tokenPromise });
1792
- });
1793
- server.on("error", reject);
1794
- });
1591
+ }
1592
+ }
1593
+ throw new Error("Login timed out. Please try again.");
1795
1594
  }
1796
1595
  async function promptOrgSelection(profiles, defaultProfileId) {
1797
1596
  if (profiles.length === 0) return null;
@@ -1834,64 +1633,76 @@ Enter number [${defaultDisplay}]: `),
1834
1633
  }
1835
1634
  async function loginCommand(options) {
1836
1635
  try {
1636
+ const directKey = options?.apiKey || process.env.MCP_USE_API_KEY;
1637
+ if (directKey) {
1638
+ await writeConfig({ apiKey: directKey });
1639
+ if (!options?.silent) {
1640
+ console.log(source_default.green.bold("\u2713 API key saved."));
1641
+ try {
1642
+ const api2 = await McpUseAPI.create();
1643
+ const authInfo = await api2.testAuth();
1644
+ console.log(source_default.gray(` Authenticated as ${authInfo.email}`));
1645
+ } catch {
1646
+ console.log(
1647
+ source_default.gray(
1648
+ " (could not verify key \u2014 will be checked on next command)"
1649
+ )
1650
+ );
1651
+ }
1652
+ }
1653
+ return;
1654
+ }
1837
1655
  if (await isLoggedIn()) {
1838
1656
  if (!options?.silent) {
1839
1657
  console.log(
1840
1658
  source_default.yellow(
1841
- "\u26A0\uFE0F You are already logged in. Run 'npx mcp-use logout' first if you want to login with a different account."
1659
+ "You are already logged in. Run 'npx mcp-use logout' first if you want to login with a different account."
1842
1660
  )
1843
1661
  );
1844
1662
  }
1845
1663
  return;
1846
1664
  }
1847
- console.log(source_default.cyan.bold("\u{1F510} Logging in to Manufact cloud...\n"));
1848
- const state = crypto.randomBytes(32).toString("hex");
1849
- const port = await findAvailablePort();
1850
- const redirectUri = `http://localhost:${port}/callback`;
1851
- console.log(source_default.gray(`Starting local server on port ${port}...`));
1852
- const { server, token } = await startCallbackServer(port, state);
1853
- const webUrl = await getWebUrl();
1854
- const loginUrl = `${webUrl}/auth/cli?redirect_uri=${encodeURIComponent(redirectUri)}&state=${state}`;
1855
- console.log(source_default.gray(`Opening browser to ${webUrl}/auth/cli...
1856
- `));
1857
- console.log(
1858
- source_default.white(
1859
- "If the browser doesn't open automatically, please visit:\n" + source_default.cyan(loginUrl)
1860
- )
1861
- );
1862
- await open_default(loginUrl);
1863
- console.log(
1864
- source_default.gray("\nWaiting for authentication... (this may take a moment)")
1865
- );
1866
- const jwtToken = await Promise.race([
1867
- token,
1868
- new Promise(
1869
- (_, reject) => setTimeout(
1870
- () => reject(new Error("Login timeout - please try again")),
1871
- LOGIN_TIMEOUT
1872
- )
1873
- )
1874
- ]);
1875
- server.close();
1876
- console.log(
1877
- source_default.gray("Received authentication token, creating API key...")
1665
+ console.log(source_default.cyan.bold("Logging in to mcp-use cloud...\n"));
1666
+ const authBaseUrl = await getAuthBaseUrl();
1667
+ const deviceResp = await requestDeviceCode(authBaseUrl);
1668
+ const {
1669
+ device_code,
1670
+ user_code,
1671
+ verification_uri,
1672
+ verification_uri_complete,
1673
+ interval
1674
+ } = deviceResp;
1675
+ const displayCode = user_code.length === 8 ? `${user_code.slice(0, 4)}-${user_code.slice(4)}` : user_code;
1676
+ console.log(source_default.white(" Visit: ") + source_default.cyan(verification_uri));
1677
+ console.log(source_default.white(" Code: ") + source_default.bold.white(displayCode));
1678
+ console.log();
1679
+ const urlToOpen = verification_uri_complete || verification_uri;
1680
+ try {
1681
+ await open_default(urlToOpen);
1682
+ console.log(source_default.gray(" Browser opened. Waiting for approval..."));
1683
+ } catch {
1684
+ console.log(source_default.gray(" Open the URL above in your browser."));
1685
+ }
1686
+ const accessToken = await pollForDeviceToken(
1687
+ authBaseUrl,
1688
+ device_code,
1689
+ interval || 5
1878
1690
  );
1691
+ console.log(source_default.gray("\n Creating persistent API key..."));
1879
1692
  const api = await McpUseAPI.create();
1880
- const apiKeyResponse = await api.createApiKey(jwtToken, "CLI");
1881
- await writeConfig({
1882
- apiKey: apiKeyResponse.api_key
1883
- });
1693
+ const keyResp = await api.createApiKeyWithAccessToken(accessToken, "CLI");
1694
+ await writeConfig({ apiKey: keyResp.key });
1884
1695
  console.log(source_default.green.bold("\n\u2713 Successfully logged in!"));
1885
1696
  try {
1886
- const api2 = await McpUseAPI.create();
1887
- const authInfo = await api2.testAuth();
1888
- console.log(source_default.cyan.bold("\n\u{1F464} Current user:\n"));
1889
- console.log(source_default.white("Email: ") + source_default.cyan(authInfo.email));
1890
- console.log(source_default.white("User ID: ") + source_default.gray(authInfo.user_id));
1891
- const apiKey = await getApiKey();
1892
- if (apiKey) {
1893
- const masked = apiKey.substring(0, 6) + "...";
1894
- console.log(source_default.white("API Key: ") + source_default.gray(masked));
1697
+ const freshApi = await McpUseAPI.create();
1698
+ const authInfo = await freshApi.testAuth();
1699
+ console.log(source_default.cyan.bold("\nCurrent user:\n"));
1700
+ console.log(source_default.white(" Email: ") + source_default.cyan(authInfo.email));
1701
+ console.log(source_default.white(" User ID: ") + source_default.gray(authInfo.user_id));
1702
+ const storedKey = await getApiKey();
1703
+ if (storedKey) {
1704
+ const masked = storedKey.substring(0, 8) + "...";
1705
+ console.log(source_default.white(" API Key: ") + source_default.gray(masked));
1895
1706
  }
1896
1707
  const profiles = authInfo.profiles ?? [];
1897
1708
  if (profiles.length > 0) {
@@ -1914,25 +1725,25 @@ async function loginCommand(options) {
1914
1725
  });
1915
1726
  const slug = selectedProfile.slug ? source_default.gray(` (${selectedProfile.slug})`) : "";
1916
1727
  console.log(
1917
- source_default.white("Org: ") + source_default.cyan(selectedProfile.profile_name) + slug
1728
+ source_default.white(" Org: ") + source_default.cyan(selectedProfile.profile_name) + slug
1918
1729
  );
1919
1730
  }
1920
1731
  }
1921
- } catch (error) {
1732
+ } catch {
1922
1733
  console.log(
1923
1734
  source_default.gray(
1924
1735
  `
1925
- Your API key has been saved to ${source_default.white("~/.mcp-use/config.json")}`
1736
+ Your API key has been saved to ${source_default.white("~/.mcp-use/config.json")}`
1926
1737
  )
1927
1738
  );
1928
1739
  }
1929
1740
  console.log(
1930
1741
  source_default.gray(
1931
- "\nYou can now deploy your MCP servers with " + source_default.white("npx mcp-use deploy")
1742
+ "\n Deploy your MCP servers with " + source_default.white("npx mcp-use deploy")
1932
1743
  )
1933
1744
  );
1934
1745
  console.log(
1935
- source_default.gray("To logout later, run " + source_default.white("npx mcp-use logout"))
1746
+ source_default.gray(" To logout, run " + source_default.white("npx mcp-use logout"))
1936
1747
  );
1937
1748
  } catch (error) {
1938
1749
  throw new Error(
@@ -2005,10 +1816,20 @@ async function whoamiCommand() {
2005
1816
  }
2006
1817
  }
2007
1818
  } catch (error) {
2008
- console.error(
2009
- source_default.red.bold("\n\u2717 Failed to get user info:"),
2010
- source_default.red(error instanceof Error ? error.message : "Unknown error")
2011
- );
1819
+ if (error?.status === 401) {
1820
+ console.error(
1821
+ source_default.red("\nYour session has expired or your API key is invalid.")
1822
+ );
1823
+ console.log(
1824
+ source_default.gray(`Run ${source_default.white("mcp-use login")} to re-authenticate.
1825
+ `)
1826
+ );
1827
+ } else {
1828
+ console.error(
1829
+ source_default.red.bold("\n\u2717 Failed to get user info:"),
1830
+ source_default.red(error instanceof Error ? error.message : "Unknown error")
1831
+ );
1832
+ }
2012
1833
  process.exit(1);
2013
1834
  }
2014
1835
  }
@@ -5169,7 +4990,7 @@ async function isPortAvailable(port, host = "localhost") {
5169
4990
  return true;
5170
4991
  }
5171
4992
  }
5172
- async function findAvailablePort2(startPort, host = "localhost") {
4993
+ async function findAvailablePort(startPort, host = "localhost") {
5173
4994
  for (let port = startPort; port < startPort + 100; port++) {
5174
4995
  if (await isPortAvailable(port, host)) {
5175
4996
  return port;
@@ -5460,7 +5281,7 @@ if (container && Component) {
5460
5281
  `${widgetName}-metadata`
5461
5282
  );
5462
5283
  await fs10.mkdir(metadataTempDir, { recursive: true });
5463
- const { createServer: createServer2 } = await import("vite");
5284
+ const { createServer } = await import("vite");
5464
5285
  const nodeStubsPlugin = {
5465
5286
  name: "node-stubs",
5466
5287
  enforce: "pre",
@@ -5487,7 +5308,7 @@ export default PostHog;
5487
5308
  return null;
5488
5309
  }
5489
5310
  };
5490
- const metadataServer = await createServer2({
5311
+ const metadataServer = await createServer({
5491
5312
  root: metadataTempDir,
5492
5313
  cacheDir: path7.join(metadataTempDir, ".vite-cache"),
5493
5314
  plugins: [nodeStubsPlugin, tailwindcss(), react()],
@@ -6052,7 +5873,7 @@ program.command("dev").description("Run development server with auto-reload and
6052
5873
  displayPackageVersions(projectPath);
6053
5874
  if (!await isPortAvailable(port, host)) {
6054
5875
  console.log(source_default.yellow.bold(`\u26A0\uFE0F Port ${port} is already in use`));
6055
- const availablePort = await findAvailablePort2(port, host);
5876
+ const availablePort = await findAvailablePort(port, host);
6056
5877
  console.log(source_default.green.bold(`\u2713 Using port ${availablePort} instead`));
6057
5878
  port = availablePort;
6058
5879
  }
@@ -6887,9 +6708,12 @@ Looked for:
6887
6708
  process.exit(1);
6888
6709
  }
6889
6710
  });
6890
- program.command("login").description("Login to Manufact cloud").action(async () => {
6711
+ program.command("login").description("Login to mcp-use cloud").option(
6712
+ "--api-key <key>",
6713
+ "Login with an API key directly (non-interactive, for CI/CD)"
6714
+ ).action(async (opts) => {
6891
6715
  try {
6892
- await loginCommand();
6716
+ await loginCommand({ apiKey: opts.apiKey });
6893
6717
  process.exit(0);
6894
6718
  } catch (error) {
6895
6719
  console.error(