TL;DR

XERJ (xerj-org/xerj) is a search engine written in Rust that ships as one static binary. You point xerj autoindex at a folder, and it works out what each file is (source code, PDF, CSV, log, mailbox, Office document) and builds one searchable index per dataset it finds. Your coding agent then asks the index for the passage it needs instead of grepping and reading whole files into its context window. It speaks the Elasticsearch REST API, so existing clients and Kibana connect to it, and the same binary is an MCP server with 13 tools.

Facts as of October 16, 2026:

  • 6,205 GitHub stars, 325 forks, Apache-2.0. GitHub Trending lists it with 715 stars today.
  • Created June 30, 2026. Latest release: v1.0.0-rc.95 (October 12, 2026). There is no 1.0 final yet; the project shipped 16 release candidates between October 1 and October 12.
  • 11 open issues and 25 open pull requests.
  • Prebuilt for Linux, macOS and Windows on x86-64 and arm64. No JVM.

Not run by us: our hands-on lab (a throwaway GitHub Actions container) could not start today because the job was blocked by a billing limit on the account. Everything below comes from the repository, its docs, the project’s own benchmark pages and public issue reports. We did not install XERJ or index anything with it.

Our verdict: XERJ is the most candid “search for agents” project we have read. It publishes its losses next to its wins and states where its headline number stops applying. The core idea is sound: an agent that gets a function with its line number spends far fewer tokens than one that greps and opens files. But the token savings only show up on code the model has not memorised, ranking on messy multilingual repos and logs is still rough, and this is a release-candidate engine with a lot of surface area. Try it on a private or niche codebase. Do not expose it to a network.

What problem it solves

Coding agents such as Claude Code, Codex and Cursor find things with grep, find and file reads. A grep hit is one line; to judge it, the agent opens the whole file. On a big tree that adds up fast. The README’s example: the PHP in WordPress core is about 5.2 million tokens, or 26 full context windows.

XERJ’s answer is to index the tree once and let the agent query it. Source files go through tree-sitter grammars (13 languages, per the case study), so each record carries the language, every symbol with its kind and line number, a searchable defs field, and the full body. A query returns the enclosing function with file:line, not a stray line.

The second idea is what the project calls reference coding. Before writing code against an unfamiliar API, the agent clones the open-source repos closest to the task, indexes them, and retrieves how they solved the problem. The README’s suggested one-prompt setup is literally an instruction to your agent:

Install XERJ (docs: https://xerj.org/llms.txt), index this project's sources, and set up
reference coding: clone and index the open-source repos closest to what we're building,
and search how they solved a problem before writing code.

That prompt asks an agent to download and run a binary from the internet and clone third-party repos. Read llms.txt yourself before handing it over.

Install and first commands

From the README:

curl -fsSL https://xerj.org/get | sh
xerj --insecure --data-dir ./data &
xerj init                                        # wire XERJ into Claude Code/Cursor (MCP + skill)
xerj autoindex ~/my-project
xerj search "how do we rotate the WAL segment"   # ranked passages, file:line
xerj def "euler_to_rotationmatrix"               # go-to-definition: file:line + signature
xerj gain                                        # token savings it reports for you

The installer downloads the matching release archive from GitHub and verifies its SHA-256 before installing to ~/.local/bin. Linux gets a static musl build by default (XERJ_LIBC=gnu for glibc). Windows uses irm https://xerj.org/get.ps1 | iex, and you can build from source with stable Rust.

The README prints sample autoindex output from a large mixed tree: 593 datasets inferred, 25,329 files indexed with 8 workers, done in 158.1 seconds. That is the project’s run, not ours.

The --insecure flag matters

Every quick-start command starts the node with --insecure. The README is blunt about what that means: no TLS and no API-key auth, so “every caller that can reach the port is a superuser who can read and delete every index, including every /_memory namespace.” A fresh node binds 127.0.0.1 only, and a cleartext bind on any other interface refuses to start unless you explicitly allow it.

Without --insecure, auth is on: an admin key is minted on first boot at <data-dir>/admin.key, and you pass it to autoindex with --api-key or XERJ_API_KEY. The full model is in docs/SECURITY_MODEL.md, which ties every claim to a source file and line. For a laptop, --insecure on localhost is fine. For a shared dev box or a container that other services can reach, start with auth.

What it indexes

Source code (tree-sitter), CSV, JSON, XML, YAML, SQLite, PDF, DOCX, PowerPoint, Excel, EPUB, Jupyter notebooks, HTML, .eml and mbox mail, common log formats and Unity scenes; rc.95 added legacy .doc and .xls. Re-indexing is incremental: byte-identical files skip the parse, so a re-run after a small edit costs roughly what changed. The project reports single-threaded extraction of about 1,500 files per second on a 6,113-file Apache Lucene checkout.

Searching: the Elasticsearch API

XERJ implements the Elasticsearch REST API: indices, documents, bulk, search, aggregations, mappings, kNN, scroll, reindex and the _cat endpoints. If you have used Elasticsearch, you already know the query side:

curl localhost:9200/_cat/indices
curl "localhost:9200/ax-*/_search?q=checkout+error"
curl localhost:9200/ax-orders/_search -H 'content-type: application/json' -d '{
  "query": { "range": { "total": { "gte": 100 } } },
  "aggs":  { "by_status": { "terms": { "field": "status" } } }
}'

The project reports passing 1,366 of 1,369 cases in Elasticsearch’s YAML conformance suite, with the gaps listed in the repo. It is explicit that this is API compatibility, not a fork or a reimplementation of Elasticsearch internals. One documented difference: scroll is a bounded up-front snapshot, so a query whose total exceeds the window gets a 400 instead of silently truncated results. Use search_after for large exports.

Vector and hybrid search use Elasticsearch’s knn and semantic syntax. Note the default embedder: it is lexical feature-hashing, which captures vocabulary overlap, not meaning. Neural embeddings are opt-in (--embed-mode neural) and download about 90 MB of model files. If you pick “semantic” search without switching modes, you get keyword-flavoured similarity under a semantic label. The docs say so; plenty of users will miss it.

Connecting an agent over MCP

The same binary is the MCP server. Start the node, then register xerj mcp in your client:

{
  "mcpServers": {
    "xerj": {
      "command": "/home/you/.local/bin/xerj",
      "args": ["mcp"],
      "env": { "XERJ_URL": "http://localhost:9200" }
    }
  }
}

Use the absolute path; desktop MCP hosts do not inherit your shell’s PATH. With auth on, add "XERJ_AUTH": "ApiKey <key>".

It exposes 13 tools. Eleven are thin proxies over existing endpoints: xerj_search, xerj_semantic_search, xerj_vector_search, xerj_hybrid_search, xerj_memory_store / xerj_memory_recall for durable agent memory, four xerj_brain_* tools for a link graph over your documents, and xerj_plan, which turns a plain-words filter into query DSL with rules rather than an LLM. The other two are xerj_map (the field map per index) and xerj_code_search (the reference-coding pipeline). The tool schemas are generated from a live tools/list and checked in CI, so the published list should not drift from what the server serves.

If you already run Claude Context or Context Mode, XERJ overlaps with both but goes further: it is a general search engine with its own storage, not just a retrieval layer or an output sandbox.

The token-savings numbers, read carefully

The repo description says “40x fewer tokens than grep.” The README’s measured headline is smaller and more specific. In the project’s reference-coding case study (8 tasks in Python, JavaScript, C and Java, 16 runs per arm, token counts from claude -p), the project reports:

ArmOutput tokensCostSolved
Memory only (tools off)260,916$11.1811/16
Native Claude Code (greps the source)26,477$3.2716/16
XERJ (reference retrieved)9,982$1.5816/16

That is 2.7x fewer output tokens than grep-driven Claude Code at the same solve rate, and about 2.1x cheaper. A broader run across 13 purpose-built libraries reports 1 of 21 tasks solved from memory for $21.90, versus 21 of 21 for $3.38 with retrieval and 21 of 21 for $4.26 with native grep.

Three things the headline does not tell you:

  1. The libraries were written for the study. That is deliberate: you cannot measure retrieval on code the model has memorised. But it means the test is the best case for retrieval, private code with rules a compiler cannot reveal.
  2. On memorised code, retrieval loses. The project’s own control on valkey and memcached: memory only solved 6 of 6 for $1.49, XERJ 5 of 6 for $4.40, native grep 6 of 6 for $9.14. Popular public libraries are where XERJ adds cost, not savings.
  3. The ~5x figure is testimony. The README’s “users report roughly 5x” comes from a field-report file that itself says it is “field testimony, not a controlled run,” relayed by the project owner. The benchmarks page also cites an independent 12-question dev-QA measurement at 1.65x fewer output tokens, smaller because the model already knew some answers.

Credit where due: the project publishes the losing control and labels the 5x as anecdote. Many tools in this space print the best number and stop.

Benchmarks against Elasticsearch, Zoekt and ripgrep

The project’s benchmarks page reports, against Elasticsearch 8.x on the same box: 55 wins, 26 ties and 4 losses across an 88-cell board, 1.72x higher bulk ingest, and 1.61x smaller on-disk size (176.2 MB vs 283.0 MB). All four losses are p99 read latency while a heavy writer runs, root-caused to a lock.

The more useful comparison for a dev machine is its head-to-head on 15 repos (154 MB), which shows real losses:

XERJ (rc.70/71)Zoektripgrep
Identifier search, one index (p50)11.7 ms15.3 ms260 ms cold
Identifier search, across all 41 datasets701 ms15.3 ms260 ms
Exact phrase (p50)829 ms37.9 ms200 ms
Index 15 repos676 s + merge tail302 sno index
Idle daemon8% CPU, 8.3 GB0.03%, 119 MBnone

The serial multi-index fan-out and the idle footprint are tracked as issues with stated targets. These numbers come from an August build; we have not seen a newer run of the same table. An idle process holding 8.3 GB on a 154 MB corpus is a real cost on a laptop.

Community reaction

Attention is high and organised: the project asks every user, human or agent, to file a short “field report” PR, and the agent field-report folder already holds 25 files. Weigh GitHub activity accordingly. Hacker News has shown little interest so far: the one submission we found, “Local code search engine for reference coding” (August 14, 2026), got 3 points.

The most useful outside evidence is an issue filed today by a user testing rc.95 on a real multilingual PBX project (PHP, Kotlin, Swift, Go, 23 UI languages). Their summary: indexing 1,104 files took 98 seconds and was “fast and painless”; when retrieval hit, it returned exactly the enclosing function with its line number, “the real value over grep.” But two of five real agent questions missed, one was partial, and hybrid/semantic modes were worse because translation catalogs and minified JS dominated results. Log search “did not work for us”: lines were merged into prose chunks without timestamp or severity fields, and a phrase that never occurs in the logs returned 1,982 hits. Their conclusion: for logs, grep was far more accurate.

Other open issues fit the same picture: a Spanish codebase where the token “de” outranks real matches, xerj search printing whole-file hits with no line number, and the cluster’s Raft ring not yet connected to the engine, so writes do not replicate. Single-node is the only mode to rely on today.

Honest limitations

  • Release-candidate software. 95 RCs, several a week, and a roadmap that is long and dense. Pin a version.
  • Wins depend on memorisation. Expect savings on private, niche or post-cutoff code; expect overhead on popular libraries.
  • Ranking needs tuning on messy repos. Translation files, minified bundles and docs can outrank the implementing code, per the field report above.
  • Logs are not first-class yet. Despite “log analytics” in the use-case list, line-oriented logs currently become prose chunks.
  • “Semantic” is lexical by default. Switch to --embed-mode neural if you want meaning-based recall.
  • Security is your job. --insecure means every local caller is a superuser. The optional Jev reranker sends result text to TypeSafe AI’s servers; it is off until you set a key.
  • Resource use. The project’s own numbers show a multi-GB idle footprint on a modest corpus.
  • Clustering is not ready. Run one node.

FAQ

What is XERJ?

An open-source (Apache-2.0) search engine in Rust, distributed as one static binary. It auto-indexes folders of code, documents and logs, implements the Elasticsearch REST API, and serves AI agents through a built-in MCP server and a CLI (xerj search, xerj def).

Is XERJ a drop-in Elasticsearch replacement?

For the API surface it covers, mostly: the project reports passing 1,366 of 1,369 Elasticsearch YAML conformance cases, and existing clients and Kibana connect. It is not a fork, scroll behaves differently, and clustering does not replicate writes yet. Treat it as a single-node replacement for dev and small workloads.

Does XERJ really cut tokens by 40x?

Not in the project’s controlled study. Its measured result is 2.7x fewer output tokens than grep-driven Claude Code on code the model had not memorised, and 26x fewer than answering from memory alone. On well-known libraries it was more expensive than memory alone. Users report about 5x in daily work, which the project labels as testimony.

Do I need an API key or a GPU?

No. Indexing, search and the default lexical embedder run on CPU with no key. Neural embeddings are an optional ~90 MB download. The only external calls are opt-in, such as the Jev reranker.

Is it safe to let my agent install it?

The installer verifies a SHA-256 checksum against the GitHub release, which is good. But the suggested prompt asks the agent to fetch instructions from a website, run a binary and clone third-party repos. Review llms.txt first, and keep the node on localhost or turn auth on.

Verdict

XERJ gets the central point right: agents waste tokens reading files, and an index that returns the right function with its line number is a better primitive than grep. The project’s honesty about losses (memorised code, fan-out latency, idle memory, read-under-write) makes its claims easier to trust than most. Use it if your agents work on large private or niche codebases, or if you want local Elasticsearch-compatible search without a JVM. Skip it, for now, if your work is mostly popular open-source libraries, if you need log analytics, or if you cannot spare a few GB of RAM for a background daemon. Whatever you do, keep it bound to localhost.

Sources