repo-semantic-search

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.

Category
访问服务器

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.

  1. 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's pyproject.toml — dev deps live in a PEP 735 [dependency-groups] table, not an extra. Always use --group dev as shown above.

  2. Start Postgres (with pgvector):

    docker compose -f docker/postgres-compose.yml up -d
    
  3. Install Ollama and pull the embedding model:

    brew install ollama
    brew services start ollama
    ollama pull nomic-embed-text
    
  4. (Optional) Customize config: copy .env.example to .env and edit as needed. Defaults assume the local Postgres/Ollama setup above. TEST_DATABASE_URL (defaults to repoindex_test on 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 from DATABASE_URL so 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 — the repos table: 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 shared code_chunks pgvector 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-hook CLI command — installs a post-commit git 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 — the repo-index command-line tool (add, sync, status, install-hook, init).
  • repo_index/mcp_server.py — the MCP server, exposing semantic_search and list_indexed_repos tools.

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 init registered 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; the post-commit hook fired, repo-index-sync.log showed a successful sync with no traceback, and repo-index status picked up the new commit sha automatically.
  • Ran semantic_search (via an in-memory MCP client) for "how does the rate limiter work" scoped to go-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

Baidu Map

百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。

官方
精选
JavaScript
Playwright MCP Server

Playwright MCP Server

一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。

官方
精选
TypeScript
Magic Component Platform (MCP)

Magic Component Platform (MCP)

一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。

官方
精选
本地
TypeScript
Audiense Insights MCP Server

Audiense Insights MCP Server

通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。

官方
精选
本地
TypeScript
VeyraX

VeyraX

一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。

官方
精选
本地
graphlit-mcp-server

graphlit-mcp-server

模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。

官方
精选
TypeScript
Kagi MCP Server

Kagi MCP Server

一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。

官方
精选
Python
e2b-mcp-server

e2b-mcp-server

使用 MCP 通过 e2b 运行代码。

官方
精选
Neon MCP Server

Neon MCP Server

用于与 Neon 管理 API 和数据库交互的 MCP 服务器

官方
精选
Exa MCP Server

Exa MCP Server

模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。

官方
精选