stacklok / stacklok/toolhive-studio
refactor(workload-upgrade): migrate manual upgrade flow to new upgrade-check / upgrade API
Nobody has claimed this yet.
- Dominant language
- TypeScript
- Stars
- 167
- Forks
- 24
- Avg merge
- 11h 32m
- Merged PRs (30d)
- 91
Description
Context
The ToolHive CLI bump to v0.29.0 (PR #2305) ships new REST endpoints dedicated to workload upgrades:
GET /api/v1beta/workloads/upgrade-check— bulk drift check (filter bygroup, include stopped viaall=true)GET /api/v1beta/workloads/{name}/upgrade-check— per-workload drift checkPOST /api/v1beta/workloads/{name}/upgrade— apply upgrade (optional{ env, secrets }merge body)
These cover end-to-end what the renderer is doing today by hand.
Current state (what we do "a mano")
Drift detection (client-side):
useIsServerFromRegistry(renderer/src/features/mcp-servers/hooks/use-is-server-from-registry.ts) parses the running image tag, matches the workload image against the registry catalog, and flags drift whenregistryTag !== localTag.- The card surfaces an amber
ArrowUpCirclebutton whenisFromRegistry && drift(renderer/src/features/mcp-servers/components/card-mcp-server/index.tsx:64).
Apply (via the generic edit endpoint):
useUpdateVersion(renderer/src/features/mcp-servers/hooks/use-update-version.tsx) orchestrates two paths:- Direct update when there is no env-var drift: confirm dialog →
useMutationUpdateWorkload→POST /workloads/{name}/editwith the new image. - Edit & review when env-var drift is detected: opens the full edit dialog pre-populated with
imageOverride,envVarsOverride,secretsOverride, then submits via the sameeditmutation.
- Direct update when there is no env-var drift: confirm dialog →
- The env-var diff itself is computed client-side from the registry entry.
New API surface (after #2305 lands)
GET .../upgrade-check returns a full UpgradeCheckResult:
status:up-to-date | upgrade-available | not-registry-sourced | server-not-found | unknowncurrent_image,candidate_image,registry_serverenv_var_drift.added/.removed(with name, description, default, required, secret) — same data we recompute today for the review dialogconfig_drift.transport/config_drift.permission_profile— drift we currently ignore
POST .../upgrade accepts PkgApiV1UpgradeRequest:
env?: Record<string, string>— overrides/mergesecrets?: string[]— secret references in<name>,target=<env>form- Empty body = upgrade preserving existing configuration
Proposed migration (two independent steps)
Step 1 — replace client-side drift detection
- Replace
useIsServerFromRegistryconsumers withgetApiV1BetaWorkloadsUpgradeCheckOptions(bulk on list views) and the per-name variant where granularity is needed. - Delete the tag parsing / registry comparison logic in the renderer.
- The card's "update available" indicator becomes `status === 'upgrade-available'`.
Step 2 — replace the apply mutation
- In
use-update-version.tsx, swapuseMutationUpdateWorkloadforpostApiV1BetaWorkloadsByNameUpgradeMutation. - Direct upgrade path →
POST .../upgradewith empty body. - Edit-&-review path → map the form values for newly added vars/secrets into
{ env, secrets }and callupgradeinstead ofedit. - Continue using the
editendpoint only for unrelated config changes (it stays the right tool for arbitrary edits).
Wins
- Backend becomes the source of truth for drift; renderer stops parsing image tags.
- We start surfacing
config_drift(transport, permission profile) which is currently silently ignored. - Clean semantic separation:
editfor arbitrary edits,upgradefor the registry-driven version bump.
Caveats / open questions
PkgApiV1UpgradeRequestdoes not acceptcmd_argumentsor arbitrary workload fields. If we want to allow tweaking those at upgrade time, we either keep usingeditfor that case or split it into two steps (upgrade then edit).- The bulk endpoint takes
allandgroupquery params — confirm the list view's current filter semantics still work with one call. - Decide UX for
status === 'not-registry-sourced' | 'server-not-found' | 'unknown'(today these workloads are silently treated as "no drift").
Acceptance criteria
- Step 1: list view and card use
getApiV1BetaWorkloadsUpgradeCheck*instead ofuseIsServerFromRegistry; the hook is removed (or reduced to non-upgrade callers, if any). - Step 2:
useUpdateVersionusespostApiV1BetaWorkloadsByNameUpgradefor both direct and edit-&-review paths. -
config_drift(transport / permission profile) is surfaced in the review dialog. - Existing tests pass / are updated; new tests cover the upgrade-status branches (
upgrade-available,up-to-date,not-registry-sourced, etc.).
References
- CLI release: https://github.com/stacklok/toolhive/releases/tag/v0.29.0
- Upstream PRs: stacklok/toolhive#5407, #5408, #5409, #5410, #5411, #5412
- Renovate bump in this repo: #2305
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with renderer/src/features/mcp-servers/hooks/use-is-server-from-registry.ts, renderer/src/features/mcp-servers/components/card-mcp-server/index.tsx, and renderer/src/features/mcp-servers/hooks/use-update-version.tsx. Trace the existing list and card upgrade flows, then inspect the generated upgrade-check and upgrade API entry points. Done means both flows use the new endpoints, config drift appears in review, and the existing tests are updated with status-branch coverage.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- api, frontend
- Issue type
- Refactor
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 48/100