Edvibe School MCP
This server exposes an MCP interface to the Edvibe School API, letting an assistant manage school operations through 78 read/write/high-risk/sensitive tools.
Read-only tools: list/get access groups, books, group/individual classes, lesson tariffs/packages, marathons, pupils, pupil tags, schedules, teachers, and validate auth tokens.
Write tools: create/update access groups, classes, pupils, teachers, tags, tariffs, schedule lessons; add/remove group members; pin book lessons to classes; assign marathon moderators/pupils; set lesson packages; invite marathon students.
Destructive tools: delete classes, pupils, teachers, tags, tariffs, access groups, schedule lessons; remove group members; write off lesson balances; deactivate marathon pupils; unassign moderators/pupils (all require client-side approval).
Sensitive tools:
LoginPupilandLoginTeacherreturn login tokens and are disabled pending approval.Transport: supports local STDIO and stateless Streamable HTTP; HTTP requires
Authorization: Bearer <API key>andX-Edvibe-School-Domain, with optionalX-Edvibe-Client-Id.Telemetry and the analytics dashboard are disabled by default; enabling them requires separate approval.
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., "@Edvibe School MCP Show me the homework assignments for my class this week"
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.
Edvibe School MCP
Status: experimental / unofficial. This is Ruslan Sungurov's private pilot, not an official Edvibe product. Do not deploy, publish, enable telemetry, or connect live schools without the approvals described in
PLAN.md.
A stateless Model Context Protocol server over the official Edvibe School API. The v1 audience is Edvibe/ProgressMe schools on the Pro plan where the School API module is available.
Current state
The MCP contract contains exactly 78 tools:
35 read + 24 write + 17 high-risk + 2 sensitive.Local STDIO and stateless Streamable HTTP transports are implemented.
The private HTTP pilot is currently served at
https://edvibe.sungurov.com/mcp.A privacy-bounded HTTP telemetry pipeline and Russian internal dashboard are implemented locally.
Telemetry and
/admin/analyticsare disabled by default and have not been enabled on the pilot server. Deployment and activation require separate approval.Central telemetry intentionally excludes STDIO and never receives the user's original prompt.
The public tool names, arguments and responses remain unchanged. LoginPupil and LoginTeacher remain in the 78-tool contract; their actual public disablement and a tenant allowlist are separate security tasks.
Related MCP server: SchoolBridge
Contract at a glance
Class | Count | Meaning |
|
|
read | 35 | Read-only |
|
|
write | 24 | Ordinary state change |
|
|
high-risk | 17 | Destructive / hard to reverse |
|
|
sensitive | 2 | Returns a login token |
|
|
Total | 78 |
Risk is determined by operation semantics, not only by the HTTP method.
HTTP connection
POST /mcp requires two headers and accepts a third optional header:
Header | Value |
|
|
| Bare school hostname |
| Optional persistent UUID v4 for this client installation |
Old configurations without X-Edvibe-Client-Id continue to work. Invalid IDs are ignored. A valid ID enables installation coverage and anonymous scenario metrics; the raw UUID is never persisted.
GET /healthz remains unauthenticated and is excluded from product metrics. All other non-MCP paths return a generic error, except the separately protected analytics routes when enabled.
Local workflow
Requirements: Node.js 20+.
npm ci
npm run cinpm run ci runs both the immutable 78-tool contract validator and the node:test runtime/privacy suite.
Run STDIO (no centralized telemetry):
MCP_TRANSPORT=stdio node src/index.jsRun HTTP locally with telemetry and analytics disabled:
MCP_TRANSPORT=http TELEMETRY_ENABLED=false ANALYTICS_DASHBOARD_ENABLED=false node src/index.jsCredentials for School API access come from the MCP client configuration. Never put real API keys in Git, examples, test fixtures or shell command history.
Safe HTTP telemetry
The telemetry API accepts only a strict event schema:
service_startedmcp_initialize_completedmcp_request_rejectedtool_call_completedservice_stoppedtelemetry_storage_degraded
Allowed data is limited to UTC time, a server request ID, transport, HMAC pseudonyms, normalized client family/version, MCP method, tool/group/risk, outcome, a fixed safe error code, upstream status and numeric timings.
The logger rejects headers, IP/User-Agent, URLs/query strings, arguments/results, bodies, API keys, raw domains, raw UUIDs, tokens, passwords, JSON-RPC body/ID, arbitrary error messages, stacks and unknown fields.
School and installation identities use HMAC-SHA256; installation identities are tenant-scoped. The HMAC key and dashboard password are read only from systemd LoadCredential files:
telemetry_hmac_keyanalytics_password
Do not store these values in .env, the unit file, process arguments, Git or logs. Increment TELEMETRY_IDENTITY_EPOCH whenever the HMAC key is rotated.
Storage
SQLite:
/var/lib/edvibe-mcp/telemetry.sqliteby default.WAL, prepared statements,
busy_timeout, private0700directories and0600files.A bounded asynchronous queue accepts events on the HTTP thread; SQLite, journald and dashboard queries run in a dedicated worker thread. Storage failure never changes an MCP response.
Detailed events use a hard 90-day maximum. Identity-free hourly/daily aggregates use a hard 365-day maximum; cleanup runs daily.
Daily retention, incremental vacuum and at most seven local backups. Retained backup databases are pruned to the same detail/aggregate cutoffs.
The same allow-listed events are written as one JSON line to stderr/journald. Journald is not the annual analytics source; activation must verify a host journald retention policy no longer than 90 days.
Dashboard
When separately enabled, the Russian internal dashboard is available at GET /admin/analytics. It uses Basic Auth with the fixed username analytics and a password from the systemd credential. Its API is read-only:
GET /admin/analytics/api/dashboardGET /admin/analytics/api/events
Responses use Cache-Control: no-store, a restrictive CSP, frame denial and no external scripts. HTTPS forwarded by a proxy is trusted only from loopback/private proxy addresses, so port 9000 must remain firewalled from public access. Time is stored in UTC and displayed in Europe/Moscow. Identity drill-down is limited to the 90-day detail window; annual analytics uses identity-free aggregates.
The relevant non-secret flags are:
Variable | Default | Purpose |
|
| Collect safe HTTP events |
|
| Serve the protected dashboard/API |
|
| Pseudonym generation epoch |
|
| SQLite path |
|
| Local backup directory |
See deploy/systemd/telemetry.conf.example. The example intentionally keeps both feature flags off and the LoadCredential/StateDirectory directives commented for the first rollout. Provision the private state directory and credential files, then uncomment those directives only during the separately approved activation stage.
Security status
Implemented in this change:
fixed safe error taxonomy; no raw upstream body in exceptions;
process-keyed HMAC identifiers in the rate-limiter registry instead of raw API keys;
bounded limiter/DNS registries with expiry;
public-address DNS validation with the validated IPv4 result pinned into the upstream connection, plus a bounded response body;
strict telemetry allowlist and fail-open storage;
pseudonymous analytics and protected read-only UI.
Still separate from this change:
an Edvibe-controlled tenant/White Label allowlist before sending
Authorizationupstream;actual public disablement of
LoginPupilandLoginTeacheruntil product/security approval.
Repository layout
manifest/ authoritative 78-operation inventory
openapi/ immutable snapshot and generated normalized copy
scripts/ contract generation, validation and deployment helper
src/ MCP transports, upstream client and telemetry runtime
src/telemetry/ schema, identities, bounded queue, worker-thread SQLite store and queries
test/ node:test runtime and privacy checks
web/ landing page and self-contained analytics UI
deploy/systemd/ non-secret systemd drop-in example
README.md CONTEXT.md PLAN.md AGENTS.mdGenerated OpenAPI and manifest files must not be edited manually. See CONTEXT.md for decisions, PLAN.md for rollout gates and AGENTS.md for repository rules.
Rollout boundary
The safe sequence is: deploy code with both flags false; verify /healthz and the 78-tool contract; inspect Node/systemd/disk/firewall/proxy logging read-only; only after separate approval provision credentials and private directories, enable collection, run one controlled read-only canary and observe it for 24 hours.
Rollback is to set both feature flags to false and restart the service. The MCP continues to work and the existing database remains private. This repository change does not itself commit, push or deploy anything.
Available Tools
78 toolsAccessGroupsAddMembersC
Add members to an access group.
Inputs: accessGroupId (required), bodyTeacherIds, bodyGroupClassIds, bodyIndividualClassIds.
AccessGroupsCreateA
Create a new access group.
Inputs: name (required).
AccessGroupsDeleteADestructive
Delete an access group.
WARNING: Destructive: removes the access group. Requires client-side approval.
Inputs: accessGroupId (required).
AccessGroupsGetDetailsARead-only
Get details of a single access group by id.
Inputs: accessGroupId (required).
AccessGroupsGetGroupClassesARead-only
List group classes linked to an access group (paginated).
Inputs: accessGroupId (required), Skip, Take.
AccessGroupsGetIndividualClassesARead-only
List individual classes linked to an access group (paginated).
Inputs: accessGroupId (required), Skip, Take.
AccessGroupsGetListBRead-only
List access groups with optional filters by teacher, group class, or individual class.
Inputs: teacherId, groupClassId, individualClassId.
AccessGroupsGetTeachersARead-only
List teachers assigned to an access group (paginated).
Inputs: accessGroupId (required), Skip, Take.
AccessGroupsRemoveMembersADestructive
Remove members from an access group.
WARNING: Destructive: unlinks members from the group. Requires client-side approval.
Inputs: accessGroupId (required), bodyTeacherIds, bodyGroupClassIds, bodyIndividualClassIds.
BooksGetBookARead-only
Get a single book by id.
Inputs: bodyBookId.
BooksGetBooksPlatformARead-only
List books available on the platform.
Inputs: no named parameters.
BooksGetBooksSchoolARead-only
List books available to the current school.
Inputs: no named parameters.
BooksPinLessonToClassC
Pin a lesson from a book to a class.
Inputs: bodyClassId, bodyLessonId.
ClassesGetStatisticsDRead-only
Get class statistics.
Inputs: bodyClassId, bodyPupilId.
GroupClassesChangeTeacherC
Change the teacher assigned to a group class.
Inputs: classId (required), teacherId (required).
GroupClassesCreateC
Create a new group class.
Inputs: bodyName (required), bodyTeacherId (required), bodyAccessGroups, bodyLessonTariffDurationId.
GroupClassesDeleteADestructive
Delete a group class.
WARNING: Destructive: removes the group class. Requires client-side approval.
Inputs: classId.
GroupClassesGetDetailARead-only
Get details of a single group class by id.
Inputs: classId.
GroupClassesGetListBRead-only
List group classes with optional filters and pagination.
Inputs: Search, PupilId, pageSkip, pageTake, +2 more.
GroupClassesUpdateC
Update an existing group class.
Inputs: classId (required), bodyName, bodyTeacherId, bodyLessonTariffDurationId.
GroupClassPupilsAddA
Add pupils to a group class. Adding a pupil to a group class activates the pupil and consumes the school's tariff limit of activated students. The limit depends on the school's tariff plan. Exceeding it returns HTTP 400 "The number of activated students has been exceeded".
Inputs: classId (required), bodyPupilIds.
GroupClassPupilsDeleteADestructive
Remove a pupil from a group class.
WARNING: Destructive: unlinks the pupil from the group class. Requires client-side approval.
Inputs: classId (required), pupilId (required).
GroupClassPupilsGetListARead-only
List pupils in a group class (paginated).
Inputs: ClassId (required), pageSkip, pageTake.
IndividualClassesChangeTeacherA
Change the teacher assigned to an individual class.
Inputs: classId (required), teacherId (required), classLearningLanguage.
IndividualClassesCreateC
Create a new individual class.
Inputs: bodyName (required), bodyPupilId (required), bodyTeacherId (required), bodyClassLanguageId.
IndividualClassesDeleteADestructive
Delete an individual class.
WARNING: Destructive: removes the individual class. Requires client-side approval.
Inputs: classId.
IndividualClassesGetDetailARead-only
Get details of a single individual class by id.
Inputs: classId.
IndividualClassesGetListARead-only
List individual classes with optional filters and pagination.
Inputs: Search, PupilId, pageSkip, pageTake, +2 more.
IndividualClassesUpdateB
Update an existing individual class.
Inputs: classId (required), bodyName, bodyLessonTariffDurationId.
LessonPackagesGetListBRead-only
List lesson packages for a pupil and/or class.
Inputs: classId (required), pupilId (required), updatedAfter.
LessonPackagesSetPackageC
Set a lesson package for a pupil and class.
Inputs: classId (required), pupilId (required), bodyComment (required), bodyLessonPackageId (required), +4 more.
LessonPackagesUpdatePackagePeriodC
Update the period of a lesson package.
Inputs: classId (required), pupilId (required), bodyLessonPackageDurationId (required), bodyToDate, +1 more.
LessonPackagesWriteOffLessonsADestructive
Write off lessons from a package.
WARNING: Destructive: decrements lesson balance. Requires client-side approval.
Inputs: bodyCount (required), bodyClassId (required), bodyComment (required), bodyPupilId (required), +1 more.
LessonTariffsAddTariffDurationB
Add a duration option to a lesson tariff.
Inputs: bodyDuration (required), lessonTariffId (required), bodyPrice, bodyLessonsCount, +1 more.
LessonTariffsCreateB
Create a new lesson tariff.
Inputs: bodyName (required), bodyDurations, bodyDescription.
LessonTariffsDeleteADestructive
Delete a lesson tariff.
WARNING: Destructive: removes the tariff. Requires client-side approval.
Inputs: lessonTariffId (required).
LessonTariffsDeleteLessonPackageADestructive
Delete a lesson package attached to a tariff.
WARNING: Destructive: removes the lesson package. Requires client-side approval.
Inputs: LessonTariffId (required), LessonTariffPackageId (required), LessonTariffDurationId (required).
LessonTariffsDeleteTariffDurationADestructive
Delete a tariff duration.
WARNING: Destructive: removes the tariff duration. Requires client-side approval.
Inputs: LessonTariffId (required), LessonTariffDurationId (required).
LessonTariffsGetListARead-only
List lesson tariffs (paginated, with soft-deleted filter).
Inputs: Search, pageSkip, pageTake, IsDeleted.
LessonTariffsGetTariffDurationListBRead-only
List lesson tariff durations (paginated).
Inputs: pageSkip, pageTake, LessonTariffIds.
LessonTariffsGetTariffForDurationIdARead-only
Get a lesson tariff by its duration id.
Inputs: schoolLessonTariffDurationId (required).
MarathonAddMarathonNewStudentsA
Add new students to a marathon by email. Note: this is a GET that mutates state.
WARNING: Mutates state: invites new students to a marathon. Requires client-side approval.
Inputs: Emails, MarathonId, ModeratorsIds, MailMessageLanguageId.
MarathonChangeActivationMarathonPupilADestructive
Activate or deactivate a pupil in a marathon.
WARNING: Destructive: changes pupil activation state in a marathon. Requires client-side approval.
Inputs: bodyPupilId, bodyIsActivate, bodyMarathonId.
MarathonCreateModeratorC
Create a marathon moderator.
Inputs: bodyTeacherId, bodyMarathonId.
MarathonDeleteModeratorADestructive
Delete a marathon moderator.
WARNING: Destructive: removes the moderator. Requires client-side approval.
Inputs: bodyMarathonId, bodyIsSelectAll, bodyModeratorId, bodySelectedPupilsIds.
MarathonGetMarathonListARead-only
List marathons (paginated, with search and folder filter).
Inputs: FolderId, pageSkip, pageTake, SearchTerm.
MarathonGetMarathonStudentsARead-only
List students in a marathon with search and filter.
Inputs: Filter, MarathonId, SearchTerm.
MarathonGetModeratorsARead-only
List marathon moderators (paginated).
Inputs: pageSkip, pageTake, TeacherId, MarathonId.
MarathonSetModeratorsForPupilB
Assign moderators to a marathon pupil.
Inputs: bodyMarathonPupilId, bodySelectedModeratorsIds.
MarathonSetPupilsForModeratorB
Assign pupils to a marathon moderator.
Inputs: bodyMarathonId, bodyModeratorId, bodySelectedPupilsIds.
MarathonUnsetModeratorsForPupilADestructive
Unassign moderators from a marathon pupil.
WARNING: Destructive: unlinks moderators from the pupil. Requires client-side approval.
Inputs: bodyMarathonPupilId, bodySelectedModeratorsIds.
MarathonUnsetPupilsForModeratorBDestructive
Unassign pupils from a marathon moderator.
WARNING: Destructive: unlinks pupils from the moderator. Requires client-side approval.
Inputs: bodyMarathonId, bodyIsSelectAll, bodyModeratorId, bodySelectedPupilsIds.
PupilsCreateC
Create a new pupil.
Inputs: bodyName (required), bodyEmail (required), bodyNote, bodyPhone, +12 more.
PupilsDeleteADestructive
Delete a pupil. The pupil must be unlinked from all classes first: remove from group classes via GroupClassPupilsDelete and delete individual classes via IndividualClassesDelete. Upstream returns HTTP 400 "Pupil have classes" if any class link remains.
WARNING: Destructive: removes the pupil. Requires client-side approval.
Inputs: pupilId.
PupilsGetCursorListBRead-only
List pupils using cursor-based pagination.
Inputs: Search, ActiveOnly, OnlyUpdated, cursorLimit, +6 more.
PupilsGetDetailARead-only
Get details of a single pupil by id.
Inputs: pupilId.
PupilsGetListARead-only
List pupils with search, filters and pagination.
Inputs: Search, pageSkip, pageTake, ActiveOnly, +4 more.
PupilsUpdateB
Update an existing pupil.
Inputs: pupilId (required), bodyName, bodyNote, bodyEmail, +12 more.
PupilTagCreateB
Create a new pupil tag.
Inputs: TagName (required).
PupilTagDeleteADestructive
Delete a pupil tag.
WARNING: Destructive: removes the tag. Requires client-side approval.
Inputs: TagId (required).
PupilTagGetListARead-only
List all pupil tags.
Inputs: no named parameters.
ScheduleCreateLessonA
Create a new lesson in the schedule. The groupId field accepts the id of any class (group or individual). Creating a lesson may activate the pupil as a side effect (response includes isActivatedPupil).
Inputs: bodyDate (required), bodyGroupId (required), bodyDurationMinutes (required), bodyPupilId, +3 more.
ScheduleDeleteLessonADestructive
Delete a lesson from the schedule.
WARNING: Destructive: removes the lesson. Requires client-side approval.
Inputs: lessonId, bodyComment, bodyDeleteRepeatLessons.
ScheduleGetPackageForGroupLessonBRead-only
Get the lesson package applicable to a group lesson.
Inputs: lessonId (required), pupilId.
ScheduleGetPackageForIndividualLessonARead-only
Get the lesson package applicable to an individual lesson.
Inputs: lessonId (required).
ScheduleGetPupilScheduleARead-only
Get a pupil's schedule for a date range.
Inputs: dateEnd (required), pupilId (required), dateStart (required), bodyGroupId, +2 more.
ScheduleGetSchoolScheduleBRead-only
Get the school schedule for a date range.
Inputs: bodyPage, bodyDateEnd, bodyGroupId, bodyPupilId, +3 more.
ScheduleGetSchoolWeekWorkTimeARead-only
Get the school's weekly work time.
Inputs: no named parameters.
ScheduleGetTeacherScheduleARead-only
Get a teacher's schedule for a date range in a given timezone.
Inputs: dateEnd (required), dateStart (required), teacherId (required), timezone.
ScheduleGetTeacherWeekWorkTimeBRead-only
Get a teacher's weekly work time.
Inputs: teacherId (required), fromMonday (required).
TeachersCreateB
Create a new teacher.
Inputs: bodyName (required), bodyEmail (required), bodyPhone, bodySkype, +7 more.
TeachersDeleteADestructive
Delete a teacher.
WARNING: Destructive: removes the teacher. Requires client-side approval.
Inputs: teacherId.
TeachersGetDetailARead-only
Get details of a single teacher by id.
Inputs: teacherId.
TeachersGetListBRead-only
List teachers with search and pagination.
Inputs: Search, pageSkip, pageTake.
TeachersUpdateC
Update an existing teacher.
Inputs: bodyName, bodyEmail, bodyPhone, bodySkype, +9 more.
UserAuthCheckAuthTokenCRead-only
Check whether an auth token is valid.
Inputs: bodySessionToken.
UserAuthLoginPupilA
Authenticate a pupil and return a login token.
WARNING: Sensitive: returns a login token. Public enablement requires recorded product/security approval. The token must never be logged, cached, or returned outside the direct successful tool result.
Inputs: bodyPupilId.
UserAuthLoginTeacherA
Authenticate a teacher and return a login token.
WARNING: Sensitive: returns a login token. Public enablement requires recorded product/security approval. The token must never be logged, cached, or returned outside the direct successful tool result.
Inputs: bodyTeacherId.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
TDQS
Scored across 78 tools
Most tools are clearly separated by resource prefix and action, and the descriptions clarify their purpose. A few list-related tools such as PupilsGetList vs PupilsGetCursorList and LessonTariffsGetTariffForDurationId vs LessonTariffsGetTariffDurationList could cause misselection, but overall boundaries are reasonably clear.
The tools generally follow a Resource + Verb + OptionalObject pattern, making them readable and predictable. Deviations such as MarathonAddMarathonNewStudents, MarathonChangeActivationMarathonPupil, and mixed Create/Add/Set/Unset verbs are minor inconsistencies within an otherwise coherent scheme.
78 tools is an extreme count for a single MCP server. While the domain is broad, the surface area is far too large for effective agent discovery and selection, and many tools could be grouped or exposed as sub-resources rather than flat tool entries.
The server covers many core school-management workflows: pupils, teachers, group/individual classes, tariffs, packages, schedules, and auth. However, there are notable gaps such as no marathon create/update/delete, no schedule lesson update/reschedule, and no access group update, which leave some workflows incomplete.
Maintenance
Related MCP Connectors
Hosted MCP for Google Workspace, Atlassian and Notion: one OAuth, per-user accounts, write access.
One MCP endpoint for your company's tools, with per-person sign-in, policy and audit.
Scopable MSP API: clients, quotes, assessments, compliance, QBRs. OAuth 2.1, read-only or read-write
Managed MCP gateway with OAuth, per-agent permissions, shared accounts and activity logs.
Related MCP Servers
- FlicenseNot gradedqualityDmaintenanceThis MCP server provides comprehensive read and write access to Google Classroom, enabling management of courses, rosters, assignments, and grades. It supports full lifecycle operations for educational environments including student submissions, teacher invitations, and coursework organization.-
- FlicenseNot gradedqualityDmaintenanceAn MCP server that enables AI agents to safely query and act on school data (attendance, fees, student records) with strict role-based access control and a two-step write approval flow.-
- AlicenseNot gradedqualityBmaintenanceEnables LLM agents and automation tools to interact with Moodle through a permission-checked MCP endpoint, covering courses, activities, question banks, enrolments, and administrative operations via 240 external functions.4GPL 3.0
- FlicenseCqualityCmaintenanceEnables interaction with the Edookit school management system API, exposing attendance, lessons, people, grades, payments, courses, timetable, and individual goals as MCP tools.46-