--- title: "Mastra memory: threads, working memory and recall" description: "Mastra memory keeps message history, working memory and semantic recall per thread and resource. Add timestamped, source-backed recall with a Mastra tool or the MCP server." canonical: https://past.dev/integrations/mastra last-updated: 2026-10-09 --- # Add memory to Mastra agents Source: https://past.dev/integrations/mastra Mastra memory stores message history, working memory and semantic recall for an agent, scoped to a thread and to a resource such as a user. It needs a storage provider, and semantic recall also needs a vector store and an embedder. A working memory update replaces the earlier value. A Mastra tool that calls past.dev over HTTP adds timestamped ingestion and recall that returns ranked documents with dates and source excerpts. ## Mastra memory scope [Mastra memory](https://mastra.ai/docs/memory/overview) is configured with the `Memory` class from `@mastra/memory` and a storage provider. It offers message history (the last N messages, on by default), working memory, semantic recall, and observational memory, which keeps a condensed observation log in place of raw history as the conversation grows. Memory is scoped by thread and resource. A thread is one conversation, and its resource is a stable identifier for the user or entity that owns it. [Working memory](https://mastra.ai/docs/memory/working-memory) persists across all threads of the same resource by default. [Semantic recall](https://mastra.ai/docs/memory/semantic-recall) searches the current thread, or every thread of the resource with `scope: 'resource'`. - **Working memory holds one current value.** A Markdown template is replaced whole on each update, and a schema merges the fields the agent sends. The earlier value is gone after the update. - **Semantic recall returns messages.** It stores messages with their embeddings and returns similar messages with the messages around them. The documentation describes no validity period for what it returns. - **Memory lives in your storage.** Another application reads it only through the storage provider you configure and operate. ## The integration: a Mastra tool per endpoint A Mastra tool is created with `createTool` from `@mastra/core/tools`: an `id`, a `description`, an `inputSchema` and an `execute` function ([tools](https://mastra.ai/docs/tools-mcp/overview)). The second argument of `execute` carries the request context, which the application sets for each call and the model never fills ([request context](https://mastra.ai/docs/server/request-context)). Two tools connect an agent to past.dev; the [API reference](/docs/memory-api/api-reference) documents every public operation. ```typescript import { createTool } from "@mastra/core/tools"; import { z } from "zod"; const BASE = "https://api.past.dev/api/v1"; const headers = { Authorization: `Bearer ${process.env.PAST_API_KEY}`, "Content-Type": "application/json", }; // The signed-in user. Your application sets it on each request. const requestContextSchema = z.object({ userId: z.string() }); export const rememberFact = createTool({ id: "remember-fact", description: "Store timestamped source text, readable by the whole project, authored by the current user.", inputSchema: z.object({ text: z.string(), happenedAt: z.string().describe("ISO 8601 event time"), }), requestContextSchema, execute: async ({ text, happenedAt }, context) => { const identity = context.requestContext?.get("userId"); const r = await fetch(`${BASE}/ingest`, { method: "POST", headers, body: JSON.stringify({ content: text, timestamp: happenedAt, identity }), }); return r.json(); }, }); export const recallMemory = createTool({ id: "recall-memory", description: "Return ranked documents with dates and source excerpts for the current user.", inputSchema: z.object({ question: z.string() }), requestContextSchema, execute: async ({ question }, context) => { const identity = context.requestContext?.get("userId"); const r = await fetch(`${BASE}/recall`, { method: "POST", headers, body: JSON.stringify({ query: question, identity }), }); return r.json(); }, }); ``` Add both tools to the agent's `tools` property. Keep Mastra memory for the conversation, and route durable facts through `rememberFact` with the time they happened. ```typescript import { Agent } from "@mastra/core/agent"; import { recallMemory, rememberFact } from "../tools/past"; export const assistant = new Agent({ id: "assistant", name: "Assistant", instructions: "Call recallMemory before you answer questions about people, decisions or history. " + "Call rememberFact for durable facts, with the time they happened.", model, // any model Mastra supports tools: { rememberFact, recallMemory }, }); ``` On each request, set the signed-in user in a `RequestContext` and pass it to the agent call. ```typescript import { RequestContext } from "@mastra/core/request-context"; const requestContext = new RequestContext<{ userId: string }>(); requestContext.set("userId", session.userId); // the signed-in user, from your auth const result = await assistant.generate("What did we decide about the Q4 budget?", { requestContext }); ``` > **Identity** > > The application sets the identity. The model never chooses it. Recall returns what the identity in the request may read, so take the identity from the signed-in user in code, and keep it out of every value that the model fills. Recall returns ranked documents with dates and source excerpts. The application decides whether those documents support an answer, contain a disagreement, or are insufficient; HTTP failures are handled separately. ## Or connect the MCP server Mastra's `MCPClient` from `@mastra/mcp` connects to a server by URL over Streamable HTTP, and `listTools()` returns its tools for an agent ([MCP overview](https://mastra.ai/docs/tools-mcp/mcp-overview), [MCPClient reference](https://mastra.ai/reference/tools/mcp-client)). The project's end-user MCP server then gives the agent `recall`, `answer` and `who_am_i`, plus `remember` while the project's write toggle is on, with no tool code to write. 1. Turn on the project's end-user MCP server on **Build › MCP server** in the console. 2. Mint an access link there, or with the account server's `create_mcp_access_link` tool. The link has the form `https://api.past.dev/mcp//link/`. It is one URL that is one identity, and the server answers it with no sign-in step. 3. Keep the link in a secret store, such as the `PAST_MCP_URL` environment variable that the code below reads. The secret in its path is the credential. ```typescript import { Agent } from "@mastra/core/agent"; import { MCPClient } from "@mastra/mcp"; const past = new MCPClient({ id: "past", servers: { past: { url: new URL(process.env.PAST_MCP_URL!) }, }, }); export const assistant = new Agent({ id: "assistant", name: "Assistant", instructions: "Call the recall tool before you answer questions about people, decisions or history.", model, // any model Mastra supports tools: await past.listTools(), }); ``` `listTools()` namespaces each tool name with its server name, so the tools of several servers do not collide. Every call through the link runs as its identity, with that identity's audiences. Mint one link per install, so that a revoked link stops one agent only. When the agent serves several people, mint one link per end user, and connect each request with the link of the user who makes it. Never share one link between users. `remember` writes to the identity's own private audience, and only that identity recalls it. Send data that the whole project must recall through the ingest call. The [end-user server documentation](/docs/mcp/serving-your-own-users) describes the tools, the links and revocation. ## Mastra memory and past.dev side by side | Requirement | Mastra memory | past.dev via tool | | --- | --- | --- | | Continue one conversation | Message history per thread | No | | Facts about one user across threads | Working memory per resource | Recall as that user's identity | | Value after a fact changes | The update replaces the earlier value | Both data points stay, each with its date | | What was true on a date | No documented query | `queryTimestamp` on recall | | Original time of a backfilled record | No documented field | `timestamp` on ingest | | Recall result | Similar messages | Ranked documents with dates and source excerpts | The [quickstart](/docs/memory-api/quickstart) shows how to ingest, wait until ingestion completes, and recall. The [benchmarks](/benchmarks) document how recall quality is measured. [Working memory](/glossary/working-memory) defines the term this page compares. ## Frequently asked questions ### Does Mastra have long-term memory? Yes. Working memory persists across the threads of one resource, and semantic recall can search every thread of a resource. Both need a storage provider, and semantic recall also needs a vector store and an embedder. ### Do I keep Mastra memory when I add a memory API? Yes. Mastra memory carries the conversation and the agent's working notes. The memory API keeps timestamped source text that other agents, applications and identities can recall with dates and source excerpts. ### Can a Mastra agent use past.dev without writing tools? Yes. Point an MCPClient at an access link of the project's end-user MCP server and pass the result of listTools to the agent. ## Related - [Memory for the Vercel AI SDK](https://past.dev/integrations/vercel-ai-sdk) - [Memory for the OpenAI Agents SDK](https://past.dev/integrations/openai-agents-sdk) - [Memory for Pydantic AI](https://past.dev/integrations/pydantic-ai) - [Working memory](https://past.dev/glossary/working-memory) - [Quickstart](https://past.dev/docs/memory-api/quickstart)