#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
- Requirements doc: Ability exposure requirements
- Slack discussion: #core-ai thread
Change History (13)
This ticket was mentioned in Slack in #core-ai by gziolo. View the logs.
6 weeks ago
This ticket was mentioned in PR #12463 on WordPress/wordpress-develop by @iamadisingh.
5 weeks ago
#3
- Keywords has-patch has-unit-tests added
#4
@
4 weeks ago
Patch testing report
Patch / PR tested
- https://github.com/WordPress/wordpress-develop/pull/12463
- PR branch
add/65568-abilities-meta-public-flag@ 3fa93682ab (on trunk @ bbc4423fd9)
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
- Confirmed the gap on trunk: constructed WP_Ability with meta.public = true and read show_in_rest.
- 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).
- Ran the PR's targeted suite: --filter 'public' --group abilities-api.
- Ran the full abilities-api group and the REST v1 list + run controller tests to check for regressions.
- 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.
#7
@
4 weeks ago
- Resolution fixed
- Status closed → reopened
@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-infocore/get-user-infocore/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.
#12
@
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.
![(please configure the [header_logo] section in trac.ini)](/chrome/site/your_project_logo.png)
Introduce a
publicflag in ability metadata as a single high-level control over client exposure. When set,publicseeds the default forshow_in_restvia null coalescing:show_in_rest = meta'show_in_rest' ?? meta'public' ?? falseAn explicit
show_in_rest: falsealways wins overpublic: true, preserving per-channel opt-out. Thepublickey stays in stored metadata so other channels (MCP, AI agents) can read it via thewp_register_ability_argsfilter.Testing:
npm run test:php -- --group abilities-api --filter 'public'Trac ticket: https://core.trac.wordpress.org/ticket/65568
## Use of AI Tools