Qmd Skill for Hermes Agent: Local Hybrid Search Guide
Hybrid local search over notes, docs, and transcripts.
Written by Neura Market from the official Hermes Agent documentation for Qmd. Commands, paths, and version numbers are reproduced from the source unchanged.
Read the official documentationQmd is an optional Hermes Agent skill that gives the agent a local, on-device search engine over your markdown notes, documentation, meeting transcripts, and other text files. It combines keyword matching, semantic vector search, and LLM reranking, with no cloud dependency. Reach for it when you want the agent to answer questions from your own corpus rather than the open web, or when you are building a personal knowledge base the agent can query.
What it does
Qmd indexes directories you register as collections, then answers queries against that index in three escalating modes. A keyword mode uses BM25 over SQLite FTS5 for exact terms and identifiers. A vector mode uses an embedding model for conceptual questions. A hybrid mode runs both, expands the query with a small language model, fuses the results, and reranks the top candidates with a dedicated reranker model. All of this runs on your machine. The skill wraps the upstream qmd tool by Tobi Lütke, which is MIT licensed, and the skill itself is MIT licensed, version 1.0.0, authored by Hermes Agent + Teknium.
The practical value is that the agent can search your notes the way you would, not just grep them. You ask "what decisions were made about the database migration" and the pipeline finds the relevant passages even when the wording differs. You ask for handleError async and the fast keyword path returns in a fraction of a second without loading any models.
Before you start
Qmd is an optional skill, installed on demand from optional-skills/research/qmd. It supports macOS and Linux only.
You need Node.js 22 or newer. The source gives install paths for both platforms:
# Check version
node --version # must be >= 22
# macOS — install or upgrade via Homebrew
brew install node@22
# Linux — use NodeSource or nvm
curl -fsSL https://deb.nodesource.com/setup_22.x | sudo -E bash -
sudo apt-get install -y nodejs
# or with nvm:
nvm install 22 && nvm use 22
On macOS there is a second prerequisite: the system SQLite lacks extension loading, so you install Homebrew's SQLite. This is a macOS-only step.
brew install sqlite
Then install qmd globally, either with npm or Bun:
npm install -g @tobilu/qmd
# or with Bun:
bun install -g @tobilu/qmd
The first run auto-downloads three local GGUF models, roughly 2GB in total. The source lists them explicitly:
| Model | Purpose | Size |
|---|---|---|
| embeddinggemma-300M-Q8_0 | Vector embeddings | ~300MB |
| qwen3-reranker-0.6b-q8_0 | Result reranking | ~640MB |
| qmd-query-expansion-1.7B | Query expansion | ~1.1GB |
Verify the install before doing anything else:
qmd --version
qmd status
In practice, the model download is the slowest part of setup. Budget for it once, and know that it is a one-time operation. If you are on a metered connection or a machine with little free disk, plan around it.
Quick reference
The source provides a compact command table. Speeds are the source's own figures.
| Command | What It Does | Speed |
|---|---|---|
qmd search "query" | BM25 keyword search (no models) | ~0.2s |
qmd vsearch "query" | Semantic vector search (1 model) | ~3s |
qmd query "query" | Hybrid + reranking (all 3 models) | ~2-3s warm, ~19s cold |
qmd get | Retrieve full document content | instant |
qmd multi-get "glob" | Retrieve multiple files | instant |
qmd collection add --name | Add a directory as a collection | instant |
qmd context add "description" | Add context metadata to improve retrieval | instant |
qmd embed | Generate/update vector embeddings | varies |
qmd status | Show index health and collection info | instant |
qmd mcp | Start MCP server (stdio) | persistent |
qmd mcp --http --daemon | Start MCP server (HTTP, warm models) | persistent |
Setup workflow
1. Add collections
A collection is a directory qmd indexes. Point it at each corpus you want searchable:
# Add a notes directory
qmd collection add ~/notes --name notes
# Add project docs
qmd collection add ~/projects/myproject/docs --name project-docs
# Add meeting transcripts
qmd collection add ~/meetings --name meetings
# List all collections
qmd collection list
Keep collection names short and stable. You will reference them later when scoping searches and when writing context descriptions.
2. Add context descriptions
Context metadata tells the search engine what a collection is about. The source states this significantly improves retrieval quality, so treat it as part of setup rather than an optional polish step:
qmd context add qmd://notes "Personal notes, ideas, and journal entries"
qmd context add qmd://project-docs "Technical documentation for the main project"
qmd context add qmd://meetings "Meeting transcripts and action items from team syncs"
Write the description as if briefing a new colleague on what lives in that folder. Vague descriptions give the retriever less to work with.
3. Generate embeddings
qmd embed
This processes every document in every collection and generates vector embeddings. Re-run it after adding new documents or collections. If you skip this step, vector and hybrid search have nothing to match against.
4. Verify
qmd status # shows index health, collection stats, model info
Search patterns
Fast keyword search (BM25)
Use this for exact terms, code identifiers, names, and known phrases. No models load, so results are near-instant.
qmd search "authentication middleware"
qmd search "handleError async"
Semantic vector search
Use this for natural language questions and conceptual queries. The embedding model loads on the first query, which the source puts at about 3 seconds.
qmd vsearch "how does the rate limiter handle burst traffic"
qmd vsearch "ideas for improving onboarding flow"
Hybrid search with reranking
This is the highest-quality mode. It uses all three models: query expansion, parallel BM25 and vector retrieval, then reranking. Reserve it for queries where the answer matters.
qmd query "what decisions were made about the database migration"
Structured multi-mode queries
You can combine search types in one query. The source shows a BM25 term alongside a vector concept, and a variant with query expansion:
# BM25 for exact term + vector for concept
qmd query $'lex: rate limiter\nvec: how does throttling work under load'
# With query expansion
qmd query $'expand: database migration plan\nlex: "schema change"'
Query syntax in lex/BM25 mode
The keyword mode supports a small operator set:
| Syntax | Effect | Example |
|---|---|---|
term | Prefix match | perf matches "performance" |
"phrase" | Exact phrase | "rate limiter" |
-term | Exclude term | performance -sports |
HyDE (hypothetical document embeddings)
For complex topics, write out what you expect the answer to look like and let the pipeline embed that instead of the question:
qmd query $'hyde: The migration plan involves three phases. First, we add the new columns without dropping the old ones. Then we backfill data. Finally we cut over and remove legacy columns.'
Scoping to collections
Restrict a search to one collection when you know where the answer lives:
qmd search "query" --collection notes
qmd query "query" --collection project-docs
Output formats
qmd search "query" --json # JSON output (best for parsing)
qmd search "query" --limit 5 # Limit results
qmd get "#abc123" # Get by document ID
qmd get "path/to/file.md" # Get by file path
qmd get "file.md:50" -l 100 # Get specific line range
qmd multi-get "journals/*.md" --json # Batch retrieve by glob
When the agent is the consumer, --json is the sensible default because it parses cleanly. When you are reading results yourself, the plain output is easier on the eye.
MCP integration
Qmd exposes an MCP server, and the source calls this the preferred integration. Once configured, Hermes Agent gets qmd tools through its native MCP client without loading the skill each time.
Option A: stdio mode
Add this to ~/.hermes/config.yaml:
mcp_servers:
qmd:
command: "qmd"
args: ["mcp"]
timeout: 30
connect_timeout: 45
This registers the tools mcp_qmd_search, mcp_qmd_vsearch, mcp_qmd_deep_search, mcp_qmd_get, and mcp_qmd_status. The tradeoff is that models load on the first search call, which the source puts at about 19 seconds cold, then stay warm for the session. That is acceptable for occasional use.
Option B: HTTP daemon mode
Start the daemon separately so models stay warm in memory:
# Start daemon (persists across agent restarts)
qmd mcp --http --daemon
# Runs on http://localhost:8181 by default
Then point Hermes Agent at it over HTTP:
mcp_servers:
qmd:
url: "http://localhost:8181/mcp"
timeout: 30
The tradeoff is roughly 2GB of RAM while running, but every query is fast at about 2 to 3 seconds. This is the right choice if you search your knowledge base often.
Keeping the daemon running
macOS (launchd)
cat > ~/Library/LaunchAgents/com.qmd.daemon.plist << 'EOF'
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.qmd.daemon</string>
<key>ProgramArguments</key>
<array>
<string>qmd</string>
<string>mcp</string>
<string>--http</string>
<string>--daemon</string>
</array>
<key>RunAtLoad</key>
<true/>
<key>KeepAlive</key>
<true/>
<key>StandardOutPath</key>
<string>/Users/YOU/.hermes/cache/scratch/qmd-daemon.log</string>
<key>StandardErrorPath</key>
<string>/Users/YOU/.hermes/cache/scratch/qmd-daemon.log</string>
</dict>
</plist>
EOF
launchctl load ~/Library/LaunchAgents/com.qmd.daemon.plist
Replace YOU in the log paths with your macOS username before loading the plist.
Linux (systemd user service)
mkdir -p ~/.config/systemd/user
cat > ~/.config/systemd/user/qmd-daemon.service << 'EOF'
[Unit]
Description=QMD MCP Daemon
After=network.target
[Service]
ExecStart=qmd mcp --http --daemon
Restart=on-failure
RestartSec=10
Environment=PATH=/usr/local/bin:/usr/bin:/bin
[Install]
WantedBy=default.target
EOF
systemctl --user daemon-reload
systemctl --user enable --now qmd-daemon
systemctl --user status qmd-daemon
MCP tools reference
Once connected, these tools are available under the mcp_qmd_* prefix:
| MCP Tool | Maps To | Description |
|---|---|---|
mcp_qmd_search | qmd search | BM25 keyword search |
mcp_qmd_vsearch | qmd vsearch | Semantic vector search |
mcp_qmd_deep_search | qmd query | Hybrid search + reranking |
mcp_qmd_get | qmd get | Retrieve document by ID or path |
mcp_qmd_status | qmd status | Index health and stats |
The MCP tools accept structured JSON queries for multi-mode search:
{
"searches": [
{"type": "lex", "query": "authentication middleware"},
{"type": "vec", "query": "how user login is verified"}
],
"collections": ["project-docs"],
"limit": 10
}
CLI usage without MCP
If MCP is not configured, the agent can still drive qmd through the terminal. The source shows the pattern for a query and for setup tasks:
terminal(command="qmd query 'what was decided about the API redesign' --json", timeout=30)
For setup and management, always use the terminal:
terminal(command="qmd collection add ~/Documents/notes --name notes")
terminal(command="qmd context add qmd://notes 'Personal research notes and ideas'")
terminal(command="qmd embed")
terminal(command="qmd status")
How the search pipeline works
Knowing the internals helps you pick a mode rather than guessing.
- Query expansion. A fine-tuned 1.7B model generates two alternative queries. The original gets 2x weight in fusion.
- Parallel retrieval. BM25 over SQLite FTS5 and vector search run simultaneously across all query variants.
- RRF fusion. Reciprocal Rank Fusion with k=60 merges results. The top rank gets a +0.05 bonus, ranks 2 and 3 get +0.02.
- LLM reranking. The qwen3-reranker scores the top 30 candidates on a 0.0 to 1.0 scale.
- Position-aware blending. Ranks 1 to 3 use 75% retrieval and 25% reranker. Ranks 4 to 10 use 60/40. Ranks 11 and beyond use 40/60, trusting the reranker more for the long tail.
Documents are chunked at natural break points such as headings, code blocks, and blank lines, targeting about 900 tokens with 15% overlap. Code blocks are never split mid-block.
Best practices
- Always add context descriptions. The source says
qmd context adddramatically improves retrieval accuracy, so describe what each collection contains. - Re-embed after adding documents.
qmd embedmust be re-run when new files land in collections. - Use
qmd searchfor speed. For code identifiers and exact names, BM25 is instant and needs no models. - Use
qmd queryfor quality. For conceptual questions or when the best possible results matter, use hybrid search. - Prefer MCP integration. Once configured, the agent gets native tools without loading the skill each time.
- Daemon mode for frequent users. If you search your knowledge base regularly, the HTTP daemon setup is the better fit.
- Put the most important query first in structured search. The first query gets 2x weight, so lead with the one you are most certain about.
When not to use it
The source does not name direct alternatives, but its own guidance implies the boundaries. If you only need exact-string lookup, the BM25 path is the right tool and the heavier modes add nothing. If your content is not text-based, qmd has nothing to index. If you are on Windows, the skill is not supported. And if you have not set up collections and embeddings, there is nothing to search, so run setup first.
Limits and gotchas
- First-run download. About 2GB of GGUF models downloads automatically on first use. It is a one-time operation, but it is not instant.
- Cold start latency. About 19 seconds when models are not in memory. The fixes are HTTP daemon mode,
qmd searchwhen models are not needed, or stdio MCP mode which loads on first search and stays warm for the session. - macOS extension loading. If you see "unable to load extension", install Homebrew SQLite with
brew install sqliteand make sure it is on PATH ahead of the system SQLite. - No collections found. Add directories with
qmd collection add --nameand then index them withqmd embed. - Multilingual content. For CJK or other non-English material, set the
QMD_EMBED_MODELenvironment variable to a multilingual model:
export QMD_EMBED_MODEL="your-multilingual-model"
- Data storage. The index and vectors live at
~/.cache/qmd/index.sqlite. Models are auto-downloaded to a local cache on first run. There are no cloud dependencies; everything runs locally.
What pairs with it
Qmd sits naturally beside the obsidian skill if your notes live in a vault, and beside arxiv if your research reading needs to be searchable alongside your own writing. The hermes-agent skill is the general reference for the runtime itself. Upstream, the project lives at tobi/qmd, and the changelog tracks releases.