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>
This commit is contained in:
185
docs/ARCHITECTURE.md
Normal file
185
docs/ARCHITECTURE.md
Normal file
@@ -0,0 +1,185 @@
|
||||
# MCP Nexus — Architecture
|
||||
|
||||
> Status: **Design draft (v0)**. This document defines the target architecture.
|
||||
> No production code exists yet; the [Roadmap](./ROADMAP.md) sequences delivery.
|
||||
|
||||
## 1. What MCP Nexus is
|
||||
|
||||
MCP Nexus is a **self-hosted control plane for [Model Context Protocol (MCP)](https://modelcontextprotocol.io) 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.
|
||||
|
||||
1. **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.
|
||||
2. **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.
|
||||
3. **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.
|
||||
4. **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.
|
||||
5. **Secure by default.** Least privilege, sandboxed MCP containers, secrets never exposed to agents unless policy allows, every action audited, TLS everywhere, signed packages.
|
||||
6. **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](./modules/15-smart-recipes.md)). 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](./modules/02-package-registry.md)).
|
||||
- **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](./modules/05-dynamic-tool-registry.md)).
|
||||
- **AgentProfile** — `{id, identity, allowed_roles}` → resolves to a visible tool set (see [AI Agent Profiles](./modules/14-agent-profiles.md)).
|
||||
- **Role** — RBAC grant mapping principals to allowed namespaces/tools (see [RBAC](./modules/08-rbac.md)).
|
||||
- **Secret** — an encrypted credential referenced by config templates, never returned to agents (see [Secrets](./modules/07-secrets-manager.md)).
|
||||
|
||||
## 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](./modules/19-security.md). 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
|
||||
|
||||
1. **Single binary** (default) — `nexus` runs on a host with Docker; embedded SQLite; embedded dashboard. Target: homelab.
|
||||
2. **Container** — the same binary in a container with the Docker socket mounted (or a remote Docker/K8s endpoint configured).
|
||||
3. **HA / multi-node** — multiple `nexus` replicas behind a load balancer, shared Postgres, leader-elected reconciler. Target: enterprise.
|
||||
|
||||
## 11. Module index
|
||||
|
||||
Each module has a dedicated design doc under [`docs/modules/`](./modules/). They share the template: *Purpose · Responsibilities · Interfaces · Data · Dependencies · Failure modes · Open questions · Milestone.*
|
||||
|
||||
| # | Module | Plane | Doc |
|
||||
|---|---|---|---|
|
||||
| 1 | Discovery Engine | Control | [01](./modules/01-discovery-engine.md) |
|
||||
| 2 | MCP Package Registry | Control | [02](./modules/02-package-registry.md) |
|
||||
| 3 | Auto Installer | Control | [03](./modules/03-auto-installer.md) |
|
||||
| 4 | Gateway | Data | [04](./modules/04-gateway.md) |
|
||||
| 5 | Dynamic Tool Registry | Data | [05](./modules/05-dynamic-tool-registry.md) |
|
||||
| 6 | Authentication | Data | [06](./modules/06-authentication.md) |
|
||||
| 7 | Secrets Manager | Cross-cutting | [07](./modules/07-secrets-manager.md) |
|
||||
| 8 | RBAC | Data | [08](./modules/08-rbac.md) |
|
||||
| 9 | Health Monitoring | Control | [09](./modules/09-health-monitoring.md) |
|
||||
| 10 | Update Manager | Control | [10](./modules/10-update-manager.md) |
|
||||
| 11 | Plugin System | Cross-cutting | [11](./modules/11-plugin-system.md) |
|
||||
| 12 | Web Dashboard | Data | [12](./modules/12-web-dashboard.md) |
|
||||
| 13 | Notifications | Cross-cutting | [13](./modules/13-notifications.md) |
|
||||
| 14 | AI Agent Profiles | Data | [14](./modules/14-agent-profiles.md) |
|
||||
| 15 | Smart Recipes | Control | [15](./modules/15-smart-recipes.md) |
|
||||
| 16 | Infrastructure Discovery | Control | [16](./modules/16-infrastructure-discovery.md) |
|
||||
| 17 | Service Graph | Cross-cutting | [17](./modules/17-service-graph.md) |
|
||||
| 18 | Metrics | Cross-cutting | [18](./modules/18-metrics.md) |
|
||||
| 19 | Security | Cross-cutting | [19](./modules/19-security.md) |
|
||||
| 20 | Future Vision | — | [20](./modules/20-future-vision.md) |
|
||||
|
||||
## 12. Open architectural questions
|
||||
|
||||
Tracked here until resolved; each module may add its own.
|
||||
|
||||
- **Stdio-only agents:** ship a `nexus connect` stdio↔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.
|
||||
Reference in New Issue
Block a user