Skip to main content
Glama

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

  1. MCP: herramientas upsert_vector, search_vectors

  2. GraphQL: Mutation indexChunk, Query vectorSearch

  3. PostgreSQL/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 sync

Ejecución de la muestra MCP:

uv run noteharbor

Levantar PostgreSQL con Docker

docker compose up -d postgres

docker-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-mcp

POSTGRES_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, GraphQL indexChunkVectorService.index_chunk()

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

  • Update: MCP·GraphQL updateChunk → lee el registro existente y solo aplica los campos modificados

  • Delete: MCP·GraphQL deleteChunk

  • Search: MCP search_vectors, GraphQL vectorSearch

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 + pgvector

Responsabilidades 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.toml

Licencia

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