Module 10 · Abilities : exposer votre site aux agents
Durée : 45-60 min · Prérequis : les modules 1 et 2, et idéalement le module 9 (Nectar), dont ce module reprend l’opposition.
À la fin de ce module, vous savez déclarer une ability avec l’attribut #[Ability] ou la facade Ability, décrire ses entrées, la protéger par une vérification de permission, et expliquer en quoi elle diffère du serveur MCP de Nectar.
1. Deux choses différentes pour les agents
Section intitulée « 1. Deux choses différentes pour les agents »Les deux touchent aux agents IA et à MCP, d’où la confusion. Elles répondent à des questions différentes.
| Nectar | Abilities | |
|---|---|---|
| Sert | votre agent de code, pendant que vous construisez le site | les agents et outils d’automatisation qui utilisent le site |
| Ce qu’il apporte | du contexte Pollora : guidelines, skills, outils MCP en lecture seule comme post_types_info |
des fonctions que votre site déclare : entrées, sorties, permissions, comportement |
| Où ça tourne | local et development uniquement |
partout où vous déclarez les abilities |
| Qui l’écrit | l’équipe Pollora | vous |
Le guide sur les agents de code est explicite : les outils de Nectar travaillent sur le code, inspecté pendant le développement, et Nectar ne se charge pas en production. L’API Abilities va dans l’autre sens : WordPress 6.9 a introduit un registre où plugins, thèmes et noyau déclarent ce qu’ils savent faire sous forme lisible par une machine, pour que des agents découvrent et invoquent les fonctions du site sans intégration sur mesure pour chacune.
2. Déclarer une ability
Section intitulée « 2. Déclarer une ability »Pollora enveloppe l’API dans le paquet pollora/abilities, avec deux façons de déclarer : l’attribut déclaratif #[Ability] et la facade impérative Ability. Utilisez l’attribut dès que ça dépasse quelques lignes.
Une classe portant #[Ability] implémente AbilityHandler : l’attribut dit ce que l’ability est, le handler ce qu’elle fait.
use Pollora\Abilities\Domain\Contracts\AbilityHandler;use Pollora\Abilities\Domain\Model\Behaviour;use Pollora\Abilities\Domain\Model\Input;use Pollora\Abilities\Domain\Schema\SchemaBuilder;use Pollora\Attributes\Ability;
#[Ability( name: 'acme/create-post', description: 'Creates a post from a title and a status.', category: 'acme-content', behaviour: Behaviour::Creates,)]final class CreatePost implements AbilityHandler{ public function schema(SchemaBuilder $schema): void { $schema->string('title', 'Title of the post to create.', required: true); $schema->enum('status', 'Publication status.', ['draft', 'publish'], default: 'draft'); }
public function authorize(Input $input): mixed { return current_user_can('edit_posts') ?: new WP_Error('forbidden', 'You cannot create posts.', ['status' => 403]); }
public function handle(Input $input): mixed { return ['id' => wp_insert_post([ 'post_title' => $input->string('title'), 'post_status' => $input->string('status', 'draft'), ])]; }}La classe est découverte partout où le système de découverte scanne (app/, un thème, un module) et instanciée par le conteneur : l’injection par constructeur fonctionne. Une classe marquée #[Ability] qui n’implémente pas AbilityHandler est journalisée comme une erreur, pas ignorée.
Les paramètres de l’attribut sont name, description et category (obligatoires), label et behaviour (facultatifs ; behaviour vaut Behaviour::Reads par défaut). La description est la description d’outil que lit l’agent.
La même chose avec la facade, pratique pour une ability courte :
use Pollora\Abilities\Domain\Model\Input;use Pollora\Abilities\Domain\Schema\SchemaBuilder;use Pollora\Support\Facades\Ability;
Ability::define('acme/get-posts') ->description('Returns the most recent posts, newest first.') ->category('acme-content') ->input(fn (SchemaBuilder $schema) => $schema ->integer('limit', 'How many posts to return.', default: 10, minimum: 1, maximum: 100)) ->can(fn (Input $input): bool => current_user_can('edit_posts')) ->using(fn (Input $input): array => array_map( static fn (WP_Post $post): array => ['id' => $post->ID, 'title' => $post->post_title], get_posts(['numberposts' => $input->integer('limit', 10)]), ));- Rien n’est enregistré tant qu’un corps n’est pas fourni par
using()ouhandledBy(). - Un nom est
namespace/slug, en minuscules alphanumériques séparées par des tirets simples. Pollora refuse à la déclaration un slug seul, un libellé ou une description vides, ou un schéma d’entrée qui n’est pas un objet. - Vous déclarez là où c’est naturel (un fournisseur de services, une classe découverte). Pollora met la déclaration en file et la publie sur
wp_abilities_api_categories_initpuiswp_abilities_api_init; vous n’accrochez jamais ces hooks vous-même.
Les catégories
Section intitulée « Les catégories »Chaque ability est rangée dans une catégorie, qui doit exister d’abord :
Ability::category('acme-content', 'Editorial', 'Posts and pages.');Les slugs sont globaux à l’installation et le noyau de WordPress en réserve déjà plusieurs : préfixez les vôtres. Une collision n’est pas bénigne : le second enregistrement est refusé et toutes les abilities pointant vers la catégorie échouent avec lui. Si la catégorie nommée par un #[Ability] n’a jamais été déclarée, Pollora la déclare pour vous.
3. Le schéma est l’interface
Section intitulée « 3. Le schéma est l’interface »Le schéma d’entrée est la seule documentation qu’un modèle de langage reçoit sur votre ability. Chaque méthode de SchemaBuilder prend une description, et il n’existe pas de surcharge qui l’omette. WordPress valide l’entrée contre le schéma avant que votre corps ne s’exécute.
- Préférez
enum()à une chaîne libre quand les valeurs acceptées sont connues : un modèle qui voit les options en choisit une, un modèle à qui l’on donne une chaîne libre invente une valeur plausible. - Posez un
maximumsur tout ce qui dimensionne une requête, pour qu’un modèle ne puisse pas demander toutes les lignes de la table. output()est facultatif ; déclarez-le quand la forme renvoyée est stable, pour qu’un client puisse valider ce qu’il reçoit.
Dans le corps, Input est un lecteur défensif (string(), integer(), boolean(), id()…). Ses accesseurs convertissent au lieu de lever : "12" envoyé pour un entier donne un appel qui fonctionne.
4. Ce qui rend une ability découvrable, et comment les agents l’atteignent
Section intitulée « 4. Ce qui rend une ability découvrable, et comment les agents l’atteignent »Une ability est découvrable dès qu’elle est enregistrée dans le registre des abilities de WordPress : un nom, une description, une catégorie et un schéma. Les abilities enregistrées sont exposées par les contrôleurs REST du noyau :
GET /wp-json/wp-abilities/v1/abilitiesGET /wp-json/wp-abilities/v1/abilities/acme/get-postsPOST /wp-json/wp-abilities/v1/abilities/acme/get-posts/runLe plugin MCP Adapter publie les abilities enregistrées via le Model Context Protocol. Une ability n’est pas un outil MCP, mais c’est de quoi un outil MCP est le plus souvent fait. Vous déclarez l’ability une fois, et ce qui consomme des abilities la reprend.
C’est pourquoi le paquet porte le nom d’abilities et non de MCP : pollora/abilities n’implémente aucune partie du protocole.
5. Sécurité : c’est la vérification de permission qui protège le site
Section intitulée « 5. Sécurité : c’est la vérification de permission qui protège le site »- Refus par défaut. Omettre
can()(facade) fait que l’ability refuse tout. Une vérification oubliée rend l’ability inutile, pas publique. - Même entrée que le corps. La vérification reçoit la même entrée, donc elle peut être par objet :
current_user_can('edit_post', $input->id('id'))plutôt qu’unedit_postsglobal. - Expliquer les refus. Renvoyez un
WP_Errorplutôt quefalse: un client qui sait pourquoi peut agir. - Le comportement est indicatif. Chaque ability déclare ce qu’elle fait au site :
reads()(par défaut),creates(),updates()oudeletes(), ouBehaviour::Reads,Creates,Updates,Deletesavec l’attribut. WordPress le publie sousmeta.annotations, et les clients le convertissent enreadOnlyHint,destructiveHintetidempotentHintpour décider du degré de précaution d’un appel : une lecture peut tourner sans surveillance, une suppression se confirme. WordPress ne le fait pas respecter. C’est le callback de permission qui protège le site. - Se tromper de comportement est pire que l’omettre : une ability qui modifie, déclarée
Reads, invite aux appels sans surveillance.
Une règle raisonnable en découle : exposez la capacité la plus étroite qui fait le travail, donnez-lui la vérification de permission la plus étroite, et commencez par des lectures.
6. À essayer sur votre projet
Section intitulée « 6. À essayer sur votre projet »Il n’y a pas d’exercice de laboratoire pour ce module. Sur un projet Pollora sous WordPress 6.9 ou plus :
- Créez une ability en lecture seule
acme/get-postsavec l’exemple de facade ci-dessus, dans un fournisseur de services (remplacezacmeet la catégorie par un préfixe qui appartient à votre projet). - Lancez
php artisan discovery:clearsi vous l’avez écrite comme classe. - Appelez-la sans client MCP :
wp eval '$a = wp_get_ability("acme/get-posts"); var_dump($a->execute(["limit" => 3]));', ouGET /wp-json/wp-abilities/v1/abilities. - Retirez l’appel à
can()et relancez : elle doit maintenant être refusée. - Facultatif : avec la skill
pollora-abilitiesde Nectar, demandez à Claude Code d’écrire une seconde ability, puis relisez vous-même sonbehaviouret sa vérification de permission.
Une classe handler se teste aussi sans WordPress : appelez directement schema(), authorize() et handle().
Vérifiez ce que vous avez retenu
Section intitulée « Vérifiez ce que vous avez retenu »Nectar ou Abilities ?
Vous voulez qu’un agent de code sache quels types de contenu votre projet enregistre pendant que vous développez. Qu’est-ce qui convient ?
- Une ability, déclarée avec
#[Ability]Les abilities sont ce que votre site offre aux agents qui l’utilisent, pas du contexte pour l’agent du développeur.
- Le serveur MCP de Nectar, avec un outil comme
post_types_info - Le plugin MCP Adapter seul
Nectar est réservé au développement et en lecture seule.
Développement ou exécution ?
Quelle affirmation est correcte ?
- Abilities et Nectar ne se chargent qu’en
localetdevelopmentSeul Nectar est réservé au développement. Les abilities sont déclarées par le site et peuvent être servies en production.
- Nectar ne se charge qu’en
localetdevelopment; les abilities sont déclarées par votre site et ne sont pas liées au développement - Les abilities ont besoin de Nectar pour être enregistrées
Les abilities demandent WordPress 6.9 ou plus, pas Nectar.
Une vérification de permission oubliée
Une ability déclarée avec la facade n’a pas de can(). Que se passe-t-il quand un agent l’appelle ?
- Elle s’exécute, tant que l’entrée respecte le schéma
- Elle refuse tout
Les vérifications de permission refusent par défaut : une vérification oubliée rend l’ability inutile, pas publique.
- Elle ne s’exécute que pour les administrateurs
C’est voulu.
Ce que fait le comportement
Une ability utilise Behaviour::Deletes. Qu’est-ce que cela garantit ?
- WordPress bloque l’appel pour les utilisateurs qui ne peuvent pas supprimer
WordPress ne fait pas respecter le comportement.
- Les clients savent qu’elle est destructive et peuvent demander confirmation, mais seul le callback de permission protège le site
- L’ability est masquée du point d’accès REST
Les annotations sont de simples indications.
Ce qui rend une ability découvrable
Que faut-il à une ability pour être listée par le registre des abilities et comprise par un agent ?
- Un nom
namespace/slugenregistré, une description, une catégorie et un schéma d’entrée décrit - Une route dans
routes/web.phppar abilityLes abilities enregistrées sont exposées par les contrôleurs REST du noyau et par MCP Adapter.
- Un nom réduit à un slug, comme
get-postsUn slug seul n’enregistre rien ; Pollora le refuse à la déclaration.
La description et celles du schéma sont ce que lit l’agent.
Progression du quiz
0 / 0 questions répondues (0%)
0 correctes
Quiz terminé !
0%
0 / 0 correctes
Pour aller plus loin
Section intitulée « Pour aller plus loin »- Abilities : accesseurs d’
Input,has()contrefilled(), API du paquet - Nectar overview : le serveur MCP côté développement et la skill
pollora-abilities - AI coding agents for WordPress projects
- Discovery : comment les classes
#[Ability]sont trouvées
Répondu: 0 / 0 (0%)