Task — engineering-spec@1
"Distribution expansion — root CLI .sh commands (install/update/changelog/help) + remote MCP connector transport for agent UIs"
doneTASK-IMP-076
§1 — Description
update.sh,changelog.sh,help.shMUST exist at the payload root beside the already-shippinginstall.sh, directly runnable (bash update.sh), mirroring the plugin commands' semantics: update = read-only check by default with--applyto execute (thin wrapper over install.sh, which owns the logic); changelog = installed version +rules_sha+ pointers, read from the manifest beside the script; help = the command surface.build.shMUST ship the trio in every payload (copy + chmod + npmfilesarray) and declare the two new channels inmanifest.yaml:root-cli,mcp-connector.cyberos-mcp.mjsMUST gain a--http [port]mode: MCP streamable-HTTP style endpoint (POST = one JSON-RPC message or batch →application/json; notifications → 202 empty; non-POST → 405;GET /healthzprobe), reusing the existinghandle()dispatch verbatim so stdio and http can never drift. stdio stays the default; zero new dependencies.- A
docs/deploy/mcp-connector.mdrunbook MUST capture the agent-UI hookup (Claude: Name + Remote MCP server URL + optional OAuth; Grok: Name + Server URL) and the production checklist: reverse-proxy TLS, supervisor, and the explicit warning that the transport ships unauthenticated - public exposure requires proxy-level auth since the tools execute repo workflows. - Grok's dialog placeholder suggests legacy
/sse; whether it accepts streamable HTTP is confirmed at hookup time, not asserted - if legacy SSE is required, that transport is a recorded follow-up (§9).
Length note: sanctioned lean profile - every §5 check ran live in-session (scripts executed, endpoint curl-verified).
§2 — Why
update.sh wraps install.sh instead of duplicating logic (single source of truth for vendoring + --check). The http mode reuses handle() so a tool added once serves both transports. Auth is deliberately NOT hand-rolled into the node process: proxy-level auth at nginx is the checklist item - a bespoke token check in a zero-dep server is worse than the battle-tested proxy layer already fronting the VPS.
§4/§5 — Acceptance + verification (all run live 2026-07-13)
- Payload contains executable trio;
check-version-sync.shstill green. ✅ (build to /tmp, ls + run) help.shprints the surface;changelog.shprints version + rules_sha from its own manifest;update.sh(no args, offline) emits install.sh --check's three-value report + verdict. ✅ (all executed)--http:/healthz200 JSON; POST tools/list returns the 4 workflow tools; notification → 202; GET → 405. ✅ (curl-verified)- stdio unchanged (default branch untouched semantics). ✅ (code path conditional on --http only)
- Runbook exists with hookup + security checklist. ✅
§5b — Testing pass (2026-07-13, post gate-1 "approve all")
- Payload rebuilt to /tmp: help.sh 17-line surface exit 0; changelog.sh prints 1.0.0 + rules_sha + pointers; endpoint healthz 200 / tools-list 4 / notification 202 / GET 405. PASS
- DEFECT CAUGHT + FIXED IN-PHASE:
update.sh --checkpassed the flag through twice (exec install.sh --check "$@"without shifting), so install.sh read the second--checkas its TARGET ("cd: --: invalid option"; root detection broken, installed=none from an installed repo). The no-arg default path had masked it at implementation time. Fix: case/shift in update.sh. Re-run, all arg shapes green - live report: installed=1.0.0 payload=1.0.0 latest=1.0.0 (GitHub releases reachable) verdict=up_to_date.
§9 — Open questions
- Grok legacy-SSE transport if streamable HTTP is rejected at hookup (recorded, not assumed).
- OAuth client id/secret support in the connector dialog - relevant only if Stephen wants Anthropic-managed auth instead of proxy auth; follow-up.
- Public URL + reverse-proxy wiring on the VPS (operator step - checklist in the runbook).
§10 — Failure modes
| Failure | Detection | Recovery |
|---|---|---|
| endpoint exposed without auth | runbook checklist item 3 (explicit warning) | proxy auth before DNS |
| oversized POST | 1MB cap destroys request | client retries within cap |
| batch of notifications only | 202 empty per transport semantics | n/a - correct |
| trio run outside a payload | changelog/update print explicit error + exit 2 | run from payload/.cyberos |
| stdio/http drift | impossible by construction - one handle() | n/a |
End of TASK-IMP-076.
Audit
Findings (resolved in-pass)
- ISS-001: draft hand-rolled a bearer-token check into the node server - replaced with the proxy-auth checklist (battle-tested layer, zero-dep server stays zero-dep); unauthenticated-by-default now stated loudly in both the runbook and §1 #4.
- ISS-002: draft asserted Grok accepts streamable HTTP from the /sse placeholder alone - reworded to confirmed-at-hookup with legacy-SSE as the recorded fallback follow-up (§1 #5, §9). Score = 10/10.