Skip to main content

Architecture

agnt follows a five-layer architecture designed for performance, reliability, and extensibility.

System Overview

┌─────────────────────────────────────────────────────────────┐
│ AI Assistant │
│ (Claude, Cursor) │
└─────────────────────────────────────────────────────────────┘

MCP Protocol
(stdio)


┌─────────────────────────────────────────────────────────────┐
│ agnt │
│ ┌─────────────────────────────────────────────────────────┐│
│ │ MCP Tools Layer ││
│ │ detect │ run │ proc │ proxy │ proxylog │ currentpage ││
│ └─────────────────────────────────────────────────────────┘│
│ ┌─────────────────────────────────────────────────────────┐│
│ │ Daemon Layer ││
│ │ background service │ persistent state │ socket IPC ││
│ └─────────────────────────────────────────────────────────┘│
│ ┌─────────────────────────────────────────────────────────┐│
│ │ Protocol Layer ││
│ │ text-based IPC commands/responses ││
│ └─────────────────────────────────────────────────────────┘│
│ ┌─────────────────────────────────────────────────────────┐│
│ │ Business Logic Layer ││
│ │ ProjectDetector │ ProcessManager │ ProxyManager ││
│ └─────────────────────────────────────────────────────────┘│
│ ┌─────────────────────────────────────────────────────────┐│
│ │ Infrastructure Layer ││
│ │ RingBuffer │ TrafficLogger │ PageTracker ││
│ └─────────────────────────────────────────────────────────┘│
└─────────────────────────────────────────────────────────────┘

┌───────────────┼───────────────┐
│ │ │
▼ ▼ ▼
OS Processes HTTP Traffic Dev Server

The daemon is a long-running background service that outlives any single client session — processes and proxies survive client disconnects. Clients (the MCP server, agnt run) talk to it over a text-based socket protocol.

Layer Responsibilities

MCP Tools Layer

Location: internal/tools/

Handles:

  • MCP protocol communication
  • JSON schema validation
  • Input/output serialization
  • Error formatting for MCP clients

Each tool is a thin wrapper that:

  1. Validates input against JSON schema
  2. Delegates to business logic
  3. Formats response for MCP protocol
// Example: detect tool handler
func handleDetect(input DetectInput) (*mcp.CallToolResult, DetectOutput, error) {
// Delegate to ProjectDetector
info, err := detector.Detect(input.Path)

// Return MCP-formatted result
return formatResult(info, err)
}

Daemon Layer

Location: internal/daemon/

A persistent background service that outlives client sessions. It owns process and proxy lifecycles, caches state, runs the event/alert system, and serves clients over socket IPC. Its in-memory state is a cache — the OS is always the source of truth, reconciled on session connect and periodically.

Protocol Layer

Location: internal/protocol/

A text-based IPC protocol of commands and responses. Because agnt run and agnt mcp are separate processes, they communicate with the daemon through this protocol rather than in-process calls.

Business Logic Layer

Location: internal/project/, internal/process/, internal/proxy/

Contains the core functionality:

PackageResponsibility
projectProject type detection, command mapping
processProcess lifecycle, output capture, graceful shutdown
proxyHTTP proxying, traffic logging, JS injection

This layer is independent of MCP - it can be used programmatically.

Infrastructure Layer

Location: internal/process/ringbuf.go, internal/proxy/logger.go

Provides low-level utilities:

  • RingBuffer - Thread-safe circular buffer for output capture
  • TrafficLogger - Circular buffer for HTTP traffic
  • PageTracker - Groups requests by page session

Key Design Decisions

Lock-Free Concurrency

agnt uses lock-free data structures wherever possible:

// ProcessManager uses sync.Map and atomics
type ProcessManager struct {
processes sync.Map // Lock-free process registry
activeCount atomic.Int64 // Atomic counter
shuttingDown atomic.Bool // Atomic shutdown flag
}

Benefits:

  • No mutex contention under load
  • Excellent concurrent read performance
  • Simple reasoning about correctness

See Lock-Free Design for details.

Bounded Memory

All buffers have fixed sizes:

BufferDefault SizePurpose
Process output256KB per streamPrevent memory exhaustion
Traffic log1000 entriesBound log storage
Page sessions100 sessionsLimit active tracking

When buffers fill, oldest data is discarded. This ensures predictable memory usage regardless of process output or traffic volume.

Graceful Degradation

The system handles failures gracefully:

  • Process crashes: Detected by monitor goroutine, state updated
  • Proxy crashes: Auto-restart (max 5/minute)
  • WebSocket disconnect: Frontend auto-reconnects with backoff
  • JS injection failure: Page loads normally without instrumentation

Stdio Transport

MCP communication uses stdio (stdin/stdout):

AI Assistant ←──stdin/stdout──→ agnt

└──→ stderr (logs)

This is the MCP standard for local tools. All logging goes to stderr to avoid corrupting the MCP protocol stream.

Component Interactions

Process Lifecycle

run {script_name: "dev"}


ProcessManager

┌────┴────┐
│ Register │ (sync.Map)
└────┬────┘

┌────┴────┐
│ Start │ (exec.Command)
└────┬────┘

┌────┴────┐
│ Monitor │ (goroutine)
└────┬────┘


State: Running


(process completes)


State: Stopped

Proxy Traffic Flow

Browser Request


┌──────────────┐
│ Proxy │─────────────────────┐
│ (hash port, │ │
│ 10000-60000)│ │
└─────┬────────┘ │
│ ▼
│ (forward) TrafficLogger
│ (capture req/res)

┌─────────────┐
│ Dev Server │
│ (port 3000) │
└─────┬───────┘

│ (response)

┌─────────────┐
│ Injector │ (add JS to HTML)
└─────┬───────┘


Browser

│ (WebSocket)

┌─────────────┐
│ Metrics WS │ (errors, perf)
└─────────────┘

Page Session Tracking

HTML Request ───────────────────────► Create Session

Resource Request (JS, CSS, img) ────────────┤

WebSocket: Error ───────────────────────────┤

WebSocket: Performance ─────────────────────┤


Page Session
(grouped view)

Extension Points

Adding New Project Types

  1. Add detection logic to internal/project/detector.go
  2. Add default commands to internal/project/commands.go
  3. Update detect tool to handle new type

Adding New MCP Tools

  1. Create handler in internal/tools/
  2. Define input/output structs with JSON schema tags
  3. Register in cmd/agnt/main.go

Adding New Diagnostics

  1. Add function to injected JavaScript (internal/proxy/injector.go)
  2. Implement in frontend API style (return {error: ...} on failure)
  3. Document in frontend API reference

Configuration

Daemon-level defaults are hardcoded in cmd/agnt/main.go:

ManagerConfig{
DefaultTimeout: 0, // No timeout
MaxOutputBuffer: 256 * 1024, // 256KB
GracefulTimeout: 5 * time.Second, // SIGTERM grace
HealthCheckPeriod: 10 * time.Second, // Zombie detection
}

Per-project behavior — scripts, proxies, hooks, alerts — is configured through the .agnt.kdl file in your project root. See the .agnt.kdl Configuration reference for the full schema.

Error Handling

MCP tools return errors in the response, not as Go errors:

// Bad: returns Go error
return nil, output, fmt.Errorf("process not found")

// Good: returns MCP error result
return &mcp.CallToolResult{IsError: true}, output, nil

This allows the AI assistant to understand and handle errors gracefully.

Next Steps