Tests (`tests/sdk/`)
-------------------
Five focused integration tests covering the SDK's contract. Each uses
the project's existing skip-if-no-db pattern (gating on both
`CLAUDE_MEM_TEST_POSTGRES_URL` and `CLAUDE_MEM_SERVER_DATABASE_URL` so
the suite runs under both the CI harness and the SDK's own env), and
the live-Chroma tests also gate on `uvx --version` (same pattern as
`tests/integration/chroma-vector-sync.test.ts`).
- `create-cmem-client.test.ts`: schema bootstrap idempotency; second
`createCmemClient` reuses the persisted `sdk-tenant.json`; per-test
`CLAUDE_MEM_DATA_DIR` under `os.tmpdir()` keeps host state untouched.
- `capture.test.ts`: `capture` writes exactly one `agent_events` row +
one `observation_generation_jobs` row (status `queued`); `captureBatch`
does the same per event in a single tx. Plus a static "no-Redis"
import-guard at the source level.
- `generate.test.ts`: `captureAndGenerate` with a stub `CmemProvider`
returning minimal-valid agent XML — verifies the observation row,
`observation_sources` link, and `completed` job state without
calling a live LLM API.
- `search.test.ts`: empty-query → `listByProject`; Chroma error path
via `ChromaMcpManager.callTool` monkey-patch (cleaner than killing
chroma-mcp mid-run) → asserts `{ chroma: false, degraded: true,
error: { message } }`; `context()` shape with degraded surfaced.
- `close.test.ts`: consumer-supplied pool stays open after
`client.close()`; SDK-owned pool closes; subsequent calls throw
"cmem-sdk: client is closed"; `close()` is idempotent.
Test count: 2065 → 2071 pass, 22 → 23 skip, 0 fail. The skips expand
to ~12 integration assertions when `CLAUDE_MEM_TEST_POSTGRES_URL` +
uvx are available.
Example (`examples/sdk-node/`)
------------------------------
Plain-Node (not Bun) script proving the headline requirement: capture
→ generate → search inline, with no worker or daemon running. ESM
module imports `claude-mem/sdk` from the local `file:../../` package.
- `index.mjs`: reads `CLAUDE_MEM_SERVER_DATABASE_URL` +
`ANTHROPIC_API_KEY` from env, bails clearly if missing, runs
`captureAndGenerate` then `search` then `context` and logs results,
then `client.close()`.
- `package.json`: type `module`, single dep `claude-mem`.
- `README.md`: prereqs (Postgres URL, API key, uvx), run command,
note that no worker is needed.
`node --check examples/sdk-node/index.mjs` → OK.
Docs (`docs/public/sdk.mdx` + nav)
----------------------------------
New Mintlify page "Using claude-mem in your app (SDK)" — 1341 words.
Covers:
- Headline: in-process capture/compress/search with no worker.
- Quick-start with minimal code sample.
- `createCmemClient` options table.
- Architecture diagram (mirror of the plan's top-of-file diagram,
marking Chroma REQUIRED).
- Chroma section: install uv/uvx; explanation of the
required-at-construction gate; runtime degradation semantics
(`{ degraded: true }` + `logger.error`).
- Tenancy: default-team + default-project bootstrap via
`sdk-tenant.json` vs. passing explicit `teamId`/`projectId`.
- Provider configuration: env-based (`ANTHROPIC_API_KEY` etc.) vs.
options-based (`{ apiKey, model, provider }`) vs. user-supplied
`CmemProvider` instance.
- Lifecycle: `close()` ownership rules.
- Error handling: Chroma init rejection, FTS fallback `{ degraded:
true }`, "client is closed" after `close()`.
`docs/public/docs.json` — new top-level nav group "SDK & Embedding"
(icon `code`) with the single `sdk` page, inserted after
"Configuration & Development". JSON validity confirmed.
Verification:
- `npm run build`: green; check:sdk-bundle clean.
- `dist/sdk/index.js`: 214.07 KB (no SDK runtime change in this phase).
- `npm run typecheck`: 0 errors.
- `bun test`: 2071 pass / 0 fail / 23 skip across 180 files.
Plan §8 (plans/2026-05-25-cmem-sdk-and-server-rename.md).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
claude-mem SDK — Plain-Node Example
A minimal Node script that proves the headline requirement of the
claude-mem/sdk package: capture, compress, and search observations
in-process, with no worker process running.
The example calls, in order:
createCmemClient({ databaseUrl })— opens the Postgres pool, bootstraps the schema, resolves tenancy, and starts theuvx chroma-mcpsubprocess.client.captureAndGenerate({ ... })— persists anagent_eventrow, creates aqueuedgeneration job, then runs the inline provider (Claude / Gemini / OpenRouter) and writes the resultingobservationrow plus theobservation_sourceslink in a single Postgres transaction.client.search({ query: 'OAuth' })— runs a semantic search against Chroma and hydrates the matching observations back from Postgres.client.context({ query: 'OAuth' })— same assearchbut returns the observations'contentjoined with\n\nfor direct injection into a prompt.await client.close()— closes Chroma + the SDK-owned pool.
Prerequisites
- Postgres, with the connection URL exported as
CLAUDE_MEM_SERVER_DATABASE_URL. The SDK runs idempotent schema bootstrap on construction, so an empty database is fine. uvxonPATH. The SDK spawns auvx chroma-mcpsubprocess for semantic search; Chroma is required (seesrc/sdk/index.tsand the plan's Executive Decision). Install with the astral-sh/uv one-liner.- One provider API key, depending on which generator you want:
ANTHROPIC_API_KEY(default)GEMINI_API_KEY(CLAUDE_MEM_SERVER_PROVIDER=gemini)OPENROUTER_API_KEY(CLAUDE_MEM_SERVER_PROVIDER=openrouter)
Run
From this directory:
npm install
CLAUDE_MEM_SERVER_DATABASE_URL=postgres://user:pass@host:5432/db \
ANTHROPIC_API_KEY=sk-ant-... \
node index.mjs
That's it. No worker or daemon required — no claude-mem worker start,
no claude-mem server start, no Redis, no Express. The SDK does the
compression inline and persists everything to Postgres + Chroma in one
process.
What you'll see
Roughly:
[sdk-node-example] creating client (no worker required)...
[sdk-node-example] client ready (teamId=…, projectId=…)
[sdk-node-example] capturing + generating one observation...
[sdk-node-example] generated observations: [
{ "id": "…", "kind": "discovery", "content": "Implementing OAuth flow with PKCE…" }
]
[sdk-node-example] searching for "OAuth"...
[sdk-node-example] search returned 1 result(s) (chroma=true, degraded=false):
- …: Implementing OAuth flow with PKCE…
[sdk-node-example] context (… chars, degraded=false):
---
Implementing OAuth flow with PKCE…
---
[sdk-node-example] client closed. No worker was running at any point.
If chroma=false and degraded=true, your uvx chroma-mcp subprocess
died between createCmemClient returning and the search call. The SDK
fell back to Postgres FTS so the call still returned hits, but the next
createCmemClient (cold start) will reject until uvx chroma-mcp can
launch again. See docs/public/sdk.mdx for
the degraded-mode contract.
Where the data lives
- Observations, events, jobs, sessions — in the Postgres database at
CLAUDE_MEM_SERVER_DATABASE_URL. The schema is created on first run. - Chroma vectors — in
~/.claude-mem/chroma/(or$CLAUDE_MEM_DATA_DIR/chroma/). - Default tenancy —
$CLAUDE_MEM_DATA_DIR/sdk-tenant.json. Production consumers should pass explicitteamIdandprojectIdtocreateCmemClientand skip this file entirely.