Tutor MCP
An open-source MCP server that turns any LLM into an Intelligent Tutoring System. 50 years of cognitive science, MIT licensed.
Ask AI about Tutor MCP
Powered by Claude ยท Grounded in docs
I know everything about Tutor MCP. Ask me about installation, configuration, usage, or troubleshooting.
0/500
Reviews
Documentation
Tutor MCP โ Open-Source AI Tutor & Intelligent Tutoring System (ITS) for LLMs
Self-hosted AI tutor runtime that turns any LLM (Claude, ChatGPT, Le Chat, Gemini) into a true Intelligent Tutoring System (ITS) โ grounded in 50 years of cognitive science (BKT, FSRS, IRT, PFA, KST) and exposed over the Model Context Protocol (MCP). Adaptive learning, spaced repetition, mastery tracking, misconception diagnosis and a metacognitive loop โ for any subject, with no item bank to curate.
Tutor MCP is the adaptive brain behind a personalised tutor. You tell an LLM (Claude, ChatGPT, โฆ) what you want to learn โ Spanish for travel, Go for backend, options trading, medieval history โ and the runtime orchestrates the journey end-to-end: what to study next, when to review, how hard the next exercise should be, when you've mastered a concept, when you're losing motivation, when you're ready to be more autonomous. It works on any subject the learner can describe in natural language โ no content catalog, no curation, no editorial backlog.
Under the hood, it provides real-time cognitive state tracking, spaced-repetition scheduling, intelligent activity routing, misconception diagnosis, a motivation layer, and a metacognitive loop that helps learners become autonomous โ all exposed as a Model Context Protocol (MCP) server that any MCP-compatible LLM can drive.
Current release: v0.3 โ the regulation pipeline (BKT-aware action selector, KST-aware concept selector, gate controller, and a pure-FSM phase orchestrator) is the single runtime engine; the legacy alert-and-router cascade has been retired.
Table of Contents
- An Intelligent Tutoring System, not a chatbot
- Compatible MCP clients
- Design Choice โ The LLM is the Content Engine
- How It Works
- Cognitive Science Engine
- Regulation Pipeline (v0.3)
- MCP Tools
- Alert, Motivation, Webhook engines
- Authentication
- Architecture
- Running
- Capacity & Sizing
- Server Configuration
- Tech Stack
- Operations
- Roadmap
- Contributing
- Security
- Acknowledgments
- License
- Author
An Intelligent Tutoring System, not a chatbot
For 50 years, Intelligent Tutoring Systems (ITS) research โ Anderson, VanLehn, Koedinger and others โ has converged on four pillars: a domain model, a learner model, a pedagogical model, and an interface. Generic LLM chat covers only the last one. Tutor MCP supplies the first three as a deterministic runtime, and delegates the interface and the natural-language content to the LLM:
| ITS pillar | Provided by | How |
|---|---|---|
| Domain model | Tutor MCP runtime | Concept graph with prerequisites, validated by KST (Knowledge Space Theory) |
| Learner model | Tutor MCP runtime | Mastery, ability and recall predicted by BKT (Bayesian Knowledge Tracing), IRT (Item Response Theory) and PFA (Performance Factor Analysis) |
| Pedagogical model | Tutor MCP runtime | Spaced-repetition scheduling via FSRS (Free Spaced Repetition Scheduler), activity router, alert engine, motivation engine, metacognitive loop |
| Interface & content | Any MCP-compatible LLM | Claude, ChatGPT, Le Chat, Gemini โ generates exercises, hints, feedback and dialogue on demand |
The cognitive science is rigid and measurable; the LLM is infinitely flexible. Together they make an ITS that works on day one for any topic โ without an editorial team.
Compatible MCP clients
Tutor MCP works out of the box with the main MCP-capable assistants โ connect it to any of them via a custom connector (see Setup workflow).
Design Choice โ The LLM is the Content Engine, the Runtime is the ITS
Traditional learning platforms (Duolingo, Anki, Khan Academy) and most "AI tutor" demos are bounded by their item banks: once the catalog is exhausted, the system stops. Tutor MCP takes the opposite bet.
The LLM is trained on humanity's knowledge. It is the most flexible content generator that has ever existed. The runtime never stores exercises โ it generates them on demand, calibrated in real time to the learner's mastery, ability, affect, and personal goal.
- Domains and prerequisite graphs are co-authored by the learner and the LLM at
init_domaintime โ not pre-curated. - Exercises, explanations, hints, feedback, motivational nudges โ all generated on the fly by the LLM.
- The runtime brings the cognitive science of an ITS: five algorithms (BKT, FSRS, IRT, PFA, KST), an alert engine, a motivation engine, a metacognitive loop.
- The LLM brings infinite domain breadth and on-demand calibration.
This is what makes the system work on day one for any topic โ and what lets it scale without an editorial team.
How It Works
The server sits between a learner and an LLM. Three parallel loops run from the first session:
Learning loop (what to learn, when) โ The LLM calls MCP tools before and after every exchange to check alerts, get the next optimal activity, and record interactions. Four cognitive algorithms update the learner model in real time. The LLM never invents its own scheduling.
Metacognitive loop (how the learner learns) โ Affect check-ins, calibration tracking, and autonomy metrics observe the learner's relationship to the system. A mirror mechanism surfaces factual observations about dependency patterns without judging. The system aims to make itself progressively unnecessary.
Motivation loop (why this matters) โ A brief engine selects a single motivational angle per exercise (milestone, competence value, growth mindset, affect reframe, plateau recontextualization, or utility value linked to the learner's personal goal). The LLM composes the text from structured signals; the system never emits canned pep talk.
Cognitive Science Engine โ BKT, FSRS, IRT, PFA, KST
Five complementary learning-science algorithms run on every interaction and jointly inform the activity router. Together they form the learner-and-pedagogical model of the ITS.
| Algorithm | Role |
|---|---|
| BKT (Bayesian Knowledge Tracing) | Tracks mastery probability per concept. Distinguishes syntax errors from knowledge gaps. |
| FSRS (Free Spaced Repetition Scheduler) | Schedules reviews using stability/difficulty curves. Determines optimal review intervals. |
| IRT (Item Response Theory) | Estimates learner ability (ฮธ) from response patterns. Calibrates exercise difficulty. |
| PFA (Performance Factor Analysis) | Weights success/failure history to predict performance on each concept. |
| KST (Knowledge Space Theory) | Validates prerequisite graphs and gates new concepts on mastery of their ancestors. |
Regulation Pipeline (v0.3)
The seven-stage regulation pipeline โ pure functions composed by an impure orchestrator โ drives activity selection through an explicit phase FSM (DIAGNOSTIC โ INSTRUCTION โ MAINTENANCE) with information-theoretic concept selection and a hygiene gate. It is the single runtime engine.
| # | Stage | Status | What it does |
|---|---|---|---|
| [7] | Threshold Resolver (algorithms/thresholds.go) | Shipped (default-on) | Unifies the three historical mastery thresholds (BKT, KST gating, mid-curriculum) at 0.85. Kill switch: REGULATION_THRESHOLD=off. |
| [1] | Goal Decomposer (tools/goal_relevance.go) | Shipped (default-on) | LLM-authored relevance vector over the concept graph; biases [4] toward goal-critical concepts. Kill switch: REGULATION_GOAL=off. |
| [5] | Action Selector (engine/action_selector.go) | Shipped | Picks the activity type for a chosen concept from BKT/IRT signals: PRACTICE, DEBUG_MISCONCEPTION, FEYNMAN_PROMPT, TRANSFER_PROBE, RECALL_EXERCISE, MASTERY_CHALLENGE. ZPD anchored at IRT_ฮธ + 0.847. The REGULATION_ACTION=off flag drops only the system-prompt appendix โ the selector itself always runs. |
| [4] | Concept Selector (engine/concept_selector.go) | Shipped (default-on) | Phase-aware concept choice: max-info-gain on the KST fringe (INSTRUCTION), most-overdue under FSRS (MAINTENANCE), max-binary-entropy untouched concept (DIAGNOSTIC). Kill switch: REGULATION_CONCEPT=off (prompt appendix only; same caveat). |
| [3] | Gate Controller (engine/gate.go) | Shipped (default-on) | Hygiene gate that may override the selection: anti-repeat window (no concept repeated within last N=3 activities), 45-min session-budget escape (CLOSE_SESSION), no-fringe escape (REST). Kill switch: REGULATION_GATE=off (prompt appendix only). |
| [2] | Phase Controller (engine/orchestrator.go + engine/phase_fsm.go) | Shipped | Pure-FSM orchestrator wiring [4]โ[5]โ[3]. Transitions are observation-driven: DIAGNOSTICโINSTRUCTION on entropy reduction ฮH โฅ 0.2 bits or N โฅ 8 diagnostic items; INSTRUCTIONโMAINTENANCE on full-graph mastery; MAINTENANCEโINSTRUCTION on FSRS retention drop. |
| [6] | Fade Controller (engine/fade_controller.go) | Shipped (opt-in) | Pure post-decision module. Maps autonomy_score ร autonomy.Trend to a 4-field handover bundle (hint_level โ {full, partial, none}, webhook_frequency โ {daily, weekly, off}, zpd_aggressiveness โ {gentle, normal, push}, proactive_review_enabled bool). Wired into the motivation brief to fade verbosity as autonomy rises. Opt-in via REGULATION_FADE=on (strict literal) โ default OFF. Integration with the webhook scheduler and action selector is follow-up work tracked from docs/regulation-design/06-fade-controller.md ยง9. |
The pure functions (SelectAction, SelectConcept, ApplyGate, EvaluatePhase) are individually unit-tested (~90 dedicated tests). The orchestrator is exercised by SQLite in-memory tests and migration safety tests for the new domains.phase, domains.phase_changed_at, domains.phase_entry_entropy columns.
Feature flags โ default-on (opt-out via =off) except REGULATION_FADE (opt-in via =on)
The first six regulation flags are active by default. Setting any of them to the literal off disables that stage; any other value (including unset) leaves it enabled. Typos in the off-direction (Off, OFF, off) leave the stage active โ operators must type off exactly to disable, which makes accidental rollback impossible.
REGULATION_FADE is the exception: it is the youngest pipeline component and its visible effects (verbosity reduction, webhook suppression) interact directly with the learner, so it ships opt-in โ set the strict literal on to enable; any other value (unset, ON, true, 1) leaves the fader off.
Two flags change the runtime (REGULATION_THRESHOLD, REGULATION_FADE); the rest control the system-prompt appendix only. The appendix tells the LLM about the new activity types and routing semantics, but the actual selection logic always runs.
| Flag | Default | Effect when toggled away from default | Touches |
|---|---|---|---|
REGULATION_THRESHOLD | on | Reverts to legacy split thresholds (BKT 0.85, KST 0.70, Mid 0.80). | runtime |
REGULATION_GOAL | on | Hides set_goal_relevance / get_goal_relevance from the MCP tool list and drops the goal-aware system-prompt section. | runtime + prompt |
REGULATION_ACTION | on | Drops the action-selector appendix (the LLM no longer sees the new activity types documented). The selector itself keeps running. | prompt only |
REGULATION_CONCEPT | on | Drops the concept-selector appendix. Selector keeps running. | prompt only |
REGULATION_GATE | on | Drops the gate appendix. Gate keeps running. | prompt only |
REGULATION_FADE | off | Opt-in (the only opt-in flag). Set to the literal on to enable [6] FadeController: maps autonomy_score ร trend to fade params (hint verbosity, webhook frequency, ZPD aggressiveness, proactive review). When on, the fader modulates the motivation_brief so that the more autonomous the learner, the terser (and ultimately silent) the brief becomes; the resulting fade_params are also surfaced in the get_next_activity JSON for downstream consumers. Strict equality with on โ any other value (including ON, true, 1) keeps the fader off. See docs/regulation-design/06-fade-controller.md. | runtime |
Surface
Tutor MCP is chat-only. The LLM drives the learning loop in conversation: it queries cognitive state with the read-side tools, narrates exercises and feedback in plain text, and persists each turn through record_interaction. There is no iframe, no embedded SPA, no client-side rendering โ everything happens in the host's chat surface (Claude Desktop, Claude.ai, or any MCP-compatible client).
MCP Tools
Core Learning
| Tool | Description |
|---|---|
get_learner_context | Session context: active domain, concept states, recent history, active misconceptions |
get_pending_alerts | Critical alerts requiring immediate action |
get_next_activity | Next optimal activity + metacognitive mirror + tutor mode + motivation brief |
record_interaction | Log result; updates BKT/FSRS/IRT/PFA; tracks hints, initiative, proactive reviews, error type, misconception type/detail |
check_mastery | Check if a concept is eligible for a mastery challenge |
get_dashboard_state | Full dashboard: progress, retention, autonomy score, calibration bias, affect history |
get_olm_snapshot | Open Learner Model snapshot: per-concept mastery, retention, last-seen, fringe membership, anti-repeat status |
get_availability_model | Learner's time windows and session frequency |
update_learner_profile | Persist learner metadata (device, objective, language, calibration bias, affect baseline, autonomy score) |
Domain Management
| Tool | Description |
|---|---|
init_domain | Create a knowledge domain with concepts, prerequisite graph, personal goal, and optional value framings |
add_concepts | Add concepts to an existing domain without resetting progress |
archive_domain | Hide a domain from dashboard/routing while preserving progress |
unarchive_domain | Reactivate an archived domain |
delete_domain | Permanently remove a domain and all its data |
set_goal_relevance | LLM-decomposed goal-relevance vector over the concept graph (0 = orthogonal, 1 = goal-critical) โ biases the concept selector toward what matters for the learner's stated objective. Disabled by REGULATION_GOAL=off. |
get_goal_relevance | Read current goal-relevance vector with staleness flags (graph-version drift, uncovered concepts). Disabled by REGULATION_GOAL=off. |
Metacognitive Loop
| Tool | Description |
|---|---|
record_affect | Session check-in: energy + confidence (start), satisfaction + perceived difficulty + next intent (end) |
calibration_check | Before exercise: learner self-assesses mastery (1-5), stores prediction for comparison |
record_calibration_result | After exercise: compares prediction with actual result, updates calibration bias |
get_autonomy_metrics | Autonomy score (0-1) with 4 components: initiative, calibration, hint independence, proactive review |
get_metacognitive_mirror | Factual mirror message if a dependency pattern is consolidated over 3+ sessions |
Diagnosis & Transfer
| Tool | Description |
|---|---|
get_misconceptions | Lists detected misconceptions per concept with status (active/resolved) and frequency |
feynman_challenge | Feynman method: learner explains a mastered concept; LLM identifies gaps for BKT injection |
transfer_challenge | Tests concept transfer in novel contexts outside initial learning |
record_transfer_result | Records transfer challenge score for a concept/context pair |
learning_negotiation | Exposes system plan with tradeoffs; learner can propose alternatives |
Session Close & Nudges
| Tool | Description |
|---|---|
record_session_close | Closes session: persists optional implementation intention (Gollwitzer if-then) and returns a recap brief (concepts practiced, wins, struggles, next review, intent prompt) |
queue_webhook_message | Queues an LLM-authored Discord message (daily_motivation, daily_recap, reactivation, reminder) for the scheduler to dispatch in a future window |
All tools accept an optional domain_id for multi-domain support. Without it, the most recently active (non-archived) domain is used.
Alert Engine
The scheduler runs background jobs that detect nine alert types:
Learning Alerts
- FORGETTING โ FSRS retention dropped below threshold; triggers recall exercise.
- PLATEAU โ No mastery progress after multiple sessions; triggers debugging case with rotating exercise format.
- ZPD_DRIFT โ Error rate too high; enriched with error-type patterns (KNOWLEDGE_GAP / LOGIC_ERROR / SYNTAX_ERROR) and IRT ability signal.
- OVERLOAD โ Session exceeds 45 minutes; suggests rest.
- MASTERY_READY โ Concept ready for mastery challenge.
Metacognitive Alerts
- DEPENDENCY_INCREASING โ Autonomy score declining over 3 consecutive sessions.
- CALIBRATION_DIVERGING โ Calibration bias exceeds threshold; persistent over/under-estimation.
- AFFECT_NEGATIVE โ Low satisfaction or excessive difficulty on 2 consecutive sessions.
- TRANSFER_BLOCKED โ BKT shows mastery but transfer scores remain low across contexts.
Critical alerts are delivered via Discord webhook; dedup prevents the same alert firing twice in one day. Alert computation, the dashboard, and priority_concept filter out concept history that no longer belongs to an active (non-archived) domain, so delete_domain keeps progression on disk (re-init_domain brings it back) without leaking into reads or webhooks.
Motivation Engine
On every get_next_activity call, a brief engine selects at most one motivational angle for the LLM to voice. Selection is signal-driven and priority-ordered:
| Brief kind | Fires when |
|---|---|
milestone | Mastery just crossed 0.5 / 0.7 / 0.85 |
competence_value | First exercise of a session on a new concept, every 5 sessions on the same concept, or combined with a milestone โ rotates through four value axes (Financial, Employment, Intellectual, Innovation) using authored domain-level statements when present |
growth_mindset | A failure occurred on the same concept within 24h |
affect_reframe | Latest end-of-session affect is negative (low satisfaction, high difficulty, or low energy) |
plateau_recontext | A PLATEAU alert is active on this concept |
why_this_exercise | Utility-value fallback linked to personal_goal on new-concept or first-of-session exercises |
Each brief also carries a Hidi-Renninger interest phase (triggered โ emerging โ sustained โ individual) inferred from session count, mastery, and self-initiated ratio. The LLM receives signals and an instruction โ no pre-written text.
Webhook Queue (LLM-Authored Nudges)
Daily motivation (8h UTC) and daily recap (21h UTC) are no longer composed by Go templates. During sessions, the LLM calls queue_webhook_message to schedule a warm, personal message tied to the learner's goal; the scheduler dispatches from the queue within a ยฑ30-minute window. A sober Go fallback fires only when the queue is empty. Failed sends are marked for retry; past-due messages are expired hourly.
Autonomy Score
A composite metric (0-1) tracking the learner's progression toward independence:
| Component (25% each) | What it measures |
|---|---|
| Initiative rate | % of sessions started without a webhook nudge |
| Calibration accuracy | How well the learner estimates their own mastery |
| Hint independence | Ability to solve mastered concepts without hints |
| Proactive review rate | % of reviews done before FSRS scheduled date |
The trend compares the last 5 sessions to the previous 5 (improving / stable / declining).
Tutor Mode
The system adapts its communication register based on affect state:
| Mode | Trigger |
|---|---|
normal | Default |
scaffolding | Learner reports high anxiety (confidence = 1) |
lighter | Learner reports fatigue (energy = 1), frustration, or boredom (negative affect with low satisfaction) |
Authentication
OAuth 2.1 with PKCE. Learners register and authenticate through a built-in flow:
GET /.well-known/oauth-authorization-serverโ server metadataGET /authorizeโ registration/login pagePOST /tokenโ exchange authorization code for JWT access + refresh tokens- Bearer token required on
/mcp - Rate limiting on auth (10/min), registration (5/min), and MCP (60/min) endpoints
Architecture
main.go HTTP server, MCP handler, OAuth, scheduler startup
โโโ auth/ OAuth 2.1 server, JWT middleware, PKCE, rate limiter
โโโ algorithms/ BKT, FSRS, IRT, KST, PFA + thresholds + BKT info-gain (all with tests)
โ โโโ bkt.go / fsrs.go / irt.go / kst.go / pfa.go Five core learning-science algorithms
โ โโโ thresholds.go Unified 0.85 mastery threshold (REGULATION_THRESHOLD)
โ โโโ bkt_info_gain.go Binary entropy + info-gain helpers for [4] concept selector
โโโ engine/
โ โโโ alert.go Learning + metacognitive alert computation
โ โโโ router.go Legacy activity routing with priority-based alert handling
โ โโโ metacognition.go Autonomy score, mirror detection, tutor mode
โ โโโ motivation.go Brief selection + composition (6 kinds, Hidi-Renninger phase)
โ โโโ scheduler.go Cron jobs: critical alerts, reviews, queued nudges, cleanup
โ โโโ olm.go / olm_graph.go Open Learner Model snapshot (per-concept + global)
โ โโโ action_selector.go [5] Pure activity-type selection (PRACTICE / DEBUG_MISCONCEPTION / โฆ)
โ โโโ concept_selector.go [4] Pure phase-aware concept selection (info-gain, FSRS overdue, max-entropy)
โ โโโ gate.go [3] Pure hygiene gate (anti-repeat, session-budget, no-fringe escape)
โ โโโ phase_fsm.go [2] Pure phase FSM (DIAGNOSTIC โ INSTRUCTION โ MAINTENANCE)
โ โโโ phase_config.go [2] Tunable thresholds (ฮH, N_diagnostic_max, anti-repeat window, โฆ)
โ โโโ orchestrator.go [2] Impure coordinator wiring [4]โ[5]โ[3] with FSM transitions
โโโ models/
โ โโโ learner.go Learner, ConceptState, Interaction, RefreshToken
โ โโโ domain.go AlertType, Activity, KnowledgeSpace, Domain (+ phase fields, value framings)
โ โโโ metacognition.go AffectState, CalibrationRecord, MirrorMessage, AutonomyMetrics
โ โโโ motivation.go MotivationBrief, RecapBrief, WebhookQueueItem, interest phases
โ โโโ regulation.go Phase enum (DIAGNOSTIC / INSTRUCTION / MAINTENANCE)
โโโ db/
โ โโโ store.go SQLite persistence: learners, domains, concepts, interactions
โ โโโ metacognition.go Affect, calibration, transfer, autonomy queries
โ โโโ misconceptions.go Misconception aggregation and status tracking
โ โโโ motivation_queries.go Brief-engine signals (failures, session counts, self-init ratio)
โ โโโ implementation_intentions.go If-then commitments (Gollwitzer)
โ โโโ webhook_queue.go LLM-authored nudge queue with dequeue/dedup/expire
โ โโโ goal_relevance.go Goal-relevance vector storage + staleness checks
โ โโโ phase.go Phase transitions, action history, anti-repeat queries
โ โโโ schema.sql Table definitions (embedded)
โ โโโ migrations.go Idempotent migrations for existing databases
โโโ tools/ 30 MCP tool handlers + system prompt + flag-gated appendices
Language conventions
The codebase mixes English and French on purpose. The split is along a single axis: who reads the string?
- English โ everything developers and operators read: source code,
identifiers, comments, log messages, error returns surfaced to clients/CI,
this README and every doc under
docs/. Keepslogcalls, panics, and Go errors in English. - French โ everything the learner ends up seeing, directly or via the
LLM's routing/prompting layer:
- MCP tool descriptions and
jsonschemaparameter descriptions (tools/*.go) โ the LLM uses these to choose which tool to call, and paraphrases them back to the learner. - The system prompt (
tools/prompt.go). - OLM (open learner model) messages (
engine/olm.go). - Rationales the orchestrator surfaces to the learner
(
engine/orchestrator.go). - DB-stored literal strings that flow through to learner-facing UI
(
db/store.go).
- MCP tool descriptions and
When in doubt: if a string is destined for the learner, write it in correct
French with full diacritics (รฉ, รจ, ร , รง, รด, รฎ, โฆ). The lint test
in tools/registration_test.go enforces diacritic consistency across tool
descriptions; widen it if you add new learner-facing surfaces.
Running
Setup workflow
The setup has two phases: deploy the server, then connect a client.
1. Deploy the server
1.1 โ Build and start
# Required
export JWT_SECRET="$(openssl rand -base64 32)"
# Optional
export PORT=3000 # default: 3000
export DB_PATH=./data/runtime.db # default: ./data/runtime.db
export BASE_URL=https://your.domain # public origin, no trailing slash
export LOG_LEVEL=debug # debug | info | warn | error
go build -o tutor-mcp && ./tutor-mcp
JWT_SECRETmust be a base64-encoded value (32 random bytes recommended). Useopenssl rand -base64 32to generate one โ a plain string will be rejected.
For real use, put the runtime behind a public reverse proxy with TLS โ see Server Configuration.
1.2 โ Verify
curl $BASE_URL/health # โ {"status":"ok"}
No manual user registration is needed at this stage. Accounts are created on demand during the first client connection (see below).
2. Connect a client
The runtime exposes a single MCP endpoint: $BASE_URL/mcp. Add it as a custom connector in any MCP-compatible client.
Prerequisite for web UIs. Claude.ai, ChatGPT, Le Chat and Gemini reach your server from their own cloud, not from your browser โ they require a publicly reachable HTTPS URL. http://localhost will not work. Put the runtime behind a public reverse proxy of your choice (Caddy, Nginx, Traefik, Cloudflare Tunnel, โฆ) with a valid TLS certificate. The steps below assume https://your.domain as the public origin.
CLI clients running on the same machine as the runtime can use http://localhost:3000 directly โ see the Advanced โ CLI clients section below.
First-time login. When a client connects for the first time, the runtime starts an OAuth 2.1 + PKCE flow with dynamic client registration (no client ID or secret to copy by hand). The client opens the runtime's /authorize page in a new tab โ click "Create one" to register (email + password) on the very first connection, or log in if the account already exists. Subsequent launches reuse the issued tokens and re-authenticate silently.
Claude (claude.ai)
Custom connectors are available on Pro, Max, Team and Enterprise plans (Free is limited to one connector).
- Open
claude.aiโ Settings (or Customize) โ Connectors. - Click the + button next to Connectors.
- Fill in:
- Name:
Tutor MCP - Remote MCP server URL:
https://your.domain/mcp
- Name:
- Click Add, then complete the OAuth login when Claude prompts you.
Reference: Anthropic โ Get started with custom connectors.
ChatGPT
Custom MCP connectors require Developer Mode and are available on Plus, Pro, Team, Enterprise and Edu plans (not Free).
- Open
chatgpt.comโ profile picture โ Settings โ Connectors. - Open Advanced at the bottom and toggle Developer mode on.
- Back in Settings โ Connectors, click Create (or Add new connector).
- Fill in:
- Name:
Tutor MCP - Description:
Adaptive learning brain (BKT/FSRS/IRT/PFA/KST) - MCP server URL:
https://your.domain/mcp
- Name:
- Click Create and complete the OAuth login. Write actions still require manual confirmation in chat.
Reference: OpenAI โ Developer mode and MCP apps in ChatGPT.
Le Chat (Mistral)
- Open
chat.mistral.aiโ Connectors. - Click + Add Connector and switch to the Custom MCP Connector tab.
- Fill in:
- Connector name:
tutor_mcp(no spaces or special characters) - Server URL:
https://your.domain/mcp - Description (optional)
- Connector name:
- Click Connect. Le Chat auto-detects OAuth 2.1 with dynamic registration โ complete the login when prompted.
Reference: Mistral โ Configuring a Custom Connector.
Gemini
As of April 2026, the consumer Gemini web app (gemini.google.com) does not yet expose a self-serve UI to add a custom MCP server. Two paths are available today:
- Gemini Enterprise (Google Cloud Console) โ register the runtime as a custom MCP server data store. The runtime uses the StreamableHTTP transport, which is the one Gemini Enterprise supports.
- Gemini CLI โ see MCP servers with Gemini CLI.
Watch Google's release notes for native consumer-app support.
Advanced โ CLI clients
Claude Code
Add a .mcp.json in your project root (or ~/.claude/mcp.json for global use):
{
"mcpServers": {
"tutor-mcp": {
"type": "http",
"url": "http://localhost:3000/mcp"
}
}
}
Claude Code runs on the same machine as the server, so http://localhost:3000/mcp works directly โ no public HTTPS endpoint required. For a remote runtime, replace the URL with https://your.domain/mcp.
Capacity & Sizing
This implementation is intentionally single-tenant, single-node โ an open-source brick meant to be self-hosted for yourself, a small group, or a modest organisation. No cluster, no multi-writer: SQLite + in-process scheduler.
The figures below include a safety buffer (~50%) against the theoretical limits. Beyond that, you'd need to switch to Postgres + an externalised scheduler.
| Profile | Active learners / day | Registered learners | Usage |
|---|---|---|---|
| Personal | 1 | 1โ5 | individual use |
| Small group (family, team) | 1โ10 | up to 30 | regular use |
| Classroom / workshop | 10โ50 | up to 150 | facilitated sessions |
| Small organisation | 50โ200 | up to 600 | sustained load |
Recommended hard ceiling: ~200 concurrent active learners. Beyond that, the scheduler's 30-minute tick and SQLite's serialised writes (WAL = single writer) become the limiting factor.
Server Configuration
Minimum (personal use or small group โค10)
- CPU: 1 vCPU
- RAM: 512 MB
- Disk: 2 GB SSD (binary ~15 MB + DB that grows slowly)
- OS: modern Linux (Debian 12+, Ubuntu 22.04+, Alpine)
- Network: outbound Internet for Discord webhooks
- Examples: Raspberry Pi 4, โฌ5/month VPS, LXC container
Recommended (up to 200 active learners)
- CPU: 2 vCPU
- RAM: 2 GB
- Disk: 20 GB SSD (the
interactionstable grows by a few KB per learner per day) - OS: modern Linux with systemd
- Reverse proxy: Caddy, Nginx, or Traefik for TLS
- Backup: daily snapshot of
data/runtime.db(in WAL mode: copyruntime.db+runtime.db-walor usesqlite3 .backup)
Idle footprint
- Go binary: ~15 MB on disk, ~30 MB RSS
- SQLite WAL database: ~10 MB initial, +~50 KB per active learner per month
- No external dependencies: no Redis, no broker, no second process โ everything fits in the binary and the
.dbfile
Tech Stack
- Go 1.25 with the official MCP Go SDK
- SQLite (via modernc.org/sqlite โ pure Go, no CGO)
- JWT for access tokens, bcrypt for passwords
- robfig/cron for background scheduling
- Wide test coverage across the five algorithms, the regulation pipeline (action / concept / gate / phase-FSM / orchestrator), the alert engine, motivation selection, misconception aggregation, OLM snapshots, goal-relevance staleness, and schema migrations
Operations
For database backup, restore, off-host copy and service control recipes, see OPERATIONS.md. Single-user systemd-user setup is documented end-to-end.
Roadmap
The current focus is the alpha-to-beta path. Active priorities are tracked on the issue tracker, labelled p0 (urgent), p1 (this sprint), p2 (when convenient).
Three algorithmic refinements are deferred to a later release โ none block daily use:
- #48 PFA fidelity to Pavlik 2009 โ sign of ฯ, ฮฒ intercept, decay term
- #49 IRT statistical robustness โ EAP/MAP prior to replace pure MLE
- #52 FSRS sub-day intervals โ hour-granularity Learning/Relearning steps
The full CHANGELOG.md tracks what has shipped.
Contributing
Contributions are welcome. The project is single-author maintained, so small focused changes land faster than large refactors. See CONTRIBUTING.md for the workflow (fork, branch from staging, conventional commits, test plan in the PR), the language conventions (English in code, French in learner-facing strings), and the in-scope / out-of-scope policy.
For something not yet on the issue tracker, please open an issue before opening a PR (typo fixes excepted).
Security
Please do not open a public issue for security vulnerabilities. See SECURITY.md for the private disclosure channels (GitHub private vulnerability advisory, or email) and the operator hardening checklist.
Acknowledgments
Tutor MCP stands on the shoulders of decades of cognitive-science research and a handful of open-source libraries:
- BKT โ Corbett & Anderson, Knowledge Tracing: Modeling the Acquisition of Procedural Knowledge (1995)
- FSRS โ Open-Spaced-Repetition team, FSRS algorithm v4
- IRT โ Lord & Novick, Statistical Theories of Mental Test Scores (1968); 2-parameter logistic model
- PFA โ Pavlik et al., Performance Factors Analysis โ A New Alternative to Knowledge Tracing (2009)
- KST โ Falmagne & Doignon, Learning Spaces (2011)
- Hidi-Renninger interest phases โ The Four-Phase Model of Interest Development (2006), used by the motivation engine
Runtime dependencies:
- modelcontextprotocol/go-sdk โ official MCP server SDK
- modernc.org/sqlite โ pure-Go SQLite driver, no CGO required
- robfig/cron โ background scheduling
- golang-jwt/jwt โ JWT signing and validation
License
This project is licensed under the MIT License โ you can use, copy, modify, distribute, and sublicense it, including for commercial purposes, as long as the copyright notice and license text are preserved.
Author
Arnaud Guiovanna
- Website: aguiovanna.fr
- GitHub: @ArnaudGuiovanna
