Make WordPress Core

Opened 6 weeks ago

Closed 4 weeks ago

Last modified 6 days ago

#65568 closed enhancement (fixed)

Add meta.public as a single flag to control ability exposure defaults

Reported by: gziolo Owned by: gziolo
Priority: normal Milestone: 7.1
Component: Abilities API Version:
Severity: normal Keywords: has-patch has-unit-tests has-dev-note
Cc: Focuses:

Description

Add a public flag to ability metadata as a single high-level control over client exposure. Setting public seeds the default for granular exposure flags. The first is show_in_rest: public set to true makes show_in_rest default to true, and an ability can still opt out by setting show_in_rest to false explicitly.

Background

Abilities carry a meta array with per-channel exposure flags like show_in_rest. That surface is growing as abilities wire into REST, MCP, and AI agents, but there is no shared notion of whether an ability is meant to be visible to external clients at all. That gap duplicates intent across every registration and makes consistent defaults hard to hold as new channels land.

Proposed behavior

Introduce public as a coarse default that cascades into granular flags. Resolution runs most specific to least specific, so an explicit per-channel value wins over public, which in turn wins over the built-in default:

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

The null coalescing operator matters: an explicit false for show_in_rest is treated as set and short circuits the fallback, so opting out is honored even when public is true. public is also included in the JSON Schema emitted for the REST endpoint so clients can read the author's intent directly.

Cascade into other channels

public is designed to seed exposure for MCP and the AI agents too. Rather than hardcode each channel, the cascade can run through wp_register_ability_args so any channel that leaves its own flag undefined inherits public:

add_filter(
        'wp_register_ability_args',
        function ( $args ) {
                if ( ! empty( $args['meta']['public'] ) && ! isset( $args['meta']['mcp']['public'] ) ) {
                        $args['meta']['mcp']['public'] = true;
                }
                return $args;
        }
);

This keeps REST as the first built-in caller while letting MCP and other channels opt into the same default without a bespoke branch each.

Backward compatibility

Abilities that set a channel flag explicitly are unaffected, since the explicit value sits at the top of resolution. Abilities that set neither flag keep current behavior. New behavior appears only when an author sets public and lets a channel fall back to it.

References

Change History (13)

This ticket was mentioned in Slack in #core-ai by gziolo. View the logs.


6 weeks ago

#2 @gziolo
6 weeks ago

  • Owner set to gziolo

This ticket was mentioned in PR #12463 on WordPress/wordpress-develop by @iamadisingh.


5 weeks ago
#3

  • Keywords has-patch has-unit-tests added

Introduce a public flag in ability metadata as a single high-level control over client exposure. When set, public seeds the default for show_in_rest via null coalescing:
show_in_rest = meta'show_in_rest' ?? meta'public' ?? false

An explicit show_in_rest: false always wins over public: true, preserving per-channel opt-out. The public key stays in stored metadata so other channels (MCP, AI agents) can read it via the wp_register_ability_args filter.

Testing: npm run test:php -- --group abilities-api --filter 'public'

Trac ticket: https://core.trac.wordpress.org/ticket/65568

## Use of AI Tools

#4 @khokansardar
4 weeks ago

Patch testing report

Patch / PR tested

Environment

WordPress: 7.1-alpha-62161-src
PHP: 8.2.18 (Docker)
MySQL: 8.0.36
OS: macOS 26.5.1
Test method: WP-CLI eval + PHPUnit (headless Abilities API change; no admin UI)
Local wordpress-develop @ http://localhost:8889

Steps

  1. Confirmed the gap on trunk: constructed WP_Ability with meta.public = true and read show_in_rest.
  2. Checked out PR #12463 and re-ran the same construction across the full resolution matrix (public only; public + explicit show_in_rest=false; neither; public=false; show_in_rest only; invalid public).
  3. Ran the PR's targeted suite: --filter 'public' --group abilities-api.
  4. Ran the full abilities-api group and the REST v1 list + run controller tests to check for regressions.
  5. Dispatched a live REST OPTIONS request and inspected the meta schema for the new public property.

Results

  • Before (trunk): pass — meta.public=true left show_in_rest=false; public stored but ignored (the reported gap).
  • public=true only: pass — show_in_rest defaults to true.
  • public=true + show_in_rest=false: pass — explicit false wins; opt-out honored.
  • neither flag set: pass — show_in_rest=false and no public key added to meta.
  • public=false: pass — show_in_rest=false.
  • show_in_rest=true only (no public): pass — unaffected; backward compatible.
  • invalid public (non-bool): pass — throws InvalidArgumentException with a clear message.
  • REST schema: pass — meta.public declared as type boolean via OPTIONS.
  • PHPUnit: pass — --filter public 9/9; full abilities-api group 360/360; REST list+run controllers 114/114 (includes new public exposure, opt-out-hidden, run-executable, and schema tests).

Conclusion
PR #12463 adds a public meta flag that seeds channel-specific exposure defaults, resolving show_in_rest most-specific-to-least-specific via $meta['show_in_rest'] ?? $meta['public'] ?? DEFAULT_SHOW_IN_REST, validating public as boolean, and declaring it in the REST item schema. Behavior matches the ticket exactly: an explicit show_in_rest:false still wins over public:true, unset abilities keep current behavior, and public is only stored when provided. The change is minimal, idiomatic, and backward compatible — no changed signatures, return shapes, or hook params — and the MCP/AI-agent cascade is correctly deferred to the wp_register_ability_args filter rather than hardcoded. Recommend commit.

@iamadisingh commented on PR #12463:


4 weeks ago
#5

Excellent, thank you for working on it.

Thanks for the review and approval! Glad to hear it.

#6 @gziolo
4 weeks ago

  • Resolutionfixed
  • Status assignedclosed

In 62729:

Add public meta flag to control exposure defaults for abilities

Introduce a public flag in ability metadata as a single high-level control over client exposure. When set, public seeds the default for channel-specific flags such as show_in_rest: an explicit show_in_rest value wins, then public, then the built-in default of false. This lets an ability opt in to all channels at once, while keeping the ability to opt out of a single channel.

The public value is stored in the ability metadata and declared in the REST item schema, so other channels such as MCP and AI agents can read it through the wp_register_ability_args filter.

Props iamadisingh, gziolo.
Fixes #65568.

#7 @gziolo
4 weeks ago

  • Resolution fixed
  • Status closedreopened

@khokansardar, I didn't include you in props. My apologies. I'm working on a follow-up commit to change existing core abilities to use meta.public rather than meta.show_in_rest, so I will keep you in mind :)

This ticket was mentioned in PR #12517 on WordPress/wordpress-develop by @gziolo.


4 weeks ago
#8

Follow-up to #12463, which introduced the public flag in ability metadata as a single high-level control over client exposure.

This change adopts that flag for the core abilities shipped with WordPress. The three core abilities now declare meta.public instead of meta.show_in_rest:

  • core/get-site-info
  • core/get-user-info
  • core/get-environment-info

### Why

public seeds show_in_rest through the null-coalescing chain (show_in_rest = meta['show_in_rest'] ?? meta['public'] ?? false), so REST exposure stays exactly the same. Using public also keeps the intent in stored metadata, so other channels (MCP, AI agents) can read it through the wp_register_ability_args filter. An explicit show_in_rest: false would still win if a channel ever needs to opt out.

### Testing

npm run test:php -- --group abilities-api --filter 'wpRegisterCoreAbilities'

Each ..._ability_is_registered test now asserts that public is true and keeps the show_in_rest assertion to confirm the flag still enables REST exposure.

---
🤖 Generated with Claude Code

@gziolo commented on PR #12517:


4 weeks ago
#9

@mukeshpanchal27, appreciate your review. I applied some follow-up changes to improve the documentation via https://github.com/WordPress/wordpress-develop/pull/12517/commits/c83213894df2538af7d8901c266cd4589226e5db. I plan another pass after reviewing PHPDoc in other places that document the example usage of the Abilities API to promote the revised approach.

#10 @gziolo
4 weeks ago

  • Resolutionfixed
  • Status reopenedclosed

In 62737:

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.

#11 @gziolo
4 weeks ago

  • Keywords needs-dev-note added

#12 @gziolo
3 weeks ago

I opened a PR against the MCP adapter proposing integration with the unified flag: https://github.com/WordPress/mcp-adapter/pull/254.

Note: See TracTickets for help on using tickets.