NoteHarbor MCP
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 vault for notes about quantum computing"
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 — TypeScript
It is a TypeScript example that connects Obsidian Markdown through MCP tools and extends it with PostgreSQL/pgvector search.
NoteHarbor separates the MCP interface and the vector store. Clients call MCP tools, and the service uses the store through domain models and Repository.
Architecture
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/)Notes become search data in the following order.
Obsidian Markdown
→ note chunk
→ externally generated embedding
→ note_chunks.embedding (pgvector)
→ cosine similarity search
→ MCP responseRelated MCP server: second-brain-mcp
Actual vectorization flow
upsert_vector is a tool that stores an already-created embedding. The process of turning Markdown into chunks and vectors can be seen in index_note.
Markdown text
→ splitMarkdownIntoChunks()
→ EmbeddingProvider.embed(chunk)
→ normalized number[]
→ NoteChunk
→ VectorService.indexChunk()
→ VectorRepository.save()The main code is split across the following files.
chunker.ts: Divides Markdown by paragraph and applies a maximum length
embeddingProvider.ts: A deterministic demo embedding provider that works without an API key
indexingPipeline.ts: Connects chunk creation, embedding, and storage
knowledge.ts: Ports for
EmbeddingProviderandVectorRepositoryindex.ts: Registers the
index_note,upsert_vector,search_vectorsMCP tools
The demo provider is a deterministic implementation for verifying the flow. It is not a model that provides semantic search quality; in a real service, you connect an external or local model to the same EmbeddingProvider port.
Quantization is applied after embedding generation.
float embedding [-1, 1]
→ clamp
→ int8 = round(value / (1 / 127))
→ 저장: values + scale + zeroPoint
→ 복원: (int8 - zeroPoint) * scaleThe sample uses symmetric scalar INT8 quantization.
Value range:
[-1, 1]Quantization range:
[-127, 127]scale:1 / 127zeroPoint:0Defines a schema to store
embedding_int8,embedding_scale, andembedding_zero_pointalong with the original embeddingThe current reference search uses a restorable float vector; the quantized ANN index will be added when an actual adapter is selected
The implementation is in quantizer.ts and indexingPipeline.ts. The index_note response also includes the embedding dimension and quantization bit width.
Why it is structured this way
NoteHarbor is an example that turns Obsidian Markdown into searchable knowledge units and exposes that capability as MCP tools.
Simple string search alone makes it hard to find related content with different wording. So notes are split into smaller chunks, and each chunk is converted into an embedding so that semantically close content can be searched.
Quantization is a choice for keeping these embeddings in a more compact representation.
Reduces memory and storage space
Reduces the amount of vector data transferred
Favorable for caching and batch processing in large knowledge bases
Precision may be lower than the original float instead
So the roles of each component are divided as follows.
Embedding: Represents the meaning of text as a numerical vector
Quantization: Reduces storage cost by lowering vector precision
Vector search: Finds nearby vectors and returns relevant chunks
MCP: Exposes this capability as a tool that LLM clients can call
The reason INT8 was chosen in this project is that the quantization principle and storage format are easy to explain in code. In a real service, you should measure search quality, memory savings, and latency, then choose one of float32, float16, INT8, or binary.
The current implementation is a mockup for verifying the flow. It does not guarantee semantic search quality or quantization performance, and in production you need to connect an embedding provider and a pgvector adapter.
Vector DB design
The vector storage unit is NoteChunk.
Field | Description |
| Identifier created from the original path and chunk order |
| Original Obsidian Markdown path |
| Chunk order within the document |
| Text returned as a search result |
| Vector generated by an external embedding model |
| Extended information such as tags, status, and source attributes |
A runnable SQL design example is available in src/infrastructure/postgres/schema.sql. The default example uses a 1,536-dimensional embedding and an HNSW index for cosine distance; adjust it to match the dimensions of your actual embedding model.
Search is translated into the following form in PostgreSQL.
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 and ORM-style boundaries
The vector store is not search-only; it is a Repository that manages the lifecycle of NoteChunk.
Create/Upsert:
upsert_vector,index_note→VectorService.indexChunk()Read:
get_chunk,list_chunksUpdate:
update_chunk→ reads an existing chunk and updates changed fields along with its quantized representationDelete:
delete_chunkSearch:
search_vectors,search_knowledge
The current execution adapter is a Map. In a production environment, you can replace the Repository's inner implementation with Drizzle ORM and 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 tools do not execute SQL directly; they pass CRUD and search through MCP → VectorService → VectorRepository → Drizzle/pgvector.
Included samples
Obsidian Markdown listing, reading, and search
The
index_note,search_knowledge,upsert_vector, andsearch_vectorsMCP toolsThe
MCP → Service → Repository → pgvectorlayerDrizzle ORM-based
note_chunksschema mappingPostgreSQL/pgvector schema and search SQL
Notion sync boundary
Smithery development server and Docker run examples
The current default execution is a memory mockup that does not include personal data or external credentials. For a PostgreSQL connection, replace the Map implementation in src/infrastructure/postgres/postgresVectorRepository.ts with a Drizzle/pg-based adapter. The README and schema are samples that openly show that transition point.
The Python/GraphQL version is available at noteharbor-python.
Getting Started
npm install
npm run devDocker:
docker build -f Dockerfile.typescript -t noteharbor-mcp-ts .
docker run --rm -p 8081:8081 noteharbor-mcp-tsWhy PostgreSQL + pgvector
NoteHarbor's search targets are not just vector data. You also need to handle relational metadata such as source path, chunk order, tags, status, and sync information.
So instead of adding a separate vector DB, this sample keeps the following together in PostgreSQL.
content: Original text snippet to display as a search resultmetadata: Tags, status, and source informationembedding: Float vector for cosine searchembedding_int8: Quantized representation to reduce storage and transfer costs
The specific reasons for choosing pgvector are as follows.
Data combination: You can combine vector similarity with
source_path, tag, and status conditions in a single SQL statementConsistency: You can manage source metadata and the search index within the same transaction boundary
Operational simplicity: The application does not have to operate PostgreSQL and a separate vector DB separately
Search features: You can use cosine distance (
<=>) and HNSW indexes as PostgreSQL extensionsScaling path: Start with a single store at first, and when scale grows, you can separate a search-only adapter
As search scale grows, a dedicated vector DB may be more suitable. The value here is in showing a flow that handles source text, metadata, and vector search within a single application boundary.
How search works
Search does not directly compare source strings; it places the question and note chunks in the same embedding space and then compares distances.
사용자 질문
→ query embedding 생성
→ INT8 양자화 후 복원
→ PostgreSQL/pgvector cosine distance 검색
→ 가까운 NoteChunk 반환
→ sourcePath·content·metadata와 함께 MCP 응답The runnable sample is the search_knowledge MCP tool.
index_notesplits Markdown into chunks and stores the embedding of each chunk.The user sends a natural-language
query.A query embedding is generated using the same
EmbeddingProvider.The query is quantized and restored in the same way as the stored vectors.
VectorRepository.search()sorts nearby chunks by cosine similarity.Search results include the source path, chunk content, metadata, and score.
search_vectors is a low-level tool that takes an already-created embedding directly, while search_knowledge is an application-level tool that connects a natural-language question to search results.
The current mock Repository computes cosine similarity in an in-memory Map. Once you switch to a PostgreSQL adapter, it will use pgvector's <=> operator and LIMIT search behind the same port.
Additional technologies and why they were introduced
Technology | Reason for inclusion |
Node.js ESM | To run the TypeScript MCP sample simply in the current Node runtime style |
MCP SDK | To register the |
Zod | To validate MCP inputs and configuration values at runtime |
Drizzle ORM | To provide a type-safe schema and query boundary when connecting the PostgreSQL adapter |
pg | The driver to use when switching to a real PostgreSQL connection adapter |
Smithery CLI | To provide a run path for developing and verifying the MCP server |
Docker | To pin down Node/MCP execution conditions in local and deployment environments |
chokidar·fast-glob | Handles Markdown vault change detection and file discovery |
gray-matter·marked | To treat Markdown frontmatter and body as knowledge chunks |
dotenv | To separate local environment settings from code |
Not all dependencies are core to vector search. Some are supporting technologies for Obsidian and Notion integration; the core of the search path is MCP SDK → Vector Service → Repository → pgvector.
Why these technologies were chosen
Technology | Reason chosen |
Obsidian Markdown | Chosen because the source is a plain-text file, giving high ownership and portability, and keeping the knowledge source from being tied to a specific SaaS |
MCP | Chosen to expose the same knowledge tool through a standard interface without building separate integration code for each LLM client |
TypeScript | Chosen because it integrates naturally with the MCP SDK and lets you manage tool input and output boundaries with types |
PostgreSQL | Chosen to consistently manage document metadata, status, and search results in one store while securing a production migration path |
pgvector | Chosen to handle source metadata and vector search together inside PostgreSQL without adding a separate vector DB |
Notion adapter | Chosen to show a boundary for syncing knowledge fragments to an external workspace when needed, rather than using Notion as the source store |
Docker | Chosen to keep PostgreSQL and MCP execution conditions consistent across local and deployment environments |
The key is not adding many tools. It is preserving the source as Markdown, keeping derived search data in PostgreSQL/pgvector, and exposing only the necessary functionality to LLMs via MCP.
Therefore, this project is not a system that uses Obsidian, Notion, and PostgreSQL all as source stores.
Obsidian Markdown: source knowledge
PostgreSQL/pgvector: derived search index
Notion: optional external sync target
MCP: LLM access boundary
Design points
Preserve original Markdown
Separate MCP tools from the service layer
NoteChunkdomain model andVectorRepositoryportReplaceable storage boundary with PostgreSQL/pgvector
Excludes real personal vault and credentials
This project is a public example for understanding the structure of MCP and knowledge search.
License
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 semantic search capability over Obsidian vaults and exposes recent notes as resources to Claude through the MCP protocol.9
- AlicenseNot gradedqualityCmaintenanceTurns an Obsidian vault into semantic memory for coding agents, providing read-only semantic search and a human-approved write workflow via MCP.5MIT
- AlicenseNot gradedqualityAmaintenanceConnects AI assistants to an Obsidian vault as a semantic knowledge graph, enabling graph navigation, semantic search, and content operations through MCP.12456MIT
- AlicenseNot gradedqualityCmaintenanceExposes an Obsidian notes vault as MCP services, enabling AI assistants to search, read, create, update, and delete notes and folders.241MIT
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.
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-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server