cli: --app-info probe + --format json envelope#1653
Conversation
Adds the #1574 app-probing and machine-readable output surface, re-implemented informed by PR #1510 and adapted to the v2/main OAuth-wired cli.ts. - `--method tools/call --tool-name <t> --app-info` — prints one JSON line describing the tool's app posture (hasApp, resourceUri, csp, permissions, domain, …) WITHOUT invoking the tool; exit 0 when it has an app, 2 (no_app) when it doesn't. A missing tool exits 5 (tool_not_found) — distinct from the no-app exit so a typo isn't mistaken for "no app". - `--method tools/list --app-info` — one NDJSON line per tool over a single connection (`| jq -c 'select(.hasApp)'`). - `--format json` — a single stdout envelope (`{result}` plus `{appInfo}` as a sibling key for App tools), no banner, so any method pipes into jq. - A tools/call returning isError:true now exits 5 (tool_is_error) after printing its payload, so && chains stop on a failed call. Implementation: `collectAppInfo()` (core `extractAppInfo` + a tolerated resource read that reports `resourceError`), an NDJSON loop in tools/list, and a single `emitResult()` that owns format + exit-code handling. `runMethod` now returns a `MethodOutcome` discriminated union so the NDJSON path can signal "already emitted". Tests: app-info.test.ts (exit-0/2/5 + NDJSON + no-invoke), format-json.test.ts (envelope shapes + bad --format), emit-result.test.ts (direct unit coverage of the isError→exit-5 and resourceError branches via exported emitResult/ collectAppInfo). Updated the tools.test "nonexistent tool" case to the new exit-5 tool_not_found contract. Per-file coverage ≥90 on all four dimensions (cli.ts 100% stmts/funcs/lines). README documents the flags, NDJSON shape, and exit semantics. Closes #1574 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BrihGWcrM9JGRyu41nzZYw
…pp-info-format-json
|
@claude review |
|
Claude finished @cliffhall's task in 2m 4s —— View job Review:
|
…#1653) Address review of PR #1653: - collectAppInfo now never throws: a malformed `_meta.ui.resourceUri` (extractAppInfo throws) folds into `{hasApp:false, resourceError}` just like a resources/read failure. This makes `tools/list --app-info` per-tool tolerant — one bad tool no longer aborts the whole NDJSON listing with a generic exit-1. Guarding inside collectAppInfo (vs. a loop-local try/catch) keeps the branch unit-testable; added an emit-result.test.ts case for the malformed-URI path. - Documented that `tools/list --app-info` always emits NDJSON regardless of `--format` (the per-tool list shape is fixed; --format json only reshapes the single-result paths) — in both the code comment and the README. - Removed the now-unused `expectJsonError` helper (its only caller moved to the exit-5 tool_not_found contract). Coverage stays ≥90 on all four dimensions (cli.ts 100% stmts/funcs/lines). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BrihGWcrM9JGRyu41nzZYw
|
Thanks — applied 2 of 3 findings and documented the third. Details: 1. 2. 3. Dead isError→5 behavior-change callout: acknowledged, no action — it's the documented intent and the README calls it out.
|
|
@claude review |
|
Claude finished @cliffhall's task in 1m 46s —— View job Re-review:
|
|
Thanks for the re-review. On the two optional observations: I'm leaving both as-is. The exit-0-with- |
Closes #1574
App-probing and machine-readable output for the CLI, re-implemented informed by PR #1510 (
33fac3f) and adapted to the v2/main OAuth-wiredcli.ts.What changed
--method tools/call --tool-name <t> --app-info— prints one JSON line describing the tool's MCP App posture (hasApp,resourceUri,csp,permissions,domain, …) without invoking the tool; exits0when it has an app,2(no_app) when it doesn't.--method tools/list --app-info— one NDJSON line per tool over a single connection.--format json— a single stdout envelope ({ result }+{ appInfo }sibling key for App tools), no banner, so any method pipes intojq.5(tool_not_found) — distinct from the no-app2; atools/callreturningisError:truenow exits5(tool_is_error) after printing its payload.Implementation
collectAppInfo()uses coreextractAppInfo()plus a toleratedresources/readthat reports failures in aresourceErrorfield instead of throwing.emitResult()owns--format/--app-infooutput and the exit-code mapping;runMethodreturns aMethodOutcomediscriminated union so the NDJSON path signals "already emitted".Tests & docs
app-info.test.ts,format-json.test.ts(end-to-end via the bundledmcp_app_demofixture), andemit-result.test.ts(direct unit coverage of theisError→exit-5 andresourceErrorbranches via exportedemitResult/collectAppInfo).tools.test"nonexistent tool" case to the new exit-5tool_not_foundcontract.clients/cli/README.mddocuments the flags, NDJSON shape, and exit semantics.npm run cipasses.Stacking
Second of the Wave 3 CLI stack. Base is
v2/1573-cli-programmatic-ergonomics(#1573), notv2/main— review/merge #1573 first. The final target isv2/main.🤖 Generated with Claude Code
https://claude.ai/code/session_01BrihGWcrM9JGRyu41nzZYw