@mcp-use/cli 3.3.2-canary.6 → 3.4.0-canary.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1369,6 +1369,80 @@ var McpUseAPI = class _McpUseAPI {
1369
1369
  body: JSON.stringify(body)
1370
1370
  });
1371
1371
  }
1372
+ /** Multipart helper: POST/PUT a tarball + fields without the JSON Content-Type. */
1373
+ async uploadMultipart(endpoint, form, timeout = 12e4) {
1374
+ const url = `${this.baseUrl}${endpoint}`;
1375
+ const headers = {
1376
+ "x-mcp-creation-location": "cli"
1377
+ };
1378
+ if (this.apiKey) headers["x-api-key"] = this.apiKey;
1379
+ if (this.orgId) headers["x-profile-id"] = this.orgId;
1380
+ const controller = new AbortController();
1381
+ const timeoutId = setTimeout(() => controller.abort(), timeout);
1382
+ try {
1383
+ const response = await fetch(url, {
1384
+ method: "POST",
1385
+ headers,
1386
+ body: form,
1387
+ signal: controller.signal
1388
+ });
1389
+ clearTimeout(timeoutId);
1390
+ if (response.status === 401) throw new ApiUnauthorizedError();
1391
+ if (!response.ok) {
1392
+ const errorText = await response.text();
1393
+ throw new Error(`API request failed: ${response.status} ${errorText}`);
1394
+ }
1395
+ return response.json();
1396
+ } catch (error) {
1397
+ clearTimeout(timeoutId);
1398
+ if (error.name === "AbortError") {
1399
+ throw new Error(`Request timeout after ${timeout / 1e3}s.`);
1400
+ }
1401
+ throw error;
1402
+ }
1403
+ }
1404
+ /**
1405
+ * Create a server from a source tarball deployed into the platform-managed
1406
+ * GitHub org (no user GitHub connection required).
1407
+ */
1408
+ async createServerFromManagedUpload(input) {
1409
+ const form = new FormData();
1410
+ form.set(
1411
+ "sourceFile",
1412
+ new Blob([new Uint8Array(input.tarball)], { type: "application/gzip" }),
1413
+ "source.tar.gz"
1414
+ );
1415
+ form.set("organizationId", input.organizationId);
1416
+ form.set("managed", "true");
1417
+ form.set("name", input.name);
1418
+ form.set("repoName", input.repoName);
1419
+ form.set("private", "true");
1420
+ form.set("branch", input.branch ?? "main");
1421
+ form.set("commitMessage", input.commitMessage ?? "Deploy from mcp-use CLI");
1422
+ if (input.port != null) form.set("port", String(input.port));
1423
+ if (input.env && Object.keys(input.env).length > 0) {
1424
+ form.set("env", JSON.stringify(input.env));
1425
+ }
1426
+ return this.uploadMultipart("/servers", form);
1427
+ }
1428
+ /** Push a new source tarball as a commit on an existing server's repo. */
1429
+ async pushSourceToServer(serverId, input) {
1430
+ const form = new FormData();
1431
+ form.set(
1432
+ "sourceFile",
1433
+ new Blob([new Uint8Array(input.tarball)], { type: "application/gzip" }),
1434
+ "source.tar.gz"
1435
+ );
1436
+ form.set("branch", input.branch ?? "main");
1437
+ form.set(
1438
+ "commitMessage",
1439
+ input.commitMessage ?? "Redeploy from mcp-use CLI"
1440
+ );
1441
+ return this.uploadMultipart(
1442
+ `/servers/${encodeURIComponent(serverId)}/source`,
1443
+ form
1444
+ );
1445
+ }
1372
1446
  async listServers(params) {
1373
1447
  const response = await this.request(`/servers${buildPaginationQuery(params)}`);
1374
1448
  return normalizePaginatedResponse(response, params);
@@ -1687,7 +1761,7 @@ async function loginCommand(options) {
1687
1761
  }
1688
1762
  return;
1689
1763
  }
1690
- if (await isLoggedIn()) {
1764
+ if (!options?.deviceCode && await isLoggedIn()) {
1691
1765
  let needsReauth = false;
1692
1766
  try {
1693
1767
  await (await McpUseAPI.create()).testAuth();
@@ -1717,29 +1791,33 @@ async function loginCommand(options) {
1717
1791
  }
1718
1792
  console.log(source_default.cyan.bold("Logging in to Manufact cloud...\n"));
1719
1793
  const authBaseUrl = await getAuthBaseUrl();
1720
- const deviceResp = await requestDeviceCode(authBaseUrl);
1721
- const {
1722
- device_code,
1723
- user_code,
1724
- verification_uri,
1725
- verification_uri_complete,
1726
- interval
1727
- } = deviceResp;
1728
- const displayCode = user_code.length === 8 ? `${user_code.slice(0, 4)}-${user_code.slice(4)}` : user_code;
1729
- console.log(source_default.white(" Visit: ") + source_default.cyan(verification_uri));
1730
- console.log(source_default.white(" Code: ") + source_default.bold.white(displayCode));
1731
- console.log();
1732
- const urlToOpen = verification_uri_complete || verification_uri;
1733
- try {
1734
- await open_default(urlToOpen);
1735
- console.log(source_default.gray(" Browser opened. Waiting for approval..."));
1736
- } catch {
1737
- console.log(source_default.gray(" Open the URL above in your browser."));
1794
+ let device_code;
1795
+ let interval;
1796
+ if (options?.deviceCode) {
1797
+ device_code = options.deviceCode.trim();
1798
+ interval = 2;
1799
+ console.log(source_default.gray(" Authenticating with provided device code..."));
1800
+ } else {
1801
+ const deviceResp = await requestDeviceCode(authBaseUrl);
1802
+ device_code = deviceResp.device_code;
1803
+ interval = deviceResp.interval || 5;
1804
+ const { user_code, verification_uri, verification_uri_complete } = deviceResp;
1805
+ const displayCode = user_code.length === 8 ? `${user_code.slice(0, 4)}-${user_code.slice(4)}` : user_code;
1806
+ console.log(source_default.white(" Visit: ") + source_default.cyan(verification_uri));
1807
+ console.log(source_default.white(" Code: ") + source_default.bold.white(displayCode));
1808
+ console.log();
1809
+ const urlToOpen = verification_uri_complete || verification_uri;
1810
+ try {
1811
+ await open_default(urlToOpen);
1812
+ console.log(source_default.gray(" Browser opened. Waiting for approval..."));
1813
+ } catch {
1814
+ console.log(source_default.gray(" Open the URL above in your browser."));
1815
+ }
1738
1816
  }
1739
1817
  const accessToken = await pollForDeviceToken(
1740
1818
  authBaseUrl,
1741
1819
  device_code,
1742
- interval || 5
1820
+ interval
1743
1821
  );
1744
1822
  console.log(source_default.gray("\n Creating persistent API key..."));
1745
1823
  const api = await McpUseAPI.create();
@@ -4699,6 +4777,49 @@ ${MCP_USE_DIR}
4699
4777
  }
4700
4778
  }
4701
4779
 
4780
+ // src/utils/tarball.ts
4781
+ import { create } from "tar";
4782
+ var EXCLUDE_DIRS = /* @__PURE__ */ new Set([
4783
+ ".git",
4784
+ "node_modules",
4785
+ "dist",
4786
+ "build",
4787
+ ".next",
4788
+ ".turbo",
4789
+ ".vercel",
4790
+ ".cache",
4791
+ "coverage",
4792
+ ".mcp-use"
4793
+ ]);
4794
+ async function packProjectTarball(projectDir, prefix = "app") {
4795
+ const stream = create(
4796
+ {
4797
+ gzip: true,
4798
+ cwd: projectDir,
4799
+ prefix,
4800
+ portable: true,
4801
+ filter: (entryPath) => {
4802
+ const segments = entryPath.split(/[/\\]/).filter((s) => s && s !== ".");
4803
+ if (segments.some((seg) => EXCLUDE_DIRS.has(seg))) return false;
4804
+ const base = segments[segments.length - 1] ?? "";
4805
+ if (base === ".DS_Store") return false;
4806
+ if (base === ".env" || base.startsWith(".env.")) return false;
4807
+ return true;
4808
+ }
4809
+ },
4810
+ ["."]
4811
+ );
4812
+ const chunks = [];
4813
+ for await (const chunk of stream) {
4814
+ chunks.push(Buffer.from(chunk));
4815
+ }
4816
+ return Buffer.concat(chunks);
4817
+ }
4818
+ function sanitizeRepoName(name) {
4819
+ const cleaned = name.trim().replace(/[^a-zA-Z0-9._-]+/g, "-").replace(/^[-.]+|[-.]+$/g, "").slice(0, 80);
4820
+ return cleaned || "mcp-server";
4821
+ }
4822
+
4702
4823
  // src/commands/deploy.ts
4703
4824
  async function parseEnvFile(filePath) {
4704
4825
  try {
@@ -5200,6 +5321,106 @@ Opening browser...`));
5200
5321
  return { ok: false, api: client };
5201
5322
  }
5202
5323
  }
5324
+ async function deployViaManagedUpload(api, options, ctx) {
5325
+ const { cwd, organizationId } = ctx;
5326
+ const projectDir = options.rootDir ? path8.resolve(cwd, options.rootDir) : cwd;
5327
+ try {
5328
+ await fs9.access(projectDir);
5329
+ } catch {
5330
+ console.log(source_default.red(`\u2717 Project directory not found: ${projectDir}`));
5331
+ process.exit(1);
5332
+ }
5333
+ const isMcp = await isMcpProject(projectDir);
5334
+ if (!isMcp && !options.yes) {
5335
+ console.log(
5336
+ source_default.yellow("\u26A0\uFE0F This doesn't look like an MCP server project.")
5337
+ );
5338
+ const shouldContinue = await prompt(
5339
+ source_default.white("Continue anyway? (y/n): ")
5340
+ );
5341
+ if (!shouldContinue) process.exit(0);
5342
+ console.log();
5343
+ }
5344
+ const envVars = await buildEnvVars(options);
5345
+ const branch = "main";
5346
+ const projectName = options.name || await getProjectName(projectDir);
5347
+ console.log(source_default.gray("Packaging project source..."));
5348
+ const tarball = await packProjectTarball(projectDir);
5349
+ console.log(
5350
+ source_default.gray(
5351
+ ` Archive size: ${(tarball.length / 1024 / 1024).toFixed(2)} MB`
5352
+ )
5353
+ );
5354
+ if (tarball.length > 80 * 1024 * 1024) {
5355
+ console.log(
5356
+ source_default.red(
5357
+ "\u2717 Project archive exceeds 80 MB. Add large/derived files to .gitignore and retry."
5358
+ )
5359
+ );
5360
+ process.exit(1);
5361
+ }
5362
+ const existingLink = !options.new ? await getProjectLink(cwd) : null;
5363
+ let serverId = existingLink?.serverId;
5364
+ if (serverId) {
5365
+ try {
5366
+ const linked = await api.getServer(serverId);
5367
+ if (linked.organizationId !== organizationId) serverId = void 0;
5368
+ } catch {
5369
+ serverId = void 0;
5370
+ }
5371
+ }
5372
+ let deploymentId;
5373
+ if (serverId) {
5374
+ console.log(source_default.gray("Uploading source and redeploying..."));
5375
+ if (Object.keys(envVars).length > 0) {
5376
+ await syncEnvVarsToServer(api, serverId, envVars);
5377
+ }
5378
+ await api.pushSourceToServer(serverId, {
5379
+ tarball,
5380
+ branch,
5381
+ commitMessage: "Redeploy from mcp-use CLI"
5382
+ });
5383
+ const dep = await api.createDeployment({
5384
+ serverId,
5385
+ branch,
5386
+ trigger: "redeploy"
5387
+ });
5388
+ deploymentId = dep.id;
5389
+ await saveProjectLink(cwd, {
5390
+ deploymentId: dep.id,
5391
+ deploymentName: projectName,
5392
+ serverId,
5393
+ linkedAt: (/* @__PURE__ */ new Date()).toISOString()
5394
+ });
5395
+ } else {
5396
+ console.log(source_default.gray("Uploading source (no GitHub required)..."));
5397
+ const result = await api.createServerFromManagedUpload({
5398
+ organizationId,
5399
+ name: projectName,
5400
+ repoName: sanitizeRepoName(projectName),
5401
+ tarball,
5402
+ branch,
5403
+ commitMessage: "Deploy from mcp-use CLI",
5404
+ port: options.port,
5405
+ env: Object.keys(envVars).length > 0 ? envVars : void 0
5406
+ });
5407
+ deploymentId = result.deploymentId ?? void 0;
5408
+ if (result.server?.id && deploymentId) {
5409
+ await saveProjectLink(cwd, {
5410
+ deploymentId,
5411
+ deploymentName: projectName,
5412
+ serverId: result.server.id,
5413
+ linkedAt: (/* @__PURE__ */ new Date()).toISOString()
5414
+ });
5415
+ }
5416
+ }
5417
+ if (!deploymentId) {
5418
+ console.log(source_default.red("\u2717 No deployment was created."));
5419
+ process.exit(1);
5420
+ }
5421
+ console.log(source_default.green("\u2713 Deployment created: ") + source_default.gray(deploymentId));
5422
+ await displayDeploymentProgress(api, deploymentId, { yes: options.yes });
5423
+ }
5203
5424
  async function deployCommand(options) {
5204
5425
  try {
5205
5426
  const cwd = process.cwd();
@@ -5317,6 +5538,22 @@ async function deployCommand(options) {
5317
5538
  }
5318
5539
  api = await ensureApiSessionForDeploy(api, options, resolvedOrgId);
5319
5540
  console.log(source_default.cyan.bold("\n\u{1F680} Deploying to Manufact cloud...\n"));
5541
+ let useNoGithubDeploy = !!options.noGithub;
5542
+ if (!useNoGithubDeploy && !options.new) {
5543
+ const link = await getProjectLink(cwd);
5544
+ if (link?.serverId) {
5545
+ try {
5546
+ const linked = await api.getServer(link.serverId);
5547
+ if (linked.connectedRepository?.isManaged) useNoGithubDeploy = true;
5548
+ } catch {
5549
+ }
5550
+ }
5551
+ }
5552
+ if (useNoGithubDeploy) {
5553
+ const organizationId = resolvedOrgId ?? await api.resolveOrganizationId();
5554
+ await deployViaManagedUpload(api, options, { cwd, organizationId });
5555
+ return;
5556
+ }
5320
5557
  const reauth = () => promptReauthenticateOn401(options, resolvedOrgId);
5321
5558
  let ghConn = await getGitHubConnectionStatusWith401Retry(
5322
5559
  api,
@@ -9381,18 +9618,27 @@ Looked for:
9381
9618
  program.command("login").description("Login to Manufact cloud").option(
9382
9619
  "--api-key <key>",
9383
9620
  "Login with an API key directly (non-interactive, for CI/CD)"
9384
- ).option("--org <slug|id|name>", "Select an organization non-interactively").action(async (opts) => {
9385
- try {
9386
- await loginCommand({ apiKey: opts.apiKey, org: opts.org });
9387
- process.exit(0);
9388
- } catch (error) {
9389
- console.error(
9390
- source_default.red.bold("\n\u2717 Login failed:"),
9391
- source_default.red(error instanceof Error ? error.message : "Unknown error")
9392
- );
9393
- process.exit(1);
9621
+ ).option("--org <slug|id|name>", "Select an organization non-interactively").option(
9622
+ "--device-code <code>",
9623
+ "Authenticate with a pre-approved device code (non-interactive; e.g. from the web onboarding flow)"
9624
+ ).action(
9625
+ async (opts) => {
9626
+ try {
9627
+ await loginCommand({
9628
+ apiKey: opts.apiKey,
9629
+ org: opts.org,
9630
+ deviceCode: opts.deviceCode
9631
+ });
9632
+ process.exit(0);
9633
+ } catch (error) {
9634
+ console.error(
9635
+ source_default.red.bold("\n\u2717 Login failed:"),
9636
+ source_default.red(error instanceof Error ? error.message : "Unknown error")
9637
+ );
9638
+ process.exit(1);
9639
+ }
9394
9640
  }
9395
- });
9641
+ );
9396
9642
  program.command("logout").description("Logout from Manufact cloud").action(async () => {
9397
9643
  await logoutCommand();
9398
9644
  });
@@ -9427,6 +9673,9 @@ program.command("deploy").description("Deploy MCP server from GitHub to Manufact
9427
9673
  ).option(
9428
9674
  "--start-command <cmd>",
9429
9675
  "Custom start command (overrides auto-detection)"
9676
+ ).option(
9677
+ "--no-github",
9678
+ "Upload local source without connecting GitHub (repo hosted in the platform-managed org)"
9430
9679
  ).action(async (options) => {
9431
9680
  await deployCommand({
9432
9681
  open: options.open,
@@ -9441,7 +9690,8 @@ program.command("deploy").description("Deploy MCP server from GitHub to Manufact
9441
9690
  yes: options.yes,
9442
9691
  region: options.region,
9443
9692
  buildCommand: options.buildCommand,
9444
- startCommand: options.startCommand
9693
+ startCommand: options.startCommand,
9694
+ noGithub: options.github === false
9445
9695
  });
9446
9696
  });
9447
9697
  program.addCommand(createClientCommand());