特に、AI とローカル環境を繋ぐブリッジとして MCP (Model Context Protocol) サーバーを自作した過程を記録します。
1. 構築したアーキテクチャ
単に Obsidian を使うだけでなく、AI(LLM)がローカルのファイルを自在に検索・閲覧できる「外部脳」としての構成を目指しました。
全文検索エンジン: Omnisearch (Obsidian Plugin)
テキスト抽出: Text Extractor (OCR対応)
AI ブリッジ: 自作 MCP サーバー (Node.js / TypeScript)
2. 【核心】自作 MCP サーバーの実装
AI が Obsidian の保管庫(Vault)にアクセスできるよう、専用の MCP サーバーを実装しました。特に、Excel や PDF の解析精度を高めるために Omnisearch API との連携に注力しています。
MCP サーバーのソースコード (index.mjs)
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { CallToolRequestSchema, ListToolsRequestSchema } from "@modelcontextprotocol/sdk/types.js";
import * as fs from "fs/promises";
import * as path from "path";
import { glob } from "glob";
const VAULT_PATH = process.env.OBSIDIAN_VAULT_PATH;
const OMNISEARCH_HOST = process.env.OMNISEARCH_HOST || "http://localhost:51361";
const server = new Server(
{ name: "obsidian-local-server", version: "1.0.0" },
{ capabilities: { tools: {} } }
);
server.setRequestHandler(ListToolsRequestSchema, async () => ({
tools: [
{
name: "list_notes",
description: "Vault内のノート一覧を取得します(.mdファイルのみ)",
inputSchema: { type: "object", properties: {} },
},
{
name: "read_note",
description: "指定されたパスのノート内容を読み込みます",
inputSchema: {
type: "object",
properties: { relativePath: { type: "string" } },
required: ["relativePath"],
},
},
{
name: "search_vault",
description: "Omnisearchを使用してPDFやOfficeファイルを含む保管庫全体を検索し、内容の抜粋を返します",
inputSchema: {
type: "object",
properties: { query: { type: "string" } },
required: ["query"],
},
}
],
}));
// ツール実行時のロジック(search_vault が今回の肝)
server.setRequestHandler(CallToolRequestSchema, async (request) => {
const { name, arguments: args } = request.params;
switch (name) {
case "search_vault": {
try {
// limitを100に増やし、AIが一度に多くの抜粋(Excerpt)を受け取れるように最適化
const url = `${OMNISEARCH_HOST}/search?q=${encodeURIComponent(args?.query)}&limit=100`;
const response = await fetch(url);
const data = await response.json();
// ファイルパスと抜粋を整形してAIに渡す
const results = data.map((res) =>
`File: ${res.path}\nContent_Found: ${res.excerpt}\n---`
).join("\n");
return {
content: [{ type: "text", text: results || "該当情報なし" }]
};
} catch (error) {
return { content: [{ type: "text", text: `Error: ${error.message}` }], isError: true };
}
}
// ... 他のツール(list_notes, read_note)の実装
}
});
const transport = new StdioServerTransport();
await server.connect(transport);-----------------
こだわりのチューニングポイント
limit=100の設定: デフォルトの検索数では、Excel 内の複数のシートに散らばった情報を拾いきれないことがあります。リミットを広げることで、AI がコンテキストを一度に把握しやすくしました。抜粋(Excerpt)の整形:
File: pathとContent_Foundを明示的に分けることで、AI が「どのファイルのどの部分を読んでいるのか」を誤認しないようにしています。
3. 遭遇したトラブルと解決策:Excel 解析の壁
構築中、Excel ファイルが物理的に存在(ls で確認済み)するのに、AI が「0 results」と答える事象に遭遇しました。
原因と対策:
インデックスの未更新: Omnisearch と Text Extractor の設定変更後、キャッシュクリア(Clear cache data)と Obsidian の再起動が必要でした。
バイナリ解析のラグ: Excel ファイルは PDF 以上にパースに時間がかかる場合があるため、バックグラウンドでのインデックス完了を待つ必要がありました。
4. 導入後の効果
開通後は、AI に対して以下のような自然な指示が可能になりました。
「K8S のまとめ Excel の内容から、Docker ネットワークとの関連性を整理して」
AI は自作の search_vault ツールを叩き、Excel 内の「k8sまとめ」「Dockerまとめ」といった複数シートの構造まで正確に把握して回答を生成します。
0 件のコメント:
コメントを投稿