@mcpcloud/cli 0.25.1 → 0.26.0-next-20260927194202

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.
Files changed (3) hide show
  1. package/README.md +2 -2
  2. package/dist/index.js +186 -65
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -103,7 +103,7 @@ mcp servers get my-api --probe # verify the endpoint serves what the con
103
103
  mcp invoke my-api getUser --args '{"id":"abc"}'
104
104
  ```
105
105
 
106
- Servers are addressable by id, name, or memorable slug throughout the CLI. Two verbs keep a server current after its API changes: `mcp servers push-spec` diffs a local spec against the stored tools (preview by default; `--apply` reconciles schemas by stable operation key, preserving curated descriptions, tags, risk classes, and enrichment), and `mcp servers regenerate` re-fetches the original source spec and rebuilds with the current codegen (`--dry-run` previews the per-tool changes first).
106
+ Servers are addressable by id, name, or memorable slug throughout the CLI. Two verbs keep a server current after its API changes: `mcp servers push-spec` diffs a local spec against the stored tools (preview by default; `--apply` reconciles schemas by stable operation key, preserving curated descriptions, tags, risk classes, and enrichment), and `mcp servers regenerate` re-fetches the original source spec and rebuilds with the current codegen (`--dry-run` previews the per-tool changes first). To read a server from a different spec URL from now on, `mcp servers update <server> --source-url <url>` checks the new URL, shows what a regenerate from it would change, and stores it (`--dry-run` checks without storing); then regenerate and deploy. Operations a spec marks `x-mcp: false` never become tools.
107
107
 
108
108
  Neither verb renames a tool — both match tools on their stable operation key. Codegen now keeps an operationId's own case (`getRecording`); servers created before that change keep their lowercased names (`getrecording`), and both previews say how many. `mcp tools rename --from-operation-ids --server <server>` (or `push-spec --rename-to-operation-ids`) adopts the new names while keeping tool ids, access grants, and saved scenarios; `mcp tools rename <old> <new>` renames one tool. Deploy afterwards — the live server keeps the old names until then.
109
109
 
@@ -565,7 +565,7 @@ _Generated from the live command tree by `bun run docs:readme` — do not edit b
565
565
  | `mcp servers test-suites run <server>` | Run the server’s test scenarios and report the suite result |
566
566
  | `mcp servers traces <server>` | Show recent tool-call traces from the edge — what a caller sent and what came back. Pass --request to open one call in full. |
567
567
  | `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. |
568
- | `mcp servers update <server>` | Update a server (name, description, tool surface, upstream key mode, runtime token TTL) |
568
+ | `mcp servers update <server>` | Update a server (name, description, tool surface, upstream key mode, runtime token TTL, spec source URL) |
569
569
  | `mcp servers variables` | Inspect and set the sandbox variables that scenario arguments reference |
570
570
  | `mcp servers variables list <server>` | List a server’s sandbox variables and which ones still need a value |
571
571
  | `mcp servers variables set <server>` | Set or clear sandbox variables (repeat --set; an empty value clears) |
package/dist/index.js CHANGED
@@ -8278,7 +8278,7 @@ async function runPushSpec(serverRef, opts) {
8278
8278
 
8279
8279
  // src/commands/servers-regenerate.ts
8280
8280
  function registerServerRegenerateCommand(servers) {
8281
- servers.command("regenerate <server>").description("Re-fetch a server's source spec and rebuild its tools with the current codegen (preserves customizations). Works for spec-URL, OpenAPI, and GraphQL sources; a pasted or manual server has nothing to re-fetch. Wraps POST /api/v1/server/regenerate.").option("--org <organizationId>", "Organization ID").option("--project <project>", "Project (id or name; defaults to the server's project)").option("--source-url <url>", "Override the stored spec source URL (needed for GraphQL servers whose introspection endpoint wasn't persisted)").option("--dry-run", "Report what would change (per tool) without writing anything").addHelpText("after", [
8281
+ servers.command("regenerate <server>").description("Re-fetch a server's source spec and rebuild its tools with the current codegen (preserves customizations). Works for spec-URL, OpenAPI, and GraphQL sources; a pasted or manual server has nothing to re-fetch. Wraps POST /api/v1/server/regenerate.").option("--org <organizationId>", "Organization ID").option("--project <project>", "Project (id or name; defaults to the server's project)").option("--source-url <url>", "Read the spec from this URL for this run only — nothing is stored (needed for GraphQL servers whose introspection endpoint wasn't persisted). To change the stored source, use `mcp servers update <server> --source-url <url>`.").option("--dry-run", "Report what would change (per tool) without writing anything").addHelpText("after", [
8282
8282
  "",
8283
8283
  "Regenerate re-introspects/re-parses the source and rebuilds each tool with",
8284
8284
  "the current codegen, so codegen fixes reach existing servers. It does not",
@@ -8711,6 +8711,185 @@ function registerServerTransferCommand(servers) {
8711
8711
  }));
8712
8712
  }
8713
8713
 
8714
+ // src/commands/servers-update-source.ts
8715
+ async function changeServerSource(args) {
8716
+ const data = await api.post("/api/v1/server/source", {
8717
+ organizationId: args.orgId,
8718
+ projectId: args.projectId,
8719
+ serverId: args.serverId,
8720
+ sourceUrl: args.sourceUrl,
8721
+ ...args.dryRun ? { dryRun: true } : {}
8722
+ });
8723
+ return data.source;
8724
+ }
8725
+ function printSourceChange(change, serverId) {
8726
+ printSuccess(change.applied ? `Spec source for ${c.bold(serverId)} is now ${change.sourceUrl}.` : `Spec source preview for ${c.bold(serverId)} — nothing stored.`);
8727
+ const doc = change.document;
8728
+ printKeyValue({
8729
+ previous: change.previousSourceUrl ?? "— (none stored)",
8730
+ source: change.urlChanged ? change.sourceUrl : `${change.sourceUrl} (unchanged — re-read)`,
8731
+ type: change.sourceType,
8732
+ document: `${doc.title ?? "Untitled"}${doc.version ? ` ${doc.version}` : ""} · ${doc.endpointCount} operation(s)`,
8733
+ "left out (x-mcp: false)": change.excludedOperations.length > 0 ? formatNameList(change.excludedOperations) : "none",
8734
+ "drift check": driftLine(change)
8735
+ });
8736
+ for (const warning of doc.warnings.slice(0, 3)) {
8737
+ printWarn(` ! ${warning}`);
8738
+ }
8739
+ const r = change.regeneration;
8740
+ printInfo("");
8741
+ printInfo("What a regenerate from this source would do:");
8742
+ printKeyValue({
8743
+ "tools that would change": String(r.updated),
8744
+ "tools that would be added": String(r.inserted),
8745
+ ...r.unchanged !== undefined ? { "tools unchanged": String(r.unchanged) } : {},
8746
+ "tools dropped from spec": String(r.removed),
8747
+ "input schemas that change": String(change.inputSchemaChanges.length)
8748
+ });
8749
+ for (const item of r.changes ?? []) {
8750
+ printInfo(` ~ ${item.name}${item.fields.length > 0 ? ` (${item.fields.join(", ")})` : ""}`);
8751
+ }
8752
+ for (const name of r.insertedNames ?? [])
8753
+ printInfo(` + ${name}`);
8754
+ if (change.inputSchemaChanges.length > 0) {
8755
+ printInfo(` ~ input schema from the new document: ${formatNameList(change.inputSchemaChanges)}`);
8756
+ }
8757
+ if (r.removed > 0) {
8758
+ printInfo(c.dim(` → ${r.removed} tool(s) the new source no longer offers would be kept, not deleted (removal stays a manual action): ${formatNameList(r.removedKeys, 5)}`));
8759
+ }
8760
+ const drift = nameDriftWarning({
8761
+ drift: r.nameDrift,
8762
+ serverId,
8763
+ offerPushSpecFlag: false
8764
+ });
8765
+ if (drift)
8766
+ printWarn(` ! ${drift}`);
8767
+ printInfo(c.dim(change.applied ? ` → run \`mcp servers regenerate ${serverId}\`, then \`mcp servers deploy ${serverId} --wait\` to ship the new source. Nothing live changes until then.` : ` → run the same command without --dry-run to store this source.`));
8768
+ }
8769
+ function driftLine(change) {
8770
+ if (!change.driftCheck.enabled)
8771
+ return "not enabled for this source";
8772
+ return change.driftCheck.reset ? "reset — the next daily check reads the new source" : "would be reset";
8773
+ }
8774
+
8775
+ // src/commands/servers-update.ts
8776
+ function registerServerUpdateCommand(servers) {
8777
+ servers.command("update <server>").description("Update a server (name, description, tool surface, upstream key mode, runtime token TTL, spec source URL)").option("--org <organizationId>", "Organization ID").option("--name <name>", "New display name").option("--description <text>", "New description").option("--discovery-mode <all|wrapper>", 'Tool surface: "all" registers every operation; "wrapper" registers searchTools/getToolDefinition/useTool INSTEAD (operations are called through useTool).').option("--code-mode <off|opt-in|default>", "Code Mode: expose codemode_execute so an agent can script several calls in one sandboxed run. Served by the edge, so it takes effect on the next deploy.").option("--upstream-api-key-mode <shared|perMember>", "Whose upstream credential the server uses: one shared key, or each member their own (Pro and above). perMember needs a protected access mode (privateOrg or unlisted) — refused while the active deployment is public.").option("--runtime-token-ttl <seconds|default>", 'Runtime token lifetime in seconds (60-86400; the default AND cap for minted tokens). Pass "default" to restore the 300s platform default.').option("--source-url <url>", "Read this server’s spec from a different URL from now on. The URL is fetched and parsed as the server’s source type first, and the report shows what a regenerate from it would change. Tools are not touched until you regenerate.").option("--dry-run", "With --source-url: fetch, parse and report without storing anything").addHelpText("after", [
8778
+ "",
8779
+ "Notes:",
8780
+ " Runtime defaults (oauth, deploymentDefaults, runtime bindings) are not",
8781
+ " v1 PATCH-able. Change them via `mcp servers deploy --upstream-*`.",
8782
+ " --discovery-mode and --code-mode change the server's TOOL SURFACE and",
8783
+ " reach clients on the next `mcp servers deploy`. Check what is live with",
8784
+ " `mcp servers get <server> --probe`.",
8785
+ "",
8786
+ "Changing the spec source (--source-url):",
8787
+ " Stores the URL together with the document it returns (codegen builds",
8788
+ " from that document) and resets the drift check so the next daily check",
8789
+ " reads the new source. Operations the spec marks `x-mcp: false` are left",
8790
+ " out. The live server changes only when you regenerate and deploy:",
8791
+ " $ mcp servers update srv_123 --source-url https://api.example.com/openapi.mcp.json --dry-run",
8792
+ " $ mcp servers update srv_123 --source-url https://api.example.com/openapi.mcp.json",
8793
+ " $ mcp servers regenerate srv_123 && mcp servers deploy srv_123 --wait",
8794
+ " `regenerate --source-url` is different: a one-run override that stores",
8795
+ " nothing."
8796
+ ].join(`
8797
+ `)).action(runAction(async (serverRef, opts) => {
8798
+ await runUpdate(serverRef, opts);
8799
+ }));
8800
+ }
8801
+ async function runUpdate(serverRef, opts) {
8802
+ const settings = buildSettingsPatch(opts);
8803
+ const hasSettings = Object.keys(settings).length > 0;
8804
+ if (opts.dryRun && opts.sourceUrl === undefined) {
8805
+ throw new Error("--dry-run previews a --source-url change; pass --source-url <url>.");
8806
+ }
8807
+ if (opts.dryRun && hasSettings) {
8808
+ throw new Error("--dry-run only previews --source-url. Run the other changes without --dry-run.");
8809
+ }
8810
+ if (!hasSettings && opts.sourceUrl === undefined) {
8811
+ throw new Error("Provide at least one of --name, --description, --discovery-mode, --code-mode, --upstream-api-key-mode, --runtime-token-ttl, or --source-url.");
8812
+ }
8813
+ const orgId = await resolveOrgId(opts.org);
8814
+ const serverId = await resolveServerId(serverRef, orgId);
8815
+ const serverData = await api.get("/api/v1/server", { organizationId: orgId, serverId });
8816
+ const projectId = serverData.server.projectId;
8817
+ let source = null;
8818
+ if (opts.sourceUrl !== undefined) {
8819
+ if (!isJsonMode()) {
8820
+ printStep(`${opts.dryRun ? "Checking" : "Reading"} ${c.bold(opts.sourceUrl)} for ${c.bold(serverId)}…`);
8821
+ }
8822
+ source = await changeServerSource({
8823
+ orgId,
8824
+ projectId,
8825
+ serverId,
8826
+ sourceUrl: opts.sourceUrl,
8827
+ dryRun: Boolean(opts.dryRun)
8828
+ });
8829
+ }
8830
+ let updated = null;
8831
+ if (hasSettings) {
8832
+ updated = await api.patch("/api/v1/server", {
8833
+ organizationId: orgId,
8834
+ projectId,
8835
+ serverId,
8836
+ ...settings
8837
+ });
8838
+ }
8839
+ if (isJsonMode()) {
8840
+ printJson({
8841
+ ...updated ? { server: updated.server } : {},
8842
+ ...source ? { source } : {}
8843
+ });
8844
+ return;
8845
+ }
8846
+ if (updated) {
8847
+ const s = updated.server;
8848
+ printSuccess(`Server ${s.id} updated.`);
8849
+ printKeyValue({
8850
+ id: s.id,
8851
+ name: s.name,
8852
+ description: s.description ?? "—",
8853
+ status: s.status,
8854
+ updated: formatDate(s.updatedAt)
8855
+ });
8856
+ }
8857
+ if (source)
8858
+ printSourceChange(source, serverId);
8859
+ }
8860
+ function buildSettingsPatch(opts) {
8861
+ const body = {};
8862
+ if (opts.name !== undefined)
8863
+ body["name"] = opts.name;
8864
+ if (opts.description !== undefined)
8865
+ body["description"] = opts.description;
8866
+ if (opts.discoveryMode !== undefined) {
8867
+ body["discoveryMode"] = validateChoice("discovery-mode", opts.discoveryMode, ["all", "wrapper"]);
8868
+ }
8869
+ if (opts.codeMode !== undefined) {
8870
+ body["codeModeMode"] = validateChoice("code-mode", opts.codeMode, [
8871
+ "off",
8872
+ "opt-in",
8873
+ "default"
8874
+ ]);
8875
+ }
8876
+ if (opts.upstreamApiKeyMode !== undefined) {
8877
+ body["upstreamApiKeyMode"] = validateChoice("upstream-api-key-mode", opts.upstreamApiKeyMode, ["shared", "perMember"]);
8878
+ }
8879
+ if (opts.runtimeTokenTtl !== undefined) {
8880
+ if (opts.runtimeTokenTtl === "default") {
8881
+ body["runtimeTokenTtlSeconds"] = null;
8882
+ } else {
8883
+ const ttl = Number(opts.runtimeTokenTtl);
8884
+ if (!Number.isInteger(ttl)) {
8885
+ throw new Error('--runtime-token-ttl must be an integer number of seconds, or "default".');
8886
+ }
8887
+ body["runtimeTokenTtlSeconds"] = ttl;
8888
+ }
8889
+ }
8890
+ return body;
8891
+ }
8892
+
8714
8893
  // src/commands/servers-mutations.ts
8715
8894
  function registerServerMutationCommands(servers) {
8716
8895
  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", [
@@ -8761,70 +8940,7 @@ function registerServerMutationCommands(servers) {
8761
8940
  created: formatDate(s.createdAt)
8762
8941
  });
8763
8942
  }));
8764
- servers.command("update <server>").description("Update a server (name, description, tool surface, upstream key mode, runtime token TTL)").option("--org <organizationId>", "Organization ID").option("--name <name>", "New display name").option("--description <text>", "New description").option("--discovery-mode <all|wrapper>", 'Tool surface: "all" registers every operation; "wrapper" registers searchTools/getToolDefinition/useTool INSTEAD (operations are called through useTool).').option("--code-mode <off|opt-in|default>", "Code Mode: expose codemode_execute so an agent can script several calls in one sandboxed run. Served by the edge, so it takes effect on the next deploy.").option("--upstream-api-key-mode <shared|perMember>", "Whose upstream credential the server uses: one shared key, or each member their own (Pro and above). perMember needs a protected access mode (privateOrg or unlisted) — refused while the active deployment is public.").option("--runtime-token-ttl <seconds|default>", 'Runtime token lifetime in seconds (60-86400; the default AND cap for minted tokens). Pass "default" to restore the 300s platform default.').addHelpText("after", [
8765
- "",
8766
- "Notes:",
8767
- " Runtime defaults (oauth, deploymentDefaults, runtime bindings) are not",
8768
- " v1 PATCH-able. Change them via `mcp servers deploy --upstream-*`.",
8769
- " --discovery-mode and --code-mode change the server's TOOL SURFACE and",
8770
- " reach clients on the next `mcp servers deploy`. Check what is live with",
8771
- " `mcp servers get <server> --probe`."
8772
- ].join(`
8773
- `)).action(runAction(async (serverRef, opts) => {
8774
- const orgId = await resolveOrgId(opts.org);
8775
- const serverId = await resolveServerId(serverRef, orgId);
8776
- const serverData = await api.get("/api/v1/server", { organizationId: orgId, serverId });
8777
- const body = {
8778
- organizationId: orgId,
8779
- projectId: serverData.server.projectId,
8780
- serverId
8781
- };
8782
- if (opts.name !== undefined)
8783
- body["name"] = opts.name;
8784
- if (opts.description !== undefined)
8785
- body["description"] = opts.description;
8786
- if (opts.discoveryMode !== undefined) {
8787
- body["discoveryMode"] = validateChoice("discovery-mode", opts.discoveryMode, ["all", "wrapper"]);
8788
- }
8789
- if (opts.codeMode !== undefined) {
8790
- body["codeModeMode"] = validateChoice("code-mode", opts.codeMode, [
8791
- "off",
8792
- "opt-in",
8793
- "default"
8794
- ]);
8795
- }
8796
- if (opts.upstreamApiKeyMode !== undefined) {
8797
- body["upstreamApiKeyMode"] = validateChoice("upstream-api-key-mode", opts.upstreamApiKeyMode, ["shared", "perMember"]);
8798
- }
8799
- if (opts.runtimeTokenTtl !== undefined) {
8800
- if (opts.runtimeTokenTtl === "default") {
8801
- body["runtimeTokenTtlSeconds"] = null;
8802
- } else {
8803
- const ttl = Number(opts.runtimeTokenTtl);
8804
- if (!Number.isInteger(ttl)) {
8805
- throw new Error('--runtime-token-ttl must be an integer number of seconds, or "default".');
8806
- }
8807
- body["runtimeTokenTtlSeconds"] = ttl;
8808
- }
8809
- }
8810
- if (Object.keys(body).length === 3) {
8811
- throw new Error("Provide at least one of --name, --description, --discovery-mode, --code-mode, --upstream-api-key-mode, or --runtime-token-ttl.");
8812
- }
8813
- const data = await api.patch("/api/v1/server", body);
8814
- if (isJsonMode()) {
8815
- printJson(data);
8816
- return;
8817
- }
8818
- const s = data.server;
8819
- printSuccess(`Server ${s.id} updated.`);
8820
- printKeyValue({
8821
- id: s.id,
8822
- name: s.name,
8823
- description: s.description ?? "—",
8824
- status: s.status,
8825
- updated: formatDate(s.updatedAt)
8826
- });
8827
- }));
8943
+ registerServerUpdateCommand(servers);
8828
8944
  servers.command("pause <server>").description("Pause the server's latest active deployment (traffic stops; worker is preserved)").option("--org <organizationId>", "Organization ID").option("--project <project>", "Project (id or name; defaults to the server's project)").addHelpText("after", [
8829
8945
  "",
8830
8946
  "Notes:",
@@ -37337,6 +37453,11 @@ var CLI_EXAMPLES = {
37337
37453
  "mcp servers regenerate srv_abc --dry-run",
37338
37454
  "mcp servers regenerate srv_abc && mcp servers deploy srv_abc --wait"
37339
37455
  ],
37456
+ "servers update": [
37457
+ 'mcp servers update srv_abc --name "Payments API"',
37458
+ "mcp servers update srv_abc --source-url https://api.example.com/openapi.mcp.json --dry-run",
37459
+ "mcp servers update srv_abc --source-url https://api.example.com/openapi.mcp.json"
37460
+ ],
37340
37461
  "servers env list": ["mcp servers env list srv_abc"],
37341
37462
  "servers env set": [
37342
37463
  "mcp servers env set srv_abc OPENAI_API_KEY=sk-...",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mcpcloud/cli",
3
- "version": "0.25.1",
3
+ "version": "0.26.0-next-20260927194202",
4
4
  "description": "The official CLI for MCPCloud — manage projects, servers, skills, and API keys from the terminal",
5
5
  "type": "module",
6
6
  "bin": {