Skip to main content
Glama

NoteHarbor MCP — TypeScript

Obsidian MarkdownをMCPツールとして接続し、PostgreSQL/pgvector検索で拡張するTypeScriptの例です。

NoteHarborはMCPインターフェースとベクターストレージを分離します。クライアントはMCPツールを呼び出し、サービスがドメインモデルとRepositoryを通じてストレージを使用します。

アーキテクチャ

MCP Client
    │
    ▼
MCP Tools
(upsert_vector / search_vectors)
    │
    ▼
Vector Service
(src/application/vectorService.ts)
    │
    ▼
VectorRepository port
(src/domain/knowledge.ts)
    │
    ├── 현재 실행 어댑터: in-memory Map 목업
    │
    └── 운영 전환 지점: PostgreSQL + pgvector
        (src/infrastructure/postgres/)

ノートは次の順序で検索データになります。

Obsidian Markdown
  → note chunk
  → externally generated embedding
  → note_chunks.embedding (pgvector)
  → cosine similarity search
  → MCP response

Related MCP server: Smart Connections MCP Server

実際のベクトル化フロー

upsert_vectorは、すでに作成したembeddingを保存するツールです。Markdownがチャンクとベクトルに変わる過程はindex_noteで確認できます。

Markdown text
  → splitMarkdownIntoChunks()
  → EmbeddingProvider.embed(chunk)
  → normalized number[]
  → NoteChunk
  → VectorService.indexChunk()
  → VectorRepository.save()

主要なコードは次のファイルに分かれています。

  • chunker.ts: Markdownを段落単位で分割し、最大長を適用

  • embeddingProvider.ts: APIキーなしで動作する決定的なデモEmbeddingプロバイダー

  • indexingPipeline.ts: チャンク生成・Embedding・保存を接続

  • knowledge.ts: EmbeddingProviderとVectorRepositoryのポート

  • index.ts: index_note、upsert_vector、search_vectors MCPツールを登録

デモプロバイダーはフローを確認するための決定的な実装です。意味ベースの検索品質を提供するモデルではなく、実際のサービスでは同じEmbeddingProviderポートに外部またはローカルモデルを接続します。

量子化はEmbedding生成後に適用します。

float embedding [-1, 1]
  → clamp
  → int8 = round(value / (1 / 127))
  → 저장: values + scale + zeroPoint
  → 복원: (int8 - zeroPoint) * scale

サンプルは対称型スカラーINT8量子化を使用します。

  • 値の範囲: [-1, 1]

  • 量子化範囲: [-127, 127]

  • scale: 1 / 127

  • zeroPoint: 0

  • 元のEmbeddingとともにembedding_int8、embedding_scale、embedding_zero_pointを保存するようにスキーマを定義

  • 現在の参照検索は復元可能なfloatベクターを使用し、量子化ANNインデックスは実際のアダプター選択時に追加

実装はquantizer.tsとindexingPipeline.tsにあります。index_note応答にもEmbedding次元と量子化ビット数が含まれます。

なぜこのように構成したのか

NoteHarborはObsidian Markdownを検索可能な知識単位に変換し、その機能をMCPツールとして提供する例です。

単純な文字列検索だけでは、表現が異なる関連内容を見つけるのは困難です。そこでノートを小さなチャンクに分割し、各チャンクをEmbeddingに変換して意味が近い内容を検索できるようにします。

量子化はこのEmbeddingをより小さな表現で保持するための選択です。

  • メモリとストレージ容量を削減

  • ベクター転送量を削減

  • 大規模ナレッジベースでキャッシュとバッチ処理に有利

  • 代わりに元のfloatより精度が低くなる可能性がある

そこで、各構成要素の役割を次のように分けました。

  1. Embedding: テキストの意味を数値ベクターで表現

  2. Quantization: ベクターの精度を下げて保存コストを削減

  3. Vector search: 近いベクターを見つけて関連チャンクを返す

  4. MCP: この機能をLLMクライアントが呼び出せるツールとして公開

このプロジェクトでINT8を選択した理由は、量子化の原理と保存形式をコードで説明しやすいからです。実際のサービスでは、検索品質・メモリ削減・レイテンシーを測定した後、float32・float16・INT8・binaryのいずれかを選択する必要があります。

現在の実装はフローを確認するためのモックアップです。意味ベースの検索品質や量子化パフォーマンスを保証するものではなく、実際の運用ではEmbeddingプロバイダーとpgvectorアダプターを接続する必要があります。

ベクターDB設計

ベクター保存単位はNoteChunkです。

フィールド

意味

id

元のパスとチャンク順序で作成した識別子

sourcePath

Obsidian Markdownの元のパス

chunkIndex

ドキュメント内のチャンク順序

content

検索結果として返すテキスト

embedding

外部Embeddingモデルが生成したベクター

metadata

タグ・ステータス・元の属性などの拡張情報

実行可能なSQL設計の例はsrc/infrastructure/postgres/schema.sqlにあります。基本的な例は1,536次元のEmbeddingとコサイン距離用のHNSWインデックスを使用し、実際のEmbeddingモデルの次元に合わせて調整します。

検索はPostgreSQLで次の形式に変換されます。

SELECT id, source_path, chunk_index, content, metadata,
       1 - (embedding <=> $1::vector) AS score
FROM note_chunks
ORDER BY embedding <=> $1::vector
LIMIT $2;

CRUDとORMスタイルの境界

ベクターストレージは検索専用ではなく、NoteChunkのライフサイクルを管理するRepositoryです。

  • Create/Upsert: upsert_vector、index_note → VectorService.indexChunk()

  • Read: get_chunk、list_chunks

  • Update: update_chunk → 既存のチャンクを読み取り、変更フィールドと量子化表現を一緒に更新

  • Delete: delete_chunk

  • Search: search_vectors、search_knowledge

現在の実行アダプターはMapです。運用環境ではRepository内部の実装をDrizzle ORMとpgに置き換えます。

db.insert(noteChunks).values(row).onConflictDoUpdate(...)
db.select().from(noteChunks).where(eq(noteChunks.id, id)).limit(1)
db.update(noteChunks).set(values).where(eq(noteChunks.id, id))
db.delete(noteChunks).where(eq(noteChunks.id, id))

MCPツールはSQLを直接実行せず、MCP → VectorService → VectorRepository → Drizzle/pgvectorの順序でCRUDと検索を渡します。

含まれるサンプル

  • Obsidian Markdownの一覧・読み取り・検索

  • index_note、search_knowledge、upsert_vector、search_vectors MCPツール

  • MCP → Service → Repository → pgvector階層

  • Drizzle ORMベースのnote_chunksスキーママッピング

  • PostgreSQL/pgvectorスキーマと検索SQL

  • Notion同期境界

  • Smithery開発サーバーとDocker実行例

現在のデフォルト実行は、個人データと外部資格情報を含まないメモリモックアップです。PostgreSQL接続では、src/infrastructure/postgres/postgresVectorRepository.tsのMap実装をDrizzle/pgベースのアダプターに置き換えます。READMEとスキーマは、その移行ポイントを公開するためのサンプルです。

Python/GraphQLバージョンはnoteharbor-pythonで確認できます。

開始

npm install
npm run dev

Docker:

docker build -f Dockerfile.typescript -t noteharbor-mcp-ts .
docker run --rm -p 8081:8081 noteharbor-mcp-ts

なぜPostgreSQL + pgvectorなのか

NoteHarborの検索対象はベクターだけのデータではありません。元のパス、チャンク順序、タグ、状態、同期情報などのリレーショナルメタデータを一緒に扱う必要があります。

そこで、このサンプルでは別のベクターDBを追加するのではなく、PostgreSQLに次のものをまとめて配置します。

  • content: 検索結果として表示する原文の断片

  • metadata: タグ・状態・元の情報

  • embedding: コサイン検索用のfloatベクター

  • embedding_int8: 保存・転送コストを削減するための量子化表現

pgvectorを選択した具体的な理由は次のとおりです。

  • データ結合: ベクター類似度とsource_path、タグ、状態条件を1つのSQLで組み合わせられる

  • 一貫性: 原文メタデータと検索インデックスを同じトランザクション境界で管理できる

  • 運用の単純さ: アプリケーションがPostgreSQLと別のベクターDBをそれぞれ運用する必要がない

  • 検索機能: コサイン距離(<=>)とHNSWインデックスをPostgreSQL拡張として使用できる

  • 拡張パス: 初期は単一ストレージで開始し、規模が大きくなったときに検索専用アダプターを分離できる

検索規模が大きくなると、専用のベクターDBがより適している場合があります。ここでは、元のテキスト、メタデータ、ベクター検索を1つのアプリケーション境界で扱うフローを示すことに意味があります。

検索はどのように動作するか

検索は元の文字列を直接比較するのではなく、質問とノートチャンクを同じEmbedding空間に置き、距離を比較します。

사용자 질문
  → query embedding 생성
  → INT8 양자화 후 복원
  → PostgreSQL/pgvector cosine distance 검색
  → 가까운 NoteChunk 반환
  → sourcePath·content·metadata와 함께 MCP 응답

実行可能なサンプルはsearch_knowledge MCPツールです。

  1. index_noteがMarkdownをチャンクに分割し、各チャンクのEmbeddingを保存します。

  2. ユーザーが自然言語queryを送信します。

  3. 同じEmbeddingProviderでクエリEmbeddingを生成します。

  4. クエリを保存ベクターと同じ方法で量子化・復元します。

  5. VectorRepository.search()がコサイン類似度基準で近いチャンクをソートします。

  6. 検索結果には元のパス、チャンク内容、メタデータ、スコアが含まれます。

search_vectorsはすでに作成されたEmbeddingを直接受け取る低レベルツールで、search_knowledgeは自然言語の質問から検索結果までを接続するアプリケーションレベルのツールです。

現在のモックRepositoryはメモリMapでコサイン類似度を計算します。PostgreSQLアダプターに切り替えると、同じポートの背後でpgvectorの<=>演算とLIMIT検索を使用します。

追加技術と投入理由

技術

投入理由

Node.js ESM

TypeScript MCPサンプルを現在のNodeランタイム方式で単純に実行するため

MCP SDK

index_note、search_knowledge、upsert_vector、search_vectorsツールを標準MCPサーバーとして登録するため

Zod

MCP入力値と設定値をランタイムで検証するため

Drizzle ORM

PostgreSQLアダプターを接続するときに型安全なスキーマ・クエリ境界を提供するため

pg

実際のPostgreSQL接続アダプターに切り替えるときに使用するドライバー

Smithery CLI

MCPサーバーを開発・検証する実行パスを提供するため

Docker

ローカルとデプロイ環境のNode/MCP実行条件を固定するため

chokidar・fast-glob

Markdown vaultの変更検知とファイル探索を担当するため

gray-matter・marked

Markdown frontmatterと本文を知識チャンクとして扱うため

dotenv

ローカル環境設定をコードと分離するため

すべての依存関係がベクター検索の核心ではありません。一部はObsidian・Notion連携のための補助技術であり、検索パスの中心はMCP SDK → Vector Service → Repository → pgvectorです。

技術を選択した理由

技術

選択理由

Obsidian Markdown

元のファイルがプレーンテキストファイルであるため、所有権と可搬性が高く、特定のSaaSに知識の元を依存させないため選択

MCP

LLMクライアントごとに別の連携コードを作成せず、同じ知識ツールを標準インターフェースとして公開するため選択

TypeScript

MCP SDKとの接続が自然で、ツールの入力・出力境界を型で管理するため選択

PostgreSQL

ドキュメントメタデータ・状態・検索結果を1つのストレージで一貫して管理し、運用移行パスを確保するため選択

pgvector

別のベクターDBを追加せず、PostgreSQL内で元のメタデータとベクター検索を一緒に扱うため選択

Notion adapter

Notionを元のストレージとして使うのではなく、必要な場合に知識の断片を外部ワークスペースに同期する境界を示すため選択

Docker

ローカル環境とデプロイ環境でPostgreSQL・MCP実行条件を一定にするため選択

核心はツールを多く追加することではありません。元のファイルはMarkdownで保存し、検索用の派生データはPostgreSQL/pgvectorに置き、LLMにはMCPで必要な機能だけを提供することです。

したがって、このプロジェクトはObsidian・Notion・PostgreSQLをすべて元のストレージとして使用するシステムではありません。

  • Obsidian Markdown: 元の知識

  • PostgreSQL/pgvector: 検索用の派生インデックス

  • Notion: オプションの外部同期先

  • MCP: LLMアクセス境界

設計ポイント

  • 元のMarkdownの保存

  • MCPツールとサービスレイヤーの分離

  • NoteChunkドメインモデルとVectorRepositoryポート

  • PostgreSQL/pgvectorに置き換え可能なストレージ境界

  • 実際の個人vaultと資格情報を除外

このプロジェクトはMCPと知識検索構造を確認するための公開例です。

ライセンス

MIT

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    A
    maintenance
    Connects AI assistants to an Obsidian vault as a semantic knowledge graph, enabling graph navigation, semantic search, and content operations through MCP.
    9 npm
    467
    MIT
  • F
    license
    A
    quality
    C
    maintenance
    Exposes Obsidian notes as a semantic search and RAG knowledge base over MCP, enabling AI assistants to index, retrieve, and analyze personal notes via natural language.
    7
    1
    -
  • A
    license
    B
    quality
    A
    maintenance
    Enables local hybrid search over Obsidian and Markdown vaults via MCP, combining vector retrieval, full-text search, reranking, graph navigation, and safe CRUD while keeping data local.
    43
    1
    MIT