Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

2 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Persistent Memory Hub MCP

An MCP server that gives AI agents long-term memory. Store facts, recall them by meaning, search by keyword, and carry context across conversations — even after the chat window is long gone.

Features

  • Semantic recall — ask "what did we decide about X?" and get the right memory even when you use different words (powered by vector embeddings)
  • Keyword search — BM25 full-text search via SQLite FTS5 for exact matches
  • Tag filtering — organize memories with metadata tags
  • Persistent — everything stored in SQLite, survives restarts
  • Zero infrastructure — no external databases, no APIs, no GPU needed
  • 7 ready-to-use MCP tools — works with Claude Desktop, Cursor, or any MCP client

Quick start

# 1. Clone and install
pip install -r requirements.txt

# 2. Start the server
python server.py

That's it. The server listens on stdio transport — the standard MCP protocol. Connect it to any MCP client and the 7 tools will appear automatically.

Tool reference

Tool What it does Example
memory_store(key, content, metadata) Save or update a memory memory_store("user-name", "Called Alice", {"tag": "identity"})
memory_recall(query, limit) Find by meaning (vector search) memory_recall("what's their name?", 3)
memory_search(keywords, limit) Find by keyword (FTS5) memory_search("Alice", 10)
memory_get(id) Get a specific memory by ID memory_get(1)
memory_list(tag, limit) List all, optionally filtered by tag memory_list("preferences")
memory_forget(id) Delete a memory memory_forget(3)
memory_stats Show database statistics memory_stats

Tool details

memory_store — Upserts by key. If key already exists, it updates the content and re-embeds it. The metadata dict can hold anything (tags, notes, timestamps). Example:

memory_store(
    key="deploy-command",
    content="Deploy with: ./deploy.sh staging",
    metadata={"tag": "devops", "project": "backend", "priority": "high"}
)

memory_recall — Uses cosine similarity on 384-dim sentence embeddings. You don't need to match exact words. "What's our deployment process?" will find the same "deploy-command" entry above, even though the words differ.

memory_search — Uses SQLite FTS5 with porter stemming and BM25 ranking for traditional keyword search. Great for finding exact terms, code snippets, or names.

Client configuration

Claude Desktop

Add to your claude_desktop_config.json:

{
  "mcpServers": {
    "memory-hub": {
      "command": "python",
      "args": ["/absolute/path/to/PersistentMemory/server.py"]
    }
  }
}

Replace /absolute/path/to/ with the actual path. Restart Claude — the 7 tools will appear in your tool list.

Cursor

In Cursor settings → Features → MCP Servers → Add new:

Name: Memory Hub
Type: command
Command: python /absolute/path/to/PersistentMemory/server.py

Custom Python client

import asyncio
from mcp import ClientSession, StdioServerParameters

async def main():
    async with ClientSession(
        StdioServerParameters(command="python", args=["server.py"])
    ) as session:
        await session.initialize()
        result = await session.call_tool("memory_store", {
            "key": "greeting",
            "content": "Hello from Python!",
            "metadata": {"tag": "test"}
        })
        print(result)

asyncio.run(main())

You can also skip MCP entirely and use MemoryStore directly:

from memory_store import MemoryStore

ms = MemoryStore()
ms.store("fact", "Python is great", {"tag": "language"})
results = ms.recall("what language?")
print(results)

How it works

Your prompt
     │
     ▼  MCP Protocol (stdio)
┌──────────────────────┐
│   fastmcp server     │  ← registers 7 tools
│   memory_store()     │
│   memory_recall()    │
│   memory_search()    │
│   ...                │
└─────────┬────────────┘
          │
┌─────────▼────────────┐
│   MemoryStore        │  ← business logic layer
└────┬────────────┬────┘
     │            │
┌────▼────┐ ┌────▼────────────┐
│Embedder │ │ Storage         │
│all-     │ │ SQLite + FTS5   │
│MiniLM-  │ │ vector BLOBs    │
│L6-v2    │ │ cosine sim      │
└─────────┘ └─────────────────┘

Embeddings

Each memory is converted to a 384-dimensional vector using sentence-transformers/all-MiniLM-L6-v2. This is a lightweight model that runs on CPU — no GPU required, no API calls. The vector captures the meaning of the text, not just the words.

Semantic search (memory_recall)

  1. Your query is converted to a vector
  2. All stored vectors are loaded from SQLite
  3. Cosine similarity is computed between query and every memory
  4. Top-K results sorted by similarity score (0 to 1)

Keyword search (memory_search)

SQLite FTS5 with porter unicode61 tokenizer provides BM25-ranked full-text search — the same algorithm used by search engines.

Project structure

PersistentMemory/
├── server.py           # MCP server — entry point
├── memory_store.py     # Business logic: connects tools → storage + embeddings
├── embeddings.py       # SentenceTransformer model + cosine similarity
├── storage.py          # SQLite CRUD + FTS5 search + vector read/write
├── requirements.txt
└── README.md

File roles

File Responsibility
server.py Defines the 7 MCP tools via @mcp.tool(), runs the server
memory_store.py Orchestrates store/recall/search/forget/list/stats
embeddings.py Loads the model once, provides embed() and cosine_similarity()
storage.py Raw SQLite operations, table creation, FTS5 triggers, query execution

Database

The server creates memories.db in the current working directory on first run.

  • WAL mode for better concurrent read performance
  • FTS5 virtual table auto-synced via triggers (insert/update/delete)
  • Embeddings stored as pickled numpy arrays in the embedding BLOB column

Managing the database

# Reset everything
rm memories.db
python server.py   # recreates tables fresh

Schema

CREATE TABLE memories (
    id          INTEGER PRIMARY KEY AUTOINCREMENT,
    key         TEXT UNIQUE NOT NULL,
    content     TEXT NOT NULL,
    metadata    TEXT DEFAULT '{}',    -- JSON
    embedding   BLOB,                 -- pickled numpy array
    created_at  TEXT DEFAULT (datetime('now')),
    updated_at  TEXT DEFAULT (datetime('now'))
);

CREATE VIRTUAL TABLE memories_fts USING fts5(
    key, content, metadata,
    content='memories',
    content_rowid='id',
    tokenize='porter unicode61'
);

Troubleshooting

Problem Likely fix
ModuleNotFoundError: No module named 'sentence_transformers' Run pip install -r requirements.txt
FastMCP() got unexpected keyword argument(s) Upgrade fastmcp: pip install --upgrade fastmcp
sqlite3.OperationalError: no such table: memories Delete memories.db and restart
First run is slow The embedding model downloads ~80MB on first use. Subsequent runs use a cached copy.
High memory usage all-MiniLM-L6-v2 uses ~500MB RAM. This is normal for transformer models.

Technical notes

  • Python 3.10+ required for str \| None union syntax
  • First launch downloads all-MiniLM-L6-v2 (~80MB) from Hugging Face Hub
  • FTS5 is included in Python's sqlite3 module since Python 3.x — no separate extension needed
  • The server uses stdio transport only (no HTTP). This is the standard for MCP and works with all clients.

About

an MCP server for AI long-term memory. It stores facts as vector embeddings (384-dim, all-MiniLM-L6-v2) in SQLite, and lets agents recall them by meaning via cosine similarity, search by keyword via FTS5/BM25, or filter by tag. Zero external infra, no GPU needed.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages