ai-usage-mcp
Read-only MCP server for querying AI usage metrics (exact tokens, cost, latency, errors, productivity) from a local SQLite database, enabling charts and analysis in Claude Code and Cursor.
README
AI Usage Dash
Dashboard of AI usage metrics at work, focused on exact (billed) tokens, collected automatically by hooks during the session — you don't run any collector, and there is no server to keep running.
Three independent layers:
- Collection (via hooks) — end-of-turn hooks write the exact usage straight to the local SQLite file (lib/db.mjs). No daemon, no HTTP.
- Storage — a single SQLite file (
metrics.db) via Node's built-innode:sqlite(no native dependency, no build step). WAL +busy_timeoutlet concurrent hook writers and the MCP reader share it safely. - Query/analysis — a read-only MCP server (stdio, spawned on demand by the client) that Claude and Cursor consume to generate charts (artifacts / canvas).
The event contract (src/types.ts) ties the three layers together. Tokens are first-class fields.
How the exact token arrives, automatically
Runs 100% local on your machine — the hooks are short-lived node processes that open
the SQLite file, write the turn's events, and exit. Nothing listens on a port.
| Client | Hook | What it does | Requires |
|---|---|---|---|
| Claude Code | Stop → hooks/claude-code-hook.mjs |
Reads transcript_path each turn, tails the transcript and extracts message.usage (exact in/out/cache) |
nothing — 100% local |
| Cursor | stop → hooks/cursor-hook.mjs |
(1) records the turn's activity right away; (2) with an admin key, pulls the exact tokens from the Admin API | CURSOR_API_KEY for exact tokens |
⚠️ Why Cursor needs an API key. Cursor's billed token count doesn't exist on the machine: the Cursor hook receives no tokens, and the local DB only has context estimates. The exact number only exists server-side (Admin API, Team/Business plan). The hook automates that pull — you still run nothing — but without the admin key you can only see activity, not the tokens.
Setup
npm install # no native build — uses Node's built-in SQLite
npm link # puts the ai-usage-* commands on your PATH
cp .env.example .env
npm link exposes every tool as a command you can call by name (ai-usage-claude-hook,
ai-usage-cursor-hook, ai-usage-mcp, ai-usage-stats, …), so nothing below hardcodes an
absolute path to this repo. Each command resolves its own location, so it works from any
directory. (Prefer not to link globally? Run them from the repo with npx ai-usage-<name>,
or fall back to node ./hooks/<file>.mjs with a path.)
There is no service to start. The hooks write to the DB directly and the MCP server is
spawned on demand by your client. The DB defaults to metrics.db at the repo root; set
IA_USAGE_DASHBOARD_DB_PATH only if you keep it elsewhere:
export IA_USAGE_DASHBOARD_DB_PATH="$HOME/somewhere/metrics.db" # optional; the commands find the repo DB by default
1. Enable the Claude Code hook
Register the hook in ~/.claude/settings.json:
{
"hooks": {
"Stop": [
{ "hooks": [{ "type": "command", "command": "ai-usage-claude-hook" }] }
],
"SubagentStop": [
{ "hooks": [{ "type": "command", "command": "ai-usage-claude-hook" }] }
]
}
}
Done — from then on, every Claude Code turn writes the exact usage on its own. The hook is silent and never blocks Claude Code; if a write ever fails it just retries next turn.
2. Enable the Cursor hook
Create ~/.cursor/hooks.json (or <project>/.cursor/hooks.json) — see the example at
hooks/cursor-hooks.example.json:
{ "version": 1, "hooks": { "stop": [{ "command": "ai-usage-cursor-hook" }] } }
For Cursor's exact tokens, also export the admin key (Cursor Dashboard → Settings → Cursor Admin API Keys):
export CURSOR_API_KEY=<cursor-admin-key>
3. Register the read-only MCP (Claude / Cursor)
claude mcp add ai-usage -- ai-usage-mcp
For Cursor, the repo already ships .cursor/mcp.json (runs npm run mcp
from the repo — no path needed).
In the client: "use the token_usage tool (period 30d, group_by model) and make a bar chart" → artifact/canvas.
Query tools (MCP)
| Tool | What it returns |
|---|---|
by_task |
AI effort per task/issue (Jira etc.): tokens, messages, tools, errors, sessions |
token_usage |
sum of exact tokens (in/out/cache) + cost, by day/model/source/user/project/task |
latency_stats |
per-turn latency: avg, p50, p95, max — by day or model |
tool_stats |
most-used tools + error rate (errors/use) + web search/fetch |
stop_reasons |
distribution of stop_reason (max_tokens truncations, refusals) |
productivity |
Cursor: code and tab accept rate, accepted/rejected lines |
query_usage |
event counts by day/user/project/tool/source |
top_tools |
most-used tools |
sessions_summary |
per-session summary with duration |
Metrics captured per event
message(Claude Code and Cursor): exact tokens,model, and inmeta:stop_reason,latency_ms(turn time),n_tools,tools,web_search/web_fetch,gitBranch.tool_use: one per tool called (feedstop_tools/tool_stats).error: one pertool_resultwith an error (denominator =tool_use→ error rate).productivity(Cursor, daily): lines added/accepted, tabs shown/accepted, applies.
Task (Jira/issue) link per session
Each AI session is linked to a task, to measure AI effort per issue. Resolution happens automatically at the start of the session, in order of precision:
.dash-task— file at the repo root with the ID (explicit override).- Git branch — a Jira-style ID in the branch name (
feature/PROJ-123-...→PROJ-123). - User prompt — an ID mentioned, or the explicit marker
#task PROJ-123(correct it any time). - If none of the above resolves precisely → the
SessionStarthook injects context instructing Claude to ask the user for the ID before starting (best-effort — aSessionStarthook can't block, so the model may skip the question). Regardless of whether it asks, the reply is captured on its own by theUserPromptSubmithook, so the guaranteed ways to set the task are.dash-task, the branch name, or#task PROJ-123.
Hooks involved (registered in ~/.claude/settings.json):
"SessionStart": [{ "hooks": [{ "type": "command", "command": "ai-usage-session-task" }] }],
"UserPromptSubmit":[{ "hooks": [{ "type": "command", "command": "ai-usage-task-capture" }] }]
The ID pattern is configurable via DASH_TASK_PATTERN (regex). The default is Jira-style
(PROJ-123). task_id becomes a first-class field on every event; query it with
by_task or token_usage group_by=task_id.
Slash command /dash_stats
Query a task's stats straight from Claude Code:
/dash_stats DEMO-100 → stats for the given task
/dash_stats → uses the ACTIVE task of the current session
Returns tokens (in/out/cache), messages, tool calls + error rate, p50/p95 latency, per-model breakdown and top tools — all for that issue.
Pieces: the ai-usage-stats command (scripts/task-stats.mjs —
resolves the task and reads the local SQLite DB directly via taskStats() in
lib/db.mjs) + the command in ~/.claude/commands/dash_stats.md. Run it as
ai-usage-stats DEMO-100 (or npm run stats -- DEMO-100 from the repo). Point it at a
non-default DB with IA_USAGE_DASHBOARD_DB_PATH. The active task is the session's most recent task state.
History backfill (optional, runs once)
The hooks capture from now on. To import ALL the existing history one single time:
npm run collect:claude # scans ~/.claude/projects/**.jsonl
CURSOR_API_KEY=<key> npm run collect:cursor
Both are idempotent (dedup by ext_id) — running them again doesn't duplicate.
Next steps
- Claude Code cost (tokens × per-model price table).
- Fixed dashboards (HTML) beyond the on-demand artifacts.
- Migrate SQLite → Postgres (swap only lib/db.mjs).
- Multi-machine collection — if the DB ever needs to live off-box, reintroduce a thin ingest endpoint in front of
insertEvents()(today it runs single-user on the machine, direct to file).
Recommended Servers
playwright-mcp
A Model Context Protocol server that enables LLMs to interact with web pages through structured accessibility snapshots without requiring vision models or screenshots.
Magic Component Platform (MCP)
An AI-powered tool that generates modern UI components from natural language descriptions, integrating with popular IDEs to streamline UI development workflow.
Audiense Insights MCP Server
Enables interaction with Audiense Insights accounts via the Model Context Protocol, facilitating the extraction and analysis of marketing insights and audience data including demographics, behavior, and influencer engagement.
VeyraX MCP
Single MCP tool to connect all your favorite tools: Gmail, Calendar and 40 more.
graphlit-mcp-server
The Model Context Protocol (MCP) Server enables integration between MCP clients and the Graphlit service. Ingest anything from Slack to Gmail to podcast feeds, in addition to web crawling, into a Graphlit project - and then retrieve relevant contents from the MCP client.
Kagi MCP Server
An MCP server that integrates Kagi search capabilities with Claude AI, enabling Claude to perform real-time web searches when answering questions that require up-to-date information.
E2B
Using MCP to run code via e2b.
Neon Database
MCP server for interacting with Neon Management API and databases
Exa Search
A Model Context Protocol (MCP) server lets AI assistants like Claude use the Exa AI Search API for web searches. This setup allows AI models to get real-time web information in a safe and controlled way.
Qdrant Server
This repository is an example of how to create a MCP server for Qdrant, a vector search engine.