mcp-memory-graph
by osaken55
README.md
# mcp-memory-graph
**A personal memory MCP server for Obsidian / markdown vaults — semantic + keyword + tag + time-aware co-occurrence hybrid retrieval, shared across 3 AI clients (Claude Code / Codex / agy).**
Built for **complex-systems thinkers** who refuse to delete old memories.
Every note stays forever; you just retrieve it better.
[](https://github.com/osaken55/mcp-memory-graph/actions/workflows/ci.yml)
[](LICENSE)
---
## Why this exists
You started keeping a memory file alongside your AI assistant. It worked. Then your `MEMORY.md` index passed the 24KB read limit and your AI couldn't even load it. The obvious fix is "compress" — but that means **deleting things you might need next year**.
This server takes the other path: **keep everything, retrieve smarter**.
## The 4 principles
1. **Memory is permanent.** Old notes are not garbage — they're future re-discoveries waiting to happen.
2. **Separate inject from retrieve.** Boot-time context stays tiny; everything else is pulled on demand via MCP tools.
3. **Honor complex-systems thinking.** One keyword should pull 3–5 related memories by *meaning, structure, AND time*.
4. **Evolve without breaking.** Adding a new note never invalidates indexing. Old notes resurface naturally.
## How it retrieves
Four signals merge into a single hybrid score:
| Signal | Source | Weight (default) |
|---|---|---|
| Semantic | [Voyage AI](https://www.voyageai.com/) embeddings (default model: `voyage-multilingual-2`) | 0.5 |
| Keyword | `ripgrep --json` over markdown body (with Node.js fallback) | 0.3 |
| Tag | `metadata.type` + wikilinks + filename tokens | 0.2 |
| Co-occurrence | "Memories accessed together in the same session" (Phase 3) | 0.2 *(addition planned)* |
Weights are overridable per environment:
```bash
MEMORY_WEIGHTS="semantic=0.5,keyword=0.3,tag=0.2"
```
## Tools
| Tool | Purpose |
|---|---|
| `memory.search` | Hybrid retrieval with recency tie-breaker |
| `memory.get` | Read one memory + (optional) auto-prefetch top-2 related |
| `memory.related` | Related memories via wikilink, tag, semantic distance |
| `memory.recent` | Sorted by `last_accessed` (SQLite) |
| `memory.by_tag` | Filter by `metadata.type` |
| `memory.by_session` | **Cluster memories born in the same conversation session** (originSessionId) |
| `memory.list_archived` | Hybrid search without recency boost (surfaces older notes) |
## Install
```bash
git clone https://github.com/osaken55/mcp-memory-graph.git
cd mcp-memory-graph
npm install
npm run build
npm test
```
Default paths:
```text
MEMORY_DIR = ~/.claude/projects/.../memory/
MEMORY_DB_PATH = ~/.claude/memory-mcp/cache.db # SQLite is OUTSIDE the memory dir
VOYAGE_MODEL = voyage-multilingual-2 # override with env
```
## Phase 0 — smoke test (no MCP client needed)
```bash
node dist/bin/probe.js
```
Reports how many `.md` files are readable, how many have/lack frontmatter, and which iCloud / Syncthing artifacts were excluded. **Read-only — zero writes.**
## Register with the 3 AI clients
### Claude Code (CLI / Plugin)
`~/.claude/settings.json`:
```json
{
"mcpServers": {
"memory-graph": {
"command": "mcp-infisical-env",
"args": ["--", "node", "/Users/junshu/Documents/Projects/mcp-memory-graph/dist/index.js"],
"env": {
"MEMORY_AUTO_PREFETCH": "true"
}
}
}
}
```
### Codex (OpenAI Codex CLI / Desktop)
`~/.codex/config.toml` (or via `codex mcp add`):
```toml
[[mcpServers]]
name = "memory-graph"
command = "mcp-infisical-env"
args = ["--", "node", "/Users/junshu/Documents/Projects/mcp-memory-graph/dist/index.js"]
```
### agy (Antigravity CLI)
Use agy's MCP config or wrap in `agy-with-context`:
```bash
# Agy doesn't natively load MCP servers in CLI mode yet;
# in the Antigravity IDE the server registers via the same MCP config above.
```
All three clients hit the **same SQLite cache**. `last_accessed`, embedding cache, and co-occurrence stats are shared — your 3 AI workforce really has one shared memory.
## Why `voyage-multilingual-2` by default
The author's memory is roughly half Japanese, half English (Obsidian Vault notes from a Japanese small business owner). On benchmarks, `voyage-multilingual-2` outperforms `voyage-3.x` on mixed-language retrieval. Pure-English vaults should override with `VOYAGE_MODEL=voyage-3-large`.
## Comparison
| Axis | Typical mcp-memory | mcp-memory-graph |
|---|---|---|
| Storage | Internal DB | Existing flat markdown directory |
| Migration | Imports / rewrites | **Zero-destructive** — never edits your notes |
| Search | Keyword OR vector | Semantic + keyword + tag (+ co-occurrence) hybrid |
| Recency | Often implicit | SQLite `last_accessed`, **outside** memory dir |
| Multilingual | English-tuned | `voyage-multilingual-2` default |
| Failure handling | Vector API down = broken | Falls back to keyword + tag automatically |
| ripgrep absent | Crashes | Node.js `readline` + RegExp fallback |
| Sync artifacts (iCloud / Syncthing) | Pollutes index | Excluded by glob |
| Same-session clustering | None | `memory.by_session(originSessionId)` |
| Auto prefetch | Manual `related` call | Opt-in `MEMORY_AUTO_PREFETCH=true` |
## Roadmap
- **Phase 0** ✅ Probe — non-destructive read of every markdown file.
- **Phase 1** ✅ Hybrid skeleton, 7 tools, SQLite `last_accessed`, ripgrep + Node.js fallback, iCloud-exclude.
- **Phase 2** ✅ Voyage embedding cache, content-hash dedup, multilingual default model, semantic + keyword + tag merged.
- **Phase 3** ✅ Time-aware co-occurrence table (`co_access`), 4th signal in `memory.related`, in-memory ring buffer auto-records pairs from `memory.get` / `memory.search`.
- **Phase 4** ✅ `npx mcp-memory-graph init` zero-config bootstrap; Jaccard coefficients for wikilink / tag signals.
- *(next)* Local embedding fallback via `@xenova/transformers`; multi-tag arrays; tag taxonomy beyond `metadata.type`.
## Real-world numbers (from the author's vault)
Built on a real corpus of 130 personal markdown memories (mix of Japanese + English).
| Operation | Result |
|---|---|
| Phase 0 probe | 131 files / 123 with frontmatter / 0 errors |
| Phase 1 `memory.search("CTO")` | top-1 score 1.000, expected hit at top |
| Phase 2 Voyage indexing | 204 s for 130 files, ~$0.015 (0.26% of free tier) |
| Phase 2 semantic query "責任を取って判断する役割" (no keyword overlap) | top-1 = CTO role memory (score 0.464) |
| Phase 2 multilingual: English query → Japanese memory | top-1 score 0.555 |
| Phase 3 co-occurrence smoke | "memories from the same session cluster correctly" |
## License
MIT — see [LICENSE](./LICENSE).
## Acknowledgements
This server was designed in a 3-AI parallel review pattern that the project author has been refining for months:
- **agy** (Gemini 3.5 Flash High) wrote the Python design draft.
- **Codex** (GPT-5.x) wrote the TypeScript scaffold and ADRs.
- **Claude Code** (Opus 4.7) wrote the CTO integration review and added the 6 unique observations (Phase 0 probe, time-aware co-occurrence, originSessionId clustering, auto-prefetch, zero-config init, the name itself).
- **The owner** (オサケンさん) wrote the principles.
If three AIs and a complex-systems thinker can build a better personal memory, so can yours.
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessSyncing