Semantic Search MCP
Semantically search markdown notes, detect duplicate notes, and retrieve file content from indexed vaults.
search_related(query, top_k=5) — Find notes semantically related to a query text, returning up to
top_kresults (default 5).check_duplicates(file_path) — Find notes that are potential duplicates of a given file, referenced by absolute path or path relative to the content directory.
get_content(path, snippet, query, context_lines) — Fetch content from an indexed file, either the full file or, when
snippet=true, a snippet around the best-matching line forquery(withcontext_lines, default 20, of surrounding context).
Click on "Deploy 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., "@Semantic Search MCP find notes related to machine learning algorithms"
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.
Semantic Search
Semantic search over markdown files. Find related notes by meaning, not just keywords. Detect duplicates before creating new notes.
Supports two server transports:
stdio MCP — For Claude Code integration (one process per session)
HTTP — Combined MCP-over-HTTP + REST on one port; one warm process shared by all clients
Features
Semantic search using sentence-transformers
Duplicate/similar note detection
Auto-updating index with file watcher
Multi-directory support
Inline tag extraction (
#tag-name)
Related MCP server: Markdown Notes MCP
Install
CPU-only install — recommended for macOS (any Mac, Apple Silicon or Intel) and Linux/Windows without an NVIDIA GPU. Saves ~5GB of CUDA binaries. On macOS, Apple GPU (MPS) is still auto-detected and used via PyTorch's built-in MPS backend — the "CPU" label refers only to the absence of CUDA, not to the compute device at runtime.
uv tool install --index https://download.pytorch.org/whl/cpu \
git+https://github.com/bborbe/semantic-searchCUDA install — only for Linux/Windows with a dedicated NVIDIA GPU. Not applicable to macOS (NVIDIA CUDA is not supported on Mac).
uv tool install git+https://github.com/bborbe/semantic-searchUpgrade
uv tool upgrade semantic-searchServer Modes
stdio MCP (per-session Claude Code)
Spawns one process per Claude Code session. Simple, but each session loads its own ~400 MB–1 GB model copy.
claude mcp add -s project semantic-search \
--env CONTENT_PATH=/path/to/vault \
-- \
uvx --from git+https://github.com/bborbe/semantic-search semantic-search-mcp serveTools available:
search_related(query, top_k=5)— Find semantically related notesget_content(path, snippet, query, context_lines)— Retrieve file content from indexed vaultscheck_duplicates(file_path)— Detect duplicate/similar notes
HTTP (shared across all clients)
Single long-running process serves MCP-over-HTTP at /mcp plus REST at /search, /duplicates, /content, /health, /reindex. All Claude Code sessions and REST clients share one warm indexer, and each request names the scope it wants — so one process gives every client its own view without a second index.
SEMANTIC_SCOPE_MAP=scopes.yaml semantic-search-http --host 127.0.0.1 --port 8321The variable may be omitted, in which case the scope map is read from ~/.config/semantic-search/config.yaml.
Scopes are declared in scopes.yaml — see design/per-vault-scoping.md for the contract and the fail-closed default. Point Claude Code at it via MCP config; clients differ only by the scope in their URL:
{
"mcpServers": {
"semantic-search": {
"type": "http",
"url": "http://127.0.0.1:8321/mcp?scope=personal"
}
}
}REST endpoints:
Endpoint | Method | Description |
| POST | MCP-over-HTTP (Claude Code); scope comes from the URL |
| GET | Semantic search within a scope |
| GET | Find duplicate notes within a scope |
| GET | Retrieve file content within a scope |
| GET | Health check with index stats (scopeless) |
| GET/POST | Force index rebuild (scopeless) |
scope is required on /search, /duplicates, and /content. Omitting it returns HTTP 400 MISSING_SCOPE; an unknown name returns 400 UNKNOWN_SCOPE. Repeating the parameter resolves to the union of the named scopes' roots — /search?q=...&scope=personal&scope=work searches both scopes in one request, and the order the scopes are named in does not change the result set. There is deliberately no union-wide fallback: naming no scope, or any undeclared name, fails the whole request closed.
Example queries:
# Search
curl 'http://127.0.0.1:8321/search?q=kubernetes+deployment'
# Find duplicates
curl 'http://127.0.0.1:8321/duplicates?file=notes/my-note.md'
# Health check
curl 'http://127.0.0.1:8321/health'Two-Step Flow
Search for related notes, then fetch the full content of any result:
# Step 1: Search for related notes
curl 'http://127.0.0.1:8321/search?q=kubernetes+deployment'
# Returns: [{"path": "notes/k8s-guide.md", "score": 0.92}, ...]
# Step 2: Fetch the content of a result
curl 'http://127.0.0.1:8321/content?path=notes/k8s-guide.md'
# Returns: {"path": "/full/resolved/path.md", "content": "# Kubernetes Guide\n...", "mode": "full"}Snippet Mode
Retrieve a focused snippet around a specific query term within a file:
# Get a focused snippet around "service mesh" in the file
curl 'http://127.0.0.1:8321/content?path=notes/k8s-guide.md&snippet=true&query=service+mesh&context_lines=10'
# Returns: {"path": "...", "content": "...\n## Service Mesh\n...", "mode": "snippet"}Remote Deployment
get_content and GET /content enable remote deployment of semantic-search clients. Callers no longer need filesystem access to the vault directory — all content retrieval happens over HTTP/MCP from any network location. The server enforces path validation: files outside indexed roots are never served.
Claude Code Plugin
This repo also ships as a Claude Code marketplace plugin with commands for setup, search, and research.
Install
claude plugin marketplace add bborbe/semantic-search
claude plugin install semantic-searchUpdate
claude plugin marketplace update semantic-search
claude plugin update semantic-search@semantic-searchQuick Start
# One-shot interactive setup: installs the binary, writes the launchd/systemd
# unit, registers the MCP server in your Claude config.
/semantic-search:configure
# Search indexed markdown
/semantic-search:search kubernetes deployment
# Multi-step research across results
/semantic-search:research kafka backup strategyCommands
Command | Description |
| Install |
| Semantic search via the running MCP server |
| Multi-step research — search, categorize, read top sources, synthesize |
Run in Background
For production-style usage, run semantic-search-http as a background service so every Claude Code session (and any REST client) shares one warm process.
Platform | Guide |
macOS (launchd) | |
Linux (systemd) |
Quick example (macOS):
launchctl load ~/Library/LaunchAgents/com.github.bborbe.semantic-search-http.plistQuick example (Linux):
systemctl --user enable --now semantic-search-http.serviceCLI Commands
One-shot commands without running a server:
# Search
CONTENT_PATH=/path/to/vault semantic-search search "kubernetes deployment"
# Find duplicates
CONTENT_PATH=/path/to/vault semantic-search duplicates path/to/note.mdBinaries
Binary | Purpose |
| Combined HTTP server — MCP at |
| stdio MCP server — one per Claude Code session. Use when HTTP service is not set up. |
| CLI only — |
Configuration
Environment Variables
Variable | Description | Default |
| Directory to index (comma-separated for multiple) |
|
| Logging level (DEBUG, INFO, WARNING, ERROR) |
|
Multiple Directories
Index multiple directories by separating paths with commas:
CONTENT_PATH=/path/to/vault1,/path/to/vault2,/path/to/docsAll directories are indexed together and searched as one unified index.
Excluding Files with .semanticignore
Place a .semanticignore file at the root of any vault to exclude paths from indexing. Each vault uses its own rules independently; a missing .semanticignore means "index everything" (no change for existing vaults).
Syntax — gitignore-style patterns powered by the pathspec library's gitwildmatch dialect. The full gitignore rule set applies:
Patterns are matched relative to the vault root.
Trailing
/matches directories (and everything inside them):archive/**matches across directory boundaries:**/draft.mdLeading
/anchors a pattern to the vault root:/scratch.md!negates a pattern (re-includes a previously excluded path):!archive/keep.md
Behaviour — matching paths are excluded from:
Full index rebuilds (
rebuild_index)Single-file add/update events (
add_file_to_index)File-watcher
created/modified/movedevents
The .semanticignore file itself is never indexed.
Runtime reload — editing .semanticignore while the watcher is running triggers an atomic reload of that vault's rules. Subsequent file events immediately use the new patterns; no restart is required.
Example .semanticignore:
# Exclude an entire directory
archive/
# Exclude all draft files in any subdirectory
**/draft.md
# Re-include one specific file from the excluded directory
!archive/keep.md
# Anchor a pattern to the vault root only
/scratch.mdHow It Works
First run downloads a small embedding model (~90MB) and indexes your markdown files (<1s for typical vaults). The index auto-updates when files change via filesystem watcher.
Indexed Content
Each markdown file is indexed with weighted components:
Component | Weight | Notes |
Filename | 3x | |
Frontmatter | 3x | |
Frontmatter | 2x | Merged with inline tags |
Frontmatter | 2x | |
Inline tags ( | 2x | Extracted from body |
First H1 heading | 2x | |
Body content | 1x | First 500 words |
Development
# Clone
git clone https://github.com/bborbe/semantic-search
cd semantic-search
# Install dev dependencies
make install
# Run checks
make check
# Run tests
make testLicense
BSD 2-Clause License — see LICENSE.
Available Tools
3 toolscheck_duplicatesA
Find notes that are potential duplicates of the given file.
Inputs: file_path (required). Returns: result
get_contentB
Fetch the content of a file from the indexed vault.
Inputs: path (required), query, snippet, context_lines. Returns: object
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
TDQS
Scored across 3 tools
Each tool serves a distinct purpose: semantic search, duplicate detection, and content retrieval. There is no overlap or ambiguity between them.
All tool names follow a consistent verb_noun pattern (search_related, check_duplicates, get_content) using snake_case. The naming is predictable and readable.
Three tools is an appropriate, focused set for a semantic search server. Each tool earns its place and the scope is well-defined.
The server covers the core needs of semantic search: finding related notes, detecting duplicates, and retrieving content. There are no missing operations that would hinder its stated purpose.
Maintenance
Related MCP Connectors
Search and reason over your Obsidian-style Markdown vault, right from ChatGPT.
Personal context for every AI: search, read, and write back to your private Markdown library.
Search your Obsidian vault to quickly find notes by title or keyword, summarize related content, a…
Markdown notes in folders, with files, that your AI assistant can read, write and organise.
Related MCP Servers
- AlicenseAqualityDmaintenanceEnables semantic search over local markdown note collections using vector embeddings, with real-time file watching and zero-config setup.4MIT
- FlicenseNot gradedqualityDmaintenanceEnables managing and searching markdown notes with semantic search, question answering, and note generation, and provides an MCP server for GitHub Copilot integration.4-
- AlicenseNot gradedqualityDmaintenanceEnables semantic search over an Obsidian vault using natural language, retrieving relevant notes and extracted conclusions.9 PyPIMIT
- AlicenseNot gradedqualityBmaintenanceEnables semantic search and retrieval over an Obsidian vault using local or API-based embeddings, allowing AI assistants to find notes by meaning, get related content, and pull context during conversations.16MIT