Skip to content

[Epic] Plugin lifecycle management (RFC THV-0077) #5525

Description

@JAORMX

Overview

Add plugin lifecycle management to ToolHive. A plugin is the "bundle of primitives" unit pioneered by Claude Code — a directory declared by a .claude-plugin/plugin.json manifest bundling slash commands, subagents, Agent Skills, hooks, MCP server configs, and LSP servers. ToolHive will let users build a plugin directory into a reproducible, content-addressable OCI artifact, push it to any OCI registry, install it (registry name, OCI reference, or git:// URL), and list/info/uninstall it — reusing the registry, OCI, groups, and storage infrastructure that already serves skills and MCP servers.

Two-layer split. The design splits cleanly into:

  • a client-agnostic distribution layer (build → OCI → push → catalog → pull → verify → inventory) — the bulk of the value, identical for every client; and
  • a per-client materialization layer (turning a verified, pulled bundle into something a specific client loads), behind a stable MaterializationAdapter seam.

v1 scope. Materialization is implemented for the two clients that consume the .claude-plugin/plugin.json family: Claude Code (in-place skills-directory install, full component set, no config mutation) and Codex (cache install + ~/.codex/config.toml mutation, reduced component set — warns on dropped commands/agents). Cursor/Copilot/Gemini adapters are future work behind the seam. Bundled MCP execution is deferred: v1 packages .mcp.json verbatim but does not run/proxy/rewrite it, records dev.toolhive.plugins.requires (resolves nothing), and reports declared MCP servers as "declared; NOT managed by ToolHive." The managed model via requires references is a follow-up RFC.

RFC: THV-0077 — under review as stacklok/toolhive-rfcs#77.

Cross-repo release gates (the bottleneck)

Every toolhive-core change is a separate Go module that must be merged → tagged → released before toolhive / toolhive-registry-server can bump go.mod and build against it. Three core release gates:

  • GATE-C1 — tag toolhive-core after shared OCI primitives (Phase 0); bump toolhive.
  • GATE-C2 — tag toolhive-core after oci/plugins (Phase 1); bump toolhive. Nothing in Phase 2 compiles before this.
  • GATE-C3 — tag toolhive-core after registry/types.Plugin (Phase 5a); bump both toolhive and toolhive-registry-server to the same tag. ✅ types.Plugin is in every core tag ≥ v0.0.28; toolhive is on v0.0.35, registry-server still needs its bump (tracked in chore(deps): update ghcr.io/stacklok/toolhive docker tag to v0.0.48 #819).

Gate ordering

P0 → GATE-C1 → P1 → GATE-C2 → P2 → P3 → P4
P5a → GATE-C3 → P5b / P5d        (P5c parallel, no core dep)

Serialized spine: P0 → C1 → P1 → C2 → P2 → P3.

Phase checklist

Gaps found in review (2026-07-29)

Items the phase issues originally missed — now folded into #5529 / #819 or listed here for tracking:

  1. PluginLookup is dead code. pluginsvc.WithPluginLookup is never wired in pkg/api/server.go (createDefaultPluginManager), so thv ai-plugin install <plain-name> always 404s. Needs a lazyPluginLookup mirroring lazySkillLookup — covered in [plugins] Phase 5b: registry provider methods + catalog routes + PluginsClient (THV-0077) #5529.
  2. types.Registry has no Plugins field. The remote provider's side-effect discovery can't see plugins; either extend core (UpstreamRegistry.Plugins — mini GATE-C4) or fetch via PluginsClient like the cached provider does for skills. Decision recorded in [plugins] Phase 5b: registry provider methods + catalog routes + PluginsClient (THV-0077) #5529.
  3. Registry-server go.mod is behind (toolhive-core v0.0.26, predates types.Plugin) — bump is a prerequisite, covered in chore(deps): update ghcr.io/stacklok/toolhive docker tag to v0.0.48 #819.
  4. No follow-up tracking for post-v1 scope (needs issues/RFCs, not blocking v1):
    • Managed bundled-MCP execution via dev.toolhive.plugins.requires — deferred to "a follow-up RFC" that doesn't exist yet.
    • Cursor/Copilot/Gemini materialization adapters behind the MaterializationAdapter seam.
    • Kubernetes operator / vMCP story for plugins — Phases 2–4 are local-mode only.

Metadata

Metadata

Assignees

Labels

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions