[cocoindex code]
AST-based semantic code search
[effect]
CocoIndex Code provides AST-based semantic search for your codebase, built on the CocoIndex Rust data transformation engine. Use it from the CLI or connect Claude, Codex, Cursor, and other coding agents through a Skill or MCP.
- 70% token savings.
- 1 min setup with default settings.
Get Started
Install
Using pipx:
Using uv:
Two install styles — they mirror the Docker image variants of the same names:
cocoindex-code[full] — batteries-included. Pulls in sentence-transformers so local embeddings (no API key required) work out of the box. The ccc init interactive prompt defaults to Snowflake/snowflake-arctic-embed-xs.
cocoindex-code (slim) — LiteLLM-only; requires a cloud embedding provider and API key. Use when you don't want the local-embedding deps (~1 GB of torch + transformers).
Next, set up your coding agent integration — or jump to Manual CLI Usage if you prefer direct control.
Coding Agent Integration
This repository is a single plugin marketplace (.claude-plugin/marketplace.json) consumed by both Claude Code and Grok — same plugin id cocoindex-code, same ccc skill. Grok optionally activates the bundled hooks and MCP server with --trust; Claude Code users can install the same marketplace and rely on the skill alone or load hooks/MCP from the plugin as needed.
Skill (Recommended)
Install the ccc skill so your coding agent automatically uses semantic search when needed:
The skill teaches the agent to handle initialization, indexing, and searching, so you don't need to run ccc init or ccc index yourself. The agent keeps the index up to date as you work.
The agent uses semantic search automatically when it would be helpful. You can also nudge it explicitly — just ask it to search the codebase, e.g. "find how user sessions are managed", or type /ccc to invoke the skill directly.
Works with Claude Code and other skill-compatible agents.
Claude Code plugin marketplace
For Claude Code users, this repository is also a plugin marketplace. Install the skill from inside Claude Code with:
This bundles the same ccc skill, with version pinning and /plugin marketplace update for updates. The repository also ships hooks/hooks.json and .mcp.json for Grok (and Claude Code plugin installs that load those files); Claude users who want skill-only search can rely on the skill alone and add MCP manually in the MCP Server section below instead of using the bundled .mcp.json.
Grok plugin
For Grok users, install via Grok's plugin system. The plugin bundles three components:
Grok does not import Claude's enabledPlugins or plugin cache; install separately even if you already use cocoindex in Claude Code.
Full install (skill + hook + MCP):
Prefer the GitHub shorthand (cocoindex-io/cocoindex-code) for install — grok plugin install cocoindex-code can fail when no marketplace plugin matches that bare name.
--trust is required so Grok activates the plugin's hooks and MCP server (skills load when the plugin is enabled).
Skill-only (match Claude Code — no auto-index hook, no MCP tool):
Install and enable as above, then disable the optional components:
- Hooks — open
/hooks, select the SessionStart / PostToolUse hooks from cocoindex-code, press Space to disable.
- MCP — open
/mcps, select cocoindex-code, press Space to disable; or persist in ~/.grok/config.toml:
The agent still owns indexing via the ccc skill (ccc index / ccc search --refresh when stale), same as Claude Code.
To avoid importing MCP servers from your Claude/Cursor user config (unrelated to this plugin):
Oh My Pi plugin
Oh My Pi reads .omp-plugin/marketplace.json (preferred) and falls back to .claude-plugin/marketplace.json.
Then restart the session (/reload-plugins does not reload extensions). Requires ccc on PATH (uv tool install --upgrade 'cocoindex-code[full]').
OMP does not execute Claude hooks/hooks.json command hooks. The TypeScript extension is the OMP equivalent of the Claude/Grok SessionStart + PostToolUse pair.
Skill-only (no MCP, no auto-index): install the skill via npx skills add cocoindex-io/cocoindex-code, or run ccc search / ccc index from the shell.
MCP Server
Alternatively, use ccc mcp to run as an MCP server:
Claude Code
Codex
OpenCode
Enter MCP server name: cocoindex-code
Select MCP server type: local
Enter command to run: ccc mcp
Or use opencode.json:
Kilo Code
Add a local MCP server in ~/.config/kilo/kilo.jsonc, kilo.jsonc, or .kilo/kilo.jsonc:
Oh My Pi
Prefer the marketplace install above. Manual project MCP (.omp/mcp.json):
Once configured, the agent can use semantic search to find code by description, explore unfamiliar codebases, and locate implementations without exact names.
Note: The cocoindex-code command (without subcommand) still works as an MCP server for backward compatibility. It auto-creates settings from environment variables on first run.
MCP Tool Reference
When running as an MCP server (ccc mcp), the following tool is exposed:
search — Search the codebase using semantic similarity.
Returns matching code chunks with file path, language, code content, line numbers, and similarity score.
Manual CLI Usage
You can also use the CLI directly — useful for manual control, running indexing after changing settings, checking status, or searching outside an agent.
The background daemon starts automatically on first use.
Tip: You can skip ccc init and go straight to ccc index — it auto-initializes new projects with default settings. If global settings are missing too (first use on the machine), it walks you through the same model setup as ccc init when run interactively; non-interactive runs (scripts, hooks) still require a one-time ccc init first.
Note: ccc index never auto-initializes the directory that holds ccc's user settings directory: your home directory by default (~/.cocoindex_code holds the global settings and daemon files), or the parent of $COCOINDEX_CODE_DIR when that directory is named .cocoindex_code. A project there would cover every repo below it. The editor hooks skip that directory even if it is already a project. If an older ccc made your home directory a project (~/.cocoindex_code/settings.yml exists), run ccc reset --all in your home directory: it removes that project's settings and indexes and keeps your global settings. To index your whole home directory on purpose, run ccc init there.
CLI Reference
Search Options
By default, ccc search scopes results to your current working directory (relative to the project root). Use --path to override.
Structural Search (ccc grep)
ccc grep finds code by structure, not text — you write a by-example pattern
and it matches the syntax tree (via cocoindex's code_match), so formatting,
whitespace, and intervening tokens don't matter. It runs entirely locally: no
index, daemon, or embeddings required.
Metavariables use the \ sigil: \NAME captures one node, \(NAME*\) a run of
siblings, \_/\* match anonymously. The pattern is matched per language, so a
single invocation scans every supported source file (others are skipped). Inside
an initialized project, ccc grep honors the project's include/exclude patterns
and .gitignore; otherwise it scans all supported source files under the path.
Results stream to the terminal file-by-file as each match is found (in completion
order, since files are matched in parallel) rather than all at once at the end.
Each matching file shows its matched line range; under a TTY the path is colored,
line numbers are dimmed, and the unmatched context around a match is dimmed so the
match stands out.
Note: ccc grep relies on cocoindex's structural code_match feature.
Until it ships in a released cocoindex, run against a local cocoindex build.
Docker
A Docker image is available for teams who want a reproducible, dependency-free
setup — no Python, uv, or system dependencies required on the host.
The recommended approach is a persistent container: start it once, and use
docker exec to run CLI commands or connect MCP sessions to it. The daemon
inside stays warm across sessions, so the embedding model is loaded only once.
Choosing an image
Two variants are published from each release:
The rest of this section uses :latest — substitute :full in the image: /
docker run commands if you want the full variant.
Mac users running the :full variant: local embedding inference is
CPU-only inside Docker, because Docker on macOS can't access Apple's Metal
(MPS) GPU. If you want local embeddings and fast inference, install
natively instead: pipx install 'cocoindex-code[full]'. The :latest
(slim) variant is unaffected — LiteLLM runs the model on the provider's
side, so Docker vs. native makes no difference.
Quick start — docker compose up -d
Bring it up in one line — no clone needed (bash / zsh):
Or grab docker/docker-compose.yml and run docker compose up -d next to it (works on any shell, including Windows cmd / PowerShell).
By default your home directory is mounted into the container (set
COCOINDEX_HOST_WORKSPACE to narrow this to a specific code folder). Index
data and the embedding model cache persist in a Docker volume across
restarts. Your global settings file at $HOME/.cocoindex_code/global_settings.yml
is visible and editable on the host; edits take effect on your next ccc command.
Pick a different image: set COCOINDEX_CODE_IMAGE to override the
default. For example, the :full variant or GHCR:
Or: docker run
Docker Desktop (macOS / Windows)
Linux (with PUID/PGID)
Shell wrapper for ccc commands
Paste this into ~/.bashrc / ~/.zshrc so ccc feels native on the host
and picks up the right project based on your current directory:
Now cd into a project under your workspace and run ccc init, ccc index,
ccc search ..., or ccc status.
Connect your coding agent
Claude Code
Register MCP from inside the target project so $PWD points there:
Or via .mcp.json:
Note: use -i (not -it). The -t flag allocates a terminal, which
interferes with MCP's JSON messaging over stdin/stdout — only add it for
interactive ccc commands like ccc init.
Codex
Upgrading from an older image
Earlier images used separate cocoindex-db and cocoindex-model-cache
volumes; the current image consolidates them into a single cocoindex-data
volume. Before pulling the new image, drop the old container and volumes —
indexes rebuild on your next ccc index, and the embedding model is
re-populated automatically on first start:
Configuration via environment variables
Pass configuration to docker run / compose with -e:
Security note: mounting $HOME gives the container read/write access
to everything under it. If that's too broad, bind-mount a narrower
directory instead (COCOINDEX_HOST_WORKSPACE=/path/to/code).
Build the image locally
Features
- Semantic Code Search: Find relevant code using natural language queries when grep doesn't work well.
- Incremental indexing: The Rust indexing engine re-indexes only changed files.
- Multi-Language Support: Python, JavaScript/TypeScript, Rust, Go, Java, C/C++, C#, SQL, Shell, and more.
- Embedded storage: No database setup required.
- Embedding providers: Local SentenceTransformers via the
[full] extra, with no API key, or 100+ cloud providers via LiteLLM.
Configuration
For a detailed guide on choosing and configuring embedding models, see EMBEDDINGS.md.
Configuration lives in two YAML files, both created automatically by ccc init.
User Settings (~/.cocoindex_code/global_settings.yml)
Shared across all projects. Controls the embedding model and environment variables for the daemon.
Note: The daemon inherits your shell environment. If an API key (e.g. OPENAI_API_KEY) is already set as an environment variable, you don't need to duplicate it in envs. The envs field is only for values that aren't in your environment.
Apple Silicon memory safety: MPS SentenceTransformer calls use CocoIndex's isolated GPU subprocess, keeping the model loaded while isolating Metal allocations from the daemon. The low and high watermarks are ratios of PyTorch's recommended maximum working set; they default here to 0.4 and 0.5. CocoIndex retries MPS out-of-memory failures with progressively smaller batches, and cocoindex-code releases unused allocator cache after each index run. Explicit COCOINDEX_RUN_GPU_IN_SUBPROCESS, PYTORCH_MPS_LOW_WATERMARK_RATIO, and PYTORCH_MPS_HIGH_WATERMARK_RATIO environment variables take precedence over these defaults.
Indexing concurrency: Multiple projects may prepare indexes concurrently, while CocoIndex serializes their GPU calls through its single MPS subprocess. A search waits only when its own project still needs the initial index.
Idle timeout: the background daemon holds the embedding model in RAM, so it exits after daemon.idle_timeout_minutes without client activity and is restarted automatically on your next ccc command or MCP search. By default, a live MCP session sends periodic heartbeats so the daemon remains warm while your coding agent is connected. Set daemon.keep_alive_with_mcp: false to let the daemon idle-exit during long-lived MCP sessions and release the model between real requests. Set idle_timeout_minutes: 0 to keep the daemon running forever.
Custom location: set COCOINDEX_CODE_DIR to place global_settings.yml somewhere other than ~/.cocoindex_code/ — useful if you want the file to live alongside your projects (e.g. on a synced folder).
indexing_params / query_params
Some embedding models expose different modes for documents vs queries (asymmetric retrieval). For example, Cohere's v3 models want input_type: search_document when embedding corpus content and input_type: search_query when embedding a user query; several SentenceTransformers models use prompt_name: passage / prompt_name: query for the same purpose. These knobs live under indexing_params and query_params:
ccc init populates these automatically for models it recognizes — including all Cohere v3, Voyage, Nvidia NIM, Gemini embedding (gemini/gemini-embedding-*, gemini/text-embedding-*, gemini/embedding-* — LiteLLM auto-maps input_type to Gemini's task_type), nomic-ai/CodeRankEmbed, nomic-ai/nomic-embed-code, nomic-ai/nomic-embed-text-v1/v1.5, mixedbread-ai/mxbai-embed-large-v1, and the Snowflake/snowflake-arctic-embed-* family — and prints the chosen defaults. For other models, it leaves a commented-out template under embedding: so you can fill it in by hand.
OpenAI embeddings (text-embedding-3-*, text-embedding-ada-002) are intentionally not in the list: they're symmetric and have no equivalent knob.
Accepted keys: prompt_name (sentence-transformers) and input_type (litellm). Other keys are rejected at daemon startup with a clear error. Note: dimensions is intentionally not exposed here — output dimension must be identical for indexing and query, so it's a model-wide setting rather than a per-side knob.
Doctor checks both sides. ccc doctor exercises the model once with indexing_params and once with query_params, reporting each as a separate Model Check (indexing) / Model Check (query) entry — so a misconfiguration on one side is diagnosable without hiding behind the other.
Legacy-bridge warning: if you're upgrading from an earlier version and your global_settings.yml uses nomic-ai/CodeRankEmbed or nomic-ai/nomic-embed-code without indexing_params / query_params, the daemon continues to apply the previous behavior (prompt_name: query at query time) and prints a one-time warning asking you to make the setting explicit. You can silence the warning by adding an empty block such as query_params: {}.
Project Settings (<project>/.cocoindex_code/settings.yml)
Per-project. Controls which files to index.
.cocoindex_code/ is automatically added to .gitignore during init.
max_file_size keeps bundled or generated files out of the index without having
to enumerate them in exclude_patterns. It accepts a plain byte count
(1048576) or a size with a binary unit suffix: B, KB, MB, GB
(case-insensitive, so 500KB and 500 kb are the same). The limit is
inclusive, and omitting the key indexes files of any size. It applies wherever
project file matching does, so ccc grep skips the same files.
After editing include_patterns, exclude_patterns, max_file_size, or language_overrides:
- Run
ccc doctor to preview which files match.
- Run
ccc index or ccc search --refresh ... to update the existing index.
- You do not need to delete the index or restart the daemon for these file-matching changes.
- After any
language_overrides change, the next run re-chunks all files, including files whose content did not change; the run reports them as reprocessed. Chunks whose text did not change keep their cached embeddings.
If you add or change custom chunkers, or edit a chunker module, restart the daemon (ccc daemon restart), then run ccc index. The daemon imports chunkers once at startup, so until it restarts it keeps running the old chunker code. The first run after the restart re-chunks all files, not only the ones the changed entry applies to; chunks whose text did not change keep their cached embeddings. A chunker counts as changed when its module: value changes or when the file of the module it names changes. Edits to other modules that file imports are not detected: after such an edit, also change the chunker's own module file (for example, bump a version comment in it).
Use chunkers when you want to control how a file type is split into chunks before indexing.
module: example_toml_chunker:toml_chunker means:
example_toml_chunker is a local Python module
toml_chunker is the function inside that module
In practice, this usually means:
- you create a Python file in your project, for example
example_toml_chunker.py
- you add a function in that file
- you point
settings.yml at it with module.path:function_name
The function should use this signature:
path is the file being indexed
content is the full text of that file
- return
language_override as a string like "toml" if you want to override language detection
- return
None as language_override if you want to keep the detected language
- return a
list[Chunk] with the chunks you want stored in the index
See src/cocoindex_code/chunking.py for the public types and tests/example_toml_chunker.py for a complete example.
Embedding Models
With the [full] extra installed, ccc init defaults to a local SentenceTransformers model (Snowflake/snowflake-arctic-embed-xs) — no API key required. To use a different model, edit ~/.cocoindex_code/global_settings.yml.
The envs entries below are only needed if the key isn't already in your shell environment — the daemon inherits your environment automatically.
Ollama (Local)
Set OLLAMA_API_BASE in envs: if your Ollama server is not at http://localhost:11434.
OpenAI
OpenAI-compatible (custom endpoint)
Many providers (vLLM, LM Studio, LocalAI, Together, Fireworks, DeepInfra, …) expose an OpenAI-compatible embedding API. Use the openai/ prefix and point OPENAI_BASE_URL at your endpoint:
Don't append /embeddings to the base URL — LiteLLM handles that.
Azure OpenAI
Gemini
Mistral
Voyage (Code-Optimized)
Cohere
AWS Bedrock
Nebius
Any LiteLLM-supported model works. When using a LiteLLM model, set provider: litellm (or omit provider — LiteLLM is the default for non-sentence-transformers models). For the full list of env vars each provider reads (API keys, base URLs, regions, …), see LiteLLM's Setting API Keys.
Local SentenceTransformers Models
Set provider: sentence-transformers and use any SentenceTransformers model (no API key required).
Example — general purpose text model:
GPU-optimised code retrieval:
nomic-ai/CodeRankEmbed delivers significantly better code retrieval than the default model. It is 137M parameters, requires ~1 GB VRAM, and has an 8192-token context window.
Note: Switching models requires re-indexing your codebase (ccc reset && ccc index) since the vector dimensions differ.
Supported Languages
Custom Database Location
By default, index databases (cocoindex.db and target_sqlite.db) live alongside settings in <project>/.cocoindex_code/. When running in Docker, you may want the databases on the container's native filesystem for performance (LMDB doesn't work well on mounted volumes) while keeping the source code and settings on a mounted volume.
Set COCOINDEX_CODE_DB_PATH_MAPPING to remap database locations by path prefix:
With this mapping, a project at /workspace/myrepo stores its databases in /db-files/myrepo/ instead of /workspace/myrepo/.cocoindex_code/. Settings files remain in the original location.
Multiple mappings are comma-separated and resolved in order (first match wins):
Both source and target must be absolute paths. If no mapping matches, the default location is used.
Troubleshooting
Run ccc doctor to check your settings, daemon health, embedding model, file matching, and index status.
sqlite3.Connection object has no attribute enable_load_extension
Some Python installations (e.g. the one pre-installed on macOS) ship with a SQLite library that doesn't enable extensions.
macOS fix: Install Python through Homebrew:
Then re-install cocoindex-code (see Get Started for install options):
Using pipx:
Using uv (install or upgrade):
MDB_MAP_FULL: Environment mapsize limit reached
The index is stored in an LMDB database whose maximum size is fixed when the daemon starts. The default ceiling is 4 GiB, which codebases with tens of thousands of files can exhaust, especially with high-dimensional embedding models like nomic-ai/CodeRankEmbed.
Raise the ceiling with the COCOINDEX_LMDB_MAP_SIZE environment variable (value in bytes). LMDB only grows the file as data is written, so a high limit doesn't pre-allocate disk — it's safe to set it generously:
Or, if you prefer to set it in your shell environment (the daemon inherits it):
The map size is read when the daemon starts, so restart it to pick up the change, then re-index:
This manual step is temporary. Once cocoindex#2108 lands, the map size grows automatically when needed and COCOINDEX_LMDB_MAP_SIZE won't be necessary.
Legacy: Environment Variables
If you previously configured cocoindex-code via environment variables, the cocoindex-code MCP command still reads them and auto-migrates to YAML settings on first run. We recommend switching to the YAML settings for new setups.
Telemetry
cocoindex-code sends anonymous usage telemetry through CocoIndex so we can see how the tool is used in aggregate and prioritize improvements. The events identify themselves as application: cocoindex-code.
We do not collect your source code, file paths, queries, search results, embeddings, settings, or any other content from your codebase or environment.
To opt out, set:
Large codebase / Enterprise
Teams indexing large or multiple repositories can share CocoIndex indexes across developers. Enterprise features include deduplication across branches.
If indexing fails with MDB_MAP_FULL, raise the LMDB map size. See MDB_MAP_FULL: Environment mapsize limit reached under Troubleshooting.
For help with remote setup, email [email protected].
Contributing
We welcome contributions. This project uses uv for development. CI checks lint, formatting, types, and tests for every PR. Run the same checks locally before opening a PR; failed pre-commit checks are the most common cause of red CI on incoming PRs.
1. Install the dev dependencies
After installing uv, sync the project. This installs everything the checks need — including prek, a fast pre-commit runner, plus Ruff, mypy, and pytest:
2. Run all checks before every PR
Run the full hook suite across all files — this is exactly what CI runs:
It runs trailing-whitespace/end-of-file fixes, Ruff lint (--fix) and format, uv.lock validation, mypy type checking, and the pytest suite. Fix anything it reports (Ruff auto-fixes most lint/format issues for you), re-run until it passes, then push.
3. (Optional) Run automatically on each commit
To have the same checks run on every git commit, install the git hook once:
For more details, see our contributing guide.
License
Apache-2.0