-
Notifications
You must be signed in to change notification settings - Fork 183
Description
Problem
thv skill install <name> with a plain name creates a "pending" record that never resolves to "installed". The only working install path today is with a fully-qualified OCI reference:
# This works
thv skill install ghcr.io/org/my-skill:v1
# These don't — skill stays "pending" forever
thv skill build ./my-skill && thv skill install my-skill
thv skill install some-published-skillThis breaks both the local development workflow (build → install) and the registry-based discovery workflow (install by name).
How thv run solves this for MCP servers
thv run has a cascading reference resolution model that skills should follow:
| Reference type | thv run example |
Resolution |
|---|---|---|
| Registry name | thv run filesystem |
Looks up in registry → gets OCI image ref → pulls |
| Direct OCI ref | thv run ghcr.io/org/server:v1 |
Pulls directly |
| Protocol scheme | thv run uvx://mcp-server-git |
Builds from source template |
| Remote URL | thv run https://api.example.com/mcp |
Proxies to remote |
The resolution cascade in pkg/runner/retriever/retriever.go:
- Protocol scheme check (
uvx://,npx://,go://) - Registry lookup (
provider.GetServer(name)) - Fallthrough to direct image reference
What thv skill install needs
Skills should support analogous reference types:
| Reference type | Example | Resolution |
|---|---|---|
| Registry/index name | thv skill install my-skill |
Look up in skill index/registry → get OCI ref → pull & extract |
| Direct OCI ref | thv skill install ghcr.io/org/my-skill:v1 |
Pull & extract (already works) |
| Local build tag | thv skill install my-skill (after thv skill build) |
Resolve from local OCI store → extract |
| Git reference | thv skill install git://github.com/org/repo#path/to/skill |
Clone & extract (future) |
Root cause
The Install method in pkg/skills/skillsvc/skillsvc.go has three paths:
- OCI reference (contains
/,:,@) →installFromOCI()→ pulls from remote → extracts → works - Plain name + LayerData →
installWithExtraction()→ extracts → works (but unreachable from CLI/API) - Plain name, no LayerData →
installPending()→ creates DB record withstatus: "pending"→ dead end
Path 3 is always hit for plain names because:
- The CLI/API never sends
LayerData(internal-only field) - No registry/index lookup exists for skills
- No local OCI store lookup exists
- Nothing ever transitions "pending" to "installed"
Proposed resolution cascade for thv skill install <name-or-ref>
1. Is it an OCI reference? (contains /, :, @)
YES → installFromOCI (already implemented)
2. Is it in the local OCI store? (from a prior `thv skill build`)
YES → extract from local store → status = "installed"
3. Is it in the skill registry/index?
YES → get OCI reference → installFromOCI
4. Not found → return actionable error:
"skill 'foo' not found. Use an OCI reference (ghcr.io/org/foo:v1)
or build locally first (thv skill build ./foo)"
Step 3 can reuse the provider pattern from pkg/registry/ — SkillIndexEntry already has a Repository field for the OCI reference.
Existing infrastructure to reuse
pkg/registry/provider pattern: Factory, base provider, API/local/remote provider implementations. TheProviderinterface withGetServer(name)maps directly to skill index lookup.pkg/skills/types.go:SkillIndexandSkillIndexEntrytypes already defined withRepository(OCI ref) fieldskillsvc.installFromOCI: Already handles pull → extract → validate → store for OCI references- Local OCI store (
ociskills.Store): Already used bybuildandinstallFromOCI, just needs a resolve-by-tag path toolhive-core/registry/types: Skill struct withPackagessupporting both "oci" and "git" registry types
Additional tech debt: hand-rolled OCI tag validation
validateOCITag in skillsvc.go used a hand-rolled regex instead of the go-containerregistry library that's already imported. This is being fixed in #4010.
Suggested phased approach
Phase 1 — Local build → install (unblocks developer workflow):
- When
installgets a plain name, check local OCI store for matching tag - If found, extract from local store (reuse
installFromOCIextraction logic) - Fix E2E lifecycle test to verify actual extraction
Phase 2 — Registry/index lookup (unblocks published skill discovery):
- Add skill index provider (following
pkg/registry/provider pattern) - Plain name → index lookup → OCI reference →
installFromOCI - Could back onto the same MCP Registry API or a dedicated skill index
Phase 3 — Git references (future):
- Add
git://protocol scheme support for skills - Clone repo, locate skill directory, extract
Context
Discovered during review of #4010 — the E2E lifecycle test annotates install as "(pending)" without verifying actual extraction. Reviewer (@reyortiz3) flagged that install stays pending.