Local Brain MCP
by ejsinfuego
README.md
Local Brain MCP
===============
Local Brain MCP is a small MCP server that you **run locally** on your machine. It lets AI assistants store and retrieve personal memories on your own disk, and optionally use **semantic (embedding) search** for RAG‑style workflows.
You run the server on your computer (e.g. `python server.py`); Cursor and other MCP clients connect to it at `http://localhost:3000/mcp`. Optionally, you can expose it via something like Cloudflare Tunnel to use the same memory from other devices.
Features
--------
- **Local, transparent storage**
- SQLite database file on disk (`memory.db` by default).
- DB location is configured via **environment variables** (see “Database layout”). The `dbUrl` *tool argument* is intentionally ignored so all clients share the same configured DB.
- Simple schema: `memories` table with `title`, `content`, `tags`, `source`, timestamps, and IDs.
- **MCP tools**
- `save_memory` — insert a memory row (optionally generating an embedding).
- `update_memory` — patch `title` / `content` / `tags` / `source` on an existing row; optional embedding refresh when `content` changes.
- `delete_memory` — remove a row by id (embeddings removed with it).
- `fetch_memories` — RAG-style retrieval: by default uses semantic vector search over embeddings; falls back to keyword search if embeddings are unavailable.
- `backfill_all_embeddings` — generate embeddings for older memories that don’t have one yet (after you configure an embedding provider).
- **Embeddings / vector search**
- The server does not run an embedding model itself. Embeddings are **handled by OpenAI** (or optionally OpenRouter) via their APIs.
- Optional embeddings are stored in a separate `memory_embeddings` table.
- **OpenAI** is the default: set `OPENAI_API_KEY` and the server will use OpenAI’s embeddings API. Optionally, set `EMBEDDING_PROVIDER=openrouter` and `OPENROUTER_API_KEY` to use OpenRouter instead.
Quick start
-----------
1) **Install dependencies**
Create and activate a virtual environment, then install Python deps.
```bash
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
```
1) **Configure embeddings (optional but recommended)**
Embeddings are generated by **OpenAI’s API** (the server has no built‑in embedding model). Set `OPENAI_API_KEY` and the server will call OpenAI to create and store embeddings for semantic search.
- **Option A: OpenAI**
```bash
export EMBEDDING_PROVIDER=openai # default, can be omitted
export OPENAI_API_KEY=sk-...
export EMBEDDING_MODEL=text-embedding-3-small # optional override
```
- **Option B: OpenRouter**
```bash
export EMBEDDING_PROVIDER=openrouter
export OPENROUTER_API_KEY=sk-or-...
# Any OpenRouter embedding model (example shown):
export EMBEDDING_MODEL=openai/text-embedding-3-small
# Optional but recommended for OpenRouter analytics:
export OPENROUTER_SITE_URL="https://your-site-or-localhost"
export OPENROUTER_APP_NAME="Local Brain MCP"
```
1) **Run the MCP server**
From the project root:
```bash
source .venv/bin/activate
python server.py
```
You should see FastMCP start up and log something like:
- `Local Brain MCP` listening on `http://0.0.0.0:3000`
- MCP endpoint: `http://localhost:3000/mcp`
You can then point Cursor (or another MCP client) at `http://localhost:3000/mcp` as a remote MCP server.
Run with Docker Compose
-----------------------
If you prefer running this in Docker, use the included `docker-compose.yml`.
1) **Set env vars (optional, for embeddings)**
Create a `.env` file in the project root (or export env vars in your shell):
```bash
# .env example
EMBEDDING_PROVIDER=openai
OPENAI_API_KEY=sk-...
EMBEDDING_MODEL=text-embedding-3-small
```
2) **Build and start**
```bash
docker compose up -d --build
# or, on systems with the legacy CLI:
docker-compose up -d --build
```
3) **Use the MCP endpoint**
The server is available at:
- `http://localhost:3000/mcp`
4) **Stop the service**
```bash
docker compose down
# or:
docker-compose down
```
Notes:
- SQLite data is persisted in the Docker volume `memory_data`.
- Inside the container, the DB path is set to `MEMORY_DB_URL=/data/memory.db`.
Cursor integration note (local env auto-load)
---------------------------------------------
When you run `server.py` locally, it will also try to load environment overrides from `~/.cursor/mcp.json` (specifically the `mcpServers.local-brain-mcp.env` block), if present. This helps you keep your server configuration in one place.
Database layout
---------------
By default, the SQLite file is `memory.db` in the project root. This can be overridden with:
- Env vars: `dbUrl`, `DB_URL`, or `MEMORY_DB_URL` (first non‑empty wins).
Notes:
- The `dbUrl` **tool parameter is intentionally ignored** for path resolution.
- If the chosen DB path is relative, it is resolved relative to the server’s current working directory.
Tables:
- `memories`
- `id` (INTEGER, primary key)
- `created_at` (TEXT, ISO datetime, default `datetime('now')`)
- `title` (TEXT, nullable)
- `content` (TEXT, required)
- `tags` (TEXT, JSON‑encoded list of strings)
- `source` (TEXT, optional source identifier)
MCP tools
---------
**`save_memory`**
**Purpose**: Insert a new memory row (and optionally its embedding).
Parameters:
- `content` (str, required): main text content of the memory.
- `title` (str, optional): short title.
- `tags` (List[str], optional): arbitrary tags, stored as JSON.
- `source` (str, optional): where the memory came from (e.g. `"cursor"`, `"cli"`, `"web"`).
- `dbUrl` (str, optional): accepted for API compatibility, but ignored for database path resolution.
- `generate_embedding` (bool, optional, default `True`):
- If `True`, and an embedding API is configured (e.g. `OPENAI_API_KEY`), the server calls OpenAI (or OpenRouter) to generate an embedding and stores it.
Returns:
- A dict with `id`, `title`, `content`, `tags`, `source`.
**`update_memory`**
**Purpose**: Update fields on an existing memory (omit fields you do not want to change).
Parameters:
- `memory_id` (int, required): id of the row to update.
- `title`, `content`, `tags`, `source` (optional): new values; at least one must be provided.
- `generate_embedding` (bool, default `True`): when `content` changes, if `True` recompute the embedding; if `False`, drop the stored embedding so vector search cannot use stale vectors.
Returns: dict with `id`, `title`, `content`, `tags`, `source`.
**`delete_memory`**
**Purpose**: Delete a memory by id.
Parameters:
- `memory_id` (int, required).
Returns: `{ "id": <int>, "deleted": <bool> }` (`deleted` is `False` if no row existed).
**`fetch_memories`**
**Purpose**: Retrieve memories relevant to a query, or the most recent rows when the query is empty.
Parameters:
- `query` (str, optional): text query; if omitted or blank (or whitespace-only), returns the latest memories (recent-first)—a “list recent” behavior, not search.
- `limit` (int, optional, default `5`, max `50`): max number of results.
- `dbUrl` (str, optional): accepted for API compatibility, but ignored for database path resolution.
- `use_vector_search` (bool, optional, default `True`): RAG-style retrieval.
- `True` (default): embed the query and return memories ranked by semantic similarity; falls back to keyword search if embeddings are unavailable.
- `False`: keyword-only — SQL `LIKE` on `content` and `title`, ordered by recency.
- `fields` (list of str, optional): if set, each result includes only these keys. Allowed: `id`, `created_at`, `title`, `content`, `tags`, `source`. Omit for all fields. Useful to shrink MCP tool payloads (e.g. exclude `content` when listing or probing).
- `tags_any` (list of str, optional): if set, return only rows containing at least one of the provided tags (case-insensitive).
- `source_prefix` (str, optional): if set, return only rows where `source` starts with this prefix.
Returns:
- A list of dicts; by default each includes `id`, `created_at`, `title`, `content`, `tags`, `source`. With `fields`, only the requested keys are present.
Using this as a RAG memory
--------------------------
Typical pattern for an AI assistant:
1. **On new long‑term information**
Call `save_memory` with:
- `content`: the text snippet you want to remember.
- `title` / `tags`: short descriptors.
- `generate_embedding=True` (default) so it’s indexed semantically.
2. **Before answering a user query**
Call `fetch_memories` with the user’s question (or a brief summary) as `query`. By default it uses RAG (semantic retrieval); use the top results as context when generating the answer.
3. **Fallback behavior**
If embeddings are not configured or not yet stored, vector search cleanly degrades to keyword search, so the tools remain usable.
4. **Hygiene**
Use `update_memory` to correct a row without duplicating it; use `delete_memory` to drop obsolete entries.
Embedding backfill (recommended after enabling embeddings)
---------------------------------------------------------
If you set an embedding provider/API key after you already have memories saved, run `backfill_all_embeddings` once to generate embeddings for older rows so semantic search works across your full history.
Development notes
-----------------
- The server is implemented in `server.py` using **FastMCP**.
- All schema creation is performed on connection; deleting `memory.db` will recreate an empty database on next start.
- Embeddings are handled by OpenAI (or OpenRouter); configuration is via environment variables only.
This server cannot be deployed
Maintenance
ActivityInactive
ResponsivenessNo issues