Institflow runs a Model Context Protocol server at /mcp (Streamable HTTP, stateless — every call is independent, so it scales like any other API request). It exposes the same domain services every REST endpoint calls, through 74 task-oriented tools, a handful of read-only resources, and a few report-style prompts — so an AI agent (Claude, or any MCP client) can search students, check a schedule, record attendance, or draft a progress report, using exactly the caller's own permissions, never more.
Institflow is its own OAuth 2.1 authorization server for this — no separate API key or manual token needed. A compliant MCP client discovers everything it needs automatically:
It fetches GET /.well-known/oauth-protected-resource (RFC 9728) from /mcp's WWW-Authenticate challenge, then GET /.well-known/oauth-authorization-server (RFC 8414) for the token/registration/authorize endpoints.
If it doesn't already have a client_id, it registers one dynamically: POST /oauth/register (RFC 7591 — a public client, no secret; disable this endpoint tenant-wide with the OAUTH_DYNAMIC_REGISTRATION setting if you'd rather pre-register clients yourself).
It opens GET /oauth/authorize with PKCE (S256 — required, not optional, under OAuth 2.1) in your browser. You sign in (if needed) and land on a consent screen showing exactly which tenant/membership you're connecting and what the client can do.
After you approve, the client exchanges the authorization code — POST /oauth/token with your PKCE code_verifier — for an access token (a normal Institflow JWT, just typ: "mcp") and a refresh token good for 30 days.
The reverse also isn't possible — a typ: "mcp" token is rejected with 401 on every ordinary /api/v1/* route, and a regular access token can't call /mcp. They're deliberately separate.
Getting a token at all requires the mcp.access permission on the membership you connect with — a member without it (most secretary/teacher roles, by default) is refused at the consent step, before any token is ever issued. Every tool additionally checks its own declared permission on every call — identical to what the equivalent REST endpoint would require.
Tools marked destructive below need mcp.destructive as well. Calling one without confirm: true never mutates anything — it returns a preview of what would happen (e.g. "would change status from active to withdrawn") and requiresConfirmation: true. The agent (or the person driving it) re-calls the same tool with confirm: true to actually execute it. Every call — preview or execution — is audited with channel: "mcp", the tool name, and a hash of its arguments (never the raw arguments, which may carry PII).
Every tool, generated directly from apps/api/src/modules/mcp/tools/*.ts — the exact source the MCP server itself registers at boot.
Academics
Tool
Description
Permission
Destructive
cancel_session
Cancel a scheduled class session, with a reason. Destructive: previews unless confirm: true is passed.
groups.write
Yes
create_group
Create a new teaching group (class section) for a course, with an optional weekly schedule.
groups.write
No
drop_enrollment
Drop a student's enrollment from a group. Destructive: previews unless confirm: true is passed.
enrollments.write
Yes
enroll_student
Enroll a student into a teaching group.
enrollments.write
No
get_group
Get full details for one teaching group by id.
groups.read
No
get_schedule
Get scheduled class sessions for a group, teacher, or student within an optional date range. Exactly one of groupId/teacherStaffId/studentId is required.
groups.read
No
list_courses
List the course catalogue (subjects/programs offered), optionally filtered by category or active status.
courses.read
No
list_groups
List teaching groups (class sections), optionally filtered by branch, course, status, or teacher.
groups.read
No
transfer_student
Transfer a student's enrollment from their current group to another group (drop + enroll in one operation). Destructive: previews unless confirm: true is passed.
enrollments.write
Yes
Admin
Tool
Description
Permission
Destructive
audit_search
Search the tenant's audit log by action, entity, actor, or date range.
audit.read
No
dashboard_summary
Owner/manager dashboard summary — currently the tenant-wide billing summary (outstanding balance, collections, overdue).
invoices.read
No
get_settings
Get the tenant's effective settings (general, attendance, grading, billing, notifications, curriculum, reminders).
settings.manage
No
invite_member
Invite a new member to the tenant by email, with a role. Destructive: previews unless confirm: true is passed.
members.manage
Yes
list_members
List members of the current tenant, with their assigned roles.
members.manage
No
list_roles
List every role (system and custom) in the tenant, with its permissions.
roles.manage
No
set_member_roles
Set a member's complete role assignment (within one branch scope, or tenant-wide) to exactly the given list of role ids. Destructive: previews the add/remove diff unless confirm: true is passed.
members.manage
Yes
update_settings
Update one or more tenant settings keys. Destructive: previews the before/after values unless confirm: true is passed.
settings.manage
Yes
Assessment
Tool
Description
Permission
Destructive
create_exam
Create a new exam/quiz/homework/project for a group.
assessment.write
No
generate_progress_report
Queue generation of a progress report for a student or an entire group over a period. Asynchronous — returns immediately once queued.
assessment.write
No
get_student_grades
Get a student's grades (per-group weighted average and per-exam breakdown), optionally scoped to one group.
assessment.read
No
publish_exam
Publish an exam so its results become visible to students/guardians. Destructive: previews unless confirm: true is passed.
assessment.write
Yes
record_results
Bulk-record (or update) student scores for an exam.
assessment.write
No
send_progress_reports
Send every published (not-yet-sent) progress report for a student or group to guardians. Destructive: previews the list unless confirm: true is passed.
assessment.write
Yes
assignments
Tool
Description
Permission
Destructive
curriculum.assignments.create
Create an assignment unit in a course: a lesson of kind "assignment" plus its instructions, due date, max score, late policy and allowed submission kinds, in one transaction.
curriculum.write
No
curriculum.assignments.grade_submission
Grade a submission (score at most the assignment’s max score, optional feedback). Emits assignment.graded.
curriculum.assignments.grade
No
curriculum.assignments.list_submissions
List an assignment’s submissions (cursor-paginated), optionally filtered by status. lessonId is the assignment unit’s id, or the id of the video lesson that carries the task.
curriculum.assignments.grade
No
curriculum.assignments.publish_to_gradebook
Publish every currently-graded submission into the gradebook as a "homework" exam per group — upserts on repeat. Destructive: the first publish for a group creates and publishes a real exam, notifying students/guardians, so it previews unless confirm: true is passed.
curriculum.assignments.grade
Yes
curriculum.assignments.return_submission
Send a submission back to the student for revision (clears any earlier grade).
curriculum.assignments.grade
No
curriculum.assignments.update
Update an assignment’s own fields (instructions, due date, max score, late policy, allowed submission kinds). Lesson-shared fields (title, section, drip gate) go through update_lesson.
curriculum.write
No
curriculum.lesson_tasks.remove
Remove the task under a video lesson. Destructive: previews (with the number of submissions that would be deleted with it) unless confirm: true is passed, which also confirms deleting those submissions.
curriculum.write
Yes
curriculum.lesson_tasks.set
Create or replace «مهمة هذا الدرس», the task under a video lesson (instructions, due date, max score, late policy, accepted submission kinds). Submissions, grading and the gradebook publish then use the curriculum.assignments.* tools with the same lessonId.
curriculum.write
No
Attendance
Tool
Description
Permission
Destructive
attendance_report
Attendance report for a group (per-student rates) or a single student (full history), within an optional date range.
attendance.read
No
get_session_roster
Get the attendance roster for a scheduled session — every enrolled student and their current attendance mark, if any.
attendance.read
No
record_attendance
Bulk-record (or update) attendance marks for every student on a session's roster.
attendance.mark
No
Billing
Tool
Description
Permission
Destructive
billing_summary
Tenant-wide billing summary — outstanding balance, this month's collections, and overdue counts/totals.
invoices.read
No
create_invoice
Create a new draft invoice for a student, with one or more line items.
invoices.write
No
get_invoice
Get full details for one invoice, including its lines and payments.
invoices.read
No
list_invoices
List invoices, filterable by status (including an "overdue" shorthand), student, guardian, or due-date range.
invoices.read
No
record_payment
Record a payment against an invoice.
payments.record
No
student_statement
Get a student's full billing statement — every invoice/payment entry and the running balance.
invoices.read
No
void_invoice
Void an invoice, with a reason. Destructive: previews unless confirm: true is passed.
invoices.write
Yes
curriculum
Tool
Description
Permission
Destructive
create_lesson
Create a lesson in a course. Omit sectionId to use (or auto-create) the course’s default section. audienceGroupIds restricts it to those groups of the course; omit or null for every group.
curriculum.write
No
delete_lesson
Delete a lesson and its video and progress rows. Destructive: previews (with how many students have progress on it) unless confirm: true is passed.
curriculum.write
Yes
get_lesson
Get one lesson by id, including its markdown body and video source.
curriculum.read
No
grant_course_access
Grant a student access to a self-paced (video) course. Idempotent — granting twice returns the existing enrollment.
enrollments.write
No
list_course_curriculum
List a course’s curriculum: its sections, each with the lessons inside it. Published lessons only unless includeDrafts is true.
curriculum.read
No
publish_lesson
Publish a lesson so enrolled students can see it. Destructive: notifies real people, so it previews unless confirm: true is passed.
curriculum.publish
Yes
revoke_course_access
Revoke a student’s self-paced course access. Destructive: previews unless confirm: true is passed.
enrollments.write
Yes
set_lesson_video
Attach a video to a lesson from a YouTube URL. The URL is validated and normalized exactly as the web editor does.
curriculum.write
No
student_course_progress
One student’s progress through their video courses — lessons completed vs. available, and the last lesson they opened.
curriculum.read
No
update_lesson
Update a lesson’s title, summary, markdown body, estimate, release rules or audience (audienceGroupIds: a list of the course’s group ids to restrict the lesson to those groups, null for every group).
curriculum.write
No
Notifications
Tool
Description
Permission
Destructive
get_notification_rule
One notification type: the effective rule, the override the institute made, what the type allows and locks, and per channel whether it can deliver, who bills it and the price per message.
notifications.read
No
get_notification_spend
Notification usage this month per channel and, when the install is on the Institflow messaging relay, the credit balance.
notifications.manage
No
list_notification_rules
The notification rules of the institute: for every kind of notice the system sends — absence, invoices, payments, grades, lessons, assignments, quizzes, class and deadline reminders, subscription alerts — who receives it, on which channels, and whether the institute changed it from the Institflow default.
notifications.read
No
notification_usage
Notification channel usage/cost for a billing period (defaults to the current month, format YYYY-MM).
notifications.read
No
preview_notification_rule
Changes nothing: the estimated monthly cost (a range, in credit units — 10 000 units per credit), how many people a proposed rule would reach, whether saving it needs cost confirmation, and warnings. Omit `rule` to describe the current rule.
notifications.read
No
reset_notification_rule
Deletes the override the institute made for one notification type: it goes back to the Institflow default. Destructive: previews the change unless confirm: true is passed. Going back to the default can switch the type or a paid channel on again; that additionally needs confirmCost: true, exactly like update_notification_rule.
notifications.manage
Yes
send_message
Send a message to a student/guardian/group/branch audience via the notification channel ladder (email/SMS/WhatsApp/push, falling back down the ladder). SMS/WhatsApp are platform/licence-approved per tenant — naming one explicitly in `channels` when it is not approved is rejected; leaving `channels` unset falls through the ladder to an approved channel (in-app always reachable) instead. Destructive: previews recipient count and quota impact unless confirm: true is passed.
notifications.send
Yes
update_notification_rule
Replaces the override the institute made for one notification type (a field left out goes back to the Institflow default). Destructive: previews before/after AND the estimated cost unless confirm: true is passed. A change that raises what the institute pays for — a paid channel switched on, recipients, lead times or scope added — additionally needs confirmCost: true, so an assistant cannot spend credit by accident even with confirm.
notifications.manage
Yes
quizzes
Tool
Description
Permission
Destructive
curriculum.quizzes.add_question
Add a question to a quiz. Returns the whole quiz (questions included).
curriculum.quizzes.manage
No
curriculum.quizzes.create
Create a quiz unit in a course: a lesson of kind "quiz" plus its settings, in one transaction. No questions yet.
curriculum.quizzes.manage
No
curriculum.quizzes.delete_question
Delete a quiz question. Returns the whole quiz (questions included).
curriculum.quizzes.manage
No
curriculum.quizzes.grade_answer
Grade a short-answer (short_text) answer (points at most the question’s own points). Finalises the attempt’s score/passed once nothing is left ungraded. Emits quiz.graded on finalisation.
curriculum.assignments.grade
No
curriculum.quizzes.list_attempts
List a quiz’s attempts (cursor-paginated), optionally filtered by student or status.
curriculum.assignments.grade
No
curriculum.quizzes.publish_to_gradebook
Publish each student’s best fully-graded attempt into the gradebook as a "quiz" exam per group — upserts on repeat. Destructive: the first publish for a group creates and publishes a real exam, notifying students/guardians, so it previews unless confirm: true is passed.
curriculum.assignments.grade
Yes
curriculum.quizzes.reorder_questions
Whole-list reorder of a quiz’s questions. Returns the whole quiz (questions included).
curriculum.quizzes.manage
No
curriculum.quizzes.update
Update a quiz’s own settings (time limit, attempts allowed, shuffle, show-answers-after, pass score, due date). Lesson-shared fields go through update_lesson.
curriculum.quizzes.manage
No
curriculum.quizzes.update_question
Update a quiz question. Returns the whole quiz (questions included).
curriculum.quizzes.manage
No
Students
Tool
Description
Permission
Destructive
add_guardian
Attach a guardian to a student — either link an existing guardian by id, or create a new one and link it.
guardians.write
No
create_student
Create a new student record in the current tenant.
students.write
No
get_student
Get full details for one student by id, including guardians and status.
students.read
No
search_students
Search/list students in the current tenant, filtered by free-text query, status, branch, group, or tag. Cursor-paginated.
students.read
No
set_student_status
Change a student's lifecycle status (lead/active/inactive/graduated/withdrawn). Destructive: previews the transition unless confirm: true is passed.
students.write
Yes
update_student
Update a student's profile fields (name, contact info, tags, ...). Does not change status — use set_student_status for that.