Content
mcp-name: io.github.billy12151/memory-arbiter-mcp
# Tool List
## English
A lightweight, fully local **AI enhancement middleware** — not another memory tool, but a layer that makes every AI client you already use noticeably smarter, by fixing the one thing they all get wrong: **context quality**.
From planning (OpenClaw) to design (OpenDesign) to code (ZCode, Codex, Cursor, Claude Code) — Memory Arbiter connects your entire AI toolchain through one shared SQLite database.
### Why Memory Arbiter?
Your AI client loads `MEMORY.md` + `memory/*.md` into the system **every turn**. As knowledge grows, 5K–20K tokens burn before the model even reads your question — and worse, the model drowns in noise, losing track of what's current, what's confirmed, and what's stale.
Memory Arbiter replaces this with a SQLite-backed search: only the relevant entries come back, everything else stays on disk.
**The result isn't just cheaper — it's sharper.** When the model receives precise, de-duplicated, trust-ranked context, the same model produces noticeably better output. Most AI execution errors aren't the model being dumb — they're the model acting on stale, contradictory, or diluted context. Fix the input, and your existing model jumps a level.
**Works with one tool. Scales to many.**
#### Better output quality (the real win)
Token savings are easy to measure. But the deeper, more impactful change is **output quality** — because Memory Arbiter fixes what the model sees before it even starts thinking.
| What pollutes AI context | How it degrades output | How Memory Arbiter fixes it |
|---|---|---|
| Key info buried in a 20K-token blob | Model attention is spread thin; it grabs the wrong detail or hallucinates. | `memory_search` returns 3–5 laser-relevant entries. High signal-to-noise. |
| Stale and current info mixed together | Model follows an outdated constraint. | Dual timeline + conflict arbitration: outdated entries are flagged or superseded. |
| "The user confirmed this" vs "the AI guessed this" — indistinguishable | Model treats a guess as ground truth. | `source_type` labels + `user_confirmed` lock. The model knows what to trust. |
| Tool switch = context reset | Each tool re-derives understanding from scratch; errors compound. | Shared memory layer: every tool starts from the same verified facts. |
**Same model. Better context. Better output.** This is the core value — everything else (token savings, cross-tool sharing, audit trails) flows from giving your AI clean, structured, de-conflicted context.
#### What does it actually enhance?
Memory Arbiter doesn't add new capabilities to your AI — it **removes the blind spots** that cap the capabilities it already has:
| Bottleneck | Without Memory Arbiter | With Memory Arbiter |
|---|---|---|
| **Attention precision** | Model scans 20K tokens, attention diluted across everything | Gets 3–5 laser-relevant entries. Attention bandwidth is freed for actual reasoning. |
| **Memory consistency** | Old and new info coexist; model may follow stale constraints | Dual timeline + conflict arbitration ensure the model only sees current, validated facts |
| **Trust calibration** | Model can't distinguish user-confirmed facts from AI guesses | `source_type` + `user_confirmed` lock — the model knows exactly what to trust |
| **Cross-tool continuity** | Switching tools = context reset, understanding drifts | Shared memory layer: every tool starts from the same verified baseline |
| **Compounding knowledge** | Memory degrades over time as files grow messy | Structured database gets richer and more precise the more you use it — a positive feedback loop |
**Model-agnostic.** Whether you run GLM, Claude, GPT, or Gemini — the stronger the model, the more sensitive it is to context quality, and the bigger the uplift Memory Arbiter delivers.
#### Solo user: still worth it with one tool
Even if you only use Cursor, Claude Code, Codex, or ZCode — Memory Arbiter upgrades your memory from flat markdown to a queryable database:
| What you get | Without Memory Arbiter | With Memory Arbiter |
|---|---|---|
| **Per-turn token cost** | Entire `MEMORY.md` + `memory/*.md` in system (5K–20K tokens) | `memory_search("keyword")` returns 3–5 hits (200–800 tokens). **~80%+ saving.** |
| **Memory scale ceiling** | Bigger = slower = more expensive. You manually trim. | Thousands of entries, retrieval cost near zero. Stop trimming. |
| **Conflict detection** | AI holds old and new info simultaneously and may not notice contradictions. | `memory_compare(id1, id2)` returns a structured verdict. `memory_arbitrate` resolves it by rules. **~90% cheaper** than LLM-based comparison. |
| **Source trust levels** | All memories are flat text — "the user said this" and "the AI guessed this" look the same. | `user_confirmed` > `document_extracted` > `agent_generated` > `unknown`. High-trust entries are auto-locked. |
| **Audit trail** | Edited in-place, no history. | Every entry carries `agent_id`, `ingest_time`, `event_time`. Full traceability. |
| **Periodic self-check** | Hope the AI remembers correctly, or manually proofread. | `memory_list_conflicts` + `memory_audit_summary` — one call, structured report. |
#### Multi-tool collaboration (bonus)
Using two or more AI clients? All solo benefits apply to each tool, **plus** a shared memory layer:
| What you get | Without Memory Arbiter | With Memory Arbiter |
|---|---|---|
| **Task handoff** | Write spec to file → other tool reads file. Path sync, version drift, copy-paste. | Tool A calls `memory_write`. Tool B calls `memory_search`. **Zero file handoff.** |
| **Cross-tool visibility** | Each tool has its own memory silo. What happened in Cursor is invisible to Claude Code. | One SQLite, isolated by `workspace` / `agent_id`. Write once, search everywhere. |
| **Conflict resolution across tools** | Two tools disagree → user manually reconciles. | `memory_arbitrate` applies the same trust/timeline rules across all tools. |
| **Storage dedup** | Each tool keeps its own copy of shared knowledge. | One database, zero duplication. **100% storage dedup.** |
#### Token savings at a glance
| Scenario | Full-file loading | With Memory Arbiter | Saving |
|---|---|---|---|
| Per-turn memory load | 5K–20K tokens in system | 200–800 tokens via `memory_search` | ~80%+ |
| Conflict detection | LLM compares pairs (N², thousands of tokens) | `memory_compare` returns structured verdict (~200 tokens) | ~90% |
| Periodic audit | LLM scans entire library (10K+ tokens) | `memory_list_conflicts` + `memory_audit_summary` serve structured candidates | ~70% |
| Spec handoff (2000 words) | ~3000 tokens loaded into context | ~500 tokens via targeted `memory_search` | ~83% |
**Positioning, in five lines:**
- ✅ **Execution quality booster** — clean, de-conflicted context → fewer hallucinations, sharper results (**this is the core value**)
- ✅ Structured memory storage — SQLite + dual timeline + source trust levels
- ✅ Token-optimization middleware — precise retrieval replaces full-file loading
- ✅ Conflict arbitration engine — rule-based verdicts with explainable rationale
- ✅ Cross-tool sharing layer — one database, every AI client shares it (optional but powerful)
- ❌ Not an LLM, does not do semantic reasoning — semantic judgement stays with the AI client
#### Real-world example: full creative pipeline (plan → design → code)
The user runs a three-tool pipeline: **OpenClaw** (planning & specs) → **OpenDesign** (page & slide design) → **ZCode** (implementation). Memory Arbiter connects the entire chain.
- **Old way**: OpenClaw writes a spec to a file → OpenDesign reads it, designs something → screenshots/specs are handed to ZCode. Path sync, version drift, copy-paste at every handoff. Or the user repeats the context each time — information loss + wasted tokens.
- **Memory Arbiter way**: OpenClaw calls `memory_write` with the spec → OpenDesign calls `memory_search("the project")` and gets full context, produces designs, writes back design decisions → ZCode calls `memory_search("the project")` and receives both the spec **and** the design decisions in one query — **zero file handoff across three tools.**
**A spec + design handoff that used to cost ~5000 tokens of repeated context loading now costs ~800 tokens via targeted `memory_search`.** This very project ships its own release tasks this way — it dogfoods itself. Full step-by-step in [`docs/INTEGRATION.md`](docs/INTEGRATION.md).
> See [`docs/INTEGRATION.md`](docs/INTEGRATION.md) for three concrete usage patterns (per-turn retrieval, scheduled audit, write-time conflict check) and the full cross-tool delegation walkthrough.
Features
- **Structured memory write**: `content`, `agent_id`, `workspace`, `tags`, `source_type`, `event_time`, `ingest_time`, `confidence`, `protection_level`, and more.
- **Source trust levels**: `user_confirmed` > `document_extracted` > `agent_generated` > `unknown`.
- **Dual timeline arbitration**: resolves conflicts by user confirmation → event time → source trust → ingest time. Every decision comes with an explainable rationale.
- **Locked protection**: `user_confirmed` memories are automatically locked — no agent can overwrite them.
- **Client policy system**: per-client enable/disable, agent allow/deny lists for multi-agent governance.
- **Graceful degradation**: `sqlite-vec` → FTS5 → `LIKE` → JSONL backup. Never crashes.
- **Zero cloud, zero LLM calls**: pure local SQLite. No Postgres, Redis, or external services.
### Quick Start
**Requirements**: Python 3.11+
```bash
# Clone
git clone https://github.com/billy12151/memory-arbiter-mcp.git
cd memory-arbiter-mcp
# Setup
python3.11 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
pip install -e .
# Optional: semantic recall via sqlite-vec
pip install '.[vec]'
# Run
memory-arbiter-mcp
```
### Connect Your Tool
Add to your tool's MCP config (see `examples/` for ready-made templates):
```json
{
"mcpServers": {
"memory-arbiter": {
"command": "/path/to/memory-arbiter-mcp/.venv/bin/memory-arbiter-mcp",
"env": {
"MEMORY_ARBIT": "zcode",
"MEMORY_ARBITER_AGENT_ID": "zcode-default",
"MEMORY_ARBITER_DB_PATH": "~/.local/share/memory-arbiter/memory.sqlite3"
}
}
}
}
```
> Change `MEMORY_ARBITER_CLIENT` for each tool (`openclaw`, `zcode`, `codex`, `cursor`, `claude-code`). Point multiple tools at the same `DB_PATH` to enable cross-tool memory sharing. (GUI tools like OpenDesign inherit the host CLI's config — no separate client name.)
> ⚠️ **New session required**: MCP servers are loaded at session startup. Already-open sessions won't see the new tools. Start a fresh session after configuring.
### Client Config Locations
| Client | Config Location |
|---|---|
| ZCode | `~/.zcode/` MCP config |
| Codex CLI | `~/.codex/` MCP config |
| Claude Code | `.mcp.json` in project root |
| Cursor | `~/.cursor/mcp.json` |
| OpenClaw | `~/.openclaw/openclaw.json` MCP config |
> **OpenDesign / OpenClaw GUI tools**: these run on top of a host CLI (Codex CLI, Claude Code, etc.) and do **not** have their own MCP config entry. Whatever MCP server the host client has loaded is automatically available — e.g. once Codex CLI configures Memory Arbiter, OpenDesign running on top of Codex can call `memory_search` / `memory_write` natively with no extra setup.
### MCP Tools
| Tool | Description |
|---|---|
| `memory_write` | Write a memory (`source_type=user_confirmed` auto-locks) |
| `memory_search` | Search memories (FTS5 → LIKE fallback) |
| `memory_compare` | memories, returns explanation only `memory_arbitrate` | Arbitrate conflict, can (`apply=true`) |
memory_confirm` | Promote a memory to user-confirmed and locked |
| `memory_supersede` | Explicitly retire a memory; bypasses user-confirmed/locked protection (`authorized=true` |
| `memory_list_conflicts` | List unresolved conflicts `memory_audit_summary` | Per-workspace stats overview (counts, oldest/newest, open conflicts, source_type distribution) |
| `memory_store_embedding` | (v0.3.1, optional) Store an embedding for a memory — enables semantic recall via `memory_search(query_embedding=...)`. Model-agnostic: you generate, memory-arbiter stores and queries it. |
| `memory_status` | Show current degradation status, storage paths Optional: Semantic Recall (v0.3.1)
By default, memory-arbiter uses **lexical recall** (FTS5 trigram + BM25 + soft-rerank) — no embedding model, no heavy dependencies, fully local. This is enough for most cases.
For queries where wording differs but meaning is the same ("happy" vs "joyful", "金营平台" vs "金融带货"), you can opt into **semantic recall**. memory-arbiter does **not** bundle an embedding model — you bring your own, so the default install stays lightweight and you keep full control over the model, language, and cost.
**Setup (4 steps):**
1. Install sqlite-vec and enable it (off by default — it's an optional dependency):
```bash
pip install memory-arbiter-mcp[vec]
export MEMORY_ARBITER_ENABLE_SQLITE_VEC=true
```
2. Choose an embedding model and install its runtime. memory-arbiter does **not** bundle or call any model — your backfill script does. Three supported paths:
- **GGUF (local, recommended)** — works withUF embedding model via `llama-cpp-python`. Reuses models you may already have (e.g. from OpenClaw/llama.cpp). Point the script at the file:
```bash
pip install llama-cpp-python export MEMORY_ARBITER_GGUF=/path/to/embedding-model # e.g. embeddinggemma-300m-qat-Q8_0.gguf
```
- **sentence-transformers (local)** — HuggingFace PyTorch models (`bge-small-zh`, `bge-base-en`, etc.):
```bash
pip install sentence-transformers
# pass --model BAAI/bge-small-zh-v1.5 to the example script
- **Remote API (OpenAI / Zhipu / Tongyi)** — call the API in your own backfill script, pass vectors to `memory_store_embedding`. memory-arbiter only needs `pip install memory-arbiter-mcp[vec]`; no model runtime on this side.
3. Set the vector dimension to match your model, then restart the MCP server (it creates the `memories_vec` table on startup):
```bash
export MEMORY_ARBITER_VEC_DIM=512 # MUST match the model: bge-small-zh=512, embeddinggemma-300m/bge-base=768, OpenAI text-embedding-3-small=1536
```
4. Backfill embeddings into existing memories, then query with a vector:
```bash
python docs/semantic_example.py # backfill all memories (uses MEMORY_ARBITER_GGUF or --model)
python docs/semantic_example.py --query "金营平台营销" # try a semantic search
```
After backfill, call `memory_search` with an optional `query_embedding` (same model, same dim) — the semantic channel joins wide-recall pool. Pass nothing and behaviour is identical to v0.3.0.
**How it ranks:** semantic candidates get a *floor score* just below content matches — they beat content-only noise but never outrank a real subject/tags hit. The arbitration and trust layer is untouched.
**Measured impact (small sample, not a formal benchmark):** on the same 15 golden queries + 18 pairwise constraints used to validate v0.3.0, enabling semantic recall improved Top-3 hit rate and pairwise ordering. The biggest win was pairwise pass rate reaching 100% — every "should-rank-above" constraint held. overhead was ~8ms per query on the memory-arbiter side (embedding generation cost is borne by the caller).
| Metric | bm25 (v0.2.6) | hybrid (v0.3.0) | hybrid + semantic (v0.3.1) |
|---|---|---|---|
| Top-1 hit rate | 46.7% | 53.3% | 53.3% |
| Top-3 hit rate | 60.0% | 66.7% | **73.3%** |
| Pairwise pass rate | 77.8% | 88.9% | **100.0%** |
### Configuration
All configuration is via environment variables. The defaults work for small libraries; the full table with explanations is in [`docs/INTEGRATION.md`](docs/INTEGRATION.md).
| Variable | Default | What to tune |
|---|---|---|
| `MEMORY_ARBITER_DB_PATH` | `./memory_arbiter.sqlite3` | Shared path for cross-tool memory. |
| `MEMORY_ARBITER_CLIENT` | `codex` | Per-tool identity (`codex`, `claude-code`, `cursor`, `zcode`, ...). |
| `MEMORY_ARBITER` | `default` key. |
| `_ARBITER_ENABLE_VEC` | ` | Set `true` to enable semantic recall (requires install memory-arbiter-mcp[vec]`). Off by default — pure lexical recall works without it. |
| `MEMORY_ARBITER_VEC_DIM` | `768` | Must match your embedding model. |
| `MEMORY_ARBITER_RECALL_POOL_CAP` | `50` | **Raise to 100–200 when exceeds ~100 entries** — first knob to turn if matches go missing. |
| `_ARBITER_CONTENT_LIKE_CAP` | `30` | Raise if many same-topic memories exist. |
| `MEMORY_ARBITER_RANKING_MODE` | `hybrid` | `hybrid` (default) or `bm25` (legacy). |
### Data Migration
Moving to a new machine? Just copy the SQLite file:
```# Copy the database
cp ~/.local/share/memory-arbiter/memory.sqlite3 /new/machine/~/.local/share/memory-arbiter/
# Reinstall the project (don't copy .venv — rebuild it)
python3.11 -m venv .venv
source .venv/bin/activate
pip install -e .
```
### Testing
```bash
python3.11 -m -r requirements.txt
python3.11 -m pytest
```
### License
MIT
---
<a id="English"></a>
## English
A lightweight, fully local **AI enhancement middleware** — not just another memory tool, but a foundational layer that makes every AI client you already use noticeably smarter, fixing a common weakness across AI models: **contextual quality**.
From planning (OpenClaw) to design (OpenDesign) to coding (ZCode, Codex, Cursor, Claude Code) — memory-arbiter ties your entire AI toolchain together with a shared SQLite database.
### Why Memory Arbiter?
Your AI client loads `MEMORY.md` and `memory/*.md` into the system prompt for every conversation. The more memories, the more tokens consumed - and the model hasn't even read your question yet, 5K-20K tokens of context have already been burned. Worse, the model is drowned in noise, unable to distinguish what's new, what's confirmed by the user, and what's outdated.
Memory Arbiter uses SQLite to retrieve relevant entries only, leaving the rest on disk.
**The result is not just cost savings - but also more accurate.** When the model receives precise, deduplicated, and credibility-ranked context, the output quality of the same model will significantly improve. Most AI execution errors aren't due to the model's limitations, but because it receives outdated, contradictory, or noisy input. Fixing the input end will directly elevate your existing model's performance.
**One tool is enough. Multiple tools are even better.**
#### Output Quality Improvement (Core Value)
Token savings are quantifiable, but the deeper and more critical change is **output quality** - because Memory Arbiter improves what the model sees before it starts thinking.
| Issues Polluting AI Context | Execution Deviation | How memory-arbiter Fixes |
|---|---|---|
| Key information buried in 20K token text | Model attention scattered, wrong focus or fabrication | `memory_search` returns 3-5 highly relevant results, maximizing signal-to-noise ratio |
| Old and new information mixed | Model executes based on outdated constraints | Dual timeline + conflict arbitration, outdated entries marked or eliminated |
| "User-confirmed" and "AI-guessed" indistinguishable | Model uses guesses as facts | `source_type` marks source, `user_confirmed` automatically locks, model knows what to trust |
| Context lost when switching tools | Each tool re-understands, cumulative understanding deviation | Shared memory layer, all tools start from the same verified facts |
**Same model, correct context, better output.** This is the core value - other benefits (token savings, cross-tool sharing, audit tracing) naturally follow.
#### What It Enhances
Memory Arbiter doesn't add new capabilities to your AI - it **removes blind spots that cap existing capabilities**:
| Bottlenecks | Without memory-arbiter | With memory-arbiter |
|---|---|---|
| **Attention Precision** | Model scans 20K tokens, attention diluted | Gets 3-5 precise results, attention bandwidth freed for real reasoning |
| **Memory Consistency** | New and old information coexist, model may follow outdated info | Dual timeline + conflict arbitration, model sees only verified facts |
| **Credibility Judgment** | Model can't distinguish user-confirmed facts from AI guesses | `source_type` + `user_confirmed` lock in, model knows what to trust |
| **Cross-Tool Continuity** | Switching tools = context reset, understanding deviation accumulates | Shared memory layer, each tool starts from the same verified facts |
| **Knowledge Compounding** | Memories degrade over time, files get messy | Structured database gets richer and more precise over time - positive feedback loop |
**Model-agnostic.** Whether you use GLM, Claude, GPT, or Gemini - the stronger the model, the more sensitive it is to context quality, and the greater the improvement Memory Arbiter brings.
#### Single-Client Users: Worth It Even with One Tool
Even if you only use Cursor, Claude Code, Codex, or ZCode, Memory Arbiter can upgrade your memories from flat markdown to a queryable database:
| Benefits | Without memory-arbiter | With memory-arbiter |
|---|---|---|
| **Token Consumption per Round** | Loads entire `MEMORY.md` + `memory/*.md` (5K-20K tokens) | `memory_search("keyword")` returns 3-5 entries (200-800 tokens). **Saves 80%+.** |
| **Memory Scale Ceiling** | Larger, slower, and more expensive, forced manual curation | SQLite stores thousands of entries, retrieval cost near zero. No need for curation. |
| **Conflict Detection** | AI remembers old and new information, may not detect contradictions | `memory_compare(id1, id2)` returns structured decisions, `memory_arbitrate` judges by rules. **Saves 90%.** |
| **Source Credibility** | All plain text, "user-said" and "AI-guessed" look the same | `user_confirmed` > `document_extracted` > `agent_generated` > `unknown`, high-credibility entries automatically locked. |
| **Audit Tracing** | Changes made without record, who, when unknown | Each memory has `agent_id` / `ingest_time` / `event_time`, complete tracing. |
| **Scheduled Self-Inspection** | Relies on AI or manual checks | `memory_list_conflicts` + `memory_audit_summary` provide reports with one call. |
#### Multi-Client Collaboration (Advanced Benefits)
Using two or more AI tools simultaneously? All single-client benefits **apply to each tool**, plus a shared memory layer:
| Benefits | Without memory-arbiter | With memory-arbiter |
|---|---|---|
| **Task Handover** | Specifications written to files → another tool reads files, path synchronization, version chaos, copy-paste | Tool A calls `memory_write`, Tool B calls `memory_search`. **Zero file transfer.** |
| **Cross-Tool Visibility** | Each tool has its own isolated memory, Cursor events invisible to Claude Code | One SQLite, isolated by `workspace` / `agent_id`, write once, search everywhere. |
| **Cross-Tool Conflict Arbitration** | Two tools write different versions of the same thing → user manual reconciliation | `memory_arbitrate` uses the same credibility/timeline rules for cross-tool decisions. |
| **Storage Deduplication** | Each tool stores its own copy of shared knowledge | One database, zero duplication. **100% storage deduplication.** |
#### Token Savings Overview
| Scenario | Full Text Loading | With memory-arbiter | Savings |
|---|---|---|---|
| Memory loading per round | 5K-20K tokens | 200-800 tokens | ~80%+ |
| Conflict detection | LLM compares line-by-line (N², thousands of tokens) | `memory_compare` returns structured decisions (~200 tokens) | ~90% |
| Regular review | LLM scans entire database (thousands of tokens) | `memory_list_conflicts` + `memory_audit_summary` provide structured candidates | ~70% |
| Specification handover (2000 words) | Loads ~3000 tokens | Precise `memory_search` ~500 tokens | ~83% |
**Positioning (Five Sentences):**
- ✅ **Execution Quality Amplifier** — Clean, conflict-free context → fewer hallucinations, more accurate results (**core value**)
- ✅ Structured memory storage — SQLite + dual timeline + source credibility
- ✅ Token optimization middleware — precise retrieval replaces full text loading
- ✅ Conflict arbitration engine — rule-based decisions with interpretable reasons
- ✅ Cross-tool sharing layer — one database, shared by all AI clients (optional but recommended)
- ❌ Not LLM, no semantic reasoning — semantic judgment left to AI clients
#### Real Case: Complete Creative Pipeline (Planning → Design → Coding)
User runs a three-tool pipeline: **OpenClaw** (planning & specifications) → **OpenDesign** (page & PPT design) → **ZCode** (code implementation). Memory Arbiter connects the entire chain.
- **Traditional Way**: OpenClaw writes specifications to files → OpenDesign reads files for design → design and specifications handed to ZCode, each handover requires path agreement, manual synchronization, version control; or users repeat descriptions, information loss, and token waste.
- **Memory Arbiter Way**: OpenClaw calls `memory_write` to write specifications → OpenDesign calls `memory_search("project")` to get complete context, designs, and writes back → ZCode calls `memory_search("project")` to get specifications **and** design decisions — **zero file transfer**.
**One specification + design handover: traditional loading ~5000 tokens, now precise retrieval ~800 tokens.** This project uses its own product to feed its own product. Complete steps see [`docs/INTEGRATION.md`](docs/INTEGRATION.md).
> Three typical use cases (on-demand retrieval per round, scheduled review, conflict detection during writing) and complete cross-tool delegation steps see [`docs/INTEGRATION.md`](docs/INTEGRATION.md).
### Core Capabilities
- **Structured Writing**: `content`, `agent_id`, `workspace`, `tags`, `source_type`, `event_time`, `ingest_time`, `confidence`, `protection_level`, etc.
- **Source Credibility**: `user_confirmed` > `document_extracted` > `agent_generated` > `unknown`.
- **Dual Timeline Arbitration**: prioritizes user confirmation → event time → source credibility → ingestion time, outputs interpretable decisions.
- **Locking Protection**: `user_confirmed` memories automatically locked, any Agent cannot overwrite.
- **Client Strategy**: enable/disable by client, allow/deny whitelist control by Agent.
- **Gradual Degradation**: `sqlite-vec` → FTS5 → `LIKE` → JSONL backup, never crashes.
- **Zero Cloud Dependency, Zero Large Model Calls**: pure local SQLite, no need for Postgres, Redis, or external services.
### Quick Start
**Requirements**: Python 3.11+
```bash
# Clone
git clone https://github.com/billy12151/memory-arbiter-mcp.git
cd memory-arbiter-mcp
# Install
python3.11 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
pip install -e .
# Optional: enable semantic recall enhancement (sqlite-vec)
pip install '.[vec]'
# Start
memory-arbiter-mcp
```
### Tool Integration
Add to your tool's MCP configuration (see `examples/` directory for a complete example):
```json
{
"mcpServers": {
"memory-arbiter": {
"command": "/path/to/memory-arbiter-mcp/.venv/bin/memory-arbiter-mcp",
"env": {
"MEMORY_ARBITER_CLIENT": "zcode",
"MEMORY_ARBITER_AGENT_ID": "zcode-default",
"MEMORY_ARBITER_DB_PATH": "~/.local/share/memory-arbiter/memory.sqlite3"
}
}
}
}
```
> Change `MEMORY_ARBITER_CLIENT` identifier for each tool (e.g., `openclaw`, `zcode`, `codex`, `cursor`, `claude-code`). Multiple tools can point to the same `DB_PATH` for cross-tool memory sharing. (OpenDesign-like GUI tools inherit the host CLI's configuration, no separate client name needed.)
> ⚠️ **New session required**: MCP Server loads when the client starts; existing sessions won't recognize the new server. Create a new session after configuration.
### Client Configuration Locations
| Client | Configuration File Location |
|---|---|
| ZCode | `~/.zcode/v2/` MCP configuration |
| Codex CLI | `~/.codex/` MCP configuration |
| Claude Code | project root `.mcp.json` |
| Cursor | `~/.cursor/mcp.json` |
| OpenClaw | `~/.openclaw/openclaw.json` MCP configuration |
> **OpenDesign / OpenClaw GUI-like tools**: These tools reside on top of the underlying CLI (Codex CLI, Claude Code, etc.) and **do not have their own MCP configuration entry**. The MCP Server loaded by the host client is inherited by GUI tools - e.g., if Codex CLI has memory-arbiter configured, OpenDesign running on Codex can directly call `memory_search` / `memory_write` without additional setup.
### MCP Tools
| Tool | Description |
|---|---|
| `memory_write` | Write memory (automatically locks `source_type=user_confirmed`) |
| `memory_search` | Search memory (FTS5 → LIKE automatically degrades) |
| `memory_compare` | Compare two memories, returns only explanations |
| `memory_arbitrate` | Arbitrate conflicts, automatically determines the winner (writes record when `apply=true`) |
| `memory_confirm` | User confirms a memory, locks protection |
| `memory_supersede` | Explicitly discard a memory; can override `user_confirmed` / locked protection (requires `authorized=true`) |
| `memory_list_conflicts` | List unresolved conflicts |
| `memory_audit_summary` | Workspace memory statistics overview (entry count, oldest/newest, open conflicts, source distribution) |
| `memory_store_embedding` | (v0.3.1, optional) Store semantic vectors for a memory - afterwards, `memory_search(query_embedding=...)` uses semantic recall. No model binding: you generate vectors, memory-arbiter stores and queries. |
| `memory_status` | View running status, mode, degradation reasons |
### Optional: Semantic Retrieval (v0.3.1)
By default, memory-arbiter uses **literal retrieval** (FTS5 trigram + BM25 + soft reordering) - no dependency on embedding models, no additional heavy dependencies, completely local. This is sufficient for most scenarios.
For "different wording but same semantics" queries (e.g., searching "happy" to match "happy"), you can **optionally enable semantic retrieval**. memory-arbiter **does not embed** models - you bring your model, so default installation remains lightweight, model selection, language, and cost entirely controlled by you.
**Four steps to enable:**
1. Install sqlite-vec and explicitly enable it (default off):
```bash
pip install memory-arbiter-mcp[vec]
export MEMORY_ARBITER_ENABLE_SQLITE_VEC=true
```
2. Choose an embedding model, install its runtime. memory-arbiter **does not embed or call** any model - your vector-filling script calls. Three ways:
- **GGUF (local, recommended)** — run any GGUF embedding model via `llama-cpp-python`. Reuse your existing model (e.g., OpenClaw / llama.cpp). Point the script to the model file:
```bash
pip install llama-cpp-python
export MEMORY_ARBITER_GGUF=/path/to/embedding-model.gguf # e.g., embeddinggemma-300m-qat-Q8_0.gguf
```
- **sentence-transformers (local)** — HuggingFace PyTorch models (`bge-small-zh`, `bge-base-en`, etc.):
```bash
pip install sentence-transformers
# Pass --model BAAI/bge-small-zh-v1.5 to example script
```
- **Remote API (OpenAI / Zhipu / Tongyi)** — call API in your vector-filling script, pass vectors to `memory_store_embedding`. memory-arbiter only requires `pip install memory-arbiter-mcp[vec]`, no model running.
3. Set vector dimension to your model's output dimension, restart MCP server (it creates `memories_vec` table on startup):
```bash
export MEMORY_ARBITER_VEC_DIM=512 # must match model: bge-small-zh=512, embeddinggemma-300m/bge-base=768, OpenAI text-embedding-3-small=1536
```
4. Batch灌 existing memories with vectors, then query with vectors:
```bash
python docs/semantic_example.py #灌向量所有记忆 (use MEMORY_ARBITER_GGUF or --model)
python docs/semantic_example.py --query "金营平台营销" # try semantic retrieval
```
After灌完, call `memory_search` with an additional `query_embedding` (same model, same dimension) - semantic recall will join the existing wide recall candidate pool. If not passed, behavior remains the same as v0.3.0.
**Ranking rules**: semantic recall candidates get a **minimum score** (slightly lower than full-text match score) - it can suppress "full-text incidental mentions" noise but never overrides actual title/tag matches. Arbitration and credibility layer logic remains unchanged.
**Actual Effect (small sample, informal benchmark)**: On the same 15 golden queries + 18 pairwise constraints used in v0.3.0 validation, enabling semantic retrieval further improves Top-3 hit rate and ranking quality. The highlight is pairwise pass rate reaches **100%** - all "those that should be ranked higher are ranked higher". memory-arbiter query latency only increases by ~8ms (query vector generation time counted on the caller side).
| Metrics | bm25 (v0.2.6) | hybrid (v0.3.0) | hybrid + semantic (v0.3.1) |
|---|---|---|---|
| Top-1 Hit Rate | 46.7% | 53.3% | 53.3% |
| Top-3 Hit Rate | 60.0% | 66.7% | **73.3% |
| Pairwise Pass Rate | 77.8% | 88.9% | **100.0% |
### Configuration
All configurations are done through environment variables, and the default values are sufficient for small memory libraries. For a complete description, see [`docs/INTEGRATION.md`](docs/INTEGRATION.md).
| Variable | Default Value | When to Adjust |
|---|---|---|
| `MEMORY_ARBITER_DB_PATH` | `./memory_arbiter.sqlite3` | Set to the same path when sharing memory across tools. |
| `MEMORY_ARBITER_CLIENT` | `codex` | A unique identifier for each tool (e.g., `codex`, `claude-code`, `cursor`, `zcode`, …). |
| `MEMORY_ARBITER_WORKSPACE` | `default` | Workspace isolation key. |
| `MEMORY_ARBITER_ENABLE_SQLITE_VEC` | `false` | Set to `true` to enable semantic search (requires `pip install memory-arbiter-mcp[vec]`). Default is off - pure literal search does not require it. |
| `MEMORY_ARBITER_VEC_DIM` | `768` | Must match your embedding model. |
| `MEMORY_ARBITER_RECALL_POOL_CAP` | `50` | **Adjust to 100–200 when memory exceeds about 100 entries** - if relevant memories are missing from search results, try adjusting this first. |
| `MEMORY_ARBITER_CONTENT_LIKE_CAP` | `30` | Increase when there are many memories on the same topic. |
| `MEMORY_ARBITER_RANKING_MODE` | `hybrid` | Choose `hybrid` (default) or `bm25` (legacy). |
### Data Migration
To switch computers, simply copy one file:
```bash
# Copy the database
cp ~/.local/share/memory-arbiter/memory.sqlite3 new-computer:~/.local/share/memory-arbiter/
# Reinstall the project (do not copy .venv, rebuild it on the new machine)
python3.11 -m venv .venv
source .venv/bin/activate
pip install -e .
```
### Testing
```bash
python3.11 -m pip install -r requirements.txt
python3.11 -m pytest
```
### License
MIT
MCP Config
Below is the configuration for this MCP Server. You can copy it directly to Cursor or other MCP clients.
mcp.json
Connection Info
You Might Also Like
No recommendations available