indexter indexes a codebase into a single local SQLite file: hybrid
(semantic + keyword) search over composed code summaries, plus a call/import/
inheritance graph, served to AI coding agents over two MCP tools,
search and neighbors. There is no server process to run, no vector
database to host, and no registry file: everything for a repository lives in
one database, keyed deterministically off its path.
Every file is parsed with tree-sitter into semantic units (functions, classes, config tables, headings, and so on) rather than indexed as flat text.
| Language | Extensions | Semantic units extracted |
|---|---|---|
| Python | .py |
Functions, methods, classes, module-level constants, docstrings |
| JavaScript | .js, .jsx |
Functions, methods, classes, module-level constants, JSDoc comments |
| TypeScript | .ts |
Functions, methods, classes, interfaces, type aliases, enums, module-level constants, TSDoc comments |
| Rust | .rs |
Functions, methods, structs, traits, enums, type aliases, constants, doc comments (///, //!) |
| Markdown | .md |
Sections, one per heading, named by their full heading path |
| HTML | .html, .htm |
Sections: headings (h1–h6), tables and lists, scoped to their enclosing container |
| CSS | .css |
Sections: rule sets and at-rules (@media, @keyframes, ...), scoped to their nesting |
| JSON | .json |
Data nodes: objects and arrays, scoped to their key/index path |
| YAML | .yaml, .yml |
Data nodes: block mappings and sequences, scoped to their key/index path |
| TOML | .toml |
Data nodes: tables and key/value pairs, scoped to their dotted key path |
| Anything else | * |
Fixed-size overlapping text chunks, so every file is searchable |
Python, JavaScript, TypeScript and Rust also resolve calls, imports and inherits edges
across files, forming the code graph neighbors walks. The rest produce containment structure
(contains) only, since there's nothing in JSON, a stylesheet, or a heading to call or import.
Python 3.11–3.13, run under a uv-managed interpreter. indexter stores
vectors with the sqlite-vec SQLite
extension, which needs a Python build whose sqlite3 module supports loading
extensions; the interpreters uv installs support this, but the system
Python on macOS and some Linux distributions does not. Running indexter with
uv run (or a uv-installed tool, below) takes care of this automatically.
uv tool install --managed-python indexter--managed-python makes sure the tool's own interpreter is one uv
installs, not whatever python3 happens to resolve to on your system. The
interpreters uv manages support loading the sqlite-vec extension used for
vector storage, and a system Python often doesn't (see Requirements above).
This installs the indexter command on your PATH. Upgrade with
uv tool upgrade indexter; uninstall with uv tool uninstall indexter.
To install from a checkout instead (for a pre-release version, or to work on indexter itself):
git clone https://github.com/jdbadger/indexter
uv tool install --managed-python ./indexterUpgrading from a 0.1.x install? See the "Upgrading from 0.1" notes in
CHANGELOG.md.
Index a repository:
indexter init /path/to/repoThis walks the repository, parses it with tree-sitter, composes and embeds
each symbol, and writes the database. The first run downloads the embedding
model (a few hundred MB), so it can take a minute or two; later runs and
re-syncs reuse the cached model. Re-index later with indexter reindex /path/to/repo (add --full to delete and rebuild the database from
scratch instead of syncing changes).
Then register the MCP server with your agent (below) and install the skill
that teaches it when to use search and neighbors:
indexter skill --installEvery client runs the same command, indexter mcp [--repo PATH]. Without
--repo, the server resolves which repository a call targets from the
working directory it was started in, walking upward to the nearest indexed
ancestor, so a single, user-level registration works for every project a
workspace-based client opens. --repo pins one repository explicitly, for
clients with no workspace concept.
Claude Code (verified end-to-end):
claude mcp add --scope user indexter -- indexter mcpClaude Desktop (no workspace: pin a repository):
{
"mcpServers": {
"indexter": {
"command": "indexter",
"args": ["mcp", "--repo", "/path/to/repo"]
}
}
}VS Code (.vscode/mcp.json in the workspace, so cwd resolves it):
{
"servers": {
"indexter": {
"command": "indexter",
"args": ["mcp"],
"cwd": "${workspaceFolder}"
}
}
}Cursor (.cursor/mcp.json):
{
"mcpServers": {
"indexter": {
"command": "indexter",
"args": ["mcp"]
}
}
}Only the Claude Code configuration above has been exercised end-to-end; the Claude Desktop, VS Code and Cursor configurations follow each client's documented format but haven't been verified against a running client.
search(query, repo?, kind?, language?, path?, limit?): hybrid semantic + keyword search. Returns matching code with each hit's closest callers, callees and containing scope. Always loaded in Claude Code.neighbors(node_id, repo?, direction?, edges?, depth?, limit?): walks the call/import/inheritance/containment graph from a node ID returned bysearch(or a previousneighborscall): who calls this, what does this import, what inherits from this, 1–3 hops out. Loaded on demand.
Both tools sync the repository's index against the files on disk before
answering, so results always reflect the current working tree; there's no
separate reindex step to remember. Neither tool ever creates a database; an
unindexed repository comes back as an error naming indexter init.
| Command | Description |
|---|---|
indexter init [PATH] |
Create (or re-sync) a repository's index. Defaults to the current directory. |
indexter reindex [PATH] [--full] |
Re-sync a previously initialized repository; --full rebuilds the database from scratch. |
indexter list |
List indexed repositories, with node counts, embedding model and size. |
indexter remove TARGET [--yes] |
Remove an indexed repository's database (by repo path or database filename). Never touches the repository itself. |
indexter mcp [--repo PATH] |
Start the MCP server over stdio. |
indexter skill [--install] [--dir PATH] [--force] |
Print the packaged skill, or install it into an agent's skills directory. |
indexter --version |
Print the installed version and exit. |
Settings are layered: built-in defaults, then ~/.config/indexter/config.toml
(global, applies to every repository), then indexter.toml or
[tool.indexter] in pyproject.toml at the repository root (per-repo,
overrides global). Unknown keys or wrong-typed values are rejected with an
error naming the key and the file it came from.
| Key | Default | Meaning |
|---|---|---|
embedding_model |
sentence-transformers/all-MiniLM-L6-v2 |
Embedding model name. |
embedding_dim |
384 |
Embedding vector dimension; must match the model. |
embedding_backend |
sentence-transformers |
sentence-transformers or fastembed. |
embed_batch_size |
32 |
Texts embedded per batch during indexing. |
embed_max_tokens |
256 |
Truncation length for composed summaries. |
ignore_patterns |
[] |
Extra gitignore-style patterns to skip while walking. |
max_file_size_bytes |
1000000 |
Files larger than this are skipped. |
search_limit |
10 |
Default number of results for search. |
snippet_max_lines |
40 |
Maximum lines shown per result snippet. |
search_max_chars |
20000 |
Character budget for a rendered response. |
chunk_size |
1000 |
Characters per chunk for oversized nodes. |
chunk_overlap |
100 |
Overlap between consecutive chunks. |
- Databases:
$XDG_DATA_HOME/indexter/<slug>-<hash>.db(defaults to~/.local/share/indexter/), one file per repository, named from the repository's canonical path. There is no registry:indexter listreads this directory directly. - Global config:
$XDG_CONFIG_HOME/indexter/config.toml(defaults to~/.config/indexter/config.toml). - Per-repo config:
indexter.tomlor[tool.indexter]inpyproject.toml, at the repository root. - Installed skill:
$CLAUDE_CONFIG_DIR/skills/indexter/SKILL.md(defaults to~/.claude/skills/indexter/SKILL.md), or whereverindexter skill --install --dir PATHpoints.
See CONTRIBUTING.md for setting up a fork, the development recipes, and
the release process.