# MCP Package Registry > Module 02 · Plane: Control · Roadmap phase: 2 > Part of [MCP Nexus architecture](../ARCHITECTURE.md). ## Purpose The Package Registry is the catalog that answers one question for the rest of the control plane: *"Given a service type, which MCP server should I install, from where, at what version, and configured how?"* It maintains a searchable, locally cached index of `Package` objects (Architecture §7) synced from one or more remote indexes plus operator-authored local packages. It is the source of truth for artifact provenance (source, digest, signature) and for the `config_schema` that [Recipes](15-smart-recipes.md) bind to at install time. The Registry does not run anything. It resolves and describes. The [Auto Installer](03-auto-installer.md) consumes its resolutions; a `Package` never becomes an `MCPInstance` inside this module. ## Responsibilities - Maintain the local `Package` catalog in the **Registry** store (Architecture §8), co-resident with Recipes. - **Sync** from multiple remote indexes (a Nexus index is a signed JSON/HTTP manifest listing packages) and merge them with local packages, with a deterministic precedence order on collision. - Provide **resolution**: `service type → recommended Package` (e.g. Home Assistant → `homeassistant-mcp`). - Provide **version selection**: given a `Package` and a constraint, return a concrete version **pinned by digest**. - Verify **signatures** on packages and index manifests before a package is eligible for install (tenet #5, Architecture §9). - Expose the `config_schema` and config **template reference** each package carries, so Recipes can render config against it. - Full-text and faceted **search** over name, service type, source, and tags for the Dashboard and API. ## Non-goals - Pulling images or creating containers — that is the [Installer](03-auto-installer.md) via the Runtime. - Deciding *whether* to install (matching a `DiscoveredResource` to a package) — that is [Smart Recipes](15-smart-recipes.md) and the reconciler. - Storing secrets or rendered config — templates are declarative; values come from [Secrets](07-secrets-manager.md) at install time. - Being an OCI registry itself. Nexus reads from GitHub / OCI / Docker Hub / self-hosted repos; it does not host artifacts. ## Interfaces ```go // Source of a package artifact (mirrors Package.source in Architecture §7). type Source string const ( SourceGitHub Source = "github" SourceOCI Source = "oci" SourceDockerHub Source = "dockerhub" SourceLocal Source = "local" ) // A concrete, install-ready version pinned by digest. type PackageVersion struct { Version string // semver-ish tag, e.g. "1.4.2" Ref string // "ghcr.io/project/homeassistant-mcp:1.4.2" Digest string // "sha256:..." — the install pin Signature Signature } type Package struct { Name string // "homeassistant-mcp" ServiceType string // "home-assistant" — resolution key Source Source Versions []PackageVersion ConfigSchema json.RawMessage // JSON Schema for config ConfigTemplateRef string // e.g. "homeassistant.yaml" Signature Signature } type Registry interface { // Resolution: service type -> recommended package. Resolve(ctx context.Context, serviceType string) (Package, error) // Version selection against a constraint ("", "latest", "~1.4", "1.4.2"). SelectVersion(ctx context.Context, pkg string, constraint string) (PackageVersion, error) Get(ctx context.Context, name string) (Package, error) Search(ctx context.Context, q Query) ([]Package, error) } // Everything-is-a-plugin (tenet #3): remote index providers are swappable. type IndexProvider interface { Name() string Fetch(ctx context.Context) ([]Package, IndexMeta, error) // signed manifest } type SyncManager interface { Sync(ctx context.Context) (SyncReport, error) // merge all providers + local AddIndex(url string, opts IndexOpts) error } ``` HTTP/API surface (control-plane API, behind Auth): - `GET /api/v1/packages?service_type=&source=&q=` — search/list. - `GET /api/v1/packages/{name}` — full package incl. versions + schema. - `POST /api/v1/packages` — register/upsert a **local** package. - `POST /api/v1/registry/sync` — trigger a sync of all configured indexes. - `GET /api/v1/registry/indexes` — configured remote indexes + last sync state. ## Data - **Reads/writes:** `Package` objects in the **Registry** store (Architecture §8), alongside `Recipe`s (which reference packages by name + `ConfigTemplateRef`). - **Module-local persistence:** `registry_index` rows (index URL, public key, last-synced digest, ETag/cursor), `package_version` rows (digest, ref, signature status), and a cached copy of each fetched index manifest for offline/deterministic resolution. - Config templates referenced by `ConfigTemplateRef` are stored as opaque blobs in the Registry store and rendered later by the Installer/Recipe engine. Resolution flow: a `DiscoveredResource.type` (Architecture §7) is mapped by a Recipe to a `serviceType`; `Resolve` returns the recommended `Package`; `SelectVersion` pins it by digest. Example entry: ``` service_type: home-assistant name: homeassistant-mcp source: oci ref: ghcr.io/project/homeassistant-mcp versions: [1.4.2 @ sha256:…, 1.4.1 @ sha256:…] config_template_ref: homeassistant.yaml ``` ## Dependencies - [Smart Recipes](15-smart-recipes.md) — consumes packages + config templates. - [Auto Installer](03-auto-installer.md) — consumes resolved, digest-pinned versions. - [Secrets Manager](07-secrets-manager.md) — config templates declare secret refs filled at install time, not here. - [Security](19-security.md) — signature/trust model for indexes and packages. - [Plugin System](11-plugin-system.md) — `IndexProvider` is a plugin point. - [Update Manager](10-update-manager.md) — watches index updates as one update source. ## Failure modes & handling - **Remote index unreachable:** sync is best-effort; last good cached manifest continues to serve `Resolve`/`SelectVersion`. Sync failures are surfaced via [Notifications](13-notifications.md), never block the control loop. - **Signature verification fails:** the offending package/version is marked `untrusted` and excluded from resolution; it is never returned as installable. - **Index collision (same package from two indexes):** deterministic precedence (local > pinned-trusted index > community index); the shadowed entry is logged. - **No package for a service type:** `Resolve` returns a typed `ErrNoPackage`; the reconciler leaves the resource un-installed and low-confidence handling applies (tenet #6). - **Missing/invalid `config_schema`:** package is listed but flagged `unschematized`; Recipes referencing it fail validation early, not at runtime. ## Security notes - Every remote index manifest and every package is **signature-verified** before eligibility (tenet #5, Architecture §9); trust anchors (public keys) are configured per index. - Versions are always **pinned by digest**; tags are advisory only. The digest is what the Installer pulls. - The Registry stores no secrets and renders no secret values; templates carry only *references*. - Mutating API calls (register local package, add index, sync) are audited (Architecture §9) and gated by [RBAC](08-rbac.md). ## Open questions - Community index governance and the trust/signing model for shared packages (mirrors Architecture §12 "Recipe distribution"). - Do we support multiple recommended packages per service type with a ranking, or strictly one canonical recommendation? - Should `SelectVersion` honor per-package update policy hints, or is that owned entirely by the [Update Manager](10-update-manager.md)? - Config-template format: single YAML template vs. a small templating dialect shared with Recipes. ## Milestone Phase 2 — *Discover → install*. Registry v1 ships with local + GitHub/OCI package indexes, digest-pinned versions, config schemas, and a starter package set so the [Installer](03-auto-installer.md) can resolve (e.g.) Postgres → `postgres-mcp` during the Phase 2 exit-criteria loop.