Bismut Vector MCP
Supports Ollama as an OpenAI-compatible embedding provider, enabling local embedding generation through Ollama's API.
Allows using OpenAI's embedding API to generate vector embeddings, providing a cloud-based alternative to local ONNX models. Supports any OpenAI-compatible endpoint via BISMUT_API_BASE.
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., "@Bismut Vector MCP index my project and search for authentication logic"
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.
Bismut Vector MCP
A local vector database exposed over the Model Context Protocol. Works with any MCP client: opencode, Claude Code/Desktop, Hermes, Cursor, Zed, Windsurf, and anything else that speaks MCP over stdio.
Everything runs on your machine. Embeddings are computed in-process with a local ONNX model, vectors live in a single SQLite file, and nothing is sent over the network.
Why it is built this way
Choice | Reason |
| Zero native compilation. The extension ships as a prebuilt npm optional dependency, so |
Local ONNX embeddings | No API keys, no network, no data leaving the machine. Works offline. |
| 384 dimensions, handles 50+ languages well. Measured much better separation than |
Cosine distance | Vectors are L2-normalized, so cosine similarity is exact and scores are directly interpretable: |
One | Lets different collections use different models and dimensions. |
Related MCP server: lore-mcp
Install
npm install
npm run buildRequires Node 22.5+ (uses the built-in node:sqlite). Developed and tested on
Node 24.
The embedding model downloads once on first use (~120 MB) into
.bismut/cache. Later runs start instantly and work offline.
Connect an editor
Registered in opencode's global config (~/.config/opencode/opencode.jsonc) as
bismut-vector. Verify any time:
npm run verify:opencode
npm run verify:opencode -- /path/to/some-projectThat reads your live opencode config, walks every registered server, and performs a real MCP handshake with the exact command and environment values it finds there.
For other editors, copy the block from examples/:
examples/opencode.json— opencode scope and overridesexamples/claude.md— Claude Code and Desktopexamples/other-editors.md— Cursor, Zed, Windsurf, Hermes, others
The common shape is:
{
"mcpServers": {
"bismut-vector": {
"command": "node",
"args": ["/absolute/path/to/Bismut_MCP_Vector/dist/index.js"],
"env": { "BISMUT_COLLECTION": "myproject" }
}
}
}opencode config is reloaded on restart, not hot-reloaded.
Tools
Tool | Purpose |
| Index files or directories. Re-running skips unchanged files. |
| Index arbitrary text: notes, memory, logs, pasted context. |
| Semantic search with path, language, and kind filters. |
| Expand a search hit into surrounding chunks. |
| Fetch one chunk in full. |
| List what is indexed. |
| List collections with model, dimension, and counts. |
| Remove documents by id, exact path, or path prefix. |
| Delete a whole collection. Requires |
| Server, database, and embedding model diagnostics. |
A typical agent session:
index_pathson the repository once.searchwith a path filter instead of guessing filenames.get_contextwhen a result looks truncated mid-function.
Chunking
Chunking is language-aware, which matters more for retrieval quality than embedding model choice:
Markdown splits on headings and carries the heading breadcrumb (
Architecture > Embeddings) into every chunk.Code splits at top-level symbol boundaries, tracking brace depth while skipping strings, template literals, and comments. One function or class per chunk, named in the chunk header. Python and other indent languages split on
def/classblocks instead.Prose and config files pack paragraphs up to a token target with overlap.
Every chunk embeds with a header (file:, title:, lang:, and the symbol
name) prepended to its body. This measurably improves retrieval for short chunks,
where the body alone gives the embedder too little to work with.
Line ranges are preserved on every chunk, so results can be opened directly.
Configuration
All settings are environment variables. Optionally use a .bismutrc.json in the
working directory instead.
Variable | Default | Meaning |
|
| SQLite database file |
|
| Model download cache |
|
| Default collection name |
|
| Any transformers.js model |
|
|
|
|
|
|
|
|
|
|
| Embeddings per forward pass |
|
| Target chunk size |
|
| Overlap between chunks |
|
| Skip files larger than this |
|
|
|
To use a cloud embedding endpoint instead:
{
"BISMUT_EMBED_PROVIDER": "openai",
"BISMUT_EMBED_MODEL": "text-embedding-3-small",
"BISMUT_API_KEY": "sk-...",
"BISMUT_API_BASE": "https://api.openai.com/v1"
}BISMUT_API_BASE works with any OpenAI-compatible endpoint (Ollama,
LM Studio, vLLM).
Changing the model changes the vector dimensionality, so it requires a new collection. The server refuses to mix models within one collection rather than silently producing nonsense results.
Storage layout
A collection's vectors live in its own vec0 virtual table. Chunk text and
document metadata live in ordinary tables, joined on the vector row id.
Filters are resolved to a doc_id list before the vector query, so path and
language filters are applied during the ANN traversal rather than after k is
taken. Applying them afterwards is the classic sqlite-vec mistake: it returns
empty results whenever the nearest k neighbours happen to fall outside the
filter.
Embeddings are cached by content hash, so re-indexing only re-embeds what actually changed.
Development
npm run typecheck # tsc --noEmit with strict + unused checks
npm test # 49 end-to-end assertions over the real MCP protocol
npm run test:chunks # print chunk boundaries per language
node scripts/probe-embed.mjs # check embedding quality for a modelnpm test spawns the built server as a subprocess and drives it with a real MCP
client, so it exercises the actual stdio transport, JSON-RPC framing, and zod
schema conversion rather than calling internals.
Known limits
Chunking uses structural heuristics, not a full parser. It is accurate for normal source files but not a substitute for an AST. Nested blocks inside a large
implor class body stay in one chunk until they exceed the token target.index_pathswalks directories serially, which is fast enough for typical repositories but noticeable on very large monorepos.PDF indexing uses
unpdfand only reads text layers. Scanned PDFs yield nothing; there is no OCR.There is no hybrid keyword/vector search. Pure semantic search can miss an exact identifier string, so pass
minScoredeliberately and use path filters to narrow.
Available Tools
10 toolsdelete_sourcesDelete indexed documentsADestructiveIdempotent
Remove documents from a collection by document id, exact uri, or path prefix. Vectors and chunks are deleted together. This is irreversible and requires at least one selector.
Inputs: all, uris, collection, uriPrefixes, +1 more.
drop_collectionDrop a collectionADestructiveIdempotent
Permanently delete an entire collection and all its vectors, chunks, and documents. Irreversible. Prefer delete_sources when removing only some documents.
Inputs: confirm (required), collection (required).
get_chunkFetch a single chunkARead-onlyIdempotent
Return the full untruncated text of one chunk by its id, with its document metadata.
Inputs: chunk_id (required).
get_contextExpand a search hit into surrounding contextARead-onlyIdempotent
Return contiguous chunks around a search hit (by chunk_id or document_id + ordinal) so you can read a whole function/section instead of an isolated fragment. Use after search when a result looks cut off.
Inputs: after, before, ordinal, chunk_id, +2 more.
index_pathsIndex files and foldersAIdempotent
Index files or directories into a vector collection for semantic search. Safe to re-run: files whose content hash is unchanged are skipped. Honors .gitignore, skips binaries/oversized files, and uses language-aware chunking (markdown sections, code symbols, prose paragraphs).
Inputs: paths (required), force, prune, exclude, +5 more.
index_textIndex raw textAIdempotent
Index arbitrary text (notes, snippets, memory entries, error logs, user-provided context) into a collection. Use a stable uri to replace previous content for the same uri, or pass replace:false to append a new version.
Inputs: uri (required), text (required), kind, meta, +3 more.
list_collectionsList collectionsARead-onlyIdempotent
List all vector collections with their embedding model, dimensionality, document/chunk counts, and on-disk size.
Inputs: no named parameters.
list_sourcesList indexed documentsARead-onlyIdempotent
List documents already indexed in a collection, with chunk counts and sizes. Use to check what is available before searching, or to confirm an index_paths run covered what you expected.
Inputs: limit, offset, uriPrefix, collection.
searchSemantic searchARead-onlyIdempotent
Run a semantic similarity query against indexed content. Returns ranked chunks with file path, line range, score (cosine similarity, 1 = identical), and text. Supports narrowing by path prefix, exact path, language, and content kind. Increase limit or use get_context when results look truncated.
Inputs: query (required), uris, kinds, limit, +6 more.
statsServer and database statisticsARead-onlyIdempotent
Show database path, embedding model/provider, collection count, and totals. Useful for verifying the server is healthy and which model is active before diagnosing empty search results.
Inputs: no named parameters.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
TDQS
Scored across 10 tools
Most tools have clearly distinct roles (index_paths vs index_text differ by source type, and list_sources vs list_collections differ by resource). The only mild overlap is between get_context and get_chunk, which both retrieve fuller content, though their descriptions distinguish fragment-level vs surrounding-context use.
Nearly all names follow a verb_noun pattern (list_collections, index_paths, delete_sources, drop_collection, get_chunk). Minor deviations are the bare single-word tools `search` and `stats`, but the overall convention is readable and predictable.
Ten tools is well within the ideal range and each one maps to a distinct stage of the index/search/retrieve lifecycle. No tool feels redundant or superfluous.
The surface covers the full lifecycle: indexing (paths/text), discovery (collections/sources), search, context retrieval (get_context/get_chunk), and deletion (delete_sources/drop_collection) plus health via stats. The main gap is an explicit create_collection or metadata-update operation, but these are likely handled implicitly by indexing.
Maintenance
Related MCP Connectors
An MCP server for search and retrieval over a local LanceDB database
Agentic search over your Dewey document collections from any MCP-compatible client.
Private-by-default, local-first memory/context/task orchestrator for MCP apps and agents.
Your personal library, readable by AI agents: durable snapshots, hybrid search, 46 MCP tools.
Related MCP Servers
- AlicenseAqualityDmaintenanceLocal-first RAG indexing and semantic search MCP server. Enables document retrieval and context-aware queries using local embedding models.314 npmMIT
- AlicenseAqualityBmaintenanceIndexes local Markdown/text files into a SQLite database with vector embeddings and provides MCP tools for semantic search without cloud dependencies.31AGPL 3.0
- AlicenseNot gradedqualityCmaintenanceProvides MCP tools for semantic search over personal knowledge sources using pluggable embeddings and local vector indexing.1MIT
- AlicenseAqualityBmaintenanceEnables natural-language semantic search over your own local files through an MCP server, fully offline without API keys or a server daemon, with optional LLM-grounded answers and exact file:line sources.5MIT