@mcpcloud/cli 0.26.0 → 0.27.0-next-20261001025900

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 +7 -1
  2. package/dist/index.js +408 -75
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -507,9 +507,12 @@ _Generated from the live command tree by `bun run docs:readme` — do not edit b
507
507
  | `mcp servers deps add <server> <child>` | Compose <child>'s tools into <server>. Both accept an id, name or slug. |
508
508
  | `mcp servers deps list [server]` | List the servers this one composes. |
509
509
  | `mcp servers deps rm <server> <dependencyId>` | Remove a composed dependency (id from `deps list`). |
510
- | `mcp servers end-user` | Read and set a server's end-user plane: the access switch and the tool allowlist customers may call. |
510
+ | `mcp servers end-user` | Read and set a server's end-user plane: the access switch, the tool allowlist customers may call, and how they sign in. |
511
511
  | `mcp servers end-user get <server>` | Show the end-user access configuration: switch, connect page, allowed tools, branding, and how customers sign in. |
512
512
  | `mcp servers end-user set <server>` | Enable or disable end-user access, and/or replace the tool allowlist. |
513
+ | `mcp servers end-user sign-in` | How customers sign in: the one mode, and the settings of the chosen one. |
514
+ | `mcp servers end-user sign-in get <server>` | Show the chosen sign-in mode, the door customers actually meet, and each mode’s settings. |
515
+ | `mcp servers end-user sign-in set <server>` | Choose how customers sign in and set its settings. Sends only what the flags name; choosing a mode turns the others off. |
513
516
  | `mcp servers env` | Manage server runtime environment bindings (encrypted secrets injected by the proxy). |
514
517
  | `mcp servers env get <server> <bindingName>` | Show metadata for one env binding. Plaintext is never returned — values are write-only. |
515
518
  | `mcp servers env list <server>` | List a server's env bindings (metadata only; values are never returned). |
@@ -531,6 +534,9 @@ _Generated from the live command tree by `bun run docs:readme` — do not edit b
531
534
  | `mcp servers ingest` | Create a server from an OpenAPI spec (file, URL, or @- stdin) or a GraphQL endpoint. Wraps POST /api/v1/server/ingest. |
532
535
  | `mcp servers list` | List servers |
533
536
  | `mcp servers logs <server>` | Show recent deployment events for a server's latest deployment |
537
+ | `mcp servers oauth` | Read and set a server's OAuth provider: the endpoints, client credentials, scopes and tool coverage it uses for delegated authorization. |
538
+ | `mcp servers oauth get <server>` | Show the OAuth provider: status, endpoints, client id, whether a secret is on file, scopes, tool coverage, and the callback URL to register. |
539
+ | `mcp servers oauth set <server>` | Set the OAuth provider. Partial: a flag left off keeps what is stored. |
534
540
  | `mcp servers oauth-connections <server>` | List OAuth provider connections bound to this server |
535
541
  | `mcp servers pause <server>` | Pause the server's latest active deployment (traffic stops; worker is preserved) |
536
542
  | `mcp servers promote <server>` | Ship the bytes a lane is serving into another lane. Config, secrets and member credentials stay with the target. |
package/dist/index.js CHANGED
@@ -6243,9 +6243,20 @@ function registerServerShapingCommands(servers) {
6243
6243
  }
6244
6244
 
6245
6245
  // src/commands/servers-end-user.ts
6246
+ import { readFileSync as readFileSync6 } from "node:fs";
6247
+
6248
+ // src/commands/servers-end-user-sign-in.ts
6246
6249
  import { readFileSync as readFileSync5 } from "node:fs";
6247
6250
  var END_USER_PATH = "/api/v1/server/end-user-access";
6248
- async function readEndUserAccess(serverRef, opts) {
6251
+ var CUSTOMER_SIGN_IN_PATH = "/api/v1/server/customer-sign-in";
6252
+ var MODES = ["account", "page", "upstream", "federation"];
6253
+ var MODE_LABEL = {
6254
+ account: "an MCPCloud account",
6255
+ page: "sign-in on your page",
6256
+ upstream: "your OAuth provider",
6257
+ federation: "your own sign-in (federation)"
6258
+ };
6259
+ async function readSignIn(serverRef, opts) {
6249
6260
  const orgId = await resolveOrgId(opts.org);
6250
6261
  const serverId = await resolveServerId(serverRef, orgId, undefined);
6251
6262
  return await api.get(END_USER_PATH, {
@@ -6253,6 +6264,164 @@ async function readEndUserAccess(serverRef, opts) {
6253
6264
  serverId
6254
6265
  });
6255
6266
  }
6267
+ function printSignIn(data) {
6268
+ const chosen = data.customerSignIn;
6269
+ if (!chosen) {
6270
+ printKeyValue({
6271
+ server: `${data.server.name} (${data.server.id})`,
6272
+ "sign-in": "unknown — this deployment does not report the choice"
6273
+ });
6274
+ return;
6275
+ }
6276
+ const live = data.signInMode ?? "account";
6277
+ const methods = Object.entries(chosen.page).filter(([, on]) => on).map(([name]) => name);
6278
+ const identity = chosen.identity;
6279
+ printKeyValue({
6280
+ server: `${data.server.name} (${data.server.id})`,
6281
+ "chosen mode": MODE_LABEL[chosen.mode],
6282
+ "live door": live === chosen.mode || chosen.mode === "page" && live === "account" && methods.length > 0 ? "the chosen mode" : `${MODE_LABEL[live] ?? live} — the chosen mode is not finished`,
6283
+ "page methods": methods.length > 0 ? methods.join(", ") : "none",
6284
+ "identity source": identity.source === "idToken" ? `id_token${identity.endpoint ? ` (fallback ${identity.endpoint} → ${identity.userIdPath ?? "(no path)"})` : ""}` : identity.endpoint ? `${identity.endpoint} → ${identity.userIdPath ?? "(no path)"}` : "not configured",
6285
+ provider: data.upstreamSignIn?.providerLabel ?? "—",
6286
+ federation: data.signIn.configured ? `${data.signIn.status} (${data.signIn.issuer}, keys via ${data.signIn.keySource})` : "not configured",
6287
+ "sign-in url": data.signIn.signInUrl ?? "—"
6288
+ });
6289
+ }
6290
+ function onOff(value, flag) {
6291
+ if (value === undefined)
6292
+ return;
6293
+ const normalized = value.trim().toLowerCase();
6294
+ if (normalized === "on" || normalized === "true")
6295
+ return true;
6296
+ if (normalized === "off" || normalized === "false")
6297
+ return false;
6298
+ throw new Error(`${flag} must be on or off.`);
6299
+ }
6300
+ function textOrNull(value) {
6301
+ if (value === undefined)
6302
+ return;
6303
+ const trimmed = value.trim();
6304
+ if (trimmed.toLowerCase() === "none")
6305
+ return null;
6306
+ return trimmed.startsWith("@") ? readFileSync5(trimmed.slice(1), "utf8").trim() : trimmed;
6307
+ }
6308
+ function buildCustomerSignInBody(opts) {
6309
+ const body = {};
6310
+ if (opts.mode !== undefined) {
6311
+ if (!MODES.includes(opts.mode)) {
6312
+ throw new Error(`--mode must be one of ${MODES.join(", ")}.`);
6313
+ }
6314
+ body.mode = opts.mode;
6315
+ }
6316
+ const page = {};
6317
+ for (const [flag, key] of [
6318
+ ["--email-code", "emailCode"],
6319
+ ["--google", "google"],
6320
+ ["--github", "github"]
6321
+ ]) {
6322
+ const value = onOff(opts[key], flag);
6323
+ if (value !== undefined)
6324
+ page[key] = value;
6325
+ }
6326
+ if (Object.keys(page).length > 0)
6327
+ body.page = page;
6328
+ const identity = {};
6329
+ if (opts.identitySource !== undefined) {
6330
+ if (opts.identitySource !== "idToken" && opts.identitySource !== "userinfo") {
6331
+ throw new Error("--identity-source must be idToken or userinfo.");
6332
+ }
6333
+ identity.source = opts.identitySource;
6334
+ }
6335
+ for (const [flag, key] of [
6336
+ ["identityEndpoint", "endpoint"],
6337
+ ["identityUserIdPath", "userIdPath"],
6338
+ ["identityEmailPath", "emailPath"],
6339
+ ["identityNamePath", "namePath"]
6340
+ ]) {
6341
+ const value = textOrNull(opts[flag]);
6342
+ if (value !== undefined)
6343
+ identity[key] = value;
6344
+ }
6345
+ if (Object.keys(identity).length > 0)
6346
+ body.identity = identity;
6347
+ const federationGiven = opts.federationIssuer !== undefined || opts.federationJwksUri !== undefined || opts.federationJwks !== undefined || opts.federationSignInUrl !== undefined;
6348
+ if (federationGiven) {
6349
+ if (!opts.federationIssuer?.trim()) {
6350
+ throw new Error("--federation-issuer is required when setting federation.");
6351
+ }
6352
+ const federation = { issuer: opts.federationIssuer.trim() };
6353
+ for (const [flag, key] of [
6354
+ ["federationJwksUri", "jwksUri"],
6355
+ ["federationJwks", "jwks"],
6356
+ ["federationSignInUrl", "signInUrl"]
6357
+ ]) {
6358
+ const value = textOrNull(opts[flag]);
6359
+ if (value !== undefined)
6360
+ federation[key] = value;
6361
+ }
6362
+ body.federation = federation;
6363
+ }
6364
+ if (Object.keys(body).length === 0) {
6365
+ throw new Error("Pass at least one of --mode, a page method, an identity flag, or the federation flags.");
6366
+ }
6367
+ return body;
6368
+ }
6369
+ function registerServerEndUserSignInCommands(endUser) {
6370
+ const signIn = endUser.command("sign-in").description("How customers sign in: the one mode, and the settings of the chosen one.");
6371
+ signIn.command("get <server>").description("Show the chosen sign-in mode, the door customers actually meet, and each mode’s settings.").option("--org <organizationId>", "Organization ID").action(runAction(async (serverRef, opts) => {
6372
+ const data = await readSignIn(serverRef, opts);
6373
+ if (isJsonMode()) {
6374
+ printJson(data.customerSignIn ?? data);
6375
+ return;
6376
+ }
6377
+ printSignIn(data);
6378
+ }));
6379
+ signIn.command("set <server>").description("Choose how customers sign in and set its settings. Sends only what the flags name; choosing a mode turns the others off.").option("--org <organizationId>", "Organization ID").option("--mode <mode>", `One of ${MODES.join(", ")}`).option("--email-code <on|off>", "Sign-in on your page: the emailed code").option("--google <on|off>", "Sign-in on your page: MCPCloud’s Google app").option("--github <on|off>", "Sign-in on your page: MCPCloud’s GitHub app").option("--identity-source <idToken|userinfo>", "Where MCPCloud reads the user id after an OAuth sign-in").option("--identity-endpoint <url>", "Identity endpoint (`none` clears it)").option("--identity-user-id-path <path>", "JSON path to the stable user id").option("--identity-email-path <path>", "JSON path to the email (display only)").option("--identity-name-path <path>", "JSON path to the name (display only)").option("--federation-issuer <iss>", "Federation: the issuer your assertions carry").option("--federation-jwks-uri <url>", "Federation: where your public keys are published").option("--federation-jwks <json|@path>", "Federation: your public keys inline").option("--federation-sign-in-url <url>", "Federation: where signed-out visitors are sent (`none` clears it)").addHelpText("after", [
6380
+ "",
6381
+ "One mode per server. Choosing one turns the others off and keeps their",
6382
+ "settings. Federation cannot be chosen before its trust exists: pass the",
6383
+ "--federation-* flags in the same call.",
6384
+ "",
6385
+ "Everything here applies on the next sign-in — no redeploy. The provider",
6386
+ "itself is `mcp servers oauth set`.",
6387
+ "",
6388
+ "Examples:",
6389
+ " $ mcp servers end-user sign-in set srv_123 --mode upstream --identity-source idToken",
6390
+ " $ mcp servers end-user sign-in set srv_123 --mode upstream \\",
6391
+ " --identity-endpoint https://api.example.com/v1/me --identity-user-id-path userId",
6392
+ " $ mcp servers end-user sign-in set srv_123 --mode page --email-code on --github on",
6393
+ " $ mcp servers end-user sign-in set srv_123 --mode federation \\",
6394
+ " --federation-issuer https://ids.example.com --federation-jwks-uri https://ids.example.com/.well-known/jwks.json"
6395
+ ].join(`
6396
+ `)).action(runAction(async (serverRef, opts) => {
6397
+ const body = buildCustomerSignInBody(opts);
6398
+ const orgId = await resolveOrgId(opts.org);
6399
+ const serverId = await resolveServerId(serverRef, orgId, undefined);
6400
+ await api.put(CUSTOMER_SIGN_IN_PATH, {
6401
+ organizationId: orgId,
6402
+ serverId,
6403
+ ...body
6404
+ });
6405
+ const updated = await readSignIn(serverRef, opts);
6406
+ if (isJsonMode()) {
6407
+ printJson(updated.customerSignIn ?? updated);
6408
+ return;
6409
+ }
6410
+ printSuccess(`Customers of ${updated.server.name} sign in with ${MODE_LABEL[updated.customerSignIn?.mode ?? "account"]}.`);
6411
+ printSignIn(updated);
6412
+ }));
6413
+ }
6414
+
6415
+ // src/commands/servers-end-user.ts
6416
+ var END_USER_PATH2 = "/api/v1/server/end-user-access";
6417
+ async function readEndUserAccess(serverRef, opts) {
6418
+ const orgId = await resolveOrgId(opts.org);
6419
+ const serverId = await resolveServerId(serverRef, orgId, undefined);
6420
+ return await api.get(END_USER_PATH2, {
6421
+ organizationId: orgId,
6422
+ serverId
6423
+ });
6424
+ }
6256
6425
  function describeSignInMode(data) {
6257
6426
  const upstream = data.upstreamSignIn;
6258
6427
  const provider = upstream?.providerLabel ?? "the OAuth provider";
@@ -6272,6 +6441,7 @@ function printEndUserAccess(data) {
6272
6441
  server: `${data.server.name} (${data.server.id})`,
6273
6442
  access: data.endUserAccess,
6274
6443
  "sign-in mode": describeSignInMode(data),
6444
+ "chosen mode": data.customerSignIn?.mode ?? "—",
6275
6445
  "connect page": data.connectPageUrl ?? "—",
6276
6446
  "allowed tools": data.allowedTools.length > 0 ? `${data.allowedTools.length}: ${data.allowedTools.join(", ")}` : "none — end users can call nothing",
6277
6447
  "display name": data.branding?.displayName ?? "—",
@@ -6284,7 +6454,7 @@ function printEndUserAccess(data) {
6284
6454
  });
6285
6455
  }
6286
6456
  function parseToolList(raw) {
6287
- const text = raw.startsWith("@") ? readFileSync5(raw.slice(1), "utf8") : raw;
6457
+ const text = raw.startsWith("@") ? readFileSync6(raw.slice(1), "utf8") : raw;
6288
6458
  const trimmed = text.trim();
6289
6459
  if (trimmed.startsWith("[")) {
6290
6460
  const parsed = JSON.parse(trimmed);
@@ -6296,7 +6466,8 @@ function parseToolList(raw) {
6296
6466
  return trimmed.split(",").map((entry) => entry.trim()).filter(Boolean);
6297
6467
  }
6298
6468
  function registerServerEndUserCommands(servers) {
6299
- const endUser = servers.command("end-user").alias("end-users").description("Read and set a server's end-user plane: the access switch and the tool allowlist customers may call.");
6469
+ const endUser = servers.command("end-user").alias("end-users").description("Read and set a server's end-user plane: the access switch, the tool allowlist customers may call, and how they sign in.");
6470
+ registerServerEndUserSignInCommands(endUser);
6300
6471
  endUser.command("get <server>").description("Show the end-user access configuration: switch, connect page, allowed tools, branding, and how customers sign in.").option("--org <organizationId>", "Organization ID").addHelpText("after", [
6301
6472
  "",
6302
6473
  "End users are deny-by-default, so `allowed tools` is exhaustive — a",
@@ -6341,7 +6512,7 @@ function registerServerEndUserCommands(servers) {
6341
6512
  const orgId = await resolveOrgId(opts.org);
6342
6513
  const enabled = opts.enable ? true : opts.disable ? false : current.endUserAccess === "enabled";
6343
6514
  const allowedTools = opts.allowTools ? parseToolList(opts.allowTools) : null;
6344
- await api.put(END_USER_PATH, {
6515
+ await api.put(END_USER_PATH2, {
6345
6516
  organizationId: orgId,
6346
6517
  projectId: current.server.projectId,
6347
6518
  serverId: current.server.id,
@@ -6362,8 +6533,168 @@ function registerServerEndUserCommands(servers) {
6362
6533
  }));
6363
6534
  }
6364
6535
 
6536
+ // src/commands/servers-oauth.ts
6537
+ import { readFileSync as readFileSync7 } from "node:fs";
6538
+ var OAUTH_PROVIDER_PATH = "/api/v1/server/oauth-provider";
6539
+ async function readProvider(serverRef, opts) {
6540
+ const orgId = await resolveOrgId(opts.org);
6541
+ const serverId = await resolveServerId(serverRef, orgId, undefined);
6542
+ return await api.get(OAUTH_PROVIDER_PATH, {
6543
+ organizationId: orgId,
6544
+ serverId
6545
+ });
6546
+ }
6547
+ function printProvider(data) {
6548
+ const { oauth } = data;
6549
+ printKeyValue({
6550
+ server: `${data.server.name} (${data.server.id})`,
6551
+ status: !oauth.enabled ? "disabled" : oauth.configured ? "enabled and configured" : "enabled — incomplete",
6552
+ provider: oauth.providerKey ? `${oauth.providerLabel ?? oauth.providerKey} (${oauth.providerKey}, ${oauth.providerKind ?? "oauth2"})` : "not set up",
6553
+ "authorization url": oauth.authorizationUrl ?? "—",
6554
+ "token url": oauth.tokenUrl ?? "—",
6555
+ "revocation url": oauth.revocationUrl ?? "—",
6556
+ "client id": oauth.clientId ?? "—",
6557
+ "client secret": oauth.hasClientSecret ? "on file (write-only)" : "none",
6558
+ scopes: oauth.scopes.length > 0 ? oauth.scopes.join(" ") : "—",
6559
+ "tool coverage": oauth.applyToAllTools ? "every tool (regenerate + deploy to apply a change)" : "only tools whose spec declares OAuth",
6560
+ "callback url": oauth.callbackUrl ?? "—"
6561
+ });
6562
+ }
6563
+ function urlOrNull(value) {
6564
+ if (value === undefined)
6565
+ return;
6566
+ return value.trim().toLowerCase() === "none" ? null : value.trim();
6567
+ }
6568
+ function readClientSecret(opts) {
6569
+ if (opts.clientSecretStdin && opts.clientSecretEnv) {
6570
+ throw new Error("--client-secret-stdin and --client-secret-env are mutually exclusive.");
6571
+ }
6572
+ if (opts.clientSecretStdin) {
6573
+ const secret = readFileSync7(0, "utf8").trim();
6574
+ if (!secret)
6575
+ throw new Error("No client secret was read from stdin.");
6576
+ return secret;
6577
+ }
6578
+ if (opts.clientSecretEnv) {
6579
+ const secret = process.env[opts.clientSecretEnv]?.trim();
6580
+ if (!secret) {
6581
+ throw new Error(`${opts.clientSecretEnv} is not set in the environment.`);
6582
+ }
6583
+ return secret;
6584
+ }
6585
+ return;
6586
+ }
6587
+ function buildOAuthProviderBody(opts) {
6588
+ if (opts.enable && opts.disable) {
6589
+ throw new Error("--enable and --disable are mutually exclusive.");
6590
+ }
6591
+ if (opts.kind !== undefined && opts.kind !== "oauth2" && opts.kind !== "openIdConnect") {
6592
+ throw new Error("--kind must be oauth2 or openIdConnect.");
6593
+ }
6594
+ const clientSecret = readClientSecret(opts);
6595
+ if (clientSecret !== undefined && opts.clearClientSecret) {
6596
+ throw new Error("A new client secret and --clear-client-secret are mutually exclusive.");
6597
+ }
6598
+ const body = {};
6599
+ if (opts.enable)
6600
+ body.enabled = true;
6601
+ if (opts.disable)
6602
+ body.enabled = false;
6603
+ if (opts.provider !== undefined)
6604
+ body.providerKey = opts.provider.trim();
6605
+ if (opts.label !== undefined)
6606
+ body.providerLabel = opts.label.trim() || null;
6607
+ if (opts.kind !== undefined)
6608
+ body.providerKind = opts.kind;
6609
+ for (const [flag, key] of [
6610
+ ["authorizationUrl", "authorizationUrl"],
6611
+ ["tokenUrl", "tokenUrl"],
6612
+ ["revocationUrl", "revocationUrl"]
6613
+ ]) {
6614
+ const value = urlOrNull(opts[flag]);
6615
+ if (value !== undefined)
6616
+ body[key] = value;
6617
+ }
6618
+ if (opts.clientId !== undefined)
6619
+ body.clientId = opts.clientId.trim() || null;
6620
+ if (clientSecret !== undefined)
6621
+ body.clientSecret = clientSecret;
6622
+ if (opts.clearClientSecret)
6623
+ body.clearClientSecret = true;
6624
+ if (opts.scopes !== undefined) {
6625
+ body.scopes = opts.scopes.split(/[\s,]+/).map((scope) => scope.trim()).filter(Boolean);
6626
+ }
6627
+ if (opts.applyToAllTools !== undefined)
6628
+ body.applyToAllTools = opts.applyToAllTools;
6629
+ if (Object.keys(body).length === 0) {
6630
+ throw new Error("Pass at least one provider flag to change.");
6631
+ }
6632
+ return body;
6633
+ }
6634
+ function registerServerOAuthCommands(servers) {
6635
+ const oauth = servers.command("oauth").description("Read and set a server's OAuth provider: the endpoints, client credentials, scopes and tool coverage it uses for delegated authorization.");
6636
+ oauth.command("get <server>").description("Show the OAuth provider: status, endpoints, client id, whether a secret is on file, scopes, tool coverage, and the callback URL to register.").option("--org <organizationId>", "Organization ID").addHelpText("after", [
6637
+ "",
6638
+ "The client secret is write-only: this shows whether one is on file,",
6639
+ "never the value.",
6640
+ "",
6641
+ "Examples:",
6642
+ " $ mcp servers oauth get srv_123",
6643
+ " $ mcp --json servers oauth get srv_123 | jq '.oauth.callbackUrl'"
6644
+ ].join(`
6645
+ `)).action(runAction(async (serverRef, opts) => {
6646
+ const data = await readProvider(serverRef, opts);
6647
+ if (isJsonMode()) {
6648
+ printJson(data);
6649
+ return;
6650
+ }
6651
+ printProvider(data);
6652
+ }));
6653
+ oauth.command("set <server>").description("Set the OAuth provider. Partial: a flag left off keeps what is stored.").option("--org <organizationId>", "Organization ID").option("--enable", "Use this provider for delegated authorization").option("--disable", "Stop using it (settings are kept)").option("--provider <key>", "Preset key (github, google, slack, …) or any identifier for a custom provider").option("--label <label>", "Shown to customers: “Continue with <label>”").option("--kind <oauth2|openIdConnect>", "Provider kind").option("--authorization-url <url>", "Authorization endpoint (`none` clears it)").option("--token-url <url>", "Token endpoint (`none` clears it)").option("--revocation-url <url>", "Revocation endpoint (`none` clears it)").option("--client-id <id>", "OAuth client id").option("--client-secret-stdin", "Read the client secret from stdin").option("--client-secret-env <VAR>", "Read the client secret from this environment variable").option("--clear-client-secret", "Remove the stored client secret").option("--scopes <list>", "Replace the scopes (comma or space separated)").option("--apply-to-all-tools", "Use this provider for every tool").option("--no-apply-to-all-tools", "Only for tools whose spec declares OAuth").addHelpText("after", [
6654
+ "",
6655
+ "Provider, credentials and scopes apply to new sign-ins and token",
6656
+ "refreshes immediately. --apply-to-all-tools is baked into the worker:",
6657
+ "regenerate and deploy to apply it.",
6658
+ "",
6659
+ "The secret never goes on the command line. Pipe it in, or name a",
6660
+ "variable:",
6661
+ ' $ printf %s "$SECRET" | mcp servers oauth set srv_123 --client-secret-stdin',
6662
+ " $ mcp servers oauth set srv_123 --client-secret-env GATHERD_OAUTH_SECRET",
6663
+ "",
6664
+ "Examples:",
6665
+ " $ mcp servers oauth set srv_123 --provider custom --label Gatherd \\",
6666
+ " --authorization-url https://gatherd.dev/oauth/authorize \\",
6667
+ " --token-url https://gatherd.dev/oauth/token --client-id abc \\",
6668
+ " --scopes read,write --enable --apply-to-all-tools",
6669
+ " $ mcp servers oauth set srv_123 --disable"
6670
+ ].join(`
6671
+ `)).action(runAction(async (serverRef, opts) => {
6672
+ const body = buildOAuthProviderBody(opts);
6673
+ const orgId = await resolveOrgId(opts.org);
6674
+ const serverId = await resolveServerId(serverRef, orgId, undefined);
6675
+ await api.put(OAUTH_PROVIDER_PATH, {
6676
+ organizationId: orgId,
6677
+ serverId,
6678
+ ...body
6679
+ });
6680
+ const updated = await readProvider(serverRef, opts);
6681
+ if (isJsonMode()) {
6682
+ printJson(updated);
6683
+ return;
6684
+ }
6685
+ printSuccess(`OAuth provider saved for ${updated.server.name}.`);
6686
+ printProvider(updated);
6687
+ if (updated.oauth.callbackUrl) {
6688
+ printInfo(` Register ${updated.oauth.callbackUrl} with the provider.`);
6689
+ }
6690
+ if (body.applyToAllTools !== undefined) {
6691
+ printInfo(" Tool coverage changed: regenerate and deploy to apply it.");
6692
+ }
6693
+ }));
6694
+ }
6695
+
6365
6696
  // src/commands/servers-env.ts
6366
- import { existsSync as existsSync3, readFileSync as readFileSync6 } from "node:fs";
6697
+ import { existsSync as existsSync3, readFileSync as readFileSync8 } from "node:fs";
6367
6698
  import { resolve } from "node:path";
6368
6699
  function registerServerEnvCommands(servers) {
6369
6700
  const env = servers.command("env").description("Manage server runtime environment bindings (encrypted secrets injected by the proxy).");
@@ -6512,7 +6843,7 @@ async function resolveSetValue(args) {
6512
6843
  printError(`--from-file path not found: ${path}`);
6513
6844
  throw new CliExitError(1);
6514
6845
  }
6515
- return readFileSync6(path, "utf-8");
6846
+ return readFileSync8(path, "utf-8");
6516
6847
  }
6517
6848
  return await readStdin();
6518
6849
  }
@@ -6991,7 +7322,7 @@ function registerServerSandboxVariableCommands(servers) {
6991
7322
  }
6992
7323
 
6993
7324
  // src/lib/mcp-invoke.ts
6994
- import { existsSync as existsSync4, readFileSync as readFileSync7 } from "node:fs";
7325
+ import { existsSync as existsSync4, readFileSync as readFileSync9 } from "node:fs";
6995
7326
  import { resolve as resolve2 } from "node:path";
6996
7327
  var DEFAULT_TIMEOUT_MS3 = 60000;
6997
7328
  var invokeRequestId = 1;
@@ -7213,7 +7544,7 @@ async function parseToolArguments(opts) {
7213
7544
  if (!existsSync4(filePath)) {
7214
7545
  return { ok: false, reason: "file-not-found", path: filePath };
7215
7546
  }
7216
- source = readFileSync7(filePath, "utf-8");
7547
+ source = readFileSync9(filePath, "utf-8");
7217
7548
  } else {
7218
7549
  source = raw;
7219
7550
  }
@@ -7876,7 +8207,7 @@ import { isAbsolute as isAbsolute2, resolve as resolve4 } from "node:path";
7876
8207
  import { createHash as createHash2 } from "node:crypto";
7877
8208
  import {
7878
8209
  existsSync as existsSync5,
7879
- readFileSync as readFileSync8,
8210
+ readFileSync as readFileSync10,
7880
8211
  watch as fsWatch
7881
8212
  } from "node:fs";
7882
8213
  import { dirname as dirname3 } from "node:path";
@@ -7931,7 +8262,7 @@ function summarizeDiff(diff) {
7931
8262
  `) };
7932
8263
  }
7933
8264
  function readSpecFile(specPath) {
7934
- const content = readFileSync8(specPath, "utf-8");
8265
+ const content = readFileSync10(specPath, "utf-8");
7935
8266
  return { content, hash: hashSpecContent(content) };
7936
8267
  }
7937
8268
  function watchSpecFile(options) {
@@ -7951,7 +8282,7 @@ function watchSpecFile(options) {
7951
8282
  return;
7952
8283
  let content;
7953
8284
  try {
7954
- content = readFileSync8(options.specPath, "utf-8");
8285
+ content = readFileSync10(options.specPath, "utf-8");
7955
8286
  } catch {
7956
8287
  return;
7957
8288
  }
@@ -8796,8 +9127,9 @@ function registerServerUpdateCommand(servers) {
8796
9127
  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", [
8797
9128
  "",
8798
9129
  "Notes:",
8799
- " Runtime defaults (oauth, deploymentDefaults, runtime bindings) are not",
8800
- " v1 PATCH-able. Change them via `mcp servers deploy --upstream-*`.",
9130
+ " The OAuth provider is `mcp servers oauth set`; how customers sign in is",
9131
+ " `mcp servers end-user sign-in set`. Other runtime defaults (deployment",
9132
+ " defaults, runtime bindings) are set via `mcp servers deploy --upstream-*`.",
8801
9133
  " --discovery-mode and --code-mode change the server's TOOL SURFACE and",
8802
9134
  " reach clients on the next `mcp servers deploy`. Check what is live with",
8803
9135
  " `mcp servers get <server> --probe`.",
@@ -9085,7 +9417,7 @@ function registerServerMutationCommands(servers) {
9085
9417
  }
9086
9418
 
9087
9419
  // src/lib/spec-pipeline.ts
9088
- import { existsSync as existsSync7, mkdirSync as mkdirSync4, readFileSync as readFileSync9, writeFileSync as writeFileSync3 } from "node:fs";
9420
+ import { existsSync as existsSync7, mkdirSync as mkdirSync4, readFileSync as readFileSync11, writeFileSync as writeFileSync3 } from "node:fs";
9089
9421
  import { dirname as dirname4, join as join5, resolve as resolve5 } from "node:path";
9090
9422
  var SOURCE_TYPES = ["openapi", "graphql", "url"];
9091
9423
  function isHttpUrl(value) {
@@ -9118,7 +9450,7 @@ async function readSpecSource(spec, override, options = {}) {
9118
9450
  if (!existsSync7(filePath)) {
9119
9451
  throw new Error(`Spec file not found: ${filePath}`);
9120
9452
  }
9121
- content = readFileSync9(filePath, "utf-8");
9453
+ content = readFileSync11(filePath, "utf-8");
9122
9454
  pathHint = filePath;
9123
9455
  }
9124
9456
  if (!content.trim()) {
@@ -9301,7 +9633,7 @@ function registerServerSpecCommands(servers) {
9301
9633
  }
9302
9634
 
9303
9635
  // src/commands/servers-test-authoring.ts
9304
- import { readFileSync as readFileSync10 } from "node:fs";
9636
+ import { readFileSync as readFileSync12 } from "node:fs";
9305
9637
  var EXECUTION_PROFILES = [
9306
9638
  "mockBindings",
9307
9639
  "previewBindings",
@@ -9319,9 +9651,9 @@ function parseTimeoutMs(value, fallbackMs) {
9319
9651
  }
9320
9652
  function readValueOption(raw) {
9321
9653
  if (raw === "@-")
9322
- return readFileSync10(0, "utf8");
9654
+ return readFileSync12(0, "utf8");
9323
9655
  if (raw.startsWith("@"))
9324
- return readFileSync10(raw.slice(1), "utf8");
9656
+ return readFileSync12(raw.slice(1), "utf8");
9325
9657
  return raw;
9326
9658
  }
9327
9659
  async function resolveServerAndProject(serverRef, org) {
@@ -9683,6 +10015,7 @@ function registerServerCommands(program) {
9683
10015
  const servers = program.command("servers").description("Manage MCP servers");
9684
10016
  registerServerDepsCommands(servers);
9685
10017
  registerServerEndUserCommands(servers);
10018
+ registerServerOAuthCommands(servers);
9686
10019
  registerServerTraceCommands(servers);
9687
10020
  registerServerGitCommands(servers);
9688
10021
  registerServerCommitCommand(servers);
@@ -9867,7 +10200,7 @@ import { existsSync as existsSync10 } from "node:fs";
9867
10200
  import { join as join8, relative } from "node:path";
9868
10201
 
9869
10202
  // src/lib/dev/state.ts
9870
- import { existsSync as existsSync8, mkdirSync as mkdirSync5, readFileSync as readFileSync11, writeFileSync as writeFileSync4 } from "node:fs";
10203
+ import { existsSync as existsSync8, mkdirSync as mkdirSync5, readFileSync as readFileSync13, writeFileSync as writeFileSync4 } from "node:fs";
9871
10204
  import { join as join6, resolve as resolve6 } from "node:path";
9872
10205
  function cachedStateMatchesTarget(cached, target) {
9873
10206
  return cached !== null && cached.serverId === target.serverId && cached.organizationId === target.organizationId;
@@ -9896,7 +10229,7 @@ function readState(cwd) {
9896
10229
  if (!existsSync8(path))
9897
10230
  return null;
9898
10231
  try {
9899
- const parsed = JSON.parse(readFileSync11(path, "utf-8"));
10232
+ const parsed = JSON.parse(readFileSync13(path, "utf-8"));
9900
10233
  if (!parsed || typeof parsed !== "object")
9901
10234
  return null;
9902
10235
  const s = parsed;
@@ -9937,7 +10270,7 @@ function ensureGitignore(cwd) {
9937
10270
  import {
9938
10271
  existsSync as existsSync9,
9939
10272
  mkdirSync as mkdirSync6,
9940
- readFileSync as readFileSync12,
10273
+ readFileSync as readFileSync14,
9941
10274
  readdirSync,
9942
10275
  rmSync,
9943
10276
  writeFileSync as writeFileSync5
@@ -10151,7 +10484,7 @@ function readToolsState(devRootDir, serverId) {
10151
10484
  if (!existsSync9(file))
10152
10485
  return null;
10153
10486
  try {
10154
- const parsed = JSON.parse(readFileSync12(file, "utf-8"));
10487
+ const parsed = JSON.parse(readFileSync14(file, "utf-8"));
10155
10488
  if (!parsed || typeof parsed !== "object")
10156
10489
  return null;
10157
10490
  const s = parsed;
@@ -10231,7 +10564,7 @@ function readLocalTool(devRootDir, serverId, toolName) {
10231
10564
  const stateEntry = state?.tools[toolName];
10232
10565
  if (!stateEntry)
10233
10566
  return null;
10234
- const view = parseToolMarkdown(readFileSync12(filePath, "utf-8"));
10567
+ const view = parseToolMarkdown(readFileSync14(filePath, "utf-8"));
10235
10568
  return {
10236
10569
  view,
10237
10570
  cloudId: stateEntry.id,
@@ -10436,14 +10769,14 @@ async function runToolsDelete(toolName, opts) {
10436
10769
  }
10437
10770
 
10438
10771
  // src/commands/tools-diff.ts
10439
- import { existsSync as existsSync12, readFileSync as readFileSync14 } from "node:fs";
10772
+ import { existsSync as existsSync12, readFileSync as readFileSync16 } from "node:fs";
10440
10773
  import { relative as relative2 } from "node:path";
10441
10774
 
10442
10775
  // src/lib/dev/handlers-sync.ts
10443
10776
  import {
10444
10777
  existsSync as existsSync11,
10445
10778
  mkdirSync as mkdirSync7,
10446
- readFileSync as readFileSync13,
10779
+ readFileSync as readFileSync15,
10447
10780
  statSync,
10448
10781
  writeFileSync as writeFileSync6
10449
10782
  } from "node:fs";
@@ -10490,7 +10823,7 @@ async function pushLocalHandlerEdit(args) {
10490
10823
  if (!resolved) {
10491
10824
  return { kind: "unknown-tool", slug: args.slug };
10492
10825
  }
10493
- const source = readFileSync13(filePath, "utf-8");
10826
+ const source = readFileSync15(filePath, "utf-8");
10494
10827
  const hash = hashSource(source);
10495
10828
  if (lastPushedHash.get(filePath) === hash) {
10496
10829
  return { kind: "noop" };
@@ -10649,7 +10982,7 @@ function inspectLocalHandler(args) {
10649
10982
  if (!existsSync11(filePath))
10650
10983
  return { kind: "no-local-file" };
10651
10984
  const cached = lastPushedHash.get(filePath);
10652
- const onDisk = hashSource(readFileSync13(filePath, "utf-8"));
10985
+ const onDisk = hashSource(readFileSync15(filePath, "utf-8"));
10653
10986
  if (cached !== undefined) {
10654
10987
  return cached === onDisk ? { kind: "matches-cache" } : { kind: "differs-from-cache" };
10655
10988
  }
@@ -10770,7 +11103,7 @@ function buildMetadataDiff(args) {
10770
11103
  let local = null;
10771
11104
  let parseError = null;
10772
11105
  try {
10773
- local = parseToolMarkdown(readFileSync14(filePath, "utf-8"));
11106
+ local = parseToolMarkdown(readFileSync16(filePath, "utf-8"));
10774
11107
  } catch (err) {
10775
11108
  parseError = err instanceof Error ? err.message : String(err);
10776
11109
  }
@@ -10813,7 +11146,7 @@ async function buildHandlerDiff(args) {
10813
11146
  const slug = handlerSlugFromToolName(args.toolName);
10814
11147
  const filePath = `${serverBundleHandlersDir(args.devRootDir)}/${slug}.ts`;
10815
11148
  const hasLocal = existsSync12(filePath);
10816
- const localContent = hasLocal ? readFileSync14(filePath, "utf-8") : null;
11149
+ const localContent = hasLocal ? readFileSync16(filePath, "utf-8") : null;
10817
11150
  const localPath = hasLocal ? relative2(args.cwd, filePath) || filePath : null;
10818
11151
  const changed = hasLocal ? localContent !== cloudResult.snapshot.content : true;
10819
11152
  return {
@@ -10928,7 +11261,7 @@ import { join as join11, relative as relative3 } from "node:path";
10928
11261
 
10929
11262
  // src/lib/editor-shell.ts
10930
11263
  import { spawn as spawn2 } from "node:child_process";
10931
- import { existsSync as existsSync13, readFileSync as readFileSync15 } from "node:fs";
11264
+ import { existsSync as existsSync13, readFileSync as readFileSync17 } from "node:fs";
10932
11265
  import { platform as platform2 } from "node:os";
10933
11266
  import { delimiter, join as join10 } from "node:path";
10934
11267
  var DEFAULT_FALLBACKS = ["vi"];
@@ -11014,7 +11347,7 @@ async function runEditor(args) {
11014
11347
  message: `${command} exited with code ${exitCode}. The file may be unsaved; re-run after fixing.`
11015
11348
  };
11016
11349
  }
11017
- const contents = readFileSync15(args.filePath, "utf-8");
11350
+ const contents = readFileSync17(args.filePath, "utf-8");
11018
11351
  return { ok: true, contents, command };
11019
11352
  }
11020
11353
 
@@ -11818,7 +12151,7 @@ import { existsSync as existsSync16 } from "node:fs";
11818
12151
  import { relative as relative6 } from "node:path";
11819
12152
 
11820
12153
  // src/commands/tools-rename-input.ts
11821
- import { existsSync as existsSync15, readFileSync as readFileSync16 } from "node:fs";
12154
+ import { existsSync as existsSync15, readFileSync as readFileSync18 } from "node:fs";
11822
12155
  import { isAbsolute as isAbsolute3, resolve as resolve7 } from "node:path";
11823
12156
  var MODES_HINT = "Pass exactly one of: <old> <new>, --from-operation-ids, or --map @renames.json.";
11824
12157
  function resolveRenameSelection(args) {
@@ -11853,7 +12186,7 @@ function readRenameMap(ref, cwd) {
11853
12186
  throw new CliExitError(1, `--map file not found: ${path}`);
11854
12187
  let parsed;
11855
12188
  try {
11856
- parsed = JSON.parse(readFileSync16(path, "utf-8"));
12189
+ parsed = JSON.parse(readFileSync18(path, "utf-8"));
11857
12190
  } catch (err) {
11858
12191
  throw new CliExitError(1, `--map file is not valid JSON (${path}): ${err instanceof Error ? err.message : String(err)}`);
11859
12192
  }
@@ -12531,13 +12864,13 @@ function isAgentKey(value) {
12531
12864
  }
12532
12865
 
12533
12866
  // src/commands/skills-lifecycle.ts
12534
- import { readFileSync as readFileSync17 } from "node:fs";
12867
+ import { readFileSync as readFileSync19 } from "node:fs";
12535
12868
  function readValueOption2(raw) {
12536
12869
  if (raw === "@-") {
12537
- return readFileSync17(0, "utf8");
12870
+ return readFileSync19(0, "utf8");
12538
12871
  }
12539
12872
  if (raw.startsWith("@")) {
12540
- return readFileSync17(raw.slice(1), "utf8");
12873
+ return readFileSync19(raw.slice(1), "utf8");
12541
12874
  }
12542
12875
  return raw;
12543
12876
  }
@@ -12887,7 +13220,7 @@ function registerSkillMutationCommands(skills) {
12887
13220
  }
12888
13221
 
12889
13222
  // src/commands/skills-test-authoring.ts
12890
- import { readFileSync as readFileSync18 } from "node:fs";
13223
+ import { readFileSync as readFileSync20 } from "node:fs";
12891
13224
  var FIXTURE_MODES = [
12892
13225
  "baseline",
12893
13226
  "validationFailure",
@@ -12896,9 +13229,9 @@ var FIXTURE_MODES = [
12896
13229
  ];
12897
13230
  function readValueOption3(raw) {
12898
13231
  if (raw === "@-")
12899
- return readFileSync18(0, "utf8");
13232
+ return readFileSync20(0, "utf8");
12900
13233
  if (raw.startsWith("@"))
12901
- return readFileSync18(raw.slice(1), "utf8");
13234
+ return readFileSync20(raw.slice(1), "utf8");
12902
13235
  return raw;
12903
13236
  }
12904
13237
  function parseAssertionsOption(raw) {
@@ -13951,14 +14284,14 @@ function registerSkillCommands(program) {
13951
14284
  }
13952
14285
 
13953
14286
  // src/lib/dev/prepare.ts
13954
- import { existsSync as existsSync24, readFileSync as readFileSync22, writeFileSync as writeFileSync12 } from "node:fs";
14287
+ import { existsSync as existsSync24, readFileSync as readFileSync24, writeFileSync as writeFileSync12 } from "node:fs";
13955
14288
  import { join as join19, relative as relative8, resolve as resolve9 } from "node:path";
13956
14289
 
13957
14290
  // src/lib/dev/bundle-sync.ts
13958
14291
  import {
13959
14292
  existsSync as existsSync18,
13960
14293
  mkdirSync as mkdirSync9,
13961
- readFileSync as readFileSync19,
14294
+ readFileSync as readFileSync21,
13962
14295
  rmSync as rmSync2,
13963
14296
  writeFileSync as writeFileSync8
13964
14297
  } from "node:fs";
@@ -14095,7 +14428,7 @@ import { spawnSync } from "node:child_process";
14095
14428
  import {
14096
14429
  existsSync as existsSync19,
14097
14430
  mkdirSync as mkdirSync10,
14098
- readFileSync as readFileSync20,
14431
+ readFileSync as readFileSync22,
14099
14432
  writeFileSync as writeFileSync9
14100
14433
  } from "node:fs";
14101
14434
  import { dirname as dirname8, join as join14 } from "node:path";
@@ -14190,7 +14523,7 @@ function isGitAvailable() {
14190
14523
  }
14191
14524
  function applyLocalExcludes(repoDir) {
14192
14525
  const excludePath = join14(repoDir, ".git", "info", "exclude");
14193
- const existing = existsSync19(excludePath) ? readFileSync20(excludePath, "utf-8") : "";
14526
+ const existing = existsSync19(excludePath) ? readFileSync22(excludePath, "utf-8") : "";
14194
14527
  const next = mergeLocalExcludes(existing);
14195
14528
  if (next === null)
14196
14529
  return;
@@ -29576,7 +29909,7 @@ import {
29576
29909
  appendFileSync as appendFileSync2,
29577
29910
  existsSync as existsSync22,
29578
29911
  mkdirSync as mkdirSync13,
29579
- readFileSync as readFileSync21,
29912
+ readFileSync as readFileSync23,
29580
29913
  readdirSync as readdirSync2,
29581
29914
  renameSync,
29582
29915
  statSync as statSync2
@@ -29628,7 +29961,7 @@ function readAll(dir) {
29628
29961
  for (const path of [...listRotatedFiles(dir), activeFilePath(dir)]) {
29629
29962
  if (!existsSync22(path))
29630
29963
  continue;
29631
- out.push(...parseLines(readFileSync21(path, "utf-8")));
29964
+ out.push(...parseLines(readFileSync23(path, "utf-8")));
29632
29965
  }
29633
29966
  return out;
29634
29967
  }
@@ -29780,9 +30113,9 @@ function printEnvBindingHint(dest, envPath, cwd) {
29780
30113
  let example = "";
29781
30114
  let envJson = "{}";
29782
30115
  try {
29783
- example = readFileSync22(examplePath, "utf-8");
30116
+ example = readFileSync24(examplePath, "utf-8");
29784
30117
  if (existsSync24(envPath))
29785
- envJson = readFileSync22(envPath, "utf-8");
30118
+ envJson = readFileSync24(envPath, "utf-8");
29786
30119
  } catch {
29787
30120
  return;
29788
30121
  }
@@ -29955,7 +30288,7 @@ async function prepareDev(opts) {
29955
30288
 
29956
30289
  // src/lib/dev/tree-sync.ts
29957
30290
  import { createHash as createHash4 } from "node:crypto";
29958
- import { existsSync as existsSync25, mkdirSync as mkdirSync14, readdirSync as readdirSync3, readFileSync as readFileSync23, writeFileSync as writeFileSync13 } from "node:fs";
30291
+ import { existsSync as existsSync25, mkdirSync as mkdirSync14, readdirSync as readdirSync3, readFileSync as readFileSync25, writeFileSync as writeFileSync13 } from "node:fs";
29959
30292
  import { join as join20 } from "node:path";
29960
30293
  var AUTHORING_GUIDE_FILE = "AGENTS.md";
29961
30294
  var STRUCTURAL_FRONTMATTER_KEYS = [
@@ -29977,7 +30310,7 @@ function listToolFiles(devRootDir, serverId) {
29977
30310
  }
29978
30311
  function readToolFile(devRootDir, serverId, toolName) {
29979
30312
  const file = join20(serverToolsDir(devRootDir, serverId), `${toolName}.md`);
29980
- const raw = readFileSync23(file, "utf-8");
30313
+ const raw = readFileSync25(file, "utf-8");
29981
30314
  try {
29982
30315
  return { raw, view: parseToolMarkdown(raw) };
29983
30316
  } catch (err) {
@@ -30105,7 +30438,7 @@ function listHandlerFiles(devRootDir) {
30105
30438
  return [];
30106
30439
  return readdirSync3(dir).filter((entry) => entry.endsWith(".ts") && !entry.endsWith(".definition.ts")).sort().map((entry) => ({
30107
30440
  slug: entry.slice(0, -3),
30108
- source: readFileSync23(join20(dir, entry), "utf-8")
30441
+ source: readFileSync25(join20(dir, entry), "utf-8")
30109
30442
  }));
30110
30443
  }
30111
30444
  function writeHandlerManifest(args) {
@@ -30133,7 +30466,7 @@ function recordHandlerPushed(args) {
30133
30466
  serverId: args.serverId,
30134
30467
  version: 1
30135
30468
  };
30136
- manifest.handlers[args.slug] = hashSource2(readFileSync23(file, "utf-8"));
30469
+ manifest.handlers[args.slug] = hashSource2(readFileSync25(file, "utf-8"));
30137
30470
  writeFileSync13(handlerManifestPath(args.devRootDir, args.serverId), JSON.stringify(manifest, null, 2), "utf-8");
30138
30471
  }
30139
30472
  function readHandlerManifest(devRootDir, serverId) {
@@ -30141,7 +30474,7 @@ function readHandlerManifest(devRootDir, serverId) {
30141
30474
  if (!existsSync25(file))
30142
30475
  return null;
30143
30476
  try {
30144
- const parsed = JSON.parse(readFileSync23(file, "utf-8"));
30477
+ const parsed = JSON.parse(readFileSync25(file, "utf-8"));
30145
30478
  return parsed.version === 1 && parsed.handlers ? parsed : null;
30146
30479
  } catch {
30147
30480
  return null;
@@ -31165,7 +31498,7 @@ import { isAbsolute as isAbsolute5, relative as relative9, resolve as resolve11
31165
31498
  import {
31166
31499
  existsSync as existsSync26,
31167
31500
  mkdirSync as mkdirSync15,
31168
- readFileSync as readFileSync24,
31501
+ readFileSync as readFileSync26,
31169
31502
  readdirSync as readdirSync4,
31170
31503
  unlinkSync,
31171
31504
  writeFileSync as writeFileSync14
@@ -31209,7 +31542,7 @@ function listSessions() {
31209
31542
  continue;
31210
31543
  const path = join22(dir, entry);
31211
31544
  try {
31212
- const parsed = JSON.parse(readFileSync24(path, "utf-8"));
31545
+ const parsed = JSON.parse(readFileSync26(path, "utf-8"));
31213
31546
  if (typeof parsed.pid !== "number") {
31214
31547
  unlinkSync(path);
31215
31548
  continue;
@@ -32581,19 +32914,19 @@ import { homedir as homedir4 } from "node:os";
32581
32914
  import { join as join27 } from "node:path";
32582
32915
 
32583
32916
  // src/lib/dev/agent-connectors/json-config-utils.ts
32584
- import { existsSync as existsSync32, mkdirSync as mkdirSync16, readFileSync as readFileSync25, writeFileSync as writeFileSync15 } from "node:fs";
32917
+ import { existsSync as existsSync32, mkdirSync as mkdirSync16, readFileSync as readFileSync27, writeFileSync as writeFileSync15 } from "node:fs";
32585
32918
  import { dirname as dirname10, join as join26, basename } from "node:path";
32586
32919
  function readJsonFile(path) {
32587
32920
  if (!existsSync32(path))
32588
32921
  return { ok: true, value: {} };
32589
32922
  try {
32590
- const raw = readFileSync25(path, "utf-8");
32923
+ const raw = readFileSync27(path, "utf-8");
32591
32924
  if (!raw.trim())
32592
32925
  return { ok: true, value: {} };
32593
32926
  return { ok: true, value: JSON.parse(raw) };
32594
32927
  } catch {
32595
32928
  try {
32596
- return { ok: false, raw: readFileSync25(path, "utf-8") };
32929
+ return { ok: false, raw: readFileSync27(path, "utf-8") };
32597
32930
  } catch {
32598
32931
  return { ok: false };
32599
32932
  }
@@ -32615,7 +32948,7 @@ function backupConfig(args) {
32615
32948
  const filename = `${args.agentId}__${basename(args.configPath)}.bak`;
32616
32949
  const backupPath = join26(args.backupsDir, filename);
32617
32950
  if (!existsSync32(backupPath)) {
32618
- const raw = readFileSync25(args.configPath, "utf-8");
32951
+ const raw = readFileSync27(args.configPath, "utf-8");
32619
32952
  writeFileSync15(backupPath, raw, "utf-8");
32620
32953
  }
32621
32954
  return { backupPath, existed: true };
@@ -32626,7 +32959,7 @@ function restoreFromBackup(args) {
32626
32959
  if (!existsSync32(backupPath)) {
32627
32960
  return { restored: false };
32628
32961
  }
32629
- const raw = readFileSync25(backupPath, "utf-8");
32962
+ const raw = readFileSync27(backupPath, "utf-8");
32630
32963
  mkdirSync16(dirname10(args.configPath), { recursive: true });
32631
32964
  writeFileSync15(args.configPath, raw, "utf-8");
32632
32965
  return { restored: true };
@@ -32715,7 +33048,7 @@ var claudeCodeConnector = {
32715
33048
  import {
32716
33049
  existsSync as existsSync34,
32717
33050
  mkdirSync as mkdirSync17,
32718
- readFileSync as readFileSync26,
33051
+ readFileSync as readFileSync28,
32719
33052
  unlinkSync as unlinkSync3,
32720
33053
  writeFileSync as writeFileSync17
32721
33054
  } from "node:fs";
@@ -32774,7 +33107,7 @@ var codexConnector = {
32774
33107
  });
32775
33108
  let existing = "";
32776
33109
  if (existsSync34(path)) {
32777
- existing = readFileSync26(path, "utf-8");
33110
+ existing = readFileSync28(path, "utf-8");
32778
33111
  }
32779
33112
  const conflict = findSectionRange(existing, args.name) !== null;
32780
33113
  let next = existing;
@@ -32796,7 +33129,7 @@ var codexConnector = {
32796
33129
  async remove(args) {
32797
33130
  const path = configPath2();
32798
33131
  if (existsSync34(path)) {
32799
- const existing = readFileSync26(path, "utf-8");
33132
+ const existing = readFileSync28(path, "utf-8");
32800
33133
  const range = findSectionRange(existing, args.name);
32801
33134
  if (range) {
32802
33135
  const next = existing.slice(0, range.start) + existing.slice(range.end);
@@ -32809,7 +33142,7 @@ var codexConnector = {
32809
33142
  agentId: this.id
32810
33143
  });
32811
33144
  if (!restored.restored && existsSync34(path)) {
32812
- const after = readFileSync26(path, "utf-8");
33145
+ const after = readFileSync28(path, "utf-8");
32813
33146
  if (!after.trim()) {
32814
33147
  try {
32815
33148
  unlinkSync3(path);
@@ -34292,12 +34625,12 @@ async function confirmRollback(deploymentId) {
34292
34625
  }
34293
34626
 
34294
34627
  // src/commands/doctor.ts
34295
- import { existsSync as existsSync40, statSync as statSync6, readFileSync as readFileSync28 } from "node:fs";
34628
+ import { existsSync as existsSync40, statSync as statSync6, readFileSync as readFileSync30 } from "node:fs";
34296
34629
  import { homedir as homedir9, platform as platform6 } from "node:os";
34297
34630
  import { join as join33, delimiter as delimiter3 } from "node:path";
34298
34631
 
34299
34632
  // src/lib/version-check.ts
34300
- import { existsSync as existsSync39, mkdirSync as mkdirSync18, readFileSync as readFileSync27, writeFileSync as writeFileSync21 } from "node:fs";
34633
+ import { existsSync as existsSync39, mkdirSync as mkdirSync18, readFileSync as readFileSync29, writeFileSync as writeFileSync21 } from "node:fs";
34301
34634
  import { join as join32 } from "node:path";
34302
34635
  var CACHE_TTL_MS = 24 * 60 * 60 * 1000;
34303
34636
  var FETCH_TIMEOUT_MS2 = 2000;
@@ -34312,7 +34645,7 @@ function readCache() {
34312
34645
  if (!existsSync39(cacheFile()))
34313
34646
  return null;
34314
34647
  try {
34315
- const raw = JSON.parse(readFileSync27(cacheFile(), "utf-8"));
34648
+ const raw = JSON.parse(readFileSync29(cacheFile(), "utf-8"));
34316
34649
  if (typeof raw.latest !== "string" || typeof raw.fetchedAt !== "number")
34317
34650
  return null;
34318
34651
  return { latest: raw.latest, fetchedAt: raw.fetchedAt };
@@ -34458,7 +34791,7 @@ function checkConfigFile() {
34458
34791
  } catch {}
34459
34792
  }
34460
34793
  try {
34461
- JSON.parse(readFileSync28(path, "utf-8"));
34794
+ JSON.parse(readFileSync30(path, "utf-8"));
34462
34795
  } catch (err) {
34463
34796
  return {
34464
34797
  name: "Config file",
@@ -35341,7 +35674,7 @@ function registerInstallationCommands(program) {
35341
35674
  }
35342
35675
 
35343
35676
  // src/commands/invoke.ts
35344
- import { existsSync as existsSync41, readFileSync as readFileSync29 } from "node:fs";
35677
+ import { existsSync as existsSync41, readFileSync as readFileSync31 } from "node:fs";
35345
35678
  import { join as join34 } from "node:path";
35346
35679
 
35347
35680
  // src/lib/dev/local-invoke.ts
@@ -35434,9 +35767,9 @@ function selectCodeModeInvocation(args) {
35434
35767
  }
35435
35768
  function readCodeOption(raw) {
35436
35769
  if (raw === "@-")
35437
- return readFileSync29(0, "utf-8");
35770
+ return readFileSync31(0, "utf-8");
35438
35771
  if (raw.startsWith("@"))
35439
- return readFileSync29(raw.slice(1), "utf-8");
35772
+ return readFileSync31(raw.slice(1), "utf-8");
35440
35773
  return raw;
35441
35774
  }
35442
35775
  function parseTimeoutMs4(value, fallbackMs) {
@@ -35718,7 +36051,7 @@ async function runLocalInvoke(args) {
35718
36051
  throw new CliExitError(1);
35719
36052
  }
35720
36053
  const env = {
35721
- ...args.envFile ? parseEnvFile(readFileSync29(args.envFile, "utf-8")) : {},
36054
+ ...args.envFile ? parseEnvFile(readFileSync31(args.envFile, "utf-8")) : {},
35722
36055
  ...parseEnvPairs(args.envPairs)
35723
36056
  };
35724
36057
  const port = await findAvailablePort({ range: 40, start: 43117 });
@@ -36710,7 +37043,7 @@ import {
36710
37043
  existsSync as existsSync42,
36711
37044
  mkdirSync as mkdirSync19,
36712
37045
  readdirSync as readdirSync5,
36713
- readFileSync as readFileSync30,
37046
+ readFileSync as readFileSync32,
36714
37047
  rmSync as rmSync3,
36715
37048
  statSync as statSync7
36716
37049
  } from "node:fs";
@@ -36730,7 +37063,7 @@ function readManifestFromPackageDir(packageDir) {
36730
37063
  return null;
36731
37064
  let pkg;
36732
37065
  try {
36733
- pkg = JSON.parse(readFileSync30(pkgPath, "utf-8"));
37066
+ pkg = JSON.parse(readFileSync32(pkgPath, "utf-8"));
36734
37067
  } catch {
36735
37068
  return null;
36736
37069
  }
@@ -38483,7 +38816,7 @@ function displayValueFor(field, value) {
38483
38816
  import {
38484
38817
  existsSync as existsSync43,
38485
38818
  mkdirSync as mkdirSync20,
38486
- readFileSync as readFileSync31,
38819
+ readFileSync as readFileSync33,
38487
38820
  writeFileSync as writeFileSync22
38488
38821
  } from "node:fs";
38489
38822
  import { join as join36 } from "node:path";
@@ -38565,7 +38898,7 @@ function readTuiState() {
38565
38898
  return {};
38566
38899
  let parsed;
38567
38900
  try {
38568
- parsed = JSON.parse(readFileSync31(path, "utf-8"));
38901
+ parsed = JSON.parse(readFileSync33(path, "utf-8"));
38569
38902
  } catch {
38570
38903
  return {};
38571
38904
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mcpcloud/cli",
3
- "version": "0.26.0",
3
+ "version": "0.27.0-next-20261001025900",
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": {