- MCP Server ToolsUpdated
- Managed MCP with chat()Updated
- Manual MCPUpdated
- MCP Type GenerationUpdated
- MCP Client InputUpdated
- MCP Server Tools: AdvancedNew
- MCP EventsNew
- MCP AppsUpdated
- Serve Tools over HTTPUpdated
- Serve Resources and PromptsUpdated
- Ask for InputUpdated
- Draft Text from a ToolUpdated
- MCP Server TasksUpdated
- MCP Server AuthUpdated
- MCP Server SessionsUpdated
- MCP Server on stdioUpdated
Persistence (Advanced)
- GenerationsUpdated
- Realtime Voice ChatUpdated
- Text-to-SpeechUpdated
- TranscriptionUpdated
- Transcription: AdvancedNew
- Voice CreationUpdated
- Audio RecordingUpdated
- Audio GenerationUpdated
- Image GenerationUpdated
- Image Generation: AdvancedNew
- Video GenerationUpdated
- Video Generation: AdvancedNew
- Live GenerationUpdated
- World GenerationUpdated
- Generation HooksUpdated
- Build your first harnessUpdated
- Host optionsNew
- Send and show mediaUpdated
- Connect clientsUpdated
- Write a client in any languageNew
- Access and tenantsNew
- Run in the terminalUpdated
- Deploy a harnessUpdated
- Send inputs safelyUpdated
- Run a tool without a model
- Durable sessionsUpdated
- Run background work on workersNew
- List, rename, and fork sessionsUpdated
- Lifecycle and retentionNew
- Run side effects onceUpdated
- Wait for a personNew
- Build on the session logUpdated
- Control how a turn endsUpdated
- Store settings per threadUpdated
- Compact a long sessionUpdated
- Fork and reset a threadUpdated
- One log for a session treeUpdated
- Write a pluginUpdated
- Track usage and costUpdated
- Ask before risky tool callsUpdated
- Give your agent coding toolsUpdated
- Sandbox and extend the coding toolsUpdated
- Run the coding tools for many threadsNew
- Undo a turnUpdated
- Build a coding agentUpdated
- Delegate to coding agentsUpdated
- Switch between agent profilesUpdated
- Auth and connectorsUpdated
- Share a thread between peopleUpdated
- Connect model providersUpdated
- Build your own UIUpdated
- Code mode in a harnessUpdated
- Work until a goal is metUpdated
- Run agents from a harnessUpdated
- Hand off a conversationNew
- Add skillsUpdated
- Use MCP serversUpdated
- Give your agent a computerNew
- Use from any MCP clientUpdated
- Use a harness from an editor (ACP)Updated
- OverviewUpdated
- Quick StartUpdated
- ProvidersUpdated
- HarnessesUpdated
- Harness AuthUpdated
- WorkspaceUpdated
- ToolsUpdated
- PolicyUpdated
- Lifecycle & SnapshotsUpdated
- Portable SnapshotsUpdated
- Keep Files After ReloadUpdated
- Save a Named VersionUpdated
- Branch From a VersionUpdated
- Send a Frozen FileUpdated
- Let the Agent Save and ForkUpdated
- Pick Which Files to KeepUpdated
- What a Snapshot StoresUpdated
- Instance DurabilityUpdated
- Durable RunsUpdated
- EventsUpdated
Sandboxes (Advanced)
- Runtime ContextUpdated
- Bring Your Own KeyUpdated
- Keep Keys on the ServerNew
- Debug LoggingUpdated
- Multimodal ContentUpdated
- Files APIUpdated
- Per-Model Type SafetyUpdated
- Runtime Adapter SwitchingUpdated
- Tree-ShakingUpdated
- Extend AdapterUpdated
- Extend Adapter: AdvancedNew
- Typed Pre-Configured OptionsUpdated
- Test with a Fake ModelUpdated
- Prompt CachingUpdated
- Mid-Conversation ChangesUpdated
- Approval Flow ProcessingUpdated
Overview
Memory stores facts from earlier turns and puts them back into the next request. The model then keeps the user's name, stack, and preferences.
memoryMiddleware finds the memory that matters for each new message, adds it to the prompt, and saves each finished turn for later.
Quick setup#
1. Install the package#
npm i @tanstack/ai-memorypnpm add @tanstack/ai-memoryyarn add @tanstack/ai-memorybun add @tanstack/ai-memory2. Add the middleware to chat()#
import { chat } from '@tanstack/ai'
import { openaiText } from '@tanstack/ai-openai'
import { memoryMiddleware } from '@tanstack/ai-memory'
import { inMemory } from '@tanstack/ai-memory/in-memory'
const memory = inMemory()
const stream = chat({
adapter: openaiText('gpt-6.1-sol'),
messages: [{ role: 'user', content: 'Hello' }],
threadId: 'demo-thread',
middleware: [
memoryMiddleware({
adapter: memory,
scope: { threadId: 'demo-thread', userId: 'alice' },
}),
],
})That is all the wiring. Memory runs on the server only, so your client code does not change.
The Quickstart puts this in a real server route with a client, and then makes it ready for production.
How it works#
Each run has two memory steps:
- Recall. Before the model runs, the middleware sends the last user message to adapter.recall(scope, query). The adapter returns a prompt block, and the middleware adds it to the system prompts. Some adapters also return tools that let the model read and write memory.
- Save. After the stream finishes, the middleware gives the { user, assistant } turn to adapter.save(scope, turn). The save is deferred, so it never blocks the response.
The adapter does the rest: it extracts, ranks, renders, and stores. The middleware never reads a stored record.
If recall or save fails, the chat continues without memory. See Operating memory.
Pick an adapter#
| You need | Use |
|---|---|
| Local development, tests, a demo | inMemory() |
| Memory that survives a restart and is shared across processes | redis() |
| A hosted memory service | hindsight(), mem0(), or honcho() |
| A backend that is not in the list | A custom adapter |
Every adapter has the same recall and save methods, so you can swap one for another and keep the rest of your code. Adapters lists the options for each one.
Tip
Memory is for context from earlier turns or earlier sessions. To keep the last few messages of the current request, send them in messages. You do not need memory for that.
Scope: whose memory is it#
scope tells the adapter which memory to read and write. MemoryScope is the same Scope type that @tanstack/ai uses for persistence:
type MemoryScope = {
threadId: string // required, the same value as ChatMiddlewareContext.threadId
userId?: string
tenantId?: string
namespace?: string // reserved, no adapter uses it yet
}Set the scope on the server, from the session of the signed-in user:
- Read userId and tenantId from your auth session. Never read them from the request body.
- If the client sends threadId, make sure that the thread belongs to the session user before you use it.
Use the function form of scope to set the scope for each request. It gets the chat context:
import { memoryMiddleware } from '@tanstack/ai-memory'
import { inMemory } from '@tanstack/ai-memory/in-memory'
// Replace with your auth. Return the user from the session, not from the body.
function getUserId(): string {
return 'demo-user'
}
const middleware = memoryMiddleware({
adapter: inMemory(),
scope: (ctx) => ({ threadId: ctx.threadId, userId: getUserId() }),
})The built-in adapters match every scope field exactly. A read with no tenantId does not find a record written with a tenantId. For the fields that each adapter uses, see Which Scope fields each adapter honors.
Next#
- Quickstart: a full server route, a client, and a production adapter.
- Adapters: every adapter and its options.
- Custom Adapter: write recall and save for your own backend.
- Operating memory: telemetry, devtools, and what happens when memory fails.