agentic-ledger
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.
Your Agent → Agentic Ledger Proxy → OpenAI / Anthropic / LiteLLM / any LLM
↓
SQLite or Postgres
↓
Live Dashboard + APIRelated MCP server: opencode-export
Quick Start
Step 1 — Start the proxy
With Docker (recommended, no Python required):
docker run -p 8000:8000 \
-e AGENTLEDGER_UPSTREAM_URL=https://api.openai.com \
-v $(pwd)/data:/data \
ghcr.io/shekharbhardwaj/agentledger:latestOr with docker compose (SQLite by default — see docker-compose.yml):
AGENTLEDGER_UPSTREAM_URL=https://api.openai.com docker compose upWith uv:
uv add agentic-ledger
AGENTLEDGER_UPSTREAM_URL=https://api.openai.com uv run python -m agentledger.proxyWith pip:
python -m venv venv && source venv/bin/activate
pip install agentic-ledger
AGENTLEDGER_UPSTREAM_URL=https://api.openai.com ./venv/bin/python -m agentledger.proxyPostgres? Install the extra and set
AGENTLEDGER_DSN:pip install "agentic-ledger[postgres]" AGENTLEDGER_DSN=postgresql://user:password@localhost/agentledgerNote: the Docker image uses SQLite only. For Postgres with Docker, install via
pipinstead.
OpenTelemetry? Install the extra and set
AGENTLEDGER_OTEL_ENDPOINT:pip install "agentic-ledger[otel]" AGENTLEDGER_OTEL_ENDPOINT=http://localhost:4318
Proxy starts on http://localhost:8000. Traces are saved to agentledger.db in the current folder (or /data/agentledger.db in Docker).
Step 2 — Point your agent at the proxy
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-agentledger-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:
import anthropic
client = anthropic.Anthropic(
base_url="http://localhost:8000", # ← proxy
api_key="your-anthropic-key",
default_headers={"x-agentledger-session-id": "run-1"},
)LiteLLM / OpenRouter / any gateway:
# Point Agentic Ledger at your gateway
AGENTLEDGER_UPSTREAM_URL=http://localhost:4000 uv run python -m agentledger.proxy
# Then point your agent at Agentic Ledger
client = OpenAI(base_url="http://localhost:8000/v1", ...)Step 3 — Open the dashboard
http://localhost:8000The web app updates live via WebSocket as calls come in. No refresh needed.
Loop Lens — every loop run with status (
running/flagged/complete), a cost-per-iteration chart, per-iteration breakdowns, and plain-English explanations of every flagSessions — every session with three views: expandable call cards (response, thinking, tool calls, cache tokens, interaction badges), a Flow DAG of agent handoffs, and a Trace waterfall with real parent links from the loop engine
Search — full-text across prompts, outputs, and agents
The classic single-file dashboard remains at http://localhost:8000/classic:
Calls tab — every LLM call with full prompt, system prompt, tool calls, tool results, output, tokens, cost, latency, and errors
Flow tab — visual DAG of your multi-agent system. Each agent is a node with aggregated cost, latency, and call count. Edges represent handoffs. Click a node to highlight its calls.
Trace tab — Gantt/waterfall timeline showing every call as a horizontal bar on a shared time axis. Parallel calls appear side-by-side at the same position — no instrumentation required. Works purely from timestamps. Click any bar to jump to the full call detail. Budget warnings show as an amber border on the bar without hiding the agent colour.
Search — full-text search across all sessions by prompt, output, agent name, or user ID
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:
AGENTLEDGER_UPSTREAM_URL=https://api.anthropic.com uv run python -m agentledger.proxyexport ANTHROPIC_BASE_URL=http://localhost:8000
claudeAgentic 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.
Running an overnight loop (Ralph-style while :; do cat PROMPT.md | claude -p; done)?
Use the built-in loop runner — it 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:
AGENTLEDGER_UPSTREAM_URL=https://api.anthropic.com \
AGENTLEDGER_COMPLETION_PROMISE="ALL TASKS COMPLETE" \
uv run python -m agentledger.proxyagentledger run --max-iterations 50 --budget 25 -- \
claude -p "$(cat PROMPT.md)" --dangerously-skip-permissionsEach 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. Any existing loop
script works too — poll GET /api/runs/{run_id} yourself, or let the proxy's
budgets (AGENTLEDGER_BUDGET_DAILY=25.00) hard-stop a runaway loop.
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-agentledger-* 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
export OTEL_EXPORTER_OTLP_PROTOCOL=http/jsonFramework 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, and OpenRouter.
What gets captured
Every LLM call is stored with:
Field | What it contains |
| UUID assigned at interception time |
| Run grouping (from header) |
| When the call was made |
| Model used |
|
|
| Full message history sent to the model |
| Extracted system prompt |
| Tool definitions available to the model |
| Tools the model decided to call |
| What the tools returned (from next call's messages) |
| Model's text output |
| Why the model stopped |
| Token usage |
| Prompt-cache usage — reads and writes are priced correctly per provider |
| Extended-thinking output (Anthropic), captured separately from |
| Estimated cost based on model pricing |
| End-to-end response time |
| HTTP status from upstream — errors are captured too |
| Upstream error message for non-200 responses |
| From |
| From |
| From |
| From |
| From |
| Parent call in a nested agent graph |
| Agent handoff tracking for the Flow DAG |
API reference
Method | Endpoint | Description |
|
| Liveness — |
|
| Readiness — pings the store; |
|
| Prometheus metrics (captures persisted/dropped, queue depth). |
|
| Audit trail of sensitive actions (admin). |
|
| Right-to-erasure: delete all of a user's captured calls (admin). |
|
| Live dashboard |
|
| WebSocket stream — powers live dashboard updates |
|
| List recent sessions with aggregated stats |
|
| List loop runs (explicit or auto-inferred) with iterations, cost, status, and flagged-call counts |
|
| One run's status ( |
|
| Derived tool executions — each tool call paired with its result, latency, and error status |
|
| Delete a session and all its calls |
|
| Full-text search across all captured calls |
|
| All calls in a session, ordered by time |
|
| Single call by action ID |
|
| JSON compliance export with SHA-256 integrity hash |
|
| Printable HTML audit report |
|
| MCP tool server — |
|
| OTLP/HTTP JSON ingest — GenAI spans from OTel-native tools become ledger calls ( |
Examples:
# All calls in a session
curl http://localhost:8000/session/run-1
# Search across all sessions
curl "http://localhost:8000/api/search?q=failed+to+connect"
# Download JSON audit trail (includes an integrity tag; keyed HMAC when configured)
curl http://localhost:8000/export/run-1 -o audit-run-1.json
# Printable HTML report — open in browser, print to PDF
open http://localhost:8000/export/run-1/reportMCP server
Agentic Ledger exposes its captured data as an MCP (Model Context Protocol) tool server at POST /mcp. Point Claude Desktop, Cursor, or any MCP-compatible client at it to query traces directly from your AI assistant.
Tools available:
Tool | Description |
| List recent sessions with cost, token, and call count summaries |
| Full trace for a single LLM call — prompt, tool calls, output, tokens, cost |
| All calls in a session in chronological order |
| Full-text search across all captured calls |
| Loop runs with iterations, cost, and status |
| One run's status — lets an agent inspect its own loop and decide whether to continue |
Configure in claude_desktop_config.json:
{
"mcpServers": {
"agentledger": {
"url": "http://localhost:8000/mcp"
}
}
}If AGENTLEDGER_API_KEY is set, pass it as a header:
{
"mcpServers": {
"agentledger": {
"url": "http://localhost:8000/mcp",
"headers": { "x-agentledger-api-key": "your-key" }
}
}
}Once connected, you can ask your assistant things like:
"What did the SearchAgent do in the last session?"
"Show me all calls that mentioned rate limit errors"
"What was the total cost of session run-abc123?"
Configuration
Environment variables
Core:
Variable | Required | Default | Description |
| Yes |
| LLM endpoint to forward requests to. Accepts OpenAI, Anthropic, LiteLLM, OpenRouter, or any OpenAI-compatible URL. |
| No |
| Database. SQLite for local dev, Postgres URL for production. |
| No |
| Host to bind to. Use |
| No |
| Port to run on. |
| No | (none) | Master admin key. When set, the dashboard, read, and management endpoints require authentication; the key grants the |
| No | (none) | When set, the proxy forwards a request only if it carries a matching |
| No | (none) | When set, compliance exports carry a tamper-evident keyed |
| No | (none) | Comma-separated additional request paths to capture, e.g. |
| No |
| Persist captures on a background worker so storage never adds latency to the agent's call. Trade-off: reads become eventually consistent (a just-captured call may not be queryable for a brief moment). Recommended for high throughput. |
| No |
| Max captures buffered in async mode before load is shed (drops are counted in |
| No |
|
|
| No | (off) | Redact PII/secrets in stored data: |
| No | (none) | Extra redaction regexes as JSON: |
| No | (keep forever) | Delete captured calls older than N days via a background purge worker. |
| No |
| Record an audit trail of who viewed/exported/deleted what plus token/erasure actions. Set |
Cost budgets — block calls that exceed a spend limit (returns HTTP 429):
Variable | Default | Description |
| (none) | Max USD per |
| (none) | Max USD per |
| (none) | Max USD total across all calls per calendar day (UTC). |
|
| What happens when a budget is exceeded: |
Rate limits — block calls that exceed request frequency (returns HTTP 429, sliding 60-second window):
Variable | Default | Description |
| (none) | Max requests per minute globally. |
| (none) | Max requests per minute per |
| (none) | Max requests per minute per |
| (none) | Max requests per minute per |
Loop engine — every call is stitched into ReAct threads (thread_id, step_index, prev_action_id) and fresh-context loop iterations are grouped into runs, with stuck-loop detection:
Variable | Default | Description |
|
|
|
|
| Consecutive identical tool calls (same tool, same arguments) before a thread is flagged stuck. |
| (none) | Flag (and in block mode, stop) threads that exceed this many ReAct steps. |
|
| Max gap between fresh-context spawns (same system prompt) that still count as iterations of one run. |
| (none) | Regex matched against response text. On match the call is flagged |
Alerts — POST to your webhook when a threshold is breached (does not block calls — see Alerts):
Variable | Default | Description |
| (none) | URL to POST alert payloads to. Required for any alerts to fire. |
| (none) | Alert when a single call costs more than |
| (none) | Alert when a single call takes longer than |
| (none) | Alert when session error rate exceeds |
| (none) | Alert when daily spend crosses |
OpenTelemetry — emit spans to any OTLP-compatible collector (requires pip install "agentic-ledger[otel]" — see OpenTelemetry export):
Variable | Default | Description |
| (none) | OTLP/HTTP base URL, e.g. |
|
| Value of |
| (none) | Comma-separated |
Pricing overrides — override or extend the built-in per-token pricing table (merged at startup):
Variable | Default | Description |
| (none) | Inline JSON map of model → |
| (none) | Path to a JSON file with the same format. Applied after |
Common startup examples
# Local dev — OpenAI (default)
AGENTLEDGER_UPSTREAM_URL=https://api.openai.com uv run python -m agentledger.proxy
# Local dev — Anthropic
AGENTLEDGER_UPSTREAM_URL=https://api.anthropic.com uv run python -m agentledger.proxy
# Local dev — LiteLLM gateway (any model)
AGENTLEDGER_UPSTREAM_URL=http://localhost:4000 uv run python -m agentledger.proxy
# Production — Postgres + auth + budgets + rate limits + alerts
AGENTLEDGER_UPSTREAM_URL=https://api.openai.com \
AGENTLEDGER_DSN=postgresql://user:password@localhost/agentledger \
AGENTLEDGER_API_KEY=my-secret \
AGENTLEDGER_BUDGET_DAILY=20.00 \
AGENTLEDGER_BUDGET_SESSION=2.00 \
AGENTLEDGER_RATE_LIMIT_SESSION_RPM=20 \
AGENTLEDGER_RATE_LIMIT_USER_RPM=60 \
AGENTLEDGER_ALERT_WEBHOOK_URL=https://hooks.slack.com/services/xxx/yyy/zzz \
AGENTLEDGER_ALERT_COST_PER_CALL=0.50 \
AGENTLEDGER_ALERT_DAILY_SPEND=15.00 \
uv run python -m agentledger.proxyWhen AGENTLEDGER_API_KEY is set, pass it to access protected endpoints:
# Header
curl -H "x-agentledger-api-key: my-secret" http://localhost:8000/session/run-1
# Query param (browser)
http://localhost:8000?api_key=my-secretScoped API tokens (RBAC)
The master key is convenient but coarse. For team access, mint scoped, revocable tokens with roles instead of sharing the master secret. Tokens are random secrets shown once at creation; only their SHA-256 hash is stored.
Roles are hierarchical:
Role | Can |
| read captured data — dashboard, API, export, MCP |
| viewer + delete sessions |
| editor + manage API tokens |
# Mint a viewer token (admin only — use the master key to bootstrap)
curl -X POST http://localhost:8000/api/tokens \
-H "x-agentledger-api-key: my-secret" \
-H "content-type: application/json" \
-d '{"name": "grafana-readonly", "role": "viewer", "expires_in_days": 90}'
# → {"token_id": "...", "token": "agl_…", "role": "viewer", ...} (token shown once)
# Use it (Bearer header, x-agentledger-token, or ?token=)
curl -H "Authorization: Bearer agl_…" http://localhost:8000/api/sessions
# List and revoke
curl -H "x-agentledger-api-key: my-secret" http://localhost:8000/api/tokens
curl -X DELETE -H "x-agentledger-api-key: my-secret" http://localhost:8000/api/tokens/<token_id>Auth is enforced only when
AGENTLEDGER_API_KEYis set; the master key is the admin bootstrap for minting tokens. The live/wsfeed accepts the same credentials (?api_key=,?token=,Authorization: Bearer, orx-agentledger-token) and rejects unauthenticated connects with close code 1008 — the dashboard forwards its page credential to the socket automatically.
Request headers
Pass these from your agent on each LLM call. All optional. They enrich captured data, power the Flow tab, and enable per-dimension budgets and rate limits.
Header | Default | Description |
| (none) | Groups all calls in a run. Use a consistent ID per agent execution (e.g. a UUID or |
| (none) | End user who triggered this run. Enables per-user rate limiting and auditing. |
| (none) | Name of the agent making this call (e.g. |
| (none) | Application name or ID. Useful when multiple apps share one proxy. |
| (none) | The |
|
|
|
| (none) | Agent handing off control (e.g. |
| (none) | Agent receiving control (e.g. |
| (auto-detected) | Framework/tool making the call (e.g. |
| (auto-inferred) | Groups sessions into a loop run (e.g. a Ralph overnight run). When absent, fresh-context sessions sharing a system prompt within |
| (auto-inferred) | Iteration number within the run. |
Single agent — fully annotated:
from openai import OpenAI
client = OpenAI(
base_url="http://localhost:8000/v1",
api_key="your-openai-key",
default_headers={
"x-agentledger-session-id": "run-abc123",
"x-agentledger-user-id": "user-42",
"x-agentledger-agent-name": "researcher",
"x-agentledger-app-id": "my-app",
"x-agentledger-environment": "production",
},
)Multi-agent system — tracking handoffs:
from openai import OpenAI
# Orchestrator
orchestrator_client = OpenAI(
base_url="http://localhost:8000/v1",
api_key="your-openai-key",
default_headers={
"x-agentledger-session-id": "run-abc123",
"x-agentledger-agent-name": "orchestrator",
},
)
# Researcher (receives handoff from orchestrator)
researcher_client = OpenAI(
base_url="http://localhost:8000/v1",
api_key="your-openai-key",
default_headers={
"x-agentledger-session-id": "run-abc123",
"x-agentledger-agent-name": "researcher",
"x-agentledger-handoff-from": "orchestrator",
"x-agentledger-handoff-to": "researcher",
},
)The Flow tab renders orchestrator → researcher as a DAG with cost and latency on each node.
OpenAI Agents SDK (openai-agents) — per-agent clients:
The openai-agents SDK uses its own internal OpenAI client. To pass Agentic Ledger headers you need to create a client per agent using OpenAIResponsesModel and set it as the agent's model.
import uuid
import os
from openai import AsyncOpenAI
from agents import Agent
from agents.models.openai_responses import OpenAIResponsesModel
SESSION_ID = f"run-{uuid.uuid4().hex[:8]}" # one per execution
BASE_URL = os.getenv("OPENAI_BASE_URL") # e.g. http://localhost:8000/v1
def al_model(agent_name: str, model: str = "gpt-4o-mini",
handoff_from: str | None = None, handoff_to: str | None = None):
"""Create a model instance that sends Agentic Ledger metadata headers."""
if not BASE_URL:
return model # proxy not configured — use default client
headers = {
"x-agentledger-session-id": SESSION_ID,
"x-agentledger-agent-name": agent_name,
}
if handoff_from:
headers["x-agentledger-handoff-from"] = handoff_from
if handoff_to:
headers["x-agentledger-handoff-to"] = handoff_to
client = AsyncOpenAI(base_url=BASE_URL, api_key=os.getenv("OPENAI_API_KEY", ""),
default_headers=headers)
return OpenAIResponsesModel(model=model, openai_client=client)
planner = Agent(name="PlannerAgent", model=al_model("PlannerAgent", handoff_to="SearchAgent"), ...)
searcher = Agent(name="SearchAgent", model=al_model("SearchAgent", handoff_from="PlannerAgent", handoff_to="WriterAgent"), ...)
writer = Agent(name="WriterAgent", model=al_model("WriterAgent", handoff_from="SearchAgent", handoff_to="EmailAgent"), ...)
emailer = Agent(name="EmailAgent", model=al_model("EmailAgent", handoff_from="WriterAgent"), ...)Each agent's calls are tagged with its name and pipeline position. The Flow tab renders the full PlannerAgent → SearchAgent → WriterAgent → EmailAgent DAG automatically.
Why per-agent clients?
set_default_openai_client()sets a single global client — fine for single-agent apps, but it can't carry differentagent_nameorhandoff_*headers per agent in a multi-agent system. Per-agentOpenAIResponsesModelinstances are the correct approach.
Alerts
Agentic Ledger fires a POST to your webhook URL when a threshold is breached. You connect it to whatever you already use — Slack, PagerDuty, Discord, email, or a custom endpoint. Agentic Ledger sends the payload; the integration is on your side.
Payload format:
{
"type": "high_cost",
"message": "Single call cost $0.1842 exceeded threshold $0.10",
"value": 0.1842,
"threshold": 0.10,
"action_id": "a1b2c3d4-...",
"session_id": "run-1",
"agent_name": "researcher",
"timestamp": "2026-04-03T12:00:00+00:00"
}Alert types:
Type | Triggered when |
| A single call exceeds |
| A single call takes longer than |
| Session error rate exceeds |
| Daily total spend crosses |
| A budget limit is hit and |
| The loop engine raised flags on a call ( |
| A run's completion promise was seen — the payload carries the full run summary (iterations, cost, tokens, flagged calls) |
Budgets vs alerts:
Budgets (
AGENTLEDGER_BUDGET_*) — block the call before it reaches the LLM. Agent gets HTTP 429.Alerts (
AGENTLEDGER_ALERT_*) — the call goes through, you get notified after.
Slack — create an Incoming Webhook and point AGENTLEDGER_ALERT_WEBHOOK_URL at it.
PagerDuty — use the Events API v2 URL or a thin adapter that maps type → PagerDuty severity.
Discord — use a Discord channel webhook URL directly.
Custom — any HTTP endpoint that accepts a JSON POST.
OpenTelemetry export
Agentic Ledger can emit every intercepted LLM call as an OTel span to any OTLP-compatible collector: Grafana Tempo, Jaeger, Honeycomb, Datadog, Dynatrace, or any vendor that supports OTLP/HTTP.
Install the extra (Docker image includes OTel — no extra step needed when using Docker):
pip install "agentic-ledger[otel]"
# or
uv add "agentic-ledger[otel]"Configure:
Variable | Default | Description |
| (none) | OTLP/HTTP base URL, e.g. |
|
| Value of |
| (none) | Comma-separated |
Example — Grafana Tempo:
AGENTLEDGER_UPSTREAM_URL=https://api.openai.com \
AGENTLEDGER_OTEL_ENDPOINT=http://localhost:4318 \
AGENTLEDGER_OTEL_SERVICE_NAME=my-agent \
uv run python -m agentledger.proxyExample — Honeycomb:
AGENTLEDGER_OTEL_ENDPOINT=https://api.honeycomb.io \
AGENTLEDGER_OTEL_HEADERS=x-honeycomb-team=YOUR_API_KEY,x-honeycomb-dataset=llm-traces \
uv run python -m agentledger.proxySpan attributes emitted (GenAI semantic conventions):
Attribute | Source |
| Provider ( |
| Always |
| Model ID |
| If set |
| If set |
| Tokens in |
| Tokens out |
| Stop reason |
| Unique call ID |
| Run grouping |
| From header |
| From header |
| Estimated cost |
| End-to-end latency |
| From header |
| Agent handoffs |
| HTTP status from upstream |
Spans are grouped into traces by session_id — all calls in a session appear as one trace in your backend. Parent-child relationships follow x-agentledger-parent-action-id. Error spans (status_code != 200) are marked with StatusCode.ERROR.
Compliance export
Every session can be exported as an integrity-tagged audit trail — useful for regulated industries, internal audits, or passing traces to external tools.
# Machine-readable JSON with an integrity tag over the calls array
curl http://localhost:8000/export/run-1 -o audit-run-1.json
# Printable HTML — open in browser and print to PDF
open http://localhost:8000/export/run-1/reportThe JSON export carries an integrity tag over the calls array. By default this is a sha256 checksum — it catches accidental corruption but is not a signature (anyone who edits the calls can recompute it). Set AGENTLEDGER_EXPORT_HMAC_KEY to switch to a keyed hmac-sha256 tag, which is tamper-evident: a recipient holding the key can detect any modification, and the tag cannot be forged without the key.
Releasing
Tagging a version triggers the full release pipeline automatically:
git tag v0.2.0
git push origin v0.2.0This runs three jobs:
Docker — builds and pushes
ghcr.io/shekharbhardwaj/agentledger:{version}and:latestto GHCRPyPI — builds and publishes
agentic-ledger=={version}to PyPI using trusted publishing (no API token needed)GitHub Release — creates a release with auto-generated changelog from commit messages
First-time PyPI setup (one time only):
Add a new pending publisher:
PyPI project name: agentic-ledger Owner: ShekharBhardwaj Repository: AgenticLedger Workflow name: release.yml Environment name: pypiCreate a
pypienvironment in GitHub: repo → Settings → Environments → New environment → name itpypiThat's it — no secrets needed
Troubleshooting
incompatible architecture (have 'arm64', need 'x86_64') on macOS — your
terminal is running under Rosetta, so Python picks its x86_64 slice while pip
installed arm64 native wheels. Check with arch (should print arm64 on
Apple Silicon). Quick fix: prefix the command with arch -arm64. Permanent
fix: uncheck "Open using Rosetta" on your terminal app, use an Apple Silicon
build of your editor, and restart any long-lived tmux server.
module 'httpx' has no attribute 'AsyncClient' — fixed in
0.3.0-alpha.2; upgrade with pip install --upgrade agentic-ledger.
Port 8000 already in use — another proxy instance (or app) is running;
stop it or set AGENTLEDGER_PORT.
401 OAuth access token has expired from Claude Code — the proxy passed
Anthropic's answer through unmodified; re-authenticate with claude →
/login. Errored calls are still captured, so you'll see the 401 in the
dashboard.
/ shows the classic dashboard instead of the web app — you're running
from a source checkout without the web-app build. cd dashboard-app && npm ci && npm run build and restart. PyPI and Docker installs always include the app.
License
MIT
mcp-name: io.github.ShekharBhardwaj/agentic-ledger
Maintenance
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/ShekharBhardwaj/AgenticLedger'
If you have feedback or need assistance with the MCP directory API, please join our Discord server