NoteHarbor MCP
Provides tools for indexing and searching Obsidian notes (Markdown files) by storing chunked content with embeddings, allowing retrieval via vector search.
Uses PostgreSQL with pgvector as the vector storage backend for persisting note chunks and performing similarity search, with a mock in-memory repository for development.
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@NoteHarbor MCPsearch my notes for 'pgvector indexing' and show the top matches"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
NoteHarbor MCP
A Python example that queries Obsidian notes via MCP and GraphQL and extends them with PostgreSQL/pgvector. It does not include personal vaults, real documents, or API keys.
The original source of NoteHarbor is Markdown files. Only the data needed for search is created separately, and the originals are preserved as-is.
Principles of this project
Local first: Original Markdown stays local, and external services are optional connections.
Source preserving: Search, embedding, and sync results do not replace the originals.
Privacy by boundary: Real vaults, personal records, and API keys stay outside the public project.
Model neutral: Embedding generators and Vector DBs are not tied to any specific vendor.
Small and replaceable: Adapters and service layers are kept small.
Related MCP server: Notes RAG MCP Server
3 public samples
MCP:
upsert_vector,search_vectorstoolsGraphQL:
indexChunkMutation,vectorSearchQueryPostgreSQL/pgvector: SQLAlchemy models, Repository ports, Docker development environment
These are examples for understanding the structure, not production services. The default Repository runs in memory, and the point where you can switch to PostgreSQL/pgvector and SQL examples are provided together.
Quick start
uv syncRun the MCP sample:
uv run noteharborStarting PostgreSQL with Docker
docker compose up -d postgresdocker-compose.yml defines the pgvector/pgvector image and a development PostgreSQL for Python MCP.
# PostgreSQL만
docker compose up -d postgres
# Python MCP까지
docker compose up --build python-mcpPOSTGRES_URL is a configuration placeholder for when you attach a real DB connection. The current default run is a mock.
GraphQL sample
The GraphQL schema is in noteharbor/api/graphql_schema.py. You can mount the get_schema() result on an external ASGI server.
Example operations:
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 and ORM style boundaries
The Repository handles the full lifecycle of NoteChunk, from creation to deletion, not just search.
Create/Upsert: MCP
upsert_vector, GraphQLindexChunk→VectorService.index_chunk()Read: MCP
get_chunk·list_chunks, GraphQLnoteChunk·noteChunksUpdate: MCP·GraphQL
updateChunk→ reads the existing record, then applies only the changed fieldsDelete: MCP·GraphQL
deleteChunkSearch: MCP
search_vectors, GraphQLvectorSearch
The current adapter is an in-memory mock for verifying behavior. The actual storage method sits behind the SQLAlchemy ORM boundary. In the real PostgreSQL adapter, MockPostgresVectorRepository is replaced with session operations like the following.
If you want to see the real ORM flow in code, check noteharbor/infrastructure/postgres/sqlalchemy_repository.py. This adapter implements the same VectorRepository port while using Session.get, select, update, and delete. The default run is still a mock, so you can read and test the example without a PostgreSQL connection.
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))The API does not handle SQL directly; it passes CRUD and search in the order MCP/GraphQL → VectorService → VectorRepository → SQLAlchemy/pgvector.
Purpose
This repository is a Python architecture sample that queries a single Markdown source through the same vector search service from both MCP and GraphQL.
Rather than fixing a specific embedding vendor or vector DB, the focus is on making the replaceable points clear.
MCP / GraphQL API
→ VectorService
→ VectorRepository port
→ 현재: in-memory MockPostgresVectorRepository
→ 전환: PostgreSQL + pgvectorResponsibilities of vectorization and search
The current Python sample does not generate embeddings directly. The indexChunk mutation and upsert_vector tool receive externally generated list[float] embeddings and store them.
The reasons for separating embedding generation are as follows.
To avoid fixing the embedding model to one of OpenAI, Voyage, or a local model
To exclude API keys and personal data from the public sample
To separate the responsibilities of the API layer and the search repository
To allow replacing the chunking and embedding batch pipeline with a separate worker in a real service
Search follows this order:
사용자 query embedding
→ GraphQL vectorSearch 또는 MCP search_vectors
→ VectorService.search()
→ Repository.search()
→ cosine similarity 계산
→ score가 높은 NoteChunk 반환The current MockPostgresVectorRepository in repository.py reproduces this flow in memory. In the real PostgreSQL adapter, cosine distance is calculated with embedding <=> :query_embedding, and 1 - distance is returned as the score.
Why PostgreSQL + pgvector
NoteChunk is not just a value with a vector; it also carries the original path, chunk sequence number, body, and metadata. Combining PostgreSQL and pgvector lets you handle relational conditions and vector similarity search within a single repository and SQL boundary.
PostgreSQL: original metadata, status, sync information, and transaction management
pgvector:
vectorcolumn, cosine distance (<=>), HNSW indexDocker: fixes the pgvector runtime conditions in the development environment
SQLAlchemy Repository: the repository replacement point
As search scale grows, a dedicated vector DB may be more suitable. Here, the flow of handling document information and vector search in a single repository is shown.
Quantization scope
The current Python repo does not include a quantization implementation. Input embeddings are received and searched as float values as-is, which is a choice to first show a model-neutral Repository boundary.
If quantization is added, an EmbeddingQuantizer port would be placed separately, with the float32 → INT8/float16 → store/restore steps between the indexing worker and the Repository adapter. Since the quantization method should be decided after measuring search quality, memory, and latency, this sample does not overstate that it is implemented.
Relationship between the two NoteHarbor samples
noteharbor-mcp: TypeScript MCP tools with embedding and INT8 quantization flow
noteharbor-python: Python MCP·GraphQL API with VectorService/Repository boundaries
Both repos are examples of the same idea expressed in different languages and API approaches, and they do not include real personal vaults or production data.
Additional technologies and reasons for inclusion
Technology | Reason for inclusion |
uv | To quickly reproduce Python dependencies, virtual environments, and lock files, and to use the same installation path in Docker |
FastMCP | To show the connection between Python functions and MCP tools concisely and clearly |
Pydantic | To validate input models and configuration at the API boundary |
SQLAlchemy | To place a PostgreSQL adapter boundary so the Repository is not tied to a specific SQL execution method |
psycopg | To handle PostgreSQL connections from Python |
pgvector Python package | To represent the PostgreSQL vector type in SQLAlchemy models |
Strawberry GraphQL | To create an example that exposes the same VectorService as GraphQL Query/Mutation |
Docker Compose | To reproduce the development environment of PostgreSQL with pgvector enabled and Python MCP together |
Each technology was chosen to explain the division of roles among API, service, repository, and development environment.
Project structure
.
├── 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.tomlLicense
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