Summary
Investigating a customer report (TKT-90) about thv run fetch "seeming off" on v0.44.0/macOS surfaced several CLI UX gaps around updating/recreating an existing workload's configuration, plus one silently-swallowed error. None of these are specific to the fetch server — they apply to any named workload.
Reproduced against main @ 4440a7ddf unless noted otherwise.
Issues found
-
Unhelpful "already exists" error on thv run. cmd/thv/app/run.go:233 — running thv run <name> for a workload that already exists (even if stopped) returns workload with name 'x' already exists with no pointer to the fix. Should suggest remediation, e.g. ... already exists; run 'thv rm <name>' to remove it first, or 'thv start <name>' to resume it.
-
thv list (default) hides stopped workloads, contradicting the "already exists" error. thv list only shows running/auth_retrying workloads by default; a stopped workload needs thv list --all/-a. This means a user can see "No MCP servers found" from list while thv run <name> correctly (but confusingly) reports "already exists" for that same name. Reproduced: thv run fetch → thv stop fetch → thv list shows nothing → thv run fetch → already-exists error.
-
thv start/thv restart silently reject run-time config flags with a bare "unknown flag" error. thv start is an alias for restartCmd (cmd/thv/app/restart.go:22-31) and only accepts --all, --group, -f/--foreground — it resumes a workload from its already-persisted config and was never meant to take flags like --permission-profile or --allow-docker-gateway (those live only on thv run, cmd/thv/app/run_flags.go). The generic Cobra "unknown flag" error doesn't explain why, which led a user to cycle through start/restart trying to apply a new permission profile before finding the correct flow (thv rm <name> then thv run ... <name> again).
-
No top-level thv --version flag, only the thv version subcommand (cmd/thv/app/version.go). Inconsistent with how prominently the CLI already surfaces version info via its upgrade-nag banner on every command.
-
Upgrade-nag banner prints unconditionally, even on commands that immediately fail. checkForUpdates() (pkg/updates/checker.go) is called at cmd/thv/app/commands.go:86, inside NewRootCmd() before cmd.Execute() — so it fires before Cobra even parses/validates flags. A user gets "A new version of ToolHive is available" noise on top of an unknown flag error, adding confusion in an already-confusing error loop.
-
Registry-lookup errors are silently swallowed and misreported as a container-image pull failure. handleRegistryLookup (pkg/runner/retriever/retriever.go:218-254) calls provider.GetServer(serverOrImage); any error from that call (genuine not-found, parse failure, network/API error, ambiguous short-name match) is swallowed at lines 247-251 and silently treated as "not in registry, try as a literal image reference." The real cause is only logged at slog.Debug and never surfaced to the user. This produces a misleading final error like failed to retrieve or pull image: image not found in registry, please check the image name or tag: fetch — which reads like the server name wasn't found, but is actually a suppressed registry-lookup failure being masked as a raw image-pull attempt. Should at minimum surface a Warn-level message like could not resolve '%s' via registry (%v), attempting to pull as a container image so users aren't left debugging a confusing error with no visibility into the real cause.
Suggested fixes (not exhaustive, open to discussion)
- Add remediation hints to the "already exists" error (item 1).
- Either default
thv list to include stopped workloads, or clarify in --help/docs that a "not shown" workload may still block thv run (item 2).
- Improve the error path for run-only flags passed to
start/restart (item 3), e.g. detect known run-flags and return a clearer message.
- Add a top-level
--version flag as an alias for thv version (item 4).
- Skip or defer the upgrade-check banner until after flag parsing/validation succeeds, or suppress it on error exits (item 5).
- Surface the swallowed registry-lookup error at
Warn level instead of Debug (item 6).
Docs follow-up (tracked separately, not in this repo)
The public guide (docs.stacklok.com/toolhive/guides-mcp/fetch) has no guidance for updating an existing workload's config — it should mention thv rm <name> as the required step before re-running with new flags, and clarify that thv start/restart cannot apply new config.
Summary
Investigating a customer report (TKT-90) about
thv run fetch"seeming off" on v0.44.0/macOS surfaced several CLI UX gaps around updating/recreating an existing workload's configuration, plus one silently-swallowed error. None of these are specific to thefetchserver — they apply to any named workload.Reproduced against
main@4440a7ddfunless noted otherwise.Issues found
Unhelpful "already exists" error on
thv run.cmd/thv/app/run.go:233— runningthv run <name>for a workload that already exists (even if stopped) returnsworkload with name 'x' already existswith no pointer to the fix. Should suggest remediation, e.g.... already exists; run 'thv rm <name>' to remove it first, or 'thv start <name>' to resume it.thv list(default) hides stopped workloads, contradicting the "already exists" error.thv listonly shows running/auth_retryingworkloads by default; a stopped workload needsthv list --all/-a. This means a user can see "No MCP servers found" fromlistwhilethv run <name>correctly (but confusingly) reports "already exists" for that same name. Reproduced:thv run fetch→thv stop fetch→thv listshows nothing →thv run fetch→ already-exists error.thv start/thv restartsilently reject run-time config flags with a bare "unknown flag" error.thv startis an alias forrestartCmd(cmd/thv/app/restart.go:22-31) and only accepts--all,--group,-f/--foreground— it resumes a workload from its already-persisted config and was never meant to take flags like--permission-profileor--allow-docker-gateway(those live only onthv run,cmd/thv/app/run_flags.go). The generic Cobra "unknown flag" error doesn't explain why, which led a user to cycle throughstart/restarttrying to apply a new permission profile before finding the correct flow (thv rm <name>thenthv run ... <name>again).No top-level
thv --versionflag, only thethv versionsubcommand (cmd/thv/app/version.go). Inconsistent with how prominently the CLI already surfaces version info via its upgrade-nag banner on every command.Upgrade-nag banner prints unconditionally, even on commands that immediately fail.
checkForUpdates()(pkg/updates/checker.go) is called atcmd/thv/app/commands.go:86, insideNewRootCmd()beforecmd.Execute()— so it fires before Cobra even parses/validates flags. A user gets "A new version of ToolHive is available" noise on top of anunknown flagerror, adding confusion in an already-confusing error loop.Registry-lookup errors are silently swallowed and misreported as a container-image pull failure.
handleRegistryLookup(pkg/runner/retriever/retriever.go:218-254) callsprovider.GetServer(serverOrImage); any error from that call (genuine not-found, parse failure, network/API error, ambiguous short-name match) is swallowed at lines 247-251 and silently treated as "not in registry, try as a literal image reference." The real cause is only logged atslog.Debugand never surfaced to the user. This produces a misleading final error likefailed to retrieve or pull image: image not found in registry, please check the image name or tag: fetch— which reads like the server name wasn't found, but is actually a suppressed registry-lookup failure being masked as a raw image-pull attempt. Should at minimum surface aWarn-level message likecould not resolve '%s' via registry (%v), attempting to pull as a container imageso users aren't left debugging a confusing error with no visibility into the real cause.Suggested fixes (not exhaustive, open to discussion)
thv listto include stopped workloads, or clarify in--help/docs that a "not shown" workload may still blockthv run(item 2).start/restart(item 3), e.g. detect known run-flags and return a clearer message.--versionflag as an alias forthv version(item 4).Warnlevel instead ofDebug(item 6).Docs follow-up (tracked separately, not in this repo)
The public guide (docs.stacklok.com/toolhive/guides-mcp/fetch) has no guidance for updating an existing workload's config — it should mention
thv rm <name>as the required step before re-running with new flags, and clarify thatthv start/restartcannot apply new config.