@mcp-use/cli 2.21.5-canary.2 → 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.
@@ -5,10 +5,16 @@ import type { ProfileInfo } from "../utils/api.js";
5
5
  */
6
6
  export declare function promptOrgSelection(profiles: ProfileInfo[], defaultProfileId?: string | null): Promise<ProfileInfo | null>;
7
7
  /**
8
- * Login command - opens browser for OAuth flow
8
+ * Login command using OAuth 2.0 Device Authorization Grant (RFC 8628).
9
+ *
10
+ * Supports three modes:
11
+ * - Interactive device flow (default): opens browser, user enters a code
12
+ * - --api-key <key>: non-interactive, stores the given key directly
13
+ * - MCP_USE_API_KEY env var: same as --api-key (for CI/CD)
9
14
  */
10
15
  export declare function loginCommand(options?: {
11
16
  silent?: boolean;
17
+ apiKey?: string;
12
18
  }): Promise<void>;
13
19
  /**
14
20
  * Logout command - revokes API key and deletes config
@@ -1 +1 @@
1
- {"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../src/commands/auth.ts"],"names":[],"mappings":"AAiBA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAgSnD;;;GAGG;AACH,wBAAsB,kBAAkB,CACtC,QAAQ,EAAE,WAAW,EAAE,EACvB,gBAAgB,CAAC,EAAE,MAAM,GAAG,IAAI,GAC/B,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC,CA6C7B;AAED;;GAEG;AACH,wBAAsB,YAAY,CAAC,OAAO,CAAC,EAAE;IAC3C,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB,GAAG,OAAO,CAAC,IAAI,CAAC,CAuJhB;AAED;;GAEG;AACH,wBAAsB,aAAa,IAAI,OAAO,CAAC,IAAI,CAAC,CA8BnD;AAED;;GAEG;AACH,wBAAsB,aAAa,IAAI,OAAO,CAAC,IAAI,CAAC,CA+DnD"}
1
+ {"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../src/commands/auth.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAoGnD;;;GAGG;AACH,wBAAsB,kBAAkB,CACtC,QAAQ,EAAE,WAAW,EAAE,EACvB,gBAAgB,CAAC,EAAE,MAAM,GAAG,IAAI,GAC/B,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC,CA6C7B;AAED;;;;;;;GAOG;AACH,wBAAsB,YAAY,CAAC,OAAO,CAAC,EAAE;IAC3C,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,GAAG,OAAO,CAAC,IAAI,CAAC,CAyJhB;AAED;;GAEG;AACH,wBAAsB,aAAa,IAAI,OAAO,CAAC,IAAI,CAAC,CA8BnD;AAED;;GAEG;AACH,wBAAsB,aAAa,IAAI,OAAO,CAAC,IAAI,CAAC,CAwEnD"}
package/dist/index.cjs CHANGED
@@ -1150,10 +1150,6 @@ var open_default = open;
1150
1150
  var import_vite_plugin_singlefile = require("vite-plugin-singlefile");
1151
1151
  var import_zod = require("zod");
1152
1152
 
1153
- // src/commands/auth.ts
1154
- var import_node_crypto = __toESM(require("crypto"), 1);
1155
- var import_node_http = require("http");
1156
-
1157
1153
  // src/utils/config.ts
1158
1154
  var import_node_fs4 = require("fs");
1159
1155
  var import_node_os3 = __toESM(require("os"), 1);
@@ -1205,6 +1201,10 @@ async function getProfileId() {
1205
1201
  async function getWebUrl() {
1206
1202
  return DEFAULT_WEB_URL;
1207
1203
  }
1204
+ async function getAuthBaseUrl() {
1205
+ const apiUrl = await getApiUrl();
1206
+ return apiUrl.replace(/\/api\/v1$/, "");
1207
+ }
1208
1208
 
1209
1209
  // src/utils/api.ts
1210
1210
  var McpUseAPI = class _McpUseAPI {
@@ -1256,6 +1256,13 @@ var McpUseAPI = class _McpUseAPI {
1256
1256
  signal: controller.signal
1257
1257
  });
1258
1258
  clearTimeout(timeoutId);
1259
+ if (response.status === 401) {
1260
+ const err = new Error(
1261
+ "Your session has expired or your API key is invalid."
1262
+ );
1263
+ err.status = 401;
1264
+ throw err;
1265
+ }
1259
1266
  if (!response.ok) {
1260
1267
  const error = await response.text();
1261
1268
  throw new Error(`API request failed: ${response.status} ${error}`);
@@ -1272,17 +1279,19 @@ var McpUseAPI = class _McpUseAPI {
1272
1279
  }
1273
1280
  }
1274
1281
  /**
1275
- * Create API key using JWT token
1282
+ * Create a persistent API key using a Better Auth access token.
1283
+ * Calls Better Auth's built-in POST /api/auth/api-key/create endpoint.
1276
1284
  */
1277
- async createApiKey(jwtToken, name = "CLI") {
1278
- const url = `${this.baseUrl}/api-key`;
1285
+ async createApiKeyWithAccessToken(accessToken, name = "CLI") {
1286
+ const authBase = await getAuthBaseUrl();
1287
+ const url = `${authBase}/api/auth/api-key/create`;
1279
1288
  const response = await fetch(url, {
1280
1289
  method: "POST",
1281
1290
  headers: {
1282
1291
  "Content-Type": "application/json",
1283
- Authorization: `Bearer ${jwtToken}`
1292
+ Authorization: `Bearer ${accessToken}`
1284
1293
  },
1285
- body: JSON.stringify({ name })
1294
+ body: JSON.stringify({ name, prefix: "mcp_" })
1286
1295
  });
1287
1296
  if (!response.ok) {
1288
1297
  const error = await response.text();
@@ -1543,273 +1552,65 @@ var McpUseAPI = class _McpUseAPI {
1543
1552
  };
1544
1553
 
1545
1554
  // src/commands/auth.ts
1546
- var LOGIN_TIMEOUT = 3e5;
1547
- async function findAvailablePort(startPort = 8765) {
1548
- for (let port = startPort; port < startPort + 100; port++) {
1549
- try {
1550
- await new Promise((resolve2, reject) => {
1551
- const server = (0, import_node_http.createServer)();
1552
- server.once("error", reject);
1553
- server.once("listening", () => {
1554
- server.close();
1555
- resolve2();
1556
- });
1557
- server.listen(port);
1558
- });
1559
- return port;
1560
- } catch {
1561
- continue;
1562
- }
1555
+ var DEVICE_CLIENT_ID = "mcp-use-cli";
1556
+ var DEVICE_POLL_TIMEOUT = 18e5;
1557
+ async function requestDeviceCode(authBaseUrl) {
1558
+ const url = `${authBaseUrl}/api/auth/device/code`;
1559
+ const response = await fetch(url, {
1560
+ method: "POST",
1561
+ headers: { "Content-Type": "application/json" },
1562
+ body: JSON.stringify({
1563
+ client_id: DEVICE_CLIENT_ID,
1564
+ scope: "openid profile email"
1565
+ })
1566
+ });
1567
+ if (!response.ok) {
1568
+ const error = await response.text();
1569
+ throw new Error(
1570
+ `Failed to request device code: ${response.status} ${error}`
1571
+ );
1563
1572
  }
1564
- throw new Error("No available ports found");
1573
+ return response.json();
1565
1574
  }
1566
- async function startCallbackServer(port, expectedState) {
1567
- return new Promise((resolve2, reject) => {
1568
- let tokenResolver = null;
1569
- const tokenPromise = new Promise((res) => {
1570
- tokenResolver = res;
1575
+ async function pollForDeviceToken(authBaseUrl, deviceCode, intervalSeconds) {
1576
+ let pollingInterval = intervalSeconds;
1577
+ const deadline = Date.now() + DEVICE_POLL_TIMEOUT;
1578
+ while (Date.now() < deadline) {
1579
+ const delayMs = pollingInterval * 1e3;
1580
+ await new Promise((r) => setTimeout(r, delayMs));
1581
+ const url = `${authBaseUrl}/api/auth/device/token`;
1582
+ const response = await fetch(url, {
1583
+ method: "POST",
1584
+ headers: { "Content-Type": "application/json" },
1585
+ body: JSON.stringify({
1586
+ grant_type: "urn:ietf:params:oauth:grant-type:device_code",
1587
+ device_code: deviceCode,
1588
+ client_id: DEVICE_CLIENT_ID
1589
+ })
1571
1590
  });
1572
- const server = (0, import_node_http.createServer)(
1573
- {
1574
- maxHeaderSize: 65536
1575
- // 64KB - handle very long JWT tokens in URL (increased from default 8192)
1576
- },
1577
- (req, res) => {
1578
- if (req.url?.startsWith("/callback")) {
1579
- const url = new URL(req.url, `http://localhost:${port}`);
1580
- const token = url.searchParams.get("token");
1581
- const state = url.searchParams.get("state");
1582
- if (state !== expectedState) {
1583
- res.writeHead(400, { "Content-Type": "text/html" });
1584
- res.end(`
1585
- <!DOCTYPE html>
1586
- <html>
1587
- <head>
1588
- <title>Security Error</title>
1589
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
1590
- <style>
1591
- body {
1592
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
1593
- display: flex;
1594
- justify-content: center;
1595
- align-items: center;
1596
- min-height: 100vh;
1597
- background: #000;
1598
- padding: 1rem;
1599
- margin: 0;
1600
- }
1601
- .container {
1602
- max-width: 28rem;
1603
- padding: 3rem;
1604
- text-align: center;
1605
- background: rgba(255, 255, 255, 0.1);
1606
- backdrop-filter: blur(40px);
1607
- border: 1px solid rgba(255, 255, 255, 0.2);
1608
- border-radius: 1.5rem;
1609
- }
1610
- h1 { color: #fff; font-size: 2rem; margin-bottom: 1rem; }
1611
- p { color: rgba(255, 255, 255, 0.8); font-size: 1rem; }
1612
- </style>
1613
- </head>
1614
- <body>
1615
- <div class="container">
1616
- <h1>Security Error</h1>
1617
- <p>Invalid state parameter. Please try logging in again.</p>
1618
- </div>
1619
- </body>
1620
- </html>
1621
- `);
1622
- return;
1623
- }
1624
- if (token && tokenResolver) {
1625
- res.writeHead(200, { "Content-Type": "text/html" });
1626
- res.end(`
1627
- <!DOCTYPE html>
1628
- <html>
1629
- <head>
1630
- <title>Login Successful</title>
1631
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
1632
- <style>
1633
- * {
1634
- margin: 0;
1635
- padding: 0;
1636
- box-sizing: border-box;
1637
- }
1638
- body {
1639
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
1640
- display: flex;
1641
- justify-content: center;
1642
- align-items: center;
1643
- min-height: 100vh;
1644
- background: #000;
1645
- padding: 1rem;
1646
- }
1647
- .container {
1648
- width: 100%;
1649
- max-width: 28rem;
1650
- padding: 3rem;
1651
- text-align: center;
1652
- -webkit-backdrop-filter: blur(40px);
1653
- border: 1px solid rgba(255, 255, 255, 0.2);
1654
- border-radius: 1.5rem;
1655
- box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.5);
1656
- }
1657
- .icon-container {
1658
- display: inline-flex;
1659
- align-items: center;
1660
- justify-content: center;
1661
- width: 6rem;
1662
- height: 6rem;
1663
- margin-bottom: 2rem;
1664
- background: rgba(255, 255, 255, 0.1);
1665
- backdrop-filter: blur(10px);
1666
- -webkit-backdrop-filter: blur(10px);
1667
- border-radius: 50%;
1668
- }
1669
- .checkmark {
1670
- font-size: 4rem;
1671
- color: #fff;
1672
- line-height: 1;
1673
- animation: scaleIn 0.5s ease-out;
1674
- }
1675
- @keyframes scaleIn {
1676
- from {
1677
- transform: scale(0);
1678
- opacity: 0;
1679
- }
1680
- to {
1681
- transform: scale(1);
1682
- opacity: 1;
1683
- }
1684
- }
1685
- h1 {
1686
- color: #fff;
1687
- margin: 0 0 1rem 0;
1688
- font-size: 2.5rem;
1689
- font-weight: 700;
1690
- letter-spacing: -0.025em;
1691
- }
1692
- p {
1693
- color: rgba(255, 255, 255, 0.8);
1694
- margin: 0 0 2rem 0;
1695
- font-size: 1.125rem;
1696
- line-height: 1.5;
1697
- }
1698
- .spinner {
1699
- display: inline-block;
1700
- width: 2rem;
1701
- height: 2rem;
1702
- border: 3px solid rgba(255, 255, 255, 0.3);
1703
- border-top-color: #fff;
1704
- border-radius: 50%;
1705
- animation: spin 0.8s linear infinite;
1706
- }
1707
- @keyframes spin {
1708
- to { transform: rotate(360deg); }
1709
- }
1710
- .footer {
1711
- margin-top: 2rem;
1712
- color: rgba(255, 255, 255, 0.6);
1713
- font-size: 0.875rem;
1714
- }
1715
- </style>
1716
- </head>
1717
- <body>
1718
- <div class="container">
1719
- <h1>Authentication Successful!</h1>
1720
- <p>You can now close this window and return to the CLI.</p>
1721
- </div>
1722
- </body>
1723
- </html>
1724
- `);
1725
- tokenResolver(token);
1726
- } else {
1727
- res.writeHead(400, { "Content-Type": "text/html" });
1728
- res.end(`
1729
- <!DOCTYPE html>
1730
- <html>
1731
- <head>
1732
- <title>Login Failed</title>
1733
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
1734
- <style>
1735
- * {
1736
- margin: 0;
1737
- padding: 0;
1738
- box-sizing: border-box;
1739
- }
1740
- body {
1741
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
1742
- display: flex;
1743
- justify-content: center;
1744
- align-items: center;
1745
- min-height: 100vh;
1746
- background: #000;
1747
- padding: 1rem;
1748
- }
1749
- .container {
1750
- width: 100%;
1751
- max-width: 28rem;
1752
- padding: 3rem;
1753
- text-align: center;
1754
- background: rgba(255, 255, 255, 0.1);
1755
- backdrop-filter: blur(40px);
1756
- -webkit-backdrop-filter: blur(40px);
1757
- border: 1px solid rgba(255, 255, 255, 0.2);
1758
- border-radius: 1.5rem;
1759
- box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.5);
1760
- }
1761
- .icon-container {
1762
- display: inline-flex;
1763
- align-items: center;
1764
- justify-content: center;
1765
- width: 6rem;
1766
- height: 6rem;
1767
- margin-bottom: 2rem;
1768
- background: rgba(255, 255, 255, 0.1);
1769
- backdrop-filter: blur(10px);
1770
- -webkit-backdrop-filter: blur(10px);
1771
- border-radius: 50%;
1772
- }
1773
- .cross {
1774
- font-size: 4rem;
1775
- color: #fff;
1776
- line-height: 1;
1777
- }
1778
- h1 {
1779
- color: #fff;
1780
- margin: 0 0 1rem 0;
1781
- font-size: 2.5rem;
1782
- font-weight: 700;
1783
- letter-spacing: -0.025em;
1784
- }
1785
- p {
1786
- color: rgba(255, 255, 255, 0.8);
1787
- margin: 0;
1788
- font-size: 1.125rem;
1789
- line-height: 1.5;
1790
- }
1791
- </style>
1792
- </head>
1793
- <body>
1794
- <div class="container">
1795
- <div class="icon-container">
1796
- <div class="cross">\u2717</div>
1797
- </div>
1798
- <h1>Login Failed</h1>
1799
- <p>No token received. Please try again.</p>
1800
- </div>
1801
- </body>
1802
- </html>
1803
- `);
1804
- }
1805
- }
1591
+ const data = await response.json();
1592
+ if (data.access_token) {
1593
+ return data.access_token;
1594
+ }
1595
+ if (data.error) {
1596
+ switch (data.error) {
1597
+ case "authorization_pending":
1598
+ break;
1599
+ case "slow_down":
1600
+ pollingInterval += 5;
1601
+ break;
1602
+ case "access_denied":
1603
+ throw new Error("Authorization was denied by the user.");
1604
+ case "expired_token":
1605
+ throw new Error("The device code has expired. Please try again.");
1606
+ default:
1607
+ throw new Error(
1608
+ data.error_description || `Device auth error: ${data.error}`
1609
+ );
1806
1610
  }
1807
- );
1808
- server.listen(port, () => {
1809
- resolve2({ server, token: tokenPromise });
1810
- });
1811
- server.on("error", reject);
1812
- });
1611
+ }
1612
+ }
1613
+ throw new Error("Login timed out. Please try again.");
1813
1614
  }
1814
1615
  async function promptOrgSelection(profiles, defaultProfileId) {
1815
1616
  if (profiles.length === 0) return null;
@@ -1852,64 +1653,76 @@ Enter number [${defaultDisplay}]: `),
1852
1653
  }
1853
1654
  async function loginCommand(options) {
1854
1655
  try {
1656
+ const directKey = options?.apiKey || process.env.MCP_USE_API_KEY;
1657
+ if (directKey) {
1658
+ await writeConfig({ apiKey: directKey });
1659
+ if (!options?.silent) {
1660
+ console.log(source_default.green.bold("\u2713 API key saved."));
1661
+ try {
1662
+ const api2 = await McpUseAPI.create();
1663
+ const authInfo = await api2.testAuth();
1664
+ console.log(source_default.gray(` Authenticated as ${authInfo.email}`));
1665
+ } catch {
1666
+ console.log(
1667
+ source_default.gray(
1668
+ " (could not verify key \u2014 will be checked on next command)"
1669
+ )
1670
+ );
1671
+ }
1672
+ }
1673
+ return;
1674
+ }
1855
1675
  if (await isLoggedIn()) {
1856
1676
  if (!options?.silent) {
1857
1677
  console.log(
1858
1678
  source_default.yellow(
1859
- "\u26A0\uFE0F You are already logged in. Run 'npx mcp-use logout' first if you want to login with a different account."
1679
+ "You are already logged in. Run 'npx mcp-use logout' first if you want to login with a different account."
1860
1680
  )
1861
1681
  );
1862
1682
  }
1863
1683
  return;
1864
1684
  }
1865
- console.log(source_default.cyan.bold("\u{1F510} Logging in to Manufact cloud...\n"));
1866
- const state = import_node_crypto.default.randomBytes(32).toString("hex");
1867
- const port = await findAvailablePort();
1868
- const redirectUri = `http://localhost:${port}/callback`;
1869
- console.log(source_default.gray(`Starting local server on port ${port}...`));
1870
- const { server, token } = await startCallbackServer(port, state);
1871
- const webUrl = await getWebUrl();
1872
- const loginUrl = `${webUrl}/auth/cli?redirect_uri=${encodeURIComponent(redirectUri)}&state=${state}`;
1873
- console.log(source_default.gray(`Opening browser to ${webUrl}/auth/cli...
1874
- `));
1875
- console.log(
1876
- source_default.white(
1877
- "If the browser doesn't open automatically, please visit:\n" + source_default.cyan(loginUrl)
1878
- )
1879
- );
1880
- await open_default(loginUrl);
1881
- console.log(
1882
- source_default.gray("\nWaiting for authentication... (this may take a moment)")
1883
- );
1884
- const jwtToken = await Promise.race([
1885
- token,
1886
- new Promise(
1887
- (_, reject) => setTimeout(
1888
- () => reject(new Error("Login timeout - please try again")),
1889
- LOGIN_TIMEOUT
1890
- )
1891
- )
1892
- ]);
1893
- server.close();
1894
- console.log(
1895
- source_default.gray("Received authentication token, creating API key...")
1685
+ console.log(source_default.cyan.bold("Logging in to mcp-use cloud...\n"));
1686
+ const authBaseUrl = await getAuthBaseUrl();
1687
+ const deviceResp = await requestDeviceCode(authBaseUrl);
1688
+ const {
1689
+ device_code,
1690
+ user_code,
1691
+ verification_uri,
1692
+ verification_uri_complete,
1693
+ interval
1694
+ } = deviceResp;
1695
+ const displayCode = user_code.length === 8 ? `${user_code.slice(0, 4)}-${user_code.slice(4)}` : user_code;
1696
+ console.log(source_default.white(" Visit: ") + source_default.cyan(verification_uri));
1697
+ console.log(source_default.white(" Code: ") + source_default.bold.white(displayCode));
1698
+ console.log();
1699
+ const urlToOpen = verification_uri_complete || verification_uri;
1700
+ try {
1701
+ await open_default(urlToOpen);
1702
+ console.log(source_default.gray(" Browser opened. Waiting for approval..."));
1703
+ } catch {
1704
+ console.log(source_default.gray(" Open the URL above in your browser."));
1705
+ }
1706
+ const accessToken = await pollForDeviceToken(
1707
+ authBaseUrl,
1708
+ device_code,
1709
+ interval || 5
1896
1710
  );
1711
+ console.log(source_default.gray("\n Creating persistent API key..."));
1897
1712
  const api = await McpUseAPI.create();
1898
- const apiKeyResponse = await api.createApiKey(jwtToken, "CLI");
1899
- await writeConfig({
1900
- apiKey: apiKeyResponse.api_key
1901
- });
1713
+ const keyResp = await api.createApiKeyWithAccessToken(accessToken, "CLI");
1714
+ await writeConfig({ apiKey: keyResp.key });
1902
1715
  console.log(source_default.green.bold("\n\u2713 Successfully logged in!"));
1903
1716
  try {
1904
- const api2 = await McpUseAPI.create();
1905
- const authInfo = await api2.testAuth();
1906
- console.log(source_default.cyan.bold("\n\u{1F464} Current user:\n"));
1907
- console.log(source_default.white("Email: ") + source_default.cyan(authInfo.email));
1908
- console.log(source_default.white("User ID: ") + source_default.gray(authInfo.user_id));
1909
- const apiKey = await getApiKey();
1910
- if (apiKey) {
1911
- const masked = apiKey.substring(0, 6) + "...";
1912
- console.log(source_default.white("API Key: ") + source_default.gray(masked));
1717
+ const freshApi = await McpUseAPI.create();
1718
+ const authInfo = await freshApi.testAuth();
1719
+ console.log(source_default.cyan.bold("\nCurrent user:\n"));
1720
+ console.log(source_default.white(" Email: ") + source_default.cyan(authInfo.email));
1721
+ console.log(source_default.white(" User ID: ") + source_default.gray(authInfo.user_id));
1722
+ const storedKey = await getApiKey();
1723
+ if (storedKey) {
1724
+ const masked = storedKey.substring(0, 8) + "...";
1725
+ console.log(source_default.white(" API Key: ") + source_default.gray(masked));
1913
1726
  }
1914
1727
  const profiles = authInfo.profiles ?? [];
1915
1728
  if (profiles.length > 0) {
@@ -1932,25 +1745,25 @@ async function loginCommand(options) {
1932
1745
  });
1933
1746
  const slug = selectedProfile.slug ? source_default.gray(` (${selectedProfile.slug})`) : "";
1934
1747
  console.log(
1935
- source_default.white("Org: ") + source_default.cyan(selectedProfile.profile_name) + slug
1748
+ source_default.white(" Org: ") + source_default.cyan(selectedProfile.profile_name) + slug
1936
1749
  );
1937
1750
  }
1938
1751
  }
1939
- } catch (error) {
1752
+ } catch {
1940
1753
  console.log(
1941
1754
  source_default.gray(
1942
1755
  `
1943
- Your API key has been saved to ${source_default.white("~/.mcp-use/config.json")}`
1756
+ Your API key has been saved to ${source_default.white("~/.mcp-use/config.json")}`
1944
1757
  )
1945
1758
  );
1946
1759
  }
1947
1760
  console.log(
1948
1761
  source_default.gray(
1949
- "\nYou can now deploy your MCP servers with " + source_default.white("npx mcp-use deploy")
1762
+ "\n Deploy your MCP servers with " + source_default.white("npx mcp-use deploy")
1950
1763
  )
1951
1764
  );
1952
1765
  console.log(
1953
- source_default.gray("To logout later, run " + source_default.white("npx mcp-use logout"))
1766
+ source_default.gray(" To logout, run " + source_default.white("npx mcp-use logout"))
1954
1767
  );
1955
1768
  } catch (error) {
1956
1769
  throw new Error(
@@ -2023,10 +1836,20 @@ async function whoamiCommand() {
2023
1836
  }
2024
1837
  }
2025
1838
  } catch (error) {
2026
- console.error(
2027
- source_default.red.bold("\n\u2717 Failed to get user info:"),
2028
- source_default.red(error instanceof Error ? error.message : "Unknown error")
2029
- );
1839
+ if (error?.status === 401) {
1840
+ console.error(
1841
+ source_default.red("\nYour session has expired or your API key is invalid.")
1842
+ );
1843
+ console.log(
1844
+ source_default.gray(`Run ${source_default.white("mcp-use login")} to re-authenticate.
1845
+ `)
1846
+ );
1847
+ } else {
1848
+ console.error(
1849
+ source_default.red.bold("\n\u2717 Failed to get user info:"),
1850
+ source_default.red(error instanceof Error ? error.message : "Unknown error")
1851
+ );
1852
+ }
2030
1853
  process.exit(1);
2031
1854
  }
2032
1855
  }
@@ -5187,7 +5010,7 @@ async function isPortAvailable(port, host = "localhost") {
5187
5010
  return true;
5188
5011
  }
5189
5012
  }
5190
- async function findAvailablePort2(startPort, host = "localhost") {
5013
+ async function findAvailablePort(startPort, host = "localhost") {
5191
5014
  for (let port = startPort; port < startPort + 100; port++) {
5192
5015
  if (await isPortAvailable(port, host)) {
5193
5016
  return port;
@@ -5478,7 +5301,7 @@ if (container && Component) {
5478
5301
  `${widgetName}-metadata`
5479
5302
  );
5480
5303
  await fs10.mkdir(metadataTempDir, { recursive: true });
5481
- const { createServer: createServer2 } = await import("vite");
5304
+ const { createServer } = await import("vite");
5482
5305
  const nodeStubsPlugin = {
5483
5306
  name: "node-stubs",
5484
5307
  enforce: "pre",
@@ -5505,7 +5328,7 @@ export default PostHog;
5505
5328
  return null;
5506
5329
  }
5507
5330
  };
5508
- const metadataServer = await createServer2({
5331
+ const metadataServer = await createServer({
5509
5332
  root: metadataTempDir,
5510
5333
  cacheDir: import_node_path8.default.join(metadataTempDir, ".vite-cache"),
5511
5334
  plugins: [nodeStubsPlugin, tailwindcss(), react()],
@@ -6070,7 +5893,7 @@ program.command("dev").description("Run development server with auto-reload and
6070
5893
  displayPackageVersions(projectPath);
6071
5894
  if (!await isPortAvailable(port, host)) {
6072
5895
  console.log(source_default.yellow.bold(`\u26A0\uFE0F Port ${port} is already in use`));
6073
- const availablePort = await findAvailablePort2(port, host);
5896
+ const availablePort = await findAvailablePort(port, host);
6074
5897
  console.log(source_default.green.bold(`\u2713 Using port ${availablePort} instead`));
6075
5898
  port = availablePort;
6076
5899
  }
@@ -6905,9 +6728,12 @@ Looked for:
6905
6728
  process.exit(1);
6906
6729
  }
6907
6730
  });
6908
- program.command("login").description("Login to Manufact cloud").action(async () => {
6731
+ program.command("login").description("Login to mcp-use cloud").option(
6732
+ "--api-key <key>",
6733
+ "Login with an API key directly (non-interactive, for CI/CD)"
6734
+ ).action(async (opts) => {
6909
6735
  try {
6910
- await loginCommand();
6736
+ await loginCommand({ apiKey: opts.apiKey });
6911
6737
  process.exit(0);
6912
6738
  } catch (error) {
6913
6739
  console.error(