open-memex
Server Configuration
Describes the environment variables required to run the server.
| Name | Required | Description | Default |
|---|---|---|---|
| OPEN_MEMEX_HOME | No | Override the storage root (default is %APPDATA%\open-memex on Windows, ~/.local/share/open-memex on macOS/Linux; the pre-rename MY_O_MEMORY_HOME name is still honored as a fallback). | |
| OPEN_MEMEX_CONFIG | No | Override the config file path (default is ~/.config/opencode/open-memex.jsonc; the pre-rename MY_O_MEMORY_CONFIG name is still honored as a fallback). |
Instructions
Guidance the server publishes about itself, which clients place ahead of the tool catalog so the model reads it before choosing anything.
You are connected to an open-memex local memory MCP server. Its tools are memory_add, memory_search, memory_list, memory_supersede, memory_forget, memory_useful, memory_workingstate, memory_status, memory_submit, memory_propose, memory_promote, memory_resolve, memory_pr_status — always call them by these full names.
Session start (the wake ritual): treat these instructions as already-known context, not as something to re-confirm. If the task needs more than they cover, call memory_search once with the task's keywords — try 2-3 phrasings and check both scopes — before concluding anything is missing. Do not ask the user to re-explain past decisions or preferences memory may already hold; search first, and only ask about what search cannot answer.
The server tells you when project outbox drafts are waiting for review — in tool results, and in these session-start instructions. When it does, summarize them (one line each) and ask the user which ones to sync into the repo; sync NOTHING the user did not name. If you already asked about these drafts this session, don't ask again. When the server reports none waiting, do nothing.
When the user says "sync memory" (or "同步记忆"), call memory_status, summarize the outbox drafts (one line each), and ask which ones to sync. ALWAYS use the memory_status tool for this — never browse the memory data directory directly. memory_status also carries the D77 reuse report (per-scope rates, most-reused, and never-used-but-retrieved "zombies") — surface it when the user asks what memory is actually being reused.
After memory_submit, ask ONE follow-up: "want me to create a branch + push + open the PR, or will you handle it yourself?" NEVER create branches, push, or open PRs without the user's explicit approval. A "yes, you do it" covers the whole chain — do NOT re-ask at each step.
memory_submit prints its deterministic curation receipts (duplicate merges, empty-body archives, title normalization) with undo commands. Relay them; do not hide an automatic change to the user's memories.
BE PROACTIVE about facts the user states directly: when the user shares a decision, preference, project convention, or fix-and-cause worth remembering across sessions, call memory_add without being asked. Keep each memory to one self-contained statement, and add a brief "(noted in memory)" so the user sees it worked.
For conclusions YOU infer (the user never stated them): when you finish a task the user would describe in one sentence, consider distilling the session — if there is something worth keeping, propose 1–3 short memories capturing the useful conclusion (what was learned or decided, how an issue was resolved, what to avoid, where the authoritative doc lives — not the raw transcript), each with its proposed scope. Save NOTHING the user did not approve; on approval call memory_add with source "inference" at the approved scope. If the knowledge already lives in project docs, save it as type "reference" pointing at the doc instead of copying it. Long-form notes are fine ONLY when the user explicitly asks to save one.
Before asking the user about past decisions, conventions, or preferences they may have told you before, call memory_search first — search well: break the question into its concepts and try 2–3 phrasings per concept (synonyms, the user's other language, shorter keyword forms), and check the other scope too, before concluding nothing is stored.
When the user asks what you remember (about them, or in this project), call memory_list with scope=both and present it conversationally — turn the structured fields into plain words (source=user → "you told me this"; source=inference → "I inferred this, check me"), and don't read raw ids aloud. If they ask to remove or hide an entry, re-run memory_list first (numbers from an older listing may have shifted), read the candidate back in full, and get a confirmation before calling memory_forget: deletion is permanent. If they hesitate, offer soft=true (hide it instead — still one-way, but the file survives).
Memories default to this project's scope; use the personal scope for facts about the user that hold across all projects. When a saved fact becomes outdated, call memory_supersede (find the old memory's id with memory_search first) instead of adding a duplicate.
When the user signals the session or task is wrapping up, call memory_workingstate (action=write) with a short note: task summary, open files, last commands, where the work stands, next step — so an interrupted task can resume. It is scratch state, not knowledge: do not save it as a memory; durable conclusions still follow the propose-first flow above.
When a person confirms a memory was useful, or accepted work that clearly relied on one, call memory_useful on it. NEVER call memory_useful just because a memory appeared in results or the session-start block — passive exposure never counts as useful. (Superseding or approving a memory also auto-records its useful signal; no need to duplicate those.)
When you save a memory, attach 2–4 aliases via memory_add's aliases parameter: alternate phrasings, synonyms, equivalents in the user's other language. They are indexed with the memory so a differently-worded question still finds it.
Capabilities
Features and capabilities supported by this server
Protocol revision2025-11-25
| Capability | Details |
|---|---|
| tools | {
"listChanged": true
} |
Tools
Functions exposed to the LLM to take actions
| Name | Description |
|---|---|
| memory_addA | Save a fact, preference, decision, or note to persistent local memory. Call this PROACTIVELY whenever the user shares something worth remembering across sessions — project conventions, tool choices, personal preferences, decisions made, error fixes and their causes. Worth saving: decisions and their reasons, preferences, conventions, gotchas, approaches tried and abandoned. Not worth saving: one-off task details or anything re-derivable from the code. Do not wait to be asked. For facts you infer yourself rather than the user stating, propose them first and save only on approval. Keep each memory to one self-contained statement; attach aliases when the parameter is available. If a note tells you the user's own wording was already stored verbatim this turn, that statement is already saved: do not retry it in a rewording — save only what that text does not contain, or use memory_supersede on the given id. Default scope is the current project; use the personal scope for facts about the user that apply across all projects. Returns the new memory's id; an identical fact already stored returns that id instead of duplicating. |
| memory_searchA | Search persistent memory by keyword (BM25 full-text). Returns matching memories from the current project and/or personal scope. Call before asking the user about past decisions, conventions, or preferences they may have told you before. Search well: break the question into its concepts and try 2–3 phrasings per concept — synonyms, the user's other language, shorter keyword forms — and check the other scope too, before concluding nothing is stored. Results are ranked by BM25 within review tiers — reviewed (approved/published) project memories come first. |
| memory_listA | List memories in a scope, newest first, as a numbered inventory. Each line keeps the raw fields — [type] id=… created=… source=… — say them back to the user in plain words (source=user → 'you told me this'; inference → 'I inferred this, check me'; keyword → 'caught from your own wording'), never read the raw id aloud unless they ask. When the user asks 'what do you remember about me?', use scope=both and present the inventory conversationally. The user may point at an entry by its number ('delete #3'): numbers are only valid for the listing you just produced — re-run memory_list, read the candidate back in full, and get a confirmation before calling memory_forget (deletion is permanent). include=all is the audit view (superseded versions, retracted, archived also shown). |
| memory_supersedeA | Replace an existing memory with a newer version. The old memory is kept as history (status: superseded) and retrieval returns the new one. Use when a saved fact becomes outdated and should be replaced rather than duplicated. Only active memories can be superseded; the new memory inherits type, tags, and aliases unless you pass replacements. An unknown id is reported back as a failure; nothing is written. |
| memory_forgetA | Delete a memory by id. Use when the user asks to forget something. To replace an outdated fact with a corrected one instead of deleting it, use memory_supersede. Deletion is permanent: before deleting, restate the memory's content to the user and get a confirmation. With soft=true the memory is hidden instead (retracted: out of lists and search, file kept, one-way). |
| memory_usefulA | Mark an existing memory as useful (D77): record that it demonstrably paid off. Call it ONLY on an explicit signal — the user confirmed it helped ('that was the one', 'saved me again'), or your completed/accepted work clearly relied on that memory and the user saw the result. NEVER call it because a memory appeared in memory_search results, a memory_list inventory, or the session-start block: passive exposure never refreshes usefulness. Exposure is counted automatically under retrieved_count; the useful buckets increment only through this tool, a human approval at review, or a supersede. Useful is split automatically: useful_self for personal memories and unpublished project drafts (the author's own utility), useful_team for approved/published project memories (team knowledge reused) — pass kind only to override. Returns the cumulative counts and reuse_rate (useful / retrieved). The counters live in the local SQLite index, not in the Markdown source of truth, and never leave this machine. |
| memory_statusA | Show the project memory sync pipeline: drafts waiting in the outbox (appdata), memories in the repo awaiting review or published, and any repo files not yet committed — plus the D77 reuse report: per-scope retrieved/useful rates, the most-reused memories, and "zombies" (retrieved but never confirmed useful). Call this at session start, when the server reports drafts waiting for review, when the user says 'sync memory' (or '同步记忆'), or when they ask what memory is pulling its weight. Then ask the user which drafts to sync. For the memories themselves use memory_list; for PR review state use memory_pr_status. (MCP server and the |
| memory_submitA | Move outbox drafts into the repo memory dir for review. Before copying, reports graph lint (never blocking) and auto-applies only deterministic curation: exact-duplicate merges, empty-body archives, and first-line title normalization. Near-duplicates are reported, never merged. Every applied action appears in the output with its undo command; pass noCurate=true to skip the auto-apply tier while keeping the lint report. Then copies the drafts in as proposed (or keeps a local approval), commits locally on the current branch, and moves the outbox originals out. Never creates a branch on its own — pass branch= only with the user's explicit approval for the full chain. Prints the push and PR commands — those need the user's explicit approval and are never run automatically. (MCP server and the |
| memory_proposeA | Copy personal memories into the project outbox as review drafts. The personal originals stay put. Use this when personal knowledge should enter a project's review flow; memory_submit then moves the drafts into the repo, and memory_promote advances drafts already there. (MCP server and the |
| memory_promoteA | Advance a project memory one step up the review ladder (proposed → approved → published), or reject it with a note. Rejected memories are never deleted — they can be revised and resubmitted. One step per call; an unknown id or a memory already published errors. Pass |
| memory_resolveA | List git-conflicted memory files, or attempt a field-level 3-way merge of one. Semantic conflicts are reported, never auto-resolved. Run it when a git pull or merge leaves memory files conflicted; the merged file is written in place (undo with git if the merge is wrong). For PR review state rather than file conflicts, use memory_pr_status. (MCP server and the |
| memory_pr_statusA | Read the current branch's GitHub PR and map its review state onto each in-repo memory: merged PR → published, PR approval → approved (approved_by = reviewer), changes-requested → suggestion only. Report by default — that mode changes nothing; apply=true writes the mapped transitions to the local files (still no push). (MCP server and the |
| memory_workingstateA | Read, write, or clear the one ephemeral per-project working-state note (§3.6, D89): where an interrupted task stands, so a fresh session can resume. Scratch state, NOT durable knowledge — it never enters memory retrieval or a review ladder; any conclusion here that should outlive the task goes through the normal memory_add/checkpoint-proposal path instead. WRITE it (action=write, content required) when the user signals the session or task is wrapping up — body rubric: task summary (one paragraph), open files, last commands, where the work stands, next step; short by design, a scratch note, not a transcript. READ it when resuming work or when the user asks what they were doing. CLEAR it on the user's request only. The note lives outside memories/, the user can edit the plain file directly, and it expires after ttl_days (default 7) — expired notes are ignored and |
Prompts
Interactive templates invoked by user choice
| Name | Description |
|---|---|
No prompts | |
Resources
Contextual data attached and managed by the client
| Name | Description |
|---|---|
No resources | |
TDQS
Scored across 13 tools
Most tools map to clearly distinct lifecycle stages, and the descriptions explicitly redirect ambiguous cases (e.g., memory_submit vs memory_promote, memory_status vs memory_pr_status, memory_forget vs memory_supersede). The main remaining overlap is among memory_status, memory_pr_status, and memory_list, plus the add/submit/propose chain, which could still cause misselection if an agent skims.
All tools consistently use the memory_ prefix and snake_case, making the set easy to scan. Most suffixes are actions, but several are nouns or adjectives (memory_pr_status, memory_workingstate, memory_useful, memory_status), which is a minor deviation from a pure verb_noun pattern.
With 13 tools, the set sits comfortably in the well-scoped range for a memory server. Each tool corresponds to a distinct operation or workflow stage, and none appear redundant enough to trim without losing lifecycle coverage.
The surface covers the core memory lifecycle: add, search, list, forget, supersede, review promotion, sync status, PR review state, conflict resolution, usefulness tracking, and working state. Minor gaps remain, such as no explicit get-by-id or direct edit/update of an existing memory's content/metadata outside supersede, but agents can work around these via search/list and supersede.