Skip to main content
Glama
README.md
# MCP-TUPAD

Servidor [MCP](https://modelcontextprotocol.io) para consultar el campus virtual
Moodle de la **Tecnicatura Universitaria en Programación a Distancia (UTN)**
desde cualquier asistente de IA.

Conecta tu cuenta del campus con Claude Code, Claude Desktop, Cursor, VS Code o
cualquier otro cliente compatible con MCP, para que puedas preguntar cosas como:

> ¿Qué se me vence esta semana?
> Explicame el apunte de la unidad 3 de Programación I.
> ¿Cómo vengo de notas en Matemática?
> ¿Hay avisos nuevos de los profesores, o alguien me escribió por privado?
> ¿Qué preguntas erré en el segundo parcial de Metodología?
> ¿Qué me quedó sin completar en la unidad 4?

## Cómo funciona

Moodle expone una API REST oficial (la misma que usa su app móvil). Este
servidor la traduce a herramientas MCP. Cada persona usa **su propio token**, y
solo accede a lo que ya ve en el campus con su usuario.

No requiere permisos de administrador ni instalar nada en el servidor de la UTN.

## Requisitos

- **Node.js 20.12 o superior** (`node --version` para verificar)
- Una cuenta del campus con usuario y contraseña propios de Moodle

## Instalación

### 1. Clonar e instalar

```bash
git clone https://github.com/brujoh88/MCP-TUPAD.git
cd MCP-TUPAD
npm install
```

### 2. Obtener tu token

Ejecutá esto reemplazando tus credenciales:

```bash
curl -s "https://tup.sied.utn.edu.ar/login/token.php" \
  -d "username=TU_USUARIO" \
  -d "password=TU_CONTRASENA" \
  -d "service=moodle_mobile_app"
```

Devuelve algo así:

```json
{ "token": "a1b2c3d4e5f6..." }
```

> ⚠️ **Ese token equivale a tu contraseña.** Quien lo tenga puede entregar
> trabajos prácticos y publicar en foros **en tu nombre**. Nunca lo compartas ni
> lo subas a un repositorio — cada compañero saca el suyo con sus credenciales.

### 3. Configurar

```bash
cp .env.example .env
```

Abrí `.env` y pegá tu token:

```
MOODLE_URL=https://tup.sied.utn.edu.ar
MOODLE_TOKEN=a1b2c3d4e5f6...
```

El archivo `.env` está en `.gitignore`, así que nunca se sube al repo.

### 4. Verificar que funcione

```bash
npm run probar-token
```

Deberías ver tu nombre y el listado de tus materias con sus IDs. Si esto falla,
el problema está en el token o la URL — no en el MCP.

## Conectarlo a tu cliente

El servidor es el mismo para todos los clientes; solo cambia dónde va la
configuración.

### Claude Code

Si abrís **este** proyecto, ya está: el archivo `.mcp.json` del repo se detecta
solo y Claude Code te pide aprobarlo al iniciar.

Para usarlo desde **cualquier** proyecto, registralo a nivel usuario:

```bash
claude mcp add --scope user --transport stdio moodle-tupad \
  -- node /ruta/absoluta/a/MCP-TUPAD/src/index.js
```

### Cursor

Creá `~/.cursor/mcp.json` (global) o `.cursor/mcp.json` (solo este proyecto):

```json
{
  "mcpServers": {
    "moodle-tupad": {
      "command": "node",
      "args": ["/ruta/absoluta/a/MCP-TUPAD/src/index.js"]
    }
  }
}
```

### Claude Desktop

Editá `claude_desktop_config.json` (Configuración → Desarrollador → Editar
configuración):

```json
{
  "mcpServers": {
    "moodle-tupad": {
      "command": "node",
      "args": ["/ruta/absoluta/a/MCP-TUPAD/src/index.js"]
    }
  }
}
```

### VS Code

Creá `.vscode/mcp.json` con el mismo bloque `mcpServers` de arriba.

> El servidor lee las credenciales del archivo `.env` del proyecto, así que no
> hace falta declarar variables de entorno en la configuración de ningún
> cliente. Si preferís pasarlas por entorno igual, tienen prioridad las que ya
> estén definidas en el sistema.

## Herramientas disponibles

| Herramienta | Qué hace | Parámetros |
|---|---|---|
| `listar_materias` | Tus materias con ID y porcentaje de avance | — |
| `ver_contenido_materia` | Índice de unidades; con `unidad`, el detalle y las URLs de los archivos | `courseid`, `unidad?` |
| `proximos_vencimientos` | Entregas y cierres de cuestionarios de **todas** las materias, ordenados por fecha | `dias?` (30) |
| `ver_trabajos_practicos` | TPs con consigna, fecha de entrega y si ya entregaste | `courseid`, `solo_pendientes?` |
| `ver_notas` | Calificaciones, estado y devoluciones del profesor | `courseid`, `solo_calificados?` |
| `leer_avisos_foro` | Publicaciones de los foros de avisos | `courseid`, `todos_los_foros?`, `limite?` |
| `leer_mensajes_directos` | Mensajería privada: lista de conversaciones o el historial de una | `conversacion?`, `limite?`, `solo_no_leidas?` |
| `ver_notificaciones` | Las notificaciones de la campanita (contenido nuevo, correcciones) | `limite?`, `solo_no_leidas?` |
| `ver_calendario` | Eventos del calendario, incluidos los que el profesor carga a mano | `dias?`, `dias_atras?`, `courseid?` |
| `ver_resumen_notas` | Cuántas actividades llevás calificadas y el promedio, materia por materia | `solo_con_nota?` |
| `ver_progreso_unidad` | Actividad por actividad, qué está completado y qué falta | `courseid`, `unidad?`, `solo_pendientes?` |
| `revisar_cuestionario` | Corrección de un cuestionario rendido, pregunta por pregunta | `courseid`, `cuestionario?`, `solo_errores?` |
| `descargar_archivo` | Baja un archivo del campus a `descargas/`, o a la carpeta que le indiques | `url`, `nombre?`, `destino?` |

El flujo típico es empezar por `listar_materias` para obtener el ID de una
materia, y usar ese ID en las demás.

Para responder "¿hay algo nuevo?" hacen falta las tres vías de comunicación,
que son independientes entre sí: `leer_avisos_foro` (foros de la materia),
`leer_mensajes_directos` (mensajes privados de profesores y tutores) y
`ver_notificaciones` (avisos automáticos del sistema).

Detalles de diseño, por si los tocás:

- **`ver_contenido_materia` devuelve un índice por defecto.** Las materias
  tienen decenas de unidades y traer todo junto llena el contexto sin
  necesidad. Primero el índice, después el detalle de la unidad que interese.
  `revisar_cuestionario` sigue el mismo patrón: sin `cuestionario`, la lista.
- **`proximos_vencimientos` no usa el calendario de Moodle.** Lo arma leyendo
  las fechas reales de las entregas y los cuestionarios, porque el calendario
  omite actividades ya completadas y devolvía listas vacías. `ver_calendario`
  sí lo consulta, pero con `core_calendar_get_calendar_events`, que devuelve
  todos los eventos del rango; la variante `..._get_action_events_by_timesort`
  filtra lo ya completado y viene vacía. Las dos herramientas se complementan.
- **`ver_resumen_notas` calcula el promedio a mano.** El campus no llena el
  total de cada materia (el ítem de tipo `course` viene siempre en `-`), así
  que el promedio sale de las notas de cada actividad. Es una consulta por
  materia: van de a tandas de 4, porque disparar más de diez juntas hace que el
  campus corte la conexión.
- **`revisar_cuestionario` desarma HTML.** Moodle no expone el enunciado, las
  opciones ni la corrección como campos: vienen dentro de un bloque de HTML por
  pregunta, con `<script>` de inicialización al final. Se recorta por el cierre
  real de cada `<div>`, no por cantidad de caracteres, para que los bloques no
  se pisen entre sí.

### Funciones que el campus no habilita

No todo lo que documenta Moodle está disponible con el token del servicio
`moodle_mobile_app`. Comprobado contra la UTN:

- `core_notes_get_course_notes` responde `nopermissions`: las "notas" del
  docente sobre el estudiante están deshabilitadas, así que no hay herramienta
  para eso.

Para ver qué habilita tu campus, `core_webservice_get_site_info` devuelve en su
campo `functions` la lista completa de funciones que tu token puede llamar.

## Compatibilidad con otros modelos

MCP es un protocolo abierto y **agnóstico del modelo**. Este servidor no
contiene código específico de ningún proveedor: expone herramientas por
JSON-RPC, y cada cliente las traduce al mecanismo de *function calling* de su
modelo. Funciona igual con Claude, GPT, Gemini o cualquier modelo con soporte
de herramientas.

## Problemas comunes

**`invalidtoken`** — El token venció o se copió incompleto. Volvé a generarlo
con el `curl` del paso 2.

**`invalidlogin` al pedir el token** — Usuario o contraseña incorrectos. Probá
entrar al campus por el navegador para confirmar tus credenciales.

**El cliente no ve el servidor** — Verificá que la ruta en la configuración sea
**absoluta** y apunte a `src/index.js`. Probá primero `npm run probar-token`.

**Una herramienta devuelve una lista vacía** — Es normal si la materia no tiene
ese tipo de contenido (por ejemplo, `ver_trabajos_practicos` en una materia sin
TPs cargados).

## Usarlo con otro campus Moodle

No hay nada atado a la UTN en el código: cambiá `MOODLE_URL` en tu `.env` por la
URL de cualquier otro Moodle que tenga los web services habilitados.

Para verificar si un campus los tiene activos:

```bash
curl -s "https://EL-CAMPUS/login/token.php?username=x&password=x&service=moodle_mobile_app"
```

Si responde `invalidlogin`, están habilitados (solo faltan credenciales válidas).
Si responde que el servicio no está disponible, el administrador tiene que
activarlos.

## Desarrollo

```bash
npm run typecheck   # chequeo de tipos vía JSDoc, sin compilar
npm start           # levanta el servidor a mano (habla MCP por stdin/stdout)
```

El proyecto es JavaScript con anotaciones JSDoc y `checkJs` activado: tenés
autocompletado y verificación de tipos en el editor sin ningún paso de
compilación.

## Licencia

MIT

TDQS

A4.2/5.0

Scored across 7 tools

Disambiguation5/5

Cada herramienta tiene un propósito claramente distinto: listar materias, ver contenido, vencimientos, trabajos prácticos, notas, foros y descargar archivos. No hay superposición entre ellas.

Naming Consistency4/5

La mayoría sigue el patrón verbo_sustantivo (listar_materias, ver_contenido_materia, ver_trabajos_practicos, ver_notas, leer_avisos_foro, descargar_archivo). Una excepción es 'proximos_vencimientos', que es un sustantivo sin verbo.

Tool Count5/5

Con 7 herramientas, la cantidad es adecuada para un asistente de gestión universitaria. Cubre las consultas principales sin ser excesivo.

Completeness3/5

Las herramientas son principalmente de consulta, faltan operaciones de creación o modificación (como entregar trabajos o publicar en foros). Para un asistente informativo es suficiente, pero hay ausencias notables para completar el ciclo de vida del estudiante.

Maintenance

ActivityMaintained
ResponsivenessSyncing