WordPress 7.1 introduces major developer-focused improvements to the Abilities API. Originally introduced in previous releases to formalize programmatic capabilities and context-aware executions, the framework in version 7.1 addresses critical developer feedback. Key updates include custom runtime validation hooks, complete invocation auditing, input type coercion over REST endpoints, and enhanced standardization across core abilities such as core/get-user-info, core/get-site-info, and core/get-environment-info.
Custom Input and Output Validation Hooks
While WP_Ability natively evaluates inputs and outputs against a defined JSON Schema, standard JSON Schema definitions cannot always cover complex runtime constraints or business rules. Additionally, REST-style validate_callback and sanitize_callback keywords in schema definitions are not executed by the Abilities API.
To solve this limitation without altering underlying JSON schemas, WordPress 7.1 introduces two new filter hooks: wp_ability_validate_input and wp_ability_validate_output.
Both filters pass three parameters to callbacks:
$is_valid: Atrueboolean or aWP_Errorobject from prior validation steps.$value: The unvalidated or candidate input/output data.$name: The registered name of the ability being evaluated.
Instead of modifying the schema directly, these filters intercept the validation process. Returning true indicates valid data, while returning a WP_Error object specifies why validation failed. If a callback returns false, WordPress automatically converts it to a generic WP_Error, though returning an explicit WP_Error is recommended for proper error reporting.
Validating Input Data
Below is an implementation example demonstrating how to enforce domain rules on input data that cannot be described through JSON Schema alone:
add_filter( 'wp_ability_validate_input', function ( $is_valid, $input, $ability_name ) {
if ( 'my-plugin/send-message' !== $ability_name ) {
return $is_valid;
}
// Preserve errors produced by the default schema validation.
if ( is_wp_error( $is_valid ) ) {
return $is_valid;
}
if ( ! is_array( $input ) || empty( $input['recipient'] ) || ! str_ends_with( $input['recipient'], '@example.com' ) ) {
return new WP_Error( 'invalid_recipient', __( 'The recipient must use the example.com domain.', 'my-plugin' ) );
}
return true;
}, 10, 3 );
Validating Output Data
Similarly, ability outputs can be verified prior to returning data to the calling client:
add_filter( 'wp_ability_validate_output', function ( $is_valid, $output, $ability_name ) {
if ( 'my-plugin/send-message' !== $ability_name ) {
return $is_valid;
}
if ( is_wp_error( $is_valid ) ) {
return $is_valid;
}
if ( ! is_array( $output ) || empty( $output['message_id'] ) ) {
return new WP_Error( 'invalid_message_output', __( 'The ability did not return a message ID.', 'my-plugin' ) );
}
return true;
}, 10, 3 );
Observing Ability Invocations with wp_ability_invoked
Prior to WordPress 7.1, auditing or tracing execution attempts was difficult because existing hooks executed after permission checks, parameter normalization, or short-circuit logic. WordPress 7.1 adds the wp_ability_invoked action right at the start of WP_Ability::execute():
do_action( 'wp_ability_invoked', $this->name, $input, $this );
Because this action triggers prior to normalization, schema validation, authorization checks, and the wp_pre_execute_ability short-circuit filter, it guarantees visibility into every invocation attempt. This includes calls that fail permission checks, pass invalid inputs, return cached results, require user approval, or proceed to normal execution.
Telemetry and Security Considerations
The wp_ability_invoked hook is ideal for logging, auditing, and real-time usage metrics:
add_action( 'wp_ability_invoked', function ( $ability_name, $input, $ability ) {
// Avoid storing sensitive input without appropriate filtering.
do_action( 'my_plugin_record_ability_invocation', array(
'ability' => $ability_name,
'timestamp' => time(),
) );
}, 10, 3 );
Important Security Note: The $input variable passed to this hook contains raw, unnormalized data. Extending plugins should avoid indiscriminately writing this value to persistent logs, as raw inputs may contain sensitive payloads, credentials, or personally identifiable information (PII).
Additionally, the existing hooks wp_before_execute_ability and wp_after_execute_ability now pass the target WP_Ability instance as an additional final argument. Developers can update their callback signatures and $accepted_args counts to inspect the full ability instance context.
Expanded core/get-user-info and Selective Field Queries
The core ability core/get-user-info has been updated in WordPress 7.1 to return richer user metadata and allow selective output filtering.
New Profile Fields
The ability now exposes five additional profile properties for authenticated users:
first_namelast_namenicknamedescriptionuser_url
Furthermore, the roles output array is now sanitized using array_values() internally, guaranteeing that it is consistently encoded as a sequential JSON array regardless of any custom PHP array key indexing.
An execution with default inputs returns the full object payload:
{
"id": 1,
"display_name": "Jane Doe",
"user_nicename": "jane-doe",
"user_login": "jane",
"roles": [
"administrator"
],
"locale": "en_US",
"first_name": "Jane",
"last_name": "Doe",
"nickname": "Jane",
"description": "Site administrator and contributor.",
"user_url": "https://example.com"
}
Selective Field Responses
To reduce payload overhead when calling abilities programmatically, callers can now specify a subset of fields via an optional fields parameter in the input payload:
$ability = wp_get_ability( 'core/get-user-info' );
$result = $ability->execute( array(
'fields' => array(
'display_name',
'first_name',
'last_name',
),
) );
The output response strictly contains only the requested attributes:
{
"display_name": "Jane Doe",
"first_name": "Jane",
"last_name": "Doe"
}
Available field names are defined via an enum in the ability’s input schema. Requesting an invalid field name triggers an immediate ability_invalid_input error prior to executing the callback logic. Authorization requirements remain unchanged: callers must be authenticated.
Schema Standardizations Across Core Abilities
WordPress 7.1 harmonizes schema definitions across all fundamental core abilities: core/get-site-info, core/get-user-info, and core/get-environment-info.
Key standardization updates include:
- Metadata Quality: Every output property now includes a translatable, Title Case
titleand an explicitdescriptionstring. This allows external tools, REST clients, WebMCP, AI models, and Model Context Protocol (MCP) clients to discover and display capabilities reliably. - Selective Fields in Environment Info: The
core/get-environment-infoability now supports the optionalfieldsinput parameter matching the syntax used bycore/get-user-infoandcore/get-site-info. - REST Exposure for User Info: The
core/get-user-infoability now includes thepublicmeta flag introduced in 7.1, exposing it through the REST endpoint at/wp-json/wp-abilities/v1/abilities.
Developers interacting with these core abilities should rely on registered schemas for feature discovery rather than assuming rigid response structures.
Typed Input Coercion for REST Ability Runs
In previous releases, running an ability over HTTP using GET or DELETE delivered all query string parameters as raw strings. For instance, integers were passed as string scalars ("10"), booleans as "true", and lists as comma-separated strings. This behavior caused strict type comparisons within execution callbacks to fail unless manual casting was implemented inside the ability code.
In WordPress 7.1, ability inputs submitted over REST run requests are automatically coerced to the native PHP data types specified in the ability’s input_schema before reaching execution callbacks.
This coercion is integrated as the sanitize_callback for the input argument, ensuring that permission callbacks and execution callbacks both receive properly typed data structures simultaneously.
Example REST Query Coercion
Consider a request targeting an ability endpoint:
GET /wp-json/wp-abilities/v1/abilities/my-plugin/list-items/run?input[limit]=10&input[featured]=true&input[ids]=1,2,3
Assuming the ability’s input schema defines limit as an integer, featured as a boolean, and ids as an array of integers, the execution callback receives natively coerced data:
array(
'limit' => 10,
'featured' => true,
'ids' => array( 1, 2, 3 ),
)
Coercion only modifies input payloads that pass schema validation via validate_input(). Unrecognized or invalid inputs reach validation unmodified and return standard ability_invalid_input errors.
Frequently asked questions
How do I validate custom rules that JSON Schema does not support in the Abilities API?
You can use the new wp_ability_validate_input and wp_ability_validate_output filters introduced in WordPress 7.1. These hooks receive the existing validation status, the data payload, and the ability name, allowing you to return a WP_Error if custom conditions fail.
Does the wp_ability_invoked action run for failed or unauthorized executions?
Yes. The wp_ability_invoked action fires at the very start of WP_Ability::execute() before input normalization, permission checks, schema validation, or short-circuit filters occur. It fires for every single invocation attempt.
How does WordPress 7.1 handle type casting for REST ability execution calls?
Input parameters sent via GET or DELETE requests over REST are automatically coerced to the native PHP types defined in the ability's input_schema (e.g., converting '10' to integer 10 and 'true' to boolean true).
Primary reference: Review the original announcement for exact release details. This article is an independent explanation and does not reproduce the source text.