Hybrid BM25 + vector search for Obsidian vaults with frontmatter awareness
Project description
qkb — Query Knowledge Base
An on-device hybrid search engine for Obsidian vaults that understands YAML frontmatter metadata. Combines BM25 keyword search (SQLite FTS5) and vector semantic search (sqlite-vec) with metadata filtering, sibling-document surfacing, and two first-class interfaces: a CLI for humans and an MCP server for LLM agents.
Status: Phase 1 (ingest, search tiers 1–3, CLI, MCP stdio).
Quickstart
1. Install (isolated, like pipx / npm -g):
uv tool install qkb-search
2. Point qkb at your vault — create ~/.config/qkb/config.toml:
[vault]
path = "~/Documents/MyVault" # your Obsidian vault (read-only to qkb)
name = "MyVault" # used to build obsidian:// links
3. Opt notes in. Only notes whose frontmatter has a context and/or
source property are indexed — and an opted-in note also needs an id and
a parseable date (created or date):
---
id: f47ac10b-58cc-4372-a567-0e02b2c3d401
context: homelab
created: 2026-03-15
---
4. Check, index, search:
qkb status # verify config, vault, and model resolve
qkb ingest # build the index (downloads the ~310 MB model once)
qkb status # see documents/chunks/vectors counts
qkb query "certificate renewal" # hybrid search
qkb mcp # stdio MCP server for Claude Code / Desktop
No separate service, no compile: embeddings run in-process via ONNX Runtime,
whose prebuilt wheels install with the package. The default model is
embeddinggemma-300M (multilingual — the same embedding model
QMD uses), cached after the first download.
Re-running qkb ingest is incremental: unchanged notes are skipped, so it's
cheap to re-index after editing notes.
Claude Code MCP registration:
claude mcp add qkb -- qkb mcp
Documents
- PRD — what we're building and why
- Technical Design — architecture, schema, search algorithms
- Architecture Decision Records — the decision log
- Implementation Plans — milestone-by-milestone build plan
The Short Version
Notes opt in to indexing via frontmatter (context and/or source properties). An ingestion pipeline walks the vault, chunks markdown with structure-aware break-point scoring, embeds in-process (fastembed/ONNX by default; Ollama or GGUF optional), and stores everything in a single SQLite file. A search engine layers BM25 (document-level, weighted columns), vector similarity (chunk-level), and Reciprocal Rank Fusion on top — exposed as qkb search / vsearch / query, qkb get <UUID>, and qkb mcp.
Inspired by QMD's search architecture, adapted for structured knowledge systems with frontmatter metadata.
Installation
qkb is a command-line tool, so install it into an isolated environment —
the same idea as pipx or npm i -g:
# Recommended (uv):
uv tool install qkb-search
# Run without installing:
uvx --from qkb-search qkb query "certificate renewal"
# Alternatives (pipx isolates like uv; plain pip uses the current env):
pipx install qkb-search
pip install qkb-search
That's the whole setup — no service, no compile. The default embedding provider runs in-process via fastembed / ONNX Runtime, whose prebuilt wheels ship with the package (the C/C++ work is done upfront by the wheel builders, the way QMD relies on node-llama-cpp's prebuilt native binaries). Requires Python ≥3.11.
The default model is embeddinggemma-300M — the same embedding model QMD
uses. GGUF (QMD) and ONNX (qkb) are just different packagings of the same
weights for different runtimes; search quality comes from the model, not the
file format. The ~310 MB quantized ONNX downloads once on first qkb ingest
and is cached.
Embedding providers
Three interchangeable providers, set via [embedding].provider:
| provider | how it runs | when to use |
|---|---|---|
local (default) |
in-process fastembed / ONNX (prebuilt wheels) | just works — no service, no compile |
ollama |
the Ollama HTTP API | you already run Ollama (e.g. a Linux box) |
gguf |
in-process llama-cpp-python (the [gguf] extra) |
you want a specific GGUF; compiles on install |
Switching provider or model changes the vectors, so run qkb ingest --full
afterward to re-embed. Any model in
fastembed's catalog
also works — e.g. a smaller/faster one:
# ~/.config/qkb/config.toml
[embedding]
provider = "local"
model = "sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2" # 384-dim, ~220 MB
dimension = 384
License
MIT
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file qkb_search-0.2.2.tar.gz.
File metadata
- Download URL: qkb_search-0.2.2.tar.gz
- Upload date:
- Size: 129.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1778f304477947b8a884a406fda913bdef1c406ec9b9f7d715f7a2aeb6f8338c
|
|
| MD5 |
ca268e8df153d6cefa5a3f47ab259eee
|
|
| BLAKE2b-256 |
6b51a23ded76faffdcebdd98ae884006cc29da01e18636c0bd98171ddd17be41
|
Provenance
The following attestation bundles were made for qkb_search-0.2.2.tar.gz:
Publisher:
release.yml on miguelarios/qkb
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
qkb_search-0.2.2.tar.gz -
Subject digest:
1778f304477947b8a884a406fda913bdef1c406ec9b9f7d715f7a2aeb6f8338c - Sigstore transparency entry: 2195651306
- Sigstore integration time:
-
Permalink:
miguelarios/qkb@9ef72b5f4701737ce554a4eb8c791b233fc6a39f -
Branch / Tag:
refs/tags/v0.2.2 - Owner: https://github.com/miguelarios
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@9ef72b5f4701737ce554a4eb8c791b233fc6a39f -
Trigger Event:
push
-
Statement type:
File details
Details for the file qkb_search-0.2.2-py3-none-any.whl.
File metadata
- Download URL: qkb_search-0.2.2-py3-none-any.whl
- Upload date:
- Size: 48.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a9ca3c4e82a371b38e4716d73905382bba26d7b93f71d9be3f50484c85b368d1
|
|
| MD5 |
28151d3042586491713d7aa108943545
|
|
| BLAKE2b-256 |
372fe80e8bb477a04c4f309f56016d7253e5415dbeaa1970d35a98e972a530c5
|
Provenance
The following attestation bundles were made for qkb_search-0.2.2-py3-none-any.whl:
Publisher:
release.yml on miguelarios/qkb
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
qkb_search-0.2.2-py3-none-any.whl -
Subject digest:
a9ca3c4e82a371b38e4716d73905382bba26d7b93f71d9be3f50484c85b368d1 - Sigstore transparency entry: 2195651308
- Sigstore integration time:
-
Permalink:
miguelarios/qkb@9ef72b5f4701737ce554a4eb8c791b233fc6a39f -
Branch / Tag:
refs/tags/v0.2.2 - Owner: https://github.com/miguelarios
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@9ef72b5f4701737ce554a4eb8c791b233fc6a39f -
Trigger Event:
push
-
Statement type: