Si ya has leído mi artículo sobre qué es la Abilities API, sabrás que esta nueva característica de WordPress permite que los plugins y temas muestren sus funcionalidades de forma estandarizada, entendible y segura a los distintos modelo de IA y otras herramientas, como aplicaciones de automatización.
Pero una cosa es entender el concepto y otra muy distinta es ponerse manos a la obra, así que he preparado este tutorial para aprender a implementar la Abilities API paso a paso, con códigos de ejemplo a partir de plugins y temas que ya lo tienen incluido para que, de este modo, si te animas, lo adaptes a tus propios proyectos.
Cómo implementar abilities en un plugin
Vamos a tomar un plugin existente, VigIA, y ver cómo incorporamos la Abilities API.
VigIA es un plugin de analíticas gratuito que creé para monitorizar la actividad de crawlers de IA en tu sitio (GPTBot, ClaudeBot, PerplexityBot y otros más de 50), permite bloquearlos y gestiona reglas de robots.txt para todos.
Al añadirle abilities lo que hacemos es que todas sus funcionalidades queden expuestas para agentes de IA, herramientas de automatización, paneles externos o cualquier sistema que sepa comunicarse con la Abilities API.
Me da hasta un poco de flojera (y cachondeito) que un plugin diseñado para vigilar a los bots de IA ahora pueda comunicarse con ellos de tú a tú. En fin, son mis cosas, ya me vas conociendo.
La idea es no mostrarte un ejemplo imaginario, sino que aprendas cómo se hace esto de incorporar la Abilities API viendo un plugin en el que ya he incluido esta habilidad. Así, si te quedas con más curiosidad, puedes instalarlo o descargarlo y enredar o hacer pruebas por tu cuenta.
Pero, vamos al lío…
Qué abilities vamos a crear
En VigIA he incluido 9 abilities organizadas en tres grupos:
| Ability | Tipo | Qué hace |
|---|---|---|
| Analíticas | ||
vigia/get-crawler-stats |
Solo lectura | Devuelve estadísticas: visitas totales, crawlers únicos y páginas rastreadas |
vigia/get-top-crawlers |
Solo lectura | Lista los crawlers más activos con su categoría y número de visitas |
vigia/get-top-pages |
Solo lectura | Muestra las páginas más rastreadas del sitio |
| Bloqueo | ||
vigia/get-blocked-items |
Solo lectura | Lista todos los bloqueos activos (User-Agent e IP) |
vigia/block-crawler |
Acción | Bloquea un crawler por patrón de User-Agent |
vigia/unblock-crawler |
Acción | Elimina un bloqueo existente |
| Robots.txt | ||
vigia/get-robots-rules |
Solo lectura | Devuelve las reglas actuales para crawlers de IA |
vigia/add-robots-disallow |
Acción | Añade una directiva Disallow para un crawler |
vigia/remove-robots-rule |
Acción | Elimina una regla del robots.txt |
Qué permitirán estas abilities
Una vez implementadas, estas capacidades permiten:
- Monitorización automatizada: un agente de IA puede consultar las estadísticas de tu sitio cada día y avisarte si detecta actividad inusual o crawlers sospechosos.
- Bloqueo reactivo: herramientas de automatización pueden detectar crawlers que ignoran tu robots.txt repetidamente y bloquearlos sin intervención humana.
- Paneles centralizados: si gestionas varios sitios con VigIA, puedes agregar los datos de todos en un único dashboard externo.
- Integración con asistentes: puedes preguntarle a Claude o ChatGPT «¿qué crawlers de IA han visitado mi web esta semana?» y obtener datos reales de tu sitio.
- Flujos de automatización: n8n, Make u otras herramientas pueden ejecutar acciones basándose en las estadísticas sin tocar el admin de WordPress.
Todo esto con una sola implementación, sin crear endpoints REST personalizados, sin documentar APIs propietarias, sin preocuparte de la validación de datos. La Abilities API se encarga de todo.
Requisitos previos
- WordPress 6.9 o superior: la Abilities API viene incluida en el núcleo desde esta versión.
- PHP 7.4 o superior: compatible con las versiones que soporta WordPress actualmente.
- Un plugin o tema existente: aunque puedes crear uno desde cero, lo ideal es aprovechar funcionalidades ya desarrolladas.
Qué tiene el plugin de ejemplo
VigIA ya tiene toda la lógica implementada en varias clases, lo que facilita la tarea:
VigIA_Database: consultas de estadísticas (get_stats(),get_visits_by_crawler(),get_top_pages()).VigIA_Blocker: gestión de bloqueos (add_useragent_block(),remove_block(),get_all_blocks()).VigIA_Robots_Manager: reglas de robots.txt (add_disallow(),remove_disallow(),get_ai_rules()).
Estas clases son candidatas ideales para exponer como abilities porque:
- Tienen
inputsyoutputsbien definidas. - Son operaciones que un agente de IA o herramienta de automatización querría ejecutar.
- Requieren permisos específicos (solo administradores).
- Ya están probadas y funcionando en producción.
Paso 1: Crear la estructura de archivos
Lo primero es organizar el código. Para ello hay que crear un archivo específico para las abilities, dentro de la carpeta includes del plugin en mi caso, y quizás también el tuyo:
vigia/ ├── assets/ │ ├── css/ │ └── js/ ├── includes/ │ ├── class-database.php │ ├── class-blocker.php │ ├── class-robots-manager.php │ └── class-abilities.php // NUEVO ├── vigia.php └── readme.txt
Luego hacemos require de este nuevo archivo en el archivo principal del plugin, que en VigIA va dentro del método load_dependencies():
private function load_dependencies() {
require_once VIGIA_PLUGIN_DIR . 'includes/class-database.php';
require_once VIGIA_PLUGIN_DIR . 'includes/class-blocker.php';
require_once VIGIA_PLUGIN_DIR . 'includes/class-robots-manager.php';
// ... otras dependencias ...
require_once VIGIA_PLUGIN_DIR . 'includes/class-abilities.php';
}
Y a continuación tenemos que registrar el gancho en init_hooks(), con la comprobación de compatibilidad:
// Abilities API (WordPress 6.9+).
if ( function_exists( 'wp_register_ability' ) ) {
add_action( 'wp_abilities_api_init', array( 'VigIA_Abilities', 'register' ) );
}
La comprobación
function_exists( 'wp_register_ability' )es fundamental para mantener compatibilidad con versiones anteriores de WordPress. Si la función no existe simplemente no registramos el gancho y el plugin sigue funcionando normalmente.
Paso 2: Crear la clase de abilities
VigIA usa una clase específica para organizar todas las abilities. Empezamos con la estructura básica:
<?php
/**
* Abilities API integration
*
* Registers VigIA abilities for WordPress 6.9+ Abilities API.
* Enables AI agents and automation tools to discover and use VigIA features.
*
* @package VigIA
* @since 1.4.0
*/
// Prevent direct access.
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Abilities class
*/
class VigIA_Abilities {
/**
* Ability namespace
*/
const NAMESPACE = 'vigia';
/**
* Category slug
*/
const CATEGORY = 'ai-crawler-analytics';
/**
* Register all VigIA abilities
*
* Called on wp_abilities_api_init hook.
*/
public static function register() {
// Verify API is available.
if ( ! function_exists( 'wp_register_ability' ) ) {
return;
}
// Register category first.
self::register_category();
// Register abilities.
self::register_get_crawler_stats();
self::register_get_top_crawlers();
// ... más abilities ...
}
/**
* Permission callback for all abilities
*
* @return bool
*/
public static function check_permission() {
return current_user_can( 'manage_options' );
}
}
Paso 3: Registrar una categoría personalizada
Las abilities deben pertenecer a una categoría. WordPress incluye algunas predefinidas como data-retrieval o site-management, pero para VigIA creé una categoría propia que describe mejor su propósito, y además es que me gustan estas pijadas de ponerle nombre a las cosas:
/**
* Register ability category
*/
private static function register_category() {
if ( ! function_exists( 'wp_register_ability_category' ) ) {
return;
}
add_action(
'wp_abilities_api_categories_init',
function () {
wp_register_ability_category(
self::CATEGORY,
array(
'label' => __( 'AI Crawler Analytics', 'vigia' ),
'description' => __( 'Monitor, analyze and control AI crawler activity on your WordPress site.', 'vigia' ),
)
);
}
);
}
Reglas para los slugs de categorías:
- Solo letras minúsculas, números y guiones.
- Sin guiones bajos ni mayúsculas.
- Descriptivo pero conciso.
Paso 4: Registrar la primera ability (solo lectura)
Empecemos con algo sencillo, la ability que devuelve las estadísticas generales de rastreadores. Es de solo lectura, no modifica nada:
/**
* Register get-crawler-stats ability
*
* Returns general statistics about AI crawler activity.
*/
private static function register_get_crawler_stats() {
wp_register_ability(
self::NAMESPACE . '/get-crawler-stats',
array(
'label' => __( 'Get Crawler Statistics', 'vigia' ),
'description' => __( 'Returns statistics about AI crawler visits including total visits, unique crawlers, and unique pages crawled.', 'vigia' ),
'category' => self::CATEGORY,
'permission_callback' => array( __CLASS__, 'check_permission' ),
'input_schema' => array(
'type' => 'object',
'properties' => array(
'days' => array(
'type' => 'integer',
'description' => __( 'Number of days to analyze. Default 30.', 'vigia' ),
'default' => 30,
'minimum' => 1,
'maximum' => 365,
),
),
),
'output_schema' => array(
'type' => 'object',
'properties' => array(
'total_visits' => array(
'type' => 'integer',
'description' => __( 'Total number of crawler visits.', 'vigia' ),
),
'unique_crawlers' => array(
'type' => 'integer',
'description' => __( 'Number of unique crawlers detected.', 'vigia' ),
),
'unique_pages' => array(
'type' => 'integer',
'description' => __( 'Number of unique pages crawled.', 'vigia' ),
),
'period_start' => array(
'type' => 'string',
'description' => __( 'Start date of the analysis period.', 'vigia' ),
),
'period_end' => array(
'type' => 'string',
'description' => __( 'End date of the analysis period.', 'vigia' ),
),
),
),
'callback' => array( __CLASS__, 'execute_get_crawler_stats' ),
)
);
}
Desglose de cada parte:
- Identificador (
vigia/get-crawler-stats): sigue el formatonamespace/ability-name. El namespace suele ser el slug del plugin. Usa solo minúsculas, números, guiones y una barra. labelydescription: el label es el nombre corto. La description es más detallada y ayuda a las IAs a entender exactamente qué hace la ability.input_schema: define qué parámetros acepta. El formato sigue la especificación JSON Schema. Aquí aceptamos un parámetro opcionaldayscon valor por defecto de 30.output_schema: define qué devuelve la ability. WordPress valida automáticamente que tu función devuelva datos que coincidan con este esquema.callback: la función que se ejecutará cuando se invoque la ability.permission_callback: determina quién puede ejecutar esta ability.
Paso 5: Crear la función de callback
Ahora la función que hace el trabajo real. Fíjate que reutilizamos los métodos existentes del plugin:
/**
* Execute get-crawler-stats ability
*
* @param array $input Input parameters.
* @return array Statistics data.
*/
public static function execute_get_crawler_stats( $input = array() ) {
$days = isset( $input['days'] ) ? absint( $input['days'] ) : 30;
$end_date = gmdate( 'Y-m-d' );
$start_date = gmdate( 'Y-m-d', strtotime( "-{$days} days" ) );
// Reutilizamos el método existente de la clase Database
$stats = VigIA_Database::get_stats( $start_date, $end_date );
return array(
'total_visits' => $stats['total_visits'],
'unique_crawlers' => $stats['unique_crawlers'],
'unique_pages' => $stats['unique_pages'],
'period_start' => $start_date,
'period_end' => $end_date,
);
}
Puntos clave:
- La función recibe un array
$inputcon los parámetros de entrada. - Debe devolver datos que coincidan con el
output_schemadefinido. - Reutilizamos las funciones existentes del plugin (
VigIA_Database::get_stats()). - No duplicamos lógica: la ability es solo una «fachada» estandarizada sobre código que ya existe.
Paso 6: Registrar abilities con acciones
Ahora viene lo interesante, una ability que modifica datos. Vamos a crear la de bloquear un rastreador:
/**
* Register block-crawler ability
*
* Blocks a crawler by User-Agent pattern.
*/
private static function register_block_crawler() {
wp_register_ability(
self::NAMESPACE . '/block-crawler',
array(
'label' => __( 'Block Crawler', 'vigia' ),
'description' => __( 'Blocks an AI crawler by its User-Agent pattern. Blocked crawlers receive a 403 response.', 'vigia' ),
'category' => self::CATEGORY,
'permission_callback' => array( __CLASS__, 'check_permission' ),
'input_schema' => array(
'type' => 'object',
'required' => array( 'pattern' ),
'properties' => array(
'name' => array(
'type' => 'string',
'description' => __( 'Display name for the blocked crawler.', 'vigia' ),
),
'pattern' => array(
'type' => 'string',
'description' => __( 'User-Agent pattern to block. Case-insensitive partial match.', 'vigia' ),
),
),
),
'output_schema' => array(
'type' => 'object',
'properties' => array(
'success' => array(
'type' => 'boolean',
'description' => __( 'Whether the block was added successfully.', 'vigia' ),
),
'message' => array(
'type' => 'string',
'description' => __( 'Result message.', 'vigia' ),
),
),
),
'callback' => array( __CLASS__, 'execute_block_crawler' ),
)
);
}
Novedades en el input_schema:
required: array con los nombres de los parámetros obligatorios. Si alguien intenta ejecutar la ability sin el parámetropatternWordPress rechaza la petición automáticamente.
La función de callback:
/**
* Execute block-crawler ability
*
* @param array $input Input parameters.
* @return array Result.
*/
public static function execute_block_crawler( $input = array() ) {
if ( empty( $input['pattern'] ) ) {
return array(
'success' => false,
'message' => __( 'User-Agent pattern is required.', 'vigia' ),
);
}
$pattern = sanitize_text_field( $input['pattern'] );
$name = ! empty( $input['name'] ) ? sanitize_text_field( $input['name'] ) : $pattern;
// Check if already blocked.
if ( VigIA_Blocker::is_useragent_blocked( $pattern ) ) {
return array(
'success' => false,
'message' => __( 'This User-Agent pattern is already blocked.', 'vigia' ),
);
}
$result = VigIA_Blocker::add_useragent_block( $name, $pattern );
if ( $result ) {
return array(
'success' => true,
'message' => sprintf(
/* translators: %s: crawler name */
__( 'Crawler "%s" has been blocked.', 'vigia' ),
$name
),
);
}
return array(
'success' => false,
'message' => __( 'Failed to block crawler.', 'vigia' ),
);
}
Paso 7: Ability con valores enumerados
Para la gestión de robots.txt se usa enum para limitar los valores permitidos:
/**
* Register remove-robots-rule ability
*
* Removes a crawler rule from robots.txt.
*/
private static function register_remove_robots_rule() {
wp_register_ability(
self::NAMESPACE . '/remove-robots-rule',
array(
'label' => __( 'Remove Robots.txt Rule', 'vigia' ),
'description' => __( 'Removes a Disallow or Allow rule for a crawler from robots.txt.', 'vigia' ),
'category' => self::CATEGORY,
'permission_callback' => array( __CLASS__, 'check_permission' ),
'input_schema' => array(
'type' => 'object',
'required' => array( 'crawler' ),
'properties' => array(
'crawler' => array(
'type' => 'string',
'description' => __( 'Crawler User-Agent name to remove from rules.', 'vigia' ),
),
'type' => array(
'type' => 'string',
'description' => __( 'Rule type to remove: "disallow" or "allow". Default "disallow".', 'vigia' ),
'default' => 'disallow',
'enum' => array( 'disallow', 'allow' ),
),
),
),
'output_schema' => array(
'type' => 'object',
'properties' => array(
'success' => array(
'type' => 'boolean',
'description' => __( 'Whether the rule was removed successfully.', 'vigia' ),
),
'message' => array(
'type' => 'string',
'description' => __( 'Result message.', 'vigia' ),
),
),
),
'callback' => array( __CLASS__, 'execute_remove_robots_rule' ),
)
);
}
enum limita los valores permitidos a una lista específica. Si alguien intenta usar un valor no permitido, por ejemplo "block" en lugar de "disallow" o "allow", WordPress rechaza la petición automáticamente con un error de validación.
Paso 8: Ability con salida compleja
La ability para obtener los bots rastreadores más activos devuelve un array de objetos:
/**
* Register get-top-crawlers ability
*/
private static function register_get_top_crawlers() {
wp_register_ability(
self::NAMESPACE . '/get-top-crawlers',
array(
'label' => __( 'Get Top Crawlers', 'vigia' ),
'description' => __( 'Returns a list of the most active AI crawlers sorted by visit count.', 'vigia' ),
'category' => self::CATEGORY,
'permission_callback' => array( __CLASS__, 'check_permission' ),
'input_schema' => array(
'type' => 'object',
'properties' => array(
'days' => array(
'type' => 'integer',
'description' => __( 'Number of days to analyze. Default 30.', 'vigia' ),
'default' => 30,
'minimum' => 1,
'maximum' => 365,
),
'limit' => array(
'type' => 'integer',
'description' => __( 'Maximum number of crawlers to return. Default 10.', 'vigia' ),
'default' => 10,
'minimum' => 1,
'maximum' => 100,
),
),
),
'output_schema' => array(
'type' => 'object',
'properties' => array(
'crawlers' => array(
'type' => 'array',
'description' => __( 'List of crawlers with visit counts.', 'vigia' ),
'items' => array(
'type' => 'object',
'properties' => array(
'name' => array(
'type' => 'string',
'description' => __( 'Crawler name.', 'vigia' ),
),
'category' => array(
'type' => 'string',
'description' => __( 'Crawler category.', 'vigia' ),
),
'visit_count' => array(
'type' => 'integer',
'description' => __( 'Number of visits.', 'vigia' ),
),
),
),
),
'total' => array(
'type' => 'integer',
'description' => __( 'Total number of unique crawlers in period.', 'vigia' ),
),
),
),
'callback' => array( __CLASS__, 'execute_get_top_crawlers' ),
)
);
}
El output_schema define que crawlers es un array donde cada elemento (items) es un objeto con propiedades específicas. Esto permite a las herramientas externas saber exactamente qué estructura de datos van a recibir.
Paso 9: Probar las abilities
Como esto no es un acto de fe, aquí te dejo varias técnicas con las que podemos probar las abilities creadas y su funcionamiento.
Desde PHP
Puedes ejecutar abilities desde cualquier parte de tu código PHP:
// Obtener una ability
$ability = wp_get_ability( 'vigia/get-crawler-stats' );
if ( $ability ) {
// Ejecutar con los parámetros por defecto (30 días)
$result = $ability->execute();
echo 'Total de visitas: ' . $result['total_visits'];
echo 'Rastreadores únicos: ' . $result['unique_crawlers'];
}
Para abilities con parámetros:
$ability = wp_get_ability( 'vigia/get-top-crawlers' );
if ( $ability ) {
$result = $ability->execute( array(
'days' => 7,
'limit' => 5,
) );
foreach ( $result['crawlers'] as $crawler ) {
echo $crawler['name'] . ': ' . $crawler['visit_count'] . ' visits';
}
}
Desde la REST API
Las abilities están disponibles automáticamente en:
# Listar todas las abilities
GET /wp-json/wp-abilities/v1/
# Listar abilities de la categoría AI Crawler Analytics
GET /wp-json/wp-abilities/v1/?category=ai-crawler-analytics
# Obtener información de una ability específica
GET /wp-json/wp-abilities/v1/vigia/get-crawler-stats
# Ejecutar una ability de solo lectura
GET /wp-json/wp-abilities/v1/vigia/get-crawler-stats/run?days=7
# Ejecutar una ability de escritura (POST)
POST /wp-json/wp-abilities/v1/vigia/block-crawler/run
Content-Type: application/json
{
"input": {
"name": "GPTBot",
"pattern": "GPTBot"
}
}
Ejemplo con cURL
# Obtener estadísticas de los últimos 7 días
curl -u usuario:contraseña_aplicacion \
"https://tusitio.com/wp-json/wp-abilities/v1/vigia/get-crawler-stats/run?days=7"
# Bloquear un crawler
curl -X POST \
-u usuario:contraseña_aplicacion \
-H "Content-Type: application/json" \
-d '{"input":{"name":"GPTBot","pattern":"GPTBot"}}' \
https://tusitio.com/wp-json/wp-abilities/v1/vigia/block-crawler/run
# Añadir regla Disallow en robots.txt
curl -X POST \
-u usuario:contraseña_aplicacion \
-H "Content-Type: application/json" \
-d '{"input":{"crawler":"ClaudeBot"}}' \
https://tusitio.com/wp-json/wp-abilities/v1/vigia/add-robots-disallow/run
Otro ejemplo de cómo implementar abilities en un plugin: List All URLs
Para darte otra perspectiva, veamos cómo el plugin oficial List All URLs implementa su ability. Este ejemplo viene directamente de la documentación oficial de WordPress:
add_action( 'wp_abilities_api_init', 'list_all_urls_register_ability' );
function list_all_urls_register_ability() {
wp_register_ability(
'list-all-urls/generate-url-list',
array(
'label' => __( 'Generate URL List', 'list-all-urls' ),
'description' => __( 'Generates a list of URLs for all published content of specified post types.', 'list-all-urls' ),
'category' => 'data-retrieval',
'input_schema' => array(
'type' => 'object',
'properties' => array(
'post_types' => array(
'type' => 'array',
'items' => array( 'type' => 'string' ),
'description' => __( 'Array of post type slugs to include.', 'list-all-urls' ),
'default' => array( 'post', 'page' ),
),
),
'additionalProperties' => false,
),
'output_schema' => array(
'type' => 'object',
'properties' => array(
'urls' => array(
'type' => 'array',
'items' => array( 'type' => 'string' ),
'description' => __( 'Array of URLs.', 'list-all-urls' ),
),
'count' => array(
'type' => 'integer',
'description' => __( 'Total number of URLs.', 'list-all-urls' ),
),
),
),
'execute_callback' => 'list_all_urls_generate_url_list_ability',
'permission_callback' => function() {
return current_user_can( 'edit_posts' );
},
'meta' => array(
'show_in_rest' => true,
'annotations' => array(
'readonly' => true,
),
),
)
);
}
Diferencias respecto al otro plugin de ejemplo:
- Usa la categoría predefinida
data-retrievalen lugar de crear una propia. - El permiso es
edit_posts(menos restrictivo quemanage_options). - El input acepta un array de tipos de contenido.
Cómo implementar abilities en un tema
Todo lo que hemos visto para plugins funciona exactamente igual en temas. La Abilities API no distingue entre plugins y temas, ambos pueden registrar y exponer abilities de la misma forma.
Estructura recomendada
Crea un archivo específico para las abilities en tu tema, igual que hice con mi plugin:
mi-tema/ ├── inc/ │ ├── customizer.php │ ├── template-functions.php │ └── abilities.php // Abilities del tema ├── functions.php └── style.css
Y en functions.php:
// Load abilities support (WordPress 6.9+)
if ( function_exists( 'wp_register_ability' ) ) {
require get_template_directory() . '/inc/abilities.php';
}
Ejemplo práctico: ability para obtener la configuración del tema
<?php
/**
* Abilities API functions for theme
*
* @package MiTema
*/
// Prevent direct access
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
add_action( 'wp_abilities_api_init', 'mitema_register_abilities' );
function mitema_register_abilities() {
// Ability to get theme customizer settings
wp_register_ability(
'mi-tema/get-theme-settings',
array(
'label' => __( 'Get Theme Settings', 'mi-tema' ),
'description' => __( 'Returns the current theme customizer settings including colors, layout, and typography options.', 'mi-tema' ),
'category' => 'site',
'input_schema' => array(
'type' => 'object',
'properties' => array(),
),
'output_schema' => array(
'type' => 'object',
'properties' => array(
'colors' => array(
'type' => 'object',
'properties' => array(
'primary' => array( 'type' => 'string' ),
'secondary' => array( 'type' => 'string' ),
'background' => array( 'type' => 'string' ),
),
),
'layout' => array( 'type' => 'string' ),
'sidebar_position' => array( 'type' => 'string' ),
),
),
'execute_callback' => function() {
return array(
'colors' => array(
'primary' => get_theme_mod( 'primary_color', '#0073aa' ),
'secondary' => get_theme_mod( 'secondary_color', '#23282d' ),
'background' => get_theme_mod( 'background_color', '#ffffff' ),
),
'layout' => get_theme_mod( 'layout_style', 'wide' ),
'sidebar_position' => get_theme_mod( 'sidebar_position', 'right' ),
);
},
'permission_callback' => '__return_true', // Public, read-only
'meta' => array(
'show_in_rest' => true,
'annotations' => array( 'readonly' => true ),
),
)
);
}
Ideas de abilities para temas
get-theme-settings: devolver la configuración actual del personalizador.get-menu-structure: obtener la estructura de menús del tema.get-widget-areas: listar las áreas de widgets disponibles.switch-color-scheme: cambiar entre esquemas de colores predefinidos.get-template-parts: listar las partes de plantilla disponibles.
Integración con agentes de IA: ejemplos prácticos
Una vez que tus abilities están registradas cualquier agente de IA compatible con MCP (Model Context Protocol) puede descubrirlas y ejecutarlas. Veamos ejemplos concretos de cómo funcionaría esto con las abilities de VigIA.
Ejemplo 1: Diagnóstico de actividad sospechosa
Imagina esta conversación con un agente de IA conectado a tu WordPress:
Tú: He notado que la web va más lenta de lo normal. ¿Puedes revisar si hay algo raro con los crawlers de IA?
Agente: Voy a consultar las estadísticas de crawlers de la última semana.
[El agente ejecuta
vigia/get-crawler-statscon days: 7]Agente: He detectado actividad inusual. En los últimos 7 días:
- 12.847 visitas de crawlers de IA
- 23 crawlers únicos detectados
- 1.234 páginas rastreadas
Voy a ver cuáles son los más activos.
[El agente ejecuta
vigia/get-top-crawlerscon days: 7, limit: 5]Agente: Aquí está el problema. GPTBot ha hecho 8.432 peticiones en una semana, lo cual es excesivo para un sitio de tu tamaño. Los siguientes son ClaudeBot (2.156) y PerplexityBot (1.847), que están dentro de lo normal.
¿Quieres que bloquee GPTBot o prefieres que lo añada al robots.txt primero para darle una oportunidad de respetar las reglas?
Tú: Añádelo al robots.txt primero, y si sigue igual en unos días lo bloqueamos.
[El agente ejecuta
vigia/add-robots-disallowcon crawler: «GPTBot»]Agente: Hecho. He añadido una regla Disallow para GPTBot en tu robots.txt. Los crawlers que respetan las reglas dejarán de rastrear tu sitio. Te aviso en una semana si sigue habiendo actividad excesiva.
Ejemplo 2: Monitorización proactiva
El agente puede revisar el estado periódicamente sin que se lo pidas:
Agente: He detectado algo que deberías saber. Al revisar tu sitio he visto que hay un crawler nuevo que no había aparecido antes: ByteSpider de ByteDance.
[El agente había ejecutado
vigia/get-top-crawlerscomo parte de su monitorización rutinaria]Agente: En las últimas 24 horas ha hecho 3.847 peticiones, lo cual es bastante agresivo para un primer día. Además, he comprobado tus reglas de robots.txt y no tienes ninguna configurada para este crawler.
[El agente ejecuta
vigia/get-robots-rules]Agente: Actualmente tienes reglas Disallow para GPTBot y ClaudeBot, pero ninguna para ByteSpider. ¿Qué quieres hacer?
- Añadir Disallow en robots.txt (le pide que no rastree)
- Bloquearlo directamente con PHP (devuelve 403)
- Dejarlo por ahora y seguir monitorizando
Tú: Bloquéalo directamente, no me fío de que respete el robots.txt.
[El agente ejecuta
vigia/block-crawlercon pattern: «Bytespider»]Agente: ByteSpider está bloqueado. A partir de ahora recibirá un error 403 Forbidden cuando intente acceder a tu sitio.
Ejemplo 3: Consulta rápida de estado
Tú: ¿Qué crawlers tengo bloqueados ahora mismo?
[El agente ejecuta
vigia/get-blocked-items]Agente: Tienes 3 bloqueos activos:
Por User-Agent:
- GPTBot (bloqueado el 15 de enero)
- ByteSpider (bloqueado el 22 de enero)
Por IP:
- 185.191.171.x – etiquetado como «Crawler agresivo desconocido» (bloqueado el 18 de enero)
Tú: Desbloquea GPTBot, quiero ver si ahora respeta el robots.txt.
[El agente ejecuta
vigia/unblock-crawlercon block_id del GPTBot]Agente: GPTBot desbloqueado. Seguirá viendo la regla Disallow en tu robots.txt. Te aviso si empieza a ignorarla.
Ejemplo 4: Informe semanal automatizado
Tú: Quiero que cada lunes me prepares un resumen de la actividad de crawlers de la semana anterior.
Agente: Entendido. Cada lunes a las 9:00 consultaré las estadísticas y te enviaré un resumen.
[Cada lunes, el agente ejecuta automáticamente:]
vigia/get-crawler-statscon days: 7vigia/get-top-crawlerscon days: 7, limit: 10vigia/get-top-pagescon days: 7, limit: 10Agente: Aquí tienes el resumen semanal de actividad de crawlers de IA:
Resumen general:
- Total de visitas: 4.523
- Crawlers únicos: 18
- Páginas rastreadas: 847
Top 5 crawlers:
- ClaudeBot: 1.847 visitas (AI Search)
- PerplexityBot: 1.234 visitas (AI Search)
- GPTBot: 567 visitas (AI Training) – respetando robots.txt
- Googlebot-Extended: 432 visitas (AI Training)
- Amazonbot: 234 visitas (AI Assistant)
Páginas más rastreadas:
- /blog/ – 234 visitas de 12 crawlers
- /productos/ – 189 visitas de 8 crawlers
- /sobre-nosotros/ – 156 visitas de 15 crawlers
Todo parece normal esta semana. GPTBot ha reducido su actividad significativamente desde que añadimos la regla Disallow.
Ejemplo 5: Integración con n8n o Make
En una herramienta de automatización como n8n podrías crear este flujo:
- Trigger: Cada día a las 8:00.
- HTTP Request: GET a
/wp-json/wp-abilities/v1/vigia/get-crawler-stats/run?days=1 - IF: Si
total_visits > 1000 - HTTP Request: GET a
/wp-json/wp-abilities/v1/vigia/get-top-crawlers/run?days=1&limit=3 - Slack/Email: Enviar alerta con los crawlers más activos.
- IF: Si algún crawler tiene más de 500 visitas en un día.
- HTTP Request: POST a
/wp-json/wp-abilities/v1/vigia/add-robots-disallow/runcon el crawler.
Todo esto funciona porque las abilities tienen esquemas bien definidos que las herramientas pueden interpretar automáticamente. No necesitas crear documentación especial ni adaptar nada.
Ejemplo 6: Panel de control multisitio
Si gestionas varios sitios WordPress con VigIA instalado un panel externo podría mostrar algo así:
┌───────────────────────────────────────────────────────────────────────┐ │ Panel de crawlers IA - Mis sitios │ ├───────────────────────────────────────────────────────────────────────┤ │ │ │ tienda.com blog.com docs.empresa.com │ │ ──────────── ──────── ────────────── │ │ Visitas: 4.523 Visitas: 1.234 Visitas: 8.901 │ │ Crawlers: 18 Crawlers: 12 Crawlers: 24 │ │ Bloqueados: 2 Bloqueados: 0 Bloqueados: 5 │ │ │ │ [Ver detalle] [Ver detalle] [Ver detalle] │ │ │ ├───────────────────────────────────────────────────────────────────────┤ │ Acciones rápidas: │ │ [Bloquear GPTBot en todos] [Añadir Disallow global] [Informe] │ └───────────────────────────────────────────────────────────────────────┘
Cada panel simplemente llama a las abilities correspondientes vía REST API en cada sitio. Sin código personalizado, sin endpoints propios, sin tener que mantener integraciones diferentes para cada instalación.
Buenas prácticas
- Reutiliza código que ya tengas: No dupliques lógica. Las funciones callback de tus abilities deberían llamar a las funciones que ya tienes en tu plugin. La ability es solo una «fachada» estandarizada.
- Descripciones claras y detalladas: Las descripciones son fundamentales para que las IAs entiendan qué hace cada ability. Sé específico sobre qué hace, qué necesita y qué devuelve. Incluye advertencias si la operación es destructiva.
- Esquemas completos: Define esquemas de entrada y salida lo más completos posible. Usa
enumpara limitar valores,minimum/maximumpara rangos,defaultpara valores predeterminados. - Permisos apropiados: No uses
__return_truecomo permission_callback a menos que la ability sea realmente pública. Piensa qué rol debería poder ejecutar cada operación. - Retro-compatibilidad: Siempre comprueba si la función
wp_register_ability()existe antes de cargar tu código de abilities. Así tu plugin o tema seguirá funcionando en versiones anteriores de WordPress. - Agrupa abilities que estén relacionadas: Usa categorías para organizar tus abilities. Si tu plugin tiene muchas funcionalidades, considera crear tu propia categoría.
- Nomenclatura consistente: Usa el slug del plugin o de tu marca como
namespace:mi-plugin/accion. Que los nombres sean descriptivos y en minúsculas con guiones.
Recursos para seguir aprendiendo
- Qué es la Abilities API y por qué tiene una importancia vital para WordPress – Mi artículo explicando los fundamentos.
- Documentación oficial de la Abilities API – Referencia completa en WordPress.org.
- PHP API Reference – Documentación técnica en GitHub.
- List All URLs – El plugin de ejemplo oficial (rama
abilities).
¿Te ha parecido interesante?
Implementar la Abilities API en tu plugin o tema no es muy complicado, hasta yo he podido hacerlo añadiendo abilities al plugin VigIA, así que para ti será muy fácil. Básicamente lo que estás haciendo es describir de una forma estructurada lo que tu código ya hace.
Lo que consigues es que esa descripción permite que otras herramientas, desde scripts de automatización hasta agentes de IA, descubran y utilicen las funcionalidades sin necesidad de documentación adicional o integraciones personalizadas.
El esfuerzo merece la pena, y cuantos más plugins y temas adopten esta API más potente se vuelve el ecosistema WordPress para la automatización y la inteligencia artificial, y lo mejor es que puedes empezar poco a poco eligiendo una o dos funciones clave, conviértelas en abilities y seguir ampliando desde ahí.
¿Dudas?, ¿te has animado?, ¿te ha funcionado? Cuéntamelo en los comentarios.
¿Te gustó este artículo? ¡Ni te imaginas lo que te estás perdiendo en YouTube!






