NoteHarbor MCP
NoteHarbor MCP
一个 Python 示例,用于通过 MCP 和 GraphQL 查询 Obsidian 笔记,并使用 PostgreSQL/pgvector 进行扩展。不包含个人 vault、真实文档和 API 密钥。
NoteHarbor 的原始形态是 Markdown 文件。仅为搜索所需的数据单独创建内容,原始文件则原样保留。
本项目的准则
本地优先(Local first):原始 Markdown 保留在本地,外部服务仅作为可连接的选项。
保留源文件(Source preserving):搜索、嵌入、同步的结果不会取代原始内容。
边界隐私(Privacy by boundary):实际 vault、个人记录和 API 密钥均保留在公开项目之外。
模型中立(Model neutral):不将 embedding 生成器和 Vector DB 绑定到特定厂商。
小而可替换(Small and replaceable):保持适配器和服务层足够小。
Related MCP server: Notes RAG MCP Server
三个公开示例
MCP:
upsert_vector、search_vectors工具GraphQL:
indexChunkMutation、vectorSearchQueryPostgreSQL/pgvector:SQLAlchemy 模型、Repository 端口、Docker 开发环境
它是用于确认结构而非生产服务的示例。默认 Repository 在内存中运行,并同时提供了迁移到 PostgreSQL/pgvector 的切入点与 SQL 示例。
快速开始
uv sync运行 MCP 示例:
uv run noteharbor用 Docker 启动 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 是接入真实数据库连接时使用的配置占位。当前默认执行为 mock。
GraphQL 示例
GraphQL schema 位于 noteharbor/api/graphql_schema.py。可以将 get_schema() 的结果挂载到外部 ASGI 服务器上。
示例操作:
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。默认执行为基于 mock,因此无需连接 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 和检索。
目的
本仓库是一个 Python 架构示例,展示同一个 Markdown 源文件如何通过 MCP 和 GraphQL 两侧由同一个向量检索服务进行查询。
与其预先固定某个特定 embedding 厂商或某个特定向量数据库,更注重于明确标注出可替换的切入点。
MCP / GraphQL API
→ VectorService
→ VectorRepository port
→ 현재: in-memory MockPostgresVectorRepository
→ 전환: PostgreSQL + pgvector向量化与检索的职责
当前 Python 示例不会直接生成 embedding。由 indexChunk mutation 和 upsert_vector 工具接收外部生成的 list[float] embedding 保存。
将 embedding 生成分开的原因如下:
为了不将 embedding 模型固定为 OpenAI、Voyage 或本地模型之一
为了排除 API 密钥和个人数据不进入公开示例
为了分离 API 层与检索存储的职责
为了在实际服务中把 chunking·embedding 批处理流水线替换为独立 worker
检索按以下顺序进行:
사용자 query embedding
→ GraphQL vectorSearch 또는 MCP search_vectors
→ VectorService.search()
→ Repository.search()
→ cosine similarity 계산
→ score가 높은 NoteChunk 반환目前 repository.py 中的 MockPostgresVectorRepository 在内存中再现此流程。真实 PostgreSQL 适配器使用 embedding <=> :query_embedding 计算余弦距离,并以 1 - distance 作为 score 返回。
为什么选择 PostgreSQL + pgvector
NoteChunk 不只是包含向量的值,还同时具有原始路径、chunk 序号、正文和元数据。将 PostgreSQL 与 pgvector 结合,可以在同一个存储库与 SQL 边界内处理关系型条件和向量相似度检索。
PostgreSQL:原始元数据、状态、同步信息和事务管理
pgvector:
vector列、余弦距离(<=>)、HNSW 索引Docker:在开发环境中固定 pgvector 的运行条件
SQLAlchemy Repository:替换存储的切入点
检索规模扩大时,专用向量数据库可能更合适。这里展示的是在同一个存储中处理文档信息与向量检索的流程。
量化的范围
当前的 Python 仓库中尚未加入量化实现。输入 embedding 以 float 值原样接收并检索,这是为了优先展示模型中性的 Repository 边界。
如需加入量化,会单独设置 EmbeddingQuantizer 端口,并由 float32 → INT8/float16 → 存储与恢复 放置于 indexing worker 与 Repository adapter 之间。由于量化方式必须在搜索质量、内存和延迟时间测量后再确定,因此本示例并不夸张地声称已经实现。
两个 NoteHarbor 示例的关系
noteharbor-mcp :TypeScript MCP 工具和 embedding、INT8 量化流程
noteharbor-python:Python MCP 与 GraphQL API,以及 VectorService/Repository 边界
两个仓库是以不同语言和 API 方式实现同一想法的示例,不包含实际个人 vault 或生产环境中的数据。
使用的技术以及原因
技术 | 使用原因 |
uv | 快速复现 Python 依赖、虚拟环境和 lock 文件,并在 Docker 中沿用相同的安装路径 |
FastMCP | 简洁明了地展示 Python 函数与 MCP 工具的连接方式 |
Pydantic | 用于验证 API 边界的输入模型和配置 |
SQLAlchemy | 使 Repository 不绑定特定 SQL 执行方式,提供前端 PostgreSQL 适配器边界 |
psycopg | 用于在 Python 中处理 PostgreSQL 连接 |
pgvector Python package | 用于以 SQLAlchemy 模型表示 PostgreSQL 的 vector 类型 |
Strawberry GraphQL | 提供将同一个 VectorService 暴露为 GraphQL Query/Mutation 的示例 |
**postgreSQL ** | 用于复现已启用 pgvector 的 PostgreSQL 与 Python 开发环境 |
每种技术都按 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.-