An MCP server that incrementally indexes repositories and documents into a Postgres + pgvector store using CocoIndex, and exposes semantic search over them.
The pipeline is source → extract (format registry) → chunk → embed → store:
- Sources (
src/mcp_coco/sources.py) — local filesystem today, as two profiles:repo(code-aware, vendored dirs excluded) anddocument(markdown/text/pdf, prose chunking). - Formats (
src/mcp_coco/formats.py) — a registry mapping a file to normalized text. PDF (viapymupdf) is just one handler; add a format by registering one. - Indexer (
src/mcp_coco/indexer.py) — the CocoIndex app: chunk + embed (sentence-transformers) and declare rows into onedoc_embeddingstable. - Search (
src/mcp_coco/db.py) — embeds the query and runs a pgvector similarity search.
CocoIndex tracks its incremental state in a local LMDB file (COCOINDEX_DB), so
re-indexing only reprocesses what changed and removes rows for deleted files.
- uv (Python package manager)
- just (task runner, optional but convenient)
- A Postgres instance with pgvector
- Docker (if you want to run pgvector via the included compose file)
If you already have a Postgres instance with pgvector, skip this step and set
DATABASE_URL accordingly.
Otherwise, use the included compose file:
docker compose up -dThis starts pgvector on localhost:5432 with user/password/db all set to cocoindex.
uv synccp .env.example .envEdit .env and set DATABASE_URL to point at your Postgres instance. For the
Docker-based database:
DATABASE_URL=postgresql://cocoindex:cocoindex@localhost:5432/cocoindex
Optional settings:
| Variable | Default | Description |
|---|---|---|
EMBED_MODEL |
sentence-transformers/all-MiniLM-L6-v2 |
Embedding model for indexing and search |
RERANK_MODEL |
cross-encoder/ms-marco-MiniLM-L-6-v2 |
Cross-encoder model for result re-ranking |
COCO_TABLE_NAME |
doc_embeddings |
Postgres table name |
COCOINDEX_DB |
/data/cocoindex/state.db |
Path to CocoIndex incremental state store |
just initjust index ./path/to/repo repo
just index ./path/to/docs documentThe first run downloads the embedding model (~80 MB) from Hugging Face.
just search "how does authentication work"Add the MCP server to your Claude Code settings
(~/.claude/settings.json for global, or .claude/settings.json in a project):
{
"mcpServers": {
"cocoindex": {
"command": "uv",
"args": ["run", "--directory", "/absolute/path/to/cocoindex-mcp", "mcp-coco-server"],
"env": {
"DATABASE_URL": "postgresql://cocoindex:cocoindex@localhost:5432/cocoindex",
"COCOINDEX_DB": "/absolute/path/to/cocoindex-mcp/.cocoindex/state.db"
}
}
}
}Replace /absolute/path/to/cocoindex-mcp with the actual path to this repository.
If your Postgres instance is elsewhere (e.g. a cloud-hosted database), adjust
DATABASE_URL accordingly. It is highly encouraged to pass your authentication information through env vars, do NOT hardcode into the connection string!
Once configured, Claude Code can use these tools:
| Tool | Description |
|---|---|
index_repo(path) |
Index a code repository |
index_documents(path) |
Index a document collection |
search(query, limit, source_kind) |
Semantic search — returns condensed summaries and a results_file path |
read_search_results(results_file, indices, rerank) |
Retrieve full details for specific results from a previous search |
To keep context lean, search writes full results to a temporary JSON file
and returns only condensed summaries (~80-char excerpts) inline. The caller
triages from the summary, then uses read_search_results to fetch full
details for the results it actually needs.
By default, read_search_results re-ranks the selected results using a
cross-encoder model (cross-encoder/ms-marco-MiniLM-L-6-v2) for more
accurate relevance ordering. Disable with rerank=false. The model is
configurable via the RERANK_MODEL environment variable.
-
Open this folder in VS Code and Reopen in Container (Dev Containers). The
dbservice starts automatically alongside the app container. -
Run the preflight check:
just install just init
Copy
.env.exampleto.envto customize settings. Inside the devcontainer the database hostname isdb(the default).
just index <path> [repo|document|auto] # index a path
just index-repo <path> # index as code repository
just index-docs <path> # index as document collection
just search "query" [limit] # semantic search
just drop <path> [repo|document|auto] # remove a source from the index
just visualize_index # show a map of what's indexed
just serve # run the MCP server over stdio
just test # run tests
just lint # run ruff