Skip to main content

Architecture

devtool-mcp follows a clean three-layer architecture designed for performance, reliability, and extensibility.

System Overview

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

MCP Protocol
(stdio)


┌─────────────────────────────────────────────────────────────┐
│ devtool-mcp │
│ ┌─────────────────────────────────────────────────────────┐│
│ │ MCP Tools Layer ││
│ │ detect │ run │ proc │ proxy │ proxylog │ currentpage ││
│ └─────────────────────────────────────────────────────────┘│
│ ┌─────────────────────────────────────────────────────────┐│
│ │ Business Logic Layer ││
│ │ ProjectDetector │ ProcessManager │ ProxyManager ││
│ └─────────────────────────────────────────────────────────┘│
│ ┌─────────────────────────────────────────────────────────┐│
│ │ Infrastructure Layer ││
│ │ RingBuffer │ TrafficLogger │ PageTracker ││
│ └─────────────────────────────────────────────────────────┘│
└─────────────────────────────────────────────────────────────┘

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

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)
}

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

devtool-mcp 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──→ devtool-mcp

└──→ 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 │──────────────────────┐
│ (port 8080)│ │
└─────┬───────┘ │
│ ▼
│ (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/devtool-mcp/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

Currently hardcoded in cmd/devtool-mcp/main.go:

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

Future: KDL-based configuration file support.

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