Skip to main content
Glama

NoteHarbor MCP

Obsidian 노트를 MCP와 GraphQL로 조회하고 PostgreSQL/pgvector로 확장하는 Python 예제입니다. 개인 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

공개 샘플 3가지

  1. MCP: upsert_vector, search_vectors 도구

  2. GraphQL: indexChunk Mutation, vectorSearch Query

  3. PostgreSQL/pgvector: SQLAlchemy 모델, Repository 포트, Docker 개발 환경

운영 서비스가 아니라 구조를 확인하기 위한 예제입니다. 기본 Repository는 메모리에서 동작하고, PostgreSQL/pgvector로 옮길 지점과 SQL 예제를 함께 제공합니다.

빠른 시작

uv sync

MCP 샘플 실행:

uv run noteharbor

Docker로 PostgreSQL 띄우기

docker compose up -d postgres

docker-compose.ymlpgvector/pgvector 이미지와 Python MCP용 개발용 PostgreSQL을 정의합니다.

# PostgreSQL만
docker compose up -d postgres

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

POSTGRES_URL은 실제 DB 연결을 붙일 때 사용할 설정 자리입니다. 현재 기본 실행은 목업입니다.

GraphQL 샘플

GraphQL 스키마는 noteharbor/api/graphql_schema.py에 있습니다. 외부 ASGI 서버에 get_schema() 결과를 마운트할 수 있습니다.

예시 작업:

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 indexChunkVectorService.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 adapter에서는 다음과 같은 세션 연산으로 MockPostgresVectorRepository를 교체합니다.

실제 ORM 흐름을 코드로 보고 싶다면 noteharbor/infrastructure/postgres/sqlalchemy_repository.py를 확인하면 됩니다. 이 adapter는 같은 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을 직접 다루지 않고 MCP/GraphQL → VectorService → VectorRepository → SQLAlchemy/pgvector 순서로 CRUD와 검색을 넘깁니다.

목적

이 저장소는 하나의 Markdown 원본을 MCP와 GraphQL 양쪽에서 같은 벡터 검색 서비스로 조회하는 Python 아키텍처 샘플입니다.

특정 embedding 업체나 벡터 DB를 정해 두기보다, 교체 가능한 지점을 분명히 두는 데 초점을 맞췄습니다.

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.pyMockPostgresVectorRepository는 이 흐름을 메모리에서 재현합니다. 실제 PostgreSQL adapter에서는 embedding <=> :query_embedding으로 cosine distance를 계산하고, 1 - distance를 score로 반환합니다.

왜 PostgreSQL + pgvector인가

NoteChunk는 벡터만 갖는 값이 아니라 원본 경로·chunk 순번·본문·메타데이터를 함께 갖습니다. PostgreSQL과 pgvector를 조합하면 관계형 조건과 벡터 유사도 검색을 한 저장소와 SQL 경계 안에서 다룰 수 있습니다.

  • PostgreSQL: 원문 메타데이터, 상태, 동기화 정보와 트랜잭션 관리

  • pgvector: vector 컬럼, cosine distance(<=>), HNSW 인덱스

  • Docker: 개발 환경에서 pgvector 실행 조건 고정

  • SQLAlchemy Repository: 저장소 교체 지점

검색 규모가 커지면 전용 벡터 DB가 더 적합할 수 있습니다. 여기서는 문서 정보와 벡터 검색을 한 저장소에서 다루는 흐름을 보여줍니다.

양자화 범위

현재 Python 레포에는 양자화 구현을 넣지 않았습니다. 입력 embedding은 float 값 그대로 받아 검색하며, 이는 모델 중립적인 Repository 경계를 먼저 보여주기 위한 선택입니다.

양자화를 추가하는 경우에는 EmbeddingQuantizer 포트를 별도로 두고 float32 → INT8/float16 → 저장·복원 단계를 indexing worker와 Repository adapter 사이에 배치합니다. 양자화 방식은 검색 품질·메모리·지연시간을 측정한 뒤 결정해야 하므로, 이 샘플에서는 구현했다고 과장하지 않습니다.

두 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 adapter 경계를 두기 위해

psycopg

Python에서 PostgreSQL 연결을 담당하기 위해

pgvector Python package

SQLAlchemy 모델에서 PostgreSQL vector 타입을 표현하기 위해

Strawberry GraphQL

같은 VectorService를 GraphQL Query/Mutation으로 노출하는 예시를 만들기 위해

Docker Compose

pgvector가 활성화된 PostgreSQL과 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

A
license - permissive license
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

  • F
    license
    Not graded
    quality
    D
    maintenance
    Provides tools for ingesting documents into a local vector database and retrieving relevant information via semantic search, enabling retrieval-augmented generation for MCP clients.
    6
  • A
    license
    Not graded
    quality
    B
    maintenance
    Indexes local Markdown/text files into a SQLite database with vector embeddings and provides MCP tools for semantic search without cloud dependencies.
    GPL 3.0
  • A
    license
    Not graded
    quality
    A
    maintenance
    Provides MCP tools for semantic search over personal knowledge sources using pluggable embeddings and local vector indexing.
    1
    MIT

View all related MCP servers

Related MCP Connectors

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

  • Token-efficient MCP memory for Markdown vaults. Tiered search, GraphRAG, AI memories.

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-python'

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