NoteHarbor MCP
NoteHarbor MCP
ObsidianノートをMCPとGraphQLで検索し、PostgreSQL/pgvectorで拡張するPythonの例です。個人のvault、実際のドキュメント、APIキーは含まれません。
NoteHarborの原本はMarkdownファイルです。検索に必要なデータだけを別に作成し、元のファイルはそのまま保持します。
このプロジェクトの基準
Local first: 元のMarkdownはローカルに残し、外部サービスは接続可能な選択肢としておきます。
Source preserving: 検索・埋め込み・同期の結果が原本を置き換えないようにします。
Privacy by boundary: 実際のvault、個人の記録、APIキーは公開プロジェクトの外に置きます。
Model neutral: 埋め込み生成器とVector DBを特定のベンダーに固定しません。
Small and replaceable: アダプターとサービス層を小さく保ちます。
Related MCP server: Notes RAG MCP Server
公開サンプル3つ
MCP:
upsert_vector,search_vectorsツールGraphQL:
indexChunkMutation,vectorSearchQueryPostgreSQL/pgvector: SQLAlchemyモデル、Repositoryポート、Docker開発環境
運用サービスではなく、構造を確認するための例です。デフォルトのRepositoryはメモリ上で動作し、PostgreSQL/pgvectorへ移す箇所とSQL例をあわせて提供します。
クイックスタート
uv syncMCPサンプルを実行:
uv run noteharborDockerでPostgreSQLを起動する
docker compose up -d postgresdocker-compose.yml は pgvector/pgvector イメージとPython MCP用の開発用PostgreSQLを定義します。
# PostgreSQL만
docker compose up -d postgres
# Python MCP까지
docker compose up --build python-mcpPOSTGRES_URL は実際のDB接続を設定するときに使う設定場所です。現在のデフォルト実行はモックです。
GraphQLサンプル
GraphQLスキーマは noteharbor/api/graphql_schema.py にあります。外部ASGIサーバーに get_schema() の結果をマウントできます。
処理例:
mutation {
indexChunk(
sourcePath: "sample.md"
content: "Obsidian knowledge"
embedding: [1.0, 0.0]
)
}
query {
vectorSearch(embedding: [0.9, 0.1], limit: 5) {
sourcePath
content
score
}
}CRUDとORMスタイルの境界
Repositoryは検索だけでなく、NoteChunkの生成から削除までの全ライフサイクルを担当します。
Create/Upsert: MCP
upsert_vector、GraphQLindexChunk→VectorService.index_chunk()Read: MCP
get_chunk・list_chunks、GraphQLnoteChunk・noteChunksUpdate: MCP・GraphQL
updateChunk→ 既存レコードを読み取ったうえで、変更フィールドのみ反映Delete: MCP・GraphQL
deleteChunkSearch: MCP
search_vectors、GraphQLvectorSearch
現在のアダプターは動作確認用のインメモリモックです。実際の保存方法はSQLAlchemy ORM境界の背後に置きます。実際のPostgreSQLアダプターでは、次のようなセッション操作で MockPostgresVectorRepository を置き換えます。
実際のORMフローをコードで確認したい場合は、noteharbor/infrastructure/postgres/sqlalchemy_repository.py を参照してください。このアダプターは同じ VectorRepository ポートを実装しながら、Session.get、select、update、delete を使用します。デフォルト実行は依然としてモックなので、PostgreSQL接続なしでサンプルを読んでテストできます。
session.get(NoteChunkModel, chunk_id)
session.scalars(select(NoteChunkModel).limit(limit)).all()
session.execute(update(NoteChunkModel).where(NoteChunkModel.id == chunk_id).values(...))
session.execute(delete(NoteChunkModel).where(NoteChunkModel.id == chunk_id))APIはSQLを直接扱わず、MCP/GraphQL → VectorService → VectorRepository → SQLAlchemy/pgvector の順でCRUDと検索を引き渡します。
目的
このリポジトリは、1つのMarkdown原本をMCPとGraphQLの両方から、同じベクトル検索サービスで参照するPythonアーキテクチャのサンプルです。
特定のembeddingベンダーやベクトルDBを決めるよりも、置き換えられる箇所を明確にする点を重視しています。
MCP / GraphQL API
→ VectorService
→ VectorRepository port
→ 현재: in-memory MockPostgresVectorRepository
→ 전환: PostgreSQL + pgvectorベクトル化と検索の責任
現在のPythonサンプルは、embeddingを直接生成しません。indexChunk mutation と index で、外部から生成された list[float] embedding を受け取って保存します。
embedding生成を分離した理由は次のとおりです。
embeddingモデルをOpenAI・Voyage・ローカルモデルのうち1つに固定しないため
APIキーと個人データを公開サンプルから除外するため
API層と検索ストレージの責任を分離するため
実サービスでは、chunking・embeddingのバッチパイプラインを別のワーカーに置き換えるため
検索は次の順序です。
사용자 query embedding
→ GraphQL vectorSearch 또는 MCP search_vectors
→ VectorService.search()
→ Repository.search()
→ cosine similarity 계산
→ score가 높은 NoteChunk 반환現在の repository.py の MockPostgresVectorRepository は、このフローをメモリ上で再現します。実際のPostgreSQLアダプターでは、embedding <=> :query_embedding でコサイン距離を計算し、1 - distance をスコアとして返します。
なぜPostgreSQL + pgvectorなのか
NoteChunkはベクトルだけを持つ値ではなく、元のパス・チャンク番号・本文・メタデータも一緒に持ちます。PostgreSQLとpgvectorを組み合わせることで、関係条件とベクトル類似度検索を1つのストレージとSQL境界内で扱えます。
PostgreSQL: 原文メタデータ、状態、同期情報、トランザクション管理
pgvector:
vectorカラム、コサイン距離(<=>)、HNSWインデックスDocker: 開発環境でpgvectorの実行条件を固定
SQLAlchemy Repository: ストレージの置き換え箇所
検索規模が大きくなると、専用ベクトルDBの方が適している場合があります。ここでは、ドキュメント情報とベクトル検索を1つのストレージで扱うフローを示します。
量子化の範囲
現在のPythonリポジトリには、量子化の実装を実装していません。入力embeddingはfloat値のまま保存・検索します。これはモデルに依存しないRepository境界を先に示すための選択です。
量子化を追加する場合には、EmbeddingQuantizer ポートを別途設けて、float32 → INT8/float16 → save & restore の段階を indexing worker と Repository adapter の間に配置します。量子化方法は検索品質・メモリ・遅延時間を測定してから決める必要があるため、このサンプルでは実装済みとは説明しません。
2つのNoteHarborサンプルの関係
noteharbor-mcp: TypeScript MCPツールとembedding・INT8量子化フロー
noteharbor-python: Python MCP・GraphQL APIとVectorService/Repository境界
2つのリポジトリは、同じ考えを異なる言語とAPI方法で表現した例であり、実際の個人vaultや本番データは含みません。
追加技術と採用理由
技術 | 採用理由 |
uv | Pythonの依存関係・仮想環境・lockファイルをすばやく再現し、Dockerでも同じ導入経路を利用するため |
FastMCP | Python関数とMCPツールの接続を短く明確に示すため |
Pydantic | API境界の入力モデルと設定を検証するため |
SQLAlchemy | Repositoryが特定のSQL実行方法に固定されないよう、PostgreSQL adapter境界を置くため |
psycopg | PythonからPostgreSQLへの接続を担当するため |
pgvector Python package | SQLAlchemyモデルでPostgreSQLの vector 型を表現するため |
Strawberry GraphQL | 同じVectorServiceをGraphQL Query/Mutationとして公開する例を示すため |
Docker Compose | pgvectorが有効化されたPostgreSQLとPython MCPの開発環境をまとめて再現するため |
各技術は、API・サービス・ストレージ・開発環境の役割を分けて説明するために選びました。
プロジェクト構造
.
├── noteharbor/
│ ├── mcp_server.py
│ ├── api/graphql_schema.py
│ ├── application/vector_service.py
│ ├── domain/
│ └── infrastructure/
│ ├── notion/mock_adapter.py
│ └── postgres/
│ ├── models.py
│ ├── repository.py
│ └── sqlalchemy_repository.py
├── tests/test_vector_repository.py
├── Dockerfile
├── docker-compose.yml
└── pyproject.tomlライセンス
MIT
This server cannot be deployed
Maintenance
Related MCP Connectors
- TaprootOAuthcom.taproothq
Persistent memory layer for AI tools. Save and recall notes across Claude and other MCP clients.
MCP-native notes and memory for ChatGPT, Claude, and other AI tools.
Search, read, and write your Apple Notes from ChatGPT/Claude via a local Mac agent + MCP relay.
Hosted MCP memory: save sessions/decisions once, search from Claude, Cursor, ChatGPT. EU-hosted FTS.
Related MCP Servers
- FlicenseNot gradedqualityDmaintenanceProvides tools for ingesting documents into a local vector database and retrieving relevant information via semantic search, enabling retrieval-augmented generation for MCP clients.6-
- FlicenseNot gradedqualityAmaintenanceEnables semantic search over personal markdown notes by indexing them into a vector database and exposing search, reindex, and status tools via MCP.-
- AlicenseNot gradedqualityAmaintenanceProvides MCP tools for semantic search over personal knowledge sources using pluggable embeddings and local vector indexing.1MIT
- FlicenseNot gradedqualityCmaintenanceEnables semantic search and related-note discovery across an Obsidian vault via MCP, providing RAG context blocks for natural language queries.-