Skip to content

v2.15.0 — chat catalog consumer + dangerous-floor escalation (PR5')

Choose a tag to compare

@zoltanam zoltanam released this 26 May 04:21
· 87 commits to master since this release
e12d598

Chat catalog consumer + dangerous-floor escalation — PR5'

Final CLI-side PR of the dynamic-tool-catalog migration. Pairs with the web-side PR1' / PR3' / PR4' work shipped to staging earlier today (2026-05-26 UTC). This release flips capabilities.supports_dynamic_catalog: true on the connect handshake — the signal the hosted backend gates the CHAT_CATALOG_EXPANSION_ENABLED flag-flip on.

What's new

57 new chat-routable tool dispatches. services/ai_tool_bridge.py::_LOCAL_TOOL_HANDLERS now routes every non-relay tool in the server-side catalog to its handler in ServonautTools. Once the staging flag flips, the hosted AI chat panel can invoke any of the following without tool_unavailable errors:

  • AWS describe (6): aws_list_regions, aws_list_amis, aws_list_instance_types, aws_list_key_pairs, aws_list_subnets, aws_list_security_groups
  • AWS lifecycle standard (3): aws_start_instance, aws_stop_instance, aws_reboot_instance
  • AWS lifecycle dangerous (2): aws_run_instances, aws_terminate_instance (MCP-relay surface only — chat surface gates these per the server tier filter + ApprovalService UX deferred to follow-up)
  • S3 read (2): s3_list_buckets, s3_list_objects
  • S3 mutations dangerous (8): s3_create_bucket, s3_delete_bucket, s3_upload_object, s3_delete_object, s3_copy_object, s3_move_object, s3_generate_presigned_url, s3_download_object
  • AWS observability + IP ban (7): cloudwatch_list_log_groups, cloudwatch_get_log_events, cloudwatch_top_ips, cloudtrail_lookup_events, ip_ban_list_configs, ip_ban_list_banned, ip_ban_set
  • Log fetch (1): get_logs
  • Hetzner read + power (8): hetzner_list_servers, hetzner_list_server_types, hetzner_list_ssh_keys, hetzner_power_on, hetzner_power_off, hetzner_shutdown, hetzner_reboot, hetzner_create_ssh_key
  • Hetzner lifecycle dangerous (3): hetzner_create_server, hetzner_delete_server, hetzner_delete_ssh_key
  • OVH read + lifecycle (11): ovh_monitoring, ovh_list_ips, ovh_firewall_rules, ovh_ssh_keys, ovh_snapshots, ovh_dns_records, ovh_billing, ovh_invoices, ovh_start_instance, ovh_stop_instance, ovh_reboot_instance
  • OVH lifecycle dangerous (2): ovh_create_instance, ovh_delete_instance
  • Memory (4): get_server_memory, list_server_memories, build_server_memory, refresh_server_memory

Dangerous-floor escalation is now ACTIVE

The 18-pattern regex floor shipped dormant in v2.14.0 (PR2'). v2.15.0 wires it into the dispatch path:

  • Every tool_call arriving via ai_tool_bridge.handle_tool_call passes through _FloorDangerousMixin._floor_dangerous(name, server_tier) before guard enforcement.
  • If the tool name matches one of the destructive-pattern regexes AND the server-asserted tier is below dangerous, the call is escalated to dangerous tier and the mismatch is audit-logged to ~/.servonaut/mcp_audit.jsonl with reason dangerous_floor_escalation.
  • This is defense-in-depth — a buggy server catalog or wire-shape regression can no longer silently downgrade a destructive operation. The CLI refuses to take the server's word on what counts as dangerous.

This also caught one legitimate pre-existing miss: run_command was historically classified standard for chat-surface dispatch despite being arbitrary shell exec. v2.15.0 escalates it to dangerous automatically. The associated audit-row test was updated to reflect the corrected tier.

Capability bit

capabilities.supports_dynamic_catalog: true on the connect handshake. Hosted backends gate their dynamic-catalog activation on this bit — older CLIs (v2.14.x and earlier) keep receiving the original 9-tool chat surface; v2.15.0+ CLIs receive the expanded catalog.

tool_catalog SSE event handler

services/ai_providers/servonaut_provider.py now handles the tool_catalog SSE event emitted by /api/ai/chat/stream. Audit-only in v2.15.0 — the handler logs receipt to mcp_audit.jsonl with reason tool_catalog_received plus catalog_version, surface, and tool_count. The static _LOCAL_TOOL_HANDLERS map remains source of truth for dispatch; the audit row proves the wire works for a future PR to consume the catalog dynamically without changing v2.15.0 behaviour.

Install / upgrade

pipx install servonaut --force
# or
pipx upgrade servonaut

Full diff

v2.14.0...v2.15.0

What's next

The dynamic-tool-catalog migration is feature-complete on both sides. Staging-flip protocol:

  1. v2.15.0 deployed to PyPI (this release)
  2. Operator runs pipx upgrade servonaut on the staging-connected CLI
  3. v2.15.0 CLI heartbeat carries capabilities.supports_dynamic_catalog: true
  4. Operator flips CHAT_CATALOG_EXPANSION_ENABLED=true on the staging web env var (no redeploy needed, ~10s container restart)
  5. End-to-end test: ask the hosted AI chat "list my EC2 regions" — the 40 newly-chat-exposed tools dispatch through this CLI's ai_tool_bridge to the local mcp/tools.py handlers and return real responses

Once staging E2E is verified, production flag-flip happens on the user's schedule.