Skip to main content
Glama

Nexus MCP

CI PyPI License: MIT

A read-only MCP server for Union College's Nexus (Moodle 4.5, hosted by Open LMS). Connect it to Claude and ask:

"What's due this week?" · "What's overdue?" · "Did I submit Lab 3?" · "What's my grade in CSC-385?" · "What changed in my courses today?" · "Find the lecture notes on shaders." · "Give me my daily briefing." · "What should I work on next?"

It uses Moodle's official web-service API with your own student token. It never scrapes the website and never writes anything. Unofficial: not affiliated with or endorsed by Union College or Open LMS.

Tools

Area

Tools

Courses

list_courses (active/past/future, term, teacher), get_course (sections, materials, activities), nexus_status

Assignments

upcoming_assignments, overdue_assignments, assignment_details, submission_status

Grades

current_grades, course_grade, grade_history

Materials

search_course_materials, get_material (pages, books, links, text files; PDFs with the pdf extra)

Calendar

upcoming_events

Announcements

recent_announcements, course_updates

Intelligence

daily_briefing, weekly_briefing, workload_analysis, what_should_i_do_next

Times are in your academic timezone (America/New_York). Errors carry a code: NEXUS_AUTH_ERROR, NEXUS_PERMISSION_ERROR, NEXUS_UNSUPPORTED, NEXUS_RESOURCE_NOT_FOUND, NEXUS_API_ERROR.

Related MCP server: academic-assistant

Setup (2 minutes)

You need uv (one-line installer below) and a Union student account.

curl -LsSf https://astral.sh/uv/install.sh | sh      # skip if you already have uv
uvx union-nexus-mcp setup

setup does everything: it opens Nexus in your browser for the Okta sign-in, registers the server with every AI client it finds on your machine (Claude Desktop, Claude Code, Cursor, Windsurf, VS Code, Gemini CLI, Codex CLI), and runs the connection test. Restart the client and ask it what's due.

On the Nexus page that says "Your registration has been confirmed", click "Click here if the app does not open automatically." and allow Open Nexus MCP Login. That hands the token to the terminal; it is stored in your OS keyring. No password is ever typed into this tool. If no prompt appears, right-click that link → Copy Link Address → paste it into the terminal. Details: docs/AUTHENTICATION.md.

Pick clients explicitly with uvx union-nexus-mcp setup --client claude-desktop --client cursor, or add a client later with uvx union-nexus-mcp install --client <key> (clients lists the keys). install --dry-run prints the snippet if you'd rather edit a config by hand:

{ "mcpServers": { "nexus": { "command": "uvx", "args": ["union-nexus-mcp", "serve"] } } }

GUI apps often can't see uvx on their PATH; install writes the absolute path for you (which uvx if editing manually).

Use it from any agent

One service layer, three doors:

Agent

How

MCP clients (Claude Desktop/Code, Cursor, Windsurf, VS Code, Gemini, Codex, custom MCP clients)

uvx union-nexus-mcp setup — stdio server, tools listed above

Command-driven agents, scripts, cron

uvx union-nexus-mcp briefing, due --days 7, overdue, next, grades, events, search <q>, updates --since 24h, or call <tool> key=value for any tool. JSON out. uvx union-nexus-mcp skill install drops a skill file into ~/.claude/skills so Claude Code knows the commands.

Your own Python agent

from nexus_mcp.moodle.nexus import Nexus → Nexus.from_settings(Settings.from_env(), token) gives every operation as async methods, no protocol in between.

Everywhere, 24/7 (Claude on web/phone, ChatGPT, Meta Muse, Grok Bot)

Host it on a free VM with serve --transport http --oauth: Claude and ChatGPT get a Connect button that signs you in with Union Okta (no key to paste); Muse, Grok and scripts can still use a bearer key. Guide: docs/HOSTING.md.

Remote agents, laptop-hosted (quick test)

uvx union-nexus-mcp expose — serves over a Cloudflare tunnel with a bearer token and prints the two values to paste (Server URL + Authorization: Bearer …). Your Nexus token never leaves the machine. Details and a stable-hostname setup: docs/REMOTE.md.

Agents with their own computer (Grok Bot, Codex cloud, a VPS cron job)

Install the package there and use the CLI: uvx union-nexus-mcp token export on your Mac, paste the two lines into the bot's ~/.config/nexus-mcp/.env, done. Recipe to paste into the bot: docs/BOT_COMPUTER.md.

Commands

uvx union-nexus-mcp setup              # login + client config + test
uvx union-nexus-mcp login              # sign in again (token expired / new machine)
uvx union-nexus-mcp test-connection    # reachability, auth, identity, capability matrix
uvx union-nexus-mcp list-courses       # --all includes past terms
uvx union-nexus-mcp install --client cursor --dry-run
uvx union-nexus-mcp tools                  # every MCP tool with its parameters
uvx union-nexus-mcp call daily_briefing --text
uvx union-nexus-mcp expose                 # public HTTPS endpoint for Grok Bot etc. (needs cloudflared)
uvx union-nexus-mcp serve --transport http --public-url https://… --oauth   # hosted, OAuth sign-in (docs/HOSTING.md)
uvx union-nexus-mcp logout

Working from a clone instead: uv sync, then uv run nexus-mcp <command>; setup/install then register the checkout itself. uv run pytest runs the mocked test-suite; uv run scripts/test_moodle_api.py --all is the verbose API diagnostic. Releases: docs/RELEASING.md.

Configuration

Copy .env.example to .env if you need to change anything. Defaults target nexus.union.edu. Notable keys: NEXUS_TOKEN (use an explicit token), NEXUS_TOKEN_STORAGE=keyring|file, NEXUS_TIMEZONE, and the NEXUS_CACHE_*_TTL lifetimes. Cached grade and submission data is always labelled cached with an as_of time.

Security

  • Read-only: no submission, grading, posting, messaging or enrolment functions exist in this codebase.

  • The only secret is your Moodle token: Keychain (or a 0600 file), sent in POST bodies, never logged.

  • .env and credential files are git-ignored. Revoke the token on Nexus under Preferences → Security keys.

Responsible use

  • Personal use only, on your own account, reading your own data. Union's Acceptable Use Policy applies; access to a system is not by itself authorization, so ask ITS if you want that confirmed.

  • Never share your token or credential file. Revoke it on Nexus (Preferences → Security keys) if in doubt.

  • What your AI client does with course content is governed by each course's AI policy and Union's Honor Code. Use this for planning and finding materials; don't use it to have an AI complete graded work unless the instructor allows it.

  • Course materials belong to instructors and publishers. Don't redistribute them.

Limitations

  • Login needs you at the keyboard (Okta/MFA). If Union expires the token, run login again.

  • Moodle has no grade-history API; grade_history returns current grades with dates and says so.

  • Material search is a text match over titles, filenames, descriptions and sections. PDF text needs uv sync --extra pdf.

  • Third-party activities (Turnitin, H5P, LTI) appear as modules without readable content.

  • Non-academic enrolments (trainings, campus resources) never end, so they count as "in progress"; use academic_only to hide them.

Linked Google Docs/Slides (Union-only sharing) can be read after a one-time nexus-mcp google login: docs/GOOGLE.md.

More: docs/API_FEASIBILITY.md (what Nexus exposes and why) · docs/ARCHITECTURE.md · CHANGELOG.md.

mcp-name: io.github.linboxin/nexus-mcp

Available Tools

19 tools
assignment_detailsA
Read-onlyIdempotent

Everything about one assignment: instructions, due/cut-off dates, submission requirements (types, file limits, word limit), current submission status, grading info and attached files. Accepts the assignment id (preferred) or its cmid.

Inputs: assignment_id (required). Returns: object

course_gradeA
Read-onlyIdempotent

Grade breakdown for one course: course total, percentage/letter when shown, and every grade item (assignments, quizzes, categories) with grade, range, weight, feedback and dates.

Inputs: course_id (required). Returns: object

course_updatesA
Read-onlyIdempotent

What changed in the student's courses since a point in time: modules whose settings/files/grades changed, new announcements and Moodle notifications. since accepts ISO-8601 ("2026-09-10T08:00"), a Unix timestamp, or relative values like "24h", "2d", "yesterday". Answers "what changed since yesterday?".

Inputs: since, course_id. Returns: object

current_gradesA
Read-onlyIdempotent

Current course total for every course the student can see grades in. Courses whose gradebook is hidden are listed with access="hidden" rather than omitted.

Inputs: include_past. Returns: object

daily_briefingA
Read-onlyIdempotent

One-call academic briefing: due today, due tomorrow, coming up, overdue work, today's events, new announcements/course changes, and a recommended priority order. text is ready to show; the structured fields back it up.

Inputs: no named parameters. Returns: object

get_courseB
Read-onlyIdempotent

Full view of one course: description, instructors, sections with every module, the materials (files/pages/books/links) and the major activities (assignments, quizzes, forums...). Module ids returned here are course-module ids (cmid).

Inputs: course_id (required). Returns: object

get_materialA
Read-onlyIdempotent

Metadata for one material plus its content when it is text-bearing (Moodle pages, books, text/HTML files, external links, and PDFs when the optional pdf extra is installed). material_id is the id from search_course_materials or a course-module id (cmid); folder files use "/".

Inputs: material_id (required). Returns: object

grade_historyA
Read-onlyIdempotent

What Nexus exposes about grade changes over time for a course. Moodle has no grade-history web service, so this returns each item's current grade with its graded/submitted date (newest first) and says so explicitly.

Inputs: course_id (required). Returns: object

list_coursesA
Read-onlyIdempotent

List the student's Nexus courses with id, name, short name, teacher and term.

    classification (from Moodle start/end dates): "inprogress" (default), "past",
    "future", "hidden", or "all". academic_only=true drops non-academic enrolments
    (trainings, campus resources) that have no term code. Use `id` with other tools.
    

Inputs: academic_only, classification. Returns: object

nexus_statusA
Read-onlyIdempotent

Who is signed in, which Moodle release Nexus runs, the timezone used for all dates, and which capabilities this account's token can use.

Inputs: no named parameters. Returns: object

overdue_assignmentsA
Read-onlyIdempotent

Assignments whose (extension-adjusted) due date has passed and that Moodle reports as not submitted. Only current courses are checked unless course_id is given; items older than max_age_days are ignored. can_still_submit says whether Nexus still accepts a submission (cut-off date).

Inputs: course_id, max_age_days. Returns: object

recent_announcementsA
Read-onlyIdempotent

Announcements posted by instructors in the last days days (the Announcements / News forum of each current course), newest first, with author, time, full text and link. include_all_forums=true also scans discussion forums.

Inputs: days, course_id, include_all_forums. Returns: object

search_course_materialsA
Read-onlyIdempotent

Search course materials (PDFs, files, pages, books, links, folders, lecture notes) by title, filename, description or section name across current courses (or one course). Returns title, course, type, description, url and a material id for get_material. Nothing is downloaded by this call.

Inputs: query (required), limit, course_id. Returns: object

submission_statusA
Read-onlyIdempotent

Authoritative submission status for one assignment straight from Moodle: not_started, draft, submitted, graded or overdue, with the grade/feedback when released. Check freshness.cached before calling the value real-time.

Inputs: assignment_id (required). Returns: object

upcoming_assignmentsA
Read-onlyIdempotent

Assignments due within the next days days, ordered by due date, each with its Moodle submission status (not_started / draft / submitted / graded / overdue), points and URL. This is the one call to make for "what's due this week?". Dates are in the student's timezone. Set include_submitted=false to hide finished work.

Inputs: days, course_id, include_submitted. Returns: object

upcoming_eventsA
Read-onlyIdempotent

Calendar events for the next days days in chronological order: assignment due dates, quiz open/close times, course events (exams, sessions), personal and site events, each with course, start/end, description, url and event type.

Inputs: days, course_id. Returns: object

weekly_briefingB
Read-onlyIdempotent

The next 7 days grouped by course: assignments (with status) and calendar deadlines, plus anything overdue.

Inputs: days. Returns: object

what_should_i_do_nextA
Read-onlyIdempotent

Prioritised to-do list built from overdue work, upcoming due dates, submission status, calendar deadlines and recent announcements. Each item explains why it was ranked where it is (HIGH / MEDIUM / LOW).

Inputs: no named parameters. Returns: object

workload_analysisA
Read-onlyIdempotent

Workload for the next days days. facts are Moodle data (counts per course and per day, points, quizzes, overdue); estimates are nexus-mcp heuristics for hours of effort and are explicitly not instructor estimates.

Inputs: days. Returns: object

Tool Schema Changelog

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

Loading tool schema changelog…

    TDQS

    A3.6/5.0

    Scored across 19 tools

    Disambiguation3/5

    The four synthesis tools (daily_briefing, weekly_briefing, workload_analysis, what_should_i_do_next) pull from largely the same underlying data (due work, overdue work, events, announcements) and could easily be mis-selected. submission_status also overlaps with the status already returned by upcoming_assignments and assignment_details. Descriptions do distinguish them somewhat, but the boundaries between the aggregate tools remain fuzzy.

    Naming Consistency3/5

    Everything is snake_case, which is good, but the pattern is mixed: some tools are verb_noun (get_material, get_course, list_courses, search_course_materials) while many are bare noun phrases (upcoming_events, recent_announcements, daily_briefing, grade_history). Readable but inconsistent verb conventions.

    Tool Count4/5

    19 tools is on the heavy side but each targets a recognizable piece of the student workflow (courses, assignments, grades, materials, events). A few synthesis tools are redundant with each other, keeping it from being ideal, but the count is defensible for the scope.

    Completeness4/5

    The surface covers the read-only student domain well: courses, assignments, submissions, grades, materials, events, announcements, and status/auth. Minor gaps (e.g. quiz/forum participation detail, any write/submit actions) exist but are not blocking for the apparent read/advice purpose.

    Related MCP Connectors

    Related MCP Servers

    • A
      license
      A
      quality
      B
      maintenance
      Read-only MCP server that lets Jeonbuk National University students query their LMS in natural language, covering daily briefings, deadlines, announcements, assignments, and course materials while keeping credentials local.
      25
      40 npm
      MIT
    • A
      license
      Not graded
      quality
      B
      maintenance
      Read-only MCP server for Chaoxing (学习通) that logs into a user's account and exposes their class schedule, enrolled courses, course materials downloads, homework status, exam schedules and scores, chapter task-point progress, notices, and personal cloud drive files. Enables any MCP client to answer natural-language questions about a student's coursework without submitting anything or modifying account data.
      23
      MIT