Skip to main content
Glama

FCEFyN MCP

Servidor Model Context Protocol 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.

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.

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:

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.

Related MCP server: moodle-utn-mcp

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:

{
  "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:

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

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:

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.

Available Tools

4 tools
get_course_contentsB
Read-only

Devuelve todas las secciones visibles, descripciones y actividades de un curso.

NameRequiredDescriptionDefault
course_idYes

TDQS

B3.4/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint=true, destructiveHint=false and openWorldHint=true, so the safety profile is covered. The description adds meaningful context with "visibles", indicating only sections visible to the caller are returned, but says nothing about pagination, ordering, or response shape.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

A single front-loaded sentence with the scope ("todas", "visibles") stated first and no wasted words. It is perhaps slightly too terse to carry usage guidance, but structurally efficient.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a one-parameter read-only listing tool backed by annotations, the description covers the essential scope. It stops short of the completeness an agent needs for large courses, omitting any note on result volume, ordering, or what happens when sections are hidden.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

There is a single parameter (course_id) with 0% schema description coverage, so the description carries the burden. It implies a course scope but never clarifies that course_id is a numeric identifier or where to obtain it; the mapping is only inferable from the tool's subject matter.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource: returns all visible sections, descriptions and activities of a course. An agent can distinguish it as a full-contents fetch, but the description never names or contrasts it with siblings like search_course_contents or read_content.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The word "todas" implies this is the exhaustive-retrieval option versus a targeted search, so the choice is inferable. However, there is no explicit when-to-use, no exclusions, and no mention of alternatives such as search_course_contents when the agent only needs a subset.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

list_coursesA
Read-only

Lista todos los cursos matriculados del usuario, con ID, nombre y enlace.

NameRequiredDescriptionDefault

No parameters

Output Schema

NameRequiredDescription
resultYes

TDQS

A3.7/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint=true, destructiveHint=false and openWorldHint=true, so the safety profile is covered externally. The description adds only the shape of the results (ID, name, link), with no mention of volume, pagination, or whether all enrollments are included.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

A single front-loaded sentence that states the scope and the returned fields with no filler. Appropriate sizing for a zero-argument listing tool.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With no inputs, an output schema present, and annotations covering the safety profile, almost nothing else is required. Minor residual gaps: it restates return fields the output schema already provides, and says nothing about result volume or pagination.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The tool takes zero parameters, so there is no parameter semantics to explain; the baseline of 4 applies. Nothing in the description conflicts with the empty schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

Names a specific verb and resource ('Lista todos los cursos matriculados del usuario') and even enumerates the fields returned (ID, nombre, enlace). It is clearly distinguishable from the content-oriented siblings (get_course_contents, read_content, search_course_contents) by implication, though it never explicitly says so.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The scope phrase 'cursos matriculados del usuario' implies when the tool applies (discovering the user's courses before drilling into contents), but there is no explicit when-to-use, when-not-to-use, or named alternative such as get_course_contents.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

read_contentB
Read-only

Lee actividad, carpeta, página, capítulo de libro o PDF del aula. Pagina con next_offset.

En carpetas y libros devuelve enlaces que se pueden leer con esta misma herramienta. Para formatos sin extracción devuelve el enlace. No envía tareas ni publica mensajes.

NameRequiredDescriptionDefault
urlYes
offsetNo
max_charsNo

TDQS

B3.4/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnly/openWorld/non-destructive, and the description adds real context beyond them: pagination via next_offset, links returned for formats without text extraction, and an explicit non-scope statement ('doesn't send tasks or publish messages'). It stops short of saying what a page/truncation boundary looks like in practice.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Very short and front-loaded: the core capability is in the first clause, followed by pagination and return-format notes. Minor waste in the trailing scope sentence, but nothing bloated.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a simple read-only tool with no output schema, it covers return behavior (extracted text vs. links) and pagination reasonably. The gaps are the undocumented offset/max_chars inputs and the absence of any indication of what the response looks like beyond 'next_offset'.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters2/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, so the description carries the full burden. It references pagination ('next_offset') and thus hints at the offset param, but the name doesn't match the actual 'offset' parameter and max_chars (the 30000-char truncation knob) is never explained anywhere.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

Names a specific verb (read) and enumerates the resource types it handles: activity, folder, page, book chapter, PDF from the classroom. That scope implicitly separates it from list_courses/get_course_contents/search_course_contents, but no sibling is ever named, so an agent must infer the boundary.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Gives a genuine usage hint for recursion: folders and books return links that can be read with this same tool. However, it never states when to pick this over search_course_contents or get_course_contents, nor what input (URL) is expected to come from those tools. Usage is implied rather than prescribed.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

search_course_contentsA
Read-only

Busca por nombre y descripción en el índice de un curso (no dentro de archivos).

NameRequiredDescriptionDefault
queryYes
course_idYes

Output Schema

NameRequiredDescription
resultYes

TDQS

A3.5/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint=true, openWorldHint=true and destructiveHint=false, so the safety profile is covered. The description adds the useful behavioral detail that search operates on the index, not file contents, but nothing about match semantics or limits; with annotations carrying the burden, a 3 fits.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

A single front-loaded sentence with the scope boundary in parentheses; nothing is wasted. It is terse but does not read as under-specified for its length.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With an output schema available and annotations covering the safety profile, the description needs only purpose and scope, which it delivers. The main residual gap is match semantics for the query parameter, which is minor given the small two-parameter surface.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, so the schema gives no parameter meaning. The description partially compensates: 'por nombre y descripción' indicates what the query string matches, and 'de un curso' implies course_id scopes the search. It still omits matching behavior (exact vs partial) and query format.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb (Busca) and resource (el índice de un curso) and adds a scope boundary (no dentro de archivos) that separates it from read_content. It does not name the sibling tools explicitly, but the boundary is clear enough to distinguish intent.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The parenthetical '(no dentro de archivos)' implies an exclusion, steering the agent away for file-content search, but it never names the alternative tool or states the condition for switching. Usage must be largely inferred.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

  1. 4 tool updatesv0.1.0
    • First observedget_course_contents
    • First observedlist_courses
    • First observedread_content
    • First observedsearch_course_contents

TDQS

A3.7/5.0

Scored across 4 tools

Disambiguation4/5

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.

Naming Consistency5/5

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.

Tool Count4/5

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.

Completeness4/5

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.

Maintenance

ActivityMaintained
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers