Le client a appelé un dimanche matin, inquiet : six commandes passées la veille au soir s’étaient retrouvées annulées automatiquement, sans qu’aucun conseiller ni aucun client n’ait demandé quoi que ce soit de ce genre. La sécurisation préventive de ce type d’accès faisant l’objet d’un article séparé, ce cas se concentre uniquement sur le diagnostic de l’incident : comment un agent MCP en théorie limité à des actions encadrées a pu déclencher six annulations sans instruction explicite.
Symptôme
Les six commandes annulées partageaient un point commun visible immédiatement dans le journal WooCommerce : toutes étaient passées entre 22h et 23h30 la veille, et toutes avaient été annulées entre 2h et 3h du matin, une plage horaire où aucun conseiller humain n’était en ligne pour interagir avec l’agent conversationnel. L’hypothèse d’une instruction humaine malveillante ou erronée a donc été écartée dès le départ.
Diagnostic
Le journal des appels d’outils MCP a montré que l’outil d’annulation avait bien été appelé six fois durant cette plage, avec à chaque fois un identifiant de session associé non pas à une conversation avec un client, mais à une tâche planifiée interne : un processus de nettoyage nocturne, écrit plusieurs mois auparavant, chargé d’annuler automatiquement les commandes restées en statut pending depuis plus de quatre heures, faute de paiement confirmé.

Le vrai problème est apparu en creusant plus loin : cette tâche de nettoyage avait été récemment réécrite pour s’appuyer sur le même serveur MCP que l’agent conversationnel, dans un souci de mutualisation du code — plutôt que d’appeler directement la fonction WooCommerce d’annulation de commande, elle passait par l’outil MCP annuler_commande, initialement conçu et validé uniquement pour un usage conversationnel avec confirmation humaine en amont.
// Code introduit récemment, censé mutualiser la logique d'annulation
add_action( 'nettoyage_commandes_nuit', function() {
$commandes_en_attente = wc_get_orders( array(
'status' => 'pending',
'date_created' => '<' . ( time() - 4 * HOUR_IN_SECONDS ),
) );
foreach ( $commandes_en_attente as $commande ) {
// Appel direct à l'outil MCP, sans passer par le garde-fou de confirmation
appeler_outil_mcp( 'annuler_commande', array(
'commande_id' => $commande->get_id(),
) );
}
} );
Le garde-fou de confirmation humaine, ajouté à l’époque uniquement dans la couche conversationnelle qui orchestrait l’agent, n’existait pas dans l’outil MCP lui-même — il avait été implémenté comme une étape de dialogue en amont de l’appel d’outil, pas comme une vérification intégrée à l’outil. La tâche de nettoyage nocturne, en appelant directement l’outil, contournait entièrement cette étape sans que personne ne l’ait anticipé au moment de la réécrire.
Correctif
Le correctif immédiat a consisté à faire revenir la tâche de nettoyage nocturne à un appel direct de la fonction WooCommerce native $order->update_status( 'cancelled', ... ), sans passer par le serveur MCP, puisqu’il s’agit d’un traitement automatisé légitime qui n’a jamais eu besoin de confirmation humaine — la confusion venait uniquement d’une mutualisation de code mal pensée.
add_action( 'nettoyage_commandes_nuit', function() {
$commandes_en_attente = wc_get_orders( array(
'status' => 'pending',
'date_created' => '<' . ( time() - 4 * HOUR_IN_SECONDS ),
) );
foreach ( $commandes_en_attente as $commande ) {
$commande->update_status( 'cancelled', 'Annulée automatiquement, paiement non confirmé après 4 heures.' );
}
} );
Le correctif structurel, plus important, a consisté à déplacer le garde-fou de confirmation à l’intérieur de l’outil MCP lui-même, plutôt que de le laisser dépendre entièrement de la couche conversationnelle qui l’appelle. Un outil sensible ne doit jamais présumer qu’il ne sera invoqué que par un chemin de code qui applique correctement le contrôle attendu.
Prévention
- Ne jamais laisser un même outil MCP être appelable à la fois par un agent conversationnel et par une tâche automatisée interne, sans un garde-fou intégré à l’outil lui-même, indépendant de l’appelant.
- Faire porter chaque appel d’outil sensible d’un identifiant de contexte explicite (conversation humaine, tâche planifiée), vérifié à l’intérieur de l’outil, pas seulement journalisé après coup.
- Auditer, après toute réécriture d’un processus automatisé existant, l’ensemble des dépendances qu’il a nouvellement introduites — la mutualisation de code, sans revue attentive, peut réintroduire des chemins d’exécution non prévus initialement.
Un outil pensé pour un usage conversationnel encadré ne devient pas automatiquement sûr pour un usage automatisé, même si le code semble faire exactement la même chose. Le contexte d’appel fait partie de la sécurité de l’outil, pas seulement son code interne.
En résumé
Cet incident n’avait rien d’un dysfonctionnement du modèle de langage lui-même : l’agent conversationnel n’a jamais reçu d’instruction d’annulation. La cause tenait à une mutualisation de code qui a fait passer un traitement automatisé légitime par un outil pensé uniquement pour un usage humain confirmé, contournant sans le vouloir le garde-fou qui n’existait qu’en amont de l’outil, jamais à l’intérieur.