Skip to content

Architecture and Contributing

Anna edited this page Jun 25, 2026 · 2 revisions

Architecture & Contributing


Hexagonal Architecture Overview

Aura Tracker GCP follows the Hexagonal Architecture pattern (Ports & Adapters). The central boundary is ports/gcp_service.go.

cmd/aura-tracker-gcp/main.go
        │
        ▼
┌───────────────────────────────────────────┐
│  internal/mcp/          (protocol layer)  │
│  - server.go                              │
│  - registry.go                            │
│  - tools/*.go                             │
│  - prompts/                               │
└───────────────┬───────────────────────────┘
                │  imports only ↓
        ports/GCPService  (interface boundary)
                │  implemented by ↓
┌───────────────▼───────────────────────────┐
│  internal/gcp/          (GCP adapter)     │
│  - *.go  (one file per domain)            │
│  - modules.go  (client dependency map)    │
└───────────────────────────────────────────┘
                │  calls ↓
        Google Cloud SDK + REST APIs

Key invariant: internal/mcp never imports internal/gcp. This is verified by:

go build ./internal/mcp/...  # compiles zero GCP SDK code

Additional layers

Layer Package Purpose
Safety decorator internal/safety/ Wraps GCPService; enforces two-step confirmation for mutations
Anonymization internal/anonymize/ Middleware that scrubs PII from every tool result
Models pkg/models/ Shared request/response structs (no GCP SDK types)
Ports ports/ GCPService interface + DLPService interface

Adding a New Tool

Follow these six steps (from CLAUDE.md):

1. Add input/output structs to pkg/models/<domain>.go

2. Add the method signature to ports/gcp_service.go

3. Implement the method on gcpAdapter in internal/gcp/<domain>.go

  • Call a.rateWait(ctx, "domain.Method") first
  • Call a.withTimeout(ctx) and defer cancel()
  • Wrap errors with wrapGCPError("domain.Method", err)

4. Create the tool definition + handler in internal/mcp/tools/<domain>.go

  • Use mcp.NewTypedToolHandler(t.handlerFunc) for automatic arg binding
  • Call handleServiceError(toolName, err) on service errors
  • Declare mcp.Required() parameters before optional ones — declaration order becomes JSON Schema order

5. Add the tool to GetTools() on the domain's *Tools struct

6. If adding a new domain, also add it to:

  • internal/gcp/modules.go (moduleClientDeps)
  • internal/mcp/registry.go (AllModules)

Error handling contract

GCP error Adapter output Handler output LLM sees
codes.PermissionDenied *PermissionDeniedError mcp.NewToolResultError(...) IsError: true + structured JSON
codes.NotFound *NotFoundError mcp.NewToolResultError(...) IsError: true + message
Any other error wrapped error return nil, err JSON-RPC -32603

Running Tests

# Always use -race
go test -race ./...

# Single package
go test -race ./internal/gcp/... -run TestAggregateBottlenecks

# Vet
go vet ./...

Smoke Testing

# Verify tools/list works
echo '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}' \
  | GCP_PROJECT_ID=my-project go run ./cmd/aura-tracker-gcp

README Hygiene

Update README.md whenever you:

  • Add, remove, or rename a tool (update the count, Tools table, architecture diagram, Project Layout, and example prompts)
  • Change environment variables, prerequisites, or architecture

Stage README changes in the same commit as the code that required them.

Clone this wiki locally