WordPress Abilities API: Complete Developer Guide

The WordPress Abilities API is no longer a future proposal. It is a Core API, introduced in WordPress 6.9, for registering, discovering, permissioning, and executing structured actions.

An ability gives a function a stable namespaced identity, human-readable documentation, JSON schemas, an execution callback, and an input-aware permission callback. Other plugins, administrative interfaces, REST clients, workflow systems, and AI adapters can inspect the same contract instead of reverse-engineering hooks or hardcoding implementation details.

This guide covers the current Core implementation, including ability categories, registration hooks, schemas, programmatic execution, REST exposure, annotations, MCP integration, security boundaries, and the engineering controls required for enterprise WordPress.

Version baseline: The Abilities API entered WordPress Core in version 6.9. The original feature-plugin repository was archived in February 2026 after the API moved into Core. Plugins that depend on it should declare WordPress 6.9 or newer, or guard their integration explicitly.

What Is the WordPress Abilities API?

The WordPress Abilities API is a registry of executable capabilities. It does not replace your plugin’s business logic. Your plugin still owns the code that analyzes content, exports data, updates an order, or triggers a deployment. The API wraps that logic in a consistent contract that WordPress can discover and execute safely.

Each registered ability includes:

  • A namespaced identifier such as my-plugin/analyze-post-title.
  • A label, description, and registered category.
  • An execution callback containing the actual business logic.
  • A permission callback that receives the same input as execution.
  • JSON Schema definitions for input and output.
  • Optional annotations describing whether the ability is read-only, destructive, or idempotent.
  • An opt-in flag for REST API discovery and execution.

The registry is useful beyond AI. A command palette, WP-CLI command, admin workflow, scheduled process, integration plugin, or remote application can all reuse the same ability contract. For repetitive operational interfaces, compare this with the patterns in my guide to developing custom WP-CLI commands.

Abilities API vs Capabilities, REST, Hooks, and MCP

SystemPrimary responsibility
Capabilities APIDetermines whether a user is authorized to perform an operation.
Hooks APILets components react to events or filter values at known extension points.
REST APIProvides an HTTP transport for reading data and invoking operations.
Abilities APIRegisters discoverable, schema-defined operations and executes them through one contract.
MCP AdapterTranslates eligible WordPress abilities into a protocol that AI clients understand.

These systems work together. An ability’s permission callback will often call current_user_can(). An ability can be exposed through the Core REST routes. An MCP adapter can translate selected abilities into tools. Hooks may fire before or after execution for logging and observability.

The Abilities API does not make the REST API obsolete. REST remains the transport. Abilities provide discovery, schemas, permissioning, and execution semantics on top of that transport.

Register a Production-Ready WordPress Ability

The following example registers a read-only ability that analyzes the title of a post. It demonstrates the full lifecycle rather than a shortened registration snippet.

Register the ability category

Categories must exist before abilities can reference them. Register them on wp_abilities_api_categories_init.

<?php
/**
 * Register the content analysis ability category.
 */
function mg_register_content_analysis_category(): void {
	wp_register_ability_category(
		'content-analysis',
		array(
			'label'       => __( 'Content Analysis', 'mg-abilities' ),
			'description' => __( 'Abilities that inspect and evaluate WordPress content.', 'mg-abilities' ),
		)
	);
}
add_action(
	'wp_abilities_api_categories_init',
	'mg_register_content_analysis_category'
);

Register the ability

Abilities must be registered on wp_abilities_api_init. Names must be lowercase and namespaced. The category, callbacks, label, and description are required.

<?php
/**
 * Register content analysis abilities.
 */
function mg_register_content_analysis_abilities(): void {
	wp_register_ability(
		'mg-abilities/analyze-post-title',
		array(
			'label'               => __( 'Analyze Post Title', 'mg-abilities' ),
			'description'         => __( 'Returns basic quality signals for a WordPress post title.', 'mg-abilities' ),
			'category'            => 'content-analysis',
			'execute_callback'    => 'mg_execute_analyze_post_title',
			'permission_callback' => 'mg_can_analyze_post_title',
			'input_schema'        => array(
				'type'                 => 'object',
				'properties'           => array(
					'post_id' => array(
						'type'        => 'integer',
						'minimum'     => 1,
						'description' => __( 'The post ID to analyze.', 'mg-abilities' ),
					),
				),
				'required'             => array( 'post_id' ),
				'additionalProperties' => false,
			),
			'output_schema'       => array(
				'type'                 => 'object',
				'properties'           => array(
					'post_id'         => array( 'type' => 'integer' ),
					'title'           => array( 'type' => 'string' ),
					'character_count' => array( 'type' => 'integer' ),
					'has_title'       => array( 'type' => 'boolean' ),
				),
				'required'             => array(
					'post_id',
					'title',
					'character_count',
					'has_title',
				),
				'additionalProperties' => false,
			),
			'meta'                => array(
				'annotations'  => array(
					'readonly'    => true,
					'destructive' => false,
					'idempotent'  => true,
				),
				'show_in_rest' => true,
			),
		)
	);
}
add_action(
	'wp_abilities_api_init',
	'mg_register_content_analysis_abilities'
);

Implement permission and execution callbacks

<?php
/**
 * Determine whether the current user may analyze the requested post.
 *
 * @param array{post_id:int} $input Validated ability input.
 * @return bool
 */
function mg_can_analyze_post_title( array $input ): bool {
	$post_id = absint( $input['post_id'] );

	return 0 < $post_id && current_user_can( 'edit_post', $post_id );
}

/**
 * Analyze a post title.
 *
 * @param array{post_id:int} $input Validated ability input.
 * @return array{post_id:int,title:string,character_count:int,has_title:bool}|WP_Error
 */
function mg_execute_analyze_post_title( array $input ) {
	$post = get_post( absint( $input['post_id'] ) );

	if ( ! $post instanceof WP_Post ) {
		return new WP_Error(
			'mg_ability_post_not_found',
			__( 'The requested post could not be found.', 'mg-abilities' )
		);
	}

	$title           = trim( wp_strip_all_tags( $post->post_title ) );
	$character_count = function_exists( 'mb_strlen' )
		? mb_strlen( $title )
		: strlen( $title );

	return array(
		'post_id'         => (int) $post->ID,
		'title'           => $title,
		'character_count' => $character_count,
		'has_title'       => '' !== $title,
	);
}

The permission callback is input-aware. Checking only edit_posts would be too broad because a user may be able to edit some posts but not the requested post. Resource-level checks belong inside the permission callback.

Input and output schemas are not decorative documentation. WordPress validates input before the permission callback, validates output after execution, and returns WP_Error when the contract is violated. This makes schema design part of application correctness.

Discover and Execute Abilities in PHP

Use wp_has_ability() for feature detection, wp_get_ability() to retrieve one ability, and wp_get_abilities() to inspect the registry.

<?php
$ability = wp_get_ability( 'mg-abilities/analyze-post-title' );

if ( null === $ability ) {
	return;
}

$result = $ability->execute(
	array(
		'post_id' => 123,
	)
);

if ( is_wp_error( $result ) ) {
	error_log( $result->get_error_message() );
	return;
}

$character_count = $result['character_count'];

WP_Ability::execute() normalizes and validates the input, runs the permission callback, executes the operation, and validates the output. Calling the execution callback directly bypasses those guarantees and should be avoided by consumers.

The execution lifecycle also fires wp_before_execute_ability and wp_after_execute_ability. These are useful for audit trails, tracing, metrics, and debugging, but logs must not capture secrets or sensitive personal data.

REST API Discovery and Execution

Setting meta.show_in_rest to true exposes an ability through the Core namespace wp-abilities/v1. The primary routes are:

  • GET /wp-json/wp-abilities/v1/abilities to list exposed abilities.
  • GET /wp-json/wp-abilities/v1/abilities/{name} to inspect one ability.
  • /wp-json/wp-abilities/v1/abilities/{name}/run to execute it.

The HTTP method for the run endpoint is derived from annotations. Read-only abilities use GET. Destructive and idempotent abilities use DELETE. Other mutating abilities use POST. The run controller validates the method, input schema, and permission callback before execution.

REST exposure is disabled by default. Keep internal abilities internal. Only expose operations that have a clear remote-use case, stable schemas, appropriate authentication, and explicit authorization.

Annotations Are Operational Metadata, Not Security

The built-in annotations communicate behavioural characteristics:

  • readonly: the ability should not modify its environment.
  • destructive: the ability may remove or irreversibly change data.
  • idempotent: repeating the same request should create no additional effect.

These values help REST routing, workflow builders, approval interfaces, and AI tools reason about risk. They do not grant permission and do not enforce that your implementation behaves as claimed. The permission callback and business logic remain authoritative.

How the Abilities API Connects WordPress to MCP and AI

The Abilities API is the WordPress-side source of truth. An adapter can inspect eligible abilities and translate their names, descriptions, schemas, and execution methods into tools understood by an external protocol such as MCP.

This separation matters. Plugins should register accurate abilities without depending on a specific AI vendor or protocol. The adapter handles transport. The AI client handles tool selection. WordPress still validates input and permissions before executing the underlying operation.

Not every ability should become an AI tool. High-risk actions may need to remain internal, require human approval, or be exposed only through a restricted operational profile. A discoverable operation is not automatically a safe autonomous operation.

Enterprise Design and Security Requirements

Design atomic contracts

Prefer one clearly bounded operation over a broad ability such as “manage the website.” Atomic abilities are easier to authorize, test, audit, retry, and compose. A workflow can combine create-draft, attach-media, and submit-for-review without giving one callback unrestricted publishing control.

Keep schemas strict and versionable

Set additionalProperties to false for object inputs unless extension is intentional. Add required fields explicitly. Avoid changing the meaning or type of an existing field after release. Create a new ability or introduce backward-compatible optional fields when the contract must evolve.

Authorize the requested resource

Permission callbacks receive validated input. Use it. Check the specific post, order, user, site, or file being requested. A generic capability check can create privilege escalation when the ability accepts identifiers supplied by a remote client.

Validate business invariants again

JSON Schema validates structure, not every domain rule. A valid integer order ID may still belong to another customer. A valid status string may be an illegal transition. The execution callback must enforce business invariants and return a bounded WP_Error on failure. Review escaping, sanitization, and validation in WordPress for the underlying defensive model.

Keep long-running work outside the request

An ability that generates a large export, performs a migration, or calls several remote services should usually enqueue a background job and return a job identifier. Synchronous ability execution should have predictable time and memory limits.

Add observability and redaction

Record the ability name, authenticated actor, result status, duration, and correlation ID where appropriate. Do not log passwords, tokens, private content, or entire request bodies by default. Enterprise auditability requires useful events, not indiscriminate data collection.

Apply normal engineering controls

Abilities are production code. Cover permission callbacks, schema failures, output validation, and error paths with automated tests. Run WPCS, PHPStan, and CI before release. My guide to enforcing WordPress Coding Standards in team projects provides a practical baseline.

When Not to Register an Ability

Do not register an ability merely because a PHP function exists. Internal helpers, rendering callbacks, low-level database methods, and unstable implementation details should remain private.

A hook is still better when components need to react to an event. A REST controller may be better for a resource-oriented CRUD API. A WP-CLI command may be enough for a private operational task. Register an ability when discoverability, schema-defined execution, reuse, and consistent permissioning provide real value.

When the operation belongs to a product rather than the active theme, package it in a plugin. The custom WordPress plugin development guide covers the maintainable foundation.

Production Implementation Checklist

  1. Require WordPress 6.9 or add an explicit compatibility strategy.
  2. Register categories and abilities on their dedicated Core hooks.
  3. Use stable, namespaced, lowercase ability names.
  4. Write descriptions for consumers that do not know your plugin internals.
  5. Define strict input and output schemas.
  6. Authorize the specific resource referenced by the input.
  7. Set truthful read-only, destructive, and idempotent annotations.
  8. Enable REST exposure only when remote execution is required.
  9. Use WP_Ability::execute() instead of calling callbacks directly.
  10. Test validation, permissions, errors, repeated execution, and audit logging.

Use the official wp_register_ability() reference, WP_Ability class reference, and Abilities REST controller reference as the source of truth as WordPress continues to refine the API.

Need an Ability Architecture for Your WordPress Product?

I help plugin teams and agencies expose operational capabilities through secure, maintainable WordPress APIs. That includes capability design, schema contracts, MCP integration, permission models, observability, and production-safe execution.

Frequently Asked Questions

Which WordPress version introduced the Abilities API?

The Abilities API entered WordPress Core in version 6.9. The earlier feature plugin and Composer package were the adoption path before Core inclusion.

Does the Abilities API replace the Capabilities API?

No. Abilities describe and execute operations. Permission callbacks should still use WordPress capabilities and resource-level authorization to determine whether the current actor may run an ability.

Are all registered abilities available through REST?

No. REST exposure is disabled by default. Set meta.show_in_rest to true only for abilities that should be discovered and executed remotely.

Does WordPress validate ability input and output?

Yes. Calling WP_Ability::execute() normalizes and validates input, checks permission, runs the callback, and validates successful output against the registered schema.

Is the Abilities API only for AI and MCP?

No. AI adapters are an important use case, but abilities can also support administrative interfaces, command palettes, workflow automation, WP-CLI tools, integrations, and plugin interoperability.

Should destructive abilities be exposed to AI agents?

Not automatically. Destructive or high-impact abilities may require restricted profiles, explicit user confirmation, additional approval workflows, or no external exposure at all.

Mehul Gohil
Mehul Gohil

Mehul Gohil is a Full Stack WordPress developer and an active member of the local WordPress community. For the last 13+ years, he has been developing custom WordPress plugins, custom WordPress themes, third-party API integrations, performance optimization, and custom WordPress websites tailored to the client's business needs and goals.

Articles: 164

Leave a Reply

Your email address will not be published. Required fields are marked *