@mcpcloud/cli 0.20.0 → 0.21.0-next-20260906205034
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/README.md +1 -0
- package/dist/index.js +128 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -551,6 +551,7 @@ _Generated from the live command tree by `bun run docs:readme` — do not edit b
|
|
|
551
551
|
| `mcp servers test-suites get <suiteId>` | Show one server test suite including its scenario membership |
|
|
552
552
|
| `mcp servers test-suites list <server>` | List test suites bound to a server |
|
|
553
553
|
| `mcp servers test-suites run <server>` | Run the server’s test scenarios and report the suite result |
|
|
554
|
+
| `mcp servers transfer <server>` | Move a server into another organization you administer, keeping its id, slug, deployment, custom domain, end-user keys and git link. Shows the plan; add --yes to execute. |
|
|
554
555
|
| `mcp servers update <server>` | Update a server (name, description, tool surface, upstream key mode, runtime token TTL) |
|
|
555
556
|
| `mcp servers variables` | Inspect and set the sandbox variables that scenario arguments reference |
|
|
556
557
|
| `mcp servers variables list <server>` | List a server’s sandbox variables and which ones still need a value |
|
package/dist/index.js
CHANGED
|
@@ -5571,6 +5571,9 @@ function describeDeployed(deployed) {
|
|
|
5571
5571
|
return `${deployed.behindHead} commit${deployed.behindHead === 1 ? "" : "s"} behind HEAD`;
|
|
5572
5572
|
}
|
|
5573
5573
|
function describeWorkingTree(tree) {
|
|
5574
|
+
if (tree.hydrated === false) {
|
|
5575
|
+
return "not loaded yet — hydrates from the stored bundle on the next commit or sync";
|
|
5576
|
+
}
|
|
5574
5577
|
const parts = [];
|
|
5575
5578
|
if (tree.added > 0)
|
|
5576
5579
|
parts.push(`${tree.added} added`);
|
|
@@ -7344,6 +7347,17 @@ function reportDeployFailure(terminalEvent) {
|
|
|
7344
7347
|
printError(`Deploy failed: ${reason ?? terminalEvent.message}`);
|
|
7345
7348
|
return true;
|
|
7346
7349
|
}
|
|
7350
|
+
async function isOnlyDeployment(args) {
|
|
7351
|
+
try {
|
|
7352
|
+
const listing = await api.get(`/api/v1/server/deployments?organizationId=${encodeURIComponent(args.orgId)}&serverId=${encodeURIComponent(args.serverId)}&limit=2`);
|
|
7353
|
+
const rows = listing.deployments;
|
|
7354
|
+
if (!Array.isArray(rows) || rows.length === 0)
|
|
7355
|
+
return false;
|
|
7356
|
+
return rows.every((row) => row.id === args.deploymentId);
|
|
7357
|
+
} catch {
|
|
7358
|
+
return false;
|
|
7359
|
+
}
|
|
7360
|
+
}
|
|
7347
7361
|
async function refreshDeploymentRow(args) {
|
|
7348
7362
|
try {
|
|
7349
7363
|
const refreshed = await api.get(`/api/v1/server/deployments?organizationId=${encodeURIComponent(args.orgId)}&serverId=${encodeURIComponent(args.serverId)}&limit=5`);
|
|
@@ -7458,6 +7472,9 @@ function registerServerLifecycleCommands(servers) {
|
|
|
7458
7472
|
"terminal at": formatDate(terminalEvent.timestamp)
|
|
7459
7473
|
} : {}
|
|
7460
7474
|
});
|
|
7475
|
+
if (!accessMode && dep.accessMode === "public" && await isOnlyDeployment({ deploymentId: dep.deploymentId, orgId, serverId })) {
|
|
7476
|
+
printWarn("This first deployment is public — anyone with the URL can call it. Redeploy with --access-mode privateOrg or unlisted to require sign-in (recommended when the upstream API needs credentials).");
|
|
7477
|
+
}
|
|
7461
7478
|
if (!opts.wait && dep.status === "queued") {
|
|
7462
7479
|
printInfo(`Deploy is running in the background — follow with \`mcp deployments get ${dep.deploymentId}\` or rerun with --wait.`);
|
|
7463
7480
|
}
|
|
@@ -7666,6 +7683,9 @@ function registerServerLifecycleCommands(servers) {
|
|
|
7666
7683
|
"terminal at": formatDate(terminalEvent.timestamp)
|
|
7667
7684
|
} : {}
|
|
7668
7685
|
});
|
|
7686
|
+
if (!accessMode && dep.accessMode === "public" && await isOnlyDeployment({ deploymentId: dep.deploymentId, orgId, serverId })) {
|
|
7687
|
+
printWarn("This first deployment is public — anyone with the URL can call it. Redeploy with --access-mode privateOrg or unlisted to require sign-in (recommended when the upstream API needs credentials).");
|
|
7688
|
+
}
|
|
7669
7689
|
if (!opts.wait && dep.status === "queued") {
|
|
7670
7690
|
printInfo(`Deploy is running in the background — follow with \`mcp deployments get ${dep.deploymentId}\` or rerun with --wait.`);
|
|
7671
7691
|
}
|
|
@@ -7727,6 +7747,107 @@ function registerServerLifecycleCommands(servers) {
|
|
|
7727
7747
|
}));
|
|
7728
7748
|
}
|
|
7729
7749
|
|
|
7750
|
+
// src/commands/servers-transfer.ts
|
|
7751
|
+
var TRANSFER_PATH = "/api/v1/server/transfer";
|
|
7752
|
+
async function resolveTargetOrganizationId(reference) {
|
|
7753
|
+
const ref = reference.trim();
|
|
7754
|
+
const data = await api.get("/api/v1/organizations");
|
|
7755
|
+
const rows = data.organizations;
|
|
7756
|
+
const match = rows.find((row) => row.id === ref) ?? rows.find((row) => row.slug === ref) ?? rows.find((row) => row.name.toLowerCase() === ref.toLowerCase());
|
|
7757
|
+
if (!match) {
|
|
7758
|
+
printError(`No organization you belong to matches ${c.bold(ref)} (by id, slug, or name).`);
|
|
7759
|
+
if (rows.length > 0) {
|
|
7760
|
+
printInfo(` ${c.dim("Yours:")} ${rows.map((row) => `${row.name} (${row.slug ?? row.id})`).join(", ")}`);
|
|
7761
|
+
}
|
|
7762
|
+
throw new CliExitError(1);
|
|
7763
|
+
}
|
|
7764
|
+
return match.id;
|
|
7765
|
+
}
|
|
7766
|
+
function printPlan(result) {
|
|
7767
|
+
printKeyValue({
|
|
7768
|
+
server: `${result.server.name} (${result.server.slug})`,
|
|
7769
|
+
from: `${result.from.organizationName ?? result.from.organizationId} / ${result.from.projectName ?? result.from.projectId}`,
|
|
7770
|
+
to: `${result.to.organizationName} / ${result.to.projectName ?? result.to.projectId ?? "(new project)"}`,
|
|
7771
|
+
"git link": result.plan.gitLink,
|
|
7772
|
+
domains: result.domains.length > 0 ? result.domains.join(", ") : "—"
|
|
7773
|
+
});
|
|
7774
|
+
const moving = result.manifest.filter((entry) => entry.count > 0);
|
|
7775
|
+
if (moving.length > 0) {
|
|
7776
|
+
printInfo(c.dim(" Rows that move:"));
|
|
7777
|
+
for (const entry of moving) {
|
|
7778
|
+
printInfo(` ${entry.table.padEnd(34)} ${entry.count}${entry.more ? "+" : ""}`);
|
|
7779
|
+
}
|
|
7780
|
+
}
|
|
7781
|
+
for (const note of result.plan.notes)
|
|
7782
|
+
printWarn(` ! ${note}`);
|
|
7783
|
+
}
|
|
7784
|
+
function reportApiError6(err) {
|
|
7785
|
+
if (err instanceof McpCloudApiError) {
|
|
7786
|
+
printError(`Transfer failed (HTTP ${err.status}): ${err.error.message}`);
|
|
7787
|
+
const blockers = err.error.details?.blockers;
|
|
7788
|
+
if (Array.isArray(blockers)) {
|
|
7789
|
+
for (const blocker of blockers) {
|
|
7790
|
+
printInfo(` ${c.dim(blocker.code + ":")} ${blocker.message}`);
|
|
7791
|
+
}
|
|
7792
|
+
}
|
|
7793
|
+
} else {
|
|
7794
|
+
printError(`Transfer failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
7795
|
+
}
|
|
7796
|
+
throw new CliExitError(1);
|
|
7797
|
+
}
|
|
7798
|
+
function registerServerTransferCommand(servers) {
|
|
7799
|
+
servers.command("transfer <server>").description("Move a server into another organization you administer, keeping its id, slug, deployment, custom domain, end-user keys and git link. Shows the plan; add --yes to execute.").requiredOption("--to-org <organization>", "Destination organization (id, slug, or name) — you must be owner or admin there").option("--to-project <project>", "Destination project (id or name, within the destination organization)").option("--create-project <name>", "Create this project in the destination organization and move the server into it").option("--org <organizationId>", "Source organization ID").option("--drop-git-link", "Move without the GitHub link when no installation in the destination reaches the repository").option("--yes", "Execute the transfer (without it, only the plan is shown)").addHelpText("after", [
|
|
7800
|
+
"",
|
|
7801
|
+
"What moves: the server row and project placement, tools, prompts, resources,",
|
|
7802
|
+
"deployments and their edge policy, versions, code + commits, sandbox and test",
|
|
7803
|
+
"artifacts, end-user access (branding, allowlist, federation), members’ pasted",
|
|
7804
|
+
"keys and end-user records, the custom domain (no traffic interruption), the",
|
|
7805
|
+
"API source, and registry listings (installers keep them).",
|
|
7806
|
+
"What stays: usage and billing history (the source org was billed for it),",
|
|
7807
|
+
"and member-scoped grants of source-org members, which become inert.",
|
|
7808
|
+
"",
|
|
7809
|
+
"Members of the source organization lose access to the server."
|
|
7810
|
+
].join(`
|
|
7811
|
+
`)).action(runAction(async (serverRef, opts) => {
|
|
7812
|
+
const orgId = await resolveOrgId(opts.org);
|
|
7813
|
+
const serverId = await resolveServerId(serverRef, orgId);
|
|
7814
|
+
const targetOrganizationId = await resolveTargetOrganizationId(opts.toOrg);
|
|
7815
|
+
const targetProjectId = await resolveOptionalProjectId(opts.toProject, targetOrganizationId);
|
|
7816
|
+
if (opts.yes && !targetProjectId && !opts.createProject) {
|
|
7817
|
+
printError("Pass --to-project <project> or --create-project <name> for the destination.");
|
|
7818
|
+
throw new CliExitError(1);
|
|
7819
|
+
}
|
|
7820
|
+
const body = {
|
|
7821
|
+
organizationId: orgId,
|
|
7822
|
+
targetOrganizationId,
|
|
7823
|
+
serverId,
|
|
7824
|
+
...targetProjectId ? { targetProjectId } : {},
|
|
7825
|
+
...opts.createProject ? { createProjectName: opts.createProject } : {},
|
|
7826
|
+
dryRun: !opts.yes,
|
|
7827
|
+
dropGitLink: Boolean(opts.dropGitLink)
|
|
7828
|
+
};
|
|
7829
|
+
let result;
|
|
7830
|
+
try {
|
|
7831
|
+
if (opts.yes && !isJsonMode()) {
|
|
7832
|
+
printStep(`Transferring ${c.bold(serverRef)} to ${c.bold(opts.toOrg)}…`);
|
|
7833
|
+
}
|
|
7834
|
+
result = await api.post(TRANSFER_PATH, body);
|
|
7835
|
+
} catch (err) {
|
|
7836
|
+
reportApiError6(err);
|
|
7837
|
+
}
|
|
7838
|
+
if (isJsonMode()) {
|
|
7839
|
+
printJson(result);
|
|
7840
|
+
return;
|
|
7841
|
+
}
|
|
7842
|
+
printPlan(result);
|
|
7843
|
+
if (result.dryRun) {
|
|
7844
|
+
printInfo(c.dim(` → nothing moved. Re-run with --yes${targetProjectId || opts.createProject ? "" : " and --to-project <project> (or --create-project <name>)"} to transfer.`));
|
|
7845
|
+
return;
|
|
7846
|
+
}
|
|
7847
|
+
printSuccess(`${result.server.name} now lives in ${result.to.organizationName} / ${result.to.projectName ?? result.to.projectId}${result.to.projectCreated ? " (project created)" : ""}. ${result.patchedRows} row(s) rehomed; id and slug unchanged.`);
|
|
7848
|
+
}));
|
|
7849
|
+
}
|
|
7850
|
+
|
|
7730
7851
|
// src/commands/servers-mutations.ts
|
|
7731
7852
|
function registerServerMutationCommands(servers) {
|
|
7732
7853
|
servers.command("create").description("Create an empty server skeleton in a project").requiredOption("--project <project>", "Project (id or name)").requiredOption("--name <name>", "Display name (1–100 chars)").option("--org <organizationId>", "Organization ID").option("--description <text>", "Optional description").option("--git-repo <owner/name>", "Host the generated code on this GitHub repo (links now, mirrors after first generation)").option("--git-path-prefix <prefix>", "Sync into this path inside the repo").option("--no-git-auto-sync", "Do not mirror commits automatically").addHelpText("after", [
|
|
@@ -8097,6 +8218,12 @@ function registerServerSpecCommands(servers) {
|
|
|
8097
8218
|
"api source": data.apiSource.id ?? "—",
|
|
8098
8219
|
project: projectId
|
|
8099
8220
|
});
|
|
8221
|
+
const warnings = Array.isArray(data.apiSource.warnings) ? data.apiSource.warnings.filter((warning) => typeof warning === "string") : [];
|
|
8222
|
+
for (const warning of warnings.slice(0, 10))
|
|
8223
|
+
printWarn(` ! ${warning}`);
|
|
8224
|
+
if (warnings.length > 10) {
|
|
8225
|
+
printWarn(` ! +${warnings.length - 10} more — \`mcp projects api-source ${data.apiSource.id ?? "<id>"}\` lists them all.`);
|
|
8226
|
+
}
|
|
8100
8227
|
printStep(`Next: \`mcp servers generate ${data.server.id} --out ./out\` or \`mcp servers deploy ${data.server.id} --wait\`.`);
|
|
8101
8228
|
}));
|
|
8102
8229
|
servers.command("generate <server>").description("Generate the server's TypeScript bundle (the exact code `deploy` ships) and optionally write it to disk — a dry-run for the codegen path. Wraps POST /api/v1/server/generate.").option("--org <organizationId>", "Organization ID").option("--project <project>", "Project (id or name; looked up from the server when omitted)").option("--out <dir>", "Write the generated files to this directory instead of just summarizing").option("--store", "Persist the bundle as the one `deploy` will ship. Required after editing prompts, resources or tool metadata: deploy reuses a server's existing bundle unchanged, so without this the change never reaches the deployed worker.").addHelpText("after", [
|
|
@@ -8564,6 +8691,7 @@ function registerServerCommands(program) {
|
|
|
8564
8691
|
registerServerGetCommand(servers);
|
|
8565
8692
|
registerServerExportCommands(servers);
|
|
8566
8693
|
registerServerLifecycleCommands(servers);
|
|
8694
|
+
registerServerTransferCommand(servers);
|
|
8567
8695
|
registerServerMutationCommands(servers);
|
|
8568
8696
|
registerServerSpecCommands(servers);
|
|
8569
8697
|
registerServerTestCommands(servers);
|
package/package.json
CHANGED