Skip to content

Commit 5f14502

Browse files
committed
Use the public meta flag for core abilities.
Adopt the high-level `public` flag introduced in [62729] for the core abilities that ship with WordPress. The `core/get-site-info`, `core/get-user-info`, and `core/get-environment-info` abilities now declare `meta.public` instead of `meta.show_in_rest`. The `public` flag seeds `show_in_rest` through the resolution chain, so REST exposure stays exactly the same. Storing the intent as `public` also lets other channels such as MCP and AI agents read it through the `wp_register_ability_args` filter. An explicit `show_in_rest` still wins when a channel needs to opt out. Also make the `public` flag default to `false` so it is always present in the resolved meta, treat a `null` value as unset, and reword the related documentation across the ability class, the registry, the REST schema, and `wp_register_ability()`. Follow-up to [62729]. Props gziolo, mukesh27, khokansardar. Fixes #65568. git-svn-id: https://develop.svn.wordpress.org/trunk@62737 602fd350-edb4-49c9-b593-d223f7449a82
1 parent 73c48d6 commit 5f14502

8 files changed

Lines changed: 99 additions & 46 deletions

File tree

src/wp-includes/abilities-api.php

Lines changed: 21 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
* - Define permission checks and execution callbacks.
1616
* - Organize abilities into logical categories.
1717
* - Validate inputs and outputs using JSON Schema.
18-
* - Expose abilities through the REST API.
18+
* - Expose abilities to clients such as the REST API.
1919
*
2020
* ## Working with Abilities
2121
*
@@ -48,7 +48,7 @@
4848
* 'required' => true,
4949
* ),
5050
* 'meta' => array(
51-
* 'show_in_rest' => true,
51+
* 'public' => true,
5252
* ),
5353
* )
5454
* );
@@ -118,10 +118,10 @@
118118
* 'execute_callback' => 'my_plugin_analyze_text',
119119
* 'permission_callback' => 'my_plugin_can_analyze_text',
120120
* 'meta' => array(
121-
* 'annotations' => array(
121+
* 'annotations' => array(
122122
* 'readonly' => true,
123123
* ),
124-
* 'show_in_rest' => true,
124+
* 'public' => true,
125125
* ),
126126
* )
127127
* );
@@ -208,16 +208,24 @@
208208
* return current_user_can( 'edit_posts' );
209209
* }
210210
*
211-
* ### REST API Integration
211+
* ### Client Exposure
212212
*
213-
* Abilities can be exposed through the REST API by setting `show_in_rest`
214-
* to `true` in the meta configuration:
213+
* Set the high-level `public` flag to make an ability available to clients
214+
* such as the REST API, MCP, or AI agents:
215215
*
216216
* 'meta' => array(
217-
* 'show_in_rest' => true,
217+
* 'public' => true,
218218
* ),
219219
*
220-
* This allows abilities to be invoked via HTTP requests to the WordPress REST API.
220+
* The `public` flag seeds the default for each per-channel flag. For the REST
221+
* API it seeds `show_in_rest`, which lets the ability be invoked via HTTP
222+
* requests. Set a per-channel flag directly to override that default. For
223+
* example, keep a public ability out of the REST API:
224+
*
225+
* 'meta' => array(
226+
* 'public' => true,
227+
* 'show_in_rest' => false,
228+
* ),
221229
*
222230
* @since 6.9.0
223231
*
@@ -264,10 +272,10 @@
264272
* @type bool|null $idempotent Optional. If true, calling the ability repeatedly with the same arguments
265273
* will have no additional effect on its environment.
266274
* }
267-
* @type bool $public Optional. Whether the ability is intended to be publicly
268-
* available to clients. When true, channel-specific exposure
269-
* flags such as `$show_in_rest` default to true. An explicitly
270-
* set channel flag always takes precedence.
275+
* @type bool $public Optional. Whether the ability is meant to be available to
276+
* clients such as the REST API, MCP, or AI agents. Seeds
277+
* the default for per-channel flags like `$show_in_rest`.
278+
* Defaults to false.
271279
* @type bool $show_in_rest Optional. Whether to expose this ability in the REST API.
272280
* When true, the ability can be invoked via HTTP requests.
273281
* Default is the value of `$public` when set, false otherwise.

src/wp-includes/abilities-api/class-wp-abilities-registry.php

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -71,10 +71,10 @@ final class WP_Abilities_Registry {
7171
* @type bool|null $idempotent Optional. If true, calling the ability repeatedly with the same arguments
7272
* will have no additional effect on its environment.
7373
* }
74-
* @type bool $public Optional. Whether the ability is intended to be publicly
75-
* available to clients. When true, channel-specific exposure
76-
* flags such as `$show_in_rest` default to true. An explicitly
77-
* set channel flag always takes precedence.
74+
* @type bool $public Optional. Whether the ability is meant to be available
75+
* to clients such as the REST API, MCP, or AI agents.
76+
* Seeds the default for per-channel flags like
77+
* `$show_in_rest`. Defaults to false.
7878
* @type bool $show_in_rest Optional. Whether to expose this ability in the REST API.
7979
* Default is the value of `$public` when set, false otherwise.
8080
* }
@@ -125,9 +125,10 @@ public function register( string $name, array $args ): ?WP_Ability {
125125
* Optional. Additional metadata for the ability.
126126
*
127127
* @type array<string, bool|string> $annotations Optional. Annotation metadata for the ability.
128-
* @type bool $public Optional. Whether the ability is intended to be
129-
* publicly available to clients. Seeds the default for
130-
* channel-specific exposure flags like `$show_in_rest`.
128+
* @type bool $public Optional. Whether the ability is meant to be
129+
* available to clients such as the REST API, MCP, or AI
130+
* agents. Seeds the default for per-channel flags like
131+
* `$show_in_rest`. Defaults to false.
131132
* @type bool $show_in_rest Optional. Whether to expose this ability in the REST API.
132133
* Default is the value of `$public` when set, false otherwise.
133134
* }

src/wp-includes/abilities-api/class-wp-ability.php

Lines changed: 25 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,14 @@ class WP_Ability {
2828
*/
2929
protected const DEFAULT_SHOW_IN_REST = false;
3030

31+
/**
32+
* The default value for the `public` meta.
33+
*
34+
* @since 7.1.0
35+
* @var bool
36+
*/
37+
protected const DEFAULT_PUBLIC = false;
38+
3139
/**
3240
* The default ability annotations.
3341
* They are not guaranteed to provide a faithful description of ability behavior.
@@ -160,10 +168,10 @@ class WP_Ability {
160168
* @type bool|null $idempotent Optional. If true, calling the ability repeatedly with the same arguments
161169
* will have no additional effect on its environment.
162170
* }
163-
* @type bool $public Optional. Whether the ability is intended to be publicly
164-
* available to clients. When true, channel-specific exposure
165-
* flags such as `$show_in_rest` default to true. An explicitly
166-
* set channel flag always takes precedence.
171+
* @type bool $public Optional. Whether the ability is meant to be available
172+
* to clients such as the REST API, MCP, or AI agents.
173+
* Seeds the default for per-channel flags like
174+
* `$show_in_rest`. Defaults to false.
167175
* @type bool $show_in_rest Optional. Whether to expose this ability in the REST API.
168176
* Default is the value of `$public` when set, false otherwise.
169177
* }
@@ -229,10 +237,10 @@ public function __construct( string $name, array $args ) {
229237
* @type bool|null $idempotent Optional. If true, calling the ability repeatedly with the same arguments
230238
* will have no additional effect on its environment.
231239
* }
232-
* @type bool $public Optional. Whether the ability is intended to be publicly
233-
* available to clients. When true, channel-specific exposure
234-
* flags such as `$show_in_rest` default to true. An explicitly
235-
* set channel flag always takes precedence.
240+
* @type bool $public Optional. Whether the ability is meant to be available
241+
* to clients such as the REST API, MCP, or AI agents.
242+
* Seeds the default for per-channel flags like
243+
* `$show_in_rest`. Defaults to false.
236244
* @type bool $show_in_rest Optional. Whether to expose this ability in the REST API.
237245
* Default is the value of `$public` when set, false otherwise.
238246
* }
@@ -262,8 +270,9 @@ public function __construct( string $name, array $args ) {
262270
* @type bool|null $idempotent If true, calling the ability repeatedly with the same arguments
263271
* will have no additional effect on its environment.
264272
* }
265-
* @type bool $public Whether the ability is intended to be publicly available
266-
* to clients. Only present when provided during registration.
273+
* @type bool $public Whether the ability is meant to be available to clients
274+
* such as the REST API, MCP, or AI agents. Defaults to
275+
* false.
267276
* @type bool $show_in_rest Whether to expose this ability in the REST API.
268277
* }
269278
* }
@@ -348,17 +357,18 @@ protected function prepare_properties( array $args ): array {
348357
)
349358
);
350359

360+
$args['meta']['annotations'] = wp_parse_args(
361+
$args['meta']['annotations'],
362+
static::$default_annotations
363+
);
364+
351365
/*
352366
* Resolve `show_in_rest` from most specific to least specific: an explicit
353367
* `show_in_rest` value wins, then the high-level `public` flag seeds the
354368
* default, then the built-in default applies.
355369
*/
356370
$args['meta']['show_in_rest'] = $args['meta']['show_in_rest'] ?? $args['meta']['public'] ?? self::DEFAULT_SHOW_IN_REST;
357-
358-
$args['meta']['annotations'] = wp_parse_args(
359-
$args['meta']['annotations'],
360-
static::$default_annotations
361-
);
371+
$args['meta']['public'] = $args['meta']['public'] ?? self::DEFAULT_PUBLIC;
362372

363373
return $args;
364374
}

src/wp-includes/abilities.php

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -132,12 +132,12 @@ function wp_register_core_abilities(): void {
132132
return current_user_can( 'manage_options' );
133133
},
134134
'meta' => array(
135-
'annotations' => array(
135+
'annotations' => array(
136136
'readonly' => true,
137137
'destructive' => false,
138138
'idempotent' => true,
139139
),
140-
'show_in_rest' => true,
140+
'public' => true,
141141
),
142142
)
143143
);
@@ -256,12 +256,12 @@ function wp_register_core_abilities(): void {
256256
return is_user_logged_in();
257257
},
258258
'meta' => array(
259-
'annotations' => array(
259+
'annotations' => array(
260260
'readonly' => true,
261261
'destructive' => false,
262262
'idempotent' => true,
263263
),
264-
'show_in_rest' => true,
264+
'public' => true,
265265
),
266266
)
267267
);
@@ -342,12 +342,12 @@ function wp_register_core_abilities(): void {
342342
return current_user_can( 'manage_options' );
343343
},
344344
'meta' => array(
345-
'annotations' => array(
345+
'annotations' => array(
346346
'readonly' => true,
347347
'destructive' => false,
348348
'idempotent' => true,
349349
),
350-
'show_in_rest' => true,
350+
'public' => true,
351351
),
352352
)
353353
);

src/wp-includes/rest-api/endpoints/class-wp-rest-abilities-v1-list-controller.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -313,7 +313,7 @@ public function get_item_schema(): array {
313313
'additionalProperties' => true,
314314
),
315315
'public' => array(
316-
'description' => __( 'Whether the ability author intends the ability to be publicly available to clients. Only present when set by the ability author.' ),
316+
'description' => __( 'Whether the ability is meant to be available to clients such as the REST API, MCP, or AI agents. Defaults to false, but individual channel settings such as show_in_rest can override it.' ),
317317
'type' => 'boolean',
318318
),
319319
),

tests/phpunit/tests/abilities-api/wpAbility.php

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -312,24 +312,54 @@ public function test_meta_explicit_show_in_rest_false_wins_over_public_true() {
312312
}
313313

314314
/**
315-
* Tests that `public` metadata is not added to the stored meta when not provided.
315+
* Tests that `public` metadata defaults to false when not provided.
316316
*
317317
* @ticket 65568
318318
*/
319-
public function test_meta_public_is_not_defaulted_when_unset() {
319+
public function test_meta_public_defaults_to_false_when_unset() {
320320
$ability = new WP_Ability( self::$test_ability_name, self::$test_ability_properties );
321321

322-
$this->assertArrayNotHasKey(
322+
$this->assertArrayHasKey(
323323
'public',
324324
$ability->get_meta(),
325-
'`public` metadata should only be present when provided during registration.'
325+
'`public` metadata should always be present in the stored meta.'
326+
);
327+
$this->assertFalse(
328+
$ability->get_meta_item( 'public' ),
329+
'`public` metadata should default to false when not provided.'
326330
);
327331
$this->assertFalse(
328332
$ability->get_meta_item( 'show_in_rest' ),
329333
'`show_in_rest` metadata should still default to false.'
330334
);
331335
}
332336

337+
/**
338+
* Tests that a null `public` value is treated as unset.
339+
*
340+
* @ticket 65568
341+
*/
342+
public function test_meta_public_null_is_treated_as_unset() {
343+
$args = array_merge(
344+
self::$test_ability_properties,
345+
array(
346+
'meta' => array(
347+
'public' => null,
348+
),
349+
)
350+
);
351+
$ability = new WP_Ability( self::$test_ability_name, $args );
352+
353+
$this->assertFalse(
354+
$ability->get_meta_item( 'public' ),
355+
'A null `public` value should use the default value of false.'
356+
);
357+
$this->assertFalse(
358+
$ability->get_meta_item( 'show_in_rest' ),
359+
'`show_in_rest` should use its default value when `public` is null.'
360+
);
361+
}
362+
333363
/**
334364
* Tests that invalid `public` value throws an exception.
335365
*

tests/phpunit/tests/abilities-api/wpRegisterAbility.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,7 @@ public function test_register_valid_ability(): void {
192192
array(
193193
'annotations' => $expected_annotations,
194194
'show_in_rest' => true,
195+
'public' => false,
195196
)
196197
);
197198

tests/phpunit/tests/abilities-api/wpRegisterCoreAbilities.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ public function test_core_get_site_info_ability_is_registered(): void {
6161
$ability = wp_get_ability( 'core/get-site-info' );
6262

6363
$this->assertInstanceOf( WP_Ability::class, $ability );
64+
$this->assertTrue( $ability->get_meta_item( 'public', false ) );
6465
$this->assertTrue( $ability->get_meta_item( 'show_in_rest', false ) );
6566

6667
$input_schema = $ability->get_input_schema();
@@ -203,6 +204,7 @@ public function test_core_get_user_info_ability_is_registered(): void {
203204
$ability = wp_get_ability( 'core/get-user-info' );
204205

205206
$this->assertInstanceOf( WP_Ability::class, $ability );
207+
$this->assertTrue( $ability->get_meta_item( 'public', false ) );
206208
$this->assertTrue( $ability->get_meta_item( 'show_in_rest', false ) );
207209

208210
$input_schema = $ability->get_input_schema();
@@ -308,6 +310,7 @@ public function test_core_get_environment_info_ability_is_registered(): void {
308310
$ability = wp_get_ability( 'core/get-environment-info' );
309311

310312
$this->assertInstanceOf( WP_Ability::class, $ability );
313+
$this->assertTrue( $ability->get_meta_item( 'public', false ) );
311314
$this->assertTrue( $ability->get_meta_item( 'show_in_rest', false ) );
312315

313316
$input_schema = $ability->get_input_schema();

0 commit comments

Comments
 (0)