NoteHarbor MCP
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가지
MCP:
upsert_vector,search_vectors도구GraphQL:
indexChunkMutation,vectorSearchQueryPostgreSQL/pgvector: SQLAlchemy 모델, Repository 포트, Docker 개발 환경
운영 서비스가 아니라 구조를 확인하기 위한 예제입니다. 기본 Repository는 메모리에서 동작하고, PostgreSQL/pgvector로 옮길 지점과 SQL 예제를 함께 제공합니다.
빠른 시작
uv syncMCP 샘플 실행:
uv run noteharborDocker로 PostgreSQL 띄우기
docker compose up -d postgresdocker-compose.yml은 pgvector/pgvector 이미지와 Python MCP용 개발용 PostgreSQL을 정의합니다.
# PostgreSQL만
docker compose up -d postgres
# Python MCP까지
docker compose up --build python-mcpPOSTGRES_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, GraphQLindexChunk→VectorService.index_chunk()Read: MCP
get_chunk·list_chunks, GraphQLnoteChunk·noteChunksUpdate: MCP·GraphQL
updateChunk→ 기존 레코드를 읽은 뒤 변경 필드만 반영Delete: MCP·GraphQL
deleteChunkSearch: MCP
search_vectors, GraphQLvectorSearch
현재 어댑터는 동작 확인을 위한 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.py의 MockPostgresVectorRepository는 이 흐름을 메모리에서 재현합니다. 실제 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
This server cannot be installed
Maintenance
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
- FlicenseNot gradedqualityDmaintenanceProvides tools for ingesting documents into a local vector database and retrieving relevant information via semantic search, enabling retrieval-augmented generation for MCP clients.6
- FlicenseNot gradedqualityBmaintenanceEnables semantic search over personal markdown notes by indexing them into a vector database and exposing search, reindex, and status tools via MCP.
- AlicenseNot gradedqualityBmaintenanceIndexes local Markdown/text files into a SQLite database with vector embeddings and provides MCP tools for semantic search without cloud dependencies.GPL 3.0
- AlicenseNot gradedqualityAmaintenanceProvides MCP tools for semantic search over personal knowledge sources using pluggable embeddings and local vector indexing.1MIT
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.
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
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