FCEFyN MCP
# FCEFyN MCP
Servidor [Model Context Protocol](https://modelcontextprotocol.io/docs/develop/build-server) para consultar tus cursos del aula virtual de **FCEFyN, Universidad Nacional de Córdoba** desde un asistente compatible con MCP. Implementado con el [SDK oficial de Python](https://github.com/modelcontextprotocol/python-sdk/tree/v1.x).
Inicia sesión con **Login UNC (SAML/CAS)**, incluyendo el formulario de usuario y el de contraseña. También admite el formulario local de Moodle si no hay un acceso SAML. No necesita un token ni permisos de administrador.
## Instalación
Requisitos: Python 3.11 o superior y [uv](https://docs.astral.sh/uv/getting-started/installation/).
```bash
git clone https://github.com/bernoulli2005/fcefyn-mcp.git
cd fcefyn-mcp
uv sync --locked
cp .env.example .env
chmod 600 .env
```
Completá `.env` localmente:
```dotenv
MOODLE_URL=https://fcefyn.aulavirtual.unc.edu.ar
MOODLE_USERNAME=tu_usuario@mi.unc.edu.ar
MOODLE_PASSWORD=tu_contraseña
```
En la copia local preparada durante el desarrollo, `.env` ya está configurado. Las variables de entorno del proceso tienen prioridad sobre el archivo.
## Conectar un cliente MCP
Agregá esta entrada a la configuración de un cliente que admita servidores locales por **stdio** (por ejemplo, Claude Desktop). Sustituí la ruta por la ubicación de tu clon y `uv` por su ruta absoluta si el cliente no lo encuentra:
```json
{
"mcpServers": {
"fcefyn": {
"command": "uv",
"args": [
"--directory", "/ruta/absoluta/fcefyn-mcp",
"run", "--frozen", "fcefyn-mcp"
]
}
}
}
```
No hace falta copiar la contraseña a la configuración del cliente. Reiniciá el cliente después de agregar el servidor.
Para ejecutarlo manualmente:
```bash
uv run fcefyn-mcp
```
El proceso espera mensajes MCP por stdin. No es un chat ni un servidor web; stdout está reservado al protocolo.
## Herramientas
| Herramienta | Uso |
| --- | --- |
| `list_courses()` | Lista todos tus cursos matriculados, con sus IDs y enlaces. |
| `get_course_contents(course_id)` | Recorre las secciones visibles, pestañas y subsecciones del curso; devuelve actividades, textos en línea y descripciones, sin duplicados. |
| `read_content(url, max_chars=30000, offset=0)` | Lee páginas, actividades y PDFs con texto. Devuelve enlaces a archivos y capítulos; usá `next_offset` para continuar un texto largo. |
| `search_course_contents(course_id, query)` | Busca en nombres y descripciones del índice del curso; no busca dentro de los archivos. |
Ejemplos de pedidos al asistente:
- “Listá mis cursos del aula virtual”.
- “Mostrame todas las unidades y los materiales de este curso”.
- “Buscá las guías de prácticos y resumí el PDF de la primera unidad”.
- “Abrí esta carpeta y mostrame sus archivos”.
El flujo habitual es listar cursos → obtener el índice de uno → leer los enlaces de las actividades. El índice puede ser grande; el asistente debe recorrer los materiales que necesite para responder. No se descargan todos los archivos automáticamente.
## Alcance y privacidad
- Solo consulta información disponible para tu cuenta. No entrega tareas, inicia intentos de examen ni publica mensajes. Moodle puede registrar las visitas como cualquier navegación normal.
- Los PDFs escaneados necesitan OCR, que este proyecto no incluye. Archivos como ZIP, videos y documentos de Office se identifican por su formato y se devuelven con un enlace para abrir desde el aula.
- Los enlaces a servicios externos permanecen en el índice, pero el servidor no los descarga ni les envía credenciales. Puede ser necesario abrirlos en el navegador.
- Límite de 30 MB por respuesta descargada y de 100.000 caracteres por fragmento. No guarda cookies ni contenidos del aula en disco.
- `.env`, cookies, descargas, cachés y entornos virtuales están excluidos de Git. El repositorio público no debe contener credenciales ni material privado de cursos.
- Si UNC cambia el formulario, exige MFA o Moodle cambia su HTML, puede ser necesario adaptar el cliente. Una sesión vencida se señala para volver a iniciar sesión en la siguiente llamada.
Proyecto independiente, sin afiliación oficial con la UNC. Usa tus propios permisos de acceso.
## Desarrollo y verificación
```bash
uv sync --locked
uv run ruff check src tests scripts
uv run ruff format --check src tests scripts
uv run pytest -q
```
Las pruebas automatizadas usan respuestas simuladas para verificar acceso SAML, paginación de cursos, pestañas, subsecciones, textos en línea, URLs permitidas y redirecciones. También inician un proceso MCP real para verificar el protocolo stdio y las herramientas.
Con credenciales locales, ejecutá la comprobación real de lectura de cursos y una muestra de materiales, a través del protocolo MCP:
```bash
uv run python scripts/verify_live.py
```
Este comando requiere acceso a internet y solo imprime conteos y formatos. No se ejecuta en CI, donde no se necesitan credenciales del aula.
TDQS
Scored across 4 tools
Each tool targets a distinct step in a browsing workflow: listing courses, dumping a course's structure, searching that structure, and reading a specific item. The main risk is get_course_contents vs search_course_contents, but descriptions clarify that one returns the full visible outline and the other searches name/description fields only. read_content is clearly the terminal action for a specific activity or resource.
All four names follow a consistent snake_case verb_noun pattern: list_courses, get_course_contents, read_content, search_course_contents. The verbs are conventional and predictable for the actions they represent, with no mixed conventions or vague names.
Four tools is on the lean side but well-scoped for a read-only LMS browsing server; each tool (list, outline, search, read) earns its place without redundancy. It is slightly thin compared with richer LMS surfaces, but not a mismatch for the stated browsing purpose.
The surface covers the full read-only discovery lifecycle: list enrolled courses, inspect course structure, search the index, and read individual activities/resources with pagination. Minor gaps remain (no user/profile or grade overview, and write actions are explicitly excluded), but core browsing workflows have no dead ends.