Jaipuria Moodle Reports MCP
Provides tools to query student marks, attendance, subject performance, cohort analytics, longitudinal trends, at-risk students, and report accuracy from Moodle data, all scoped by campus and read-only.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@Jaipuria Moodle Reports MCPshow me declining students in the 2024 cohort"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
Jaipuria Moodle Reports MCP
A Jaipuria Google-account-accessible Model Context Protocol (MCP) server that makes the Jaipuria
student-report-system data queryable in plain language. Connect it to any MCP host (a dashboard,
Codex, ChatGPT, Claude.ai, Claude CLI) and ask about student marks, attendance, subjects, cohort
analytics, longitudinal trends, at-risk students, and report accuracy — every ingested student.
Every verified @jaipuria.ac.in account can use the MCP across all campuses.
Endpoint: https://moodle-mcp.tryrehearsal.ai/mcp · Health: /health · Source tools: 27
Repo: github.com/mansigambhir-1313/Moodle-MCP · Owner: Jaipuria AI Labs
Overview
The pipeline in moodle-agent ingests Moodle data, computes analytics, and
generates validated student reports into a Supabase project. This MCP is the read side of that
project for Jaipuria account holders: it exposes the raw data and the pipeline's outputs as
26 structured query/status tools plus one report-generation action that a host LLM routes on.
It is data-first — the primary surface is the raw gradebook and attendance (queryable for
every student, report or not); the generated reports and their two-scheme accuracy scores are a
secondary layer. Data access is read-only; create_report is the sole action and delegates report
generation to the authenticated agent service. This MCP never ingests or emails.
Design lineage: the Rehearsal MCP patterns (bounded caches, routing-contract docstrings, response budgets, secret stripping, graceful degradation), adapted from that server's per-student RLS model to institutional data access with Google sign-in granting Jaipuria IDs all campuses.
Where it fits
Upstream: the shared
student-report-systemSupabase project (tablesstudents,courses,enrolments,marks,attendance_sessions,student_reports,report_accuracy), written bymoodle-agent.Downstream: any MCP host — Codex, ChatGPT/Claude connectors, a faculty dashboard, or CLI.
Related MCP server: VortexIQ MCP Connector
What makes it exclusive
Longitudinal, not just snapshot — one run holds every trimester (T1–T6). Tools like
student_trajectoryanddeclining_studentscatch a student sliding term-over-term, which a point-in-time query never shows.Single-pane views —
student_360andcohort_pulsereturn a whole student / whole cohort in one call, ready for a dashboard drawer or landing screen.Accuracy as first-class data — every generated report carries a two-scheme validation score (faithfulness panel + two-turn LLM judge). Ask "which reports are flagged and why?"
Teaching & curriculum signals —
section_compare(A-vs-B fairness),assessment_breakdown(quiz vs assignment vs project),subject_difficulty(curriculum pressure points).
Tools (27)
Every data tool is SELECT-only, campus-scoped to the caller, bounded, and carries a
WHAT / USE WHEN / DO NOT USE / RETURNS routing docstring. create_report is separately marked
as a non-destructive write action.
Students — raw data (primary)
Tool | What it returns |
| Roster for a campus/batch (± section), every ingested student |
| One student's complete record — per-subject component marks + attendance |
| Flat, component-level gradebook rows for a student |
| Per-subject attendance (present / sessions / %) for a student |
Subjects — raw data (primary)
Tool | What it returns |
| Subjects/courses for a scope, with trimester, sections, enrolment |
| A subject's cohort marks, pass rate, attendance, per-component means |
| Section-vs-section means + spread (teaching/marking signal) |
| Cohort performance by assessment kind (quiz/assignment/project…) |
| Subjects ranked hardest-first (pass rate + zeros) |
Insights — longitudinal & single-pane (hero)
Tool | What it returns |
| A student's marks/attendance trend across trimesters + label |
| One-call student view: percentile rank, trend, risk flags, accuracy |
| One-call cohort KPIs: marks, attendance, pass rate, at-risk, distribution |
| Auto intervention list — reasons + suggested action, ranked |
| Cohort-wide biggest term-over-term mark drops (early warning) |
| Cross-cohort campus KPIs, risks, and teaching signals |
Analytics & at-risk (primary)
Tool | What it returns |
| Cohort marks snapshot — mean, pass rate, distribution, zeros |
| Cohort attendance — mean, counts below 75% / 65% |
| Highest overall marks in a scope |
| Campus-vs-campus means for a batch |
| Composite risk ranking (zeros + attendance + failing marks) |
| Students below an attendance threshold |
| Students with a recorded zero (most urgent) |
Reports (secondary)
Tool | What it returns |
| The generated narrative report for a student |
| Whether enough source data exists to generate a report |
| Status/result for a durable report job created by |
| The caller's principal and allowed campuses |
Action (write)
Tool | What it does |
| Queues one idempotent report job; returns a request id for |
See docs/INNOVATION_ROADMAP.md for Phase-3 ideas
(attendance_eligibility, attendance_marks_link, anomalies, roster_health).
Quickstart
Connect Codex (deployed server)
This repository contains a managed Codex plugin bundle under plugins/moodle-mcp:
.codex-plugin/plugin.json supplies presentation metadata and .mcp.json points at the production
server. The checked-in .agents/plugins/marketplace.json exposes that bundle for a workspace admin
to import from GitHub. Install moodle-mcp, start a new Codex desktop task, and complete the Google
OAuth prompt. The server uses dynamic client registration and PKCE, so no static bearer token
belongs in the plugin.
Because the plugin declares .mcp.json, workspace distribution is desktop-only even though the
server is remote HTTPS. See docs/TEAM_ROLLOUT.md for the admin import,
pilot, release-gate, and rollback steps.
See docs/CODEX_GAP_ANALYSIS.md for the compatibility and security
review.
Connect a static-token host (legacy/non-OAuth deployment)
claude mcp add moodle --transport http https://moodle-mcp.tryrehearsal.ai/mcp \
--header "Authorization: Bearer <your MCP_TOKENS value>"Then ask, in plain language:
"cohort pulse for jaipur 2024-26" · "who's declining" · "build my watchlist" · "show JJ24PG001's full record" · "hardest subjects" · "compare sections of Wealth Management"
Run locally
cd moodle-mcp
python3 -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt
cp .env.example .env # fill in the vars below
uvicorn server:app --port 8899
curl localhost:8899/health # {"status":"ok",...}Smoke test (real MCP handshake + live queries)
MCP_URL="http://localhost:8899/mcp" MCP_TOKEN="<a token>" python test_client.pyConfiguration
config.py (pydantic-settings, reads .env + env vars). validate_config() is a fail-closed
boot check on the Supabase vars.
Variable | Description | Where to get it |
| Report project URL ( | Supabase → Settings → API |
| Custom | Supabase signing key + SQL role |
| Custom JWT restricted to encrypted | Supabase signing key + SQL role |
| Custom JWT restricted to the audit RPC | Supabase signing key + SQL role |
| Gateway key used with the three custom-role JWTs | Supabase → Settings → API |
| JSON map of faculty tokens → | You generate it |
| Single all-campus break-glass token (alternative to | You generate it |
| Base for report links (default | — |
| Public URL of this service (optional) | Render dashboard |
| Tool calls allowed per token per window (default | — |
| Rate-limit window in seconds (default | — |
Malformed MCP_TOKENS (bad JSON or wrong shape) fails the boot loudly rather than silently
locking everyone out; short tokens log a warning. All logging goes to stderr; audit lines record
who / which tool / campus scope / outcome and never contain token contents, student ids, or PII.
Access model
Every verified Jaipuria Google account, including accounts in the student roster, can use all tools across all campuses. Google email verification is required, and lookalike domains are rejected. The legacy static-token mode and explicitly granted external OAuth accounts retain their configured campus scopes; every tool intersects a requested campus with that grant.
Generate a per-campus token block:
python3 -c "import secrets; print('mcp_'+secrets.token_urlsafe(24))" # one per faculty// MCP_TOKENS (single-line JSON in the env var)
{
"mcp_...indore": {"name": "Indore TNP", "campuses": ["indore"]},
"mcp_...office": {"name": "Programme Office", "campuses": null} // null = all campuses
}The Supabase credential stays server-side and is never handed to the host. create_report does
not write through that credential; it calls the separately authenticated report service.
Architecture
MCP host (Codex / ChatGPT / Claude / dashboard)
│ MCP over HTTP + Bearer <faculty token>
▼
server.py (FastMCP /mcp, /health)
get_authenticated_service() → verify token → MoodleService(allowed_campuses)
│
tools/* (7 modules, 27 tools) — each: Params model + _impl(svc,…) + register()
│ every query .in_("campus", allowed) ; strip_secrets ; response budgets
▼
Supabase (read service role) — students · courses · enrolments · marks ·
attendance_sessions · student_reports · report_accuracyFull design: docs/ARCHITECTURE.md.
Key files
Path | Purpose |
| FastMCP app, |
| Settings + |
| Read-only |
| Shared helpers: |
| Primary data tools |
| Cohort rollups |
| Cached report reads + on-demand generation |
| TTL cache, budgets/scoping, tool hints |
| End-to-end MCP client smoke test |
Caches (OOM-safe — bounded TTLCache only)
_run_cache (latest final run per scope), _rollup_cache / _marks_cache (cohort raw-data
rollups). Cohort tools page past PostgREST's 1000-row cap and cache the result for 5 min.
Deployment
Render (
render.yamlblueprint or Docker): paid always-on web instances, shared Redis,uvicorn server:app, health check/health. Configure the split Supabase credentials, OAuth, audit HMAC, and signed report-queue secret described indocs/SECURITY_SCALABILITY_RELEASE.md.Docker:
docker build -t moodle-mcp . && docker run -p 8000:8000 --env-file .env moodle-mcpThe production blueprint uses a Standard instance to remove free-tier cold starts and permit horizontal scaling after load-test evidence supports it.
Environment | URL | Notes |
Production |
| Custom domain; |
Local |
|
|
Full test/deploy steps: DEPLOY.md.
Runbooks
Rotate access tokens — regenerate MCP_TOKENS (same generator), update the Render env var; the
service restarts and old tokens stop working. Re-issue the new tokens to faculty.
Add a per-campus faculty — add one "mcp_...": {"name": "...", "campuses": ["<campus>"]} entry
to MCP_TOKENS, redeploy, hand them their token.
Add a new tool — follow docs/ARCHITECTURE.md §11: add a Params model + _impl(svc,…) +
register(), campus-scope every query, strip_secrets, write the routing docstring, register in
server.py. Reuse the raw-data helpers in tools/common.py.
Unexpected cold start / first request slow — verify the live service actually uses the paid
blueprint plan and that the health check is passing; /health is safe for an uptime probe.
Verify a deploy — curl <url>/health, then
MCP_URL="<url>/mcp" MCP_TOKEN="<token>" python test_client.py.
Security & safety
Two enforcement layers wrap every tool centrally (security.py), so a new tool can't
accidentally run unauthenticated or leak a stack trace:
TransportGuard(ASGI): every/mcprequest is, before JSON-RPC: body-capped (MCP_MAX_BODY_BYTES→ 413), per-IP rate-limited (MCP_IP_RATE_LIMIT→ 429, fail-open) to blunt unauthenticated floods / token guessing, and auth-gated — a valid bearer or real HTTP 401 +WWW-Authenticate, blocking tool enumeration./health(now just{"status":"ok"}, no version fingerprint) stays open; CORS preflight passes.GuardMiddleware(FastMCPon_call_tool): per-call rate limit keyed by the token (hashed, so two tokens sharing a name don't share a budget), audit log (who/tool/scope/outcome, no secrets/PII), and a catch-all error boundary — combined withmask_error_details=True, any unexpected exception returns a generic message while full detail is logged server-side only (no Supabase URL / schema / key leak).
Token resolution is cached and constant-time (hmac.compare_digest). Tokens support an optional
per-token expires (ISO date/datetime) so a grant can be revoked by date without a redeploy.
Boot is fail-closed: malformed token config, a token <24 chars (unless ALLOW_WEAK_TOKENS), or
a bad expires format all crash the process loudly.
Least-privilege DB roles
The MCP reads with the key in SUPABASE_DATA_KEY. Use a SELECT-only credential instead of
the full service_role key (which bypasses RLS and can write): apply
sql/2026-08-26_reporting_readonly_role.sql, then mint
a JWT with {"role":"reporting_readonly"} signed with the project JWT secret and set it as the key.
PostgREST then runs every query as a role that physically cannot write. The server logs a warning
at boot whenever it detects a full service_role key still in use. OAuth state and audit delivery
use separate mcp_oauth_writer and mcp_audit_writer credentials.
Data invariants: read-only data queries · one explicitly annotated report-generation action ·
campus-scope every query · uniform {"found": false}
misses (no existence oracle) · explicit field projections + secret stripping (run ids / storage
keys / hashes / emails never leave the server) · service-role key server-side only · response
budgets + paging · bounded caches only (OOM-safe). Detail in docs/ARCHITECTURE.md §3, §11.
Verified in testing: tokenless → 401; bad token → 401; a jaipur-scoped token cannot read noida
cohort data (available:false) and a cross-campus student is a uniform miss; the rate limiter
blocks past the window; an internal exception is masked from the client but logged server-side.
This server cannot be deployed
Maintenance
Related MCP Connectors
Query your org's data in natural language — read-only MCP access to SQL, NoSQL, files & warehouses.
Ask questions in plain language, get answers from your business database. No SQL required.
Ask business questions in plain English. Get instant answers from your database, no SQL needed.
Ask data questions in natural language. Get SQL, insights, and charts from your databases.
Related MCP Servers
- AlicenseNot gradedqualityBmaintenanceEnables querying your team's work (stats, overdue, workload, active members, etc.) via natural language, scoped to your department with read-only signed-token access.5 npmMIT
- AlicenseNot gradedqualityCmaintenanceEnables read-only access to VortexIQ ecommerce AI data, including store audits, KPIs, alerts, brand DNA, and reports, through natural language queries.MIT
- FlicenseNot gradedqualityCmaintenanceProvides tools for querying student academic data such as subjects, marks, performance reports, timetable, exams, fees, events, holidays, and assignments via natural language.-
- FlicenseNot gradedqualityCmaintenanceEnables read-only access to company data across PostgreSQL, MongoDB Atlas, and flat files through MCP tools, allowing AI assistants to query and retrieve information via natural language.-