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>
15 KiB
MCP Nexus — Architecture
Status: Design draft (v0). This document defines the target architecture. No production code exists yet; the Roadmap sequences delivery.
1. What MCP Nexus is
MCP Nexus is a self-hosted control plane for Model Context Protocol (MCP) servers. It is the single source of truth for every MCP server on a network.
AI agents connect to one endpoint. Behind it, Nexus discovers infrastructure, installs the right MCP servers, configures them, secures them, keeps them healthy and up to date, and routes agent tool calls to the correct backend.
The mental model: "Kubernetes for MCP." Nexus is a reconciler that continuously drives observed state (what MCP servers are actually running) toward desired state (what should be running, given discovered services + recipes + policy).
2. Design tenets
These are load-bearing. Every module doc must be consistent with them.
- One endpoint, many agents. Agents never learn about individual MCP servers. They speak MCP to the Gateway; the Gateway is an MCP server to them and an MCP client to everything upstream.
- Reconcile, don't script. State changes flow through a control loop (desired vs observed), not imperative one-shot commands. This is what makes discovery→install→heal→update robust.
- Everything is a plugin. Discovery methods, fingerprinters, package providers, installers, notifiers, and auth backends are all interfaces with swappable implementations. The core knows only the interfaces.
- Single static binary first. Nexus ships as one Go binary with embedded storage and embedded dashboard assets. External Postgres/Redis are optional scale-out choices, never requirements.
- Secure by default. Least privilege, sandboxed MCP containers, secrets never exposed to agents unless policy allows, every action audited, TLS everywhere, signed packages.
- Confidence, not certainty. Discovery/fingerprinting is probabilistic. Every discovered resource carries a confidence score; automated actions above a threshold, human-in-the-loop below it.
3. Technology choices
| Concern | Choice | Rationale |
|---|---|---|
| Control-plane language | Go | Single static binary, first-class Docker/K8s client libs, strong concurrency for discovery + routing, trivial to self-host. |
| MCP transport (agent ↔ Nexus) | Streamable HTTP (+ stdio shim) | The current MCP HTTP transport; stdio shim lets local agents (Claude Desktop, Cursor) attach via a thin stdio→HTTP bridge. |
| MCP transport (Nexus ↔ upstream) | stdio and Streamable HTTP | Upstream MCP servers vary; the client layer abstracts transport. |
| Wire protocol | JSON-RPC 2.0 | MCP is JSON-RPC 2.0. |
| Embedded state | SQLite (via modernc.org/sqlite, cgo-free) |
Single-binary friendly; keeps registry, inventory, audit, config. |
| Optional scale-out state | Postgres | For HA / multi-node deployments. |
| Container runtime | Docker API (containerd/K8s adapters later) | Installer targets Docker first; runtime is an interface. |
| Dashboard | React (embedded via embed.FS) |
Served by the same binary; no separate deploy. |
| Metrics | Prometheus exposition | /metrics; Grafana dashboards shipped as JSON. |
| Config | Declarative YAML + API | Desired state is data, matching tenet #2. |
4. High-level component map
AI Agents (Claude, Cursor, VSCode, Goose, OpenWebUI, …)
│ MCP (Streamable HTTP / stdio bridge)
▼
┌───────────────────────────────────────────────────────────────────────┐
│ MCP NEXUS CONTROL PLANE │
│ │
│ ┌─────────────┐ ┌──────────────┐ ┌───────────────────────────┐ │
│ │ API Gateway │──▶│ Auth / RBAC │──▶│ MCP Router + Aggregator │ │
│ │ (agent edge)│ │ (policy) │ │ (namespaced tool routing) │ │
│ └─────────────┘ └──────────────┘ └────────────┬──────────────┘ │
│ │ │ │
│ ▼ ▼ │
│ ┌─────────────┐ ┌───────────────────┐ │
│ │ Dashboard │ │ Upstream MCP client│ │
│ │ (React) │ │ pool (per server) │ │
│ └─────────────┘ └─────────┬─────────┘ │
│ │ │
│ ══════════════════ CONTROL LOOP (reconciler) ══════╪═════════════════ │
│ │ │
│ ┌───────────┐ ┌──────────┐ ┌───────────┐ ┌──────┴─────┐ │
│ │ Discovery │─▶│ Registry │─▶│ Installer │─▶│ Runtime │ │
│ │ Engine │ │ + Recipes│ │ │ │ (Docker…) │ │
│ └───────────┘ └──────────┘ └───────────┘ └────────────┘ │
│ │ │ │
│ ▼ ▼ │
│ ┌───────────┐ ┌──────────┐ ┌─────────┐ ┌────────┐ ┌──────────┐ │
│ │ Inventory │ │ Health │ │ Updater │ │Secrets │ │ Audit / │ │
│ │ (state) │ │ Monitor │ │ │ │ Vault │ │ Metrics │ │
│ └───────────┘ └──────────┘ └─────────┘ └────────┘ └──────────┘ │
└───────────────────────────────────────────────────────────────────────┘
│
Docker · K8s · LXC · VMs · Bare metal
│
Home Assistant · UniFi · Proxmox · Postgres · Ollama · GitHub · Grafana · …
5. The two planes
Nexus has a clean split that the module docs inherit:
Data plane (hot path, per request)
Agent → API Gateway → Auth/RBAC → MCP Router → upstream MCP client → MCP server → real service
Latency-sensitive. Must stay up even while the control plane reconciles. Concerns: connection pooling, tool namespacing, request scoping to the agent's RBAC-visible tool set, streaming responses, rate limiting, audit.
Control plane (cold path, continuous)
Discovery → Registry/Recipes → Installer → Runtime → Inventory, with Health, Updater, Secrets, and Notifications reacting to inventory state.
Eventually consistent. Runs as background reconcilers. A failure here degrades management (no new installs) but must not take down the data plane.
6. The reconciliation loop
The heart of the system (tenet #2). One loop, many controllers, all edge-triggered off an event bus plus periodic resync:
observe Discovery emits DiscoveredResource events → Inventory
decide Reconciler diffs desired (Recipes ⨯ Inventory ⨯ Policy) vs observed
→ produces a plan of actions
act Installer / Updater / Health execute actions via Runtime
record Inventory + Audit updated; Router refreshes upstream set
notify Notifications fire on meaningful transitions
Desired state = for each discovered service with confidence ≥ threshold, the recipe-matched MCP server should be installed, configured, running, healthy, and current. The reconciler is idempotent: re-running with the same inputs is a no-op.
7. Core domain objects
These types are shared vocabulary across every module.
- DiscoveredResource —
{uuid, type, version, ip, hostname, ports, capabilities, health, confidence, source, first_seen, last_seen}. Output of Discovery. - Recipe — declarative match+install+config rule (see Smart Recipes). Maps a fingerprint to an MCP package + config template.
- Package — a resolvable MCP server artifact
{name, source(github|oci|dockerhub|local), image/ref, versions, config_schema, signature}(see Registry). - MCPInstance — a managed running MCP server
{id, package, version, config, container_ref, state, health, bound_resource_uuid}. The unit the reconciler manages. - Tool — an MCP tool exposed by an instance, namespaced as
{namespace}.{tool}at the Gateway (see Dynamic Tool Registry). - AgentProfile —
{id, identity, allowed_roles}→ resolves to a visible tool set (see AI Agent Profiles). - Role — RBAC grant mapping principals to allowed namespaces/tools (see RBAC).
- Secret — an encrypted credential referenced by config templates, never returned to agents (see Secrets).
8. Storage model
Single embedded SQLite database (Postgres-compatible schema for scale-out). Logical stores:
| Store | Holds | Notes |
|---|---|---|
| Inventory | DiscoveredResources, MCPInstances | Source of observed + desired state |
| Registry | Packages, Recipes | Syncable from remote indexes |
| Identity | Users, AgentProfiles, Roles, API keys | RBAC + auth |
| Secrets | Encrypted secrets, envelope keys | Encrypted at rest; see Secrets module |
| Audit | Append-only action log | Immutable; every mutating action |
| Config | Desired-state overrides, settings | Declarative YAML mirrored here |
9. Security architecture (summary)
Full detail in Security. Cross-cutting rules every module honors:
- Isolation: each MCP instance runs in its own sandboxed container; least-privilege, read-only rootfs where possible, no host network unless the recipe demands it.
- Secret handling: secrets are injected into MCP containers at runtime (env/file mount), never persisted in config sent to agents, never logged.
- Request scoping: the Router only ever exposes an agent the tools its resolved RBAC allows — an agent cannot call, or even see, a tool outside its role.
- Supply chain: packages are signature-verified before install; pinned by digest.
- Audit: every mutating control-plane action and every agent tool call is recorded with principal, target, and outcome.
- Transport: TLS terminated at the API Gateway; internal component calls over localhost/socket.
10. Deployment topologies
- Single binary (default) —
nexusruns on a host with Docker; embedded SQLite; embedded dashboard. Target: homelab. - Container — the same binary in a container with the Docker socket mounted (or a remote Docker/K8s endpoint configured).
- HA / multi-node — multiple
nexusreplicas behind a load balancer, shared Postgres, leader-elected reconciler. Target: enterprise.
11. Module index
Each module has a dedicated design doc under docs/modules/. They share the template: Purpose · Responsibilities · Interfaces · Data · Dependencies · Failure modes · Open questions · Milestone.
| # | Module | Plane | Doc |
|---|---|---|---|
| 1 | Discovery Engine | Control | 01 |
| 2 | MCP Package Registry | Control | 02 |
| 3 | Auto Installer | Control | 03 |
| 4 | Gateway | Data | 04 |
| 5 | Dynamic Tool Registry | Data | 05 |
| 6 | Authentication | Data | 06 |
| 7 | Secrets Manager | Cross-cutting | 07 |
| 8 | RBAC | Data | 08 |
| 9 | Health Monitoring | Control | 09 |
| 10 | Update Manager | Control | 10 |
| 11 | Plugin System | Cross-cutting | 11 |
| 12 | Web Dashboard | Data | 12 |
| 13 | Notifications | Cross-cutting | 13 |
| 14 | AI Agent Profiles | Data | 14 |
| 15 | Smart Recipes | Control | 15 |
| 16 | Infrastructure Discovery | Control | 16 |
| 17 | Service Graph | Cross-cutting | 17 |
| 18 | Metrics | Cross-cutting | 18 |
| 19 | Security | Cross-cutting | 19 |
| 20 | Future Vision | — | 20 |
12. Open architectural questions
Tracked here until resolved; each module may add its own.
- Stdio-only agents: ship a
nexus connectstdio↔HTTP bridge binary, or document per-agent proxy config? (Leaning: ship the bridge.) - Multi-tenancy depth: is a "project"/"tenant" a first-class object above roles, or is RBAC enough for v1? (Leaning: RBAC only for v1.)
- Recipe distribution: community recipe repo governance and trust model.
- K8s runtime parity: how much of the Docker installer semantics map cleanly to a K8s operator vs a separate controller.