Skip to main content

BrainMemory-MCP

A Model Context Protocol (MCP) server that gives AI/LLM agents a durable brain memory — the ability to store, recall, search, connect, summarize, and forget information across sessions through standardized MCP tool calls.

Since v0.4.0 memory is modelled internally as a small knowledge graph:

  • memories are the graph nodes (content, category, tags, importance),
  • connections are directed links between memories (e.g. related_to, caused_by, part_of),
  • details are extra facts attached to a single memory.

This makes recall precise — instead of only matching words, the server can walk the connections you build (multi-hop recall) and explain how two memories relate (shortest path). The tool vocabulary stays "memory"-oriented (no "entity" wording), and it is still just SQLite under the hood — zero extra dependencies.

The server runs in two modes:

  • stdio (default) — the server is launched as a subprocess by an MCP client (e.g. via uvx brainmemory-mcp).
  • web — MCP over HTTP + Server-Sent Events (SSE) with --web, so remote MCP-capable clients (Claude, IDE agents, etc.) can connect over the network.

Memory is persisted locally under ~/.brainmemory-mcp (a SQLite database).

Cognitive Tools (15)

Since v0.9.0 the tool surface is consolidated: every operation takes a list, so acting on one memory or fifty is the same call (a single item is just a list of one). Detail and link writes are unified into one mixed-operation batch tool per entity. The result is full CRUD over all three entities with 15 tools.

Tool Description
store_memories Persist one or more memories (content, category, tags, importance).
recall_memories Fetch one or more memories by id; opt-in include_details / include_links for the richer payload; optional content_chars to preview/truncate long content.
search_memory Search-engine style: rank memories by relevance (BM25) for multi-word/long queries; also searches details; optional graph expand; optional content_chars to preview/truncate long content.
list_memories List stored memories (most important & recent first); optional content_chars to preview/truncate long content.
update_memories Modify one or more memories (only supplied fields change).
forget_memories Delete one or more memories (now soft-deletes into trash for safety).
edit_details Add / update / delete extra facts attached to memories — mixed ops in one batch.
edit_links Create (link) / remove (unlink) directed connections — mixed ops in one batch.
recall_related Multi-hop recall: memories connected to one memory, up to depth hops; optional content_chars to preview/truncate long content.
connect_memories Shortest connection (path) between two memories; optional content_chars to preview/truncate long content.
memory_map Return a map (nodes + links) of the memory graph; optional content_chars to preview/truncate long content.
summarize_memories Summary statistics: totals, categories, top tags, connection stats, most-connected memories.
export_graph_html Export the complete graph to a standalone interactive 3D HTML file at an absolute output_path.
restore_memories Soft-delete trash, history, rollback, and trash purge management; list_trash / history ops accept a per-item content_chars to preview/truncate long content.
transfer_memories Download/upload migration JSON inline across MCP servers, with optional absolute file paths and keyset pagination for large graphs.

Every list-taking tool processes items independently and reports a per-item status — one bad item never aborts the batch.

File export and migration

File operations are exposed through the same MCP tool registry in both stdio and HTTP/SSE modes. export_graph_html takes an absolute output_path.

For migration between machines, transfer_memories(op="export") returns the portable migration object in its data field (download), and transfer_memories(op="import", data=...) accepts that object directly (upload). This avoids incorrectly asking a remote HTTP server to read a path from the client's filesystem:

{"op": "export"}
{"op": "import", "data": {"format": "brainmemory-export", "format_version": 1, "memories": []}, "on_conflict": "overwrite"}

Server-local file workflows remain supported by supplying an absolute output_path on export or an absolute input_path on import. Relative paths (including ./file and ../file) and explicit . / .. path segments are rejected. Parent directories are created for file exports.

{"output_path": "/absolute/workspace/memory-graph.html"}
{"op": "export", "output_path": "/absolute/workspace/brainmemory.json"}
{"op": "import", "input_path": "/absolute/workspace/brainmemory.json", "on_conflict": "skip"}

Paginated migration for large graphs (v0.11.7+)

Two independent servers (e.g. a local stdio server and a remote HTTP/SSE server, or vice versa) share no filesystem, so migrating between them goes through the calling agent's context — one giant export can be too big for a huge memory graph. Add limit (and scope="memories", scope="links", or scope="trash") to page through it instead:

{"op": "export", "scope": "memories", "limit": 200}

Each call returns has_more / next_cursor at the top level; keep calling with cursor=<next_cursor> until has_more is false, feeding each page's data straight into {"op": "import", "data": ...} on the destination server. Page through every scope="memories" batch first, then repeat with scope="links" — an import never errors on a link whose endpoints don't exist yet, it just skips it (reported in links_skipped), so exporting links before their memories only under-imports links, it never corrupts data. scope="all" (the default) stays a single unpaginated full-graph export/import, unchanged from before; limit/cursor require a single scope ("memories", "links", or "trash" — not "all"). Pagination uses a stable keyset cursor (plain base64url text — safe to copy/paste through any client), so it stays O(page size) per call regardless of how large the graph is.

Row count alone does not bound payload size: a limit of 100 can still be too big if some memories hold large content (e.g. full book-text sections). Start with a modest limit (15-25) for graphs with long-content memories and raise it once you have confirmed pages stay comfortably within your client's tool-result budget.

{"op": "export", "scope": "memories", "limit": 200, "cursor": "<next_cursor>"}
{"op": "export", "scope": "links", "limit": 500}
{"op": "export", "scope": "trash", "limit": 50}

scope="trash" transfers exact soft-delete snapshots separately, preserving memory IDs, deleted_at, embedded details, and embedded links without a restore/forget workaround. Import trash pages with the same op="import" (the payload's own "trash" key routes it automatically — no separate import op); on_conflict="skip" is idempotent and "overwrite" replaces an existing trash snapshot. category/tags filters and scope="all" are not applicable to scope="trash" (trash rows carry no category/tag filtering).

Mixed-operation batches

edit_details — each item's op selects the operation:

{"items": [
  {"op": "add",    "memory_id": "<id>", "content": "config lives in /etc/nginx"},
  {"op": "update", "detail_id": "<id>", "content": "corrected fact"},
  {"op": "delete", "detail_id": "<id>"}
]}

edit_links — connect/disconnect memories, mixed in one call:

{"items": [
  {"op": "link",   "from_id": "<a>", "to_id": "<b>", "relation": "depends_on", "weight": 0.9},
  {"op": "link",   "from_id": "<a>", "to_id": "<c>"},
  {"op": "unlink", "from_id": "<a>", "to_id": "<d>"}
]}

Re-linking the same from/to/relation updates the weight (upsert). Detail ids are returned by the add op and by recall_memories(include_details=true).

Two read-only resources are exposed as JSON: brainmemory://stats (the summary) and brainmemory://graph (the nodes + links map).

Migrating from v0.8.0 or earlier: the singular tools (store_memory, recall_memory, update_memory, forget_memory, add_detail, link_memories, unlink_memories) and the v0.8.0 bulk names (store_memories kept its name; add_details, link_memories_bulk, unlink_memories_bulk were folded into edit_details / edit_links) are replaced by the 15 tools above. The database is untouched — only the tool names/shapes changed, not the storage or graph model.

Search (like a search engine)

search_memory no longer needs a single keyword. It tokenises your query and ranks memories by relevance, so full sentences work:

  • Full-text + BM25 — a SQLite FTS5 index over content/tags/category/ details (kept in sync by triggers), ranked with BM25. Multi-word / long queries match memories containing any (or, with mode="all", every) term, with prefix + Porter stemming (sync matches syncing).
  • Graph spreading activation — with expand=True (default), memories connected in the knowledge graph to a text hit are pulled in with a decayed score, so related context surfaces even without the query words.
  • Ranking blends text relevance with importance and recency. Each result carries relevance (0..1), match_type (text | related | list), matched_terms, and distance (hops from a text hit).
  • Fallback — where a SQLite build lacks FTS5, search degrades to a tokenised LIKE term-coverage scorer, so it always works. summarize_memories reports the active engine (fts5-bm25 or like-fallback).

Example: search_memory("Burp Firefox proxy sync") returns the relevant memories ranked, plus anything linked to them — in a single call.

Preview long content (content_chars)

Every read/browse tool that returns memory bodies accepts an optional content_chars (positive int) parameter that truncates each memory's content to a preview of that many characters instead of returning the full body — handy for saving an agent's token/tool-result budget when browsing large memories (e.g. book-length content). It is available on:

  • search_memory and list_memories
  • recall_memories (applies to the memory and any included details)
  • recall_related (root + related memories)
  • connect_memories (memories on the path)
  • memory_map (nodes)
  • restore_memories — per-item on the list_trash and history ops

Any item that gets truncated also gains content_truncated: true and content_length (the original character count), so callers know there is more and can fetch the full text with recall_memories. Omit it (or pass a non-positive value) to keep the full content — the default is unchanged.

Since v0.13.1 the same tools also accept an optional content_offset (a 0-based start character, default 0 = from the beginning) alongside content_chars (the length cap). Together they form a movable window so you can page through long content — e.g. content_offset=200, content_chars=200 returns characters 200–399. When content_offset > 0 the returned item also carries a content_offset field. (For restore_memories, pass content_offset per-item on the list_trash / history ops, same as content_chars.)

Write tools (store_memories, update_memories) and transfer_memories deliberately do not truncate, so request echoes and graph migrations stay full-fidelity.

Install

From PyPI:

python3 -m pip install brainmemory-mcp

From a local checkout:

python3 -m pip install .

Both install the package and a console script named brainmemory-mcp.

For development (editable install):

python3 -m pip install -e ".[dev]"

To build/publish a release, see docs/RELEASING.md.

Run

stdio mode (default)

Best for local MCP clients that launch the server themselves. Memory in ~/.brainmemory-mcp.

brainmemory-mcp

# Or without the console script
python3 -m brainmemory_mcp

# With a custom memory location
brainmemory-mcp --data-dir /path/to/memory

Web mode (HTTP + SSE)

Enable with --web for remote / networked clients.

# Defaults: 127.0.0.1:8765, memory in ~/.brainmemory-mcp
brainmemory-mcp --web

# Custom host/port and memory location
brainmemory-mcp --web --host 0.0.0.0 --port 9000 --data-dir /path/to/memory

# Optional Bearer authorization (prefer the env var to avoid shell history)
BRAINMEMORY_KEY='replace-with-a-strong-secret' brainmemory-mcp --web
# Equivalent CLI form: brainmemory-mcp --web --key 'replace-with-a-strong-secret'

When --key or BRAINMEMORY_KEY is set, every web request must include:

Authorization: Bearer replace-with-a-strong-secret

This protects both endpoints. Without a key, web mode remains unauthenticated for backward compatibility. Use HTTPS through a reverse proxy when exposing the server over a network; a Bearer key sent over plain HTTP is not encrypted.

Endpoints once running in web mode:

  • SSE stream: http://<host>:<port>/sse
  • Message POST: http://<host>:<port>/messages/

Configuration

Option Env var Default
--web BRAINMEMORY_WEB false (stdio)
--host BRAINMEMORY_HOST 127.0.0.1
--port BRAINMEMORY_PORT 8765
--key BRAINMEMORY_KEY unset (authorization disabled)
--data-dir BRAINMEMORY_HOME ~/.brainmemory-mcp

Connect a client

Configure the client to launch the server as a subprocess:

{
  "mcpServers": {
    "brainmemory": {
      "command": "uvx",
      "args": ["brainmemory-mcp"]
    }
  }
}

If installed on your PATH, you can use "command": "brainmemory-mcp" with "args": [] instead.

Web (SSE)

Start the server with --web, then point an SSE-capable client at the /sse endpoint:

{
  "mcpServers": {
    "brainmemory": {
      "url": "http://127.0.0.1:8765/sse",
      "headers": {
        "Authorization": "Bearer replace-with-a-strong-secret"
      }
    }
  }
}

How memory is stored

Memories live in ~/.brainmemory-mcp/memory.db (SQLite, WAL mode) across three tables:

  • memories — nodes: id, content, category, tags, importance (1–5), created_at, updated_at.
  • memory_details — extra facts attached to a memory (cascade-deleted with it).
  • memory_links — directed connections source_id -> target_id with a relation and weight (cascade-deleted with either endpoint).

Search uses a SQLite FTS5 full-text index (memories_fts, kept in sync by triggers) ranked with BM25, augmented by graph spreading activation. Graph operations (multi-hop recall_related, shortest-path connect_memories, degree centrality in summarize_memories) are computed with plain SQL + a little Python — no external services or vector database required.

Nothing is ever silently deleted — removal only happens through forget_memories or explicit delete ops in edit_details / edit_links. When an older database is opened that lacks the newest schema (the graph tables or the FTS index), it is backed up automatically to ~/.brainmemory-mcp/backups/ before the new objects are added.

License

MIT

Metadata

Release files for brainmemory-mcp 0.13.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for brainmemory-mcp 0.13.1
File Size Uploaded Provenance Actions
brainmemory_mcp-0.13.1.tar.gz 54.5 kB Without provenance Details for brainmemory_mcp-0.13.1.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for brainmemory-mcp 0.13.1
File Interpreter ABI Platform Actions
brainmemory_mcp-0.13.1-py3-none-any.whl Python 3 none any Details for brainmemory_mcp-0.13.1-py3-none-any.whl

Total release size: 100.5 kB

Release files / brainmemory_mcp-0.13.1.tar.gz

Download URL brainmemory_mcp-0.13.1.tar.gz
Size 54.5 kB
Tags Source
SHA-256 checksum
How to use checksums
2b45cb36dc9dd87a6e854db1ce917f73c6151f59ea37ab3a3f131f0c54b4564a
BLAKE2b-256 checksum
How to use checksums
f64c9eebe4f87716f370f1a8106cf95aa9cdf939c6528ab97676999c413f24cc
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.14

Release files / brainmemory_mcp-0.13.1-py3-none-any.whl

Download URL brainmemory_mcp-0.13.1-py3-none-any.whl
Size 46.0 kB
Tags Python 3
SHA-256 checksum
How to use checksums
79fedae726254e804e830fee86690ac11ca93602089b7b07ef8c0d0f62f53c8f
BLAKE2b-256 checksum
How to use checksums
e671a956965856d8add8e13e34879eeb4715d9cc4f0cff3b66586a0c65fbbade
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.14

Security for brainmemory-mcp 0.13.1

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page