Skip to main content
Glama

NoteHarbor MCP

Пример на Python для получения заметок Obsidian через MCP и GraphQL, расширяемый с помощью PostgreSQL/pgvector. Личные 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

Три публичных примера

  1. MCP: инструменты upsert_vector, search_vectors

  2. GraphQL: мутация indexChunk, запрос vectorSearch

  3. PostgreSQL/pgvector: модели SQLAlchemy, порт Repository, среда разработки Docker

Это пример для изучения структуры, а не рабочий сервис. Базовый Repository работает в памяти; также приведены точки перехода на PostgreSQL/pgvector и примеры SQL.

Быстрый старт

uv sync

Запуск примера MCP:

uv run noteharbor

Запуск PostgreSQL через Docker

docker compose up -d postgres

Файл docker-compose.yml определяет образ pgvector/pgvector и PostgreSQL для разработки Python MCP.

# PostgreSQL만
docker compose up -d postgres

# Python MCP까지
docker compose up --build python-mcp

POSTGRES_URL — это место для настройки подключения к реальной БД. Сейчас запуск по умолчанию выполняется на моке.

Пример GraphQL

Схема GraphQL находится в 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, GraphQL indexChunk → VectorService.index_chunk()

  • Read: MCP get_chunk·list_chunks, GraphQL noteChunk·noteChunks

  • Update: MCP·GraphQL updateChunk → после чтения существующей записи обновляются только изменённые поля

  • Delete: MCP·GraphQL deleteChunk

  • Search: MCP search_vectors, GraphQL vectorSearch

Текущий адаптер — это in-memory мок для проверки работы. Реальное хранение размещается за границей 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 напрямую, а передаёт CRUD и поиск по цепочке MCP/GraphQL → VectorService → VectorRepository → SQLAlchemy/pgvector.

Назначение

Этот репозиторий — образец Python-архитектуры, в которой один исходный Markdown-документ запрашивается через один и тот же сервис векторного поиска как через MCP, так и через GraphQL.

Вместо того чтобы заранее выбирать конкретного поставщика эмбеддингов или векторную БД, фокус сделан на явных точках замены.

MCP / GraphQL API
  → VectorService
  → VectorRepository port
  → 현재: in-memory MockPostgresVectorRepository
  → 전환: PostgreSQL + pgvector

Ответственность за векторизацию и поиск

Текущий Python-пример не создаёт эмбеддинги напрямую. Мутация indexChunk и инструмент upsert_vector принимают и сохраняют эмбеддинги list[float], созданные извне.

Причины выноса генерации эмбеддингов:

  • чтобы не привязывать модель эмбеддингов к одной из: OpenAI, Voyage или локальная модель

  • чтобы исключить API-ключи и личные данные из публичного примера

  • чтобы разделить ответственность API-слоя и хранилища поиска

  • чтобы в реальном сервисе конвейер чанкинга и генерации эмбеддингов можно было заменить отдельным worker'ом

Поиск выполняется в следующем порядке:

사용자 query embedding
  → GraphQL vectorSearch 또는 MCP search_vectors
  → VectorService.search()
  → Repository.search()
  → cosine similarity 계산
  → score가 높은 NoteChunk 반환

Сейчас MockPostgresVectorRepository в repository.py воспроизводит этот поток в памяти. В реальном адаптере PostgreSQL косинусное расстояние вычисляется через embedding <=> :query_embedding, а 1 - distance возвращается как score.

Почему PostgreSQL + pgvector

NoteChunk — это не только значение с вектором, но и запись, содержащая исходный путь, порядковый номер чанка, текст и метаданные. Сочетание PostgreSQL и pgvector позволяет обрабатывать реляционные условия и поиск по векторной схожести в одном хранилище и в границах SQL.

  • PostgreSQL: метаданные исходников, состояние, информация о синхронизации и управление транзакциями

  • pgvector: колонка vector, косинусное расстояние (<=>), индекс HNSW

  • Docker: фиксирует условия запуска pgvector в среде разработки

  • SQLAlchemy Repository: точка замены хранилища

При росте масштаба поиска может оказаться более подходящей специализированная векторная БД. Здесь показан поток, в котором информация о документах и векторный поиск обрабатываются в одном хранилище.

Область квантования

В текущий Python-репозиторий реализация квантования не включена. Входные эмбеддинги принимаются как есть — значениями float — и используются для поиска; это выбор, сделанный, чтобы в первую очередь показать модель-нейтральную границу Repository.

Если квантование добавляется, предусматривается отдельный порт EmbeddingQuantizer, а этапы float32 → INT8/float16 → сохранение/восстановление размещаются между indexing worker'ом и адаптером Repository. Поскольку способ квантования следует выбирать после измерения качества поиска, памяти и задержки, этот пример не утверждает, что оно уже реализовано.

Связь двух примеров NoteHarbor

  • noteharbor-mcp: инструменты TypeScript MCP и поток эмбеддингов и 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

для обеспечения подключения к PostgreSQL из Python

pgvector Python package

для представления типа vector PostgreSQL в моделях SQLAlchemy

Strawberry GraphQL

для создания примера, в котором один и тот же VectorService доступен через GraphQL Query/Mutation

Docker Compose

для совместного воспроизведения среды разработки PostgreSQL с включённым pgvector и 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

Related MCP Connectors

Related MCP Servers