Caura — governed shared memory for AI agent fleets
Caura (formerly MemClaw) is an Agent DB — governed shared memory for AI agent fleets. Agents commit what they learn once; every agent in the fleet recalls it through MCP tools, REST, or Caura Rail (preview), subject to tenant isolation, visibility scope (scope_agent / scope_team / scope_org) and caller trust level.
caura-client is the official Python client for Caura and the canonical
package name; caura and
caura-sdk install the same client.
It is a thin wrapper over the Caura REST API. Point it at a managed
(https://caura.ai) or self-hosted (http://localhost:8000) deployment.
Formerly
memclaw-client. That name is now only a redirect shell: installing it gives you this package and nomemclaw_clientmodule, so move imports tocaura_clientand the retiredMemClaw/MemClawError/MemClawAPIErrornames to theCauraclass andCauraAPIErrorbelow.
Install
pip install caura-client
Quickstart
from caura_client import Caura
# Recommended: the client is a context manager and closes its HTTP
# connection on exit (use close() for manual management).
with Caura("mc_xxx", tenant_id="my-team", agent_id="my-agent") as mc:
# Write a memory — enriched server-side with type, title, tags, importance.
mc.write("Q3 revenue target is $4M, set on 2026-04-15.")
# Search (ranked raw results)
for m in mc.search("Q3 revenue target", top_k=5):
print(m.title, "—", m.content)
# Recall (LLM-synthesized context brief)
print(mc.recall("Q3 revenue target").summary)
tenant_id is required. On caura.ai it is the id of the tenant your key
belongs to; GET /api/v1/whoami returns it
(curl -s https://caura.ai/api/v1/whoami -H "X-API-Key: mc_xxx"). A
standalone self-hosted server uses default.
Self-hosted? Pass base_url:
with Caura("standalone", tenant_id="default", base_url="http://localhost:8000") as mc:
...
Plain http:// sends the API key in clear, so the client allows it only to a
loopback host (localhost, 127.0.0.0/8, ::1) and otherwise raises
ValueError naming the host. Use https://, or pass allow_insecure_http=True
(or set CAURA_ALLOW_INSECURE_HTTP=true) to accept the risk, e.g. on a trusted
private network.
New to Caura? Start with what Caura is and the LongMemEval benchmark harness. To reach the same memory from an MCP client with no SDK, see the caura page. Source and issues live at github.com/caura-ai/caura.
API
| Method | Endpoint | Returns |
|---|---|---|
write(content, ...) |
POST /api/v1/memories |
Memory |
search(query, top_k=5, ...) |
POST /api/v1/search |
SearchResult (a list[Memory]) |
recall(query, top_k=5, ...) |
POST /api/v1/recall |
RecallResult |
health() |
GET /api/v1/health |
dict |
stats() |
GET /api/v1/stats |
Stats |
get_document(doc_id, *, collection, ...) |
GET /api/v1/documents/{doc_id} |
dict |
submit_interview(...) |
POST /api/v1/interview/submit |
dict |
close() |
— | None |
The client is a context manager (with Caura(...) as mc:) and raises
AuthError (401/403), NotFoundError (404), or CauraAPIError on HTTP failures.
Network failures and timeouts raise TransportError, with the original httpx
exception in __cause__. Catch CauraError to handle both HTTP and transport
failures. Transport errors have no HTTP status code; requests are not retried.
Every result also exposes the full API payload on .raw. search() returns a
SearchResult: a list of Memory that also carries the response's
recall_tracked, diagnostic (set by diagnostic=True) and warnings (for
example a parameter the server ignored), and the whole body on .raw.
recall() reads the brief's memories and asks the server not to repeat them
under items (items_alias=False), which halves the response. Pass
items_alias=True if you read raw["items"].
Reading as an agent with a tenant key
With a tenant-scoped key, search() and recall() read as the tenant, so they
do not return any agent's scope_agent memories, including ones this client
wrote with agent_id. Pass caller_agent_id to read as that agent:
with Caura("mc_tenant_key", tenant_id="my-team", agent_id="my-agent") as mc:
mine = mc.search("deploy checklist", caller_agent_id=mc.agent_id)
The server then treats the read as that agent's: it registers the agent if it
is new, and holds the read to the agent's fleet and trust level, so a trust-1
agent reads its own fleet and is refused another. It is opt-in, never sent from
agent_id alone. An agent-scoped key already reads as its agent, and may only
name itself here.
Deployment stats
stats() returns three counters — tenant_count, memory_count,
agent_count — for the landing-page status bar:
with Caura("mc_xxx", tenant_id="my-team") as mc:
s = mc.stats()
print(s.tenant_count, s.memory_count, s.agent_count)
These are deployment-wide totals, not scoped to your tenant_id — the
server counts across every tenant on the deployment. The underlying endpoint
is unauthenticated (no key required to call it directly), but this method
still sends your API key header for consistency with every other call.
Fetching a document
get_document() returns the full DocOut envelope — the stored record is
nested under the "data" key, not returned directly. collection is a
required keyword-only argument, and a missing document raises
NotFoundError:
with Caura("mc_xxx", tenant_id="my-team", agent_id="my-agent") as mc:
doc = mc.get_document("doc-123", collection="interviews")
record = doc["data"] # the stored record lives under "data"
Lifecycle
Caura holds an httpx.Client, so prefer the with form above — the
connection is closed on exit. For manual management, call close()
explicitly when you are done:
mc = Caura("mc_xxx", tenant_id="my-team", agent_id="my-agent")
try:
mc.write("...")
finally:
mc.close()
Request headers
Every request carries X-API-Key (your key), Content-Type: application/json
and a User-Agent of the form
caura-client-python/<version> (python/<major>.<minor>). The User-Agent
lets a Caura server count which SDK families talk to it; it names only the
package, its version and the Python release. The client sends nothing to any
host other than the base_url you configure.
submit_interview() is an Interviewer-internal surface
submit_interview() is used by the caura-interviewer adapter below to
submit parsed session windows to the server. It is not intended as a
general-purpose SDK method. By default the server stores the window, advances
its watermark and answers 200 with "status": "accepted" and
memories_written 0: the memories are written in the background, after the
response. A server with interview_async_submit turned off interviews the
window in-line, up to a 90s budget, and answers 200 committed or 207
partial with the count; that is why timeout defaults to 120s rather than the
client-wide 30s. The returned body carries an extra "http_status" key. New
SDK users should not need it.
For credentials, scopes, and the full API surface, see the Caura docs. Production fleets should use per-agent keys.
Unknown fields on writes are rejected
write() forwards any extra keyword arguments straight into the request body
(mc.write("...", some_field=1)). The API rejects a field it does not declare
with 422 and names it:
try:
mc.write("a memory", tags=["alpha"]) # `tags` is not a write field
except CauraAPIError as exc:
exc.details["unknown_fields"] # ["tags"]
This used to return 201 with the field silently discarded, so an integration
that "worked" may start failing here — the data it sent was never being stored.
Caller-owned keys belong under metadata (mc.write("...", metadata={"tags": [...]})).
search() and recall() are unaffected: filter bodies still accept unknown
fields, deliberately. See
api-surfaces.md.
Sessions and the work behind a memory
Caura's value report counts per agent session: a memory is reused once per session that recalls it, and its origin is the work its session did before writing it. Put calls in a session, and pass the provider usage you already have:
mc = Caura("mc_xxx", tenant_id="my-team", agent_id="my-agent", session_id="run-2026-10-11-a")
# Sum `usage` from your model provider's responses since the last write.
mc.write("Staging resets every Saturday at 23:00 UTC.",
origin={"tokens_in": 18_400, "tokens_out": 1_250, "turns": 6})
mc.set_session("run-2026-10-11-b") # a new conversation or task run
origin is sent as SDK-reported. Leave it out rather than estimate.
caura-interviewer — Claude Code + Cursor adapter
Installing this package provides the caura-interviewer CLI. When upgrading an
older install, run caura-interviewer install; for one release it can find and replace
an existing memclaw-interviewer crontab entry.
The Caura Interviewer is a disk-parser adapter for Claude Code and Cursor
workstations. It reads agent session transcripts read-only — Claude
Code's ~/.claude/projects/…/*.jsonl or Cursor's
~/.cursor/projects/…/agent-transcripts/…/*.jsonl — tracks a per-file
cursor via the server's forward-only watermark documents (no local state),
and submits event windows to POST /api/v1/interview/submit, where Caura
synthesizes them into typed memories. Requires the tenant to have
interviewer.enabled = true.
export CAURA_API_KEY=mc_xxx CAURA_TENANT_ID=my-team
export CAURA_INTERVIEWER_PROJECTS="-Users-me-work-*" # allowlist, default-deny
caura-interviewer status --since-hours 24 # cursors vs. local line counts
caura-interviewer run --dry-run -v # parse + window, submit nothing
caura-interviewer run --max-windows 8 # submit due windows
caura-interviewer run --harness cursor # harvest Cursor instead (or
# CAURA_INTERVIEWER_HARNESS=cursor)
Every CAURA_* variable above also answers to its pre-rename MEMCLAW_* name; where both are set the first non-empty value wins.
Privacy: default-deny — with no allowlist the CLI lists discovered
project dirs and exits with guidance; --all-projects is the explicit
opt-in. Credential-shaped strings are scrubbed locally before anything
leaves the machine, and the server masks PII again on receipt.
Triggers: run it from cron, or wire the harness's session-end hook so a
session is interviewed the moment it ends (a failed harvest never fails
the session — the hook always exits 0). The SAME hook command serves both
harnesses: each sends transcript_path on stdin, and the harness is
inferred from the path shape.
Claude Code (~/.claude/settings.json):
{ "hooks": { "SessionEnd": [ { "hooks": [
{ "type": "command", "command": "caura-interviewer hook", "timeout": 300 }
] } ] } }
Cursor (~/.cursor/hooks.json):
{ "version": 1, "hooks": {
"sessionEnd": [ { "command": "caura-interviewer hook" } ]
} }
Schedule the cron in one command. Rather than hand-editing crontab,
install writes an idempotent cron entry (and a 0600 env file it sources,
since cron doesn't inherit your shell environment). Config comes from the
same flags/env as run:
caura-interviewer install --interval 30m # add --harness cursor for Cursor
caura-interviewer uninstall # removes the entry + env file
It refuses to schedule a job that would no-op (missing credentials or no
project allowlist). On Windows (no crontab), use Task Scheduler to run
caura-interviewer run on a timer instead.
A run ends with a one-line summary. On a default server it reports windows "accepted for synthesis in the background", since the memories are written after the response; "memories written" counts what a synchronous server wrote in-line.
Crash-safety is inherited from the Interviewer protocol: the watermark advances only after the server has stored a window, and retries of the same window dedup server-side via a deterministic attempt id, so each window is stored once. Its memories come from the server's background synthesis. A window whose synthesis keeps failing is parked server-side for an operator; its watermark has already moved, so this adapter does not send it again.
License
Apache-2.0
Metadata
Release files for caura-client 1.1.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| caura_client-1.1.1.tar.gz | 73.0 kB | With provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| caura_client-1.1.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 122.1 kB
Release files / caura_client-1.1.1.tar.gz
| Download URL | caura_client-1.1.1.tar.gz |
|---|---|
| Size | 73.0 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
11f989ecf8f94d46c8decbfc510520d3fedd91eb49a131998c53170fba860a3d
|
|
BLAKE2b-256 checksum How to use checksums |
8e8342229ce04e63a23dfcaa317b2623479f71270028e0ffb0aeeb0a207b2354
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 20, 2026.
Transparency logRelease files / caura_client-1.1.1-py3-none-any.whl
| Download URL | caura_client-1.1.1-py3-none-any.whl |
|---|---|
| Size | 49.0 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
a042b220c171c6b5e10d9cb8967cce56b8c3c5c18a19f24a5b303d526e8e8592
|
|
BLAKE2b-256 checksum How to use checksums |
51a16768c77aafb65442e2ee836c39edf6d1e2665f1eb7fc2486756ff69eb536
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 20, 2026.
Transparency logSecurity for caura-client 1.1.1
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for caura_client-1.1.1.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 20, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.