IAMaîtriser l'IA générative Plan du corpus

Creer un plugin

De zero a un plugin data complet : structure de dossiers, manifeste, composants, test en local, avec l'exemple integral de boite-a-outils-data.

Temps de lecture : 15 min | Niveau : Intermediaire

Ce que tu sauras faire apres

  • Creer la structure de dossiers d'un plugin et son manifeste.
  • Remplir les champs de plugin.json sans en inventer aucun.
  • Placer correctement skills, agents, hooks et serveur MCP.
  • Tester ton plugin en local avec --plugin-dir, sans marketplace.
  • Diagnostiquer un composant qui ne se charge pas.

1. Le squelette minimal

Un plugin, c'est un dossier avec un plugin.json dedans. Rien de plus pour commencer.

mkdir -p boite-a-outils-data/.claude-plugin

Puis boite-a-outils-data/.claude-plugin/plugin.json :

{
  "name": "boite-a-outils-data",
  "description": "Skills, agent et hooks pour le travail SQL, dbt et pipelines",
  "version": "1.0.0",
  "author": {
    "name": "Ton Nom"
  }
}

Quatre champs, quatre roles :

  • name : obligatoire. Il identifie le plugin et devient le prefixe de chaque skill et agent. Pas d'espaces. Ecris-le en kebab-case, sinon claude plugin validate t'avertit.
  • description : le texte affiche pour ton plugin dans /plugin.
  • version : optionnel. Le fixer garde les utilisateurs sur cette version tant que tu ne la changes pas.
  • author : exige un name a l'interieur. email et url sont optionnels.

Rappel qui coute cher quand on l'oublie : seul plugin.json va dans .claude-plugin/. Les composants ranges la-dedans ne se chargent pas. Et la racine du plugin est le dossier du plugin lui-meme, pas ~/.claude/.


2. Les champs du manifeste

Le tableau ci-dessous reprend les champs que tu vas reellement utiliser. La reference officielle en liste davantage.

Metadonnees

ChampTypeRole
nameStringIdentifiant, obligatoire, kebab-case
displayNameStringNom affiche dans l'interface a la place de name
versionStringChaine de version
descriptionStringExplication courte de ce que fournit le plugin
authorObjectname obligatoire, email et url optionnels
homepageStringURL de documentation. Doit etre une URL valide, sinon le plugin ne se charge pas
repositoryStringURL du depot source. Non validee
licenseStringIdentifiant SPDX comme MIT ou Apache-2.0
keywordsArray de stringsEtiquettes pour la recherche
defaultEnabledBooleanSi le plugin demarre actif quand l'utilisateur n'a rien regle. Defaut true
dependenciesArray de strings ou d'objetsPlugins qui doivent etre actifs pour que celui-ci fonctionne
metadataObjectObjet libre pour tes propres donnees. Claude Code ne le lit pas
$schemaStringURL de schema JSON pour l'autocompletion dans ton editeur. Ignoree au chargement

Champs de composants

Tu n'en as besoin que si tu ranges un composant ailleurs que dans son emplacement par defaut.

ChampComportement
skillsChemin ou tableau de chemins. S'ajoute au scan par defaut de skills/
commandsChemin, tableau, ou objet associant un nom de commande a source ou content. Remplace le scan de commands/
agentsChemin ou tableau de fichiers .md. Les dossiers ne sont pas acceptes. Remplace le scan de agents/
hooksChemin, objet inline, ou tableau. Charge en plus de hooks/hooks.json
mcpServersChemin, objet inline, ou tableau. Charge en plus de .mcp.json ; un serveur declare plus tard remplace un homonyme
lspServersMeme logique, charge en plus de .lsp.json
outputStylesRemplace le scan de output-styles/
workflowsFichiers .js de workflow. Remplace le scan de workflows/

Retiens la nuance : skills, hooks et mcpServers s'ajoutent au defaut ; commands, agents, outputStyles et workflows remplacent le defaut. Quand tu remplaces un dossier par defaut qui existe quand meme, Claude Code t'avertit avec Default <dossier>/ folder is ignored because the manifest sets "<cle>".

Trois regles de chemin a respecter :

  • Commence chaque chemin par ./. Un chemin ecrit commands/deploy.md echoue a la validation. Seul skills accepte aussi ".", qui designe la racine du plugin.
  • Utilise des barres obliques vers l'avant, comme ./commands/deploy.md. Sur macOS et Linux, un chemin contenant une barre inverse est rejete, meme s'il reste dans le plugin.
  • Ne sors pas de la racine du plugin. Un chemin comme ../shared-utils est refuse. L'erreur est path escapes plugin directory et le plugin se charge sans ce composant.

3. Exemple complet : boite-a-outils-data 🧰

On construit un plugin utile au quotidien pour un data engineer : une skill de revue SQL, une skill de documentation dbt, un agent d'audit de pipeline, une commande de profilage, un hook de verification et un serveur MCP.

L'arborescence

boite-a-outils-data/
├── .claude-plugin/
│   └── plugin.json
├── skills/
│   ├── revue-sql/
│   │   └── SKILL.md
│   └── doc-modele-dbt/
│       └── SKILL.md
├── agents/
│   └── auditeur-pipeline.md
├── commands/
│   └── profil-table.md
├── hooks/
│   └── hooks.json
├── scripts/
│   └── verifie_sql.sh
├── .mcp.json
└── README.md

Note que scripts/ n'est pas un emplacement special : c'est un dossier ordinaire que les hooks appellent via ${CLAUDE_PLUGIN_ROOT}.

Le manifeste

{
  "name": "boite-a-outils-data",
  "displayName": "Boite a outils Data",
  "description": "Revue SQL, documentation dbt, audit de pipeline et garde-fous pour le travail data",
  "version": "1.0.0",
  "author": {
    "name": "Ton Nom",
    "email": "toi@exemple.fr"
  },
  "license": "MIT",
  "keywords": ["sql", "dbt", "airflow", "data-engineering"]
}

Pas de champ de composant ici : tout est a son emplacement par defaut, donc Claude Code les trouve seul.

Skill 1 — skills/revue-sql/SKILL.md

Une skill est un fichier SKILL.md que Claude peut charger quand sa description correspond a ta tache. Donne-lui toujours une description, c'est elle qui declenche.

---
name: revue-sql
description: Relit une requete SQL analytique pour la correction, la performance et la lisibilite. A utiliser quand on te demande de relire, optimiser ou valider du SQL.
---

Tu relis une requete SQL analytique. Procede dans cet ordre.

1. Correction : cle et type de chaque jointure, risque de duplication de lignes,
   colonnes non agregees absentes du GROUP BY, comparaisons a NULL faites avec =.
2. Performance : SELECT * sur une table de faits, fonction appliquee a une colonne
   filtree qui casse l'usage d'index, sous-requete correlee remplacable par une
   jointure ou une fonction de fenetre.
3. Lisibilite : CTE nommes a la place des sous-requetes imbriquees, alias explicites.

Rends un tableau : Ligne | Probleme | Correction proposee | Gravite.
Puis donne la requete corrigee complete dans un bloc de code sql.
Reponds en francais.

Skill 2 — skills/doc-modele-dbt/SKILL.md

---
name: doc-modele-dbt
description: Genere le fichier schema.yml d'un modele dbt a partir de son SQL. A utiliser quand on te demande de documenter un modele dbt ou d'ajouter des tests dbt.
---

Tu documentes un modele dbt.

1. Lis le fichier SQL du modele indique et deduis ses colonnes de sortie.
2. Produis un bloc yaml pour schema.yml contenant le nom du modele, sa description
   en une phrase, une description par colonne en francais sans jargon, et les tests
   pertinents : unique et not_null sur la cle primaire, relationships sur chaque cle
   etrangere, accepted_values sur les colonnes de statut.
3. Ne propose jamais un test que tu ne peux pas justifier par le SQL lu.

Termine en listant les colonnes dont tu n'as pas pu deduire le sens.
Reponds en francais.

Agent — agents/auditeur-pipeline.md

Un subagent est un assistant separe, avec ses propres instructions et sa propre fenetre de contexte, a qui Claude peut deleguer une tache.

---
name: auditeur-pipeline
description: Audite un DAG Airflow ou un projet dbt pour la robustesse et l'idempotence. A utiliser apres une modification d'orchestration.
model: sonnet
---

Tu es auditeur de pipelines de donnees. Verifie ces points un par un :

1. Idempotence : relancer une execution sur la meme partition donne-t-il le meme resultat ?
2. Fenetres de donnees : les dates viennent-elles du contexte d'execution ou sont-elles codees en dur ?
3. Gestion d'echec : retries, timeout et alerte sur chaque tache ?
4. Dependances : une tache lit-elle une table qu'une autre n'a pas encore ecrite dans le meme run ?
5. Tests de donnees : verification de fraicheur et de volumetrie apres chargement ?

Rends un rapport avec, pour chaque point : Statut (OK / A corriger / Non applicable),
Preuve (fichier et ligne), Correction proposee.
Ne modifie aucun fichier. Tu audites, tu ne corriges pas. Reponds en francais.

Cet agent s'appelle boite-a-outils-data:auditeur-pipeline, et tu l'invoques explicitement avec @agent-boite-a-outils-data:auditeur-pipeline.

Les champs de frontmatter supportes pour un agent de plugin sont name, description, model, effort, maxTurns, tools, disallowedTools, skills, memory, background, omitClaudeMd, isolation et color. Les champs permissionMode, hooks, mcpServers et initialPrompt sont ignores : un fichier d'agent ne peut pas ajouter de hooks ni de serveurs MCP tout seul.

Commande — commands/profil-table.md

Une commande est un simple fichier Markdown que tu lances par son nom. C'est l'ancien format ; prefere skills/ pour du neuf. Ici c'est utile pour un geste court et repetitif.

---
description: Genere la requete de profilage d'une table
---

Genere une requete SQL de profilage pour la table indiquee. Elle doit retourner,
en une seule execution : le nombre total de lignes, et pour chaque colonne le nombre
de valeurs nulles, le nombre de valeurs distinctes, le minimum et le maximum
quand le type le permet.

Demande d'abord le moteur SQL cible s'il n'est pas precise : la syntaxe change
entre PostgreSQL, BigQuery, Snowflake et SQL Server.

Le fichier commands/profil-table.md devient /boite-a-outils-data:profil-table. Un sous-dossier ajoute un segment : commands/db/migrate.md devient /boite-a-outils-data:db:migrate.

Hook — hooks/hooks.json

Les hooks se rangent dans hooks/hooks.json, sous une cle de premier niveau "hooks", dans la meme forme que l'objet hooks d'un settings.json. Tu peux donc recopier un hook existant sans le reecrire.

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [
          {
            "type": "command",
            "command": "\"${CLAUDE_PLUGIN_ROOT}/scripts/verifie_sql.sh\""
          }
        ]
      }
    ]
  }
}

Et le script scripts/verifie_sql.sh, a rendre executable avec chmod +x :

#!/bin/bash
# Refuse un SELECT * ecrit dans un fichier .sql du projet.
fichier=$(jq -r '.tool_input.file_path // empty')
case "$fichier" in
  *.sql)
    if grep -qiE 'select[[:space:]]+\*' "$fichier"; then
      echo "Attention : SELECT * detecte dans $fichier" >&2
    fi
    ;;
esac
exit 0

Trois points a retenir :

  • Les hooks ne t'attendent pas. Un hook de plugin ne se declenche pas seulement quand tu utilises une skill du plugin. Claude Code l'enregistre des que la session charge le plugin, et il se declenche sur son evenement a partir de la. Pour limiter, resserre le matcher.
  • Le guillemetage compte. Quand command n'a pas de args, la commande passe par un shell : entoure donc le chemin ${CLAUDE_PLUGIN_ROOT} de guillemets doubles. Avec args, chaque element est passe tel quel, sans shell, et aucun guillemet n'est necessaire.
  • L'environnement est fourni. Chaque processus de hook recoit CLAUDE_PLUGIN_ROOT et CLAUDE_PLUGIN_DATA.

Serveur MCP — .mcp.json

Le fichier .mcp.json a la racine du plugin a la meme forme qu'un .mcp.json de projet.

{
  "mcpServers": {
    "entrepot": {
      "command": "python",
      "args": ["${CLAUDE_PLUGIN_ROOT}/mcp/serveur_entrepot.py"],
      "env": {
        "ENTREPOT_DSN": "${CLAUDE_PROJECT_DIR}/.env.entrepot"
      }
    }
  }
}

Une fois le plugin charge, lance /mcp dans la session : le serveur apparait sous le nom plugin:boite-a-outils-data:entrepot. Ses outils sont nommes mcp__plugin_boite-a-outils-data_entrepot__<outil>.

Dans args, aucun guillemetage n'est necessaire : chaque element est passe comme un argument unique.


4. Valider et tester en local 🧪

Valider

claude plugin validate ./boite-a-outils-data

La commande verifie le manifeste et le frontmatter de chaque fichier de skill, d'agent et de commande. Elle affiche le chemin du manifeste verifie puis ✔ Validation passed, ou Validation passed with warnings s'il reste des avertissements. En cas d'echec, chaque ligne au-dessus du resultat nomme le champ a corriger.

Ajoute --strict pour echouer aussi sur les avertissements. C'est ce que tu mets en CI :

claude plugin validate --strict ./boite-a-outils-data

La commande sort avec le code 0 en cas de succes, 1 en cas d'echec, 2 en cas d'erreur inattendue.

Charger pour une session

Pas besoin de marketplace pour tester. Tu charges le dossier directement, puis tu lances /boite-a-outils-data:revue-sql dans la session. Le drapeau est repetable et accepte aussi une archive .zip :

claude --plugin-dir ./boite-a-outils-data --plugin-dir ./autre-plugin.zip

Un plugin charge ainsi est un plugin de session uniquement. claude plugin list l'affiche comme <nom>@inline avec le scope session, mais seulement si le meme drapeau precede la sous-commande :

claude --plugin-dir ./boite-a-outils-data plugin list

Quand tu modifies un fichier pendant la session, relance /reload-plugins.

Charger a chaque session, sans drapeau

Claude Code charge comme plugin, dans toutes les sessions, tout dossier place sous ~/.claude/skills/ qui contient un .claude-plugin/plugin.json. La commande claude plugin init echafaude un plugin de ce type. Elle demande Claude Code v2.1.157 ou plus recent :

claude plugin init boite-a-outils-data --with skills hooks

Elle cree ~/.claude/skills/boite-a-outils-data/ avec un .claude-plugin/plugin.json et un SKILL.md a la racine. Elle annonce ensuite que le plugin se chargera sous l'identifiant boite-a-outils-data@skills-dir.

Les valeurs acceptees par --with sont skills, agents, hooks, mcp, lsp, output-style et channel. Tu peux en enchainer plusieurs, comme ci-dessus. La commande n'a aucun drapeau pour ecrire ailleurs que dans ~/.claude/skills/.

Pour arreter de le charger : supprime son dossier, ou lance claude plugin disable boite-a-outils-data@skills-dir.


5. Quand ca ne marche pas 🔧

Deroule ces quatre verifications dans l'ordre :

  1. claude plugin validate <chemin> dans ton shell.
  2. /reload-plugins dans la session, pour appliquer tes dernieres editions.
  3. /plugin dans la session, onglets Installed puis Errors.
  4. claude plugin list dans ton shell. Il affiche les plugins de session et de dossier de skills avec Status: ✔ loaded ou l'erreur de chargement.

Les pannes les plus frequentes :

SymptomeCauseCorrection
<composant> path not found: <chemin> dans l'onglet ErrorsUn chemin de composant du manifeste pointe vers rienCorrige le chemin ou cree le dossier, puis /reload-plugins
Le plugin se charge mais ses skills manquentskills/ est dans .claude-plugin/, ou une entree skills pointe vers un fichierDeplace skills/ a la racine ; chaque entree doit pointer vers un dossier contenant SKILL.md
--plugin-dir ne charge rien, sans erreurTu as pointe la racine d'une marketplace au lieu d'un pluginPointe le dossier d'un seul plugin, celui qui contient .claude-plugin/plugin.json
Un hook ne se declenche jamaisLe matcher ne correspond pasVerifie le journal de debug des hooks, qui montre quels hooks ont matche et leurs codes de sortie
Un serveur MCP n'apparait pasConfig invalideLance /mcp dans la session pour voir son statut

6. Template a copier-coller 📋

Prompt pour faire echafauder ton plugin par Claude Code.

Contexte : je suis [DEV / DATA ENGINEER / DATA ANALYST] et je travaille avec
[TECHNOS : ex. dbt, Airflow, PostgreSQL, pandas].

Je veux creer un plugin Claude Code nomme [NOM_EN_KEBAB_CASE].
Son but en une phrase : [BUT_DU_PLUGIN].

Il doit contenir :
- skills : [LISTE : nom + ce que la skill doit faire]
- agents : [LISTE : nom + role de l'agent]
- hooks : [LISTE : evenement + ce qui doit se declencher]
- serveurs MCP : [LISTE ou "aucun"]

Fais exactement ceci, dans cet ordre :
1. Donne l'arborescence complete du dossier, en bloc de code text.
2. Ecris .claude-plugin/plugin.json avec uniquement des champs reels du manifeste.
   N'invente aucun champ. Ne mets pas de champ de composant si tout est
   a son emplacement par defaut.
3. Ecris chaque fichier SKILL.md avec un frontmatter contenant name et description,
   la description etant redigee pour declencher au bon moment.
4. Ecris chaque fichier d'agent avec un frontmatter limite aux champs supportes.
5. Ecris hooks/hooks.json si des hooks sont demandes, en entourant de guillemets
   doubles tout chemin ${CLAUDE_PLUGIN_ROOT} place dans un champ command sans args.
6. Donne les commandes exactes a lancer pour valider et tester en local.

Contraintes : tout en francais dans les contenus. Chemins avec des barres obliques
vers l'avant uniquement. Aucun chemin sortant de la racine du plugin.

A retenir

  • Un plugin se cree avec deux choses : un dossier et un .claude-plugin/plugin.json dont name est obligatoire.
  • Chaque composant a un emplacement par defaut : skills/, agents/, commands/, hooks/hooks.json, .mcp.json. Tant que tu les respectes, aucun champ de composant n'est necessaire dans le manifeste.
  • skills, hooks et mcpServers s'ajoutent au scan par defaut ; commands et agents le remplacent.
  • Teste avec claude --plugin-dir ./mon-plugin, sans marketplace, et relance /reload-plugins apres chaque edition.
  • claude plugin validate --strict avant de partager : il verifie le manifeste et tous les frontmatters, et sort en code 1 sur avertissement.
  • Un hook de plugin se declenche des que la session charge le plugin, pas seulement quand tu utilises ses skills. Resserre le matcher.

Sources

Corpus personnel de formation · genere le 26/09/2026 · source : 03-creer-un-plugin.md