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:
- Validates input against JSON schema
- Delegates to business logic
- 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:
| Package | Responsibility |
|---|---|
project | Project type detection, command mapping |
process | Process lifecycle, output capture, graceful shutdown |
proxy | HTTP 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:
| Buffer | Default Size | Purpose |
|---|---|---|
| Process output | 256KB per stream | Prevent memory exhaustion |
| Traffic log | 1000 entries | Bound log storage |
| Page sessions | 100 sessions | Limit 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
- Add detection logic to
internal/project/detector.go - Add default commands to
internal/project/commands.go - Update
detecttool to handle new type
Adding New MCP Tools
- Create handler in
internal/tools/ - Define input/output structs with JSON schema tags
- Register in
cmd/devtool-mcp/main.go
Adding New Diagnostics
- Add function to injected JavaScript (
internal/proxy/injector.go) - Implement in frontend API style (return
{error: ...}on failure) - 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
- Learn about Lock-Free Design
- Understand Graceful Shutdown