MW
Tutorial

Abilities API Changes in WordPress 7.1

WordPress 7.1 upgrades the Abilities API: a unified public flag, wp_get_abilities() filtering, execution lifecycle hooks, JSON Schema for clients, and typed REST input.

advanced 25 min Aug 20, 2026
You understand the WordPress 7.0 Abilities API at a high level Comfortable with wp_register_ability() and JSON Schema

ABILITIES API CHANGES IN WORDPRESS…

The Abilities API in WordPress 7.0 let plugins describe what AI (and other callers) can do. WordPress 7.1 is the ops release for that API: filtering, a single public flag, execution lifecycle hooks, client-ready JSON Schema, and REST input that arrives already typed.

If you need the 7.0 primer first, start at the Abilities API hub. If you already registered abilities in 7.0, read this before you expose them to MCP or REST clients.

1. The unified public flag

In 7.0 you opted into REST with meta.show_in_rest. Every new client (MCP, WebMCP, a future agent adapter) would have needed its own flag. 7.1 adds meta.public as the general exposure intent.

Resolution for REST:

$show_in_rest = $meta['show_in_rest'] ?? $meta['public'] ?? false;

Channel-specific values win. Explicit false is kept (null-coalescing, not “empty means inherit”).

wp_register_ability( 'my-plugin/export-users', [
  'label'               => __( 'Export users', 'my-plugin' ),
  'description'         => __( 'Exports user data as CSV.', 'my-plugin' ),
  'category'            => 'data-export',
  'execute_callback'    => 'my_plugin_export_users',
  'permission_callback' => function (): bool {
      return current_user_can( 'export' );
  },
  'meta' => [
      'public' => true, // REST + future clients inherit this
  ],
] );

Core abilities core/get-site-info, core/get-user-info, and core/get-environment-info now set meta.public instead of show_in_rest. REST behaviour is unchanged. The WordPress MCP Adapter will honour public in its next release. WP-CLI still lists every ability.

Existing 'show_in_rest' => true keeps working. Migrate to public => true when the ability is meant for multiple client types.

2. Filtering with wp_get_abilities()

Before 7.1, wp_get_abilities() always returned the full registry. Everyone rolled their own array_filter. Now it accepts $args:

$abilities = wp_get_abilities( [
    'category'  => 'data-export',
    'namespace' => 'my-plugin',
    'metadata'  => [ 'public' => true ],
] );

Declarative filters combine with AND. Custom per-item callbacks and a final result callback are also supported. Two global filters wrap the pipeline:

  • wp_get_abilities_item_include — keep or drop one ability
  • wp_get_abilities_result — reshape the final list

The REST abilities list controller now uses wp_get_abilities() instead of filtering after a full fetch. PHP and REST share one pipeline. You can also request the raw registry when you truly need every ability unfiltered.

Filtering does not replace authorisation. A filtered list can still include abilities the current user cannot execute.

3. Execution lifecycle

7.1 adds wp_ability_invoked at the start of WP_Ability::execute() — before input normalization, validation, permission checks, and the wp_pre_execute_ability short-circuit.

WP Action wp_ability_invoked Priority 10 3 args: $ability_name, $input, $ability
audit.php
add_action( 'wp_ability_invoked', function ( $ability_name, $input, $ability ) {
  // Fires even when input is invalid or permission fails.
  // Do not log $input blindly — it may contain secrets.
  do_action( 'my_plugin_record_ability_invocation', [
      'ability'   => $ability_name,
      'timestamp' => time(),
  ] );
}, 10, 3 );

That is the hook for telemetry, tracing, and invocation counts. It runs for invalid input, failed permissions, short-circuits, cached results, and approval-gated calls.

wp_before_execute_ability and wp_after_execute_ability now receive the WP_Ability instance as a final argument. Existing two-arg callbacks still work; bump $accepted_args to receive the object.

Custom validation beyond JSON Schema:

  • wp_ability_validate_input
  • wp_ability_validate_output

Each receives true|WP_Error $is_valid, the value, and the ability name. Return true or a WP_Error. Returning false becomes a generic error. REST-style validate_callback / sanitize_callback schema keywords are not run by the Abilities API — use these filters.

There is also wp_pre_execute_ability to short-circuit execution. See New execution lifecycle filters.

4. Client-ready schemas and core abilities

core/get-site-info, core/get-user-info, and core/get-environment-info now share schema conventions: every output property has a translatable Title Case title and a description. Clients (REST, MCP, AI) can present fields without hard-coding labels.

core/get-user-info adds first_name, last_name, nickname, description, user_url. roles is normalized with array_values() so JSON is always an array. Optional fields input requests a subset; unknown names fail with ability_invalid_input before the callback. core/get-environment-info supports the same fields pattern.

core/get-user-info is now on REST at /wp-json/wp-abilities/v1/abilities. JSON Schema is prepared for client compatibility — see JSON Schema preparation.

5. Typed input on REST run

GET and DELETE used to pass every query string as a string. "10" was not 10; "true" was not true; ids=1,2,3 was one string.

7.1 coerces run-request input to input_schema types after validation accepts the value, as the input argument’s sanitize_callback. Permission and execute callbacks both see native types:

GET /wp-json/wp-abilities/v1/abilities/my-plugin/list-items/run
    ?input[limit]=10&input[featured]=true&input[ids]=1,2,3

becomes limit => 10, featured => true, ids => [1, 2, 3]. Invalid input is not coerced; you still get ability_invalid_input.

 
WordPress 7.0

strings

WordPress 7.1

schema types

limit=10 "10" 10 (int)
featured=true "true" true (bool)
ids=1,2,3 "1,2,3" [1, 2, 3]
Invalid input Callback saw junk ability_invalid_input first

Landmines

public vs permission
Wrong
meta.public => true and skip permission_callback
Right
Always set permission_callback; public only controls discovery
A listed ability is not an executable ability.
Invocation logging
Wrong
error_log( wp_json_encode( $input ) ) on wp_ability_invoked
Right
Log ability name + timestamp; redact or drop raw input
Credentials and PII land in log files.
Schema keywords
Wrong

Rely on validate_callback inside input_schema

Right

Use wp_ability_validate_input / wp_ability_validate_output

REST-style schema callbacks never run.

Next: WordPress 7.1 Editor Changes — the always-iframed canvas and the block APIs around it.

You've completed this tutorial!

Get the next one in your inbox. Practical tips, no fluff.

Subscribe

Get weekly notes in your inbox

Practical tips, tutorials and resources. No spam.