semcode
Acerca de Semantic code-search (semcode) MCP. Indexa símbolos de código e historial de commits. Combina embeddings densos con vectores dispersos BM25 para una búsqueda híbrida que equilibra la comprensión semántica con la precisión de palabras clave.
Documentación
semcode
Un servidor MCP (Model Context Protocol) que proporciona búsqueda semántica híbrida sobre código en un conjunto de
repositorios de GitHub que tú listas en config.yaml. Analiza símbolos
con Tree-sitter e indexa tanto el código como el historial de commits de git, para que los clientes de IA puedan consultarlos por
lenguaje natural o por nombre de símbolo.
La recuperación híbrida combina embeddings densos con BM25, de modo que tanto las consultas en lenguaje natural
("¿dónde publicamos eventos de pedidos?") como las búsquedas por nombre de símbolo (PlaceOrderRequest) funcionan bien.
Enviado en:
Cómo funciona
- Obtiene archivos fuente de los repositorios de GitHub configurados
- Analiza símbolos de código (funciones, clases, métodos, componentes) usando Tree-sitter
- Genera dos embeddings por símbolo: un vector semántico denso (proveedor conectable: Jina Code V2 por defecto, o Voyage / OpenAI / Ollama) y un vector disperso BM25 basado en tokens de identificadores de código (camelCase / snake_case divididos en subpalabras)
- Almacena ambos en Qdrant y los recupera con búsqueda híbrida — Reciprocal Rank Fusion (RRF) sobre los resultados densos y dispersos — para que las consultas en lenguaje natural y las búsquedas por nombre de símbolo funcionen bien
- Opcionalmente indexa el historial de commits en una colección separada de Qdrant (solo denso)
- Expone herramientas de búsqueda e indexación a través del protocolo MCP (y una pequeña API HTTP)
La indexación es incremental — los archivos se omiten cuando su SHA de blob de Git coincide con la última versión indexada.
Los archivos que ya no existen (o que se analizan a cero símbolos) se limpian automáticamente. Pasa force: true
para re-embedding todo.
¿Quieres más detalles? ¡Mira el blog!
Documentación
La documentación detallada de los internals del sistema RAG está en goodbyeplanet.github.io/semcode:
- Pipeline de ingesta — cómo se descubre, analiza, embedding y almacena el código
- Vectores densos — proveedores de embeddings y estrategia de texto
- Vectores dispersos — BM25 y el tokenizador de código
- Recuperación con RRF — búsqueda híbrida y herramientas MCP
- Configuración — todas las variables de entorno y config.yaml
- Changelog — historial de versiones, generado a partir de mensajes de commit
Lenguajes soportados
El lenguaje se detecta automáticamente por extensión de archivo o nombre de archivo — no se necesita configuración.
Go, Java, Python, TypeScript / JavaScript (React), Rust, C#, C, C++, Ruby, PHP, Kotlin, Scala, Swift, Dart, Bash, SQL, Lua, R, Dockerfile, Docker Compose, Markdown, JSON, HTML, CSS, XML.
La mayoría de los analizadores son conscientes del framework donde importa — estereotipos Spring y rutas HTTP para Java/Kotlin, FastAPI/Pydantic
para Python, ASP.NET para C#, Rails para Ruby, Laravel/Symfony para PHP, widgets React/SwiftUI/Flutter, etc. Ver
server/parser/ para los detalles de extracción por lenguaje.
Configuración
Requisitos previos: Python 3.12+, Docker, token de GitHub
# Install dependencies
uv sync
# Copy environment file, then edit .env to set GITHUB_TOKEN
# (a fine-grained PAT with Contents: read on the target repos is sufficient)
cp .env.example .env
# Optional — only if you want curated/static services (see below for the alternative):
# copy the services config, then list the repositories you want indexed
cp config.example.yaml config.yaml
Configura qué repositorios indexar en config.yaml:
services:
- name: my-service
github_repo: owner/repo
github_ref: main # optional, defaults to "main" — branch, tag, or commit SHA
root: src/main/java # optional — limit indexing to this subdirectory (useful for monorepos)
exclude: # optional — skip matching paths
- "**/vendor/**"
- "**/node_modules/**"
El indexador descubre e indexa automáticamente todos los archivos con extensiones reconocidas. Usa root para limitar un servicio a un
subdirectorio dentro de un repositorio compartido, y exclude para omitir rutas que no quieras indexar (pruebas, artefactos de compilación, código
generado, etc.).
Escalando más allá de un puñado de repos: config.yaml es una lista estática y curada — excelente para un pequeño número de servicios,
pero indexar cientos de repos de esta manera significa cientos de entradas mantenidas a mano. Como alternativa (o complemento),
POST /reindex acepta una definición de repositorio en línea y lo registra sobre la marcha, sin necesidad de entrada en config.yaml — ver
examples/github-actions/reindex-on-merge.yml para un flujo de trabajo listo para usar
que auto-registra un repositorio y lo reindexa en cada merge. Detalles en la sección API HTTP a continuación. Si un
nombre colisiona entre los dos, la entrada de config.yaml siempre gana.
No necesitas un config.yaml en absoluto para ejecutarlo de esta manera — un archivo faltante se trata como cero servicios configurados, no como un
error. docker-compose.yaml refleja esto: por defecto no monta config.yaml, por lo que make docker-up /
make docker-up-jina funcionan de inmediato para configuraciones solo de registro ad-hoc. Si también quieres servicios curados,
copia config.example.yaml a config.yaml (arriba) y usa los objetivos -with-config en su lugar, que superponen
docker-compose.config-yaml.yml encima para agregar el montaje: make docker-up-with-config / make docker-up-jina-with-config
(o docker compose -f docker-compose.yaml -f docker-compose.config-yaml.yml up -d directamente). No edites a mano la
línea de volumen en docker-compose.yaml en sí — montar por bind un config.yaml que no existe en el host silenciosamente
crea un directorio vacío allí en lugar de dejar la ruta ausente, lo que rompe el servidor (se muestra como un error
claro si ocurre: CONFIG_PATH (...) is a directory, not a file).
Un solo GITHUB_TOKEN lee cada repositorio que indexas de esta manera. Para un puñado de entradas config.yaml, un
PAT de grano fino con alcance a esos repos es suficiente, pero para auto-registro a nivel de organización — donde cualquier repositorio puede incorporarse
solo agregando el flujo de trabajo — un PAT necesitaría que su lista de acceso a repos se actualizara fuera de banda cada vez que un nuevo repositorio
comience a usarlo. Una GitHub App instalada a nivel de organización (todos los repos, Contents: read) evita eso: los nuevos repos se cubren
automáticamente, sin mantenimiento de token por incorporación.
Ejecución
Hay dos formas de ejecutarlo, dependiendo de si quieres que los embeddings provengan de un contenedor local o de un proveedor alojado. Elige una:
Ruta A — Jina local vía TEI (predeterminado, sin clave API requerida):
make docker-up-jina
# or: docker-compose --profile jina up
Ruta B — proveedor alojado (Voyage / OpenAI) o Ollama local:
# 1. In .env, set EMBEDDINGS_PROVIDER=voyage|openai|ollama and the relevant API key.
# 2. Then start without the jina profile:
make docker-up
# or: docker-compose up
¿Usando config.yaml para servicios curados? Usa la variante -with-config del objetivo que corresponda arriba
(make docker-up-with-config / make docker-up-jina-with-config) — ver la sección Configuración.
⚠ El
EMBEDDINGS_PROVIDERpredeterminado esjina. Si inicias sin--profile jinapero dejas el proveedor en el predeterminado, semcode arrancará (Jina esrequired: falseen compose) pero la primera llamada de embedding fallará con un error de conexión — no hay respaldo automático.
Servicios iniciados con health checks y volúmenes persistentes:
| Servicio | Perfil | Puerto | Volumen | Propósito |
|---|---|---|---|---|
| Qdrant | siempre | 6333 (HTTP), 6334 (gRPC) | qdrant_data | Base de datos vectorial |
| Jina Embeddings (TEI) | jina | 8087 | embeddings_cache | Servidor de modelo de embeddings |
| semcode MCP | siempre | 8090 | monta ./config.yaml de solo lectura con -with-config | Servidor MCP + HTTP |
El servidor MCP inicia con colecciones vacías — dispara un índice inicial llamando a la herramienta MCP reindex
o POST /reindex (ver abajo).
Conectando clientes de IA
Una vez que el servidor esté en ejecución, apunta tu cliente de IA a http://localhost:8090/mcp.
Claude Code (CLI)
claude mcp add --transport http semcode http://localhost:8090/mcp
Otros clientes MCP (Claude Desktop, Cursor, etc.) — agrega una entrada a la configuración MCP del cliente:
{
"mcpServers": {
"semcode": {
"transport": "http",
"url": "http://localhost:8090/mcp"
}
}
}
Conexión a través de stdio
En lugar de apuntar a un servidor HTTP en ejecución, el cliente puede iniciar el proceso del servidor en sí y
hablar con él a través de stdin/stdout. Esto aún necesita Qdrant accesible (por ejemplo, docker-compose up qdrant)
y un entorno Python local con dependencias instaladas (uv sync).
Claude Code (CLI)
claude mcp add semcode --transport stdio --env MCP_TRANSPORT=stdio -- uv run --directory /path/to/semcode python -m server.main
Otros clientes MCP — agrega una entrada a la configuración MCP del cliente:
{
"mcpServers": {
"semcode": {
"command": "uv",
"args": ["run", "--directory", "/path/to/semcode", "python", "-m", "server.main"],
"env": {
"MCP_TRANSPORT": "stdio"
}
}
}
}
GITHUB_TOKEN, QDRANT_URL, y las variables del proveedor de embeddings aún se leen de .env en el
directorio del proyecto — uv run lo recoge automáticamente.
Conexión a través de SSE
SSE es el transporte HTTP MCP heredado, reemplazado por
streamable-http. Úsalo solo para clientes que aún no soportanstreamable-http— las nuevas configuraciones deberían usar la configuraciónstreamable-httparriba.
Establece MCP_TRANSPORT=sse en .env (o el entorno) e inicia el servidor de la misma manera que
streamable-http (make docker-up / make docker-up-jina, o uv run python -m server.main
localmente). El servidor expone un endpoint SSE en http://localhost:8090/sse.
Claude Code (CLI)
claude mcp add --transport sse semcode http://localhost:8090/sse
Otros clientes MCP — agrega una entrada a la configuración MCP del cliente:
{
"mcpServers": {
"semcode": {
"transport": "sse",
"url": "http://localhost:8090/sse"
}
}
}
Indexación
El pipeline de indexación está orientado a símbolos: cada función, clase, método o componente se convierte en su propio fragmento con un embedding vectorial y un payload rico.
- Descubrimiento — lista todos los archivos en el repositorio en
github_ref, aplicando filtrosrootyexclude - Detección de cambios — compara el SHA de blob de Git del archivo con el último valor indexado; los archivos sin cambios se omiten
- Análisis — Tree-sitter recorre el AST y emite objetos
CodeSymbolpor lenguaje - Texto de embedding denso — etiqueta de lenguaje, tipo de símbolo, clase padre, paquete, extras de framework (estereotipo Spring,
ruta HTTP, Lombok, React memo), docstring, firma y fuente (fuente truncada en
EMBEDDING_MAX_CHARS, un predeterminado consciente del proveedor — ver docs/configuration.md) - Texto de embedding disperso (BM25) — firma, docstring y fuente. Los identificadores de código se dividen en subpalabras (
camelCase, snake_case) antes de la tokenización, por lo que
getUserByIdse indexa comoget,user,by,idasí como el token completo - Procesamiento por lotes — el proveedor denso procesa en lotes de 32 (Jina/TEI, Ollama) o 128 (Voyage, OpenAI); BM25 se ejecuta en proceso
- Upsert — ambos vectores se almacenan bajo un solo punto en Qdrant, claveado por un UUID determinista (por servicio / archivo / símbolo / línea)
- Limpieza — las entradas para archivos que ya no están en el repositorio (o que ahora se analizan a cero símbolos) se eliminan
La indexación del historial de Git es un pipeline separado y opcional que embedding los mensajes de commit y las rutas de archivos
cambiados en la colección git_commits. Los diffs unificados completos se almacenan en el payload y son recuperables
a través de la herramienta get_commit. El número de commits por servicio está limitado por GIT_HISTORY_MAX_COMMITS
(predeterminado 500).
Pruebas
uv sync --group dev
uv run pytest
Las pruebas viven bajo tests/:
tests/parser/test_*.py— un archivo por lenguaje; captura el comportamiento del analizador contra fixtures canónicos entests/fixtures/<language>/tests/test_pipeline.py,tests/test_store.py,tests/test_git_history.py— pruebas de integración para el pipeline de indexación y el almacén Qdranttests/test_reindex_route.py— pruebas de rutas HTTP
Herramientas MCP
| Herramienta | Descripción |
|---|---|
search_code | Búsqueda híbrida (densa + BM25) por consulta, con filtros opcionales por idioma, servicio y tipo de símbolo |
find_symbol | Busca un símbolo por nombre: coincidencia exacta o coincidencia de token sin distinguir mayúsculas cuando exact=false |
find_usages | Encuentra código que referencia un nombre de símbolo dado (búsqueda semántica, luego excluye la definición en sí) |
get_code_context | Obtiene el código fuente completo de un archivo, o un símbolo específico dentro de él, directamente desde GitHub |
reindex | Activa la indexación de código de uno o todos los servicios (incremental por defecto; force para re-embedding) |
index_history | Indexa el historial de commits de git; obtiene automáticamente los diffs de los commits que les falten |
search_commits | Busca en el historial de commits de git con lenguaje natural |
get_commit | Obtiene detalles completos de un commit específico, incluidos archivos modificados y diffs |
list_indexed_services | Lista los servicios indexados con recuentos de fragmentos y archivos, idiomas y hora de última indexación |
index_stats | Muestra estadísticas de la colección Qdrant y los servicios configurados |
find_symbol(exact=false) coincide con un índice de texto completo sobre los tokens camelCase/snake_case del nombre del símbolo, por lo que
order o ord encuentra placeOrderRequest en ~2 ms independientemente del tamaño de la colección. Los fragmentos a mitad de token (rder) también
coinciden, pero recurren a un escaneo del lado del cliente que es lineal en el tamaño de la colección. Las colecciones indexadas antes de que existiera este campo
usan ese mismo recurso hasta que se reindexen, y como la detección de cambios omite archivos sin cambios, poblar el
campo requiere una reindexación forzada (POST /reindex {"force": true}), que re-embediza cada símbolo. Consulta
docs/retrieval-rrf.md.
Prompts de MCP
| Prompt | Argumentos | Descripción |
|---|---|---|
service_overview | service | Guía al cliente para producir una visión general arquitectónica de un servicio: puntos de entrada HTTP, tipos de dominio y convenciones notables del framework |
system_design_overview | (ninguno) | Guía al cliente para producir una visión general completa del diseño del sistema: inventario de servicios, topología de comunicación, almacenes de datos compartidos y preocupaciones transversales: incluye diagramas Mermaid |
API HTTP
Además de las herramientas MCP, el servidor expone dos endpoints HTTP para activar la indexación desde CI/CD o programadores externos:
| Endpoint | Cuerpo | Descripción |
|---|---|---|
POST /reindex | {"service": "<name>"?, "force": <bool>?, "github_repo": "<owner/repo>"?, "github_ref": "<ref>"?, "root": "<path>"?, "exclude": [<glob>, ...]?} | Reindexa uno o todos los servicios: devuelve NDJSON |
POST /reindex-history | {"service": "<name>"?, "force": <bool>?} | Indexa el historial de commits de git: devuelve NDJSON |
Todos los cuerpos son opcionales: omite service para actuar sobre todos los servicios, omite force para indexación incremental.
Ambos endpoints transmiten JSON delimitado por nuevas líneas (un marco por línea) para que puedas consumir el progreso
en tiempo real desde pipelines de CI/CD o cualquier otro cliente.
Registrar un repositorio sin config.yaml: si el cuerpo de POST /reindex incluye github_repo, el
nombre service se registra con esa definición de repositorio (persistido, por lo que sobrevive a reinicios y se comporta
como un servicio config.yaml a partir de entonces) antes de que se ejecute la indexación: service es obligatorio en este caso.
github_ref tiene como valor predeterminado main; root/exclude reflejan los mismos campos en config.yaml. Un nombre service
ya definido en config.yaml siempre tiene prioridad sobre uno registrado de esta manera. No hay
autenticación en este endpoint, igual que en el resto de /reindex, así que colócalo detrás de tu propio límite
de red antes de exponerlo. Consulta
examples/github-actions/reindex-on-merge.yml para un
flujo de trabajo listo para usar.
Formas de los marcos:
// in-flight progress
{"type": "progress", "phase": "discovery|upserting|cleanup", "current": 12, "total": 200, "percentage": 6.0, "service": "my-service"}
// final summary (one per request)
{"type": "done", "result": {"files": 42, "chunks": 318, "skipped": 5}}
// emitted instead of "done" on failure
{"type": "error", "message": "..."}
Para /reindex, los marcos upserting llevan current como un recuento monótonamente creciente de archivos
resueltos (indexados, omitidos como sin cambios o descartados por un error de obtención/análisis), por lo que siempre termina en
total. Los archivos se indexan de forma concurrente, por lo que los marcos se emiten por lote en lugar de por archivo.
Para /reindex-history, el valor phase es discovery|embedding|upserting y el resultado done es
{"new": int, "skipped": int, "diff_updated": int}.
Variables de entorno
| Variable | Predeterminado | Descripción |
|---|---|---|
GITHUB_TOKEN | (obligatorio) | Token de GitHub con acceso de lectura al repositorio |
QDRANT_URL | http://localhost:6333 | URL de conexión de Qdrant |
QDRANT_COLLECTION | code_symbols | Nombre de la colección para vectores de símbolos de código |
QDRANT_COMMITS_COLLECTION | git_commits | Nombre de la colección para vectores de mensajes de commit |
EMBEDDINGS_PROVIDER | jina | Uno de jina, jina-api, voyage, openai, ollama: consulta Proveedores de embedding a continuación |
GIT_HISTORY_MAX_COMMITS | 500 | Máximo de commits indexados por servicio |
CODE_CONTEXT_CACHE_SIZE | 128 | Archivos almacenados en caché en memoria para get_code_context (clave por SHA de blob); 0 desactiva |
CODE_CONTEXT_CACHE_TTL | 900 | Segundos durante los cuales el contenido de un archivo en caché sigue siendo válido |
MCP_TRANSPORT | streamable-http | Uno de streamable-http, sse, stdio |
MCP_HOST / MCP_PORT | 127.0.0.1 / 8090 | Dirección de enlace del servidor |
MCP_STATELESS_HTTP | true | Sirve streamable-http sin seguimiento de sesión (no se necesitan sesiones fijas); false restaura sesiones MCP por cliente. Solo streamable-http |
CONFIG_PATH | ./config.yaml | Ruta al archivo de configuración de servicios |
Proveedores de embedding
El backend de embedding se puede seleccionar mediante EMBEDDINGS_PROVIDER. El predeterminado es jina para que las
implementaciones existentes sigan funcionando sin cambios. Cada proveedor deriva sus propias dimensiones de vector del
modelo configurado: no es necesario establecer dimensiones manualmente a menos que quieras anularlas.
| Variable | Predeterminado | Se aplica a | Descripción |
|---|---|---|---|
JINA_URL | http://localhost:8087 | jina | URL base de TEI |
JINA_MODEL | jinaai/jina-embeddings-v2-base-code | jina | Solo informativo: la bandera --model-id del contenedor TEI es lo que realmente carga. Edita docker-compose.yaml para cambiar modelos. |
JINA_DIMENSIONS | 768 | jina | Dimensiones del vector del modelo TEI |
JINA_API_KEY | (obligatorio si provider=jina-api) | jina-api | Clave de API de Jina AI (endpoint alojado en api.jina.ai) |
JINA_API_MODEL | jina-embeddings-v2-base-code | jina-api | Modelo Jina alojado: también admite jina-code-embeddings-0.5b, jina-code-embeddings-1.5b |
JINA_API_DIMENSIONS | (nativo) | jina-api | Anulación opcional de Matryoshka (los modelos de embeddings de código admiten reducción); obligatorio para modelos sin un valor nativo predeterminado |
VOYAGE_API_KEY | (obligatorio si provider=voyage) | voyage | Clave de API de Voyage AI |
VOYAGE_MODEL | voyage-code-3 | voyage | Modelo de embedding de Voyage |
VOYAGE_DIMENSIONS | (nativo) | voyage | Anulación opcional: Voyage code-3 admite 256 / 512 / 1024 / 2048 |
OPENAI_API_KEY | (obligatorio si provider=openai) | openai | Clave de API de OpenAI |
OPENAI_EMBEDDING_MODEL | text-embedding-3-large | openai | Modelo de embedding de OpenAI |
OPENAI_DIMENSIONS | (nativo) | openai | Anulación opcional (los modelos text-embedding-3-* admiten reducción) |
OLLAMA_URL | http://localhost:11434 | ollama | URL base de Ollama |
OLLAMA_MODEL | nomic-embed-text | ollama | Modelo de embedding de Ollama |
OLLAMA_DIMENSIONS | (nativo) | ollama | Obligatorio si se usa un modelo que no está en la tabla de dimensiones integrada |
voyage-code-3 supera a jinaai/jina-embeddings-v2-base-code en la mayoría de los puntos de referencia de recuperación de código,
por lo que cambiar a Voyage también es una palanca de calidad, no solo de flexibilidad.
Cambio de proveedores sobre un índice existente: si el tamaño de los vectores del nuevo proveedor difiere
de la colección Qdrant existente, el servidor falla rápidamente al inicio con un error claro que señala
la colección problemática. Para cambiar, elimina ambas colecciones (code_symbols y git_commits) mediante la
interfaz o API de Qdrant, y luego reindexa. No hay migración automática.
Configuración solo alojada (sin contenedor TEI local): establece EMBEDDINGS_PROVIDER y la clave de API correspondiente
en .env, luego inicia sin el perfil jina (docker-compose up / make docker-up). El
contenedor jina-embeddings no se iniciará.
Colecciones Qdrant
code_symbols — un punto por símbolo analizado, que lleva dos vectores nombrados:
text-dense— distancia coseno, HNSW (m=16,ef_construct=128), tamaño determinado por el proveedor de incrustacionestext-sparse— BM25 sobre tokens de subpalabras de identificadores de código, índice disperso en memoria
search_code consulta ambos mediante una llamada Qdrant query_points con FusionQuery(fusion=RRF). Los campos de
payload indexados (language, service, symbol_type, chunk_tier, parent_name, file_path) son
utilizables como filtros. search_code y find_symbol exponen chunk_tier ("method" o "class")
directamente, por lo que una consulta puede limitarse solo a clases o solo a métodos. El payload completo también incluye
signature, docstring, annotations, package,
start_line, end_line, file_hash, indexed_at, y extras específicos del lenguaje (http_method,
http_route, spring_stereotype, lombok_annotations, is_async, uses_memo, …).
git_commits — un vector solo denso por commit (coseno, HNSW m=16 / ef_construct=128).
El payload incluye sha, service, message, author_name, author_email, committed_at,
indexed_at, has_diff, diff_truncated, y files (matriz de archivos modificados con filename,
status, additions, deletions, patch). sha, service, author_name, y has_diff son
campos de payload indexados.
Versionado y lanzamientos
La versión vive en un solo lugar: version en pyproject.toml. server/main.py la lee a través de
importlib.metadata.version("semcode") y la reporta como el serverInfo.version de MCP, por lo que un cliente siempre ve
la versión del paquete que realmente está ejecutando.
Nunca edites esa versión manualmente. Los lanzamientos están automatizados con
release-please (.github/workflows/release.yml):
- Los commits llegan a
mainusando Conventional Commits. - release-please abre (o actualiza) un PR de
chore(main): release X.Y.Zque incrementapyproject.tomly escribeCHANGELOG.mda partir de esos mensajes de commit. - Fusionar ese PR etiqueta el commit como
vX.Y.Zy crea un Release de GitHub. Nada se lanza hasta que un humano lo fusiona.
Un lanzamiento actualmente produce un Release de GitHub con el código fuente y el changelog generado. Nada se publica en
PyPI, y no se envía ninguna imagen de contenedor — semcode todavía se construye localmente desde el Dockerfile.
Tipos de commit e incrementos de versión
| Prefijo de commit | Incremento | Sección del changelog |
|---|---|---|
feat!: / BREAKING CHANGE: | major | ⚠ Cambios de ruptura |
feat: | minor | Características |
fix: | patch | Correcciones de errores |
perf: | patch | Mejoras de rendimiento |
deps: | patch | Dependencias |
refactor: | patch | Refactorización de código |
docs: test: style: chore: ci: build: | patch | oculto |
Dependabot está configurado (.github/dependabot.yml) para usar el prefijo deps, por lo que una semana de actualizaciones de
dependencias aún produce un lanzamiento de parche que marca qué conjunto de dependencias se probó junto.
Major está reservado para cambios de ruptura en las firmas de herramientas MCP o el esquema de configuración — las superficies de las que dependen los clientes y las implementaciones.
Notas
- Debido a que los mensajes de commit en
mainimpulsan todo, los títulos de squash-merge deben ser Conventional Commits bien formados. El repositorio debería tener "Default to PR title for squash merges" habilitado. - El PR de lanzamiento se crea con
GITHUB_TOKEN, que por diseño no activa otros flujos de trabajo — CI no se vuelve a ejecutar en el propio PR de lanzamiento. Los commits que lanza ya fueron probados enmain.
Estructura del proyecto
server/
├── main.py # MCP server entry point + lifespan
├── config.py # Settings and service configuration
├── state.py # Shared store singletons
├── parser/ # Tree-sitter parsers (Go, Java, Python, TypeScript, Rust, C#, C, C++, Ruby, PHP, Kotlin, Scala, Swift, Dart, Bash, SQL, Lua, R, Dockerfile, Compose, Markdown, JSON, HTML, CSS, XML)
├── embeddings/ # Pluggable dense providers (Jina/Voyage/OpenAI/Ollama) + BM25 sparse + code identifier tokenizer
├── indexer/ # GitHub fetcher, code indexing pipeline, git history pipeline
├── store/ # Qdrant vector stores (code_symbols hybrid + git_commits dense)
├── tools/ # MCP tool implementations (search, index, history, admin)
├── prompts/ # MCP prompt templates (service_overview, system_design_overview)
└── routes/ # HTTP routes (reindex, reindex-history)