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 demandelicenseDeclaration).spec.compatibility: plagesplatformetsdkcomme">=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 unereason; marquezsensitive: truece 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/*.regoavec_test.rego(/tmp/toolchains/opa test policies/opa),policies/openfga/model.fgaettests.fga.yaml(/tmp/toolchains/fga model test --tests ...). Les politiques OPA sont installées en brouillon.evaluations/promptfoo.yaml(fournisseurexec:, assertions déterministes) etevaluations/ragas.jsonl(question,ground_truth,reference_contexts) notés avecrag/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 dossierdashboardssousobservabilityest 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 parplugin publishavec l'interpréteur des packs ; les compteurs sont joints à la publication et vérifiés par le contrôletests.
Liste de vérification avant publication
-
enterprise-ai plugin validatene 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 testetfga model testpassent ; les évaluations atteignent leurs seuils. -
plugin signpuisplugin publishavec 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.
-
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.lockpacks/bootstrap.shRé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. -
É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.
-
Valider. Les mêmes contrôles sont refaits par le Control Plane à la publication et à l'installation.
enterprise-ai plugin validate packs/my-supportRé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.
-
Tester. Lancez les quatre types pytest, puis les tests OPA et OpenFGA.
/tmp/toolchains/packs-venv/bin/python -m pytest -q packs/my-support/testscd packs/my-supportenterprise-ai test authzRésultat attendu : chaque suite passe.
En cas d'échec :
enterprise-ai testrenvoie 1 pour une suite en échec ou un binaireopaoufgaabsent. -
Construire et signer.
buildécrit l'archive, son empreinte SHA-256 et le SBOM SPDX ;signsigne l'archive et chaque SKILL.md et écrit le descripteur<name>-<version>.pack.json.enterprise-ai plugin build packs/my-support --out distenterprise-ai plugin sign packs/my-support --out dist --key cosign.key --signer acmeacme: 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 ».
-
Publier.
publishlance 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-supportRé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_failedou 409marketplace_version_existsviennent du Control Plane. -
Preflight et installation. POST /v1/marketplace/preflight lance chaque contrôle d'installation sans rien installer.
plugin installouvre 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.readRésultat attendu : l'identifiant de l'approbation et son quorum sont affichés ; après approbation et application, l'installation est
installeddans Marketplace > Installations.En cas d'échec : 403
permission_unsafe, 422config_invalid, 422permission_not_requested, 409package_suspended, 422preflight_failed, 422smoke_failed(tout est annulé). -
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(pasinstalled, ou version non supérieure), 409rollback_unavailable. -
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
suspendedouuninstalled; la réponse de la désinstallation listeremoved.En cas d'échec : 409
installation_statepour un état qui n'autorise pas le changement.
Dépannage
| Symptôme | Cause | Correction |
|---|---|---|
plugin validate signale un droit manquant | Un agent, une skill ou un outil utilise un droit non demandé. | Ajoutez-le à spec.requestedPermissions avec une reason. |
403 permission_unsafe | Le pack demande un droit que la plateforme refuse. | Retirez ce droit. |
422 permission_not_requested | L'installateur a accordé un droit que le pack ne demande pas. | N'accordez que des droits demandés. |
422 config_invalid | La configuration d'installation ne respecte pas spec.configSchema. | Corrigez les valeurs --config. |
422 preflight_failed | Un 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_failed | Le 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écution | Le serveur MCP a demandé authorize pour un droit non accordé. | Mettez à niveau l'installation avec ce droit, ou retirez l'appel. |
409 marketplace_version_exists | La version est déjà publiée. | Augmentez metadata.version. |
Étapes suivantes
- Guide Marketplace pour exploiter les installations.
- Guide de développement de plugin, guide de développement de skill et guide de développement de connecteur.
- Guide de développement MCP et guide de développement de politiques.