repo-semantic-search
Enables semantic code search over local repositories, providing tools like semantic_search and list_indexed_repos to Claude Code, so users can find relevant code sections via natural language instead of grep.
README
repo-semantic-search
Semantic code search for any local repo, available to Claude Code as an MCP tool instead of grep.
What this is
A custom pipeline, built from scratch: CocoIndex chunks and embeds a repo's
files (tree-sitter-aware chunking, Ollama for local embeddings), the vectors
land in Postgres/pgvector, and a small MCP server (repo_index.mcp_server)
exposes semantic search over them as Claude Code tools. A git post-commit
hook keeps each registered repo's index in sync automatically.
An earlier version of this README described adopting a third-party tool, cocoindex-code, instead of building this. That path was abandoned in favor of the custom Postgres/pgvector pipeline described below, which is now built, registered with Claude Code, and verified end-to-end against a real repo.
Architecture
flowchart TD
subgraph Indexing["Indexing (write path)"]
Repo["Any registered git repo"] -->|git commit| Hook["post-commit hook<br/>nohup, non-blocking"]
Hook --> CLI["repo-index CLI<br/>add / sync / status / install-hook / init"]
CLI --> Registry["registry.py<br/>repos table"]
CLI --> Flow["flow.py<br/>CocoIndex pipeline"]
Flow -->|chunk + embed| Ollama["Ollama<br/>nomic-embed-text"]
Flow -->|upsert rows, repoindex role| PG[("Postgres + pgvector<br/>code_chunks table")]
Registry -->|repoindex role| PG
end
subgraph Querying["Querying (read path)"]
Claude["Claude Code"] -->|semantic_search<br/>list_indexed_repos| MCP["mcp_server.py<br/>MCP server"]
MCP -->|embed query| Ollama
MCP -->|SELECT only, repoindex_ro role| PG
end
Two independent paths sharing one Postgres database: indexing (triggered by
commits, writes via the read-write repoindex role) and querying (triggered
by Claude Code, reads via the read-only repoindex_ro role — the MCP server
has no write path at all).
Setup
Prerequisites, in order — repo-index init (below) will fail with a raw
connection-refused traceback if Postgres isn't running yet.
-
Create the venv and install dependencies:
python3 -m venv .venv .venv/bin/pip install --group dev -e .Note:
pip install -e '.[dev]'silently does not install the dev dependencies for this project'spyproject.toml— dev deps live in a PEP 735[dependency-groups]table, not an extra. Always use--group devas shown above. -
Start Postgres (with pgvector):
docker compose -f docker/postgres-compose.yml up -d -
Install Ollama and pull the embedding model:
brew install ollama brew services start ollama ollama pull nomic-embed-text -
(Optional) Customize config: copy
.env.exampleto.envand edit as needed. Defaults assume the local Postgres/Ollama setup above.TEST_DATABASE_URL(defaults torepoindex_teston the same Postgres instance) is used only by the test suite (tests/conftest.py), which truncates its tables between runs — keep it pointed at a separate database fromDATABASE_URLso tests never touch real registered-repo data.
Components
repo_index/settings.py— loads Postgres/Ollama config from env vars (DATABASE_URL,READONLY_DATABASE_URL,OLLAMA_API_BASE,OLLAMA_EMBED_MODEL), with sane localhost defaults.repo_index/registry.py— therepostable: which repos are registered, their filesystem path, and last-synced commit/timestamp.repo_index/flow.py— the CocoIndex flow that chunks files, embeds them via Ollama, and writes rows into the sharedcode_chunkspgvector table.repo_index/sync.py— orchestrates a sync run for one registered repo (resolve HEAD commit, run the flow, update the registry).repo_index/hooks.py+install-hookCLI command — installs apost-commitgit hook that re-syncs a repo's index in the background after every commit, without blocking or failing the commit itself.repo_index/cli.py— therepo-indexcommand-line tool (add,sync,status,install-hook,init).repo_index/mcp_server.py— the MCP server, exposingsemantic_searchandlist_indexed_repostools.
Adding a new repo to the index
.venv/bin/repo-index init /path/to/repo --name my-repo
init is shorthand for add (register in Postgres) + sync (chunk, embed,
and index the current HEAD) + install-hook (wire up the git hook), in one
step. Individual steps can also be run on their own, e.g. to re-sync
on demand:
.venv/bin/repo-index sync my-repo
.venv/bin/repo-index status
status lists every registered repo with its path and last-synced commit.
Staying current: the git hook
install-hook (also run by init) drops a post-commit hook into the
target repo's .git/hooks/. After every commit, it launches
repo-index sync <name> in the background (nohup ... &), logging to
.git/repo-index-sync.log inside the target repo, so commits are never
blocked or slowed down by re-indexing.
Registering with Claude Code
The MCP server runs as a stdio process out of this project's venv:
claude mcp add repo-semantic-search -s user -- \
/Users/shlomi.hassan/projects/repo-semantic-search/.venv/bin/python -m repo_index.mcp_server
claude mcp list # should show repo-semantic-search - ✔ Connected
Registered at user scope, so semantic_search and list_indexed_repos are
available as tools in every Claude Code session (after a restart — newly
registered MCP servers only appear in new sessions). This coexists with
any other MCP servers already registered (e.g. an earlier, unrelated
cocoindex-code server from the exploratory phase); nothing here depends on
or conflicts with it.
Verified working (2026-08-04)
Registered the MCP server (claude mcp list shows repo-semantic-search - ✔ Connected), then ran the full pipeline end-to-end against a real repo,
~/projects/go-ip2country:
repo-index initregistered the repo, indexed it (134 chunks across the repo's Go source, tests, docs, and README), and installed the hook.- Made a real commit in
go-ip2country; thepost-commithook fired,repo-index-sync.logshowed a successful sync with no traceback, andrepo-index statuspicked up the new commit sha automatically. - Ran
semantic_search(via an in-memory MCP client) for"how does the rate limiter work"scoped togo-ip2country: the top-ranked result (score 0.80) was the README's "How the rate limiter works" section, followed by the section on mutex locking/eviction — genuinely relevant, correctly ranked results.
Why semantic_search sets ivfflat.probes explicitly
The code_chunks table has a single ivfflat vector index shared across
all repos, and semantic_search's repo-scoped queries filter with WHERE repo_name = $1 after the approximate-nearest-neighbor index scan. With
pgvector's default ivfflat.probes = 1, this could silently return fewer
than top_k results for a given repo even when more relevant matches exist
in the table — reproduced directly against Postgres: a query with
top_k=5 returned only 2 rows through the ivfflat index at the default
probe count, but all 5 (including the actual
internal/ratelimit/fixedwindow.go implementation) with either a forced
sequential scan or ivfflat.probes raised to 10.
semantic_search now runs each query inside a transaction with SET LOCAL ivfflat.probes = 10, which restored full recall in re-testing (see below).
This is a scoped, low-risk mitigation (session/transaction-local, no schema
change); a per-repo partial index or an HNSW index remain possible future
upgrades if recall issues resurface at larger scale, but aren't needed now.
CLI reference
repo-index add <path> [--name NAME] # register a repo
repo-index sync <name> # chunk, embed, index current HEAD
repo-index install-hook <name> # install the post-commit hook
repo-index init <path> [--name NAME] # add + sync + install-hook
repo-index status # list registered repos + last sync
推荐服务器
Baidu Map
百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。
Playwright MCP Server
一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。
Magic Component Platform (MCP)
一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。
Audiense Insights MCP Server
通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。
VeyraX
一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。
graphlit-mcp-server
模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。
Kagi MCP Server
一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。
e2b-mcp-server
使用 MCP 通过 e2b 运行代码。
Neon MCP Server
用于与 Neon 管理 API 和数据库交互的 MCP 服务器
Exa MCP Server
模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。