
The problem
Running multiple Claude Code agents means Alt-Tabbing between terminals, hoping nothing goes wrong. You can't see which agent is stuck, which one is looping, or how much it's all costing. There's no coordination, no visibility, and no way to intervene without switching to the right terminal window.
Other tools solve pieces of this. There are terminal HUDs for a single agent, TUI managers with no web dashboard, and read-only event timelines. None of them put monitoring, control, cost tracking, and mobile access in one place.
What I built
A real-time command center that shows all your Claude Code agents in one browser tab. Every tool call streams in live via WebSocket. You can send instructions to any agent from the dashboard. Missions auto-create from agent activity. Real API costs are computed from Claude Code's JSONL session logs.

What it does
| Feature | Description |
|---|---|
| Live agent monitoring | Status dots (active, idle, stuck), current tool and target file, diff stats, session duration |
| Instruction injection | Send a message to any running agent, delivered over stderr on its next tool call |
| Anti-pattern alerts | STUCK, LOOP, SPIRAL, ERRORS, and MARATHON, with desktop notifications |
| Real cost tracking | Token costs read from JSONL logs: input, output, cache, broken down per model and per session |
| Mission board | Missions created from agent sessions and subagent descriptions, closed out when the session ends |
| Mobile companion | QR code login and touch-friendly tabs, so you can watch agents from your phone on the same WiFi |
| Layered security | Origin gate, path guard, WS verify, connection limits, payload size, field limits, and tool scanning |
| Expandable timeline | Click any event for the full tool input and output: diffs, commands, errors, stack traces |
Mobile companion

Architecture
Agent activity is captured through Claude Code's hooks (PreToolUse, PostToolUse, Stop). Events land in a SQLite database and get broadcast to connected dashboards over WebSocket. Instructions travel the same path backwards: queued in the database, picked up by the hook on the agent's next tool call, and injected as a stderr warning.
Tech decisions
Two dependencies
The entire server runs on just 2 runtime dependencies: better-sqlite3 for the database and ws for WebSocket. No Express, no React, no build step. The dashboard is vanilla HTML/CSS/JS served directly. This keeps the install fast and the attack surface small.
Hooks instead of a wrapper
Mission Control does not wrap or proxy Claude Code. The hook script runs on every tool call and POSTs the event to the server, which keeps it out of the way of normal operation. If the server is down, the agents carry on as if it were never there.
SQLite instead of Postgres
An embedded database eliminates external dependencies. For a local development tool monitoring a handful of agents, SQLite's single-writer model is perfectly adequate. Historical daily cost data persists in a summary table even after raw events are purged (default 90 days).
What building this taught me
1. Instruction delivery is harder than monitoring
Monitoring is the easy direction: hooks fire, events stream, the dashboard updates. Sending instructions back means working inside Claude Code's constraints, and there is no push channel to an agent. Instructions sit in a queue until the agent's hook fires on its next tool call, so an idle agent leaves the instruction waiting. Most of the work went into making that delay legible: pending versus delivered states, a toast when it lands, and documentation that says plainly what the limit is.
2. Anti-pattern detection needs careful thresholds
My first thresholds for STUCK (no events) and LOOP (identical tool calls) fired constantly on healthy agents. Thirty seconds of silence turned out to be a normal thinking pause, and counting every repeated tool call as a loop flagged perfectly legitimate repeated reads. Settling on 2 minutes for STUCK and 3 or more identical write calls for LOOP took real testing across different workloads.
3. Security for local tools still matters

Mission Control only accepts connections from localhost and private network IPs, but agents still print sensitive things: API keys, tokens, credentials. The layered defense (secret scanning, constant-time auth comparison, rate limiting on login attempts) is not over-engineering. It is there for the moment you share your screen or hand someone the mobile view.



What I would do differently
I would add a plugin system at the start. People want their own alert rules, their own dashboard panels, and hooks into Linear or Slack. Retrofitting that into the current monolith is harder than designing for it would have been.
References
- Claude Code Hooks Documentation
- sniffly, where I took the JSONL log parsing idea
- claude-hud, for the context window health gauge
- agenttop, for anti-pattern detection
Links
- GitHub: claude-mission-control