Tout site WordPress en version 6.9 ou ultérieure a de quoi faire tourner un serveur MCP personnalisé. L’API Abilities, intégrée au cœur, permet à une extension de décrire ce qu’elle sait faire, et le MCP Adapter, une extension canonique du répertoire WordPress.org, transforme ces descriptions en outils qu’un assistant IA comme Claude peut appeler.
Ce guide prend environ une heure. Vous créez une petite extension avec une capacité en lecture seule, vous connectez un client sous un utilisateur dédié doté de son propre rôle, puis vous ajoutez une capacité d’écriture qui laisse la dernière étape à une personne. Chaque bout de code ci-dessous a été testé sur WordPress 7.1.3 avec MCP Adapter 0.7.1, connecté par le même proxy que celui qu’utilise Claude Desktop.
Key takeaways
- A WordPress site on 6.9 or later already has what it needs for a custom MCP server. The Abilities API in core describes what a plugin can do, and the MCP Adapter plugin turns those descriptions into tools an AI assistant such as Claude can call. arvindkumar.ca
- You need WordPress 6.9 or later, PHP 7.4 or later, HTTPS and a staging copy, because Application Passwords are only offered over HTTPS unless the site is set as a local environment. Keep your abilities in a small plugin of their own, not the theme’s functions.php. arvindkumar.ca
- Register each ability with wp_register_ability() and give it input and output schemas, a clear description and its own capability for the permission check. Setting additionalProperties to false makes WordPress reject any input the schema doesn’t name. arvindkumar.ca
- Connect the client as a dedicated user with its own role and an Application Password, then point it at the adapter’s default server. Your ability shows up through the three default tools (discover, get info and execute), not as a tool of its own, unless you list it in a custom server. arvindkumar.ca
- For a write ability, prepare the change and stop short of making it live. A draft-creating ability fixes the status in code and accepts only a title and content, so the rule sits in your code where no agent can talk its way past it. arvindkumar.ca
- An Application Password has no scope of its own, so it carries every capability of its user across the whole REST API. Use a role that can’t do damage, and treat permission callbacks and schemas as the second line. Both have to hold. arvindkumar.ca
- The common mistakes are connecting as an administrator or Editor, registering outside the hooks, using __return_true as a permission check, and setting ‘public’ => true when you meant MCP only. Test the result by confirming that refused users, disallowed input, published posts and REST API publishing all fail as they should. arvindkumar.ca
Summarised from this post, formatted for answer engines.
De quoi avez-vous besoin avant de créer un serveur MCP personnalisé pour WordPress?
Il vous faut WordPress 6.9 ou ultérieur, PHP 7.4 ou ultérieur, HTTPS et une copie de préproduction du site. C’est avec WordPress 6.9 que l’API Abilities a rejoint le cœur. L’extension MCP Adapter, en version 0.7.1 au moment d’écrire ces lignes, exige les mêmes versions minimales. WordPress n’offre les mots de passe d’application que sur HTTPS, sauf si le site est déclaré comme environnement local; un site de préproduction en simple HTTP ne vous permettra donc pas d’en créer un.
Côté client, il faut un client MCP comme Claude Desktop, ainsi que Node.js, car un site WordPress distant se connecte par le proxy @automattic/mcp-wordpress-remote d’Automattic.
Placez les capacités dans une petite extension qui leur est propre, et non dans le fichier functions.php du thème, pour qu’elles survivent à un changement de thème.
Comment enregistrer une capacité avec l’API Abilities de WordPress?
Vous enregistrez une catégorie sur wp_abilities_api_categories_init, puis la capacité sur wp_abilities_api_init avec wp_register_ability(). Chaque capacité a besoin d’un nom de la forme namespace/ability-name, d’une étiquette, d’une description, d’une catégorie, d’une fonction de rappel pour les permissions et de la fonction qui fait le travail. Ajoutez aussi des schémas d’entrée et de sortie : WordPress valide l’entrée selon le schéma avant que votre code s’exécute.
- Créez un dossier dans wp-content/plugins, enregistrez-y le fichier ci-dessous sous le nom my-site-abilities.php et activez l’extension. C’est réussi si l’extension s’active sans avis avec
WP_DEBUGactivé, et que Utilisateurs, Ajouter un utilisateur propose un rôle d’agent IA.
<?php
/**
* Plugin Name: My Site Abilities
* Requires at least: 6.9
*/
register_activation_hook( __FILE__, function () {
add_role( 'ai_agent', 'AI agent', array(
'read' => true,
'edit_posts' => true,
'list_site_drafts' => true,
) );
} );
register_deactivation_hook( __FILE__, function () {
remove_role( 'ai_agent' );
} );
add_action( 'wp_abilities_api_categories_init', function () {
wp_register_ability_category( 'my-site', array(
'label' => 'My site',
'description' => 'Abilities for working with this site\'s content.',
) );
} );
add_action( 'wp_abilities_api_init', function () {
wp_register_ability( 'my-site/list-drafts', array(
'label' => 'List drafts',
'description' => 'Lists the most recently edited draft posts with ID, title and last modified date. Read only.',
'category' => 'my-site',
'input_schema' => array(
'type' => 'object',
'properties' => array(
'limit' => array( 'type' => 'integer', 'minimum' => 1, 'maximum' => 20 ),
),
'additionalProperties' => false,
'default' => array( 'limit' => 10 ),
),
'output_schema' => array(
'type' => 'object',
'properties' => array(
'drafts' => array(
'type' => 'array',
'items' => array(
'type' => 'object',
'properties' => array(
'id' => array( 'type' => 'integer' ),
'title' => array( 'type' => 'string' ),
'modified' => array( 'type' => 'string' ),
),
),
),
),
),
'permission_callback' => function () {
return current_user_can( 'list_site_drafts' );
},
'execute_callback' => function ( $input = array() ) {
$posts = get_posts( array(
'post_type' => 'post',
'post_status' => 'draft',
'orderby' => 'modified',
'numberposts' => $input['limit'] ?? 10,
) );
$drafts = array();
foreach ( $posts as $post ) {
$drafts[] = array(
'id' => $post->ID,
'title' => get_the_title( $post ),
'modified' => get_post_datetime( $post, 'modified' )->format( DATE_ATOM ),
);
}
return array( 'drafts' => $drafts );
},
'meta' => array(
'mcp' => array( 'public' => true ),
'annotations' => array(
'readonly' => true,
'destructive' => false,
'idempotent' => true,
),
),
) );
} );
Quelques lignes de ce fichier en font plus qu’il n’y paraît :
- La description est ce que lit un assistant pour décider s’il appelle la capacité; indiquez donc ce qui est retourné et que la capacité ne fait que lire.
- La vérification des permissions utilise sa propre capacité,
list_site_drafts, accordée uniquement au rôle d’agent IA. Vérifier une capacité native commeedit_others_postsobligerait l’utilisateur qui se connecte à avoir les droits d’un éditeur, ce qui est beaucoup plus de pouvoir qu’il n’en faut pour lister des brouillons. additionalPropertiesà false fait en sorte que WordPress rejette toute entrée que le schéma ne nomme pas, de sorte qu’un agent ne peut pas glisser un paramètre que vous n’aviez pas prévu. La valeurdefaultdu schéma compte aussi : sans elle, un appel sans entrée échoue avec « input is not of type object ».- La date de modification vient de
get_post_datetime()et non du champpost_modified_gmt, car ce champ contient des zéros pour un brouillon enregistré une seule fois. 'mcp' => array( 'public' => true )expose la capacité par le serveur par défaut du MCP Adapter, et nulle part ailleurs. L’adaptateur respecte aussi l’indicateur plus général'public' => true, mais à partir de WordPress 7.1, le cœur utilise ce même indicateur pour lister aussi la capacité dans son API REST. Si MCP est la seule porte que vous voulez ouvrir, utilisez la clémcp. Une capacité sans aucun des deux indicateurs reste privée, accessible uniquement par un serveur MCP personnalisé qui la nomme explicitement.
Comment exposer la capacité par le MCP Adapter et connecter un client?
Installez le MCP Adapter, donnez à un utilisateur dédié le rôle d’agent IA et un mot de passe d’application, puis pointez votre client vers le serveur par défaut de l’adaptateur. Voici l’entrée Claude Desktop dont vous aurez besoin à l’étape 3, avec l’URL de votre préproduction, le nom d’utilisateur de l’agent et son mot de passe d’application :
{
"mcpServers": {
"my-site": {
"command": "npx",
"args": ["-y", "@automattic/mcp-wordpress-remote@latest"],
"env": {
"WP_API_URL": "https://staging.example.com/wp-json/mcp/mcp-adapter-default-server",
"WP_API_USERNAME": "mcp-agent",
"WP_API_PASSWORD": "xxxx xxxx xxxx xxxx xxxx xxxx"
}
}
}
}
- Installez l’adaptateur depuis Extensions, Ajouter : cherchez MCP Adapter et activez-le. C’est réussi si
https://staging.example.com/wp-json/mcpliste/mcp/mcp-adapter-default-server. - Créez un utilisateur réservé à l’agent avec le rôle d’agent IA, puis, sous Utilisateurs, Profil, Mots de passe d’application, ajoutez-en un nommé d’après le client. C’est réussi si WordPress affiche le nouveau mot de passe une seule fois; copiez-le à ce moment-là.
- Dans Claude Desktop, ouvrez Settings, Developer, Edit config, ajoutez l’entrée ci-dessus, enregistrez et redémarrez l’application. C’est réussi si le serveur s’affiche comme connecté.
- Demandez à l’assistant de lister les capacités (abilities) de votre site, puis de lister vos brouillons. Si
my-site/list-draftsapparaît et renvoie les titres de vos brouillons, c’est que ça fonctionne.
Votre ability n’apparaîtra pas d’elle-même dans la liste d’outils du client. Le serveur par défaut offre trois outils, mcp-adapter-discover-abilities, mcp-adapter-get-ability-info et mcp-adapter-execute-ability, et l’assistant accède à vos abilities par leur intermédiaire. La découverte liste aussi trois abilities en lecture seule fournies par le cœur de WordPress : les infos du site, les infos de l’utilisateur courant et les infos de l’environnement. Pour donner à une ability son propre outil, listez-la dans un serveur personnalisé créé sur le hook mcp_adapter_init. Si la connexion échoue, WordPress MCP Adapter errors and how to fix them passe en revue chaque message d’erreur.
Comment ajouter une ability d’écriture qui exige l’approbation d’une personne?
Faites en sorte que l’ability prépare la modification sans la mettre en ligne. Pour du contenu, cela signifie qu’elle crée des brouillons et ne publie jamais. Votre client MCP peut demander une confirmation avant d’exécuter un outil, mais cette demande relève du client; la règle, elle, doit se trouver dans votre code, là où aucun agent ne peut la contourner à force de persuasion.
- Ajoutez ce deuxième enregistrement dans le même callback
wp_abilities_api_init, sousmy-site/list-drafts. Si, en demandant un brouillon à l’assistant, un nouveau brouillon apparaît dans Articles, rédigé par l’utilisateur agent, et que rien n’est mis en ligne, c’est que ça fonctionne.
wp_register_ability( 'my-site/create-draft', array(
'label' => 'Create draft',
'description' => 'Saves a new draft post with a title and content, and returns its ID and edit link. Never publishes.',
'category' => 'my-site',
'input_schema' => array(
'type' => 'object',
'properties' => array(
'title' => array( 'type' => 'string', 'minLength' => 1, 'maxLength' => 200 ),
'content' => array( 'type' => 'string', 'minLength' => 1 ),
),
'required' => array( 'title', 'content' ),
'additionalProperties' => false,
),
'output_schema' => array(
'type' => 'object',
'properties' => array(
'id' => array( 'type' => 'integer' ),
'edit_link' => array( 'type' => 'string' ),
),
),
'permission_callback' => function () {
return current_user_can( 'edit_posts' );
},
'execute_callback' => function ( $input ) {
$id = wp_insert_post( array(
'post_type' => 'post',
'post_title' => sanitize_text_field( $input['title'] ),
'post_content' => wp_kses_post( $input['content'] ),
'post_status' => 'draft', // Never 'publish': a person does that.
'post_author' => get_current_user_id(),
), true );
if ( is_wp_error( $id ) ) {
return $id;
}
return array(
'id' => $id,
'edit_link' => get_edit_post_link( $id, 'raw' ),
);
},
'meta' => array(
'mcp' => array( 'public' => true ),
'annotations' => array(
'readonly' => false,
'destructive' => false,
'idempotent' => false,
),
),
) );
Le statut est fixé dans le code et le schéma n’accepte qu’un titre et un contenu; une requête qui ajoute post_status est donc rejetée avant même l’exécution du callback. Le lien de modification indique à la personne exactement où faire la révision. Tout ce qui supprime, modifie des articles en lot ou touche aux réglages reste hors de la première version.
Comment limiter l’accès de l’agent?
Donnez-lui un rôle incapable de faire des dégâts, parce qu’un mot de passe d’application n’a pas de portée propre. Il hérite de toutes les capacités de son utilisateur et fonctionne sur toute l’API REST, pas seulement sur vos abilities.
C’est pourquoi le plugin ajoute le rôle agent IA au lieu de réutiliser Éditeur. Testé avec le mot de passe d’application de l’agent sur l’API REST classique, ce rôle pouvait créer des brouillons et lire les siens, mais WordPress refusait de publier un article, d’ouvrir le brouillon d’une autre personne ou de téléverser un fichier. Le mot de passe d’application d’un Éditeur, lui, a publié un article par la même API en une seule requête, peu importe ce que les abilities permettaient. Les rôles et les capacités encadrent de la même façon les fonctions d’IA intégrées au site, ce dont j’ai parlé dans How to limit WordPress AI features by role.
La capacité list_site_drafts est la seule exception volontaire : par l’ability, l’agent voit les titres de tous les brouillons, et rien de plus.
Vos callbacks de permission et vos schémas forment la deuxième ligne de défense. Le rôle détermine ce que l’utilisateur peut faire n’importe où sur le site; l’ability détermine ce qu’elle fait ici, avec quelles données et sur quels articles. Les deux doivent tenir.
Que devriez-vous ajouter ensuite à votre serveur MCP?
D’autres abilities construites de la même façon : une tâche chacune, en lecture seule d’abord, et toute modification enregistrée à un endroit où une personne la révise. En voici quelques-unes qui suivent ce modèle :
- Un audit en lecture seule qui liste les brouillons sans extrait ni image mise en avant.
- Une vérification en lecture seule qui liste les articles publiés non mis à jour depuis un an, pour qu’une personne les révise.
- Un rapport en lecture seule des articles qui pointent vers une URL donnée, avant que vous la changiez ou la supprimiez.
- Une ability d’écriture qui enregistre des méta-descriptions suggérées sous forme de brouillons ou dans un champ personnalisé pour révision, jamais directement sur la page en ligne.
Quelles sont les erreurs courantes, et comment vérifier le résultat?
Voici les erreurs que je vérifierais en premier :
- Se connecter en tant qu’administrateur ou qu’Éditeur. L’agent hérite de toutes les capacités de cet utilisateur, par l’API REST comme par vos abilities. Utilisez un rôle dédié et un mot de passe d’application par client.
- Enregistrer en dehors des hooks.
wp_register_ability()retournenullquand la fonction s’exécute en dehors dewp_abilities_api_initou qu’elle nomme une catégorie qui n’existe pas, et avecWP_DEBUGdésactivé, elle échoue en silence. - Utiliser
__return_truecomme vérification des permissions. L’introduction à l’adaptateur sur le blogue des développeurs WordPress le déconseille pour les opérations destructrices. - Définir
'public' => truealors que vous vouliez seulement MCP. Sur WordPress 7.1 et les versions ultérieures, cela liste aussi l’abilité dans l’API REST du cœur. - Ajouter le rôle alors que l’extension est déjà active. Le rôle est créé à l’activation; désactivez donc l’extension, puis réactivez-la après l’ajout.
Pour vérifier le résultat, testez-le comme n’importe quelle extension. Je teste mes extensions avec Playwright, et ici, je confirmerais quatre choses : un utilisateur sans la capacité est refusé, une entrée que le schéma n’autorise pas est rejetée, l’abilité d’écriture ne produit jamais un article publié, et le mot de passe d’application de l’agent ne peut pas non plus publier par l’API REST. L’IA peut aider à écrire ces tests, mais la réflexion et la révision restent entre les mains du développeur.
Faites rouler l’abilité en lecture seule sur un site de préproduction pendant une semaine et observez où l’assistant répond bien et où il devine. Cela vous indique quelle abilité écrire ensuite bien mieux que n’importe quelle liste dressée au départ.
