feat: per-command MCP outputSchema — Phase 4#941
Conversation
Hand-author per-command MCP outputSchemas for the 13 typed commands whose closed result shapes live in the contracts layer (mirroring CommandResultMap): press, fill, longpress, boot, shutdown, viewport, home, back, rotate, app-switcher, clipboard, appstate, keyboard. The new COMMAND_OUTPUT_SCHEMAS registry is injected into tools/list via listCommandTools(). It is additive-only: untyped/dynamic tools (snapshot, gestures, perf, logs, …) carry no outputSchema key and stay byte-identical. Schemas are non-strict (no additionalProperties:false) so the additive cost object rides into structuredContent and still validates. MCP agents can now trust structuredContent against the advertised schema instead of re-parsing text.
Size Report
Startup median (7 runs, lower is better):
Top changed chunks:
|
|
Checked current head One cleanup before I would call this ready: please type-tie Right now a misspelled key or a new |
Replace Partial<Record<string, JsonSchema>> with `satisfies Record<keyof CommandResultMap, JsonSchema>`, so the one-for-one invariant with the typed-result spine is compiler-enforced: a new CommandResultMap entry without an output schema is now a missing-key error, and a misspelled/extra key is an excess-property error (previously both compiled and silently omitted the schema). The lookup in listCommandTools guards with an `in` check since the registry is keyed by the typed commands only.
|
Done — pushed ec3f908.
The |
|
Re-reviewed current head I rechecked the scoped diff against Phase 4 in |
Phase 4 (agent-cost): per-command MCP
outputSchemaMCP agents currently have to re-parse the text content to learn a command's result shape. This slice advertises a per-command
outputSchemaso agents can truststructuredContentdirectly.What this does
src/mcp/command-output-schemas.ts: a hand-authored, partial-coverageCOMMAND_OUTPUT_SCHEMASregistry keyed by daemon command name. It mirrors the typed-result spineCommandResultMap(src/core/command-descriptor/command-result.ts) one-for-one — schemas authored by hand from the matchingsrc/contracts/*types (there is no type→JSON-Schema generator in the repo).listCommandTools()sotools/listnow returnsoutputSchemafor the typed commands. No router/server changes (the protocol version supportsoutputSchema).src/mcp/__tests__/command-tools.test.tscovering a typed command's discriminant, the byte-identical untyped path, and structuredContent↔schema consistency.The 13 typed commands
press,fill,longpress,boot,shutdown,viewport,home,back,rotate,app-switcher,clipboard,appstate,keyboard.The genuinely-dynamic commands (snapshot overlays, gestures, perf, logs, …) are intentionally absent — exactly as
CommandResultMapomits them rather than inventing a shape.Design notes
outputSchemakey and stay byte-identical to today.additionalProperties: falseanywhere, so the additivecostobject (opted in via--cost/includeCost) and any other additive fields ride intostructuredContentand still validate.constdiscriminants, and discriminated-union branches (clipboardonaction,appstateonplatform, the interaction trio onkind) mirror the source contract types. Union shapes useoneOfwith mutually-exclusiveconstdiscriminants so additive fields never break the exactly-one-of contract.Verification
tsc --noEmit: exit 0oxfmt --write+oxlint --deny-warnings: exit 0fallow audit --base origin/main: CLEANvitest run src/mcp: 22 passed