Technical Briefing: qmd

The Problem

QMD is a local search engine combining BM25 full-text search, vector semantic search, and LLM reranking via GGUF models. It indexes markdown notes, meeting transcripts, and documentation for agentic workflows. The codebase contains 120 files across TypeScript and Python, with execution starting from MCP server or CLI entry points that route through a central store and database layer.

What This Does

QMD exposes an MCP server with query, get, multi_get, and status tools, plus a CLI for indexing and searching. The TypeScript codebase (30 files) handles the primary interface, while 20 Python files in finetune/ manage model training and evaluation. A single GGUF model inference call occurs during search reranking. The repository has no lockfile for Python dependencies (finetune/pyproject.toml), creating non-reproducible builds. Eighty-three test files exist across TypeScript and Python, but CI lacks dependency vulnerability scanning.

How It Is Wired

Execution starts at src/mcp/server.ts:522 (startMcpServer, reaches 29 functions) or src/index.ts:331 (createStore, reaches 18 functions). The internal call graph has 701 resolved edges between self-contained functions. Key hubs: src/store.ts (109 functions, 31 types, called from 9 files, instability 0.18) and src/llm.ts (60 functions, 22 types, calls model for inference). Traced paths show startMcpServer -> createStore -> syncConfigToDb reaches the database; createSession -> createMcpServer -> buildInstructions -> getStatus also routes through DB. src/cli/qmd.ts (67 functions, 4 types) is the CLI entry point, calling into 8 modules and reading/writing the database. The module graph shows src/store as the most imported module (14 dependents), creating high blast radius for changes.

Entry points and reach:

  • startMcpServer src/mcp/server.ts:522 → 29 functions
  • startMcpHttpServer src/mcp/server.ts:543 → 24 functions
  • createStore src/index.ts:331 → 18 functions
  • main src/bench-rerank.ts:189 → 6 functions

What code touches outside itself: 63 functions read/write database; 7 call model for inference; 1 reads/writes files.

How To Use It

Setup: Install via bun install -g @tobilu/qmd or npm install -g @tobilu/qmd. No Dockerfile exists; container setup requires test/Containerfile (debian:bookworm-slim, no USER directive). Environment variables or config files are not explicitly documented in the analysis.

Configuration: No specific env vars identified in static analysis. The MCP server can use stdio (default) or HTTP transport (qmd mcp --http for localhost:8181).

Running it:

# MCP server stdio (default)
qmd mcp

# MCP server HTTP transport
qmd mcp --http                    # localhost:8181
qmd mcp --http --port 8080        # custom port

# Background daemon
qmd mcp --http --daemon

# CLI search examples
qmd search "project timeline"           # Fast keyword search
qmd vsearch "how to deploy"             # Semantic search
qmd query "quarterly planning process"  # Hybrid + reranking

Real-World Use

An agentic workflow might invoke qmd query "error handling" --all --files --min-score 0.4 to retrieve structured JSON results for an LLM, then use qmd get "#docid" --full to fetch specific document content. The MCP query tool supports typed sub-queries (lex/vec/hybrid) combined via RRF + reranking. Integration with Claude Desktop uses the configuration in ~/Library/Application Support/Claude/claude_desktop_config.json pointing to qmd mcp.

Code Health & Issues

The code health audit found 8 findings (0 critical, 1 high, 6 medium, 1 low), ranked by severity and confidence:

  • HIGH - Pin third-party GitHub Actions to a commit SHA: .github/workflows declares oven-sh/setup-bun@v2; a tag can be moved, so the action running with your token and secrets is whatever its owner last pushed.
  • MEDIUM - Declare least-privilege permissions for GITHUB_TOKEN: .github/workflows/ci.yml has 1 workflow declaring no permissions; without declaration the token inherits the repository default.
  • MEDIUM - Enable Dependabot or Renovate: 2 manifest(s) exist but no update bot configured; without a bot a published advisory sits unpatched.
  • MEDIUM - Pin the container base image by digest: test/Containerfile uses debian:bookworm-slim; an untagged or mutable base means today's build and last month's contain different libc and a different CVE set.
  • MEDIUM - Gate pull requests on a dependency vulnerability scan: .github/workflows has no dependency scan in CI; this is the one gate that would catch a known-vulnerable package before it reaches a build.
  • MEDIUM - Set persist-credentials: false on checkout: .github/workflows/ci.yml checkout keeps the token, then dependencies are installed; the token stays in .git/config for every later step.
  • MEDIUM - Add a non-root USER to the image: test/Containerfile has no USER directive; a process running as root in the container is root against every mounted volume.
  • LOW - Set timeout-minutes on the workflow jobs: .github/workflows/ci.yml has 2 workflow(s) declaring no job timeout; a wedged step runs to the six-hour platform default.

The Bottom Line

QMD delivers a functional local search engine with hybrid BM25/vector/reranking capabilities, suitable for on-device knowledge management and agentic workflows. The codebase shows signs of growth without corresponding maintenance: no lockfile for Python dependencies, absent dependency scanning in CI, and a large, unstable store module (src/store.ts) that ripples widely on changes. Teams needing a self-hosted search index with MCP integration can use it today, but should budget refactoring effort for the store layer and establish dependency governance before production deployment.