Abilities get single public flag for REST API, MCP, and AI agents
v7.1
WordPress 7.1 introduces a new public metadata flag for abilities. The flag provides a single, high-level way to indicate that an ability is intended to be available to external clients such as the REST API, MCP adapters, and AI agents.
Table of contents:
- Registering a public ability
- How exposure defaults are resolved
- What problem does this change fix?
- Using public in other integrations
- Exposure is not authorisation
- Changes to resolved metadata
- Backward compatibility
- When to use each flag
Previously, ability authors had to express that intent separately for every exposure channel. For example, an ability exposed through the REST API needed to set show_in_rest directly:
'meta' => array(
'show_in_rest' => true,
),
As the Abilities API gains more client integrations, repeating the same intent through several channel-specific flags becomes difficult to maintain. The new public flag establishes a common default while preserving granular control for each channel.
Registering a public ability
An ability intended for client exposure can now set meta.public when it is registered:
function my_plugin_register_abilities(): void {
wp_register_ability(
'my-plugin/export-users',
array(
'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' => array(
'public' => true,
),
)
);
}
add_action( 'wp_abilities_api_init', 'my_plugin_register_abilities' );
For the REST API, setting public to true makes show_in_rest default to true. The ability can therefore be discovered and invoked through the REST abilities endpoints, subject to its permission callback.
How exposure defaults are resolved
Channel-specific settings take precedence over the general public setting. The effective REST exposure value is resolved as follows:
$show_in_rest = $meta['show_in_rest'] ?? $meta['public'] ?? false;
For example, an ability that is generally public but must not be exposed through REST can use:
'meta' => array(
'public' => true,
'show_in_rest' => false,
),
The explicit show_in_rest value wins over public.
Conversely, an ability that is not generally public can still opt into REST specifically:
'meta' => array(
'public' => false,
'show_in_rest' => true,
),
The resolution uses null-coalescing semantics so an explicit false is preserved. It is not treated as a missing value.
A null value is treated as unset and falls back to the next value in the chain.
In practical terms:
Registration metadata
Effective public
Effective show_in_rest
No exposure metadata
false
false
public => true
true
true
public => false
false
false
show_in_rest => true
false
true
public => true, show_in_rest => false
true
false
public => false, show_in_rest => true
false
true
This precedence allows for a broad exposure default while opting in or out of individual channels.
What problem does this change fix?
Ability metadata already contained channel-specific exposure settings such as show_in_rest. As support for MCP, AI agents, and other clients develops, requiring ability authors to configure each channel independently would duplicate the same policy across multiple properties:
'meta' => array(
'show_in_rest' => true,
'mcp' => array(
'public' => true,
),
// Additional flags for future clients.
),
This also makes it difficult for a newly introduced channel to determine whether an existing ability was intended for external use.
The public flag fixes this by recording the ability author’s general exposure intent in one stable location:
'meta' => array(
'public' => true,
),
Individual integrations can use that value as their default while retaining a more specific channel-level override.
REST is the first built-in consumer of this behaviour. Other integrations can adopt the same default without adding channel-specific logic to WordPress Core.
Using public in other integrations
The resolved public value remains available in the ability’s metadata. Client integrations can inspect this value when determining whether an ability should be exposed.
The WordPress MCP Adapter will respect the unified public flag starting with its next release. WP-CLI does not apply this exposure check because its ability-listing functionality returns all registered abilities.
Other integrations should generally resolve exposure when abilities are selected for that integration:
function my_plugin_is_ability_exposed(
WP_Ability $ability,
string $channel
): bool {
$meta = $ability->get_meta();
return $meta[ $channel ]['public'] ?? $meta['public'] ?? false;
}
Integrations that need to derive their own channel-specific metadata during registration can use the existing <a href="https://developer.wordpress.org/reference/hooks/wp_register_ability_args/">wp_register_ability_args</a> filter:
add_filter(
'wp_register_ability_args',
function ( array $args, string $name ): array {
if (
! isset( $args['meta']['my_client']['public'] )
&& isset( $args['meta']['public'] )
) {
$args['meta']['my_client']['public'] =
(bool) $args['meta']['public'];
}
return $args;
},
10,
2
);
An integration should follow the same precedence rule as REST:
- Use an explicit channel-specific value when present.
- Otherwise, inherit
public. - Otherwise, use the channel’s built-in default.
Integrations should not overwrite an explicit channel opt-out merely because public is true.
Exposure is not authorisation
The public flag controls discoverability and client exposure. It does not make an ability executable without authorisation, and it does not replace the ability’s permission_callback.
Every ability must continue to implement an appropriate permission check:
'permission_callback' => function (): bool {
return current_user_can( 'manage_options' );
},
An ability with public => true may be visible through a client while still requiring authentication and specific WordPress capabilities to execute.
Developers should not treat public, show_in_rest, or any other exposure flag as a security boundary. Authorisation must be enforced by the ability itself.
Changes to resolved metadata
In WordPress 7.1, the resolved metadata for every ability includes a boolean public property. It defaults to false when it is not supplied during registration.
For example:
$ability = wp_get_ability( 'my-plugin/export-users' );
$meta = $ability->get_meta();
$is_public = $meta['public']; // Always a boolean in WordPress 7.1.
This gives consumers a consistent value to inspect without having to test whether the key exists.
The new property is also declared in the REST API’s ability metadata schema, allowing REST clients to inspect the general exposure intent.
Backward compatibility
The change does not alter the signature or return value of <a href="https://developer.wordpress.org/reference/functions/wp_register_ability/">wp_register_ability()</a> or other Abilities API functions.
Existing channel-specific registrations continue to work:
'meta' => array(
'show_in_rest' => true,
),
An explicit show_in_rest value remains authoritative. Plugins are not required to replace it with public.
Abilities that previously supplied neither public nor show_in_rest remain unavailable through REST. Their resolved metadata now contains public => false, but their exposure behaviour is unchanged.
Developers should consider migrating from show_in_rest => true to public => true when an ability is generally intended for use by multiple client types. Continue using show_in_rest directly when exposure is intentionally limited to REST or when overriding the general policy.
Existing Core abilities
The following abilities included with WordPress now use meta.public instead of setting meta.show_in_rest directly:
core/get-site-infocore/get-user-infocore/get-environment-info
Their REST availability has not changed. Because public => true supplies the default for show_in_rest, these abilities remain exposed through REST as before.
Using the high-level flag also allows other client integrations to recognise that these Core abilities are intended for external use.
When to use each flag
Use public when the ability is generally intended for consumption by external clients.
Use a channel-specific flag when:
- The ability should be exposed through only that channel.
- The ability needs to opt out of a channel despite being generally public.
- A client integration provides behaviour that cannot be represented by the general flag.
The change was introduced in changeset [62729], with Core abilities migrated in changeset [62737]. See Trac ticket #65568 for the complete discussion.
Props to @gziolo and @benjamin_zekavica for peer review.
Fetched August 4, 2026


