- 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
- 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
- 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
Quickstart
Add memoryMiddleware to a working chat route. The route recalls memory before each answer and saves each turn.
Quick setup#
1. Install the package#
npm i @tanstack/ai-memorypnpm add @tanstack/ai-memoryyarn add @tanstack/ai-memorybun add @tanstack/ai-memory2. Create the memory adapter#
Start with inMemory(). It keeps records in a Map in the server process:
// memory.ts
import { inMemory } from '@tanstack/ai-memory/in-memory'
export const memory = inMemory()3. Add the middleware to the server route#
Set the scope from your auth session. The onRecall log lets you see memory work:
// api/chat.ts
import {
chat,
chatParamsFromRequest,
toServerSentEventsResponse,
} from '@tanstack/ai'
import { openaiText } from '@tanstack/ai-openai'
import { memoryMiddleware } from '@tanstack/ai-memory'
import { memory } from './memory'
// Replace with your auth. Read the user from the session, never from the body.
function getUserId(_request: Request): string {
return 'demo-user'
}
export async function POST(request: Request) {
const params = await chatParamsFromRequest(request)
const userId = getUserId(request)
const stream = chat({
adapter: openaiText('gpt-6.1-sol'),
messages: params.messages,
threadId: params.threadId,
middleware: [
memoryMiddleware({
adapter: memory,
// In a real app, make sure that this thread belongs to userId.
scope: { threadId: params.threadId, userId },
onRecall: ({ result }) => {
console.log('memory recalled', result.fragments?.length ?? 0, 'items')
},
}),
],
})
return toServerSentEventsResponse(stream)
}4. Use the chat in the client#
Memory runs on the server only. The client is a normal chat client:
import { useState } from 'react'
import { fetchServerSentEvents, useChat } from '@tanstack/ai-react'
export function Chat() {
const [input, setInput] = useState('')
const { messages, sendMessage } = useChat({
connection: fetchServerSentEvents('/api/chat'),
threadId: 'support-chat',
})
return (
<>
{messages.map((message) => (
<div key={message.id}>
{message.parts.map((part, index) =>
part.type === 'text' ? <p key={index}>{part.content}</p> : null,
)}
</div>
))}
<form
onSubmit={(event) => {
event.preventDefault()
void sendMessage(input)
setInput('')
}}
>
<input value={input} onChange={(event) => setInput(event.target.value)} />
<button type="submit">Send</button>
</form>
</>
)
}For Vue, Solid, Svelte, and the other frameworks, use the client from the Quick Start. Nothing in it changes for memory.
5. Try it#
- Send My favorite language is TypeScript.
- Send Which language do I like?.
The server log shows memory recalled 2 items (or a similar count) for the second message. The first turn was saved and then recalled into the prompt.
How it works#
- Before the model runs, the adapter finds records in the same scope that match the last user message. They go into the system prompt.
- After the stream finishes, the adapter saves the user message and the assistant answer.
- inMemory() scores by word overlap and recency. Records are lost when the process restarts.
Move to production#
Use Redis#
redis() keeps memory across restarts and shares it between server processes. Change only memory.ts. The route stays the same:
// memory.ts
import Redis from 'ioredis'
import { redis } from '@tanstack/ai-memory/redis'
const client = new Redis(process.env.REDIS_URL ?? 'redis://localhost:6379')
export const memory = redis({ redis: client })Install ioredis (or redis) yourself. Both are optional peer dependencies. For node-redis and the other options, see redis().
To use a hosted service, export hindsight(), mem0(), or honcho() from memory.ts instead. See Adapters.
Add semantic scoring (optional)#
Word overlap misses a question that uses different words than the stored text. Add an embedder to inMemory() or redis() to also score by meaning:
// memory.ts
import OpenAI from 'openai'
import { inMemory } from '@tanstack/ai-memory/in-memory'
const openai = new OpenAI()
export const memory = inMemory({
embedder: {
async embed(text) {
const result = await openai.embeddings.create({
model: 'text-embedding-3-small',
input: text,
})
const embedding = result.data[0]?.embedding
if (!embedding) throw new Error('embedding request returned no vector')
return embedding
},
},
})The embedder runs on each recall (for the query) and on each save (for the stored text).
Your route now remembers what the user says and can move to Redis with a one-file change. To log memory in production and watch it in devtools, see Operating memory.