@rosthq/cli 0.5.3 → 0.5.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -42241,7 +42241,7 @@ Agents should explain the job, the required tool category, the minimum permissio
42241
42241
  order: 48,
42242
42242
  title: "CLI and MCP installation guide",
42243
42243
  summary: "Install the public CLI, register remote token-backed MCP clients, and find the full command and tool catalog.",
42244
- version: "2026-06-18.2",
42244
+ version: "2026-06-18.4",
42245
42245
  public: true,
42246
42246
  audiences: ["human", "cli", "mcp", "in_app_agent"],
42247
42247
  stages: ["company_setup", "staffing"],
@@ -42394,7 +42394,7 @@ export {{envPrefix}}_CLI_ALLOW_FILE_TOKEN_STORE=1
42394
42394
  {{cli}} login --device
42395
42395
  \`\`\`
42396
42396
 
42397
- For CI, do not run an interactive device login on every job (a device code still needs a human approver, which CI does not have). Note that the CLI session is a **short-lived** auth session (it carries an expiry), not a durable credential \u2014 so storing the session file as a long-lived CI secret will stop working once it expires, and re-approving a device code unattended is not possible. For ongoing unattended automation, prefer a **seat-scoped MCP token** (mint it once with \`mcp install --scope seat\`; it stays valid until revoked) used directly by your MCP client, and treat that client config as a secret. Revoke and re-mint if a runner image is rebuilt or shared.
42397
+ For CI, do not run an interactive device login on every job (a device code still needs a human approver, which CI does not have). Note that the CLI session is a **short-lived** auth session (it carries an expiry), not a durable credential \u2014 so storing the session file as a long-lived CI secret will stop working once it expires, and re-approving a device code unattended is not possible. For ongoing unattended automation, prefer a **seat-scoped MCP token** used directly by your MCP client, and treat that client config as a secret. Such a token now defaults to a **90-day** expiry, so for automation that must outlive that window, mint it with an explicit \`--expires-in <days>\` (up to 365) or, accepting the long-lived-credential tradeoff, \`--no-expiry\` \u2014 e.g. \`mcp install --scope seat --seat-id <id> --expires-in 365\`. Rotate it before it lapses (\`--rotate <old-token-id>\`), and revoke and re-mint if a runner image is rebuilt or shared.
42398
42398
 
42399
42399
  ## First-run path
42400
42400
 
@@ -42503,7 +42503,22 @@ After registering, confirm the client can actually reach the server with the new
42503
42503
 
42504
42504
  ### Token lifetime and rotation
42505
42505
 
42506
- Tokens minted by \`{{cli}} mcp install\` today carry **no expiry** \u2014 they stay valid until revoked, for both tenant-admin and seat-scoped tokens. (\`mcp install\` does not set an \`expires_at\`; the underlying \`mcp_token.create\` command does accept an optional \`expires_at\`, and default expiry plus one-step rotation are planned \u2014 but do not assume an automatic TTL on a token you minted through \`mcp install\`.) Because such a token is long-lived, rotate it deliberately:
42506
+ Every MCP token now carries an expiry. Tokens minted by \`{{cli}} mcp install\` default to a **90-day** TTL (for both tenant-admin and seat-scoped tokens); after that the token stops authenticating and you re-mint. Override the lifetime at mint time:
42507
+
42508
+ - \`--expires-in <days>\` \u2014 set an explicit TTL, between **1 and 365** days. Example: \`{{cli}} mcp install --client codex --expires-in 30\`.
42509
+ - \`--no-expiry\` \u2014 mint a token with **no practical expiry** (a ~100-year TTL). This is a deliberate loosening for long-lived automation; the CLI prints a warning. Prefer a finite \`--expires-in\` and rotate instead.
42510
+
42511
+ \`mcp install\` echoes the chosen expiry, and \`{{cli}} command mcp_token.list\` reports \`expires_in_days\` for every token so you can see what is about to lapse.
42512
+
42513
+ **One-step rotation.** Replace a live token in a single command \u2014 it mints the replacement, prints the new registration block, then revokes the old token:
42514
+
42515
+ \`\`\`bash
42516
+ {{cli}} mcp install --client <client> --rotate <old-token-id>
42517
+ \`\`\`
42518
+
42519
+ Rotation **preserves the original token's scope and seat**: rotating a seat-scoped token mints a new seat-scoped token for the same seat, and rotating a tenant-admin token mints a tenant-admin token \u2014 you do **not** pass \`--scope\`/\`--seat-id\` (and if you do, they must match the old token, or the command errors without minting). The CLI looks the old token up first, so an unknown \`--rotate\` id fails cleanly with nothing minted. If the mint succeeds but revoking the old token fails (or the id isn't found at revoke time), the CLI says so on stderr and prints the manual \`mcp_token.revoke\` command \u2014 the new token is already live, so revoke the old one by hand. Find the \`<old-token-id>\` with \`{{cli}} command mcp_token.list\`.
42520
+
42521
+ You can also rotate the long way (mint then revoke separately):
42507
42522
 
42508
42523
  1. Re-run \`{{cli}} mcp install --client <client>\` (add \`--scope seat --seat-id <seat-id>\` for a seat token) to mint and register a fresh token.
42509
42524
  2. Revoke the old one (below). Rotate on a periodic cadence, and whenever the scope or seat changes.
@@ -42637,7 +42652,7 @@ These ergonomic wrappers (including the \`{{cli}} agent\` group) require **{{cli
42637
42652
 
42638
42653
  | Command | Purpose | Scope | Safe example |
42639
42654
  |---|---|---|---|
42640
- | \`{{cli}} mcp install --client claude-code|codex|cursor\` | Help syntax for supported MCP clients. | Tenant | \`{{cli}} mcp install --client codex\` |
42655
+ | \`{{cli}} mcp install --client claude-code|codex|cursor [--scope seat --seat-id <id>] [--expires-in <days>|--no-expiry] [--rotate <token-id>]\` | Help syntax for supported MCP clients. \`--expires-in <days>\` (1..365) or \`--no-expiry\` sets the token TTL (default 90 days); \`--rotate <token-id>\` mints a replacement then revokes the old token. | Tenant | \`{{cli}} mcp install --client codex --expires-in 30\` |
42641
42656
  | \`{{cli}} mcp install --client claude-code\` | Mint a tenant-admin MCP token and print a Claude Code registration command. | Tenant | \`{{cli}} mcp install --client claude-code\` |
42642
42657
  | \`{{cli}} mcp install --client codex\` | Mint a tenant-admin MCP token and print Codex TOML. | Tenant | \`{{cli}} mcp install --client codex\` |
42643
42658
  | \`{{cli}} mcp install --client cursor\` | Mint a tenant-admin MCP token and print Cursor JSON. | Tenant | \`{{cli}} mcp install --client cursor\` |
@@ -42822,7 +42837,7 @@ These rows are quick, at-a-glance triage. For deeper auth, tenant, scope, confir
42822
42837
  - Node present but too old (npx launches but the CLI rejects it): run \`node --version\`; if it is below v22, upgrade Node.
42823
42838
  - Stale npx cache: rerun with \`npx {{cliPackage}}@latest --help\` or clear the npm cache.
42824
42839
  - MCP connection not working after registering: call \`rost_list_commands\` with \`{}\` (any token); with a tenant-admin token read \`rost://tenant/status\`, with a seat-scoped token call \`rost_get_context\` with \`{}\` (a seat token cannot read \`rost://tenant/status\`). A 401 / not-authorized shape means the token did not register \u2014 re-run \`mcp install\`.
42825
- - Revoked or invalid MCP token: run \`{{cli}} mcp install --client <client>\` again to mint and register a fresh one. (Tokens minted by \`mcp install\` carry no expiry \u2014 they stay valid until revoked.)
42840
+ - Revoked, **expired**, or invalid MCP token: run \`{{cli}} mcp install --client <client>\` again to mint and register a fresh one (or rotate with \`--rotate <old-token-id>\`). Tokens minted by \`mcp install\` default to a 90-day expiry \u2014 check \`expires_in_days\` in \`{{cli}} command mcp_token.list\`; mint with \`--expires-in <days>\` or \`--no-expiry\` to change it.
42826
42841
  - Confirmation required: a human approves from the \`approveVia\` web link or runs the \`{{cli}} command confirmation.approve --json ...\` command shown in the CLI error output (an agent never approves its own request \u2014 see the confirmations-guide).
42827
42842
  - Command denied by scope or manifest: switch to a tenant-admin token for setup, or ask a human Steward to update the seat Charter and permission manifest.
42828
42843
  - Need command guidance: run \`{{cli}} docs\`, \`{{cli}} reference search "onboarding"\`, or \`{{cli}} reference get agent-reference-map\`.
@@ -43527,7 +43542,7 @@ Templates may draft. Humans approve. A stock agent should not go live until a hu
43527
43542
  order: 77,
43528
43543
  title: "Troubleshooting guide",
43529
43544
  summary: "How users and agents should diagnose common setup, tool, Signal, Friction, and MCP problems.",
43530
- version: "2026-06-18.1",
43545
+ version: "2026-06-18.2",
43531
43546
  public: true,
43532
43547
  audiences: ["human", "cli", "mcp", "in_app_agent"],
43533
43548
  stages: ["company_setup", "staffing", "operating_rhythm"],
@@ -43571,7 +43586,7 @@ Before calling a command that changes state, discover its exact shape so you do
43571
43586
  - "Wrong tenant": \`{{cli}} tenants\` then \`{{cli}} use <tenant-slug-or-id>\`.
43572
43587
  - "Command denied by scope or manifest": a seat token cannot run tenant-admin setup. Switch to a tenant-admin token, or ask a human Steward to update the seat Charter and permission manifest.
43573
43588
  - "Confirmation required": the command is gated. The CLI prints \`{{cli}} command confirmation.approve --json ...\` or a web link. A human approves; an agent does not approve its own request.
43574
- - Revoked or invalid MCP token: run \`{{cli}} mcp install --client <client>\` again to mint and register a fresh one. Tokens minted by \`mcp install\` carry no expiry \u2014 they stay valid until revoked.
43589
+ - Revoked, **expired**, or invalid MCP token: run \`{{cli}} mcp install --client <client>\` again to mint and register a fresh one (or rotate with \`--rotate <old-token-id>\`). Tokens minted by \`mcp install\` default to a 90-day expiry \u2014 check \`expires_in_days\` in \`{{cli}} command mcp_token.list\`.
43575
43590
 
43576
43591
  ## Agent-creation failures
43577
43592
 
@@ -44224,12 +44239,29 @@ function renderReferenceCorpusIndex() {
44224
44239
  }
44225
44240
 
44226
44241
  // src/mcp-install.ts
44242
+ var MCP_TOKEN_DEFAULT_EXPIRY_DAYS = 90;
44243
+ var MCP_TOKEN_MIN_EXPIRY_DAYS = 1;
44244
+ var MCP_TOKEN_MAX_EXPIRY_DAYS = 365;
44245
+ var MCP_TOKEN_NO_EXPIRY_SENTINEL_DAYS = 36525;
44246
+ var mcpTokenScopeSchema = external_exports.enum(["tenant_admin", "seat"]);
44227
44247
  var mcpTokenOutputSchema = external_exports.object({
44228
44248
  token_id: external_exports.string().min(1),
44229
44249
  token: external_exports.string().min(1),
44230
- scope: external_exports.enum(["tenant_admin", "seat"]),
44250
+ scope: mcpTokenScopeSchema,
44231
44251
  seat_id: external_exports.string().min(1).nullable()
44232
44252
  }).strict();
44253
+ var mcpTokenRevokeOutputSchema = external_exports.object({
44254
+ token_id: external_exports.string().min(1),
44255
+ revoked: external_exports.boolean()
44256
+ }).strict();
44257
+ var mcpTokenListEntrySchema = external_exports.object({
44258
+ token_id: external_exports.string().min(1),
44259
+ scope: mcpTokenScopeSchema,
44260
+ seat_id: external_exports.string().min(1).nullable()
44261
+ }).passthrough();
44262
+ var mcpTokenListOutputSchema = external_exports.object({
44263
+ tokens: external_exports.array(mcpTokenListEntrySchema)
44264
+ }).passthrough();
44233
44265
  var pendingConfirmationSchema = external_exports.object({
44234
44266
  confirmationId: external_exports.string().min(1),
44235
44267
  commandId: external_exports.string().min(1)
@@ -44245,6 +44277,9 @@ function parseMcpInstallOptions(args) {
44245
44277
  let scope = "tenant_admin";
44246
44278
  let seatId;
44247
44279
  let json2 = false;
44280
+ let expiresInDays;
44281
+ let noExpiry = false;
44282
+ let rotateTokenId;
44248
44283
  for (let index = 0; index < args.length; index += 1) {
44249
44284
  const arg = args[index];
44250
44285
  if (arg === "--client") {
@@ -44257,6 +44292,14 @@ function parseMcpInstallOptions(args) {
44257
44292
  seatId = requireValue(args, index, "--seat-id");
44258
44293
  scope = "seat";
44259
44294
  index += 1;
44295
+ } else if (arg === "--expires-in") {
44296
+ expiresInDays = parseExpiresIn(requireValue(args, index, "--expires-in"));
44297
+ index += 1;
44298
+ } else if (arg === "--no-expiry") {
44299
+ noExpiry = true;
44300
+ } else if (arg === "--rotate") {
44301
+ rotateTokenId = requireValue(args, index, "--rotate");
44302
+ index += 1;
44260
44303
  } else if (arg === "--json") {
44261
44304
  json2 = true;
44262
44305
  } else {
@@ -44269,22 +44312,53 @@ function parseMcpInstallOptions(args) {
44269
44312
  if (scope === "seat" && !seatId) {
44270
44313
  throw new Error("Seat-scoped MCP install requires --seat-id.");
44271
44314
  }
44315
+ if (noExpiry && expiresInDays !== void 0) {
44316
+ throw new Error("Use either --expires-in or --no-expiry, not both.");
44317
+ }
44318
+ const expiry = noExpiry ? { kind: "none" } : expiresInDays === void 0 ? { kind: "default" } : { kind: "days", days: expiresInDays };
44272
44319
  return {
44273
44320
  client,
44274
44321
  scope,
44275
44322
  ...seatId === void 0 ? {} : { seatId },
44276
- json: json2
44323
+ json: json2,
44324
+ expiry,
44325
+ ...rotateTokenId === void 0 ? {} : { rotateTokenId }
44277
44326
  };
44278
44327
  }
44279
44328
  var MCP_TOKEN_CAUTION = "Security: this minted MCP token is a live credential that lands in the client's plaintext config \u2014 prefer the narrowest scope (--scope seat --seat-id <id>) and revoke it immediately if it leaks.";
44329
+ var NO_EXPIRY_WARNING = "Warning: --no-expiry mints a token with no practical expiry (a ~100-year TTL). This is a long-lived credential by choice \u2014 rotate it deliberately and revoke immediately if it leaks.";
44280
44330
  async function renderMcpInstall(input) {
44281
- const token = await createMcpToken(input.client, {
44282
- scope: input.options.scope,
44283
- ...input.options.seatId === void 0 ? {} : { seat_id: input.options.seatId }
44284
- });
44331
+ const stderrLines = [];
44332
+ const mintScope = input.options.rotateTokenId === void 0 ? { scope: input.options.scope, ...input.options.seatId === void 0 ? {} : { seatId: input.options.seatId } } : await resolveRotationScope(input.client, input.options);
44333
+ const token = await createMcpToken(input.client, mcpTokenCreateBody(mintScope, input.options.expiry));
44334
+ let rotationOldRevoked = null;
44335
+ if (input.options.rotateTokenId !== void 0) {
44336
+ const oldTokenId = input.options.rotateTokenId;
44337
+ try {
44338
+ const output = await executeWithConfirmation(input.client, "mcp_token.revoke", { token_id: oldTokenId });
44339
+ const revoke = mcpTokenRevokeOutputSchema.safeParse(output);
44340
+ rotationOldRevoked = revoke.success && revoke.data.revoked === true;
44341
+ if (!rotationOldRevoked) {
44342
+ const reason = revoke.success ? "the token id was not found for your tenant (it was not revoked \u2014 check the id)" : "the revoke response could not be parsed";
44343
+ stderrLines.push(
44344
+ `Warning: the new token (${token.token_id}) was minted, but the old token (${oldTokenId}) was NOT revoked \u2014 ${reason}. It is still live. Revoke it by hand: ${cliBrand.binName} command mcp_token.revoke --json '{"token_id":"${oldTokenId}"}'`
44345
+ );
44346
+ }
44347
+ } catch (error51) {
44348
+ rotationOldRevoked = false;
44349
+ stderrLines.push(
44350
+ `Warning: the new token (${token.token_id}) was minted, but revoking the old token (${oldTokenId}) failed: ${revokeFailureReason(error51)}. It is still live. Revoke it by hand: ${cliBrand.binName} command mcp_token.revoke --json '{"token_id":"${oldTokenId}"}'`
44351
+ );
44352
+ }
44353
+ }
44285
44354
  const mcpUrl = `${input.appUrl.replace(/\/+$/, "")}/mcp`;
44286
44355
  const registration = registrationFor(input.options.client, mcpUrl, token.token);
44356
+ const expiryLine = expiryEcho(input.options.expiry);
44357
+ if (input.options.expiry.kind === "none") {
44358
+ stderrLines.push(NO_EXPIRY_WARNING);
44359
+ }
44287
44360
  if (input.options.json) {
44361
+ stderrLines.unshift(MCP_TOKEN_CAUTION);
44288
44362
  return {
44289
44363
  stdout: `${JSON.stringify({
44290
44364
  client: input.options.client,
@@ -44292,32 +44366,119 @@ async function renderMcpInstall(input) {
44292
44366
  scope: token.scope,
44293
44367
  seat_id: token.seat_id,
44294
44368
  mcp_url: mcpUrl,
44369
+ expiry: expiryDescriptor(input.options.expiry),
44370
+ ...input.options.rotateTokenId === void 0 ? {} : { rotated_from: input.options.rotateTokenId, rotated_old_revoked: rotationOldRevoked },
44295
44371
  registration
44296
44372
  }, null, 2)}
44297
44373
  `,
44298
- stderr: `${MCP_TOKEN_CAUTION}
44299
- `
44374
+ stderr: stderrLines.length > 0 ? `${stderrLines.join("\n")}
44375
+ ` : ""
44300
44376
  };
44301
44377
  }
44378
+ const stdoutLines = [
44379
+ input.options.rotateTokenId === void 0 ? "MCP token minted. It is shown once in the registration block below." : "MCP token rotated. The replacement is shown once in the registration block below.",
44380
+ expiryLine
44381
+ ];
44382
+ if (input.options.rotateTokenId !== void 0 && rotationOldRevoked === true) {
44383
+ stdoutLines.push(`Old token revoked: ${input.options.rotateTokenId}`);
44384
+ }
44385
+ stdoutLines.push(
44386
+ "",
44387
+ registration,
44388
+ "",
44389
+ MCP_TOKEN_CAUTION,
44390
+ `Revoke later with: ${cliBrand.binName} command mcp_token.revoke --json '{"token_id":"${token.token_id}"}'`,
44391
+ ""
44392
+ );
44302
44393
  return {
44303
- stdout: [
44304
- "MCP token minted. It is shown once in the registration block below.",
44305
- "",
44306
- registration,
44307
- "",
44308
- MCP_TOKEN_CAUTION,
44309
- `Revoke later with: ${cliBrand.binName} command mcp_token.revoke --json '{"token_id":"${token.token_id}"}'`,
44310
- ""
44311
- ].join("\n"),
44312
- stderr: ""
44394
+ stdout: stdoutLines.join("\n"),
44395
+ stderr: stderrLines.length > 0 ? `${stderrLines.join("\n")}
44396
+ ` : ""
44397
+ };
44398
+ }
44399
+ function mcpTokenCreateBody(mint, expiry) {
44400
+ const base = {
44401
+ scope: mint.scope,
44402
+ ...mint.seatId === void 0 ? {} : { seat_id: mint.seatId }
44313
44403
  };
44404
+ if (expiry.kind === "days") {
44405
+ return { ...base, expires_in_days: expiry.days };
44406
+ }
44407
+ if (expiry.kind === "none") {
44408
+ const sentinel = new Date(Date.now() + MCP_TOKEN_NO_EXPIRY_SENTINEL_DAYS * 24 * 60 * 60 * 1e3);
44409
+ return { ...base, expires_at: sentinel.toISOString() };
44410
+ }
44411
+ return base;
44412
+ }
44413
+ async function resolveRotationScope(client, options) {
44414
+ const oldTokenId = options.rotateTokenId;
44415
+ const listOutput = await executeWithConfirmation(client, "mcp_token.list", { include_revoked: true });
44416
+ const parsed = mcpTokenListOutputSchema.safeParse(listOutput);
44417
+ if (!parsed.success) {
44418
+ throw new Error("Could not read the token list to rotate \u2014 mcp_token.list returned an unexpected shape.");
44419
+ }
44420
+ const old = parsed.data.tokens.find((entry) => entry.token_id === oldTokenId);
44421
+ if (!old) {
44422
+ throw new Error(
44423
+ `--rotate token id "${oldTokenId}" was not found in this tenant's tokens (nothing was minted). List ids with: ${cliBrand.binName} command mcp_token.list --json '{"include_revoked":true}'`
44424
+ );
44425
+ }
44426
+ const userSpecifiedScope = options.seatId !== void 0 || options.scope === "seat";
44427
+ if (userSpecifiedScope && options.scope !== old.scope) {
44428
+ throw new Error(
44429
+ `--rotate preserves the old token's scope (${old.scope}); the requested scope (${options.scope}) does not match. Omit --scope/--seat-id to rotate in place, or revoke and mint a new token with the different scope.`
44430
+ );
44431
+ }
44432
+ if (old.scope === "seat" && options.seatId !== void 0 && options.seatId !== old.seat_id) {
44433
+ throw new Error(
44434
+ `--rotate preserves the old token's seat (${old.seat_id ?? "unknown"}); the requested --seat-id (${options.seatId}) does not match.`
44435
+ );
44436
+ }
44437
+ if (old.scope === "seat") {
44438
+ if (old.seat_id === null) {
44439
+ throw new Error(`--rotate token "${oldTokenId}" is seat-scoped but has no seat id; cannot rotate it in place.`);
44440
+ }
44441
+ return { scope: "seat", seatId: old.seat_id };
44442
+ }
44443
+ return { scope: "tenant_admin" };
44444
+ }
44445
+ function expiryEcho(expiry) {
44446
+ if (expiry.kind === "days") {
44447
+ return `Expiry: ${expiry.days} day${expiry.days === 1 ? "" : "s"} from now.`;
44448
+ }
44449
+ if (expiry.kind === "none") {
44450
+ return "Expiry: none (a ~100-year TTL \u2014 see the warning on stderr).";
44451
+ }
44452
+ return `Expiry: ${MCP_TOKEN_DEFAULT_EXPIRY_DAYS} days from now (default).`;
44453
+ }
44454
+ function expiryDescriptor(expiry) {
44455
+ if (expiry.kind === "days") {
44456
+ return { mode: "days", days: expiry.days };
44457
+ }
44458
+ if (expiry.kind === "none") {
44459
+ return { mode: "none", days: null };
44460
+ }
44461
+ return { mode: "default", days: MCP_TOKEN_DEFAULT_EXPIRY_DAYS };
44462
+ }
44463
+ function revokeFailureReason(error51) {
44464
+ if (error51 instanceof CommandClientError) {
44465
+ return error51.message;
44466
+ }
44467
+ if (error51 instanceof Error) {
44468
+ return error51.message;
44469
+ }
44470
+ return "unknown error";
44314
44471
  }
44315
44472
  async function createMcpToken(client, body) {
44473
+ const output = await executeWithConfirmation(client, "mcp_token.create", body);
44474
+ return mcpTokenOutputSchema.parse(output);
44475
+ }
44476
+ async function executeWithConfirmation(client, commandId, body) {
44316
44477
  try {
44317
- const result = await client.execute("mcp_token.create", body);
44318
- return mcpTokenOutputSchema.parse(result.output);
44478
+ const result = await client.execute(commandId, body);
44479
+ return result.output;
44319
44480
  } catch (error51) {
44320
- const pending = pendingMcpTokenConfirmation(error51);
44481
+ const pending = pendingCommandConfirmation(error51, commandId);
44321
44482
  if (!pending) {
44322
44483
  throw error51;
44323
44484
  }
@@ -44325,10 +44486,10 @@ async function createMcpToken(client, body) {
44325
44486
  confirmation_id: pending.confirmationId
44326
44487
  });
44327
44488
  const approvalOutput = confirmationApprovalOutputSchema.parse(approved.output);
44328
- return mcpTokenOutputSchema.parse(approvalOutput.output);
44489
+ return approvalOutput.output;
44329
44490
  }
44330
44491
  }
44331
- function pendingMcpTokenConfirmation(error51) {
44492
+ function pendingCommandConfirmation(error51, commandId) {
44332
44493
  if (!(error51 instanceof CommandClientError) || error51.status !== 409 || !error51.response || error51.response.ok) {
44333
44494
  return null;
44334
44495
  }
@@ -44336,7 +44497,7 @@ function pendingMcpTokenConfirmation(error51) {
44336
44497
  return null;
44337
44498
  }
44338
44499
  const pending = pendingConfirmationSchema.safeParse(error51.response.error.details);
44339
- if (!pending.success || pending.data.commandId !== "mcp_token.create") {
44500
+ if (!pending.success || pending.data.commandId !== commandId) {
44340
44501
  return null;
44341
44502
  }
44342
44503
  return pending.data;
@@ -44379,6 +44540,16 @@ function parseScope(value) {
44379
44540
  }
44380
44541
  throw new Error("MCP token scope must be tenant-admin or seat.");
44381
44542
  }
44543
+ function parseExpiresIn(value) {
44544
+ if (!/^\d+$/.test(value)) {
44545
+ throw new Error(`--expires-in must be a whole number of days (${MCP_TOKEN_MIN_EXPIRY_DAYS}..${MCP_TOKEN_MAX_EXPIRY_DAYS}).`);
44546
+ }
44547
+ const days = Number.parseInt(value, 10);
44548
+ if (days < MCP_TOKEN_MIN_EXPIRY_DAYS || days > MCP_TOKEN_MAX_EXPIRY_DAYS) {
44549
+ throw new Error(`--expires-in must be between ${MCP_TOKEN_MIN_EXPIRY_DAYS} and ${MCP_TOKEN_MAX_EXPIRY_DAYS} days.`);
44550
+ }
44551
+ return days;
44552
+ }
44382
44553
  function requireValue(args, index, flag) {
44383
44554
  const value = args[index + 1];
44384
44555
  if (!value) {
@@ -45964,7 +46135,7 @@ async function executeOnboard(io, client, args) {
45964
46135
  }
45965
46136
  async function executeMcp(io, client, appUrl2, args) {
45966
46137
  if (args[0] !== "install") {
45967
- io.stderr.write(`Usage: ${cliBrand.binName} mcp install --client claude-code|codex|cursor
46138
+ io.stderr.write(`Usage: ${cliBrand.binName} mcp install --client claude-code|codex|cursor [--scope seat --seat-id <id>] [--expires-in <days>|--no-expiry] [--rotate <token-id>]
45968
46139
  `);
45969
46140
  return 1;
45970
46141
  }
@@ -46085,7 +46256,7 @@ function printUsage(io) {
46085
46256
  ...operationUsageLines(cliBrand.binName),
46086
46257
  `${cliBrand.binName} onboard status`,
46087
46258
  `${cliBrand.binName} onboard run`,
46088
- `${cliBrand.binName} mcp install --client claude-code|codex|cursor`,
46259
+ `${cliBrand.binName} mcp install --client claude-code|codex|cursor [--scope seat --seat-id <id>] [--expires-in <days>|--no-expiry] [--rotate <token-id>]`,
46089
46260
  `${cliBrand.binName} init [--client claude-code|codex|cursor]`,
46090
46261
  `${cliBrand.binName} docs`,
46091
46262
  `${cliBrand.binName} reference <list|search|get> [options]`,