Skip to main content
Glama

NoteHarbor MCP — TypeScript

Ein TypeScript-Beispiel, das Obsidian-Markdown über MCP-Werkzeuge anbindet und um PostgreSQL/pgvector-Suche erweitert.

NoteHarbor trennt die MCP-Schnittstelle vom Vektorspeicher. Der Client ruft MCP-Werkzeuge auf, und der Service nutzt den Speicher über das Domänenmodell und das Repository.

Architektur

MCP Client
    │
    ▼
MCP Tools
(upsert_vector / search_vectors)
    │
    ▼
Vector Service
(src/application/vectorService.ts)
    │
    ▼
VectorRepository port
(src/domain/knowledge.ts)
    │
    ├── 현재 실행 어댑터: in-memory Map 목업
    │
    └── 운영 전환 지점: PostgreSQL + pgvector
        (src/infrastructure/postgres/)

Notizen werden in der folgenden Reihenfolge zu Suchdaten.

Obsidian Markdown
  → note chunk
  → externally generated embedding
  → note_chunks.embedding (pgvector)
  → cosine similarity search
  → MCP response

Related MCP server: second-brain-mcp

Tatsächlicher Vektorisierungsablauf

upsert_vector ist ein Werkzeug zum Speichern bereits erstellter Embeddings. Den Prozess, wie Markdown in Chunks und Vektoren umgewandelt wird, sehen Sie in index_note.

Markdown text
  → splitMarkdownIntoChunks()
  → EmbeddingProvider.embed(chunk)
  → normalized number[]
  → NoteChunk
  → VectorService.indexChunk()
  → VectorRepository.save()

Der Hauptcode ist auf folgende Dateien verteilt:

  • chunker.ts: Teilt Markdown nach Absätzen und wendet eine maximale Länge an

  • embeddingProvider.ts: Deterministischer Demo-Embedding-Provider, der ohne API-Schlüssel funktioniert

  • indexingPipeline.ts: Verbindet Chunk-Erstellung, Embedding und Speicherung

  • knowledge.ts: Ports für EmbeddingProvider und VectorRepository

  • index.ts: Registriert die MCP-Werkzeuge index_note, upsert_vector, search_vectors

Der Demo-Provider ist eine deterministische Implementierung, um den Ablauf zu veranschaulichen. Er bietet keine semantische Suchqualität; in einem echten Dienst wird derselbe EmbeddingProvider-Port mit einem externen oder lokalen Modell verbunden.

Die Quantisierung wird nach der Embedding-Erzeugung angewendet.

float embedding [-1, 1]
  → clamp
  → int8 = round(value / (1 / 127))
  → 저장: values + scale + zeroPoint
  → 복원: (int8 - zeroPoint) * scale

Das Beispiel verwendet symmetrische skalare INT8-Quantisierung.

  • Wertebereich: [-1, 1]

  • Quantisierungsbereich: [-127, 127]

  • scale: 1 / 127

  • zeroPoint: 0

  • Das Schema speichert embedding_int8, embedding_scale und embedding_zero_point zusammen mit dem ursprünglichen Embedding

  • Die aktuelle Referenzsuche verwendet wiederherstellbare Float-Vektoren; ein quantisierter ANN-Index wird bei der Auswahl eines echten Adapters hinzugefügt

Die Implementierung befindet sich in quantizer.ts und indexingPipeline.ts. Die Antwort von index_note enthält auch die Embedding-Dimension und die Anzahl der Quantisierungsbits.

Warum diese Struktur?

NoteHarbor ist ein Beispiel, das Obsidian-Markdown in durchsuchbare Wissenseinheiten umwandelt und diese Funktionen als MCP-Werkzeuge bereitstellt.

Mit einfacher String-Suche lassen sich verwandte Inhalte mit anderer Formulierung schwer finden. Deshalb werden Notizen in kleine Chunks zerlegt und jeder Chunk in ein Embedding umgewandelt, sodass nach inhaltlich ähnlichen Inhalten gesucht werden kann.

Die Quantisierung ist eine Option, um diese Embeddings kompakter zu speichern.

  • Reduziert Speicher und Speicherplatz

  • Reduziert die Datenmenge bei der Vektorübertragung

  • Vorteilhaft für Cache und Batch-Verarbeitung in großen Wissensdatenbanken

  • Dafür kann die Präzision geringer sein als beim ursprünglichen Float

Daher wurden die Rollen der Komponenten wie folgt aufgeteilt:

  1. Embedding: Drückt die Bedeutung von Text als numerischen Vektor aus

  2. Quantisierung: Reduziert die Speicherkosten durch geringere Präzision der Vektoren

  3. Vektorsuche: Findet nahe Vektoren und gibt relevante Chunks zurück

  4. MCP: Stellt diese Funktionen als Werkzeuge für LLM-Clients bereit

Die Wahl von INT8 in diesem Projekt liegt daran, dass sich das Quantisierungsprinzip und die Speicherform leicht im Code erklären lassen. In einem echten Dienst sollten Sie nach Messung von Suchqualität, Speicherersparnis und Latenz zwischen float32, float16, INT8 und binary wählen.

Die aktuelle Implementierung ist ein Mockup, um den Ablauf zu veranschaulichen. Sie garantiert weder semantische Suchqualität noch Quantisierungsleistung; im Produktivbetrieb müssen ein Embedding-Provider und ein pgvector-Adapter angebunden werden.

Vektor-DB-Design

Die Vektorspeichereinheit ist NoteChunk.

Feld

Bedeutung

id

Identifikator aus Quellpfad und Chunk-Reihenfolge

sourcePath

Quellpfad des Obsidian-Markdown

chunkIndex

Reihenfolge des Chunks im Dokument

content

Text, der als Suchergebnis zurückgegeben wird

embedding

Vektor, der von einem externen Embedding-Modell erzeugt wurde

metadata

Erweiterte Informationen wie Tags, Status, Quellattribute

Ein ausführbares SQL-Designbeispiel finden Sie in src/infrastructure/postgres/schema.sql. Das Standardbeispiel verwendet ein 1.536-dimensionales Embedding und einen HNSW-Index für die Kosinus-Distanz; es wird an die Dimension des tatsächlichen Embedding-Modells angepasst.

Die Suche wird in PostgreSQL wie folgt umgesetzt:

SELECT id, source_path, chunk_index, content, metadata,
       1 - (embedding <=> $1::vector) AS score
FROM note_chunks
ORDER BY embedding <=> $1::vector
LIMIT $2;

CRUD- und ORM-Stil-Grenzen

Der Vektorspeicher ist kein reiner Suchspeicher, sondern ein Repository, das den Lebenszyklus von NoteChunk verwaltet.

  • Create/Upsert: upsert_vector, index_noteVectorService.indexChunk()

  • Read: get_chunk, list_chunks

  • Update: update_chunk → liest den vorhandenen Chunk und aktualisiert geänderte Felder sowie die quantisierte Darstellung

  • Delete: delete_chunk

  • Search: search_vectors, search_knowledge

Der aktuelle Ausführungsadapter ist eine Map. In einer Produktionsumgebung ersetzen Sie die Implementierung im Repository durch Drizzle ORM und pg.

db.insert(noteChunks).values(row).onConflictDoUpdate(...)
db.select().from(noteChunks).where(eq(noteChunks.id, id)).limit(1)
db.update(noteChunks).set(values).where(eq(noteChunks.id, id))
db.delete(noteChunks).where(eq(noteChunks.id, id))

MCP-Werkzeuge führen kein SQL direkt aus, sondern übergeben CRUD und Suche in der Reihenfolge MCP → VectorService → VectorRepository → Drizzle/pgvector.

Enthaltene Beispiele

  • Obsidian-Markdown auflisten, lesen, durchsuchen

  • MCP-Werkzeuge index_note, search_knowledge, upsert_vector, search_vectors

  • Schicht MCP → Service → Repository → pgvector

  • Drizzle-ORM-basierte Zuordnung des note_chunks-Schemas

  • PostgreSQL/pgvector-Schema und Such-SQL

  • Notion-Synchronisationsgrenze

  • Beispiele für Smithery-Entwicklungsserver und Docker-Ausführung

Die aktuelle Standardausführung ist ein In-Memory-Mockup, das keine persönlichen Daten und keine externen Anmeldeinformationen enthält. Bei einer PostgreSQL-Verbindung wird die Map-Implementierung in src/infrastructure/postgres/postgresVectorRepository.ts durch einen Drizzle/pg-basierten Adapter ersetzt. README und Schema sind Beispiele, um diesen Übergangspunkt öffentlich zu zeigen.

Die Python/GraphQL-Version finden Sie unter noteharbor-python.

Erste Schritte

npm install
npm run dev

Docker:

docker build -f Dockerfile.typescript -t noteharbor-mcp-ts .
docker run --rm -p 8081:8081 noteharbor-mcp-ts

Warum PostgreSQL + pgvector?

Die Suchziele von NoteHarbor sind nicht nur Vektordaten. Es müssen auch relationale Metadaten wie Quellpfad, Chunk-Reihenfolge, Tags, Status und Synchronisationsinformationen verarbeitet werden.

Deshalb werden in diesem Beispiel keine separate Vektor-DB, sondern folgende Daten in PostgreSQL abgelegt:

  • content: Originaltextfragment, das als Suchergebnis angezeigt wird

  • metadata: Tags, Status, Quellinformationen

  • embedding: Float-Vektor für die Kosinus-Suche

  • embedding_int8: Quantisierte Darstellung zur Reduzierung von Speicher- und Übertragungskosten

Die konkreten Gründe für pgvector sind:

  • Datenkombination: Vektorähnlichkeit und Bedingungen wie source_path, Tags und Status lassen sich in einem SQL kombinieren

  • Konsistenz: Originalmetadaten und Suchindex können in derselben Transaktionsgrenze verwaltet werden

  • Betriebliche Einfachheit: Die Anwendung muss nicht PostgreSQL und eine separate Vektor-DB betreiben

  • Suchfunktionen: Kosinus-Distanz (<=>) und HNSW-Index sind als PostgreSQL-Erweiterung verfügbar

  • Skalierungspfad: Zunächst mit einem einzigen Speicher beginnen und bei wachsendem Umfang einen suchspezifischen Adapter ausgliedern

Bei sehr großem Suchumfang kann eine dedizierte Vektor-DB besser geeignet sein. Hier geht es darum, den Ablauf zu zeigen, bei dem Originaltext, Metadaten und Vektorsuche in einer Anwendungsgrenze behandelt werden.

Wie funktioniert die Suche?

Die Suche vergleicht nicht direkt die Originaltextzeichenfolgen, sondern legt Frage und Notiz-Chunks in denselben Embedding-Raum und vergleicht dann die Distanzen.

사용자 질문
  → query embedding 생성
  → INT8 양자화 후 복원
  → PostgreSQL/pgvector cosine distance 검색
  → 가까운 NoteChunk 반환
  → sourcePath·content·metadata와 함께 MCP 응답

Ein ausführbares Beispiel ist das MCP-Werkzeug search_knowledge.

  1. index_note teilt Markdown in Chunks und speichert das Embedding jedes Chunks.

  2. Der Benutzer sendet eine natürliche Sprachabfrage query.

  3. Mit demselben EmbeddingProvider wird ein Query-Embedding erzeugt.

  4. Die Query wird wie die gespeicherten Vektoren quantisiert und wiederhergestellt.

  5. VectorRepository.search() sortiert die nahen Chunks nach Kosinus-Ähnlichkeit.

  6. Die Suchergebnisse enthalten Quellpfad, Chunk-Inhalt, Metadaten und Score.

search_vectors ist ein Low-Level-Werkzeug, das direkt ein bereits erstelltes Embedding entgegennimmt, während search_knowledge ein Anwendungswerkzeug ist, das von der natürlichen Sprachfrage bis zum Suchergebnis verbindet.

Das aktuelle Mockup-Repository berechnet die Kosinus-Ähnlichkeit in einer In-Memory-Map. Nach der Umstellung auf den PostgreSQL-Adapter werden hinter demselben Port die <=>-Operation und LIMIT-Suche von pgvector verwendet.

Zusätzliche Technologien und Gründe für ihren Einsatz

Technologie

Grund für den Einsatz

Node.js ESM

Um das TypeScript-MCP-Beispiel einfach mit der aktuellen Node-Laufzeit auszuführen

MCP SDK

Um die Werkzeuge index_note, search_knowledge, upsert_vector, search_vectors als Standard-MCP-Server zu registrieren

Zod

Zur Laufzeitvalidierung von MCP-Eingaben und Konfigurationswerten

Drizzle ORM

Bietet typsichere Schema- und Query-Grenzen beim Anbinden des PostgreSQL-Adapters

pg

Treiber für die Umstellung auf einen echten PostgreSQL-Verbindungsadapter

Smithery CLI

Bietet einen Ausführungspfad zum Entwickeln und Validieren des MCP-Servers

Docker

Fixiert die Node/MCP-Ausführungsbedingungen in lokalen und Bereitstellungsumgebungen

chokidar·fast-glob

Zuständig für die Erkennung von Änderungen im Markdown-Vault und die Dateisuche

gray-matter·marked

Behandelt Markdown-Frontmatter und -Text als Wissens-Chunks

dotenv

Trennt lokale Umgebungseinstellungen vom Code

Nicht alle Abhängigkeiten sind für die Vektorsuche zentral. Einige sind Hilfstechnologien für die Obsidian- und Notion-Integration; der Kern des Suchpfads ist MCP SDK → Vector Service → Repository → pgvector.

Gründe für die Technologiewahl

Technologie

Auswahlgrund

Obsidian Markdown

Gewählt, weil die Originale als Klartextdateien vorliegen, was Eigentum und Portabilität erhöht und das Wissensoriginal nicht an eine bestimmte SaaS bindet

MCP

Gewählt, um dieselben Wissenswerkzeuge über eine Standardschnittstelle bereitzustellen, ohne für jeden LLM-Client separate Integrationscodes zu schreiben

TypeScript

Gewählt, weil die Verbindung zum MCP SDK natürlich ist und die Ein-/Ausgabegrenzen der Werkzeuge typisiert verwaltet werden

PostgreSQL

Gewählt, um Dokumentmetadaten, Status und Suchergebnisse konsistent in einem Speicher zu verwalten und einen Produktionsübergangspfad zu sichern

pgvector

Gewählt, um Originalmetadaten und Vektorsuche in PostgreSQL zu behandeln, ohne eine separate Vektor-DB hinzuzufügen

Notion-Adapter

Gewählt, um eine Grenze zu zeigen, die Wissensfragmente bei Bedarf in einen externen Workspace synchronisiert, statt Notion als Originalspeicher zu verwenden

Docker

Gewählt, um die PostgreSQL- und MCP-Ausführungsbedingungen in lokalen und Bereitstellungsumgebungen einheitlich zu gestalten

Der Kern liegt nicht darin, viele Werkzeuge anzubinden. Es geht darum, die Originale als Markdown zu bewahren, abgeleitete Suchdaten in PostgreSQL/pgvector zu halten und dem LLM nur die benötigten Funktionen über MCP bereitzustellen.

Daher ist dieses Projekt kein System, das Obsidian, Notion und PostgreSQL alle als Originalquellen verwendet.

  • Obsidian-Markdown: Originalwissen

  • PostgreSQL/pgvector: Abgeleiteter Suchindex

  • Notion: Optionales externes Synchronisationsziel

  • MCP: Zugriffsgrenze für LLMs

Design-Punkte

  • Bewahrung des Original-Markdowns

  • Trennung von MCP-Werkzeugen und Service-Schicht

  • NoteChunk-Domänenmodell und VectorRepository-Port

  • Austauschbare Speichergrenze mit PostgreSQL/pgvector

  • Ausschluss von echten persönlichen Vaults und Anmeldeinformationen

Dieses Projekt ist ein öffentliches Beispiel, um die Struktur von MCP und Wissenssuche zu veranschaulichen.

Lizenz

MIT

F
license - not found
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

View all related MCP servers

Related MCP Connectors

  • Markdown-based note-taking with a hosted MCP server. Your notes serve you and your AI.

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

  • Search and reason over your Obsidian-style Markdown vault, right from ChatGPT.

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

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