Skip to content

Conversation

@jorgefilipecosta
Copy link
Member

@jorgefilipecosta jorgefilipecosta commented Dec 26, 2025

Part of: WordPress/ai#40 cc: @Jameswlepage
Equivalent core PR of: WordPress/gutenberg#74234 (in Gutenberg).
Inspired by the work on https://github.com/galatanovidiu/mcp-adapter-implementation-example/tree/experiment/layerd-mcp-tools/includes/Abilities by @galatanovidiu.

Ticket: https://core.trac.wordpress.org/ticket/64455

Core abilities organization

This PR also proposes a logic for how core abilities are organized. In abilities.php, we have two functions: wp_register_core_abilities and wp_register_ability_category (or in the case of Gutenberg, _gutenberg_register_core_abilities and _gutenberg_register_core_ability_categories). These functions then call ability registration functions that are inside the abilities folder. If the ability is simple, it can be registered in just a single internal function, e.g., _wp_register_site_info_ability; for complex abilities, we can register them in a class (like this post management one).

The abilities can be in both Gutenberg and core. Having them in Gutenberg allows us to use them in the workflows functionality that is being worked on by @senadir and allows us to get some testing before core is released.

Gutenberg unregisters the equivalent core ones, so Gutenberg is the source of truth. The same ability can exist in Gutenberg and core, but Gutenberg takes precedence so we can test changes in Gutenberg before releasing in core (similar to what happens with blocks and other WordPress artifacts).

Core Post management abilities

This PR adds core post management abilities for the WordPress abilities API: core/create-post, core/get-post, core/find-posts, and core/update-post.

It supports nested query support for meta_query, tax_query, and date_query matching WordPress's native WP_Query structure with AND/OR relations. This allows very complex query operations which the REST API does not allow and may be useful for agents to find information.

It uses the permission callback mechanism and tries to correctly check permissions for all cases. The basic permission checking logic first checks the basic and common use case (user can edit, create, or see a post), then we go into specifics in separate functions that are reused for checking status changes (e.g., publish), author changes, and taxonomy assignment permissions.

The class WP_Posts_Abilities_Gutenberg is organized into 6 main areas:

  • Ability Registration: The main part that does the wp_register_ability calls.
  • Initialization: Initializes shared data between the abilities, e.g., schemas.
  • Output Formatting: Formats the post object (and taxonomies) for output shared between all abilities.
  • Permission Checking: Permission checking logic; some parts are also shared between abilities.
  • Query Processing: Utilities to process queries and map the ability input format to the WP_Query format.
  • Data Processing: Utilities to process input data, sanitize it, map to other formats, etc.

Missing

The idea of this PR is mainly to get feedback if this is the right direction for the post management abilities. There are some things that are missing:

  • Automated testing: Deeply covering all the abilities. If we agree with this approach, I will add the tests to this PR.
  • Partial Edits: Not all edits are possible, e.g., it is not possible to mark a post as sticky. We need to decide if marking a post as sticky should be done on the update/create post abilities, following the REST API approach, or if we should have a specific ability for that, following the wp_core PHP API approach where we have the wp_update_post function and also stick_post/unstick_post functions. We can decide on what to do regarding the missing edits as follow-ups, as the PR is already too big.
  • Pagination: Pagination was not yet implemented; we need to have a general solution for pagination at the abilities API level.

Test plan

  • Open /wp-admin/edit.php?post_type=post.
  • Open the browser console and paste the following code to load the abilities API:
const abilitiesAPI = (await import( '@wordpress/abilities' ) );
  • Verify core/create-post creates posts with various fields (title, content, status, meta, taxonomies):
await abilitiesAPI.executeAbility('core/create-post', {post_type: 'post', title: 'Hello World', content: 'My awesome content', 'status': 'publish', meta: {a:23, c:1} } );
  • Verify core/get-post retrieves posts by ID with optional taxonomy/meta inclusion:
await abilitiesAPI.executeAbility('core/get-post', {id: 334, include_meta: true, include_taxonomies: true} )
  • Verify core/find-posts queries work with nested meta_query, tax_query, and date_query.
  • Create posts with the following structure where the title a-23|c-1 represents a post with meta key "a" value of 23 and meta key "c" value of 1. Also adds the correct tags.
Screenshot 2025-12-26 at 17 25 02
  • Execute complex find post queries like:
await abilitiesAPI.executeAbility('core/find-posts', {

    post_type: 'post',

    include_meta: true,

    meta_query: {

        queries: [

            {

                key: 'footnotes',

                compare: 'EXISTS',

            },  

        ]

    }

});

await abilitiesAPI.executeAbility('core/find-posts', {

    post_type: 'post',

    include_meta: true,

    meta_query: {

        relation: 'AND',

        queries: [

            {

                key: 'a',

                compare: '=',

                value: '23'

            },

            {

                relation: 'OR',

                queries: [

            {

                key: 'b',

                compare: '=',

                value: '1'

            }, {

                key: 'c',

                compare: '=',

                value: '1'

            } ] }

                

        ]

    }

});

await abilitiesAPI.executeAbility('core/find-posts', {

    post_type: 'post',

    include_meta: true,

    tax_query: {

        relation: 'AND',

        queries: [

            {

                taxonomy: 'post_tag',

                field: 'slug',

                terms: ['t-23']

            },

            {

                relation: 'OR',

                queries: [

                    {

                        taxonomy: 'post_tag',

                        field: 'slug',

                        terms: ['c-1']

                    },

                    {

                        taxonomy: 'post_tag',

                        field: 'slug',

                        terms: ['b-1']

                    }

                ]

            }

        ]

    }

});



await abilitiesAPI.executeAbility('core/find-posts', {

    post_type: 'post',

    date_query: {

        relation: 'AND',

        queries: [

            { year: 2025 },

            {

                relation: 'OR',

                queries: [

                    { day: 26 },

                    { month: 11 }

                ]

            }

        ]

    }

});
  • Verify core/update-post modifies existing posts correctly:
await abilitiesAPI.executeAbility('core/update-post', {
    id: 327,
    content: 'Hello'
});
  • Test permission checks prevent unauthorized access, e.g., users without publish permissions trying to change status to published, etc.

@jorgefilipecosta jorgefilipecosta added enhancement php Pull requests that update php code labels Dec 26, 2025
@github-actions
Copy link

Test using WordPress Playground

The changes in this pull request can previewed and tested using a WordPress Playground instance.

WordPress Playground is an experimental project that creates a full WordPress instance entirely within the browser.

Some things to be aware of

  • The Plugin and Theme Directories cannot be accessed within Playground.
  • All changes will be lost when closing a tab with a Playground instance.
  • All changes will be lost when refreshing the page.
  • A fresh instance is created each time the link below is clicked.
  • Every time this pull request is updated, a new ZIP file containing all changes is created. If changes are not reflected in the Playground instance,
    it's possible that the most recent build failed, or has not completed. Check the list of workflow runs to be sure.

For more details about these limitations and more, check out the Limitations page in the WordPress Playground documentation.

Test this pull request with WordPress Playground.

@github-actions
Copy link

The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the props-bot label.

Core Committers: Use this line as a base for the props when committing in SVN:

Props jorgefilipecosta.

To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement php Pull requests that update php code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant