Skip to content

chore: rollup of #2568 CLI sub-issue PRs (not for merge) - #2589

Closed
cliffhall wants to merge 31 commits into
v2/mainfrom
v2/chore/2568-cli-rollup
Closed

cliffhall wants to merge 31 commits into
v2/mainfrom
v2/chore/2568-cli-rollup

Conversation

@cliffhall

Copy link
Copy Markdown
Member

Refs #2568

Rollup — not for merge. This branch merges all seven #2568 sub-issue PRs onto v2/main to show they combine cleanly and pass CI together. Each change lands through its own PR; close this one once they have merged.

Issue PR Change
#2537 #2583 Own-property server lookup (--server constructor → not found)
#2517 #2572 Stored-auth flags read tokens from the active byIssuer slot
#2435 #2576 -q / --quiet
#2434 #2579 --completion <bash|zsh|fish>
#2433 #2581 servers/add, servers/edit, servers/remove
#2431 #2582 --output <path> / --output-format raw|json
#2420 #2587 Skills catalog budget flags for CLI and TUI

Merge conflicts resolved here

All were additive. Both sides were kept in each:

#2537 and #2420 both touch core/mcp/node/{config,servers}.ts. They merged without conflicts.

Local verification on the combined tree

  • clients/cli: npm run check (format, lint, typecheck) passes.
  • clients/tui: npm run check passes.
  • clients/web: tsc -b passes.
  • core/mcp/node unit tests: 150 of 150 pass.
  • clients/cli test:coverage on the first six branches: 566 of 566 pass, with thresholds met.

CI on this PR is the check for everything combined.

🤖 Generated with Claude Code

cliffhall and others added 26 commits October 5, 2026 01:13
…ult to a file (#2431)

Writes the single-result path's output to a file instead of stdout.
json is the whole result pretty-printed; raw is the text of the
text-bearing blocks, or the decoded bytes of a single binary block.
Under --format json stdout carries an { output } envelope in place of
{ result }. Flag combinations that would be silently inert are
rejected up front. The TUI half is split to #2571.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: cliffhall <cliff@futurescale.com>
`--quiet` drops the CLI's non-essential output so a scripted caller gets the
result payload on stdout and, on failure, only the error envelope on stderr:

- a stdio server's own stderr (piped and drained instead of inherited)
- the non-strict schema-portability hint on tools/list
- the --verify one-line summary (a failing run's envelope carries it)
- "Authorization complete." / "Authorization complete. Retrying…"
- the --relogin revocation-failure warning

Kept on purpose: the --strict report (explicitly requested; the detail
behind exit 6), and everything a human must act on for interactive OAuth
(authorization URL, step-up [y/N], "open it by hand").

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: cliffhall <cliff@futurescale.com>
…2434)

`mcp-inspector --cli --completion <bash|zsh|fish>` prints a completion
script and exits without connecting. The flag list, which flags take a
value, and which take a path are read from the CLI's own commander
program, so the scripts cannot drift from --help. --method completes the
same ONE_SHOT_METHODS list parseArgs validates, plus servers/list and
servers/show; --transport, --log-level, --format and --protocol-era
complete their accepted values.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: cliffhall <cliff@futurescale.com>
Since the OAuth store was re-keyed per authorization server (#1625),
acquired tokens live at servers[url].byIssuer[activeIssuer].tokens, but
the CLI's stored-auth lookups still read the server-level tokens field.
--use-stored-auth, --list-stored-auth and --wait-for-auth therefore never
saw a token the store actually held.

Add resolveStoredCredentials, which resolves a stored server's tokens and
client information the way the shared store answers a ctx-less read: the
activeIssuer slot first, then the legacy top-level fallback, never an
arbitrary issuer. Tokens and client come from the same source, with a
preregistered (static) client winning as it does in the auth provider.
Every stored-auth lookup goes through it, and the refresh write-back
persists rotated tokens to the slot they were read from.

Closes #2517

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: cliffhall <cliff@futurescale.com>
A usage error made Commander write its own error line during parse(),
before the envelope, so --quiet stderr was two lines. The error is still
thrown and reaches the envelope with the same message.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: cliffhall <cliff@futurescale.com>
…2435)

resolveSecretStore() announces a keychain fallback or a store caveat
(e.g. memory) with console.warn on first use, so an HTTP/SSE --quiet run
on a box without a keychain still printed it. Core caches the resolution,
so the CLI now settles it once at parse time with console.warn muted; no
core change, and the selected store is unaffected.

Also reword the README so it no longer says --quiet empties stderr, since
the table below lists what it deliberately keeps.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: cliffhall <cliff@futurescale.com>
Bash splits `--method=tools/` on '=' (COMP_WORDBREAKS), so the value
completes against the option two words back; zsh keeps it as one word,
so the option prefix moves into IPREFIX via compset. Fish already
handles the form natively; all three are covered by shell tests.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: cliffhall <cliff@futurescale.com>
…2433)

The CLI could only read the server catalog. Add --method servers/add,
servers/edit and servers/remove, which write the same catalog file the web
and TUI clients use by driving the web backend's own /api/servers routes
in-process (createRemoteApp + app.request), so validation, the secret-store
split and the atomic write are shared rather than re-implemented.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: cliffhall <cliff@futurescale.com>
Core reports advisories with console.warn from many places the CLI
reaches (cleanRoots, OAuth endpoint overrides, oauth-persist, file-lock,
secret-store selection), so muting only the secret-store resolution left
the rest leaking. parseArgs now swaps console.warn for a no-op when -q is
in argv, and runCli restores it in finally so a module caller (the
launcher, the test runner) never inherits the mute. Replaces the narrower
quiet-secret-store helper. Everything --quiet keeps is written to the
streams directly, never through console.warn.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: cliffhall <cliff@futurescale.com>
… not found

A server name that matches an inherited Object.prototype member
(`constructor`, `toString`, ...) resolved to the inherited function on a
bare `map[name]` read, so the CLI failed with an unrelated
"Cannot use 'in' operator" error instead of "Server 'constructor' not
found".

Both by-name lookups on the --server path now go through the existing
`getOwnEntry` helper (an `Object.hasOwn` read):
- `selectServerEntry` in core/mcp/node/servers.ts, which is the lookup
  the CLI's `--server` actually reaches (and `servers list <name>`);
- `loadServerFromConfig` in core/mcp/node/config.ts, the lookup named
  in the issue (web `--config` + `--server` single mode).

Closes #2537

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: cliffhall <cliff@futurescale.com>
A rename moves the entry's secrets to the new name, but a session store in
the CLI process is empty, so the move would drop secrets another process
holds. Also reword the README's servers/remove sentence to name the flags it
actually rejects.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: cliffhall <cliff@futurescale.com>
Commander accepts -qe KEY=V, which enabled quiet without the pre-parse
handling (Commander's writeErr and the console.warn mute). Read clusters
the way Commander does: q is the flag, and e takes a value, so anything
after it in the token is that value.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: cliffhall <cliff@futurescale.com>
#2431)

Buffer.from(x, 'base64') silently drops invalid characters, so a
malformed payload was written as unrelated bytes. Decode through core's
base64ToBytes (atob, which throws) and report output_not_raw instead.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: cliffhall <cliff@futurescale.com>
…can (#2435)

A -q that is another option's value (--client-secret -q) was taken for the
flag. Commander's error output is now buffered and written from
exitOverride unless opts().quiet is already set, and the console.warn mute
moves to just after parse(). Combined -qe still counts because Commander
parses it natively; argvRequestsQuiet is gone.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: cliffhall <cliff@futurescale.com>
The shared transport builder drops -e/--cwd for an SSE/HTTP target, so
servers/add (or an edit with a new URL) reported success without storing
them. A whitespace-only --rename was likewise silently ignored.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: cliffhall <cliff@futurescale.com>
…found

An explicitly blank --config fell through to the default catalog, and a
non-object catalog entry passed the existence precheck only to hit the
route's idempotent DELETE and report success without writing.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: cliffhall <cliff@futurescale.com>
…lags (#2420)

Web exposes the per-server skills catalog budget (skillCatalogMaxSkills /
skillCatalogMaxBytes) in Server Settings; the CLI and TUI already honored
the values from a catalog/config file entry but had no flag for them.

Add both flags to the CLI and TUI, validated as positive integers by a
shared core coerce (skillCatalogLimitParser), and overlay them onto the
resolved server settings in loadServerEntries the way --protocol-era is,
so verifySkills reads them through getServerSettings().

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: cliffhall <cliff@futurescale.com>
…into v2/chore/2568-cli-rollup

Signed-off-by: cliffhall <cliff@futurescale.com>

# Conflicts:
#	clients/cli/README.md
…to v2/chore/2568-cli-rollup

Signed-off-by: cliffhall <cliff@futurescale.com>

# Conflicts:
#	clients/cli/README.md
#	clients/cli/__tests__/README.md
#	clients/cli/src/cli.ts
#	clients/cli/src/handlers/method-types.ts
…et flag error (#2420)

Copilot review: `1e3` and `9007199254740993` are positive integers that
the parser rejects by design, so 'Expected a positive integer' did not tell
the user how to correct them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: cliffhall <cliff@futurescale.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: cliffhall <cliff@futurescale.com>
@cliffhall cliffhall added the v2 Issues and PRs for v2 label Oct 5, 2026
cliffhall and others added 3 commits October 5, 2026 05:36
…lls pane (#2420)

Copilot review: SkillsTab verifies one skill per gesture, and the catalog
budget is a run-level bound, so the TUI flags (like the file values) have
no observable effect there today. Say so in the README and --help rather
than claiming the pane reads within the budget; making the pane verify the
whole catalog is tracked in #2590.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: cliffhall <cliff@futurescale.com>
…ader (#2420)

Copilot review: tui.tsx is outside the TUI coverage include and
tui-servers.test.ts calls the loader directly, so a misspelt option or a
dropped forwarding field would have passed. Mock the loader to capture the
options runTui builds and assert both flags arrive parsed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: cliffhall <cliff@futurescale.com>
cliffhall and others added 2 commits October 5, 2026 06:17
Copilot review: the budget is read only by verifySkills, so accepting the
flags on a run without --verify would make a job look bounded when nothing
is bounded. Reject them there, as --require-digests already is.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: cliffhall <cliff@futurescale.com>
@cliffhall

Copy link
Copy Markdown
Member Author

Closing: this rollup did its job. It merged all seven #2568 sub-issue PRs onto v2/main and passed CI together (build + coverage green on 287f3f76). All seven have now merged individually: #2583, #2572, #2576, #2579, #2581, #2582 and #2587. #2581 and #2582 were updated from v2/main before merging, with the same additive conflict resolutions used here, and passed CI again.

@cliffhall cliffhall closed this Oct 5, 2026
@cliffhall
cliffhall deleted the v2/chore/2568-cli-rollup branch October 5, 2026 14:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

v2 Issues and PRs for v2

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant