repolore
Ask why your code is the way it is, and get back the PR, review thread or issue that says so.
Lore: what a team knows about its own code. Most of it was written down once, in a PR or a review thread, and then buried.
RAG over your own engineering history on GitHub: PRs, review threads, issues, commits and docs, searchable from a CLI and from Claude Code over MCP. It answers questions like "why did we drop the retry middleware?" with the passages that explain it and links to them.
Why this one
- The history, not just the code. PR descriptions, review threads, issues, commits that never had a PR, the
repos' markdown, and the
.plans/folders in your local checkouts. The reasons behind code live there. - Everything stays on your machine. It uses your own GitHub token and embeds locally, so no model API key is needed, and nothing leaves your machine except GitHub API calls and the one-time model download from GitHub.
- Made for Claude Code. Sessions get
search,read,list_reposandrefreshtools and answer with links to the sources. Codex and other MCP clients work too. - One model for every session. Each session runs a small relay, about 70 MB; one shared server holds the model.
- Stays current cheaply. A refresh fetches only what changed on GitHub and embeds only the chunks that changed.
- Every default was measured. Each retrieval choice went through
repolore evalagainst a fixed question set, and EXPERIMENTS.md logs every result. - Hand-built. No RAG or eval framework: fetching, chunking, embedding, retrieval and evaluation are each written out, so every stage stays visible.
Install
You need uv and a GitHub login. It runs on Linux, Windows and macOS 14+ on Apple Silicon.
uv tool install --managed-python --python 3.12 repolore-mcp # the repolore and repolore-mcp commands
repolore setup # pick repos, register with Claude Code, first refresh
The package is repolore-mcp: PyPI's repolore is a different project.
For GitHub access, being logged in with the GitHub CLI (gh auth login) is enough.
Otherwise set REPOLORE_GITHUB_TOKEN to a classic token with the repo scope, plus read:org for organization
repos. Organizations with SSO also need the token authorized for them.
repolore setup checks the GitHub token, lists the repos it found and asks which to index, writes your pick to
repolore.toml, registers repolore with Claude Code and Codex when they're on PATH, and starts the first refresh in
the background. It finds the repos you own or can read as a collaborator or organization member, plus your own PRs
and issues in other people's repos. Every question has a flag, which repolore setup --help lists, so it can run
without asking: repolore setup --include "you/*" --register --refresh (plus --replace-config when
repolore.toml exists).
The first refresh downloads the embedding model from GitHub, about 1.4 GB, and embeds everything on your machine,
which takes a while for a large account, so start with a few repos. As a rough guide, 3,713 chunks took 22 minutes
to embed on a 12-core desktop CPU, after a 9-minute download. On Apple Silicon it embeds on the GPU through MLX; set
REPOLORE_DEVICE=cpu to keep it on the CPU. repolore doctor shows how far it has got and which engine a build
uses.
How the install works: Python, PyTorch, the latest main and uvx
--managed-python makes uv use its own Python build, which can load the SQLite extension the index needs; some
system builds, such as python.org's on macOS, can't. If the commands aren't found afterwards, run
uv tool update-shell and open a new terminal. The install takes about 160 MB.
The first launch of each command compiles its Python code, which took about 25 seconds on Windows, close to Claude
Code's 30-second wait for an MCP server; later launches take a few seconds. Adding --compile-bytecode to the
install does that work up front instead. On some Windows machines uv then fails with "os error 1224"
(astral-sh/uv#14017); install without it there.
Reranking (--rerank, off by default) needs PyTorch on top, about 900 MB more, and says how to add it when you ask
for it.
To run the latest main instead of the last release, install git+https://github.com/ryckakas/repolore the same
way.
Without a tool install, uvx runs it too:
uvx --managed-python --python 3.12 --from repolore-mcp repolore setup --no-register, and the other commands the
same way. See Other MCP clients for launching the server
like that.
Setting up by hand
repolore setup runs these steps, which you can also run yourself:
-
See which repos it found:
repolore reposCreate
repolore.tomlat the config path thatrepolore versionprints, then runrepolore reposagain to check the selection:include = ["you/some-repo", "your-org/*"] # globs over owner/name; every repo when unset exclude = ["your-org/old-*"] # never these local_roots = ["~/projects"] # checkouts in here add their local .plans/ documents
-
Fetch and index:
repolore ingest # fetch from GitHub; later runs fetch only what changed repolore build # embed and index; later runs embed only what changed
-
Register the MCP server with Claude Code once:
claude mcp add --scope user --transport stdio repolore -- repolore-mcp
For Codex and other clients, see Other MCP clients.
Use it
repolore search "why did we drop the retry middleware?"
repolore search "rate limit handling" --repo you/some-repo --kind pr -k 10
repolore refresh # fetch what changed and rebuild the index with its recorded settings
repolore doctor # check every part of the setup, with the fix for each failure
repolore server status # the shared MCP server; `repolore server stop` stops it
Or ask Claude Code in a new session. It gets search, read, list_repos and refresh tools and can answer
questions about your history with links to the sources. search cuts each passage at 1,500 characters, and read
returns a source's whole text from the index, plans and private repos included, a page at a time.
The shared server
All sessions share one server that holds the model: each session's repolore-mcp is a small relay (about 70 MB)
that starts the shared server when none is running and passes the tools through. The server loads the model in the
background when it starts, which takes about half a minute, so only a search in that time waits for it, and it exits
10 minutes after the last session closes. It listens on 127.0.0.1 only and needs a token that changes every time it
starts. repolore server status shows it and repolore server stop stops it. repolore-mcp --standalone serves
from the session's own process, with its own copy of the model, as before.
Keeping it fresh
repolore refresh ingests what changed and rebuilds the index with the settings it was built with, and only one
refresh runs at a time. Search keeps working meanwhile. An MCP client can do the same: the server's refresh tool
starts one in the background, and list_repos says when the index was built, how the last refresh went and, while
one runs, how far it has got (repos fetched, chunks embedded). Claude Code asks before the first call; to allow it
for good, add mcp__repolore__refresh to permissions.allow in your Claude Code settings. To have Claude Code
refresh once at the start of every session instead, add a SessionStart hook to ~/.claude/settings.json:
{
"hooks": {
"SessionStart": [
{ "matcher": "startup", "hooks": [{ "type": "command", "command": "repolore refresh", "async": true }] }
]
}
}
Upgrading, your data, and when something breaks
uv tool upgrade repolore-mcp updates repolore itself. On Windows, close the sessions using repolore (in Claude Code
you can disable it in /mcp instead) and run repolore server stop first, since running programs keep their files
locked. Elsewhere, a session opened before the upgrade says so on its next search; reconnect repolore in your client
(/mcp in Claude Code).
The index and the raw GitHub responses live in the data directory that repolore version prints; they contain your
private repos' content, so treat that folder like the repos themselves.
When something doesn't work, repolore doctor checks each part of the setup and prints the fix for each one that
fails: GitHub access and the token's scopes, repolore.toml, the index, the embedding model, the last refresh, the
shared server and the MCP clients that have repolore registered. It makes one GitHub request, and when Claude Code is
installed it asks claude mcp get repolore, which starts repolore to check that it connects.
Other MCP clients
repolore-mcp is a standard stdio MCP server, so other clients can run it too. Most take a JSON entry in the
portable mcpServers format (VS Code's own mcp.json calls the object servers):
{ "mcpServers": { "repolore": { "command": "repolore-mcp" } } }
A client can also launch it through uvx, without a tool install:
{ "mcpServers": { "repolore": { "command": "uvx", "args": ["--managed-python", "--python", "3.12", "repolore-mcp"] } } }
The first launch downloads about 190 MB, which can take longer than a client waits for a server to start (30
seconds in Claude Code), so run uvx --managed-python --python 3.12 --from repolore-mcp repolore setup --no-register
once first: it fills uv's cache and sets repolore up.
Codex
Add it to ~/.codex/config.toml with longer timeouts. repolore setup adds this entry when codex is on PATH, with
an env_vars line for the variables below that are set when it runs:
[mcp_servers.repolore]
command = "repolore-mcp"
startup_timeout_sec = 60
tool_timeout_sec = 300
Codex's startup timeout covers the server's tools/list, which takes about 8 seconds when no shared server is
running yet and longer after a reboot, and its tool timeout covers a search that waits for the model to load, about
10 seconds warm and longer cold. Its documented defaults are 10 and 60 seconds. When building a session's tool list,
Codex waits only about a second for servers that aren't required (mcp_optional_startup_grace_ms); set that to 0,
or required = true on this server, to wait for the startup timeout instead.
Codex gives a server only a short list of environment variables: PATH, the home, app data and temp folders, and a
few more. That's enough for gh auth token (checked on Windows). If you rely on REPOLORE_GITHUB_TOKEN,
HTTPS_PROXY, REPOLORE_DEVICE or an XDG_* directory, list it in env_vars = ["REPOLORE_GITHUB_TOKEN"]. The relay
finds its folders through XDG_*, and the shared server, which every client's sessions share, and the refreshes it
starts inherit the environment of whichever session started it.
What the experiments found
Every retrieval choice was measured with repolore eval against 35 questions about the author's public repos, each
with the sources expected to answer it.
EXPERIMENTS.md has the full log. In short:
- Plain vector search won. Keyword search (BM25) alone was worse on every metric, because questions use plain words rather than the exact identifiers. Fusing the two rankings and reranking with a cross-encoder both gave gains within the noise and moved some first-place hits down.
- Bigger chunks were better. One chunk per markdown section, never split, beat 800, 400 and 200-token caps. No section in that corpus came near 8,192 tokens, which is now the most a chunk holds by default: on larger corpora, the few longer sections were slow to embed and were cut off at the model's 32K-token window (#17), so they are now split instead.
- The model matters most.
bge-small(33M parameters) lost a third of the first-place hits compared withvoyage-4-nano(340M), the default. - Less can be more. Indexing every commit instead of only those without a PR lowered MRR: the commits restate their PR and push its passages down.
- One passage per source. Plain search spent the top 5 on 3.3 sources on average, the rest being more sections
of the same document. Keeping each source's best section raised recall@5 from 0.857 to 0.904 and lowered no
question, so Claude Code's search does that by default (
--distincton the CLI). - "Not found" is the open problem. Cosine scores of answerable and unanswerable questions overlap. The reranker's scores separated them much better on the 3 unanswerable questions so far, which makes them the most promising way to say "nothing indexed covers this".
| Default config, 35 questions | recall@5 | recall@10 | MRR | nDCG@10 |
|---|---|---|---|---|
| First baseline (400-token chunks) | 0.818 | 0.919 | 0.809 | 0.804 |
| Now (whole sections) | 0.865 | 0.919 | 0.814 | 0.810 |
A search takes a median of 308 ms over 2,587 chunks on a desktop CPU, query embedding included. With 35 questions, one question moves recall by about 3%, so these numbers show direction, not precision. The biggest limit is the question set itself: several "misses" are valid answers that weren't listed as expected.
Development
uv sync
uv run repolore --help
uv run pytest
To have Claude Code use your checkout instead of the installed tool:
claude mcp add --scope user --transport stdio repolore -- uv run --directory /path/to/repolore --no-sync repolore-mcp
main only changes through PRs. After cloning, enable the hooks that block direct commits and pushes to it:
git config core.hooksPath .githooks
Documentation
- EXPERIMENTS.md: every retrieval experiment and its numbers
- AGENTS.md: the architecture, stage by stage, and the conventions for working on it
- SECURITY.md: reporting a vulnerability
License
MIT
Metadata
Release files for repolore-mcp 0.2.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| repolore_mcp-0.2.0.tar.gz | 413.3 kB | With provenance | Details for repolore_mcp-0.2.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| repolore_mcp-0.2.0-py3-none-any.whl | Python 3 | none | any | Details for repolore_mcp-0.2.0-py3-none-any.whl |
Total release size: 496.5 kB
Release files / repolore_mcp-0.2.0.tar.gz
| Download URL | repolore_mcp-0.2.0.tar.gz |
|---|---|
| Size | 413.3 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
e7e6b60f72dfbb45068cd6896fe0e7e93a2f8d88fe96f3beddd3704d39f44fe4
|
|
BLAKE2b-256 checksum How to use checksums |
656d267bc3d0b89bd7fa23fafae4d23144d426b080fc4f9c3d728ab9674ac7c8
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Nov 7, 2026.
Transparency logRelease files / repolore_mcp-0.2.0-py3-none-any.whl
| Download URL | repolore_mcp-0.2.0-py3-none-any.whl |
|---|---|
| Size | 83.2 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
fb973cd72d35776b15b144363419e3fc589c7a9d8214d9a1fa2119fc9fce02ab
|
|
BLAKE2b-256 checksum How to use checksums |
c4bf0a2616219c6ab74b59be5493d4f723525f55cd7bda94e4049ad518dc7ad0
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Nov 7, 2026.
Transparency logSecurity for repolore-mcp 0.2.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
Both files in this release have been uploaded with provenance metadata.
Attestation for repolore_mcp-0.2.0.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Nov 7, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.