Skip to content

Navigation Menus

New in v3.3.0. WordPress nav menus were previously only reachable through the Elementor Pro Nav Menu widget, so free and agent-driven builds couldn’t wire up site navigation. This adds full nav-menu management over MCP, following the same two-dispatcher pattern as ACF.

Two tools, edit_theme_options, always-on. Contributed by @Mrshahidali420 (#85).

Each tool takes { operation, arguments }. Call it with no operation to get its catalog, then call again to run one.

// discover
menu-read({})
// → { mode: "read", operations: [ { operation: "list-menus", … }, … ] }
// act
menu-read({ operation: "get-menu", arguments: { menu: "primary" } })
OperationArgumentsReturns
list-menusNoneAll menus: id, name, slug, item count, assigned locations.
get-menu{ menu }One menu and its nested item tree.
list-locationsNoneThe theme’s registered menu locations + which menu (if any) is assigned.
render{ menu | location, depth?, … }The menu rendered to HTML via wp_nav_menu.
OperationNotes
create-menu / rename-menu / delete-menuManage menus.
assign-location / unassign-locationAttach a menu to a registered theme location.
add-itemCustom link, page, post, CPT, category, or taxonomy term.
update-itemChange a menu item (unspecified fields are preserved).
delete-itemRemove one item.
reorder-itemsRe-order / re-parent many items in one call; other fields preserved.

Menu references accept an id, slug, or name. Item types resolve and validate their object_id (existing post/term), and parents must belong to the same menu.

So a custom HTML header (e.g. an Elementor Canvas page built from an HTML/shortcode widget) can hold a live menu, the plugin registers a shortcode:

[emcp_menu location="primary" depth="2" class="my-nav" container="nav"]
[emcp_menu id="14"]
[emcp_menu slug="footer-menu"]

Edit the menu in WordPress (or via menu-write) and every header that uses the shortcode updates automatically: no re-render, no hardcoded nav. It’s separable by design: if you prefer a tools-only build, menu-read → render covers the agent side.

// 1. create a menu and assign it to the header location
menu-write({ operation: "create-menu", arguments: { name: "Main" } })
menu-write({ operation: "assign-location", arguments: { menu: "Main", location: "primary" } })
// 2. add items
menu-write({ operation: "add-item", arguments: { menu: "Main", type: "page", object_id: 12 } })
menu-write({ operation: "add-item", arguments: { menu: "Main", type: "custom", title: "Blog", url: "https://example.com/blog" } })
// 3. read it back as a nested tree
menu-read({ operation: "get-menu", arguments: { menu: "Main" } })