Sign inSign up

cylian/engram

By cylian

•Updated about 1 month ago

Persistent knowledge base MCP server with pluggable search backends and graph relation.

Image
Machine learning & AI
1

2.4K

cylian/engram repository overview

⁠Engram — Persistent Knowledge Base MCP Server

A Model Context Protocol⁠ server that gives AI agents persistent memory. Markdown files as source of truth, pluggable search backends, typed graph relations.

Website: engram-kb.org⁠ · Docker Hub: cylian/engram⁠

⁠Quick Start

⁠stdio

Your agent manages the server. Recommended for Claude Code, ChatGPT Desktop, Cursor.

claude mcp add --transport stdio engram -- \
  docker run -i --rm -v ./knowledge:/knowledge cylian/engram
⁠SSE

Persistent server on the network. Share knowledge across multiple agents.

docker run -d --name engram \
  -p 8192:8192 \
  -v ./knowledge:/knowledge \
  cylian/engram --transport sse

claude mcp add --transport sse engram http://your-host:8192/sse
⁠HTTP

Streamable HTTP. Session state lives in the server process: run one instance per URL.

docker run -d --name engram \
  -p 8192:8192 \
  -v ./knowledge:/knowledge \
  cylian/engram --transport streamable-http

claude mcp add --transport http engram http://your-host:8192/mcp

⁠Search Backends

Two backends ship out of the box. Switch with --backend or ENGRAM_BACKEND:

BackendDefaultDescription
xapian✓Fast full-text search with configurable stemming
sqliteSQLite FTS5 — query your index with standard SQL tools

Query syntax: with sqlite, tokens match literally (docker-proxy, port:8080 are safe) and a trailing * means prefix search. With xapian, boolean operators and wildcards apply, and a query with no result is retried with the spelling suggestion built from the index.

The backend is pluggable — see Custom Backend⁠ below.

⁠Tools

ToolDescriptionKey Parameters
rememberCreate or update an entry (upsert). Same title updates it; a similar but different title returns {"conflict": true, "candidates": [...]} without writing — retry with entry_id or force. Returns size and warnings (structure, size over 512 B / 1 KB, index update failure, missing link target).title, content, tags, entry_id, force
recallRead an entry with its graph relations (outgoing + backlinks). Returns size and last_modified.entry_id
searchFull-text search with optional tag filterquery, tags, limit
listBrowse entries sorted by titletags, limit
tagsList all tags with entry counts—
forgetDelete an entry (file and index). Returns warnings if the index could not be updated.entry_id
rebuildRebuild search index from Markdown files—

⁠Graph Relations

Link entries with kb://uuid#type URLs in Markdown content:

This service runs on [Saturn](kb://a1b2c3d4-...#runs-on)
and depends on [PostgreSQL](kb://f9e8d7c6-...#depends-on).

recall returns both directions, plus size metadata:

{
  "id": "a1b2c3d4-...",
  "title": "My API Service",
  "content": "...",
  "tags": ["..."],
  "size": 1024,
  "last_modified": "2026-03-14",
  "relations": {
    "out": [{"type": "runs-on", "id": "e5f6...", "title": "Saturn"}],
    "in": [{"type": "depends-on", "id": "b7c8...", "title": "Frontend App"}]
  }
}

Like HATEOAS⁠ for knowledge — every response carries the links to navigate the graph.

⁠Usage Examples

⁠Store knowledge

Ask your agent:

"Remember that our API runs on port 8080 and depends on PostgreSQL 15."

Engram creates a Markdown file with a unique UUID, indexes it, and confirms. The agent can now recall this fact in any future session.

"What do we know about PostgreSQL?"

Engram searches across all entries by content, title, and tags. Results are ranked by relevance.

⁠Navigate the graph

"What depends on PostgreSQL?"

If entries link to the PostgreSQL article with kb://uuid#depends-on, Engram returns all backlinks — showing every service that depends on it, without the agent having to search for each one.

⁠Share knowledge across agents

Start Engram with SSE or HTTP transport. Multiple agents — even from different providers (Claude, ChatGPT, Copilot) — connect to the same server. What one agent remembers, all others can recall.

Agent A: "Remember that the deploy key rotates every 90 days."
Agent B: "When does the deploy key expire?"
→ Agent B finds the answer immediately.

⁠Prompt Your Agent

Add this to your system prompt or project instructions to make your agent use Engram as a reflex, not an afterthought:

Engram is your persistent memory. Using it is mandatory, not optional.

Engram stores ZERO discoverable information. If you can derive it from
code, git history, configuration files, or existing documentation, it
does not belong in Engram. Engram captures decisions and their context,
diagnostics and their root causes, procedures learned the hard way —
the kind of knowledge that is lost when a conversation ends.

Before working on any topic: search Engram first. Always. Even if you think you know.
Before answering a question about infrastructure or architecture: search first.
Before proposing a solution: check if a past decision exists in Engram.

After resolving a diagnostic: remember the root cause and the fix.
After executing a procedure: remember the steps.
After making an architecture decision: remember the choice and the rationale.
After discovering something about the infrastructure: remember it.

⁠Configuration

All options have ENGRAM_* environment variable fallbacks. CLI args take priority.

OptionEnv varDefaultDescription
--data-pathENGRAM_DATA_PATH/knowledgeRoot path for knowledge data
--backendENGRAM_BACKENDxapianSearch backend
--languageENGRAM_LANGUAGEenStemmer language
--transportENGRAM_TRANSPORTstdioMCP transport
--hostENGRAM_HOST0.0.0.0Listen address (SSE/HTTP)
--portENGRAM_PORT8192Listen port (SSE/HTTP)

⁠Storage Format

Entries are Markdown files with YAML frontmatter in <data-path>/entries/:

---
id: a1b2c3d4-e5f6-7890-abcd-ef1234567890
title: Entry Title
tags: [infrastructure, postgresql]
---

Markdown content here...

The search index is a rebuildable cache in <data-path>/index/<backend>/. Delete it and rebuild — no data is ever lost.

⁠Custom Backend

Create backend/{name}/main.py with a class inheriting SearchBackend:

from backend import SearchBackend

class MyBackend(SearchBackend):
    def index(self, entry):
        """Index or update an entry (upsert)."""
        ...

    def unindex(self, entry_id):
        """Remove an entry from the index."""
        ...

    def search(self, query_str, tags, limit):
        """Full-text search. Return [{id, score}]."""
        ...

    def rebuild(self, entries):
        """Rebuild index from entries list. Return count."""
        ...

    def get_relations(self, entry_id):
        """Return {out: [{type, id}], in: [{type, id}]}."""
        ...

Then use it with --backend {name}. Engram loads it automatically via importlib.

The constructor receives <data-path>/index/{name} as first argument, plus language= if it accepts that parameter. Set the class attribute needs_rebuild = True when your index was recreated empty (schema change): KnowledgeBase rebuilds it from the Markdown files at startup. Index failures raised by index() / unindex() are reported to the agent as warnings, never as errors.

⁠Example: adding Whoosh backend
FROM cylian/engram:latest

# Install Whoosh
RUN pip install --no-cache-dir whoosh==2.7.4

# Add backend
COPY whoosh_backend/ /app/backend/whoosh/
docker build -t engram-whoosh .
docker run -i --rm -v ./knowledge:/knowledge engram-whoosh --backend whoosh

⁠Development

Tests run on both backends from a virtualenv (the image ships without tests/):

python3 -m venv .venv
.venv/bin/pip install -r src/requirements.txt
.venv/bin/pip install coverage pytest

# Test (every test runs on xapian and on sqlite)
.venv/bin/coverage run -m pytest tests/ -v
.venv/bin/coverage report
# Build
docker build -t engram .

# Run locally (SSE)
docker run -d --name engram -p 8192:8192 -v ./knowledge:/knowledge engram --transport sse

⁠License

MIT⁠

Tag summary

Content type

Image

Digest

sha256:d39ec3cfb…

Size

52.3 MB

Last updated

about 1 month ago

docker pull cylian/engram