Skip to main content
Glama
Kirill-FD

llm-analytics-mcp

by Kirill-FD

Sistema analítico basado en LLM con integración MCP

Un servidor MCP que proporciona al modelo de lenguaje un conjunto de herramientas para analizar datos tabulares: carga, limpieza, creación de gráficos y generación de informes. No se desarrolla una interfaz de chat propia; se utiliza la interfaz web de una plataforma existente (Claude como cliente principal y ChatGPT como alternativa).

El mismo registro de herramientas se publica a la vez por dos protocolos:

Protocolo

Endpoint

Cliente

MCP (Streamable HTTP)

/mcp

Claude: web, escritorio, cualquier cliente MCP

REST + OpenAPI

/tools/*, /openapi.json

ChatGPT Custom GPT Action


Qué puede hacer el sistema

12 herramientas, 5 habilidades. La lista completa está disponible mediante la llamada a describe_system o en ARCHITECTURE.md.

Herramienta

Habilidad

Función

list_datasets

—

Catálogo de datos disponibles

load_data

DataLoadingSkill

Carga de CSV/TSV/Excel/JSON/Parquet desde el catálogo, una ruta o una URL

describe_data

DataLoadingSkill

Estructura, tipos, valores faltantes, duplicados

clean_data

DataCleaningSkill

Duplicados, valores faltantes, normalización, valores atípicos

suggest_analysis

InsightGenerationSkill

Selección automática del plan de análisis según la estructura de los datos

plot_trend

VisualizationSkill

Evolución de la métrica en el tiempo

plot_distribution

VisualizationSkill

Histograma o gráfico de barras (el tipo se elige automáticamente)

correlation_analysis

VisualizationSkill

Mapa de calor de correlaciones

plot_breakdown

VisualizationSkill

Desglose de la métrica por categorías

collect_evidence

InsightGenerationSkill

Cifras verificables para el texto del informe

build_report

ReportingSkill

Informe en Markdown, HTML y PDF

describe_system

—

Introspección: composición de habilidades y herramientas

Funcionalidades adicionales:

  1. Selección automática del análisis: suggest_analysis determina qué columna es el eje temporal, cuáles son métricas y cuáles desgloses, y devuelve un plan de llamadas listo para usar con la justificación de cada paso.

  2. Multiformato y multifuente: CSV, TSV, Excel, JSON, Parquet; catálogo, ruta local o enlace HTTP(S). Esto último es fundamental para el escenario web: el servidor no tiene acceso al archivo subido en el chat del navegador.

  3. Generación del informe con un solo comando: build_report completa por sí mismo los gráficos que faltan y entrega el documento en tres formatos.


Related MCP server: Claude Data Buddy

Instalación

Se requiere Python 3.10 o superior.

git clone <адрес-репозитория>
cd llm-analytics-mcp

python -m venv .venv
source .venv/bin/activate          # Windows: .venv\Scripts\activate

pip install -r requirements.txt

Paso 1. Datos de prueba

Los datos del repositorio están generados sintéticamente según el esquema de Superstore. La especificación técnica lo permite expresamente: «puedes generar los datos por tu cuenta o utilizar un conjunto de datos conocido».

python scripts/prepare_dataset.py --synthetic --rows 4000

Los archivos listos ya se encuentran en data/; el comando solo es necesario si quieres regenerarlos o cambiar su tamaño.

Por qué datos sintéticos y no Kaggle

El generador permite controlar qué es exactamente lo que demuestra el sistema:

  • Hay patrones verificables incorporados — tendencia ascendente, estacionalidad anual con pico a finales de año y la relación «descuento superior al 30% → beneficio negativo». Gracias a ello, las conclusiones del análisis son sustanciales y no aleatorias.

  • Los defectos se introducen a propósito. El Superstore real está prácticamente limpio: sin valores faltantes ni duplicados, DataCleaningSkill informaría «0 filas eliminadas» y no habría nada que demostrar en la limpieza.

  • Reproducibilidad. Con seed=42 fijo, quien revise obtiene exactamente los mismos datos y los mismos números en el informe que en el ejemplo.

  • El repositorio es autosuficiente. No se necesita una cuenta de Kaggle para ejecutar el proyecto.

La carga del Superstore real también es compatible: la estructura de las columnas coincide:

python scripts/prepare_dataset.py --input ~/Downloads/Sample-Superstore.csv

Qué genera el script

Archivo

Función

data/superstore_clean.csv

Datos ajustados a las columnas de la especificación

data/superstore_raw.csv

La misma tabla con defectos introducidos

Columnas: Date, Product, Region, Sales, Quantity, Profit (de la especificación), más los desgloses Category, Sub-Category, Segment, Discount, Ship Mode. Periodo: 2021–2024, 48 meses.

La composición de los defectos se imprime al iniciar y es determinista:

Defecto

Volumen

Valores faltantes en Sales / Profit / Quantity

~3.5% / 4.5% / 2%

Filas completamente duplicadas

~0.8%

Inconsistencias en la escritura de Region (west, East, CENTRAL)

~6% de las filas

Valores atípicos extremos en Sales

12 filas

Formato de fecha alternativo (15/03/2022)

~10% de las filas


Paso 2. Verificación sin servidor

Ejecución completa de toda la cadena, desde la carga hasta el informe en PDF:

PYTHONPATH=src python -m analytics_mcp.selfcheck

El script reproduce lo que hace la LLM en el diálogo, pero de forma determinista. Resulta útil como prueba de humo (smoke test) antes de la demostración: si funciona, es casi seguro que el problema está en la integración, no en el análisis.


Paso 3. Inicio del servidor

PYTHONPATH=src uvicorn analytics_mcp.app:app --host 127.0.0.1 --port 8000

Comprobación:

curl http://127.0.0.1:8000/health

Direcciones útiles:

Dirección

Qué es

http://127.0.0.1:8000/health

Estado y número de componentes registrados

http://127.0.0.1:8000/docs

Swagger UI: todos los instrumentos se pueden invocar manualmente

http://127.0.0.1:8000/openapi.json

Especificación para Custom GPT Action

http://127.0.0.1:8000/mcp

Endpoint MCP

Si el puerto está ocupado. Un proceso iniciado anteriormente puede seguir respondiendo con código antiguo; el síntoma es engañoso: /health responde, pero los cambios no se aplican. Antes de reiniciar: pkill -f uvicorn.


Paso 4. Dirección pública mediante ngrok

Claude accede al servidor desde el exterior, por lo que se necesita una dirección HTTPS.

# 1. Установка и регистрация: https://ngrok.com/download
ngrok config add-authtoken <ваш-токен>

# 2. В личном кабинете ngrok зарезервируйте бесплатный статический домен
#    (Domains -> Create Domain). Без него адрес меняется при каждом
#    перезапуске, и настройку коннектора придётся повторять.

# 3. Запуск туннеля
ngrok http 8000 --domain=ваш-домен.ngrok-free.app

A continuación, configure la dirección en el entorno y reinicie el servidor:

cp .env.example .env
# в .env укажите:
#   PUBLIC_BASE_URL=https://ваш-домен.ngrok-free.app

export PUBLIC_BASE_URL=https://ваш-домен.ngrok-free.app
export MCP_ALLOWED_HOSTS='127.0.0.1:*,localhost:*,*.ngrok-free.app'
PYTHONPATH=src uvicorn analytics_mcp.app:app --host 127.0.0.1 --port 8000

La causa más frecuente de «no se conecta». El MCP SDK incluye por defecto protección contra DNS rebinding y solo acepta una cabecera Host del tipo localhost. Detrás del túnel, Host contiene el dominio de ngrok y la solicitud se rechaza en la fase de conexión del conector, sin un error claro en la interfaz. La variable MCP_ALLOWED_HOSTS resuelve exactamente este problema.


Paso 5. Conexión con Claude (escenario principal)

  1. Abra Settings → Connectors → Add custom connector.

  2. Indique la dirección: https://ваш-домен.ngrok-free.app/mcp (tenga en cuenta el sufijo /mcp).

  3. Guarde y asegúrese de que el conector pasa al estado de conectado.

  4. En un nuevo diálogo, active el conector analytics_mcp mediante el menú de herramientas.

  5. Copie el contenido de prompts/system_prompt.md en la descripción del proyecto (Project instructions); esto define el orden de las llamadas.

Consulta de prueba: «¿Qué conjuntos de datos están disponibles?»: el modelo debe invocar list_datasets y mostrar el contenido del catálogo.


Paso 6. Conexión con ChatGPT (escenario alternativo)

  1. Descargue la especificación desde la dirección pública:

    PUBLIC_BASE_URL=https://ваш-домен.ngrok-free.app \
      PYTHONPATH=src python scripts/export_openapi.py
  2. Cree un Custom GPT: Explore GPTs → Create → Configure.

  3. Create new action → Schema: pegue el contenido de openapi.json.

  4. Authentication: None.

  5. En el campo Instructions pegue prompts/system_prompt.md.

Los detalles y particularidades de la visualización de gráficos están en prompts/gpt_action_setup.md.


Escenario de demostración

El orden de las consultas está pensado para que en las capturas se vea la cadena de llamadas y no una sola consulta. La captura clave es el paso 4: se ve lo que planifica el modelo, no un código fijo.

#

Consulta del usuario

Llamadas esperadas

1

¿Qué conjuntos de datos están disponibles?

list_datasets

2

Carga superstore_raw y describe la estructura

load_data, describe_data

3

Limpia los datos

clean_data

4

¿Qué merece la pena analizar aquí?

suggest_analysis

5

Construye estos gráficos

plot_trend, plot_breakdown, plot_distribution, correlation_analysis

6

Haz un informe con conclusiones y recomendaciones

collect_evidence, build_report

Un ejemplo del resultado está en docs/report_example.md; los gráficos, en docs/plots/.


Capturas de pantalla del funcionamiento

Los materiales de la demostración se encuentran en docs/screenshots/:

Archivo

Qué se muestra

01-list-datasets.png

Claude llama a list_datasets y muestra el catálogo del servidor

02-clean.png

Informe de clean_data: normalización de Region, 30 duplicados, 817 valores atípicos

02.2-clean.png

Comparación de versiones del conjunto de datos «con imputación de faltantes» y «sin ella»

03-suggest-analysis.png

El modelo comprueba las hipótesis del informe con nuevas llamadas a las herramientas

03.2-suggest-analysis.png

Lista priorizada de líneas de análisis posterior

04-plots.png

Construcción de gráficos; el modelo señala explícitamente lo que las herramientas no saben hacer

Las capturas muestran la propiedad clave del sistema: la cadena de llamadas la controla la LLM. El modelo decide por sí mismo qué herramientas invocar, detecta las limitaciones del conjunto (por ejemplo, la falta de filtrado de filas) y las comunica en lugar de forzar el resultado.

Verificación de la integración

# Полный цикл по обоим транспортам: initialize, tools/list, tools/call,
# возврат изображения, обработка ошибочных аргументов
python scripts/integration_test.py

Estructura del repositorio

llm-analytics-mcp/
├── README.md                    инструкция (этот файл)
├── ARCHITECTURE.md              архитектура и роль MCP/скиллов
├── openapi.json                 спецификация для Custom GPT Action
├── requirements.txt
├── .env.example
├── data/                        тестовые данные
├── docs/
│   ├── report_example.md/html/pdf   пример сгенерированного отчёта
│   ├── plots/                       примеры графиков
│   └── screenshots/                 скриншоты диалога
├── prompts/
│   ├── system_prompt.md         инструкция для LLM
│   └── gpt_action_setup.md      настройка Custom GPT Action
├── scripts/
│   ├── prepare_dataset.py       подготовка данных
│   ├── export_openapi.py        выгрузка спецификации
│   └── integration_test.py      проверка обоих транспортов
└── src/analytics_mcp/
    ├── core/                    реестр инструментов, хранилище, модели
    ├── skills/                  бизнес-логика этапов анализа
    ├── tools/                   инструменты, публикуемые наружу
    ├── transports/              адаптеры MCP и REST
    ├── rendering/               оформление графиков, артефакты
    ├── app.py                   сборка ASGI-приложения
    └── selfcheck.py             сквозная самопроверка

Cómo añadir tu propia herramienta

El núcleo no cambia. Cree el archivo src/analytics_mcp/tools/my_tools.py:

from __future__ import annotations

from analytics_mcp.core.datasets import store
from analytics_mcp.core.registry import tool


@tool(tags=("stats",), skill="DataLoadingSkill", title="Топ значений")
def top_values(column: str, dataset_id: str | None = None, limit: int = 10) -> dict:
    """Возвращает самые частые значения колонки.

    Args:
        column: Имя колонки.
        dataset_id: Датасет. По умолчанию — последний использованный.
        limit: Сколько значений вернуть.
    """
    record = store.get(dataset_id)
    record.require_column(column)
    counts = record.df[column].value_counts().head(limit)
    return {str(k): int(v) for k, v in counts.items()}

Reinicie el servidor. La herramienta aparecerá inmediatamente en ambos protocolos: en tools/list de MCP y en /openapi.json de REST. El paquete tools importa sus módulos automáticamente; el esquema JSON se deduce de la firma y la descripción, del docstring.


Limitaciones conocidas

Se enumeran a propósito: son los límites del prototipo, no deficiencias.

  • Almacenamiento de datasets en memoria. Al reiniciar el servidor, los datos cargados se pierden. Para un prototipo es aceptable; en producción — Redis o disco.

  • No hay autorización. El entorno de demostración está detrás de un túnel temporal. Para producción — clave de API en el encabezado y verificación en el lado de FastAPI.

  • No hay filtrado de filas. Las herramientas trabajan con el dataset completo: no se puede construir un subconjunto «solo la región West para 2024». Esto se nota en la demostración — el modelo informa honestamente de lo que no puede calcular, en lugar de forzar el resultado.

  • Cinco skills, no más. Es una elección deliberada: mejor cinco funcionales que diez formales.

  • No hay pruebas unitarias — solo la autocomprobación de extremo a extremo selfcheck.py y la prueba de integración de ambos transportes.

Related MCP Connectors

Related MCP Servers