Anthropic SDK Integration
Chat history and memory for Claude apps
Every Claude conversation your app has, persisted, remembered, and searchable across sessions. No vector database or memory service to run alongside your app.
Blocks in, blocks out
The Messages API returns blocks. Store them as blocks.
Claude answers with response.content as an array of typed blocks. Keep it as an array between calls and the next tool_result finds its pair by id. Flatten it to a string and that pairing is gone.
Every block round-trips with its type and id. The next tool_result matches on tool_use_id, the assistant picks up mid-tool.
The tool_use_id becomes a note inside a sentence. Claude sees a stray tool_result on the next call with nothing to pair against.
What DialogueDB adds
A managed history for Claude, plus three things the SDK leaves you
Blocks handled. The same client covers the three concerns a Claude app runs into next.
Every user gets their own conversation
Two users chat with the same assistant and never see each other. Their history, memory, and search all stay separated with one line of code.
Returning users do not start over
Facts worth keeping stay with the user, not the transcript. The next conversation opens with what they already told your assistant.
Answer follow-ups from earlier sessions
Search past exchanges by meaning to pull the right context into the next call. No embeddings pipeline and no vector database to run alongside your app.
The integration pattern
Five calls around the request you already make
Load, save, call, save. Nothing wraps messages.create, and no adapter class sits between your code and Claude.
// 1. Load the stored conversation for this user
const dialogue = await db.getOrCreateDialogue({ id, namespace })
await dialogue.loadMessages({ order: "asc" })
// 2. Persist the incoming turn before the model call
await dialogue.saveMessage({ role: "user", content: input })
// 3. Call Claude with the rebuilt history
const response = await anthropic.messages.create({
model: "claude-opus-4-8",
max_tokens: 16000,
messages: toMessageParams(dialogue),
})
// 4. Store the blocks exactly as they came back
await dialogue.saveMessage({
role: "assistant",
content: response.content,
})
// 5. A tool result is a block, not a string
await dialogue.saveMessage({
role: "user",
content: [{ type: "tool_result", tool_use_id, content: result }],
})DialogueDB does not proxy the Messages API or ship a client wrapper. The calls above are the whole integration.
The same client does more
Same install, six more things it gets you.
Resume a conversation on any worker
Load the dialogue by id and rebuild the messages array from scratch, whichever instance handles the request.
dialogue.loadMessages({ order: "asc" })Facts and preferences across conversations
Store what the assistant should remember about a user, independent of any single transcript.
db.createMemory()Recall by meaning before you call Claude
Search stored memories against the new turn and put the matches in the system prompt.
db.searchMemories()Token counts alongside the message
Keep usage from each response on the message it belongs to, so cost per conversation is a query, not a guess.
metadata: { inputTokens }Sub-agent branches off a conversation
Give a delegated sub-agent its own transcript while the main conversation stays clean. Parent and child dialogues stay linked without mixing turns.
dialogue.createThread()Per-conversation scratchpad
Give each conversation its own state object for workflow flags, partial results, or in-progress context.
dialogue.saveState()Install in 3 steps
From npm install to the first persisted turn, in under five minutes.
Install the client
npm install dialogue-dbSet the API key
DIALOGUE_DB_API_KEY=...Save each turn
await dialogue.saveMessage({
role: "assistant",
content: response.content,
})No credit card. Free tier to start. Starter is $29/month when usage grows.
The reference example
A working turn function and a manual tool loop you can clone and run as-is, both showing a conversation that survives a restart.
Runs on Edge and serverless too. The client is HTTP-only, with no connection pool and no native dependencies.
Messages API
anthropic-sdk
A conversation that survives a simulated cold restart, plus a manual tool loop where every tool_use and tool_result block is persisted.
Open the exampleClaude Agent SDK
anthropic-agent-sdk
The same storage pattern behind an autonomous agent, with per-agent tracking and an audit trail of what each run did.
Open the exampleWhere DialogueDB fits
Four ways to keep a Messages API conversation, and what each one asks of you.
| Approach | Survives a restart | Keeps content blocks intact | Memory and search included |
|---|---|---|---|
| In-process array | No | Yes, until the process ends | No |
| Your own table | Yes | If you model JSON columns for it | Add a vector store and a pipeline |
| Standalone memory service | Yes, for extracted facts | Rarely, transcripts are summarized | Memory yes, full transcript no |
| DialogueDB | Yes | Yes, stored as sent | Yes, one API |
If you know the Messages API, you already know this
Everything nests under a namespace, and the concepts line up with what the Anthropic SDK already gives you.
The data model
namespace
scoped to one user or tenant
dialogue
messages, threads, state
indexed for semantic search
memory
facts and preferences, cross-conversation
indexed for semantic search
| Anthropic SDK | DialogueDB |
|---|---|
messages: MessageParam[] you pass to messages.create | dialogue.loadMessages({ order: 'asc' }) rebuilds it |
MessageParam | message, stored as sent |
content block array (text, tool_use, tool_result) | Kept as an array, exactly as returned |
role: 'user' | 'assistant' | Same role field on stored messages |
system prompt string | Composed from db.searchMemories() at request time |
usage.input_tokens, usage.output_tokens | metadata on the assistant message |
What DialogueDB sees
Only what's needed for conversation storage
Your call to the Messages API goes straight from your server to Anthropic. DialogueDB sits beside that request, never inside it.
The client only receives what you explicitly send: message content, memory values, and the identifiers you scope them with. Your Anthropic key, your user tokens, and everything else in your app never pass through it.
What we receive
- Message content blocks and roles
- Memory values and metadata
- Dialogue and namespace IDs
What we never see
- Your Anthropic API key
- User authentication tokens
- Your prompts, model settings, or tool schemas
Two independent calls from your server
Your server
src/turn.ts
Your code decides what goes where.
nothing routed through us
Anthropic
api.anthropic.com
The model call goes direct. DialogueDB never sees the request or the credential.
DialogueDB
Messages and memory only
Receives what you explicitly save. Nothing implicit, nothing extra.
Frequently asked questions
One API for messages, memory, and search
Add it to your Anthropic SDK app in minutes.