Files
Alex Newman e22824295b docs(sdk): cmem-sdk Phase 8 — tests, plain-Node example, docs page
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>
2026-05-29 15:22:27 -07:00
..

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:

  1. createCmemClient({ databaseUrl }) — opens the Postgres pool, bootstraps the schema, resolves tenancy, and starts the uvx chroma-mcp subprocess.
  2. client.captureAndGenerate({ ... }) — persists an agent_event row, creates a queued generation job, then runs the inline provider (Claude / Gemini / OpenRouter) and writes the resulting observation row plus the observation_sources link in a single Postgres transaction.
  3. client.search({ query: 'OAuth' }) — runs a semantic search against Chroma and hydrates the matching observations back from Postgres.
  4. client.context({ query: 'OAuth' }) — same as search but returns the observations' content joined with \n\n for direct injection into a prompt.
  5. 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.
  • uvx on PATH. The SDK spawns a uvx chroma-mcp subprocess for semantic search; Chroma is required (see src/sdk/index.ts and 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 explicit teamId and projectId to createCmemClient and skip this file entirely.