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
In zustand, how does setState decide whether to notify subscribers?
{ query: "createStoreImpl", repository: "pmndrs/zustand" }- functioncreateStoreImplsrc/vanilla.ts:60–97
- functionsetStatesrc/vanilla.ts:66–81
{ 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
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
- 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 - 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 - 03
Parse
tree-sitter, Python ast and a heading scanner turn each file into named entities with line ranges.
13 languages - 04
Store
Files and entities go to SQLite in batches, and a failed batch rolls back on its own.
200 files per commit - 05
Index
One Algolia record per entity, ranked classes first, then functions, docs and imports.
1000 records per request
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.
search_codeRanked matches over functions, classes, imports, doc sections and Terraform blocks, each with a link to the exact lines.Up to 50 per pageget_fileA file with line numbers, so Claude can read the code around a hit.400 lines, 100,000 chars per callanalyze_repositoryFile, line and entity counts, languages, top directories and largest files.Counts onlyadd_repositoryAdds a repository by https URL, from GitHub or a public GitLab, Bitbucket or self-hosted server, and starts indexing.Needs the API keyTwo 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.
make install-claude # merge CodeSage into the Claude Desktop configmake install-claude ARGS=--dry-run # preview first, with the key masked
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.
make setup # .venv, npm ci, backend/.env and a generated API key$EDITOR backend/.env # ALGOLIA_APP_ID, ALGOLIA_ADMIN_API_KEY, GITHUB_TOKENmake start # backend on :8001, dashboard on :5173make install-claude # add CodeSage to Claude Desktop, then restart itThen 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-Keyonce a key is set.make setupgenerates one.Loopback only without a key
The backend refuses to start on a non-loopback address without a key, and it checks
HostandOriginheaders 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.
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.