---
title: Persistent memory for CrewAI agents, on your own machine
slug: semurg-memory-for-crewai
date: 2026-10-25
author: Semurg
section: how-to
tags: [crewai, python, mcp, agents, memory, how-to]
summary: Give a CrewAI crew a memory that outlives the run, on your own disk, through CrewAI's own MCP adapter, with the exact program our tests run and what to do when the node restarts.
status: published
covers: [route:POST /v1/mcp, route:POST /v1/memory/recall, mcp:memory_remember, mcp:memory_recall]
constants: [24.04, 3.12]
---
> Last checked on r11.132.20261027.a274a28 (27 Oct 2026): every command below ran on a fresh node of that release. The current release is r11.135.20261028.2ff2348 (28 Oct 2026), and the commands have not been re-checked on it yet.

> Tested end to end with `crewai 1.15.30`, `crewai-tools 1.15.30` and `mcp 1.30.0`. The release it ran on is named in the checked line at the top of this page.

A CrewAI crew forgets what its agents learned when the run ends. This guide gives the crew a memory that outlives the run: a Semurg node
on your own machine, reached through the MCP adapter in `crewai-tools`, where every fact stays on your disk and comes back cited to where
it was said. Any other agent or framework that uses the same node and the same scope reads the same memory.

> Diagram crewai-memory: Your CrewAI program stores a fact through the MCP adapter in crewai-tools; your node keeps it in the scope you named, on your own disk; a later run, another agent or a plain HTTP call on the same scope reads it back, cited.

## What you need

- An Intel or AMD x86_64 machine with Ubuntu 24.04, on Linux directly or on Windows through WSL2. One node is free, with no licence key.
- Python 3.12, which Ubuntu 24.04 ships. We test in the official `python:3.12-slim` container.

## Step one: install a node

On Ubuntu 24.04 (x86_64), with sudo:

```bash
curl -fsSL https://one.semurg.io/install | sudo bash
```

The [install page](/install) lists what the installer needs and what it changes. The installer ends by printing your API token, and the
node keeps a copy. Put it in a file only you can read, and have every new terminal load it:

```bash
mkdir -p ~/.config/semurg
(umask 077; sudo grep '^SEMURG_API_TOKEN=' /etc/semurg/semurg.env | cut -d= -f2- > ~/.config/semurg/token)
echo 'export SEMURG_API_TOKEN="$(cat ~/.config/semurg/token)"' >> ~/.bashrc
export SEMURG_API_TOKEN="$(cat ~/.config/semurg/token)"
```

## Step two: install CrewAI's MCP tools

Ubuntu keeps its own Python packages apart, so install into a virtual environment of your own. Keep the quotes: some shells read the
brackets otherwise.

```bash
sudo apt install -y python3-venv
python3 -m venv ~/semurg-crewai && . ~/semurg-crewai/bin/activate
pip install 'crewai-tools[mcp]'
```

## Step three: connect

Save the program below as `snippet.py`. It is the program our tests run as written, shown here from the [agents page](/agents#crewai)
rather than copied, so what you read is always the file that ran.

```python
# Run: python snippet.py   (SEMURG_API_TOKEN must be set in the environment)
import os
from crewai_tools import MCPServerAdapter

URL = os.environ.get("SEMURG_MCP_URL", "http://127.0.0.1:4000/v1/mcp")
HEADERS = {"Authorization": "Bearer " + os.environ["SEMURG_API_TOKEN"],
           "X-Semurg-Scope": os.environ.get("SEMURG_MCP_SCOPE", "default")}
FACTS = {"entities": [{"name": "Ada", "type": "person"}, {"name": "Acme", "type": "organisation"}],
         "relations": [{"source": "Ada", "target": "Acme", "type": "WORKS_AT"}]}

params = {"url": URL, "transport": "streamable-http", "headers": HEADERS}
with MCPServerAdapter(params, connect_timeout=60) as tools:
    by = {t.name: t for t in tools}
    by["memory_remember"].run(facts=FACTS)
    print(by["memory_recall"].run(subject="Ada"))
```

*Ran: this exact file, end to end against a fresh node on release r11.132.20261027.a274a28, with crewai 1.15.30, crewai-tools 1.15.30, mcp 1.30.0. Install: `pip install 'crewai-tools[mcp]'`. Its run record: https://one.semurg.io/agents#crewai*

It reads your token from `SEMURG_API_TOKEN`, the node's address from `SEMURG_MCP_URL` (without it, `http://127.0.0.1:4000/v1/mcp`) and
the scope from `SEMURG_MCP_SCOPE` (without it, the scope named `default`). It opens an `MCPServerAdapter` over streamable HTTP with the
token and the `X-Semurg-Scope` header, stores two facts with `memory_remember` (Ada works at Acme) and prints what `memory_recall`
returns for Ada. The adapter closes when the `with` block ends.

## Step four: run it

Give the program a scope of its own, so it never writes into a memory you use for real work:

```bash
export SEMURG_MCP_SCOPE=try-crewai
python snippet.py
```

CrewAI prints the tool's answer as it hands it back: the node's answer for Ada, with `state` set to `answered` and the `WORKS_AT` fact
that links Ada to Acme, each item cited. Storing the same facts again changes nothing, so it is safe to run twice.

Now read the same fact from outside CrewAI, over plain HTTP. What one client writes, any other client on the same node and scope reads:

```bash
curl -sS http://127.0.0.1:4000/v1/memory/recall \
  -H "Authorization: Bearer $SEMURG_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"scope":"try-crewai","subject":"Ada"}'
```

## The memory tools in CrewAI

Inside the `with` block, the adapter yields each memory tool as a CrewAI tool with the same name. You can run one yourself, as the
program does, or pass the list to an agent's `tools` and let the model choose (our test runs the tools directly, without a model).

- `memory_status`: reports whether shared memory is on for this node and which of your scopes hold facts.
- `memory_remember`: stores entities, the relationships between them and the passages they came from, or plain text, with an optional source label and date.
- `memory_recall`: recalls what is stored about one named entity, its own facts first, each item cited.
- `memory_relate`: adds one typed relationship between two entities in a scope, creating an end that is not stored yet.
- `memory_context`: answers a question in plain words with cited items and a verdict on whether the store covers it.
- `memory_document`: takes a whole document (PDF with a text layer, Word, HTML or plain text) and stores it as cited passages in a scope.
- `memory_forget`: deletes one thing per call: an entity and everything that names it, one fact, one document, every document under a source label, or the whole scope. It is unreachable at once and not yet wiped from the disk.
- `memory_export`: reads everything a scope holds, one page per call: send each reply's `next` back as `cursor` until it is empty. The records are the entities, the facts with their dated sources and whether each is current or ended, and each stored document once; a forgotten passage is never exported.
- `memory_import`: stores the records `memory_export` produced into any scope, through the same writes `memory_remember` makes. A page sent twice stores nothing twice, and the reply names every record it skipped.

Every answer says its `state`: `answered`, `nothing_stored` (nothing stored matched, so the agent should say it does not know) or `could_not_judge`.

## Troubleshooting

- `KeyError: 'SEMURG_API_TOKEN'`: the token is not in this shell. Run the last line of step one, or open a new terminal.
- An error that names `401` (unauthorised): the token is wrong or belongs to another node. Read it again from the node as in step one.
- After the node restarts (an update restarts it), an open CrewAI adapter did not recover by itself in our October 2026 restart test, run on an earlier release. Leave the `with` block and open a new `MCPServerAdapter`; a new one answers at once.
- If the node is not running or is stalled, a CrewAI call can wait instead of failing. Check the node with `curl -s http://127.0.0.1:4000/api/health`, and set the per-call timeout your framework offers.
- CrewAI checks each call's arguments against the tool's own schema, so an argument the tool does not declare is refused before it reaches the node. Use the argument names the tool lists.

Use `127.0.0.1` rather than `localhost`: on Ubuntu `localhost` can resolve to the IPv6 address first, and the node listens on IPv4 loopback
only. To reach a node on another machine, set `SEMURG_MCP_URL`, or forward its port with `ssh -N -L 4000:127.0.0.1:4000 you@your-node`.

## Next

- The [agents page](/agents): every framework program, with its versions and the release it last ran on in our tests.
- [Give your agent persistent memory with MCP in five minutes](/blog/agent-memory-mcp-five-minutes): the memory calls over plain HTTP too.
- [Connecting to Semurg over MCP](/blog/connect-over-mcp): the endpoint, the protocol versions it speaks and how a refusal comes back.
- If a step does not work, tell us on the [contact page](/contact), with the reply you got.

*This post was written with AI assistance. Every result in it is checked against a published release or record before the post goes out.*
