Files
mcp-gateway-nexus/docs/modules/02-package-registry.md
drjones 8d3ffef920 docs: initial architecture and design for MCP Nexus
MCP Nexus is a self-hosted control plane for MCP servers — "Kubernetes for
MCP." Agents connect to one endpoint; Nexus discovers services, installs the
right MCP servers, aggregates them behind a namespaced router, secures access
with auth/RBAC, and heals/updates them via a reconcile loop.

This first commit is design-phase only (no runnable code yet):
- README.md            project front door + module map
- docs/ARCHITECTURE.md target design: tenets, two-plane split, reconcile
                       loop, domain types, storage, security, deployment
- docs/ROADMAP.md      phased delivery (foundations -> walking skeleton ->
                       discover+install -> secure -> operate -> extend/scale)
- docs/modules/01-20   one design doc per module, all cross-linked

Backend stack decision: Go (single static binary, embedded SQLite + embedded
React dashboard). Repo initialized in /root with a whitelist .gitignore so
only project files are tracked.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-07 04:38:27 +00:00

187 lines
8.1 KiB
Markdown

# 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.