Exemple d’elicitation MCP : support client et configuration de Claude Code

Un exemple d’elicitation MCP fonctionnel pour le support client : un serveur TypeScript qui met en pause l’escalade d’un ticket, demande à l’agent la priorité, le domaine produit et l’état de la panne via un formulaire, puis reprend. Inclut la configuration de Claude Code, la gestion du refus et de l’annulation, et une checklist de tests.

Exemple d’elicitation MCP : support client et configuration de Claude Code
Cristian Da Conceicao
Fondateur de Picasso IA

Votre agent de support tape « escalate SUP-1042 » dans Claude Code et l’outil se lance. Puis il se bloque, car personne ne lui a indiqué la priorité, le domaine produit, ni si les clients sont privés d’accès. Une configuration faible devine et alerte la mauvaise équipe. Une meilleure configuration pose la question. Cette question, envoyée depuis le serveur vers la personne devant le terminal, c’est ce que fait l’elicitation MCP. Cet exemple d’elicitation MCP pour un service de support client présente toute la boucle : le code du serveur, le schéma du formulaire, la configuration de Claude Code, et la conduite à tenir quand l’agent refuse.

Tout ce qui suit cible la révision 2025-11-25 du Model Context Protocol et le SDK TypeScript. Le serveur est assez court pour être lu d’une traite, et chaque partie correspond à une règle de la spécification.

Ce que fait réellement l’elicitation MCP

Normalement, un client MCP appelle un outil, le serveur travaille, et un résultat revient. L’elicitation ajoute une étape au milieu. Pendant que l’outil s’exécute, le serveur envoie une requête elicitation/create au client. Le client affiche à la personne une boîte de dialogue, recueille une réponse et la renvoie. L’outil poursuit alors avec des données réelles au lieu d’une supposition.

Mains tenant un formulaire papier et un stylo plume au-dessus d’un bureau en chêne

Cela fait de l’elicitation une primitive de human in the loop. Le serveur reste maître de ce dont il a besoin. Le client reste maître de la manière dont la question s’affiche, des serveurs autorisés à poser des questions, et de la possibilité pour la personne de refuser.

Mode formulaire et mode URL

La spécification définit deux modes :

  • Le mode formulaire recueille des données structurées directement dans le protocole. Le serveur envoie un court message accompagné d’un requestedSchema, et le client en tire un formulaire.
  • Le mode URL envoie la personne vers une adresse externe pour tout ce qui est sensible, comme une connexion ou un paiement. Les données ne transitent jamais par le client. La révision 2025-11-25 l’a introduit.

Les schémas de formulaire sont volontairement simples. Ce sont des objets plats, avec uniquement des propriétés primitives :

Type de schémaOptions utilesUsage typique
stringminLength, maxLength, pattern, format (email, uri, date, date-time)Adresse e-mail de contact, courte note
number ou integerminimum, maximum, defaultClients concernés
booleandefault« S’agit-il d’une panne ? »
enum à sélection uniqueenum, ou oneOf avec titresPriorité, domaine produit
enum à sélection multiplearray avec minItems et maxItemsPlateformes concernées

Les objets imbriqués et les tableaux d’objets sont volontairement exclus, afin que n’importe quel client puisse dessiner le formulaire sans rien deviner.

Trois réponses possibles

Chaque réponse porte un action :

  1. accept : la personne a soumis le formulaire, et content contient les valeurs.
  2. decline : la personne a refusé délibérément.
  3. cancel : la personne a fermé la boîte de dialogue sans rien choisir.

Votre serveur doit traiter ces trois issues comme des résultats normaux. La plupart des bugs d’elicitation viennent du fait de ne gérer que la première.

Le scénario du support client

Imaginez une équipe support avec un helpdesk plein de tickets et une petite astreinte. Les agents travaillent dans Claude Code, et un serveur MCP appelé support-desk donne au modèle une seule action d’écriture : escalate_ticket. Il prend un identifiant de ticket et confie le dossier aux bons ingénieurs.

Agent du service client avec un casque devant un bureau et deux écrans

Pourquoi deviner échoue ici

Le modèle peut lire un ticket et en déduire une priorité. Il aura souvent raison. Quand il se trompe, une question de facturation atterrit dans le canal des incidents, ou une panne de connexion reste dans une file lente pendant la nuit. Vous pourriez élargir le schéma d’entrée de l’outil en espérant que le modèle remplisse correctement chaque champ, mais un appel d’outil avec des valeurs inventées ressemble exactement à un appel avec des valeurs réelles.

L’elicitation confie la décision à la personne qui en est responsable. Le modèle fournit l’identifiant du ticket. L’humain apporte le jugement.

Les champs que demande le serveur

Cinq champs suffisent. Au-delà, les agents commencent à fermer la boîte de dialogue sans la lire.

ChampTypeRaison de sa présence
priorityenum à sélection uniqueOriente vers la bonne file d’astreinte
areaenum à sélection uniqueDésigne l’équipe responsable
affectedCustomersentier, de 1 à 10 000Distingue un utilisateur isolé d’un incident massif
customerEmailstring, format emailPermet à l’ingénieur de faire le suivi
outagebooleanOuvre le canal de l’incident

Vue de dessus d’un bureau avec un ordinateur portable, des flèches sur un carnet et des notes adhésives en séquence

Seuls priority et area sont obligatoires. Les autres ont des valeurs par défaut, si bien qu’un agent pressé peut valider et passer à la suite.

💡 Gardez le formulaire court. Chaque champ supplémentaire est une raison pour l’agent d’appuyer sur annuler.

Construire le serveur de support

Le serveur tient dans un seul fichier TypeScript, un transport stdio et deux petites dépendances.

Développeur qui tape sur un ordinateur portable dans un loft calme

Fichiers du projet et dépendances

mkdir support-desk-mcp && cd support-desk-mcp
npm init -y
npm pkg set type=module
npm install @modelcontextprotocol/sdk zod
npm install -D typescript tsx @types/node
mkdir src

Régler type sur module permet au fichier d’utiliser await au niveau supérieur et les imports ES.

L’outil d’escalade

Enregistrez ceci sous src/server.ts :

import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";

const server = new McpServer({ name: "support-desk", version: "1.0.0" });

const EscalationForm = z.object({
  priority: z.enum(["p1", "p2", "p3"]),
  area: z.enum(["billing", "login", "api", "mobile-app"]),
  affectedCustomers: z.number().int().min(1).max(10000).default(1),
  customerEmail: z.string().email().optional(),
  outage: z.boolean().default(false),
});

// Stub: swap in your helpdesk API call.
async function createEscalation(ticketId: string, data: z.infer<typeof EscalationForm>) {
  return `ESC-${Date.now().toString(36).toUpperCase()}`;
}

const reply = (text: string, isError = false) => ({
  content: [{ type: "text" as const, text }],
  isError,
});

server.registerTool(
  "escalate_ticket",
  {
    description: "Escalate a support ticket to the on-call team. Asks the agent for missing details.",
    inputSchema: { ticketId: z.string().describe("Ticket ID, for example SUP-1042") },
  },
  async ({ ticketId }) => {
    if (!server.server.getClientCapabilities()?.elicitation) {
      return reply("This client cannot show forms. Ask the agent for priority and area, then retry.", true);
    }

    const answer = await server.server.elicitInput({
      message: `Ticket ${ticketId} needs a few details before it reaches the on-call team.`,
      requestedSchema: {
        type: "object",
        properties: {
          priority: {
            type: "string",
            title: "Priority",
            oneOf: [
              { const: "p1", title: "P1 Service down" },
              { const: "p2", title: "P2 Major feature broken" },
              { const: "p3", title: "P3 Minor issue" },
            ],
          },
          area: {
            type: "string",
            title: "Product area",
            enum: ["billing", "login", "api", "mobile-app"],
          },
          affectedCustomers: {
            type: "integer",
            title: "Customers affected",
            minimum: 1,
            maximum: 10000,
            default: 1,
          },
          customerEmail: { type: "string", format: "email", title: "Customer email" },
          outage: { type: "boolean", title: "Is this an outage?", default: false },
        },
        required: ["priority", "area"],
      },
    });

    if (answer.action === "decline") {
      return reply(`The agent declined to escalate ${ticketId}. The ticket is unchanged.`);
    }
    if (answer.action === "cancel") {
      return reply(`Escalation of ${ticketId} was cancelled. Nothing was changed.`);
    }

    const parsed = EscalationForm.safeParse(answer.content);
    if (!parsed.success) {
      return reply("The form answers were invalid. Ask again.", true);
    }

    const escalationId = await createEscalation(ticketId, parsed.data);
    return reply(`Escalated ${ticketId} as ${parsed.data.priority} in ${parsed.data.area}. Reference ${escalationId}.`);
  }
);

await server.connect(new StdioServerTransport());

Remarquez que mode est absent de l’appel elicitInput. Le mode formulaire est celui par défaut, ce qui garde la requête lisible pour les anciens clients.

Vérifier d’abord les capacités du client

Les premières lignes du gestionnaire comptent plus qu’il n’y paraît. Un client qui prend en charge l’elicitation déclare une capacité elicitation lors de l’initialisation. Un objet elicitation vide compte uniquement comme mode formulaire, et un serveur ne doit jamais envoyer un mode que le client n’a pas déclaré.

Si la capacité est absente, le serveur renvoie un message d’erreur en texte brut au lieu de se bloquer. Le modèle lit ce message et demande à l’agent dans le chat. Un outil qui échoue poliment vaut mieux qu’un outil qui gèle.

💡 Dans un serveur stdio, n’écrivez jamais sur la sortie standard. Les lignes console.log parasites corrompent le flux du protocole. Envoyez les messages de débogage avec console.error.

Configurer Claude Code pas à pas

Une fois le serveur écrit, Claude Code doit savoir qu’il existe.

Mains tapant sur un ordinateur portable avec une fenêtre de terminal à l’écran

Enregistrer le serveur

Depuis n’importe quel dossier, ajoutez-le avec la CLI. Tout ce qui suit le double tiret est la commande qui lance le serveur :

claude mcp add support-desk -- npx -y tsx /absolute/path/to/support-desk-mcp/src/server.ts

Pour partager la configuration avec vos coéquipiers, ajoutez --scope project. Claude Code écrit alors un fichier .mcp.json à la racine du dépôt :

{
  "mcpServers": {
    "support-desk": {
      "type": "stdio",
      "command": "npx",
      "args": ["-y", "tsx", "/absolute/path/to/support-desk-mcp/src/server.ts"]
    }
  }
}
PortéeChargé dansPartagé avec l’équipeStocké dans
local (par défaut)Projet courant uniquementNon~/.claude.json
projectProjet courant uniquementOui, via le contrôle de version.mcp.json
userTous vos projetsNon~/.claude.json

💡 Sous Windows natif, encapsulez le lanceur : claude mcp add support-desk -- cmd /c npx -y tsx C:\path\to\src\server.ts.

Confirmer la connexion

Deux vérifications vous indiquent que le serveur fonctionne :

claude --version
claude mcp list

Dans une session, /mcp ouvre le panneau des serveurs avec l’état de connexion. Vous devriez voir support-desk listé comme connecté.

Si un serveur cesse de se connecter après une mise à jour, consultez le journal des modifications de Claude Code. Les notes de la version 2.1.287, qui a ajouté les invites d’URL venant des serveurs sur le protocole 2025-11-25, indiquent d’ajouter "bareElicitationCapability": true à l’entrée de configuration de ce serveur lorsqu’il ne se connecte plus.

Lancer le flux d’escalade

Démarrez une session dans votre projet et tapez une demande simple :

Escalate ticket SUP-1042 using the support-desk tool.

Claude appelle mcp__support-desk__escalate_ticket. L’outil se met en pause, Claude Code affiche le formulaire, et l’agent renseigne la priorité, le domaine, le nombre de clients concernés, l’e-mail et l’indicateur de panne.

Écran d’ordinateur portable affichant une boîte de dialogue simple, avec un doigt au-dessus du pavé tactile

Une fois que l’agent a validé, l’outil reprend et renvoie quelque chose comme Escalated SUP-1042 as p1 in login. Reference ESC-LQ3F9A2. Le modèle peut citer cette référence directement dans sa réponse.

Claude Code expose aussi les hooks Elicitation et ElicitationResult, filtrés sur le nom du serveur. Un script peut répondre seul à un formulaire connu, ou journaliser chaque réponse avant qu’elle n’atteigne le serveur. Réservez cela aux formulaires à faible risque dans l’automatisation, et gardez une personne devant tout ce qui touche aux clients.

Gérer le refus, l’annulation et les mauvaises saisies

Les démos du cas idéal masquent la partie qui détermine si les agents font confiance à l’outil. Les gens ferment les boîtes de dialogue, changent d’avis et se trompent dans les valeurs.

Doigt au-dessus de la touche Échap d’un ordinateur portable

Ce que chaque action doit déclencher

ActionCe que la personne a faitCe que l’outil doit faire
acceptA soumis le formulaireRevalider, puis créer l’escalade
declineA refusé délibérémentNe pas toucher au ticket, le signaler, proposer une voie manuelle
cancelA fermé la boîte de dialogueNe rien modifier, autoriser une nouvelle tentative plus tard

Remarquez l’appel safeParse dans le serveur. Les clients doivent valider les réponses par rapport au schéma, et les serveurs doivent le refaire. Les valeurs par défaut et les formats ne sont que des indications pour l’interface, pas des garanties sur les données reçues.

Renvoyez un résultat isError en cas de mauvaise saisie au lieu de lever une exception. Le modèle voit le message et peut demander à l’agent de relancer l’outil.

Les erreurs qui cassent l’elicitation

La plupart des échecs proviennent d’une courte liste de mauvaises habitudes.

Données sensibles dans les formulaires

Cadenas en laiton sur une porte en bois vert patiné

La spécification est claire : les serveurs ne doivent pas demander de mots de passe, de jetons API ni d’identifiants de paiement via le mode formulaire. Les réponses du formulaire transitent par le client, elles peuvent donc finir dans des journaux et des transcriptions. Tout élément secret relève du mode URL, où la personne le saisit sur une page que le client ne peut pas lire.

Un nom ou une adresse e-mail, c’est différent. Le serveur peut les demander, et la personne peut les relire et refuser.

Schémas imbriqués

Un requestedSchema avec un objet imbriqué ou une liste d’objets sera rejeté ou mal affiché. Aplatissez-le. Si vous avez besoin d’une liste d’éléments, lancez plusieurs petites elicitations ou acceptez un enum à sélection multiple.

Autres habitudes qui posent problème :

  • Traiter cancel comme decline, si bien qu’une boîte fermée ressemble à un refus.
  • Faire confiance à une identité saisie dans un formulaire. Identifiez les utilisateurs par l’autorisation, pas par un champ de texte.
  • Demander deux fois les mêmes détails dans une même session.
  • Oublier qu’un serveur distant doit lier son état à l’utilisateur, et pas seulement à un identifiant de session.

Une courte liste de tests

Ingénieur qualité avec un presse-papiers à checklist et deux ordinateurs portables

Lancez ces cinq cas avant que de vrais tickets ne touchent l’outil :

  • Valider avec les seules valeurs par défaut, et vérifier que affectedCustomers passe à 1.
  • Valider avec une adresse e-mail mal orthographiée, et vérifier que le serveur la rejette.
  • Refuser, et vérifier que le ticket n’a pas changé.
  • Annuler avec la touche Échap, et vérifier que rien n’est écrit.
  • Se connecter depuis un client sans elicitation, et vérifier que le message de repli en texte brut apparaît.

Vous pouvez aussi lancer le serveur sous le MCP Inspector depuis le dossier du projet avec npx @modelcontextprotocol/inspector npx tsx src/server.ts pour voir défiler les messages elicitation/create bruts.

Rédiger des réponses avec Claude Sonnet 5

Une fois que l’escalade renvoie une référence, l’agent doit encore répondre au client. Claude Sonnet 5 sur PicassoIA prend en charge cette étape de rédaction, et il lit aussi les captures d’écran, ce qui est utile quand un client joint une image d’erreur.

  1. Ouvrez la page de Claude Sonnet 5 et collez le résumé du ticket ainsi que la référence de l’escalade dans Prompt.
  2. Renseignez System Prompt une seule fois : « Vous rédigez des réponses de support courtes et calmes. Ne promettez jamais de délai de correction. »
  3. Laissez Effort sur low pour les brouillons rapides. Montez-le à medium ou high quand le ticket demande un vrai raisonnement. Selon la page du modèle, low désactive la réflexion pour des réponses plus rapides et moins coûteuses.
  4. Baissez Max Tokens depuis la valeur par défaut de 8 192 à environ 600 pour que les réponses restent courtes.
  5. Joignez une capture d’écran dans Image si le client en a envoyé une. Max Image Resolution vaut 0,5 mégapixel par défaut, ce qui suffit largement pour une boîte de dialogue d’erreur.
ParamètreValeur par défautConseil pour les réponses de support
EffortfaibleÀ monter uniquement pour les bugs complexes
Max Tokens8 192Régler vers 600
System PromptvideFixez le ton et les limites une seule fois
Max Image Resolution0,5 MPGardez tel quel pour les captures d’écran

Pour un raisonnement plus poussé, Claude Opus 4.7 figure dans la même collection. Commencez par Sonnet 5 et ne passez à un modèle supérieur que lorsqu’un brouillon passe à côté du sujet.

Créer vos propres images avec Picasso IA

Les documentations de support et les articles du centre d’aide gagnent à être illustrés de vraies images. Les scènes de bureau que vous avez vues plus haut, un casque posé sur une table ou un presse-papiers sous une lumière de fenêtre, se créent avec un seul prompt.

Essayez PicassoIA Image pour un premier jet rapide, ou Flux 2 Pro si vous voulez une texture plus fine. Un prompt qui fonctionne bien :

A support engineer at a wooden desk, 35mm lens, soft window light from the left, shallow depth of field, Kodak Portra 400 film grain, no text.

Choisissez un modèle, collez le prompt, modifiez un seul détail à chaque essai et comparez les résultats. Dix minutes d’expérimentation vous apprendront plus sur la formulation des prompts que n’importe quelle liste de règles. Ouvrez Picasso IA, créez votre première image d’en-tête et insérez-la dans votre prochain article de support.

Partager cet article

Choisissez votre langue