@connect-xlab/mcp-server 0.1.0

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 (72) hide show
  1. package/dist/config.js +18 -0
  2. package/dist/http.js +30 -0
  3. package/dist/index.js +178 -0
  4. package/dist/tools/addComment.js +31 -0
  5. package/dist/tools/approveMember.js +29 -0
  6. package/dist/tools/banMember.js +26 -0
  7. package/dist/tools/createCategory.js +36 -0
  8. package/dist/tools/createClass.js +37 -0
  9. package/dist/tools/createEvent.js +39 -0
  10. package/dist/tools/createLabLink.js +28 -0
  11. package/dist/tools/createNewsletterEdition.js +38 -0
  12. package/dist/tools/createPodcastEpisode.js +38 -0
  13. package/dist/tools/createPodcastShow.js +34 -0
  14. package/dist/tools/createPost.js +41 -0
  15. package/dist/tools/createShopProduct.js +46 -0
  16. package/dist/tools/deleteCategory.js +26 -0
  17. package/dist/tools/deleteClass.js +26 -0
  18. package/dist/tools/deleteEvent.js +26 -0
  19. package/dist/tools/deleteLabLink.js +26 -0
  20. package/dist/tools/deletePost.js +26 -0
  21. package/dist/tools/deleteShopProduct.js +23 -0
  22. package/dist/tools/demoteAdmin.js +26 -0
  23. package/dist/tools/getEarnings.js +28 -0
  24. package/dist/tools/getLabSettings.js +23 -0
  25. package/dist/tools/getShopSales.js +35 -0
  26. package/dist/tools/getStats.js +23 -0
  27. package/dist/tools/kickMember.js +29 -0
  28. package/dist/tools/listAdmins.js +23 -0
  29. package/dist/tools/listCategories.js +29 -0
  30. package/dist/tools/listChannelSubscribers.js +35 -0
  31. package/dist/tools/listClasses.js +23 -0
  32. package/dist/tools/listComments.js +31 -0
  33. package/dist/tools/listEvents.js +33 -0
  34. package/dist/tools/listInvitations.js +23 -0
  35. package/dist/tools/listLabLinks.js +23 -0
  36. package/dist/tools/listLabs.js +32 -0
  37. package/dist/tools/listLevels.js +23 -0
  38. package/dist/tools/listMembers.js +49 -0
  39. package/dist/tools/listNewsletterEditions.js +18 -0
  40. package/dist/tools/listNewsletterSubscribers.js +31 -0
  41. package/dist/tools/listPlugins.js +23 -0
  42. package/dist/tools/listPodcastEpisodes.js +31 -0
  43. package/dist/tools/listPodcastShows.js +18 -0
  44. package/dist/tools/listPodcastSubscribers.js +26 -0
  45. package/dist/tools/listPosts.js +49 -0
  46. package/dist/tools/listShops.js +28 -0
  47. package/dist/tools/newsletterDashboard.js +18 -0
  48. package/dist/tools/pinPost.js +31 -0
  49. package/dist/tools/promoteAdmin.js +26 -0
  50. package/dist/tools/publishPodcastEpisode.js +31 -0
  51. package/dist/tools/rejectMember.js +29 -0
  52. package/dist/tools/revokeInvitation.js +26 -0
  53. package/dist/tools/searchMembers.js +33 -0
  54. package/dist/tools/sendDm.js +35 -0
  55. package/dist/tools/sendInvitation.js +34 -0
  56. package/dist/tools/sendNewsletterEdition.js +23 -0
  57. package/dist/tools/setMetaPixel.js +34 -0
  58. package/dist/tools/setWelcomeDm.js +38 -0
  59. package/dist/tools/togglePlugin.js +27 -0
  60. package/dist/tools/unbanMember.js +26 -0
  61. package/dist/tools/unpublishPodcastEpisode.js +31 -0
  62. package/dist/tools/updateCategory.js +38 -0
  63. package/dist/tools/updateClass.js +42 -0
  64. package/dist/tools/updateEvent.js +44 -0
  65. package/dist/tools/updateLabLink.js +34 -0
  66. package/dist/tools/updateLabSettings.js +46 -0
  67. package/dist/tools/updateLevel.js +34 -0
  68. package/dist/tools/updatePodcastEpisode.js +40 -0
  69. package/dist/tools/updatePost.js +36 -0
  70. package/dist/tools/updateShopProduct.js +46 -0
  71. package/dist/tools/updateSlug.js +29 -0
  72. package/package.json +29 -0
@@ -0,0 +1,27 @@
1
+ import { z } from 'zod';
2
+ import { http, formatApiError } from '../http.js';
3
+ export const togglePluginTool = {
4
+ name: 'toggle_plugin',
5
+ description: "Active ou desactive un plug-in par son meta_type. Valeurs typiques : 'facebook' (Meta Pixel), 'google' (Google Ads).",
6
+ inputSchema: {
7
+ type: 'object',
8
+ properties: {
9
+ lab: { type: 'string' },
10
+ meta_type: { type: 'string', description: 'facebook | google | ...' },
11
+ enabled: { type: 'boolean' },
12
+ },
13
+ required: ['lab', 'meta_type', 'enabled'],
14
+ additionalProperties: false,
15
+ },
16
+ zodSchema: z.object({ lab: z.string(), meta_type: z.string(), enabled: z.boolean() }),
17
+ async handler(args) {
18
+ const parsed = this.zodSchema.parse(args);
19
+ try {
20
+ const { data } = await http.put(`/mcp/labs/${encodeURIComponent(parsed.lab)}/plugins/${encodeURIComponent(parsed.meta_type)}/toggle`, { enabled: parsed.enabled });
21
+ return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
22
+ }
23
+ catch (err) {
24
+ return { isError: true, content: [{ type: 'text', text: formatApiError(err) }] };
25
+ }
26
+ },
27
+ };
@@ -0,0 +1,26 @@
1
+ import { z } from 'zod';
2
+ import { http, formatApiError } from '../http.js';
3
+ export const unbanMemberTool = {
4
+ name: 'unban_member',
5
+ description: "Restaure un membre banni : status 'blocked' -> 'approved'. Idempotent.",
6
+ inputSchema: {
7
+ type: 'object',
8
+ properties: {
9
+ lab: { type: 'string' },
10
+ member_id: { type: 'number' },
11
+ },
12
+ required: ['lab', 'member_id'],
13
+ additionalProperties: false,
14
+ },
15
+ zodSchema: z.object({ lab: z.string(), member_id: z.number().int().positive() }),
16
+ async handler(args) {
17
+ const parsed = this.zodSchema.parse(args);
18
+ try {
19
+ const { data } = await http.put(`/mcp/labs/${encodeURIComponent(parsed.lab)}/members/${parsed.member_id}/unban`);
20
+ return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
21
+ }
22
+ catch (err) {
23
+ return { isError: true, content: [{ type: 'text', text: formatApiError(err) }] };
24
+ }
25
+ },
26
+ };
@@ -0,0 +1,31 @@
1
+ import { z } from 'zod';
2
+ import { http, formatApiError } from '../http.js';
3
+ export const unpublishPodcastEpisodeTool = {
4
+ name: 'unpublish_podcast_episode',
5
+ description: "Depublie un episode (published -> draft + published_at=null). Le retire de la vue publique.",
6
+ inputSchema: {
7
+ type: 'object',
8
+ properties: {
9
+ lab: { type: 'string' },
10
+ show_id: { type: 'number' },
11
+ episode_id: { type: 'number' },
12
+ },
13
+ required: ['lab', 'show_id', 'episode_id'],
14
+ additionalProperties: false,
15
+ },
16
+ zodSchema: z.object({
17
+ lab: z.string(),
18
+ show_id: z.number().int().positive(),
19
+ episode_id: z.number().int().positive(),
20
+ }),
21
+ async handler(args) {
22
+ const p = this.zodSchema.parse(args);
23
+ try {
24
+ const { data } = await http.patch(`/mcp/labs/${encodeURIComponent(p.lab)}/podcast/shows/${p.show_id}/episodes/${p.episode_id}/unpublish`);
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,38 @@
1
+ import { z } from 'zod';
2
+ import { http, formatApiError } from '../http.js';
3
+ export const updateCategoryTool = {
4
+ name: 'update_category',
5
+ description: "Met a jour une categorie (nom, description, privee, niveau min). Champs optionnels.",
6
+ inputSchema: {
7
+ type: 'object',
8
+ properties: {
9
+ lab: { type: 'string' },
10
+ category_id: { type: 'number' },
11
+ name: { type: 'string', maxLength: 30 },
12
+ description: { type: 'string', maxLength: 255 },
13
+ is_private: { type: 'boolean' },
14
+ min_user_level: { type: 'number', minimum: 0 },
15
+ },
16
+ required: ['lab', 'category_id'],
17
+ additionalProperties: false,
18
+ },
19
+ zodSchema: z.object({
20
+ lab: z.string(),
21
+ category_id: z.number().int().positive(),
22
+ name: z.string().max(30).optional(),
23
+ description: z.string().max(255).optional(),
24
+ is_private: z.boolean().optional(),
25
+ min_user_level: z.number().int().min(0).nullable().optional(),
26
+ }),
27
+ async handler(args) {
28
+ const parsed = this.zodSchema.parse(args);
29
+ const { lab, category_id, ...body } = parsed;
30
+ try {
31
+ const { data } = await http.put(`/mcp/labs/${encodeURIComponent(lab)}/categories/${category_id}`, 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,42 @@
1
+ import { z } from 'zod';
2
+ import { http, formatApiError } from '../http.js';
3
+ export const updateClassTool = {
4
+ name: 'update_class',
5
+ description: "Met a jour une classe/cours (nom, description, publication, prix, niveau min). Champs optionnels : n'envoie que ceux que tu veux changer. Toggle is_published pour publier/depublier.",
6
+ inputSchema: {
7
+ type: 'object',
8
+ properties: {
9
+ lab: { type: 'string' },
10
+ class_id: { type: 'number' },
11
+ name: { type: 'string', maxLength: 50 },
12
+ description: { type: 'string', maxLength: 400 },
13
+ access: { type: 'string', maxLength: 255, description: "'all' | 'paid' | autre valeur libre" },
14
+ min_user_level: { type: 'number', minimum: 0 },
15
+ is_published: { type: 'boolean' },
16
+ price: { type: 'string', description: "Prix en euros (chaine, ex '19.90'). null pour gratuit." },
17
+ },
18
+ required: ['lab', 'class_id'],
19
+ additionalProperties: false,
20
+ },
21
+ zodSchema: z.object({
22
+ lab: z.string(),
23
+ class_id: z.number().int().positive(),
24
+ name: z.string().max(50).optional(),
25
+ description: z.string().max(400).optional(),
26
+ access: z.string().max(255).optional(),
27
+ min_user_level: z.number().int().min(0).nullable().optional(),
28
+ is_published: z.boolean().optional(),
29
+ price: z.string().max(255).nullable().optional(),
30
+ }),
31
+ async handler(args) {
32
+ const parsed = this.zodSchema.parse(args);
33
+ const { lab, class_id, ...body } = parsed;
34
+ try {
35
+ const { data } = await http.put(`/mcp/labs/${encodeURIComponent(lab)}/classes/${class_id}`, body);
36
+ return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
37
+ }
38
+ catch (err) {
39
+ return { isError: true, content: [{ type: 'text', text: formatApiError(err) }] };
40
+ }
41
+ },
42
+ };
@@ -0,0 +1,44 @@
1
+ import { z } from 'zod';
2
+ import { http, formatApiError } from '../http.js';
3
+ export const updateEventTool = {
4
+ name: 'update_event',
5
+ description: "Met a jour un evenement du calendrier (titre, description, date, duree, lien, reminder). Champs optionnels.",
6
+ inputSchema: {
7
+ type: 'object',
8
+ properties: {
9
+ lab: { type: 'string' },
10
+ event_id: { type: 'number' },
11
+ title: { type: 'string', maxLength: 50 },
12
+ description: { type: 'string' },
13
+ event_date: { type: 'string', description: 'ISO 8601 date' },
14
+ timezone: { type: 'string' },
15
+ duration: { type: 'string' },
16
+ link: { type: 'string' },
17
+ reminder_enabled: { type: 'boolean' },
18
+ },
19
+ required: ['lab', 'event_id'],
20
+ additionalProperties: false,
21
+ },
22
+ zodSchema: z.object({
23
+ lab: z.string(),
24
+ event_id: z.number().int().positive(),
25
+ title: z.string().max(50).optional(),
26
+ description: z.string().optional(),
27
+ event_date: z.string().optional(),
28
+ timezone: z.string().optional(),
29
+ duration: z.string().optional(),
30
+ link: z.string().nullable().optional(),
31
+ reminder_enabled: z.boolean().optional(),
32
+ }),
33
+ async handler(args) {
34
+ const parsed = this.zodSchema.parse(args);
35
+ const { lab, event_id, ...body } = parsed;
36
+ try {
37
+ const { data } = await http.put(`/mcp/labs/${encodeURIComponent(lab)}/events/${event_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,34 @@
1
+ import { z } from 'zod';
2
+ import { http, formatApiError } from '../http.js';
3
+ export const updateLabLinkTool = {
4
+ name: 'update_lab_link',
5
+ description: "Met a jour un lien sidebar (label ou url). Champs optionnels.",
6
+ inputSchema: {
7
+ type: 'object',
8
+ properties: {
9
+ lab: { type: 'string' },
10
+ link_id: { type: 'number' },
11
+ label: { type: 'string', maxLength: 30 },
12
+ url: { type: 'string', maxLength: 255 },
13
+ },
14
+ required: ['lab', 'link_id'],
15
+ additionalProperties: false,
16
+ },
17
+ zodSchema: z.object({
18
+ lab: z.string(),
19
+ link_id: z.number().int().positive(),
20
+ label: z.string().max(30).optional(),
21
+ url: z.string().max(255).optional(),
22
+ }),
23
+ async handler(args) {
24
+ const parsed = this.zodSchema.parse(args);
25
+ const { lab, link_id, ...body } = parsed;
26
+ try {
27
+ const { data } = await http.put(`/mcp/labs/${encodeURIComponent(lab)}/links/${link_id}`, 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,46 @@
1
+ import { z } from 'zod';
2
+ import { http, formatApiError } from '../http.js';
3
+ export const updateLabSettingsTool = {
4
+ name: 'update_lab_settings',
5
+ description: "Met a jour les settings d'un Lab. Tous les champs sont optionnels — n'envoie que ceux que tu veux changer. Attention : changer is_free ou monthly_price_eur impacte les futurs paiements. Demande confirmation user.",
6
+ inputSchema: {
7
+ type: 'object',
8
+ properties: {
9
+ lab: { type: 'string' },
10
+ name: { type: 'string', maxLength: 255 },
11
+ description: { type: 'string' },
12
+ full_description: { type: 'string' },
13
+ is_free: { type: 'boolean' },
14
+ monthly_price_eur: { type: 'number', minimum: 0 },
15
+ trial_period_days: { type: 'number', minimum: 0 },
16
+ trial_enabled: { type: 'boolean' },
17
+ affiliation_enabled: { type: 'boolean' },
18
+ affiliation_percent: { type: 'number', minimum: 0, maximum: 100 },
19
+ },
20
+ required: ['lab'],
21
+ additionalProperties: false,
22
+ },
23
+ zodSchema: z.object({
24
+ lab: z.string(),
25
+ name: z.string().max(255).optional(),
26
+ description: z.string().optional(),
27
+ full_description: z.string().optional(),
28
+ is_free: z.boolean().optional(),
29
+ monthly_price_eur: z.number().min(0).optional(),
30
+ trial_period_days: z.number().int().min(0).optional(),
31
+ trial_enabled: z.boolean().optional(),
32
+ affiliation_enabled: z.boolean().optional(),
33
+ affiliation_percent: z.number().int().min(0).max(100).optional(),
34
+ }),
35
+ async handler(args) {
36
+ const parsed = this.zodSchema.parse(args);
37
+ const { lab, ...body } = parsed;
38
+ try {
39
+ const { data } = await http.put(`/mcp/labs/${encodeURIComponent(lab)}/settings`, body);
40
+ return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
41
+ }
42
+ catch (err) {
43
+ return { isError: true, content: [{ type: 'text', text: formatApiError(err) }] };
44
+ }
45
+ },
46
+ };
@@ -0,0 +1,34 @@
1
+ import { z } from 'zod';
2
+ import { http, formatApiError } from '../http.js';
3
+ export const updateLevelTool = {
4
+ name: 'update_level',
5
+ description: "Renomme un niveau XP ou modifie le seuil de points requis pour l'atteindre. Champs optionnels.",
6
+ inputSchema: {
7
+ type: 'object',
8
+ properties: {
9
+ lab: { type: 'string' },
10
+ level_id: { type: 'number' },
11
+ name: { type: 'string', maxLength: 255 },
12
+ points_threshold: { type: 'number', minimum: 0 },
13
+ },
14
+ required: ['lab', 'level_id'],
15
+ additionalProperties: false,
16
+ },
17
+ zodSchema: z.object({
18
+ lab: z.string(),
19
+ level_id: z.number().int().positive(),
20
+ name: z.string().max(255).optional(),
21
+ points_threshold: z.number().int().min(0).optional(),
22
+ }),
23
+ async handler(args) {
24
+ const parsed = this.zodSchema.parse(args);
25
+ const { lab, level_id, ...body } = parsed;
26
+ try {
27
+ const { data } = await http.put(`/mcp/labs/${encodeURIComponent(lab)}/levels/${level_id}`, 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,40 @@
1
+ import { z } from 'zod';
2
+ import { http, formatApiError } from '../http.js';
3
+ export const updatePodcastEpisodeTool = {
4
+ name: 'update_podcast_episode',
5
+ description: "Met a jour un episode podcast (titre, description, access, season). Champs optionnels.",
6
+ inputSchema: {
7
+ type: 'object',
8
+ properties: {
9
+ lab: { type: 'string' },
10
+ show_id: { type: 'number' },
11
+ episode_id: { type: 'number' },
12
+ title: { type: 'string', maxLength: 255 },
13
+ description: { type: 'string' },
14
+ access: { type: 'string', enum: ['free', 'premium'] },
15
+ season: { type: 'number', minimum: 1 },
16
+ },
17
+ required: ['lab', 'show_id', 'episode_id'],
18
+ additionalProperties: false,
19
+ },
20
+ zodSchema: z.object({
21
+ lab: z.string(),
22
+ show_id: z.number().int().positive(),
23
+ episode_id: z.number().int().positive(),
24
+ title: z.string().max(255).optional(),
25
+ description: z.string().nullable().optional(),
26
+ access: z.enum(['free', 'premium']).optional(),
27
+ season: z.number().int().min(1).optional(),
28
+ }),
29
+ async handler(args) {
30
+ const p = this.zodSchema.parse(args);
31
+ const { lab, show_id, episode_id, ...body } = p;
32
+ try {
33
+ const { data } = await http.put(`/mcp/labs/${encodeURIComponent(lab)}/podcast/shows/${show_id}/episodes/${episode_id}`, 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,36 @@
1
+ import { z } from 'zod';
2
+ import { http, formatApiError } from '../http.js';
3
+ export const updatePostTool = {
4
+ name: 'update_post',
5
+ description: "Modifie un post existant (titre / corps / categorie). Champs optionnels : n'envoie que ce que tu veux changer. IMPORTANT : action ecriture, demander confirmation.",
6
+ inputSchema: {
7
+ type: 'object',
8
+ properties: {
9
+ lab: { type: 'string' },
10
+ post_id: { type: 'number' },
11
+ title: { type: 'string', maxLength: 255 },
12
+ body: { type: 'string' },
13
+ category_id: { type: 'number' },
14
+ },
15
+ required: ['lab', 'post_id'],
16
+ additionalProperties: false,
17
+ },
18
+ zodSchema: z.object({
19
+ lab: z.string(),
20
+ post_id: z.number().int().positive(),
21
+ title: z.string().max(255).optional(),
22
+ body: z.string().optional(),
23
+ category_id: z.number().int().positive().optional(),
24
+ }),
25
+ async handler(args) {
26
+ const parsed = this.zodSchema.parse(args);
27
+ const { lab, post_id, ...body } = parsed;
28
+ try {
29
+ const { data } = await http.put(`/mcp/labs/${encodeURIComponent(lab)}/posts/${post_id}`, 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,46 @@
1
+ import { z } from 'zod';
2
+ import { http, formatApiError } from '../http.js';
3
+ export const updateShopProductTool = {
4
+ name: 'update_shop_product',
5
+ description: "Modifie un produit boutique (titre, prix, description, publication). Champs optionnels. Prix en centimes.",
6
+ inputSchema: {
7
+ type: 'object',
8
+ properties: {
9
+ lab: { type: 'string' },
10
+ shop_id: { type: 'number' },
11
+ title: { type: 'string', maxLength: 255 },
12
+ short_description: { type: 'string' },
13
+ description: { type: 'string' },
14
+ price_cents: { type: 'number', minimum: 0 },
15
+ payment_mode: { type: 'string', enum: ['external', 'stripe'] },
16
+ payment_link_text: { type: 'string', maxLength: 255 },
17
+ support_email: { type: 'string', format: 'email' },
18
+ is_published: { type: 'boolean' },
19
+ },
20
+ required: ['lab', 'shop_id'],
21
+ additionalProperties: false,
22
+ },
23
+ zodSchema: z.object({
24
+ lab: z.string(),
25
+ shop_id: z.number().int().positive(),
26
+ title: z.string().max(255).optional(),
27
+ short_description: z.string().optional(),
28
+ description: z.string().optional(),
29
+ price_cents: z.number().int().min(0).nullable().optional(),
30
+ payment_mode: z.enum(['external', 'stripe']).optional(),
31
+ payment_link_text: z.string().max(255).nullable().optional(),
32
+ support_email: z.string().email().nullable().optional(),
33
+ is_published: z.boolean().optional(),
34
+ }),
35
+ async handler(args) {
36
+ const p = this.zodSchema.parse(args);
37
+ const { lab, shop_id, ...body } = p;
38
+ try {
39
+ const { data } = await http.put(`/mcp/labs/${encodeURIComponent(lab)}/shops/${shop_id}`, body);
40
+ return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
41
+ }
42
+ catch (err) {
43
+ return { isError: true, content: [{ type: 'text', text: formatApiError(err) }] };
44
+ }
45
+ },
46
+ };
@@ -0,0 +1,29 @@
1
+ import { z } from 'zod';
2
+ import { http, formatApiError } from '../http.js';
3
+ export const updateSlugTool = {
4
+ name: 'update_slug',
5
+ description: "Change l'URL (slug) publique du Lab. Cooldown 30j entre 2 changements. IMPORTANT : les anciennes URLs partagees ne fonctionneront plus.",
6
+ inputSchema: {
7
+ type: 'object',
8
+ properties: {
9
+ lab: { type: 'string' },
10
+ new_slug: { type: 'string', pattern: '^[a-zA-Z0-9_-]+$', description: 'Nouveau slug (alphanumeric + tirets/underscores)' },
11
+ },
12
+ required: ['lab', 'new_slug'],
13
+ additionalProperties: false,
14
+ },
15
+ zodSchema: z.object({
16
+ lab: z.string(),
17
+ new_slug: z.string().max(255).regex(/^[a-zA-Z0-9_-]+$/),
18
+ }),
19
+ async handler(args) {
20
+ const p = this.zodSchema.parse(args);
21
+ try {
22
+ const { data } = await http.put(`/mcp/labs/${encodeURIComponent(p.lab)}/slug`, { new_slug: p.new_slug });
23
+ return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
24
+ }
25
+ catch (err) {
26
+ return { isError: true, content: [{ type: 'text', text: formatApiError(err) }] };
27
+ }
28
+ },
29
+ };
package/package.json ADDED
@@ -0,0 +1,29 @@
1
+ {
2
+ "name": "@connect-xlab/mcp-server",
3
+ "version": "0.1.0",
4
+ "description": "Serveur MCP pour piloter ton Lab ConnectXLab depuis Claude Desktop / Cursor / n'importe quel client MCP.",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "bin": { "connectxlab-mcp": "dist/index.js" },
8
+ "files": ["dist"],
9
+ "scripts": {
10
+ "build": "tsc",
11
+ "dev": "tsx watch src/index.ts",
12
+ "start": "node dist/index.js",
13
+ "prepublishOnly": "npm run build"
14
+ },
15
+ "engines": { "node": ">=20" },
16
+ "keywords": ["mcp", "model-context-protocol", "connectxlab", "claude"],
17
+ "author": "ConnectXLab",
18
+ "license": "MIT",
19
+ "dependencies": {
20
+ "@modelcontextprotocol/sdk": "^1.0.0",
21
+ "axios": "^1.7.0",
22
+ "zod": "^3.23.0"
23
+ },
24
+ "devDependencies": {
25
+ "@types/node": "^22.0.0",
26
+ "tsx": "^4.19.0",
27
+ "typescript": "^5.5.0"
28
+ }
29
+ }