Skip to main content

Claudehub Monitor

Disclaimer. This software is provided AS IS, without warranty of any kind, express or implied. Use it entirely at your own risk. The author accepts no liability for data loss, corruption, hardware damage, business interruption, quota or usage consumed, or any consequential damages. It runs an HTTP server on your network and, when Claude Code credentials are present, makes small API calls with them to read your usage; you are responsible for who can reach that server and for being authorised to use the account. It is not certified for regulated, forensic, safety-critical or high-assurance use. The LICENSE file is the governing text and prevails wherever this note and the licence differ.

A desktop and LAN web dashboard that shows what Claude Code is doing in real time — across every repo and machine on your network.

claudehub-monitor is the central collector: Claude Code hooks POST lifecycle events to it over HTTP, and it surfaces them as an animated desktop card, system sounds, a scrolling history, a usage quota toast, and a live web page you can open from your phone.

  Claude Code (any repo / any machine)
        │  hook fires  →  claudehub-hooks  →  HTTP POST /event
        ▼
  claudehub-monitor  ──►  desktop flash card + toasts + sounds + history (SQLite)
        │
        └─►  web dashboard  ◄── browser on the LAN (SSE live updates)

Quick start

  1. Start the monitor (once per machine):
pip install claudehub-monitor
claudehub          # short alias
claudehub-monitor  # full name (same command)
  1. Wire hooks into each repo you use with Claude Code:
pip install claudehub-hooks
claudehub-hooks install
claudehub-hooks install --url http://192.168.1.50:7070/event   # remote monitor
  1. Open Claude Code in that repo — events flow to the monitor automatically.

Test hook connectivity:

python .claude/hooks/claude_hook.py --test

Install

Monitor (claudehub-monitor):

pip install claudehub-monitor
claudehub          # short alias
claudehub-monitor  # full name (same command)

Hooks (claudehub-hooks — separate package, stdlib only):

pip install claudehub-hooks
claudehub-hooks install

Components

Package / path Role
claudehub-monitor (claudehub/monitor.py) Monitor: HTTP server, desktop UI, web dashboard, SQLite store.
claudehub-hooks Pip installer that writes .claude/hooks/claude_hook.py + settings.json entries.
claudehub/dashboard.html Web dashboard page (served at /).
monitor_config.json Example monitor config (copy to ~/.claude/monitor_config.json).
.claude/hooks/monitor_config.json Per-repo hook transport config (written by claudehub-hooks install).
~/.claude/claude_events.db SQLite history + per-session status (auto-created).

Hook events

claudehub-hooks install registers these Claude Code hooks:

Event When it fires Monitor behavior
SessionStart Session begins Card shows “Session Started”; history entry
UserPromptSubmit User sends a message Thinking — ring animation, persistent toast
PostToolUse Tool used (matcher: Edit|Write|NotebookEdit|MultiEdit|Read) Reading (Read) or Editing (others); brief flash → back to thinking
Stop Turn completes Done — green tick, ding, clears session
StopFailure API / runtime error Error — red card, pong sound
Notification Claude needs input Waiting — persistent ask toast
PermissionRequest Allow/deny dialog shown Waiting — permission toast + orange card
PermissionGranted User approved a tool Resumes Thinking; history shows “Allowed ✓”
PermissionDenied User denied a tool Resumes Thinking; history shows “Denied ✗”
PreToolUse Tool about to run Resolves pending permission → Thinking (implicit allow for sub-tools)

Hook self-test (claude_hook.py --test) sends __test__ — shown on the card as a purple “Test” flash (not recorded in history).

Event payload (from hooks)

Hooks POST JSON to POST /event:

{
  "event": "PostToolUse",
  "datetime": "<ISO-8601 UTC>",
  "session_id": "<str>",
  "host": "<hostname>",
  "client": "<launcher>",        // CLAUDE_CODE_ENTRYPOINT, e.g. "claude-vscode"; "" if unset
  "cwd": "<working dir>",
  "files": ["<path>"],           // PostToolUse
  "prompt": "<first 500 chars>", // UserPromptSubmit
  "tool_name": "<tool>",         // PostToolUse / Permission* / PreToolUse
  "command": "<cmd>",            // Permission* / PreToolUse
  "notification_type": "<type>", // Notification
  "error_type": "<type>",        // StopFailure
  "error_message": "<msg>"       // StopFailure
}

Status model

Each surfaced status maps to colour, icon, and animation on the desktop:

Status Trigger (hook event) Colour Desktop animation
Thinking UserPromptSubmit blue rotating arc ring
Reading PostToolUse (Read) teal sweeping scan bar
Editing PostToolUse (Edit/Write/…) blue rippling bars
Waiting PermissionRequest / Notification amber persistent ask toast
Allowed PermissionGranted / PreToolUse (after approve) green ding; back to thinking
Denied PermissionDenied red thud; back to thinking
Done Stop green ✓ tick + ding
Error StopFailure red pong

Read/Edit briefly flash, then the card returns to Thinking until the turn ends (Stop) — mirroring how Claude actually works. The desktop card shows the status name; the history table and web feed show the matching icon.

Usage toast

When Claude Code OAuth credentials exist (~/.claude/.credentials.json), a fixed top-right toast shows subscription quota:

  • 5-hour session and 7-day weekly utilization (%)
  • Reset times for each window
  • Refreshed every 5 minutes (one minimal Haiku probe call per refresh)
  • VS Code (W): N active session(s) (T total) and Claude Desktop: Running, both refreshed every 2 seconds (independent of the 5-minute quota refresh above, since this changes far more often than quota). Each row is hidden entirely when there is nothing to report.

If credentials are missing, the toast is skipped.

Where those numbers come from

The three session numbers are deliberately from two different sources, because neither alone is correct:

Number Source Why
(W) windows Local window scan Counts open VS Code windows. Not a process count: VS Code spawns ~24 Code.exe processes for 2 windows (one per renderer, extension host, GPU process…), so windows are enumerated by title instead.
N active Hooks Sessions that fired a hook event within the last minute — i.e. genuinely mid-turn right now. Only hooks know this.
T total Local process scan One running Claude Code CLI process is one session. Works with no hooks installed at all, which is the point: hooks only exist in repos where claudehub-hooks install was run, so a hook-derived total silently omits every session in every other repo.

Claude Desktop: Running is also a local process scan. The native Claude Desktop app talks to Claude over MCP, not through Claude Code's hook system, so it can never report to this monitor over HTTP — the only way to know it is open is to look at the OS process list on the machine the monitor itself runs on. That also means it reports only on local Claude Desktop, never on a remote machine sending hook events here.

Both apps ship an executable named claude.exe, so they are told apart by executable path (the VS Code extension's bundled native-binary path versus the app's WindowsApps install), never by process name. All of these scans are Windows-only and fail closed: if a scan cannot run, the row is hidden rather than showing a misleading 0 or not running.

Run it

claudehub
claudehub-monitor
python -m claudehub

The app starts hidden in the system tray by default (the HTTP server and web dashboard run regardless). Double-click the tray icon to open the window; "Minimize to Tray" or closing the window hides it again; the tray menu's Quit exits. Only one instance runs per machine.

Web dashboard (view on the go)

With the monitor running, open from any device on the LAN:

http://<monitor-ip>:7070/

It shows a live grid of active sessions, the last prompt, and a recent-activity feed — pushed over Server-Sent Events (auto-reconnecting), with a JSON snapshot for the initial load.

Group by repo: check "Group by repo" in the header to re-arrange the session grid into one section per repository (derived from each hook's reported cwd), rather than one flat grid. Useful once several repos are reporting to the same monitor at once — a session with no cwd on record groups under "(unknown)" rather than being dropped.

History & search: the collapsed "History & search" panel below the recent- activity feed queries the full event history (not just the last few events), filterable by host, repo, event type, and date range. It only fetches once opened, so a normal dashboard view pays nothing for it.

Subagent count: when a session launches one or more subagents (Claude Code's Task tool), its card shows a small "+N agents running" badge for as long as any are still in flight. This is a count, not a breakdown — see "Subagent visibility" below for why the hook payload doesn't support more.

Open TCP 7070 on the monitor machine's firewall:

New-NetFirewallRule -DisplayName "Claude Monitor" -Direction Inbound `
  -Protocol TCP -LocalPort 7070 -Action Allow

HTTP API

Method / path Purpose
POST /event Ingest one hook event (from claudehub-hooks dispatcher).
GET / The web dashboard.
GET /api/status Active sessions snapshot + last prompt (JSON).
GET /api/events Recent events feed (JSON).
GET /api/history Paginated event history with filters — see below (JSON).
GET /api/usage Last 5h / 7d quota reading plus who and where it came from (JSON). See below.
GET /events/stream Live event stream (SSE).
GET /health {"status":"ok","service":"claudehub","version","host","instance_id","account"}.

GET /api/usage (for phones and widgets)

Returns the cached result of the usage toast's 5-minute probe, so polling it costs nothing. ?refresh=1 asks for a fresh probe; that is a real 1-token call against your quota, so at most one is started per minute and extra requests just get the cached reading.

{
  "service": "claudehub", "version": "0.4.0", "host": "DESKTOP-1",
  "instance_id": "454a1bd4...", "account": {"email": "you@example.com", "plan": "max", "org": ""},
  "fetched_at": 1792844418.9, "overall_status": "allowed", "error": "", "refreshing": false,
  "session": {"status": "allowed", "utilization": 0.20, "reset": 1792857000},
  "weekly":  {"status": "allowed", "utilization": 0.67, "reset": 1792904400}
}

utilization is a fraction (0.67 = 67%), reset is a Unix time in seconds. Before the first probe finishes, or on a PC with no Claude Code login, it answers 503 with an error string. If a later probe fails, the last good reading is kept and error says why.

instance_id is a UUID made once per PC (~/.claude/claudehub_instance_id), so a client that finds several monitors can tell them apart, and account lets it group PCs logged in to the same Claude account. No token is ever sent.

Discovery. pip install claudehub-monitor[discovery] adds zeroconf, and the monitor then advertises itself on the LAN as _claudehub._tcp (mDNS). A client can also find it by probing port 7070 for a /health answer with "service": "claudehub".

Locking it down. Set monitor.api_token in monitor_config.json to make /api/usage require an X-ClaudeHub-Token: <token> header. /health stays open for discovery but then leaves out the account. The rest of the dashboard API is unchanged and, like before, open to anything that can reach port 7070.

GET /api/history query parameters

Queries the full events table directly, not the small capped in-memory feed /api/events reads from.

Param Meaning
host Exact match on reporting hostname.
repo Exact match on the derived repo label (see per-repo grouping above).
event Exact match on event name, e.g. PostToolUse.
session_id Exact match on session id.
since / until ISO-8601 UTC bounds on when the event was received.
limit Rows per page, default 50, capped at 500.
offset Rows to skip, for paging.

Response: {"events": [...], "total": <int>, "limit": <int>, "offset": <int>}.

Note: since/until filter on a column (received_at_iso) added in 0.2.0. Events recorded by an older version have it unset and are excluded from any date-filtered query rather than matching incorrectly — the older received_at column is local time-of-day only (no date), so it cannot support a range filter at all.

Configuration

Monitor (claudehub-monitor)

Copy monitor_config.json to ~/.claude/monitor_config.json, or set CLAUDE_MONITOR_CONFIG to point at a file:

{
  "transport": { "http": { "enabled": true, "url": "http://127.0.0.1:7070/event", "timeout_s": 2.0 } },
  "monitor":   { "http_host": "0.0.0.0", "http_port": 7070, "history_max": 50, "start_hidden": true,
                 "api_token": "" },
  "logging":   { "level": "INFO", "file": "", "max_bytes": 1048576, "backup_count": 5 },
  "sounds":    { "enabled": true },
  "notifications": { "enabled": false },
  "usage":     { "refresh_minutes": 5, "manual_refresh_min_seconds": 60 }
}

usage sets how often the quota probe runs. Every probe is a real 1-token API call on your subscription, so this is the one setting that costs something:

  • refresh_minutes (default 5): how often the toast and /api/usage get a new reading. Clamped to 1 minute to 24 hours. 0 turns the timer off: one reading at start, then only when a client asks with ?refresh=1. Env var: CLAUDE_MONITOR_USAGE_MINUTES.
  • manual_refresh_min_seconds (default 60): the shortest gap between probes a client can trigger with ?refresh=1. Clamped to 10 seconds to 1 hour.

Clients polling /api/usage without ?refresh=1 never cause a probe, however often they poll. /api/usage reports both values as refresh_seconds and manual_refresh_min_seconds.

notifications.enabled (default false) turns on Windows action-center notifications for Waiting and Error states, in addition to — not instead of — the existing sound and in-app toast. Uses the tray icon already created for the system-tray feature, so there's no extra dependency; it is a no-op if there's no system tray available.

Monitor env vars (override the file): CLAUDE_MONITOR_CONFIG, CLAUDE_MONITOR_PORT, CLAUDE_MONITOR_HOST, CLAUDE_MONITOR_SOUNDS, CLAUDE_MONITOR_NOTIFY, CLAUDE_MONITOR_LOG_LEVEL, CLAUDE_MONITOR_LOG_FILE.

Logs and the SQLite database default to ~/.claude/.

Hooks (claudehub-hooks)

Per-repo transport config lives at .claude/hooks/monitor_config.json (created by claudehub-hooks install). Point hooks at a monitor on another machine:

{
  "transport": {
    "http": { "url": "http://192.168.1.50:7070/event" }
  }
}

Hook env vars (override the file):

Variable Default Meaning
CLAUDE_HUB_URL http://127.0.0.1:7070/event Monitor endpoint
CLAUDE_HUB_TIMEOUT 2.0 HTTP timeout (seconds)
CLAUDE_HUB_HTTP_ENABLED true Set to false to silence all hooks
CLAUDE_HUB_CONFIG — Path to a monitor_config.json override
CLAUDE_HOOK_LOG_LEVEL INFO Dispatcher log level
CLAUDE_HOOK_LOG_FILE ~/.claude/claude_hook.log Dispatcher log path

Subagent visibility

The card and dashboard show a count of subagents currently in flight under a session ("+2 agents running") — incremented on the PreToolUse for Claude Code's Task tool, decremented on the matching PostToolUse.

This is deliberately a count, not a per-subagent breakdown. A subagent's own inner tool calls (its own Reads, Edits, etc.) do not reach this monitor's hooks at all — only the outer call, the parent session invoking the Task tool, produces a PreToolUse/PostToolUse pair. Building true nested visibility (each subagent's own activity, individually) is not something the current Claude Code hook contract supports from the monitor side; it would need a change on that side, not here.

Multi-instance & reopen recovery

The monitor tracks status per session in SQLite, so:

  • Several Claude instances (local or remote) can report at once — the card shows the latest, the history/web distinguish by host + session.
  • If you close and reopen the monitor mid-turn, it restores the in-progress state from session_status; a 2-second watchdog idles a session that goes silent.

Requirements

  • Python 3.11+ (monitor); Python 3.10+ (hooks)
  • claudehub-monitor: PySide6 and anthropic (installed automatically)
  • claudehub-hooks: stdlib only
  • Windows for sounds (winsound); silently disabled elsewhere.

Metadata

Release files for claudehub-monitor 0.4.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for claudehub-monitor 0.4.0
File Size Uploaded Provenance
claudehub_monitor-0.4.0.tar.gz 56.2 kB Without provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for claudehub-monitor 0.4.0
File Interpreter ABI Platform
claudehub_monitor-0.4.0-py3-none-any.whl Python 3 none any Details

Total release size: 113.4 kB

Release files / claudehub_monitor-0.4.0.tar.gz

Download URL claudehub_monitor-0.4.0.tar.gz
Size 56.2 kB
Tags Source
SHA-256 checksum
How to use checksums
7d3b3860e02968588c311bdc71bc00344feaf25c4183f97f715da5b90d66f895
BLAKE2b-256 checksum
How to use checksums
40e24ec342d4e833021d6ea29eb9f5a91b83f896d34f4a32cae0c4c515b6e5b0
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.5

Release files / claudehub_monitor-0.4.0-py3-none-any.whl

Download URL claudehub_monitor-0.4.0-py3-none-any.whl
Size 57.2 kB
Tags Python 3
SHA-256 checksum
How to use checksums
b7d9ffa929ed7db1198a76be2f30b1b85b9c1aadf38f8885e9806f1b257c8d80
BLAKE2b-256 checksum
How to use checksums
052f1648e0e2c9c29ad59c57e605c48e4c2c2119911e7142fe185440e3823753
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.5

Security for claudehub-monitor 0.4.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page