flux7-memory
Provides embeddings for hybrid search and LLM reranking of search results, enhancing memory retrieval relevance.
Allows using OpenAI-compatible APIs as an embedding provider for dense vector search in hybrid retrieval mode.
______ ____
/ __/ //_ /_____ _ ___ __ _
/ _// /__/ /___/ ' \/ -_) ' \
/_/ /____/_/ /_/_/_/\__/_/_/_/flux7-memory
A lightweight MCP server in Go for shared memory across AI agents. Single binary, zero cgo, usable standalone over stdio or as a shared daemon behind flux7-mesh. Hybrid markdown + SQLite store with full-text search, optional dense-vector hybrid retrieval, LLM reranking, and three transports: MCP stdio, HTTP JSON-RPC, and MCP SSE. Comes with a Python SDK for provider-agnostic integration.
Features
8 MCP tools —
memory_store,memory_recall,memory_search,memory_context,memory_get,memory_list,memory_forget,memory_historyBi-temporal — every memory is a series of versions:
valid_from/valid_toon writes,valid_at(what held then) andas_of(what mem7 believed then) on readsTamper-evident workspace — every entry sealed in a hash chain (HMAC with
MEM7_CHAIN_KEY), checked bymem7 verifyHybrid storage — append-only markdown workspace as source of truth, SQLite (FTS5) as a rebuildable index
Field-weighted BM25 — FTS5 ranking with tuned weights: object content (5x), entity key (2x), tags (0.5x)
Hybrid search (opt-in) — BM25 + dense cosine similarity merged via Reciprocal Rank Fusion (RRF). Requires an external embedding provider (Ollama or any OpenAI-compatible API)
LLM reranking (opt-in) — post-RRF listwise reranking via Ollama, with graceful degradation if the reranker is unavailable
Natural language mode —
mode="natural"strips stop words, applies wildcard stemming, and OR-joins tokens so agents can query in plain language instead of FTS5 syntaxNeighbor inclusion —
include_neighbors=trueautomatically fetches sequential neighbors (e.g.t004,t006aroundt005) to capture context spread across consecutive entriesAccess tracking —
access_countandlast_accessedare bumped onmemory_recall, providing usage signals without creating feedback loopsThree transports — MCP stdio (default, for Claude Code / Cursor), HTTP JSON-RPC via
mem7 serve(for SDKs and direct API calls), and MCP Streamable HTTP onPOST /mcp(for flux7-mesh daemon mode: one process, shared DB). The older HTTP+SSE pair (/sse,/messages) remains for existing clients; the MCP spec deprecates itSnapshot reminder —
POST /memory/snapshot_reminder(and the matching MCP method) lets an agent runtime inject a pre-compaction instruction into its contextRebuildable index —
mem7 rescandrops the SQLite index and replays the markdown workspace to restore consistencyTag filters, agent tracking, TTL
Related MCP server: litopys
Quick start
go install github.com/KTCrisis/flux7-memory/cmd/mem7@latestOr build from source :
cd flux7-memory
go build -o ~/go/bin/mem7 ./cmd/mem7Default stdio mode (MCP client spawns the binary) :
~/go/bin/mem7If a mem7 serve daemon is already running, stdio mode auto-detects it and becomes a thin proxy (stdin↔HTTP) instead of opening a second local store. Same command, zero config change.
Daemon mode (shared across multiple clients via HTTP + SSE) :
MEM7_TOKEN=mem7_secret123 ~/go/bin/mem7 serve --listen :9070Exposes /rpc (HTTP JSON-RPC), /sse + /messages (MCP SSE transport), /healthz, and /memory/snapshot_reminder. flux7-mesh connects via SSE for MCP tool calls and via /rpc for decision writes — one daemon, one database.
Rebuild the SQLite index from the markdown workspace :
~/go/bin/mem7 rescanDrop TTL-expired entries from the index (the markdown workspace is left untouched ; rescan re-evaluates TTL on replay) :
~/go/bin/mem7 pruneConfiguration
Variable | Default | Description |
|
| Data directory (hosts |
|
| HTTP bind address when in |
| (empty) | Bearer token required on |
| (empty) | JSON file of read scopes per agent; empty = reads not scoped |
| (empty) | Key of the workspace's hash chain (HMAC-SHA256); empty = SHA-256 |
|
| Soft ceiling on live entries |
| (empty) | Base URL of the embedding provider. Setting this enables hybrid search |
|
| Model name passed to the embedding API |
|
| Provider format: |
| (empty) | Bearer token for the embedding API (required for OpenAI, optional for Ollama) |
| (empty) | Base URL of the reranking LLM. Setting this enables LLM reranking after RRF merge |
|
| Model name passed to the Ollama generate API for reranking |
Flags on mem7 serve mirror MEM7_LISTEN, MEM7_TOKEN and MEM7_SCOPES : --listen :9070 --token mem7_... --scopes scopes.json.
To run the daemon as a systemd service, see contrib/systemd/mem7.service (adapt User= and paths, then sudo systemctl enable --now mem7).
Hybrid search setup
Hybrid search is entirely opt-in. Without MEM7_EMBED_URL, mem7 uses pure BM25.
With local Ollama :
MEM7_EMBED_URL=http://localhost:11434 \
MEM7_EMBED_MODEL=nomic-embed-text \
~/go/bin/mem7With OpenAI API :
MEM7_EMBED_URL=https://api.openai.com \
MEM7_EMBED_MODEL=text-embedding-3-small \
MEM7_EMBED_PROVIDER=openai \
MEM7_EMBED_KEY=sk-... \
~/go/bin/mem7With any OpenAI-compatible endpoint (vLLM, LiteLLM, Azure OpenAI, etc.) :
MEM7_EMBED_URL=http://localhost:8000 \
MEM7_EMBED_MODEL=BAAI/bge-small-en-v1.5 \
MEM7_EMBED_PROVIDER=openai \
~/go/bin/mem7When enabled, memory_store computes and persists an embedding alongside each entry. memory_search retrieves BM25 top-2N and cosine top-2N candidates, then merges them via Reciprocal Rank Fusion (RRF, k=60) into the final top-N. Embeddings are stored as BLOBs in SQLite and cached in memory for sub-ms cosine search.
LLM reranking setup
LLM reranking is opt-in on top of hybrid search. It over-fetches 3x candidates, merges via RRF, then uses an LLM to score relevance before returning the final top-N. Falls back to non-reranked results if the LLM is unavailable.
MEM7_EMBED_URL=http://localhost:11434 \
MEM7_RERANK_URL=http://localhost:11434 \
MEM7_RERANK_MODEL=gemma4:e4b \
~/go/bin/mem7Python SDK
A provider-agnostic Python client for mem7, wrapping all MCP tools via JSON-RPC over HTTP.
Install
pip install flux7-memoryOr from source :
pip install ./sdk/pythonUsage
from mem7 import Mem7
m = Mem7("http://localhost:9070", token="my-token")
# Store a memory
m.store("user.prefs", "prefers dark mode", tags=["user"])
# Search (returns formatted text)
print(m.search("dark mode", limit=5))
# Context (returns structured Memory objects)
for mem in m.context("dark mode", limit=5):
print(f"{mem.key}: {mem.value}")
# Formatted block for LLM prompt injection
block = m.context_block("user preferences", limit=10)
# Other tools
m.recall(key="user.prefs")
m.list(tags=["user"])
m.get("memory/2026-05-07.md")
m.forget(key="user.prefs")Workspace layout
~/.mem7/
├── workspace/
│ ├── MEMORY.md # reserved for long-term notes
│ └── memory/
│ ├── 2026-04-11.md # append-only daily logs
│ └── 2026-04-12.md
└── index.db # SQLite (facts + facts_fts + embeddings)The markdown files are the source of truth ; index.db is a derived cache that can be dropped and rebuilt from the markdown at any time via mem7 rescan.
Each entry is written as a level-2 heading followed by a fenced mem7 envelope (plain key/value metadata) and a free-form body, terminated by a horizontal rule. A human can edit these files in place — the next rescan picks up the changes.
Example :
## example_key
```mem7
op: store
agent: claude
tags: demo, example
created: 2026-04-11T20:00:00Z
updated: 2026-04-11T20:00:00Z
```
Free-form markdown content lives here.
---Usage with flux7-mesh
In your config.yaml :
mcp_servers:
- name: memory
transport: stdio
command: /home/user/go/bin/mem7
env:
MEM7_DIR: /home/user/.mem7flux7-mesh discovers the tools via tools/list ; no per-tool wiring is required. Grants and policies apply as usual.
Provenance and agent scopes
When mem7 sits behind flux7-mesh as a streamable-http upstream, the mesh puts two things in the _meta of each tools/call:
traceparent, the W3C trace context of the governed call. mem7 records its trace id on every write and every deletion, in the markdown (trace:in the envelope) and in the index;memory_recall,memory_searchandmemory_contextreturn it. A memory points back to the decision that produced it.art.flux7/agent, the agent the mesh authenticated, for an upstream configured withforward_identity: true. mem7 honours it only on a request that carried its bearer token: anyone can write a_meta, only the mesh holds the token. The vouched identity replaces theagentargument the caller declared.
# flux7-mesh
memory:
url: http://localhost:9070
token: "${MEM7_TOKEN}" # expanded from the service environment
mcp_servers:
- name: memory
transport: streamable-http
url: http://localhost:9070/mcp
forward_identity: true
headers:
Authorization: "Bearer ${MEM7_TOKEN}"With a scopes file, an identified agent reads its own memories plus the owners listed for it; only administrators overwrite or forget another agent's memory, forget by tags, or read the raw workspace (memory_get):
{ "read": { "claude": ["*"], "sup7": ["scout7"] }, "admin": ["claude"] }Clients that reach mem7 directly with the token but name no agent (the supervisor, the console, the mesh's own decision writer) are not scoped. Without a scopes file, identities still sign the writes and reads stay open. contrib/systemd/enable-token.sh wires the token and the scopes into the systemd units of a single machine.
To share the same memory across several machines behind flux7-mesh, run mem7 serve on one host and point the other hosts at it via the upcoming remote-client mode (Phase 1.5 of the roadmap).
Time: valid and as-of
Every memory is a series of versions on two axes: when it holds in the world (valid_from, valid_to) and when mem7 believed it (tx_from, tx_to). memory_store takes optional valid_from / valid_to (a date 2026-03-20 or RFC3339); a new version ends the ones it overlaps and keeps what they said outside its period, so a value given "from now" ends the old one now, and a date in the past corrects history without erasing what was believed. Reads take valid_at and as_of; without them they return what mem7 believes now of what holds now, as before. memory_forget stops believing a key, and a read as_of an earlier moment still sees it. An index built before versions is rebuilt from the markdown at start. See Time.
Hash chain (tamper evidence)
Every entry mem7 writes to the workspace (store, deletion, deletion by tags) carries the seal of the entry before it (prev:) and its own (hash:), computed over its parsed fields. Edit an entry, drop one or reorder them, and mem7 verify names the first place the chain no longer holds:
$ MEM7_CHAIN_KEY=... mem7 verify
211 entries: 78 sealed, 133 written before the chain
seals: HMAC-SHA256 with MEM7_CHAIN_KEY
chain holdsWith MEM7_CHAIN_KEY the seal is an HMAC-SHA256: editing the workspace without the key leaves a break no one can reseal. Without it, a plain SHA-256 catches accidents and careless edits, not a forger. Keep the key stable: entries sealed with one key do not verify with another. Entries written before the chain existed are counted, not checked; the chain starts at the first sealed entry. The workspace stays plain markdown you can read and edit by hand; an edit now shows.
Tools
memory_store
Upsert a memory entry by key. The markdown workspace receives an append-only section ; the SQLite index is updated in place. If hybrid search is enabled, an embedding is computed and stored alongside the entry.
Parameter | Type | Required | Description |
| string | no | When the fact starts to hold (date or RFC3339); default now; in the past, it corrects history |
| string | no | When it stops holding; default open |
| string | yes | Unique key for this memory |
| string | yes | Content to remember (free-form markdown allowed) |
| string[] | no | Tags for filtering and grouping |
| string | no | Identifier of the storing agent |
| number | no | Time-to-live in seconds (0 = permanent) |
memory_recall
Recall memories by key, tags, or agent, most recently updated first. Bumps access_count and last_accessed on returned entries.
Parameter | Type | Required | Description |
| string | no | What held at that moment (date or RFC3339); default now |
| string | no | What mem7 believed at that moment; default now |
| string | no | Exact key to recall |
| string[] | no | Filter by tags (AND logic) |
| string | no | Filter by agent |
| number | no | Max results (default 10) |
memory_search
Full-text search over memories using SQLite FTS5, ranked by field-weighted BM25. When hybrid search is enabled, results are merged with dense cosine similarity via RRF. Supports FTS5 operators in raw mode : foo* prefix, AND / OR / NOT, quoted phrases.
Parameter | Type | Required | Description |
| string | no | What held at that moment (date or RFC3339); default now |
| string | no | What mem7 believed at that moment; default now |
| string | yes | Search query |
| string | no |
|
| string[] | no | Post-filter by tags |
| string | no | Post-filter by agent |
| string | no | Lower bound on |
| string | no | Upper bound on |
| number | no | Max results (default 10) |
| boolean | no | Fetch sequential neighbors around matching entries (default false) |
| number | no | How many neighbors to fetch on each side (default 1) |
memory_context
Same search capabilities as memory_search but returns a JSON array of structured objects instead of formatted markdown. Designed for programmatic use by agent SDKs.
Parameter | Type | Required | Description |
| string | no | What held at that moment (date or RFC3339); default now |
| string | no | What mem7 believed at that moment; default now |
| string | yes | Search query |
| string | no |
|
| string[] | no | Post-filter by tags |
| string | no | Post-filter by agent |
| string | no | Lower bound on |
| string | no | Upper bound on |
| number | no | Max results (default 10) |
| boolean | no | Fetch sequential neighbors (default false) |
| number | no | Neighbors on each side (default 1) |
Returns a JSON array of { "key", "value", "tags", "agent", "updated" } objects.
memory_get
Read a file from the markdown workspace, optionally between from_line and to_line (1-indexed, inclusive). Paths are resolved relative to the workspace root and refused if they escape it.
Parameter | Type | Required | Description |
| string | yes | Workspace-relative path (e.g. |
| number | no | First line to read |
| number | no | Last line to read |
memory_list
List memory keys with metadata (without values).
Parameter | Type | Required | Description |
| string | no | What held at that moment (date or RFC3339); default now |
| string | no | What mem7 believed at that moment; default now |
| string[] | no | Filter by tags |
| string | no | Filter by agent |
memory_forget
Delete memories by key and/or tags. A tombstone section is appended to the markdown workspace, and the SQLite index soft-deletes the matching rows.
Parameter | Type | Required | Description |
| string | no | Exact key to delete |
| string[] | no | Delete all entries matching these tags (AND logic) |
| string | no | Recorded on the tombstone |
memory_history
The life of one key, oldest first, read from the markdown workspace (the index only knows the current state): every store, update and deletion, by key or by tags, with its author, the trace id of the governed call behind it, and its seal in the hash chain. Entries written before the chain show as unsealed. Scoped like a read.
Parameter | Type | Required | Description |
| string | yes | Exact key |
HTTP endpoints
mem7 serve exposes these routes :
Method | Path | Description |
|
| Liveness probe — returns |
|
| JSON-RPC 2.0 endpoint — same MCP tool surface as stdio |
|
| Returns a structured instructional payload for an agent runtime to inject into its context before compaction |
|
| The workspace's hash chain report, as |
Bearer auth is applied to /rpc, /mcp, /sse, /messages and /memory/* when MEM7_TOKEN (or --token) is set.
Example :
curl -s -X POST http://localhost:9070/rpc \
-H "Authorization: Bearer $MEM7_TOKEN" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/call",
"params":{"name":"memory_search","arguments":{"query":"roadmap*"}}}'Architecture
Claude Code / flux7-mesh / Python SDK / scripts
│
MCP stdio ┴ HTTP JSON-RPC
│
┌─────▼─────┐
│ Dispatcher │ ← MCP protocol layer
└─────┬─────┘
│
┌─────▼─────┐
│ Store │ ← orchestrator
└──┬──┬──┬──┬┘
│ │ │ │
┌──────▼┐ │ ┌▼──────────┐ ┌▼─────────┐
│markdown│ │ │ sqlite │ │ reranker │
│workspace│ │ │ (facts + │ │ (Ollama) │
│(truth) │ │ │ FTS5 + │ │ opt-in │
└────────┘ │ │ embeds) │ └───────────┘
│ └───────────┘
┌──────▼──────┐
│ embedder │ ← opt-in, external
│ (Ollama / │
│ OpenAI) │
└─────────────┘Every write goes through the markdown writer first and then updates the SQLite index. If hybrid search is enabled, an embedding is computed via the external provider and stored as a BLOB. Reads consult the index only ; embeddings are cached in memory for sub-ms cosine search. If the index is corrupted or out of sync, mem7 rescan drops it and replays the markdown chronologically to reconstruct a consistent state.
License
Apache 2.0
This server cannot be deployed
Maintenance
Related MCP Connectors
One memory, every AI. A shared, user-owned markdown memory your AI clients read and write over MCP.
Persistent memory, hybrid search and a goal graph for AI agents, over stdio or remote HTTP.
- memnodeOAuthdev.memnode
Persistent, inspectable memory for AI agents with lineage, correction, and a hosted MCP endpoint.
Persistent memory for AI agents across Claude, ChatGPT and any MCP client.
Related MCP Servers
- AlicenseBqualityCmaintenancePersistent memory engine for AI coding agents. Single Go binary, zero runtime dependencies, MCP-native. Stores, searches, and deduplicates memories across sessions using embedded SQLite with hybrid FTS + semantic search, memory decay, relation graph, and token-budget context assembly.1023MIT

litopysofficial
AlicenseNot gradedqualityBmaintenancePersistent graph-based memory for AI agents, stored as plain markdown — no vector DB. Typed nodes and 11 relation types via 5 MCP tools (search, get, create, link, related), stdio and HTTP/SSE transports.3MIT- AlicenseNot gradedqualityAmaintenanceLocal-first, file-based memory layer for AI agents — one shared Markdown vault across Claude, Codex, Gemini, Cursor and any MCP client. Provides read/write memory tools with an audit trail, per-agent trust levels, and Git sync; no cloud and no lock-in.2MIT
- AlicenseNot gradedqualityFmaintenanceLocal-first, auditable memory for AI agents. Provides durable context for MCP hosts with SQLite storage, CLI, and MCP tools for memory management.2Apache 2.0