@skeleton-watches/tutor-mcp
v0.1.4
Published
MCP server, tuteur d'exos d'entretien (skeleton-projects, watches).
Readme
tutor-mcp
Serveur MCP tuteur d'exos pour les *-watches-skeleton. Il aide le candidat à
raisonner sans coder à sa place, et log toutes les interactions pour que le recruteur
puisse les relire en post-mortem.
Le serveur se configure par exo via un fichier .tutor/config.json posé à la racine
du projet : il y lit la liste des TODO : les hints en cascade et la sandbox autorisée.
La suite de ce document en décrit le format.
Build
npm install
npm run buildProduit dist/server.js (ESM, shebang #!/usr/bin/env node).
Capabilities exposées
Tools
| Tool | Comportement |
|--------------------------------|-----------------------------------------------------------------------------------------------------------------------|
| list_todos | Liste les TODO de l'exo (depuis .tutor/config.json + scan source). Renvoie id, file, line, summary. |
| explain_test(name) | Cherche un test par substring ou chemin, retourne l'extrait. Le LLM doit l'expliquer en français sans recopier la sol.|
| run_tests([filter]) | Lance la commande de test configurée, parse pass/fail (Jest, Vitest, Surefire, Django, pytest). Tail de la sortie. |
| get_hint(todo_id, level) | Hint progressif (1 = orientation, 2 = approche, 3 = pseudo-code). Toujours logé. |
| review_my_code(file) | Charge le code écrit + injecte une consigne anti-prescription. Pose des questions, ne récrit pas. |
| get_api_contract([endpoint]) | Sert tout ou partie d'API.md (filtrage par section ou substring). |
Resources
Lecture-seule, soumises à la sandbox :
README.mdde l'exoAPI.md(chemin viaapi_contract_path, par défaut../API.md).tutor/config.json- Tous les
test_fileslistés dans la config
Prompts
tutor, pose la posture (aide-au-raisonnement, pas livreur de code). À charger en système au début de la conversation.
Configuration par exo
Chaque skeleton porte un .tutor/config.json ; voir config.schema.json pour le schéma complet.
{
"exo": "react-watches",
"stack": "react",
"test_command": "npm test --silent",
"test_filter_flag": "--",
"api_contract_path": "../API.md",
"todos": [
{
"id": "format_price_eur",
"title": "formatPriceEur, formatage prix",
"files": ["src/utils/price.ts"],
"test_files": ["src/utils/price.test.ts"],
"hints": {
"1": "Lis les exemples du JSDoc...",
"2": "Le piège : Intl.NumberFormat insère un espace insécable...",
"3": "Pseudo-code : amount.toString().replace(/\\B(?=(\\d{3})+(?!\\d))/g, ' ') + ' €'"
}
}
],
"sandbox": {
"allowed_paths": ["src", "README.md", "../API.md", ".tutor"],
"denied_patterns": ["node_modules", "-solution", "/.git/"]
}
}Lancer le serveur
Le serveur lit la config depuis process.cwd() par défaut, ou le chemin passé via :
- variable d'env :
TUTOR_PROJECT_ROOT=/chemin/vers/skeleton - flag CLI :
tutor-mcp --project /chemin/vers/skeleton
Côté agent (mcp.json)
{
"mcpServers": {
"tutor": {
"command": "node",
"args": ["/abs/chemin/tutor-mcp/dist/server.js"],
"env": { "TUTOR_PROJECT_ROOT": "/abs/chemin/<exo>-skeleton" }
}
}
}Une fois publié sur npm :
{
"mcpServers": {
"tutor": {
"command": "npx",
"args": ["-y", "@skeleton-watches/tutor-mcp"],
"cwd": "/abs/chemin/<exo>-skeleton"
}
}
}Sandbox
Sandbox.resolveSafe filtre tout chemin demandé via :
- Allowlist :
config.sandbox.allowed_paths(préfixes relatifs au project root). Hors-projet (..) seulement si explicitement listé (ex.../API.md). - Denylist :
config.sandbox.denied_patterns.node_modules,-solution,/.git/etc. sont interdits par défaut. Les*-solution/ne sont jamais lus même si l'agent essaie.
Logs
<project>/.tutor/session.log.jsonl, une ligne par appel d'outil :
{"ts":"2026-05-08T10:14:22.123Z","event":"session_start","exo":"react-watches","stack":"react"}
{"ts":"2026-05-08T10:14:55.001Z","tool":"list_todos","count":4}
{"ts":"2026-05-08T10:15:10.444Z","tool":"get_hint","todo_id":"format_price_eur","level":1,"hint_excerpt":"Lis les exemples..."}
{"ts":"2026-05-08T10:16:02.700Z","tool":"run_tests","cmd":"npm test --silent","exit":1,"passing":6,"failing":17}Pour le post-mortem recruteur :
- ratio
level1 / 2 / 3 par TODO → autonomie - séquence des appels (a-t-il itéré ou tout de suite réclamé level 3 ?)
- temps
get_hint(level=3)→run_testsréussi → vitesse d'intégration
Pour le recruteur : rapport post-entretien
Côté recruteur uniquement (jamais distribué au candidat) : un script Node qui
agrège .tutor/session.log.jsonl et produit un Markdown autonome (stats + timeline +
prompt d'évaluation calibré + log brut).
npm run report -- ../react-watches-skeleton > rapport.md
# Puis pipe vers le LLM de ton choix :
npm run report -- ../react-watches-skeleton | clip # claude.ai / ChatGPT
npm run report -- ../react-watches-skeleton | claude -p # Claude Code headlessTout vit dans recruiter/, un dossier absent du paquet npm : il est exclu par
package.json#files, donc un candidat qui fait npx -y @skeleton-watches/tutor-mcp
ne le reçoit jamais. Le détail de la génération du rapport est documenté dans le
dépôt du recruteur, pas ici.
Limites connues
explain_testest un grep + tronquage, pas un parser AST, peut louper des tests aux noms ambigus.- Pas de transport SSE (volontaire,
stdiocouvre 100% des usages locaux). - Le rapport recruteur n'appelle pas l'API directement (volontaire, pas de dépendance Anthropic, pas de clé à gérer, prompt auditable avant envoi).
