Semurg: local persistent memory for AI agents over MCP
13 October 2026 · Semurg · 7 min read · Markdown
announcementmcpagentsmemoryknowledge-graphgraph-rag
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.
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_contextstarts 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-Userheader 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 saysnothing_storedand lists the stored names it resembles asrelated. - Answers that say how well they are supported. A context reply carries a grounding state:
groundedwhen the stored text covers the question,grounded_by_meaningwhen a passage found by meaning answers it in other words, andrelatedwhen 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_ofreads memory as it was known on a date, andvalid_atas 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:
{"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"}]}}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:
curl -fsSL https://one.semurg.io/install | sudo bashThe 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:
sudo grep '^SEMURG_API_TOKEN=' /etc/semurg/semurg.envTo 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 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:
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:
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 listclaude 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 gives each repository its own scope in six lines of CLAUDE.md.
The agents page 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: 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: 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: one app, many users, none of them meeting.
- The develop page: the memory routes over HTTP, the limits each call refuses by name, and a rules file you can drop into your repository as
CLAUDE.mdorAGENTS.mdso a coding agent uses the memory well. Every route and parameter is listed in 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.