TL;DR No indexa, y no necesitas montarle un MCP de búsqueda para arreglarlo. Sin índice, el alcance lo pones tú, y se pone al arrancar: lanza
claudedesde el subdirectorio en el que vas a trabajar, no desde la raíz. En un monorepo mío eso pasa de 57.720 ficheros en alcance a 2.977. Segunda palanca, el plugin de LSP de tu lenguaje. Y si tu repo es pequeño, no tienes que hacer nada.
Casi nadie llega a esta pregunta por curiosidad. Llega con una decisión pendiente. Se ve en lo que la gente busca justo después:
Claude Code semantic search
Code-index-MCP
Claude Code context
Es decir: ¿me falta algo? ¿tengo que enchufarle un índice?
La respuesta corta es no. La larga es que hay dos cosas que sí deberías hacer, y ninguna es esa.
No, y es a propósito
La primera pregunta de la FAQ de Anthropic es literalmente esta:
"¿Claude Code indexa mi codebase entero o usa una base de datos vectorial para almacenar información sobre él?" "No. Claude Code tiene acceso a un system prompt y a una serie de herramientas que puede usar para navegar tu codebase bajo demanda. Por ejemplo, si necesita entender algo, usará una herramienta de búsqueda para buscar en él y leerá ficheros bajo demanda."
Puedes comprobarlo sin fiarte de nadie:
find ~/.claude -maxdepth 3 \
\( -iname "*index*" -o -iname "*embed*" -o -iname "*vector*" \
-o -iname "*.db" -o -iname "*.faiss" \) 2>/dev/null
Sale un sessions-index.json, y ese indexa tus conversaciones, no tu código: sessionId, firstPrompt, summary. Ni un símbolo. (Lo que sí encontrarás más hondo es ~/.claude/projects/<proyecto>/memory/, que son los apuntes que Claude escribe para sí mismo, no un índice.)
En su lugar hay tres herramientas: Glob busca ficheros por nombre, Grep busca dentro del contenido, y Read abre. Las dos primeras son el mismo ripgrep, que Claude Code trae empaquetado.
Lo que te ahorras: cero espera al abrir el proyecto, y ninguna respuesta salida de un índice de hace tres commits. Lo que pagas: cada sesión empieza a ciegas, y localizar algo cuesta lecturas que ocupan contexto.
De ahí sale todo lo demás. Si no hay índice, el alcance lo tienes que poner tú.
Acción 1: arranca donde trabajas, no en la raíz
Es gratis, es instantáneo y es la que más mueve la aguja.
El directorio desde el que lanzas claude decide tres cosas a la vez: qué ficheros puede leer y editar sin pedirte permiso, qué CLAUDE.md se cargan al arrancar, y qué .claude/settings.json aplica.
En un monorepo mío de trabajo, medido:
claude # desde la raíz
# alcance: 57.720 ficheros
cd <el-paquete-que-toco> && claude
# alcance: 2.977 ficheros
Veinte veces menos superficie que rastrear, sin instalar nada. Y de paso, de los cuatro CLAUDE.md que hay repartidos por ese árbol, solo se cargan el de tu paquete y los de sus padres, en vez de que se te vayan colando los de equipos en cuyo código no entras.
El precio, y es real: desde ahí Claude no llega a los paquetes hermanos. Cuando la tarea los cruce:
claude --add-dir ../shared
O lo dejas puesto en .claude/settings.json del paquete:
{ "permissions": { "additionalDirectories": ["../shared"] } }
La regla práctica: arranca en la raíz solo cuando la tarea de verdad cruce subsistemas. El resto del tiempo, acotado.
Acción 2: enciende el LSP de tu lenguaje
La segunda que más se nota, y por el mismo motivo. Sin language server, cuando preguntas dónde se define una función la respuesta sale de grep: te devuelve todas las coincidencias de texto (comentarios, strings, nombres parecidos) y Claude abre varios ficheros para desambiguar. Con LSP, goToDefinition devuelve una ubicación resuelta semánticamente. Se acaban las lecturas de desambiguación, que son las caras.
/plugin install typescript-lsp@claude-plugins-official
Hay plugin oficial para Python, Rust, Go, Java, PHP y ocho más. Ojo, que el plugin no instala el language server: si te sale Executable not found in $PATH te falta el binario, y eso está contado entero en por qué tu plugin de LSP no arranca.
Y no, no te montes un índice
La documentación menciona la vía, pero léela despacio porque es condicional y no una recomendación: "si tu organización ya tiene una búsqueda de código o un índice RAG sobre el repositorio, exponlo como herramienta MCP".
O sea: si ya lo tienes montado y mantenido por otro motivo, enchúfalo. Construir uno solo para Claude es asumir el trabajo que Anthropic decidió no hacer, y te devuelve el problema que no tenías, que es mantenerlo fresco. Las dos acciones de arriba no caducan nunca.
Si aún te sobra ruido
Por orden de lo que se nota:
| Ajuste | Dónde | Qué recorta |
|---|---|---|
permissions.deny con Read(...) |
.claude/settings.json |
Abrir código vendorizado o generado que sí está commiteado |
claudeMdExcludes |
.claude/settings.local.json |
Cargar CLAUDE.md de otros equipos si arrancas en la raíz |
worktree.sparsePaths |
.claude/settings.json |
Sacar a disco el árbol entero en cada worktree |
CLAUDE_CODE_GLOB_NO_IGNORE=false |
Variable de entorno | Los resultados de vendor/ y node_modules/ (ver abajo) |
Ese último merece explicación, porque es la asimetría que a nadie le cuadra: Grep respeta tu .gitignore y Glob no. Y lo raro es que son el mismo motor. En el binario de la 2.1.220, Glob se construye así:
Yt(process.env.CLAUDE_CODE_GLOB_NO_IGNORE || "true") // por defecto, true
Yt(process.env.CLAUDE_CODE_GLOB_HIDDEN || "true") // por defecto, true
["--files", "--glob", patron, "--sort=modified",
...noIgnore ? ["--no-ignore"] : [],
...hidden ? ["--hidden"] : []]
Lanza literalmente rg --files --no-ignore --hidden. Por eso, buscando **/*Controller* en mi Laravel, Glob te saca vendor/orchestra/... mientras Grep te da tus app/Http/Controllers/.... Esa segunda variable, CLAUDE_CODE_GLOB_HIDDEN, no aparece en ninguna página de la documentación.
Documentación oficial: Monorepos y repos grandes · Tools reference