v2.14.0 — dynamic-catalog CLI consumer (PR2')
Dynamic-catalog CLI consumer — PR2'
This release is the CLI half of the dynamic-tool-catalog migration. The server side (PR1' on the web backend) shipped to staging on 2026-05-26 00:23 UTC; this release wires the CLI to emit the new connect/heartbeat fields and adds defense-in-depth for any future server-emitted catalog.
No user-visible behaviour change in this release — all wire changes are additive and the server doesn't yet act on the new fields. Defense-in-depth is dormant until v2.15.x (PR5'). The catalog-drift CI gate is the immediate value: any future CLI tool addition now fails CI until the server-side catalog accepts it.
What's new (wire-format v1.0)
The CLI now emits two enriched messages to /api/cli/heartbeat:
providers_configured— array of bare provider names ("aws","hetzner","ovh"). Re-resolved on every heartbeat from the runningServonautApp's service state, so mid-session credential changes propagate within ~30s without forcing a reconnect.cli_release_channel— read from theSERVONAUT_RELEASE_CHANNELenv var. Defaults to"stable", falls back to"dev"for editable / pipx-install-from-source builds. Lets server-side telemetry segment usage by release channel.version—servonaut.__version__. Lets the server gate on minimum-CLI-version requirements without a forced upgrade.capabilities.supports_dynamic_catalog— hardcodedfalsefor v2.14.x. Flips totruein v2.15.x (PR5') when the CLI starts honouring a server-emittedtool_catalogSSE event.
Old CLIs without these fields stay fully functional — server-side resolution defaults to "advertise everything for this user," matching pre-PR1' behaviour. Additive contract, no breakage.
Defense-in-depth: dangerous-tool pattern floor
New module servonaut.services.dangerous_tool_floor ships an 18-regex floor that forces the dangerous guard tier for any tool whose name matches a known-destructive pattern:
- AWS lifecycle:
aws_run_instances,aws_terminate_instance - S3 mutations + presigned URL:
s3_create_bucket,s3_delete_*,s3_upload_object,s3_copy_object,s3_move_object,s3_generate_presigned_url - Hetzner / OVH create + delete (prefix-pattern — auto-catches future tools)
- Cross-provider destructive:
deploy,provision,security_scan,run_command,transfer_file,ip_ban_set
When the CLI starts consuming server-emitted catalogs in v2.15.x, any tool whose server-claimed tier is below dangerous AND whose name matches this floor will be escalated to dangerous and the mismatch audit-logged to ~/.servonaut/mcp_audit.jsonl with reason dangerous_floor_escalation. Belt-and-suspenders posture: server catalog can be wrong; CLI refuses to take its word on destructive ops.
Catalog-drift CI gate
tests/test_catalog_drift.py is the new forcing function. Every CI run asserts:
set(server_catalog_v1.json["tools"]) == set(tool_schemas.TOOL_SCHEMAS) - CATALOG_EXCLUDED_CLI_ONLYwhere CATALOG_EXCLUDED_CLI_ONLY is the 7-tool set of CLI-local-only meta tools that intentionally don't appear in the hosted-MCP catalog (whoami, relay_status, relay_reconnect, api_request, mcp_tool_call, check_status, get_server_info). Anyone adding a new tool to the CLI must either land it in the server-side catalog in the same PR (via the web-side resolver update) or extend CATALOG_EXCLUDED_CLI_ONLY with a justification — drift fails CI loudly.
Install / upgrade
pipx install servonaut --force
# or
pipx upgrade servonautFull diff
What's next
PR3' (web-side providers_configured consumer) kicks off on the web backend. Once it deploys to staging, hosted MCP starts filtering its catalog by which providers the user has configured on at least one connected CLI. PR4' (chat-surface unification, brings the same dynamic catalog to the hosted AI chat panel) and PR5' (CLI consumes server-emitted catalog + escalation wired through _floor_dangerous) follow.