Internal Development Guidelines
Guidelines for developing the Go backend of mcpd, including core control plane, gateway, and infrastructure components.
Prerequisites
- Go 1.25+
- protoc (for gRPC)
- golangci-lint (for linting)
- wire (installed via
make tools)
Development Commands
Core Development
# Build all packages
make build
# Run tests
make test
# Or directly with go
go test ./...
# Format code
make fmt
# Lint code
make lint-check
# Auto-fix linting issues
make lint-fix
# Generate Wire dependency injection code
make wire
# Generate gRPC protobuf code
make proto
# Install development tools (wire)
make tools
Testing
Running Tests
# Run all tests
make test
go test ./...
# Run tests with verbose output
go test -v ./...
# Run tests with race detector
go test -race ./...
# Run tests with coverage
go test -cover ./...
# Run tests for specific package
go test ./internal/infra/scheduler
# Run specific test function
go test -run TestSchedulerBasic ./internal/infra/scheduler
Test Organization
- Test files use
_test.go suffix
- Table-driven tests preferred for multiple scenarios
- Use
testify/assert and testify/require for assertions
- Mock interfaces defined in test files when needed
Common Test Pattern
func TestFeature(t *testing.T) {
tests := []struct {
name string
// test fields
}{
{name: "scenario1"},
{name: "scenario2"},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// test logic
})
}
}
Code Architecture Patterns
Domain-Driven Design
The codebase follows Domain-Driven Design principles with clear separation:
Domain Layer (internal/domain/):
- Pure business logic and interfaces
- No dependencies on infrastructure
- Core types:
ServerSpec, Instance, Catalog, Profile, Transport, Scheduler, Router, Lifecycle
- Domain errors:
RouteError, ProtocolError
Application Layer (internal/app/):
- Orchestrates domain services
- Uses Google Wire for dependency injection
- Key components:
ControlPlane, Application, catalog providers, reload manager
- Wires together scheduler, lifecycle, router, and telemetry
Infrastructure Layer (internal/infra/):
- Concrete implementations of domain interfaces
- External integrations (gRPC, Prometheus, file system)
- Organized by technical concern (catalog, scheduler, lifecycle, router, transport, etc.)
Dependency Injection with Wire
The project uses Google Wire for compile-time dependency injection.
Key Files:
internal/app/wire.go: Wire build tags and initialization function
internal/app/wire_gen.go: Generated code (DO NOT EDIT)
internal/app/wire_sets.go: Provider sets
internal/app/providers.go: Provider functions
Regenerating Wire Code:
make wire
Wire Pattern Example:
// Provider function
func NewScheduler(cfg Config) Scheduler {
return &basicScheduler{cfg: cfg}
}
// Wire set
var SchedulerSet = wire.NewSet(
NewScheduler,
wire.Bind(new(domain.Scheduler), new(*basicScheduler)),
)
Code Style and Conventions
Naming Conventions
- Types: PascalCase for exported, camelCase for unexported
- Functions: camelCase for unexported, PascalCase for exported
- Interfaces: Named by capability (e.g.,
Scheduler, Router, Transport)
- Implementations: Often prefixed with strategy (e.g.,
basicScheduler, metricRouter)
- Constants: PascalCase with descriptive names
Error Handling
- Return errors, don't panic (except for truly unrecoverable situations)
- Wrap errors with context:
fmt.Errorf("context: %w", err)
- Use domain-specific error types when needed (
RouteError, ProtocolError)
- Avoid defensive programming; validate only at trust boundaries
Logging
- Use structured logging with
zap.Logger
- Log levels: Debug, Info, Warn, Error
- Include context fields:
zap.String("server", name), zap.Int("instance", id)
- Avoid logging sensitive information (credentials, tokens)
Concurrency
- Use context for cancellation and timeouts
- Protect shared state with mutexes (
sync.RWMutex for read-heavy workloads)
- Use channels for coordination between goroutines
- Always handle goroutine lifecycle (start, stop, cleanup)
Important Guidelines
- Code, identifiers, comments, and CLI commands use English
- Prioritize readability and maintainability over premature optimization
- Use
camelCase for variables/functions, PascalCase for types/structs
- Avoid defensive programming; validate only at trust boundaries
- Keep abstractions minimal; use interfaces only to isolate change points
- Evaluate risk before modifying public APIs or protocols
Common Development Tasks
Modifying Core Logic
- Update domain interfaces in
internal/domain/ if needed
- Implement changes in
internal/infra/ or internal/app/
- Update wire providers if adding new dependencies
- Regenerate wire code:
make wire
- Run tests:
make test
- Lint:
make lint-check
Adding Wails UI Features
- Implement Go service methods in
internal/ui/
- Regenerate TypeScript bindings:
make wails-bindings
- Use bindings in frontend:
import { ServiceMethod } from '@/bindings/...'
- See
frontend/CLAUDE.md for frontend-specific guidance
Adding a New Transport Type
- Define transport in
internal/domain/transport.go
- Implement
domain.Transport interface in internal/infra/transport/
- Register transport in lifecycle manager
- Add configuration schema to
internal/domain/types.go
Adding a New RPC Method
- Define protobuf message and service in
proto/mcpv/control/v1/control.proto
- Run
make proto to generate Go code
- Implement handler in
internal/app/control_plane_api.go
- Add client method in
internal/infra/gateway/gateway.go
Modifying Configuration Schema
- Update domain types in
internal/domain/types.go
- Update validation in
internal/app/validate.go
- Update example in
docs/catalog.example.yaml
- Run
go run ./cmd/mcpv validate to test
Adding Metrics
- Define metric in
internal/domain/metrics.go
- Register metric in
internal/infra/telemetry/prometheus.go
- Instrument code with metric updates
- Metrics automatically exposed at
/metrics endpoint
Troubleshooting
Wire Generation Fails
# Ensure wire is installed
make tools
# Check for circular dependencies in provider functions
# Review internal/app/wire_sets.go for issues
Tests Failing
# Run tests with verbose output
go test -v ./...
# Run specific test with race detector
go test -race -run TestName ./path/to/package
Converted and distributed by TomeVault — claim your Tome and manage your conversions.
1---2name: internal-development3description: Guidelines for Go backend development including architecture patterns, code style, testing, and common development tasks. Use when working on internal/, cmd/, or Go code. Use when this capability is needed.4---56# Internal Development Guidelines78Guidelines for developing the Go backend of mcpd, including core control plane, gateway, and infrastructure components.910## Prerequisites1112- Go 1.25+13- protoc (for gRPC)14- golangci-lint (for linting)15- wire (installed via `make tools`)1617## Development Commands1819### Core Development2021```bash22# Build all packages23make build2425# Run tests26make test27# Or directly with go28go test ./...2930# Format code31make fmt3233# Lint code34make lint-check35# Auto-fix linting issues36make lint-fix3738# Generate Wire dependency injection code39make wire4041# Generate gRPC protobuf code42make proto4344# Install development tools (wire)45make tools46```4748## Testing4950### Running Tests5152```bash53# Run all tests54make test55go test ./...5657# Run tests with verbose output58go test -v ./...5960# Run tests with race detector61go test -race ./...6263# Run tests with coverage64go test -cover ./...6566# Run tests for specific package67go test ./internal/infra/scheduler6869# Run specific test function70go test -run TestSchedulerBasic ./internal/infra/scheduler71```7273### Test Organization7475- Test files use `_test.go` suffix76- Table-driven tests preferred for multiple scenarios77- Use `testify/assert` and `testify/require` for assertions78- Mock interfaces defined in test files when needed7980### Common Test Pattern8182```go83func TestFeature(t *testing.T) {84 tests := []struct {85 name string86 // test fields87 }{88 {name: "scenario1"},89 {name: "scenario2"},90 }91 for _, tt := range tests {92 t.Run(tt.name, func(t *testing.T) {93 // test logic94 })95 }96}97```9899## Code Architecture Patterns100101### Domain-Driven Design102103The codebase follows Domain-Driven Design principles with clear separation:104105**Domain Layer** (`internal/domain/`):106- Pure business logic and interfaces107- No dependencies on infrastructure108- Core types: `ServerSpec`, `Instance`, `Catalog`, `Profile`, `Transport`, `Scheduler`, `Router`, `Lifecycle`109- Domain errors: `RouteError`, `ProtocolError`110111**Application Layer** (`internal/app/`):112- Orchestrates domain services113- Uses Google Wire for dependency injection114- Key components: `ControlPlane`, `Application`, catalog providers, reload manager115- Wires together scheduler, lifecycle, router, and telemetry116117**Infrastructure Layer** (`internal/infra/`):118- Concrete implementations of domain interfaces119- External integrations (gRPC, Prometheus, file system)120- Organized by technical concern (catalog, scheduler, lifecycle, router, transport, etc.)121122### Dependency Injection with Wire123124The project uses Google Wire for compile-time dependency injection.125126**Key Files**:127- `internal/app/wire.go`: Wire build tags and initialization function128- `internal/app/wire_gen.go`: Generated code (DO NOT EDIT)129- `internal/app/wire_sets.go`: Provider sets130- `internal/app/providers.go`: Provider functions131132**Regenerating Wire Code**:133```bash134make wire135```136137**Wire Pattern Example**:138```go139// Provider function140func NewScheduler(cfg Config) Scheduler {141 return &basicScheduler{cfg: cfg}142}143144// Wire set145var SchedulerSet = wire.NewSet(146 NewScheduler,147 wire.Bind(new(domain.Scheduler), new(*basicScheduler)),148)149```150151## Code Style and Conventions152153### Naming Conventions154155- **Types**: PascalCase for exported, camelCase for unexported156- **Functions**: camelCase for unexported, PascalCase for exported157- **Interfaces**: Named by capability (e.g., `Scheduler`, `Router`, `Transport`)158- **Implementations**: Often prefixed with strategy (e.g., `basicScheduler`, `metricRouter`)159- **Constants**: PascalCase with descriptive names160161### Error Handling162163- Return errors, don't panic (except for truly unrecoverable situations)164- Wrap errors with context: `fmt.Errorf("context: %w", err)`165- Use domain-specific error types when needed (`RouteError`, `ProtocolError`)166- Avoid defensive programming; validate only at trust boundaries167168### Logging169170- Use structured logging with `zap.Logger`171- Log levels: Debug, Info, Warn, Error172- Include context fields: `zap.String("server", name)`, `zap.Int("instance", id)`173- Avoid logging sensitive information (credentials, tokens)174175### Concurrency176177- Use context for cancellation and timeouts178- Protect shared state with mutexes (`sync.RWMutex` for read-heavy workloads)179- Use channels for coordination between goroutines180- Always handle goroutine lifecycle (start, stop, cleanup)181182## Important Guidelines183184- Code, identifiers, comments, and CLI commands use **English**185- Prioritize **readability and maintainability** over premature optimization186- Use `camelCase` for variables/functions, `PascalCase` for types/structs187- **Avoid defensive programming**; validate only at trust boundaries188- Keep abstractions minimal; use interfaces only to isolate change points189- Evaluate risk before modifying public APIs or protocols190191## Common Development Tasks192193### Modifying Core Logic1941951. Update domain interfaces in `internal/domain/` if needed1962. Implement changes in `internal/infra/` or `internal/app/`1973. Update wire providers if adding new dependencies1984. Regenerate wire code: `make wire`1995. Run tests: `make test`2006. Lint: `make lint-check`201202### Adding Wails UI Features2032041. Implement Go service methods in `internal/ui/`2052. Regenerate TypeScript bindings: `make wails-bindings`2063. Use bindings in frontend: `import { ServiceMethod } from '@/bindings/...'`2074. See `frontend/CLAUDE.md` for frontend-specific guidance208209### Adding a New Transport Type2102111. Define transport in `internal/domain/transport.go`2122. Implement `domain.Transport` interface in `internal/infra/transport/`2133. Register transport in lifecycle manager2144. Add configuration schema to `internal/domain/types.go`215216### Adding a New RPC Method2172181. Define protobuf message and service in `proto/mcpv/control/v1/control.proto`2192. Run `make proto` to generate Go code2203. Implement handler in `internal/app/control_plane_api.go`2214. Add client method in `internal/infra/gateway/gateway.go`222223### Modifying Configuration Schema2242251. Update domain types in `internal/domain/types.go`2262. Update validation in `internal/app/validate.go`2273. Update example in `docs/catalog.example.yaml`2284. Run `go run ./cmd/mcpv validate` to test229230### Adding Metrics2312321. Define metric in `internal/domain/metrics.go`2332. Register metric in `internal/infra/telemetry/prometheus.go`2343. Instrument code with metric updates2354. Metrics automatically exposed at `/metrics` endpoint236237## Troubleshooting238239### Wire Generation Fails240241```bash242# Ensure wire is installed243make tools244245# Check for circular dependencies in provider functions246# Review internal/app/wire_sets.go for issues247```248249### Tests Failing250251```bash252# Run tests with verbose output253go test -v ./...254255# Run specific test with race detector256go test -race -run TestName ./path/to/package257```258259---260> Converted and distributed by [TomeVault](https://tomevault.io/claim/wibus-wee) — claim your Tome and manage your conversions.261<!-- tomevault:4.0:skill_md:2026-04-13 -->