A super simple monitoring system for Claude agents — full session traces as JSON in blob storage.
Project description
AMS — Agent Monitoring System
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 ends
That's the whole integration. Swap query for traced_query.
Why
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.
What it captures
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.
Install
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.
Configure storage
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_KEY
Or write to local disk for development:
export AMS_STORAGE=local
export AMS_LOCAL_DIR=./ams-data # optional
Layout in the bucket
{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.
Usage
One-call (drop-in for query)
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)
With ClaudeSDKClient
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()
Custom storage
Pass any object with put_session(session) -> str:
tracer = Tracer(storage=MyStorage())
Options
| 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.
How it works
See docs/architecture.md for the module map and the two-channel design (hooks + message stream) that AMS fuses into one session.
Schema
See docs/schema.md for the full session JSON schema with an example. The contract lives in one file: ams/schema.py.
Frontend
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.
Development
python -m venv .venv && . .venv/bin/activate
pip install -e ".[dev]"
pytest
License
MIT
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 ams_observability-0.2.0.tar.gz.
File metadata
- Download URL: ams_observability-0.2.0.tar.gz
- Upload date:
- Size: 123.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c0894413e61c798fb7d91ffa5b76a885b1037d7c91a8190611e8356f7385ec99
|
|
| MD5 |
47ad50336e0937b146396161a3b3caab
|
|
| BLAKE2b-256 |
e555768a5de1e7f3e46b29583b2da19c38eacf36d53e958f8120aed8251f8b54
|
Provenance
The following attestation bundles were made for ams_observability-0.2.0.tar.gz:
Publisher:
release.yml on mathu97/ams
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
ams_observability-0.2.0.tar.gz -
Subject digest:
c0894413e61c798fb7d91ffa5b76a885b1037d7c91a8190611e8356f7385ec99 - Sigstore transparency entry: 1761605993
- Sigstore integration time:
-
Permalink:
mathu97/ams@c853f60a5761ee2000b1bc51f3f431c2a5a7a7c9 -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/mathu97
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@c853f60a5761ee2000b1bc51f3f431c2a5a7a7c9 -
Trigger Event:
release
-
Statement type:
File details
Details for the file ams_observability-0.2.0-py3-none-any.whl.
File metadata
- Download URL: ams_observability-0.2.0-py3-none-any.whl
- Upload date:
- Size: 23.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
35b5c7f7f0420b42c18ef8b732d4ffd9e2accc638f0369d70dd66f5653e82e69
|
|
| MD5 |
5a3465b630c484fd0ba855fb105791f2
|
|
| BLAKE2b-256 |
1a5a14bae6d6319a6f8c69b0375ff84e90f12cacebda498a823661d6890979b3
|
Provenance
The following attestation bundles were made for ams_observability-0.2.0-py3-none-any.whl:
Publisher:
release.yml on mathu97/ams
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
ams_observability-0.2.0-py3-none-any.whl -
Subject digest:
35b5c7f7f0420b42c18ef8b732d4ffd9e2accc638f0369d70dd66f5653e82e69 - Sigstore transparency entry: 1761606059
- Sigstore integration time:
-
Permalink:
mathu97/ams@c853f60a5761ee2000b1bc51f3f431c2a5a7a7c9 -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/mathu97
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@c853f60a5761ee2000b1bc51f3f431c2a5a7a7c9 -
Trigger Event:
release
-
Statement type: