Reorganising a site's main menu is one of those jobs that sounds small and isn't. A client wants "Services" split into three, the blog moved under "Resources", two dead links gone and the order changed. In wp-admin that's twenty minutes of dragging items around on the menu visitors are using right now. One wrong drop and the header is broken until you notice.
It's also a job people now ask an AI client to do. The risky way is to let the agent edit the live menu item by item. This post shows a safer pattern that works with any MCP client (Claude Code, Claude Desktop, Cursor, VS Code and others): build the new menu beside the old one, check it, swap the theme location, and keep the old menu as your way back.
Everything here uses WPPilot Free, a self-hosted WordPress plugin that turns your site into an MCP server. Your AI client brings its own model; there's no relay in between.
Disclosure: I work on WPPilot. Ability names and behaviour below are taken from the public WordPress core ability reference.
One scope note first: classic menus
The menu abilities in WPPilot work on classic navigation menus: the ones you manage under Appearance → Menus and assign to theme locations like "Primary" or "Footer". If your site runs a block theme whose header uses a Navigation block, this exact workflow doesn't apply. Check before you start: if Appearance → Menus exists and your theme lists menu locations, you're good.
The abilities you'll use (all Free)
| Step | Ability | What it does |
|---|---|---|
| Look | list-menus |
Classic menus and the theme locations they occupy |
| Look | list-menu-locations |
Locations the active theme registers, and what's in each |
| Look | list-menu-items |
One menu as an ordered list with parents, targets, URLs, labels, CSS classes |
| Look |
list-content, search-content
|
Find the page and post IDs the new items should point to |
| Build | create-menu |
Creates an empty classic menu and returns its ID |
| Build | upsert-menu-item |
Adds or partially updates an item (custom URL, page, post, taxonomy…) |
| Build | reorder-menu-items |
Sets order and nesting in one call |
| Swap | assign-menu-location |
Puts a menu into a location. The previous assignment is captured first, so this is reversible from the change ledger |
| Check |
list-changes, get-change, rollback-change
|
The change ledger and rollback for supported operations |
Two abilities to keep away from in this job: delete-menu (permanent, menus have no trash) and delete-menu-item (permanent). Both need explicit confirmation anyway. The whole point of this pattern is that you never need them until the new menu has been live for a while.
Setup in five minutes
You need self-hosted WordPress 6.9+, PHP 8.0+ and HTTPS if your client connects remotely.
- Download
wppilot.zipfrom the latest GitHub release (it isn't on WordPress.org) and upload it under Plugins → Add New → Upload Plugin. - Leave the safety profile on Production Safe, the install default. It blocks PHP execution, WP-CLI, filesystem and database access, and plugin/theme installs, none of which a menu job needs.
- Connect your client. With Claude Code over OAuth:
claude mcp add --transport http wppilot https://your-site.com/wp-json/mcp/wppilot-oauth
# then run /mcp and finish the browser sign-in
Use a WordPress account that can already manage menus in wp-admin. Every MCP call inherits that user's permissions, so the agent can't do more than that account can.
Step 1: Have the agent describe what's there
Start with reads only. This prompt changes nothing:
Change nothing on the site.
1. Run list-menu-locations and tell me which menu sits in each location.
2. For the menu in the primary header location, run list-menu-items and
show it as an indented outline: label, target (page ID or URL), parent.
3. Flag items that point to a trashed or draft page, or to a custom URL
on this domain that could be a page link instead.
You now have a written snapshot of the current menu. Save it next to your ticket. It's the plan's "before".
Step 2: Build the new menu beside the old one
Give the agent the target structure in plain language and make it build a new menu:
Create a new classic menu called "Primary 2026-10 (staging)" with create-menu.
Do NOT modify or assign any existing menu.
Target structure:
- Home
- Services
- Web design (page "Web Design")
- Care plans (page "WordPress Care Plans")
- Audits (page "Site Audit")
- Resources
- Blog (posts page)
- Guides (category "guides")
- About
- Contact
Use search-content or list-content to find the real page IDs and link items
to those objects, not to hard-coded URLs. If a page doesn't exist, stop and
list what's missing instead of creating pages.
When all items exist, use reorder-menu-items once to set the final order and
nesting, then show me list-menu-items for the new menu.
Why link to objects instead of URLs? If someone later changes a page slug, an object-linked item follows it; a pasted URL silently breaks.
The live header hasn't changed at this point. Visitors still see the old menu, because the new one isn't assigned to any location.
Step 3: Review the new menu like a diff
Ask for a side-by-side before you swap:
Compare the current primary menu and "Primary 2026-10 (staging)" as two
outlines. List: items added, items removed, items renamed, items moved.
Confirm every item in the new menu points at a published page, post or term.
This is the review step people skip when they drag items around by hand. Read it properly. If something's wrong, fix it with upsert-menu-item or another reorder-menu-items call on the staging menu, which still isn't live.
Step 4: Swap the location (the only live change)
Use assign-menu-location to put "Primary 2026-10 (staging)" into the primary
header location. Then run list-changes and show me the entry for that call,
including whether rollback is available.
That one call is the entire live change. Because assign-menu-location captures the previous assignment before writing, the ledger entry should show a rollback option. Load the site in a private window and click through the header on desktop and mobile.
If your client can open a browser, get-page-view-link gives it the URL to look at. If it can't, capture-page asks the site to take a screenshot, though it only runs while someone has WPPilot's Visual Runtime tab open in wp-admin. compare-captures then scores a before and after capture of the same width and points at the regions that changed.
Step 5: If anything looks wrong, swap back
You have two ways back, and neither involves rebuilding anything:
-
Ledger rollback: ask the agent to run
rollback-changeon theassign-menu-locationentry. It needs explicit confirmation and verifies the restored state against the stored before-image. -
Plain re-assign: put the old menu back into the location with another
assign-menu-locationcall. The old menu was never edited, so it comes back exactly as it was.
Once the new menu has been live for a week or two and nobody has complained, rename it with update-menu and delete the old one by hand, or with delete-menu and confirmation, knowing it's permanent.
What this pattern doesn't protect you from
-
Creating and editing items isn't the reversible part. The docs promise ledger rollback for the location assignment. They don't make that promise for
create-menuorupsert-menu-item. That's why you build on a menu nobody sees. -
Theme-specific extras. Mega-menu settings, icons or CSS classes added by a theme or plugin may live outside what
list-menu-itemsreturns. Check any menu that relies on them by hand. - Caching. A page cache can keep serving the old header for a while after the swap. Purge it before you judge the result.
- It's not a backup. Take one before any live change, as always.
Why bother with an agent at all?
For a five-item menu, don't. Where this pays off is the messy one: 40 items, three levels, half of them custom URLs someone pasted in 2019. Having the agent map every item to a real object, flag dead targets and produce a before/after diff is the tedious part, and it's all reads. The only write that touches visitors is a single location swap you can undo.
Links
- WPPilot: https://wppilot.co
- WordPress core ability reference (menus, content, ledger): https://wppilot.co/docs/reference/wordpress-core
- Change ledger and rollback: https://wppilot.co/docs/change-ledger-and-rollback
- Safety profiles: https://wppilot.co/docs/safety-profiles
- Source and releases: https://github.com/wppilot-labs/wordpress-mcp-elementor-wppilot
What's the worst menu you've inherited? Tell me in the comments.
Top comments (1)
Some comments may only be visible to logged-in visitors. Sign in to view all comments.