@lyrra/mcp-server 1.1.3 → 1.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (105) hide show
  1. package/README.md +59 -242
  2. package/dist/auth-session.js +160 -0
  3. package/dist/eduflow-block-docs.js +438 -0
  4. package/dist/index.js +179 -12
  5. package/dist/lyrra-http.js +80 -0
  6. package/dist/openapi-parse.js +61 -0
  7. package/dist/register-eduflow-block-tools.js +31 -0
  8. package/package.json +36 -13
  9. package/Dockerfile +0 -16
  10. package/dist/client.d.ts +0 -23
  11. package/dist/client.d.ts.map +0 -1
  12. package/dist/client.js +0 -92
  13. package/dist/client.js.map +0 -1
  14. package/dist/config.d.ts +0 -8
  15. package/dist/config.d.ts.map +0 -1
  16. package/dist/config.js +0 -8
  17. package/dist/config.js.map +0 -1
  18. package/dist/http-server.d.ts +0 -8
  19. package/dist/http-server.d.ts.map +0 -1
  20. package/dist/http-server.js +0 -481
  21. package/dist/http-server.js.map +0 -1
  22. package/dist/index.d.ts +0 -3
  23. package/dist/index.d.ts.map +0 -1
  24. package/dist/index.js.map +0 -1
  25. package/dist/resources/block-types.d.ts +0 -318
  26. package/dist/resources/block-types.d.ts.map +0 -1
  27. package/dist/resources/block-types.js +0 -297
  28. package/dist/resources/block-types.js.map +0 -1
  29. package/dist/resources/flow-schema.d.ts +0 -147
  30. package/dist/resources/flow-schema.d.ts.map +0 -1
  31. package/dist/resources/flow-schema.js +0 -143
  32. package/dist/resources/flow-schema.js.map +0 -1
  33. package/dist/server-factory.d.ts +0 -8
  34. package/dist/server-factory.d.ts.map +0 -1
  35. package/dist/server-factory.js +0 -82
  36. package/dist/server-factory.js.map +0 -1
  37. package/dist/tools/admin.d.ts +0 -265
  38. package/dist/tools/admin.d.ts.map +0 -1
  39. package/dist/tools/admin.js +0 -118
  40. package/dist/tools/admin.js.map +0 -1
  41. package/dist/tools/ai-designer.d.ts +0 -297
  42. package/dist/tools/ai-designer.d.ts.map +0 -1
  43. package/dist/tools/ai-designer.js +0 -89
  44. package/dist/tools/ai-designer.js.map +0 -1
  45. package/dist/tools/analytics.d.ts +0 -95
  46. package/dist/tools/analytics.d.ts.map +0 -1
  47. package/dist/tools/analytics.js +0 -44
  48. package/dist/tools/analytics.js.map +0 -1
  49. package/dist/tools/auth.d.ts +0 -61
  50. package/dist/tools/auth.d.ts.map +0 -1
  51. package/dist/tools/auth.js +0 -36
  52. package/dist/tools/auth.js.map +0 -1
  53. package/dist/tools/blocks.d.ts +0 -457
  54. package/dist/tools/blocks.d.ts.map +0 -1
  55. package/dist/tools/blocks.js +0 -173
  56. package/dist/tools/blocks.js.map +0 -1
  57. package/dist/tools/connections.d.ts +0 -173
  58. package/dist/tools/connections.d.ts.map +0 -1
  59. package/dist/tools/connections.js +0 -81
  60. package/dist/tools/connections.js.map +0 -1
  61. package/dist/tools/eduflow.d.ts +0 -409
  62. package/dist/tools/eduflow.d.ts.map +0 -1
  63. package/dist/tools/eduflow.js +0 -139
  64. package/dist/tools/eduflow.js.map +0 -1
  65. package/dist/tools/participants.d.ts +0 -221
  66. package/dist/tools/participants.d.ts.map +0 -1
  67. package/dist/tools/participants.js +0 -70
  68. package/dist/tools/participants.js.map +0 -1
  69. package/dist/tools/presentation.d.ts +0 -233
  70. package/dist/tools/presentation.d.ts.map +0 -1
  71. package/dist/tools/presentation.js +0 -57
  72. package/dist/tools/presentation.js.map +0 -1
  73. package/dist/tools/projects.d.ts +0 -131
  74. package/dist/tools/projects.d.ts.map +0 -1
  75. package/dist/tools/projects.js +0 -55
  76. package/dist/tools/projects.js.map +0 -1
  77. package/dist/tools/resources.d.ts +0 -93
  78. package/dist/tools/resources.d.ts.map +0 -1
  79. package/dist/tools/resources.js +0 -37
  80. package/dist/tools/resources.js.map +0 -1
  81. package/dist/tools/store.d.ts +0 -125
  82. package/dist/tools/store.d.ts.map +0 -1
  83. package/dist/tools/store.js +0 -66
  84. package/dist/tools/store.js.map +0 -1
  85. package/mcp-config.example.json +0 -14
  86. package/src/client.ts +0 -106
  87. package/src/config.ts +0 -7
  88. package/src/http-server.ts +0 -591
  89. package/src/index.ts +0 -23
  90. package/src/resources/block-types.ts +0 -298
  91. package/src/resources/flow-schema.ts +0 -148
  92. package/src/server-factory.ts +0 -109
  93. package/src/tools/admin.ts +0 -128
  94. package/src/tools/ai-designer.ts +0 -97
  95. package/src/tools/analytics.ts +0 -49
  96. package/src/tools/auth.ts +0 -39
  97. package/src/tools/blocks.ts +0 -186
  98. package/src/tools/connections.ts +0 -83
  99. package/src/tools/eduflow.ts +0 -150
  100. package/src/tools/participants.ts +0 -77
  101. package/src/tools/presentation.ts +0 -61
  102. package/src/tools/projects.ts +0 -61
  103. package/src/tools/resources.ts +0 -41
  104. package/src/tools/store.ts +0 -67
  105. package/tsconfig.json +0 -19
@@ -1,97 +0,0 @@
1
- import { z } from 'zod';
2
- import { LyrraClient } from '../client.js';
3
- import { client as defaultClient } from '../client.js';
4
-
5
- export function createAiDesignerTools(c: LyrraClient) {
6
- return {
7
- ai_generate_plan: {
8
- description: 'Générer automatiquement un plan/structure de parcours pédagogique à partir d\'un sujet et d\'objectifs. L\'IA propose une liste de blocs organisés.',
9
- inputSchema: z.object({
10
- flowId: z.string().uuid().describe('ID du parcours'),
11
- topic: z.string().describe('Sujet du parcours (ex: "Les fractions en CM2")'),
12
- targetAudience: z.string().optional().describe('Public cible (ex: "Élèves de CM2, 9-10 ans")'),
13
- objectives: z.string().optional().describe('Objectifs pédagogiques souhaités'),
14
- duration: z.string().optional().describe('Durée estimée (ex: "30 minutes")'),
15
- context: z.string().optional().describe('Contexte additionnel pour l\'IA'),
16
- }),
17
- handler: async ({ flowId, ...data }: any) => {
18
- return c.post(`/flows/${flowId}/chatbot/generate-plan`, data, 'eduflow');
19
- },
20
- },
21
-
22
- ai_generate_block: {
23
- description: 'Générer le contenu d\'un bloc spécifique avec l\'IA. Peut créer du texte enrichi, des quiz, des évaluations, des graphiques, etc.',
24
- inputSchema: z.object({
25
- flowId: z.string().uuid().describe('ID du parcours'),
26
- blockType: z.string().describe('Type de bloc à générer (text, quiz, evaluation, chart, timeline, etc.)'),
27
- instructions: z.string().describe('Instructions pour le contenu du bloc (ex: "Créer un quiz de 5 questions sur les fractions")'),
28
- context: z.string().optional().describe('Contexte du parcours pour l\'IA'),
29
- }),
30
- handler: async ({ flowId, ...data }: any) => {
31
- return c.post(`/flows/${flowId}/chatbot/generate-block`, data, 'eduflow');
32
- },
33
- },
34
-
35
- ai_generate_presentation: {
36
- description: 'Générer automatiquement la page de présentation du parcours (description, objectifs affichés, image, structure visible).',
37
- inputSchema: z.object({
38
- flowId: z.string().uuid().describe('ID du parcours'),
39
- style: z.string().optional().describe('Style de présentation souhaité'),
40
- }),
41
- handler: async ({ flowId, ...data }: any) => {
42
- return c.post(`/flows/${flowId}/chatbot/generate-presentation`, data, 'eduflow');
43
- },
44
- },
45
-
46
- ai_generate_objectives: {
47
- description: 'Générer automatiquement les objectifs pédagogiques du parcours à partir du contenu existant.',
48
- inputSchema: z.object({
49
- flowId: z.string().uuid().describe('ID du parcours'),
50
- }),
51
- handler: async ({ flowId }: { flowId: string }) => {
52
- return c.post(`/flows/${flowId}/chatbot/generate-objectives`, {}, 'eduflow');
53
- },
54
- },
55
-
56
- ai_save_objectives: {
57
- description: 'Sauvegarder les objectifs pédagogiques (globaux et par étape) sur le parcours.',
58
- inputSchema: z.object({
59
- flowId: z.string().uuid().describe('ID du parcours'),
60
- globalObjective: z.string().describe('Objectif global du parcours'),
61
- steps: z.array(z.object({
62
- id: z.string().describe('ID de l\'étape'),
63
- title: z.string().describe('Titre de l\'objectif'),
64
- description: z.string().optional().describe('Description détaillée'),
65
- blockId: z.string().optional().describe('Bloc associé à cet objectif'),
66
- })).describe('Liste des objectifs par étape'),
67
- }),
68
- handler: async ({ flowId, ...objectives }: any) => {
69
- return c.post(`/flows/${flowId}/chatbot/save-objectives`, objectives, 'eduflow');
70
- },
71
- },
72
-
73
- ai_chat_message: {
74
- description: 'Envoyer un message au designer IA pour discuter de la conception du parcours, poser des questions, demander des modifications.',
75
- inputSchema: z.object({
76
- flowId: z.string().uuid().describe('ID du parcours'),
77
- message: z.string().describe('Message à envoyer à l\'IA designer'),
78
- }),
79
- handler: async ({ flowId, message }: { flowId: string; message: string }) => {
80
- return c.post(`/flows/${flowId}/chatbot/message`, { message }, 'eduflow');
81
- },
82
- },
83
-
84
- ai_get_conversation: {
85
- description: 'Récupérer l\'historique de conversation avec le designer IA pour un parcours.',
86
- inputSchema: z.object({
87
- flowId: z.string().uuid().describe('ID du parcours'),
88
- }),
89
- handler: async ({ flowId }: { flowId: string }) => {
90
- return c.get(`/flows/${flowId}/chatbot/conversation`, 'eduflow');
91
- },
92
- },
93
- };
94
- }
95
-
96
- // Backward compatibility for stdio mode
97
- export const aiDesignerTools = createAiDesignerTools(defaultClient);
@@ -1,49 +0,0 @@
1
- import { z } from 'zod';
2
- import { LyrraClient } from '../client.js';
3
- import { client as defaultClient } from '../client.js';
4
-
5
- export function createAnalyticsTools(c: LyrraClient) {
6
- return {
7
- analytics_overview: {
8
- description: 'Tableau de bord global : nombre de projets, parcours EduFlow, apprenants, crédits utilisés, avec évolution par période.',
9
- inputSchema: z.object({
10
- period: z.enum(['day', 'week', 'month', 'semester', 'year']).optional().describe('Période d\'analyse (défaut: month)'),
11
- }),
12
- handler: async ({ period }: { period?: string }) => {
13
- const query = period ? `?period=${period}` : '';
14
- return c.get(`/analytics/overview${query}`);
15
- },
16
- },
17
-
18
- analytics_flow_learners: {
19
- description: 'Statistiques des apprenants pour un parcours spécifique : progression, scores, temps passé par apprenant.',
20
- inputSchema: z.object({
21
- flowId: z.string().uuid().describe('ID du parcours'),
22
- }),
23
- handler: async ({ flowId }: { flowId: string }) => {
24
- return c.get(`/analytics/flow/${flowId}/learners`);
25
- },
26
- },
27
-
28
- analytics_flow_dashboard: {
29
- description: 'Dashboard analytics complet d\'un parcours : vue d\'ensemble, taux de complétion, blocs les plus/moins visités.',
30
- inputSchema: z.object({
31
- flowId: z.string().uuid().describe('ID du parcours'),
32
- }),
33
- handler: async ({ flowId }: { flowId: string }) => {
34
- return c.get(`/flows/${flowId}/analytics`, 'eduflow');
35
- },
36
- },
37
-
38
- analytics_my_stats: {
39
- description: 'Mes statistiques personnelles : projets créés, parcours créés, temps total, crédits utilisés.',
40
- inputSchema: z.object({}),
41
- handler: async () => {
42
- return c.get('/analytics/my-stats');
43
- },
44
- },
45
- };
46
- }
47
-
48
- // Backward compatibility for stdio mode
49
- export const analyticsTools = createAnalyticsTools(defaultClient);
package/src/tools/auth.ts DELETED
@@ -1,39 +0,0 @@
1
- import { z } from 'zod';
2
- import { LyrraClient } from '../client.js';
3
- import { client as defaultClient } from '../client.js';
4
-
5
- export function createAuthTools(c: LyrraClient) {
6
- return {
7
- auth_login: {
8
- description: 'Se connecter à LYRRA Studio avec email et mot de passe. ⚠️ NE PAS utiliser si LYRRA_CLIENT_SECRET est configuré — l\'authentification est alors automatique via API Key. Utiliser uniquement si aucune clé API n\'est définie.',
9
- inputSchema: z.object({
10
- email: z.string().email().describe('Adresse email du compte'),
11
- password: z.string().describe('Mot de passe'),
12
- }),
13
- handler: async ({ email, password }: { email: string; password: string }) => {
14
- const result = await c.post('/auth/login', { email, password });
15
- if (result.token) c.setToken(result.token);
16
- return result;
17
- },
18
- },
19
-
20
- auth_get_profile: {
21
- description: 'Récupérer le profil de l\'utilisateur connecté (nom, email, crédits, rôle, institution, etc.).',
22
- inputSchema: z.object({}),
23
- handler: async () => {
24
- return c.get('/auth/me');
25
- },
26
- },
27
-
28
- auth_list_api_keys: {
29
- description: 'Lister toutes les clés API de l\'utilisateur connecté.',
30
- inputSchema: z.object({}),
31
- handler: async () => {
32
- return c.get('/api-keys');
33
- },
34
- },
35
- };
36
- }
37
-
38
- // Backward compatibility for stdio mode
39
- export const authTools = createAuthTools(defaultClient);
@@ -1,186 +0,0 @@
1
- import { z } from 'zod';
2
- import { LyrraClient } from '../client.js';
3
- import { client as defaultClient } from '../client.js';
4
-
5
- const blockContentSchema = z.record(z.any()).describe(
6
- 'Contenu du bloc (structure variable selon le type). Utiliser la resource lyrra://block-types pour connaître la structure attendue.'
7
- );
8
-
9
- export function createBlocksTools(c: LyrraClient) {
10
- return {
11
- block_list_types: {
12
- description: `Lister tous les types de blocs disponibles avec leur documentation complète.
13
- Types disponibles : text, audio, quiz, video, pdf, image, loop, evaluation, split, merge, timer, start, end, chart, timeline, dys_reader, dys_image_zones, dys_reading_practice, dys_clock, glossary, subflow, mindmap, form, certification, email, voice_assessment, presentation, browser, self_assessment.
14
- Utiliser plutôt la resource lyrra://block-types pour la documentation complète.`,
15
- inputSchema: z.object({}),
16
- handler: async () => {
17
- return {
18
- types: [
19
- { type: 'start', name: 'Début', description: 'Point de départ du parcours. Un seul par flux.' },
20
- { type: 'end', name: 'Fin', description: 'Point de fin du parcours. Peut avoir plusieurs fins.' },
21
- { type: 'text', name: 'Texte', description: 'Contenu textuel/HTML enrichi. TOUJOURS utiliser format: "html".' },
22
- { type: 'audio', name: 'Audio', description: 'Intègre un projet audio LYRRA avec texte synchronisé.' },
23
- { type: 'video', name: 'Vidéo', description: 'Intègre une vidéo (URL YouTube/Vimeo ou fichier uploadé).' },
24
- { type: 'pdf', name: 'PDF', description: 'Affiche un document PDF intégré.' },
25
- { type: 'image', name: 'Image', description: 'Affiche une image avec légende optionnelle.' },
26
- { type: 'quiz', name: 'Quiz', description: 'Question QCM/vrai-faux/ordonnancement avec branchement conditionnel.' },
27
- { type: 'evaluation', name: 'Évaluation', description: 'Évaluation complète avec plusieurs questions et scoring.' },
28
- { type: 'form', name: 'Formulaire', description: 'Formulaire avec champs personnalisés et logique conditionnelle.' },
29
- { type: 'chart', name: 'Graphique', description: 'Graphique interactif (bar, line, pie, scatter, radar, area, doughnut).' },
30
- { type: 'timeline', name: 'Frise chronologique', description: 'Frise avec événements datés.' },
31
- { type: 'mindmap', name: 'Carte mentale', description: 'Carte mentale interactive avec nœuds et liens.' },
32
- { type: 'glossary', name: 'Glossaire', description: 'Liste de termes et définitions.' },
33
- { type: 'split', name: 'Embranchement', description: 'Sépare le flux en branches parallèles.' },
34
- { type: 'merge', name: 'Fusion', description: 'Rejoint les branches parallèles.' },
35
- { type: 'loop', name: 'Boucle', description: 'Permet de répéter une séquence de blocs.' },
36
- { type: 'timer', name: 'Minuteur', description: 'Bloc avec temps limité, pause, rappel ou expiration.' },
37
- { type: 'subflow', name: 'Sous-parcours', description: 'Intègre un autre parcours EduFlow comme bloc.' },
38
- { type: 'certification', name: 'Certification', description: 'Génère un certificat PDF personnalisé.' },
39
- { type: 'email', name: 'Email', description: 'Envoie un email automatique (certification, rappel).' },
40
- { type: 'voice_assessment', name: 'Évaluation vocale', description: 'Enregistrement et évaluation de la voix de l\'étudiant.' },
41
- { type: 'self_assessment', name: 'Auto-évaluation', description: 'L\'étudiant s\'évalue lui-même sur des critères définis.' },
42
- { type: 'presentation', name: 'Présentation', description: 'Diaporama avec slides.' },
43
- { type: 'browser', name: 'Navigateur', description: 'Intègre une page web dans un iframe.' },
44
- { type: 'dys_reader', name: 'Lecteur DYS', description: 'Texte adapté pour la dyslexie avec police et espacement spéciaux.' },
45
- { type: 'dys_image_zones', name: 'Zones d\'image DYS', description: 'Image avec zones cliquables pour la dyslexie.' },
46
- { type: 'dys_reading_practice', name: 'Pratique lecture DYS', description: 'Exercice de lecture adapté dyslexie.' },
47
- { type: 'dys_clock', name: 'Horloge DYS', description: 'Exercice de lecture de l\'heure pour la dyscalculie.' },
48
- ],
49
- };
50
- },
51
- },
52
-
53
- block_get: {
54
- description: 'Récupérer les détails complets d\'un bloc (type, contenu, position, conditions, commentaire).',
55
- inputSchema: z.object({
56
- flowId: z.string().uuid().describe('ID du parcours'),
57
- blockId: z.string().uuid().describe('ID du bloc'),
58
- }),
59
- handler: async ({ flowId, blockId }: { flowId: string; blockId: string }) => {
60
- return c.get(`/flows/${flowId}/blocks/${blockId}`, 'eduflow');
61
- },
62
- },
63
-
64
- block_create: {
65
- description: `Créer un nouveau bloc dans un parcours EduFlow. Le bloc est ajouté mais pas encore connecté (utiliser connection_add ensuite).
66
- IMPORTANT pour le type "text" : toujours utiliser content.format = "html" et du HTML dans content.text (pas de Markdown).`,
67
- inputSchema: z.object({
68
- flowId: z.string().uuid().describe('ID du parcours'),
69
- type: z.enum([
70
- 'text', 'audio', 'quiz', 'video', 'pdf', 'image', 'loop', 'evaluation',
71
- 'split', 'merge', 'timer', 'start', 'end', 'chart', 'timeline',
72
- 'dys_reader', 'dys_image_zones', 'dys_reading_practice', 'dys_clock',
73
- 'glossary', 'subflow', 'mindmap', 'form', 'certification', 'email',
74
- 'voice_assessment', 'presentation', 'browser', 'self_assessment',
75
- ]).describe('Type de bloc à créer'),
76
- title: z.string().describe('Titre du bloc'),
77
- content: blockContentSchema,
78
- positionX: z.number().optional().describe('Position X dans l\'éditeur visuel (défaut: 0)'),
79
- positionY: z.number().optional().describe('Position Y dans l\'éditeur visuel (défaut: 0)'),
80
- order: z.number().optional().describe('Ordre d\'affichage'),
81
- }),
82
- handler: async ({ flowId, ...blockData }: any) => {
83
- // Use batch update to add a single block
84
- const flow = await c.get(`/flows/${flowId}`, 'eduflow');
85
- const existingBlocks = flow.blocks || [];
86
- const newBlock = {
87
- id: crypto.randomUUID(),
88
- ...blockData,
89
- positionX: blockData.positionX ?? (existingBlocks.length * 300),
90
- positionY: blockData.positionY ?? 200,
91
- order: blockData.order ?? existingBlocks.length,
92
- };
93
- const allBlocks = [...existingBlocks.map((b: any) => ({
94
- id: b.id, type: b.type, title: b.title,
95
- content: b.content, positionX: b.positionX, positionY: b.positionY,
96
- order: b.order, conditions: b.conditions, comment: b.comment,
97
- })), newBlock];
98
- await c.put(`/flows/${flowId}/blocks`, allBlocks, 'eduflow');
99
- return { success: true, block: newBlock };
100
- },
101
- },
102
-
103
- block_update: {
104
- description: 'Mettre à jour un bloc existant (titre, contenu, position, conditions).',
105
- inputSchema: z.object({
106
- flowId: z.string().uuid().describe('ID du parcours'),
107
- blockId: z.string().uuid().describe('ID du bloc à modifier'),
108
- title: z.string().optional().describe('Nouveau titre'),
109
- content: blockContentSchema.optional(),
110
- positionX: z.number().optional().describe('Nouvelle position X'),
111
- positionY: z.number().optional().describe('Nouvelle position Y'),
112
- order: z.number().optional().describe('Nouvel ordre'),
113
- conditions: z.any().optional().describe('Conditions de branchement'),
114
- comment: z.string().optional().describe('Commentaire créateur'),
115
- }),
116
- handler: async ({ flowId, blockId, ...data }: any) => {
117
- return c.put(`/flows/${flowId}/blocks/${blockId}`, data, 'eduflow');
118
- },
119
- },
120
-
121
- block_batch_update: {
122
- description: 'Mettre à jour plusieurs blocs en une seule opération. Utile pour repositionner ou réorganiser tous les blocs. Envoie aussi les edges (connexions) si fournis.',
123
- inputSchema: z.object({
124
- flowId: z.string().uuid().describe('ID du parcours'),
125
- blocks: z.array(z.object({
126
- id: z.string().describe('ID du bloc'),
127
- type: z.string().describe('Type du bloc'),
128
- title: z.string().describe('Titre'),
129
- content: z.any().describe('Contenu'),
130
- positionX: z.number().optional(),
131
- positionY: z.number().optional(),
132
- order: z.number().optional(),
133
- conditions: z.any().optional(),
134
- comment: z.string().optional(),
135
- })).describe('Liste complète des blocs (REMPLACE tous les blocs existants)'),
136
- edges: z.array(z.object({
137
- id: z.string().describe('ID de la connexion'),
138
- source: z.string().describe('ID du bloc source'),
139
- target: z.string().describe('ID du bloc cible'),
140
- label: z.string().optional(),
141
- })).optional().describe('Connexions entre blocs'),
142
- }),
143
- handler: async ({ flowId, blocks, edges }: any) => {
144
- return c.put(`/flows/${flowId}/blocks`, { blocks, edges: edges || [] }, 'eduflow');
145
- },
146
- },
147
-
148
- block_delete: {
149
- description: 'Supprimer un bloc d\'un parcours. Les connexions liées sont automatiquement supprimées.',
150
- inputSchema: z.object({
151
- flowId: z.string().uuid().describe('ID du parcours'),
152
- blockId: z.string().uuid().describe('ID du bloc à supprimer'),
153
- }),
154
- handler: async ({ flowId, blockId }: { flowId: string; blockId: string }) => {
155
- // Get current flow, remove block, remove related edges, batch update
156
- const flow = await c.get(`/flows/${flowId}`, 'eduflow');
157
- const blocks = (flow.blocks || []).filter((b: any) => b.id !== blockId);
158
- const edges = (flow.edges || []).filter(
159
- (e: any) => e.source !== blockId && e.target !== blockId
160
- );
161
- const mappedBlocks = blocks.map((b: any) => ({
162
- id: b.id, type: b.type, title: b.title,
163
- content: b.content, positionX: b.positionX, positionY: b.positionY,
164
- order: b.order, conditions: b.conditions, comment: b.comment,
165
- }));
166
- await c.put(`/flows/${flowId}/blocks`, { blocks: mappedBlocks }, 'eduflow');
167
- await c.put(`/flows/${flowId}`, { edges }, 'eduflow');
168
- return { success: true, remainingBlocks: blocks.length, remainingEdges: edges.length };
169
- },
170
- },
171
-
172
- block_generate_tts: {
173
- description: 'Générer l\'audio TTS (text-to-speech) pour un bloc texte. L\'audio est attaché au bloc.',
174
- inputSchema: z.object({
175
- flowId: z.string().uuid().describe('ID du parcours'),
176
- blockId: z.string().uuid().describe('ID du bloc'),
177
- }),
178
- handler: async ({ flowId, blockId }: { flowId: string; blockId: string }) => {
179
- return c.post(`/blocks/${blockId}/generate-tts`, { flowId }, 'eduflow');
180
- },
181
- },
182
- };
183
- }
184
-
185
- // Backward compatibility for stdio mode
186
- export const blocksTools = createBlocksTools(defaultClient);
@@ -1,83 +0,0 @@
1
- import { z } from 'zod';
2
- import { LyrraClient } from '../client.js';
3
- import { client as defaultClient } from '../client.js';
4
-
5
- export function createConnectionsTools(c: LyrraClient) {
6
- return {
7
- connection_list: {
8
- description: `Lister toutes les connexions (edges) d'un parcours EduFlow. Chaque connexion relie un bloc source à un bloc cible.
9
- Règles de connexion :
10
- - Le bloc "start" ne peut avoir QUE des connexions sortantes
11
- - Le bloc "end" ne peut avoir QUE des connexions entrantes
12
- - Un bloc "split" doit avoir au moins 2 connexions sortantes
13
- - Un bloc "merge" doit avoir au moins 2 connexions entrantes
14
- - Un bloc "quiz" peut avoir des connexions conditionnelles (branchement selon la réponse)`,
15
- inputSchema: z.object({
16
- flowId: z.string().uuid().describe('ID du parcours'),
17
- }),
18
- handler: async ({ flowId }: { flowId: string }) => {
19
- const flow = await c.get(`/flows/${flowId}`, 'eduflow');
20
- return {
21
- edges: flow.edges || [],
22
- blocks: (flow.blocks || []).map((b: any) => ({ id: b.id, type: b.type, title: b.title })),
23
- };
24
- },
25
- },
26
-
27
- connection_add: {
28
- description: 'Ajouter une connexion entre deux blocs. Le parcours est un graphe orienté : source → target.',
29
- inputSchema: z.object({
30
- flowId: z.string().uuid().describe('ID du parcours'),
31
- source: z.string().describe('ID du bloc source (d\'où part la connexion)'),
32
- target: z.string().describe('ID du bloc cible (où arrive la connexion)'),
33
- label: z.string().optional().describe('Label affiché sur la connexion (ex: "Bonne réponse", "Mauvaise réponse")'),
34
- animated: z.boolean().optional().describe('Animer la connexion visuellement'),
35
- }),
36
- handler: async ({ flowId, source, target, label, animated }: any) => {
37
- const flow = await c.get(`/flows/${flowId}`, 'eduflow');
38
- const edges = flow.edges || [];
39
- const newEdge = {
40
- id: `e-${source}-${target}`,
41
- source,
42
- target,
43
- label: label || '',
44
- animated: animated || false,
45
- };
46
- // Check for duplicate
47
- const exists = edges.some((e: any) => e.source === source && e.target === target);
48
- if (exists) {
49
- return { success: false, error: 'Cette connexion existe déjà' };
50
- }
51
- edges.push(newEdge);
52
- await c.put(`/flows/${flowId}`, { edges }, 'eduflow');
53
- return { success: true, edge: newEdge, totalEdges: edges.length };
54
- },
55
- },
56
-
57
- connection_remove: {
58
- description: 'Supprimer une connexion entre deux blocs.',
59
- inputSchema: z.object({
60
- flowId: z.string().uuid().describe('ID du parcours'),
61
- edgeId: z.string().optional().describe('ID de la connexion à supprimer'),
62
- source: z.string().optional().describe('Alternative : ID du bloc source'),
63
- target: z.string().optional().describe('Alternative : ID du bloc cible'),
64
- }),
65
- handler: async ({ flowId, edgeId, source, target }: any) => {
66
- const flow = await c.get(`/flows/${flowId}`, 'eduflow');
67
- let edges = flow.edges || [];
68
- if (edgeId) {
69
- edges = edges.filter((e: any) => e.id !== edgeId);
70
- } else if (source && target) {
71
- edges = edges.filter((e: any) => !(e.source === source && e.target === target));
72
- } else {
73
- return { success: false, error: 'Fournir edgeId OU source+target' };
74
- }
75
- await c.put(`/flows/${flowId}`, { edges }, 'eduflow');
76
- return { success: true, remainingEdges: edges.length };
77
- },
78
- },
79
- };
80
- }
81
-
82
- // Backward compatibility for stdio mode
83
- export const connectionsTools = createConnectionsTools(defaultClient);
@@ -1,150 +0,0 @@
1
- import { z } from 'zod';
2
- import { LyrraClient } from '../client.js';
3
- import { client as defaultClient } from '../client.js';
4
- import { config } from '../config.js';
5
-
6
- export function createEduflowTools(c: LyrraClient) {
7
- return {
8
- eduflow_list: {
9
- description: 'Lister tous les parcours EduFlow de l\'utilisateur. Retourne id, titre, statut, nombre de blocs, dates.',
10
- inputSchema: z.object({}),
11
- handler: async () => {
12
- return c.get('/flows', 'eduflow');
13
- },
14
- },
15
-
16
- eduflow_get: {
17
- description: 'Récupérer les détails complets d\'un parcours EduFlow : blocs, connexions (edges), objectifs, paramètres de gamification, présentation, etc.',
18
- inputSchema: z.object({
19
- flowId: z.string().uuid().describe('ID du parcours EduFlow'),
20
- }),
21
- handler: async ({ flowId }: { flowId: string }) => {
22
- return c.get(`/flows/${flowId}`, 'eduflow');
23
- },
24
- },
25
-
26
- eduflow_get_urls: {
27
- description: 'Obtenir tous les liens utiles d\'un parcours EduFlow : prévisualisation, édition, page publique, analytics, lien participant. À utiliser quand l\'utilisateur demande un lien ou une URL.',
28
- inputSchema: z.object({
29
- flowId: z.string().uuid().describe('ID du parcours EduFlow'),
30
- }),
31
- handler: async ({ flowId }: { flowId: string }) => {
32
- const base = config.frontendUrl;
33
- return {
34
- preview: `${base}/eduflow/flows/${flowId}/play`,
35
- public: `${base}/eduflow/flows/${flowId}/public`,
36
- edit: `${base}/eduflow/edit/${flowId}`,
37
- presentation: `${base}/eduflow/edit/${flowId}/presentation`,
38
- analytics: `${base}/eduflow/flows/${flowId}/analytics`,
39
- };
40
- },
41
- },
42
-
43
- eduflow_create: {
44
- description: 'Créer un nouveau parcours EduFlow. Les blocs "start" et "end" sont automatiquement créés et connectés.',
45
- inputSchema: z.object({
46
- title: z.string().describe('Titre du parcours'),
47
- description: z.string().optional().describe('Description du parcours'),
48
- language: z.enum(['fr', 'en', 'es', 'it', 'de']).optional().describe('Langue du parcours (défaut: fr)'),
49
- }),
50
- handler: async (args: { title: string; description?: string; language?: string }) => {
51
- const result = await c.post('/flows', args, 'eduflow');
52
- const flowId = result?.flow?.id;
53
- if (flowId) {
54
- const startId = `start-1`;
55
- const endId = `end-1`;
56
- const blocks = [
57
- { id: startId, type: 'start', title: 'Début', content: {}, order: 0, positionX: 250, positionY: 50 },
58
- { id: endId, type: 'end', title: 'Fin', content: {}, order: 1, positionX: 250, positionY: 300 },
59
- ];
60
- const edges = [
61
- { id: `edge-${startId}-${endId}`, source: startId, target: endId },
62
- ];
63
- await c.put(`/flows/${flowId}/blocks`, { blocks, edges }, 'eduflow');
64
- }
65
- return result;
66
- },
67
- },
68
-
69
- eduflow_update: {
70
- description: 'Mettre à jour les propriétés d\'un parcours EduFlow (titre, description, edges, gamification, objectifs, présentation, etc.).',
71
- inputSchema: z.object({
72
- flowId: z.string().uuid().describe('ID du parcours'),
73
- title: z.string().optional().describe('Nouveau titre'),
74
- description: z.string().optional().describe('Nouvelle description'),
75
- edges: z.array(z.object({
76
- id: z.string(),
77
- source: z.string().describe('ID du bloc source'),
78
- target: z.string().describe('ID du bloc cible'),
79
- label: z.string().optional().describe('Label de la connexion'),
80
- animated: z.boolean().optional(),
81
- })).optional().describe('Connexions entre blocs (remplace toutes les connexions existantes)'),
82
- objectives: z.any().optional().describe('Objectifs pédagogiques {globalObjective, steps[]}'),
83
- gamification: z.any().optional().describe('Configuration gamification {badges, missions, leaderboard, certifications}'),
84
- presentationEnabled: z.boolean().optional().describe('Activer/désactiver la page de présentation'),
85
- presentationData: z.any().optional().describe('Données de la page de présentation'),
86
- chatbotEnabled: z.boolean().optional().describe('Activer/désactiver le chatbot IA'),
87
- facialVerificationEnabled: z.boolean().optional().describe('Activer/désactiver la vérification faciale'),
88
- deadline: z.string().optional().describe('Date limite ISO 8601'),
89
- language: z.enum(['fr', 'en', 'es', 'it', 'de']).optional().describe('Langue du parcours'),
90
- }),
91
- handler: async ({ flowId, ...data }: any) => {
92
- return c.put(`/flows/${flowId}`, data, 'eduflow');
93
- },
94
- },
95
-
96
- eduflow_delete: {
97
- description: 'Supprimer définitivement un parcours EduFlow et tous ses blocs, connexions, participants.',
98
- inputSchema: z.object({
99
- flowId: z.string().uuid().describe('ID du parcours à supprimer'),
100
- }),
101
- handler: async ({ flowId }: { flowId: string }) => {
102
- return c.delete(`/flows/${flowId}`, 'eduflow');
103
- },
104
- },
105
-
106
- eduflow_duplicate: {
107
- description: 'Dupliquer un parcours EduFlow existant avec tous ses blocs et connexions.',
108
- inputSchema: z.object({
109
- flowId: z.string().uuid().describe('ID du parcours à dupliquer'),
110
- }),
111
- handler: async ({ flowId }: { flowId: string }) => {
112
- return c.post(`/flows/${flowId}/duplicate`, {}, 'eduflow');
113
- },
114
- },
115
-
116
- eduflow_change_status: {
117
- description: 'Changer le statut d\'un parcours : draft (brouillon), published (publié, accessible aux étudiants), archived (archivé).',
118
- inputSchema: z.object({
119
- flowId: z.string().uuid().describe('ID du parcours'),
120
- status: z.enum(['draft', 'published', 'archived']).describe('Nouveau statut'),
121
- }),
122
- handler: async ({ flowId, status }: { flowId: string; status: string }) => {
123
- return c.patch(`/flows/${flowId}/status`, { status }, 'eduflow');
124
- },
125
- },
126
-
127
- eduflow_export: {
128
- description: 'Exporter un parcours EduFlow au format JSON (contient blocs, connexions, paramètres).',
129
- inputSchema: z.object({
130
- flowId: z.string().uuid().describe('ID du parcours à exporter'),
131
- }),
132
- handler: async ({ flowId }: { flowId: string }) => {
133
- return c.get(`/flows/${flowId}/export`, 'eduflow');
134
- },
135
- },
136
-
137
- eduflow_get_public: {
138
- description: 'Récupérer les informations publiques d\'un parcours (page de présentation, objectifs). Ne nécessite pas d\'authentification.',
139
- inputSchema: z.object({
140
- flowId: z.string().uuid().describe('ID du parcours'),
141
- }),
142
- handler: async ({ flowId }: { flowId: string }) => {
143
- return c.get(`/flows/${flowId}/public`, 'eduflow');
144
- },
145
- },
146
- };
147
- }
148
-
149
- // Backward compatibility for stdio mode
150
- export const eduflowTools = createEduflowTools(defaultClient);