Skip to main content
L’API mods est l’ensemble des méthodes qu’un mod appelle pour agir : ajouter des commandes et des outils, appeler un modèle, exécuter du travail entre les événements et accéder au système de fichiers, aux processus et au réseau. Chaque hook la reçoit comme premier argument, $, avec les méthodes regroupées dans des espaces de noms tels que $.ui et $.fs. Les événements décident quand un hook s’exécute, et l’API mods est ce que le hook appelle une fois qu’il le fait. Créez votre premier mod avant de commencer ici. Pour chaque méthode, consultez les méthodes de l’API mods ou lisez les types pour votre build.

Ajouter une commande ou un outil

Un mod peut ajouter une commande que l’utilisateur peut exécuter et un outil que Claude peut appeler. Enregistrez les deux dans un hook session.start. Claude Code attend ce hook avant la première invite, donc ce que vous enregistrez est disponible dès le premier tour.

Ajouter une commande

Une commande est destinée à l’utilisateur. Enregistrez-la, puis gérez command.run pour son nom. Cet exemple ajoute une commande /standup qui prend un nombre de jours facultatif :
Après le démarrage de la session, /standup apparaît avec sa description dans la liste que vous voyez quand vous tapez /. Le argumentHint s’affiche dans l’invite après que vous ayez tapé la commande et un espace, comme dans /standup [days]. Quand vous exécutez /standup 3, le deuxième hook retourne Summary for the last 3 day(s): ..., et la transcription affiche ce texte après le nom du plugin. Le hook n’appelle jamais next, car la commande n’a pas d’autre comportement que le vôtre. Le text que vous retournez s’affiche dans la transcription et Claude le lit. Pour ne rien imprimer, comme une commande qui ouvre seulement un volet, retournez {}. Pour laisser la commande s’exécuter pendant que Claude travaille, ajoutez immediate: true à l’enregistrement. Choisissez un nom qu’aucune commande intégrée n’utilise. Tapez / dans une session pour les voir. $.command.register lève une exception pour un nom pris, avec un message tel que "/focus" refused: it is the built-in /focus". Un hook qui lève une exception est ignoré, donc le reste de votre hook session.start ne s’exécute pas non plus. Enregistrez les commandes en dernier dans ce hook, ou enveloppez l’appel dans try et catch.

Ajouter un outil

Un outil est destiné à Claude. Enregistrez-le avec un nom, une description que Claude lit, et un schéma JSON pour son entrée. Claude le voit sous un nom plus long composé de mcp__, du nom de votre plugin, de deux traits de soulignement et du nom que vous avez enregistré. Vous gérez ses appels dans un hook tool.call filtré sur ce nom complet. Cet exemple, d’un plugin nommé my-mod, enregistre ticket, donc le nom complet est mcp__my-mod__ticket. Il donne à Claude un outil qui recherche un ticket dans un suivi de problèmes :
Quand vous posez une question sur un ticket, Claude peut appeler mcp__my-mod__ticket avec son id. Le deuxième hook récupère le ticket et retourne le corps de la réponse, que Claude lit comme le résultat de l’outil. Quand le serveur répond avec un statut d’erreur, Claude lit Lookup failed with status et le numéro.

Appeler un modèle

Un mod peut poser une question à un modèle de son propre chef, en dehors de la conversation, pour une petite tâche comme trier ou résumer un morceau de texte. $.model.complete envoie une invite à un modèle avec les identifiants de votre session et se résout en la réponse. Il n’a pas d’historique de conversation. Ce hook répond à une commande /triage, enregistrée comme une commande, en demandant à un petit modèle d’étiqueter le texte tapé après :
Quand vous exécutez /triage the export button does nothing, le mod envoie ce texte au modèle et affiche sa réponse, comme Label: bug. La conversation de Claude ne fait pas partie de la demande. Quand le modèle ne répond pas, l’étiquette est unknown. Une défaillance de l’API Claude ne rejette pas l’appel, donc vérifiez r.isAnswered, et lisez r.reason quand c’est false. L’appel rejette seulement pour une demande que Claude Code n’enverra pas, comme un modèle que votre organisation bloque. Les types pour votre build listent les autres options, comme effort, et les limites donnent la valeur par défaut de maxTokens. $.model.fork({ prompt }) pose une question sur la conversation actuelle à la place, avec le même modèle et la même invite système, donc l’API Claude sert la plupart de celle-ci à partir du cache d’invite. Ces appels utilisent le plan ou la clé API de l’utilisateur.

Exécuter du travail en arrière-plan

Le travail qui dépasse un événement, comme vérifier quelque chose une fois par minute, s’exécute sur un minuteur que vous démarrez à partir de session.start. Un hook lui-même s’exécute pour un événement et a une limite de temps de 10 secondes de son propre temps d’exécution. Le temps passé à attendre next ou un appel de l’API mods ne compte pas, sauf un $.clock.sleep. $.clock.every et $.clock.after remplacent setInterval et setTimeout, avec le délai en millisecondes en premier : $.clock.after(5000, fn) appelle fn une fois, cinq secondes à partir de maintenant. Chacun retourne un minuteur avec une méthode cancel(), et await $.clock.now() donne l’heure en millisecondes. Ce hook recherche les vérifications d’une demande de tirage une fois par minute et affiche le résultat sous l’invite. summarize est une fonction de votre propre création qui transforme la sortie JSON de la commande en quelques mots :
La session démarre comme d’habitude. Une minute plus tard, une ligne apparaît sous l’invite avec un ⚠, le nom du mod, puis checks: et votre résumé. Elle est remplacée une fois par minute après cela. Le rappel du minuteur s’exécute en dehors de tout événement, donc il continue de s’exécuter entre les tours et n’en démarre pas un. Si le rappel lève une exception, l’erreur va au journal de débogage et le minuteur s’exécute à nouveau à l’intervalle suivant.

Afficher quelque chose sans démarrer un tour

Un travail en arrière-plan peut afficher quelque chose à l’utilisateur sans démarrer un tour. Chacun de ces appels met du texte à un endroit différent :

Démarrer un tour à partir d’un travail en arrière-plan

Quand un travail en arrière-plan trouve quelque chose qui nécessite l’attention de Claude, il peut démarrer un tour en soumettant une invite avec $.prompt.submit({ text }). Claude lit le texte après une phrase qui nomme votre mod comme l’expéditeur. Pour l’envoyer comme les propres paroles de l’utilisateur, sans cette phrase, ajoutez asUser: true. L’appel attend que la session soit inactive, puis démarre un nouveau tour. Il se résout quand ce tour démarre, donc ne l’await pas dans un gestionnaire qui s’exécute pendant que Claude travaille.

Arrêter le travail en arrière-plan

Le travail en arrière-plan s’arrête de deux façons. Les minuteurs s’arrêtent quand le module se recharge. Pour le travail de longue durée à l’intérieur d’un hook, next.signal est un AbortSignal qui s’interrompt quand l’événement que votre hook gère est abandonné, par exemple quand l’utilisateur interrompt, donc passez-le à tout ce qui est de longue durée.

Envoyer et recevoir des messages entre les sessions

Un mod peut envoyer un message en texte brut à une autre de vos sessions ou à l’un des sous-agents de cette session, et observer les messages qui arrivent et partent. $.session.send({ to, text }) en envoie un, la même livraison que l’outil SendMessage fait. to est { sessionId } pour une session, { agentId } pour un sous-agent de $.agent.list(), ou l’adresse de chaîne d’où provient un message reçu. L’appel se résout une fois que le message est mis en file d’attente, avec { isDelivered: true }. Quand rien n’a été livré, il se résout avec { isDelivered: false, reason }, et reason dit pourquoi. Ce hook répond à une commande /ping, enregistrée comme une commande, en demandant à la session dont vous tapez l’id après un statut :
Quand le message est mis en file d’attente, rien n’apparaît dans votre session, et Claude de l’autre session lit Status? One line. Quand rien n’a été livré, une petite boîte en haut à droite donne la raison et disparaît après quelques secondes. Deux événements permettent à un mod d’observer les messages. Retournez next(e) des deux pour passer chaque message inchangé : Une session définie pour refuser les messages entrants refuse un message avant que session.receive se déclenche, donc un hook ne le voit jamais. Un message qui est retenu pour votre approbation atteint d’abord le hook, donc un mod peut lire un message que vous n’avez pas encore approuvé. Le next(e) du hook rejette quand le message n’est pas livré. Le nom de l’expéditeur sur un message reçu est ce que l’expéditeur a écrit, donc ne basez pas une décision sur celui-ci.

Accéder aux fichiers, processus et au réseau

Un mod accède au système de fichiers, aux processus et au réseau via l’API mods, avec les mêmes permissions que l’utilisateur exécutant Claude Code. Le module hooks lui-même n’a pas d’API Node.js, pas de globales de minuteur comme setTimeout, et pas d’accès réseau ou fichier de son propre chef. Les API JavaScript standard et web comme URL, TextEncoder, AbortController, et crypto.subtle sont disponibles. Chaque espace de noms ci-dessous couvre un type d’accès : Les fichiers et processus ont quelques règles qui leur sont propres :
  • Chemins : un chemin relatif est sous le répertoire de travail de la session
  • $.fs.list : retourne les entrées d’un répertoire comme { name, kind, size, isLink } et ne descend pas dans les sous-répertoires
  • $.process.run : prend une liste d’arguments et n’utilise pas de shell. Il se résout en { exitCode, stdout, stderr } quel que soit le code de sortie. Il rejette si le programme ne peut pas démarrer ou s’exécute toujours au délai d’expiration, qui est de 30 secondes par défaut, donc enveloppez-le dans try et catch.
Chacun de ces appels est lui-même un événement, nommé pour son espace de noms et sa méthode sans le $., comme fs.read pour $.fs.read. Un mod plus tôt dans la chaîne peut observer, réécrire ou refuser votre appel, c’est ainsi qu’une organisation restreint ce que les mods atteignent.

Prochaines étapes