@mcpcloud/cli 0.18.0 → 0.19.0-next-20260901020935

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 +193 -42
  2. package/dist/index.js +535 -181
  3. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -20,12 +20,14 @@ var __toESM = (mod, isNodeMode, target) => {
20
20
  }
21
21
  target = mod != null ? __create(__getProtoOf(mod)) : {};
22
22
  const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
23
- for (let key of __getOwnPropNames(mod))
24
- if (!__hasOwnProp.call(to, key))
25
- __defProp(to, key, {
26
- get: __accessProp.bind(mod, key),
27
- enumerable: true
28
- });
23
+ if (mod && typeof mod === "object" || typeof mod === "function") {
24
+ for (let key of __getOwnPropNames(mod))
25
+ if (!__hasOwnProp.call(to, key))
26
+ __defProp(to, key, {
27
+ get: __accessProp.bind(mod, key),
28
+ enumerable: true
29
+ });
30
+ }
29
31
  if (canCache)
30
32
  cache.set(mod, to);
31
33
  return to;
@@ -48,7 +50,7 @@ var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
48
50
  var __require = /* @__PURE__ */ createRequire(import.meta.url);
49
51
 
50
52
  // ../../node_modules/.bun/commander@12.1.0/node_modules/commander/lib/error.js
51
- var require_error = __commonJS((exports) => {
53
+ var require_error = __commonJS(function(exports) {
52
54
  class CommanderError extends Error {
53
55
  constructor(exitCode, code, message) {
54
56
  super(message);
@@ -72,7 +74,7 @@ var require_error = __commonJS((exports) => {
72
74
  });
73
75
 
74
76
  // ../../node_modules/.bun/commander@12.1.0/node_modules/commander/lib/argument.js
75
- var require_argument = __commonJS((exports) => {
77
+ var require_argument = __commonJS(function(exports) {
76
78
  var { InvalidArgumentError } = require_error();
77
79
 
78
80
  class Argument {
@@ -151,7 +153,7 @@ var require_argument = __commonJS((exports) => {
151
153
  });
152
154
 
153
155
  // ../../node_modules/.bun/commander@12.1.0/node_modules/commander/lib/help.js
154
- var require_help = __commonJS((exports) => {
156
+ var require_help = __commonJS(function(exports) {
155
157
  var { humanReadableArgName } = require_argument();
156
158
 
157
159
  class Help {
@@ -400,7 +402,7 @@ var require_help = __commonJS((exports) => {
400
402
  });
401
403
 
402
404
  // ../../node_modules/.bun/commander@12.1.0/node_modules/commander/lib/option.js
403
- var require_option = __commonJS((exports) => {
405
+ var require_option = __commonJS(function(exports) {
404
406
  var { InvalidArgumentError } = require_error();
405
407
 
406
408
  class Option {
@@ -551,7 +553,7 @@ var require_option = __commonJS((exports) => {
551
553
  });
552
554
 
553
555
  // ../../node_modules/.bun/commander@12.1.0/node_modules/commander/lib/suggestSimilar.js
554
- var require_suggestSimilar = __commonJS((exports) => {
556
+ var require_suggestSimilar = __commonJS(function(exports) {
555
557
  var maxDistance = 3;
556
558
  function editDistance(a, b) {
557
559
  if (Math.abs(a.length - b.length) > maxDistance)
@@ -624,7 +626,7 @@ var require_suggestSimilar = __commonJS((exports) => {
624
626
  });
625
627
 
626
628
  // ../../node_modules/.bun/commander@12.1.0/node_modules/commander/lib/command.js
627
- var require_command = __commonJS((exports) => {
629
+ var require_command = __commonJS(function(exports) {
628
630
  var EventEmitter = __require("node:events").EventEmitter;
629
631
  var childProcess = __require("node:child_process");
630
632
  var path = __require("node:path");
@@ -1867,7 +1869,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
1867
1869
  });
1868
1870
 
1869
1871
  // ../../node_modules/.bun/commander@12.1.0/node_modules/commander/index.js
1870
- var require_commander = __commonJS((exports) => {
1872
+ var require_commander = __commonJS(function(exports) {
1871
1873
  var { Argument } = require_argument();
1872
1874
  var { Command } = require_command();
1873
1875
  var { CommanderError, InvalidArgumentError } = require_error();
@@ -2226,7 +2228,7 @@ var init_main = __esm(() => {
2226
2228
  });
2227
2229
 
2228
2230
  // ../../node_modules/.bun/sisteransi@1.0.5/node_modules/sisteransi/src/index.js
2229
- var require_src = __commonJS((exports, module) => {
2231
+ var require_src = __commonJS(function(exports, module) {
2230
2232
  var ESC2 = "\x1B";
2231
2233
  var CSI2 = `${ESC2}[`;
2232
2234
  var beep = "\x07";
@@ -3073,65 +3075,65 @@ ${i}` : `${s}${v("inverse", e)}${i}`;
3073
3075
  // ../../node_modules/.bun/@clack+prompts@1.4.0/node_modules/@clack/prompts/dist/index.mjs
3074
3076
  var exports_dist = {};
3075
3077
  __export(exports_dist, {
3076
- updateSettings: () => j,
3077
- unicodeOr: () => w2,
3078
- unicode: () => tt2,
3079
- text: () => Pe,
3080
- tasks: () => Ge,
3081
- taskLog: () => Oe,
3082
- symbolBar: () => yt,
3083
- symbol: () => P2,
3084
- stream: () => q2,
3085
- spinner: () => ft,
3086
- settings: () => h,
3087
- selectKey: () => Ee,
3088
- select: () => xe,
3089
- progress: () => _e,
3090
- path: () => Ie,
3091
- password: () => Ce,
3092
- outro: () => ye,
3093
- note: () => Se,
3094
- multiselect: () => ve,
3095
- multiline: () => fe,
3096
- log: () => R2,
3097
- limitOptions: () => F2,
3098
- isTTY: () => It,
3099
- isCancel: () => q,
3100
- isCI: () => ot2,
3101
- intro: () => ge,
3102
- groupMultiselect: () => pe,
3103
- group: () => he,
3104
- date: () => le,
3105
- confirm: () => ue,
3106
- cancel: () => me,
3107
- box: () => ae,
3108
- autocompleteMultiselect: () => ie,
3109
- autocomplete: () => At,
3110
- S_WARN: () => mt,
3111
- S_SUCCESS: () => pt,
3112
- S_STEP_SUBMIT: () => H2,
3113
- S_STEP_ERROR: () => ut2,
3114
- S_STEP_CANCEL: () => at2,
3115
- S_STEP_ACTIVE: () => Tt,
3116
- S_RADIO_INACTIVE: () => U2,
3117
- S_RADIO_ACTIVE: () => z2,
3118
- S_PASSWORD_MASK: () => Et,
3119
- S_INFO: () => ht2,
3120
- S_ERROR: () => gt,
3121
- S_CORNER_TOP_RIGHT: () => ct,
3122
- S_CORNER_TOP_LEFT: () => Mt,
3123
- S_CORNER_BOTTOM_RIGHT: () => $t,
3124
- S_CORNER_BOTTOM_LEFT: () => dt,
3125
- S_CONNECT_LEFT: () => Gt,
3126
- S_CHECKBOX_SELECTED: () => K2,
3127
- S_CHECKBOX_INACTIVE: () => Y2,
3128
- S_CHECKBOX_ACTIVE: () => et2,
3129
- S_BAR_START_RIGHT: () => _t,
3130
- S_BAR_START: () => lt,
3131
- S_BAR_H: () => st2,
3132
- S_BAR_END_RIGHT: () => xt,
3078
+ S_BAR: () => $2,
3133
3079
  S_BAR_END: () => x2,
3134
- S_BAR: () => $2
3080
+ S_BAR_END_RIGHT: () => xt,
3081
+ S_BAR_H: () => st2,
3082
+ S_BAR_START: () => lt,
3083
+ S_BAR_START_RIGHT: () => _t,
3084
+ S_CHECKBOX_ACTIVE: () => et2,
3085
+ S_CHECKBOX_INACTIVE: () => Y2,
3086
+ S_CHECKBOX_SELECTED: () => K2,
3087
+ S_CONNECT_LEFT: () => Gt,
3088
+ S_CORNER_BOTTOM_LEFT: () => dt,
3089
+ S_CORNER_BOTTOM_RIGHT: () => $t,
3090
+ S_CORNER_TOP_LEFT: () => Mt,
3091
+ S_CORNER_TOP_RIGHT: () => ct,
3092
+ S_ERROR: () => gt,
3093
+ S_INFO: () => ht2,
3094
+ S_PASSWORD_MASK: () => Et,
3095
+ S_RADIO_ACTIVE: () => z2,
3096
+ S_RADIO_INACTIVE: () => U2,
3097
+ S_STEP_ACTIVE: () => Tt,
3098
+ S_STEP_CANCEL: () => at2,
3099
+ S_STEP_ERROR: () => ut2,
3100
+ S_STEP_SUBMIT: () => H2,
3101
+ S_SUCCESS: () => pt,
3102
+ S_WARN: () => mt,
3103
+ autocomplete: () => At,
3104
+ autocompleteMultiselect: () => ie,
3105
+ box: () => ae,
3106
+ cancel: () => me,
3107
+ confirm: () => ue,
3108
+ date: () => le,
3109
+ group: () => he,
3110
+ groupMultiselect: () => pe,
3111
+ intro: () => ge,
3112
+ isCI: () => ot2,
3113
+ isCancel: () => q,
3114
+ isTTY: () => It,
3115
+ limitOptions: () => F2,
3116
+ log: () => R2,
3117
+ multiline: () => fe,
3118
+ multiselect: () => ve,
3119
+ note: () => Se,
3120
+ outro: () => ye,
3121
+ password: () => Ce,
3122
+ path: () => Ie,
3123
+ progress: () => _e,
3124
+ select: () => xe,
3125
+ selectKey: () => Ee,
3126
+ settings: () => h,
3127
+ spinner: () => ft,
3128
+ stream: () => q2,
3129
+ symbol: () => P2,
3130
+ symbolBar: () => yt,
3131
+ taskLog: () => Oe,
3132
+ tasks: () => Ge,
3133
+ text: () => Pe,
3134
+ unicode: () => tt2,
3135
+ unicodeOr: () => w2,
3136
+ updateSettings: () => j
3135
3137
  });
3136
3138
  import { styleText as e, stripVTControlCharacters as nt2 } from "node:util";
3137
3139
  import V2 from "node:process";
@@ -4070,7 +4072,8 @@ var PROFILE_FIELD_KEYS = [
4070
4072
  "editorOpenTarget",
4071
4073
  "editorCommand",
4072
4074
  "defaultAgent",
4073
- "aliases"
4075
+ "aliases",
4076
+ "gitRepoDirs"
4074
4077
  ];
4075
4078
  function pickProfileFields(source) {
4076
4079
  const out = {};
@@ -5621,7 +5624,7 @@ function registerAliasCommands(program2) {
5621
5624
  "",
5622
5625
  "Examples:",
5623
5626
  " $ mcp alias set forge server:forge-code-issues-api",
5624
- ' $ mcp alias set staging project:"Payments"',
5627
+ ' $ mcp alias set payments project:"Payments"',
5625
5628
  " $ mcp servers deploy forge --wait",
5626
5629
  " $ mcp alias list",
5627
5630
  " $ mcp alias rm forge"
@@ -6080,7 +6083,7 @@ function registerAuthCommands(program2) {
6080
6083
  "Examples:",
6081
6084
  " $ mcp login # default: browser flow",
6082
6085
  " $ mcp login --paste # SSH/headless: paste the key",
6083
- " $ mcp login --key mck_abc… # save a key directly",
6086
+ " $ mcp login --key mcf_abc… # save a key directly",
6084
6087
  "",
6085
6088
  "See `mcp help auth` for the full identity model."
6086
6089
  ].join(`
@@ -6794,6 +6797,225 @@ function requireServer(server) {
6794
6797
  throw new CliExitError(1);
6795
6798
  }
6796
6799
 
6800
+ // src/commands/servers-git.ts
6801
+ var GIT_LINK_PATH = "/api/v1/server/git-link";
6802
+ var GIT_SYNC_PATH = "/api/v1/server/git-sync";
6803
+ var GIT_SYNC_STATUS_PATH = "/api/v1/server/git-sync-status";
6804
+ var COMMIT_PATH = "/api/v1/server/commit";
6805
+ function reportApiError2(err, action) {
6806
+ if (err instanceof McpCloudApiError) {
6807
+ printError(`${action} failed (HTTP ${err.status}): ${err.error.message}`);
6808
+ } else {
6809
+ printError(`${action} failed: ${err instanceof Error ? err.message : String(err)}`);
6810
+ }
6811
+ throw new CliExitError(1);
6812
+ }
6813
+ function shortSha(sha) {
6814
+ return sha ? sha.slice(0, 7) : "—";
6815
+ }
6816
+ function parseRepoRef(ref) {
6817
+ const match = /^([^/\s]+)\/([^/\s]+)$/.exec(ref.trim());
6818
+ if (!match || !match[1] || !match[2])
6819
+ return null;
6820
+ return { owner: match[1], name: match[2] };
6821
+ }
6822
+ function describeDeployed(deployed) {
6823
+ if (!deployed)
6824
+ return "—";
6825
+ if (!deployed.stamped)
6826
+ return "untracked (predates fingerprinting — redeploy to stamp)";
6827
+ if (deployed.behindHead === 0)
6828
+ return "up to date with HEAD";
6829
+ if (deployed.behindHead === null)
6830
+ return "behind HEAD (deployed from an uncommitted tree)";
6831
+ return `${deployed.behindHead} commit${deployed.behindHead === 1 ? "" : "s"} behind HEAD`;
6832
+ }
6833
+ function describeWorkingTree(tree) {
6834
+ const parts = [];
6835
+ if (tree.added > 0)
6836
+ parts.push(`${tree.added} added`);
6837
+ if (tree.modified > 0)
6838
+ parts.push(`${tree.modified} modified`);
6839
+ if (tree.deleted > 0)
6840
+ parts.push(`${tree.deleted} deleted`);
6841
+ if (parts.length === 0)
6842
+ return "clean";
6843
+ return `${parts.join(", ")} (uncommitted)`;
6844
+ }
6845
+ async function linkRepoAtCreation(args) {
6846
+ const repo = parseRepoRef(args.repoRef);
6847
+ if (!repo) {
6848
+ printError(`Invalid --git-repo "${args.repoRef}". Expected owner/name — the server was created without a linked repo.`);
6849
+ return;
6850
+ }
6851
+ try {
6852
+ await api.post(GIT_LINK_PATH, {
6853
+ organizationId: args.orgId,
6854
+ serverId: args.serverId,
6855
+ owner: repo.owner,
6856
+ name: repo.name,
6857
+ pathPrefix: args.pathPrefix,
6858
+ autoSync: args.autoSync,
6859
+ deferInitialSync: true
6860
+ });
6861
+ printSuccess(`Linked ${repo.owner}/${repo.name}${args.pathPrefix ? ` (prefix ${args.pathPrefix})` : ""} — the repo fills itself after the first generation${args.autoSync ? "" : " once you run `mcp servers git sync`"}.`);
6862
+ } catch (err) {
6863
+ printError(`Server created, but linking ${repo.owner}/${repo.name} failed: ${err instanceof McpCloudApiError ? err.error.message : String(err)}`);
6864
+ printInfo(` ${c.dim("Link it later with")} ${c.bold("mcp servers git link <server> --repo " + args.repoRef)}`);
6865
+ }
6866
+ }
6867
+ function registerServerGitCommands(servers) {
6868
+ const git = servers.command("git").description("Link a server to a GitHub repo and drive the sync loop from the terminal");
6869
+ git.command("status <server>").description("Show the server's git link, mirror + reconcile state, and working-tree changes").option("--org <organizationId>", "Organization ID").action(runAction(async (serverRef, opts) => {
6870
+ const orgId = await resolveOrgId(opts.org);
6871
+ const serverId = await resolveServerId(serverRef, orgId);
6872
+ const data = await api.get(GIT_SYNC_STATUS_PATH, {
6873
+ organizationId: orgId,
6874
+ serverId
6875
+ });
6876
+ if (isJsonMode()) {
6877
+ printJson(data);
6878
+ return;
6879
+ }
6880
+ if (!data.repo) {
6881
+ printInfo("Not linked to a Git repository.");
6882
+ printInfo(` ${c.dim("Link one with")} ${c.bold("mcp servers git link <server> --repo <owner/name>")}`);
6883
+ return;
6884
+ }
6885
+ printKeyValue({
6886
+ repo: `${data.repo.owner}/${data.repo.name}@${data.repo.branch}`,
6887
+ ...data.repo.pathPrefix ? { "path prefix": data.repo.pathPrefix } : {},
6888
+ "auto-sync": data.repo.autoSync ? "on (5-minute sweep)" : "off",
6889
+ "mirror push": data.sync ? `${data.sync.status} · ${shortSha(data.sync.lastPushedGitSha)}${data.sync.lastSyncedAt ? ` · ${formatDate(data.sync.lastSyncedAt)}` : ""}${data.sync.mirrorBehindHead ? " · behind HEAD" : ""}` : "—",
6890
+ "last pull": data.pull ? `${data.pull.status} · ${shortSha(data.pull.lastPulledGitSha)}${data.pull.lastPulledAt ? ` · ${formatDate(data.pull.lastPulledAt)}` : ""}` : "—",
6891
+ "working tree": data.workingTree ? describeWorkingTree(data.workingTree) : "—",
6892
+ deployed: describeDeployed(data.deployed)
6893
+ });
6894
+ if (data.sync?.error)
6895
+ printError(`mirror error: ${data.sync.error}`);
6896
+ if (data.pull?.error)
6897
+ printError(`pull error: ${data.pull.error}`);
6898
+ if ((data.pull?.conflictedFileCount ?? 0) > 0) {
6899
+ printInfo(` ${c.dim("Resolve by committing or discarding the local edits, then push again from git.")}`);
6900
+ }
6901
+ const tree = data.workingTree;
6902
+ if (tree && tree.added + tree.modified + tree.deleted > 0) {
6903
+ printInfo(` ${c.dim("Commit with")} ${c.bold('mcp servers commit <server> -m "…"')} ${c.dim("to make these mirrorable + deployable.")}`);
6904
+ } else if (data.sync?.mirrorBehindHead) {
6905
+ printInfo(` ${c.dim("Committed HEAD is not mirrored yet — push it with")} ${c.bold("mcp servers git sync <server>")}${c.dim(data.repo.autoSync ? " (or wait for auto-sync)" : "")}`);
6906
+ }
6907
+ }));
6908
+ git.command("link <server>").description("Link an existing GitHub repo (App installation must have access) and run the initial sync").requiredOption("--repo <owner/name>", "Repository, as owner/name").option("--branch <branch>", "Branch to sync (default: the repo default branch)").option("--path-prefix <prefix>", "Sync into this path inside the repo").option("--auto-sync", "Mirror each committed change automatically").option("--org <organizationId>", "Organization ID").addHelpText("after", [
6909
+ "",
6910
+ "Notes:",
6911
+ " The initial sync mirrors COMMITTED code. On a server with no commit",
6912
+ ' yet, run `mcp servers commit <server> -m "…"` first — the link is',
6913
+ " saved either way; only the first push waits.",
6914
+ " An empty repository is fine: the push bootstraps its first commit."
6915
+ ].join(`
6916
+ `)).action(runAction(async (serverRef, opts) => {
6917
+ const repo = parseRepoRef(opts.repo);
6918
+ if (!repo) {
6919
+ printError(`Invalid --repo "${opts.repo}". Expected owner/name, e.g. my-org/my-mcp-server.`);
6920
+ throw new CliExitError(1);
6921
+ }
6922
+ const orgId = await resolveOrgId(opts.org);
6923
+ const serverId = await resolveServerId(serverRef, orgId);
6924
+ try {
6925
+ const data = await api.post(GIT_LINK_PATH, {
6926
+ organizationId: orgId,
6927
+ serverId,
6928
+ owner: repo.owner,
6929
+ name: repo.name,
6930
+ branch: opts.branch,
6931
+ pathPrefix: opts.pathPrefix,
6932
+ autoSync: opts.autoSync === true
6933
+ });
6934
+ if (isJsonMode()) {
6935
+ printJson({ linked: true, ...data });
6936
+ return;
6937
+ }
6938
+ printSuccess(`Linked to ${data.owner}/${data.name} and synced. \`mcp dev\` now runs git-native for this server.`);
6939
+ } catch (err) {
6940
+ if (err instanceof McpCloudApiError && (err.error.code === "uncommitted_changes" || err.error.code === "no_commit_to_sync")) {
6941
+ printSuccess(`Linked to ${repo.owner}/${repo.name}.`);
6942
+ printError(`Initial sync skipped: ${err.error.message}`);
6943
+ printInfo(` ${c.dim("Then push with")} ${c.bold("mcp servers git sync <server>")}`);
6944
+ throw new CliExitError(1);
6945
+ }
6946
+ reportApiError2(err, "Link");
6947
+ }
6948
+ }));
6949
+ git.command("sync <server>").description("Push committed HEAD to the linked repo now (no cron wait)").option("--org <organizationId>", "Organization ID").action(runAction(async (serverRef, opts) => {
6950
+ const orgId = await resolveOrgId(opts.org);
6951
+ const serverId = await resolveServerId(serverRef, orgId);
6952
+ try {
6953
+ const data = await api.post(GIT_SYNC_PATH, {
6954
+ organizationId: orgId,
6955
+ serverId
6956
+ });
6957
+ if (isJsonMode()) {
6958
+ printJson(data);
6959
+ return;
6960
+ }
6961
+ printSuccess(`Mirror push landed as ${shortSha(data.commitSha)}.`);
6962
+ } catch (err) {
6963
+ reportApiError2(err, "Sync");
6964
+ }
6965
+ }));
6966
+ git.command("unlink <server>").description("Unlink the GitHub repo — the repo is untouched, mirroring and reconciliation stop").option("--org <organizationId>", "Organization ID").action(runAction(async (serverRef, opts) => {
6967
+ const orgId = await resolveOrgId(opts.org);
6968
+ const serverId = await resolveServerId(serverRef, orgId);
6969
+ try {
6970
+ const data = await api.delete(`${GIT_LINK_PATH}?organizationId=${encodeURIComponent(orgId)}&serverId=${encodeURIComponent(serverId)}`);
6971
+ if (isJsonMode()) {
6972
+ printJson(data);
6973
+ return;
6974
+ }
6975
+ if (data.cleared) {
6976
+ printSuccess("Unlinked. The repository itself is untouched.");
6977
+ } else {
6978
+ printInfo("Nothing to do — this server was not linked.");
6979
+ }
6980
+ } catch (err) {
6981
+ reportApiError2(err, "Unlink");
6982
+ }
6983
+ }));
6984
+ }
6985
+ function registerServerCommitCommand(servers) {
6986
+ servers.command("commit <server>").description("Commit the working tree as a new internal version (advances the branch — mirror + deploy see it)").requiredOption("-m, --message <message>", "Commit message").option("--branch <branch>", "Branch to commit on (default: main)").option("--org <organizationId>", "Organization ID").addHelpText("after", [
6987
+ "",
6988
+ "Notes:",
6989
+ " Headless semantics: commits whatever the working tree holds right",
6990
+ " now — including edits reconciled from a `git push`. A clean tree is",
6991
+ " a no-op success, not an error. Check what would land with",
6992
+ " `mcp servers git status <server>`."
6993
+ ].join(`
6994
+ `)).action(runAction(async (serverRef, opts) => {
6995
+ const orgId = await resolveOrgId(opts.org);
6996
+ const serverId = await resolveServerId(serverRef, orgId);
6997
+ try {
6998
+ const data = await api.post(COMMIT_PATH, {
6999
+ organizationId: orgId,
7000
+ serverId,
7001
+ message: opts.message,
7002
+ branch: opts.branch
7003
+ });
7004
+ if (isJsonMode()) {
7005
+ printJson(data);
7006
+ return;
7007
+ }
7008
+ if (data.noop) {
7009
+ printInfo("Nothing to commit — the working tree is clean.");
7010
+ return;
7011
+ }
7012
+ printSuccess(`Committed ${data.changedFileCount} file${data.changedFileCount === 1 ? "" : "s"} (${data.commitId}).`);
7013
+ } catch (err) {
7014
+ reportApiError2(err, "Commit");
7015
+ }
7016
+ }));
7017
+ }
7018
+
6797
7019
  // src/commands/servers-prompts.ts
6798
7020
  import { readFileSync as readFileSync3 } from "node:fs";
6799
7021
 
@@ -6815,7 +7037,7 @@ async function resolveServerScope(serverRef, org) {
6815
7037
 
6816
7038
  // src/commands/servers-prompts.ts
6817
7039
  var PROMPT_PATH = "/api/v1/server/prompt";
6818
- function reportApiError2(err, action) {
7040
+ function reportApiError3(err, action) {
6819
7041
  if (err instanceof McpCloudApiError) {
6820
7042
  printError(`${action} failed (HTTP ${err.status}): ${err.error.message}`);
6821
7043
  const details = err.error.details;
@@ -6871,7 +7093,7 @@ function registerServerPromptCommands(servers) {
6871
7093
  { key: "description", label: "DESCRIPTION", width: 48 }
6872
7094
  ]);
6873
7095
  } catch (err) {
6874
- reportApiError2(err, "Listing prompts");
7096
+ reportApiError3(err, "Listing prompts");
6875
7097
  }
6876
7098
  }));
6877
7099
  prompts.command("set <server> <name>").description("Create or replace a prompt by name. Re-running with the same name updates it.").option("--org <organizationId>", "Organization ID").option("--template <markdown>", "Prompt body, inline").option("--template-file <path>", "Prompt body, read from a file").option("--arguments <json>", "JSON array of {name, description, required}; defaults to []").option("--description <text>", "What the prompt is for").addHelpText("after", [
@@ -6909,7 +7131,7 @@ function registerServerPromptCommands(servers) {
6909
7131
  }
6910
7132
  printInfo(c.dim(" Not live yet: run `mcp servers generate <server> --store`, then deploy."));
6911
7133
  } catch (err) {
6912
- reportApiError2(err, "Saving the prompt");
7134
+ reportApiError3(err, "Saving the prompt");
6913
7135
  }
6914
7136
  }));
6915
7137
  prompts.command("rm <server> <name>").description("Remove a prompt by name.").option("--org <organizationId>", "Organization ID").action(runAction(async (server, name, opts) => {
@@ -6922,7 +7144,7 @@ function registerServerPromptCommands(servers) {
6922
7144
  }
6923
7145
  printSuccess(`Removed prompt ${c.bold(name)}.`);
6924
7146
  } catch (err) {
6925
- reportApiError2(err, "Removing the prompt");
7147
+ reportApiError3(err, "Removing the prompt");
6926
7148
  }
6927
7149
  }));
6928
7150
  }
@@ -6930,7 +7152,7 @@ function registerServerPromptCommands(servers) {
6930
7152
  // src/commands/servers-resources.ts
6931
7153
  import { readFileSync as readFileSync4 } from "node:fs";
6932
7154
  var RESOURCE_PATH = "/api/v1/server/resource";
6933
- function reportApiError3(err, action) {
7155
+ function reportApiError4(err, action) {
6934
7156
  if (err instanceof McpCloudApiError) {
6935
7157
  printError(`${action} failed (HTTP ${err.status}): ${err.error.message}`);
6936
7158
  } else {
@@ -6968,7 +7190,7 @@ function registerServerResourceCommands(servers) {
6968
7190
  { key: "mime", label: "MIME", width: 20 }
6969
7191
  ]);
6970
7192
  } catch (err) {
6971
- reportApiError3(err, "Listing resources");
7193
+ reportApiError4(err, "Listing resources");
6972
7194
  }
6973
7195
  }));
6974
7196
  resources.command("set <server> <uri>").description("Create or replace a resource by URI. Re-running with the same URI updates it.").option("--org <organizationId>", "Organization ID").option("--name <name>", "Human-readable name (defaults to the URI)").option("--description <text>", "What the resource holds").option("--mime <type>", "MIME type", "text/markdown").option("--content <text>", "Body of a static resource, inline").option("--content-file <path>", "Body of a static resource, from a file").option("--upstream <template>", "Upstream path template — makes this a DYNAMIC resource fetched per read").addHelpText("after", [
@@ -7010,7 +7232,7 @@ function registerServerResourceCommands(servers) {
7010
7232
  printSuccess(`${data.resource.replaced ? "Updated" : "Created"} ${resourceType} resource ${c.bold(uri)} on ${scope.serverName}.`);
7011
7233
  printInfo(c.dim(" Not live yet: run `mcp servers generate <server> --store`, then deploy."));
7012
7234
  } catch (err) {
7013
- reportApiError3(err, "Saving the resource");
7235
+ reportApiError4(err, "Saving the resource");
7014
7236
  }
7015
7237
  }));
7016
7238
  resources.command("rm <server> <uri>").description("Remove a resource by URI.").option("--org <organizationId>", "Organization ID").action(runAction(async (server, uri, opts) => {
@@ -7023,7 +7245,7 @@ function registerServerResourceCommands(servers) {
7023
7245
  }
7024
7246
  printSuccess(`Removed resource ${c.bold(uri)}.`);
7025
7247
  } catch (err) {
7026
- reportApiError3(err, "Removing the resource");
7248
+ reportApiError4(err, "Removing the resource");
7027
7249
  }
7028
7250
  }));
7029
7251
  }
@@ -7031,7 +7253,7 @@ function registerServerResourceCommands(servers) {
7031
7253
  // src/commands/servers-shape.ts
7032
7254
  var SCORE_PATH = "/api/v1/server/usability-score";
7033
7255
  var SHAPE_PATH = "/api/v1/server/shape";
7034
- function reportApiError4(err, action) {
7256
+ function reportApiError5(err, action) {
7035
7257
  if (err instanceof McpCloudApiError) {
7036
7258
  printError(`${action} failed (HTTP ${err.status}): ${err.error.message}`);
7037
7259
  } else {
@@ -7075,7 +7297,7 @@ function registerServerShapingCommands(servers) {
7075
7297
  printSuccess(`Scoring started for ${scope.serverName}.`);
7076
7298
  printInfo(c.dim(` ${data.message}`));
7077
7299
  } catch (err) {
7078
- reportApiError4(err, "Starting the scoring run");
7300
+ reportApiError5(err, "Starting the scoring run");
7079
7301
  }
7080
7302
  return;
7081
7303
  }
@@ -7096,7 +7318,7 @@ function registerServerShapingCommands(servers) {
7096
7318
  printSuccess(`${formatPercent(mean)} ±${formatPercent(max - min)} across ${data.batch.length} runs`);
7097
7319
  printInfo(c.dim(` ${data.batch[0]?.scored ?? 0} scenarios · ${data.catalogSize ?? "—"} tools · judged by ${data.modelKey ?? "—"}`));
7098
7320
  } catch (err) {
7099
- reportApiError4(err, "Reading the score");
7321
+ reportApiError5(err, "Reading the score");
7100
7322
  }
7101
7323
  }));
7102
7324
  servers.command("shape <server>").description("Propose task-shaped tools over this server’s endpoints (starts a run, or reads the last one)").option("--org <organizationId>", "Organization ID").option("--start", "Start a new shaping run instead of reading the last").addHelpText("after", [
@@ -7126,7 +7348,7 @@ function registerServerShapingCommands(servers) {
7126
7348
  printSuccess(`Shaping started for ${scope.serverName}.`);
7127
7349
  printInfo(c.dim(` ${data.message}`));
7128
7350
  } catch (err) {
7129
- reportApiError4(err, "Starting the shaping run");
7351
+ reportApiError5(err, "Starting the shaping run");
7130
7352
  }
7131
7353
  return;
7132
7354
  }
@@ -7159,7 +7381,7 @@ function registerServerShapingCommands(servers) {
7159
7381
  }
7160
7382
  printInfo(c.dim(" Accept a proposal in the dashboard; nothing is created here."));
7161
7383
  } catch (err) {
7162
- reportApiError4(err, "Reading shaping proposals");
7384
+ reportApiError5(err, "Reading shaping proposals");
7163
7385
  }
7164
7386
  }));
7165
7387
  }
@@ -8084,13 +8306,14 @@ function registerServerGetCommand(servers) {
8084
8306
  organizationId: orgId,
8085
8307
  serverId
8086
8308
  });
8309
+ const gitLink = await api.get("/api/v1/server/git-link", { organizationId: orgId, serverId }).then((r) => r.gitLink).catch(() => null);
8087
8310
  const probe = opts.probe ? await probeServerSurface({
8088
8311
  configured: data.server,
8089
8312
  organizationId: orgId,
8090
8313
  serverId
8091
8314
  }) : null;
8092
8315
  if (isJsonMode()) {
8093
- printJson(probe ? { ...data, probe } : data);
8316
+ printJson({ ...data, gitLink, ...probe ? { probe } : {} });
8094
8317
  if (probe?.drift.length)
8095
8318
  throw new CliExitError(1);
8096
8319
  return;
@@ -8105,6 +8328,7 @@ function registerServerGetCommand(servers) {
8105
8328
  url: s.deploymentUrl ?? "—",
8106
8329
  "tool surface": describeToolSurface(s),
8107
8330
  "upstream keys": s.upstreamApiKeyMode ?? "shared",
8331
+ git: gitLink ? `${gitLink.owner}/${gitLink.name}@${gitLink.branch} (mcp servers git status)` : "—",
8108
8332
  "latest deployment": s.latestDeploymentId ?? "—",
8109
8333
  "last deployed": s.lastDeployedAt ? formatDate(s.lastDeployedAt) : "—",
8110
8334
  created: formatDate(s.createdAt),
@@ -8720,7 +8944,7 @@ function registerServerLifecycleCommands(servers) {
8720
8944
 
8721
8945
  // src/commands/servers-mutations.ts
8722
8946
  function registerServerMutationCommands(servers) {
8723
- 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").addHelpText("after", [
8947
+ 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", [
8724
8948
  "",
8725
8949
  "Notes:",
8726
8950
  " Empty-skeleton create. Source-material import (OpenAPI/GraphQL spec)",
@@ -8743,6 +8967,15 @@ function registerServerMutationCommands(servers) {
8743
8967
  if (opts.description !== undefined)
8744
8968
  body["description"] = opts.description;
8745
8969
  const data = await api.post("/api/v1/servers", body);
8970
+ if (opts.gitRepo) {
8971
+ await linkRepoAtCreation({
8972
+ orgId,
8973
+ serverId: data.server.id,
8974
+ repoRef: opts.gitRepo,
8975
+ pathPrefix: opts.gitPathPrefix,
8976
+ autoSync: opts.gitAutoSync
8977
+ });
8978
+ }
8746
8979
  if (isJsonMode()) {
8747
8980
  printJson(data);
8748
8981
  return;
@@ -9038,7 +9271,7 @@ function validateSourceType(value) {
9038
9271
  return v2;
9039
9272
  }
9040
9273
  function registerServerSpecCommands(servers) {
9041
- servers.command("ingest").description("Create a server from an OpenAPI spec (file, URL, or @- stdin) or a GraphQL endpoint. Wraps POST /api/v1/server/ingest.").requiredOption("--project <project>", "Project (id or name) to create the server in").requiredOption("--spec <file|url>", "OpenAPI spec (local file, http(s) URL, or @- for stdin), or — with --source-type graphql — the GraphQL endpoint URL to introspect").option("--org <organizationId>", "Organization ID").option("--source-type <type>", `Override spec classification: ${SOURCE_TYPES.join(" | ")} (OpenAPI auto-detected; graphql requires an endpoint URL)`).addHelpText("after", [
9274
+ servers.command("ingest").description("Create a server from an OpenAPI spec (file, URL, or @- stdin) or a GraphQL endpoint. Wraps POST /api/v1/server/ingest.").requiredOption("--project <project>", "Project (id or name) to create the server in").requiredOption("--spec <file|url>", "OpenAPI spec (local file, http(s) URL, or @- for stdin), or — with --source-type graphql — the GraphQL endpoint URL to introspect").option("--org <organizationId>", "Organization ID").option("--source-type <type>", `Override spec classification: ${SOURCE_TYPES.join(" | ")} (OpenAPI auto-detected; graphql requires an endpoint URL)`).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", [
9042
9275
  "",
9043
9276
  "Examples:",
9044
9277
  " $ mcp servers ingest --project proj_123 --spec ./openapi.yaml",
@@ -9059,6 +9292,15 @@ function registerServerSpecCommands(servers) {
9059
9292
  projectId,
9060
9293
  payload
9061
9294
  }));
9295
+ if (opts.gitRepo) {
9296
+ await linkRepoAtCreation({
9297
+ orgId,
9298
+ serverId: data.server.id,
9299
+ repoRef: opts.gitRepo,
9300
+ pathPrefix: opts.gitPathPrefix,
9301
+ autoSync: opts.gitAutoSync
9302
+ });
9303
+ }
9062
9304
  if (isJsonMode()) {
9063
9305
  printJson(data);
9064
9306
  return;
@@ -9527,6 +9769,8 @@ function registerServerTestCommands(servers) {
9527
9769
  function registerServerCommands(program2) {
9528
9770
  const servers = program2.command("servers").description("Manage MCP servers");
9529
9771
  registerServerDepsCommands(servers);
9772
+ registerServerGitCommands(servers);
9773
+ registerServerCommitCommand(servers);
9530
9774
  registerServerPromptCommands(servers);
9531
9775
  registerServerResourceCommands(servers);
9532
9776
  registerServerShapingCommands(servers);
@@ -13231,8 +13475,8 @@ function registerSkillCommands(program2) {
13231
13475
  }
13232
13476
 
13233
13477
  // src/lib/dev/prepare.ts
13234
- import { existsSync as existsSync20, writeFileSync as writeFileSync12 } from "node:fs";
13235
- import { join as join18, relative as relative6 } from "node:path";
13478
+ import { existsSync as existsSync20, readFileSync as readFileSync20, writeFileSync as writeFileSync12 } from "node:fs";
13479
+ import { join as join18, relative as relative6, resolve as resolve8 } from "node:path";
13236
13480
 
13237
13481
  // src/lib/dev/bundle-sync.ts
13238
13482
  import {
@@ -13443,6 +13687,24 @@ function runGit(args, cwd) {
13443
13687
  stderr: res.stderr ?? ""
13444
13688
  };
13445
13689
  }
13690
+ function getOriginUrl(repoDir) {
13691
+ try {
13692
+ const res = runGit(["remote", "get-url", "origin"], repoDir);
13693
+ const url = res.stdout.trim();
13694
+ return res.status === 0 && url ? url : null;
13695
+ } catch {
13696
+ return null;
13697
+ }
13698
+ }
13699
+ function originMatchesRepo(originUrl, owner, name) {
13700
+ const normalized = originUrl.trim().toLowerCase();
13701
+ const target = `${owner}/${name}`.toLowerCase();
13702
+ const patterns = [
13703
+ `github.com/${target}`,
13704
+ `github.com:${target}`
13705
+ ];
13706
+ return patterns.some((p2) => normalized.endsWith(p2) || normalized.endsWith(`${p2}.git`));
13707
+ }
13446
13708
  function isGitAvailable() {
13447
13709
  try {
13448
13710
  return runGit(["--version"]).status === 0;
@@ -28543,6 +28805,18 @@ function buildInspectorModule() {
28543
28805
  ` if (response.body && !isStream) {`,
28544
28806
  ` const buf = new Uint8Array(await response.clone().arrayBuffer())`,
28545
28807
  ` responseBodyText = decodeBody(buf)`,
28808
+ ` // Rebuild the response from the captured bytes. Under Bun, streaming`,
28809
+ ` // the original body after the clone-read yields ZERO bytes inside a`,
28810
+ ` // node:http server callback (the recorder saw the full body while the`,
28811
+ ` // client got Content-Length: 0) — a buffer-backed Response cannot`,
28812
+ ` // drain. content-length is dropped so node:http recomputes it.`,
28813
+ ` const rebuiltHeaders = new Headers(response.headers)`,
28814
+ ` rebuiltHeaders.delete('content-length')`,
28815
+ ` response = new Response(buf.byteLength > 0 ? buf : null, {`,
28816
+ ` status: response.status,`,
28817
+ ` statusText: response.statusText,`,
28818
+ ` headers: rebuiltHeaders,`,
28819
+ ` })`,
28546
28820
  ` } else if (response.body && isStream) {`,
28547
28821
  ` const [forUser, forCapture] = response.body.tee()`,
28548
28822
  ` const reader = forCapture.getReader()`,
@@ -29000,6 +29274,47 @@ async function installDependencies(options) {
29000
29274
  }
29001
29275
 
29002
29276
  // src/lib/dev/prepare.ts
29277
+ function missingEnvBindingNames(envExample, envJson) {
29278
+ let present = {};
29279
+ try {
29280
+ const parsed = JSON.parse(envJson);
29281
+ if (parsed && typeof parsed === "object") {
29282
+ present = parsed;
29283
+ }
29284
+ } catch {}
29285
+ const names = [];
29286
+ for (const line of envExample.split(`
29287
+ `)) {
29288
+ const match = /^([A-Za-z_][A-Za-z0-9_]*)=/.exec(line.trim());
29289
+ const name = match?.[1];
29290
+ if (!name)
29291
+ continue;
29292
+ const value = present[name];
29293
+ if (typeof value === "string" && value.length > 0)
29294
+ continue;
29295
+ if (!names.includes(name))
29296
+ names.push(name);
29297
+ }
29298
+ return names;
29299
+ }
29300
+ function printEnvBindingHint(dest, envPath, cwd) {
29301
+ const examplePath = join18(dest, ".env.example");
29302
+ if (!existsSync20(examplePath))
29303
+ return;
29304
+ let example = "";
29305
+ let envJson = "{}";
29306
+ try {
29307
+ example = readFileSync20(examplePath, "utf-8");
29308
+ if (existsSync20(envPath))
29309
+ envJson = readFileSync20(envPath, "utf-8");
29310
+ } catch {
29311
+ return;
29312
+ }
29313
+ const missing = missingEnvBindingNames(example, envJson);
29314
+ if (missing.length === 0)
29315
+ return;
29316
+ printInfo(` → this server expects ${missing.map((n) => c.bold(n)).join(", ")} — add value${missing.length === 1 ? "" : "s"} to ${relative6(cwd, envPath)} (upstream calls 401 until then)`);
29317
+ }
29003
29318
  var ENTRY_CANDIDATES = ["src/worker.ts", "src/server.ts", "src/index.ts"];
29004
29319
  async function prepareDev(opts) {
29005
29320
  const cwd = opts.cwd;
@@ -29024,7 +29339,19 @@ async function prepareDev(opts) {
29024
29339
  gitNative = true;
29025
29340
  projectId = projectId ?? gitLink.projectId;
29026
29341
  serverName = `${gitLink.owner}/${gitLink.name}`;
29027
- repoDir = gitCloneDir(cwd, gitLink.owner, gitLink.name);
29342
+ const registered = readConfig().gitRepoDirs?.[opts.serverId];
29343
+ const chosen = opts.repoDirOverride ? resolve8(cwd, opts.repoDirOverride) : registered;
29344
+ repoDir = chosen ?? gitCloneDir(cwd, gitLink.owner, gitLink.name);
29345
+ if (chosen && existsSync20(join18(repoDir, ".git"))) {
29346
+ const origin = getOriginUrl(repoDir);
29347
+ if (!origin || !originMatchesRepo(origin, gitLink.owner, gitLink.name)) {
29348
+ printError(`${repoDir} is a checkout of ${origin ?? "an unknown remote"}, not ${gitLink.owner}/${gitLink.name}. Pass a checkout of the linked repo, or drop --repo to use the default clone.`);
29349
+ process.exit(1);
29350
+ }
29351
+ }
29352
+ if (registered && !opts.repoDirOverride) {
29353
+ printInfo(` → using registered checkout ${repoDir}`);
29354
+ }
29028
29355
  dest = gitLink.pathPrefix ? join18(repoDir, gitLink.pathPrefix) : repoDir;
29029
29356
  printStep(`Git-linked server — cloning ${gitLink.owner}/${gitLink.name}@${gitLink.branch}…`);
29030
29357
  try {
@@ -29035,6 +29362,11 @@ async function prepareDev(opts) {
29035
29362
  onStep: (m2) => printStep(m2)
29036
29363
  });
29037
29364
  printSuccess(result.cloned ? `Cloned to ${relative6(cwd, repoDir)}` : `Using existing clone at ${relative6(cwd, repoDir)}`);
29365
+ const cfg = readConfig();
29366
+ if (cfg.gitRepoDirs?.[opts.serverId] !== repoDir) {
29367
+ cfg.gitRepoDirs = { ...cfg.gitRepoDirs, [opts.serverId]: repoDir };
29368
+ writeConfig(cfg);
29369
+ }
29038
29370
  } catch (err) {
29039
29371
  if (err instanceof GitNotAvailableError) {
29040
29372
  printError(err.message);
@@ -29050,6 +29382,7 @@ async function prepareDev(opts) {
29050
29382
  `, "utf-8");
29051
29383
  printInfo(` → seeded ${relative6(cwd, env)} (empty; fill secrets here)`);
29052
29384
  }
29385
+ printEnvBindingHint(dest, env, cwd);
29053
29386
  printInfo(` ${"Open the clone in your editor; commit + push to sync (config under mcpcloud/ is read-only)."}`);
29054
29387
  } else if (!opts.offline) {
29055
29388
  if (!projectId) {
@@ -29075,6 +29408,7 @@ async function prepareDev(opts) {
29075
29408
  `, "utf-8");
29076
29409
  printInfo(` → seeded ${relative6(cwd, env)} (empty; fill secrets here)`);
29077
29410
  }
29411
+ printEnvBindingHint(dest, env, cwd);
29078
29412
  printStep("Fetching tool metadata…");
29079
29413
  const tools = await fetchToolsForServer({
29080
29414
  organizationId: opts.organizationId,
@@ -29145,7 +29479,7 @@ async function prepareDev(opts) {
29145
29479
 
29146
29480
  // src/lib/dev/tree-sync.ts
29147
29481
  import { createHash as createHash4 } from "node:crypto";
29148
- import { existsSync as existsSync21, mkdirSync as mkdirSync14, readdirSync as readdirSync3, readFileSync as readFileSync20, writeFileSync as writeFileSync13 } from "node:fs";
29482
+ import { existsSync as existsSync21, mkdirSync as mkdirSync14, readdirSync as readdirSync3, readFileSync as readFileSync21, writeFileSync as writeFileSync13 } from "node:fs";
29149
29483
  import { join as join19 } from "node:path";
29150
29484
  var AUTHORING_GUIDE_FILE = "AGENTS.md";
29151
29485
  var STRUCTURAL_FRONTMATTER_KEYS = [
@@ -29167,7 +29501,7 @@ function listToolFiles(devRootDir, serverId) {
29167
29501
  }
29168
29502
  function readToolFile(devRootDir, serverId, toolName) {
29169
29503
  const file = join19(serverToolsDir(devRootDir, serverId), `${toolName}.md`);
29170
- const raw = readFileSync20(file, "utf-8");
29504
+ const raw = readFileSync21(file, "utf-8");
29171
29505
  try {
29172
29506
  return { raw, view: parseToolMarkdown(raw) };
29173
29507
  } catch (err) {
@@ -29287,7 +29621,7 @@ function listHandlerFiles(devRootDir) {
29287
29621
  return [];
29288
29622
  return readdirSync3(dir).filter((entry) => entry.endsWith(".ts") && !entry.endsWith(".definition.ts")).sort().map((entry) => ({
29289
29623
  slug: entry.slice(0, -3),
29290
- source: readFileSync20(join19(dir, entry), "utf-8")
29624
+ source: readFileSync21(join19(dir, entry), "utf-8")
29291
29625
  }));
29292
29626
  }
29293
29627
  function writeHandlerManifest(args) {
@@ -29315,7 +29649,7 @@ function recordHandlerPushed(args) {
29315
29649
  serverId: args.serverId,
29316
29650
  version: 1
29317
29651
  };
29318
- manifest.handlers[args.slug] = hashSource2(readFileSync20(file, "utf-8"));
29652
+ manifest.handlers[args.slug] = hashSource2(readFileSync21(file, "utf-8"));
29319
29653
  writeFileSync13(handlerManifestPath(args.devRootDir, args.serverId), JSON.stringify(manifest, null, 2), "utf-8");
29320
29654
  }
29321
29655
  function readHandlerManifest(devRootDir, serverId) {
@@ -29323,7 +29657,7 @@ function readHandlerManifest(devRootDir, serverId) {
29323
29657
  if (!existsSync21(file))
29324
29658
  return null;
29325
29659
  try {
29326
- const parsed = JSON.parse(readFileSync20(file, "utf-8"));
29660
+ const parsed = JSON.parse(readFileSync21(file, "utf-8"));
29327
29661
  return parsed.version === 1 && parsed.handlers ? parsed : null;
29328
29662
  } catch {
29329
29663
  return null;
@@ -30213,7 +30547,7 @@ function registerConfigCommands(program2) {
30213
30547
  }
30214
30548
  printSuccess(`Saved editor command: ${cfg.editorCommand}`);
30215
30549
  }));
30216
- config.command("use <name>").description("Switch the active profile (e.g. staging, prod)").action(runAction((name) => {
30550
+ config.command("use <name>").description("Switch the active profile (e.g. selfhost, prod)").action(runAction((name) => {
30217
30551
  setCurrentProfile(name);
30218
30552
  if (isJsonMode()) {
30219
30553
  printJson({ currentProfile: name });
@@ -30293,13 +30627,13 @@ function registerConfigCommands(program2) {
30293
30627
 
30294
30628
  // src/commands/dev.ts
30295
30629
  import { existsSync as existsSync34 } from "node:fs";
30296
- import { isAbsolute as isAbsolute4, relative as relative7, resolve as resolve9 } from "node:path";
30630
+ import { isAbsolute as isAbsolute4, relative as relative7, resolve as resolve10 } from "node:path";
30297
30631
 
30298
30632
  // src/lib/dev/sessions.ts
30299
30633
  import {
30300
30634
  existsSync as existsSync22,
30301
30635
  mkdirSync as mkdirSync15,
30302
- readFileSync as readFileSync21,
30636
+ readFileSync as readFileSync22,
30303
30637
  readdirSync as readdirSync4,
30304
30638
  unlinkSync,
30305
30639
  writeFileSync as writeFileSync14
@@ -30343,7 +30677,7 @@ function listSessions() {
30343
30677
  continue;
30344
30678
  const path = join21(dir, entry);
30345
30679
  try {
30346
- const parsed = JSON.parse(readFileSync21(path, "utf-8"));
30680
+ const parsed = JSON.parse(readFileSync22(path, "utf-8"));
30347
30681
  if (typeof parsed.pid !== "number") {
30348
30682
  unlinkSync(path);
30349
30683
  continue;
@@ -30803,7 +31137,7 @@ function emitLines(text, filter) {
30803
31137
  }
30804
31138
  }
30805
31139
  function sleep2(ms) {
30806
- return new Promise((resolve8) => setTimeout(resolve8, ms));
31140
+ return new Promise((resolve9) => setTimeout(resolve9, ms));
30807
31141
  }
30808
31142
 
30809
31143
  // src/lib/dev/bootstrap.ts
@@ -31097,6 +31431,17 @@ async function maybeOpenEditor(args) {
31097
31431
  printSavedNeverHint();
31098
31432
  return { opened: false, reason: "saved-never" };
31099
31433
  }
31434
+ if (args.override === "no" || args.override === "never") {
31435
+ if (args.override === "never") {
31436
+ const cfg = readConfig();
31437
+ cfg.editorOpenPreference = "never";
31438
+ writeConfig(cfg);
31439
+ }
31440
+ return {
31441
+ opened: false,
31442
+ reason: args.override === "never" ? "saved-never" : "declined"
31443
+ };
31444
+ }
31100
31445
  if (detection.kind === "none") {
31101
31446
  if (args.override === "yes" || args.override === "always") {
31102
31447
  printError("Cannot open editor: no supported editor found on PATH.");
@@ -31112,6 +31457,9 @@ async function maybeOpenEditor(args) {
31112
31457
  let workingCommand;
31113
31458
  let workingDisplay;
31114
31459
  let savedFromPicker = false;
31460
+ if (detection.kind === "multiple" && isNonInteractive()) {
31461
+ return { opened: false, reason: "non-interactive" };
31462
+ }
31115
31463
  if (detection.kind === "multiple") {
31116
31464
  const picked = await promptForEditor(detection.candidates, args);
31117
31465
  if (picked === null) {
@@ -31144,21 +31492,16 @@ async function maybeOpenEditor(args) {
31144
31492
  cfg.editorOpenPreference = "always";
31145
31493
  writeConfig(cfg);
31146
31494
  willOpen = true;
31147
- } else if (args.override === "never") {
31148
- const cfg = readConfig();
31149
- cfg.editorOpenPreference = "never";
31150
- writeConfig(cfg);
31151
- willOpen = false;
31152
- savedNeverThisRun = true;
31153
31495
  } else if (args.override === "yes") {
31154
31496
  willOpen = true;
31155
- } else if (args.override === "no") {
31156
- willOpen = false;
31157
31497
  } else if (args.override !== "ask" && savedPref === "always") {
31158
31498
  willOpen = true;
31159
31499
  } else if (savedFromPicker) {
31160
31500
  willOpen = true;
31161
31501
  }
31502
+ if (willOpen === null && isNonInteractive()) {
31503
+ return { opened: false, reason: "non-interactive" };
31504
+ }
31162
31505
  if (willOpen === null) {
31163
31506
  const promptResult = await promptOpenChoice(workingCommand, args.dir, args.target);
31164
31507
  willOpen = promptResult.open;
@@ -31702,19 +32045,19 @@ import { homedir as homedir4 } from "node:os";
31702
32045
  import { join as join26 } from "node:path";
31703
32046
 
31704
32047
  // src/lib/dev/agent-connectors/json-config-utils.ts
31705
- import { existsSync as existsSync28, mkdirSync as mkdirSync16, readFileSync as readFileSync22, writeFileSync as writeFileSync15 } from "node:fs";
32048
+ import { existsSync as existsSync28, mkdirSync as mkdirSync16, readFileSync as readFileSync23, writeFileSync as writeFileSync15 } from "node:fs";
31706
32049
  import { dirname as dirname10, join as join25, basename } from "node:path";
31707
32050
  function readJsonFile(path) {
31708
32051
  if (!existsSync28(path))
31709
32052
  return { ok: true, value: {} };
31710
32053
  try {
31711
- const raw = readFileSync22(path, "utf-8");
32054
+ const raw = readFileSync23(path, "utf-8");
31712
32055
  if (!raw.trim())
31713
32056
  return { ok: true, value: {} };
31714
32057
  return { ok: true, value: JSON.parse(raw) };
31715
32058
  } catch {
31716
32059
  try {
31717
- return { ok: false, raw: readFileSync22(path, "utf-8") };
32060
+ return { ok: false, raw: readFileSync23(path, "utf-8") };
31718
32061
  } catch {
31719
32062
  return { ok: false };
31720
32063
  }
@@ -31736,7 +32079,7 @@ function backupConfig(args) {
31736
32079
  const filename = `${args.agentId}__${basename(args.configPath)}.bak`;
31737
32080
  const backupPath = join25(args.backupsDir, filename);
31738
32081
  if (!existsSync28(backupPath)) {
31739
- const raw = readFileSync22(args.configPath, "utf-8");
32082
+ const raw = readFileSync23(args.configPath, "utf-8");
31740
32083
  writeFileSync15(backupPath, raw, "utf-8");
31741
32084
  }
31742
32085
  return { backupPath, existed: true };
@@ -31747,7 +32090,7 @@ function restoreFromBackup(args) {
31747
32090
  if (!existsSync28(backupPath)) {
31748
32091
  return { restored: false };
31749
32092
  }
31750
- const raw = readFileSync22(backupPath, "utf-8");
32093
+ const raw = readFileSync23(backupPath, "utf-8");
31751
32094
  mkdirSync16(dirname10(args.configPath), { recursive: true });
31752
32095
  writeFileSync15(args.configPath, raw, "utf-8");
31753
32096
  return { restored: true };
@@ -31836,7 +32179,7 @@ var claudeCodeConnector = {
31836
32179
  import {
31837
32180
  existsSync as existsSync30,
31838
32181
  mkdirSync as mkdirSync17,
31839
- readFileSync as readFileSync23,
32182
+ readFileSync as readFileSync24,
31840
32183
  unlinkSync as unlinkSync3,
31841
32184
  writeFileSync as writeFileSync17
31842
32185
  } from "node:fs";
@@ -31895,7 +32238,7 @@ var codexConnector = {
31895
32238
  });
31896
32239
  let existing = "";
31897
32240
  if (existsSync30(path)) {
31898
- existing = readFileSync23(path, "utf-8");
32241
+ existing = readFileSync24(path, "utf-8");
31899
32242
  }
31900
32243
  const conflict = findSectionRange(existing, args.name) !== null;
31901
32244
  let next = existing;
@@ -31917,7 +32260,7 @@ var codexConnector = {
31917
32260
  async remove(args) {
31918
32261
  const path = configPath2();
31919
32262
  if (existsSync30(path)) {
31920
- const existing = readFileSync23(path, "utf-8");
32263
+ const existing = readFileSync24(path, "utf-8");
31921
32264
  const range = findSectionRange(existing, args.name);
31922
32265
  if (range) {
31923
32266
  const next = existing.slice(0, range.start) + existing.slice(range.end);
@@ -31930,7 +32273,7 @@ var codexConnector = {
31930
32273
  agentId: this.id
31931
32274
  });
31932
32275
  if (!restored.restored && existsSync30(path)) {
31933
- const after = readFileSync23(path, "utf-8");
32276
+ const after = readFileSync24(path, "utf-8");
31934
32277
  if (!after.trim()) {
31935
32278
  try {
31936
32279
  unlinkSync3(path);
@@ -32092,7 +32435,7 @@ var cursorConnector = {
32092
32435
  // src/lib/dev/agent-connectors/vscode-copilot.ts
32093
32436
  import { existsSync as existsSync33, unlinkSync as unlinkSync6, writeFileSync as writeFileSync20 } from "node:fs";
32094
32437
  import { homedir as homedir8, platform as platform4 } from "node:os";
32095
- import { join as join30, resolve as resolve8 } from "node:path";
32438
+ import { join as join30, resolve as resolve9 } from "node:path";
32096
32439
  function userLevelConfigPath() {
32097
32440
  const home = homedir8();
32098
32441
  const p2 = platform4();
@@ -32106,7 +32449,7 @@ function userLevelConfigPath() {
32106
32449
  return join30(xdg, "Code", "User", "mcp.json");
32107
32450
  }
32108
32451
  function isHomeDir(cwd) {
32109
- return resolve8(cwd) === resolve8(homedir8());
32452
+ return resolve9(cwd) === resolve9(homedir8());
32110
32453
  }
32111
32454
  function hasWorkspaceVscode(cwd) {
32112
32455
  return existsSync33(join30(cwd, ".vscode"));
@@ -32319,14 +32662,14 @@ function parseGraceMs(value, fallbackMs) {
32319
32662
  return Math.round(n * 1000);
32320
32663
  }
32321
32664
  function resolveSpecPath(cwd, raw) {
32322
- return isAbsolute4(raw) ? raw : resolve9(cwd, raw);
32665
+ return isAbsolute4(raw) ? raw : resolve10(cwd, raw);
32323
32666
  }
32324
32667
  function buildConnectionName(serverId) {
32325
32668
  const slug = serverId.toLowerCase().replace(/[^a-z0-9]+/g, "-").slice(0, 24) || "mcp-server";
32326
32669
  return `mcpsh-dev-${slug}`;
32327
32670
  }
32328
32671
  function registerDevCommand(program2) {
32329
- const dev = program2.command("dev").description("Run a deployed MCP server locally with hot reload + agent auto-wire").option("--server <server>", "Server (id, name, or slug; default: from .mcpcloud/state.json)").option("--org <organizationId>", "Organization ID (overrides default)").option("--port <number>", "Local port (default 8787)").option("--transport <list>", "Comma-separated: http,sse,stdio (default http,sse)").option("--stdio", "Shorthand for --transport stdio").option("--offline", "Use cached bundle, skip Convex sync").option("--no-watch", "Disable file watcher").option("--no-connect", "Disable agent auto-wire prompts").option("--auto-connect <agent>", "Auto-wire the named agent (claude-code, cursor, codex, vscode-copilot, continue)").option("--no-inspector", "Disable the inspector UI (default: enabled at /__inspect)").option("--spec <path>", "Watch this OpenAPI spec file and regenerate the bundle on change").option("--no-auto-regen", "Show spec drift but do not auto-apply (manual file edit still works)").option("--regen-grace <seconds>", "Grace period before auto-applying spec changes (default 5)").option("--enrich-on-change", "Preserve per-tool enrichment when the spec changes").option("--open <choice>", "Open an editor on startup: yes | always | no | never | ask").option("--open-target <choice>", "What to open: tools (.mcpcloud/tools/<server>/) | bundle (read-only .mcpcloud/server/) | all (both). When omitted on first run, mcp dev prompts.").option("--editor <command>", "Editor binary for this invocation (e.g. cursor, code, windsurf). Overrides saved editorCommand and the multi-editor picker.").option("--verbose", "Verbose logs").action(runAction(runDev));
32672
+ const dev = program2.command("dev").description("Run a deployed MCP server locally with hot reload + agent auto-wire").option("--server <server>", "Server (id, name, or slug; default: from .mcpcloud/state.json)").option("--org <organizationId>", "Organization ID (overrides default)").option("--port <number>", "Local port (default 8787)").option("--transport <list>", "Comma-separated: http,sse,stdio (default http,sse)").option("--stdio", "Shorthand for --transport stdio").option("--offline", "Use cached bundle, skip Convex sync").option("--repo <path>", "Git-native only: serve from this checkout of the linked repo (remembered per server, so later runs anywhere reuse it)").option("--no-watch", "Disable file watcher").option("--no-connect", "Disable agent auto-wire prompts").option("--auto-connect <agent>", "Auto-wire the named agent (claude-code, cursor, codex, vscode-copilot, continue)").option("--no-inspector", "Disable the inspector UI (default: enabled at /__inspect)").option("--spec <path>", "Watch this OpenAPI spec file and regenerate the bundle on change").option("--no-auto-regen", "Show spec drift but do not auto-apply (manual file edit still works)").option("--regen-grace <seconds>", "Grace period before auto-applying spec changes (default 5)").option("--enrich-on-change", "Preserve per-tool enrichment when the spec changes").option("--open <choice>", "Open an editor on startup: yes | always | no | never | ask").option("--open-target <choice>", "What to open: tools (.mcpcloud/tools/<server>/) | bundle (read-only .mcpcloud/server/) | all (both). When omitted on first run, mcp dev prompts.").option("--editor <command>", "Editor binary for this invocation (e.g. cursor, code, windsurf). Overrides saved editorCommand and the multi-editor picker.").option("--verbose", "Verbose logs").action(runAction(runDev));
32330
32673
  dev.addHelpText("after", `
32331
32674
  Git-native mode (automatic):
32332
32675
  If the chosen server is linked to a GitHub repo, mcp dev clones it under
@@ -32334,7 +32677,7 @@ Git-native mode (automatic):
32334
32677
  your own editor, then commit + push: the webhook reconciles src/** back into
32335
32678
  MCPCloud. The CLI's tool/handler PATCH watchers are off in this mode (native
32336
32679
  git is the version control). Config under mcpcloud/ is a read-only mirror —
32337
- change it in the dashboard. Deploy when ready with \`mcp deploy\` or the UI.
32680
+ change it in the dashboard. Deploy when ready with \`mcp servers deploy\` or the UI.
32338
32681
  An unlinked server uses the default flow (download bundle, edit via dashboard).
32339
32682
  `);
32340
32683
  dev.command("list").description("List running mcp dev sessions on this machine").action(runAction(runDevList));
@@ -32477,7 +32820,8 @@ async function runDev(opts) {
32477
32820
  port,
32478
32821
  transports,
32479
32822
  offline: Boolean(opts.offline),
32480
- inspector: inspectorEnabled
32823
+ inspector: inspectorEnabled,
32824
+ repoDirOverride: typeof opts.repo === "string" ? opts.repo : undefined
32481
32825
  });
32482
32826
  let specPath = null;
32483
32827
  if (opts.spec) {
@@ -32762,7 +33106,7 @@ async function runWithRuntime(args) {
32762
33106
  printInfo(` ${c.dim(`Edit files in ${relative7(args.cwd, args.prepared.destDir)}/ to hot-reload.`)}`);
32763
33107
  if (args.gitNative && args.prepared.repoDir) {
32764
33108
  printInfo(` ${c.dim(`Git-native: edit + commit + push in ${relative7(args.cwd, args.prepared.repoDir)}/ — the webhook reconciles src/**.`)}`);
32765
- printInfo(` ${c.dim("Config under mcpcloud/ is read-only; change it in the dashboard. Deploy when ready (mcp deploy).")}`);
33109
+ printInfo(` ${c.dim("Config under mcpcloud/ is read-only; change it in the dashboard. Deploy when ready (mcp servers deploy).")}`);
32766
33110
  }
32767
33111
  if (toolsWatcher) {
32768
33112
  printInfo(` ${c.dim(`Edit tool metadata in .mcpcloud/tools/${args.serverId}/ (changes push to cloud on save).`)}`);
@@ -32803,7 +33147,7 @@ async function runWithRuntime(args) {
32803
33147
  parts.push(`${status.deletedFileCount} deleted`);
32804
33148
  }
32805
33149
  printSuccess(`Push ${sha} reconciled (${parts.join(", ")}) — changes ready for deployment.`);
32806
- printInfo(` ${c.dim("Deploy with")} ${c.bold("mcp deploy")} ${c.dim("or the dashboard — no preview needed, this session is your local server.")}`);
33150
+ printInfo(` ${c.dim("Deploy with")} ${c.bold("mcp servers deploy")} ${c.dim("or the dashboard — no preview needed, this session is your local server.")}`);
32807
33151
  },
32808
33152
  onError: () => {}
32809
33153
  }) : null;
@@ -33408,12 +33752,12 @@ async function confirmRollback(deploymentId) {
33408
33752
  }
33409
33753
 
33410
33754
  // src/commands/doctor.ts
33411
- import { existsSync as existsSync36, statSync as statSync6, readFileSync as readFileSync25 } from "node:fs";
33755
+ import { existsSync as existsSync36, statSync as statSync6, readFileSync as readFileSync26 } from "node:fs";
33412
33756
  import { homedir as homedir9, platform as platform6 } from "node:os";
33413
33757
  import { join as join32, delimiter as delimiter3 } from "node:path";
33414
33758
 
33415
33759
  // src/lib/version-check.ts
33416
- import { existsSync as existsSync35, mkdirSync as mkdirSync18, readFileSync as readFileSync24, writeFileSync as writeFileSync21 } from "node:fs";
33760
+ import { existsSync as existsSync35, mkdirSync as mkdirSync18, readFileSync as readFileSync25, writeFileSync as writeFileSync21 } from "node:fs";
33417
33761
  import { join as join31 } from "node:path";
33418
33762
  var CACHE_TTL_MS = 24 * 60 * 60 * 1000;
33419
33763
  var FETCH_TIMEOUT_MS2 = 2000;
@@ -33428,7 +33772,7 @@ function readCache() {
33428
33772
  if (!existsSync35(cacheFile()))
33429
33773
  return null;
33430
33774
  try {
33431
- const raw = JSON.parse(readFileSync24(cacheFile(), "utf-8"));
33775
+ const raw = JSON.parse(readFileSync25(cacheFile(), "utf-8"));
33432
33776
  if (typeof raw.latest !== "string" || typeof raw.fetchedAt !== "number")
33433
33777
  return null;
33434
33778
  return { latest: raw.latest, fetchedAt: raw.fetchedAt };
@@ -33574,7 +33918,7 @@ function checkConfigFile() {
33574
33918
  } catch {}
33575
33919
  }
33576
33920
  try {
33577
- JSON.parse(readFileSync25(path, "utf-8"));
33921
+ JSON.parse(readFileSync26(path, "utf-8"));
33578
33922
  } catch (err) {
33579
33923
  return {
33580
33924
  name: "Config file",
@@ -33929,7 +34273,7 @@ var TOPICS = {
33929
34273
  " 2. Paste flow (SSH/headless): mcp login --paste",
33930
34274
  " Prompts for an API key generated in the dashboard.",
33931
34275
  "",
33932
- " 3. Environment variable: export MCPCLOUD_API_KEY=mck_…",
34276
+ " 3. Environment variable: export MCPCLOUD_API_KEY=mcf_…",
33933
34277
  " Highest precedence. Overrides any saved key for the current process.",
33934
34278
  "",
33935
34279
  "Show the active identity:",
@@ -33945,12 +34289,12 @@ var TOPICS = {
33945
34289
  title: "Profiles",
33946
34290
  body: [
33947
34291
  "A profile is a named bundle of API key + base URL + default org. Use profiles to switch between",
33948
- "staging and production deployments without editing JSON by hand.",
34292
+ "production and self-hosted deployments without editing JSON by hand.",
33949
34293
  "",
33950
- " $ mcp config profile add staging --url https://staging.example.com --key mck_…",
33951
- " $ mcp config use staging",
34294
+ " $ mcp config profile add selfhost --url https://selfhost.example.com --key mcf_…",
34295
+ " $ mcp config use selfhost",
33952
34296
  " $ mcp config profile list",
33953
- " $ mcp --profile staging servers list # one-off override",
34297
+ " $ mcp --profile selfhost servers list # one-off override",
33954
34298
  "",
33955
34299
  "Resolution order for the active profile:",
33956
34300
  " 1. --profile <name> flag",
@@ -34045,10 +34389,10 @@ function prompt2(question) {
34045
34389
  throw new Error("Interactive prompts are disabled in CI mode. Pass --org / --project / --name explicitly or run `mcp init` outside CI.");
34046
34390
  }
34047
34391
  const rl = createInterface3({ input: process.stdin, output: process.stderr });
34048
- return new Promise((resolve10) => {
34392
+ return new Promise((resolve11) => {
34049
34393
  rl.question(question, (answer) => {
34050
34394
  rl.close();
34051
- resolve10(answer.trim());
34395
+ resolve11(answer.trim());
34052
34396
  });
34053
34397
  });
34054
34398
  }
@@ -34188,6 +34532,15 @@ async function runSpecInit(args) {
34188
34532
  }
34189
34533
  const ingest = await api.post("/api/v1/server/ingest", buildIngestBody({ organizationId: orgId, projectId: project.id, payload }));
34190
34534
  const server = ingest.server;
34535
+ if (opts.gitRepo) {
34536
+ await linkRepoAtCreation({
34537
+ orgId,
34538
+ serverId: server.id,
34539
+ repoRef: opts.gitRepo,
34540
+ pathPrefix: opts.gitPathPrefix,
34541
+ autoSync: opts.gitAutoSync
34542
+ });
34543
+ }
34191
34544
  if (!isJsonMode()) {
34192
34545
  printSuccess(`Server ${c.bold(server.id)} created from spec.`);
34193
34546
  }
@@ -34297,7 +34650,7 @@ function reportDeploySuccess(args) {
34297
34650
 
34298
34651
  // src/commands/init.ts
34299
34652
  function registerInitCommand(program2) {
34300
- program2.command("init").description("Guided onboarding: pick org → pick/create project → create a server (empty skeleton, or generated from an OpenAPI/GraphQL spec via --from-spec)").option("--org <organizationId>", "Organization ID").option("--project <project>", "Reuse an existing project (id or name) instead of creating one").option("--name <serverName>", "Name for the new server (skeleton path only)").option("--from-spec <file|url>", "Build from an OpenAPI spec (local file, http(s) URL, or @- for stdin), or — with --source-type graphql — a GraphQL endpoint URL to introspect").option("--source-type <type>", `Override spec classification: ${SOURCE_TYPES.join(" | ")} (OpenAPI auto-detected; graphql requires an endpoint URL)`).option("--deploy", "After importing the spec, deploy the server").option("--wait", "With --deploy, follow events until the deploy is terminal").option("--wait-timeout <seconds>", "Max seconds to wait for a terminal deploy state (default 300)", "300").addHelpText("after", [
34653
+ program2.command("init").description("Guided onboarding: pick org → pick/create project → create a server (empty skeleton, or generated from an OpenAPI/GraphQL spec via --from-spec)").option("--org <organizationId>", "Organization ID").option("--project <project>", "Reuse an existing project (id or name) instead of creating one").option("--name <serverName>", "Name for the new server (skeleton path only)").option("--from-spec <file|url>", "Build from an OpenAPI spec (local file, http(s) URL, or @- for stdin), or — with --source-type graphql — a GraphQL endpoint URL to introspect").option("--source-type <type>", `Override spec classification: ${SOURCE_TYPES.join(" | ")} (OpenAPI auto-detected; graphql requires an endpoint URL)`).option("--deploy", "After importing the spec, deploy the server").option("--wait", "With --deploy, follow events until the deploy is terminal").option("--wait-timeout <seconds>", "Max seconds to wait for a terminal deploy state (default 300)", "300").option("--git-repo <owner/name>", "Host the generated code on this GitHub repo (spec path only; 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", [
34301
34654
  "",
34302
34655
  "Two modes:",
34303
34656
  " Skeleton (default): resolve org → pick/create project → create an empty server.",
@@ -34420,7 +34773,7 @@ function registerInstallationCommands(program2) {
34420
34773
  }
34421
34774
 
34422
34775
  // src/commands/invoke.ts
34423
- import { existsSync as existsSync37, readFileSync as readFileSync26 } from "node:fs";
34776
+ import { existsSync as existsSync37, readFileSync as readFileSync27 } from "node:fs";
34424
34777
  import { join as join33 } from "node:path";
34425
34778
 
34426
34779
  // src/lib/dev/local-invoke.ts
@@ -34431,7 +34784,7 @@ function isReadyMessage(value) {
34431
34784
  async function bootLocalRuntime(args) {
34432
34785
  const startupErrors = [];
34433
34786
  let handle;
34434
- const ready = new Promise((resolve10, reject) => {
34787
+ const ready = new Promise((resolve11, reject) => {
34435
34788
  const timer = setTimeout(() => {
34436
34789
  reject(new Error(`The local runtime did not become ready within ${READY_TIMEOUT_MS / 1000}s.${startupErrors.length > 0 ? `
34437
34790
  ${startupErrors.join("").trim()}` : ""}`));
@@ -34452,7 +34805,7 @@ ${startupErrors.join("").trim()}` : ""}`));
34452
34805
  if (!isReadyMessage(message))
34453
34806
  return;
34454
34807
  clearTimeout(timer);
34455
- resolve10(typeof message.baseUrl === "string" ? message.baseUrl : `http://127.0.0.1:${args.port}`);
34808
+ resolve11(typeof message.baseUrl === "string" ? message.baseUrl : `http://127.0.0.1:${args.port}`);
34456
34809
  });
34457
34810
  });
34458
34811
  try {
@@ -34513,9 +34866,9 @@ function selectCodeModeInvocation(args) {
34513
34866
  }
34514
34867
  function readCodeOption(raw) {
34515
34868
  if (raw === "@-")
34516
- return readFileSync26(0, "utf-8");
34869
+ return readFileSync27(0, "utf-8");
34517
34870
  if (raw.startsWith("@"))
34518
- return readFileSync26(raw.slice(1), "utf-8");
34871
+ return readFileSync27(raw.slice(1), "utf-8");
34519
34872
  return raw;
34520
34873
  }
34521
34874
  function parseTimeoutMs4(value, fallbackMs) {
@@ -34797,7 +35150,7 @@ async function runLocalInvoke(args) {
34797
35150
  throw new CliExitError(1);
34798
35151
  }
34799
35152
  const env = {
34800
- ...args.envFile ? parseEnvFile(readFileSync26(args.envFile, "utf-8")) : {},
35153
+ ...args.envFile ? parseEnvFile(readFileSync27(args.envFile, "utf-8")) : {},
34801
35154
  ...parseEnvPairs(args.envPairs)
34802
35155
  };
34803
35156
  const port = await findAvailablePort({ range: 40, start: 43117 });
@@ -35789,7 +36142,7 @@ import {
35789
36142
  existsSync as existsSync38,
35790
36143
  mkdirSync as mkdirSync19,
35791
36144
  readdirSync as readdirSync5,
35792
- readFileSync as readFileSync27,
36145
+ readFileSync as readFileSync28,
35793
36146
  rmSync as rmSync3,
35794
36147
  statSync as statSync7
35795
36148
  } from "node:fs";
@@ -35809,7 +36162,7 @@ function readManifestFromPackageDir(packageDir) {
35809
36162
  return null;
35810
36163
  let pkg;
35811
36164
  try {
35812
- pkg = JSON.parse(readFileSync27(pkgPath, "utf-8"));
36165
+ pkg = JSON.parse(readFileSync28(pkgPath, "utf-8"));
35813
36166
  } catch {
35814
36167
  return null;
35815
36168
  }
@@ -35878,15 +36231,15 @@ async function loadPlugins(program2) {
35878
36231
  }
35879
36232
  var PACKAGE_SPEC_PATTERN = /^(@[a-z0-9~-][\w.~-]*\/)?[a-z0-9~-][\w.~-]*(@[-\w.^~><=*+|]+)?$/i;
35880
36233
  function runNpm(args, cwd) {
35881
- return new Promise((resolve10) => {
36234
+ return new Promise((resolve11) => {
35882
36235
  const isWindows = process.platform === "win32";
35883
36236
  const child = spawn7(isWindows ? "npm.cmd" : "npm", args, {
35884
36237
  shell: isWindows,
35885
36238
  cwd,
35886
36239
  stdio: "inherit"
35887
36240
  });
35888
- child.on("close", (code) => resolve10(code ?? 1));
35889
- child.on("error", () => resolve10(1));
36241
+ child.on("close", (code) => resolve11(code ?? 1));
36242
+ child.on("error", () => resolve11(1));
35890
36243
  });
35891
36244
  }
35892
36245
  async function installPlugin(packageSpec) {
@@ -36410,7 +36763,7 @@ function clipboardCandidatesFor(platform7, waylandDisplay) {
36410
36763
  return linux;
36411
36764
  }
36412
36765
  async function defaultIsExecutable(binary) {
36413
- return await new Promise((resolve10) => {
36766
+ return await new Promise((resolve11) => {
36414
36767
  const isWin = process.platform === "win32";
36415
36768
  const cmd = isWin ? "where" : "command";
36416
36769
  const args = isWin ? [binary] : ["-v", binary];
@@ -36418,8 +36771,8 @@ async function defaultIsExecutable(binary) {
36418
36771
  shell: !isWin,
36419
36772
  stdio: "ignore"
36420
36773
  });
36421
- child.on("error", () => resolve10(false));
36422
- child.on("exit", (code) => resolve10(code === 0));
36774
+ child.on("error", () => resolve11(false));
36775
+ child.on("exit", (code) => resolve11(code === 0));
36423
36776
  });
36424
36777
  }
36425
36778
  async function detectClipboard(probe) {
@@ -36441,7 +36794,7 @@ async function copyToClipboard(text, probe) {
36441
36794
  };
36442
36795
  }
36443
36796
  const spawnImpl = probe?.spawnImpl ?? spawn8;
36444
- return await new Promise((resolve10) => {
36797
+ return await new Promise((resolve11) => {
36445
36798
  const child = spawnImpl(candidate.binary, candidate.args, {
36446
36799
  stdio: ["pipe", "ignore", "pipe"]
36447
36800
  });
@@ -36450,7 +36803,7 @@ async function copyToClipboard(text, probe) {
36450
36803
  stderr += chunk.toString("utf-8");
36451
36804
  });
36452
36805
  child.on("error", (err) => {
36453
- resolve10({
36806
+ resolve11({
36454
36807
  kind: "failed",
36455
36808
  binary: candidate.binary,
36456
36809
  message: err instanceof Error ? err.message : String(err)
@@ -36458,9 +36811,9 @@ async function copyToClipboard(text, probe) {
36458
36811
  });
36459
36812
  child.on("exit", (code) => {
36460
36813
  if (code === 0) {
36461
- resolve10({ kind: "ok", binary: candidate.binary });
36814
+ resolve11({ kind: "ok", binary: candidate.binary });
36462
36815
  } else {
36463
- resolve10({
36816
+ resolve11({
36464
36817
  kind: "failed",
36465
36818
  binary: candidate.binary,
36466
36819
  message: stderr.trim() || `${candidate.binary} exited with status ${code ?? "?"}`
@@ -36470,7 +36823,7 @@ async function copyToClipboard(text, probe) {
36470
36823
  try {
36471
36824
  child.stdin?.end(text);
36472
36825
  } catch (err) {
36473
- resolve10({
36826
+ resolve11({
36474
36827
  kind: "failed",
36475
36828
  binary: candidate.binary,
36476
36829
  message: err instanceof Error ? err.message : String(err)
@@ -36532,7 +36885,7 @@ var CLI_EXAMPLES = {
36532
36885
  "config show": ["mcp config show"],
36533
36886
  "config set-url": ["mcp config set-url https://your-deployment.example.com"],
36534
36887
  "config set-org": ["mcp config set-org org_abc"],
36535
- "config use": ["mcp config use staging"],
36888
+ "config use": ["mcp config use selfhost"],
36536
36889
  "servers list": [
36537
36890
  "mcp servers list",
36538
36891
  "mcp servers list --filter status=active",
@@ -37273,13 +37626,14 @@ function liveBadge(frame, label = "LIVE") {
37273
37626
  return palette.semGreen + dot + " " + ansi.reset + ansi.bold + palette.ink100 + label + ansi.reset;
37274
37627
  }
37275
37628
  var SPLASH_LINES = [
37276
- "███╗ ███╗ ██████╗██████╗ ███████╗██╗ ██╗",
37277
- "████╗ ████║██╔════╝██╔══██╗██╔════╝██║ ██║",
37278
- "██╔████╔██║██║ ██████╔╝███████╗███████║",
37279
- "██║╚██╔╝██║██║ ██╔═══╝ ╚════██║██╔══██║",
37280
- "██║ ╚═╝ ██║╚██████╗██║ ███████║██║ ██║",
37281
- "╚═╝ ╚═╝ ╚═════╝╚═╝ ╚══════╝╚═╝ ╚═╝"
37629
+ "███╗ ███╗ ██████╗██████╗ ██████╗██╗ ██╗",
37630
+ "████╗ ████║██╔════╝██╔══██╗ ██╔════╝██║ ██║",
37631
+ "██╔████╔██║██║ ██████╔╝ ██║ ██║ ██║",
37632
+ "██║╚██╔╝██║██║ ██╔═══╝ ██║ ██║ ██║",
37633
+ "██║ ╚═╝ ██║╚██████╗██║ ╚██████╗███████╗██║",
37634
+ "╚═╝ ╚═╝ ╚═════╝╚═╝ ╚═════╝╚══════╝╚═╝"
37282
37635
  ];
37636
+ var SPLASH_ATTRIBUTION = "by MCPCloud.sh".padStart(49);
37283
37637
  function centerLine(text, width) {
37284
37638
  const len = visibleLength(text);
37285
37639
  if (len >= width)
@@ -37543,7 +37897,7 @@ function displayValueFor(field, value) {
37543
37897
  import {
37544
37898
  existsSync as existsSync39,
37545
37899
  mkdirSync as mkdirSync20,
37546
- readFileSync as readFileSync28,
37900
+ readFileSync as readFileSync29,
37547
37901
  writeFileSync as writeFileSync22
37548
37902
  } from "node:fs";
37549
37903
  import { join as join35 } from "node:path";
@@ -37625,7 +37979,7 @@ function readTuiState() {
37625
37979
  return {};
37626
37980
  let parsed;
37627
37981
  try {
37628
- parsed = JSON.parse(readFileSync28(path, "utf-8"));
37982
+ parsed = JSON.parse(readFileSync29(path, "utf-8"));
37629
37983
  } catch {
37630
37984
  return {};
37631
37985
  }
@@ -38094,8 +38448,8 @@ function renderSplash(state) {
38094
38448
  const colored = ansi.bold + palette.ink100 + (SPLASH_LINES[i] ?? "") + ansi.reset;
38095
38449
  out.push(moveTo(splashTop + i, 1) + palette.voidBg + centerLine(colored, cols) + ansi.reset);
38096
38450
  }
38097
- const tagline = palette.ink35 + "T H E F U L L M C P T O O L C H A I N" + ansi.reset;
38098
- out.push(moveTo(splashTop + SPLASH_LINES.length + 1, 1) + palette.voidBg + centerLine(tagline, cols) + ansi.reset);
38451
+ const attribution = palette.ink35 + SPLASH_ATTRIBUTION + ansi.reset;
38452
+ out.push(moveTo(splashTop + SPLASH_LINES.length, 1) + palette.voidBg + centerLine(attribution, cols) + ansi.reset);
38099
38453
  const spinner = SPINNER_FRAMES[state.spinnerFrame % SPINNER_FRAMES.length] ?? "⠋";
38100
38454
  const loading = palette.ink60 + spinner + ansi.reset + palette.ink35 + " fetching " + state.tab + " from " + state.orgId + "…" + ansi.reset;
38101
38455
  out.push(moveTo(splashTop + SPLASH_LINES.length + 3, 1) + palette.voidBg + centerLine(loading, cols) + ansi.reset);
@@ -38910,7 +39264,7 @@ async function startInspector(state, handles, redraw) {
38910
39264
  redraw();
38911
39265
  }
38912
39266
  function registerUiCommand(program2) {
38913
- program2.command("ui").description("Interactive terminal UI: browse orgs/servers/skills, tail deployment logs, and watch the live `mcp dev` inspector").option("--org <organizationId>", "Organization ID (overrides default)").option("--no-splash", "Skip the splash screen and jump straight to the list view").addHelpText("after", [
39267
+ program2.command("ui").description("Interactive terminal UI: browse orgs/servers/skills, tail deployment logs, and watch the live `mcp dev` inspector").option("--org <organizationId>", "Organization ID (overrides default)").option("--splash", "Always show the splash screen, even after the first run").option("--no-splash", "Skip the splash screen and jump straight to the list view").addHelpText("after", [
38914
39268
  "",
38915
39269
  "Tabs (1-6):",
38916
39270
  " 1 Servers",
@@ -38968,7 +39322,8 @@ function registerUiCommand(program2) {
38968
39322
  " Last tab, org, sort, filter, and selected row are persisted to",
38969
39323
  " ~/.mcpcloud/tui-state.json so the next launch resumes where you",
38970
39324
  " left off. The splash is shown once per install; pass --no-splash",
38971
- " to suppress it sooner, or delete tui-state.json to reset."
39325
+ " to suppress it sooner, or --splash to bring it back (demos,",
39326
+ " screenshots)."
38972
39327
  ].join(`
38973
39328
  `)).action(runAction(async (opts) => {
38974
39329
  if (!process.stdout.isTTY) {
@@ -38986,8 +39341,7 @@ function registerUiCommand(program2) {
38986
39341
  } else {
38987
39342
  orgId = await resolveOrgId(undefined);
38988
39343
  }
38989
- const splashFlagDisabled = opts.splash === false;
38990
- const showSplash = !splashFlagDisabled && persisted.splashSeen !== true;
39344
+ const showSplash = opts.splash === true || opts.splash !== false && persisted.splashSeen !== true;
38991
39345
  const sortByTab = {
38992
39346
  servers: persisted.sortByTab?.servers ?? null,
38993
39347
  projects: persisted.sortByTab?.projects ?? null,
@@ -39641,7 +39995,7 @@ function registerUiCommand(program2) {
39641
39995
  }
39642
39996
  return false;
39643
39997
  };
39644
- await new Promise((resolve10) => {
39998
+ await new Promise((resolve11) => {
39645
39999
  const flushAndExit = () => {
39646
40000
  try {
39647
40001
  if (persistTimer) {
@@ -39651,7 +40005,7 @@ function registerUiCommand(program2) {
39651
40005
  writeTuiState(snapshotForPersist());
39652
40006
  } catch {}
39653
40007
  shutdown(handles);
39654
- resolve10();
40008
+ resolve11();
39655
40009
  };
39656
40010
  const executePaletteCommand = (id) => {
39657
40011
  if (id.startsWith("tab:")) {
@@ -40321,10 +40675,10 @@ function buildCommand(manager) {
40321
40675
  }
40322
40676
  }
40323
40677
  function runShell(command) {
40324
- return new Promise((resolve10) => {
40678
+ return new Promise((resolve11) => {
40325
40679
  const child = spawn9(command, { shell: true, stdio: "inherit" });
40326
- child.on("close", (code) => resolve10(code ?? 1));
40327
- child.on("error", () => resolve10(1));
40680
+ child.on("close", (code) => resolve11(code ?? 1));
40681
+ child.on("error", () => resolve11(1));
40328
40682
  });
40329
40683
  }
40330
40684
  function registerUpdateCommand(program2) {
@@ -40424,7 +40778,7 @@ function createProgram() {
40424
40778
  " $ mcp servers list --field id,name --format jsonl",
40425
40779
  " $ mcp servers list --filter status=active --no-table",
40426
40780
  "",
40427
- "Self-host / staging: point the CLI elsewhere with MCPCLOUD_BASE_URL,",
40781
+ "Self-hosted deployment: point the CLI elsewhere with MCPCLOUD_BASE_URL,",
40428
40782
  "`mcp config set-url <url>`, or --base-url <url> (each overrides the default)."
40429
40783
  ].join(`
40430
40784
  `));