Skip to main content
Glama

code-indexer

M8ven Verified

Semantic code index over Ollama + Qdrant, with a one-shot CLI (code-indexer, typer) and an MCP stdio server (code-indexer-mcp) that is a thin wrapper over that same CLI: every MCP tool builds the argv of the matching code-indexer subcommand and runs it, relaying its output — one code path, so the two surfaces can never diverge. The index maps one or more local project directories in Qdrant, using Ollama (qwen3-embedding:8b, 4096-dim) for embeddings. Fully LAN-local; no cloud.

Agents never index anything themselves. They only call semantic-search (which transparently runs a staleness check + incremental indexing first) plus a few admin subcommands. Chunks, hashes, and collections are never exposed.

Full command reference: SKILL-CLI.md — the agent-facing document with the decision tree, per-command options, and index-state meanings. This README is the overview; each fact lives in exactly one place.

Installation (CLI)

From the repo directory, install both executables as editable uv tools (on PATH in ~/.local/bin, edits to the checkout take effect immediately):

uv tool install -e .

This installs code-indexer (and the optional code-indexer-mcp server executable). Verify with code-indexer list-projects.

Related MCP server: qdrant-codebase-mcp

Quick start

code-indexer add-project /path/to/repo        # register + initial index (foreground)
code-indexer semantic-search "auth token refresh" --project /path/to/repo --format compact
code-indexer overview --project /path/to/repo # project map for orientation
code-indexer index-status /path/to/repo       # indexing state
code-indexer watch /path/to/repo --background # keep the index fresh (opt-in)

The agent workflow (overview → skeleton → search → get-code-context) is in SKILL-CLI.md.

Architecture

  • Index: tree-sitter AST chunks (function/class-level, symbol names, schema v3 signatures/visibility) with contextual headers, embedded via Ollama into one Qdrant collection per project (idx_<slug> / idx_<name>).

  • Manifest (SQLite, WAL): files, chunks, content hashes, symbol index, FTS5 lexical table, index fingerprint.

  • CLI (code-indexer): every operation, one-shot, foreground; per-project flock serializes concurrent passes.

  • MCP server (code-indexer-mcp): thin wrapper over the same CLI subprocess; all foreground.

CLI ↔ MCP parity

CLI subcommand

MCP tool

add-project

add_project

lookup-project

lookup_project

remove-project

remove_project

list-projects

list_projects

semantic-search

semantic_search

index-status

index_status

reindex-project

reindex_project

find-symbol / browse mode

find_symbol / find_symbols

skeleton (alias map)

skeleton

outline (alias file-outline)

outline

find-definition

find_definition

find-references

find_references

find-callers

find_callers

find-callees

find_callees

deps

deps

changed-symbols

changed_symbols

get-code-context

get_code_context

overview, index-more, doctor, watch, unwatch, eval, eval-compare

CLI-only (no MCP tool)

MCP options are a subset of CLI flags; the MCP tool schemas (see src/code_indexer/server.py) are the source of truth for what each tool accepts — e.g. semantic_search exposes project, limit, file_filter, symbol_type, language, ranking, format, fresh, per_file, max_tokens, rerank, mode. There is no MCP watch/unwatch: a watcher makes no sense inside an already long-lived server. The server resolves the CLI from CODE_INDEXER_BIN, else code-indexer on PATH, else python -m code_indexer.cli; CODE_INDEXER_BIN is the only env var the server process reads itself — all other configuration flows through the CLI subprocess's inherited environment.

Index states

Canonical states, the same everywhere (CLI and MCP):

State

Meaning

Agent action

idle

indexed, no pass running

normal use

indexing

a pass is running

wait/retry

never-indexed

registered but no completed pass (interrupted add-project)

do not trust empty results; tell the owner

needs-reindex

index built with a different embed model/dimension/text format/chunker

do not run reindex-project yourself; report to the owner (queries fail with ConfigError until then)

error

last pass failed

run code-indexer doctor

How indexing / staleness works

  • First index (add-project, CLI and MCP alike) runs in the foreground: the call returns when indexing finishes. If it is interrupted, the project stays registered but reports state=never-indexed (files=0 chunks=0 last_indexed=never); reindex-project builds it.

  • Staleness: searches/status trigger a re-scan when STALE_TTL (60 s) elapsed. The scan uses a stat fast-path (size, mtime_ns, inode match ⇒ no re-hash; PARANOID_HASH=1 disables it). --fresh forces the scan now; --skip-stale-check skips the probe. Hits carry file_hash and indexed_at; get-code-context re-resolves --symbol on the live file or returns line ranges with a stderr warning and stale: true in JSON when the file changed.

  • Incremental diff: files are classified by content hash (sha256, not mtime — branch switches re-index correctly). Only chunks whose hash changed are re-embedded; point IDs are deterministic uuid5, so upserts are idempotent; deleted files are purged.

  • Chunking: ~1000-char cap (CHUNK_MAX_CHARS); tree-sitter AST units, regex-window fallback. The embedded text carries a contextual header (embed_text.py, format contextual-header-v1).

  • Fingerprint: the manifest records embed_model, embed_dim, embed_text_version, chunker_version. Any mismatch ⇒ needs-reindex (see table above). --skip-stale-check does not bypass this gate.

  • Filtering: .gitignore/.codeindexignore (nested), --include scope globs, secret-bearing file skips (filename globs + high-confidence content patterns; --allow-sensitive per-project override), files > 1 MB (MAX_FILE_BYTES), binaries; pure-data chunks are down-weighted in ranking.

  • Concurrency: per-project flock + WAL SQLite; two processes indexing one project serialize into exactly one pass ("indexing in progress").

Schema history

Version

What changed

Migration

v3

signature/visibility columns on symbols

automatic ALTER; old rows NULL until reindex-project

v4

index fingerprint + stat fast-path columns on files

automatic ALTER; fingerprint backfills on next pass

v5

files.loc/files.language for overview; graph tables (refs/imports for find-callers/find-callees/deps)

automatic ALTER; overview/graph data populate on next reindex

—

FTS5 lexical table (chunks_fts, hybrid retrieval)

automatic backfill on next pass

Current schema version: 5 (auto-migrates on open; reindex-project backfills derived columns eagerly).

Configuration

Resolution order: CLI flags > environment variables > defaults. Full list with defaults and one-line descriptions: SKILL-CLI.md §Key rules. Note: OLLAMA_URL/QDRANT_URL defaults are LAN placeholders and must be set.

Platform support

Linux (inotify via watchdog) is the supported platform. Watcher degradation to quiet-period polling happens on inotify exhaustion; Windows is unsupported (the locking uses fcntl). The index itself is pure SQLite/Qdrant and is platform-neutral; only watch/locking are POSIX-bound.

Troubleshooting

Run code-indexer doctor — one line per check (ok|warn|fail <name>: <detail>), exit 1 on any fail. Covers runtime versions, Ollama (reachable/model/dimension), Qdrant (reachable/version/collections/dims), INDEX_ROOT writability + disk, SQLite integrity, watcher pidfile/locks, and per-project fingerprint status. Symptom → action:

  • Searches misbehave / empty results → doctor, then index-status <path>.

  • ConfigError: index ... was built with ... → needs-reindex; owner runs reindex-project.

  • never-indexed after a killed add-project → reindex-project.

  • Watcher dead → code-indexer unwatch --all, then re-run watch --background.

Security / privacy

Local-only by design (LAN Ollama/Qdrant). Sensitive files (.env*, keys, credentials*, high-confidence secret content) are skipped by default and the skip is counted in index-status; --allow-sensitive is a stored per-project override. .gitignore/.codeindexignore are honored.

Limitations

  • References are heuristic (textual) unless backed by the AST refs/import tables; find-references output is always confidence=heuristic.

  • C/C++/Java visibility is weak by design (everything public).

  • The language matrix and per-language caveats live in SKILL-CLI.md §semantic-search and docs/languages.md.

Performance

Embedding throughput depends on the Ollama host: measured ~1.2 docs/s with GPU passthrough (8B model, 4096-dim, batch 48) vs ~0.17 docs/s CPU-only. The first index of a large repo is the slow part; incremental updates re-embed only changed chunks. Qdrant upserts ~550 pts/s. Baseline numbers are 2026-09 measurements on the owner's LAN host; re-measure with code-indexer eval (see SKILL-CLI.md) before quoting new ones.

MCP client config (Claude Desktop / Hermes / any stdio MCP client)

{
  "mcpServers": {
    "code-indexer": {
      "command": "uv",
      "args": [
        "--directory", "/path/to/code-indexer",
        "run", "code-indexer-mcp"
      ],
      "env": {
        "OLLAMA_URL": "http://192.168.X.X:11434",
        "QDRANT_URL": "http://192.168.X.X:6333"
      }
    }
  }
}

When installed as a tool, point command at code-indexer-mcp directly (no uv wrapper). stdout is reserved for the MCP transport — all log output goes to stderr; never pipe server stdout into anything that expects log lines. Tool listing does not require Ollama or Qdrant to be reachable.

Development

uv sync                                  # install deps (.venv, incl. watchdog)
uv run code-indexer-mcp                  # run the stdio MCP server
uv run code-indexer list-projects        # one-shot CLI (no daemon)
uv run pytest -m "not live"              # offline suite (CI runs this)
uv run pytest -m live                    # live-backend tests (needs Ollama/Qdrant up)

Offline tests use fake embedder/store seams (no backends required); live tests are marked live and deselected by default. Docs drift is guarded by tests/test_docs.py. Constraints: pins numpy<2 (1.26.4), qdrant-client<1.15, mcp<2, tree-sitter==0.26.0, tree-sitter-language-pack==1.18.0 (older x86-64 CPUs without x86-64-v2; all pure/prebuilt wheels).

Upgrading between versions

Manifests auto-migrate (see Schema history). reindex-project is only required when index-status reports needs-reindex (fingerprint mismatch) or to eagerly backfill schema-v3/v5 derived columns.

Available Tools

6 tools
add_projectA

Register an absolute project directory for semantic indexing.

Creates the Qdrant collection and starts a full initial index in the
background (check progress with index_status). Errors if the path does
not exist or is already registered.
ParametersJSON Schema
NameRequiredDescriptionDefault
pathYes

Output Schema

ParametersJSON Schema
NameRequiredDescription
resultYes

TDQS

A4.4/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are provided, so the description carries the full burden and does meaningfully discharge it: it discloses side effects (creates a Qdrant collection, kicks off a background initial index) and two failure conditions. It stops short of describing idempotency, permissions, or what happens if indexing fails partway.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Three tight sentences with the core action front-loaded, followed by side effects and then error conditions. No filler or restatement of the tool name.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

An output schema exists, so return values need no explanation, and the description covers the mutation's side effects and error cases. For a no-annotation write tool it is nearly complete, missing only permissions/auth requirements.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The single parameter has 0% schema description coverage, so the description must compensate. It adds real meaning by specifying the path must be an absolute project directory and must already exist, though it gives no format examples or edge-case handling.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

Specific verb ('Register') plus resource ('absolute project directory') plus domain ('semantic indexing'). It clearly distinguishes itself from siblings like remove_project, list_projects, and reindex_project.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Names a sibling for follow-up ('check progress with index_status') and states two preconditions that cause errors (path missing, already registered). It does not explicitly contrast with reindex_project, which the 'already registered' clause implies but never names.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

index_statusA

Check indexing state for a project: idle | indexing | stale | error, plus last-pass progress. Also triggers the staleness check.

ParametersJSON Schema
NameRequiredDescriptionDefault
pathYes

Output Schema

ParametersJSON Schema
NameRequiredDescription
resultYes

TDQS

A3.5/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations, so the description carries full burden. It discloses the interesting side effect – the tool also triggers the staleness check – which is behavior beyond a pure read. However it doesn't say whether this mutates state, whether it's safe to call repeatedly, or cost/latency implications of triggering a check.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two terse sentences, front-loaded with the outcome space; every clause earns its place and the enum-like state list is immediately scannable.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Output schema exists so return value needn't be explained, and the description covers states plus the side effect. But the undocumented 'path' param and unstated mutation/reversibility of the staleness trigger leave the agent with unanswered questions for a tool that does more than read.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 0% with one required param ('path') left undocumented. The description implies a project scope but never says the param identifies the project by path, and gives no format guidance. Baseline 3 given the single-param simplicity, but a gap remains.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

Specific verb (Check) and resource (indexing state for a project), and enumerates the four possible states. It is distinguishable from siblings like reindex_project (which mutates) and list_projects, though it doesn't explicitly name alternatives.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Usage is implied: check status before/after indexing or when results seem stale. No explicit when-to-use, when-not-to-use, or alternative routing is given, so the agent must infer context from the sibling set.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

list_projectsA

List registered projects with per-project index summary and staleness.

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

Output Schema

ParametersJSON Schema
NameRequiredDescription
resultYes

TDQS

A3.5/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries the full burden. It does disclose that results carry an index summary and staleness flag, which tells the agent something about the shape and freshness semantics of the output. It does not state that the operation is read-only/non-mutating, nor anything about pagination or scoping, so it is only partially transparent.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

One sentence, no filler, with the resource and the returned information front-loaded. Every clause earns its place.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

An output schema exists, so return values need not be explained, and with zero parameters the schema side is fully covered. For a simple list tool the description is nearly complete, missing only explicit routing versus its sibling listing/inspection tools.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The tool takes zero parameters, so there is nothing for the description to clarify; the schema is trivially complete. Baseline 4 applies since no parameter explanation is possible or needed.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb ('List') and resource ('registered projects') and goes further by naming the payload contents (per-project index summary and staleness). That distinguishes it from index_status and reindex_project, though it never names those siblings explicitly.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

No indication of when to prefer this over index_status or semantic_search, and no prerequisites or ordering advice. The tool's read-only nature is only implied by the verb 'List'; the agent must infer all usage context.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

reindex_projectA

Force a full rebuild of a project's index (chunker/model change, suspected corruption).

ParametersJSON Schema
NameRequiredDescriptionDefault
pathYes

Output Schema

ParametersJSON Schema
NameRequiredDescription
resultYes

TDQS

A3.9/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are provided, so the description carries the disclosure burden. It does indicate this is a 'force full' destructive-ish rebuild scoped to a project, which is meaningful, but it omits whether the operation is blocking, whether it can run while the project is in use, or reset semantics.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

One tight sentence with no waste; the operation is front-loaded and reasons are compactly delivered in a parenthetical.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Output schema exists, so return values needn't be explained. However, with no annotations, a destructive full rebuild, and ambiguous 'path', the description should say more about safety and what 'path' means to be complete.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Only one parameter (path) with 0% schema description coverage. The description does not clarify what 'path' refers to (project root, project id, config path). With a single parameter the baseline is 4 per rules, but the ambiguous 'path' semantics and absent description warrant a modest downgrade to 3.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb (force full rebuild) and resource (project's index), and parenthetically gives the precise conditions that motivate it. Clearly distinguishable from siblings like add_project or semantic_search.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The parenthetical lists trigger conditions (chunker/model change, suspected corruption), which is explicit when-to-use guidance. It lacks any when-not-to-use or named alternative, so it falls short of a 5.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

remove_projectA

Deregister a project and DELETE its Qdrant collection + manifest + registry entry entirely.

ParametersJSON Schema
NameRequiredDescriptionDefault
pathYes

Output Schema

ParametersJSON Schema
NameRequiredDescription
resultYes

TDQS

A3.8/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries the full behavioral burden and does well by naming exactly what is destroyed (collection, manifest, registry entry) and signaling irreversibility via 'DELETE ... entirely'. It omits permission/auth requirements and behavior when the project does not exist or is already removed.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

One tightly written sentence with the destructive action and its full blast radius front-loaded. No filler.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

An output schema exists so return values need not be described, and the destruction scope is covered. However, for an unannotated destructive tool the description should also address the undocumented path parameter and failure/idempotency behavior, which it does not.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters2/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0% and the description never mentions the single required 'path' parameter, so it adds no meaning about what path refers to or how it is resolved. The bare parameter name is the only clue available.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb ('deregister') and resource (project) with an explicit scope that enumerates exactly what is torn down: Qdrant collection, manifest, and registry entry. This cleanly distinguishes it from siblings like add_project, list_projects, and reindex_project.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The destructive language implies a caution context for use, but there is no explicit statement of when to choose this over reindex_project or list_projects, nor any prerequisite or confirmation guidance. Usage is only implied.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

  1. 6 tool updatesv0.1.0
    • First observedadd_project
    • First observedindex_status
    • First observedlist_projects
    • First observedreindex_project
    • First observedremove_project
    • First observedsemantic_search

TDQS

A4.1/5.0

Scored across 6 tools

Disambiguation5/5

Each tool has a clearly distinct purpose: add_project registers, remove_project deregisters, list_projects lists, semantic_search searches, index_status checks status, and reindex_project forces rebuild. No overlapping or ambiguous functions.

Naming Consistency5/5

All tool names follow a consistent verb_noun pattern (add_project, remove_project, list_projects, semantic_search, index_status, reindex_project) with snake_case throughout.

Tool Count5/5

Six tools perfectly cover the lifecycle of project management and indexing for a code indexer: add, remove, list, search, status, and reindex. No tool is extraneous or missing.

Completeness5/5

The tool set provides complete CRUD for projects (add, remove, list) plus essential operations for indexing and searching (semantic_search, index_status, reindex_project). This covers all core workflows for a code indexer.

Maintenance

ActivityMaintained
ResponsivenessWithin a week

Related MCP Connectors

Related MCP Servers

  • A
    license
    A
    quality
    C
    maintenance
    Enables indexing and semantic search of codebases and documents via MCP, using Ollama embeddings and Qdrant vector store.
    5
    Apache 2.0
  • F
    license
    A
    quality
    C
    maintenance
    Provides a search_code tool for coding agents to perform local, offline semantic code search on a tree-sitter AST indexed codebase using GGUF embeddings, with no network calls or API keys.
    1
    -