AgentStack
Browse Sign in
Browse Why AgentStack Sell Docs
Sign in
MCP verified MIT Self-run

Vecgrep

mcp-jeffbai996-vecgrep · by jeffbai996

grep using meaning and similarity instead of keywords - local/self-hosted, multimodal, and runnable on any user-defined corpus

No reviews yet
0 installs
36 views
0.0% view→install

Install

$ agentstack add mcp-jeffbai996-vecgrep

✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.

Security review

✓ Passed

No issues found. Passed automated security review. · v0.1.0 How review works →

  • Prompt-injection patterns
  • Secret / credential exfiltration
  • Dangerous shell & filesystem operations
  • Untrusted network calls
  • Known-malicious package signatures

What it can access

  • Network access No
  • Filesystem access No
  • Shell / process execution Used
  • Environment & secrets No
  • Dynamic code execution No

From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.

View the full security report →

Verified badge

Passed review? Show it. Paste this badge into your README, it links to the public security report.

AgentStack Verified badge Links to your public security report.
[![AgentStack Verified](https://agentstack.voostack.com/badges/verified.svg)](https://agentstack.voostack.com/security/report/mcp-jeffbai996-vecgrep)

Reliability & compatibility

Security review passed
0 installs to date
no reviews yet
2mo ago

Declared compatibility

Claude CodeClaude DesktopCursorWindsurf

Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.

Preview Execution monitoring

We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.

How agent discovery & health will work →
Are you the author of Vecgrep? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

vecgrep

> grep for meaning, not keywords

v1.0.0 · stable CLI + HTTP API surface · see [CHANGELOG.md](CHANGELOG.md) for release history

vecgrep is a local-first semantic search engine for any corpus you throw at it. Drop in documents — text, markdown, PDFs, URLs — and search by concept instead of exact words. Runs on your machine, no cloud roundtrip required.

The web UI: index a corpus from the sidebar, pick a mode (hybrid / vector / bm25), search. Each result shows confidence (high / soft / weak), which retriever found it (V semantic, K keyword, VK both), and the matched chunk in context. Tucked into the bottom of the page is a primer explaining how vecgrep finds things — vector vs BM25 vs hybrid, what the % can and can't tell you.

$ vecgrep search "what did we decide about rate hikes" --corpus notes
[1]  92.4%  notes  papers/2026-q1-review.md
     ... the committee discussed the path forward and
     the committee opted to hold rates steady through Q2
     citing softening labor data ...

[2]  88.1%  notes  notes/meeting-mar-12.md
     ... after some back and forth
     the rate path discussion landed on no change
     for the rest of the year ...

Why

Keyword grep is brittle. You search for rate hikes, you miss the doc that says monetary tightening. Embeddings fix that — semantically equivalent text scores high regardless of wording.

vecgrep wraps that into a tool that feels like a CLI utility, not a research notebook. No cloud account, no Docker, no Postgres extension, no ceremony. Index a folder, run a query, get ranked chunks back with surrounding context.

The closest equivalents — txtai, chroma, LlamaIndex — are libraries you import. vecgrep is a binary you run.

Contents

  • [Status](#status)
  • [Install](#install)
  • [Quickstart](#quickstart)
  • [What it indexes](#what-it-indexes)
  • [How it works](#how-it-works)
  • [Storage layout](#storage-layout)
  • [Configuration](#configuration)
  • [Web UI](#web-ui)
  • [MCP server](#mcp-server)
  • [Roadmap](#roadmap)
  • [Why not just use X?](#why-not-just-use-x)
  • [Contributing](#contributing)
  • [Tests](#tests)
  • [License](#license)

Status

Alpha (v0.7.x). What's shipped:

  • Retrieval — hybrid (BM25 + vector + RRF, BM25-weighted by default), cross-encoder rerank, optional per-corpus recency decay.
  • Serving — MCP server (stdio + streamable HTTP), named corpora, optional bearer-token auth. Corpora embedded with different models can be served side by side — each queries with its own model.
  • Ops — incremental indexing, file watcher, embedding cache, model/backend migration, vecgrep status diagnostic, hermetic pytest suite.
  • UI — web UI with confidence-tier coloring + V/K/VK match badges.
  • Adapters — plaintext, markdown, PDF, URLs, Discord JSONL, Claude export, ChatGPT export.

The public API (HTTP + CLI flags) is unstable until v1.0 — expect breaking changes within v0.x.

Install

pip install vecgrep                # base — Ollama embedding, hybrid search
pip install "vecgrep[openai]"      # also: OpenAI embedding fallback
pip install "vecgrep[rerank]"      # also: cross-encoder reranking (~hundreds of MB, torch)
pip install "vecgrep[watch]"       # also: file watcher for `vecgrep watch`
pip install "vecgrep[mcp]"         # also: MCP server for Claude Desktop / Cursor

You also need Ollama running locally for the default embedding backend:

ollama pull bge-m3
ollama serve

If you'd rather use OpenAI, install with [openai], set OPENAI_API_KEY, and vecgrep will fall back to text-embedding-3-small automatically when Ollama isn't reachable.

Quickstart

# Index a folder (mixed file types are fine; incremental — unchanged files skipped)
vecgrep index ./my-docs --corpus papers

# Re-embed everything regardless of content hash
vecgrep index ./my-docs --corpus papers --force

# Index a URL — vecgrep fetches and strips boilerplate
vecgrep index https://example.com/article --corpus web

# Watch a folder and re-index on change
vecgrep watch ./my-docs --corpus papers

# Hybrid search (default — BM25 + vector fused via RRF), top 10
vecgrep search "missile guidance systems" --corpus papers --top 10

# Pure-vector or pure-BM25 if you want to A/B
vecgrep search "rate hikes" --mode vector
vecgrep search "FOMC" --mode bm25

# Watch mode — re-run the same query at a fixed interval, print a diff
# of source IDs. Pair with `vecgrep watch` while ingesting to confirm
# new chunks are showing up where you expect.
vecgrep search "rate hikes" --watch --interval 5

# Cross-encoder reranking on the candidate pool — slower, more accurate
vecgrep search "what did we decide about rates" --rerank

# Filter results — repeatable, all ANDed
vecgrep search "rate hikes" --filter "source:*2026*.md" --filter "corpus:papers"

# Show the score decomposition for each hit (cosine, BM25, RRF, rerank)
vecgrep search "rate hikes" --explain

# Inspect / clear the embedding cache
vecgrep cache stats
vecgrep cache clear --identity ollama:bge-m3

# Re-embed a corpus to a different backend / model
vecgrep corpora migrate papers --to-backend openai --to-model text-embedding-3-small

# Recency decay: down-rank stale chunks. Half-life in days; a hit one half-life
# old ranks as if half as relevant. Good for chat logs / journals; leave off for
# durable reference. No re-index needed — applied at search time from each
# chunk's parsed date.
vecgrep corpora decay chatlogs --half-life 14
vecgrep corpora decay chatlogs --off

# Search across every corpus you have
vecgrep search "rate hikes"

# Don't persist — index, query, throw away
vecgrep index ./scratch --corpus tmp --ephemeral

# Move a corpus between machines
vecgrep corpora export papers --out papers.tar.gz
vecgrep corpora import papers.tar.gz --rename papers-from-laptop

# Manage corpora
vecgrep corpora list
vecgrep corpora delete papers

# One-shot diagnostic: daemon, auth, per-corpus chunk counts, last-update age
vecgrep status
vecgrep status --json    # for scripting / monitoring

# Launch the web UI on http://127.0.0.1:8765
vecgrep serve

What it indexes

| Adapter | Inputs | |---|---| | plaintext | .txt, .log, .csv, .tsv, .rst, .org, .tex, .srt, extensionless | | markdown | .md, .markdown, .mdx | | pdf | .pdf (text layer only — scanned PDFs need OCR first) | | url | http://, https:// — strips `, , etc., keeps prose | | discordjsonl | .jsonl files where each line is a Discord message (DiscordChatExporter format or any one-message-per-line JSONL with author + content) | | claudeexport | Anthropic data export conversations.json — one Document per conversation | | chatgpt_export | OpenAI data export conversations.json` — one Document per conversation, follows main thread |

Pointing vecgrep index at a directory walks it recursively and dispatches each file to the matching adapter. Unrecognized files are skipped silently — no errors.

How it works

                          ┌──▶ vector (qdrant) ──┐
docs ──▶ adapters ──▶ chunkers ──┤                      ├──▶ RRF ──▶ [decay] ──▶ dedup ──▶ [rerank] ──▶ top-k
                          └──▶ bm25 (inverted) ──┘
  • Adapters convert source formats to text. They run once per source; chunkers handle slicing.
  • Chunkers slice text into overlapping windows. SentenceWindowChunker is the default — 3 sentences with 1-sentence overlap. FixedTokenChunker (tiktoken-backed) is the alternate for code, logs, anything where sentence boundaries are noisy.
  • Embed backends are pluggable. Each corpus pins the model it was built with; corpora on different models can be served at once, each querying with its own.
  • Default: Ollama bge-m3 (1024-dim) — strong on paraphrase-heavy and multilingual queries.
  • Alternates: any other Ollama model via VECGREP_EMBED_MODEL (e.g. nomic-embed-text, 768-dim, lighter/faster; mxbai-embed-large, 1024-dim).
  • Fallback: OpenAI text-embedding-3-small (1536-dim) takes over when Ollama is unreachable and OPENAI_API_KEY is set.
  • Qdrant runs in embedded mode (no server, no Docker) at ~/.vecgrep/qdrant/. Each named corpus is its own collection.
  • BM25 index runs alongside Qdrant, persisted as a pickle per corpus. Tokenizer splits identifiers (sharpe_ratiosharpe, ratio) so code search isn't blind to underscore- or camelCase-style naming.
  • Hybrid retrieval is the default. Each retriever returns its top 50 candidates; their ranks are fused via Reciprocal Rank Fusion (score = Σ w / (60+rank)). BM25's weight is 1.5 by default — high enough to float exact-keyword hits over the vector noise floor on short queries, low enough to leave long conceptual queries vector-dominated (override with VECGREP_BM25_WEIGHT). Pure-vector or pure-BM25 are available with --mode vector / --mode bm25.
  • Recency decay (optional, per corpus). Set a half-life in days with vecgrep corpora decay --half-life N and a hit's fused score is multiplied by 0.5 ** (age_days / half_life) — a chunk one half-life old ranks as if half as relevant. Applied before the top-k cut, so a fresh chunk can rescue itself above a stale one, and lexical closeness alone can't float a stale chunk to the top. Off by default; undated chunks are never penalized. The date comes from a doc_timestamp parsed at index time (frontmatter date:/Saved: lines, a YYYY-MM-DD filename, then file mtime). Tune fast for chat/journal corpora, off for durable reference.
  • Dedup. The sentence-window chunker emits overlapping windows, so one passage can surface as several near-identical hits. vecgrep collapses same-source hits whose character ranges overlap before truncating to top-k, keeping the strongest, so the result list isn't padded with the same text at three ranks.
  • Confidence display. The displayed % is a calibrated relevance estimate, not a raw score; ranking always uses the underlying fused score.
  • With reranking on: comes straight from the cross-encoder (the cleanest P(relevant) proxy).
  • Otherwise: a sigmoid over cosine, calibrated per embedding model — different models put their noise floor and signal range in different places, so the same % means the same thing across corpora.
  • BM25-only hits (which have no cosine) are rescaled rank-relative, so a real keyword match doesn't read as the ~1.6% raw-RRF noise floor.
  • The web UI surfaces V/K/VK badges and tier colors plus a "how search works" panel.
  • Cross-encoder reranker (--rerank, off by default) rescores the candidate pool with BAAI/bge-reranker-base. Lazy-loaded — the heavy torch import only happens when you ask for it. It's a real quality win on hard, paraphrase-heavy queries where plain hybrid whiffs, but it adds meaningful latency (order ~100ms+ for a small candidate pool) and on easy literal queries it can be a wash or worse — so it's opt-in, not the default. Reach for it when a hybrid search returns near-misses for something you know is in the corpus.

Each corpus pins the embedding backend, model, and dimension at index time and refuses to mix models within a single corpus — but the engine resolves each corpus's query embedding from its own pinned model, so multiple corpora on different models coexist fine. To change the model of an existing corpus, vecgrep corpora migrate it (or recreate it).

Storage layout

~/.vecgrep/
├── qdrant/         # vector store, one collection per corpus
├── bm25/           # BM25 inverted index, one pickle per corpus
├── corpora.json    # named-corpus metadata
└── config.json     # optional, env vars override

--ephemeral mode (CLI flag or UI toggle) keeps everything in memory and skips both files and the vector store on disk. Useful for a one-shot grep over a directory you don't want polluting your indexed corpora.

Configuration

vecgrep reads from ~/.vecgrep/config.json and environment variables, in that order. Env vars win.

| Variable | Default | Notes | |---|---|---| | VECGREP_HOME | ~/.vecgrep | Storage root | | VECGREP_OLLAMA_URL | http://localhost:11434 | Ollama endpoint | | VECGREP_EMBED_MODEL | bge-m3 | Ollama model | | VECGREP_OPENAI_EMBED_MODEL | text-embedding-3-small | OpenAI model | | OPENAI_API_KEY | unset | If set, used as fallback when Ollama is down | | VECGREP_API_HOST | 127.0.0.1 | API bind host | | VECGREP_API_PORT | 8765 | API port | | VECGREP_API_TOKEN | unset | If set, /api/* requires Authorization: Bearer (health stays public) | | VECGREP_TOP_K | 5 | Default --top value | | VECGREP_ALIASES_FILE | $VECGREP_HOME/aliases.json | Entity alias map (personal data — keep it out of any repo). See docs/aliases.example.json. Missing = no expansion. | | VECGREP_OAUTH_ENABLED | unset | 1 enables OAuth 2.1 on /mcp (embedded auth server: /authorize, /token, /.well-known). | | VECGREP_OAUTH_ISSUER_URL | unset | Public base URL the MCP endpoint is reachable at. Required when OAuth is on. | | VECGREP_BM25_WEIGHT | 1.5 | Weight on BM25 contribution to RRF fusion. >1 boosts literal-keyword matches over semantic noise on short queries. Set to 1.0 for pure RRF, higher for keyword-leaning ranking. | | VECGREP_BM25_COVERAGE_MODE | penalty | How BM25 treats docs that match only some query tokens. penalty keeps them but demotes the score by (matched/total)²; filter drops anything below a coverage threshold (higher precision, but can zero out the BM25 half of a multi-token query). | | VECGREP_EMBED_CACHE_MAX_ROWS | 50000 | Row cap on the sqlite embedding cache (~1 GB at a 1024-dim model). Oldest entries are evicted first once over the cap. Set / GET /api/chunk/... / MCP get_chunk.

  • Hard filters — the caller passes explicit constraints; vecgrep never

guesses intent: --filter date:2026-01-15 (or date:today), after: (or relative: after:7d, after:24h, after:2w), before:, channel:, source_path:, speaker: (who said it — chunk-level, [bot] suffix optional), bot:true|false, has:code|table|link (content shape). A leading - inverts any filter (-corpus:scratch excludes). Recognized filters fail closed (a typo'd date reads as zero results, not silently ignored) — in either polarity.

  • Insight tools (v1.2)related (query-by-example: more

evidence like this chunk, no re-embedding), compare (one query, two time windows, source-level delta — "how did we talk about X then vs now"), stats (counts, date coverage, gap days — a broken archiver shows up here), summarize (speaker tally + span + sampled chunks for rollups; sampling always explicit). All four: CLI + /api/* + MCP.

  • Timeline mode — "what happened?" gets an ordered event sequence, not

ranked chunks: vecgrep timeline "query" / POST /api/timeline / MCP timeline. Contiguous chronological slices grouped by source file, speakers + timestamps preserved.

  • Incident objectservice.incident() / MCP incident: one structured

answer (title, sources, participants, time range, primary timeline, related context separated, confidence).

  • Alias expansion — one entity, many surface forms (nickname ⇄ handle ⇄

another language), config-driven via an out-of-repo map (VECGREP_ALIASES_FILE); a query naming one form finds evidence written under the others.

  • Clear scores — every result carries relevance_pct, a qualitative

relevance_label (exact / strong / related / weak), raw component scores, and a precise anchor citation (path#L12-L24).

Web UI

vecgrep serve boots the FastAPI server and serves a single-page React UI from the same port. Every action the UI supports has a CLI equivalent.

Controls:

  • Index forms with a built-in dropdown explainer for source types.
  • Corpus list with delete.
  • Search bar with top-k slider, mode toggle (hybrid/vector/bm25), and reranker checkbox.

Source & license

This open-source MCP server is cataloged on AgentStack and links to its original source — we do not rehost the code.

Install and usage instructions live in the source repository linked above.

Reviews

No reviews yet, be the first.

Versions

  • v0.1.0 Imported from the upstream source.