Files
mcp-gateway-nexus/docs/ARCHITECTURE.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

186 lines
15 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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.