- name
- claude-code-agent-monitor
- description
- Real-time monitoring dashboard for Claude Code agent activity with SQLite, Express, React, WebSocket, and native desktop apps
- triggers
- ["set up monitoring for claude code agents","track agent sessions and tool usage","create a dashboard for claude code activity","monitor agent performance in real time","configure claude code session tracking","visualize agent tool invocations","integrate claude code monitoring hooks","deploy agent monitoring dashboard"]
# claude-code-agent-monitor
> Skill by [ara.so](https://ara.so) — Claude Code Skills collection.
## Overview
**Claude Code Agent Monitor** is a full-stack monitoring platform that tracks Claude Code agent sessions, tool usage, subagent orchestration, and performance metrics in real-time. It uses a hook-based architecture where Claude Code fires events (tool use, session stop) that are captured by a Node.js handler, stored in SQLite, and broadcast via WebSocket to a React dashboard.
**Stack**: Node.js + Express + better-sqlite3 (WAL mode) + WebSocket (RFC 6455) + React 18 + TypeScript + Vite + TailwindCSS + Electron (desktop apps).
**Key capabilities**:
- Real-time session tracking with live agent cards
- Tool usage analytics (bar charts, heatmaps, D3.js visualizations)
- Subagent orchestration flow diagrams (Mermaid)
- Kanban board for agent status
- Browser notifications (Web Push API + VAPID)
- RESTful API + Swagger/OpenAPI docs
- MCP server for dashboard introspection
- VS Code extension + macOS/Windows native apps
- i18n support (English, Chinese, Vietnamese, Korean, Spanish)
- Prometheus metrics export + Grafana dashboards
- Kubernetes, Docker, Terraform deployment recipes
## Installation
### Prerequisites
- **Node.js** ≥ 20.x
- **Python** ≥ 3.6 (for hook handler script)
- **Claude Code** installed and configured
### Quick Start
```bash
# Clone the repository
git clone https://github.com/hoangsonww/Claude-Code-Agent-Monitor.git
cd Claude-Code-Agent-Monitor
# Install dependencies
npm install
# Build the React frontend
npm run build
# Start the server (production mode)
npm start
# Server runs on http://localhost:3000
```
For development with hot reload:
```bash
# Terminal 1: Start backend server
npm run dev
# Terminal 2: Start Vite dev server for React frontend
npm run dev:client
# Frontend runs on http://localhost:5173
```
### Hook Handler Setup
The Python hook handler captures Claude Code events and forwards them to the dashboard server.
1. **Copy the hook script** to your Claude Code hooks directory:
```bash
# macOS/Linux
cp hook-handler/hook_handler.py ~/.claude-code/hooks/
# Windows
copy hook-handler\hook_handler.py %USERPROFILE%\.claude-code\hooks\
```
2. **Configure the hook endpoint** in `hook_handler.py`:
```python
# hook_handler.py
DASHBOARD_URL = os.getenv("DASHBOARD_URL", "http://localhost:3000")
```
3. **Enable hooks in Claude Code config** (`~/.claude-code/config.json`):
```json
{
"hooks": {
"enabled": true,
"onToolUse": "~/.claude-code/hooks/hook_handler.py",
"onSessionStop": "~/.claude-code/hooks/hook_handler.py"
}
}
```
4. **Test the hook**:
```bash
python3 hook_handler.py --test
# Should POST to http://localhost:3000/api/events
```
## Configuration
### Environment Variables
Create a `.env` file in the project root:
```bash
# Server
PORT=3000
HOST=0.0.0.0
NODE_ENV=production
# Database
DB_PATH=./data/agent-monitor.db
# WAL mode is enabled by default for better concurrency
# WebSocket
WS_PORT=3001
WS_PATH=/ws
# VAPID keys for push notifications (generate with npm run generate-vapid)
VAPID_PUBLIC_KEY=your_public_key_here
VAPID_PRIVATE_KEY=your_private_key_here
VAPID_SUBJECT=mailto:your-email@example.com
# CORS (comma-separated origins)
CORS_ORIGINS=http://localhost:5173,http://localhost:3000
# Retention policy (days)
DATA_RETENTION_DAYS=30
# Prometheus metrics
METRICS_ENABLED=true
METRICS_PORT=9090
```
### Server Configuration
Edit `server/config.js` for advanced settings:
```javascript
module.exports = {
server: {
port: process.env.PORT || 3000,
host: process.env.HOST || '0.0.0.0',
},
database: {
path: process.env.DB_PATH || './data/agent-monitor.db',
walMode: true, // Write-Ahead Logging for concurrent reads/writes
busyTimeout: 5000,
},
websocket: {
port: process.env.WS_PORT || 3001,
path: process.env.WS_PATH || '/ws',
heartbeatInterval: 30000, // ping clients every 30s
},
retention: {
enabled: true,
intervalHours: 24,
retentionDays: parseInt(process.env.DATA_RETENTION_DAYS, 10) || 30,
},
cors: {
origins: process.env.CORS_ORIGINS?.split(',') || ['http://localhost:5173'],
},
};
```
## API Reference
### REST Endpoints
The server exposes a RESTful API documented via Swagger at `http://localhost:3000/api-docs`.
#### POST `/api/events`
**Receive hook events** from Claude Code.
```bash
curl -X POST http://localhost:3000/api/events \
-H "Content-Type: application/json" \
-d '{
"type": "tool_use",
"sessionId": "sess_abc123",
"agentId": "agent_main",
"toolName": "file_read",
"timestamp": "2026-08-02T22:01:04Z",
"metadata": {
"path": "/src/app.ts",
"duration_ms": 150
}
}'
```
#### GET `/api/sessions`
**List all sessions** with pagination.
```bash
curl http://localhost:3000/api/sessions?page=1&limit=20
```
Response:
```json
{
"sessions": [
{
"id": "sess_abc123",
"agentId": "agent_main",
"startTime": "2026-08-02T20:00:00Z",
"endTime": null,
"status": "active",
"toolCount": 47,
"subagentCount": 3
}
],
"total": 150,
"page": 1,
"limit": 20
}
```
#### GET `/api/sessions/:id`
**Get session details** including tool usage and subagents.
```bash
curl http://localhost:3000/api/sessions/sess_abc123
```
#### GET `/api/agents`
**List all agents** with activity summary.
```bash
curl http://localhost:3000/api/agents
```
#### GET `/api/tools/stats`
**Tool usage statistics** (counts, durations, success rates).
```bash
curl http://localhost:3000/api/tools/stats?period=7d
```
#### GET `/api/health`
**Health check** with database status, WebSocket connection count, and system metrics.
```bash
curl http://localhost:3000/api/health
```
Response:
```json
{
"status": "healthy",
"timestamp": "2026-08-02T22:01:04Z",
"database": {
"connected": true,
"walMode": true,
"rowCount": 15234
},
"websocket": {
"clients": 3
},
"uptime": 864213
}
```
#### DELETE `/api/sessions/:id`
**Delete a session** and all associated events.
```bash
curl -X DELETE http://localhost:3000/api/sessions/sess_abc123
```
### WebSocket Events
Connect to `ws://localhost:3001/ws` to receive real-time updates.
**Client → Server**:
```json
{
"type": "subscribe",
"channels": ["sessions", "tools", "agents"]
}
```
**Server → Client**:
```json
{
"type": "session_start",
"data": {
"sessionId": "sess_abc123",
"agentId": "agent_main",
"timestamp": "2026-08-02T22:01:04Z"
}
}
```
Event types: `session_start`, `session_stop`, `tool_use`, `subagent_spawn`, `agent_status_change`.
## Hook Events
The hook handler sends events in the following format:
### Tool Use Event
```json
{
"type": "tool_use",
"sessionId": "sess_abc123",
"agentId": "agent_main",
"toolName": "file_edit",
"timestamp": "2026-08-02T22:01:04Z",
"metadata": {
"path": "/src/components/Dashboard.tsx",
"linesChanged": 15,
"duration_ms": 230,
"success": true
}
}
```
### Session Stop Event
```json
{
"type": "session_stop",
"sessionId": "sess_abc123",
"agentId": "agent_main",
"timestamp": "2026-08-02T23:00:00Z",
"metadata": {
"duration_seconds": 3600,
"toolsUsed": 47,
"subagentsSpawned": 3,
"exitReason": "user_stop"
}
}
```
### Subagent Spawn Event
```json
{
"type": "subagent_spawn",
"sessionId": "sess_abc123",
"parentAgentId": "agent_main",
"subagentId": "agent_test_runner",
"timestamp": "2026-08-02T22:15:00Z",
"metadata": {
"purpose": "run_vitest_suite",
"context": {
"testFile": "src/components/Dashboard.test.tsx"
}
}
}
```
## Code Examples
### React Component: Live Agent Card
```typescript
// src/components/AgentCard.tsx
import React, { useEffect, useState } from 'react';
import { Activity, Clock, Zap } from 'lucide-react';
interface Agent {
id: string;
status: 'active' | 'idle' | 'stopped';
sessionId: string;
toolCount: number;
lastActivity: string;
}
export const AgentCard: React.FC<{ agentId: string }> = ({ agentId }) => {
const [agent, setAgent] = useState<Agent | null>(null);
useEffect(() => {
// Fetch initial agent data
fetch(`/api/agents/${agentId}`)
.then(res => res.json())
.then(setAgent);
// Subscribe to WebSocket updates
const ws = new WebSocket('ws://localhost:3001/ws');
ws.onopen = () => {
ws.send(JSON.stringify({
type: 'subscribe',
channels: ['agents'],
filter: { agentId }
}));
};
ws.onmessage = (event) => {
const update = JSON.parse(event.data);
if (update.type === 'agent_status_change' && update.data.agentId === agentId) {
setAgent(prev => prev ? { ...prev, ...update.data } : null);
}
};
return () => ws.close();
}, [agentId]);
if (!agent) return <div className="animate-pulse">Loading...</div>;
const statusColors = {
active: 'bg-green-500',
idle: 'bg-yellow-500',
stopped: 'bg-gray-500'
};
return (
<div className="bg-gray-800 rounded-lg p-4 border border-gray-700">
<div className="flex items-center justify-between mb-3">
<h3 className="text-lg font-semibold text-white">{agent.id}</h3>
View on GitHub