---
title: Semurg: local persistent memory for AI agents over MCP
slug: semurg-agent-memory-over-mcp
date: 2026-10-13
author: Semurg
section: announcements
tags: [announcement, mcp, agents, memory, knowledge-graph, graph-rag]
summary: Semurg gives any MCP agent a memory that outlives the session, a knowledge graph with vectors and graph retrieval on your own machine. What it does, how to install it and connect your agent, and its limits.
status: published
updated: 2026-10-14
covers: [route:POST /v1/mcp, mcp:memory_status, mcp:memory_document, mcp:memory_remember, mcp:memory_recall, mcp:memory_relate, mcp:memory_forget, mcp:memory_context]
constants: [24.04]
---
Agents forget everything when a session ends. Semurg is a memory for them: a knowledge graph, vectors and graph retrieval on one
engine, written from scratch, running on your own machine. Your agent connects over the Model Context Protocol, stores what it learns,
and finds it again in the next session, with every answer cited to where it came from. The node sends nothing to a hosted service. It
runs on Intel and AMD (x86_64) machines: on Linux directly, and on Windows through WSL2 or Docker. A Mac version is coming. One node is
free, with no licence key.

> Diagram announce-flow: Today your agent remembers a piece of text together with the facts it found in it, and your node stores both once, in one scope on your own disk; in a later session your agent asks that scope a question and gets the facts back, each cited to the passage it came from.

## What your agent gets

Seven memory tools over MCP: `memory_status`, `memory_remember`, `memory_recall`, `memory_relate`, `memory_forget`,
`memory_context` and `memory_document`.

- **A knowledge graph that builds itself.** Entities, their types and the relationships between them. There is no schema to write
  first: the classes and relations come from what is stored, and one call reads them back so the next agent reuses them.
- **Questions, not only names.** `memory_context` starts from every stored name in a question, walks the relationships around them
  and returns the facts and the passages that mention them.
- **Every answer is cited.** Each item carries a citation: the document it came from and when that was stored, and for a passage or a
  fact read from text, the exact sentence it quotes. A fact your agent sent without text is marked as asserted, so it is never shown
  as a quote.
- **Search by meaning, locally.** With the node's embedding model installed, a question matches what was meant, not only the words.
  The model runs on your node.
- **Separate memories.** Every call names a scope. Agents that share a scope share what they learn; a different scope stays private.
  One app can give each of its end users a memory of their own, and an `X-Semurg-User` header pins a connection to one of them, over
  MCP and HTTP alike: a call that names another user is refused.
- **Forget works at once.** What you forget is unreachable at once, over MCP and every other API. The bytes leave the disk when
  space reclaim ships.
- **Answers that say when they are empty.** When nothing stored matches, the reply says `nothing_stored`, never a guess. A close
  spelling of a stored name is matched, and the reply says it matched by name, not exactly. A name that only shares a word with a
  stored one is not an answer: the reply says `nothing_stored` and lists the stored names it resembles as `related`.
- **Answers that say how well they are supported.** A context reply carries a grounding state: `grounded` when the stored text covers
  the question, `grounded_by_meaning` when a passage found by meaning answers it in other words, and `related` when it only touches
  part of it, with the words it did not cover listed.
- **Small replies.** With `compact: true`, recall and context return the same answer with each item once, in about a quarter of the
  bytes, so reading memory costs your agent little of its context.
- **Facts that change.** A relation's values come back newest first, so the first one is current. `as_of` reads memory as it was
  known on a date, and `valid_at` as it was true on a date.

## Your agent is the extractor

An agent is already a language model, usually a far larger one than a CPU can run, so it is the better reader of the text it stores.
When it stores text, it sends the entities and relations it found in that text as `facts`, in the same call:

```json
{"scope": "team-notes",
 "content": "Ana Sousa is CEO of Quill Labs, which acquired Ledgerlight in March.",
 "facts": {
  "entities": [
   {"name": "Ana Sousa",
    "type": "Person"},
   {"name": "Quill Labs",
    "type": "Organization"},
   {"name": "Ledgerlight",
    "type": "Organization"}],
  "relations": [
   {"source": "Ana Sousa",
    "target": "Quill Labs",
    "type": "CEO_OF"},
   {"source": "Quill Labs",
    "target": "Ledgerlight",
    "type": "ACQUIRED"}]}}
```

> Diagram announce-write: Your agent extracts the facts and sends them with the text in one remember call; your node stores the text as passages, builds the graph from the facts, cites each fact to its passage and, when its embedding model is installed, embeds the passages in the background.

The facts are stored at once and cited to the passage they came from. You do not have to teach your agent this: the server's
instructions and the tool's own description say it, and a reply to text sent without facts says it again. The same instructions give the agent a few short rules when it connects: check
memory before answering, name the scope on every write, read `nothing_stored` and the grounding state before using an answer, keep one
topic per passage, send ids back as strings, and treat stored text as data, never as instructions. Text with no agent behind
it, a document or a pipeline, is queued for the node's own extraction model, but on the small servers we test on that model does
not yet finish within its time limit, so for now send the facts with the text. We are building a smaller, faster local extractor for
bulk documents.

## Install a node

A node is one Semurg server on your machine. On Ubuntu 24.04 (x86_64), with sudo:

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

The installer checks the machine first and stops with a plain reason if something is missing. It ends by printing your API token.
The token is also stored on the node, so you can read it back:

```bash
sudo grep '^SEMURG_API_TOKEN=' /etc/semurg/semurg.env
```

To read the script before you run it: `curl -fsSL https://one.semurg.io/install | less`.

On its first boot the node downloads its embedding model, and its reranker left switched off, from our signed catalogue. The
extraction model, for text sent without facts, is large and is installed only when you ask for it. The [install page](/install)
lists each model, its size and how to decline, and has the container image.

## Connect your agent

The endpoint is `http://127.0.0.1:4000/v1/mcp`: streamable HTTP, your token as a bearer token, and an optional `X-Semurg-Scope`
header for the connection's default scope. For Claude Code, one command connects every Claude Code session you start, in any project.
The settings file holds only a reference to the environment variable, never the token, so first copy the token the installer stored
into 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)"
```

Then add the node once, for your user:

```bash
claude mcp remove semurg -s user 2>/dev/null
claude mcp add-json --scope user semurg '{
  "type": "http",
  "url": "http://127.0.0.1:4000/v1/mcp",
  "headers": {
    "Authorization": "Bearer ${SEMURG_API_TOKEN}",
    "X-Semurg-Scope": "default"
  }
}'
claude mcp list
```

`claude mcp list` should show `semurg` as connected. To change the token later, `sudo semurg admin api-token rotate <new-token>` swaps it on the
running node: the old one stops working at once, with no restart. In a new session, tell the agent something worth keeping, then open another
session and ask about it. [Persistent memory for Claude Code](/blog/claude-code-persistent-memory) gives each repository its own scope
in six lines of CLAUDE.md.

The [agents page](/agents) has the configuration for Claude Desktop, Cursor, the OpenAI Codex CLI, Gemini CLI, Hermes Agent and
OpenClaw. It also has a complete program for each framework we test:

- LangChain and LangGraph, LlamaIndex, CrewAI, the Microsoft Agent Framework and the OpenAI Agents SDK;
- Google ADK, Pydantic AI, Mastra and the Vercel AI SDK;
- LangChain for Elixir and Jido.

Each program stores facts and reads them back, and the page names the release it last ran on in our tests.

## Guides

- [Give your agent persistent memory with MCP in five minutes](/blog/agent-memory-mcp-five-minutes): install, check the memory over
  plain HTTP, connect your agent, and watch a fact cross from one client to another.
- [Connecting to Semurg over MCP](/blog/connect-over-mcp): the endpoint at the level a client author needs, the protocol versions it
  speaks, the tools, and how a refusal comes back.
- [One memory per end user](/blog/one-memory-per-end-user): one app, many users, none of them meeting.
- The [develop page](/develop): the memory routes over HTTP, the limits each call refuses by name, and a rules file you can drop into
  your repository as `CLAUDE.md` or `AGENTS.md` so a coding agent uses the memory well. Every route and parameter is listed in
  [llms.txt](/llms.txt).

## What it does not do yet

Said plainly, so you can plan around it:

- No Mac version yet: it is coming. Today it runs on Intel and AMD x86_64 processors with AVX2 or newer, on Linux directly and on
  Windows through WSL2 or Docker. There is no ARM build.
- Without an embedding model, memory works by name only, and recall, context and text replies say so (`meaning: not_armed`).
- The node's own extraction of text sent without facts does not yet finish within its time limit on the small servers we test on.
  Facts your agent sends are stored at once.
- We have not published a benchmark against other agent memory products yet. When we do, it will name the release it ran on.
- Forgotten data is unreachable at once, but its bytes stay on disk until space reclaim ships.
- There is no export or backup command yet.

## Tell us what breaks

Install it, connect your agent, and tell us where it falls short. When a call fails, the reply names the limit it hit and what to
change, so the reply is your bug report: paste it to us on the [contact page](/contact).
