NoteHarbor MCP
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 responseRelated MCP server: Smart Connections MCP Server
实际向量化流程
upsert_vector 是保存已生成 embedding 的工具。Markdown 转换为 chunk 和向量的过程可以在 index_note 中查看。
Markdown text
→ splitMarkdownIntoChunks()
→ EmbeddingProvider.embed(chunk)
→ normalized number[]
→ NoteChunk
→ VectorService.indexChunk()
→ VectorRepository.save()主要代码分布在以下文件中。
chunker.ts:按段落拆分 Markdown 并应用最大长度
embeddingProvider.ts:无需 API 密钥即可运行的确定性演示 embedding provider
indexingPipeline.ts:连接 chunk 生成、embedding 和存储
knowledge.ts:
EmbeddingProvider和VectorRepository端口index.ts:注册
index_note、upsert_vector、search_vectorsMCP 工具
演示 provider 是用于确认流程的确定性实现。它并不是提供基于语义的搜索质量的模型,在实际服务中,可以在同一个 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 / 127zeroPoint:0定义 schema 以与原始 embedding 一起存储
embedding_int8、embedding_scale、embedding_zero_point当前参考搜索使用可恢复的 float 向量,量化 ANN 索引在实际选择 adapter 时添加
实现位于 quantizer.ts 和 indexingPipeline.ts 中。index_note 响应中也包含 embedding 维度和量化位数。
为什么这样设计
NoteHarbor 是一个将 Obsidian Markdown 转换为可搜索的知识单元,并通过 MCP 工具提供该功能的示例。
仅靠简单的字符串搜索很难找到表达方式不同的相关内容。因此,将笔记拆分为较小的 chunk,并将每个 chunk 转换为 embedding,从而可以搜索语义相近的内容。
量化是为了将 embedding 以更小的表示形式保存而做出的选择。
减少内存和存储空间
减少向量传输量
有利于大规模知识库中的缓存和批处理
但精度可能低于原始 float
因此,各组成部分的角色划分如下。
Embedding:将文本的语义表示为数值向量
Quantization:降低向量的精度以节省存储成本
Vector search:查找相近的向量并返回相关 chunk
MCP:将这些功能暴露为 LLM 客户端可调用的工具
本项目选择 INT8 的原因是更容易用代码解释量化原理和存储形式。在实际服务中,应在测量搜索质量、内存节省和延迟之后,从 float32、float16、INT8、binary 中选择一种。
当前实现是用于确认流程的模拟(mockup)。它不保证基于语义的搜索质量或量化性能,在实际运营中需要接入 embedding provider 和 pgvector adapter。
向量数据库设计
向量存储单元是 NoteChunk。
字段 | 含义 |
| 由原始路径和 chunk 序号生成的标识符 |
| Obsidian Markdown 原始路径 |
| 文档内 chunk 的序号 |
| 作为搜索结果返回的文本 |
| 外部 embedding 模型生成的向量 |
| 标签、状态、原始属性等扩展信息 |
可执行的 SQL 设计示例位于 src/infrastructure/postgres/schema.sql。默认示例使用 1,536 维 embedding 和用于 cosine distance 的 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_chunksUpdate:
update_chunk→ 读取现有 chunk,并同时更新变更字段和量化表示Delete:
delete_chunkSearch:
search_vectors、search_knowledge
当前执行 adapter 是 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_vectorsMCP 工具MCP → Service → Repository → pgvector分层基于 Drizzle ORM 的
note_chunksschema 映射PostgreSQL/pgvector schema 和搜索 SQL
Notion 同步边界
Smithery 开发服务器和 Docker 运行示例
当前默认运行是不包含个人数据和外部凭据的内存模拟(memory mockup)。在 PostgreSQL 连接中,将 src/infrastructure/postgres/postgresVectorRepository.ts 中的 Map 实现替换为基于 Drizzle/pg 的 adapter。README 和 schema 是公开展示该转换点的示例。
Python/GraphQL 版本可在 noteharbor-python 中查看。
开始
npm install
npm run devDocker:
docker build -f Dockerfile.typescript -t noteharbor-mcp-ts .
docker run --rm -p 8081:8081 noteharbor-mcp-ts为什么选择 PostgreSQL + pgvector
NoteHarbor 的搜索目标不仅仅是只有向量的数据。还需要同时处理原始路径、chunk 序号、标签、状态、同步信息等关系型元数据。
因此,本示例没有额外添加独立的向量数据库,而是将以下内容一起放在 PostgreSQL 中。
content:作为搜索结果展示的原文片段metadata:标签、状态、原始信息embedding:用于 cosine 搜索的 float 向量embedding_int8:为降低存储和传输成本而使用的量化表示
选择 pgvector 的具体原因如下。
数据结合:可以在一条 SQL 中组合向量相似度与
source_path、标签、状态条件一致性:可以在同一事务边界内管理原文元数据和搜索索引
运维简单性:应用程序无需分别运维 PostgreSQL 和独立的向量数据库
搜索功能:可以通过 PostgreSQL 扩展使用 cosine distance(
<=>)和 HNSW 索引扩展路径:初期从单一存储开始,规模扩大时可以分离出搜索专用 adapter
当搜索规模扩大时,专用向量数据库可能更合适。这里的意义在于展示在同一个应用程序边界内处理原文、metadata 和向量搜索的流程。
搜索如何工作
搜索不是直接比较原文字符串,而是将问题和笔记 chunk 放在同一个 embedding 空间中,然后比较距离。
사용자 질문
→ query embedding 생성
→ INT8 양자화 후 복원
→ PostgreSQL/pgvector cosine distance 검색
→ 가까운 NoteChunk 반환
→ sourcePath·content·metadata와 함께 MCP 응답可执行的示例是 search_knowledge MCP 工具。
index_note将 Markdown 拆分为 chunk,并保存每个 chunk 的 embedding。用户发送自然语言
query。使用同一个
EmbeddingProvider生成 query embedding。以与存储向量相同的方式对 query 进行量化和恢复。
VectorRepository.search()根据 cosine similarity 对相近的 chunk 进行排序。搜索结果包含原始路径、chunk 内容、metadata 和 score。
search_vectors 是直接接收已生成 embedding 的低层工具,而 search_knowledge 是从自然语言问题到搜索结果的应用程序级工具。
当前模拟 Repository 在内存 Map 中计算 cosine similarity。切换到 PostgreSQL adapter 后,将在同一个端口后面使用 pgvector 的 <=> 运算和 LIMIT 搜索。
附加技术与投入原因
技术 | 投入原因 |
Node.js ESM | 以当前 Node 运行时方式简单运行 TypeScript MCP 示例 |
MCP SDK | 将 |
Zod | 在运行时验证 MCP 输入值和配置值 |
Drizzle ORM | 在连接 PostgreSQL adapter 时提供类型安全的 schema 和查询边界 |
pg | 切换到实际 PostgreSQL 连接 adapter 时使用的驱动程序 |
Smithery CLI | 提供开发、验证 MCP 服务器的运行路径 |
Docker | 固定本地和部署环境中的 Node/MCP 运行条件 |
chokidar·fast-glob | 负责 Markdown vault 变更检测和文件搜索 |
gray-matter·marked | 将 Markdown frontmatter 和正文作为知识 chunk 处理 |
dotenv | 将本地环境配置与代码分离 |
并非所有依赖项都是向量搜索的核心。有些是用于 Obsidian、Notion 集成的辅助技术,搜索路径的核心是 MCP SDK → Vector Service → Repository → pgvector。
选择技术的原因
技术 | 选择原因 |
Obsidian Markdown | 原文是纯文本文件,所有权和可移植性高,不将知识原文依赖到特定 SaaS |
MCP | 无需为每个 LLM 客户端编写单独的集成代码,以标准接口暴露相同的知识工具 |
TypeScript | 与 MCP SDK 的连接自然,并可用类型管理工具输入、输出边界 |
PostgreSQL | 在单一存储中一致地管理文档元数据、状态和搜索结果,并确保生产转换路径 |
pgvector | 无需添加独立向量数据库,即可在 PostgreSQL 中同时处理原文元数据和向量搜索 |
Notion adapter | 不是为了将 Notion 作为原始存储,而是展示在需要时将知识片段同步到外部工作区的边界 |
Docker | 在本地和部署环境中统一 PostgreSQL、MCP 的运行条件 |
核心不是接入大量工具。原文以 Markdown 保存,搜索用的派生数据放在 PostgreSQL/pgvector 中,仅通过 MCP 向 LLM 提供所需功能。
因此,本项目不是将 Obsidian、Notion、PostgreSQL 都作为原始来源的系统。
Obsidian Markdown:原始知识
PostgreSQL/pgvector:搜索用派生索引
Notion:可选的外部同步目标
MCP:LLM 访问边界
设计要点
保留原始 Markdown
分离 MCP 工具与服务层
NoteChunk领域模型和VectorRepository端口可替换为 PostgreSQL/pgvector 的存储边界
排除实际个人 vault 和凭据
本项目是用于确认 MCP 和知识搜索结构的公开示例。
许可证
MIT
This server cannot be deployed
Maintenance
Related MCP Connectors
Personal knowledge base MCP server with semantic search, auto-categorization, metadata extraction
Markdown-based note-taking with a hosted MCP server. Your notes serve you and your AI.
Token-efficient MCP memory for Markdown vaults. Tiered search, GraphRAG, AI memories.
Search and reason over your Obsidian-style Markdown vault, right from ChatGPT.
Related MCP Servers
- AlicenseNot gradedqualityAmaintenanceConnects AI assistants to an Obsidian vault as a semantic knowledge graph, enabling graph navigation, semantic search, and content operations through MCP.13 npm469MIT
- FlicenseNot gradedqualityDmaintenanceEnables semantic search and related-note discovery across an Obsidian vault via MCP, providing RAG context blocks for natural language queries.-
- FlicenseAqualityCmaintenanceExposes 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.71-
- AlicenseBqualityAmaintenanceEnables 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.431MIT