Skip to content

v2.14.0 — dynamic-catalog CLI consumer (PR2')

Choose a tag to compare

@zoltanam zoltanam released this 26 May 01:40
· 88 commits to master since this release
f4dcb94

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:

// First message on every (re)connect
{
  "type": "cli.handshake",
  "version": "2.14.0",
  "cli_release_channel": "stable",        // or "beta" / "dev"
  "providers_configured": ["aws", "hetzner", "ovh"],
  "capabilities": {"supports_dynamic_catalog": false}
}

// Every 30s afterwards
{"type": "cli.heartbeat", "providers_configured": ["aws", "hetzner"]}
  • providers_configured — array of bare provider names ("aws", "hetzner", "ovh"). Re-resolved on every heartbeat from the running ServonautApp's service state, so mid-session credential changes propagate within ~30s without forcing a reconnect.
  • cli_release_channel — read from the SERVONAUT_RELEASE_CHANNEL env var. Defaults to "stable", falls back to "dev" for editable / pipx-install-from-source builds. Lets server-side telemetry segment usage by release channel.
  • versionservonaut.__version__. Lets the server gate on minimum-CLI-version requirements without a forced upgrade.
  • capabilities.supports_dynamic_catalog — hardcoded false for v2.14.x. Flips to true in v2.15.x (PR5') when the CLI starts honouring a server-emitted tool_catalog SSE 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_ONLY

where 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 servonaut

Full diff

v2.13.0...v2.14.0

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.