enaho-mcp
# enaho-mcp
[](https://m8ven.ai/mcp/andermc66-enaho-mcp-1n7he9)
[](https://m8ven.ai/mcp/andermc66-enaho-mcp-1n7he9)
Servidor MCP y CLI para los microdatos del **INEI** (Perú): ENAHO, ENDES,
ENAPRES, ENA y ocho encuestas más.
El portal de microdatos del INEI es una aplicación ASP con dropdowns en cascada y
sin API. Bajar un módulo son cuatro clicks; bajar una encuesta completa a través
de los años son cientos. Pero el problema grande no es mecánico: es **saber qué
pedir**. Que el ingreso del hogar está en la Sumaria y no en el módulo de empleo,
que las llaves de unión son `conglome`/`vivienda`/`hogar`, que en la ENDES son
`hhid`/`caseid` y su factor viene multiplicado por un millón, o que un promedio
sin factor de expansión no representa a nadie.
Este servidor mete ese conocimiento en las herramientas, no en el prompt.
---
## Qué hace bien
**Reproduce las cifras oficiales.** Validado contra el informe técnico de pobreza
2023 del INEI:
| Indicador | enaho-mcp | Oficial INEI |
|---|---|---|
| Incidencia de pobreza 2023 | **29.046 %** | 29.0 % |
| Pobreza extrema 2023 | **5.747 %** | 5.7 % |
| Pobreza extrema 2022 | **5.010 %** | 5.0 % |
| Pobreza extrema 2021 | **4.123 %** | 4.1 % |
| Gini del ingreso per cápita 2023 | **0.4233** | ~0.42 |
| Población expandida 2023 | **34 107 048** | ~34.1 M |
**Nunca devuelve microdatos al contexto.** Las herramientas devuelven metadatos,
rutas en disco y agregados chicos. Cuando el parquet está listo, el análisis
libre se hace con pandas sobre esa ruta.
**Estadística correcta bajo diseño complejo.** Linealización de Taylor con
estimador de conglomerado último, IC logit para proporciones, mediana por
Woodruff, chi-cuadrado corregido por Rao-Scott, prueba de diferencia entre
dominios con su covarianza, Gini y percentiles por bootstrap rescalado de
Rao-Wu-Yue, **regresión lineal y logit con errores estándar por sandwich de
conglomerados** (el equivalente de `svy: reg`) e **índices FGT** de pobreza. Sin
scipy ni samplics: código auditable y bajo test.
**Genera el entregable.** Informes en Word, Excel, PDF, Markdown y HTML
construidos ejecutando las estimaciones, no copiando números.
**Avisa de lo que suele salir mal en silencio.** Filas perdidas en cada merge,
estratos con un solo conglomerado, coeficientes de variación por encima del
umbral de publicación del INEI, variables que cambian de significado entre olas,
pesos DHS sin escalar y líneas de pobreza en otra unidad que el gasto.
---
## Instalación
```bash
git clone <este-repo> && cd enaho-mcp
uv sync
uv run enaho doctor # comprueba catálogo, índice, cache y dependencias
```
Registro en Claude Code (scope `user` para tenerlo en todos los proyectos):
```bash
claude mcp add --scope user --transport stdio enaho \
-- uv run --directory /ruta/absoluta/enaho-mcp python -m enaho_mcp.interfaces.mcp.servidor
```
Verificación: `claude mcp list`, y dentro de la sesión `/mcp`.
Sin cliente MCP: `uv run enaho-mcp --diagnostico` lista herramientas, resources y
prompts.
---
## Verificación
Este servidor ha sido verificado por [M8ven](https://m8ven.ai/mcp/andermc66-enaho-mcp-1n7he9) (Trust Score: 67/100):
- ✅ Sin exfiltración de credenciales ni acceso a archivos sensibles
- ✅ Sin ofuscación de código
- ✅ Variables de entorno declaradas (`ENAHO_MCP_HOME`, `ENAHO_MCP_LIMITE_GB`, `ENAHO_MCP_DEBUG`)
- ✅ Disponible en [Glama MCP Registry](https://glama.ai/mcp/servers/n0qjhud98v)
**Sugerencias de mejora pendientes:**
- Añadir anotaciones `readOnlyHint`/`destructiveHint` a herramientas
- Declarar `inputSchema` con validación en todas las herramientas
- Añadir manejo de errores estructurado en handlers
- Añadir archivo LICENSE (MIT)
- Añadir tests que ejerciten las herramientas declaradas
---
## Encuestas soportadas
`enaho encuestas` las lista, y dice además cuáles del portal **no** tienen perfil.
| id | Encuesta | Llave de hogar | Factor | Perfil |
|---|---|---|---|---|
| `enaho` | Condiciones de Vida y Pobreza | `conglome/vivienda/hogar` | `factor07` | verificado, 12 módulos curados |
| `endes` | Demográfica y de Salud Familiar | `hhid` (hogar), `caseid` (mujer) | `hv005`, `v005` ÷10⁶ | verificado, 13 módulos curados |
| `enaho-panel` | ENAHO Panel | seguimiento propio | propio | acotado |
| `enapres` | Programas Presupuestales | `conglomerado/vivienda/hogar` | `factor` | inferido |
| `ena` | Nacional Agropecuaria | `id_prod` (no es un hogar) | `factor_productor` | inferido |
| `enut` | Nacional de Uso del Tiempo | `conglomerado/vivienda/hogar` | `factor` | inferido |
| `enares` | Relaciones Sociales | `conglomerado/vivienda/hogar` | `factor` | inferido |
| `enapref` | Presupuestos Familiares | `conglomerado/vivienda/hogar` | `factor` | inferido |
| `epen-*` | Permanente de Empleo Nacional | `conglomerado/muestra/selviv/hogar` | `fac300_anual` | inferido |
| `epe-lima` | Permanente de Empleo (Lima) | `conglome/vivienda/hogar` | `fac500a` | inferido |
| `enco` | Nacional Continua (2006) | `conglome/vivienda/hogar` | `factor` | inferido |
| `cenagro` | Censo Nacional Agropecuario | `p001/p002/p003/p007x/p008` | ninguno (censo) | inferido, 11 módulos |
| `mapa-pobreza` | Mapa de Pobreza (distrital) | `id_hogar_m` | `facfinal_proy` | inferido, 4 módulos |
**«Inferido» significa inferido**, y el servidor lo declara en cada respuesta
(`aviso_perfil`) en vez de fingir certeza: esas llaves salieron del índice de
variables, no de un diccionario. Funcionan; hay que verificarlas con
`enaho perfil` antes de publicar.
**Dos rompen supuestos del resto.** El CENAGRO es un censo: no tiene factor de
expansión porque no se muestreó nada, y sus cifras son conteos exactos sin error
de muestreo. El CENAGRO y el Mapa de Pobreza publican **un archivo por
departamento y ninguno nacional**; el servidor los apila y añade la columna
`corte`, en vez de quedarse con Amazonas y llamarlo Perú.
El catálogo del INEI tiene **67 encuestas**. Las que no tienen perfil se pueden
listar y descargar, y `enaho sondear` propone cómo unirlas mirando los datos
reales, siempre marcado como inferencia. Añadir un perfil: ver
[docs/anadir-una-encuesta.md](docs/anadir-una-encuesta.md).
**Los Censos Nacionales de Población y Vivienda no están en este portal**: se
distribuyen por REDATAM, que es otro sistema. El Mapa de Pobreza es el atajo
para el caso que lleva a la mayoría de la gente a querer el censo — desagregar
por distrito — con estimaciones que el INEI ya publica calculadas.
---
## Uso desde la CLI
La CLI y el servidor MCP llaman a los **mismos casos de uso**. Lo que funciona en
uno funciona en el otro.
```bash
# 1. ¿Qué hay?
enaho encuestas
enaho modulos 2023 # ENAHO por defecto
enaho modulos 2023 --encuesta endes
# 2. ¿Cómo se llama la variable que busco?
enaho buscar variable "pobreza" --anio 2023
enaho buscar rastrear p207 --desde 2015 --hasta 2024 # ¿cambió de significado?
enaho describir 2023 74 --encuesta endes
# 3. Bajar y preparar
enaho descargar -a 2023 -m 01 -m 34
enaho unir -a 2023 -m 01 -m 34 --nivel hogar -o hogares2023
# 4. Estimar
enaho perfil hogares2023 -v pobreza -v factor07
enaho estimar hogares2023 pobreza -e proporcion --valor 1 --peso-adicional mieperho
enaho geografia hogares2023 --nivel departamento -o hogares2023_dep
# 5. Comprobar antes de publicar
enaho calidad hogares2023 --formato html
enaho sondear 2024 -m 1856 -m 1860 --encuesta enapres
# 6. Analizar más a fondo
enaho comparar hogares2023 pobreza -g estrato --a 8 --b 1 -e proporcion --valor 1
enaho desigualdad hogares2023 gashog2d -i gini -i p90_p10 --peso-adicional mieperho
enaho distribucion hogares2023 gashog2d --peso-adicional mieperho
enaho pobreza hogares2023_pc gasto_pc_mes --linea linea --peso-adicional mieperho
enaho regresion hogares2023 gashog2d -x mieperho -x estrato --peso-adicional mieperho
enaho serie pobreza --desde 2019 --hasta 2023 -m 01 -m 34 -e proporcion --valor 1
# 7. Sacar el resultado
enaho exportar hogares2023 -f dta # dta sav csv xlsx parquet feather
enaho informe mi_informe.json --formato docx
# 8. Mantenimiento
enaho ubigeo derivar --corte Arequipa
enaho catalogo estado
enaho catalogo actualizar -E ENAHO -E ENDES
enaho docs convertir 2023 -d diccionario # PDF del INEI -> Markdown
enaho docs buscar mieperho --exacto # cita documento y pagina
```
Todos los comandos aceptan `--json`; los que operan sobre una encuesta aceptan
`--encuesta`.
---
## Documentación
| Documento | Para qué |
|---|---|
| [Recetario](docs/recetario.md) | Diez recetas completas, cada una diciendo **qué error evita**. Empieza aquí. |
| [Referencia de herramientas](docs/herramientas.md) | Las 32 herramientas MCP con sus parámetros. Generada desde el servidor. |
| [Los métodos](docs/estadistica.md) | Qué estimador se usa para cada cosa y por qué. |
| [Añadir una encuesta](docs/anadir-una-encuesta.md) | De 15 perfiles a las 67 encuestas del portal. |
| [`examples/`](examples) | Scripts ejecutables. Los tests los corren, así que no se pudren. |
---
## Informes
`enaho_informe` no redacta copiando números: recibe la **narrativa** y una
especificación de qué calcular, ejecuta las estimaciones con la maquinaria de
diseño complejo y pinta los cuadros. Los números del documento no pasan por el
contexto del modelo, así que no se degradan al recopiarlos.
```json
{
"titulo": "Pobreza monetaria en el Perú, 2023",
"autor": "…",
"secciones": [
{"tipo": "texto", "titulo": "Introducción", "texto": "…"},
{"tipo": "estimacion", "titulo": "Incidencia por dominio",
"dataset": "hogares2023", "variable": "pobreza",
"estadistico": "proporcion", "valor": 1,
"por": ["dominio"], "peso_adicional": "mieperho"},
{"tipo": "desigualdad", "dataset": "hogares2023", "variable": "gashog2d",
"indicadores": ["gini", "p90_p10"]},
{"tipo": "cruce", "dataset": "hogares2023", "fila": "pobreza", "columna": "estrato"},
{"tipo": "comparacion", "dataset": "hogares2023", "variable": "pobreza",
"variable_grupo": "estrato", "grupo_a": 8, "grupo_b": 1},
{"tipo": "serie", "anio_inicio": 2019, "anio_fin": 2023,
"modulos": ["01", "34"], "variable": "pobreza"}
]
}
```
Formatos: `docx`, `xlsx` (una hoja por cuadro, números como números), `pdf`,
`md`, `html`. Los tres primeros necesitan `uv sync --extra informes`; md y html
no necesitan nada.
Tres cosas que hace y que un «escribe un docx con estos números» no da:
- **Los códigos salen etiquetados.** Un cuadro por dominio dice «Lima
Metropolitana», no «8».
- **Las advertencias viajan pegadas a su cuadro.** Si tres celdas tienen
CV > 15 %, el cuadro sale con su nota al pie diciendo que el INEI no las
publica.
- **Una sección rota no tumba el informe.** Queda marcada dentro del documento
con su sugerencia de arreglo y el resto se genera igual.
---
## Herramientas MCP (32)
| Grupo | Herramientas |
|---|---|
| Descubrimiento | `enaho_buscar_variable`, `enaho_rastrear_variable`, `enaho_listar_modulos`, `enaho_describir_modulo`, `enaho_listar_encuestas` |
| Adquisición | `enaho_descargar`, `enaho_descargar_documentacion`, `enaho_estado_cache`, `enaho_estado_catalogo`, `enaho_actualizar_catalogo` |
| Preparación | `enaho_unir_modulos`, `enaho_agregar_modulo`, `enaho_perfil`, `enaho_listar_datasets`, `enaho_exportar` |
| Diagnóstico | `enaho_calidad`, `enaho_sondear_llaves` |
| Documentación | `enaho_documentacion_convertir`, `enaho_documentacion_buscar` |
| Estimación | `enaho_estimar`, `enaho_comparar`, `enaho_desigualdad`, `enaho_tabla_cruzada` |
| Modelos | `enaho_regresion`, `enaho_pobreza_fgt`, `enaho_distribucion` |
| Series | `enaho_serie` |
| Informes | `enaho_informe` |
| Geografía | `enaho_geografia`, `enaho_ubigeo_buscar` |
| Panel | `enaho_panel_inspeccionar`, `enaho_panel_armar` |
**Resources** (6): `enaho://modulos`, `enaho://encuestas`, `enaho://cache`,
`enaho://modulos/{anio}`, `enaho://ficha/{anio}/{modulo}`,
`enaho://dominio/{encuesta}`
**Prompts** (5): `/enaho-pobreza`, `/enaho-empleo`, `/enaho-explorar`,
`/enaho-otra-encuesta`, `/enaho-verificar`
Referencia completa con parámetros: [docs/herramientas.md](docs/herramientas.md).
---
## El detalle que más importa: el universo de población
Comprobado contra la ENAHO 2023, con tres formas de calcular lo mismo y tres
resultados distintos:
| Método | Universo | Pobreza |
|---|---|---|
| Hogares × `factor07 × mieperho` | 34 107 048 personas | **29.05 %** ← cifra oficial |
| Hogares × `factor07` | 10 196 775 hogares | 23.15 % ← *hogares* pobres, no personas |
| Roster del módulo 02 × `factor07` | 36 252 082 personas | 28.46 % ← universo equivocado |
El roster del módulo 02 incluye trabajadores del hogar, pensionistas y sus
familiares, que quedan fuera de `mieperho`. Para indicadores de población que
deban reproducir cifras oficiales hay que usar el archivo a nivel hogar con
`peso_adicional="mieperho"`.
El servidor **detecta la situación y la advierte** cuando estimas sobre un roster
de personas sin peso adicional, en vez de dejar que publiques un número que se
parece al bueno. La comprobación es genérica: en la ENDES la heredan `HV009` y
`HHID`.
---
## Arquitectura
Cuatro capas con la regla de dependencia hacia adentro:
```
interfaces/ MCP y CLI. Capas finas: validan, delegan, formatean.
│
▼
aplicacion/ Casos de uso. El contrato compartido por ambas interfaces.
│ contenedor.py es el composition root.
▼
dominio/ Núcleo. Sin red, sin disco, sin MCP.
├── modelo/ Value objects y entidades (Anio, CodigoModulo, Ubigeo…)
├── conocimiento/ Perfiles de encuesta: llaves, factores y trampas de cada una
├── servicios/ Unión, estimación, regresión, pobreza, tabulación, geografía
└── puertos.py Interfaces (Protocol) que la infraestructura implementa
▲
│
infraestructura/ Adaptadores: catálogo INEI, descarga, lectura, parquet, ubigeo
```
La regla de dependencia está **bajo test**: `tests/test_interfaces.py` analiza el
AST de cada módulo del dominio y falla si aparece un import de infraestructura o
una llamada a E/S.
Cuatro decisiones que conviene conocer antes de leer el código:
- **`pandas.DataFrame` es un primitivo del dominio** (ADR-001, en
`dominio/modelo/tabla.py`). El dominio de este sistema *es* estadística sobre
tablas rectangulares; inventar una tabla propia y traducir en cada frontera no
compra nada. Lo que sí queda prohibido en el dominio es tocar E/S.
- **Los casos de uso son funciones, no clases.** Reciben primitivos y devuelven
un `dict` chico y serializable. Ese `dict` es lo que hace que cada comando de
CLI sean diez líneas de presentación en vez de una segunda implementación.
- **Lo que cambia entre encuestas es un `PerfilEncuesta`**, no una rama en el
código. La infraestructura y los estimadores son agnósticos; el perfil declara
llaves, factores, escala del peso y tabla de módulos.
- **La encuesta viaja en los metadatos del dataset.** Después de unir no hay que
repetir `encuesta="endes"` en cada llamada: el parquet lo recuerda y el
parámetro solo sirve para sobreescribirlo.
---
## Desarrollo
```bash
uv sync --group dev --all-extras
uv run pytest # 454 tests, ninguno toca la red
uv run ruff check src tests
uv run mypy src/enaho_mcp
uv run python scripts/generar_referencia.py # regenera docs/herramientas.md
```
Los ZIP de juguete se construyen en tiempo de test con pyreadstat en vez de
versionarse como binarios: son deterministas, se leen con el mismo código que los
reales y no engordan el repositorio.
Variables de entorno: `ENAHO_MCP_HOME` (raíz del cache), `ENAHO_MCP_LIMITE_GB`,
`ENAHO_MCP_MAX_MODULOS`, `ENAHO_MCP_DEBUG`.
---
## Limitaciones conocidas
- **La tabla de ubigeo empaquetada cubre solo los 25 departamentos.** Escribir de
memoria los 1 800+ distritos sería inventar datos. Para trabajar a nivel
provincia o distrito: `enaho ubigeo importar ruta/al/ubigeo.csv` con columnas
`codigo,departamento,provincia,distrito`.
- **El índice de variables cubre 16 de las 67 encuestas.** ENUT, ENARES, ENAPREF
y ENCO se pueden descargar y leer, pero no buscar. El servidor lo dice
explícitamente en vez de devolver un «no encontrado» que se leería como «esa
variable no existe».
- **La tabla de ubigeo con nombres hay que derivarla.** `enaho ubigeo derivar`
la extrae del módulo 632 del Mapa de Pobreza, que es la fuente oficial más
cercana en este portal, pero son 24 descargas y los códigos son de la división
política de ese operativo: los distritos creados después no están.
- **La documentación se convierte, no se interpreta.** `enaho docs convertir`
pasa los PDF del INEI a Markdown conservando el texto en orden de lectura,
pero **no reconstruye tablas**: el diccionario no las dibuja con líneas. Los 27
PDF de la ENAHO 2023 tienen capa de texto; si alguna ola vieja resulta ser un
escaneo, el conversor lo detecta y lo dice, pero no hace OCR.
- **Las llaves sondeadas son inferencia.** `enaho sondear` propone y muestra la
evidencia; no cura. Contrástalas con el diccionario antes de publicar.
- **Los perfiles marcados «inferido» lo son.** ENAPRES, ENA, ENUT, ENARES,
ENAPREF, EPEN, EPE y ENCO tienen llaves deducidas del índice de variables, no
contrastadas contra un diccionario. Cada respuesta lo declara.
- **ENAHO Panel no está resuelto, está acotado.** Es otro dataset: formato ancho,
llaves de seguimiento propias y factores calibrados para la submuestra seguida.
`enaho_panel_inspeccionar` reporta las columnas reales y `enaho_panel_armar`
reestructura a formato largo, pero **no asigna factor de expansión**: eso hay
que verificarlo con el manual del panel.
- **El refresco del catálogo depende de que el portal no cambie.** Si el crawl
falla, el servidor vuelve solo al catálogo empaquetado, que nunca se borra.
Ver [DECISIONES.md](DECISIONES.md) para las desviaciones respecto del documento
de diseño original y por qué.
---
## Licencia
MIT.
TDQS
Scored across 32 tools
Each tool targets a distinct stage of the ENAHO workflow: discovery, download, documentation, merging, quality, analysis, and reporting. Statistical tools clearly differ in output (e.g., means, cross-tabs, inequality, regression, poverty, distribution), and no two tools have obviously overlapping functionality.
All tools share the consistent 'enaho_' prefix, but the pattern varies: some are verb_noun (listar_modulos), some noun_verb (documentacion_convertir), and many are single words (perfil, estimar). The mixed conventions are readable but not uniform.
With 32 tools, this is well above the typical 3-15 range for a well-scoped server. While each tool has a specific purpose, the sheer number feels heavy and could overwhelm an agent; several statistical tools (e.g., estimar, pobreza_fgt, distribucion, serie) could be consolidated.
The tool set thoroughly covers the ENAHO lifecycle: catalog queries, downloads, caching, documentation conversion and search, merging, profiling, quality checks, key inference, weighted estimation, cross-tabs, comparison, inequality, geography, panel data, regression, poverty, distribution, time series, and report generation. No major operational gaps appear.