Drop-in auto-record wrapper for the Anthropic Python SDK — every LLM turn captured to Neruva Memory. Recall later via the Neruva MCP / API. Cross-session, cross-vendor, portable.
Project description
neruva-record
Drop-in auto-record wrapper for the Anthropic Python SDK. Every
messages.create() call captures both directions to a Neruva Memory
namespace as a side-effect. Stop manually saving things; query the
namespace later for "what did this agent do?"
Pairs with @neruva/mcp
(captures MCP tool calls). Together: full agent-loop capture.
Install
pip install neruva-record anthropic
export NERUVA_API_KEY=... # https://app.neruva.io
Use
import anthropic
from neruva_record import auto_record
client = auto_record(
anthropic.Anthropic(),
index="brain", # one per user/account
namespace="main", # one per agent (free-form)
ttl_days=30, # optional: auto-expire records after N days
)
# Drop-in: client behaves identically to bare Anthropic.
response = client.messages.create(
model="claude-opus-4-7",
max_tokens=200,
messages=[{"role": "user", "content": "Hi!"}],
)
# Recording happened as a side-effect. Query it later via the MCP
# or the REST API.
Async
from anthropic import AsyncAnthropic
from neruva_record import auto_record
client = auto_record(
AsyncAnthropic(),
index="brain", namespace="main",
)
response = await client.messages.create(
model="claude-opus-4-7", max_tokens=200,
messages=[{"role": "user", "content": "Hi!"}],
)
Naming convention
| Field | What | Example |
|---|---|---|
index |
One per user/account. Agent-type-neutral. | brain |
namespace |
One per agent the user runs. Free-form. | main, support-bot, research |
# multi-agent setup -- same brain, one namespace per agent
support = auto_record(Anthropic(), index="brain", namespace="support-bot")
research = auto_record(Anthropic(), index="brain", namespace="research")
ops = auto_record(Anthropic(), index="brain", namespace="orchestrator")
What gets recorded
Per LLM turn, one record:
{
"id": "llm-<unix-ms>-<rand>",
"text": "USER: <user-message>\n\nASSISTANT: <response>",
"metadata": {
"kind": "llm_turn",
"vendor": "anthropic",
"model": "claude-opus-4-7",
"stop_reason": "end_turn",
"input_tokens": 12,
"output_tokens": 87,
"latency_ms": 1240,
"ts": <unix-ms>,
"_auto_expire_at": <unix-ms-or-omitted>
}
}
User text is truncated at 600 chars, assistant at 1800 chars (for
embedding quality and storage cost). The full conversation history
isn't stored — only the most recent user message and the response.
For full trace capture, also use
@neruva/mcp which
records every tool call alongside this.
Querying back
Use the Neruva MCP or the REST API to query the recorded turns:
curl -X POST https://api.neruva.io/v1/indexes/brain/text/query \
-H "Api-Key: $NERUVA_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"namespace": "main",
"text": "what did I ask about deployment?",
"topK": 5,
"includeMetadata": true,
"filter": { "vendor": { "$eq": "anthropic" } }
}'
Failure mode
Recording is fire-and-forget. If the recording POST fails for any
reason, your messages.create() call returns normally — the recorder
swallows its own errors. You will never get fewer responses than you
asked for; you may get fewer records than you'd see in a perfect world.
TTL / decay
Set ttl_days=N and each record carries _auto_expire_at = ts + N days. The Neruva server-side decay sweep automatically removes
records past their expiry on the next query/upsert touch (max once
per 15 minutes per namespace). No background job to run.
client = auto_record(
anthropic.Anthropic(),
index="brain", namespace="main",
ttl_days=7, # only the last week is retained
)
Claude Code hook recorder (0.2.0+, one-command install in 0.3.0+)
Capture everything in a Claude Code session — built-in tool calls (Bash, Read, Write, Edit, Grep, Glob, WebFetch, WebSearch), every MCP tool call, every user prompt, every assistant text response, every subagent, every task — into one Neruva namespace. Zero code change in your agent.
One-command install (recommended)
pip install neruva-record
neruva-record-install
That's it. The installer:
- Backs up
~/.claude/settings.jsonwith a timestamp - Merges in our 10 hook entries (preserving any existing user hooks)
- Sets
NERUVA_API_KEY+NERUVA_AUTO_RECORDin the env block - Runs
claude mcp add neruvato register the MCP so your agent can also callmcp__neruva__*tools directly (skipped if theclaudeCLI isn't on PATH, or pass--no-mcpto skip explicitly)
Then restart Claude Code.
Non-interactive:
NERUVA_API_KEY=nv_... neruva-record-install --yes
# or
neruva-record-install --api-key nv_... --namespace research-bot --ttl 7 --yes
To remove later:
neruva-record-install --uninstall
Manual install (for the curious)
If you'd rather edit ~/.claude/settings.json yourself:
{
"env": {
"NERUVA_API_KEY": "nv_...",
"NERUVA_AUTO_RECORD": "brain/main:30"
},
"hooks": {
"UserPromptSubmit": [
{ "hooks": [{ "type": "command", "command": "neruva-record-hook", "async": true }] }
],
"PostToolUse": [
{ "matcher": "*", "hooks": [{ "type": "command", "command": "neruva-record-hook", "async": true }] }
],
"PostToolUseFailure": [
{ "matcher": "*", "hooks": [{ "type": "command", "command": "neruva-record-hook", "async": true }] }
],
"Stop": [
{ "hooks": [{ "type": "command", "command": "neruva-record-hook", "async": true }] }
],
"SessionStart": [
{ "hooks": [{ "type": "command", "command": "neruva-record-hook", "async": true }] }
],
"SessionEnd": [
{ "hooks": [{ "type": "command", "command": "neruva-record-hook", "async": true }] }
],
"SubagentStart": [
{ "hooks": [{ "type": "command", "command": "neruva-record-hook", "async": true }] }
],
"SubagentStop": [
{ "hooks": [{ "type": "command", "command": "neruva-record-hook", "async": true }] }
],
"TaskCreated": [
{ "hooks": [{ "type": "command", "command": "neruva-record-hook", "async": true }] }
],
"TaskCompleted": [
{ "hooks": [{ "type": "command", "command": "neruva-record-hook", "async": true }] }
]
}
}
async: true is the magic — Claude Code fires the hook
fire-and-forget without waiting for it to return, so your tool calls
never slow down. Recording happens in parallel.
What gets recorded
Per Claude Code event, one record. Each carries metadata.kind and
metadata.event so you can filter at query time.
| Event | metadata.kind | What's captured |
|---|---|---|
UserPromptSubmit |
user_prompt |
The user's prompt text |
PostToolUse |
tool_call |
tool name + input summary + result |
PostToolUseFailure |
tool_failure |
tool name + input + error |
Stop |
assistant_stop |
session boundary marker |
SessionStart |
session_start |
source + model + cwd |
SessionEnd |
session_end |
end reason |
SubagentStart |
subagent_start |
agent type + prompt |
SubagentStop |
subagent_stop |
agent type |
TaskCreated |
task_created |
task title + description |
TaskCompleted |
task_completed |
task title |
Events to/from Neruva's own MCP (mcp__neruva__memory_*) are
auto-skipped to prevent recording the recording.
Query later
curl -X POST https://api.neruva.io/v1/indexes/brain/text/query \
-H "Api-Key: $NERUVA_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"namespace": "main",
"text": "what bash commands did I run?",
"topK": 10,
"filter": { "kind": { "$eq": "tool_call" }, "tool": { "$eq": "Bash" } }
}'
Or via the Neruva MCP installed in another agent: memory_query_text.
Stacks with auto_record(Anthropic())
You can run both at once. The hook recorder captures Claude Code's
tool calls + prompts + assistant turns; auto_record(Anthropic())
captures the LLM turns of any Python script you write that uses
Anthropic SDK directly. They share the same namespace if you want.
Roadmap
- v0.1: Anthropic Python (sync + async, non-streaming) ✅
- v0.2: Claude Code hook recorder (
neruva-record-hook) ✅ - v0.3: Anthropic streaming + tool-use turn capture
- v0.4: OpenAI Python
- v0.5: Google Gemini Python
- v0.6: TypeScript versions of all the above
- v1.0: Stable API, security audit
Related
@neruva/mcp— MCP tool-call auto-recordneruva-mcp— Python MCP serverneruva.io/docs— full documentation
License
MIT — Clouthier Simulation Labs.
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 neruva_record-0.5.0.tar.gz.
File metadata
- Download URL: neruva_record-0.5.0.tar.gz
- Upload date:
- Size: 19.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
27abb6858992ff8f08a0b4acb766bfe3faab552619935d29db3342e91a810f9a
|
|
| MD5 |
33127c5be622fd0bc1d4d1ccaeb05969
|
|
| BLAKE2b-256 |
0c417c3716d525a0c72573397a77bf0249a618e03eb8c57d32ef9593cd055e62
|
File details
Details for the file neruva_record-0.5.0-py3-none-any.whl.
File metadata
- Download URL: neruva_record-0.5.0-py3-none-any.whl
- Upload date:
- Size: 18.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
89952a7ca07c0b339cedf7fb847c58a5447b857844fdcbb594fbcf5ca625e11e
|
|
| MD5 |
8f876c9a7df1d87b3e11d4aad8a6172a
|
|
| BLAKE2b-256 |
b6bdc7278c6656fb164c1a7377bb5dc67bfba45f8419d3a83378c22c3ec6fe14
|