NoteHarbor MCP
NoteHarbor — TypeScript
Пример на TypeScript, который подключает Obsidian Markdown к инструментам MCP и расширяет его поиском через PostgreSQL/pgvector.
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: детерминированный демонстрационный embedding-провайдер без API-ключа
indexingPipeline.ts: связывает создание chunk, embedding и сохранение
knowledge.ts: порты
EmbeddingProviderиVectorRepositoryindex.ts: регистрирует инструменты MCP
index_note,upsert_vector,search_vectors
Демонстрационный провайдер — это детерминированная реализация для проверки потока, а не модель для качественного смыслового поиска. В реальном сервисе к тому же порту 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Схема хранит
embedding_int8,embedding_scale,embedding_zero_pointвместе с исходным embeddingСейчас поиск работает с восстановленным float-вектором, а ANN-индекс на основе квантования добавляется при выборе реального адаптера
Реализация находится в 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.
Текущая реализация — это заглушка (mock) для проверки потока. Она не гарантирует качество смыслового поиска или производительность квантования. В реальной эксплуатации к порту embedding-провайдера подключается настоящая модель, а к порту репозитория — pgvector-адаптер.
Проектирование векторной БД
Единица хранения векторов — NoteChunk.
поле | значение |
| идентификатор из исходного пути и номера chunk |
| исходный путь Obsidian Markdown |
| номер chunk внутри документа |
| текст, который возвращается в результатах поиска |
| вектор, созданный внешней моделью embedding |
| расширяемые данные: теги, статус, атрибуты и т.д. |
Рабочий пример SQL-схемы находится в src/infrastructure/postgres/schema.sql. В базовом примере используется 1536-мерный embedding и HNSW-индекс для cosine distance. При реальном использовании размерность подстраивается под конкретную модель 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.
Create/Upsert:
upsert_vector,index_note→VectorService.indexChunk()Read:
get_chunk,list_chunksUpdate:
update_chunk→ читает существующий chunk, обновляет поля и квантованное представлениеDelete:
delete_chunkSearch:
search_vectors,search_knowledge
Сейчас адаптером выступает Map в памяти. В production внутренности репозитория заменяются на 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.
Что входит в пример
чтение и поиск по Obsidian Markdown
инструменты MCP:
index_note,search_knowledge,upsert_vector,search_vectorsслои:
MCP → Service → Repository → pgvectorсхема
note_chunksна основе Drizzle ORMсхема PostgreSQL/pgvector и SQL для поиска
граница синхронизации с Notion
dev-сервер Smithery и пример запуска в Docker
Базовая конфигурация запускается без личных данных и без внешних учётных данных — используется заглушка в памяти. При подключении PostgreSQL Map-реализация в src/infrastructure/postgres/postgresVectorRepository.ts заменяется адаптером на Drizzle/pg. README и схема показывают, где именно происходит этот переход.
Версия на 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: float-вектор для cosine-поискаembedding_int8: квантованное представление для экономии памяти и трафика
Конкретные причины выбора pgvector:
Совместимость данных: векторная близость и условия по
source_path, тегам, статусу комбинируются в одном SQLСогласованность: метаданные исходного текста и поисковый индекс управляются в одной транзакционной границе
Простота эксплуатации: приложению не нужно отдельно обслуживать PostgreSQL и отдельную векторную БД
Возможности поиска: cosine distance (
<=>) и HNSW-индекс доступны как расширение PostgreSQLПуть масштабирования: сначала единое хранилище, а при росте можно выделить отдельный поисковый адаптер
При большом масштабе поиска выделенная векторная БД может подойти лучше. Здесь главное — показать поток, в котором исходный текст, метаданные и векторный поиск находятся в одной границе приложения.
Как работает поиск
Поиск не сравнивает исходные строки напрямую. Вопрос и chunk заметки помещаются в одно и то же пространство embedding, после чего сравнивается расстояние.
사용자 질문
→ query embedding 생성
→ INT8 양자화 후 복원
→ PostgreSQL/pgvector cosine distance 검색
→ 가까운 NoteChunk 반환
→ sourcePath·content·metadata와 함께 MCP 응답Рабочий пример — инструмент MCP search_knowledge.
index_noteразбивает Markdown на chunk и сохраняет embedding каждого chunk.Пользователь отправляет запрос на естественном языке
query.Тот же
EmbeddingProviderсоздаёт embedding запроса.Запрос квантуется и восстанавливается тем же способом, что и сохранённые векторы.
VectorRepository.search()сортирует chunk по cosine similarity.В результатах — исходный путь, содержимое chunk, метаданные и score.
search_vectors — низкоуровневый инструмент, который принимает уже готовый embedding. search_knowledge — прикладной инструмент, который связывает естественный вопрос с результатами поиска.
Сейчас мок-репозиторий вычисляет cosine similarity в памяти через Map. После перехода на PostgreSQL-адаптер за тем же портом будут использоваться оператор <=> из pgvector и поиск с LIMIT.
Дополнительные технологии и зачем они здесь
технология | зачем используется |
Node.js ESM | чтобы просто запускать TypeScript MCP-пример в текущей среде выполнения Node |
MCP SDK | чтобы регистрировать инструменты |
Zod | для проверки входных данных MCP и конфигурации во время выполнения |
Drizzle ORM | чтобы при подключении PostgreSQL-адаптера получить типобезопасную границу схемы и запросов |
pg | драйвер для перехода на реальный адаптер подключения PostgreSQL |
Smithery CLI | предоставляет путь запуска для разработки и проверки MCP-сервера |
Docker | фиксирует условия запуска Node/MCP в локальной и развёрнутой среде |
chokidar·fast-glob | отвечает за отслеживание изменений в vault Markdown и обход файлов |
gray-matter·marked | для работы с frontmatter Markdown и телом заметки как с единицами знания |
dotenv | чтобы отделить локальные настройки окружения от кода |
Не все зависимости — ядро векторного поиска. Часть — вспомогательные технологии для интеграции с Obsidian и Notion. Центр поискового пути: MCP SDK → Vector Service → Repository → pgvector.
Почему выбраны эти технологии
технология | причина выбора |
Obsidian Markdown | исходник — обычные текстовые файлы: высокая управляемость и переносимость, знание не привязано к конкретному SaaS |
MCP | чтобы не писать отдельную интеграцию под каждого LLM-клиента, а открыть один и тот же инструмент знаний через стандартный интерфейс |
TypeScript | естественная связка с MCP SDK, границы входа и выхода инструментов управляются типами |
PostgreSQL | чтобы согласованно хранить метаданные документов, статусы и результаты поиска в одном хранилище и иметь путь к production |
pgvector | чтобы без отдельной векторной БД работать с метаданными исходного текста и векторным поиском внутри PostgreSQL |
Notion adapter | чтобы показать границу: Notion не является исходным хранилищем, но при необходимости фрагменты знаний синхронизируются во внешний workspace |
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
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.5 npm466MIT
- FlicenseNot gradedqualityCmaintenanceEnables 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