@lyrra/mcp-server 1.0.1 → 1.1.1

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 (87) hide show
  1. package/Dockerfile +16 -0
  2. package/README.md +95 -16
  3. package/dist/client.d.ts +7 -1
  4. package/dist/client.d.ts.map +1 -1
  5. package/dist/client.js +5 -5
  6. package/dist/client.js.map +1 -1
  7. package/dist/config.d.ts +1 -0
  8. package/dist/config.d.ts.map +1 -1
  9. package/dist/config.js +1 -0
  10. package/dist/config.js.map +1 -1
  11. package/dist/http-server.d.ts +8 -0
  12. package/dist/http-server.d.ts.map +1 -0
  13. package/dist/http-server.js +466 -0
  14. package/dist/http-server.js.map +1 -0
  15. package/dist/index.js +3 -69
  16. package/dist/index.js.map +1 -1
  17. package/dist/server-factory.d.ts +8 -0
  18. package/dist/server-factory.d.ts.map +1 -0
  19. package/dist/server-factory.js +82 -0
  20. package/dist/server-factory.js.map +1 -0
  21. package/dist/tools/admin.d.ts +132 -0
  22. package/dist/tools/admin.d.ts.map +1 -1
  23. package/dist/tools/admin.js +105 -101
  24. package/dist/tools/admin.js.map +1 -1
  25. package/dist/tools/ai-designer.d.ts +148 -0
  26. package/dist/tools/ai-designer.d.ts.map +1 -1
  27. package/dist/tools/ai-designer.js +80 -76
  28. package/dist/tools/ai-designer.js.map +1 -1
  29. package/dist/tools/analytics.d.ts +47 -0
  30. package/dist/tools/analytics.d.ts.map +1 -1
  31. package/dist/tools/analytics.js +38 -34
  32. package/dist/tools/analytics.js.map +1 -1
  33. package/dist/tools/auth.d.ts +30 -0
  34. package/dist/tools/auth.d.ts.map +1 -1
  35. package/dist/tools/auth.js +31 -27
  36. package/dist/tools/auth.js.map +1 -1
  37. package/dist/tools/blocks.d.ts +200 -0
  38. package/dist/tools/blocks.d.ts.map +1 -1
  39. package/dist/tools/blocks.js +154 -150
  40. package/dist/tools/blocks.js.map +1 -1
  41. package/dist/tools/connections.d.ts +86 -0
  42. package/dist/tools/connections.d.ts.map +1 -1
  43. package/dist/tools/connections.js +70 -66
  44. package/dist/tools/connections.js.map +1 -1
  45. package/dist/tools/eduflow.d.ts +223 -0
  46. package/dist/tools/eduflow.d.ts.map +1 -1
  47. package/dist/tools/eduflow.js +114 -93
  48. package/dist/tools/eduflow.js.map +1 -1
  49. package/dist/tools/participants.d.ts +110 -0
  50. package/dist/tools/participants.d.ts.map +1 -1
  51. package/dist/tools/participants.js +62 -58
  52. package/dist/tools/participants.js.map +1 -1
  53. package/dist/tools/presentation.d.ts +116 -0
  54. package/dist/tools/presentation.d.ts.map +1 -1
  55. package/dist/tools/presentation.js +51 -47
  56. package/dist/tools/presentation.js.map +1 -1
  57. package/dist/tools/projects.d.ts +65 -0
  58. package/dist/tools/projects.d.ts.map +1 -1
  59. package/dist/tools/projects.js +48 -44
  60. package/dist/tools/projects.js.map +1 -1
  61. package/dist/tools/resources.d.ts +46 -0
  62. package/dist/tools/resources.d.ts.map +1 -1
  63. package/dist/tools/resources.js +32 -28
  64. package/dist/tools/resources.js.map +1 -1
  65. package/dist/tools/store.d.ts +62 -0
  66. package/dist/tools/store.d.ts.map +1 -1
  67. package/dist/tools/store.js +59 -55
  68. package/dist/tools/store.js.map +1 -1
  69. package/mcp-config.example.json +4 -5
  70. package/package.json +7 -2
  71. package/src/client.ts +12 -5
  72. package/src/config.ts +1 -0
  73. package/src/http-server.ts +573 -0
  74. package/src/index.ts +3 -94
  75. package/src/server-factory.ts +109 -0
  76. package/src/tools/admin.ts +20 -14
  77. package/src/tools/ai-designer.ts +16 -10
  78. package/src/tools/analytics.ts +13 -7
  79. package/src/tools/auth.ts +32 -26
  80. package/src/tools/blocks.ts +18 -12
  81. package/src/tools/connections.ts +14 -8
  82. package/src/tools/eduflow.ts +36 -12
  83. package/src/tools/participants.ts +15 -9
  84. package/src/tools/presentation.ts +12 -6
  85. package/src/tools/projects.ts +14 -8
  86. package/src/tools/resources.ts +12 -6
  87. package/src/tools/store.ts +14 -8
@@ -0,0 +1,109 @@
1
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import { LyrraClient } from './client.js';
3
+
4
+ // Tool factory functions
5
+ import { createAuthTools } from './tools/auth.js';
6
+ import { createEduflowTools } from './tools/eduflow.js';
7
+ import { createBlocksTools } from './tools/blocks.js';
8
+ import { createConnectionsTools } from './tools/connections.js';
9
+ import { createParticipantsTools } from './tools/participants.js';
10
+ import { createAnalyticsTools } from './tools/analytics.js';
11
+ import { createAiDesignerTools } from './tools/ai-designer.js';
12
+ import { createPresentationTools } from './tools/presentation.js';
13
+ import { createStoreTools } from './tools/store.js';
14
+ import { createProjectsTools } from './tools/projects.js';
15
+ import { createResourcesTools } from './tools/resources.js';
16
+ import { createAdminTools } from './tools/admin.js';
17
+
18
+ // Resources
19
+ import { BLOCK_TYPES_RESOURCE } from './resources/block-types.js';
20
+ import { FLOW_SCHEMA_RESOURCE } from './resources/flow-schema.js';
21
+
22
+ type ToolDef = { description: string; inputSchema: any; handler: (args: any) => Promise<any> };
23
+
24
+ function registerToolsOnServer(server: McpServer, tools: Record<string, ToolDef>) {
25
+ for (const [name, tool] of Object.entries(tools)) {
26
+ server.tool(
27
+ name,
28
+ tool.description,
29
+ tool.inputSchema.shape
30
+ ? Object.fromEntries(
31
+ Object.entries(tool.inputSchema.shape).map(([key, schema]: [string, any]) => [key, schema])
32
+ )
33
+ : {},
34
+ async (args: any) => {
35
+ try {
36
+ const result = await tool.handler(args);
37
+ return {
38
+ content: [{ type: 'text' as const, text: JSON.stringify(result, null, 2) }],
39
+ };
40
+ } catch (error: any) {
41
+ return {
42
+ content: [{ type: 'text' as const, text: `Erreur: ${error.message}` }],
43
+ isError: true,
44
+ };
45
+ }
46
+ }
47
+ );
48
+ }
49
+ }
50
+
51
+ /**
52
+ * Create and configure an MCP server with all tools registered.
53
+ * @param client - LyrraClient to use for API calls. If not provided, uses default (env vars).
54
+ */
55
+ export function createMcpServer(client?: LyrraClient): McpServer {
56
+ const c = client || new LyrraClient();
57
+
58
+ const server = new McpServer({
59
+ name: 'lyrra-studio',
60
+ version: '1.0.0',
61
+ description: 'Serveur MCP pour piloter LYRRA Studio - Plateforme EdTech de création de parcours pédagogiques, conversion PDF→Audio, et store d\'audiobooks.',
62
+ });
63
+
64
+ const allTools: Record<string, ToolDef> = {
65
+ ...createAuthTools(c),
66
+ ...createEduflowTools(c),
67
+ ...createBlocksTools(c),
68
+ ...createConnectionsTools(c),
69
+ ...createParticipantsTools(c),
70
+ ...createAnalyticsTools(c),
71
+ ...createAiDesignerTools(c),
72
+ ...createPresentationTools(c),
73
+ ...createStoreTools(c),
74
+ ...createProjectsTools(c),
75
+ ...createResourcesTools(c),
76
+ ...createAdminTools(c),
77
+ };
78
+
79
+ registerToolsOnServer(server, allTools);
80
+
81
+ // Resources
82
+ server.resource(
83
+ 'block-types',
84
+ BLOCK_TYPES_RESOURCE.uri,
85
+ { description: BLOCK_TYPES_RESOURCE.description, mimeType: BLOCK_TYPES_RESOURCE.mimeType },
86
+ async () => ({
87
+ contents: [{
88
+ uri: BLOCK_TYPES_RESOURCE.uri,
89
+ mimeType: BLOCK_TYPES_RESOURCE.mimeType,
90
+ text: JSON.stringify(BLOCK_TYPES_RESOURCE.content, null, 2),
91
+ }],
92
+ })
93
+ );
94
+
95
+ server.resource(
96
+ 'flow-construction-guide',
97
+ FLOW_SCHEMA_RESOURCE.uri,
98
+ { description: FLOW_SCHEMA_RESOURCE.description, mimeType: FLOW_SCHEMA_RESOURCE.mimeType },
99
+ async () => ({
100
+ contents: [{
101
+ uri: FLOW_SCHEMA_RESOURCE.uri,
102
+ mimeType: FLOW_SCHEMA_RESOURCE.mimeType,
103
+ text: JSON.stringify(FLOW_SCHEMA_RESOURCE.content, null, 2),
104
+ }],
105
+ })
106
+ );
107
+
108
+ return server;
109
+ }
@@ -1,7 +1,9 @@
1
1
  import { z } from 'zod';
2
- import { client } from '../client.js';
2
+ import { LyrraClient } from '../client.js';
3
+ import { client as defaultClient } from '../client.js';
3
4
 
4
- export const adminTools = {
5
+ export function createAdminTools(c: LyrraClient) {
6
+ return {
5
7
  // --- Versions ---
6
8
  version_list: {
7
9
  description: 'Lister toutes les versions d\'un parcours EduFlow.',
@@ -9,7 +11,7 @@ export const adminTools = {
9
11
  flowId: z.string().uuid().describe('ID du parcours'),
10
12
  }),
11
13
  handler: async ({ flowId }: { flowId: string }) => {
12
- return client.get(`/flows/${flowId}/versions`, 'eduflow');
14
+ return c.get(`/flows/${flowId}/versions`, 'eduflow');
13
15
  },
14
16
  },
15
17
 
@@ -20,7 +22,7 @@ export const adminTools = {
20
22
  versionLabel: z.string().optional().describe('Label de la version (ex: "v2.0 - Ajout quiz")'),
21
23
  }),
22
24
  handler: async ({ flowId, versionLabel }: any) => {
23
- return client.post(`/flows/${flowId}/versions`, { versionLabel }, 'eduflow');
25
+ return c.post(`/flows/${flowId}/versions`, { versionLabel }, 'eduflow');
24
26
  },
25
27
  },
26
28
 
@@ -31,7 +33,7 @@ export const adminTools = {
31
33
  versionId: z.string().uuid().describe('ID de la version à activer'),
32
34
  }),
33
35
  handler: async ({ flowId, versionId }: { flowId: string; versionId: string }) => {
34
- return client.post(`/flows/${flowId}/versions/${versionId}/activate`, {}, 'eduflow');
36
+ return c.post(`/flows/${flowId}/versions/${versionId}/activate`, {}, 'eduflow');
35
37
  },
36
38
  },
37
39
 
@@ -40,7 +42,7 @@ export const adminTools = {
40
42
  description: 'Récupérer les statistiques de gamification de l\'utilisateur (niveau, streak, badges, missions).',
41
43
  inputSchema: z.object({}),
42
44
  handler: async () => {
43
- return client.get('/gamification/stats');
45
+ return c.get('/gamification/stats');
44
46
  },
45
47
  },
46
48
 
@@ -48,7 +50,7 @@ export const adminTools = {
48
50
  description: 'Récupérer les prochains objectifs de gamification à atteindre.',
49
51
  inputSchema: z.object({}),
50
52
  handler: async () => {
51
- return client.get('/gamification/objectives');
53
+ return c.get('/gamification/objectives');
52
54
  },
53
55
  },
54
56
 
@@ -64,7 +66,7 @@ export const adminTools = {
64
66
  if (page) params.set('page', String(page));
65
67
  if (limit) params.set('limit', String(limit));
66
68
  const query = params.toString() ? `?${params}` : '';
67
- return client.get(`/activity${query}`);
69
+ return c.get(`/activity${query}`);
68
70
  },
69
71
  },
70
72
 
@@ -75,7 +77,7 @@ export const adminTools = {
75
77
  }),
76
78
  handler: async ({ period }: { period?: string }) => {
77
79
  const query = period ? `?period=${period}` : '';
78
- return client.get(`/activity/stats${query}`);
80
+ return c.get(`/activity/stats${query}`);
79
81
  },
80
82
  },
81
83
 
@@ -84,7 +86,7 @@ export const adminTools = {
84
86
  description: 'Lister les webhooks configurés pour recevoir des événements (block.completed, flow.completed, etc.).',
85
87
  inputSchema: z.object({}),
86
88
  handler: async () => {
87
- return client.get('/webhooks');
89
+ return c.get('/webhooks');
88
90
  },
89
91
  },
90
92
 
@@ -96,7 +98,7 @@ export const adminTools = {
96
98
  name: z.string().optional().describe('Nom du webhook'),
97
99
  }),
98
100
  handler: async (data: any) => {
99
- return client.post('/webhooks', data);
101
+ return c.post('/webhooks', data);
100
102
  },
101
103
  },
102
104
 
@@ -106,7 +108,7 @@ export const adminTools = {
106
108
  webhookId: z.string().uuid().describe('ID du webhook'),
107
109
  }),
108
110
  handler: async ({ webhookId }: { webhookId: string }) => {
109
- return client.delete(`/webhooks/${webhookId}`);
111
+ return c.delete(`/webhooks/${webhookId}`);
110
112
  },
111
113
  },
112
114
 
@@ -116,7 +118,11 @@ export const adminTools = {
116
118
  webhookId: z.string().uuid().describe('ID du webhook à tester'),
117
119
  }),
118
120
  handler: async ({ webhookId }: { webhookId: string }) => {
119
- return client.post(`/webhooks/${webhookId}/test`, {});
121
+ return c.post(`/webhooks/${webhookId}/test`, {});
120
122
  },
121
123
  },
122
- };
124
+ };
125
+ }
126
+
127
+ // Backward compatibility for stdio mode
128
+ export const adminTools = createAdminTools(defaultClient);
@@ -1,7 +1,9 @@
1
1
  import { z } from 'zod';
2
- import { client } from '../client.js';
2
+ import { LyrraClient } from '../client.js';
3
+ import { client as defaultClient } from '../client.js';
3
4
 
4
- export const aiDesignerTools = {
5
+ export function createAiDesignerTools(c: LyrraClient) {
6
+ return {
5
7
  ai_generate_plan: {
6
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.',
7
9
  inputSchema: z.object({
@@ -13,7 +15,7 @@ export const aiDesignerTools = {
13
15
  context: z.string().optional().describe('Contexte additionnel pour l\'IA'),
14
16
  }),
15
17
  handler: async ({ flowId, ...data }: any) => {
16
- return client.post(`/flows/${flowId}/chatbot/generate-plan`, data, 'eduflow');
18
+ return c.post(`/flows/${flowId}/chatbot/generate-plan`, data, 'eduflow');
17
19
  },
18
20
  },
19
21
 
@@ -26,7 +28,7 @@ export const aiDesignerTools = {
26
28
  context: z.string().optional().describe('Contexte du parcours pour l\'IA'),
27
29
  }),
28
30
  handler: async ({ flowId, ...data }: any) => {
29
- return client.post(`/flows/${flowId}/chatbot/generate-block`, data, 'eduflow');
31
+ return c.post(`/flows/${flowId}/chatbot/generate-block`, data, 'eduflow');
30
32
  },
31
33
  },
32
34
 
@@ -37,7 +39,7 @@ export const aiDesignerTools = {
37
39
  style: z.string().optional().describe('Style de présentation souhaité'),
38
40
  }),
39
41
  handler: async ({ flowId, ...data }: any) => {
40
- return client.post(`/flows/${flowId}/chatbot/generate-presentation`, data, 'eduflow');
42
+ return c.post(`/flows/${flowId}/chatbot/generate-presentation`, data, 'eduflow');
41
43
  },
42
44
  },
43
45
 
@@ -47,7 +49,7 @@ export const aiDesignerTools = {
47
49
  flowId: z.string().uuid().describe('ID du parcours'),
48
50
  }),
49
51
  handler: async ({ flowId }: { flowId: string }) => {
50
- return client.post(`/flows/${flowId}/chatbot/generate-objectives`, {}, 'eduflow');
52
+ return c.post(`/flows/${flowId}/chatbot/generate-objectives`, {}, 'eduflow');
51
53
  },
52
54
  },
53
55
 
@@ -64,7 +66,7 @@ export const aiDesignerTools = {
64
66
  })).describe('Liste des objectifs par étape'),
65
67
  }),
66
68
  handler: async ({ flowId, ...objectives }: any) => {
67
- return client.post(`/flows/${flowId}/chatbot/save-objectives`, objectives, 'eduflow');
69
+ return c.post(`/flows/${flowId}/chatbot/save-objectives`, objectives, 'eduflow');
68
70
  },
69
71
  },
70
72
 
@@ -75,7 +77,7 @@ export const aiDesignerTools = {
75
77
  message: z.string().describe('Message à envoyer à l\'IA designer'),
76
78
  }),
77
79
  handler: async ({ flowId, message }: { flowId: string; message: string }) => {
78
- return client.post(`/flows/${flowId}/chatbot/message`, { message }, 'eduflow');
80
+ return c.post(`/flows/${flowId}/chatbot/message`, { message }, 'eduflow');
79
81
  },
80
82
  },
81
83
 
@@ -85,7 +87,11 @@ export const aiDesignerTools = {
85
87
  flowId: z.string().uuid().describe('ID du parcours'),
86
88
  }),
87
89
  handler: async ({ flowId }: { flowId: string }) => {
88
- return client.get(`/flows/${flowId}/chatbot/conversation`, 'eduflow');
90
+ return c.get(`/flows/${flowId}/chatbot/conversation`, 'eduflow');
89
91
  },
90
92
  },
91
- };
93
+ };
94
+ }
95
+
96
+ // Backward compatibility for stdio mode
97
+ export const aiDesignerTools = createAiDesignerTools(defaultClient);
@@ -1,7 +1,9 @@
1
1
  import { z } from 'zod';
2
- import { client } from '../client.js';
2
+ import { LyrraClient } from '../client.js';
3
+ import { client as defaultClient } from '../client.js';
3
4
 
4
- export const analyticsTools = {
5
+ export function createAnalyticsTools(c: LyrraClient) {
6
+ return {
5
7
  analytics_overview: {
6
8
  description: 'Tableau de bord global : nombre de projets, parcours EduFlow, apprenants, crédits utilisés, avec évolution par période.',
7
9
  inputSchema: z.object({
@@ -9,7 +11,7 @@ export const analyticsTools = {
9
11
  }),
10
12
  handler: async ({ period }: { period?: string }) => {
11
13
  const query = period ? `?period=${period}` : '';
12
- return client.get(`/analytics/overview${query}`);
14
+ return c.get(`/analytics/overview${query}`);
13
15
  },
14
16
  },
15
17
 
@@ -19,7 +21,7 @@ export const analyticsTools = {
19
21
  flowId: z.string().uuid().describe('ID du parcours'),
20
22
  }),
21
23
  handler: async ({ flowId }: { flowId: string }) => {
22
- return client.get(`/analytics/flow/${flowId}/learners`);
24
+ return c.get(`/analytics/flow/${flowId}/learners`);
23
25
  },
24
26
  },
25
27
 
@@ -29,7 +31,7 @@ export const analyticsTools = {
29
31
  flowId: z.string().uuid().describe('ID du parcours'),
30
32
  }),
31
33
  handler: async ({ flowId }: { flowId: string }) => {
32
- return client.get(`/flows/${flowId}/analytics`, 'eduflow');
34
+ return c.get(`/flows/${flowId}/analytics`, 'eduflow');
33
35
  },
34
36
  },
35
37
 
@@ -37,7 +39,11 @@ export const analyticsTools = {
37
39
  description: 'Mes statistiques personnelles : projets créés, parcours créés, temps total, crédits utilisés.',
38
40
  inputSchema: z.object({}),
39
41
  handler: async () => {
40
- return client.get('/analytics/my-stats');
42
+ return c.get('/analytics/my-stats');
41
43
  },
42
44
  },
43
- };
45
+ };
46
+ }
47
+
48
+ // Backward compatibility for stdio mode
49
+ export const analyticsTools = createAnalyticsTools(defaultClient);
package/src/tools/auth.ts CHANGED
@@ -1,33 +1,39 @@
1
1
  import { z } from 'zod';
2
- import { client } from '../client.js';
2
+ import { LyrraClient } from '../client.js';
3
+ import { client as defaultClient } from '../client.js';
3
4
 
4
- export const authTools = {
5
- auth_login: {
6
- 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.',
7
- inputSchema: z.object({
8
- email: z.string().email().describe('Adresse email du compte'),
9
- password: z.string().describe('Mot de passe'),
10
- }),
11
- handler: async ({ email, password }: { email: string; password: string }) => {
12
- const result = await client.post('/auth/login', { email, password });
13
- if (result.token) client.setToken(result.token);
14
- return result;
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
+ },
15
18
  },
16
- },
17
19
 
18
- auth_get_profile: {
19
- description: 'Récupérer le profil de l\'utilisateur connecté (nom, email, crédits, rôle, institution, etc.).',
20
- inputSchema: z.object({}),
21
- handler: async () => {
22
- return client.get('/auth/me');
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
+ },
23
26
  },
24
- },
25
27
 
26
- auth_list_api_keys: {
27
- description: 'Lister toutes les clés API de l\'utilisateur connecté.',
28
- inputSchema: z.object({}),
29
- handler: async () => {
30
- return client.get('/api-keys');
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
+ },
31
34
  },
32
- },
33
- };
35
+ };
36
+ }
37
+
38
+ // Backward compatibility for stdio mode
39
+ export const authTools = createAuthTools(defaultClient);
@@ -1,11 +1,13 @@
1
1
  import { z } from 'zod';
2
- import { client } from '../client.js';
2
+ import { LyrraClient } from '../client.js';
3
+ import { client as defaultClient } from '../client.js';
3
4
 
4
5
  const blockContentSchema = z.record(z.any()).describe(
5
6
  'Contenu du bloc (structure variable selon le type). Utiliser la resource lyrra://block-types pour connaître la structure attendue.'
6
7
  );
7
8
 
8
- export const blocksTools = {
9
+ export function createBlocksTools(c: LyrraClient) {
10
+ return {
9
11
  block_list_types: {
10
12
  description: `Lister tous les types de blocs disponibles avec leur documentation complète.
11
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.
@@ -55,7 +57,7 @@ Utiliser plutôt la resource lyrra://block-types pour la documentation complète
55
57
  blockId: z.string().uuid().describe('ID du bloc'),
56
58
  }),
57
59
  handler: async ({ flowId, blockId }: { flowId: string; blockId: string }) => {
58
- return client.get(`/flows/${flowId}/blocks/${blockId}`, 'eduflow');
60
+ return c.get(`/flows/${flowId}/blocks/${blockId}`, 'eduflow');
59
61
  },
60
62
  },
61
63
 
@@ -79,7 +81,7 @@ IMPORTANT pour le type "text" : toujours utiliser content.format = "html" et du
79
81
  }),
80
82
  handler: async ({ flowId, ...blockData }: any) => {
81
83
  // Use batch update to add a single block
82
- const flow = await client.get(`/flows/${flowId}`, 'eduflow');
84
+ const flow = await c.get(`/flows/${flowId}`, 'eduflow');
83
85
  const existingBlocks = flow.blocks || [];
84
86
  const newBlock = {
85
87
  id: crypto.randomUUID(),
@@ -93,7 +95,7 @@ IMPORTANT pour le type "text" : toujours utiliser content.format = "html" et du
93
95
  content: b.content, positionX: b.positionX, positionY: b.positionY,
94
96
  order: b.order, conditions: b.conditions, comment: b.comment,
95
97
  })), newBlock];
96
- await client.put(`/flows/${flowId}/blocks`, allBlocks, 'eduflow');
98
+ await c.put(`/flows/${flowId}/blocks`, allBlocks, 'eduflow');
97
99
  return { success: true, block: newBlock };
98
100
  },
99
101
  },
@@ -112,7 +114,7 @@ IMPORTANT pour le type "text" : toujours utiliser content.format = "html" et du
112
114
  comment: z.string().optional().describe('Commentaire créateur'),
113
115
  }),
114
116
  handler: async ({ flowId, blockId, ...data }: any) => {
115
- return client.put(`/flows/${flowId}/blocks/${blockId}`, data, 'eduflow');
117
+ return c.put(`/flows/${flowId}/blocks/${blockId}`, data, 'eduflow');
116
118
  },
117
119
  },
118
120
 
@@ -133,7 +135,7 @@ IMPORTANT pour le type "text" : toujours utiliser content.format = "html" et du
133
135
  })).describe('Liste complète des blocs (REMPLACE tous les blocs existants)'),
134
136
  }),
135
137
  handler: async ({ flowId, blocks }: any) => {
136
- return client.put(`/flows/${flowId}/blocks`, blocks, 'eduflow');
138
+ return c.put(`/flows/${flowId}/blocks`, blocks, 'eduflow');
137
139
  },
138
140
  },
139
141
 
@@ -145,7 +147,7 @@ IMPORTANT pour le type "text" : toujours utiliser content.format = "html" et du
145
147
  }),
146
148
  handler: async ({ flowId, blockId }: { flowId: string; blockId: string }) => {
147
149
  // Get current flow, remove block, remove related edges, batch update
148
- const flow = await client.get(`/flows/${flowId}`, 'eduflow');
150
+ const flow = await c.get(`/flows/${flowId}`, 'eduflow');
149
151
  const blocks = (flow.blocks || []).filter((b: any) => b.id !== blockId);
150
152
  const edges = (flow.edges || []).filter(
151
153
  (e: any) => e.source !== blockId && e.target !== blockId
@@ -155,8 +157,8 @@ IMPORTANT pour le type "text" : toujours utiliser content.format = "html" et du
155
157
  content: b.content, positionX: b.positionX, positionY: b.positionY,
156
158
  order: b.order, conditions: b.conditions, comment: b.comment,
157
159
  }));
158
- await client.put(`/flows/${flowId}/blocks`, mappedBlocks, 'eduflow');
159
- await client.put(`/flows/${flowId}`, { edges }, 'eduflow');
160
+ await c.put(`/flows/${flowId}/blocks`, mappedBlocks, 'eduflow');
161
+ await c.put(`/flows/${flowId}`, { edges }, 'eduflow');
160
162
  return { success: true, remainingBlocks: blocks.length, remainingEdges: edges.length };
161
163
  },
162
164
  },
@@ -168,7 +170,11 @@ IMPORTANT pour le type "text" : toujours utiliser content.format = "html" et du
168
170
  blockId: z.string().uuid().describe('ID du bloc'),
169
171
  }),
170
172
  handler: async ({ flowId, blockId }: { flowId: string; blockId: string }) => {
171
- return client.post(`/blocks/${blockId}/generate-tts`, { flowId }, 'eduflow');
173
+ return c.post(`/blocks/${blockId}/generate-tts`, { flowId }, 'eduflow');
172
174
  },
173
175
  },
174
- };
176
+ };
177
+ }
178
+
179
+ // Backward compatibility for stdio mode
180
+ export const blocksTools = createBlocksTools(defaultClient);
@@ -1,7 +1,9 @@
1
1
  import { z } from 'zod';
2
- import { client } from '../client.js';
2
+ import { LyrraClient } from '../client.js';
3
+ import { client as defaultClient } from '../client.js';
3
4
 
4
- export const connectionsTools = {
5
+ export function createConnectionsTools(c: LyrraClient) {
6
+ return {
5
7
  connection_list: {
6
8
  description: `Lister toutes les connexions (edges) d'un parcours EduFlow. Chaque connexion relie un bloc source à un bloc cible.
7
9
  Règles de connexion :
@@ -14,7 +16,7 @@ Règles de connexion :
14
16
  flowId: z.string().uuid().describe('ID du parcours'),
15
17
  }),
16
18
  handler: async ({ flowId }: { flowId: string }) => {
17
- const flow = await client.get(`/flows/${flowId}`, 'eduflow');
19
+ const flow = await c.get(`/flows/${flowId}`, 'eduflow');
18
20
  return {
19
21
  edges: flow.edges || [],
20
22
  blocks: (flow.blocks || []).map((b: any) => ({ id: b.id, type: b.type, title: b.title })),
@@ -32,7 +34,7 @@ Règles de connexion :
32
34
  animated: z.boolean().optional().describe('Animer la connexion visuellement'),
33
35
  }),
34
36
  handler: async ({ flowId, source, target, label, animated }: any) => {
35
- const flow = await client.get(`/flows/${flowId}`, 'eduflow');
37
+ const flow = await c.get(`/flows/${flowId}`, 'eduflow');
36
38
  const edges = flow.edges || [];
37
39
  const newEdge = {
38
40
  id: `e-${source}-${target}`,
@@ -47,7 +49,7 @@ Règles de connexion :
47
49
  return { success: false, error: 'Cette connexion existe déjà' };
48
50
  }
49
51
  edges.push(newEdge);
50
- await client.put(`/flows/${flowId}`, { edges }, 'eduflow');
52
+ await c.put(`/flows/${flowId}`, { edges }, 'eduflow');
51
53
  return { success: true, edge: newEdge, totalEdges: edges.length };
52
54
  },
53
55
  },
@@ -61,7 +63,7 @@ Règles de connexion :
61
63
  target: z.string().optional().describe('Alternative : ID du bloc cible'),
62
64
  }),
63
65
  handler: async ({ flowId, edgeId, source, target }: any) => {
64
- const flow = await client.get(`/flows/${flowId}`, 'eduflow');
66
+ const flow = await c.get(`/flows/${flowId}`, 'eduflow');
65
67
  let edges = flow.edges || [];
66
68
  if (edgeId) {
67
69
  edges = edges.filter((e: any) => e.id !== edgeId);
@@ -70,8 +72,12 @@ Règles de connexion :
70
72
  } else {
71
73
  return { success: false, error: 'Fournir edgeId OU source+target' };
72
74
  }
73
- await client.put(`/flows/${flowId}`, { edges }, 'eduflow');
75
+ await c.put(`/flows/${flowId}`, { edges }, 'eduflow');
74
76
  return { success: true, remainingEdges: edges.length };
75
77
  },
76
78
  },
77
- };
79
+ };
80
+ }
81
+
82
+ // Backward compatibility for stdio mode
83
+ export const connectionsTools = createConnectionsTools(defaultClient);