Aller au contenu

Serveur MCP

La Messagerie expose un serveur MCP (Model Context Protocol) à /mcp, sur le serveur du chat. Un agent — Claude Code, Claude Desktop, un assistant de code, votre propre agent — y lit les conversations, cherche dans les messages et, si son jeton le permet, répond aux visiteurs, écrit des notes, affecte, résout et étiquette.

https://chat.exemple.fr/mcp

Ses outils passent par le même service que l’API REST, donc par les mêmes fonctions que l’inbox : ce qu’écrit un agent apparaît aussitôt aux conseillers.

Créez un jeton dans Administration › API et MCP, bouton Nouveau jeton, avec MCP coché sous Accès. Le même jeton peut aussi ouvrir l’API REST. Ses droits, ses boîtes, sa durée et sa révocation sont ceux de tout jeton : voir Un jeton.

À sa création, la fenêtre Jeton créé donne, prêts à copier, la commande pour Claude Code et la configuration mcpServers, avec l’adresse de votre serveur.

Fenêtre de terminal
export MESSAGERIE_TOKEN="msg_k3v9x2ma_…"
claude mcp add --transport http messagerie https://chat.exemple.fr/mcp \
--header "Authorization: Bearer $MESSAGERIE_TOKEN"

Le shell remplace $MESSAGERIE_TOKEN par sa valeur : c’est elle que Claude Code enregistre. Pour ne pas l’écrire dans la configuration, déclarez plutôt le serveur dans le .mcp.json du projet, où Claude Code lit la variable au démarrage :

.mcp.json
{
"mcpServers": {
"messagerie": {
"type": "http",
"url": "https://chat.exemple.fr/mcp",
"headers": { "Authorization": "Bearer ${MESSAGERIE_TOKEN}" }
}
}
}

Tout client qui parle le transport HTTP de MCP se branche avec deux choses : l’adresse https://chat.exemple.fr/mcp et l’en-tête Authorization: Bearer <jeton>. La plupart lisent une configuration de la forme ci-dessus ; la façon d’y citer une variable d’environnement dépend du client.

Un client qui ne sait lancer que des processus locaux (stdio) passe par un relais, comme mcp-remote :

mcpServers
{
"mcpServers": {
"messagerie": {
"command": "npx",
"args": [
"-y",
"mcp-remote",
"https://chat.exemple.fr/mcp",
"--header",
"Authorization:Bearer ${MESSAGERIE_TOKEN}"
],
"env": { "MESSAGERIE_TOKEN": "msg_…" }
}
}
}
  • Transport : HTTP « Streamable », réponses JSON, jamais de flux. Seul POST /mcp répond ; GET et DELETE répondent 405.
  • Session : aucune. Chaque requête vérifie le jeton et ses droits ; une révocation vaut dès la requête suivante.
  • Page web : une requête qui porte un en-tête Origin est refusée (403) — un client MCP est un programme, pas une page ouverte dans un navigateur.
  • Serveur : il se présente comme messagerie, version 0.1.0, avec des instructions pour l’agent : commencer par whoami, puis list_conversations et get_conversation ; relire une réponse avant send_reply, car elle part au visiteur.
  • Limite : 240 requêtes par minute et par jeton ; au-delà, 429.

Les outils ont des noms anglais et des descriptions en français. Chacun annonce sa nature : un outil de lecture est marqué readOnlyHint, un outil d’écriture ne l’est pas, et aucun n’est destructif (destructiveHint: false). Chaque outil rend son résultat en JSON, dans un bloc de texte.

OutilDroitsCe qu’il fait
whoamilectureLe jeton utilisé : son nom, ses droits, les boîtes qu’il atteint.
list_inboxeslectureLes boîtes de réception que le jeton atteint.
list_agentslectureLes conseillers actifs, à qui une conversation peut être affectée.
list_conversationslectureLes conversations, les plus récentes d’abord.
get_conversationlectureUne conversation entière, avec tous ses messages.
search_messageslectureLes messages qui contiennent tous ces mots.
list_contactslectureLes contacts, cherchés par nom, e-mail ou identifiant client.
get_contactlectureUne fiche de contact et ses conversations.
start_conversationécritureÉcrire le premier à un client, par SMS ou par e-mail.
send_replyécritureRépondre au visiteur.
add_noteécritureÉcrire une note interne.
assign_conversationécritureAffecter à un conseiller, ou remettre dans la file.
resolve_conversationécritureRésoudre.
add_tagécriturePoser une étiquette.
remove_tagécritureRetirer une étiquette.

Un jeton en lecture seule ne voit pas les outils d’écriture : ils ne figurent pas dans la liste que reçoit l’agent. Aucun outil ne supprime, et aucun ne prend une autre identité que celle du jeton.

Le jeton utilisé : son nom, ses droits (read ou write), les boîtes qu’il atteint. Sans argument. Rend label, prefix, access, surfaces, inboxIds (null : toutes les boîtes de son créateur) et expiresAt.

Les boîtes de réception que ce jeton atteint : leur identifiant et leur nom. Sans argument. Rend id, name, description.

Les conseillers actifs, à qui une conversation peut être affectée. Sans argument. Rend id, name, role (agent ou supervisor).

Les conversations, les plus récentes d’abord : le contact, l’état, le conseiller, les étiquettes et le dernier message. Non résolues par défaut.

ArgumentTypeRequisDescription
statustextenonunresolved (par défaut), ai : l’IA répond, open, pending, resolved, all.
inbox_idtextenonUne boîte de réception seulement (list_inboxes).
assignee_idtextenonUn conseiller (list_agents), ou none pour la file d’attente.
limitentiernon50 par défaut, de 1 à 200.

Chaque conversation rendue porte id, contact (son nom), email, status, inbox_id, assignee, unread, priority, sentiment, tags (leurs noms), last_message (from et text, coupé à 240 caractères) et last_message_at.

Une conversation entière : le contact, l’état, le résumé de l’IA, les métadonnées et tous les messages — du visiteur, de l’IA, des conseillers, les notes internes et les événements.

ArgumentTypeRequisDescription
conversation_idUUIDouiL’identifiant de la conversation.

Rend id, status, site, inbox_id, assignee, priority, sentiment, intent, tags, summary, data, contact (id, name, email, phone, identified) et messages. Chaque message porte id, at et from :

fromAutres champs
visitortext, files (les noms des fichiers joints)
agentauthor, text, files
noteauthor, text
aitext, confidence (0 à 1)
eventevent : ce qui s’est passé, en données ({ "type": "resolved", "agent": "…" })
handoff— l’IA a passé la main

Un message supprimé pour tout le monde ne porte que id, at et deleted: true.

Les messages qui contiennent tous ces mots, accents à part, les plus récents d’abord — 20 au plus.

ArgumentTypeRequisDescription
querytexteouiTrois caractères au moins.

Rend conversationId, messageId, contactName, author (visitor, agent, ai, note), at, body.

Les contacts — visiteurs et clients —, cherchés par nom, e-mail ou identifiant client ; 200 au plus.

ArgumentTypeRequisDescription
querytextenonTout le monde si absent.

Une fiche de contact et ses conversations.

ArgumentTypeRequisDescription
contact_idUUIDouiL’identifiant du contact.

list_contacts et get_contact rendent les mêmes objets que GET /contacts et GET /contacts/{id}.

Proposés aux jetons en Lecture et écriture seulement. Chacun rend la conversation telle qu’elle est ensuite, sous la forme de get_conversation.

Écrit le premier à un client : par SMS depuis un numéro de la messagerie, ou par e-mail — voir Écrire en premier. La conversation reste dans la file.

ArgumentTypeRequisDescription
channeltexteouisms ou email.
texttexteouiLe message.
contact_idUUIDnonUn contact connu (list_contacts).
phonetextenonPour un SMS : le numéro, +33612345678.
emailtextenonPour un e-mail : l’adresse.
nametextenonLe nom d’un nouveau contact.

Envoie une réponse au visiteur, signée du nom du jeton. Markdown léger accepté (gras, italique, listes, liens). La conversation quitte l’IA et reste dans la file ; une conversation résolue est rouverte.

ArgumentTypeRequisDescription
conversation_idUUIDouiL’identifiant de la conversation.
texttexteouiLa réponse.
resolvebooléennonRésoudre la conversation avec cette réponse.

Ajoute une note que seule l’équipe voit — jamais le visiteur.

ArgumentTypeRequisDescription
conversation_idUUIDouiL’identifiant de la conversation.
texttexteouiLa note.

Confie la conversation à un conseiller (list_agents), ou la remet dans la file avec null. Le conseiller en est averti dans l’inbox.

ArgumentTypeRequisDescription
conversation_idUUIDouiL’identifiant de la conversation.
agent_idUUID ou nullouiUn conseiller, ou null pour la file.

Marque la conversation comme résolue.

ArgumentTypeRequisDescription
conversation_idUUIDouiL’identifiant de la conversation.

Pose une étiquette sur la conversation. Une étiquette déclarée dans Administration › Réponses types et étiquettes, onglet Étiquettes, prend sa couleur.

ArgumentTypeRequisDescription
conversation_idUUIDouiL’identifiant de la conversation.
labeltexteouiLe nom de l’étiquette, 60 caractères au plus.

Retire une étiquette de la conversation.

ArgumentTypeRequisDescription
conversation_idUUIDouiL’identifiant de la conversation.
labeltexteouiLe nom de l’étiquette.

Un appel, tel que le client l’envoie :

tools/call
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "send_reply",
"arguments": {
"conversation_id": "4f1c2e8a-7b3d-4c9e-a1f0-2d5e6b7c8a90",
"text": "Votre dossier est **complet** : le virement part demain.",
"resolve": true
}
}
}

Un outil refusé ne lève pas d’erreur JSON-RPC : il rend un résultat marqué isError, dont le texte est un objet JSON — un code, et ce qu’il veut dire. L’agent le lit et peut se reprendre.

Résultat refusé
{
"isError": true,
"content": [
{
"type": "text",
"text": "{\"code\":\"TOKEN_READ_ONLY\",\"message\":\"Ce jeton ne permet que la lecture.\"}"
}
]
}
CodeSens
CONVERSATION_NOT_FOUNDCette conversation n’existe pas, ou ce jeton ne l’atteint pas.
CONTACT_NOT_FOUNDCe contact n’existe pas, ou ce jeton ne l’atteint pas.
AGENT_NOT_FOUNDCe conseiller n’existe pas ou n’est plus actif : voyez list_agents.
TOKEN_READ_ONLYCe jeton ne permet que la lecture.
EMPTY_MESSAGELe message est vide.
INVALID_REQUESTLa demande est mal formée.
RATE_LIMITEDTrop de demandes : réessayez dans une minute.

Une requête refusée avant tout outil reçoit une erreur JSON-RPC -32000, son code en message, avec le statut HTTP qui le dit :

CodeStatutCause
TOKEN_INVALID401Pas de jeton, un jeton mal formé ou inconnu, un jeton qui n’ouvre pas le MCP, ou dont le créateur n’est plus conseiller actif.
TOKEN_EXPIRED401Le jeton a expiré.
TOKEN_REVOKED401Le jeton a été révoqué.
ORIGIN_REFUSED403La requête porte un en-tête Origin : elle vient d’une page web.
METHOD_NOT_ALLOWED405Un GET ou un DELETE : il n’y a ni session ni flux à ouvrir ou fermer.
RATE_LIMITED429Plus de 240 requêtes en une minute pour ce jeton.
401 Unauthorized
{ "jsonrpc": "2.0", "error": { "code": -32000, "message": "TOKEN_REVOKED" }, "id": null }

Une fois le serveur branché, on parle à l’agent comme à un collègue ; il choisit les outils.

  • « Quelles conversations attendent dans la file depuis ce matin ? Résume-les en une ligne chacune. »
  • « Retrouve les messages qui parlent de “délai de remboursement” cette semaine, et dis-moi ce qui revient le plus. »
  • « Lis la conversation avec Léa Martin et propose une réponse — ne l’envoie pas. »
  • « Ajoute une note interne à cette conversation : contrat vérifié, dossier complet. »
  • « Étiquette “Sinistre” toutes les conversations ouvertes qui parlent d’un accident, puis confie-les à Claire Dubois. »

L’agent n’a jamais plus de droits que son jeton : ni les boîtes qu’il n’atteint pas, ni les outils d’écriture avec un jeton en lecture seule. Ce qu’il écrit est signé du nom du jeton dans le fil, et se lit comme l’action d’un conseiller.

Un logiciel libre d’Eodia, studio de logiciel IA-natif — frère de basedb.