@connect-xlab/mcp-server 0.2.1 → 0.3.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 (52) hide show
  1. package/dist/index.js +95 -0
  2. package/dist/tools/approveAssistantAction.js +40 -0
  3. package/dist/tools/correctAssistantAction.js +36 -0
  4. package/dist/tools/createDmSequence.js +36 -0
  5. package/dist/tools/createLinkInBioBlock.js +27 -18
  6. package/dist/tools/createQuiz.js +38 -0
  7. package/dist/tools/createQuizAnswer.js +42 -0
  8. package/dist/tools/createQuizItem.js +46 -0
  9. package/dist/tools/createQuizProfile.js +50 -0
  10. package/dist/tools/createShopProduct.js +7 -5
  11. package/dist/tools/deleteQuiz.js +33 -0
  12. package/dist/tools/deleteQuizAnswer.js +37 -0
  13. package/dist/tools/deleteQuizItem.js +35 -0
  14. package/dist/tools/deleteQuizProfile.js +35 -0
  15. package/dist/tools/getAssistantAction.js +33 -0
  16. package/dist/tools/getAssistantProducts.js +31 -0
  17. package/dist/tools/getAssistantSettings.js +31 -0
  18. package/dist/tools/getAssistantSummary.js +36 -0
  19. package/dist/tools/getDmSequence.js +33 -0
  20. package/dist/tools/getDmSequenceSettings.js +31 -0
  21. package/dist/tools/getDmSequenceStats.js +38 -0
  22. package/dist/tools/getLinkInBioCatalog.js +31 -0
  23. package/dist/tools/getQuiz.js +33 -0
  24. package/dist/tools/getQuizLead.js +35 -0
  25. package/dist/tools/getQuizSettings.js +31 -0
  26. package/dist/tools/getQuizStats.js +33 -0
  27. package/dist/tools/listAssistantActions.js +40 -0
  28. package/dist/tools/listDmEnrollments.js +40 -0
  29. package/dist/tools/listDmSequenceTargetPosts.js +31 -0
  30. package/dist/tools/listDmSequences.js +31 -0
  31. package/dist/tools/listQuizLeads.js +48 -0
  32. package/dist/tools/listQuizTemplates.js +31 -0
  33. package/dist/tools/listQuizzes.js +31 -0
  34. package/dist/tools/manageDmEnrollment.js +36 -0
  35. package/dist/tools/previewDmSequence.js +40 -0
  36. package/dist/tools/rejectAssistantAction.js +33 -0
  37. package/dist/tools/reorderQuizItems.js +36 -0
  38. package/dist/tools/requestAssistantPostDraft.js +38 -0
  39. package/dist/tools/setDmSequenceStatus.js +36 -0
  40. package/dist/tools/setQuizPublished.js +35 -0
  41. package/dist/tools/updateAssistantProducts.js +34 -0
  42. package/dist/tools/updateAssistantSettings.js +90 -0
  43. package/dist/tools/updateDmSequence.js +44 -0
  44. package/dist/tools/updateDmSequenceSettings.js +44 -0
  45. package/dist/tools/updateLinkInBioBlock.js +13 -8
  46. package/dist/tools/updateQuiz.js +56 -0
  47. package/dist/tools/updateQuizAnswer.js +44 -0
  48. package/dist/tools/updateQuizItem.js +46 -0
  49. package/dist/tools/updateQuizProfile.js +52 -0
  50. package/dist/tools/updateQuizSettings.js +34 -0
  51. package/dist/tools/updateShopProduct.js +7 -5
  52. package/package.json +1 -1
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Tool `list_quiz_templates` — modeles de quiz par niche.
3
+ * Scope : quiz.read.
4
+ */
5
+ import { z } from 'zod';
6
+ import { http, formatApiError } from '../http.js';
7
+ export const listQuizTemplatesTool = {
8
+ name: 'list_quiz_templates',
9
+ description: "Liste les modeles de quiz par niche (key, name, available). Seuls les modeles available=true sont utilisables dans create_quiz.template.",
10
+ inputSchema: {
11
+ type: 'object',
12
+ properties: {
13
+ lab: { type: 'string', description: 'group_url ou id du Lab' },
14
+ },
15
+ required: ['lab'],
16
+ additionalProperties: false,
17
+ },
18
+ zodSchema: z.object({
19
+ lab: z.string(),
20
+ }),
21
+ async handler(args) {
22
+ const p = this.zodSchema.parse(args);
23
+ try {
24
+ const { data } = await http.get(`/mcp/labs/${encodeURIComponent(p.lab)}/quiz/templates`);
25
+ return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
26
+ }
27
+ catch (err) {
28
+ return { isError: true, content: [{ type: 'text', text: formatApiError(err) }] };
29
+ }
30
+ },
31
+ };
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Tool `list_quizzes` — quiz du Lab.
3
+ * Scope : quiz.read.
4
+ */
5
+ import { z } from 'zod';
6
+ import { http, formatApiError } from '../http.js';
7
+ export const listQuizzesTool = {
8
+ name: 'list_quizzes',
9
+ description: "Liste les quiz du Lab : id, slug, title, status (draft/published), public_key (lien public /quiz/{public_key}), question_count, leads_count.",
10
+ inputSchema: {
11
+ type: 'object',
12
+ properties: {
13
+ lab: { type: 'string', description: 'group_url ou id du Lab' },
14
+ },
15
+ required: ['lab'],
16
+ additionalProperties: false,
17
+ },
18
+ zodSchema: z.object({
19
+ lab: z.string(),
20
+ }),
21
+ async handler(args) {
22
+ const p = this.zodSchema.parse(args);
23
+ try {
24
+ const { data } = await http.get(`/mcp/labs/${encodeURIComponent(p.lab)}/quiz/quizzes`);
25
+ return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
26
+ }
27
+ catch (err) {
28
+ return { isError: true, content: [{ type: 'text', text: formatApiError(err) }] };
29
+ }
30
+ },
31
+ };
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Tool `manage_dm_enrollment` — met en pause / reprend / annule l'inscription d'un membre.
3
+ * Scope : dm_sequences.write.
4
+ */
5
+ import { z } from 'zod';
6
+ import { http, formatApiError } from '../http.js';
7
+ export const manageDmEnrollmentTool = {
8
+ name: 'manage_dm_enrollment',
9
+ description: "Agit sur l'inscription d'un membre a une sequence : pause, resume ou cancel (arret definitif pour ce membre).",
10
+ inputSchema: {
11
+ type: 'object',
12
+ properties: {
13
+ lab: { type: 'string', description: 'group_url ou id du Lab' },
14
+ enrollment_id: { type: 'number', minimum: 1, description: 'id de l\'inscription (list_dm_enrollments)' },
15
+ action: { type: 'string', enum: ['pause', 'resume', 'cancel'] },
16
+ },
17
+ required: ['lab', 'enrollment_id', 'action'],
18
+ additionalProperties: false,
19
+ },
20
+ zodSchema: z.object({
21
+ lab: z.string(),
22
+ enrollment_id: z.number().int().positive(),
23
+ action: z.enum(['pause', 'resume', 'cancel']),
24
+ }),
25
+ async handler(args) {
26
+ const { lab, enrollment_id, ...body } = this.zodSchema.parse(args);
27
+ const p = { lab, enrollment_id };
28
+ try {
29
+ const { data } = await http.post(`/mcp/labs/${encodeURIComponent(p.lab)}/dm-sequences/enrollments/${p.enrollment_id}/status`, body);
30
+ return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
31
+ }
32
+ catch (err) {
33
+ return { isError: true, content: [{ type: 'text', text: formatApiError(err) }] };
34
+ }
35
+ },
36
+ };
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Tool `preview_dm_sequence` — previsualise les messages d'une sequence.
3
+ * Scope : dm_sequences.read.
4
+ */
5
+ import { z } from 'zod';
6
+ import { http, formatApiError } from '../http.js';
7
+ export const previewDmSequenceTool = {
8
+ name: 'preview_dm_sequence',
9
+ description: "Rend les messages avec un membre fictif (n'envoie rien) et signale les erreurs de template. steps = [{body_template}].",
10
+ inputSchema: {
11
+ type: 'object',
12
+ properties: {
13
+ lab: { type: 'string', description: 'group_url ou id du Lab' },
14
+ sequence_id: { type: 'number', minimum: 1, description: 'id de la sequence (list_dm_sequences)' },
15
+ steps: { type: 'array', minItems: 1, items: { type: 'object', properties: { body_template: { type: 'string' } }, required: ['body_template'] } },
16
+ link_url: { type: 'string', maxLength: 500 },
17
+ first_name: { type: 'string', maxLength: 50 },
18
+ },
19
+ required: ['lab', 'sequence_id', 'steps'],
20
+ additionalProperties: false,
21
+ },
22
+ zodSchema: z.object({
23
+ lab: z.string(),
24
+ sequence_id: z.number().int().positive(),
25
+ steps: z.array(z.object({ body_template: z.string() })).min(1),
26
+ link_url: z.string().max(500).nullable().optional(),
27
+ first_name: z.string().max(50).nullable().optional(),
28
+ }),
29
+ async handler(args) {
30
+ const { lab, sequence_id, ...body } = this.zodSchema.parse(args);
31
+ const p = { lab, sequence_id };
32
+ try {
33
+ const { data } = await http.post(`/mcp/labs/${encodeURIComponent(p.lab)}/dm-sequences/sequences/${p.sequence_id}/preview`, body);
34
+ return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
35
+ }
36
+ catch (err) {
37
+ return { isError: true, content: [{ type: 'text', text: formatApiError(err) }] };
38
+ }
39
+ },
40
+ };
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Tool `reject_assistant_action` — rejette une proposition de Lou.
3
+ * Scope : assistant.write.
4
+ */
5
+ import { z } from 'zod';
6
+ import { http, formatApiError } from '../http.js';
7
+ export const rejectAssistantActionTool = {
8
+ name: 'reject_assistant_action',
9
+ description: "Rejette une proposition de Lou (rien n'est envoye).",
10
+ inputSchema: {
11
+ type: 'object',
12
+ properties: {
13
+ lab: { type: 'string', description: 'group_url ou id du Lab' },
14
+ uuid: { type: 'string', description: 'uuid de la proposition (list_assistant_actions)' },
15
+ },
16
+ required: ['lab', 'uuid'],
17
+ additionalProperties: false,
18
+ },
19
+ zodSchema: z.object({
20
+ lab: z.string(),
21
+ uuid: z.string(),
22
+ }),
23
+ async handler(args) {
24
+ const p = this.zodSchema.parse(args);
25
+ try {
26
+ const { data } = await http.post(`/mcp/labs/${encodeURIComponent(p.lab)}/assistant/actions/${encodeURIComponent(p.uuid)}/reject`);
27
+ return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
28
+ }
29
+ catch (err) {
30
+ return { isError: true, content: [{ type: 'text', text: formatApiError(err) }] };
31
+ }
32
+ },
33
+ };
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Tool `reorder_quiz_items` — reordonne les questions / pages.
3
+ * Scope : quiz.write.
4
+ */
5
+ import { z } from 'zod';
6
+ import { http, formatApiError } from '../http.js';
7
+ export const reorderQuizItemsTool = {
8
+ name: 'reorder_quiz_items',
9
+ description: "Reordonne les questions et pages du quiz : order = liste complete des item_id dans le nouvel ordre.",
10
+ inputSchema: {
11
+ type: 'object',
12
+ properties: {
13
+ lab: { type: 'string', description: 'group_url ou id du Lab' },
14
+ quiz_id: { type: 'number', minimum: 1, description: 'id du quiz (list_quizzes)' },
15
+ order: { type: 'array', items: { type: 'number' }, minItems: 1 },
16
+ },
17
+ required: ['lab', 'quiz_id', 'order'],
18
+ additionalProperties: false,
19
+ },
20
+ zodSchema: z.object({
21
+ lab: z.string(),
22
+ quiz_id: z.number().int().positive(),
23
+ order: z.array(z.number().int().positive()).min(1),
24
+ }),
25
+ async handler(args) {
26
+ const { lab, quiz_id, ...body } = this.zodSchema.parse(args);
27
+ const p = { lab, quiz_id };
28
+ try {
29
+ const { data } = await http.put(`/mcp/labs/${encodeURIComponent(p.lab)}/quiz/quizzes/${p.quiz_id}/items/reorder`, body);
30
+ return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
31
+ }
32
+ catch (err) {
33
+ return { isError: true, content: [{ type: 'text', text: formatApiError(err) }] };
34
+ }
35
+ },
36
+ };
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Tool `request_assistant_post_draft` — demande un brouillon de post a Lou.
3
+ * Scope : assistant.write.
4
+ */
5
+ import { z } from 'zod';
6
+ import { http, formatApiError } from '../http.js';
7
+ export const requestAssistantPostDraftTool = {
8
+ name: 'request_assistant_post_draft',
9
+ description: "Demande a Lou un brouillon de post (kind : announcement, discussion_starter, weekly_recap ; topic optionnel). Le brouillon arrive dans les propositions (list_assistant_actions) : rien n'est publie sans validation.",
10
+ inputSchema: {
11
+ type: 'object',
12
+ properties: {
13
+ lab: { type: 'string', description: 'group_url ou id du Lab' },
14
+ kind: { type: 'string', enum: ['announcement', 'discussion_starter', 'weekly_recap'] },
15
+ topic: { type: 'string', maxLength: 500 },
16
+ category_id: { type: 'number', minimum: 1 },
17
+ },
18
+ required: ['lab', 'kind'],
19
+ additionalProperties: false,
20
+ },
21
+ zodSchema: z.object({
22
+ lab: z.string(),
23
+ kind: z.enum(['announcement', 'discussion_starter', 'weekly_recap']),
24
+ topic: z.string().max(500).nullable().optional(),
25
+ category_id: z.number().int().min(1).optional(),
26
+ }),
27
+ async handler(args) {
28
+ const { lab, ...body } = this.zodSchema.parse(args);
29
+ const p = { lab };
30
+ try {
31
+ const { data } = await http.post(`/mcp/labs/${encodeURIComponent(p.lab)}/assistant/drafts/post`, body);
32
+ return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
33
+ }
34
+ catch (err) {
35
+ return { isError: true, content: [{ type: 'text', text: formatApiError(err) }] };
36
+ }
37
+ },
38
+ };
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Tool `set_dm_sequence_status` — publie / met en pause / archive une sequence DM.
3
+ * Scope : dm_sequences.write.
4
+ */
5
+ import { z } from 'zod';
6
+ import { http, formatApiError } from '../http.js';
7
+ export const setDmSequenceStatusTool = {
8
+ name: 'set_dm_sequence_status',
9
+ description: "Change l'etat d'une sequence : publish (met le brouillon en ligne : les membres concernes commencent a recevoir les DM), pause, archive (definitif). Demander confirmation a l'owner avant publish.",
10
+ inputSchema: {
11
+ type: 'object',
12
+ properties: {
13
+ lab: { type: 'string', description: 'group_url ou id du Lab' },
14
+ sequence_id: { type: 'number', minimum: 1, description: 'id de la sequence (list_dm_sequences)' },
15
+ action: { type: 'string', enum: ['publish', 'pause', 'archive'] },
16
+ },
17
+ required: ['lab', 'sequence_id', 'action'],
18
+ additionalProperties: false,
19
+ },
20
+ zodSchema: z.object({
21
+ lab: z.string(),
22
+ sequence_id: z.number().int().positive(),
23
+ action: z.enum(['publish', 'pause', 'archive']),
24
+ }),
25
+ async handler(args) {
26
+ const { lab, sequence_id, ...body } = this.zodSchema.parse(args);
27
+ const p = { lab, sequence_id };
28
+ try {
29
+ const { data } = await http.post(`/mcp/labs/${encodeURIComponent(p.lab)}/dm-sequences/sequences/${p.sequence_id}/status`, body);
30
+ return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
31
+ }
32
+ catch (err) {
33
+ return { isError: true, content: [{ type: 'text', text: formatApiError(err) }] };
34
+ }
35
+ },
36
+ };
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Tool `set_quiz_published` — publie / depublie un quiz.
3
+ * Scope : quiz.write.
4
+ */
5
+ import { z } from 'zod';
6
+ import { http, formatApiError } from '../http.js';
7
+ export const setQuizPublishedTool = {
8
+ name: 'set_quiz_published',
9
+ description: "Publie (published=true) ou repasse en brouillon (published=false) un quiz. Publication refusee si : moins de 5 questions, moins de 2 profils, ou un profil qu'aucune reponse notee ne permet d'atteindre. Le plug-in Quiz doit aussi etre active pour que le quiz soit public.",
10
+ inputSchema: {
11
+ type: 'object',
12
+ properties: {
13
+ lab: { type: 'string', description: 'group_url ou id du Lab' },
14
+ quiz_id: { type: 'number', minimum: 1, description: 'id du quiz (list_quizzes)' },
15
+ published: { type: 'boolean' },
16
+ },
17
+ required: ['lab', 'quiz_id', 'published'],
18
+ additionalProperties: false,
19
+ },
20
+ zodSchema: z.object({
21
+ lab: z.string(),
22
+ quiz_id: z.number().int().positive(),
23
+ published: z.boolean(),
24
+ }),
25
+ async handler(args) {
26
+ const p = this.zodSchema.parse(args);
27
+ try {
28
+ const { data } = await http.post(`/mcp/labs/${encodeURIComponent(p.lab)}/quiz/quizzes/${p.quiz_id}/${p.published ? 'publish' : 'unpublish'}`);
29
+ return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
30
+ }
31
+ catch (err) {
32
+ return { isError: true, content: [{ type: 'text', text: formatApiError(err) }] };
33
+ }
34
+ },
35
+ };
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Tool `update_assistant_products` — configure les produits recommandes par Lou.
3
+ * Scope : assistant.write.
4
+ */
5
+ import { z } from 'zod';
6
+ import { http, formatApiError } from '../http.js';
7
+ export const updateAssistantProductsTool = {
8
+ name: 'update_assistant_products',
9
+ description: "Configure les produits que Lou peut recommander : products = [{group_shop_id, enabled, pitch_notes?, ideal_for?}] (ids de get_assistant_products).",
10
+ inputSchema: {
11
+ type: 'object',
12
+ properties: {
13
+ lab: { type: 'string', description: 'group_url ou id du Lab' },
14
+ products: { type: 'array', maxItems: 100, items: { type: 'object', properties: { group_shop_id: { type: 'number' }, enabled: { type: 'boolean' }, pitch_notes: { type: 'string' }, ideal_for: { type: 'string' } }, required: ['group_shop_id', 'enabled'] } },
15
+ },
16
+ required: ['lab', 'products'],
17
+ additionalProperties: false,
18
+ },
19
+ zodSchema: z.object({
20
+ lab: z.string(),
21
+ products: z.array(z.object({ group_shop_id: z.number().int().positive(), enabled: z.boolean(), pitch_notes: z.string().max(1000).nullable().optional(), ideal_for: z.string().max(500).nullable().optional() })).max(100),
22
+ }),
23
+ async handler(args) {
24
+ const { lab, ...body } = this.zodSchema.parse(args);
25
+ const p = { lab };
26
+ try {
27
+ const { data } = await http.put(`/mcp/labs/${encodeURIComponent(p.lab)}/assistant/products`, body);
28
+ return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
29
+ }
30
+ catch (err) {
31
+ return { isError: true, content: [{ type: 'text', text: formatApiError(err) }] };
32
+ }
33
+ },
34
+ };
@@ -0,0 +1,90 @@
1
+ /**
2
+ * Tool `update_assistant_settings` — modifie les reglages de Lou.
3
+ * Scope : assistant.write.
4
+ */
5
+ import { z } from 'zod';
6
+ import { http, formatApiError } from '../http.js';
7
+ export const updateAssistantSettingsTool = {
8
+ name: 'update_assistant_settings',
9
+ description: "Modifie les reglages de Lou (seuls les champs fournis changent). Chaque capacite allow_* a souvent un mode : draft (Lou propose, l'owner valide), assisted (Lou envoie seule les cas surs), autopilot (Lou envoie seule). Activer Lou (enabled=true) exige l'abonnement XLab Agent. Changer un mode remet son compteur de confiance a zero.",
10
+ inputSchema: {
11
+ type: 'object',
12
+ properties: {
13
+ lab: { type: 'string', description: 'group_url ou id du Lab' },
14
+ enabled: { type: 'boolean' },
15
+ language: { type: 'string', maxLength: 8 },
16
+ tone_preset: { type: 'string', enum: ['friendly', 'professional', 'casual', 'enthusiastic', 'formal'] },
17
+ tone_instructions: { type: 'string', maxLength: 1000 },
18
+ allow_comment_replies: { type: 'boolean' },
19
+ comment_reply_mode: { type: 'string', enum: ['draft', 'assisted', 'autopilot'] },
20
+ allow_dm_replies: { type: 'boolean' },
21
+ dm_reply_mode: { type: 'string', enum: ['draft', 'assisted', 'autopilot'] },
22
+ allow_course_question_replies: { type: 'boolean' },
23
+ course_question_reply_mode: { type: 'string', enum: ['draft', 'assisted', 'autopilot'] },
24
+ allow_sales_question_replies: { type: 'boolean' },
25
+ sales_question_reply_mode: { type: 'string', enum: ['draft', 'assisted', 'autopilot'] },
26
+ allow_sequence_replies: { type: 'boolean' },
27
+ sequence_reply_mode: { type: 'string', enum: ['draft', 'assisted', 'autopilot'] },
28
+ allow_comment_sales_pitch: { type: 'boolean' },
29
+ comment_sales_pitch_mode: { type: 'string', enum: ['draft', 'assisted', 'autopilot'] },
30
+ allow_course_sales_pitch: { type: 'boolean' },
31
+ course_sales_pitch_mode: { type: 'string', enum: ['draft', 'assisted', 'autopilot'] },
32
+ allow_post_drafts: { type: 'boolean' },
33
+ allow_sales_dms: { type: 'boolean' },
34
+ allow_milestone_offers: { type: 'boolean' },
35
+ allow_product_suggestions: { type: 'boolean' },
36
+ weekly_recap_enabled: { type: 'boolean' },
37
+ morning_brief_enabled: { type: 'boolean' },
38
+ morning_brief_hour: { type: 'number', minimum: 0, maximum: 23 },
39
+ allow_freeform_commands: { type: 'boolean' },
40
+ sales_dm_cooldown_days: { type: 'number', minimum: 1, maximum: 365 },
41
+ sales_dm_daily_cap: { type: 'number', minimum: 1, maximum: 100 },
42
+ daily_proposal_cap: { type: 'number', minimum: 1, maximum: 200 },
43
+ },
44
+ required: ['lab'],
45
+ additionalProperties: false,
46
+ },
47
+ zodSchema: z.object({
48
+ lab: z.string(),
49
+ enabled: z.boolean().optional(),
50
+ language: z.string().max(8).nullable().optional(),
51
+ tone_preset: z.enum(['friendly', 'professional', 'casual', 'enthusiastic', 'formal']).optional(),
52
+ tone_instructions: z.string().max(1000).nullable().optional(),
53
+ allow_comment_replies: z.boolean().optional(),
54
+ comment_reply_mode: z.enum(['draft', 'assisted', 'autopilot']).optional(),
55
+ allow_dm_replies: z.boolean().optional(),
56
+ dm_reply_mode: z.enum(['draft', 'assisted', 'autopilot']).optional(),
57
+ allow_course_question_replies: z.boolean().optional(),
58
+ course_question_reply_mode: z.enum(['draft', 'assisted', 'autopilot']).optional(),
59
+ allow_sales_question_replies: z.boolean().optional(),
60
+ sales_question_reply_mode: z.enum(['draft', 'assisted', 'autopilot']).optional(),
61
+ allow_sequence_replies: z.boolean().optional(),
62
+ sequence_reply_mode: z.enum(['draft', 'assisted', 'autopilot']).optional(),
63
+ allow_comment_sales_pitch: z.boolean().optional(),
64
+ comment_sales_pitch_mode: z.enum(['draft', 'assisted', 'autopilot']).optional(),
65
+ allow_course_sales_pitch: z.boolean().optional(),
66
+ course_sales_pitch_mode: z.enum(['draft', 'assisted', 'autopilot']).optional(),
67
+ allow_post_drafts: z.boolean().optional(),
68
+ allow_sales_dms: z.boolean().optional(),
69
+ allow_milestone_offers: z.boolean().optional(),
70
+ allow_product_suggestions: z.boolean().optional(),
71
+ weekly_recap_enabled: z.boolean().optional(),
72
+ morning_brief_enabled: z.boolean().optional(),
73
+ morning_brief_hour: z.number().int().min(0).max(23).optional(),
74
+ allow_freeform_commands: z.boolean().optional(),
75
+ sales_dm_cooldown_days: z.number().int().min(1).max(365).optional(),
76
+ sales_dm_daily_cap: z.number().int().min(1).max(100).optional(),
77
+ daily_proposal_cap: z.number().int().min(1).max(200).optional(),
78
+ }),
79
+ async handler(args) {
80
+ const { lab, ...body } = this.zodSchema.parse(args);
81
+ const p = { lab };
82
+ try {
83
+ const { data } = await http.put(`/mcp/labs/${encodeURIComponent(p.lab)}/assistant/settings`, body);
84
+ return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
85
+ }
86
+ catch (err) {
87
+ return { isError: true, content: [{ type: 'text', text: formatApiError(err) }] };
88
+ }
89
+ },
90
+ };
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Tool `update_dm_sequence` — modifie le brouillon d'une sequence DM.
3
+ * Scope : dm_sequences.write.
4
+ */
5
+ import { z } from 'zod';
6
+ import { http, formatApiError } from '../http.js';
7
+ export const updateDmSequenceTool = {
8
+ name: 'update_dm_sequence',
9
+ description: "Modifie le BROUILLON d'une sequence (la version en ligne ne change qu'a la publication). trigger (keyword : post_ids de list_dm_sequence_target_posts + keywords, max 10) ; goal (objectif qui arrete la sequence : reply, first_comment, lesson_completed + module_id, none ; window_days 1-90) ; config (link_url, group_shop_id) ; steps remplace toute la liste (max 8 messages : delay_seconds depuis le message precedent + body_template). Variables utilisables dans body_template : {{prenom}}, {{lab}}, {{lien}} (config.link_url), {{app}} (lien d'installation de l'app mobile, aux couleurs du Lab). revision facultative.",
10
+ inputSchema: {
11
+ type: 'object',
12
+ properties: {
13
+ lab: { type: 'string', description: 'group_url ou id du Lab' },
14
+ sequence_id: { type: 'number', minimum: 1, description: 'id de la sequence (list_dm_sequences)' },
15
+ trigger: { type: 'object', properties: { post_ids: { type: 'array', items: { type: 'number' } }, keywords: { type: 'array', items: { type: 'string' }, maxItems: 10 } } },
16
+ goal: { type: 'object', properties: { type: { type: 'string', enum: ['reply', 'first_comment', 'lesson_completed', 'none'] }, window_days: { type: 'number', minimum: 1, maximum: 90 }, module_id: { type: 'number' } } },
17
+ config: { type: 'object', properties: { link_url: { type: 'string' }, group_shop_id: { type: 'number' } } },
18
+ steps: { type: 'array', maxItems: 8, items: { type: 'object', properties: { delay_seconds: { type: 'number', minimum: 0 }, body_template: { type: 'string' } }, required: ['delay_seconds', 'body_template'] } },
19
+ revision: { type: 'number', minimum: 1 },
20
+ },
21
+ required: ['lab', 'sequence_id'],
22
+ additionalProperties: false,
23
+ },
24
+ zodSchema: z.object({
25
+ lab: z.string(),
26
+ sequence_id: z.number().int().positive(),
27
+ trigger: z.object({ post_ids: z.array(z.number().int()).optional(), keywords: z.array(z.string().max(60)).max(10).optional() }).optional(),
28
+ goal: z.object({ type: z.enum(['reply', 'first_comment', 'lesson_completed', 'none']).optional(), window_days: z.number().int().min(1).max(90).optional(), module_id: z.number().int().nullable().optional() }).optional(),
29
+ config: z.object({ link_url: z.string().max(500).nullable().optional(), group_shop_id: z.number().int().nullable().optional() }).optional(),
30
+ steps: z.array(z.object({ delay_seconds: z.number().int().min(0), body_template: z.string() })).max(8).optional(),
31
+ revision: z.number().int().min(1).optional(),
32
+ }),
33
+ async handler(args) {
34
+ const { lab, sequence_id, ...body } = this.zodSchema.parse(args);
35
+ const p = { lab, sequence_id };
36
+ try {
37
+ const { data } = await http.put(`/mcp/labs/${encodeURIComponent(p.lab)}/dm-sequences/sequences/${p.sequence_id}`, body);
38
+ return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
39
+ }
40
+ catch (err) {
41
+ return { isError: true, content: [{ type: 'text', text: formatApiError(err) }] };
42
+ }
43
+ },
44
+ };
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Tool `update_dm_sequence_settings` — modifie les reglages Sequences DM.
3
+ * Scope : dm_sequences.write.
4
+ */
5
+ import { z } from 'zod';
6
+ import { http, formatApiError } from '../http.js';
7
+ export const updateDmSequenceSettingsTool = {
8
+ name: 'update_dm_sequence_settings',
9
+ description: "Modifie les reglages Sequences DM (seuls les champs fournis changent). revision est facultative (la revision courante est utilisee par defaut). Desactiver le plug-in met en pause toutes les inscriptions en cours.",
10
+ inputSchema: {
11
+ type: 'object',
12
+ properties: {
13
+ lab: { type: 'string', description: 'group_url ou id du Lab' },
14
+ enabled: { type: 'boolean' },
15
+ timezone: { type: 'string', maxLength: 64, description: 'ex Europe/Paris' },
16
+ window_start: { type: 'string', maxLength: 5, description: 'HH:MM' },
17
+ window_end: { type: 'string', maxLength: 5, description: 'HH:MM, apres window_start' },
18
+ member_daily_cap: { type: 'number', minimum: 1, maximum: 20 },
19
+ revision: { type: 'number', minimum: 1 },
20
+ },
21
+ required: ['lab'],
22
+ additionalProperties: false,
23
+ },
24
+ zodSchema: z.object({
25
+ lab: z.string(),
26
+ enabled: z.boolean().optional(),
27
+ timezone: z.string().max(64).nullable().optional(),
28
+ window_start: z.string().max(5).nullable().optional(),
29
+ window_end: z.string().max(5).nullable().optional(),
30
+ member_daily_cap: z.number().int().min(1).max(20).optional(),
31
+ revision: z.number().int().min(1).optional(),
32
+ }),
33
+ async handler(args) {
34
+ const { lab, ...body } = this.zodSchema.parse(args);
35
+ const p = { lab };
36
+ try {
37
+ const { data } = await http.put(`/mcp/labs/${encodeURIComponent(p.lab)}/dm-sequences/settings`, body);
38
+ return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
39
+ }
40
+ catch (err) {
41
+ return { isError: true, content: [{ type: 'text', text: formatApiError(err) }] };
42
+ }
43
+ },
44
+ };
@@ -1,21 +1,24 @@
1
1
  /**
2
2
  * Tool `update_link_in_bio_block` — modifie un bloc existant. Tous les champs
3
- * sont optionnels : seuls ceux fournis sont modifies.
3
+ * sont optionnels : seuls ceux fournis sont modifies (l'API complete le reste
4
+ * avec les valeurs actuelles du bloc).
4
5
  */
5
6
  import { z } from 'zod';
6
7
  import { http, formatApiError } from '../http.js';
7
8
  export const updateLinkInBioBlockTool = {
8
9
  name: 'update_link_in_bio_block',
9
- description: "Modifie un bloc existant (identifie par block_id, entier renvoye par get_link_in_bio ou create_link_in_bio_block). Champs modifiables : title, subtitle, action_url, image_url, is_active (afficher/masquer sans supprimer).",
10
+ description: "Modifie un bloc existant (identifie par block_id, entier renvoye par get_link_in_bio ou create_link_in_bio_block). Seuls les champs fournis changent : title, subtitle, url (bloc externe), image_url, quiz_id (bloc quiz), lab_group_id (bloc Lab), is_active (afficher/masquer sans supprimer). Le type (kind) d'un bloc ne change pas.",
10
11
  inputSchema: {
11
12
  type: 'object',
12
13
  properties: {
13
14
  lab: { type: 'string' },
14
15
  block_id: { type: 'number', minimum: 1 },
15
- title: { type: 'string', maxLength: 60 },
16
- subtitle: { type: 'string', maxLength: 120 },
17
- action_url: { type: 'string' },
16
+ title: { type: 'string', maxLength: 200 },
17
+ subtitle: { type: 'string', maxLength: 300 },
18
+ url: { type: 'string' },
18
19
  image_url: { type: 'string' },
20
+ quiz_id: { type: 'number', minimum: 1 },
21
+ lab_group_id: { type: 'number', minimum: 1 },
19
22
  is_active: { type: 'boolean' },
20
23
  },
21
24
  required: ['lab', 'block_id'],
@@ -24,10 +27,12 @@ export const updateLinkInBioBlockTool = {
24
27
  zodSchema: z.object({
25
28
  lab: z.string(),
26
29
  block_id: z.number().int().positive(),
27
- title: z.string().max(60).optional(),
28
- subtitle: z.string().max(120).optional(),
29
- action_url: z.string().optional(),
30
+ title: z.string().max(200).optional(),
31
+ subtitle: z.string().max(300).optional(),
32
+ url: z.string().optional(),
30
33
  image_url: z.string().optional(),
34
+ quiz_id: z.number().int().positive().optional(),
35
+ lab_group_id: z.number().int().positive().optional(),
31
36
  is_active: z.boolean().optional(),
32
37
  }),
33
38
  async handler(args) {
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Tool `update_quiz` — modifie l'accueil / l'apparence / la capture d'un quiz.
3
+ * Scope : quiz.write.
4
+ */
5
+ import { z } from 'zod';
6
+ import { http, formatApiError } from '../http.js';
7
+ export const updateQuizTool = {
8
+ name: 'update_quiz',
9
+ description: "Modifie un quiz (seuls les champs fournis changent) : accueil (title, subtitle, cta_label), capture (capture_phone = demander le WhatsApp, capture_promise = promesse affichee au moment de laisser ses coordonnees, footer_note), apparence (color hex, show_progress, background_type text|image, logo_path, background_path).",
10
+ inputSchema: {
11
+ type: 'object',
12
+ properties: {
13
+ lab: { type: 'string', description: 'group_url ou id du Lab' },
14
+ quiz_id: { type: 'number', minimum: 1, description: 'id du quiz (list_quizzes)' },
15
+ title: { type: 'string', maxLength: 255 },
16
+ subtitle: { type: 'string' },
17
+ cta_label: { type: 'string', maxLength: 120 },
18
+ capture_phone: { type: 'boolean' },
19
+ capture_promise: { type: 'string' },
20
+ footer_note: { type: 'string', maxLength: 255 },
21
+ color: { type: 'string', maxLength: 16, description: 'couleur hex, ex #5A6BE3' },
22
+ show_progress: { type: 'boolean' },
23
+ background_type: { type: 'string', enum: ['text', 'image'] },
24
+ logo_path: { type: 'string', maxLength: 255 },
25
+ background_path: { type: 'string', maxLength: 255 },
26
+ },
27
+ required: ['lab', 'quiz_id'],
28
+ additionalProperties: false,
29
+ },
30
+ zodSchema: z.object({
31
+ lab: z.string(),
32
+ quiz_id: z.number().int().positive(),
33
+ title: z.string().max(255).nullable().optional(),
34
+ subtitle: z.string().nullable().optional(),
35
+ cta_label: z.string().max(120).nullable().optional(),
36
+ capture_phone: z.boolean().optional(),
37
+ capture_promise: z.string().nullable().optional(),
38
+ footer_note: z.string().max(255).nullable().optional(),
39
+ color: z.string().max(16).nullable().optional(),
40
+ show_progress: z.boolean().optional(),
41
+ background_type: z.enum(['text', 'image']).optional(),
42
+ logo_path: z.string().max(255).nullable().optional(),
43
+ background_path: z.string().max(255).nullable().optional(),
44
+ }),
45
+ async handler(args) {
46
+ const { lab, quiz_id, ...body } = this.zodSchema.parse(args);
47
+ const p = { lab, quiz_id };
48
+ try {
49
+ const { data } = await http.put(`/mcp/labs/${encodeURIComponent(p.lab)}/quiz/quizzes/${p.quiz_id}`, body);
50
+ return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
51
+ }
52
+ catch (err) {
53
+ return { isError: true, content: [{ type: 'text', text: formatApiError(err) }] };
54
+ }
55
+ },
56
+ };