Real-time monitoring and visualization for Claude Code agents through comprehensive hook event tracking. You can watch the full breakdown here.
This system provides complete observability into Claude Code agent behavior by capturing, storing, and visualizing Claude Code Hook events in real-time. It enables monitoring of multiple concurrent agents with session tracking, event filtering, and live updates.
Claude Agents → Hook Scripts → HTTP POST → Bun Server → SQLite → WebSocket → Vue Client
Before getting started, ensure you have the following installed:
- Claude Code - Anthropic's official CLI for Claude
- Astral uv - Fast Python package manager (required for hook scripts)
- Bun, npm, or yarn - For running the server and client
- Anthropic API Key - Set as
ANTHROPIC_API_KEYenvironment variable - OpenAI API Key (optional) - For multi-model support with just-prompt MCP tool
- ElevenLabs API Key (optional) - For audio features
To setup observability in your repo,we need to copy the .claude directory to your project root.
To integrate the observability hooks into your projects:
-
Copy the entire
.claudedirectory to your project root:cp -R .claude /path/to/your/project/
-
Update the
settings.jsonconfiguration:Open
.claude/settings.jsonin your project and modify thesource-appparameter to identify your project:{ "hooks": { "PreToolUse": [{ "matcher": ".*", "hooks": [ { "type": "command", "command": "uv run .claude/hooks/pre_tool_use.py" }, { "type": "command", "command": "uv run .claude/hooks/send_event.py --source-app YOUR_PROJECT_NAME --event-type PreToolUse" } ] }], // ... (mirror the above for other event types) } }Replace
YOUR_PROJECT_NAMEwith a unique identifier for your project (e.g.,my-api-server,react-app, etc.). -
Ensure the observability server is running:
# From the observability project directory (this codebase) ./scripts/start-system.sh
Now your project will send events to the observability system whenever Claude Code performs actions.
You can quickly view how this works by running this repositories .claude setup.
# 1. Start both server and client
./scripts/start-system.sh
# 2. Open http://localhost:5173 in your browser
# 3. Open Claude Code and run the following command:
Run git ls-files to understand the codebase.
# 4. Watch events stream in the client
# 5. Copy the .claude folder to other projects you want to emit events from.
cp -R .claude <directory of your codebase you want to emit events from>claude-code-hooks-multi-agent-observability/
│
├── apps/ # Application components
│ ├── server/ # Bun TypeScript server
│ │ ├── src/
│ │ │ ├── index.ts # Main server with HTTP/WebSocket endpoints
│ │ │ ├── db.ts # SQLite database management & migrations
│ │ │ └── types.ts # TypeScript interfaces
│ │ ├── package.json
│ │ └── events.db # SQLite database (gitignored)
│ │
│ └── client/ # Vue 3 TypeScript client
│ ├── src/
│ │ ├── App.vue # Main app with theme & WebSocket management
│ │ ├── components/
│ │ │ ├── EventTimeline.vue # Event list with auto-scroll
│ │ │ ├── EventRow.vue # Individual event display
│ │ │ ├── FilterPanel.vue # Multi-select filters
│ │ │ ├── ChatTranscriptModal.vue # Chat history viewer
│ │ │ ├── StickScrollButton.vue # Scroll control
│ │ │ └── LivePulseChart.vue # Real-time activity chart
│ │ ├── composables/
│ │ │ ├── useWebSocket.ts # WebSocket connection logic
│ │ │ ├── useEventColors.ts # Color assignment system
│ │ │ ├── useChartData.ts # Chart data aggregation
│ │ │ └── useEventEmojis.ts # Event type emoji mapping
│ │ ├── utils/
│ │ │ └── chartRenderer.ts # Canvas chart rendering
│ │ └── types.ts # TypeScript interfaces
│ ├── .env.sample # Environment configuration template
│ └── package.json
│
├── .claude/ # Claude Code integration
│ ├── hooks/ # Hook scripts (Python with uv)
│ │ ├── send_event.py # Universal event sender
│ │ ├── pre_tool_use.py # Tool validation & blocking
│ │ ├── post_tool_use.py # Result logging
│ │ ├── notification.py # User interaction events
│ │ ├── stop.py # Session completion
│ │ └── subagent_stop.py # Subagent completion
│ │
│ └── settings.json # Hook configuration
│
├── scripts/ # Utility scripts
│ ├── start-system.sh # Launch server & client
│ ├── reset-system.sh # Stop all processes
│ └── test-system.sh # System validation
│
└── logs/ # Application logs (gitignored)
If you want to master claude code hooks watch this video
The hook system intercepts Claude Code lifecycle events:
-
send_event.py: Core script that sends event data to the observability server- Supports
--add-chatflag for including conversation history - Validates server connectivity before sending
- Handles all event types with proper error handling
- Supports
-
Event-specific hooks: Each implements validation and data extraction
pre_tool_use.py: Blocks dangerous commands, validates tool usagepost_tool_use.py: Captures execution results and outputsnotification.py: Tracks user interaction pointsstop.py: Records session completion with optional chat historysubagent_stop.py: Monitors subagent task completion
Bun-powered TypeScript server with real-time capabilities:
- Database: SQLite with WAL mode for concurrent access
- Endpoints:
POST /events- Receive events from agentsGET /events/recent- Paginated event retrieval with filteringGET /events/filter-options- Available filter valuesWS /stream- Real-time event broadcasting
- Features:
- Automatic schema migrations
- Event validation
- WebSocket broadcast to all clients
- Chat transcript storage
Vue 3 application with real-time visualization:
-
Visual Design:
- Dual-color system: App colors (left border) + Session colors (second border)
- Gradient indicators for visual distinction
- Dark/light theme support
- Responsive layout with smooth animations
-
Features:
- Real-time WebSocket updates
- Multi-criteria filtering (app, session, event type)
- Live pulse chart with session-colored bars and event type indicators
- Time range selection (1m, 3m, 5m) with appropriate data aggregation
- Chat transcript viewer with syntax highlighting
- Auto-scroll with manual override
- Event limiting (configurable via
VITE_MAX_EVENTS_TO_DISPLAY)
-
Live Pulse Chart:
- Canvas-based real-time visualization
- Session-specific colors for each bar
- Event type emojis displayed on bars
- Smooth animations and glow effects
- Responsive to filter changes
- Event Generation: Claude Code executes an action (tool use, notification, etc.)
- Hook Activation: Corresponding hook script runs based on
settings.jsonconfiguration - Data Collection: Hook script gathers context (tool name, inputs, outputs, session ID)
- Transmission:
send_event.pysends JSON payload to server via HTTP POST - Server Processing:
- Validates event structure
- Stores in SQLite with timestamp
- Broadcasts to WebSocket clients
- Client Update: Vue app receives event and updates timeline in real-time
| Event Type | Emoji | Purpose | Color Coding |
|---|---|---|---|
| PreToolUse | 🔧 | Before tool execution | Session-based |
| PostToolUse | ✅ | After tool completion | Session-based |
| Notification | 🔔 | User interactions | Session-based |
| Stop | 🛑 | Response completion | Session-based |
| SubagentStop | 👥 | Subagent finished | Session-based |
| PreCompact | 📦 | Context compaction | Session-based |
-
Copy the event sender:
cp .claude/hooks/send_event.py YOUR_PROJECT/.claude/hooks/
-
Add to your
.claude/settings.json:{ "hooks": { "PreToolUse": [{ "matcher": ".*", "hooks": [{ "type": "command", "command": "uv run .claude/hooks/send_event.py --source-app YOUR_APP --event-type PreToolUse" }] }] } }
Already integrated! Hooks run both validation and observability:
{
"type": "command",
"command": "uv run .claude/hooks/pre_tool_use.py"
},
{
"type": "command",
"command": "uv run .claude/hooks/send_event.py --source-app cc-hooks-observability --event-type PreToolUse"
}# System validation
./scripts/test-system.sh
# Manual event test
curl -X POST http://localhost:4000/events \
-H "Content-Type: application/json" \
-d '{
"source_app": "test",
"session_id": "test-123",
"hook_event_type": "PreToolUse",
"payload": {"tool_name": "Bash", "tool_input": {"command": "ls"}}
}'Copy .env.sample to .env in the project root and fill in your API keys:
Application Root (.env file):
ANTHROPIC_API_KEY– Anthropic Claude API key (required)ENGINEER_NAME– Your name (for logging/identification)GEMINI_API_KEY– Google Gemini API key (optional)OPENAI_API_KEY– OpenAI API key (optional)ELEVEN_API_KEY– ElevenLabs API key (optional)
Client (.env file in apps/client/.env):
VITE_MAX_EVENTS_TO_DISPLAY=100– Maximum events to show (removes oldest when exceeded)
- Server:
4000(HTTP/WebSocket) - Client:
5173(Vite dev server)
- Blocks dangerous commands (
rm -rf, etc.) - Prevents access to sensitive files (
.env, private keys) - Validates all inputs before execution
- No external dependencies for core functionality
- Server: Bun, TypeScript, SQLite
- Client: Vue 3, TypeScript, Vite, Tailwind CSS
- Hooks: Python 3.8+, Astral uv, TTS (ElevenLabs or OpenAI), LLMs (Claude or OpenAI)
- Communication: HTTP REST, WebSocket
If your hook scripts aren't executing properly, it might be due to relative paths in your .claude/settings.json. Claude Code documentation recommends using absolute paths for command scripts.
Solution: Use the custom Claude Code slash command to automatically convert all relative paths to absolute paths:
# In Claude Code, simply run:
/convert_paths_absoluteThis command will:
- Find all relative paths in your hook command scripts
- Convert them to absolute paths based on your current working directory
- Create a backup of your original settings.json
- Show you exactly what changes were made
This ensures your hooks work correctly regardless of where Claude Code is executed from.
And prepare for Agentic Engineering
Learn to code with AI with foundational Principles of AI Coding
Follow the IndyDevDan youtube channel for more AI coding tips and tricks.

