Sandbox
@flupkede/codesearch

MCP server for offline semantic code search

codesearch gives agents a local way to search code by meaning, symbols, and text across one repository or many. It uses hybrid retrieval, tree-sitter chunking, and symbol lookup so the agent can find the right file or definition without scanning everything manually.

75 stars17 forksRustUpdated 7d ago
Who it's for

Builders who want Claude Code, Cursor, or any MCP client to search and navigate codebases locally.

What it delivers

You can find relevant code, definitions, and dependencies across multiple repos without re-explaining context or leaving your editor.

What it does

Hybrid search

Combines vector embeddings, BM25 full-text search, and reciprocal rank fusion for semantic queries.

Symbol navigation

Finds definitions, usages, imports, and dependents from the same MCP server.

Tree-sitter chunking

Chunks code by AST structure so results line up with functions, classes, and document sections.

Multi-repo serve mode

Runs one server across registered repositories and fans out searches across repo groups.

Offline local operation

Works without Docker or runtime model downloads and keeps data on the local machine.

Claude Code guard hooks

Ships hooks that steer Claude Code from grep and web search toward codesearch when it can answer better.

How to get it

  1. 1Or build from source
    git clone https://github.com/flupkede/codesearch.git
    cd codesearch
    cargo build --release
  2. 2The default quantized MiniLM model favors startup speed and a small download. For…
    codesearch --model embeddinggemma-q4 index /path/to/notes --force

README

codesearch

License: Apache-2.0 Built with Rust MCP server GitHub release GitHub stars

Multi-repo semantic code search for AI agents — a Rust MCP server with vector + BM25 hybrid retrieval, symbol navigation, and cross-repository orchestration. Fully local, fully offline, no GPU, no Docker.

codesearch gives AI agents (OpenCode, Claude Code, Cursor, and any MCP client) deep codebase understanding through 5 unified MCP tools. Index once, search semantically across multiple repositories simultaneously.

Why codesearch?

  • Multi-repo serve mode: Fan-out queries across repository groups with cross-repo RRF ranking
  • Hybrid retrieval: Vector embeddings + BM25 full-text search fused with Reciprocal Rank Fusion
  • Symbol navigation: Jump to definitions, find usages, trace imports and dependents — in the same tool
  • AST-aware chunking: Tree-sitter parsing for 17 languages — chunks align to functions/classes (and Markdown sections), not arbitrary line ranges
  • Token-efficient: Returns metadata by default; agents fetch full code only when needed via get_chunk
  • Lightweight footprint: Hundreds of MB on disk, runs on CPU only, no runtime model downloads (works behind enterprise proxies)
  • Zero config for single repos: codesearch index && codesearch mcp — done

How does this compare?

The MCP code-search ecosystem grew rapidly in late 2025 / early 2026 and many projects share the same baseline stack (Rust + tree-sitter + BM25 + embeddings + MCP). codesearch's deliberate focus is:

Focus areacodesearchTypical alternative
Repository scopeMulti-repo serve with cross-repo RRFUsually single repo at a time
Footprint~hundreds of MB, CPU-only, no DockerGB-scale, GPU, Docker, or cloud
Enterprise / offlineNo runtime fetches; static binaryOften pulls models at first run
Symbol navigationfind (def/usages/imports/dependents) co-located with semantic searchOften a separate code-graph tool
Token cost per callcompact=true by default; chunks fetched on demandFrequently dumps full snippets

codesearch is intentionally narrower than full code-graph or knowledge-graph tools — it picks "lightweight, multi-repo, MCP-native, fully offline" and stays on that lane.

Architecture

graph TB
    Agent[AI Agent / MCP Client] -->|MCP stdio or HTTP| Router{MCP Router}

    Router --> Search[search tool]
    Router --> Find[find tool]
    Router --> Explore[explore tool]
    Router --> GetChunk[get_chunk tool]
    Router --> FindImpact[find_impact tool]
    Router --> Status[status tool]

    Search -->|mode=semantic| Semantic[Vector ANN + BM25 + RRF Fusion]
    Search -->|mode=literal| Literal[Tantivy FTS / Regex]

    Find -->|definition/usages| SymbolIndex[Symbol Index]
    Find -->|imports/dependents| DepGraph[Dependency Graph]

    Explore -->|outline| TreeSitter[Tree-sitter AST]
    Explore -->|similar| Semantic

    Semantic --> Arroy[arroy ANN vectors]
    Semantic --> Tantivy[Tantivy BM25]
    Arroy --> LMDB[(LMDB)]
    Tantivy --> TantivyIdx[(Tantivy Index)]

    GetChunk --> LMDB

    FindImpact -->|C# symbols| CSharpHelper[scip-csharp helper]
    CSharpHelper -->|SCIP index| ScipLMDB[(LMDB scip_symbols)]

    subgraph "Serve Mode (multi-repo + federation)"
        ServeRouter[HTTP Router] -->|project/group routing| Repo1[Repo A]
        ServeRouter --> Repo2[Repo B]
        ServeRouter --> RepoN[Repo N]
        ServeRouter -->|"@peer fan-out · TLS"| CloudPeer["Cloud serve peer<br/>results merged via RRF"]
    end

    Router -->|client mode| ServeRouter

Quick Start

Install

Download pre-built binaries from Releases:

PlatformDownload
Windows x86_64codesearch-windows-x86_64.zip
Windows x86_64 + C#codesearch-windows-x86_64-with-csharp.zip
Linux x86_64codesearch-linux-x86_64.tar.gz
Linux x86_64 + C#codesearch-linux-x86_64-with-csharp.tar.gz
macOS ARM64codesearch-macos-arm64.tar.gz
macOS ARM64 + C#codesearch-macos-arm64-with-csharp.tar.gz

Or build from source:

git clone https://github.com/flupkede/codesearch.git
cd codesearch
cargo build --release

Index a repository

# Register and index the current repo (adds to ~/.codesearch/repos.json)
codesearch index add

# Register and index a repo from outside the repo folder
codesearch index add /path/to/my-project

# Incremental update (only changed files)
codesearch index /path/to/my-project

# Full rebuild
codesearch index /path/to/my-project --force

codesearch index add is intended to be run from inside the repo you want to register — pass the path explicitly if launched from elsewhere. First-time indexing takes 2–5 minutes; subsequent runs are incremental (10–30s) and branch switches re-index automatically. Use codesearch index list/rm/prune to manage registrations (see Serve Mode).

Embedding model

The default quantized MiniLM model favors startup speed and a small download. For multilingual text and notes, EmbeddingGemma 300M is available as a quantized ONNX model with retrieval-specific query and document prompts:

codesearch --model embeddinggemma-q4 index /path/to/notes --force

Changing models requires a full reindex because embedding dimensions and vector spaces are model-specific. Keep the same model selected for later indexing runs. Search rejects a --model value that differs from the indexed model and points to the required --force rebuild instead of mixing incompatible vector spaces.

MCP Configuration

codesearch connects to AI agents via MCP. Two modes:

ModeHowBest for
Local (stdio)codesearch mcp — single repo, auto-index + file watchingWorking on one project
Serve (HTTP)codesearch serve — multi-repo, TUI dashboard, lazy FSWMultiple repos, cross-repo search

Local / Single Repo

The agent spawns codesearch mcp as a subprocess. It auto-detects the nearest index and starts a file watcher.

OpenCode~/.config/opencode/config.json:

{
  "mcp": {
    "codesearch": {
      "type": "local",
      "command": ["codesearch", "mcp"],
      "enabled": true
    }
  }
}

Claude Code / Claude Desktop~/.config/claude-code/config.json or claude_desktop_config.json (identical schema):

{
  "mcpServers": {
    "codesearch": {
      "command": "codesearch",
      "args": ["mcp"]
    }
  }
}

Serve / Multi-Repo

Start the server first, then connect your agent. The server manages all registered repos with a TUI dashboard, lazy filesystem watchers, and idle eviction.

# Start the server (default port 39725)
codesearch serve

OpenCode — connect via HTTP:

{
  "mcp": {
    "codesearch": {
      "type": "remote",
      "url": "http://127.0.0.1:39725/mcp",
      "enabled": true
    }
  }
}

Claude Code / Claude Desktop — force serve connection via --mode client:

{
  "mcpServers": {
    "codesearch": {
      "command": "codesearch",
      "args": ["mcp", "--mode", "client"]
    }
  }
}

Note: In multi-repo mode, agents must specify project or group in tool calls. status always works without scope. get_chunk auto-routes when the chunk_id is unique across repos; if ambiguous, it returns candidates and requires project.

Agent Guidance (making agents use codesearch, not grep)

codesearch publishes instructions to every MCP client on connect (via the initialize handshake) — when to reach for codesearch vs grep/glob, which tool to pick, and the serve-mode caveats. Most clients (OpenCode, Cursor) surface these automatically.

If your agent skips codesearch and falls back to grep/glob too often, paste this quickstart into its rules (AGENTS.md for Claude Code/OpenCode, .cursorrules for Cursor):

Prefer codesearch for semantic, cross-file, or symbol-oriented lookup ("where is X implemented", "find usages of Y", "how does Z flow"). Use plain grep/glob for a single known file, trivial one-line edits, or exact literal searches. In remote-serve mode, returned paths are from the server's filesystem — read content via get_chunk rather than opening paths locally, and unindexed dirs (.venv, node_modules, build/) simply return nothing.

OpenCode: put this in the user-level ~/.config/opencode/AGENTS.md (applies across all projects). Claude Code reads a project-level AGENTS.md, so add it per-project (or symlink a shared one).

Claude Code specifically tends to ignore this advice more than other clients — its MCP tool schemas are deferred (an extra ToolSearch call is needed before codesearch tools are even callable), while Grep/Glob are always fully loaded and zero-friction, and spawned subagents don't inherit AGENTS.md or the MCP initialize instructions at all.

To make the preference structural instead of advisory, this repo ships three Claude Code PreToolUse hooks:

  • grep-guard — on Grep. Blocks a grep against an in-repo path when codesearch covers that repo (the target repo — resolved from the grep target's own git root — is registered with the serve hub in ~/.codesearch/repos.json, or a CODESEARCH_SERVER env var is set for remote-serve setups), with a message telling the model how to load and call codesearch instead. Grep is auto-allowed only when the serve hub is genuinely down, established by a live probe of the unauthenticated /healthz endpoint (CODESEARCH_SERVER > 127.0.0.1:$CODESEARCH_SERVE_PORT > 127.0.0.1:39725); only a connection-level failure counts as down. A low-confidence or empty codesearch result is a successful call meaning "reformulate the query", so it does not open the escape hatch — the deny message steers to find/explore/a single clean term instead. Greps outside any registered repo are never blocked, and the hook fails open (never traps the model).
  • subagent-preamble — on Agent (the subagent-spawn tool). Prepends a short codesearch preamble to every subagent prompt, since subagents otherwise don't inherit AGENTS.md or MCP instructions at all.
  • web-guard — on WebSearch/WebFetch. When you have remote documentation projects mounted (codesearch remote mount, e.g. cloud/inriver, cloud/example-dam), it blocks the first web call with guidance to search those indexed mounts first — often more precise and current than the open web. Same 5-minute retry-escape; when no mounts are configured it does nothing.

Install (idempotent — user scope applies to every project; --project is this repo only):

codesearch hooks claude install            # preferred — self-contained, all platforms
codesearch hooks claude install --project  # project scope (./.claude)

The native command embeds the hook scripts in the binary (no source tree needed) and merges the registrations into settings.json. The equivalent from-source installers still live in integrations/claude-code/ (install.ps1 / install.sh) if you'd rather run them directly.

Note: the grep-guard detects "codesearch is available for this repo" via that repo's registration with the serve hub (~/.codesearch/repos.json, honoring the CODESEARCH_REPOS_CONFIG override) or CODESEARCH_SERVERnot by checking whether a codesearch process is running (that runs almost constantly as a multi-repo hub and would false-fire in every directory), and not via a local .codesearch.db directory (a stale db from a since-unregistered repo used to deny Grep even though the hub could not answer for it). For a remote-serve setup with no local registration, set CODESEARCH_SERVER to opt back into enforcement.

MCP Tools Reference

search — Code Search

ParameterTypeDescription
querystringNatural language, code snippet, regex, or exact term
mode"semantic" | "literal"Search backend (default: semantic)
filter_pathstringPath prefix filter (semantic mode)
file_globstringGlob filter (literal mode), e.g. "src/**/*.rs"
languagestringLanguage filter (literal mode)
regexboolTreat query as regex (literal mode)
phraseboolExact phrase match (literal mode)
compactboolMetadata only, no code (default: true)
limitintMax results (default: 10 semantic, 20 literal)
projectstringTarget specific repo (multi-repo)
groupstringSearch across repo group (multi-repo)

filter_path semantics by routing mode:

  • Local project (stdio, or project=<local-alias>/local group on a serve hub): filter_path is a repo-relative prefix (e.g. src, docs/api) — matched against the routed project's own root, not the <alias>/… prefix shown in results.
  • Federated / mounted project (project=<peer>/<alias> or an @peer group fan-out): filter_path is matched client-side on the namespaced result path (<peer>/<alias>/…) — i.e. exactly the path you see in the results — because the peer only matches its own un-namespaced store paths. The hub over-fetches from the peer and post-filters.

Both modes now work without any client-side workaround; earlier releases dropped every hit when filter_path was combined with a serve-routed or federated project.

Semantic mode combines vector similarity (fastembed) + BM25 lexical scoring + exact identifier boosting, fused with RRF. Best for conceptual queries and mixed natural-language + symbol searches.

Literal mode uses Tantivy FTS. Use regex=true for patterns with punctuation (foo::bar, Vec<T>). Use phrase=true for multi-word exact matches.

find — Symbol Navigation

ParameterTypeDescription
symbolstringSymbol name or file path (for imports)
kind"definition" | "usages" | "imports" | "dependents"Navigation type
definition_kindstringFilter: Function, Class, Method, Struct, Trait, Enum, Interface
project / groupstringMulti-repo routing

explore — File Exploration

ParameterTypeDescription
targetstringFile path (outline) or chunk_id (similar)
kind"outline" | "similar"Exploration type
limitintMax results for similar mode
project / groupstringMulti-repo routing

Outline returns all top-level symbols in a file (kind, signature, line range). Similar finds semantically related chunks to a given chunk_id.

get_chunk — Read Code

ParameterTypeDescription
chunk_idintChunk ID from search/explore results
context_linesintExtra lines before/after (0-20, default: 0)
projectstringDisambiguate if chunk_id exists in multiple repos

In multi-repo mode: auto-routes when chunk_id is unique; returns candidates list when ambiguous.

find_impact — Symbol Reference Impact

Find all call-sites and references to a symbol with file/line precision — the recommended tool for "who calls X?" / "what breaks if I rename X?". Powered by per-language SCIP semantic analysis; precision backends ship per language: C# (bundled scip-csharp helper) and TypeScript (via npx scip-typescript, resolved on the host on demand — no bundle shipped), more planned. When no backend is available for a language, find_impact reports it — fall back to find kind="usages" (lexical) only then.

ParameterTypeDescription
symbol_namestringSymbol name (e.g. "FieldDefinition.Validate")
filestringFile path for position-based lookup
lineintLine number for position-based lookup
languagestringLanguage hint (auto-detected from file extension)
project / groupstringMulti-repo routing

Returns a list of references with file, start_line, end_line, and kind (e.g. "call", "definition"). Exposes index_age_seconds so agents can reason about staleness.

Note: SCIP precision requires the -with-csharp release variant (or a separately installed scip-csharp helper) for C#, and npx (with scip-typescript, fetched on first use) on the host's PATH for TypeScript. Without a backend for a language, find_impact returns a clear message — use find kind="usages" as the lexical fallback. See C# Semantic Search.

status — Index Info

ParameterTypeDescription
kind"index" | "projects"What to query
project / groupstringMulti-repo routing

Serve Mode (Multi-Repo)

For working across multiple repositories simultaneously:

codesearch serve

This starts a background HTTP server with:

  • TUI dashboard (ratatui) showing repo status, CPU usage, active sessions
  • Lazy filesystem watchers — activated on first query per repo
  • Idle eviction (30min) — unused repos are unloaded from memory
  • Session tracking via MCP keep-alive

TUI Keyboard Shortcuts

KeyAction
/ Navigate repo list
iShow info overlay (chunks, files, model, DB size)
dRun doctor diagnostics on selected repo
nForce reindex selected repo
rRemove selected repo (with confirmation dialog)
lReload repos config from disk
qQuit serve

Repository Registration

Repos are registered via codesearch index add:

# Register a repo (creates index + adds to ~/.codesearch/repos.json)
codesearch index add /path/to/my-project

# Remove a repo
codesearch index rm /path/to/my-project

# List registered repos
codesearch index list

# Clean up stale entries (relocates moved repos, drops the rest)
codesearch index prune

The repository alias (the key in repos.json, used for groups and the MCP project argument) is always derived automatically from the directory name — there is no --alias flag.

Serve reads ~/.codesearch/repos.json on startup and manages all registered repos.

Moved or renamed repositories

If you rename or move a registered folder, serve does not crash. On startup it tries to relocate each missing repo automatically: it captures every repo's git remote (remote.origin.url) at registration, and on a missing path it scans nearby folders (bounded depth, override with CODESEARCH_RELOCATE_MAX_DEPTH, default 3) for a git checkout with the same remote. A single unambiguous match is rewritten into repos.json; otherwise the entry is logged and skipped (never indexed against a dead path). Run codesearch index prune to relocate what can be relocated and drop the rest.

A hand-edited repos.json is also tolerated: empty entries, orphaned metadata, and group references to unknown repos are cleaned up on load rather than crashing.

Groups

Groups let you search across related repositories:

codesearch groups add my-group --aliases repo1 repo2 repo3
codesearch groups list

Then in MCP tools: group="my-group" fans out the query to all repos in the group.

The all group

The name all is a reserved virtual group that always resolves to every registered repository — no setup required:

group="all"   # fans out to all repos, equivalent to listing every alias
  • It is not stored in repos.json and always reflects the current set of registered repos (register or remove a repo and all updates automatically).
  • It appears in codesearch groups list (marked virtual) and in the scope_required error's available_groups, so agents discover it without extra setup.
  • It is not the default — when no project/group is specified in multi-repo mode, codesearch still returns scope_required (safe-by-default). Use group="all" explicitly when you want to search everywhere.
  • codesearch groups add all and codesearch groups remove all are rejected — the name is reserved.

Git Worktree Auto-Index

When using git worktree add to create parallel working directories, codesearch can auto-register new worktrees via a post-checkout git hook.

Setup (run inside any repo you want worktree auto-indexing for):

codesearch hooks git install

This installs a post-checkout hook that POSTs the worktree path to the running serve instance whenever a new worktree is checked out. The hook reads the serve URL from ~/.codesearch/serve_url (automatically managed by codesearch serve).

The install target is resolved with git rev-parse --git-path hooks, so it honours core.hooksPath (and, inside a linked worktree, the shared common-dir hooks) rather than assuming .git/hooks/. An existing post-checkout is not overwritten — codesearch's logic is chained in as a marker-delimited block.

How it works:

  1. codesearch serve writes its URL to ~/.codesearch/serve_url on startup (deletes on shutdown)
  2. The post-checkout hook reads that file and POSTs the working directory to POST /repos
  3. Serve registers the worktree path and begins indexing (deduped — won't re-register existing paths)

Claude Code Guard Hooks

codesearch hooks claude install (--project for repo scope) installs the PreToolUse guard hooks that steer agents to codesearch before Grep/WebSearch/WebFetch. See Agent Guidance above for what each guard does.

MCP Connection Modes

The codesearch mcp command supports three modes:

ModeBehavior
auto (default)Connects to serve if running, otherwise local stdio
clientAlways connects to serve, fails if not running
localAlways uses local DB (classic single-repo stdio)
codesearch mcp --mode client  # force serve connection

The serve endpoint is available at /mcp (Streamable HTTP transport).

Federation (remote peers)

codesearch can fan-out read queries (search, get_chunk) to remote peers — other codesearch serve instances (e.g. a cloud-hosted docs/KB peer) — and merge results with local indexes via RRF. This lets a team share one knowledge base while each dev keeps code search local.

Manage peer entries (pure local config — does not call the remote):

codesearch remote add cloud \
  --url https://codesearch-serve.<env>.<region>.azurecontainerapps.io \
  --api-key $API_KEY --timeout-secs 90
codesearch remote list          # show configured peers
codesearch remote rm cloud      # remove a peer entry

A group then references a peer via @-prefix ("groups": { "docs": ["@cloud"] }), and group="docs" fans the query out over TLS. Remote misses never hard-fail — they degrade to local-only results with a warnings field.

Manage indexes ON a peer — the same index verbs, scoped with --remote <peer>:

# list the repos living on the cloud peer
codesearch index list --remote cloud

# register a path on the peer's filesystem (NOT your local FS)
codesearch index add /data/docs/vendor-docs --remote cloud

# remove a repo by its alias on the peer (NOT a local path)
codesearch

Files in the repo

Repository payload30 top-level entries
  • .cargo
  • .claude
  • .githooks
  • .github
  • docker
  • examples
  • helpers
  • integrations
  • scripts
  • src
  • tests
  • .codesearchignore
  • .dockerignore
  • .gitattributes
  • .gitignore
  • AGENTS.develop.md
  • AGENTS.md
  • build.ps1
  • build.rs
  • build.sh
  • Cargo.lock
  • Cargo.toml
  • CHANGELOG.md
  • CLAUDE.md
  • Dockerfile
  • LICENSE
  • README_CSharp.md
  • README.md
  • RELEASING.md
  • rust-toolchain.toml

Discussion (0)

Ask about usage, or say what you built with it

Sign in to join the discussion.

No comments yet. Be the first to say what this is good for.

More connectors

Real-time global intelligence dashboard. AI-powered news aggregation, geopolitical monitoring, and infrastructure tracking in a unified situational awareness interface

86k

High-performance code intelligence MCP server. Indexes codebases into a persistent knowledge graph — average repo in milliseconds. 158 languages, sub-ms queries, 99% fewer tokens. Single static binary, zero dependencies.

43k

Universal provider proxy for OpenAI Codex & Claude Code — use any LLM (Claude, Gemini, Grok, DeepSeek, Ollama…) with Codex CLI, App, SDK, and Claude Code

14k
okf-memory/
okf-agent-memory

Git-native persistent memory for AI coding agents. Implements Google OKF v0.2 with sub-300µs in-memory BM25 search, embedded MCP server, and progressive disclosure. Slashes token bloat by 80% with zero external databases or dependencies. Built in pure Go.

547
tirth8205/
code-review-graph

Local-first code intelligence graph for MCP and CLI. Builds a persistent map of your codebase so AI coding tools read only what matters, with benchmarked context reductions on reviews and large-repo workflows.

31k
2akouwu/
reverify

Stop your AI from making things up — it proposes, deterministic tools decide, every claim checked against ground truth with evidence. Grounded facts and context survive resets. Reverse engineering is the proving ground. MCP server + CLI.

1.1k