Skip to main content
Glama
JuanCoder23
by JuanCoder23
README.md
# mcp-diagnostico-tickets

[![CI](https://github.com/JuanCoder23/mcp-diagnostico-tickets/actions/workflows/ci.yml/badge.svg)](https://github.com/JuanCoder23/mcp-diagnostico-tickets/actions/workflows/ci.yml)

Servidor MCP en Python que, dado un ticket de soporte, cruza cuatro fuentes (Zendesk, Datadog, Snowflake y AWS) y devuelve una posible causa raíz, las hipótesis con su evidencia y qué revisar.

> **Esto es una demostración, no el código de producción.** En Simetrik construí y operé un servidor MCP con este mismo propósito, conectado a los sistemas reales. Ese código y esos datos son de la empresa y no están aquí. Este repositorio está hecho aparte para mostrar el diseño: las cuatro fuentes están simuladas con archivos JSON y **todos los datos son sintéticos** (servicios con prefijo `demo-`, clientes inventados).

## El problema

Cuando llega un ticket de soporte que parece técnico ("los pagos quedan en pendiente"), quien lo atiende tiene que averiguar si detrás hay un fallo de plataforma. Eso significa abrir el ticket en Zendesk, buscar en Datadog si hay monitores en alerta para ese servicio, revisar en Snowflake si las cargas de datos terminaron bien y mirar en AWS el estado de los recursos y el último despliegue. Son cuatro herramientas, cuatro pantallas y el mismo recorrido en cada ticket, antes de poder formular una primera hipótesis.

## La solución

Un servidor [MCP](https://modelcontextprotocol.io) (Model Context Protocol) expone ese recorrido como herramientas que Claude puede llamar. El ingeniero escribe "investiga el ticket TK-1003" y Claude recibe la evidencia de las cuatro fuentes ya reunida.

| Herramienta | Qué hace |
|---|---|
| `obtener_ticket` | Trae el ticket de Zendesk |
| `consultar_alertas_datadog` | Monitores en alerta del servicio |
| `consultar_cargas_snowflake` | Resultado de las últimas cargas de datos |
| `consultar_estado_aws` | Tareas de ECS, cola de SQS, conexiones de RDS y último despliegue |
| `diagnosticar_ticket` | Llama a las cuatro anteriores y devuelve causa raíz posible, hipótesis y qué revisar |

También publica un *prompt* de MCP, `investigar_ticket`, que le indica al modelo cómo usar la herramienta y en qué formato responder.

## Arquitectura

```mermaid
flowchart LR
    U["Ingeniero de soporte"] --> C["Claude<br/>(cliente MCP)"]
    C -- "stdio" --> S["servidor.py<br/>registra las herramientas"]
    S --> D["diagnostico.py<br/>reglas explícitas"]
    D --> F["fuentes.py"]
    F --> Z[("Zendesk<br/>simulado")]
    F --> DD[("Datadog<br/>simulado")]
    F --> SF[("Snowflake<br/>simulado")]
    F --> A[("AWS<br/>simulado")]
```

El código está repartido en tres archivos, cada uno con una sola responsabilidad:

| Archivo | Responsabilidad |
|---|---|
| [`servidor.py`](servidor.py) | Registra las herramientas y el prompt con el SDK oficial de MCP |
| [`diagnostico.py`](diagnostico.py) | Reúne la evidencia y aplica las reglas |
| [`fuentes.py`](fuentes.py) | Una función por sistema externo; aquí leen los JSON de [`datos/`](datos) |

### Cómo se forman las hipótesis

`diagnostico.py` tiene cuatro reglas. Cada una es una función que mira la evidencia y devuelve una hipótesis o nada:

| Regla | Se activa cuando | Ticket de ejemplo |
|---|---|---|
| Despliegue reciente | Hubo un despliegue en la hora previa al ticket y hay alertas activas | `TK-1003` |
| Tareas caídas | ECS tiene menos tareas en ejecución que las deseadas | `TK-1001` |
| Carga fallida | Alguna carga de Snowflake terminó con error | `TK-1002` |
| Conexiones saturadas | RDS usa el 90 % o más de sus conexiones | `TK-1005` |

Las hipótesis se ordenan por cantidad de evidencia a favor, y la primera se presenta como posible causa raíz. Si ninguna regla se activa (`TK-1004`), el servidor lo dice explícitamente y sugiere pedir más información al cliente, en lugar de inventar una causa.

### Qué es distinto en esta demostración

| | Sistema que operé en Simetrik | Este repositorio |
|---|---|---|
| Fuentes | APIs reales de Zendesk, Datadog, Snowflake y AWS | Archivos JSON con datos sintéticos |
| Quién redacta el diagnóstico | Claude, a partir de lo que devolvían las herramientas | Reglas fijas en Python, para que el resultado sea reproducible y se pueda probar sin una clave de API |
| Credenciales | Sí | Ninguna |

Las reglas de este repositorio no son las del sistema original: están hechas para la demostración.

## Cómo correrlo

Requisitos: Python 3.12 (el SDK de MCP exige 3.10 o superior), o Docker.

### En local

```bash
git clone https://github.com/JuanCoder23/mcp-diagnostico-tickets.git
cd mcp-diagnostico-tickets
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements-dev.txt
```

Ejecutar las pruebas:

```bash
pytest
```

Probar el servidor sin Claude, con el cliente de ejemplo:

```bash
python cliente_demo.py TK-1003
```

### Con Docker

```bash
docker build -t mcp-diagnostico-tickets .
```

El servidor se comunica por la entrada y salida estándar, así que el contenedor se ejecuta con `-i`. No hace falta arrancarlo a mano: lo lanza el cliente MCP.

### Conectarlo a Claude

En Claude Code:

```bash
claude mcp add diagnostico-tickets -- docker run -i --rm mcp-diagnostico-tickets
```

En Claude Desktop, dentro de `claude_desktop_config.json`:

```json
{
  "mcpServers": {
    "diagnostico-tickets": {
      "command": "docker",
      "args": ["run", "-i", "--rm", "mcp-diagnostico-tickets"]
    }
  }
}
```

Después se le puede pedir: *"Investiga el ticket TK-1003"*.

## Ejemplo de entrada y salida

Entrada: una llamada a la herramienta `diagnosticar_ticket`.

```json
{ "ticket_id": "TK-1003" }
```

Salida (la que produce `python cliente_demo.py TK-1003`; se omite el bloque `evidencia`, que trae los datos crudos de cada fuente):

```json
{
  "aviso": "Demostración con datos sintéticos. No es un diagnóstico real.",
  "ticket": {
    "id": "TK-1003",
    "asunto": "Errores 500 al crear órdenes",
    "descripcion": "Desde las 14:05 la API responde error 500 cuando intentamos crear una orden.",
    "cliente": "Tienda Ejemplo S.A.",
    "servicio": "demo-api-ordenes",
    "prioridad": "alta",
    "creado_en": "2026-03-10T14:20:00"
  },
  "posible_causa_raiz": "El último despliegue introdujo un error",
  "hipotesis": [
    {
      "titulo": "El último despliegue introdujo un error",
      "evidencia": [
        "AWS: último despliegue 30 minutos antes del ticket",
        "Datadog: 'Tasa de errores 5xx' en alerta (12.5 con umbral 2)"
      ],
      "que_revisar": [
        "Comparar la hora de inicio de la alerta con la hora del despliegue",
        "Revisar los cambios incluidos en el despliegue",
        "Evaluar con el equipo dueño del servicio si conviene revertirlo"
      ]
    }
  ],
  "que_revisar": [
    "Comparar la hora de inicio de la alerta con la hora del despliegue",
    "Revisar los cambios incluidos en el despliegue",
    "Evaluar con el equipo dueño del servicio si conviene revertirlo"
  ]
}
```

Si el ticket no existe, la herramienta responde con un error que el modelo puede leer:

```
Error executing tool diagnosticar_ticket: No existe el ticket TK-9999
```

## Pruebas

19 pruebas con pytest, sin red ni credenciales:

- [`tests/test_fuentes.py`](tests/test_fuentes.py): cada fuente filtra por servicio y todos los servicios de ejemplo llevan el prefijo `demo-`.
- [`tests/test_diagnostico.py`](tests/test_diagnostico.py): un caso por regla, el caso sin evidencia y el orden de las hipótesis.
- [`tests/test_servidor.py`](tests/test_servidor.py): el servidor publica las cinco herramientas y devuelve errores legibles.

El CI de GitHub Actions ejecuta las pruebas y construye la imagen de Docker en cada push y en cada pull request.

## Limitaciones

- Las fuentes son archivos fijos: no hay llamadas a ninguna API.
- Las reglas son cuatro y deliberadamente simples. Sirven para mostrar el flujo, no para diagnosticar un sistema real.
- Solo usa el transporte stdio, pensado para un cliente en la misma máquina.
- No tiene autenticación, porque no expone datos reales.

## Licencia

MIT. Ver [LICENSE](LICENSE).