Eduardo Silveira
All projects

code-context-mcp

An MCP server that indexes a repository into a local vector store and returns the few relevant blocks, with line ranges, instead of whole files.

Status
Public and working. Small on purpose.
Stack
Python 3.10+ · ChromaDB · LM Studio / nomic-embed-text · MCP
Size
~16k of Python
Links
GitHub

Reading a file into an agent's context is the default move and usually the wrong one. A 3,000-line module answers a question with 2,900 lines of noise, and the noise is not free — it displaces the rest of the task.

This indexes a project into a local Chroma collection and exposes semantic search over it, so the answer to "where does the permission check happen" is four blocks with path:line ranges rather than four files.

project files ──▶ chunker ──▶ LM Studio (nomic-embed) ──▶ ChromaDB
                                                              │
   the agent ◀── MCP tools (search_code / index_project) ◀────┘

Everything is local

Embeddings come from LM Studio's OpenAI-compatible endpoint running nomic-embed-text on the machine. No API key, no upload, and no clause anywhere about what happens to a private codebase after it is embedded.

That constraint also happens to make re-indexing cheap enough to do casually, which matters more than it sounds — an index you are reluctant to rebuild is an index that is out of date.

Incremental by content hash

Indexing walks the project honouring .gitignore, splits files into line-windowed chunks, embeds each one, and upserts. A re-index hashes file contents and skips anything unchanged, so the second run over a repository costs almost nothing.

Line windows rather than syntax-aware chunks was a deliberate compromise. A tree-sitter chunker per language is better and is also the kind of dependency that turns a small tool into a project; windowed chunks with overlap retrieve well enough that I have not needed to find out where the ceiling is.

Four tools, nothing else

index_project, search_code, index_status, clear_index. The status tool exists because the failure mode of a retrieval server is silent: it returns results that look fine and are stale, and the only way to catch that is to be able to ask how many files and chunks it thinks it has and which model produced them.