Build This Now
Build This Now
Raccourcis clavierGuide de la Status Line
Guide des HooksHooks cross-platform pour Claude CodeHooks de configuration Claude CodeStop HooksAgents Claude Code Auto-ValidantsHooks de Cycle de Vie de Session Claude CodeHooks de sauvegarde de contexte pour Claude CodeHook d'activation des skillsHook de Permission Claude CodeLes MCP Tool Hooks dans Claude Code
speedy_devvkoen_salo
Blog/Toolkit/Hooks/MCP Tool Hooks in Claude Code

Les MCP Tool Hooks dans Claude Code

Comment appeler les outils d'un serveur MCP directement depuis les hooks de Claude Code avec type: mcp_tool — schéma, syntaxe de substitution, cas d'usage et patterns de production.

Arrêtez de configurer. Commencez à construire.

Templates SaaS avec orchestration IA.

Published Apr 24, 20269 min readToolkit hubHooks index

Le problème : tes hooks exécutent des scripts shell. Chaque fois qu'un hook doit appeler un serveur MCP, il lance un sous-processus, câble le transport, gère l'auth, parse la réponse et formate la sortie JSON vers stdout. Pour un formateur ou un scan de sécurité qui se déclenche à chaque écriture de fichier, ça finit par peser.

La solution rapide : depuis la v2.1.118, les hooks ont un nouveau type qui appelle les outils MCP directement. Ajoute ça dans .claude/settings.json pour lancer un scan de sécurité après chaque écriture de fichier, sans sous-processus :

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [
          {
            "type": "mcp_tool",
            "server": "semgrep",
            "tool": "scan_file",
            "input": { "path": "${tool_input.file_path}" }
          }
        ]
      }
    ]
  }
}

Le serveur MCP tourne déjà. Le hook court-circuite le shell et appelle directement la connexion RPC du serveur. La sortie texte de l'outil passe par le même parseur de décision JSON que n'importe quel hook de commande.

Ce qu'est vraiment type: "mcp_tool"

Avant la v2.1.118, les hooks avaient quatre types de handlers : command, http, prompt et agent. Maintenant il y en a cinq :

TypeCe qui s'exécute
commandSous-processus shell (stdin/stdout)
httpPOST vers une URL
mcp_toolAppel RPC direct vers un serveur MCP connecté
promptÉvaluation LLM en un seul tour (Haiku par défaut)
agentSous-agent multi-tour avec accès Read/Grep/Glob

Le type mcp_tool couvre tous les événements de hook, comme command et http. La seule contrainte pratique : SessionStart et Setup se déclenchent pendant que les serveurs se connectent encore, donc ces hooks peuvent renvoyer une erreur "server not connected" au premier lancement. Les suivants se passent bien.

Le schéma complet

Trois champs sont spécifiques aux hooks mcp_tool. Le reste est partagé avec tous les types de hooks :

{
  "type": "mcp_tool",
  "server": "my-mcp-server",
  "tool": "tool_name",
  "input": {
    "arg1": "${tool_input.file_path}",
    "arg2": "${session_id}"
  },
  "timeout": 30,
  "statusMessage": "Checking...",
  "if": "Edit(*.ts|*.tsx)"
}
ChampRequisDescription
serverOUINom exact du serveur MCP tel que configuré dans settings
toolOUINom de l'outil sur ce serveur
inputnonArguments passés à l'outil. Supporte la substitution ${path}
timeoutnonSecondes avant annulation du hook
statusMessagenonTexte du spinner affiché pendant l'exécution
ifnonFiltre en syntaxe permission-rule. Le hook ne se déclenche que si l'appel complet correspond

Important : server doit correspondre exactement au nom du serveur dans ta configuration MCP. Un seul caractère de différence et le hook échoue silencieusement avec une erreur non bloquante.

La substitution dans input

Les valeurs string dans input supportent la notation ${field.path} pour naviguer dans le JSON complet de l'événement. Pour un hook PostToolUse sur un appel Write, l'événement ressemble à ça :

{
  "session_id": "abc123",
  "cwd": "/your/project",
  "hook_event_name": "PostToolUse",
  "tool_name": "Write",
  "tool_use_id": "toolu_01...",
  "tool_input": {
    "file_path": "/your/project/src/api.ts",
    "content": "..."
  },
  "tool_response": { "filePath": "/your/project/src/api.ts", "success": true },
  "duration_ms": 142
}

Donc "${tool_input.file_path}" résout vers /your/project/src/api.ts. N'importe quel champ de cet objet est accessible. Le champ duration_ms a été ajouté en v2.1.119, une release après le lancement de mcp_tool.

Comment la sortie est traitée

Le contenu texte de l'outil MCP est traité exactement comme le stdout d'un hook de commande. Si c'est du JSON valide, Claude Code applique les champs de décision. Sinon, le texte devient du contexte pour Claude.

Les champs de décision fonctionnent comme sur n'importe quel hook :

{
  "decision": "block",
  "reason": "Security issue found in src/api.ts: SQL injection risk on line 42."
}

Retourne ça depuis un hook MCP PostToolUse et Claude reçoit le message et corrige le fichier. L'outil a déjà tourné, donc c'est consultatif, pas préventif. Pour bloquer avant l'exécution d'un outil, utilise PreToolUse et retourne permissionDecision: "deny".

Un champ est exclusif aux hooks mcp_tool sur PostToolUse : updatedMCPToolOutput. Il remplace ce que Claude voit comme sortie de l'outil avant que ça entre dans la conversation. Un serveur MCP actif peut post-traiter le résultat d'un autre outil avant que Claude le lise.

Pourquoi c'est mieux que les hooks shell

Deux différences concrètes, pas juste la vitesse.

Serveurs avec état. Un sous-processus shell repart de zéro à chaque fois. Un serveur MCP est un processus vivant avec son propre état : configs chargées, connexions ouvertes, caches, contexte de session accumulé. Un MCP de lint qui a pré-parsé ton tsconfig.json au démarrage ne le re-parse pas à chaque écriture de fichier. Un hook shell, si.

Pas de dépendance à l'environnement shell. Les hooks de commande échouent silencieusement quand PATH est mauvais, quand jq n'est pas installé, quand ~/.zshrc écrit quelque chose sur stdout sur des shells non-interactifs. Les hooks MCP contournent tout ça. L'appel va directement de Claude Code au serveur via la connexion RPC existante.

Le champ if : cible tes hooks

Sans if, un hook se déclenche sur chaque événement correspondant au matcher. Avec if, le hook ne s'active que quand l'appel complet (nom et arguments) correspond à la syntaxe permission-rule :

{
  "type": "mcp_tool",
  "server": "semgrep",
  "tool": "scan_file",
  "if": "Edit(*.py|*.ts|*.js)",
  "input": { "path": "${tool_input.file_path}" }
}

Ce hook ne tourne jamais sur les fichiers .md ou .json. Sur un projet avec beaucoup d'éditions de documentation, la différence de perfo est réelle.

Pattern 1 : Scan de sécurité à chaque écriture

Un serveur MCP de sécurité qui accepte un chemin de fichier et retourne les résultats. Bloque Claude s'il trouve quelque chose :

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [
          {
            "type": "mcp_tool",
            "server": "semgrep",
            "tool": "scan_file",
            "if": "Write(*.ts|*.py|*.js|*.go)",
            "input": { "path": "${tool_input.file_path}" },
            "statusMessage": "Scanning..."
          }
        ]
      }
    ]
  }
}

Si l'outil MCP retourne un résultat, structure la réponse comme ça :

{
  "decision": "block",
  "reason": "Semgrep finding: [description of issue at line N]"
}

Claude reçoit le message de blocage et retravaille le fichier. Le scan tourne sur le ruleset mis en cache du serveur, pas un parse de sous-processus à froid.

Pattern 2 : Hook Stop avec vérification externe

Un hook Stop qui appelle un MCP Linear ou Jira pour vérifier si le ticket associé est vraiment fermé avant de laisser Claude déclarer que c'est terminé :

{
  "hooks": {
    "Stop": [
      {
        "hooks": [
          {
            "type": "mcp_tool",
            "server": "linear",
            "tool": "get_issue_status",
            "input": { "issue_id": "${tool_input.issue_id}" }
          }
        ]
      }
    ]
  }
}

L'outil MCP retourne le statut du ticket. S'il revient en In Progress, le JSON de réponse doit porter decision: "block" avec une raison. Claude continue à travailler.

Pense toujours à vérifier stop_hook_active dans ta logique de hook Stop. L'événement JSON inclut ce champ avec la valeur "true" quand Claude continue déjà depuis un hook Stop précédent. Un serveur qui ne vérifie pas ça crée une boucle infinie. Intègre la protection directement dans l'outil MCP : si stop_hook_active vaut "true" dans l'input, retourne une sortie vide et sors proprement.

Pattern 3 : Vérification d'erreurs de prod avant de stopper

Après qu'une feature soit terminée par Claude, vérifie si quelque chose de nouveau a cassé en staging avant de marquer la session comme complète. Un MCP Sentry gère la vérification :

{
  "hooks": {
    "Stop": [
      {
        "hooks": [
          {
            "type": "mcp_tool",
            "server": "sentry",
            "tool": "get_new_errors_since",
            "input": { "minutes": "5", "skip_if_active": "${stop_hook_active}" }
          }
        ]
      }
    ]
  }
}

Si de nouvelles erreurs sont apparues dans les cinq dernières minutes, l'outil MCP les retourne avec decision: "block". Claude lit les détails d'erreur et corrige la régression avant de s'arrêter.

Pattern 4 : Injection automatique de docs avant chaque prompt

Un hook UserPromptSubmit avec un MCP Context7 qui récupère la documentation à jour pour n'importe quelle lib mentionnée dans le prompt, avant que Claude le traite :

{
  "hooks": {
    "UserPromptSubmit": [
      {
        "hooks": [
          {
            "type": "mcp_tool",
            "server": "context7",
            "tool": "get_library_docs",
            "input": { "prompt": "${prompt}" },
            "timeout": 15
          }
        ]
      }
    ]
  }
}

Avant, ça nécessitait que Claude appelle explicitement l'outil MCP. Maintenant ça se passe automatiquement à chaque prompt. Claude démarre avec la doc à jour plutôt que ses données d'entraînement.

Pattern 5 : Application de politiques pour les équipes d'agents

Dans les workflows multi-agents, un serveur MCP de politique partagée peut contrôler quel agent écrit dans quels répertoires. La variable d'environnement CLAUDE_AGENT_NAME identifie l'agent courant. Un hook PreToolUse appelle le serveur de politique :

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [
          {
            "type": "mcp_tool",
            "server": "policy-server",
            "tool": "check_write_permission",
            "input": {
              "agent": "${agent_name}",
              "path": "${tool_input.file_path}"
            }
          }
        ]
      }
    ]
  }
}

Le serveur de politique détient la carte d'autorisation complète. Mets-le à jour une fois et chaque agent dans chaque projet hérite des nouvelles règles, sans toucher un seul settings.json.

Pattern 6 : MCP Tool Hooks dans le frontmatter des agents

Les hooks ne doivent pas forcément vivre dans settings.json. Ils peuvent se trouver dans le frontmatter YAML d'un agent, scopés au cycle de vie de cet agent :

---
name: backend-developer
description: Builds API endpoints and database logic
hooks:
  PostToolUse:
    - matcher: "Write"
      hooks:
        - type: mcp_tool
          server: semgrep
          tool: scan_file
          input: { "path": "${tool_input.file_path}" }
  Stop:
    - hooks:
        - type: agent
          prompt: "Verify all API endpoints have corresponding tests. Block if any are missing."
---

Chaque agent spécialisé dans une équipe orchestrée porte sa propre logique de validation. L'agent backend scanne les problèmes de sécurité. L'agent frontend vérifie l'accessibilité. Aucun n'a besoin d'un hook global qui s'applique à tout le monde.

Contrôle de l'élicitation

L'événement Elicitation se déclenche quand un serveur MCP demande une entrée utilisateur en cours de tâche. Un hook mcp_tool peut répondre automatiquement aux prompts connus en appelant un gestionnaire de secrets :

{
  "hooks": {
    "Elicitation": [
      {
        "matcher": "my-db-server",
        "hooks": [
          {
            "type": "mcp_tool",
            "server": "secrets-manager",
            "tool": "get_credential",
            "input": { "key": "${elicitation.field_name}" }
          }
        ]
      }
    ]
  }
}

Les demandes de credentials prévisibles se résolvent automatiquement. La tâche tourne sans interruption.

Les serveurs MCP à coupler avec des hooks

Tous les serveurs MCP n'ont pas vocation à être des cibles de hooks. Ceux qui s'y prêtent le mieux sont les outils qui doivent se déclencher sur des événements spécifiques sans intervention utilisateur :

ServeurÉvénementCe qu'il fait
SemgrepPostToolUse: WriteScan de sécurité à chaque écriture
SentryStopVérifie les nouvelles erreurs de staging avant de terminer
Linear / JiraStop, TaskCompletedVérifie le statut du ticket, met à jour à la complétion
Context7UserPromptSubmitRécupère la doc à jour des libs mentionnées
ElevenLabsStop, NotificationAudio TTS à la complétion de tâche
SlackNotification, StopAlertes d'équipe sans boilerplate curl
E2BStopLance les scripts générés dans un sandbox avant de marquer terminé
claude-memPostCompact, SessionStartRestaure le contexte de session après compaction
n8nTaskCompletedDéclenche un workflow externe à la complétion

Bug connu : PostToolUse + événements MCP + additionalContext

Il existe un bug ouvert (issue GitHub #24788) où additionalContext des hooks est silencieusement ignoré quand l'événement déclencheur est un appel d'outil MCP. Ça affecte les hooks de type "command" qui répondent aux événements d'outils MCP, pas les hooks mcp_tool eux-mêmes.

La distinction est importante : les hooks qui SONT des invocations MCP fonctionnent bien. Les hooks qui RÉPONDENT aux appels d'outils MCP et retournent additionalContext, non. Le contournement consiste à utiliser exit 2 plus stderr pour les messages critiques depuis les hooks PostToolUse ciblant des appels d'outils MCP. Le pattern de blocage fonctionne, l'injection consultative non.

Les MCP Tool Hooks complètent le système de hooks

Avant, les hooks étaient un filet de sécurité. Des commandes shell qui pouvaient bloquer des choses dangereuses ou lancer des formateurs. Stateless, locaux au processus, déconnectés de tout ce que tes serveurs MCP savaient déjà.

Maintenant : les hooks sont une couche d'orchestration déterministe. N'importe quel événement, n'importe quel outil MCP, contrôle total des décisions, avec un état qui persiste entre les appels et zéro overhead de sous-processus.

Le pipeline est complet. PreToolUse valide. PostToolUse formate et scanne. PostToolBatch lance les tests. Stop vérifie avec de vraies données externes. Chaque étape peut être une invocation d'outil MCP, et aucune ne nécessite un script shell.

Continue in Hooks

  • Hooks de configuration Claude Code
    Assemble scripts, agents et docs dans des hooks de configuration Claude Code. Une commande lance un script déterministe, passe la sortie à un agent de diagnostic, et journalise une doc vivante.
  • Hooks de sauvegarde de contexte pour Claude Code
    Un hook de sauvegarde de contexte Claude Code piloté par StatusLine. Écrit des snapshots structurés toutes les 10K tokens pour que la compaction automatique ne mange jamais les erreurs, signatures et décisions.
  • Hooks cross-platform pour Claude Code
    Hooks Claude Code cross-platform : supprime les wrappers .cmd, .sh et .ps1 et invoque node directement pour qu'un seul fichier .mjs tourne sur macOS, Linux et Windows dans toute l'équipe.
  • Guide des Hooks
    Les hooks Claude Code depuis les bases : codes de sortie, sortie JSON, commandes asynchrones, endpoints HTTP, matchers PreToolUse et PostToolUse, patterns de production.
  • Hook de Permission Claude Code
    Installe un hook de permission Claude Code à trois niveaux : approbation instantanée pour les appels sûrs, refus instantané pour les dangereux, vérification LLM pour la zone grise. Sans flag skip.
  • Agents Claude Code Auto-Validants
    Agents Claude Code auto-validants : câble des hooks lint PostToolUse, des hooks Stop, et des sous-agents réviseurs en lecture seule dans les définitions d'agents pour que le mauvais code ne soit jamais livré.

More from Toolkit

  • Raccourcis clavier
    Configurer keybindings.json dans Claude Code : 17 contextes, syntaxe des touches, séquences d'accords, combinaisons de modificateurs, et comment désactiver n'importe quel raccourci par défaut instantanément.
  • Guide de la Status Line
    Configure une status line Claude Code affichant le nom du modèle, la branche git, le coût de session et l'utilisation du contexte. Config settings.json, JSON d'entrée, scripts bash, Python, Node.js.
  • SEO IA et optimisation GEO
    Un tour d'horizon de la Generative Engine Optimization : comment faire citer ton contenu dans les réponses de ChatGPT, Claude et Perplexity plutôt que simplement classé sur Google.
  • Claude Code vs Cursor en 2026
    Une comparaison côte à côte de Claude Code et Cursor en 2026 : modèles d'agents, fenêtres de contexte, niveaux de prix, et comment chaque outil s'adapte aux différents workflows des développeurs.

Arrêtez de configurer. Commencez à construire.

Templates SaaS avec orchestration IA.

On this page

Ce qu'est vraiment type: "mcp_tool"
Le schéma complet
La substitution dans input
Comment la sortie est traitée
Pourquoi c'est mieux que les hooks shell
Le champ if : cible tes hooks
Pattern 1 : Scan de sécurité à chaque écriture
Pattern 2 : Hook Stop avec vérification externe
Pattern 3 : Vérification d'erreurs de prod avant de stopper
Pattern 4 : Injection automatique de docs avant chaque prompt
Pattern 5 : Application de politiques pour les équipes d'agents
Pattern 6 : MCP Tool Hooks dans le frontmatter des agents
Contrôle de l'élicitation
Les serveurs MCP à coupler avec des hooks
Bug connu : PostToolUse + événements MCP + additionalContext
Les MCP Tool Hooks complètent le système de hooks

Arrêtez de configurer. Commencez à construire.

Templates SaaS avec orchestration IA.