@connect-xlab/mcp-server 0.3.1 → 0.3.3

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.
package/dist/index.js CHANGED
@@ -134,6 +134,7 @@ import { getNewsletterRevenueTool } from './tools/getNewsletterRevenue.js';
134
134
  import { stopNewsletterTool } from './tools/stopNewsletter.js';
135
135
  import { reactivateNewsletterTool } from './tools/reactivateNewsletter.js';
136
136
  import { reorderClassTool } from './tools/reorderClass.js';
137
+ import { listCalendarsTool } from './tools/listCalendars.js';
137
138
  import { getCalendarTool } from './tools/getCalendar.js';
138
139
  import { getCalendarPipelineTool } from './tools/getCalendarPipeline.js';
139
140
  import { updateCalendarTool } from './tools/updateCalendar.js';
@@ -328,6 +329,7 @@ const tools = [
328
329
  // Reorder cours
329
330
  reorderClassTool,
330
331
  // Calendrier IA (booking)
332
+ listCalendarsTool,
331
333
  getCalendarTool,
332
334
  getCalendarPipelineTool,
333
335
  updateCalendarTool,
@@ -5,11 +5,12 @@ import { z } from 'zod';
5
5
  import { http, formatApiError } from '../http.js';
6
6
  export const addCalendarQuestionTool = {
7
7
  name: 'add_calendar_question',
8
- description: "Ajoute une question de qualification au Calendrier IA. question_type = text (reponse libre) ou qcm (choix multiple — options requises, min 2). icon et required optionnels.",
8
+ description: "Ajoute une question de qualification au Calendrier IA. question_type = text (reponse libre) ou qcm (choix multiple — options requises, min 2). icon et required optionnels. Un Lab peut avoir plusieurs calendriers : event_id (via list_calendars) cible l'un d'eux, sinon le premier du Lab.",
9
9
  inputSchema: {
10
10
  type: 'object',
11
11
  properties: {
12
12
  lab: { type: 'string' },
13
+ event_id: { type: 'number', minimum: 1, description: 'Calendrier vise (list_calendars). Defaut : le premier du Lab.' },
13
14
  label: { type: 'string', maxLength: 500 },
14
15
  question_type: { type: 'string', enum: ['text', 'qcm'] },
15
16
  icon: { type: 'string', maxLength: 40 },
@@ -21,6 +22,7 @@ export const addCalendarQuestionTool = {
21
22
  },
22
23
  zodSchema: z.object({
23
24
  lab: z.string(),
25
+ event_id: z.number().int().positive().optional(),
24
26
  label: z.string().min(1).max(500),
25
27
  question_type: z.enum(['text', 'qcm']),
26
28
  icon: z.string().max(40).nullable().optional(),
@@ -5,6 +5,8 @@
5
5
  * - kind=lab : autre Lab de l'owner (lab_group_id)
6
6
  * - kind=quiz : quiz du Lab (quiz_id) — l'URL publique /quiz/{public_key}
7
7
  * est resolue cote API, jamais par le client.
8
+ * - kind=calendar : calendrier du Lab (calendar_event_id) — la reservation
9
+ * s'ouvre en fenetre sur la page publique.
8
10
  *
9
11
  * Les ids se trouvent via get_link_in_bio_catalog. Position ajoutee
10
12
  * automatiquement en fin de liste.
@@ -12,15 +14,16 @@
12
14
  import { z } from 'zod';
13
15
  import { http, formatApiError } from '../http.js';
14
16
  const SELLABLE_TYPES = ['agent_ai', 'group_shop', 'newsletter', 'podcast_show', 'lab_channel'];
15
- const KINDS = ['external', 'product', 'lab', 'quiz'];
17
+ const KINDS = ['external', 'product', 'lab', 'quiz', 'calendar'];
16
18
  export const createLinkInBioBlockTool = {
17
19
  name: 'create_link_in_bio_block',
18
- description: "Ajoute un bloc a la page Link in Bio (title requis). Quatre types :\n" +
20
+ description: "Ajoute un bloc a la page Link in Bio (title requis). Cinq types :\n" +
19
21
  "- kind=external : lien URL libre (url requis)\n" +
20
22
  "- kind=product : offre du Lab (sellable_type + sellable_id requis, sellable_type dans agent_ai/group_shop/newsletter/podcast_show/lab_channel)\n" +
21
23
  "- kind=lab : autre Lab de l'owner (lab_group_id requis)\n" +
22
24
  "- kind=quiz : quiz du Lab (quiz_id requis). Le bloc reste masque sur la page publique tant que le quiz n'est pas publie et le plug-in Quiz active.\n" +
23
- "Les ids se trouvent via get_link_in_bio_catalog. Champs optionnels : subtitle, image_url. Position en fin de liste (utiliser reorder pour changer).",
25
+ "- kind=calendar : calendrier du Lab (calendar_event_id requis, ids via get_link_in_bio_catalog ou list_calendars). La reservation s'ouvre en fenetre sur la page publique. Masque si le calendrier est inactif ; un calendrier payant doit etre vendu dans une boutique Stripe active.\n" +
26
+ "Les ids se trouvent via get_link_in_bio_catalog. Champs optionnels : subtitle, image_url, layout (forme du bloc : classic = petite image + bouton a droite, par defaut ; image = texte et bouton a contour a gauche, grande image a droite ; button = image + titre en haut, gros bouton pleine largeur en dessous), button_label (texte du bouton, 60 car. max ; vide = texte par defaut du type de bloc). Position en fin de liste (utiliser reorder pour changer).",
24
27
  inputSchema: {
25
28
  type: 'object',
26
29
  properties: {
@@ -28,12 +31,15 @@ export const createLinkInBioBlockTool = {
28
31
  kind: { type: 'string', enum: [...KINDS] },
29
32
  title: { type: 'string', maxLength: 200 },
30
33
  subtitle: { type: 'string', maxLength: 300 },
34
+ layout: { type: 'string', enum: ['classic', 'image', 'button'] },
35
+ button_label: { type: 'string', maxLength: 60 },
31
36
  url: { type: 'string', description: 'kind=external : URL du lien' },
32
37
  image_url: { type: 'string' },
33
38
  sellable_type: { type: 'string', enum: [...SELLABLE_TYPES] },
34
39
  sellable_id: { type: 'number', minimum: 1 },
35
40
  lab_group_id: { type: 'number', minimum: 1, description: "kind=lab : id d'un autre Lab de l'owner" },
36
41
  quiz_id: { type: 'number', minimum: 1, description: 'kind=quiz : id du quiz' },
42
+ calendar_event_id: { type: 'number', minimum: 1, description: 'kind=calendar : id du calendrier' },
37
43
  },
38
44
  required: ['lab', 'kind', 'title'],
39
45
  additionalProperties: false,
@@ -43,12 +49,15 @@ export const createLinkInBioBlockTool = {
43
49
  kind: z.enum(KINDS),
44
50
  title: z.string().max(200),
45
51
  subtitle: z.string().max(300).optional(),
52
+ layout: z.enum(['classic', 'image', 'button']).optional(),
53
+ button_label: z.string().max(60).optional(),
46
54
  url: z.string().optional(),
47
55
  image_url: z.string().optional(),
48
56
  sellable_type: z.enum(SELLABLE_TYPES).optional(),
49
57
  sellable_id: z.number().int().positive().optional(),
50
58
  lab_group_id: z.number().int().positive().optional(),
51
59
  quiz_id: z.number().int().positive().optional(),
60
+ calendar_event_id: z.number().int().positive().optional(),
52
61
  }),
53
62
  async handler(args) {
54
63
  const parsed = this.zodSchema.parse(args);
@@ -5,18 +5,18 @@ import { z } from 'zod';
5
5
  import { http, formatApiError } from '../http.js';
6
6
  export const getCalendarTool = {
7
7
  name: 'get_calendar',
8
- description: "Lit la config complete du Calendrier IA (booking) d'un Lab : details du RDV, type de lieu, prix, duree, plage de dates, scoring IA, disponibilites, questions de qualification, regles de disqualification, relances.",
8
+ description: "Lit la config complete du Calendrier IA (booking) d'un Lab : details du RDV, type de lieu, prix, duree, plage de dates, scoring IA, disponibilites, questions de qualification, regles de disqualification, relances. Un Lab peut avoir plusieurs calendriers : event_id (via list_calendars) cible l'un d'eux, sinon le premier du Lab.",
9
9
  inputSchema: {
10
10
  type: 'object',
11
- properties: { lab: { type: 'string' } },
11
+ properties: { lab: { type: 'string' }, event_id: { type: 'number', minimum: 1, description: 'Calendrier vise (list_calendars). Defaut : le premier du Lab.' } },
12
12
  required: ['lab'],
13
13
  additionalProperties: false,
14
14
  },
15
- zodSchema: z.object({ lab: z.string() }),
15
+ zodSchema: z.object({ lab: z.string(), event_id: z.number().int().positive().optional() }),
16
16
  async handler(args) {
17
17
  const p = this.zodSchema.parse(args);
18
18
  try {
19
- const { data } = await http.get(`/mcp/labs/${encodeURIComponent(p.lab)}/calendar`);
19
+ const { data } = await http.get(`/mcp/labs/${encodeURIComponent(p.lab)}/calendar`, { params: p.event_id ? { event_id: p.event_id } : undefined });
20
20
  return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
21
21
  }
22
22
  catch (err) {
@@ -5,18 +5,18 @@ import { z } from 'zod';
5
5
  import { http, formatApiError } from '../http.js';
6
6
  export const getCalendarPipelineTool = {
7
7
  name: 'get_calendar_pipeline',
8
- description: "Liste le pipeline des leads (prospects) du Calendrier IA : personnes ayant demarre / complete le formulaire ou reserve un creneau, avec leur scoring (hot/warm/cold) et statut.",
8
+ description: "Liste le pipeline des leads (prospects) du Calendrier IA : personnes ayant demarre / complete le formulaire ou reserve un creneau, avec leur scoring (hot/warm/cold) et statut. Un Lab peut avoir plusieurs calendriers : event_id (via list_calendars) cible l'un d'eux, sinon le premier du Lab.",
9
9
  inputSchema: {
10
10
  type: 'object',
11
- properties: { lab: { type: 'string' } },
11
+ properties: { lab: { type: 'string' }, event_id: { type: 'number', minimum: 1, description: 'Calendrier vise (list_calendars). Defaut : le premier du Lab.' } },
12
12
  required: ['lab'],
13
13
  additionalProperties: false,
14
14
  },
15
- zodSchema: z.object({ lab: z.string() }),
15
+ zodSchema: z.object({ lab: z.string(), event_id: z.number().int().positive().optional() }),
16
16
  async handler(args) {
17
17
  const p = this.zodSchema.parse(args);
18
18
  try {
19
- const { data } = await http.get(`/mcp/labs/${encodeURIComponent(p.lab)}/calendar/pipeline`);
19
+ const { data } = await http.get(`/mcp/labs/${encodeURIComponent(p.lab)}/calendar/pipeline`, { params: p.event_id ? { event_id: p.event_id } : undefined });
20
20
  return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
21
21
  }
22
22
  catch (err) {
@@ -6,7 +6,7 @@ import { z } from 'zod';
6
6
  import { http, formatApiError } from '../http.js';
7
7
  export const getLinkInBioCatalogTool = {
8
8
  name: 'get_link_in_bio_catalog',
9
- description: "Liste les ids utilisables dans les blocs Link in Bio : products (type + id pour kind=product), labs (id pour kind=lab = autres Labs de l'owner), quizzes (id + published pour kind=quiz) et quiz_plugin_enabled. Un bloc quiz n'apparait sur la page publique que si le quiz est publie ET le plug-in Quiz active.",
9
+ description: "Liste les ids utilisables dans les blocs Link in Bio : products (type + id pour kind=product), labs (id pour kind=lab = autres Labs de l'owner), quizzes (id + published pour kind=quiz), calendars (id + paid/active/sold pour kind=calendar) et quiz_plugin_enabled. Un bloc quiz n'apparait sur la page publique que si le quiz est publie ET le plug-in Quiz active.",
10
10
  inputSchema: {
11
11
  type: 'object',
12
12
  properties: {
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Tool `list_calendars` — calendriers du plug-in Calendrier IA d'un Lab
3
+ * (un Lab peut en avoir plusieurs). L'id sert d'event_id aux autres outils
4
+ * calendrier et de calendar_event_id aux blocs Link in Bio.
5
+ */
6
+ import { z } from 'zod';
7
+ import { http, formatApiError } from '../http.js';
8
+ export const listCalendarsTool = {
9
+ name: 'list_calendars',
10
+ description: "Liste les calendriers (Calendrier IA) d'un Lab : id, nom, type (gratuit/payant), etat. L'id se passe en event_id aux outils calendrier (get_calendar, update_calendar...) et en calendar_event_id a un bloc Link in Bio de type calendar.",
11
+ inputSchema: {
12
+ type: 'object',
13
+ properties: { lab: { type: 'string' } },
14
+ required: ['lab'],
15
+ additionalProperties: false,
16
+ },
17
+ zodSchema: z.object({ lab: z.string() }),
18
+ async handler(args) {
19
+ const p = this.zodSchema.parse(args);
20
+ try {
21
+ const { data } = await http.get(`/mcp/labs/${encodeURIComponent(p.lab)}/calendars`);
22
+ return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
23
+ }
24
+ catch (err) {
25
+ return { isError: true, content: [{ type: 'text', text: formatApiError(err) }] };
26
+ }
27
+ },
28
+ };
@@ -2,7 +2,7 @@ import { z } from 'zod';
2
2
  import { http, formatApiError } from '../http.js';
3
3
  export const sendNewsletterEditionTool = {
4
4
  name: 'send_newsletter_edition',
5
- description: "Marque une edition draft comme envoyee. NOTE : cet appel MCP flag l'edition en 'sent' — l'envoi email reel se fait via l'endpoint xlab standard. Demander confirmation.",
5
+ description: "Envoie maintenant une edition (brouillon ou programmee) a tous les abonnes de la newsletter, par email. Action irreversible : demander confirmation avant.",
6
6
  inputSchema: {
7
7
  type: 'object',
8
8
  properties: { lab: { type: 'string' }, edition_id: { type: 'number' } },
@@ -6,11 +6,12 @@ import { z } from 'zod';
6
6
  import { http, formatApiError } from '../http.js';
7
7
  export const updateCalendarTool = {
8
8
  name: 'update_calendar',
9
- description: "Met a jour la config du Calendrier IA. N'envoie que ce que tu veux changer. Les fenetres horaires hot/warm par jour (scoring) restent gerees via le front.",
9
+ description: "Met a jour la config du Calendrier IA. N'envoie que ce que tu veux changer. Les fenetres horaires hot/warm par jour (scoring) restent gerees via le front. Un Lab peut avoir plusieurs calendriers : event_id (via list_calendars) cible l'un d'eux, sinon le premier du Lab.",
10
10
  inputSchema: {
11
11
  type: 'object',
12
12
  properties: {
13
13
  lab: { type: 'string' },
14
+ event_id: { type: 'number', minimum: 1, description: 'Calendrier vise (list_calendars). Defaut : le premier du Lab.' },
14
15
  name: { type: 'string', maxLength: 200 },
15
16
  location_type: { type: 'string', enum: ['google_meet', 'zoom', 'phone', 'in_person'] },
16
17
  description: { type: 'string' },
@@ -36,6 +37,7 @@ export const updateCalendarTool = {
36
37
  },
37
38
  zodSchema: z.object({
38
39
  lab: z.string(),
40
+ event_id: z.number().int().positive().optional(),
39
41
  name: z.string().max(200).optional(),
40
42
  location_type: z.enum(['google_meet', 'zoom', 'phone', 'in_person']).optional(),
41
43
  description: z.string().nullable().optional(),
@@ -5,11 +5,12 @@ import { z } from 'zod';
5
5
  import { http, formatApiError } from '../http.js';
6
6
  export const updateCalendarAvailabilityTool = {
7
7
  name: 'update_calendar_availability',
8
- description: "Definit les disponibilites hebdomadaires du Calendrier IA. days est un tableau (max 7) d'un objet par jour : day_of_week (0=dimanche..6=samedi), is_available, start_time et end_time au format HH:MM (end > start).",
8
+ description: "Definit les disponibilites hebdomadaires du Calendrier IA. days est un tableau (max 7) d'un objet par jour : day_of_week (0=dimanche..6=samedi), is_available, start_time et end_time au format HH:MM (end > start). Un Lab peut avoir plusieurs calendriers : event_id (via list_calendars) cible l'un d'eux, sinon le premier du Lab.",
9
9
  inputSchema: {
10
10
  type: 'object',
11
11
  properties: {
12
12
  lab: { type: 'string' },
13
+ event_id: { type: 'number', minimum: 1, description: 'Calendrier vise (list_calendars). Defaut : le premier du Lab.' },
13
14
  days: {
14
15
  type: 'array',
15
16
  maxItems: 7,
@@ -31,6 +32,7 @@ export const updateCalendarAvailabilityTool = {
31
32
  },
32
33
  zodSchema: z.object({
33
34
  lab: z.string(),
35
+ event_id: z.number().int().positive().optional(),
34
36
  days: z.array(z.object({
35
37
  day_of_week: z.number().int().min(0).max(6),
36
38
  is_available: z.boolean(),
@@ -41,7 +43,7 @@ export const updateCalendarAvailabilityTool = {
41
43
  async handler(args) {
42
44
  const p = this.zodSchema.parse(args);
43
45
  try {
44
- const { data } = await http.put(`/mcp/labs/${encodeURIComponent(p.lab)}/calendar/availability`, { days: p.days });
46
+ const { data } = await http.put(`/mcp/labs/${encodeURIComponent(p.lab)}/calendar/availability`, { days: p.days, event_id: p.event_id });
45
47
  return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
46
48
  }
47
49
  catch (err) {
@@ -5,11 +5,12 @@ import { z } from 'zod';
5
5
  import { http, formatApiError } from '../http.js';
6
6
  export const updateCalendarDisqualifRulesTool = {
7
7
  name: 'update_calendar_disqualif_rules',
8
- description: "Remplace les regles de disqualification automatique des leads du Calendrier IA. rules est le jeu complet de regles : condition_type = phone_country (indicatif telephonique) ou question_answer (reponse a une question, question_id requis), disqualifying_values = valeurs qui disqualifient. Envoyer un tableau vide efface toutes les regles.",
8
+ description: "Remplace les regles de disqualification automatique des leads du Calendrier IA. rules est le jeu complet de regles : condition_type = phone_country (indicatif telephonique) ou question_answer (reponse a une question, question_id requis), disqualifying_values = valeurs qui disqualifient. Envoyer un tableau vide efface toutes les regles. Un Lab peut avoir plusieurs calendriers : event_id (via list_calendars) cible l'un d'eux, sinon le premier du Lab.",
9
9
  inputSchema: {
10
10
  type: 'object',
11
11
  properties: {
12
12
  lab: { type: 'string' },
13
+ event_id: { type: 'number', minimum: 1, description: 'Calendrier vise (list_calendars). Defaut : le premier du Lab.' },
13
14
  rules: {
14
15
  type: 'array',
15
16
  items: {
@@ -29,6 +30,7 @@ export const updateCalendarDisqualifRulesTool = {
29
30
  },
30
31
  zodSchema: z.object({
31
32
  lab: z.string(),
33
+ event_id: z.number().int().positive().optional(),
32
34
  rules: z.array(z.object({
33
35
  condition_type: z.enum(['phone_country', 'question_answer']),
34
36
  question_id: z.number().int().positive().nullable().optional(),
@@ -38,7 +40,7 @@ export const updateCalendarDisqualifRulesTool = {
38
40
  async handler(args) {
39
41
  const p = this.zodSchema.parse(args);
40
42
  try {
41
- const { data } = await http.put(`/mcp/labs/${encodeURIComponent(p.lab)}/calendar/disqualif-rules`, { rules: p.rules });
43
+ const { data } = await http.put(`/mcp/labs/${encodeURIComponent(p.lab)}/calendar/disqualif-rules`, { rules: p.rules, event_id: p.event_id });
42
44
  return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
43
45
  }
44
46
  catch (err) {
@@ -5,11 +5,12 @@ import { z } from 'zod';
5
5
  import { http, formatApiError } from '../http.js';
6
6
  export const updateCalendarFollowupConfigTool = {
7
7
  name: 'update_calendar_followup_config',
8
- description: "Configure les relances automatiques des prospects du Calendrier IA (max 2). trigger_type = abandon_step1 (a quitte au formulaire) ou no_booking (n'a pas reserve). Pour chaque : enabled, delay_hours (1..720), message_template optionnel.",
8
+ description: "Configure les relances automatiques des prospects du Calendrier IA (max 2). trigger_type = abandon_step1 (a quitte au formulaire) ou no_booking (n'a pas reserve). Pour chaque : enabled, delay_hours (1..720), message_template optionnel. Un Lab peut avoir plusieurs calendriers : event_id (via list_calendars) cible l'un d'eux, sinon le premier du Lab.",
9
9
  inputSchema: {
10
10
  type: 'object',
11
11
  properties: {
12
12
  lab: { type: 'string' },
13
+ event_id: { type: 'number', minimum: 1, description: 'Calendrier vise (list_calendars). Defaut : le premier du Lab.' },
13
14
  configs: {
14
15
  type: 'array',
15
16
  maxItems: 2,
@@ -31,6 +32,7 @@ export const updateCalendarFollowupConfigTool = {
31
32
  },
32
33
  zodSchema: z.object({
33
34
  lab: z.string(),
35
+ event_id: z.number().int().positive().optional(),
34
36
  configs: z.array(z.object({
35
37
  trigger_type: z.enum(['abandon_step1', 'no_booking']),
36
38
  enabled: z.boolean(),
@@ -41,7 +43,7 @@ export const updateCalendarFollowupConfigTool = {
41
43
  async handler(args) {
42
44
  const p = this.zodSchema.parse(args);
43
45
  try {
44
- const { data } = await http.put(`/mcp/labs/${encodeURIComponent(p.lab)}/calendar/followup-config`, { configs: p.configs });
46
+ const { data } = await http.put(`/mcp/labs/${encodeURIComponent(p.lab)}/calendar/followup-config`, { configs: p.configs, event_id: p.event_id });
45
47
  return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
46
48
  }
47
49
  catch (err) {
@@ -6,10 +6,12 @@
6
6
  */
7
7
  import { z } from 'zod';
8
8
  import { http, formatApiError } from '../http.js';
9
+ /** Polices acceptees par le back (LinkInBioPage::FONTS). */
10
+ const LIB_FONTS = ['fredoka', 'nunito', 'inter', 'dm-sans', 'poppins', 'montserrat', 'outfit', 'space-grotesk', 'playfair', 'lora', 'bricolage', 'bebas', 'caveat'];
9
11
  const SOCIAL_KEYS = ['instagram', 'tiktok', 'youtube', 'x', 'linkedin', 'facebook', 'whatsapp', 'telegram'];
10
12
  export const updateLinkInBioTool = {
11
13
  name: 'update_link_in_bio',
12
- description: "Upsert (remplacement) de la page Link in Bio. Cree la page si absente. C'est un upsert COMPLET : tu dois TOUJOURS fournir slug, display_name, photo_layout et theme (fais get_link_in_bio d'abord pour recuperer les valeurs actuelles, puis renvoie-les avec tes modifs). Autres champs : bio, accent_color (#RRGGBB), desktop_layout (split/centered), avatar_url, banner_url, portrait_url, socials{instagram, tiktok, youtube, x, linkedin, facebook, whatsapp, telegram}. theme = light | dark | violet | yellow. photo_layout = avatar | banner | portrait. Les uploads d'images (avatar/banniere) se font via le front — ici on passe des URLs. Attention : changer le slug a un cooldown de 30 jours.",
14
+ description: "Upsert (remplacement) de la page Link in Bio. Cree la page si absente. C'est un upsert COMPLET : tu dois TOUJOURS fournir slug, display_name, photo_layout et theme (fais get_link_in_bio d'abord pour recuperer les valeurs actuelles, puis renvoie-les avec tes modifs). Autres champs : bio, accent_color (#RRGGBB), desktop_layout (split/centered), font_title / font_subtitle / font_body (polices de toute la page : titre = nom, sous-titres = titres des liens et boutons, texte = bio et descriptions ; null = police par defaut ; omis = inchange), avatar_url, banner_url, portrait_url, socials{instagram, tiktok, youtube, x, linkedin, facebook, whatsapp, telegram}. theme = light | dark | violet | yellow. photo_layout = avatar | banner | portrait. Les uploads d'images (avatar/banniere) se font via le front — ici on passe des URLs. Attention : changer le slug a un cooldown de 30 jours.",
13
15
  inputSchema: {
14
16
  type: 'object',
15
17
  properties: {
@@ -21,6 +23,9 @@ export const updateLinkInBioTool = {
21
23
  accent_color: { type: 'string', pattern: '^#[0-9A-Fa-f]{6}$' },
22
24
  photo_layout: { type: 'string', enum: ['avatar', 'banner', 'portrait'] },
23
25
  desktop_layout: { type: 'string', enum: ['split', 'centered'] },
26
+ font_title: { type: ['string', 'null'], enum: [...LIB_FONTS, null] },
27
+ font_subtitle: { type: ['string', 'null'], enum: [...LIB_FONTS, null] },
28
+ font_body: { type: ['string', 'null'], enum: [...LIB_FONTS, null] },
24
29
  avatar_url: { type: 'string' },
25
30
  banner_url: { type: 'string' },
26
31
  portrait_url: { type: 'string' },
@@ -42,6 +47,9 @@ export const updateLinkInBioTool = {
42
47
  accent_color: z.string().regex(/^#[0-9A-Fa-f]{6}$/).optional(),
43
48
  photo_layout: z.enum(['avatar', 'banner', 'portrait']),
44
49
  desktop_layout: z.enum(['split', 'centered']).optional(),
50
+ font_title: z.enum(LIB_FONTS).nullable().optional(),
51
+ font_subtitle: z.enum(LIB_FONTS).nullable().optional(),
52
+ font_body: z.enum(LIB_FONTS).nullable().optional(),
45
53
  avatar_url: z.string().optional(),
46
54
  banner_url: z.string().optional(),
47
55
  portrait_url: z.string().optional(),
@@ -7,7 +7,7 @@ import { z } from 'zod';
7
7
  import { http, formatApiError } from '../http.js';
8
8
  export const updateLinkInBioBlockTool = {
9
9
  name: 'update_link_in_bio_block',
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
+ 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, layout (forme du bloc : classic = petite image + bouton a droite, par defaut ; image = texte et bouton a contour a gauche, grande image a droite ; button = image + titre en haut, gros bouton pleine largeur en dessous), button_label (texte du bouton, 60 car. max ; vide = texte par defaut du type de bloc), quiz_id (bloc quiz), calendar_event_id (bloc calendrier), lab_group_id (bloc Lab), is_active (afficher/masquer sans supprimer). Le type (kind) d'un bloc ne change pas.",
11
11
  inputSchema: {
12
12
  type: 'object',
13
13
  properties: {
@@ -15,9 +15,12 @@ export const updateLinkInBioBlockTool = {
15
15
  block_id: { type: 'number', minimum: 1 },
16
16
  title: { type: 'string', maxLength: 200 },
17
17
  subtitle: { type: 'string', maxLength: 300 },
18
+ layout: { type: 'string', enum: ['classic', 'image', 'button'] },
19
+ button_label: { type: 'string', maxLength: 60 },
18
20
  url: { type: 'string' },
19
21
  image_url: { type: 'string' },
20
22
  quiz_id: { type: 'number', minimum: 1 },
23
+ calendar_event_id: { type: 'number', minimum: 1 },
21
24
  lab_group_id: { type: 'number', minimum: 1 },
22
25
  is_active: { type: 'boolean' },
23
26
  },
@@ -29,9 +32,12 @@ export const updateLinkInBioBlockTool = {
29
32
  block_id: z.number().int().positive(),
30
33
  title: z.string().max(200).optional(),
31
34
  subtitle: z.string().max(300).optional(),
35
+ layout: z.enum(['classic', 'image', 'button']).optional(),
36
+ button_label: z.string().max(60).optional(),
32
37
  url: z.string().optional(),
33
38
  image_url: z.string().optional(),
34
39
  quiz_id: z.number().int().positive().optional(),
40
+ calendar_event_id: z.number().int().positive().optional(),
35
41
  lab_group_id: z.number().int().positive().optional(),
36
42
  is_active: z.boolean().optional(),
37
43
  }),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@connect-xlab/mcp-server",
3
- "version": "0.3.1",
3
+ "version": "0.3.3",
4
4
  "description": "Serveur MCP pour piloter ton Lab ConnectXLab depuis Claude Desktop / Cursor / n'importe quel client MCP.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",