MCP follow-up: converge the Codex review of #662
Follow-up to #662, which merged before the Codex review landed. Triage of the three findings (each answered in-thread on #662):
- P1 — Nix vendorHash (go.mod): already fixed before the merge — the branch's
Refresh the Nix vendorHashcommit rode the squash into db1ff0c, and the Nix flake job was green on the merged head. No action here. - P2 — paginated actions without a
pageparameter (dispatch): confirmed. Six operations (ListWebhooks, ListChatbots, ListMessageTypes, ListPingablePeople, ListQuestionAnswerers, ListUploadVersions) are marked paginated by the behavior model but declare nopagequery parameter, so thenext_pagea listing returns was rejected as an unknown parameter and pages past the first were unreachable. Fixed by synthesizing the parameter at catalog load from the paginated trait — trait-driven rather than a name table, so it covers whatever the model marks paginated and no-ops once the SDK export declares the parameter itself. Pinned by a catalog invariant (every paginated operation declares exactly one integerpagequery param) and a round-trip test throughlist_webhooks. - P2 — MCP command errors on the protocol stream (commands/mcp.go): confirmed. Errors from the command's RunE rendered through
cli.Execute's stdout writers, landing a CLI error envelope in the JSON-RPC transport where it reads as a malformed protocol message. Fixed with astdout_wireannotation on the command: Execute reports errors for wire commands on stderr — plain lines, hint included when the message doesn't already carry it, same exit codes.
Also picks up Copilot's suppressed nit from the same review pass: next_page was emitted as a string ("2") while every page parameter advertises an integer schema; it's now a number, and the pagination round-trip test covers passing it straight back.
The remaining Copilot/Codex-adjacent thread on #662 (raw AccountClient verbs bypassing OnOperationGate) stays declined for the reasons on the thread: the right fix is new basecamp-sdk surface (Do(ctx, OperationInfo, ...)) that both basecamp api and basecamp mcp ride, not a 250-entry dispatch table here.
Summary by cubic
Fixes the two P2 findings from the Codex review on #662 plus a related schema mismatch.
- Synthesizes a
pagequery parameter for operations the model marks paginated but the SDK export leaves undeclared, sonext_pagevalues are accepted by the dispatcher. - Routes
basecamp mcperrors to stderr instead of stdout, keeping CLI error envelopes out of the JSON-RPC transport. - Emits
next_pageas a number, matching thepageparameter's integer schema.
The declined thread about raw AccountClient verbs is intentionally not addressed here; the right fix is new basecamp-sdk surface.