Implements the walking skeleton from the roadmap: agents connect to one HTTP
endpoint and Nexus aggregates upstream MCP servers behind it with namespaced
tools. Written in Go (single static binary), no external services required.
What works end-to-end (verified live + hermetic e2e tests):
- MCP protocol layer: JSON-RPC 2.0 + initialize/tools.list/tools.call
(internal/mcp), with stdio (subprocess) and Streamable HTTP client
transports, and a reusable stream transport for in-process wiring.
- Router + Dynamic Tool Registry: connects/initializes upstreams in parallel,
loads tools, namespaces them as {namespace}.{tool}, dispatches tools/call to
the owning upstream; refreshes on list_changed. A failed upstream stays
not-ready without taking down the gateway (data plane stays up).
- Gateway: single MCP endpoint (POST /mcp) that is an MCP server to agents,
plus /healthz and a minimal /metrics exposition.
- CLI (cmd/nexus): `serve`, `connect` (stdio<->HTTP bridge for local agents),
`demo-mcp` (built-in zero-dep demo server: echo/add/now), `version`.
- Config: declarative YAML with env expansion + validation.
- Store: persistence interfaces + in-memory impl (SQLite lands later).
Foundations for later phases: domain types (ARCHITECTURE §7), two-plane
split, event-driven refresh seam.
Tooling: Makefile (build/test/vet/fmt with version ldflags), config.example
.yaml. GOPATH moved to /go so it doesn't collide with the module root at /root;
.gitignore whitelist extended to track Go sources while ignoring build output.
Tests: unit (registry/namespacing) + full e2e (client -> demo server over
pipes -> router -> HTTP gateway: initialize, aggregated tools/list, tool-call
routing, unknown-tool error). go vet + gofmt clean.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
154 lines
5.7 KiB
Go
154 lines
5.7 KiB
Go
// Package domain defines the core vocabulary shared across every MCP Nexus
|
|
// module, as described in docs/ARCHITECTURE.md §7. These types are the shared
|
|
// language of both the data plane (Gateway/Router) and the control plane
|
|
// (Discovery/Registry/Installer/…).
|
|
package domain
|
|
|
|
import "time"
|
|
|
|
// Plane distinguishes the two planes of the system (ARCHITECTURE §5).
|
|
type Plane string
|
|
|
|
const (
|
|
PlaneData Plane = "data"
|
|
PlaneControl Plane = "control"
|
|
)
|
|
|
|
// InstanceState is the lifecycle state of a managed MCP server (see the Health
|
|
// Monitoring module doc for the full state machine).
|
|
type InstanceState string
|
|
|
|
const (
|
|
StateUnknown InstanceState = "unknown"
|
|
StatePending InstanceState = "pending"
|
|
StateStarting InstanceState = "starting"
|
|
StateRunning InstanceState = "running"
|
|
StateUnhealthy InstanceState = "unhealthy"
|
|
StateOffline InstanceState = "offline"
|
|
StateUpdating InstanceState = "updating"
|
|
StateRestarting InstanceState = "restarting"
|
|
StateQuarantine InstanceState = "quarantined"
|
|
)
|
|
|
|
// Health is a coarse health verdict for a resource or instance.
|
|
type Health string
|
|
|
|
const (
|
|
HealthUnknown Health = "unknown"
|
|
HealthHealthy Health = "healthy"
|
|
HealthDegraded Health = "degraded"
|
|
HealthUnhealthy Health = "unhealthy"
|
|
)
|
|
|
|
// TransportKind identifies how Nexus talks MCP to an upstream server.
|
|
type TransportKind string
|
|
|
|
const (
|
|
// TransportStdio spawns the server as a subprocess and speaks
|
|
// newline-delimited JSON-RPC over stdin/stdout.
|
|
TransportStdio TransportKind = "stdio"
|
|
// TransportHTTP connects to a server over MCP Streamable HTTP.
|
|
TransportHTTP TransportKind = "http"
|
|
)
|
|
|
|
// DiscoveredResource is a service found on the network by the Discovery Engine.
|
|
type DiscoveredResource struct {
|
|
UUID string `json:"uuid"`
|
|
Type string `json:"type"`
|
|
Version string `json:"version,omitempty"`
|
|
IP string `json:"ip,omitempty"`
|
|
Hostname string `json:"hostname,omitempty"`
|
|
Ports []int `json:"ports,omitempty"`
|
|
Capabilities []string `json:"capabilities,omitempty"`
|
|
Health Health `json:"health"`
|
|
Confidence float64 `json:"confidence"`
|
|
Source string `json:"source,omitempty"`
|
|
Attributes map[string]string `json:"attributes,omitempty"`
|
|
FirstSeen time.Time `json:"first_seen"`
|
|
LastSeen time.Time `json:"last_seen"`
|
|
}
|
|
|
|
// PackageSource identifies where an MCP package artifact comes from.
|
|
type PackageSource string
|
|
|
|
const (
|
|
SourceGitHub PackageSource = "github"
|
|
SourceOCI PackageSource = "oci"
|
|
SourceDockerHub PackageSource = "dockerhub"
|
|
SourceLocal PackageSource = "local"
|
|
)
|
|
|
|
// Package is a resolvable MCP server artifact from the Registry.
|
|
type Package struct {
|
|
Name string `json:"name"`
|
|
Description string `json:"description,omitempty"`
|
|
Source PackageSource `json:"source"`
|
|
Ref string `json:"ref"` // image ref or repo
|
|
Versions []string `json:"versions,omitempty"`
|
|
ConfigSchema string `json:"config_schema,omitempty"`
|
|
Signature string `json:"signature,omitempty"`
|
|
}
|
|
|
|
// Recipe is a declarative match+install+config rule (Smart Recipes module).
|
|
type Recipe struct {
|
|
Name string `yaml:"name" json:"name"`
|
|
Match RecipeMatch `yaml:"match" json:"match"`
|
|
Install RecipeInstall `yaml:"install" json:"install"`
|
|
Config map[string]string `yaml:"config" json:"config"`
|
|
}
|
|
|
|
// RecipeMatch is the fingerprint predicate for a recipe.
|
|
type RecipeMatch struct {
|
|
Type string `yaml:"type,omitempty" json:"type,omitempty"`
|
|
Ports []int `yaml:"ports,omitempty" json:"ports,omitempty"`
|
|
HTTPTitle []string `yaml:"http_title,omitempty" json:"http_title,omitempty"`
|
|
}
|
|
|
|
// RecipeInstall describes what to install when a recipe matches.
|
|
type RecipeInstall struct {
|
|
DockerImage string `yaml:"docker_image,omitempty" json:"docker_image,omitempty"`
|
|
Package string `yaml:"package,omitempty" json:"package,omitempty"`
|
|
}
|
|
|
|
// MCPInstance is a managed, running MCP server — the unit the reconciler drives.
|
|
type MCPInstance struct {
|
|
ID string `json:"id"`
|
|
Namespace string `json:"namespace"`
|
|
Package string `json:"package,omitempty"`
|
|
Version string `json:"version,omitempty"`
|
|
Transport TransportKind `json:"transport"`
|
|
State InstanceState `json:"state"`
|
|
Health Health `json:"health"`
|
|
BoundUUID string `json:"bound_resource_uuid,omitempty"`
|
|
ContainerRef string `json:"container_ref,omitempty"`
|
|
Config map[string]string `json:"config,omitempty"`
|
|
CreatedAt time.Time `json:"created_at"`
|
|
UpdatedAt time.Time `json:"updated_at"`
|
|
}
|
|
|
|
// Tool is an MCP tool exposed by an instance, namespaced at the Gateway as
|
|
// "{namespace}.{name}".
|
|
type Tool struct {
|
|
Namespace string `json:"namespace"`
|
|
Name string `json:"name"` // upstream (un-namespaced) name
|
|
QualifiedID string `json:"qualified_id"` // "{namespace}.{name}"
|
|
Title string `json:"title,omitempty"`
|
|
Description string `json:"description,omitempty"`
|
|
// InputSchema is the raw JSON schema object as provided by the upstream.
|
|
InputSchema map[string]any `json:"input_schema,omitempty"`
|
|
}
|
|
|
|
// AgentProfile binds an agent identity to a set of allowed roles.
|
|
type AgentProfile struct {
|
|
ID string `json:"id"`
|
|
Identity string `json:"identity"`
|
|
AllowedRoles []string `json:"allowed_roles,omitempty"`
|
|
}
|
|
|
|
// Role is an RBAC grant of namespaces/tools to principals.
|
|
type Role struct {
|
|
Name string `json:"name"`
|
|
AllowedNamespaces []string `json:"allowed_namespaces,omitempty"`
|
|
AllowedTools []string `json:"allowed_tools,omitempty"`
|
|
}
|