A super simple monitoring system for Claude agents. Capture a whole Claude Agent SDK session end to end — every tool call, every subagent and why it was invoked, the model's reasoning, results, timing, and cost — as one readable JSON object in blob storage.
No collector, no database, no agent. One JSON file per session in S3-compatible storage. Built to be trivially easy to read and filter (the things that make Arize and friends painful).
from ams.claude import traced_query
async for message in traced_query(prompt="Cancel my membership", options=options):
...
# session written to storage automatically when the stream endsThat's the whole integration. Swap query for traced_query.
We monitor our Claude agents with Arize today, but it's hard to read, and hard to search/filter for a single session. AMS keeps the data model deliberately flat and typed so a session is obvious to a human and easy to query by a machine. Field names follow the OpenTelemetry GenAI semantic conventions (gen_ai.*) where there's a natural equivalent, so the data can later be re-emitted as OTLP without renaming.
A session is one trace of ordered events:
| Event | Source | Detail captured |
|---|---|---|
user_prompt |
UserPromptSubmit hook |
the prompt |
llm_message |
message stream | model, thinking / chain-of-thought, assistant text, token usage |
tool_call |
PreToolUse + PostToolUse / PostToolUseFailure hooks |
tool name, input, result, error, timing |
subagent |
SubagentStart / SubagentStop hooks |
agent type, why it was invoked (the prompt), transcript path; child tool calls nest underneath |
notification |
Notification hook |
message |
Plus session totals: token usage (incl. cache read/write), cost (USD), turn count, tool/subagent/error counts, and wall-clock + API duration.
The Claude Agent SDK has no built-in OpenTelemetry — AMS captures everything through hooks and the message stream, which together are the only place this data lives.
pip install ams-observability # published name; you import it as `ams`Or from a local checkout: pip install -e .. S3-compatible storage (boto3) is included by default.
Requires Python 3.10+ and the claude-agent-sdk in your project.
S3-compatible storage is the default. Works against AWS S3, Cloudflare R2, MinIO — anything speaking the S3 API.
export AMS_S3_BUCKET=my-agent-traces
export AMS_S3_PREFIX=ams # optional, default "ams"
export AMS_S3_ENDPOINT_URL=https://<account>.r2.cloudflarestorage.com # omit for AWS S3
export AMS_S3_REGION=auto
# credentials via the standard AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEYOr write to local disk for development:
export AMS_STORAGE=local
export AMS_LOCAL_DIR=./ams-data # optional{prefix}/sessions/{YYYY}/{MM}/{DD}/{session_id}.json full session
{prefix}/index/{session_id}.json compact summary (for listing/filtering)
The small index/ objects let a frontend build a searchable session list without opening every full session.
from ams import Agent
from ams.claude import traced_query
async for message in traced_query(
prompt="...",
options=options, # your ClaudeAgentOptions
agent=Agent(name="support-bot", version="2026.06"),
environment="prod",
tags=["voice", "cancellation"],
metadata={"team_id": "t_42"},
):
print(message)Merge AMS hooks into your options, feed messages to the tracer, and finish() when done:
from ams import Tracer
from ams.claude import instrument_options
tracer = Tracer(environment="prod", tags=["chat"])
options = instrument_options(my_options, tracer)
async with ClaudeSDKClient(options=options) as client:
await client.query("...")
async for message in client.receive_response():
tracer.record_message(message)
session = tracer.finish()Pass any object with put_session(session) -> str:
tracer = Tracer(storage=MyStorage())| Tracer arg / env | Default | Notes |
|---|---|---|
storage / AMS_STORAGE |
S3 | local to write to disk |
agent |
— | Agent(name=..., version=...) |
environment |
— | e.g. prod, staging |
tags, metadata |
— | free-form, promoted into the index for filtering |
capture_thinking |
True |
record the model's reasoning blocks |
redact / AMS_REDACT |
False |
opt-in PII redaction (email / phone / card / SSN) |
AMS never throws into your agent: hook and storage failures are logged, not raised.
See docs/architecture.md for the module map and the two-channel design (hooks + message stream) that AMS fuses into one session.
See docs/schema.md for the full session JSON schema with an example. The contract lives in one file: ams/schema.py.
A simple frontend to browse and filter sessions is planned (not built yet). See docs/frontend-notes.md for the intended design — it reads the index/ summaries to list sessions and fetches a full session JSON on click.
python -m venv .venv && . .venv/bin/activate
pip install -e ".[dev]"
pytestMIT