Skip to content

Abilities API: add core/read-nav-menus ability - #858

Open
Builder106 wants to merge 3 commits into
WordPress:developfrom
SankofaForge-Hacks:add/nav-menu-core-abilities
Open

Abilities API: add core/read-nav-menus ability#858
Builder106 wants to merge 3 commits into
WordPress:developfrom
SankofaForge-Hacks:add/nav-menu-core-abilities

Conversation

@Builder106

@Builder106 Builder106 commented Jul 11, 2026

Copy link
Copy Markdown

What?

See WordPress/mcp-adapter#181

Adds a read-only core/read-nav-menus ability: fetch a single nav menu (with its items) by ID, slug, or registered theme location, or list every menu on the site alongside the registered theme locations and their current assignments.

Why?

An end-to-end agent session against a live WordPress site surfaced a gap: there's no way for an agent to list, inspect, or otherwise reason about the site's navigation menus. wp_get_nav_menus(), wp_get_nav_menu_items(), get_registered_nav_menus(), and get_nav_menu_locations() are all stable core functions, but none of them are exposed through the Abilities API. Full details and the manual-work-cost ranking are in WordPress/mcp-adapter#181.

This is proposed as a core/* ability, following the same path as core/read-users and core/read-settings: land here first for review and real-usage feedback, then a WordPress core Trac ticket and eventual merge proposal. Core Trac ticket: https://core.trac.wordpress.org/ticket/65612

How?

core/read-nav-menus is one ability with a oneOf input schema, rather than split list/get abilities, matching the shape of the existing Users and Settings ability classes:

  • id, slug, or location: returns a single menu, with its items, resolved via wp_get_nav_menu_object() and wp_get_nav_menu_items(). location resolves through get_nav_menu_locations() and errors if nothing is assigned there.
  • no input, optionally with search: returns every menu (without items) plus the site's registered theme locations (get_registered_nav_menus()) and which menu is currently assigned to each (get_nav_menu_locations()).

Since core does not yet register a navigation ability category, the class registers one itself on wp_abilities_api_categories_init, the same way Main.php registers the ai category, so it's easy to drop once core has its own. Like Users and Settings, it unregisters any core-provided core/read-nav-menus first so the plugin's version always wins, and hooks on wp_abilities_api_init at priority 11 to run after core's own registration.

Permission is gated behind edit_theme_options, the same capability WordPress requires to manage menus in wp-admin. Nav menus are ordinarily public (they're what renders in the site header), but a menu not assigned to any location, or the full location-assignment map, exposes more of the site's structure than the front end does, so I went with the more conservative gate here. Happy to loosen this if reviewers think read access should be broader.

Nav menu items come back from wp_get_nav_menu_items() as WP_Post objects, but most of the fields used here (title, url, classes, etc.) are dynamic properties wp_setup_nav_menu_item() adds at runtime, not part of WP_Post's declared property list. format_menu_item() reads them via get_object_vars() rather than direct property access so static analysis doesn't flag them as undefined.

Use of AI Tools

AI assistance: Yes
Tool(s): Claude Code
Model(s): Claude Sonnet 5
Used for: Writing of integration tests. Reviewed by me before this PR was opened.

Testing Instructions

  1. Check out this branch.

  2. Run the plugin's PHPUnit integration suite, or just tests/Integration/Includes/Abilities/Nav_Menus/Nav_MenusTest.php. Note: this hadn't been run in CI as of opening the PR; PHPCS and PHPStan have since been fixed and are passing.

  3. Manually: register a nav menu with at least one item, assign it to a theme location, then call core/read-nav-menus:

    • with no input, to get the collection
    • with id set to the menu's term ID
    • with slug set to the menu's slug
    • with location set to the theme location slug

    Check the response shapes match the output schema in each case.

Changelog Entry

Added - Read-only core/read-nav-menus ability for fetching nav menus and their items.

Open WordPress Playground Preview Open WordPress Playground Preview

@Builder106
Builder106 requested a review from a team July 11, 2026 16:56
@github-actions

github-actions Bot commented Jul 11, 2026

Copy link
Copy Markdown

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.

If you're merging code through a pull request on GitHub, copy and paste the following into the bottom of the merge commit message.

Co-authored-by: Builder106 <builder106@git.wordpress.org>

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

@codecov

codecov Bot commented Jul 11, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 97.59450% with 7 lines in your changes missing coverage. Please review.
✅ Project coverage is 78.99%. Comparing base (6dcd0ab) to head (145b33f).

Files with missing lines Patch % Lines
includes/Abilities/Nav_Menus/Nav_Menus.php 97.58% 7 Missing ⚠️
Additional details and impacted files
@@              Coverage Diff              @@
##             develop     #858      +/-   ##
=============================================
+ Coverage      78.44%   78.99%   +0.54%     
- Complexity      2454     2506      +52     
=============================================
  Files            104      105       +1     
  Lines           9925    10216     +291     
=============================================
+ Hits            7786     8070     +284     
- Misses          2139     2146       +7     
Flag Coverage Δ
unit 78.99% <97.59%> (+0.54%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@jeffpaul jeffpaul added this to the 1.3.0 milestone Jul 13, 2026
@jeffpaul jeffpaul moved this from Triage to Needs review in WordPress AI Roadmap Jul 13, 2026
Registers a read-only core/read-nav-menus ability that retrieves a
single nav menu (with items) by ID, slug, or registered theme
location, or the full collection of menus alongside registered theme
locations and their current assignments. Follows the shape of the
existing core/read-users and core/read-settings abilities: a single
ability per resource with a oneOf input schema, rather than split
list/get abilities.

Also registers a navigation ability category, since core does not yet
provide one.

See WordPress/mcp-adapter#181 and the core.trac.wordpress.org ticket
proposing this for core.
Leftover from adapting the Users ability's pattern; the collection
branch is reached by falling through the id/slug/location checks
rather than a dedicated lookup-type helper, so the constant was never
read.
- Fully qualify \WP_Error, \WP_Term, \WP_Post in docblock annotations
  (SlevomatCodingStandard.Namespaces.FullyQualifiedClassNameInAnnotation)
- Invert the two nav-menu/location loops to early-continue instead of
  nesting the append inside the if (SlevomatCodingStandard.ControlStructures.EarlyExit)
- Align consecutive assignment operators in format_menu()
  (Generic.Formatting.MultipleStatementAlignment)
- Rename format_menu_item()'s closure param off the reserved word `class`
- Read nav menu item's dynamic properties (added by wp_setup_nav_menu_item(),
  not part of WP_Post's real property list) via get_object_vars() instead of
  direct property access, since PHPStan has no way to know they exist on a
  plain WP_Post
@Builder106
Builder106 force-pushed the add/nav-menu-core-abilities branch from 5b28f3e to 145b33f Compare July 18, 2026 22:51
@Builder106

Copy link
Copy Markdown
Author

@gziolo @jorgefilipecosta Just a quick heads up: I've pushed a few updates to fix the initial PHPCS and PHPStan CI failures I mentioned in the PR description, and cleaned up an unused constant. All CI checks are green now, so this is ready for review whenever you have a chance.

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

Labels

None yet

Projects

Status: Needs review

Development

Successfully merging this pull request may close these issues.

2 participants