Skip to main content
Glama
README.md
# qs-rag

**ローカルの知識ベースを自然文でセマンティック検索する MCP サーバー。**
Markdown ノートや過去の作業ログを「見出し単位」で索引し、`grep` / 全文探索の代わりに意味で当てにいく。

> Model Context Protocol (MCP) server for semantic search over a local knowledge base. Fully local, zero API cost.

実際のインサイドセールス業務で日次運用しているツール。AI エージェント(Claude Code)が過去の方針・フォーマット・経緯を思い出すための「外部記憶」として使っている。

## なぜ作ったか

エージェントに数百件の Markdown メモや過去セッションログを参照させたいが、`grep` はキーワード一致しか拾えず「あれ何だっけ」系の曖昧な問いに弱い。かといってクラウドのベクトル DB を挟むとコストと秘匿性の問題が出る。
→ **埋め込み・ストア・検索を全部ローカルに閉じた RAG** を MCP として実装した。

## 設計のポイント

- **完全ローカル / API コストゼロ** — 埋め込みは Ollama (`snowflake-arctic-embed2`・1024次元・多言語)。外部送信なし
- **ネイティブビルド不要のストア** — `node:sqlite` にベクトルを置き、メモリ上のブルートフォース cos 類似で検索。件数規模的にこれで十分速く、依存を増やさない
- **2段階の差分インデックス** — `mtime` で粗くふるい、変化したものだけ `sha256` で確定判定。検索のたびに 30 秒スロットルで自動更新するので索引が陳腐化しない
- **Progressive Disclosure** — まず `search_knowledge` で当たりを付け、`get_detail` で本文+同一ファイル内の隣接チャンクを開く。エージェントが文脈を横に広げられる
- **埋め込み層を 1 ファイルに隔離** — 別モデル / 別エンジンへの差し替えは `src/embed.js` だけ変えれば済む

## MCP ツール

| ツール | 役割 |
|---|---|
| `search_knowledge(query, limit?, exclude?, domain?)` | 自然文クエリで意味検索。まずこれで当たりを付ける |
| `get_detail(id)` | チャンク本文+同一ファイル内の隣接チャンク一覧 |
| `reindex_now()` | 即時フル差分 reindex |

`domain` でソース種別(個人ノート / 同期ドキュメント / 過去セッションログ)を絞り込める。

`cc-log` は Claude Code の `~/.claude/projects/-Users-kazuki/*.jsonl` に加え、
Codex の `~/.codex/sessions/YYYY/MM/DD/*.jsonl` も同じ索引へ統合する。
Codex側は `response_item.payload` の user / assistant 本文だけを抽出し、
システム指示・推論・ツール出力・起動時のAGENTS wrapperは除外する。
検索時の差分reindexにより、Codexの新規・更新セッションも自動反映される。

## スタック

Node.js (ESM) / `@modelcontextprotocol/sdk` / Ollama / `node:sqlite`

## セットアップ

```bash
npm install
# Ollama を起動し、埋め込みモデルを pull
ollama pull snowflake-arctic-embed2

node bin/reindex.js          # 初回フルインデックス
node bin/search.js "クエリ"   # ターミナルから直接検索
```

MCP ホスト(Claude Code 等)には stdio サーバーとして登録する。

### 索引対象の指定

既定では `~/notes`(Markdown)と `~/.claude/projects`・`~/.codex/sessions`(エージェントのセッションログ)を索引する。差し替えは環境変数で行う。

| 環境変数 | 既定値 | 用途 |
|---|---|---|
| `RAG_SOURCES` | (なし) | 索引対象を JSON 配列で直接指定 |
| `RAG_SOURCES_FILE` | (なし) | 同じ JSON を書いたファイルのパス |
| `RAG_DB_PATH` | `~/.qs-rag/index.db` | インデックス DB の置き場所 |
| `QS_RAG_OLLAMA_URL` | `http://127.0.0.1:11434` | Ollama のエンドポイント |
| `QS_RAG_MODEL` | `snowflake-arctic-embed2` | 埋め込みモデル |

```jsonc
// RAG_SOURCES の例。root は ~ 展開に対応する
[
  { "root": "~/notes",     "kind": "md",    "domain": "knowledge", "excludeDirs": ["_archive"] },
  { "root": "~/work/docs", "kind": "md",    "domain": "context" },
  { "root": "~/.claude/projects", "kind": "jsonl", "domain": "cc-log", "recursive": true }
]
```

`kind` は `md`(Markdown を見出し単位でチャンク化)か `jsonl`(セッションログを turn 単位で取り込み)。`domain` は検索時の絞り込み・除外の単位になる。

---

<sub>個人で開発した MCP サーバー群の 1 つ。設計の出発点として yuya-mcp の RAG パートを参考にした。</sub>