Real-time token economics observer for agentic AI systems — exposed over MCP for Claude Code, Cursor, and any MCP-compatible client.
Project description
ai-tokenpulse
Real-time token economics and context-budget observer for agentic AI systems. Watches every Claude API call your agents make and tells you — live, in your editor — how much you've spent, how full the context window is, and whether the agent is wasting tokens.
Exposed over MCP so it works natively in Claude Code, Cursor, and any MCP-compatible client.
Most agent failures aren't reasoning failures — they're budget failures dressed up as reasoning failures. Context windows silently saturate; long autonomous loops quietly burn credits; cost overruns surface only at billing time.
ai-tokenpulsemakes the resource layer observable while it's happening.
Install
pip install ai-tokenpulse
This gives you three console scripts: ai-tokenpulsed (the daemon), ai-tokenpulse (CLI), and ai-tokenpulse-mcp (the MCP stdio adapter).
How it works
Claude Code / Cursor Custom SDK agents
│ │ │
stdio│ https│ │ unix socket
(MCP)│ (proxy)│ │ (ingest)
▼ ▼ ▼
┌──────────┐ ┌──────────────────────────────────────┐
│ MCP │ │ ai-tokenpulsed (daemon) │
│ adapter │◄──┤ proxy │ ingest API │ query API │
└──────────┘ │ │ │
│ ▼ │
│ pricing · anomaly · efficiency │
│ ▼ │
│ SQLite (WAL) + .ai-tokenpulse/ JSON exports │
└──────────────────────────────────────┘
A single long-running daemon (ai-tokenpulsed) owns all state in a local SQLite database. Three thin adapters connect to it:
- HTTP proxy — point
ANTHROPIC_BASE_URLat it; captures every model call from Claude Code or Cursor - MCP stdio adapter — spawned by your IDE; lets you ask questions like "how saturated are we?" mid-session
- SDK wrapper (
TrackedClient) — for custom agents built directly on the Anthropic SDK
Quickstart — Claude Code or Cursor
1. Add the MCP server to your client config.
Claude Code (.mcp.json in your project, or ~/.claude/mcp.json):
{
"mcpServers": {
"ai-tokenpulse": {
"command": "ai-tokenpulse-mcp"
}
}
}
Cursor (.cursor/mcp.json or ~/.cursor/mcp.json) uses the same shape.
2. Start the daemon (leave it running):
ai-tokenpulsed --proxy-port 9000 --project my-project
3. Open a session and route your client's traffic through the proxy:
ai-tokenpulse session start "what-im-working-on"
export ANTHROPIC_BASE_URL=http://127.0.0.1:9000
claude # or open Cursor
That's it. Every API call now records a step. Inside Claude Code or Cursor, ask questions naturally — "what's our context saturation right now?" — and the agent calls get_context_saturation over MCP and answers from the live data.
When you're done:
ai-tokenpulse session end
Quickstart — Custom Anthropic SDK agents
import anthropic
from ai_tokenpulse import TrackedClient
raw = anthropic.Anthropic()
client = TrackedClient(raw, session_id="sess_abc", project="my-project")
response = client.messages.create(
model="claude-sonnet-4-6",
max_tokens=1024,
messages=[{"role": "user", "content": "..."}],
)
# step recorded automatically; response is unmodified
TrackedClient extracts usage from each response and writes a step to the daemon over its Unix socket. The caller gets back the original response object.
Pass fail="closed" to make calls hard-fail if the daemon is unreachable; the default is fail="open" so observability can't break your IDE.
CLI
ai-tokenpulse session start <name> open a new session
ai-tokenpulse session end close the current session
ai-tokenpulse session list recent sessions (active marked)
ai-tokenpulse burn [session_id] totals for current (or named) session
ai-tokenpulse health ping the daemon
# Analytics (mirror the MCP tools, scriptable from a shell)
ai-tokenpulse efficiency [session_id] composite efficiency score
ai-tokenpulse saturation [session_id] current context window % used
ai-tokenpulse models [session_id] per-model cost/token split
ai-tokenpulse anomalies [session_id] [--z-threshold N]
ai-tokenpulse compaction [session_id] should we compact now?
ai-tokenpulse exhaustion [session_id] [--target-cost USD]
ai-tokenpulse budget [session_id] --target-cost USD
ai-tokenpulse compare <session_a> <session_b>
MCP tools (13)
Session state
get_session_burn(session_id?)— totals: tokens, cost, step count, unpriced countget_context_saturation(session_id?)— % of context window used (latest step)get_step_cost(step_id)— itemized per-step costlist_sessions(limit?)— recent sessions with active/closed flag
Attribution
get_tool_costs(session_id?)— per-tool token + cost breakdownget_model_breakdown(session_id?)— Sonnet vs Opus vs Haiku split
Tagging
tag_session(tag, session_id?)— set or update human-readable tag
Analysis (Phase 4)
detect_burn_anomalies(z_threshold=2.0)— steps with token totals deviating >Nσ from session meansuggest_compaction_point()— fires when utilization > 70% AND projection > 90% in 3 stepsproject_exhaustion(target_cost_usd?)—steps_to_context_limit,steps_to_cost_limit, growth rateestimate_remaining_budget(target_cost_usd)— steps left at avg costcompare_sessions(session_a, session_b)— diff with warning when tags differget_efficiency_score()— composite 0–1 score: burn-rate stability, output yield, decision density
session_id defaults to .ai-tokenpulse/current-session (the session you opened with the CLI), so you rarely have to pass it explicitly.
Data model
Each recorded event is a step — one model API call:
{
"schema_version": 2,
"session_id": "sess_2026_04_27_abc",
"session_tag": "refactor-auth", # optional, for like-for-like compare_sessions
"step_id": "step_042",
"timestamp": "2026-04-27T14:32:11Z",
"model": "claude-opus-4-7",
"input_tokens": 12450,
"output_tokens": 380,
"cached_tokens": 8200, # cache_read_input_tokens, priced at 0.1x
"tool_use_count": 2, # tool_use blocks emitted in the response
"context_window_size": 200000,
"context_used": 45230,
"cost_usd": 0.0871 # null when model isn't in price table
}
Persistence is .ai-tokenpulse/ai-tokenpulse.db (SQLite, WAL mode) with optional JSON exports per session for human inspection.
Privacy
The proxy extracts only usage, model, id, and tool_use block counts from responses. Request bodies, response message content, tool call inputs, and tool call outputs are never logged, persisted, or written to .ai-tokenpulse. They live in proxy memory only for the duration of forwarding.
Configuration
| Flag / env var | Default | Purpose |
|---|---|---|
ai-tokenpulsed --proxy-port <N> |
(off) | Run the HTTP proxy on this port |
ai-tokenpulsed --proxy-host <H> |
127.0.0.1 |
Proxy bind address (loopback only by default; see security note below) |
ai-tokenpulsed --allow-public-bind |
(off) | Acknowledge non-loopback bind. Refused without this flag |
ai-tokenpulsed --upstream <URL> |
https://api.anthropic.com |
Where the proxy forwards |
ai-tokenpulsed --project <name> / AI_TOKENPULSE_PROJECT |
default |
Project label and socket name suffix — see below |
ai-tokenpulsed --db <path> |
./.ai-tokenpulse/ai-tokenpulse.db |
SQLite location |
AI_TOKENPULSE_SESSION |
(unset) | Override active session for the SDK wrapper |
X-Ai-Tokenpulse-Session (HTTP header) |
(unset) | Per-request session override for the proxy |
Multiple projects
The daemon's Unix socket is named after the project: ~/.ai-tokenpulse/<project>.sock. To run more than one daemon on the same machine — e.g., one for each open project — give each a distinct --project (or shell-level AI_TOKENPULSE_PROJECT) and they will coexist. Clients (CLI, MCP adapter, SDK wrapper, proxy) resolve the right socket from the same env var, so a per-project shell environment is enough.
Security: loopback bind enforced
The proxy forwards Authorization: Bearer … and x-api-key headers transparently to upstream. Binding it to a non-loopback interface would let anyone reachable on that interface issue API calls billed to your account. The daemon refuses non-loopback --proxy-host values unless you pass --allow-public-bind to acknowledge the risk. Don't pass that flag on shared or untrusted networks.
What "fail-open" really means
The proxy is fail-open with respect to the daemon: if a step fails to record (SQLite hiccup, schema error, etc.), the user's API call is unaffected. The proxy is not fail-open with respect to itself — if ai-tokenpulsed crashes or isn't running, ANTHROPIC_BASE_URL=http://localhost:9000 points at nothing and every IDE call will fail until the daemon is back up. There is no automatic fallback to api.anthropic.com. Run the daemon under a supervisor (launchd, systemd, tmux, etc.) if uptime matters.
Limitations
- Linux and macOS only. The daemon uses Unix domain sockets for the daemon ↔ adapter ↔ SDK-wrapper IPC, which Windows does not support.
pip install ai-tokenpulsewill succeed on Windows (the wheel is platform-agnostic) but the daemon and CLI will fail at runtime. WSL2 works. - Anthropic only. Other LLM providers would need separate work.
- Streaming SDK calls aren't yet captured by
TrackedClient(the proxy handles streams cleanly; the SDK wrapper warns and falls through if you passstream=True). - Heuristic efficiency thresholds. The composite score is calibrated against rough defaults; you'll want to tune them with real session data. Raw component values are always exposed so you can see why, not just what.
- Local-only store. No identity, no auth, no team rollups. Per-developer benchmarks across machines require shipping
.ai-tokenpulse/exports somewhere. - Pricing data needs manual refresh. The bundled price table warns when older than 30 days. Unknown models record with
cost_usd: nullrather than a fabricated number. - Tool attribution is partial. The proxy counts
tool_useblocks per turn but doesn't yet attribute cost back to which tool. - Fail-open by default. If the daemon is down, the proxy keeps your IDE working but silently skips recording. Pass
fail="closed"toTrackedClientfor hard failure.
Development
git clone https://github.com/abhinandang90/ai-tokenpulse
cd ai-tokenpulse
pip install -e .
python -m unittest discover -s tests
Test layout:
tests/test_smoke.py— daemon + SDK wrapper end-to-endtests/test_mcp.py— MCP stdio adapter end-to-end (spawnsai-tokenpulse-mcp)tests/test_proxy.py— proxy with stub upstream (buffered + SSE streaming + pass-through)tests/test_analytics.py— pure-stat helpers and Phase 4 derived signals
License
MIT — see LICENSE.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file ai_tokenpulse-0.1.0.tar.gz.
File metadata
- Download URL: ai_tokenpulse-0.1.0.tar.gz
- Upload date:
- Size: 39.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.2
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8e03eb5e47a749e8bf6c950ae36be7b3751a869e1e0ecceb825d909e5f80a889
|
|
| MD5 |
9beb7fc97d0bce5d7a8c232094c61970
|
|
| BLAKE2b-256 |
16399fc5a01767d71109a65866421d0538d434cb7382848b626f4a4fd918ea07
|
File details
Details for the file ai_tokenpulse-0.1.0-py3-none-any.whl.
File metadata
- Download URL: ai_tokenpulse-0.1.0-py3-none-any.whl
- Upload date:
- Size: 33.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.2
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
02adf8f09c38a2b8e146b04cba6611a65990045c2223a8a927465ef752104f7a
|
|
| MD5 |
910dff82859593827307a4811b820840
|
|
| BLAKE2b-256 |
0575cfd2a05621eb77a1ab288113cb82e544a5365a2e906042f402a1fe1f45c2
|