agentic-ledger

MCP serverAI & models

Local-first flight recorder for AI agents: sessions, costs, loop runs, and stuck-loop flags.

Unavailable. This server has no hosted endpoint yet, so ahel can't serve it.

Connect ahel once, and every AI you use reads what you have installed.

From the project's README

As published by shekharbhardwaj/agenticledger in README.md.

Agentic Ledger

Runtime observability for AI agents - see exactly what your agent did, why it did it, and what it cost.

Website: agentic-ledger.dev

The numbers are meant to match your provider bill. If they don't, that's a bug we want.

Works with any agent framework, any LLM provider, any model gateway. Zero code changes required. Point your agent at the proxy and everything is captured automatically.


How it works

Agentic Ledger runs as a transparent proxy between your agent and the LLM provider. It intercepts every request and response, assigns it an action_id, stores it, and returns the upstream response unmodified. Your agent never knows the proxy is there. The full picture, with diagrams and a module map for contributors, lives in ARCHITECTURE.md.

Your Agent  →  Agentic Ledger Proxy  →  OpenAI / Anthropic / LiteLLM / any LLM
                      ↓
               SQLite or Postgres
                      ↓
               Live Dashboard + API

Quick Start

Step 1 - Start the proxy

Coming from Helicone or LangSmith? The migration page does the translation in two lines. Running a context compressor like Headroom? They chain.

Two commands, zero config, no terminal held hostage:

uv tool install agentic-ledger    # or: pipx install agentic-ledger, or pip install -U agentic-ledger
agenticledger start     # runs in the background; terminal freed

A tool-managed install (uv tool / pipx) gets its own isolated environment and one unambiguous shim on PATH, so shadowing by another Python's copy becomes rare and doctor-detectable, and agenticledger upgrade always means exactly one thing. Plain pip works too; if a machine ever grows competing installs, agenticledger doctor --fix untangles them.

agenticledger start prints the dashboard URL and gives your terminal back - closing the window doesn't stop it. agenticledger status tells you it's up and healthy, agenticledger logs shows what it's doing, agenticledger stop shuts it down. Want a config file anyway? agenticledger init writes a commented one; see Configuration for what goes in it.

Or with Docker (no Python required):

docker run -p 8000:8000 \
  -e AGENTICLEDGER_UPSTREAM_URL=https://api.openai.com \  # optional: omit to route by call format
  -v $(pwd)/data:/data \
  ghcr.io/shekharbhardwaj/agentic-ledger:latest

The image is multi-arch (amd64/arm64), runs as a non-root user, and every release is signed with Sigstore and ships an SBOM. Hardening a shared deployment (TLS, auth keys, redaction, verification)? See the deployment guide.

Using Anthropic / Claude? Nothing to configure: with no upstream set, the proxy routes each call by its wire format, so Anthropic-style calls go to Anthropic and OpenAI-style calls go to OpenAI, side by side through one proxy. Setting an explicit upstream_url (a gateway like LiteLLM or OpenRouter, LM Studio, or a pinned provider) switches to the classic one-proxy-one-provider behavior, mismatch hints included.

Or with docker compose (SQLite by default - see docker-compose.yml):

AGENTICLEDGER_UPSTREAM_URL=https://api.openai.com docker compose up

With uv:

uv add agentic-ledger
AGENTICLEDGER_UPSTREAM_URL=https://api.openai.com uv run python -m agenticledger.proxy

With pip:

python -m venv venv && source venv/bin/activate
pip install -U agentic-ledger
AGENTICLEDGER_UPSTREAM_URL=https://api.openai.com ./venv/bin/python -m agenticledger.proxy

Postgres? Install the extra and set AGENTICLEDGER_DSN:

pip install "agentic-ledger[postgres]"
AGENTICLEDGER_DSN=postgresql://user:password@localhost/agenticledger

Note: the Docker image uses SQLite only. For Postgres with Docker, install via pip instead.

OpenTelemetry? Install the extra and set AGENTICLEDGER_OTEL_ENDPOINT:

pip install "agentic-ledger[otel]"
AGENTICLEDGER_OTEL_ENDPOINT=http://localhost:4318

Proxy starts on http://localhost:8000. Traces are saved to ~/.agenticledger/agenticledger.db when started with agenticledger start (one home for the background service, wherever you launched it from), to agenticledger.db in the current folder when run in the foreground (agenticledger serve / python -m agenticledger.proxy), or to /data/agenticledger.db in Docker.


Step 2 - Point your agent at the proxy

For Claude Code, BMAD, or OpenClaw, one command writes the config for you (backed up, merged, Docker-aware):

agenticledger connect claude-code    # or: bmad, openclaw

For everything else, two changes: set base_url to the proxy and add a session ID header to group calls into a run. Everything else - your API key, model, messages - stays exactly the same.

OpenAI:

from openai import OpenAI

client = OpenAI(
    base_url="http://localhost:8000/v1",  # ← proxy
    api_key="your-openai-key",
    default_headers={"x-agenticledger-session-id": "run-1"},
)

response = client.chat.completions.create(
    model="gpt-4o",
    messages=[{"role": "user", "content": "Research the top 3 AI trends in 2026"}],
)

Anthropic (no upstream config needed: /v1/messages calls route to Anthropic automatically):

import anthropic

client = anthropic.Anthropic(
    base_url="http://localhost:8000",  # ← proxy
    api_key="your-anthropic-key",
    default_headers={"x-agenticledger-session-id": "run-1"},
)

Azure OpenAI: point AzureOpenAI(azure_endpoint="http://localhost:8000") at the ledger with your resource set as the upstream; deployments are priced from the model the response names. See the Azure guide.

AWS Bedrock: install agentic-ledger[bedrock], give the ledger AWS credentials through the standard chain, and point boto3 (endpoint_url) or Claude Code (ANTHROPIC_BEDROCK_BASE_URL) at it; the ledger re-signs each call itself. Both wires are covered: InvokeModel and the modern Converse/ConverseStream APIs. See the Bedrock guide.

LiteLLM / OpenRouter / any gateway:

# Point Agentic Ledger at your gateway
AGENTICLEDGER_UPSTREAM_URL=http://localhost:4000 uv run python -m agenticledger.proxy

# Then point your agent at Agentic Ledger
client = OpenAI(base_url="http://localhost:8000/v1", ...)

Step 3 - Open the dashboard

http://localhost:8000

The web app updates live via WebSocket as calls come in. No refresh needed.

  • Loop Lens - every loop run with its observed status (Running / Flagged / Completion declared / Ended / Calls blocked), one open metric strip (recorded spend, the run ceiling with an honest accounting track, model calls), Overview / Activity / Cache views, a recorded-concern band that jumps straight to the evidence, a Block calls action that refuses a running loop's further calls at the wall (and Allow calls again to lift it; the agent being blocked cannot), per-iteration breakdowns, and plain-English explanations of every flag. Pick any two runs with to diff them side by side - cost, iterations, calls, flags, duration with signed deltas, plus a prompt drift diff showing exactly what changed in the system prompt and opening instruction between the runs.
  • Sessions - flat, scannable rows and three views: call rows (time, model, one status, latency, cost) that expand into a four-tab inspector (Response, Tools, Prompt, Raw), a Flow DAG of agent handoffs, and a Trace waterfall with real parent links from the loop engine. Rows say whose they are at a glance: team badge, red for real failures, amber for deliberate refusals, purple for replays, and a run chip linking each session to its loop.
  • Replay the whole run - the question that decides a model switch isn't "how did it handle one call?" but "would my loop have survived?" Pick a run or session, pick a destination (a local model is free), and every step re-runs with its original inputs. You get a report card, not homework: "34 / 40 moments matched", the fumbles named ("dropped the tools"), and the cost both ways. Each step is a real captured moment replayed honestly - after step one a different model would have steered a different conversation, so the ledger compares moments, not fairy tales.
  • In your pocket - agenticledger share opens an https tunnel you own (via cloudflared, no account), prints the pairing link, and draws a QR in the terminal: point your phone's camera and the dashboard is in your hand, kill switch and ceilings included. --wifi for a same-network link, --rotate to un-pair every device, or press Pair a device in the dashboard's ⚿ panel. Local machines never need a key; everyone else meets the auto-generated pairing key. The dashboard fits a phone: one pane at a time, a back button, prev/next arrows to flip between runs.
  • The cache audit - every run answers "was I paying full price for repeated text?" Received discount is exact from the provider's own cache reports; the missed amount is a labeled estimate with its method shown; every verdict carries the reason and a one-line fix, including "nothing missed, you're fine". Also at GET /api/runs/{id}/cache-audit.
  • Yours to keep - dark, light, or system appearance (a browser-local choice), and URLs that hold the investigation: deep links to runs and sessions, working Back/Forward, no credentials ever in ordinary links.
  • Named instances - agenticledger start --name demo --port 8003 runs a second ledger beside your everyday one: own state, own database, its dashboard wears an amber name chip so it can never pass for the real thing. stop, status, logs, share, and run all take --name.
  • The spend meter - a run's detail reads its money live: spent so far, burning $/h, "at this pace $Y by 8:00 AM". Give any run a cost ceiling and the proxy refuses further calls the moment spend reaches it (amber, costing nothing) until you raise or clear it; the ceiling survives restarts and guards auto-detected loops too. A webhook alert fires at 80%.
  • Names, pins, projects - call it "the overnight auth fix" instead of cc-73a26366, ★ pin what matters to the top, file work under a project and the Sessions view reads as sections: a heading per project, its sessions beneath, the unfiled pile last. A run filed under a project files its sessions with it.
  • Settings - the ⚙ shows what the proxy is actually running with: config file in effect, upstream, budgets, replay targets, each row labeled file / env / default. Read-only, secrets hidden.
  • Replay & what-if - open any call and ↻ Replay it: pick a destination (the panel lists what your local server actually has loaded), and the exact captured prompt re-executes there - same provider, the other one, or a free local model via LM Studio; tool calls, schemas, and system prompts are translated between the Anthropic and OpenAI wire formats automatically. Works even on calls your own budget blocked - the wall can say no and you can still see what would have happened, for $0. Replays tie back to their original with ↩ Open original. The what-if box answers the cheaper question first: reprice any run or session on another model with pure math, no API calls. (Configure AGENTICLEDGER_REPLAY_API_KEY and/or the per-provider AGENTICLEDGER_REPLAY_*_KEY targets.)
  • Reports - where the money goes: spend per day, model mix with latency p50/p95/p99, per-agent totals, a by-team table with each team's spend against its card's daily allowance ("who ran dry?" in one glance), and cache savings - what your prompt-cache traffic would have cost at full input rates versus what it actually cost. Errors and blocks are counted apart everywhere: red = something broke, amber = the ledger refused on purpose - a healthy wall never makes a healthy agent look sick
  • Search - full-text search across all sessions by prompt, output, agent name, or user ID

Configuration

agenticledger init writes agenticledger.toml with every option commented. Uncomment what you need - a working setup looks like this:

[proxy]
port = 8000
upstream_url = "https://api.anthropic.com"
db = "sqlite:///agenticledger.db"

[keys]
# Prefer *_file: the file's contents are the key, so no secret lives in
# this file or your shell history (chmod 600 the key file).
api_key_file = "~/.agenticledger/api.key"       # dashboard/admin access
ingest_key_file = "~/.agenticledger/ingest.key" # closes the open relay

[budgets]
daily = 25.0          # whole-ledger daily ceiling, USD
session = 5.0         # per-session ceiling

[replay]
# Free local replay via LM Studio (any key works there):
openai_url = "http://localhost:1234"
openai_key = "lm-studio"

Three rules:

  1. The file is found in this order: AGENTICLEDGER_CONFIG, then ./agenticledger.toml (the folder you start from), then ~/.agenticledger/config.toml. First match wins; the startup banner names the file in effect.
  2. Anything typed in the command beats the file. Env vars override per-setting (AGENTICLEDGER_PORT=9000 agenticledger start uses 9000 for that run without touching the file) - which is also why Docker and CI setups configured by env vars are unaffected.
  3. Changes apply on restart (agenticledger stop then start).

Every setting in the environment-variable reference below has a config-file home; an [env] section passes any other AGENTICLEDGER_* variable through verbatim.


Providers, step by step

Every provider below rides the same proxy; the only thing that changes is which base URL you point at it. Each recipe assumes the proxy is up (agenticledger start) and ends with the same check: run one call, open http://localhost:8000, and see it in Sessions.

OpenAI (and any OpenAI-compatible API)

  1. Point the client at the proxy:
    export OPENAI_BASE_URL=http://localhost:8000/v1
    
  2. Keep your OPENAI_API_KEY exactly as it was - the proxy passes your auth header through untouched.
  3. Make a call; it appears in Sessions with an O mark.

Anthropic

  1. Point the client at the proxy:
    export ANTHROPIC_BASE_URL=http://localhost:8000
    
  2. Keep your ANTHROPIC_API_KEY as it was.
  3. Make a call; it appears with an A mark. No upstream config needed - the proxy routes Anthropic-shaped calls to Anthropic by wire format.

AWS Bedrock (direct capture)

  1. Give the ledger AWS credentials of its own through the standard chain (env vars, ~/.aws profile, or an instance role) scoped to bedrock:InvokeModel and bedrock:InvokeModelWithResponseStream, then install the extra and restart:
    pip install "agentic-ledger[bedrock]"
    agenticledger stop && agenticledger start
    
  2. Check the ⚙ Settings panel: the Bedrock row should read "signing as the ledger in ".
  3. Point the client at the proxy - Claude Code:
    export CLAUDE_CODE_USE_BEDROCK=1
    export ANTHROPIC_BEDROCK_BASE_URL=http://localhost:8000
    
    boto3: boto3.client("bedrock-runtime", endpoint_url="http://localhost:8000").
  4. Make a call; it appears with an orange B mark. The ledger strips the caller's identity and re-signs with its own credentials. Full guide: docs/integrations/bedrock.md.

Azure OpenAI

  1. Set the upstream to your resource:
    agenticledger config set proxy.upstream_url https://<resource>.openai.azure.com
    agenticledger stop && agenticledger start
    
  2. Point the client's Azure endpoint at http://localhost:8000; keep your api-key header as it was.
  3. Calls are tagged azure-openai and priced by the model the RESPONSE names, so deployment aliases can't hide the real model. Full guide: docs/integrations/azure-openai.md.

Local models (LM Studio, Ollama with the OpenAI API)

  1. Set the upstream to the local server:
    agenticledger config set proxy.upstream_url http://localhost:1234
    agenticledger stop && agenticledger start
    
  2. export OPENAI_BASE_URL=http://localhost:8000/v1 in the agent.
  3. Calls appear with a purple mark and $0 cost. Full guide: docs/integrations/lm-studio.md.

Gateways (OpenRouter, LiteLLM)

  1. Set the upstream to the gateway:
    agenticledger config set proxy.upstream_url https://openrouter.ai/api
    agenticledger stop && agenticledger start
    
  2. export OPENAI_BASE_URL=http://localhost:8000/v1; keep the gateway key as it was.
  3. Gateway-prefixed model ids ("anthropic/claude-...") price correctly via substring matching. Guides: openrouter.md, litellm.md.

Framework-specific recipes (CrewAI, LangGraph, AutoGen, Vercel AI SDK, pydantic-ai, and more) live in docs/integrations/.


Coding agents - Claude Code, Ralph loops & friends

Claude Code (and most coding agents) can be pointed at the proxy with a single environment variable - no headers, no code changes:

agenticledger start
export ANTHROPIC_BASE_URL=http://localhost:8000
claude

No upstream config needed: calls route to the provider matching their wire format.

Agentic Ledger fingerprints Claude Code traffic automatically: every call is tagged framework=claude-code, and instead of one undifferentiated bucket, each Claude Code session appears under its real session UUID (the same id claude --resume shows), with prompt-cache reads/writes captured and priced correctly - cache traffic is where most of a coding agent's real spend lives.

Want a loop filed under a name you chose? Put one word in front of the command you already run:

agenticledger run nightly-digest -- python agent.py

Your command runs exactly as before; its LLM calls land on the run tile named nightly-digest, and each launch counts as the next iteration, so tomorrow's run joins the same tile. Nothing in your agent's code changes. Add --project acme to file the run under a dashboard project as it starts.

Running an overnight loop (Ralph-style while :; do cat PROMPT.md | claude -p; done)? The same command with loop flags re-executes your command each iteration, attributes every call to the run (via the base URL, no headers needed), and stops on a completion promise, a budget ceiling, or the iteration cap:

AGENTICLEDGER_UPSTREAM_URL=https://api.anthropic.com \
AGENTICLEDGER_COMPLETION_PROMISE="ALL TASKS COMPLETE" \
uv run python -m agenticledger.proxy
agenticledger run overnight --max-iterations 50 --budget 25 -- \
  claude -p "$(cat PROMPT.md)" --dangerously-skip-permissions

Each iteration shows up as iteration N of the run in /api/runs; when the agent prints the completion promise in a response, run status flips to complete and the loop exits with a cost/token summary. The word after run is the run's name; without one the run is named after the folder and the minute (myproject-0819-1936). Rerunning the same name continues its iteration count instead of restarting at 1. Any existing loop script works too - poll GET /api/runs/{run_id} yourself, or let the proxy's budgets (AGENTICLEDGER_BUDGET_DAILY=25.00) hard-stop a runaway loop.

Iterating on the prompt? Rerun and use ⇆ compare in the Loop Lens to diff the two runs - cost, iterations, calls, and flags side by side - so "did the new prompt actually help" gets a number instead of a feeling.

The same recipe works for any client with a base-URL override (Codex CLI, opencode, OpenClaw, LiteLLM-based stacks) - set the OpenAI/Anthropic base URL to the proxy and traffic is captured; add x-agenticledger-* headers when you want explicit attribution.

OTel-native tools (Gemini CLI, Codex [otel], AutoGen/AG2, Pydantic AI, Vercel AI SDK) don't need the proxy at all - point their OTLP exporter at the ledger and GenAI spans are ingested directly:

export OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:8000

Both OTLP/HTTP encodings are accepted: JSON always, protobuf when the [otel] extra is installed (the Docker image includes it). gRPC exporters should switch to HTTP: OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf.

Framework guides - one per integration in docs/integrations: Claude Code, Codex CLI, opencode, OpenClaw, BMAD-METHOD, LangGraph/LangChain, CrewAI, OpenAI Agents SDK, Gemini CLI, AutoGen/AG2, Pydantic AI, Vercel AI SDK, LiteLLM, OpenRouter, and LM Studio (fully offline: local model, local ledger).

Production deployment - TLS termination, auth keys, redaction, image signature/SBOM verification, enterprise mirrors, and scaling guidance in docs/deployment.md.


The numbers

Measured, not promised. Reproduce them with python scripts/loadtest.py --calls 2000 --seed 1000000 (Apple M-series MacBook, SQLite backend; re-measured on 0.10 with the provider adapter architecture in place - same numbers, 2,580 to 2,800 calls/s on both):

WhatResult
Sustained capture throughput2,886 proxied calls/sec
Added latency per call10ms p50 · 12ms p95
Direct store writes~38,000 saves/sec
One million calls on disk271 MB
Open one session at 1M calls2 ms
Session list at 1M calls335 ms
30-day report at 1M calls719 ms

The honest caveats: the session list aggregates every session on every load, so it grows with total history; the report window uses a timestamp index, so it grows with the window's traffic, not the table. Your agent's provider latency (hundreds of ms per call) dwarfs the proxy's overhead by an order of magnitude. Postgres numbers vary with your server; the same script measures them with --dsn. Cost math has its own guardrails and a five-minute parity check against your provider console: see docs/accuracy.md.

What gets captured

Every LLM call is stored with:

Shortened here. Read the whole README on GitHub.

Signals

GitHub stars
5
Forks
4
Last commit
Sep 2026
Advanced
Delivery
agentic-ledger MCP server → your ahel gateway (mcp.ahel.ai) → every connected AI client.
Catalog kind
mcp-server
Gateway key
io-github-shekharbhardwaj-agentic-ledger
Source
github.com/shekharbhardwaj/agenticledger