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: second-brain-mcp

实际向量化流程

upsert_vector 是保存已生成 embedding 的工具。Markdown 转换为 chunk 和向量的过程可以在 index_note 中查看。

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

主要代码分布在以下文件中。

演示 provider 是用于确认流程的确定性实现。它并不是提供基于语义的搜索质量的模型,在实际服务中,可以在同一个 EmbeddingProvider 端口上接入外部或本地模型。

量化在 embedding 生成之后应用。

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

示例使用对称标量 INT8 量化。

  • 值范围:[-1, 1]

  • 量化范围:[-127, 127]

  • scale1 / 127

  • zeroPoint0

  • 定义 schema 以与原始 embedding 一起存储 embedding_int8embedding_scaleembedding_zero_point

  • 当前参考搜索使用可恢复的 float 向量,量化 ANN 索引在实际选择 adapter 时添加

实现位于 quantizer.tsindexingPipeline.ts 中。index_note 响应中也包含 embedding 维度和量化位数。

为什么这样设计

NoteHarbor 是一个将 Obsidian Markdown 转换为可搜索的知识单元,并通过 MCP 工具提供该功能的示例。

仅靠简单的字符串搜索很难找到表达方式不同的相关内容。因此,将笔记拆分为较小的 chunk,并将每个 chunk 转换为 embedding,从而可以搜索语义相近的内容。

量化是为了将 embedding 以更小的表示形式保存而做出的选择。

  • 减少内存和存储空间

  • 减少向量传输量

  • 有利于大规模知识库中的缓存和批处理

  • 但精度可能低于原始 float

因此,各组成部分的角色划分如下。

  1. Embedding:将文本的语义表示为数值向量

  2. Quantization:降低向量的精度以节省存储成本

  3. Vector search:查找相近的向量并返回相关 chunk

  4. MCP:将这些功能暴露为 LLM 客户端可调用的工具

本项目选择 INT8 的原因是更容易用代码解释量化原理和存储形式。在实际服务中,应在测量搜索质量、内存节省和延迟之后,从 float32、float16、INT8、binary 中选择一种。

当前实现是用于确认流程的模拟(mockup)。它不保证基于语义的搜索质量或量化性能,在实际运营中需要接入 embedding provider 和 pgvector adapter。

向量数据库设计

向量存储单元是 NoteChunk

字段

含义

id

由原始路径和 chunk 序号生成的标识符

sourcePath

Obsidian Markdown 原始路径

chunkIndex

文档内 chunk 的序号

content

作为搜索结果返回的文本

embedding

外部 embedding 模型生成的向量

metadata

标签、状态、原始属性等扩展信息

可执行的 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/Upsertupsert_vectorindex_noteVectorService.indexChunk()

  • Readget_chunklist_chunks

  • Updateupdate_chunk → 读取现有 chunk,并同时更新变更字段和量化表示

  • Deletedelete_chunk

  • Searchsearch_vectorssearch_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_notesearch_knowledgeupsert_vectorsearch_vectors MCP 工具

  • MCP → Service → Repository → pgvector 分层

  • 基于 Drizzle ORM 的 note_chunks schema 映射

  • 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 dev

Docker:

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 工具。

  1. index_note 将 Markdown 拆分为 chunk,并保存每个 chunk 的 embedding。

  2. 用户发送自然语言 query

  3. 使用同一个 EmbeddingProvider 生成 query embedding。

  4. 以与存储向量相同的方式对 query 进行量化和恢复。

  5. VectorRepository.search() 根据 cosine similarity 对相近的 chunk 进行排序。

  6. 搜索结果包含原始路径、chunk 内容、metadata 和 score。

search_vectors 是直接接收已生成 embedding 的低层工具,而 search_knowledge 是从自然语言问题到搜索结果的应用程序级工具。

当前模拟 Repository 在内存 Map 中计算 cosine similarity。切换到 PostgreSQL adapter 后,将在同一个端口后面使用 pgvector 的 <=> 运算和 LIMIT 搜索。

附加技术与投入原因

技术

投入原因

Node.js ESM

以当前 Node 运行时方式简单运行 TypeScript MCP 示例

MCP SDK

index_notesearch_knowledgeupsert_vectorsearch_vectors 工具注册为标准 MCP 服务器

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

F
license - not found
Not graded
quality - not tested
B
maintenance

Maintenance

Maintainers
Response time
Release cycle
Releases (12mo)
Commit activity

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Servers

View all related MCP servers

Related MCP Connectors

  • 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.

View all MCP Connectors

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/kris-atelier/noteharbor-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server