Serveur MCP
Le serveur MCP (Model Context Protocol) de SupDesk donne aux assistants IA et aux autres outils alimentés par des LLM un accès direct au centre d’aide, au tableau de feedback, au changelog, aux fils de messages, aux programmes bêta et à la liste d’attente d’un projet.
Point de terminaison
https://mcp.supdesk.app/mcpAuthentification
Passez une clé d’API en tant que token Bearer :
Authorization: Bearer sd_live_...Générez des clés dans la console SupDesk, sous Réglages du workspace → Clés d’API.
La clé détermine le projet : aucun outil ne prend d’argument project_id. Il n’y a rien
qu’un modèle doive deviner ou puisse se tromper.
Ce que vous obtenez
Les outils disponibles dépendent du projet. Une fonctionnalité désactivée dans les Réglages du
projet voit ses outils totalement absents de tools/list, exactement comme l’API REST répond
404 sur ses routes.
| Groupe | Outils | Nécessite |
|---|---|---|
| Centre d’aide | 10 | Centre d’aide activé |
| Tableau de feedback | 5 | Tableau de feedback activé |
| Changelog | 5 | Changelog activé |
| Messages | 5 | Messages privés activés |
| Tests bêta | 8 | Bêta activé |
| Liste d’attente | 4 | Liste d’attente activée |
| Vue d’ensemble | 1 | Toujours disponible |
La lecture fonctionne sur toutes les formules. L’écriture exige une formule payante — créer,
mettre à jour ou supprimer avec une clé en lecture seule renvoie forbidden, alors que les
lectures avec cette même clé fonctionnent normalement.
Centre d’aide
list_articles— articles, la modification la plus récente d’abord. Inclut brouillons et archives ; filtrez avecstatuspour ce que les clients voient.get_article— un article par id, avec son corps en Markdown.search_articles— recherche plein texte sur les articles publiés, classée, avec extrait.create_article— crée un brouillon. Publier est un appel distinct, pour que rien n’atteigne les clients par accident.update_article— modifie n’importe quel champ ; passezstatusàpublishedouarchived.delete_article— définitif ; archivez plutôt si vous voulez conserver l’article.list_article_categories,create_article_category,update_article_category,delete_article_category— supprimer une catégorie laisse ses articles en place, sans catégorie.
Le corps des articles est du Markdown, pas du HTML. Le portail les rend via une visionneuse qui n’évalue jamais le HTML : le HTML parvient donc aux lecteurs comme du texte littéral.
create_article échoue avec limit_reached dès que le quota d’articles de la formule est
épuisé — Free 5, Pro 50, Team illimité.
Tableau de feedback
list_feedback— posts, les plus récents d’abord. Couvre les trois types (bug,feature,feedback) sauf si vous restreignez avectype.get_feedback,create_feedback,update_feedback—update_feedbackfait avancer un post dansbacklog → open → planned → in_progress → done.delete_feedback— supprime le post, ses votes et ses commentaires.
Changelog, messages, bêta et liste d’attente
Parité complète avec l’API REST, sur les mêmes requêtes :
- Changelog —
list_changelog,get_changelog_entry,create_changelog_entry,update_changelog_entry,delete_changelog_entry. Les entrées naissent en brouillon, car c’est la publication qui envoie l’e-mail d’annonce à vos abonnés. - Messages —
list_threads,get_thread(avec l’historique complet),create_thread,reply_to_thread,update_thread. - Tests bêta —
list_beta_programs,get_beta_program,create_beta_program,update_beta_program,delete_beta_program,list_beta_testers,add_beta_tester,remove_beta_tester.add_beta_testerest idempotent sur l’e-mail et renvoie le token d’acceptation ; ce serveur n’envoie pas d’e-mail, la remise de l’invitation vous revient donc. - Liste d’attente —
list_waitlist,add_waitlist_signup(idempotent sur l’e-mail),update_waitlist_signup,remove_waitlist_signup.
Vue d’ensemble
project_stats renvoie des décomptes sur tout le projet — posts de feedback par statut, fils
ouverts, articles publiés et en brouillon, et les votes utile / pas utile reçus par ces
articles — pour qu’un agent réponde à « qu’est-ce qui reste ? » sans parcourir quatre listes
paginées.
Sortie structurée
Chaque outil déclare un outputSchema et renvoie à la fois structuredContent (typé et validé)
et un bloc de texte content contenant les mêmes données en JSON. Traitez celui que votre client
prend en charge.
const result = await mcpClient.callTool("get_article", { id: "8f7a..." });
result.structuredContent.article.title; // typé
JSON.parse(result.content[0].text); // les mêmes données, en repliAnnotations
Chaque outil porte des annotations, pour qu’un client sache ce qu’il invoque avant de l’invoquer :
| Annotation | Présente sur |
|---|---|
readOnlyHint: true | list_*, get_*, search_*, project_stats |
idempotentHint: true | les lectures et update_* |
destructiveHint: true | delete_*, remove_* |
Pagination
Les outils de listage acceptent un cursor opaque et renvoient un next_cursor — repassez-le
pour la page suivante, et arrêtez-vous quand il vaut null.
let cursor;
do {
const page = await mcpClient.callTool("list_articles", { status: "published", cursor });
handle(page.structuredContent.data);
cursor = page.structuredContent.next_cursor;
} while (cursor);Un curseur est un token, pas un calcul — traitez-le comme opaque. Un curseur malformé renvoie
invalid_request plutôt que de repartir silencieusement de la première page, ce qui ferait
boucler indéfiniment un parcours paginé.
Notez qu’il s’agit d’une convention dans le schéma d’arguments de chaque outil. Le curseur au niveau du protocole MCP s’applique aux listages d’outils et de ressources, pas à ce qu’un outil renvoie ; le listage de ressources ci-dessous utilise le mécanisme réel.
Ressources
Pour attacher du contenu du projet comme contexte, plutôt que d’appeler un outil et de coller le résultat :
| URI | Contenu |
|---|---|
supdesk://articles/{slug} | Un article d’aide publié, en Markdown. |
supdesk://board | Les posts de feedback ouverts du projet, en JSON. |
Le listage d’articles est réellement paginé au niveau du protocole et couvre uniquement les articles publiés — une ressource est du contenu qu’un client peut afficher tel quel, et un brouillon n’est pas publié pour une raison.
Prompts
Deux points de départ, câblés aux données réelles du projet :
draft_article_from_thread— transforme une conversation de support résolue en article du centre d’aide, pour que le prochain client avec cette question trouve la réponse au lieu d’ouvrir un fil. Nécessite le centre d’aide et les messages privés.triage_feedback— évalue un post et recommande un statut, avec le raisonnement. Il consulte d’abordsearch_articles, pour qu’une réponse existante ressorte plutôt que du travail nouveau soit proposé.
Tous deux utilisent la capacité de complétion de MCP pour leurs arguments : pendant que vous tapez une catégorie, le serveur répond avec les slugs de catégorie réels du projet, au lieu de vous laisser deviner.
Journalisation
Les écritures en plusieurs étapes envoient des notifications de progression via la journalisation
MCP — create_article vérifie le quota de la formule, cherche un slug libre puis insère, et le
signale à chaque étape. Un client sans support de journalisation n’est pas affecté ; l’écriture a
lieu dans tous les cas.
Exemples
Claude Desktop
Ajoutez ceci à ~/Library/Application Support/Claude/claude_desktop_config.json :
{
"mcpServers": {
"sup-desk": {
"command": "npx",
"args": [
"-y",
"mcp-remote",
"https://mcp.supdesk.app/mcp",
"--header",
"Authorization: Bearer sd_live_..."
]
}
}
}Répondre à une question, ou écrire la réponse
// Vérifier si le centre d'aide le couvre déjà.
const hits = await mcpClient.callTool("search_articles", {
query: "réinitialiser mot de passe",
limit: 5
});
if (hits.structuredContent.data.length === 0) {
// Rien pour l'instant — rédigez-le. Il est créé en brouillon.
const created = await mcpClient.callTool("create_article", {
title: "Comment réinitialiser mon mot de passe ?",
body: "Ouvrez **Réglages**, puis choisissez *Réinitialiser le mot de passe*.",
category_id: "3c2b..."
});
// Publier est délibéré et distinct.
await mcpClient.callTool("update_article", {
id: created.structuredContent.article.id,
status: "published"
});
}Trier le tableau
const open = await mcpClient.callTool("list_feedback", {
status: "open",
limit: 10
});
await mcpClient.callTool("update_feedback", {
id: open.structuredContent.data[0].id,
status: "planned"
});Tout ce qui reste, en un appel
const stats = await mcpClient.callTool("project_stats", {});
// { posts_by_status: { open: 12, planned: 3, ... }, open_threads: 4,
// articles_published: 18, articles_draft: 2,
// article_helpful: 240, article_not_helpful: 11 }Limites de débit
Le serveur MCP partage les limites de l’API REST : 120 requêtes par 60 secondes et par projet. Voir Limites de débit et usage.
Erreurs
Un appel d’outil en échec renvoie isError: true, avec le même vocabulaire d’erreurs que l’API
REST :
{
"error": {
"code": "limit_reached",
"message": "Your plan allows 5 help-center articles."
}
}| Code | Signification |
|---|---|
unauthorized | Clé d’API manquante, malformée, révoquée ou inconnue. |
forbidden | Clé valide, mais la formule n’a pas d’accès en écriture à l’API. |
invalid_request | Mauvais arguments — y compris un cursor malformé. |
not_found | Aucun id de ce genre dans ce projet. |
limit_reached | Un quota de la formule est épuisé (par exemple le plafond d’articles). |
rate_limited | Trop de requêtes dans la fenêtre courante. |
internal_error | Quelque chose a échoué de notre côté. Réessayer est sans risque. |
La liste complète est documentée sous Erreurs.