Skip to content

v2.9.0 — Per-provider managers + full MCP lifecycle parity

Choose a tag to compare

@zoltanam zoltanam released this 10 May 02:09

Highlights

Per-provider manager screens. New ⚙ Manage entries under both Hetzner and OVH in the sidebar — each opens a provider-scoped table with a state-aware action toolbar:

  • Hetzner → ⚙ Manage — Create / Power on / Shutdown / Power off / Reboot / Delete. Start enables only on stopped servers, Shutdown / Power off / Reboot only on running ones.
  • OVH → ⚙ Manage — Create / Start / Stop / Reboot / Delete, routed automatically across Cloud / VPS / dedicated bare-metal per row's provider_type.

The unified InstanceListScreen stays unchanged as the search-and-SSH fast path.

MCP tool lifecycle parity. 10 new tools so AI agents have the same surface as the UI:

Provider New tools
Hetzner power_on, power_off, shutdown, reboot (standard tier), delete_ssh_key (dangerous)
OVH create_instance, delete_instance (dangerous), start_instance, stop_instance, reboot_instance (standard)

Power management at the standard guard tier; create/delete at dangerous. Every mutating tool's description carries an explicit "Confirm with the user before calling" cue, plus a top-level confirmation-protocol prompt in the MCP server's instructions block (summarise → state args → wait for affirmative reply).

Total tool count: 39 (up from 24 in the README's prior count — hetzner_* was missing from the docs entirely).

Hetzner SSH keys screen. New Hetzner → 🔑 SSH Keys surface with full CRUD against the Hetzner Cloud project's key registry — the same registry the create-server wizard injects from. Replaces the prior CLI-hint placeholder.

OVH SSH keys screen migrated. Was managing /me/sshKey (account-level, used by VPS / dedicated). Now manages /cloud/project/{id}/sshkey — the registry the cloud-create wizard reads from. Adding a key actually shows up in the wizard now.

OVH cloud-create wizard fixes.

  • Region-first picker. Region is a Select populated from GET /cloud/project/{id}/region; on change, flavors and images reload filtered. Picking a flavor in the wrong region is impossible by construction (was the source of Flavor X could not be found errors).
  • Datacentre labels. Region dropdown reads GRA11 — Gravelines, France instead of bare codes. Stable mapping for ~13 known prefixes; unknown codes fall back gracefully.
  • Pricing surfaced. _extract_ovh_price helper parses all three OVH price-block shapes (text / value / legacy priceInUcents); flavor table grows Hourly / Monthly columns and the confirm modal includes the cost line.
  • Hide dead regions. Regions with zero deployable flavors are filtered out of the picker; available: false flavors are hidden from the table within otherwise-active regions.

Notable bug fixes

  • fix(relay) — login wrote auth tokens but never relay.{base_url, mercure_url}, so freshly-logged-in users tripped a spurious "MCP relay URLs not configured" toast. New RelayManager.ensure_configured() derives URLs from the API base (prod splits api/apex; staging single-host) and persists.
  • fix(screens) — Textual 8.x rejects push_screen_wait outside a worker context. Audited every callsite; six screens were latently broken and would've crashed the moment a user clicked a destructive action button. Mechanical fix across hetzner_create, ovh_cloud_create, ovh_resize, ovh_snapshots, ovh_firewall, ovh_ip_management.
  • fix(custom-servers) — Save / Cancel row was clipped on small terminals (sat at the end of #add_form, off-screen on a 24-row terminal). Now docked to the screen bottom, mirroring the wizard pattern.
  • fix(ovh-storage)OVHStorageScreen._get_project_ids reached for a non-existent config_manager.config attribute, always returned [] regardless of what was configured. Bug was unique to this one screen — verified by grep across the codebase.
  • fix(hetzner-create) — wizard relied on the service-side fallback to default_hetzner_ssh_key; if that wasn't set the footgun guard fired with a generic message. Now pre-selects the configured default in the keys table + adds an actionable submit-time check pointing at the right Settings field.
  • fix(ovh-ip) — global .hidden { display: none } rule was missing; several screens (instance_list memory banner, ovh_ip_management forms, ovh_firewall) toggled classes="hidden" to no effect. Fixed at the root.

UX polish

  • Sidebar relay indicator moved from the dock-bottom row to the brand block (under "Server Manager"), aligned to the same left margin.
  • Curved-border style language applied uniformly to every OVH-screen DataTable + the new manager surfaces. Action toolbars match the Snapshot Manager visual rhythm.
  • OVH IP Management restructured — each form now in its own card-styled Container so show/hide is atomic; removes the gaps the user reported between sections.

Tests

~150 new tests landed across the release covering: new service methods (HetznerService.{power_on,power_off,shutdown,reboot,delete_ssh_key,list_locations,list_images}, OVHCloudService.list_regions, flavor available + price field shapes), 15 region-label parametrised cases, all 11 new MCP tools (dispatch / guard rejection / validation / api-error wrapping), Hetzner setup screen dropdown seeding/refresh/saved-deprecated value preservation, relay URL derivation and ensure_configured.

Upgrade

pipx install --force servonaut
# or
pip install --upgrade servonaut

Full diff: v2.7.0...v2.9.0