Hindsight 統合メモリアクセス:FastMCP ブリッジと動的マルチプロジェクトルーティング
技術ノート
公開 / DavidHLPL
このページでは、Vectorize Hindsight メモリエンジンを端末エージェント OMP (Oh-My-Pi) と Codex CLI に接続するブリッジ実装と設定規約を記録する。中心となる改善は、**マルチプロジェクト適応型の分離ルーティング(Dynamic Workspace Routing)**と、セッション状態を保持する FastMCP stdio ブリッジである。
範囲と確認(2026-09-08):2026-08-17 snapshot の Hindsight v0.9.1 接続記録。OMP、Codex、Node.js、bridge は版固定が不十分。コードは過去の例で、その修訂は全文・プロトコルの静的確認のみ。再起動や現 client 設定の互換検証はしていない。
1. マルチプロジェクト適応型分離
複数プロジェクトを開発するとき、グローバルな単一 Bank(例えば常に ulticode を使う方式)をハードコードすると、異なるコードベースのメモリ、エンティティ、ルールが互いに汚染される。
Git root のディレクトリ名から ulticode、resicache、davidhlp_github_io 等の Bank ID を作る。これはアプリ層の論理分割で、独立 DB・process・アクセス制御としての物理隔離ではない。basename と文字置換だけなので、異なるパスの同名 repo や a.b と a_b は衝突する。必要なら client ごとに異なる HINDSIGHT_BANK_ID を明示し、OMP の実 Bank も照合する。
プロジェクトワークスペース (/path/to/project-a) ──> Git ルート検出 ──> Bank: project-a ──> Hindsight /mcp/project-a/
プロジェクトワークスペース (/path/to/project-b) ──> Git ルート検出 ──> Bank: project-b ──> Hindsight /mcp/project-b/
2. 過去の FastMCP ブリッジ例 (~/.hindsight/hindsight-bridge.mjs)
Codex CLI と一部の Agent 子プロセスは stdio パイプ経由で JSON-RPC 通信を行う。一方、Hindsight v0.9.1 が提供するのは HTTP ベースの Streamable FastMCP エンドポイントである。そのため、プロトコル変換と Session ID の状態保持を行う軽量なローカル Node.js ブリッジが必要になる:
#!/usr/bin/env node
/**
* Dynamic Multi-Project FastMCP Bridge for Hindsight
* サブディレクトリからでも Git リポジトリルートを解決し、正確なプロジェクト Bank ID を求める。
*/
import readline from "node:readline";
import path from "node:path";
import { execSync } from "node:child_process";
import fs from "node:fs";
function findProjectRoot() {
try {
const gitRoot = execSync("git rev-parse --show-toplevel", {
stdio: ["pipe", "pipe", "ignore"],
encoding: "utf-8"
}).trim();
if (gitRoot && fs.existsSync(gitRoot)) return gitRoot;
} catch {}
let curr = process.cwd();
while (curr && curr !== path.dirname(curr)) {
if (fs.existsSync(path.join(curr, ".git"))) return curr;
curr = path.dirname(curr);
}
return process.cwd();
}
function getDynamicBankId() {
if (process.env.HINDSIGHT_BANK_ID?.trim()) {
return process.env.HINDSIGHT_BANK_ID.trim();
}
const root = findProjectRoot();
const baseName = path.basename(root);
if (!baseName || baseName === "/" || baseName === "." || baseName === "root") {
return "default";
}
return baseName.toLowerCase().replace(/[^a-z0-9_-]/g, "_");
}
const BANK_ID = getDynamicBankId();
const BASE_URL = process.env.HINDSIGHT_API_BASE_URL || "http://127.0.0.1:8888";
const TARGET_URL = `${BASE_URL.replace(/\/+$/, "")}/mcp/${BANK_ID}/`;
let sessionId = null;
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
terminal: false
});
rl.on("line", async (line) => {
const trimmed = line.trim();
if (!trimmed) return;
try {
const payload = JSON.parse(trimmed);
const headers = {
"Content-Type": "application/json",
Accept: "application/json, text/event-stream"
};
if (sessionId) {
headers["mcp-session-id"] = sessionId;
}
const res = await fetch(TARGET_URL, {
method: "POST",
headers,
body: JSON.stringify(payload)
});
const returnedSessionId = res.headers.get("mcp-session-id");
if (returnedSessionId) {
sessionId = returnedSessionId;
}
const contentType = res.headers.get("content-type") || "";
if (contentType.includes("text/event-stream")) {
const text = await res.text();
for (const l of text.split("\n")) {
if (l.startsWith("data:")) {
const data = l.slice(5).trim();
if (data && data !== "[DONE]") {
process.stdout.write(data + "\n");
}
}
}
} else {
const json = await res.json();
process.stdout.write(JSON.stringify(json) + "\n");
}
} catch (err) {
try {
const parsed = JSON.parse(trimmed);
if (parsed.id !== undefined) {
process.stdout.write(
JSON.stringify({
jsonrpc: "2.0",
id: parsed.id,
error: {
code: -32603,
message: `Hindsight Bridge Error: ${err.message}`
}
}) + "\n"
);
}
} catch {}
}
});
例に残るプロトコルの不足
Session ID 転送と Git root 検出の考え方は残るが、完全な汎用 MCP proxy ではない。SSE 応答全体の終了を待って転送し、完全なイベント境界を扱わず、通知の空応答、並行 handshake、session 失効後の再初期化、交渉後の protocol version header も明示処理しない。MCP 2025-06-18 transport では通知成功は本文なし HTTP 202 を返せ、Streamable HTTP 自体が SSE を扱う。Session ID は server が割り当てた場合に後続転送が必要。実 client でこれらを検証し、一回の tools/list 成功を汎用互換の証明にしない。
3. Agent クライアント設定
パス設定規約:Node プロセスと環境は
argsのパス内の~を自動展開しない。/home/user/のような絶対パスを使うか、環境変数から注入すること。
1. OMP 設定(~/.omp/agent/config.yml と ~/.omp/agent/mcp.json)
~/.omp/agent/config.yml:
memory:
backend: hindsight
hindsight:
apiUrl: http://127.0.0.1:8888
scoping: per-project # プロジェクトごとに Bank を自動分離
autoRecall: true # セッション開始時にメモリを取得して注入
autoRetain: true # セッション終了時にメモリを抽出して保持
mentalModelsEnabled: true # メンタルモデルを有効化
mentalModelAutoSeed: true # シードメンタルモデルを自動生成
recallBudget: mid
autolearn:
enabled: true
autoContinue: true
~/.omp/agent/mcp.json(<USER_HOME> を実際の絶対パスに置き換える):
{
"mcpServers": {
"hindsight": {
"type": "stdio",
"command": "node",
"args": ["<USER_HOME>/.hindsight/hindsight-bridge.mjs"]
}
}
}
2. Codex 設定(~/.codex/config.toml)
[mcp_servers.hindsight]
command = "node"
args = ["<USER_HOME>/.hindsight/hindsight-bridge.mjs"]
[mcp_servers.hindsight.env]
HINDSIGHT_API_BASE_URL = "http://127.0.0.1:8888"
# HINDSIGHT_BANK_ID はハードコードしない。ブリッジが Git ルートで自動ルーティングする。
4. 検証結果と実測指標
以下は 2026-08-17 raw の一回の書き込み・検索の転記で、再実行ではない。回数、負荷、全版、計時方法がなく、score と 0.059s は性能基準ではない。テスト文も現プロジェクトの設定事実ではない:
[書き込み段階(Codex MCP Bridge -> GPU Fact Extraction)]
書き込み:"Backend uses Java 17 and Spring Boot 3.2.5 with three owner services: auth 9101, admin 9102, app 9103."
抽出結果:3 件の Fact エンティティの抽出に成功し、BAAI/bge-m3 でベクトル索引を作成した。
[クエリ段階(OMP / Codex Recall)]
クエリ:"What port does the auth service use?"
結果:"Backend has three owner services: auth (9101), admin (9102), and app (9103)" にヒットした。
指標:リランカー スコア 0.965、セマンティックスコア 0.668、検索応答時間 0.059s。
5. 証拠と不確実性
- 出典報告:固定 raw
hindsight-local-deployment-and-agent-integrationは handshake、tool 発見、一回の検索成功を記録する。完全な状態機械や全 client 互換ではない。 - 総合:bridge code、両端設定、観測値を Entity 接続記録に整理。
- 静的に確認可能:Git root と明示
HINDSIGHT_BANK_IDがなければ CWD の basename から Bank を作る。空名、/、.、rootの場合だけdefault。非 Git ではサブディレクトリによるずれが残る。 - 未確認:現在の OMP
per-project命名との一致、model・権限・retain/recall 全経路は別途実測が必要。
6. 関連ページ
出典と関連文書
研究記録
- 元の文書: src/content/note/ja/hindsight-omp-codex-integration.md
- 出典リビジョン: f49953fb0d4bd52a39bf81167b0c4b7dd7d78a64
- 証拠の状態: 有効