From eaad18116a47401527e62a0d92182eb8ebb30950 Mon Sep 17 00:00:00 2001 From: Kyle Isom Date: Thu, 26 Mar 2026 11:12:52 -0700 Subject: [PATCH] P0.1: Repository and module setup Go module, Makefile with standard targets, golangci-lint v2 config, CLAUDE.md, and empty CLI/agent binaries. Build, vet, and lint all pass. Co-Authored-By: Claude Opus 4.6 (1M context) --- .gitignore | 18 ++++++++++ .golangci.yaml | 80 +++++++++++++++++++++++++++++++++++++++++++ CLAUDE.md | 58 +++++++++++++++++++++++++++++++ Makefile | 35 +++++++++++++++++++ PROGRESS_V1.md | 2 +- cmd/mcp-agent/main.go | 33 ++++++++++++++++++ cmd/mcp/main.go | 33 ++++++++++++++++++ go.mod | 10 ++++++ go.sum | 10 ++++++ 9 files changed, 278 insertions(+), 1 deletion(-) create mode 100644 .gitignore create mode 100644 .golangci.yaml create mode 100644 CLAUDE.md create mode 100644 Makefile create mode 100644 cmd/mcp-agent/main.go create mode 100644 cmd/mcp/main.go create mode 100644 go.mod create mode 100644 go.sum diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..377373a --- /dev/null +++ b/.gitignore @@ -0,0 +1,18 @@ +# Binaries +/mcp +/mcp-agent + +# Runtime data +srv/ + +# Database files +*.db +*.db-wal +*.db-shm + +# IDE +.idea/ +.vscode/ + +# OS +.DS_Store diff --git a/.golangci.yaml b/.golangci.yaml new file mode 100644 index 0000000..ff150bc --- /dev/null +++ b/.golangci.yaml @@ -0,0 +1,80 @@ +version: "2" + +run: + timeout: 5m + tests: true + +linters: + default: none + enable: + - errcheck + - govet + - ineffassign + - unused + - errorlint + - gosec + - staticcheck + - revive + + settings: + errcheck: + check-blank: false + check-type-assertions: true + + govet: + enable-all: true + disable: + - shadow + - fieldalignment + + gosec: + severity: medium + confidence: medium + excludes: + - G104 + + errorlint: + errorf: true + asserts: true + comparison: true + + revive: + rules: + - name: error-return + severity: error + - name: unexported-return + severity: error + - name: error-strings + severity: warning + - name: if-return + severity: warning + - name: increment-decrement + severity: warning + - name: var-naming + severity: warning + - name: range + severity: warning + - name: time-naming + severity: warning + - name: indent-error-flow + severity: warning + - name: early-return + severity: warning + +formatters: + enable: + - gofmt + - goimports + +issues: + max-issues-per-linter: 0 + max-same-issues: 0 + + exclusions: + paths: + - vendor + rules: + - path: "_test\\.go" + linters: + - gosec + text: "G101" diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..5aefce3 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,58 @@ +# CLAUDE.md + +## Project Overview + +MCP (Metacircular Control Plane) is the orchestrator for the Metacircular platform. It manages container lifecycle, tracks what services run where, and transfers files between the operator's workstation and managed nodes. + +## Architecture + +MCP has two components: +- **CLI** (`mcp`) — thin client on the operator's workstation. Reads local service definition files, pushes intent to agents, queries status. No database. +- **Agent** (`mcp-agent`) — smart per-node daemon. Manages containers via podman, stores the registry (SQLite), monitors for drift, alerts the operator. + +Services have one or more components (containers). Container naming: `-`. + +## Build Commands + +```bash +make all # vet → lint → test → build both binaries +make build # go build ./... +make test # go test ./... +make vet # go vet ./... +make lint # golangci-lint run ./... +make proto # regenerate gRPC code from .proto files +make proto-lint # buf lint + buf breaking +make mcp # build CLI binary +make mcp-agent # build agent binary +make clean # remove binaries +``` + +Run a single test: `go test ./internal/registry/ -run TestComponentCRUD` + +## Project Structure + +- `cmd/mcp/` — CLI entry point +- `cmd/mcp-agent/` — Agent entry point +- `internal/agent/` — Agent core (deploy, lifecycle, sync, adopt, status, files) +- `internal/runtime/` — Container runtime abstraction (podman) +- `internal/registry/` — SQLite registry (services, components, events) +- `internal/monitor/` — Monitoring subsystem (watch loop, alerting) +- `internal/servicedef/` — Service definition file parsing (TOML) +- `internal/auth/` — MCIAS integration (token validation, interceptor) +- `internal/config/` — Configuration loading (CLI + agent) +- `proto/mcp/v1/` — Proto definitions +- `gen/mcp/v1/` — Generated Go gRPC code + +## Critical Rules + +1. Agent is gRPC-only (no REST). This is a deliberate exception to the platform's REST+gRPC parity rule. +2. Container naming convention: `-` (e.g., `metacrypt-api`, `metacrypt-web`). +3. File operations are scoped to `/srv//`. Path traversal is rejected. +4. Alert commands use exec-style invocation (argv array, no shell). Never use `sh -c`. +5. The agent binds to the overlay interface only, not all interfaces. It does NOT sit behind MC-Proxy. +6. Every RPC is audit-logged at info level (method, caller, timestamp). +7. `active: true/false` in service definitions controls desired state. `mcp stop/start` update the file. + +## Module Path + +`git.wntrmute.dev/kyle/mcp` diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..b881fad --- /dev/null +++ b/Makefile @@ -0,0 +1,35 @@ +.PHONY: build test vet lint proto proto-lint clean all + +LDFLAGS := -trimpath -ldflags="-s -w -X main.version=$(shell git describe --tags --always --dirty)" + +mcp: + CGO_ENABLED=0 go build $(LDFLAGS) -o mcp ./cmd/mcp + +mcp-agent: + CGO_ENABLED=0 go build $(LDFLAGS) -o mcp-agent ./cmd/mcp-agent + +build: + go build ./... + +test: + go test ./... + +vet: + go vet ./... + +lint: + golangci-lint run ./... + +proto: + protoc --go_out=. --go_opt=module=git.wntrmute.dev/kyle/mcp \ + --go-grpc_out=. --go-grpc_opt=module=git.wntrmute.dev/kyle/mcp \ + proto/mcp/v1/*.proto + +proto-lint: + buf lint + buf breaking --against '.git#branch=master,subdir=proto' + +clean: + rm -f mcp mcp-agent + +all: vet lint test mcp mcp-agent diff --git a/PROGRESS_V1.md b/PROGRESS_V1.md index 0de1993..0a01bbe 100644 --- a/PROGRESS_V1.md +++ b/PROGRESS_V1.md @@ -2,7 +2,7 @@ ## Phase 0: Project Scaffolding -- [ ] **P0.1** Repository and module setup +- [x] **P0.1** Repository and module setup - [ ] **P0.2** Proto definitions and code generation ## Phase 1: Core Libraries diff --git a/cmd/mcp-agent/main.go b/cmd/mcp-agent/main.go new file mode 100644 index 0000000..1495d6e --- /dev/null +++ b/cmd/mcp-agent/main.go @@ -0,0 +1,33 @@ +package main + +import ( + "fmt" + "os" + + "github.com/spf13/cobra" +) + +var ( + version = "dev" + cfgPath string +) + +func main() { + root := &cobra.Command{ + Use: "mcp-agent", + Short: "Metacircular Control Plane agent", + } + root.PersistentFlags().StringVarP(&cfgPath, "config", "c", "", "config file path") + + root.AddCommand(&cobra.Command{ + Use: "version", + Short: "Print version", + Run: func(cmd *cobra.Command, args []string) { + fmt.Println(version) + }, + }) + + if err := root.Execute(); err != nil { + os.Exit(1) + } +} diff --git a/cmd/mcp/main.go b/cmd/mcp/main.go new file mode 100644 index 0000000..b6489ee --- /dev/null +++ b/cmd/mcp/main.go @@ -0,0 +1,33 @@ +package main + +import ( + "fmt" + "os" + + "github.com/spf13/cobra" +) + +var ( + version = "dev" + cfgPath string +) + +func main() { + root := &cobra.Command{ + Use: "mcp", + Short: "Metacircular Control Plane CLI", + } + root.PersistentFlags().StringVarP(&cfgPath, "config", "c", "", "config file path") + + root.AddCommand(&cobra.Command{ + Use: "version", + Short: "Print version", + Run: func(cmd *cobra.Command, args []string) { + fmt.Println(version) + }, + }) + + if err := root.Execute(); err != nil { + os.Exit(1) + } +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..09c734e --- /dev/null +++ b/go.mod @@ -0,0 +1,10 @@ +module git.wntrmute.dev/kyle/mcp + +go 1.25.7 + +require github.com/spf13/cobra v1.10.2 + +require ( + github.com/inconshreveable/mousetrap v1.1.0 // indirect + github.com/spf13/pflag v1.0.9 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..a6ee3e0 --- /dev/null +++ b/go.sum @@ -0,0 +1,10 @@ +github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= +github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= +github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU= +github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4= +github.com/spf13/pflag v1.0.9 h1:9exaQaMOCwffKiiiYk6/BndUBv+iRViNW+4lEMi0PvY= +github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=