Persistent long-term memory & knowledge retrieval for AI assistants. Exposes RAG (vector storage + semantic search) and context management (memories) as MCP tools, so opencode can store and recall context across sessions.
👤 Identity: The Coder — "I'm The Coder, Selamat datang dan Semoga perjalanan mu menyenangkan"
📖 Project documentation site: https://adyoi.github.io/mcp-rag-memory/ (see
docs/)
- RAG — ingest documents (text / files / whole directories), chunk + embed locally, store in SQLite, and semantically retrieve relevant context.
- Hybrid search — FTS5 BM25 keyword hits fused with vector similarity (reciprocal-rank fusion). Tunable
SEARCH_MODE=hybrid|vector|keyword. - Context Management —
rememberfacts/decisions/preferences,recallthem later (score-decayed by staleness), rate by importance, consolidate duplicates, filter by type/tag. - Zero external APIs by default — local hashing-embedder (1024-dim), built-in
node:sqlite. Works fully offline. OptionalEMBEDDING_PROVIDER=transformersfor a higher-quality ONNX model. - Safe ingestion — content-hash deduplication, file size guard (
RAG_MAX_FILE_MB) and an opt-in path allowlist (RAG_ALLOWED_DIRS). - MCP server — runs on stdio, 18 tools.
npm install
npm test # 92 checks: unit + MCP round-trip via SDK client
npm run build # compile to dist/
npm run cli -- stats # CLI playgroundInstall or run directly from the npm package — no tsx, no source checkout:
npx mcp-rag-memoryPoint your MCP client at npx mcp-rag-memory (the published bin), or
at the local dev entry: node --import tsx src/mcp/rag-server.ts.
This is a standard MCP server (stdio transport, official
@modelcontextprotocol/sdk). It speaks the MCP spec, so any agent
that supports MCP can use it — not just opencode. Memory & knowledge
become shared across all your tools: remember once, recall everywhere.
{
"mcp": {
"rag-memory": {
"type": "local",
"command": ["node", "--import", "tsx", "D:/Project/mcp-server/src/mcp/rag-server.ts"],
"cwd": "D:/Project/mcp-server",
"enabled": true,
"environment": { "RAG_DB_DIR": "D:/Project/mcp-server/.rag-data" }
}
}
}claude_desktop_config.json (in %APPDATA%\Claude):
{
"mcpServers": {
"rag-memory": {
"command": "node",
"args": ["--import", "tsx", "D:/Project/mcp-server/src/mcp/rag-server.ts"],
"env": { "RAG_DB_DIR": "D:/Project/mcp-server/.rag-data" }
}
}
}Settings → MCP → add server, same command/args pattern as Claude
Desktop. No cwd needed — use absolute paths for the entry file and DB.
mcp.json in .vscode/:
{
"servers": {
"rag-memory": {
"type": "stdio",
"command": "node",
"args": ["--import", "tsx", "D:/Project/mcp-server/src/mcp/rag-server.ts"],
"env": { "RAG_DB_DIR": "D:/Project/mcp-server/.rag-data" }
}
}
}Tip: once published (or installed), just point every client at
npx mcp-rag-memory— notsx, no absolute paths, no local checkout. Locally you can still usenode --import tsx <abs-path>/src/mcp/rag-server.ts.
The MCP server is registered here so it works in every project, not just this folder:
{
"$schema": "https://opencode.ai/config.json",
"mcp": {
"rag-memory": {
"type": "local",
"command": ["node", "--import", "tsx", "src/mcp/rag-server.ts"],
"cwd": "D:/Project/mcp-server",
"enabled": true,
"environment": {
"RAG_DB_DIR": "D:/Project/mcp-server/.rag-data"
}
}
}
}
cwdandRAG_DB_DIRare absolute paths so the server resolvestsxfrom this project'snode_modulesand stores data in the same DB regardless of which project opencode is opened from.
Keeps project-specific settings (model, LSP, permissions, instructions) — no mcp block here:
{
"$schema": "https://opencode.ai/config.json",
"model": "anthropic/claude-sonnet-4-6",
"lsp": true,
"instructions": [".opencode/instructions.md"],
"permission": {
"edit": "allow",
"bash": { "git *": "allow", "*": "ask" }
}
}.opencode/instructions.md tells every new session to call
memory_context(topic="The Coder") first, so the assistant always knows
who you are without asking. The same instructions file is also wired into
the global config to apply to every project.
{
"$schema": "https://opencode.ai/tui.json",
"attention": {
"enabled": true,
"sound": true,
"notifications": true,
"volume": 0.4
}
}Restart opencode after any config change.
| Group | Tool | Purpose |
|---|---|---|
| System | system_stats |
DB dir, embedding backend, doc/memory counts |
| RAG ingest | rag_ingest_text |
Store text as a knowledge document (dedup-aware) |
rag_ingest_file |
Store a file (content_type auto-detected) |
|
rag_ingest_dir |
Recursively ingest source files | |
| RAG query | rag_search |
Hybrid (BM25 + vector) search, ranked chunks + scores |
rag_retrieve |
Ready-to-inject context block with token count | |
| RAG docs | rag_list_documents, rag_document_stats |
Inventory |
rag_delete_document |
Remove a document + chunks + FTS rows | |
| Memory | memory_remember |
Save long-term memory (type/importance/tags) |
memory_recall |
Semantic memory search (decay + recall count) | |
memory_context |
Compact context block from memories for prompts | |
memory_list, memory_get, memory_update, memory_forget |
CRUD | |
memory_consolidate |
Dedupe near-identical + promote hot memories | |
memory_stats |
Counts, tokens, avg importance, by type |
The 18 MCP tools are called by the AI automatically — but you can also trigger them directly with custom commands. Sources (same name, project wins):
- Per-project:
.opencode/commands/ - Global:
~/.config/opencode/commands/
| Command | Backing tool | Use |
|---|---|---|
/remember <content> [--type][--importance] |
memory_remember |
Save a memory |
/recall <topic> |
memory_recall |
Search memories semantically |
/context <topic> |
memory_context |
Context block for prompts |
/consolidate |
memory_consolidate |
Dedupe + promote hot memories |
/search <query> |
rag_search |
Semantic search documents |
/retrieve <query> |
rag_retrieve |
Raw context block + tokens |
/ingest <text> |
rag_ingest_text |
Store knowledge text |
/docs |
rag_list_documents |
List all documents |
/stats |
system_stats + doc/memory stats |
Full statistics |
npm run cli -- remember "deploys every Friday" --type task --importance 0.7
npm run cli -- recall "deployment schedule"
npm run cli -- ingest-dir ./src/rag
npm run cli -- search "vector similarity"
npm run cli -- mem-context "database"src/
├── db/database.ts node:sqlite (documents, chunks, memories, chunks_fts, meta)
├── rag/
│ ├── embedder.ts local hashing embedder (1024-d, FNV-1a, n-grams)
│ ├── embeddings.ts provider layer: local | transformers (optional dep)
│ ├── chunker.ts paragraph/code-aware chunking with overlap
│ ├── vector-search.ts vector cache + FTS5 BM25 + RRF hybrid scoring
│ └── pipeline.ts async ingest/search/retrieve, dedup, guards, doc mgmt
├── memory/memory.ts remember / recall / consolidate + decay + prune
├── mcp/rag-server.ts MCP server (18 tools, stdio, npm bin)
├── cli.ts CLI playground
└── test/test-all.ts full test suite (unit + MCP round-trip)
Data lives in .rag-data/rag.sqlite (git-ignored).
| Variable | Default | Purpose |
|---|---|---|
RAG_DB_DIR |
.rag-data |
Where the SQLite store lives |
SEARCH_MODE |
hybrid |
hybrid | vector | keyword |
EMBEDDING_PROVIDER |
local |
local (zero-dep) | transformers (needs optional @huggingface/transformers) |
EMBEDDING_MODEL |
Xenova/all-MiniLM-L6-v2 |
Transformers model name |
RAG_MAX_FILE_MB |
10 |
Reject files larger than this |
RAG_ALLOWED_DIRS |
(unset = anywhere) | Semicolon/pipe/comma-separated allowed ingest roots |
RAG_MEMORY_HALF_LIFE_DAYS |
14 |
Recall score decay half-life |
RAG_PRUNE |
0 |
Set 1 to allow consolidate to delete non-essential memories |
RAG_PRUNE_IMPORTANCE |
0.2 |
Delete memories below this importance |
RAG_PRUNE_AGE_DAYS |
90 |
...and older than this (never-recalled only) |
npm test spins up the real MCP server over stdio using the SDK client
and exercises every tool end-to-end against a scratch DB (.test-data).
The docs/ directory is a self-contained static site of this
project (single index.html, no build step). To publish it on GitHub
Pages:
- Push this repo to GitHub.
- Repo Settings → Pages → Build and deployment → Source: Deploy from a branch.
- Choose branch
mainand folder/docs. - Your site is live at
https://adyoi.github.io/mcp-rag-memory/.
The lsp block in opencode.json has the wrong shape.
Each language key must have a command array:
"lsp": {
"typescript": {
"command": ["typescript-language-server", "--stdio"]
}
}Fields like language_id or extensions alone are not allowed — the
schema enforces additionalProperties: false and requires command.
Fix: either write the command array, or (if you just want built-in
LSP) replace the whole block with "lsp": true.
- Wrong config filename — opencode reads only
opencode.json,opencode.jsonc, or.opencode/opencode.json. A file namedopencode.jsonxis silently ignored; no error, no tools. - File not in the right place — global MCP config lives at
~/.config/opencode/opencode.json, not in the project folder. - Missing
cwdfor global use — whencommanduses a relative path (src/mcp/rag-server.ts), opencode resolves it against the workspace directory, not the project. Add"cwd"to point at the project that containsnode_modules/tsx:
"rag-memory": {
"type": "local",
"command": ["node", "--import", "tsx", "src/mcp/rag-server.ts"],
"cwd": "D:/Project/mcp-server"
}- Missing env var —
RAG_DB_DIRmust be set (absolute path for global config) or the DB defaults to.rag-datarelative to cwd.
The attention feature is off by default. Create
~/.config/opencode/tui.json:
{
"$schema": "https://opencode.ai/tui.json",
"attention": {
"enabled": true,
"sound": true,
"notifications": true,
"volume": 0.4
}
}Known issue (#40445): sound silently fails when opencode runs under the Node runtime instead of Bun — the audio library depends on Bun FFI. Symptoms: no sound despite correct config. Not a configuration error.
tsx compiles TypeScript on first invocation. This is normal and only
happens on cold start; subsequent tool calls within the same session are
instant.
Plugins that run synchronous I/O or write to stderr on every tool call
can slow down the TUI. This project no longer ships any plugins — keep
opencode.json plugin-free for clean operation.
The opencode schema uses custom JSON-Schema extensions (allowComments,
allowTrailingCommas). ajv-cli rejects these by default. Validate
config manually instead:
node -e "JSON.parse(require('fs').readFileSync('opencode.json','utf8')); console.log('OK')"