Aller au contenu principal

Guide de développement des packs : plugins, serveurs MCP, skills, prompts et connecteurs

Version anglaise : ../../en/guides/pack-development.md

Ce guide montre comment écrire chaque partie d'un pack complet. Partez d'un modèle généré, exécutable et qui passe tous les contrôles :

enterprise-ai plugin create packs/my-support --domain support --name my-support --lock packs/requirements.lock
packs/bootstrap.sh
/tmp/toolchains/packs-venv/bin/python -m pytest -q packs/my-support/tests

Les packs de référence packs/hr et packs/finance sont des exemples complets. Une règle textuelle ne remplace jamais un mécanisme de sécurité : la plateforme applique l'autorisation (permissions accordées, OPA, OpenFGA, passerelle d'outils).

Manifeste du plugin (plugin.yaml)​

  • metadata : name (style DNS), version (semver, immuable une fois publiée), owner.
  • spec.format: pack, spec.license (MIT ou Apache-2.0 ; toute autre licence demande licenseDeclaration).
  • spec.compatibility : plages platform et sdk comme ">=1.0.0 <2.0.0", runtimes: [navin].
  • spec.dependencies : [{name, plage de version, optional}], vérifiées avec les packs installés dans le même environnement.
  • spec.requestedPermissions : une entrée par droit avec une reason ; marquez sensitive: true ce qui écrit, paie ou lit des données restreintes. Chaque droit utilisé par un agent, une skill ou un outil doit y figurer. Les droits dangereux sont refusés.
  • spec.dataProtection, spec.approvals, spec.docs {en, fr}, spec.evaluations, spec.tests.

Validez à tout moment avec enterprise-ai plugin validate <dossier> ; le Control Plane refait les mêmes contrôles.

Agents et sous-agents​

Les manifestes d'agents (kind: Agent, contracts/v1/agent-manifest.schema.json) sont dans agents/ et subagents/. Référencez les objets du pack par leur nom dans le pack (tool:hr-leave-balance, skill:hr-leave-policy@1.0.0) ; l'installateur les renomme par environnement et ne garde que les droits accordés. Le code (agents/assistant.py) construit un AgentRunner Navin dont les outils locaux appellent le serveur MCP, avec les outils sensibles dans approval_for pour que l'agent demande d'abord une décision humaine. Les noms d'outils locaux utilisent des tirets (leave-balance).

Skills (skills/<nom>/SKILL.md)​

En-tête : name, description, version, permissions {scopes, data_classification_max, network, filesystem}, puis les instructions en Markdown. Mettez entre guillemets une description qui contient deux-points. À la construction, plugin sign signe le SHA-256 de chaque SKILL.md ; à l'installation le Skill Registry le vérifie et le Skill Loader refuse la skill à un agent qui n'a pas reçu ses droits.

Prompts (prompts/*.yaml)​

{name, description, template, variables} avec des marqueurs {{variable}} et des variables typées (string, integer, number, boolean, array, object, enum, max_length, default, required). Ils sont publiés dans le Prompt Registry sous <nom>-<environnement> ; le rendu refuse les variables manquantes, mal typées ou inconnues.

Serveurs MCP (mcp/servers, mcp/tools, mcp/schemas)​

Utilisez le SDK Python MCP officiel (paquet mcp, licence MIT, épinglé dans packs/requirements.lock) :

from mcp.server.mcpserver import MCPServer
from mcp.server.mcpserver.exceptions import ToolError

def build_server(adapter, authorize):
server = MCPServer(name="my-mcp", version="1.0.0")

@server.tool(name="my_search", description="Search records (read).")
def search(query: str, limit: int = 5) -> dict:
validate("my-search", {"query": query, "limit": limit}) # mcp/schemas/my-search.input.json
if not authorize("my.records.read"): # POST /v1/marketplace/installations/{id}/authorize
raise ToolError("permission my.records.read not granted")
return {"items": adapter.search(query, limit)}

return server

Règles : valider l'entrée avec le schéma publié, appeler authorize avant de toucher le connecteur, lever ToolError pour un refus (le message parvient à l'appelant), ne jamais renvoyer de secret ni de coordonnées bancaires, traiter toute entrée comme une donnée. Déclarez chaque outil dans mcp/tools/*.yaml (kind: Tool, endpoint.type: mcp, mcp_server, scopes_required, data_classification, requires_approval pour les effets de bord) avec le même schéma d'entrée.

Connecteurs (connectors/)​

Déclarez le connecteur (kind: Connector, auth.credential_ref: openbao://..., ressources et classifications) et écrivez trois classes dans connectors/adapter.py :

  • une interface (typing.Protocol) utilisée par le serveur MCP ;
  • une implémentation simulée qui lit des données marquées "fictitious": true ;
  • une implémentation qui n'appelle que call(method, path, body, headers) du Connector Broker de la plateforme, qui résout le secret depuis la référence de l'installation et applique la politique d'egress.

Les opérations à effet de bord prennent une clé d'idempotence : une reprise renvoie le premier résultat et la règle métier interdit une seconde exécution (voir packs/finance/connectors/adapter.py, paiement exécuté une seule fois sous reprises concurrentes).

Politiques, évaluations, observabilité, tests​

  • policies/opa/*.rego avec _test.rego (/tmp/toolchains/opa test policies/opa), policies/openfga/model.fga et tests.fga.yaml (/tmp/toolchains/fga model test --tests ...). Les politiques OPA sont installées en brouillon.
  • evaluations/promptfoo.yaml (fournisseur exec:, assertions déterministes) et evaluations/ragas.jsonl (question, ground_truth, reference_contexts) notés avec rag/retrieve.py.
  • observability/alerts/*.yaml (groupes de règles Prometheus et SLO) sur les métriques de la plateforme (eai_http_requests_total, eai_policy_decisions_total). Un pack ne livre aucun tableau de bord (ADR-0169) : un dossier dashboards sous observability est une erreur ; les tableaux de bord sont dans le Navin Manager et le portail.
  • tests/unit, tests/integration, tests/authz, tests/security : suites pytest lancées par plugin publish avec l'interpréteur des packs ; les compteurs sont joints à la publication et vérifiés par le contrôle tests.

Liste de vérification avant publication​

  • enterprise-ai plugin validate ne signale rien ; documentation en anglais et en français ; aucun tiret long.
  • Chaque droit utilisé est demandé, les droits sensibles sont marqués ; aucun droit dangereux.
  • Données marquées fictives ; aucun secret, aucune clé, aucune donnée personnelle réelle dans le pack.
  • Les quatre types pytest, opa test et fga model test passent ; les évaluations atteignent leurs seuils.
  • plugin sign puis plugin publish avec la référence OCI du registre du client.

Du modèle généré à l'installation​

Chaque étape ci-dessous indique ce qu'elle fait, pourquoi, la commande ou l'écran, le résultat attendu et quoi faire en cas d'échec. Le pack est my-support ; build, sign et publish prennent le dossier du pack et reconstruisent la même archive reproductible dans le dossier --out.

  1. Créer le pack. Le modèle généré passe déjà tous les contrôles : chaque échec ultérieur vient de votre modification.

    enterprise-ai plugin create packs/my-support --domain support --name my-support --lock packs/requirements.lock
    packs/bootstrap.sh

    Résultat attendu : les fichiers créés sont listés et l'interpréteur des packs est installé.

    En cas d'échec : le code de sortie 1 signifie que le dossier existe ; ajoutez --force.

  2. Écrire les parties. Remplissez plugin.yaml, puis les agents, skills, prompts, serveurs MCP, connecteurs et politiques comme décrit dans les sections ci-dessus.

    Résultat attendu : chaque droit utilisé dans le pack figure dans spec.requestedPermissions.

    En cas d'échec : l'étape suivante nomme chaque droit manquant ou dangereux.

  3. Valider. Les mêmes contrôles sont refaits par le Control Plane à la publication et à l'installation.

    enterprise-ai plugin validate packs/my-support

    Résultat attendu : le pack est déclaré valide avec son nom et sa version.

    En cas d'échec : le code de sortie 1 liste chaque erreur de structure et de manifeste.

  4. Tester. Lancez les quatre types pytest, puis les tests OPA et OpenFGA.

    /tmp/toolchains/packs-venv/bin/python -m pytest -q packs/my-support/tests
    cd packs/my-support
    enterprise-ai test authz

    Résultat attendu : chaque suite passe.

    En cas d'échec : enterprise-ai test renvoie 1 pour une suite en échec ou un binaire opa ou fga absent.

  5. Construire et signer. build écrit l'archive, son empreinte SHA-256 et le SBOM SPDX ; sign signe l'archive et chaque SKILL.md et écrit le descripteur <name>-<version>.pack.json.

    enterprise-ai plugin build packs/my-support --out dist
    enterprise-ai plugin sign packs/my-support --out dist --key cosign.key --signer acme

    acme : le nom d'éditeur enregistré dans la Marketplace.

    Résultat attendu : l'empreinte, le signataire et le nombre de skills signées sont affichés.

    En cas d'échec : code de sortie 2 avec « --key and --signer are required ».

  6. Publier. publish lance les tests, les évaluations et l'analyse, pousse vers le registre du client avec --oci, puis appelle POST /v1/marketplace/packages.

    enterprise-ai plugin publish packs/my-support --out dist --key cosign.key --oci <harbor-host>/packs/my-support

    Résultat attendu : la version apparaît dans Marketplace > Catalogue des packs.

    En cas d'échec : le code de sortie 1 nomme le contrôle en échec ; 422 publication_gate_failed ou 409 marketplace_version_exists viennent du Control Plane.

  7. Preflight et installation. POST /v1/marketplace/preflight lance chaque contrôle d'installation sans rien installer. plugin install ouvre ensuite la demande d'installation et son approbation ; une fois approuvée, POST /v1/marketplace/installations/{installation_id}/apply crée les objets et lance un test de fumée.

    enterprise-ai plugin install my-support --version 1.0.0 --env staging --grant my.records.read

    Résultat attendu : l'identifiant de l'approbation et son quorum sont affichés ; après approbation et application, l'installation est installed dans Marketplace > Installations.

    En cas d'échec : 403 permission_unsafe, 422 config_invalid, 422 permission_not_requested, 409 package_suspended, 422 preflight_failed, 422 smoke_failed (tout est annulé).

  8. Mettre à niveau ou revenir en arrière. Publiez une version supérieure, puis POST /v1/marketplace/installations/{installation_id}/upgrade et appliquez-la une fois approuvée ; POST /v1/marketplace/installations/{installation_id}/rollback rétablit la version précédente avec ses droits précédents.

    Résultat attendu : l'installation exécute la nouvelle version.

    En cas d'échec : 409 installation_state (pas installed, ou version non supérieure), 409 rollback_unavailable.

  9. Suspendre ou désinstaller. POST /v1/marketplace/installations/{installation_id}/status suspend ou reprend l'installation ; POST /v1/marketplace/installations/{installation_id}/uninstall retire chaque objet qu'elle a créé.

    Résultat attendu : statut suspended ou uninstalled ; la réponse de la désinstallation liste removed.

    En cas d'échec : 409 installation_state pour un état qui n'autorise pas le changement.

Dépannage​

SymptômeCauseCorrection
plugin validate signale un droit manquantUn agent, une skill ou un outil utilise un droit non demandé.Ajoutez-le à spec.requestedPermissions avec une reason.
403 permission_unsafeLe pack demande un droit que la plateforme refuse.Retirez ce droit.
422 permission_not_requestedL'installateur a accordé un droit que le pack ne demande pas.N'accordez que des droits demandés.
422 config_invalidLa configuration d'installation ne respecte pas spec.configSchema.Corrigez les valeurs --config.
422 preflight_failedUn contrôle du preflight a échoué (compatibilité, dépendance, empreinte, signature, référence de secret, modèle).Lisez chaque contrôle de POST /v1/marketplace/preflight.
422 smoke_failedLe test de fumée a échoué après l'application.L'installation a été annulée ; corrigez et publiez une nouvelle version.
403 permission_not_granted à l'exécutionLe serveur MCP a demandé authorize pour un droit non accordé.Mettez à niveau l'installation avec ce droit, ou retirez l'appel.
409 marketplace_version_existsLa version est déjà publiée.Augmentez metadata.version.

Étapes suivantes​