Local-first multi-agent memory MCP server with hybrid search, behavioral graphs, knowledge triples, and lifecycle management
Project description
localmem
Local-first multi-agent memory MCP server. Persistent storage for LLM agents — hybrid (dense + sparse) vector search, behavioral pattern graphs, temporal knowledge triples, layered wake-up context, lifecycle management, and a read-only browser dashboard. All on-device, no cloud dependencies.
Exposes its functionality over the Model Context Protocol (SSE transport), so any MCP-capable client — Claude Code, Cursor, Continue, custom agents — can read and write memory through a single shared server.
Why
Most "memory" for LLM agents is either a flat key-value store or a single-agent knowledge graph. Real agent systems need more:
- Per-agent namespaces. Each agent's notes, decisions, and observations
stay in its own wing. A reserved
sharedwing carries cross-agent context. - Hybrid retrieval. Dense embeddings catch semantic matches, sparse BM25 catches exact terms, RRF fuses both. Either signal alone misses too much.
- Behavioral graphs. Some relationships live in entries; others live in the connections between them — co-occurrence, sequence, community structure.
- Temporal knowledge. Facts change. Knowledge triples track validity windows and surface contradictions automatically.
- Graceful forgetting. Three-tier lifecycle (hot → warm summaries → cold compressed archive) keeps the working set fast without losing history.
- Token-aware loading. Layered wake-up context (L0 manifest → L1 critical → L2 scoped search → L3 verbatim) gives an agent ~170 tokens of high-signal context without pulling the whole store.
Quick start
Use a virtualenv. Recent macOS / Homebrew / Debian Python installs are "externally managed" (PEP 668) and
pip install localmemdirectly will refuse. A dedicated venv also keeps thelocalmemconsole script onPATHautomatically when activated.
# 1. Create a venv (use Python 3.13 — 3.14 + Apple Silicon has a known
# sentence-transformers shutdown issue, see Known issues below)
python3.13 -m venv ~/.venvs/localmem
source ~/.venvs/localmem/bin/activate
# 2. Install from PyPI
pip install 'localmem[dashboard,analytics]'
# 3. Scaffold a working config + data directory
mkdir -p ~/localmem-data && cd ~/localmem-data
localmem init --wing my_assistant --dashboard
# 4. Start the MCP server (SSE on http://localhost:8781)
localmem -c localmem.yaml serve
Connect any MCP client to http://localhost:8781/sse and the 22 tools below
become available.
Dashboard (optional, read-only):
# In another terminal (venv active):
localmem -c localmem.yaml dashboard # REST + WS on http://localhost:8782
# The prebuilt React frontend isn't shipped on PyPI yet. To get the UI:
git clone https://github.com/jordanaftermidnight/localmem.git ~/localmem-source
cd ~/localmem-source/dashboard
npm install
VITE_API_URL=http://localhost:8782 VITE_WS_URL=ws://localhost:8782/ws npm run build
cd dist && python3 -m http.server 8785
Then open http://localhost:8785.
Headless / always-on (macOS LaunchAgents):
For a server that survives logout, reboot, and crashes, generate + load the LaunchAgents in one command (after the Quick start above is working):
git clone https://github.com/jordanaftermidnight/localmem.git ~/localmem-source 2>/dev/null
python3 ~/localmem-source/deploy/setup-launchd.py --load
launchctl list | grep localmem
Three services come up: com.localmem.serve (:8781), com.localmem.dashboard
(:8782), com.localmem.frontend (:8785). All have RunAtLoad=true and
KeepAlive=true — they auto-start on login and respawn on crash. Combined
with Docker Desktop's "start on login" + --restart unless-stopped on the
Qdrant container (see docs/DEPLOY.md when written), the
stack runs entirely headless.
Working from source (contributing or pinning a specific commit):
git clone https://github.com/jordanaftermidnight/localmem.git
cd localmem
python3.13 -m venv .venv && source .venv/bin/activate
pip install -e '.[dev,dashboard,analytics]'
MCP tools
| Group | Tool | Purpose |
|---|---|---|
| Memory (6) | localmem_store, localmem_search, localmem_retrieve, localmem_update, localmem_pin, localmem_unpin |
Entry CRUD + hybrid search |
| Graph (5) | localmem_graph_add_node, localmem_graph_add_edge, localmem_graph_query, localmem_graph_neighbors, localmem_graph_communities |
Behavioral pattern graph |
| Knowledge (3) | localmem_triple_assert, localmem_triple_query, localmem_triple_contradictions |
Temporal triples with contradiction detection |
| System (3) | localmem_wake, localmem_health, localmem_metrics |
Layered wake-up + observability |
| Intelligence (3) | localmem_intel_detect, localmem_intel_alerts, localmem_intel_report |
Pattern detection (opt-in via config) |
| Operations (2) | localmem_prune, localmem_archive |
Retention triggers |
Storage stack
- Qdrant — embedded by default (path-backed,
single-writer). Switch to a remote Qdrant via
storage.qdrant_mode: serverstorage.qdrant_urlto unblock live embedding migrations and multi-process writers.
- NetworkX — in-process directed multigraph with multi-hop traversal and Louvain community detection.
- SQLite (WAL) — temporal triples, agent diaries, wing/room taxonomy, importance scoring with time-decay.
Configuration
localmem.yaml at the repo root is the single source of truth. The shipped
defaults run locally with zero edits — set wings: to name your agents and
you're done. See inline comments for every section. Highlights:
wings: [list]— your agent namespaces.sharedis implicit.embedding.model—all-MiniLM-L6-v2(384d, fast) or BGE-large (1024d, quality). Switch live withlocalmem migrate-embeddings --to <model>.retention.enabled: true— opt in to the three-tier lifecycle.dashboard.auth_enabled: true+ bearer key for remote dashboard access.intelligence.detectors.*— each pattern detector is off until you point it at a specific wing/room (or node selector for the graph cluster detector). Nothing runs you didn't ask for.
Any string value supports ${VAR} or ${VAR:-default} env-var
interpolation, so secrets stay out of YAML on disk.
Dashboard
A read-only browser UI under dashboard/ (Vite + React + dockview). 10
panels: Health, Entries, Metrics, Alerts, Graph, Wings/Rooms, Triples,
Diaries, Logs, Admin. Pin/unpin and lifecycle triggers live in Admin.
Localhost-only by default; flip on bearer auth to expose it remotely.
Observability
localmem healthandlocalmem_healthMCP tool — per-wing entry counts, store connectivity, embedding device, retention worker status.localmem_metricsMCP tool — per-tool call counts, p50/p95/p99 latency, error counts (rolling window)./metricsPrometheus exposition endpoint on the dashboard sidecar (text/plain; version=0.0.4). See docs/DASHBOARD.md for the metric reference and example scrape config.- Structured logging (text or JSON) with optional
RotatingFileHandler. See docs/LOGGING.md for Loki + Promtail and ELK + Filebeat shipping configs.
Deployment
deploy/ contains installer scripts for the three major platforms — each
generates a config dir, sets up a service (systemd / launchd / Scheduled
Tasks), and writes an api_key to a perms-restricted env file:
deploy/setup-ubuntu.sh --auth --qdrant-server http://qdrant:6333
deploy/setup-macos.sh --auth
deploy/setup-windows.ps1 -Auth
Documentation
docs/ARCHITECTURE.md— full specificationdocs/DASHBOARD.md— dashboard panels, auth, metricsdocs/LIFECYCLE.md— retention / consolidation / archivedocs/MIGRATIONS.md— schema and embedding migrationsdocs/LOGGING.md— log shipping recipes
Project layout
localmem/
├── src/localmem/ # Package source
├── dashboard/ # React + dockview frontend
├── deploy/ # Installers + service units
├── docs/ # Architecture, dashboard, lifecycle, migrations, logging
├── manifests/ # Per-agent wake-up manifests
├── tests/ # 300+ tests
├── localmem.yaml # Default configuration
└── pyproject.toml
Known issues
- Python 3.14 + Apple Silicon + sentence-transformers: the
lokyprocess pool used by sentence-transformers can crash silently at shutdown on Python 3.14 / arm64 macOS. Python 3.13 and earlier are unaffected. Either use Python 3.13 (verified end-to-end on this build) or switch to the sparse-only retrieval path viaembedding.model: "Qdrant/bm25".
License
MIT. See LICENSE.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file localmem-0.1.2.tar.gz.
File metadata
- Download URL: localmem-0.1.2.tar.gz
- Upload date:
- Size: 502.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c10a9ba1b3a08890f182bfdaf9179759a6cbd6c90c07a023453641b89e8e3e18
|
|
| MD5 |
7cb0d70eecb753bb67a0fa2c5347805d
|
|
| BLAKE2b-256 |
6643956c0cc00b4e160348c862e292880682a3f284f99bcfb5331024b88d4366
|
Provenance
The following attestation bundles were made for localmem-0.1.2.tar.gz:
Publisher:
publish.yml on jordanaftermidnight/localmem
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
localmem-0.1.2.tar.gz -
Subject digest:
c10a9ba1b3a08890f182bfdaf9179759a6cbd6c90c07a023453641b89e8e3e18 - Sigstore transparency entry: 1995861199
- Sigstore integration time:
-
Permalink:
jordanaftermidnight/localmem@f14817752a872e8c4a8364ab47d69fc52b49a8da -
Branch / Tag:
refs/tags/v0.1.2 - Owner: https://github.com/jordanaftermidnight
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@f14817752a872e8c4a8364ab47d69fc52b49a8da -
Trigger Event:
push
-
Statement type:
File details
Details for the file localmem-0.1.2-py3-none-any.whl.
File metadata
- Download URL: localmem-0.1.2-py3-none-any.whl
- Upload date:
- Size: 88.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e68a5517bfb961b5b684c2b7c5510886dfa8a3879c112f11f018adb41c563c5a
|
|
| MD5 |
ffea455656ec174db26b8ad0d75cc602
|
|
| BLAKE2b-256 |
2c71547dc154253962386b32f80dc0ebe8ce983f6f6be27d1b53c1027206042c
|
Provenance
The following attestation bundles were made for localmem-0.1.2-py3-none-any.whl:
Publisher:
publish.yml on jordanaftermidnight/localmem
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
localmem-0.1.2-py3-none-any.whl -
Subject digest:
e68a5517bfb961b5b684c2b7c5510886dfa8a3879c112f11f018adb41c563c5a - Sigstore transparency entry: 1995861251
- Sigstore integration time:
-
Permalink:
jordanaftermidnight/localmem@f14817752a872e8c4a8364ab47d69fc52b49a8da -
Branch / Tag:
refs/tags/v0.1.2 - Owner: https://github.com/jordanaftermidnight
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@f14817752a872e8c4a8364ab47d69fc52b49a8da -
Trigger Event:
push
-
Statement type: