Back to Browse

Memory Arbiter MCP Server

Developer ToolsUse Caution4.8MCP RegistryLocal
Free

Server data from the Official MCP Registry

Shared memory store for AI coding tools with dual-timeline conflict arbitration.

About

Shared memory store for AI coding tools with dual-timeline conflict arbitration.

Security Report

4.8
Use Caution4.8High Risk

Memory Arbiter is a well-structured local MCP server for managing shared memory across AI coding tools. The codebase demonstrates good security practices with proper input validation, graceful degradation, and appropriate permission scoping. Minor code quality concerns around broad exception handling and env var usage do not significantly impact the overall security posture. Supply chain analysis found 4 known vulnerabilities in dependencies (0 critical, 3 high severity). Package verification found 1 issue.

8 files analyzed · 10 issues found

Security scores are indicators to help you make informed decisions, not guarantees. Always review permissions before connecting any MCP server.

Permissions Required

This plugin requests these system permissions. Most are normal for its category.

File System Read

Reads files on your machine. Normal for tools that analyze or process local data.

File System Write

Writes or modifies files on your machine. Check that this is expected for the tool.

env_vars

Check that this permission is expected for this type of plugin.

sqlite_database

Check that this permission is expected for this type of plugin.

What You'll Need

Set these up before or after installing:

Client identity (e.g. zcode, codex, cursor, claude-code)Optional

Environment variable: MEMORY_ARBITER_CLIENT

Agent identifier for this clientOptional

Environment variable: MEMORY_ARBITER_AGENT_ID

Path to the SQLite database fileOptional

Environment variable: MEMORY_ARBITER_DB_PATH

How to Install

Add this to your MCP configuration file:

{
  "mcpServers": {
    "io-github-billy12151-memory-arbiter-mcp": {
      "env": {
        "MEMORY_ARBITER_CLIENT": "your-memory-arbiter-client-here",
        "MEMORY_ARBITER_DB_PATH": "your-memory-arbiter-db-path-here",
        "MEMORY_ARBITER_AGENT_ID": "your-memory-arbiter-agent-id-here"
      },
      "args": [
        "memory-arbiter-mcp"
      ],
      "command": "uvx"
    }
  }
}

Documentation

View on GitHub

From the project's GitHub README.

mcp-name: io.github.billy12151/memory-arbiter-mcp

memory-arbiter-mcp

中文 | English


English

Memory Arbiter is a trustworthy local fact layer for AI agents.

Chinese name: 迷码. Short name / CLI alias: mema.

It can be used as shared memory, but its real job is fact governance: keeping long-running project context searchable, traceable, source-aware, conflict-aware, and safe to recall.

Shared memory lets every tool see the same data. Memory Arbiter goes further: it helps agents tell which facts are current, user-confirmed, stale, conflicting, superseded, or still waiting for judgment.

# Instead of dumping 20K tokens of MEMORY.md into every prompt:
memory(action="find", data={"query": "auth migration plan"})  → 3 laser-relevant entries, ~400 tokens

Shared memory is the starting point. Fact governance is the moat.

The core is fully local and model-free by default: one SQLite database, no Postgres, no Redis, no hosted memory service, and no model call is required for lexical recall, structured-claim detection, governance, or repair. Optional semantic recall and semantic conflict notices use your own local GGUF models; optional update checks can be disabled.

The problem

Many memory systems focus on how agents remember. Memory Arbiter focuses on what happens after memory becomes shared.

When Claude Code, Cursor, Codex, ZCode, WorkBuddy, OpenClaw, or other tools all write into the same long-term context, forgetting is no longer the only failure mode. The harder failures are:

  • stale facts mixed with current decisions;
  • AI guesses treated like user-confirmed truth;
  • contradictory conclusions written by different tools;
  • long project histories drowning the few facts that matter;
  • tool switching causing either context loss or repeated context pollution;
  • local memory growing until every prompt starts with thousands of irrelevant tokens.

Memory Arbiter turns those risks into explicit data structures: source labels, confidence, event time, version history, supersede chains, conflict records, structured claim gates, section indexes, workspace boundaries, and doctor diagnostics.

The model still does semantic reasoning. Arbiter keeps the input side cleaner.

What Memory Arbiter does

NeedWhy ordinary memory is not enoughMemory Arbiter's answer
Targeted recallA flat MEMORY.md or large vector blob returns too much context.memory(action="find") returns a small set of relevant, ranked entries instead of loading full files.
Source trustUser-confirmed facts, document extracts, and AI guesses look the same.source_type, confidence, user_confirmed, and locked records make trust visible.
Time and evolutionOld decisions stay next to new decisions, and the model may follow the stale one.event_time, ingest_time, version, history (memory_review), and supersede (memory_govern) preserve the evolution chain.
ConflictsTwo memories can disagree and both still be retrieved.Deterministic structured-claim collisions, optional write-time semantic notices, conflict records/signals, and explicit governance make disagreement visible; there is no periodic vector scanner.
Write-time safetyLast-write-wins silently overwrites or piles up contradictory facts.Structured claim gates persist explicit collisions as pending_llm and require the host LLM to submit a snapshot-pinned judgment before the affected claim is used. The judgment is guidance, not an automatic edit or supersede.
Long documentsThe relevant paragraph is buried inside a 10K+ character memory.Section split returns the matched sections instead of forcing the model to scan the whole document.
Project boundariesGlobal memory can leak facts across unrelated projects.Workspace isolation supports none, weak, and strict modes with alias canonicalization.
Long-running healthUsers only notice memory problems after bad answers.doctor reports config, vector readiness, split health, consistency, capacity, and conflict buildup.
Privacy and ownershipHosted memory adds another service and another data boundary.Local SQLite, user-owned files, optional local embeddings, no built-in LLM dependency.

Daily mental model

Most agents only need four product tools (the default MCP surface):

  1. memory — daily operations: remember new facts, find active facts, read a memory by ID, update an existing current memory, judge conflicts, and status. Call action=help for field examples.
  2. memory_review — read-only inspection: overview, doctor, conflicts, conflict detail, judgments, history, expired memories, audit, and entities.
  3. memory_govern — explicit user-authorized governance: retire a whole memory, resolve a conflict, confirm a memory, or correct a judgment. Not for ordinary updates.
  4. memory_repair — maintenance: section split, rebuild claims/embeddings, cleanup, vector resync, entity backfill, pending activation, and backup replay. Prefer dry-run first.

Low-level tool implementations remain in the codebase and are reused by the product tools, but their schemas are not exposed by default. Set MEMORY_ARBITER_TOOL_PROFILE=legacy_full (or full) to expose them alongside the product tools.

How it differs

Memory Arbiter does not compete by saying that other tools cannot share memory. Shared memory is becoming standard. Memory Arbiter focuses on what shared memory needs next.

Compared withMemory Arbiter focuses on
Plain markdown memoryTargeted recall instead of full prompt loading, plus history and conflict state.
Vector memoryNot just similar recall, but source trust, stale/superseded state, and conflict-aware recall.
Graph memoryNot just what is connected, but what is current, trusted, conflicting, or safe to use.
Hosted memoryLocal SQLite, caller-owned policy, no hosted database, and a model-free default core; optional local GGUF features remain local.
Generic MCP memoryA fact-governance layer: trust labels, time evolution, structured claim gates, doctor, and repair tools.

Graph-like signals exist where they help governance: event time, ingest time, entity/scope, conflict edges, supersede chains, sections, and workspace boundaries. Memory Arbiter treats original facts as the primary asset and derived indexes as support structures.

Token savings are a side effect

The main value is better context quality. Token savings are the most visible effect.

ScenarioFull-file loadingWith Memory ArbiterSaving
Per-turn memory load5K–20K tokens in system prompt200–800 tokens via memory(action="find")~80%+
Conflict detectionLLM compares pairs with large contextStructured candidates + focused judgment~90%
Periodic auditLLM scans the whole librarymemory_review(conflicts) + memory_review(audit)~70%
Spec handoffRe-load full spec/design notesQuery the relevant facts and decisions~80%+

Same model. Better input. Better output.

Works with one tool. Scales to many.

With one tool, Memory Arbiter upgrades local memory from flat files into a queryable fact layer with trust labels, history, conflict signals, and diagnostics.

With multiple tools, it also becomes shared memory: Tool A writes, Tool B searches, Tool C audits. No file handoff, no copy-paste, no version drift.

Example pipeline:

  1. OpenClaw writes a spec with memory(action="remember").
  2. OpenDesign reads the spec with memory(action="find"), writes back design decisions.
  3. ZCode searches once and gets both the spec and design decisions.

Three tools, one local fact layer.

For concrete usage patterns and a cross-tool walkthrough, see docs/INTEGRATION.md.

Core capabilities

  • Targeted retrieval — return the relevant entries instead of loading full memory files every turn.
  • Trust levels — separate user-confirmed facts, document extracts, AI-generated notes, and unknown sources.
  • Temporal history — track event time, ingest time, versions, history snapshots, and supersede chains.
  • Conflict governance — deterministic claim collisions and optional semantic notices discover candidates; agents/users inspect, judge, resolve, or supersede explicitly. No periodic vector scanner runs.
  • Structured claim gates — write/edit-time deterministic claim detection with required, snapshot-pinned host-LLM judgment before the affected claim is used; the receipt does not mutate either memory.
  • Long-document section split — asynchronously split safe Markdown headings into searchable sections; ambiguous prose returns an Agent continuation request.
  • Workspace isolation — choose none, weak, or strict isolation with workspace alias canonicalization.
  • Smart tag ranking and filters — tags act as discrete ranking/filter labels, not weak text fragments.
  • Semantic recall — optional local GGUF embeddings for meaning-based recall, while lexical recall remains the default.
  • Doctor diagnostics — read-only health checks for config, vector readiness, split, claims, consistency, capacity, and conflicts.
  • Graceful degradation — sqlite-vec → FTS5 → LIKE for recall; when SQLite is unavailable or unwritable, writes attempt a schema-1 JSONL backup and report failure if that append fails.
  • Local-first storage — pure SQLite, no hosted database, no Redis/Postgres requirement, and no model dependency for the core; local GGUF features are opt-in.

What it is not: Memory Arbiter is not an LLM and does not replace your AI client. It is a structured storage, retrieval, arbitration, and diagnostics layer underneath the model.

Quick Start

Requirements: Python 3.11+ (3.11, 3.12, or 3.13).

# Clone
git clone https://github.com/billy12151/memory-arbiter-mcp.git
cd memory-arbiter-mcp

# Setup — use whichever python3.1x you have (>=3.11)
python3.11 -m venv .venv
source .venv/bin/activate
pip install -e .

# Optional: semantic recall via sqlite-vec
pip install -e '.[vec]'

# Run (short alias)
mema

# Compatible long names still work:
# memory-arbiter
# memory-arbiter-mcp
Zero-install via uvx

If you just want to run the server without managing a Python environment, install uv once, then:

uvx --from memory-arbiter-mcp mema

This pulls the published package and launches the mema entry point. mema is the short alias for Memory Arbiter; memory-arbiter-mcp and memory-arbiter remain compatible long names. uvx only shortens the install path; embedding models and sqlite-vec still need separate setup if you want semantic recall.

Setup helper

Instead of editing config.json by hand, run:

mema setup

The helper writes a working config to ~/.config/memory-arbiter/config.json, checks your environment, and prints the exact commands or download URLs you still need. It does not run pip or download models for you.

Useful flags: --print-config, --no-config, --force.

Local Console MVP

Start the read-only local governance Console:

mema console

It opens http://127.0.0.1:18876 by default. Optional flags:

mema console --no-open       # start the server without opening a browser
mema console --port 18877    # use a different port when 18876 is busy

The Console listens on 127.0.0.1 by default and is local-only in this version. It is a visibility and review surface, not a memory editor: Overview, Conflicts, Conflict Detail, Memories, Doctor, and Settings are read-only. The UI switches between English (mema Console) and Chinese (迷码 Console); the CLI remains English-only and uses the mema alias.

The Support Panel offers GitHub Star, feature request, UX feedback, and bug report shortcuts through prefilled public issue links. It does not upload memory content automatically, does not store GitHub tokens, and does not call GitHub APIs.

Useful boundary: do not expose the Console port publicly. It can display memory content from your local database.

Connect your tool

Add Memory Arbiter to your MCP config. With a local virtualenv:

{
  "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"
      }
    }
  }
}

Or via uvx:

{
  "mcpServers": {
    "memory-arbiter": {
      "command": "uvx",
      "args": ["--from", "memory-arbiter-mcp", "memory-arbiter"],
      "env": {
        "MEMORY_ARBITER_CLIENT": "zcode",
        "MEMORY_ARBITER_AGENT_ID": "zcode-default"
      }
    }
  }
}

Change MEMORY_ARBITER_CLIENT for each tool (openclaw, zcode, codex, cursor, claude-code, workbuddy, ...). Put shared database, vector, and model settings in ~/.config/memory-arbiter/config.json; keep per-client identity in the MCP env block.

New session required: MCP servers are loaded at session startup. Already-open sessions will not see newly added tools.

Agent onboarding guide

For the compact rule agents should save to their persistent local memory file, see the local memory_arbiter/AGENT_ONBOARDING.md file or the GitHub copy at https://github.com/billy12151/memory-arbiter-mcp/blob/main/memory_arbiter/AGENT_ONBOARDING.md. Agents can also read the same guide through memory(action="help", data={"topic": "agent_onboarding"}).

Client config locations

ClientConfig location
ZCode~/.zcode/v2/ MCP config
Codex CLI~/.codex/ MCP config
Claude Code.mcp.json in project root
Cursor~/.cursor/mcp.json
WorkBuddy~/.workbuddy/mcp.json
OpenClaw~/.openclaw/openclaw.json MCP config

OpenDesign and OpenClaw GUI tools run on top of a host CLI. They inherit whatever MCP server the host client has loaded.

MCP tools

v0.11.0 introduces a task-oriented default MCP surface. New clients see four product tools instead of the legacy low-level tool list:

ToolDescription
memoryDaily memory operations: remember, find, read, update, submit conflict judgment, and status. Use action=help for command-specific fields.
memory_reviewRead-only inspection: overview, doctor, conflicts, conflict detail, judgments, history, expired memories, audit, and entities.
memory_governExplicit user-authorized governance: retire a whole memory, resolve a conflict, confirm a memory, correct a judgment, and govern workspace aliases / pending workspaces. Do not use for ordinary updates.
memory_repairMaintenance and repair: split, rebuild claims/embeddings, cleanup, vector resync, entity backfill, pending activation, and backup replay. Prefer dry-run first.

Low-level tool implementations remain inside Memory Arbiter and are reused by the product tools, but their schemas are not exposed by default. This keeps ordinary Agent context smaller and makes the daily path easier to choose.

Advanced compatibility: set MEMORY_ARBITER_TOOL_PROFILE=legacy_full (or full) to expose the legacy low-level MCP tool surface alongside the product tools.

Optional: write-time semantic conflict check

Memory Arbiter can optionally run a local Qwen2.5-0.5B model after a successful write to discover semantic-conflict candidates asynchronously. The write does not wait for classification and remains fail-open. The model is only a candidate signal, never the final judge: its output must include all required fields with exact boolean/number types and an allowed reason_code enum (additional explanation fields are allowed), and a deterministic pair-text gate has veto power. medium is the default balanced gate; strong is the more conservative, lower-noise option. Configure it with semantic_conflict.pair_text_gate or MEMORY_ARBITER_SEMANTIC_CONFLICT_GATE.

Pipeline: specific bounded candidate recall with subject/tag ranking → bounded pair selection → 0.5B pair classification → pair-text gate → open semantic_notices row. Candidate ranking suppresses noisy/common tags, preserves specific tags and subject fallback, and happens before pair_limit. Historical jobs whose memory/version/claim snapshot is stale are skipped; stale undelivered notices are also marked stale and skipped during delivery.

A successful response from any of the four product tools may carry at most one compact semantic notice stub in its top-level notices array. That semantic stub can coexist with existing system notices such as update, onboarding, or backup notices. It contains a notice_id and a memory_repair(task="notice", data={"action":"read", ...}) call, not the full evidence. The Agent should read the full notice, then execute its returned left_read_call and right_read_call to read both full memories. Only after both reads succeed should the Agent assess the advisory candidate and tell the user if it appears credible, without presenting it as a confirmed conflict; dismiss false positives or resolve notices already handled. Delivery performs only the open → open + delivered_at state transition. Public dismiss/resolve calls make terminal transitions, while stale undelivered snapshots may transition internally to stale. The database delivery claim is atomic best effort, not a transport-level exactly-once guarantee.

Semantic notices do not automatically create a conflict, submit a judgment, edit a memory, or supersede either side. They complement the deterministic structured-claim gate and require Agent/user review. Notice read/dismiss/resolve and runtime control via memory_repair(task="semantic_control", ...) do not require governance authorization. The legacy vector conflict-candidate scan has been removed; embedding/sqlite-vec remain supported for semantic recall, section recall, and workspace candidate shortlists, but do not feed a conflict scanner.

GGUF classification is strictly serial (max_concurrency=1) in one child process. The default 5 s job budget only decides whether another pair may start; each started inference has its own 30 s hard timeout, while model loading has a separate 120 s timeout. A timed-out child is terminated before a later request can start a fresh generation. memory(action="status") and memory_repair(task="semantic_control", data={"action":"status"}) expose worker/backend state, generation, PID/in-flight details, timeout/restart counters, and configured budgets.

Backup-only replay

When SQLite is unavailable or unwritable, a write attempts one append-only schema-1 JSONL envelope and returns backup_only=true only if that backup succeeds; an unavailable/unwritable JSONL path makes the write fail rather than claim durability. Backup-only records have no SQLite memory_id and are not searchable yet. Once SQLite is usable, Memory Arbiter can attach a compact backup_replay_pending notice. Preview without changing state:

memory_repair(task="replay_backup", data={"dry_run": true})

After the user confirms, replay with dry_run=false, authorized=true. Each main memory row and replay receipt commit atomically and repeated runs are idempotent. Derived claims, embeddings, section split, and semantic enqueue run after that commit; warnings remain retryable through the replay receipt instead of rolling back the recovered row. A formal call processes at most 200 entries and reports next_offset/has_more. Invalid lines do not block valid entries, the source JSONL is retained, and only the schema-1 envelope is replayable—legacy flat JSONL rows are reported as unsupported and are not converted automatically. Under strict, replay preserves the backed-up canonical workspace and writes an unconfirmed canonical as pending.

Product-tool validation rejects malformed known fields and enforces these main limits: content 2 MiB UTF-8; subject 2,000 characters; query 32,000 characters; at most 100 tags of 256 characters each; metadata 256 KiB JSON; workspace/source references 2,000 characters; batch IDs 1,000. IDs and bounded integer/timeout fields retain controlled numeric-string coercion; booleans are not accepted as IDs. Unknown harmless fields are stripped with a warning, while likely misspellings of protected fields are rejected with did_you_mean; invalid enums/timestamps and NaN/Inf are rejected.

After a PyPI release, the optional production smoke can be run from the dedicated Python 3.13 environment with mema-production-smoke --expected-version X.Y.Z. It writes, reads, searches, retires, and verifies one uniquely marked record in the configured database; it is not a release gate.

The model is not bundled with the default PyPI/uvx package. Install the local runtime extra and point it at a GGUF file:

pip install "memory-arbiter-mcp[semantic-local]"   # pulls llama-cpp-python

Then set semantic_conflict.model_path (or MEMORY_ARBITER_SEMANTIC_CONFLICT_MODEL_PATH). A configured model path auto-enables the feature unless enabled=false is explicit; without a path it remains off by default. Processing is local-only; the current implementation supports only the local_gguf backend.

Optional: Semantic Recall

By default, Memory Arbiter uses lexical recall: FTS5 trigram + BM25 + soft rerank. This is local, lightweight, and enough for many projects.

For meaning-based recall, enable sqlite-vec and bring your own embedding model. The built-in automatic path supports local GGUF models through llama-cpp-python. The default product surface does not accept externally generated embedding values: memory_repair(task="resync_vectors") only synchronizes existing vectors' parent-status metadata and does not write embeddings. A custom remote-embedding script must use the low-level memory_store_embedding tool exposed by MEMORY_ARBITER_TOOL_PROFILE=legacy_full (or full), or call the equivalent library API directly.

pip install memory-arbiter-mcp[vec]
pip install llama-cpp-python

Recommended local model: embeddinggemma-300m-qat-Q8_0.gguf (768 dimensions). Configure it in ~/.config/memory-arbiter/config.json, restart the MCP server, then backfill existing memories with docs/semantic_example.py if you are using a source checkout.

Semantic candidates receive a floor score below strong subject/tag matches. They help find meaning-equivalent memories without letting fuzzy vector matches override precise labels.

Tag scoring and filters

Tags are treated as discrete labels, not as a sentence. A memory tagged v0.7.2 and release should outrank a subject that only incidentally contains one query word.

memory(action="find") supports (forwarded to the low-level search):

  • tags_filter: strict AND over tags;
  • after_time / before_time: ingest-time bounds;
  • source_type: source filter;
  • has_more and total_estimate: signals that results may continue.

Use whitespace between mixed ASCII/CJK tokens, such as "v0.7.2 发版", so token matching works as intended.

Workspace isolation: none / weak / strict

By default, workspace is a stored label and does not filter recall. If you need project isolation, set isolation.

LevelWrite workspaceSearch without workspaceSearch with workspaceNew workspace
none (default)optionalfull libraryignoredsilent
weakrecommendedfull librarysame workspace boosted, cross-workspace demotedwrite_hints.new_workspace_detected
strictrequirederrorhard filter to canonical workspacewritten as pending until memory_govern(action="confirm_pending_workspace")

Under strict, by-id/detail paths (read, history, conflict detail, judgments, audit, and explicit-workspace mutation helpers) also use the caller workspace and may return forbidden_strict_workspace / not-found style responses when the record is outside that workspace.

Use weak when unsure. strict trades recallability for isolation: a wrong workspace can make memories silently unrecallable.

For weak/strict, exact canonicals and confirmed/rejected aliases are checked first. If local embeddings are ready, vector similarity (default cosine-distance cutoff 0.25) supplies at most a shortlist; rules decide AUTO/KEEP/ASK, and the optional local GGUF backend may only suggest among those candidates. A busy/unavailable model falls back to ASK. Weak mode may auto-merge only high-confidence identity-grade relations; strict mode keeps unresolved new workspaces pending. User accept_workspace_alias / reject_workspace_alias decisions are persistent and authoritative.

Optional: Long-document Section Split

Long memories create two problems: search may miss the relevant paragraph, and even successful recall may return the whole document.

Section split breaks long documents into searchable sections. Queries can return only the matched sections while preserving the original memory.

memory(action="remember", data={"content": long_doc})
  → saves original content first
  → if vec ready and content > split.threshold:
      - Markdown headings that fit limits → async rule-based split
      - otherwise → split_request for agent-side continuation

memory(action="find", data={"query": "query"})
  → matched sections when section search is confident
  → full memory when section coverage is high or no section match is available

memory(action="read", data={"memory_id": id, "sections": "catalog" | "all"})
  → inspect or fetch section bodies

Section split is bound to vector readiness. There is no separate on/off switch in v0.8.0+. Short notes stay unsplit and pay no cost.

Configuration

Configuration is read from MEMORY_ARBITER_CONFIG, then ~/.config/memory-arbiter/config.json, then environment variables/defaults. Durable database, vector, and model settings belong in the config file; per-client identity usually belongs in the MCP env block.

Storage and access
JSON pathEnv fallbackDefaultUse
db_pathMEMORY_ARBITER_DB_PATH./memory_arbiter.sqlite3Shared SQLite path.
backup_jsonlMEMORY_ARBITER_BACKUP_JSONL./memory_arbiter.backup.jsonlSchema-1 append fallback when SQLite is unavailable or unwritable.
policy_pathMEMORY_ARBITER_POLICYnoneOptional JSON policy file.
Search tuning
JSON pathEnv fallbackDefaultUse
recall_pool_capMEMORY_ARBITER_RECALL_POOL_CAP50Raise to 100–200 when stores exceed ~100 entries.
content_like_capMEMORY_ARBITER_CONTENT_LIKE_CAP30Raise when many same-topic memories exist.
Structured conflicts
JSON pathEnv fallbackDefaultUse
structured_claim_modeMEMORY_ARBITER_STRUCTURED_CLAIM_MODEbeta_allSet off only as an emergency kill switch.
Workspace isolation
JSON pathEnv fallbackDefaultUse
isolationMEMORY_ARBITER_ISOLATIONnonenone, weak, or strict.
workspace_match_distanceMEMORY_ARBITER_WORKSPACE_MATCH_DISTANCE0.25Cosine cutoff for workspace alias merge.
Semantic recall
JSON pathEnv fallbackDefaultUse
vec.enabledMEMORY_ARBITER_ENABLE_SQLITE_VECfalseEnable sqlite-vec semantic recall.
vec.dimMEMORY_ARBITER_VEC_DIM768Must match the embedding model.
embedding.providerMEMORY_ARBITER_EMBEDDING_PROVIDERinferred from model pathgguf for built-in local auto-embedding.
embedding.model_pathMEMORY_ARBITER_EMBEDDING_MODEL_PATHnoneGGUF embedding model path.
embedding.auto_queryMEMORY_ARBITER_EMBEDDING_AUTO_QUERYtrueAuto-encode plain-text queries.
embedding.auto_writeMEMORY_ARBITER_EMBEDDING_AUTO_WRITEtrueAuto-embed writes/edits.
Semantic conflict notices
JSON pathEnv fallbackDefaultUse
semantic_conflict.enabledMEMORY_ARBITER_SEMANTIC_CONFLICT_ENABLEDfalseOptional local semantic notices; setting model_path auto-enables unless explicitly false.
semantic_conflict.backendMEMORY_ARBITER_SEMANTIC_CONFLICT_BACKENDlocal_ggufOnly supported backend in this release.
semantic_conflict.model_pathMEMORY_ARBITER_SEMANTIC_CONFLICT_MODEL_PATHnoneLocal classifier GGUF path.
semantic_conflict.pair_text_gateMEMORY_ARBITER_SEMANTIC_CONFLICT_GATEmediummedium or lower-noise strong.
semantic_conflict.on_writeMEMORY_ARBITER_SEMANTIC_CONFLICT_ON_WRITEasyncasync or off; writes never wait for classification.
semantic_conflict.queue_max_sizeMEMORY_ARBITER_SEMANTIC_CONFLICT_QUEUE_MAX_SIZE100Bounded worker queue; same-memory jobs coalesce.
semantic_conflict.candidate_limitMEMORY_ARBITER_SEMANTIC_CONFLICT_CANDIDATE_LIMIT30Metadata-overlap candidates fetched before pair limiting.
semantic_conflict.pair_limitMEMORY_ARBITER_SEMANTIC_CONFLICT_PAIR_LIMIT10Maximum pairs considered per write-time job.
semantic_conflict.n_ctx / n_threads / n_batchmatching MEMORY_ARBITER_SEMANTIC_CONFLICT_* vars1024 / 4 / 128Local GGUF runtime sizing.
semantic_conflict.resident / preloadmatching MEMORY_ARBITER_SEMANTIC_CONFLICT_* varstrue / falseKeep loaded after use / begin loading at startup.
semantic_conflict.job_timeout_msMEMORY_ARBITER_SEMANTIC_CONFLICT_JOB_TIMEOUT_MS5000Between-pair job budget, not an inference timeout.
semantic_conflict.inference_timeout_msMEMORY_ARBITER_SEMANTIC_CONFLICT_INFERENCE_TIMEOUT_MS30000Hard deadline for one started inference.
semantic_conflict.load_timeout_msMEMORY_ARBITER_SEMANTIC_CONFLICT_LOAD_TIMEOUT_MS120000Separate model startup/load deadline.
semantic_conflict.min_pair_budget_msMEMORY_ARBITER_SEMANTIC_CONFLICT_MIN_PAIR_BUDGET_MS1000Do not start another pair below this remaining job budget.

semantic_conflict.max_concurrency is reserved and clamped to 1.

Long-document split
JSON pathEnv fallbackDefaultUse
split.thresholdMEMORY_ARBITER_SPLIT_THRESHOLD4000Minimum character count to trigger split.
split.section_vec_distance_thresholdMEMORY_ARBITER_SECTION_VEC_DISTANCE_THRESHOLD0.42Section vector cutoff; recalibrate if switching models.
split.section_fulltext_thresholdMEMORY_ARBITER_SECTION_FULLTEXT_THRESHOLD0.8Return full text when enough sections match.
split.max_sectionsMEMORY_ARBITER_MAX_SECTIONS50Max sections per memory.
split.max_section_charsMEMORY_ARBITER_MAX_SECTION_CHARS3600Max characters per section slice.
Per-client environment
VariableDefaultUse
MEMORY_ARBITER_CLIENTcodexTool identity.
MEMORY_ARBITER_AGENT_IDdefaultAgent identity inside the client.
MEMORY_ARBITER_WORKSPACEdefaultWorkspace label; isolation only applies when configured.
MEMORY_ARBITER_CONFIGnoneAlternate JSON config path.
MEMORY_ARBITER_RANKING_MODEhybridhybrid or legacy bm25.
MEMORY_ARBITER_GGUFnoneLegacy GGUF path fallback; prefer config file.

Data migration

Moving to a new machine is just copying the SQLite database and reinstalling the package:

scp ~/.local/share/memory-arbiter/memory.sqlite3 newmachine:~/.local/share/memory-arbiter/

python3.11 -m venv .venv
source .venv/bin/activate
pip install -e .

Doctor: health diagnostics

When search feels wrong, embeddings may be misconfigured, or the database might be degraded, run:

mema doctor
mema doctor --json
mema doctor --deep
mema doctor --db PATH

Doctor is read-only and runs outside the MCP server, so it can diagnose even when the MCP process is down. It checks config integrity, vector enablement, split state, claim indexing, data consistency, capacity, conflict backlog, and update-check state. Exit codes are script-friendly: 0 clean, 1 warnings, 2 critical findings.

Testing

python3.11 -m pip install -r requirements.txt
python3.11 -m pytest

CI treats the core test matrix on Python 3.11/3.12/3.13, the Python 3.12 sqlite-vec job, quality/security checks, and build/twine validation as required workflow jobs. mema-production-smoke is intentionally manual after release and is not a CI or publication gate. On Python 3.13, installing [semantic-local] may compile llama-cpp-python locally when no matching wheel is available, so a C/C++ toolchain and CMake may be required.

License

Apache License 2.0. Copyright (c) 2026 张志维 (billy12151).

Memory Arbiter version 0.8.2 and later are offered under Apache-2.0 going forward. Prior MIT grants remain valid for copies previously distributed under MIT, including 0.8.0 and 0.8.1. Versions before 0.8.2 were released under MIT.


中文

memory-arbiter 是 AI Agent 的本地可信事实层。中文名:迷码。短称 / CLI alias:mema。

它可以作为共享记忆层使用,但真正价值不是“把记忆放到同一个地方”,而是事实治理:让长期项目上下文变得可检索、可追溯、可信度可区分、冲突可发现,并且可以安全召回。

共享记忆让每个工具都能看到同一份数据。memory-arbiter 进一步帮助 agent 判断:哪些事实是当前的,哪些是用户确认的,哪些已经过期,哪些互相矛盾,哪些已被废弃,哪些还需要裁决。

# 不用每轮把 2 万 token 的 MEMORY.md 塞进 prompt:
memory(action="find", data={"query": "认证迁移方案"})  → 3 条精准结果,约 400 token

共享记忆只是起点,事实治理才是护城河。

核心默认完全本地且无需模型:一个 SQLite 数据库,不需要 Postgres、Redis、托管 memory 服务;字面召回、结构化 claim 检测、治理和修复都不要求模型调用。可选语义召回与语义冲突 notice 使用你自己的本地 GGUF 模型;可选更新检查可以关闭。

它解决什么问题

很多 memory 工具解决的是“怎么让 agent 记住”。memory-arbiter 关注的是共享之后更难的问题。

当 Claude Code、Cursor、Codex、ZCode、WorkBuddy、OpenClaw 或其他工具都能写入同一份长期上下文时,真正的风险不再只是“忘记”,而是:

  • 旧事实和新决策混在一起;
  • AI 猜测被当成用户确认事实;
  • 不同工具写入互相矛盾的结论;
  • 长期项目历史越来越长,真正相关的事实被噪音淹没;
  • 切换工具时,不是上下文丢失,就是重复加载、重复污染;
  • 本地记忆增长到每轮 prompt 都先消耗几千到几万无关 token。

memory-arbiter 把这些风险变成显式的数据结构:来源标签、可信度、事实时间、写入时间、版本历史、废弃链、冲突记录、结构化 claim 门禁、分段索引、workspace 边界和 doctor 体检。

模型仍然负责语义理解。arbiter 负责把输入侧变干净。

memory-arbiter 做什么

需求普通记忆为什么不够memory-arbiter 的回答
精准召回扁平 MEMORY.md 或大块向量记忆容易返回过多上下文。memory(action="find") 只返回少量相关、排序后的条目,而不是加载全文。
来源可信度用户确认、文档提取、AI 猜测看起来一样。source_typeconfidenceuser_confirmed 和 locked 记录让可信度可见。
时间演进旧决策和新决策并存,模型可能跟着旧口径走。event_timeingest_timeversion、history(memory_review)、supersede(memory_govern)保留演进链。
冲突处理两条记忆可以互相矛盾,却同时被召回。确定性结构化 claim 碰撞、可选写入时语义 notice、冲突记录/信号和显式治理让矛盾可见;系统不再运行定期向量扫描器。
写入安全last-write-wins 会静默覆盖,或继续堆积矛盾事实。结构化 claim 门禁把显式碰撞持久化为 pending_llm,受影响 claim 在使用前必须由宿主 LLM 携 snapshot pins 提交 judgment。judgment 只是 guidance,不会自动编辑或废弃记忆。
长文档相关段落埋在 10K+ 字符的长记忆里。分段索引返回命中段落,而不是让模型扫整篇文档。
项目边界全局记忆容易把无关项目事实串在一起。workspace 隔离支持 noneweakstrict 三档,并做别名归一。
长期健康用户往往等到回答变差才发现记忆库有问题。doctor 检查配置、向量链、分段、claims、一致性、容量和冲突积压。
隐私和所有权托管 memory 又引入一个服务和数据边界。本地 SQLite、用户自有文件、可选本地 embedding、无内置 LLM 依赖。

日常心智模型

大多数 agent 只需要四个产品工具(默认 MCP 工具面):

  1. memory —— 日常操作:remember 写新事实、find 搜活跃事实、read 按 ID 取记忆、update 更新已有 current 记忆、judge 提交冲突判断、status 看运行状态。不确定字段时用 action=help
  2. memory_review —— 只读审计:overview、doctor、conflicts、conflict_detail、judgments、history、expired、audit、entities。
  3. memory_govern —— 用户授权治理:整条记忆过期、关闭冲突、确认记忆、纠正 judgment。不要用于普通更新。
  4. memory_repair —— 维护修复:分段、重建 claims/embeddings、清理、向量状态同步、entity 回灌、pending 激活、备份恢复。优先 dry-run。

低层工具实现仍保留在代码库内并由产品工具复用,但默认不暴露它们的 schema。设置 MEMORY_ARBITER_TOOL_PROFILE=legacy_full(或 full)可同时暴露低层工具。

和其他 memory 的区别

memory-arbiter 不靠“别人不能共享,我们能共享”来做差异化。shared memory 正在成为标准能力。memory-arbiter 关注的是 shared memory 之后更深一层的问题。

对比对象memory-arbiter 关注什么
普通 markdown memory不全文加载 prompt,而是精准召回,并保留历史和冲突状态。
向量 memory不只找相似内容,还要知道来源可信度、过期状态、废弃状态和冲突状态。
图 memory不只知道什么和什么有关,还要知道什么是当前的、可信的、冲突的、可安全使用的。
托管 memory本地 SQLite、调用方自有策略、无托管数据库、默认核心无需模型;可选本地 GGUF 能力仍留在本机。
通用 MCP memory事实治理层:可信度标签、时间演进、结构化 claim 门禁、doctor 和修复工具。

memory-arbiter 有轻量图关系信号:事实时间、写入时间、entity/scope、冲突边、废弃链、分段和 workspace 边界。但它不把产品定位成重型图数据库;原文事实是主资产,派生索引用来辅助治理。

省 token 是副作用

核心价值是上下文质量更高。省 token 是最直观的结果。

场景全文加载使用 memory-arbiter节省
每轮记忆加载system prompt 塞 5K–20K tokensmemory(action="find") 返回 200–800 tokens~80%+
冲突检测LLM 带大上下文逐条比较结构化候选 + 聚焦判断~90%
定期审查LLM 扫全库memory_review(conflicts) + memory_review(audit)~70%
规格交接重复加载完整规格/设计记录查询相关事实和决策~80%+

同一个模型,输入更干净,输出更准。

一个工具能用,多个工具更有价值

只用一个工具时,memory-arbiter 把本地记忆从扁平文件升级成带可信度、历史、冲突信号和诊断的可查询事实层。

多个工具一起用时,它同时成为共享记忆:工具 A 写,工具 B 搜,工具 C 审计。零文件传递,零复制粘贴,零版本漂移。

示例管线:

  1. OpenClaw 用 memory(action="remember") 写入规格。
  2. OpenDesign 用 memory(action="find") 读取规格,并写回设计决策。
  3. ZCode 一次搜索拿到规格和设计决策。

三个工具,一层本地事实层。

完整跨工具示例见 docs/INTEGRATION.md

核心能力

  • 精准召回 —— 返回相关条目,而不是每轮加载完整 memory 文件。
  • 可信度分层 —— 区分用户确认、文档提取、AI 生成和未知来源。
  • 时间历史 —— 跟踪事实时间、写入时间、版本、历史快照和废弃链。
  • 冲突治理 —— 确定性 claim 碰撞和可选语义 notice 发现候选,agent/用户再显式查看、判断、关闭或废弃;没有定期向量扫描器。
  • 结构化 claim 门禁 —— 写入/编辑时检测确定性 claim 冲突,受影响 claim 使用前要求携 snapshot pins 的宿主 LLM judgment;receipt 不修改任何一侧记忆。
  • 长文档分段 —— 后台异步把安全 Markdown 标题拆成可搜索段落;结构不明确的长文返回 Agent 续接请求。
  • workspace 隔离 —— 支持 noneweakstrict 三档和别名归一。
  • tag 精排与过滤 —— tag 是离散标签信号,不是弱文本片段。
  • 语义召回 —— 可选本地 GGUF embedding;默认仍是轻量字面检索。
  • doctor 体检 —— 只读检查配置、向量链、分段、claims、一致性、容量和冲突。
  • 逐级降级 —— 召回按 sqlite-vec → FTS5 → LIKE 降级;SQLite 不可用或不可写时尝试 schema-1 JSONL 备份,追加失败会明确报错,不会虚报已持久化。
  • 本地优先 —— 纯 SQLite,无托管数据库,无 Redis/Postgres 要求,核心无需模型;本地 GGUF 能力按需开启。

它不是什么: memory-arbiter 不是 LLM,也不替代你的 AI 客户端。它是模型下面的一层结构化存储、检索、仲裁和诊断工具。

快速开始

要求: Python 3.11+(3.11、3.12、3.13 均可)。

# 克隆
git clone https://github.com/billy12151/memory-arbiter-mcp.git
cd memory-arbiter-mcp

# 安装 —— 用任意 Python 3.11+
python3.11 -m venv .venv
source .venv/bin/activate
pip install -e .

# 可选:启用 sqlite-vec 语义召回
pip install -e '.[vec]'

# 启动(短命令)
mema

# 兼容长命令仍可用:
# memory-arbiter
# memory-arbiter-mcp
uvx 零安装启动

只想跑起来、不想管理 Python 环境时,先安装 uv,然后:

uvx --from memory-arbiter-mcp mema

这会拉取已发布包并启动 mema 入口。mema 是 Memory Arbiter / 迷码的短命令;memory-arbiter-mcpmemory-arbiter 仍作为兼容长命令保留。uvx 只省安装步骤;如果要启用语义召回,embedding 模型和 sqlite-vec 仍需单独配置。

配置助手

不想手写 config.json 时运行:

mema setup

它会把可用配置写到 ~/.config/memory-arbiter/config.json,检查环境,并打印你还需要执行的命令或模型下载链接。它不会替你运行 pip 或下载模型。

常用参数:--print-config--no-config--force

本地 Console MVP

启动只读本地治理控制台:

mema console

默认打开 http://127.0.0.1:18876。可选参数:

mema console --no-open       # 只启动服务,不自动打开浏览器
mema console --port 18877    # 18876 被占用时换一个端口

Console 默认监听 127.0.0.1,当前版本仅限本地。它是可见性与审计入口,不是记忆编辑器:总览、冲突列表、冲突详情、记忆查看、体检、设置页都只读。UI 可在英文(mema Console)和中文(迷码 Console)间切换;CLI 仍只使用英文 mema alias。

支持面板提供 GitHub 点星、提需求、体验反馈和反馈问题入口,通过预填公开 issue 链接跳转。Console 不会自动上传记忆内容,不保存 GitHub token,也不调用 GitHub API。

边界提醒:不要把 Console 端口暴露到公网。页面会展示本地数据库里的记忆内容。

接入工具

把 memory-arbiter 加进 MCP 配置。本地 venv 方式:

{
  "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"
      }
    }
  }
}

或使用 uvx

Documentation truncated — see the full README on GitHub.

Reviews

No reviews yet

Be the first to review this server!