Add long-term memory to a Mastra agent (input/output processors, per-user recall) Mohammed, founder of MemorySync, published a guide showing how to add per-user long-term memory to Mastra agents using the memorysync-mastra package's input and output processors. The integration hooks into Mastra's processor pipeline to extract durable facts about each user and recall them across threads, with identity resolved per call via a custom resolver, static IDs, or Mastra's RequestContext. The author notes the code is adapted from documentation and was not run for the post. Disclosure: I'm Mohammed, founder of MemorySync. I drafted this article with AI assistance and reviewed it for accuracy against the official MemorySync and Mastra docs. The code is adapted from the docs and I haven't run it. Mastra agents already have memory. Give an agent a Memory from @mastra/memory and pass memory: { resource, thread } on each call: resource is a stable id for the user and thread isolates one conversation Mastra memory docs https://mastra.ai/docs/memory/overview . Mastra also has working memory and semantic recall, stored in whichever storage provider you configure. If that's working for you, keep it. This post is for when you want durable facts about each user extracted and recalled by a hosted service, wired in through Mastra's own processor pipeline. It uses MemorySync's Mastra integration, memorysync-mastra . MemorySync is a hosted memory layer, and I'm building it. Mastra behaviour comes from its docs on memory https://mastra.ai/docs/memory/overview and processors https://mastra.ai/docs/agents/processors . Everything about MemorySync comes from its Mastra Memory guide https://docs.memorysync.io/guides/mastra?utm source=devto&utm medium=social&utm campaign=pivot oct19&utm content=mastra-agent-long-term-memory-processors . The code is adapted from those docs. I haven't run it for this post, so check it against your versions. npm install memorysync-mastra Per the guide it supports @mastra/core 1.42+ Node 20+ for the package; current @mastra/core itself requires Node 22+ . npm currently lists 1.1.0 with peer @mastra/core =1.42.0 <2.0.0 . Set MEMORYSYNC API KEY in the environment, or pass apiKey , and keep it server-side. Mastra processors are hooks in the agent pipeline: inputProcessors run before messages reach the model, outputProcessors run after the model responds and before the result is returned processors docs https://mastra.ai/docs/agents/processors . MemorySync uses one of each: js import { Agent } from "@mastra/core/agent"; import { openai } from "@ai-sdk/openai"; import { createMemorySyncProcessors } from "memorysync-mastra"; const { inputProcessor, outputProcessor } = createMemorySyncProcessors { userId: "customer-7", // scopes memory to one end user sessionId: "thread-42", // groups the stored facts by thread } ; const agent = new Agent { id: "assistant", name: "Assistant", instructions: "You are a helpful assistant.", model: openai "your-model" , inputProcessors: inputProcessor , outputProcessors: outputProcessor , } ; await agent.generate "I'm vegetarian and I fly aisle." ; // Later, same user, any thread const { text } = await agent.generate "Book my trip: flight plus a dinner spot." ; Per the guide: generate / stream call it uses processInput , not the per-step hook , so multi-step tool loops don't add the block twice. Your instructions stay first; the memory block is appended after them. onError a console warning by default and never thrown. Prefer less wiring? withMemorySync config, { userId } wraps your agent config and merges both processors in, keeping any processors you already have MemorySync's run after yours . A Mastra server agent serves many users, so identity is resolved per call, in this priority order: js import { RequestContext } from "@mastra/core/request-context"; import { createMemorySyncProcessors, MASTRA RESOURCE ID KEY, MASTRA THREAD ID KEY, } from "memorysync-mastra"; // 1. Your own resolver wins over everything createMemorySyncProcessors { resolveIdentity: requestContext = { userId: currentSession.userId, sessionId: currentSession.threadId, } , } ; // 2. Static ids scripts, workers createMemorySyncProcessors { userId: "customer-7", sessionId: "thread-42" } ; // 3. Mastra's RequestContext, set by your server middleware const ctx = new RequestContext ; ctx.set MASTRA RESOURCE ID KEY, "customer-7" ; ctx.set MASTRA THREAD ID KEY, "thread-42" ; await agent.generate messages, { requestContext: ctx } ; // 4. Mastra's own memory ids on the call await agent.generate "I moved to Lisbon.", { memory: { resource: "customer-7", thread: "thread-42" }, } ; Option 4 is handy if you already use Mastra memory: the same resource id becomes the MemorySync user. If no source yields a user id, recall is skipped and the write is refused reported through onError , never sent to a default scope. Take the id from your auth session, not from the request body. js const stream = await agent.stream "Plan a dinner for my team.", { memory: { resource: "customer-7", thread: "thread-42" }, } ; for await const chunk of stream.textStream process.stdout.write chunk ; The user's message is sent once, after the full stream. Per the guide, a stream that errors mid-flight finishReason: "error" sends nothing, so the retry sends the message once. createMemorySyncProcessors { userId, mode: "query" } ; // relevant to the latest message default createMemorySyncProcessors { userId, mode: "profile" } ; // stable overview of the user createMemorySyncProcessors { userId, mode: "full" } ; // both createMemorySyncProcessors { userId, persist: false } ; // read-only createMemorySyncProcessors { userId, recall: false } ; // write-only createMemorySyncProcessors { userId, k: 12, template: "What you know about this user:\n{context}", } ; js import { createMemorySyncTools } from "memorysync-mastra"; const agent = new Agent { id: "assistant", name: "Assistant", instructions: "Use the memory tools to remember durable facts.", model: openai "your-model" , tools: { ...createMemorySyncTools { userId: "customer-7" } }, } ; // For agents you don't fully trust: search and list only createMemorySyncTools { userId: "customer-7", readOnly: true } ; The five tools are add memory , search memory , list memories , update memory tags and importance only and delete memory ; they return readable error strings instead of throwing. For workflows there are getMemoryContext , searchMemories and saveTurn . saveTurn sends only the user's message and throws on failure. The guide says a turn sent by both the processors and saveTurn is recognised server-side and extracted once. resource must match across calls. The thread can differ. @mastra/core 1.x from 1.42, and current core needs Node 22+. Does a Mastra agent remember previous conversations? Yes, if you give it a Memory from @mastra/memory and pass memory: { resource, thread } on each call. To recall earlier messages, call the agent with the same resource and thread values. lastMessages controls how many recent messages are included Mastra memory docs https://mastra.ai/docs/memory/overview . Where does Mastra store memory? In the storage provider you configure. Memory needs a storage provider to persist message history; the quickstart uses @mastra/libsql Mastra memory docs https://mastra.ai/docs/memory/overview . What's the difference between resource and thread in Mastra memory? resource is a stable id for the user or entity, and thread isolates one conversation. Each thread's owner can't be changed after creation, so don't reuse a thread id across owners. Working memory and semantic recall are resource-scoped by default, so they carry across that user's threads Mastra memory docs https://mastra.ai/docs/memory/overview . Can I use Mastra's Memory and the MemorySync processors together? Yes. MemorySync's guide recommends running both: Mastra's Memory for the conversation history, and the MemorySync processors for durable per-user facts across threads. The processors can also read the user and thread from the same memory: { resource, thread } option MemorySync Mastra guide https://docs.memorysync.io/guides/mastra?utm source=devto&utm medium=social&utm campaign=pivot oct19&utm content=mastra-agent-long-term-memory-processors . If you want to see the save, reload and recall loop before wiring up an agent, the MemorySync playground runs it in your browser: memorysync.io/try https://memorysync.io/try?utm source=devto&utm medium=social&utm campaign=pivot oct19&utm content=mastra-agent-long-term-memory-processors .