NoteHarbor MCP
NoteHarbor MCP
Ejemplo en Python para consultar notas de Obsidian con MCP y GraphQL y ampliarlo con PostgreSQL/pgvector. No incluye vaults personales, documentos reales ni claves de API.
El original de NoteHarbor son archivos Markdown. Solo se crean por separado los datos necesarios para la búsqueda y el original se conserva tal cual.
Criterios de este proyecto
Local first: el Markdown original permanece local y los servicios externos quedan como opción conectable.
Source preserving: los resultados de búsqueda, embedding y sincronización no reemplazan el original.
Privacy by boundary: el vault real, los registros personales y las claves de API quedan fuera del proyecto público.
Model neutral: el generador de embeddings y la base de datos vectorial no se fijan a un proveedor concreto.
Small and replaceable: los adaptadores y la capa de servicios se mantienen pequeños.
Related MCP server: Notes RAG MCP Server
Tres muestras públicas
MCP: herramientas
upsert_vector,search_vectorsGraphQL: Mutation
indexChunk, QueryvectorSearchPostgreSQL/pgvector: modelos SQLAlchemy, puertos de Repository, entorno de desarrollo Docker
Es un ejemplo para comprobar la estructura, no un servicio de producción. El Repository básico funciona en memoria y se incluyen los puntos de migración a PostgreSQL/pgvector junto con ejemplos de SQL.
Inicio rápido
uv syncEjecución de la muestra MCP:
uv run noteharborLevantar PostgreSQL con Docker
docker compose up -d postgresdocker-compose.yml define la imagen pgvector/pgvector y un PostgreSQL de desarrollo para el MCP de Python.
# PostgreSQL만
docker compose up -d postgres
# Python MCP까지
docker compose up --build python-mcpPOSTGRES_URL es el lugar de configuración que se usará al conectar la base de datos real. La ejecución predeterminada actual es un mock.
Muestra de GraphQL
El esquema GraphQL está en noteharbor/api/graphql_schema.py. El resultado de get_schema() se puede montar en un servidor ASGI externo.
Ejemplo de operación:
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
}
}Límites entre CRUD y el estilo ORM
El Repository se encarga de todo el ciclo de vida de NoteChunk, desde la creación hasta la eliminación, no solo de la búsqueda.
Create/Upsert: MCP
upsert_vector, GraphQLindexChunk→VectorService.index_chunk()Read: MCP
get_chunk·list_chunks, GraphQLnoteChunk·noteChunksUpdate: MCP·GraphQL
updateChunk→ lee el registro existente y solo aplica los campos modificadosDelete: MCP·GraphQL
deleteChunkSearch: MCP
search_vectors, GraphQLvectorSearch
El adaptador actual es un mock en memoria para verificar el funcionamiento. El almacenamiento real queda detrás del límite del ORM de SQLAlchemy. En el adaptador real de PostgreSQL, MockPostgresVectorRepository se sustituye por operaciones de sesión como las siguientes.
Si quieres ver el flujo ORM real en código, consulta noteharbor/infrastructure/postgres/sqlalchemy_repository.py. Este adaptador implementa el mismo puerto VectorRepository y usa Session.get, select, update, delete. La ejecución predeterminada sigue siendo un mock, por lo que puedes leer y probar el ejemplo sin conexión a 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))La API no maneja SQL directamente; pasa el CRUD y la búsqueda en el orden MCP/GraphQL → VectorService → VectorRepository → SQLAlchemy/pgvector.
Propósito
Este repositorio es una muestra de arquitectura Python que consulta un mismo Markdown original desde MCP y GraphQL mediante el mismo servicio de búsqueda vectorial.
En lugar de fijar un proveedor de embeddings o una base de datos vectorial concretos, el foco está en dejar claros los puntos de sustitución.
MCP / GraphQL API
→ VectorService
→ VectorRepository port
→ 현재: in-memory MockPostgresVectorRepository
→ 전환: PostgreSQL + pgvectorResponsabilidades de la vectorización y la búsqueda
La muestra actual de Python no genera embeddings directamente. La mutation indexChunk y la herramienta upsert_vector reciben y almacenan embeddings list[float] generados externamente.
Las razones para separar la generación de embeddings son las siguientes:
no fijar el modelo de embeddings entre OpenAI, Voyage o un modelo local;
excluir claves de API y datos personales de la muestra pública;
separar las responsabilidades de la capa de API y del almacén de búsqueda;
en un servicio real, sustituir el pipeline por lotes de chunking y embedding por un worker independiente.
La búsqueda sigue el siguiente orden:
사용자 query embedding
→ GraphQL vectorSearch 또는 MCP search_vectors
→ VectorService.search()
→ Repository.search()
→ cosine similarity 계산
→ score가 높은 NoteChunk 반환El MockPostgresVectorRepository actual en repository.py reproduce este flujo en memoria. En el adaptador real de PostgreSQL, la distancia coseno se calcula con embedding <=> :query_embedding y se devuelve 1 - distance como score.
Por qué PostgreSQL + pgvector
NoteChunk no es solo un valor con vector, sino que incluye también la ruta original, el número de orden del chunk, el cuerpo y los metadatos. Al combinar PostgreSQL y pgvector, las condiciones relacionales y la búsqueda por similitud vectorial pueden tratarse dentro de un mismo almacén y del límite de SQL.
PostgreSQL: metadatos del original, estado, información de sincronización y gestión de transacciones.
pgvector: columna
vector, distancia coseno (<=>), índice HNSW.Docker: fija las condiciones de ejecución de pgvector en el entorno de desarrollo.
SQLAlchemy Repository: punto de sustitución del almacén.
Cuando la escala de búsqueda crece, una base de datos vectorial dedicada puede ser más adecuada. Aquí se muestra el flujo que maneja la información documental y la búsqueda vectorial en un mismo almacén.
Alcance de la cuantización
El repositorio Python actual no incluye implementación de cuantización. Los embeddings de entrada se reciben y se buscan como valores float, una elección para mostrar primero un límite de Repository neutral respecto al modelo.
Si se añade cuantización, se define un puerto EmbeddingQuantizer aparte y se coloca el paso float32 → INT8/float16 → almacenar/restaurar entre el worker de indexación y el adaptador del Repository. Como el método de cuantización debe decidirse tras medir calidad de búsqueda, memoria y latencia, esta muestra no exagera diciendo que está implementado.
Relación entre las dos muestras de NoteHarbor
noteharbor-mcp: herramientas MCP en TypeScript y flujo de embeddings y cuantización INT8
noteharbor-python: API MCP·GraphQL en Python y límites de VectorService/Repository
Ambos repositorios son ejemplos que desarrollan la misma idea en otro lenguaje y forma de API, y no incluyen vaults personales reales ni datos de producción.
Tecnologías adicionales y motivo de su uso
Tecnología | Motivo de uso |
uv | para reproducir rápidamente dependencias, entorno virtual y archivo lock de Python, y usar la misma ruta de instalación también en Docker |
FastMCP | para mostrar de forma breve y clara la conexión entre funciones Python y herramientas MCP |
Pydantic | para validar los modelos de entrada y la configuración en el límite de la API |
SQLAlchemy | para establecer un límite de adaptador PostgreSQL de modo que el Repository no quede fijado a una forma concreta de ejecutar SQL |
psycopg | para encargarse de la conexión a PostgreSQL desde Python |
pgvector Python package | para representar el tipo vector de PostgreSQL en los modelos SQLAlchemy |
Strawberry GraphQL | para crear un ejemplo que exponga el mismo VectorService como Query/Mutation de GraphQL |
Docker Compose | para reproducir juntos el PostgreSQL con pgvector activado y el entorno de desarrollo del MCP de Python |
Cada tecnología se eligió para explicar por separado los roles de API, servicio, almacén y entorno de desarrollo.
Estructura del proyecto
.
├── 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.tomlLicencia
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