Ultra-fast indexed code search engine — faster than ripgrep on repeated searches, lighter than zoekt. Built-in MCP server for AI agents. Also does fast file finding (fd/find alternative).
Overview
Ultra-fast indexed code search engine with MCP server for AI coding tools. Pre-builds a trigram inverted index, then searches in milliseconds. Designed for repeated searches on large codebases — by humans and AI agents alike. - — trigram inverted index makes repeated searches 2-46x faster than ripgrep - — --find mode locates files 2-15x faster than fd - — built-in Model Context Protocol server for AI coding tools (Claude Code, Cursor, etc.) - — --format llm produces Markdown with language tags, context lines, and token-aware truncation - — search only changed files (--changed), recent commits (--since 1h), respects .gitignore - — install via cargo/npm/pip, then xg "pattern". Index builds automatically on first search - — files edited since the last index build are rescanned on every search; on Linux, xg serve watches the tree so this check costs ~0.
README
xgrep
Ultra-fast indexed code search engine with MCP server for AI coding tools.
Pre-builds a trigram inverted index, then searches in milliseconds. Designed for repeated searches on large codebases — by humans and AI agents alike.
Features
- Indexed search — trigram inverted index makes repeated searches 2-46x faster than ripgrep
- File discovery —
--findmode locates files 2-15x faster than fd - MCP server — built-in Model Context Protocol server for AI coding tools (Claude Code, Cursor, etc.)
- LLM-optimized output —
--format llmproduces Markdown with language tags, context lines, and token-aware truncation - Git-aware — search only changed files (
--changed), recent commits (--since 1h), respects.gitignore - Zero config — install via cargo/npm/pip, then
xg "pattern". Index builds automatically on first search - Always fresh — files edited since the last index build are rescanned on every search; on Linux,
xg servewatches the tree so this check costs ~0.1ms - Syntax-aware — tree-sitter outlines (
--outline), enclosing-block results (--expand block), definitions, references and a repo map for Rust, Python, JS/TS, Go, Java, C/C++ and Ruby - Boolean and history search — file-level
--and/--not, search insidegit diff(--in-diff) or commit messages (--log)
Why xgrep?
| ripgrep | zoekt | xgrep | |
|---|---|---|---|
| Setup | None | Server required | None (cargo/npm/pip) |
| First search | Instant | After server start | Auto-builds index |
| Repeated search (Linux kernel) | 1,687ms | 170ms (server) | 37ms |
| File discovery (next.js, 26K files) | N/A | N/A | 9ms (fd: 191ms) |
| Index size | N/A | 155% of source | ~13% of source (+6% build-only sidecar) |
| AI agent integration | None | None | MCP server built-in |
xgrep is not a ripgrep replacement. Use ripgrep for one-off searches. Use xgrep when you search the same codebase repeatedly — the index pays for itself after ~2 searches.
Installation
Choose your package manager:
# Rust (recommended — native binary, no runtime)
cargo install xgrep-search # Requires Rust 1.85+
# npm (Node.js 18+)
npm install -g xgrep
# pip (Python 3.8+)
pip install xgrep-search
Prebuilt binaries are also available on the Releases page.
The cargo build and the release binaries include tree-sitter grammars for the syntax-aware features (--outline, --expand block, outline/find_references/repo_map over MCP), which need a C compiler to build and add ~12MB to the binary. To leave them out, use cargo install xgrep-search --no-default-features; the npm and pip packages are built this way.
Quick Start
xg "pattern" # Search (auto-builds index on first run)
Usage
xg "pattern" # Smart-case search (all-lowercase = case-insensitive)
xg "Pattern" # Mixed/upper case in pattern = case-sensitive
xg "pattern" -i # Force case-insensitive
xg "pattern" -s # Force case-sensitive (disable smart-case)
xg "pattern" /path/to/repo # Search a specific directory
xg -e "handle_\w+" # Regex search
xg "pattern" -w # Match whole words only
xg "pattern" -t rs # Filter by file type
xg "pattern" -C 3 # Context lines (symmetric)
xg "pattern" -A 2 -B 1 # 2 lines after, 1 line before
xg "pattern" -g "*.rs" # Include only paths matching glob (repeatable)
xg "pattern" -g "!*_test.rs" # Exclude paths matching glob (! prefix)
xg "pattern" --format llm # Markdown output for LLMs
xg "pattern" --format llm --max-tokens 4000 # Cap LLM output (approx. tokens)
xg "pattern" -M 200 # Cut lines longer than 200 chars (--max-columns)
xg "pattern" --json # JSON Lines: one object per match + summary
xg "pattern" --changed # Only git changed files
xg "pattern" --fresh # Rebuild a very stale index before searching
xg "foo" --and bar --not test # Files containing foo and bar but not test
xg "pattern" --in-diff main...HEAD # Only lines added/removed in a git diff
xg "pattern" --log # Search commit messages
xg "pattern" --expand block # Show the enclosing function/class per match
xg --outline src/lib.rs # List symbols without bodies
xg "pattern" dir1 dir2 # Search several paths
cat app.log | xg "error" # Search standard input (also: xg "error" -)
xg "pattern" --exclude vendor # Exclude paths containing "vendor"
xg "pattern" --absolute-paths # Show absolute paths
xg "pattern" --no-hints # Suppress regex pattern hints
xg --find "*.rs" # Find files by glob pattern
xg --list-types # Show supported file types
xg status # Show index status
xg init # Explicitly rebuild index
Without a PATH, xg searches standard input when it is a pipe or a file with data; otherwise (a terminal, /dev/null, NUL, or empty input as CI runners often attach) it searches the current directory. - always reads standard input. Matches are shown as :LINE:TEXT.
Search is smart-case by default: an all-lowercase pattern matches case-insensitively, while any uppercase letter makes the search case-sensitive. Use -i or -s to override (priority: -i > -s > smart-case).
Environment Variables
| Variable | Description | Default |
|---|---|---|
XGREP_LLM_CONTEXT |
Default context lines for --format llm |
3 |
XGREP_ABSOLUTE_PATHS |
Set to 1 to always use absolute paths |
unset |
XGREP_NO_HINTS |
Set to 1 to suppress regex pattern hints |
unset |
XGREP_MAX_FILESIZE |
Skip larger files when indexing (512K, 20M, 0 = no limit) |
10M |
XGREP_NO_OVERLAY |
Set to 1 to skip rescanning files changed since the index build |
unset |
XGREP_NO_WATCH |
Set to 1 to disable file watching in xg serve |
unset |
Run xg --help for all options.
MCP Server
xgrep runs as an MCP server, giving AI coding tools fast indexed search.
xg serve # Start MCP server
xg serve --root /path/to/repo # Specific directory
Claude Code
{
"mcpServers": {
"xgrep": {
"command": "xg",
"args": ["serve"]
}
}
}
Available tools: search (ranked, paginated, multi-query, expand:"block"), find_definitions, find_references, find_files, outline, repo_map, read_file, index_status, build_index, session_reset
Results already returned in the session are collapsed to one line (already shown: path:L10-40), and every tool honours max_tokens.
See docs/agents.md for agent-oriented usage patterns.
Performance
Measurement environment (tables below): Apple M4, 32GB RAM, NVMe SSD, macOS 15. Warm filesystem cache,
hyperfine --warmup 5 --runs 20. Results on other hardware — especially shared Linux machines or HDD — will differ; see Reproducibility below.
The Linux kernel, next.js and index-cost tables below were measured with v0.7 (index format v3) and have not been re-run for the current index format (v4).
Search: Linux kernel (92,947 files, 2.0GB)
| Query | xg | ripgrep | vs ripgrep | Pattern type |
|---|---|---|---|---|
struct file_operations |
37ms | 1,687ms | 46x faster | focused |
printk |
52ms | 1,756ms | 34x faster | focused |
EXPORT_SYMBOL |
66ms | 1,773ms | 27x faster | focused |
The queries above were selected for xgrep’s sweet spot (trigram index filters well). For distributed patterns that appear across most files, xgrep approaches ripgrep speed — see the Reproducibility section for honest numbers.
File discovery: next.js (27,332 files)
| Query | xg --find | fd | vs fd |
|---|---|---|---|
*.ts (4,838 files) |
20.8ms | 187.3ms | 9x faster |
config (substring) |
12.7ms | 188.1ms | 15x faster |
Index cost
| Metric | xgrep | zoekt |
|---|---|---|
| Build time (Linux kernel) | 6s | 46s |
| Index size | 175MB (8% of source) | 3.0GB (155%) |
| Breakeven | ~2 searches | — |
First run includes a one-time index build. See docs/benchmarks.md for full results including medium/small repos.
v0.7 → current, same machine
crates.io registry sources (13,818 files, 361MB), WSL2 on a 28-core machine, warm cache, median of 5 runs:
| v0.7 | current | |
|---|---|---|
| Index build | 1.8s | 0.2s |
| Index on disk (incl. sidecar) | 138MB | 70MB |
serialize |
43ms | 20ms |
the (151k matching lines) |
107ms | 40ms |
the -l |
107ms | 22ms |
the -g '*.md' |
160ms | 11ms |
Lowercased trigrams with per-posting position/next-byte masks cut candidate files that do not match by ~77%.
Reproducibility
The numbers above were measured on the author’s Apple M4 with a warm filesystem cache. Third-party benchmarks on shared Linux machines (different CPU, spinning disk, cold cache) show a different picture depending on pattern type:
| Query | hits | M4 result | Third-party Linux (example) |
|---|---|---|---|
CONFIG_PREEMPT_RT |
~300 | xg wins big | xg wins big (cache fits in L3) |
EXPORT_SYMBOL_GPL |
~21k | xg wins | xg faster, but gap narrows |
raw_spin_lock_irqsave |
~1.2k | xg wins | xg ≈ ripgrep (even) |
devm_kzalloc |
~7.4k | xg wins | xg slower than ripgrep |
The pattern: when trigram intersection leaves many candidate files (widely-used symbols),
xgrep must scan nearly the whole codebase — the index overhead then outweighs the savings.
This is a known limitation being investigated. Use bench/fair-bench.sh to measure on
your own hardware; the script runs both focused and distributed queries for a complete picture.
Limitations
- Single-character queries bypass the index (full scan) — no speed advantage over ripgrep; 2-character literals use a coarser trigram-prefix lookup
- Large files (> 10MB by default) are left out of the index;
xg status, MCPindex_statusand zero-result hints report how many. SetXGREP_MAX_FILESIZE(e.g.50M, or0for no limit) and rebuild to index them - Binary files (any NUL byte, including UTF-16 text) are not indexed
- Freshness — edited files are rescanned on each search; with more than 500 changed files the search warns and relies on the background rebuild (
--freshrebuilds first) - Syntax features cover Rust, Python, JS/TS, Go, Java, C/C++ and Ruby; other languages fall back to regex heuristics. The npm and Python packages are built without them
- find_references is text-based: different symbols with the same name are not told apart
When to use ripgrep instead: one-off searches, very small codebases (< 100 files), or single-character queries.
How It Works
- Index Build: Walks the codebase, extracts ASCII-lowercased 3-byte trigrams from each file, and builds an inverted index with delta+varint compression plus a 2-byte mask per posting (position mod 8, next-byte bloom)
- Search: Intersects the rarest posting lists first, prunes candidates with the masks, then verifies matches in the files, streaming results in path order
- Freshness: Files changed since the build (found by a parallel stat probe, or by the file watcher in
xg serve) are excluded from the index scan and searched directly - MCP Server: Exposes search, syntax tools and file reading via JSON-RPC over stdio, with ranking, pagination, token budgets and session dedup
Contributing
See CONTRIBUTING.md for development setup and guidelines.
License
Licensed under either of
- Apache License, Version 2.0 (LICENSE-APACHE or )
- MIT license (LICENSE-MIT or )
at your option.
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.
Install
This server does not publish a one-line install command.
Open the repository installation guide New to MCP? Start with the mcpskill.org guide →Configuration
{
"mcpServers": {
"xgrep": {
"command": "xg",
"args": ["serve"]
}
}
}