Skip to content

Self-hosted MCP server · Algolia search · Claude Desktop

Give Claude a search index of your codebase.

CodeSage parses a repository into functions, classes, imports, Markdown sections and Terraform blocks, indexes them in Algolia, and gives Claude Desktop seven tools to search and read them. A web dashboard adds repositories, shows indexing live and searches the index.

7
MCP tools
13
languages parsed
1000
files per run
Live
WebSocket progress
Claude Desktopcodesage connected

In zustand, how does setState decide whether to notify subscribers?

search_code{ query: "createStoreImpl", repository: "pmndrs/zustand" }
  1. functioncreateStoreImplsrc/vanilla.ts:60–97
  2. functionsetStatesrc/vanilla.ts:66–81
get_file{ path: "src/vanilla.ts", start_line: 66, end_line: 81 }
66  const setState: StoreApi<TState>['setState'] = (partial, replace) => {
69    const nextState =
70      typeof partial === 'function'
71        ? (partial as (state: TState) => TState)(state)
72        : partial
73    if (!Object.is(nextState, state)) {
74      const previousState = state
75      state =
76        (replace ?? (typeof nextState !== 'object' || nextState === null))
77          ? (nextState as TState)
78          : Object.assign({}, state, nextState)
79      listeners.forEach((listener) => listener(state, previousState))
80    }
81  }

setState computes the next state, then compares it with Object.is on line 73. Only when the state changed does it merge (or replace) and call every listener with the new and previous state, on line 79.src/vanilla.ts#L66-L81

The tool calls and results are real CodeSage output for pmndrs/zustand (120 files, 1,025 entities), lightly trimmed. Lines 67–68, two comments, are omitted. Claude's wording is illustrative.

What it does

Your repository, as something Claude can query

Claude can't open your repository on its own. CodeSage indexes the repository ahead of time and answers Claude's questions from that index, so Claude cites real files and line numbers instead of guessing.

01Index

Add a GitHub repository in the dashboard, or ask Claude to add one from GitLab, Bitbucket or a self-hosted server. A background job fetches, parses and indexes it while the dashboard shows progress after every batch.

02Search

Every function, class, import, Markdown heading and Terraform block becomes an Algolia record. A question in plain words still returns the best partial matches, and each hit links to the exact lines on its host.

03Read

Claude reads files with line numbers, lists definitions, gets language and directory breakdowns, and runs literal or regex searches, all from the copy of your code that CodeSage keeps in SQLite.

Indexing

From a URL to a searchable index in one background job

  1. 01

    Check

    The GitHub API (or git ls-remote on other hosts) confirms the repository and branch exist. A refusal creates nothing.

    10 s, one attempt
  2. 02

    Fetch

    Lists files through the GitHub trees API or a shallow clone. Vendored and minified files and lockfiles are skipped.

    under 1 MB each, 1000 files, 100 MB
  3. 03

    Parse

    tree-sitter, Python ast and a heading scanner turn each file into named entities with line ranges.

    13 languages
  4. 04

    Store

    Files and entities go to SQLite in batches, and a failed batch rolls back on its own.

    200 files per commit
  5. 05

    Index

    One Algolia record per entity, ranked classes first, then functions, docs and imports.

    1000 records per request
pendingprocessingcompletedorfailed

Two runs at a time. A run that skips files still completes and is flagged as partially indexed. A server restart marks interrupted runs failed, and you can reprocess them. Read the full pipeline

For Claude

Seven MCP tools

Claude Desktop starts a small stdio bridge that turns each tool call into an HTTP request to the backend. The bridge answers tools/list itself, so the tools show up even while the backend is down.

list_repositoriesEvery indexed repository with status, size and any error.No arguments
search_codeRanked matches over functions, classes, imports, doc sections and Terraform blocks, each with a link to the exact lines.Up to 50 per page
get_fileA file with line numbers, so Claude can read the code around a hit.400 lines, 100,000 chars per call
list_definitionsDefinitions by file and line, filtered by path prefix, type or name.Up to 500
analyze_repositoryFile, line and entity counts, languages, top directories and largest files.Counts only
find_patternsLiteral or regex line matches across the repository.Regex stops at 10 s
add_repositoryAdds a repository by https URL, from GitHub or a public GitLab, Bitbucket or self-hosted server, and starts indexing.Needs the API key

Two ways in

A dashboard for people, an MCP server for Claude

The React dashboard adds GitHub repositories, follows indexing over a WebSocket (and polls every 3 seconds as a fallback), searches with suggestions and opens each result at its line on GitHub.

Claude Desktop reaches the same backend through the MCP bridge. One command merges CodeSage into your Claude Desktop config, backs up the old file and keeps the key out of sight.

Terminal window
make install-claude # merge CodeSage into the Claude Desktop config
make install-claude ARGS=--dry-run # preview first, with the key masked
The CodeSage repository page for pmndrs/zustand, showing 186 search results for 'create store' with file paths, line ranges and code snippets.
Searching pmndrs/zustand in the dashboard. A real screenshot from a local run.

Languages

What gets parsed

  • Pythonast
  • JavaScripttree-sitter
  • TypeScripttree-sitter
  • Javatree-sitter
  • Gotree-sitter
  • Rusttree-sitter
  • Ctree-sitter
  • C++tree-sitter
  • C#tree-sitter
  • PHPtree-sitter
  • Rubytree-sitter
  • Markdownheadings
  • Terraform / HCLtree-sitter

CSS, SCSS, HTML, JSON, YAML, XML and SQL files are stored and readable with get_file, but they yield no entities. C and C++ cover .c and .cpp only, so headers are not indexed. Languages reference

Quick start

Running in four commands

You need Python 3.13 with uv, Node.js 22, and an Algolia application ID and Admin API key. A GitHub token is optional but raises GitHub's rate limit and lets the server read your private github.com repositories.

Terminal
make setup # .venv, npm ci, backend/.env and a generated API key
$EDITOR backend/.env # ALGOLIA_APP_ID, ALGOLIA_ADMIN_API_KEY, GITHUB_TOKEN
make start # backend on :8001, dashboard on :5173
make install-claude # add CodeSage to Claude Desktop, then restart it

Then open http://localhost:5173, enter owner/repo, and ask Claude:"Search the zustand repository for where the store is created."

Security

Safe defaults on localhost, a locked-down stack for servers

  • The API key guards writes

    Creating, editing, deleting and reprocessing repositories, tool calls, suggestions and the WebSocket all need X-API-Key once a key is set. make setup generates one.

  • Loopback only without a key

    The backend refuses to start on a non-loopback address without a key, and it checks Host and Origin headers to block DNS rebinding and cross-site writes.

  • nginx holds the key in prod

    The Docker prod stack terminates TLS, asks for a basic-auth login and adds the key itself, so the browser bundle never contains it.

  • The bridge refuses plain HTTP

    The MCP bridge won't send a key or login over http:// to a remote host. Other git hosts are cloned without a shell, credentials or prompts, over https only.

Security model in full

Documentation

Everything else

CodeSage is self-hosted: you run the backend, the dashboard and the MCP bridge on your own machine or server, with your own Algolia account. This site documents it and doesn't run it. Built for the Algolia MCP Server Challenge.