--- title: "Haystack agent memory: State, memory stores and recall" description: "The Haystack Agent keeps no messages between runs. Its State, its memory stores, and how to add timestamped, source-backed agent memory with a tool or an MCPToolset." canonical: https://past.dev/integrations/haystack last-updated: 2026-10-09 --- # Add memory to Haystack agents Source: https://past.dev/integrations/haystack Haystack agent memory has two parts. The Agent component keeps a State during one run and returns its messages, and the caller passes the conversation back on the next run. Memory across sessions comes from memory store integrations, which back retrievers, writers and memory tools with an external service. A tool that calls past.dev over HTTP adds timestamped ingestion and recall that returns ranked documents with dates and source excerpts. ## Haystack agent memory scope The [Agent](https://docs.haystack.deepset.ai/docs/agent) component of Haystack 3.3 combines a chat generator with tool execution. `agent.run(messages=[...])` takes the conversation as input and returns `messages` and `last_message`. The caller stores that history and passes it back on the next run. [State](https://docs.haystack.deepset.ai/docs/state) shares data between tools during one run, and `state_schema` returns its keys with the result. For memory across sessions, Haystack documents [memory stores](https://docs.haystack.deepset.ai/docs/mem0memorystore): integrations that back memory retrievers, memory writers and ready-made memory tools with an external memory service. - **History is the caller's data.** The Agent documentation describes no built-in message store. - **State ends with the run.** It carries tool results inside one run and is returned with the result. - **Long-term memory is an integration.** Each memory store takes its model of facts, time and scope from the service behind it. ## The integration: two tools The `@tool` decorator from `haystack.tools` converts a function into a `Tool`, with the name, description and parameter schema taken from the function and its `Annotated` hints ([tools](https://docs.haystack.deepset.ai/docs/tool)). The Agent takes a list of tools. A parameter mapped with `inputs_from_state` is filled from the Agent's State before the tool runs and is excluded from the schema the model sees ([State](https://docs.haystack.deepset.ai/docs/state)). ```python import os, requests from typing import Annotated from haystack.components.agents import Agent from haystack.dataclasses import ChatMessage from haystack.tools import tool BASE = "https://api.past.dev/api/v1" HEADERS = {"Authorization": f"Bearer {os.environ['PAST_API_KEY']}"} @tool(inputs_from_state={"user_id": "identity"}) def remember( text: Annotated[str, "Source text to store"], happened_at: Annotated[str, "ISO 8601 time the text was written or the event occurred"], identity: str, # injected from state, excluded from the model's schema ) -> str: """Store timestamped source text, readable by the whole project, authored by the current user.""" return requests.post(f"{BASE}/ingest", headers=HEADERS, json={ "content": text, "timestamp": happened_at, "identity": identity, }).text @tool(inputs_from_state={"user_id": "identity"}) def recall( question: Annotated[str, "The question to answer from memory"], identity: str, # injected from state, excluded from the model's schema ) -> str: """Return ranked documents with dates and source excerpts for the current user.""" return requests.post(f"{BASE}/recall", headers=HEADERS, json={ "query": question, "identity": identity, }).text agent = Agent( chat_generator=chat_generator, # any Haystack chat generator that supports tools tools=[remember, recall], system_prompt="Call recall before you answer questions about people, decisions or history.", state_schema={"user_id": {"type": str}}, ) result = agent.run( messages=[ChatMessage.from_user("What did we decide about the Q4 budget?")], user_id="demo-user", # the signed-in user, set by your application ) print(result["last_message"].text) ``` > **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 The `mcp-haystack` package provides `MCPToolset` and `StreamableHttpServerInfo` in `haystack_integrations.tools.mcp`, and the toolset goes to the Agent's `tools` ([MCPToolset](https://docs.haystack.deepset.ai/docs/mcptoolset)). 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. ```python import os from haystack.components.agents import Agent from haystack_integrations.tools.mcp import MCPToolset, StreamableHttpServerInfo past = MCPToolset(server_info=StreamableHttpServerInfo(url=os.environ["PAST_MCP_URL"])) agent = Agent( chat_generator=chat_generator, # any Haystack chat generator that supports tools tools=past, system_prompt="Call recall before you answer questions about people, decisions or history.", ) ``` 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. ## Choosing per requirement - Tool results inside one run: State. - The current conversation: the messages you pass to `agent.run` and store yourself. - Timestamped source history with stable source ids, identity-scoped recall and dated source excerpts: the tools or the MCP server above. The [quickstart](/docs/memory-api/quickstart) shows how to ingest, wait until ingestion completes, and recall. The [benchmarks](/benchmarks) document how recall is measured. [Context engineering](/context-engineering) covers how to place recalled documents in a prompt. ## Frequently asked questions ### Does the Haystack Agent remember previous conversations? The Agent returns the messages of a run, and the caller passes them back on the next run. Memory across sessions comes from a memory store integration or an external memory service. ### Can I use past.dev in a Haystack pipeline without an Agent? Yes. Any component can call the recall endpoint and pass the returned documents to the next component in the pipeline, for example a prompt builder. ## Related - [Memory for LlamaIndex](https://past.dev/integrations/llamaindex) - [Memory for LangChain](https://past.dev/integrations/langchain) - [Memory for DSPy](https://past.dev/integrations/dspy) - [Context engineering](https://past.dev/context-engineering) - [Quickstart](https://past.dev/docs/memory-api/quickstart)