RFC: Plugin lifecycle management in ToolHive#77
Conversation
Proposes adding plugin (multi-primitive bundle) lifecycle management to ToolHive, mirroring the existing skills system: build, push, install, uninstall, list, info, validate, and marketplace generation. Plugins are packaged as reproducible OCI artifacts (dev.toolhive.plugins.v1), reusing the shared toolhive-core OCI primitives, the SQLite entries table, the registry provider seam, groups, the git resolver, and the multi-client PathResolver. Centers on what makes plugins different from skills: an executable surface (hooks + bundled MCP servers), addressed via a pre-install component inventory, signature verification over the OCI Referrers API, and install-time audit events. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Dotted-edge label collided with the .-> arrow syntax; angle brackets and special characters in flowchart labels broke the lexer. Use pipe-label form and quote labels containing special characters. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Make explicit (verified against current Claude Code docs) that a skills-directory plugin activates all components — commands, agents, hooks, MCP, LSP, skills — not just skills. Add project-scope trust caveats (MCP per-server approval, LSP trust, monitors don't load, no repo-root walk-up) and note the session-only --plugin-dir/--plugin-url alternatives. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…alization Plugin bundle formats never converged (unlike skills' SKILL.md standard), so a single tree does not install across clients. Reframe the design into two layers: a fully client-agnostic distribution layer (build/OCI/push/catalog/ pull/verify/inventory) and a per-client materialization layer behind a MaterializationAdapter seam. Scope v1 materialization to the .claude-plugin family (Claude Code + Codex), with Cursor/Copilot/Gemini as future adapters. Add the "plugin formats did not converge" design constraint, the adapter interface, a multi-manifest alternative, and corrected goals/non-goals/ open-questions/forward-compatibility. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…x source) Codex does NOT auto-discover in-place: loading requires a cache install plus a [plugins.*] enabled entry in ~/.codex/config.toml, and Codex activates only a subset of components (skills/MCP/apps/hooks — no commands or subagents). Model Codex as a distinct v1 adapter: cache-install + surgical config.toml round-trip edit, SupportedComponents + install-time warning on dropped commands/agents, revert-on-uninstall. Add an adapter comparison table, a client-config-mutation security note, and update goals/summary/open-questions. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
So I went and did the cross-client research to sanity-check this (Claude Code, Cursor, Codex, Copilot, Gemini, Kiro, Continue, Goose, Zed, OpenCode, plus MCPB / Agent Skills / the OCI side). Two things came out of it: the factual claims mostly hold up, and a bigger structural problem that I think we got backwards. The structural one first, because it matters more. This RFC is written through the skills lens: distribute an artifact, place the files where the client loads them, done. That works for skills because a skill is inert Markdown with no runtime. But a plugin isn't inert. Its So if we "install" a plugin by dropping its Right now the RFC files "managed MCP servers from plugins" under Non-Goals and Open Question #4, as a forward-looking v2 thing. I think that's inverted. For ToolHive specifically, running the bundled MCP servers through the runtime is the v1 thesis. It's the only part of a plugin install that a Concrete recommendation, and to be clear up front: this is an install-time thing, not a packaging one. The OCI artifact stays exactly what the RFC already says, one opaque, verbatim, single-layer, signable tree. I'm not touching that, and per-primitive layers (Alternative 2) are still the wrong call for v1. What changes is materialization: instead of placing the whole tree verbatim into the client's load path, treat components differently by their nature.
One honest consequence: this breaks the RFC's "byte-identical to a native install" compatibility goal, but only for The genuinely hard v1 question, and the one the RFC should center on, is the gap between how plugins define servers and how we run them. Plugins use local On the materialization mechanism for the inert half: I'd drop the in-place skills-directory approach entirely. It's a discovery side-door meant for authoring ( Go native instead. And the cleanest way to do that without standing up a daemon: we're already pulling and extracting the OCI artifact, so stage the bundle to a local ToolHive-managed dir and register that dir as a local-path marketplace source for the client. No URL server to keep alive, works offline, and we still own the supply chain because we verify the digest/signature before staging. The client then installs from the local path through its own native lifecycle (cache + enable). The "that's a lot of client state to own" worry doesn't really apply... registration + And note the bundle we stage isn't the raw one. The Smaller factual fixes from the research, all verified against current docs:
The central differentiation claim (nobody packages a multi-primitive bundle as a signed OCI artifact) is solid, for what it's worth. Happy to restructure the RFC around the bundle-decomposition thesis if we agree on it. That's a bigger rewrite than the factual patches, so flagging it before I touch anything. |
ToolHive already round-trip-edits ~/.codex/config.toml to register MCP servers (pkg/client TOMLMapConfigUpdater + pkg/fileutils AtomicWriteFile/WithFileLock, with a test proving hand-maintained fields survive). Reframe the Codex adapter's config mutation from "riskiest piece" to reuse of well-trodden code; narrow Open Question #1b to the local-marketplace-vs-direct-[plugins] choice. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Per review: v1 does not run/proxy/rewrite a plugin's bundled MCP servers. The artifact carries .mcp.json verbatim but ToolHive ignores it; the intended managed model references first-class ToolHive servers via the `requires` mechanism rather than executing servers bundled in the plugin. Add a dedicated "MCP servers in a plugin" section, Alternative 7 (run/repackage bundled MCP, considered+deferred), reframe Open Question #4 and Forward Compatibility, update Goals/Non-Goals/Summary/Security/mitigations, and add a deferred-work note. Managed MCP via references = follow-up RFC. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Summary
Proposes adding plugin lifecycle management to ToolHive, mirroring the existing skills system (RFC-0030). A plugin is the cross-vendor "bundle of primitives" unit (slash commands, subagents, Agent Skills, hooks, MCP server configs, LSP servers) declared by
.claude-plugin/plugin.json.ToolHive will let users build a plugin directory into a reproducible OCI artifact, push it to any OCI registry, install it (from registry name / OCI ref /
git://), and list/info/uninstall it — reusing the registry, OCI, groups, and SQLiteentriesinfrastructure that already serves skills. As a bridge to native client tooling, it can also generate amarketplace.json.Why
entry_typediscriminator,x/dev.toolhive/<type>registry namespace, shared OCI primitives) that anticipates exactly this extension.What's genuinely new vs. skills
settings.jsonmutation. The RFC recommends the in-place skills-directory-plugin mechanism for v1 (pure filesystem, no settings mutation), with marketplace-cache deferred as a per-clientPathResolverstrategy. (Open Question PortTHV-0597#1)thv plugin info,--require-signaturevia cosign + the OCI Referrers API, and install-time audit events.Notes for reviewers
THV-XXXX-...per the RFC convention; will rename to match this PR number.toolhive(primary),toolhive-core(newoci/plugins+ shared-primitive refactor), andtoolhive-registry-server(plugin catalog).THV-0597#1 (install target) and PortTHV-1566#4 (running bundled MCP servers throughthvwith isolation as the v2 headline).🤖 Generated with Claude Code