Get the plugin

Documentation

Everything WP MCP does, and exactly how the safety net works.

WP MCP is the open-source plugin wpmcp, built on the official WordPress Abilities API. This section is for developers, agencies, and anyone connecting an AI client over MCP.

Introduction

wpmcp is a WordPress plugin that turns a site into an MCP (Model Context Protocol) server. It lets an AI agent, Claude, Cursor, or any other MCP-compatible client, read and edit a WordPress site directly: create and update posts and pages, manage media, adjust settings, and more.

The core promise

Handing an AI agent write access to a live WordPress site is risky. A bad edit on a client’s production site is the kind of mistake that is hard to walk back, and every other WordPress AI tool asks you to trust the model to get it right.

wpmcp takes the opposite approach: it assumes the agent will get something wrong eventually, and makes sure that nothing it does is permanent.

Nothing an agent does through wpmcp is unrecoverable. Every write goes through a safety engine that snapshots the affected object before the change happens. If the change breaks something, it can be rolled back, either by the agent itself (via a rollback tool) or by a human, with one click, from the WordPress admin screen. You can undo a single operation, or unwind an entire agent session back to where it started.

How this is different

Most AI-for-WordPress tools focus entirely on the building and editing experience. wpmcp treats recoverability as the headline feature, not an afterthought:

  • Enforced in code, not by convention. No tool that mutates the database can bypass the safety wrapper. This is a structural guarantee, not a guideline the code happens to follow.
  • Works at the WordPress data layer. Snapshots capture the actual post row, meta, terms, and comments, so rollback does not depend on understanding any particular page builder’s internal format.
  • Runs inside WordPress. wpmcp is a single WordPress plugin built on the official WordPress Abilities API, not a separate Node process or local proxy. One install, and it works with any MCP client.
  • Free and open where it matters. The safety engine and the core content/media/settings tools are free and GPL-2.0 licensed.

Who this is for

  • Developers and agencies who want to let an AI agent build or maintain WordPress sites (including client sites) without babysitting every change.
  • MCP client builders and power users connecting Claude Code, Claude Desktop, Cursor, or another MCP client to a WordPress install.
  • Anyone nervous about giving an AI agent write access to production, and wants an undo button that actually works.

Project status

wpmcp is actively developed and past its feature-parity milestone: the safety engine (snapshot, apply, verify, rollback) and 407 registered abilities (274 free, 133 Pro) are shipped and covered by the test suite. A few integration paths (Polylang, WPML, multisite network calls, Google analytics/Search Console through Site Kit) are verified in production rather than CI, and release notes flag those honestly per release. Where this documentation describes a planned feature, it says so explicitly.

What it covers today

  • Page builders. Gutenberg (free) and, on Pro, Elementor deep editing plus structural read and write for Bricks, Divi, WPBakery, Beaver Builder, Breakdance, Oxygen 6, classic Oxygen (4.x and earlier), Avada and Thrive Architect through detect-builder, get-builder-content and update-builder-content. Every builder write is an ordinary post snapshot, so it rolls back exactly. Classic Oxygen writes need Oxygen active, and pre-4.0 Oxygen pages are read-only.
  • Block suites and themes. On Pro, dedicated support for Kadence Blocks, GenerateBlocks, Spectra, Otter Blocks and the Blocksy companion blocks, with schema-checked writes and pattern import (including the WordPress.org Pattern Directory, on request only). Theme settings packs for Astra and the Kadence theme (free) and GeneratePress and Blocksy (Pro).
  • WooCommerce. Products, variations, coupons, tax rates, orders, notes, sales reports and a two-step product import on the free tier; on Pro, a deep operations catalog for order create and edit, shipping zones and methods, webhooks, payment gateways with every secret masked, review moderation, totals reports, the system status report and brands. See WooCommerce Tools.
  • Plugin integrations. SEO metadata for Yoast SEO, Rank Math, SEOPress, The SEO Framework, SureRank and Slim SEO (free) and All in One SEO (Pro); Redirection; ACF (values and structure), Meta Box, JetEngine and Pods; Polylang, WPML and TranslatePress; BuddyPress; forms (Contact Form 7 with Flamingo, Forminator, SureForms and MetForm free, and the Pro forms pack for Gravity Forms, WPForms, Formidable, Ninja Forms and Fluent Forms); events, donations and memberships (read); FunnelKit funnels (Pro: reads, plus undoable changes to funnels, steps and checkout products through FunnelKit itself); and read-only status for UpdraftPlus, Duplicator, Solid Security, MonsterInsights and W3 Total Cache, plus a W3 Total Cache purge. The full list, with limits, is on the integrations page.

Where to go next

Getting Started

Requirements

Dependency Version
WordPress >= 6.9 (this version bundles the Abilities API that wpmcp registers its tools on)
PHP >= 8.1
Composer needed to install from source
MySQL / MariaDB whatever your WordPress install already uses

wpmcp is distributed from GitHub releases, not from the WordPress.org plugin directory.

Install the release zip

Download wpmcp.zip from the latest release (or use the direct link https://github.com/wpmcp/wpmcp/releases/latest/download/wpmcp.zip), then upload it in wp-admin under Plugins > Add New > Upload Plugin. With WP-CLI:

wp plugin install https://github.com/wpmcp/wpmcp/releases/latest/download/wpmcp.zip --activate

Install from source

If you would rather run from a checkout, clone the plugin directly into your wp-content/plugins directory and install its dependencies:

git clone https://github.com/wpmcp/wpmcp.git wp-content/plugins/wpmcp
cd wp-content/plugins/wpmcp
composer install --no-dev

--no-dev skips the PHPUnit/test tooling, which you do not need on a production or staging install. If you plan to contribute to the plugin itself, see Contributing and tests for the full dev setup instead.

Activate the plugin

  1. Log into wp-admin.
  2. Go to Plugins.
  3. Find wpmcp and click Activate.

On activation, wpmcp creates its snapshot storage table (wp_wpmcp_snapshots) and registers a wpmcp top-level admin menu item. That screen lists recent agent operations and lets a human restore any of them with one click, the same rollback mechanism the MCP tools use, just exposed to a person instead of an agent.

What activation does not do

Activating the plugin does not, by itself, expose write access to the outside world. An MCP client still needs valid WordPress credentials (an OAuth 2.1 grant approved by a signed-in user, or an Application Password) to call any tool, and every ability is capability-checked against edit_posts before it runs. See Connecting clients for how to actually wire up an AI agent.

Next steps

  • Connecting clients: sign a client in over OAuth 2.1, or generate an Application Password, and point it at your site.
  • The safety model: understand what happens before and after every write.

Connecting an MCP Client

wpmcp exposes its tools over the Model Context Protocol via the official WordPress Abilities API. Every ability wpmcp registers requires a caller to be authenticated as a WordPress user who can edit_posts, so the first step is deciding how the client signs in. There are two ways, and the WP MCP > Connection screen in wp-admin shows a ready-made config block for each client either way.

wpmcp ships a full OAuth 2.1 authorization server with PKCE (S256 is mandatory), so an MCP client that supports OAuth signs in the way it signs in anywhere else: it opens your site, you log in as a WordPress user, approve the client, and the client receives a token. No password is ever pasted into a config file, and a connection can be lowered or revoked later without touching the client.

OAuth is off by default. Switch it on by defining WPMCP_OAUTH_ENABLED in wp-config.php (or returning true from the wpmcp_oauth_enabled filter):

define( 'WPMCP_OAUTH_ENABLED', true );

Then, in your client, add the MCP endpoint and let it discover the authorization server. The Connection screen lists the discovery addresses clients use for this site, including sites installed in a subdirectory.

When you approve a client you choose what it may do:

  • Full access: the token can do what your user can do (the mcp scope).
  • Read-only: the token may list and read, and is refused anything that changes the site (the mcp:read scope). A client that asks only for mcp:read is narrowed to it automatically.
  • A scoped identity (site owners): the connection is bound to one of your scoped identities, so governance decides what it may do.

A token never gains scope when it is refreshed, and lowering a connection’s access on the Connection screen takes effect on its next request without reconnecting. Clients can register dynamically or sign in with a client metadata document (an https URL as their client ID); site owners can require approval of new clients, and approve or block them, on the same screen. Blocking a client revokes its tokens.

Option B: an Application Password

For clients or scripts without OAuth support, wpmcp also accepts WordPress’s built-in Application Passwords. No separate API key system, no additional plugin.

Step 1: create an Application Password

  1. In wp-admin, go to Users -> Profile (or edit the specific user the agent should act as).
  2. Scroll to Application Passwords.
  3. Give it a name (e.g. claude-code) and click Add New Application Password.
  4. Copy the generated password immediately. It is shown once.

Use a dedicated user for agent access rather than your own admin account, so you can revoke it independently and see its actions clearly in the operations list.

Step 2: encode the credential

MCP clients that speak HTTP typically expect a Basic auth header, which is the base64 encoding of username:application-password:

echo -n "your-username:xxxx xxxx xxxx xxxx xxxx xxxx" | base64

Keep the spaces in the application password exactly as WordPress generated them before encoding.

Step 3: point your client at the MCP endpoint

Claude Code

Add an entry to your project’s .mcp.json:

{
  "mcpServers": {
    "wpmcp": {
      "type": "http",
      "url": "https://your-site.com/wp-json/mcp/wpmcp-server",
      "headers": {
        "Authorization": "Basic BASE64_OF_username:application-password"
      }
    }
  }
}

Replace your-site.com with your actual domain and the Authorization value with the base64 string from step 2.

Cursor and Claude Desktop

Both are MCP-compatible clients, and the same endpoint and Basic-auth header work in their respective MCP server configuration. The .mcp.json example above will need to be adapted to whatever configuration format the client expects, but the URL and header are identical: no wpmcp-specific setup beyond the Application Password.

Local development: the WP-CLI stdio bridge

On a machine that has WP-CLI and the site’s files, a client can also run wpmcp over stdio through the bundled WP-CLI command, wp mcp-stdio serve, with no HTTP credentials at all; the client runs it as a local process and talks to it over stdin and stdout.

Verifying the connection

Once connected, ask your client to call the list-post-types or list-posts tool. If you get a result back, the connection and auth are working. If you get a 401/403, check that the WordPress user has at least the edit_posts capability (an Editor or Administrator role satisfies this by default); for an Application Password, double-check the base64 encoding too; for OAuth, check that WPMCP_OAUTH_ENABLED is defined and that the client was approved with the access level you expected.

See Tools reference for the full list of available tools once you are connected.

The Safety Model

This is the part of wpmcp that the rest of the product exists to support. Every tool that mutates existing data routes through a single orchestrator, Safe_Mutation::run(). No exceptions, and this is enforced in code: a tool cannot write to a post or option except by calling this method.

The snapshot -> apply -> verify -> rollback flow

snapshot (before)  ->  apply the change  ->  verify  ->  ok?
     |                                          |         |
 stored in                              on failure:    return
 wp_wpmcp_snapshots                     auto-rollback   operation id
 (keyed by operation + session)         + raise error
  1. Snapshot. Before the mutation runs, Snapshot::capture() records the target’s before-image and Safe_Mutation stores it, gzip-compressed, in the wp_wpmcp_snapshots table, keyed by a generated operation ID (a UUID) and the calling session’s ID.
  2. Apply. The tool’s actual mutation callback runs (e.g. wp_update_post(), update_option(), wp_delete_post()).
  3. Verify (optional, tool-specific). Some tools pass a verification callback. update-blocks, for example, checks that the new block markup still parses as valid Gutenberg blocks. If verification fails, Safe_Mutation immediately restores the snapshot and throws a Mutation_Failed exception, so a bad write never survives past the same request.
  4. Rollback, on demand. Separate from the automatic verify-and-revert above, any successfully-applied operation can be undone later via the rollback-operation or rollback-session tools, or from the wpmcp wp-admin screen.

Every write tool returns an operation_id in its response specifically so a caller (human or agent) can reference it later for rollback.

What gets captured

Snapshot capture is dispatched by object type (src/Safety/Snapshot.php). The core types are post, option, user, comment, term and wc_order, and the integrations add their own (listed below). This is what makes product edits, order changes, comment edits, menu edits, page builder layouts and plugin data undoable through the same engine as posts and settings, not a separate one-off mechanism per domain.

Posts (and attachments, which are WordPress posts of type attachment):

  • The full post row, every column from get_post($id, ARRAY_A), not a hand-picked subset of fields. This matters for the resurrection path below: a partial capture would mean a force-deleted post comes back missing its original post type, author, parent, slug, dates, or menu order, silently rebuilt from wp_insert_post()’s defaults.
  • All post meta (get_post_meta($id)), every key and value.
  • Taxonomy term assignments, captured per taxonomy registered on that post type via wp_get_object_terms().
  • Comments and their comment meta, captured only for use by the force-delete resurrection path (see below). WordPress’s wp_delete_post($id, true) destroys comments and commentmeta with no equivalent in the trash or in-place-update paths, so they have to be captured up front to be restorable.

A WooCommerce product is a post (post_type = product) and a navigation menu item is a post (post_type = nav_menu_item), so update-product, delete-product, update-menu-item, and remove-menu-item all restore exactly through this same full-row-plus-meta-plus-terms path: price, stock, description, title, url, parent, and position all come back as they were.

Options (used by update-settings and assign-menu-to-location):

  • The option’s current value.
  • Whether the option existed before the write. Options have no trash/soft-delete equivalent: a write either changes an existing value or introduces a brand-new option. Recording existed lets rollback choose between putting the old value back (update_option()) or removing the option entirely (delete_option()) if it wasn’t there before.

Users (used by update-user):

  • The user’s core profile fields and all usermeta, captured before any change so rollback-operation restores display name, email, url, nickname, name fields, and description exactly. Role and password are never touched by any wpmcp tool, so neither is part of this capture.

Comments (used by moderate-comment, edit-comment, and delete-comment):

  • The comment row and its comment meta. delete-comment‘s resurrection path reinserts the comment via wp_insert_comment(); the content, author, dates, and post association are restored, but (like force-deleted posts’ comments) the comment gets a new auto-increment ID, since WordPress core does not let a caller choose one.

WooCommerce orders (used by update-order-status):

  • The order’s prior status, captured via the wc_order object type so it is HPOS- and CPT-safe (it does not assume orders are stored as posts). rollback-operation restores the exact prior status.

Integration snapshot types

  • Page builders. WPBakery, Avada, Divi, Bricks, Beaver Builder, Breakdance, Oxygen 6, classic Oxygen and Thrive Architect all keep their layouts in post_content and post meta, so a builder write is an ordinary post snapshot and rolls back exactly. The rollback path then refreshes or invalidates the builder’s generated CSS the same way a forward write does.
  • WooCommerce (Pro catalog). wc_order_full (a whole order, for orders.update), wc_shipping_zone (a whole zone with its methods), wc_webhook, wc_tax_rate, and option for a payment gateway’s settings or a store setting. Creates record a creation row so rollback removes exactly what was created.
  • Plugin tables. db_rows for a form entry’s status in the plugin’s own table (Gravity Forms, WPForms, Fluent Forms), plugin_table_rows for Pods table storage and TranslatePress dictionary rows, buddypress_rows for BuddyPress, redirection_item for Redirection redirects, and aioseo_row for All in One SEO. Restoring a raw table row is an administrator action.
  • Structure. acf_structure for ACF field groups, post types and taxonomies (keyed by the ACF key, so a create and its undo agree on what was created), acf_options for ACF options pages, option_set when a theme settings write spans two options, and theme_scaffold for a scaffolded child theme.

Some writes cannot be snapshotted and say so with recoverable: false rather than implying an undo that does not exist: a WooCommerce refund (the status comes back, the money does not), WooCommerce system status tools, the W3 Total Cache purge, and Forminator and SureForms entry deletes, which are also off by default.

Personal data in snapshots

A snapshot is a verbatim before-image, so some snapshots hold personal data. wpmcp treats restoring one as the same privilege as reading it:

  • Comments and reviews. Restoring a comment snapshot needs moderate_comments, the capability that reading a commenter’s email and IP takes; restoring a product review also needs edit_product on its product. Change sets and the history screen never show a commenter’s email, IP address or browser details.
  • Posts with comments. Rolling back a deleted post as a user without moderate_comments brings the post and its comments back, with each commenter’s email, IP and user agent left blank, and the rollback response says so.
  • Other personal data. A payment gateway settings snapshot holds its credentials, so restoring it takes manage_woocommerce. A Flamingo entry snapshot takes edit_users. BuddyPress and raw table snapshots take manage_options.
  • Reversible is not erased. Because a deleted form entry can be restored, a copy stays in the snapshot history until it is pruned. A reversible delete is not a GDPR erasure, and the ops that do it say so.

Rollback on shared data

Some objects keep changing after an agent writes to them, because other people use the site. BuddyPress is the clearest case: members join groups, reply to activity and trigger notifications. A BuddyPress write records every row it and BuddyPress’s own hooks created, and its rollback restores only the rows it saved, by id. It removes only the rows the write itself created, keeps membership requests, notifications and replies that other members added after the write, keeps reply numbering in a thread consistent, and leaves in place, and reports, any created row that has since changed hands. The same principle applies elsewhere: rolling back a WooCommerce shipping zone create removes the zone only while it is still the zone that was created, and undoing a Redirection create never removes a different redirect that took its place.

Snapshots are serialized with gzencode(wp_json_encode($before)) and stored in a LONGBLOB column.

Operation rollback vs session rollback

Two distinct undo scopes, both backed by the same snapshot table:

  • rollback-operation restores exactly one snapshot, identified by its operation_id. Simple: look up the row, apply that snapshot.
  • rollback-session unwinds an entire agent session. It pulls every snapshot recorded under a session_id, walks them oldest-first, and for each distinct object restores only the earliest snapshot seen, that object’s state from before the session touched it at all. If the same post was edited three times in one session, only the first (pre-session) snapshot is applied; the two later ones are skipped once the object’s identity has already been restored. The tool’s return value (restored_count) counts snapshot rows processed, not distinct objects restored, so it can be larger than the number of objects that actually changed.

Object identity for deduplication is object_type:object_id for posts, users, comments, and orders, and option:<option name> for options (the raw database object_id column is always 0 for option rows, since options are identified by name, not a numeric ID; the real name lives inside the serialized blob).

The meta-purge

Rollback is a full restore, not an additive merge. If the mutation being undone added a new meta key that did not exist at snapshot time, a rollback that only restored the snapshotted keys would leave that new key behind as orphaned meta, i.e. the object would not be truly back to its pre-mutation state.

To prevent that, Rollback_Service::apply_snapshot():

  1. Diffs the object’s current meta against the snapshotted meta.
  2. Deletes any meta key present now but absent from the snapshot (added by the mutation being undone).
  3. Deletes and re-adds every snapshotted key/value pair exactly as captured.

This is what makes rollback exact rather than approximate: a restored object matches its pre-mutation state, including the absence of anything the agent added.

Force-delete and resurrection, with ID verification

Trashing a post (delete-post without force: true) is not routed through Safe_Mutation at all: WordPress’s own trash already makes it reversible, so a redundant snapshot would buy nothing. Force-deleting (force: true) permanently removes the post row, so that path is safe-wrapped.

When a force-deleted post needs to be rolled back, the row no longer exists, so a plain wp_update_post() would silently no-op. Instead, Rollback_Service re-inserts the post at its original ID using wp_insert_post()’s import_id parameter, then replays the captured comments on top of it via wp_insert_comment() (comment IDs themselves cannot be preserved, WordPress core always assigns a new auto-increment comment ID, but content, author, dates, and thread association with the post are restored).

Two safety checks guard this resurrection path:

  • Identity check before choosing the restore path. If a post already exists at the target ID, wpmcp does not assume it’s safe to just update it in place. It compares post_date_gmt (immutable after creation) between the live row and the snapshot. If they don’t match, the row at that ID is a different post that has since reclaimed the ID (e.g. someone manually re-imported content after the original was force-deleted), and blindly updating it would silently overwrite an unrelated post. In that case rollback routes through the resurrection path instead, which triggers the next check.
  • ID collision check after resurrection. wp_insert_post()’s import_id is only honored if that ID is still free; on a collision it silently falls back to a new auto-increment ID instead of erroring. wpmcp checks the returned ID against the ID it asked for, and if they don’t match, throws a Mutation_Failed rather than leaving a “restored” post sitting at the wrong ID with no error. A rollback that silently succeeds at the wrong ID would be worse than one that fails loudly.

Known limitations, stated honestly

Free-tier history retention bounds session rollback, by whole runs. Snapshot_Store::history_limit() returns 20 by default; the wpmcp_snapshot_history_limit filter raises or lowers that number on any site at no cost, and the Pro build adds that filter itself, returning unlimited (PHP_INT_MAX) while the licence is active. After every write, Safe_Mutation::run() calls Snapshot_Store::prune(), which trims the table back toward that limit. Pruning is session-aware: a bulk run recorded under one session is kept or dropped as a whole, never cut in the middle, and the most recent run is always kept whole however large it is, so it stays fully undoable with rollback-session. A single write that would itself exceed the limit holds pruning until it finishes, and the next write outside the hold prunes as usual, by whole sessions. What the cap means in practice on a free-tier site: once newer runs have pushed an older session out of the table, rollback-session for that session is refused with the reason, instead of silently undoing only the part that survived. Pro sites keep every session while the licence is active, and if a licence ends the history is held at its current depth until the owner trims it or sets a depth, so a lapse never deletes undo points on its own.

Media force-delete does not restore file bytes. Force-deleting an attachment (or deleting one without MEDIA_TRASH defined) permanently unlinks the physical file from disk. Rollback via Safe_Mutation restores the media’s database record faithfully, the post row, all meta, and taxonomy terms, but it cannot restore bytes already deleted from the filesystem. The delete-media tool response signals this explicitly with "files_recoverable": false and a warning field pointing at the tracking issue. Full file-level recovery is tracked as issue #24 and is not yet implemented. Because of this, delete-media is disabled by default: a site must opt in via the wpmcp_enable_delete_media filter, and every call additionally requires confirm: true.

Note on wording: an earlier design-spec draft stated that snapshot capture does not record taxonomy terms. That is now out of date. The current Snapshot::capture() implementation does capture per-taxonomy term assignments for posts, and Rollback_Service::apply_snapshot() restores them via wp_set_object_terms(). What is genuinely not yet captured is a small set of secondary post fields not covered by the full-row capture’s practical use today (the design intentionally captures the entire row, so this gap is narrower than earlier drafts suggested); consult src/Safety/Snapshot.php directly if you need the exact current field list, since this is an area still evolving.

Driving rollback

  • From an agent: call the rollback-operation or rollback-session MCP tools directly (see Tools reference).
  • From a human: open the wpmcp screen in wp-admin, which lists recent operations (via the same list-operations data) with a one-click Restore button per row.

Tools Reference

WP MCP currently registers 407 abilities in the canonical test environment: 274 free and 133 Pro. Every tool below is registered as a WordPress ability named wpmcp/<tool-name> via the official Abilities API, each with its own permission_callback (content tools require edit_posts; sensitive domains are gated by stronger capabilities).

Every mutating tool routes through Safe_Mutation::run(), so it is snapshotted before it runs and can be undone with rollback-operation or rollback-session, except the small set of deliberately risky operations that are disabled by default and honestly say so in their descriptions. See The safety model for how the engine works.

This page is generated from the plugin source and its ability manifest; the descriptions below are the exact ones the AI agent sees.

Safety & history (3 tools)

  • list-operations: List recent safety snapshot operations
  • rollback-operation: Undo a single operation by restoring its pre-change snapshot
  • rollback-session: Undo all operations from a session by restoring each object’s pre-session snapshot

Content & pages (16 tools)

  • count-content: Counts so a job can be sized before it starts: posts per public type by status, media by MIME family, comments by status, terms per taxonomy, users per role. From core counting APIs, so figures match wp-admin
  • create-post: Create a post, page, or custom post type
  • delete-post: Delete a post, page or CPT entry. Trash by default (reversible). force:true deletes permanently, is off until the wpmcp_enable_delete_post filter opts in, needs confirm:true, and is snapshotted (undoable)
  • diff-revisions: Diff two revisions of a post, or one revision against the post’s current state: a unified diff per changed field (title, content, excerpt) rather than two full documents. Unchanged fields are omitted
  • duplicate-post: Copy a post, page or CPT entry (content, meta but _edit_lock and _wp_old_slug, terms, builder data; children optional) as a draft unless status is set. stage:true: one linked draft to edit; publish_stage: writes it over the original (same ID, URL, comments; refused if the original changed, unless force; undo: rollback-operation); discard_stage deletes it
  • get-page: Read a page
  • get-post: Read a single post, page, or custom post type
  • get-revision: Read a single post revision’s fields
  • list-post-types: List registered post types (posts, pages, custom post types)
  • list-posts: List/search posts, pages, or custom post types
  • list-revisions: List a post’s revisions (id, author, date, change excerpt)
  • list-taxonomies: List registered taxonomies (categories, tags, custom taxonomies)
  • restore-revision: Restore a post to a given revision
  • set-post-terms: Assign taxonomy terms to a post (replace, append, or remove)
  • update-blocks: Update a page’s block content
  • update-post: Partially update a post, page, or custom post type

Taxonomy terms (6 tools)

  • create-term: Create a category, tag or custom taxonomy term. Snapshot-first; rollback-operation reverses the creation. Refuses an existing slug, a missing parent, or a parent on a non-hierarchical taxonomy
  • delete-term: Delete a term, snapshot-first. Rolling back restores it at its original term_id and term_taxonomy_id and refiles the posts that were in it. Reports objects affected; refuses a taxonomy’s default term
  • get-term: Read one term by term_id or slug, with its meta and full ancestor chain. Ancestors disambiguate a hierarchical taxonomy, where several same-named terms sit under different parents
  • list-terms: List terms in a taxonomy with search, parent filter, ordering and pagination. Empty terms are INCLUDED unless hide_empty is set (the opposite of core’s default), so a partial list never leads to creating a duplicate
  • set-term-meta: Write or delete a term meta value, snapshot-first (whole term plus meta map captured, so rollback is exact). value:null deletes the key, staying distinct from an empty string. Protected keys are refused
  • update-term: Update a term’s name, slug, description or parent, snapshot-first. Refuses a slug held by another term, and refuses reparenting onto itself or a descendant: core permits that and the resulting cycle makes the taxonomy unrenderable

Site content search (2 tools)

  • reindex-search: Build or rebuild the search-content index. The index updates on save; use this for the first build or after bulk or out-of-band changes. Cursor-based: each call indexes up to batch_size posts and returns next_offset
  • search-content: Search all site text, incl. what post_content search misses: Elementor and Bricks settings, block attributes, template parts, reusable blocks and nav menus. Each hit has a location (block path, element id or menu item id) and a snippet. Read-only; hits re-checked against read_post. Empty index: run reindex-search

Gutenberg blocks (5 tools)

  • convert-html-to-blocks: Convert raw HTML to Gutenberg block markup: h1-h6, p, img, ul/ol, blockquote, pre/code, hr and table map to core blocks; anything else is wrapped in core/html so nothing is lost. Pure transform, never touches a post; write it with update-blocks
  • get-block-type: Return full detail for a single registered block type by name: its attributes schema, declared supports and block-context wiring (uses_context, provides_context)
  • list-block-types: List registered block types: name, title, category, is_dynamic and attribute names. Optional category (exact) and search (substring on name) filters
  • parse-blocks: Parse block markup into its block tree via parse_blocks(). Takes “blocks” (raw markup) or “id” (parses that post’s post_content). Each node reports blockName, attrs, recursively parsed innerBlocks, and an innerHTML summary
  • serialize-blocks: Serialize a block tree (parse-blocks shape) back into block markup via serialize_blocks(). A pure transform that never touches a post; write the result with update-blocks

Surgical block edits & patterns (7 tools)

  • add-block: Insert ONE block (“” markup) into a post at path (zero-based indexes into the parse-blocks tree; the last may equal the sibling count to append). Needs expected_hash (content_hash from parse-blocks); stale reads refused. Snapshot-first; other blocks stay byte-identical
  • duplicate-block: Duplicate the block at path (deep copy, inserted immediately after the original within the same parent) and return the copy’s new_path. Needs expected_hash (content_hash from parse-blocks); stale reads refused. Snapshot-first
  • insert-pattern: Insert a registered block pattern’s blocks into a post at path (add-block’s path semantics; whitespace filler dropped). Needs expected_hash (content_hash from parse-blocks); stale reads refused. Snapshot-first; existing blocks stay byte-identical
  • list-patterns: List registered block patterns: name, title, description, categories. Optional search (substring on name or title). Markup is not returned; insert-pattern inserts it server-side
  • move-block: Move the block at from_path to position to_index among its own siblings (same parent only; compose remove-block + add-block to move across parents). Needs expected_hash (content_hash from parse-blocks); stale reads refused. Snapshot-first
  • remove-block: Remove ONE block by path (zero-based indexes into the parse-blocks tree, descending innerBlocks); nested removals keep the container. Needs expected_hash (content_hash from parse-blocks); stale reads refused. Snapshot-first; undo: rollback-operation
  • update-block: Update ONE block by path (zero-based indexes into the parse-blocks tree, through innerBlocks): replace attrs (whole) and/or inner_html (leaf blocks only; a container’s children by their own paths). Needs expected_hash (content_hash from parse-blocks); stale reads refused. Snapshot-first; other blocks stay byte-identical

Composite page builds (1 tool)

  • build-page: Compose a complete page from ONE declarative spec: title, a recursive sections/blocks tree, media references (existing attachment ids), and optional menu placement. The whole composition is a single atomic, recoverable operation: the spec is strictly validated (node-path-addressed errors, bounded size/nodes/depth) before any write, a mid-build failure automatically removes everything it created, and on success one operation_id is returned whose rollback-operation removes the page and its menu placement entirely. Markup is composed deterministically from the spec; nothing in the spec is evaluated or executed. dialect “gutenberg” (default, free) builds block markup; dialect “elementor” (PRO, requires Elementor) builds an _elementor_data element tree. Set dry_run=true to validate and compose WITHOUT writing: the reply reports element counts per node type, nesting depth, markup size, unknown widget types, atomic props that had to be coerced, and every referential problem at once

Custom block builder (8 tools, 8 Pro)

  • create-custom-block, Pro: Create a custom Gutenberg block from a data spec (title, attributes, template with {{name}} placeholders). Validated, stored as a wpmcp_block post and registered at runtime; a render_callback interprets the template (no code generation, no eval). Remove with delete-custom-block
  • delete-custom-block, Pro: Delete a custom block by moving it to the trash. Snapshotted: returns an operation_id for rollback-operation (restore-post also works)
  • get-custom-block, Pro: Read one custom block’s stored spec by id
  • list-block-control-types, Pro: List the attribute types a custom-block spec may use and the block.json type each maps to
  • list-custom-blocks, Pro: List the custom blocks on this site (id, name, title, active/inactive)
  • set-block-status, Pro: Enable (publish) or disable (draft) a custom block by id. Snapshotted: returns an operation_id for rollback-operation; a no-op status change writes nothing
  • update-custom-block, Pro: Replace a custom block’s spec by id (re-validated before it is stored). Snapshotted: returns an operation_id for rollback-operation
  • validate-block-spec, Pro: Statically validate a custom-block spec (title, attributes, template) without storing it

Media library & stock images (12 tools, 1 Pro)

  • delete-media: Delete a Media Library attachment. Off until the wpmcp_enable_delete_media filter opts in; needs confirm:true. force:true deletes permanently, snapshotted (undoable)
  • get-media: Read a Media Library attachment: title, URL, every registered size, dimensions, mime type, alt text, caption and description
  • import-stock-image: Sideload a stock search result into the Media Library. SSRF-guarded: https only, host allowlist (wpmcp_remote_media_allowed_hosts filter) checked first, no redirects, size caps, bytes must verify as an image. Stores attribution and license. Undo: rollback-operation
  • insert-stock-image, Pro: Run import-stock-image’s SSRF-guarded import, then insert the image into the post’s builder content as a Gutenberg image block. Returns two operation ids, each undoable on its own (insert, import)
  • list-media: List Media Library attachments, filtered by type (“image” or a mime like “image/png”), after/before dates and search, paged newest first with total/pages
  • resize-media: Regenerate the given registered image sizes of an attachment from its original and report each file (name, dimensions, URL). Snapshot-first with a file backup (undoable)
  • search-stock-images: Search openly-licensed stock images: openverse (keyless, Creative Commons), pexels and unsplash (own key via set-stock-key). Results carry license, license_url, attribution and source_url for import-stock-image
  • set-stock-key: Store (or clear with an empty api_key) your own pexels or unsplash API key. Keys are encrypted at rest with a site-salt-derived key and never echoed back
  • sideload-image: Add an image to the Media Library from a url, or from a prompt via the site’s AI provider (ratio e.g. 16:9). Undo: rollback-operation
  • update-media: Update an attachment’s title, alt text, caption or description
  • upload-media: Upload a file to the Media Library from base64 data, an https url, or grant:true (returns a one-use upload URL and curl, for large files). Type sniffed from the bytes, not mime_type; executables and SVG refused; capped at the upload limit. Undo: rollback-operation
  • upload-svg: Add an SVG to the Media Library from markup or an allowlisted URL. A fail-closed sanitizer rejects script, foreignObject, event handlers and external references; only sanitized markup is stored. Rollback deletes it

Options & post meta (4 tools)

  • get-option: Read a single wp_options value by name. Refuses a conservative denylist of sensitive/core option names (auth keys and salts, siteurl, home, active_plugins and secret/password/token-shaped names)
  • get-post-meta: Read a post’s meta, either the full map or a single key. Protected meta (a leading underscore, or is_protected_meta) is always skipped
  • set-post-meta: Set a single meta key/value on a post. Refuses protected meta keys (a leading underscore, or is_protected_meta). Snapshotted via object_type post; rollback-operation restores the prior value
  • update-option: Update one wp_options value by name. Refuses get-option’s denylist; off until the wpmcp_enable_option_write filter opts in. Snapshotted; rollback-operation restores the prior value or removes a new option

Site settings (2 tools)

  • get-settings: Read WordPress site settings (general, reading, writing, discussion, media, permalinks), each with its group, type and whether it is writable
  • update-settings: Update WordPress site settings from a strict allowlist. Validates/coerces each value (enum, int range, bool), rejects unsafe permalink structures, skips read-only or non-allowlisted keys and still applies the valid subset

Users (4 tools)

  • create-user: Create a new non-admin user. Auto-generates a strong password (never returned) and emails the new user so they can set their own. Rejects admin and unknown roles; defaults to subscriber
  • get-user: Read one user’s profile detail, including an is_admin flag derived from live capabilities. Never returns the password hash
  • list-users: List WordPress users as safe summary rows (id, username, display name, email, roles, registration date). Never returns password hashes or other secrets
  • update-user: Update a non-admin user’s profile fields (display name, email, url, nickname, first/last name, description). Refuses admin-capable users. Never changes role or password. Snapshotted so the change can be rolled back

Comments (5 tools)

  • delete-comment: Permanently delete a comment. Off until the wpmcp_enable_delete_comment filter opts in; needs confirm:true. Rollback restores it under a new ID
  • edit-comment: Edit a comment’s content or author name, email, url. Reversible
  • get-comment: Read one comment
  • list-comments: List comments, optionally by post and status, with paging
  • moderate-comment: Set a comment’s status: approve, unapprove, spam, trash or untrash. Reversible
  • add-menu-item: Add an item to a navigation menu (custom link by title and url, or an object link via type, object, object_id). Additive; a mistaken item can be removed with remove-menu-item
  • assign-menu-to-location: Assign a navigation menu to a registered theme location (the nav_menu_locations theme_mod). Snapshotted; rollback-operation restores the prior assignment
  • create-menu: Create a new navigation menu (a nav_menu term). Creation has no prior state to snapshot; a mistaken menu can be removed with delete-menu
  • delete-menu: Delete a navigation menu (a nav_menu term). Off until the wpmcp_enable_delete_menu filter opts in; needs confirm:true. Not automatically reversible: the menu name and items are returned for a manual rebuild
  • get-menu: Read one navigation menu with its ordered items (id, title, url, type, parent, order)
  • list-menu-locations: List the theme’s registered menu locations and the menu (if any) assigned to each
  • list-menus: List the site’s navigation menus as safe summary rows (id, name, slug, item count)
  • remove-menu-item: Remove an item from a navigation menu. Snapshotted as a post; rollback-operation resurrects it at its original id, re-attached to its menu
  • update-menu-item: Update a navigation menu item’s title, url, parent, or position. Snapshotted as a post; rollback-operation restores the prior values exactly

Plugins & themes (13 tools)

  • activate-plugin: Activate an installed plugin. Snapshots the prior active_plugins option (undoable)
  • deactivate-plugin: Deactivate a plugin. Refuses protected packages (wpmcp, Elementor). Snapshots the prior active_plugins option (undoable)
  • delete-plugin: Permanently delete an installed plugin’s files. Disabled by default (wpmcp_enable_delete_plugin filter) and needs confirm:true. Refuses protected or active plugins. Not rollback-able
  • delete-theme: Permanently delete an installed theme’s files. Disabled by default (wpmcp_enable_delete_theme filter) and needs confirm:true. Refuses the active theme (or its active parent). Not rollback-able
  • get-plugin-info: Fetch full wordpress.org plugin directory info for a slug: version, rating, installs, homepage, download link and compatibility
  • install-plugin: Install a plugin from wordpress.org by slug. activate: true also needs activate_plugins and returns a rollbackable operation_id
  • install-theme: Install a theme from wordpress.org by slug. activate: true also needs switch_themes and returns rollbackable operation_ids
  • list-plugins: List installed plugins with active status, protected-package flag and pending update info; updates:true lists core, plugin and theme updates with auto-update state
  • list-themes: List installed themes with active status, parent theme and pending update info
  • search-plugins: Search the wordpress.org plugin directory by keyword, with optional tag/author filters and a capped per_page
  • switch-theme: Activate an installed theme. Snapshots the prior template/stylesheet options (undoable)
  • update-plugin: Update an installed plugin to the latest wordpress.org version. Disabled by default (wpmcp_enable_update_plugin filter) and needs confirm:true. File changes are not rollback-able
  • update-theme: Update an installed theme to the latest wordpress.org version. Disabled by default (wpmcp_enable_update_theme filter) and needs confirm:true. File changes are not rollback-able

Theme builder (headers, footers, 404) (6 tools)

  • create-site-part: Create a header, footer or 404 site part, shown where an include rule matches and no exclude rule does; not the Elementor theme-template tools. Capped per part type; wpmcp/delete-site-part frees a slot
  • delete-site-part: Trash a site part, freeing its per-part-type slot. Snapshot-first: operation_id rolls it back
  • list-site-parts: List site parts with their conditions and status, optionally by part_type
  • resolve-site-part: Which site part wins for a context (most specific, then priority), with every candidate and why. post_id alone fills post_type and term_ids
  • set-site-part-status: Activate (publish) or deactivate (draft) a site part; only active parts resolve. Snapshot-first
  • update-site-part: Edit a site part; omitted fields are kept, part_type is fixed. Snapshot-first: operation_id rolls it back

Custom CSS & JavaScript (2 tools, 2 Pro)

  • add-custom-js, Pro: Store the site-wide JS snippet printed in wp_footer (replaces the previous one; js=“” + replace=true clears). XSS-CLASS SURFACE, off by default: needs WPMCP_ALLOW_JS_INJECTION or wpmcp_allow_js_injection, plus unfiltered_html and manage_options. Snapshot-first; closing the gate stops rendering stored JS
  • add-scoped-css, Pro: Store CSS printed in wp_head only on one post/page (post_id required). Pass css, or selector plus bare declarations; element_id (Elementor) scopes it to .elementor-element-. Appends; replace=true overwrites, css=“” + replace=true clears. Site-wide: add-custom-css. Needs edit_css and manage_options. Refuses script-capable CSS (markup, expression(), script or data: URLs, @import), even obfuscated. Snapshot-first per page

WooCommerce (32 tools, 3 Pro)

  • add-order-note: Add an internal or customer-facing note to a WooCommerce order. Additive only; nothing to roll back
  • apply-product-import: Apply a plan-product-import plan: same rows and mode plus its plan_hash. Refused if the store or rows changed; confirm:true above the threshold. rollback-session on its session_id undoes it
  • bulk-update-products: Update up to 50 products and variations in one call; each item {id, …fields} as update-product/update-variation take them, same rules and permission checks. Per item: ok, operation_id or error; one failure does not stop the rest. rollback-session on the returned session_id undoes the batch
  • create-coupon: Create a coupon. Refuses a code already in use, an unknown discount_type, a percent over 100 or an invalid email restriction. Status defaults to draft. Undo with delete-coupon
  • create-product: Create a simple WooCommerce product via the CRUD layer. Creation has no prior state to snapshot; a mistaken product can be removed with delete-product
  • create-tax-rate: Create a tax rate. rate is a percentage from 0 to 100; country must be a known ISO code or “” for all; class must be standard or an existing class. Undo with delete-tax-rate
  • create-variation: Add a variation to a variable product. attributes maps each variation attribute to one of its options (“” or omitted = any); unknown attributes or options are refused. Prices, sku, status and stock follow update-variation’s rules. Undo with delete-variation
  • delete-coupon: Delete a coupon (trash by default, force for permanent). Disabled until the site opts in via the wpmcp_enable_delete_coupon filter; needs confirm:true. Snapshotted: rollback-operation restores it at the same id
  • delete-product: Delete a WooCommerce product (trash by default, force for permanent). Off until the wpmcp_enable_delete_product filter opts in; needs confirm:true. Snapshotted: rollback resurrects a force-deleted product at its id with price, stock and terms
  • delete-tax-rate: Delete a tax rate. Disabled until the site opts in via the wpmcp_enable_delete_tax_rate filter; needs confirm:true. Snapshotted: rollback-operation restores it at the same id
  • delete-variation: Permanently delete a product variation (there is no variation trash). Disabled until the site opts in via the wpmcp_enable_delete_variation filter; needs confirm:true. Snapshotted: rollback-operation resurrects it at the same id, attached to its parent
  • get-coupon: Read one coupon’s full settings by id or code (limits, restrictions, usage)
  • get-order: Read full detail for one WooCommerce order (status, billing email, payment method, line items, customer note). HPOS- and CPT-safe
  • get-product: Read full detail for one WooCommerce product (prices, stock, description, categories, tags)
  • get-sales-report: Read-only sales summary over a date range: order count, gross sales, items sold and top products by quantity. Aggregated over wc_get_orders() (HPOS- and CPT-safe)
  • list-coupons: List coupons as summary rows (id, code, status, type, amount, usage, expiry), with code search, status filter and paging
  • list-low-stock-products: List products and variations at or below a managed-stock threshold (default: the store’s low-stock setting) or out of stock, as rows whose ids feed update-product/update-variation. Page while has_more is true
  • list-orders: List WooCommerce orders as safe summary rows (id, number, status, total, currency, date), filterable by status and customer, with paging. HPOS- and CPT-safe
  • list-product-categories: List WooCommerce product categories (the product_cat taxonomy) as summary rows (id, name, slug, parent, count)
  • list-products: List WooCommerce products as safe summary rows (id, name, sku, price, stock status), filterable by search, status, type, or category, with paging
  • list-tax-rates: List the store’s tax classes and tax rates, filterable by class or country, with paging. Read-only
  • list-variations: List the variations of one variable WooCommerce product as safe summary rows (id, sku, attributes, prices, stock), with paging
  • plan-product-import: Plan a product import; writes nothing. Rows: sku, name, type (simple/variable), regular_price, sale_price, stock, categories, attributes, variations, images (media ids or https URLs), status. Per row: create, update (diff), skip or error; plus a plan_hash
  • update-coupon: Update a coupon’s fields with the same checks as create-coupon. Snapshotted: rollback-operation restores every setting and usage history
  • update-order-status: Change a WooCommerce order’s status, validated against the store’s registered statuses. Snapshotted via the wc_order object type so rollback-operation restores the prior status exactly. HPOS- and CPT-safe
  • update-product: Update a WooCommerce product’s fields (price, stock, description, etc.). stock_status is accepted only while stock is unmanaged and never on a variable product. Snapshotted as a post; rollback-operation restores the prior price and stock exactly
  • update-tax-rate: Update a tax rate with the same checks as create-tax-rate; postcodes and cities replace the whole list. Snapshotted: rollback-operation restores the row and its locations
  • update-variation: Update a WooCommerce variation: regular_price, sale_price, sku, status (publish/private), manage_stock, stock_quantity and stock_status (only while stock is unmanaged). Snapshotted; rollback-operation restores price and stock, keeps it attached to its parent and re-syncs the parent’s price range
  • validate-coupon: Check whether a coupon code would be accepted: published, expiry, usage limits, and (when email or subtotal is given) per-customer limit, email allow-list and spend rules. Each rule reports pass, fail or skipped; cart-dependent rules are flagged
  • woo-ops, Pro: List WooCommerce ops (e.g. orders.get) by domain: products, variations, orders, refunds, coupons, customers, reviews, reports, shipping, taxes, webhooks, settings, gateways, system-status. Each op: mode, route, path params, capability, confirm, enabled, snapshot type, full-rollback flag. Read-only
  • woo-read, Pro: Run one read op from woo-ops as an in-process wc/v3 request as the current user, gated by op capability and governance (wpmcp/woo-{op}, dots as dashes). Path params fill the route, the rest are query. Returns the raw wc/v3 body (may include personal data; secrets redacted); 20 per page, max 50. Read-only
  • woo-write, Pro: Run one woo-ops write op or a batch of up to 25 as in-process wc/v3 requests as the current user, gated like woo-read. Path params fill the route, the rest are the body (query for deletes). Changes to existing state are snapshotted (operation_id for rollback-operation); creates return recoverable:false and an undo_op. Refunds are not recoverable and call the gateway only with api_refund:true. Deletes and refunds need the wpmcp_woo_op_enabled filter and confirm:true. A batch prechecks every item, refuses whole on any failure and shares one session_id for rollback-session

SEO metadata (5 tools)

  • get-crawler-files: robots.txt (served output, physical file, managed rules), llms.txt, and the served sitemap (core or SEO plugin) with its post types
  • get-seo-meta: Read a post’s SEO title, meta description, focus keyword, canonical URL, and robots flags (noindex/nofollow) via the active SEO plugin’s postmeta keys
  • get-seo-status: Which SEO plugin is active, by name and version
  • update-crawler-files: Add robots.txt rules (filter, never the file), build llms.txt from pages, or exclude post types, taxonomies or posts from the core sitemap (lists replace). Refused if a physical file or the SEO plugin owns it. Snapshotted
  • update-seo-meta: Set a post’s SEO title, meta description, focus keyword, canonical URL and/or robots flags (noindex/nofollow) via the active SEO plugin’s postmeta keys. Snapshotted as a post; rollback-operation restores the prior values exactly

SEO depth: term SEO, social meta, schema (6 tools, 6 Pro)

  • generate-meta-tags, Pro: Propose a post’s title, description, canonical, robots, OpenGraph and Twitter tags from SEO plugin fields or the post record, each with its source. Read-only proposal
  • generate-schema-markup, Pro: Generate schema.org JSON-LD (Article, WebPage, LocalBusiness or Product) for a post from title, dates, author, excerpt or SEO description, featured image, permalink and, for Product, the WooCommerce record. Proposal only: writes nothing
  • get-social-meta, Pro: Read a post’s OpenGraph and Twitter overrides (title, description, image) from the active SEO plugin as one field set. Plugins with unmapped social storage return a structured unsupported response
  • get-term-seo-meta, Pro: Read a term’s SEO fields (as get-seo-meta) by taxonomy plus term_id or slug. Fields the plugin lacks on terms are in unsupported_fields; no term storage returns supported:false
  • set-social-image, Pro: Set a post’s OpenGraph and/or Twitter image from an attachment_id or image_url via the active SEO plugin. Snapshotted, undo with rollback-operation. Unmapped plugins return supported:false
  • update-term-seo-meta, Pro: Set a term’s SEO fields (as update-seo-meta) by taxonomy plus term_id or slug. Snapshotted; rollback-operation undoes it. Fields the plugin lacks on terms return in skipped_fields
  • create-redirect: Create a managed redirect from a source path to a target URL or target_post_id (which survives slug changes). Chains are flattened to one hop and loops refused; the stored target is reported. Snapshotted; rollback-operation removes it
  • delete-redirect: Delete one managed redirect. The whole row is snapshotted first, so rollback-operation brings the same redirect back; use update-redirect with enabled:false instead to stop it firing while keeping its hit history
  • find-broken-links: Scan published content for internal links that are dead, point at a non-public post or go through a redirect. background:true queues a batched scan; poll it with scan_id. Proposes fixes, changes no content
  • list-redirects: List this site’s managed redirects (source path, resolved target, status code, enabled, hit count), plus pending suggestions raised when a published post was deleted or moved. Filter by enabled state or a search string. Read-only
  • update-redirect: Change one managed redirect: its source, target/target_post_id, status code, enabled state or notes. Only the fields you pass change. Retargeting re-runs chain flattening and loop detection. Snapshotted and reversible

Internal linking (3 tools)

  • find-orphan-posts: List published posts or pages that have zero incoming internal links (orphans), by scanning the most-recent posts for links that resolve to this site’s own content
  • get-link-map: Summarize the internal-link graph: per-post outgoing and incoming link counts, the orphan list and the most-linked posts
  • suggest-internal-links: Suggest related published posts a given post should link to, ranked by shared categories/tags and title keyword overlap, excluding posts it already links to

Content analysis (accessibility, SEO, contrast) (8 tools, 8 Pro)

  • add-alt-text-from-context, Pro: Write missing image alt text in a post from filename, nearest heading or title. Keeps existing alt unless overwrite_existing=true; skips decorative images. Dry run unless apply=true; one snapshot, one rollback
  • analyze-accessibility, Pro: Scan a post’s HTML for WCAG issues (missing alt text, heading jumps, empty or vague link text, unlabeled form controls): scored findings with element locations. Read-only
  • analyze-seo, Pro: op score (default; post_id): on-page SEO 0-100 with findings (title/meta length, headings, words, alt, links, keyword density, readability). op keywords: volume, difficulty, CPC, intent. op backlinks (target domain or URL): link counts. Both use your provider key (set-seo-data-key), cached. Read-only
  • check-contrast, Pro: WCAG contrast ratio of a foreground/background hex pair, with AA/AAA pass/fail for normal and large text. Read-only
  • extract-content, Pro: A post’s plain text and structure (headings, word count, link and image counts). keywords=N adds the top N terms and 2-3 word phrases, title and headings weighted. Read-only
  • fix-color-contrast, Pro: Raise failing inline text/background color pairs in a post to a target WCAG ratio by lightness only (hue kept); before/after color, ratio, level per pair. Dry run unless apply=true; one snapshot, one rollback
  • fix-link-text, Pro: Replace empty or generic internal link text (“click here”, “read more”) with the destination title (WCAG 2.4.4, SEO). Skips external links and anchors with markup. Dry run unless apply=true; one snapshot, one rollback
  • set-seo-data-key, Pro: Save SEO data provider credentials for analyze-seo (empty api_key clears); dataforseo: “login:password”. Encrypted, never echoed

Elementor catalog (free) (4 tools)

  • get-global-settings: Read the Elementor kit’s global colors, typography, spacing and layout. Returns the settings_hash the global color and typography writes require
  • get-widget-schema: The settings schema for one Elementor widget type: curated typed params (defaults, enums, responsive hints, required plugin) for cataloged widgets, or with full:true (and for non-cataloged widgets) the full introspected control stack
  • list-widgets: List Elementor registered widget types (name, title, categories, icon, tier, availability), annotated from the curated catalog (purpose, cataloged flag). Filter by tier (free/pro), category or case-insensitive search (name, title, catalog keywords)
  • regenerate-elementor-css: Rebuild Elementor CSS; all needs confirm

Elementor deep editing (33 tools, 33 Pro)

  • add-custom-css, Pro: Add site-wide custom CSS via core Additional CSS, so it works on any site (Elementor Pro not required). Appends, or replaces with replace=true. Snapshot-first when the Additional-CSS post exists (undo: rollback-operation)
  • add-widget, Pro: Add a widget to a page’s _elementor_data under parent_id (or top level) at an optional position. Cataloged widget_types (list-widgets) take typed params validated before any write; other registered widgets take raw settings. Needs expected_hash from get-elementor-data. Undo: rollback-operation
  • apply-template, Pro: Copy a library template’s content into a page with fresh ids that never collide, appended (default, optionally under parent_id at position) or replacing the whole page (mode=replace). Needs expected_hash from get-elementor-data. Undo: rollback-operation
  • create-code-snippet, Pro: Create an Elementor Custom Code snippet (elementor_snippet post). location: wp_head, wp_body_open or wp_footer. Stored on any site; renders where Elementor Pro Custom Code is active. Not snapshotted (creates only); remove with delete-code-snippet
  • create-popup, Pro: Create an Elementor popup (elementor_library post, type popup), optionally seeded with elements and trigger/display settings. Not snapshotted (creates only); configure further with set-popup-settings, remove with delete-post
  • create-theme-template, Pro: Create an Elementor theme-builder template: a header, footer, single, archive, loop-item, search-results or error-404 elementor_library post, optionally seeded with elements and display conditions. Not snapshotted (creates only); remove with delete-theme-template
  • delete-code-snippet, Pro: Delete an Elementor Custom Code snippet by moving it to the trash (reversible through WordPress trash / restore-post), mirroring the default delete-post path
  • delete-theme-template, Pro: Delete an Elementor library template by moving it to the trash (reversible through WordPress trash / restore-post), mirroring the default delete-post path
  • detect-elementor-version, Pro: Report the Elementor and Elementor Pro versions, supports_atomic (Elementor 4.0+) and atomic_tools_registered: the four atomic write tools register only on a builder that renders atomic elements, so check before assuming they exist
  • export-page, Pro: Export a page’s Elementor content to a portable structure (content element tree + page_settings + type + version), the envelope import-template accepts, so a design can move between pages or sites
  • export-template, Pro: Export an elementor_library template as the portable envelope import-template accepts (element tree, page_settings, conditions, type, version), so it round-trips between sites intact
  • generate-widget, Pro: Generate a widget of any cataloged type (list-widgets / get-widget-schema) into a page’s _elementor_data under parent_id or top level, with a seedable element id. Unknown types and invalid settings are refused before any write. Undo: rollback-operation
  • get-custom-css, Pro: Read the site’s WordPress core Additional CSS
  • get-elementor-data, Pro: A page’s parsed Elementor tree (id, elType, widgetType, settings, children). Large pages: summary=true (skeleton with labels and child/descendant counts), max_depth (cut nodes report truncated_children) or element_id for one subtree. Reports total/returned_elements, truncated; data_hash covers the whole page, so windowed reads are a valid expected_hash
  • get-theme-template, Pro: Read one Elementor library template: its type, display conditions and element count
  • import-template, Pro: Create an elementor_library template from an export-page or export-template envelope, also across sites. Element ids are regenerated; the envelope’s page_settings and display conditions apply when present. Not snapshotted (creates only); remove with delete-post
  • list-code-snippets, Pro: List the Elementor Custom Code snippets stored on this site (elementor_snippet posts) with their location, priority and code
  • list-dynamic-tags, Pro: List registered Elementor dynamic tags (name, title, group), optionally by group. Most come from Elementor Pro, so the list is short or empty without it
  • list-global-classes, Pro: List the Elementor v4 global CSS classes of the active kit in stored order (empty when the feature has not been used), with the state_hash the global class write tools require as expected_hash
  • list-theme-templates, Pro: List Elementor theme-builder templates (header, footer, single, archive, …), optionally filtered to one template_type, each with its display conditions
  • move-element, Pro: Reparent an element by id: remove it and append it as a child of a new parent in the page’s _elementor_data. Refuses moves into itself or its descendants. Undo: rollback-operation (post snapshot)
  • remove-element, Pro: Remove an element (and its children) from a page’s _elementor_data by id. Undo: rollback-operation (_elementor_data is ordinary postmeta the post snapshot captures)
  • replace-system-colors, Pro: Atomically replace all four Elementor system color slots (primary, secondary, text, accent) on the active kit: each exactly once with a valid hex color, or nothing is written. No “title” keeps the current one. Needs expected_hash from get-global-settings. Undo: rollback-operation
  • replace-system-typography, Pro: Atomically replace all four Elementor system typography slots (primary, secondary, text, accent) on the active kit: each exactly once, or nothing is written. Each entry needs at least one typography_* field; unknown keys are refused; a font enables custom typography; no “title” keeps the current one. Needs expected_hash from get-global-settings. Undo: rollback-operation
  • resolve-theme-template, Pro: Which Elementor theme-builder template wins for a location (header, footer, single, archive, …): every candidate with its conditions, specificity and matching excludes, plus the winner. post_type/post_id resolve against a real target, else only specificity counts
  • save-as-template, Pro: Save a page’s Elementor content as a reusable elementor_library template of template_type (page, section, container, header, footer, single, archive, popup, …; default page). Not snapshotted (creates only); remove with delete-post
  • set-dynamic-tag, Pro: Bind an Elementor dynamic tag to an element setting, written into settings[dynamic][setting_key] in Elementor’s [elementor-tag …] format; other bindings are kept. Needs expected_hash from get-elementor-data. Undo: rollback-operation
  • set-popup-settings, Pro: Set an Elementor popup’s trigger/display settings (open/close triggers, timing, advanced rules), merged into the popup’s _elementor_page_settings. Snapshot-first; undo: rollback-operation
  • set-template-conditions, Pro: Set where an Elementor theme template renders: conditions as part arrays ([“include”,“singular”,“post”]) or slash strings (“include/general”), through Elementor Pro’s conditions manager when present, else the same _elementor_conditions meta. Snapshot-first; undo: rollback-operation
  • update-element, Pro: Update an Elementor element’s settings by id, merging the given settings into its existing settings. Writes the page’s _elementor_data, snapshot-first; undo: rollback-operation
  • update-global-colors, Pro: Update the active Elementor kit’s global colors: system_colors entries patch the four system tokens by _id (color/title); custom_colors entries update one by _id or append. Colors must be hex. Needs expected_hash from get-global-settings. Undo: rollback-operation
  • update-global-typography, Pro: Update the active Elementor kit’s global typography: system_typography entries patch the four system tokens by _id; custom_typography entries update one by id or append. typography* fields merge in; setting a font enables custom typography so the token renders. Needs expected_hash from get-global-settings. Undo: rollback-operation
  • update-widget, Pro: Patch a cataloged widget’s settings by element id from typed params (add-widget’s schema; see get-widget-schema), validated and merged. Non-cataloged widgets are refused toward update-element. Needs expected_hash from get-elementor-data. Undo: rollback-operation

Elementor structural suite (8 tools, 8 Pro)

  • add-container, Pro: Create an Elementor layout element (container by default, or section/column) at top level or under parent_id, optional position. Columns need a parent; widgets are never parents. Needs expected_hash from get-elementor-data; stale reads refused. Undo: rollback-operation
  • batch-update, Pro: Apply N Elementor element settings updates atomically under ONE snapshot: every {element_id, settings} is validated first, one unknown id refuses the batch, and any failure rolls it all back. Needs expected_hash from get-elementor-data. One rollback-operation undoes it
  • duplicate-element, Pro: Deep-copy an Elementor element and its subtree with fresh ids, inserted right after the original. New ids use Elementor’s 7-char hex format and are unique on the page. Needs expected_hash from get-elementor-data. Undo: rollback-operation
  • find-element, Pro: Search a page’s Elementor tree by el_type, widget_type, setting_key + setting_value and/or css_class (AND-combined, at least one). Each match reports element_id, types, navigator label and ancestor path; returns the current data_hash to chain a mutation
  • reorder-elements, Pro: Reorder the children of one Elementor parent (or the top level without parent_id) to an explicit id order, which must be an exact permutation of the current children. Needs expected_hash from get-elementor-data. Undo: rollback-operation
  • set-element-label, Pro: Set an Elementor element’s navigator label (stored as the _title setting); an empty label clears the custom name. All other settings survive untouched. Needs expected_hash from get-elementor-data. Undo: rollback-operation
  • update-container, Pro: Merge settings into an Elementor container, section or column by id: given keys are set, others survive. Widgets are refused (use update-element). Needs expected_hash from get-elementor-data. Undo: rollback-operation
  • update-page-settings, Pro: Merge settings into a page’s Elementor page settings (_elementor_page_settings): given keys are set, others survive. Post field keys (post_title, post_status, template, …) are refused, use the post tools. Needs expected_hash = settings_hash from get-elementor-data. Undo: rollback-operation

Elementor v4 atomic elements (4 tools, 4 Pro)

  • add-atomic-widget, Pro: Add an Elementor 4.0+ atomic widget (e-* widgetType, e.g. e-heading, e-paragraph, e-button, e-image) to a page. Friendly params (title, content, text, image_url, alt, link) become typed $$type props for known types; any type also takes raw $$type-wrapped settings.
  • add-div-block, Pro: Add an Elementor 4.0+ atomic div-block (e-div-block) to a page under parent_id (or top level), optional position.
  • add-flexbox, Pro: Add an Elementor 4.0+ atomic flexbox (e-flexbox) to a page under parent_id (or top level), optional position.
  • update-atomic-widget, Pro: Update an Elementor 4.0+ atomic widget’s settings by element id. Friendly params map to typed $$type props for known types (only those passed change); raw $$type-wrapped settings also work.

Elementor global classes (4 tools, 4 Pro)

  • create-global-class, Pro: Create an Elementor v4 global class (Class Manager) with a label and styles; returns its g- id. Pass friendly “styles” and/or raw $$type “props”, optional breakpoint/state; validated against Elementor’s schema, so a property it would drop is refused. Needs expected_hash from list-global-classes. Undo: rollback-operation
  • delete-global-class, Pro: Delete an Elementor v4 global class by g- id. Without confirm:true it is a dry run listing every post using the class; with it, the class set is snapshotted, then the class deleted (rollback-operation restores it). Needs expected_hash from list-global-classes
  • reorder-global-classes, Pro: Set the order of Elementor v4 global classes, which is their CSS source order and decides ties at equal specificity. Pass order: [g-id, …]; omitted classes follow in current order and unknown ids are refused. Needs expected_hash from list-global-classes. Undoable
  • update-global-class, Pro: Update an Elementor v4 global class by g- id: rename it and/or merge styles into one breakpoint + state variant (replace_variant:true replaces it), so other responsive or hover rules survive. Needs expected_hash from list-global-classes. Undo: rollback-operation

Elementor global variables (4 tools, 4 Pro)

  • create-global-variable, Pro: Create an Elementor v4 global variable; returns its e-gv- id. label is the CSS variable name (letters, digits, - _). value: hex/rgb()/hsl() color, font family, size like 16px, or any CSS for custom-size (sizes need Elementor Pro). Needs expected_hash from list-global-variables. Undoable
  • delete-global-variable, Pro: Delete an Elementor v4 global variable by e-gv- id. Without confirm:true it is a dry run listing the posts and classes using it. Needs expected_hash from list-global-variables. Undoable
  • list-global-variables, Pro: List Elementor v4 global variables (color, font, size design tokens) with the state_hash the write tools need as expected_hash
  • update-global-variable, Pro: Update an Elementor v4 global variable by e-gv- id: label, value, or size/custom-size switch (no other type change). Needs expected_hash from list-global-variables. Undoable

Brand kits (4 tools, 4 Pro)

  • apply-brand-kit, Pro: Apply a brand kit to the active Elementor kit (system colors, swatches, the four system typography tokens, logo) as ONE snapshotted operation. Without confirm:true it only returns the per-token diff and settings_hash; with it, expected_hash must match. Any invalid entry refuses the whole kit. Undo: rollback-brand-kit or rollback-operation
  • get-brand-kit, Pro: Return one brand kit in the shape apply-brand-kit writes: system slots as hex, named swatches with id, typography mapped to Elementor typography* fields. Entries failing validation are listed in “invalid” and make the kit unappliable
  • list-brand-kits, Pro: List brand kits (bundled presets plus any from the wpmcp_brand_kits option or filter): slug, category, source, the four system colors, font families, logo flag. Filter by category, source (bundled/site) or search
  • rollback-brand-kit, Pro: Undo a brand-kit apply by restoring its single snapshot (palette, swatches, typography, logo). Defaults to the most recent apply not yet rolled back; pass operation_id to target a specific apply

Custom widget builder (9 tools, 9 Pro)

  • compile-custom-widget, Pro: Compile a published custom-widget spec into a native Elementor widget; the plugin, never the agent, writes and lints the PHP. Off unless wpmcp_enable_widget_compiler is on; needs edit_files, honors DISALLOW_FILE_EDIT. set-widget-status disables it
  • create-custom-widget, Pro: Create a custom Elementor widget from a data spec (title, controls, template with {{name}} placeholders), stored as a wpmcp_widget post. Without unfiltered_html the template is wp_kses_post-filtered (template_filtered: true)
  • delete-custom-widget, Pro: Move a custom widget to the trash (reversible via restore-post)
  • get-custom-widget, Pro: Read a custom widget’s stored spec by id
  • list-control-types, Pro: List the control types a custom-widget spec may use and their Elementor controls
  • list-custom-widgets, Pro: List this site’s custom widgets (id, name, title, active/inactive)
  • set-widget-status, Pro: Enable (publish) or disable (draft) a custom widget by id
  • update-custom-widget, Pro: Replace a custom widget’s spec by id (re-validated; same wp_kses_post gate as create)
  • validate-widget-spec, Pro: Validate a custom-widget spec without storing it

Page builders: Bricks, Divi, WPBakery, Beaver Builder, Breakdance, Oxygen, Avada, Thrive (3 tools, 3 Pro)

  • detect-builder, Pro: Post builder: elementor, bricks, divi, wpbakery, avada, beaver-builder, breakdance, oxygen, oxygen-classic, thrive, gutenberg, classic. Read-only
  • get-builder-content, Pro: Bricks elements; Divi/WPBakery/Avada shortcodes, Thrive HTML, oxygen-classic JSON (last four with paths); Beaver Builder/Breakdance/Oxygen nodes. Or scope design_system|templates|catalog (element: its controls) for builder bricks|breakdance|oxygen. Read-only
  • update-builder-content, Pro: Bricks JSON; Divi/WPBakery/Avada shortcodes, Thrive HTML; oxygen-classic/Beaver Builder/Breakdance/Oxygen JSON tree. All but Bricks/Divi: operation update (path, attrs, text), add (to, index, element), remove (path), move (path, to, index); to “” = top. scope design_system (bricks|breakdance): add/update/remove at classes|variables|palettes/[/]; expected_hash: read’s hashes.. Undo: rollback-operation

Theme structure (sidebars, shortcodes) (8 tools)

  • create-sidebar-widget: Add a classic widget (registered id_base, incl. block) to a sidebar at a 0-based position, sanitized by its update(). Undoable
  • delete-sidebar-widget: Delete a classic widget instance. Undoable
  • list-shortcodes: List registered shortcode tags ($shortcode_tags): tag name and a short description of its callback where resolvable. Optional search (substring of the tag name) narrows the result
  • list-sidebar-widgets: List the widgets assigned to a single sidebar (by sidebar_id): widget id and display name, from wp_get_sidebars_widgets() resolved against the registered widgets
  • list-sidebars: List the sidebars/widget areas registered via register_sidebar(): id, name, description
  • move-sidebar-widget: Move a classic widget to a sidebar or wp_inactive_widgets at a 0-based position. Undoable
  • render-shortcode: Render a shortcode string (e.g. “[gallery ids="1,2"]”) via do_shortcode() and return the HTML. Only invokes registered shortcode tags; input without an opening “[” is refused
  • update-sidebar-widget: Merge settings into a classic widget (id like text-2) via its update(). Undoable

Content export & import (3 tools)

  • export-content: WXR export via core export_wp(), narrowed by content, author, start_date, end_date, status, to a protected uploads dir; returns path, size, count. Once per PHP process (core limit). mirror:true instead writes builder pages as stable JSON for git (post_id, or all 50 per page)
  • import-content: Import a WXR file as new posts (title, content, status, post_type, postmeta). Off by default (wpmcp_enable_import filter), needs confirm:true, not snapshotted (recoverable:false); returns created_post_ids for delete-post. mirror:true + post_id restores that page from its export-content mirror (undoable)
  • list-exports: List the WXR export files previously generated by export-content: file name, size in bytes and created timestamp for each

Async backups (7 tools)

  • cancel-backup-job: Cancel a queued backup job: unschedule its WP-Cron event and mark it canceled. Errors if the job is unknown or no longer queued (running or terminal)
  • delete-backup-archive: Delete a site-backup archive by job id or path and report bytes freed. The job record stays, flagged deleted, so history never points at a missing file. Paths outside the site-backup directory are refused
  • get-backup-manifest: Read a completed site-backup archive’s manifest by job id or path: origin site_url/home_url, table prefix, multisite, WordPress/PHP/plugin versions, scope, per-table row counts, BLOB tables and file count. Read-only; paths outside the site-backup dir are refused
  • get-backup-status: Return a backup job’s current record (status: queued/running/completed/failed/canceled, result artifact reference or error, timestamps) by job id
  • list-backup-jobs: List backup jobs, newest first, optionally by status (queued/running/completed/failed/canceled), plus schedules (next run, overdue, last run)
  • restore-site-backup: Restore this site in place from a site-backup archive (job_id, or a path in the site-backup dir). dry_run (default true) reports format_version, scope (all/database), prefix, multisite, WP downgrade, BLOB tables and a full db.sql parse (truncated dumps refused). dry_run=false takes a database safety archive first (job id returned; no restore if it fails), holds maintenance mode, imports each statement and rolls back on failure. preserve_session (default true) keeps the caller signed in; include_files (default false, scope all) swaps in staged wp-content
  • trigger-backup: Queue a WP-Cron backup job; returns its job id. type=full: portable site archive (zip: full SQL dump, wp-content, origin manifest) to restore or migrate; database/files/uploads narrow it; content: WXR export. every=daily [HH:MM], weekly [mon-sun] [HH:MM] (site time) or off schedules that type instead; each run prunes its archives past keep (default 7), never manual ones or after a failed run

Migration: find & replace, URL rewrite, site push (4 tools)

  • find-replace-content: Serialization-safe find and replace in post content, titles, excerpts or meta. dry_run (default) previews; apply returns a rollback-session id; over 10 posts needs confirm:true
  • push-site-archive: Push a site-backup archive (job_id or path; scope all or database) to another wpmcp site at target_url as its admin (target_user + target_app_password, or target_token). dry_run (default) only checks the target; dry_run:false + confirm:true uploads resumable chunks for max_seconds (call again to continue); apply:true has the target restore it (safety archive first) and rewrite URLs. Needs the outgoing-migration opt-in here, incoming there
  • receive-site-archive: Target side of push-site-archive. action start (sha256, bytes, manifest; checked before upload, resumes), chunk (upload_id, offset, base64 data), status, apply (verify, restore with a safety archive, rewrite URLs; dry_run default, confirm:true applies). Needs the incoming-migration opt-in
  • rewrite-site-urls: Rewrite one site URL to another across the database (options, postmeta, posts, termmeta, usermeta, comments), serialization-aware for plain, JSON-escaped, percent-encoded and scheme-relative forms; object values are refused and reported. dry_run (default) reports per-table counts; dry_run:false + confirm:true takes a database safety archive first or refuses, returning its job_id for restore-site-backup. Skips wpmcp_db_protected_tables (usermeta by default); never rewrites GUIDs

Local to live sync (change sets) (3 tools)

  • apply-change-set: Apply a change set (change_set from get-change-set raw=true, or path) here; dry_run defaults to true. Only selected objects are written, snapshot-first; media, terms and templates are only added; objects changed here since the base are refused unless keyed in force; deletions never apply. rollback-session undoes it
  • build-change-set: Build a local-live sync change set (objects, media bytes, terms, templates, global classes, base revisions) from a ledger marker (session_id, operation_id or since_id) and/or object refs (post:ID, option:theme_mods_X, term:TAX:SLUG) as a JSON artifact in the site-backup dir. dry_run only lists it
  • get-change-set: Inspect a change-set artifact before it is applied: origin, objects, dependencies, exclusions, truncation. include_objects=true adds full data; raw=true returns the artifact to pass to apply-change-set on the target. Read-only; site-backup dir only

Database (6 tools)

  • delete-rows: Delete rows matching a mandatory equality WHERE. Needs confirm:true and the wpmcp_enable_db_writes filter; refuses protected tables. Undo: rollback-operation (rows reinserted with their ids) if the table has a primary key and the WHERE fits the before-image cap, else recoverable:false with the before-image in the write audit log
  • describe-table: Return the columns, types and keys of a database table
  • insert-row: Insert a row into a table via $wpdb->insert() (parameterized). Refuses protected tables. Disabled by default (wpmcp_enable_db_writes filter)
  • list-tables: List database tables with estimated row counts and sizes
  • query: Run a read-only SQL query (SELECT/SHOW/DESCRIBE/EXPLAIN/WITH). Writes, DDL, stacked statements and file-access SQL are rejected before running. Reads of users/usermeta tables are blocked (wpmcp_db_allow_user_table_reads filter opts in, with secrets masked). Results are capped
  • update-rows: Update rows matching a mandatory equality WHERE via $wpdb->update(). Needs confirm:true and the wpmcp_enable_db_writes filter; refuses protected tables. Undo: rollback-operation if the table has a primary key and the WHERE fits the before-image cap, else recoverable:false with the before-image in the write audit log

Filesystem (6 tools)

  • delete-file: Delete a file in the WordPress install. Needs confirm:true. Backs up the file (restore recovers it). Refuses wp-config.php/.htaccess. Off by default (wpmcp_enable_fs_writes filter); needs edit_files, honors DISALLOW_FILE_EDIT
  • edit-file: Replace an exact string in a file (must match once unless replace_all). Backs up the original (restore recovers it). Refuses wp-config.php/.htaccess. Off by default (wpmcp_enable_fs_writes filter); needs edit_files, honors DISALLOW_FILE_EDIT. PHP is checked and reverted as in write-file
  • list-directory: List entries (files/dirs with size and mtime) of a directory inside the WordPress install. Optional bounded recursive listing
  • read-file: Read a file inside the WordPress install (core, plugins, themes, uploads); the path cannot leave it
  • search-files: Search file contents for a substring across a directory tree inside the WordPress install. Filterable by extension; results are capped
  • write-file: Create or overwrite a file in the WordPress install. Backs up an existing file (restore recovers it). Refuses wp-config.php/.htaccess. Off by default (wpmcp_enable_fs_writes filter); needs edit_files, honors DISALLOW_FILE_EDIT. PHP must parse; a fatal on the front end or admin after saving reverts it; if that check cannot run, refused unless unchecked:true

Scanners & cache (4 tools)

  • analyze-performance: Scan server config, WordPress internals (database size, autoloaded options, cron backlog, object cache, OPcache, plugin count) and a page (frontpage, url or post_id) for performance bottlenecks. Returns a scored report with ranked fixes
  • clear-cache: Flush caches: object cache, all transients, OPcache when enabled and any detected page-cache plugin through its own API. Returns what each layer cleared or lacked. Idempotent; not snapshotted (a cache has no before-image)
  • get-cache-status: Report active caching layers: the object cache backend (external or internal), OPcache and any page-cache plugin (WP Rocket, W3 Total Cache, WP Super Cache, LiteSpeed Cache, WP Fastest Cache) detected by its functions or constants
  • scan-security: Scan this site for malware (deep=true: whole tree), core file integrity, hardening gaps, outdated software and known vulnerabilities (vulnerabilities=true; sends slugs and versions to wpvulnerability.net). Returns a 0-100 score, A-F grade and ranked fixes

Diagnostics & transients (5 tools)

  • delete-transient: Delete one named transient (cache data, not snapshotted) or run cleanup: expired_transients, revisions (beyond keep newest per post, default 5), auto_drafts (7+ days old), trash (older than days, default 0), spam_comments, orphaned_meta. Dry run by default (counts, bytes, sample ids); dry_run:false+confirm applies up to 200 items/10s, returning a cursor to resume. rollback-operation undoes all but transients; attachments and parents are skipped
  • get-debug-config: Debug constants (WP_DEBUG, WP_DEBUG_LOG, WP_DEBUG_DISPLAY, SCRIPT_DEBUG, SAVEQUERIES) and, when logging is on, the debug.log path. No secrets
  • get-debug-log: Bounded tail (max 200 lines / 64KB) of the debug log, by default WP_CONTENT_DIR/debug.log or the WP_DEBUG_LOG path. A path argument is confined to WP_CONTENT_DIR
  • get-site-health: Run Site Health tests, plugin-added ones included, as plain text. Async tests past timeout seconds (default 10, max 30) report not completed. cached: last full run. mail_test: how mail is sent (no secrets); your or admin email + confirm sends a test (manage_options, rate limited)
  • list-transients: List transients (name, expiry) with an optional search substring; limit default 50, max 500

Advanced Custom Fields (3 tools)

  • get-fields: Read a post’s ACF field values, keyed by field name, via get_fields()
  • list-field-groups: List registered ACF (Advanced Custom Fields) field groups: key, title, flattened location rules and whether each is active
  • update-fields: Set one or more ACF field values on a post via update_field(). Snapshotted as a post; rollback-operation restores the prior values exactly. Disabled by default (site must opt in via the wpmcp_enable_acf_write filter)

Integrations (read/write dispatcher pairs) (26 tools)

  • acf-read: Read op on Advanced Custom Fields (field groups, post types, taxonomies, options and field values): operation + its args (list-operations lists each op’s schema)
  • acf-write: Write op on Advanced Custom Fields (field groups, post types, taxonomies, options and field values): operation + args per its schema (list-operations on the read half). Snapshot-first if possible (undo: rollback-operation); destructive ops need confirm:true
  • contactform7-read: Read op on Contact Form 7 (forms, markup, mail templates, Flamingo entries): operation + its args (list-operations lists each op’s schema)
  • contactform7-write: Write op on Contact Form 7 (forms, markup, mail templates, Flamingo entries): operation + args per its schema (list-operations on the read half). Snapshot-first if possible (undo: rollback-operation); destructive ops need confirm:true
  • forminator-read: Read op on Forminator (forms, their field definitions, and submissions): operation + its args (list-operations lists each op’s schema)
  • forminator-write: Write op on Forminator (forms, their field definitions, and submissions): operation + args per its schema (list-operations on the read half). Snapshot-first if possible (undo: rollback-operation); destructive ops need confirm:true
  • give-read: Read op on GiveWP (donation forms, pricing, goals, and totals): operation + its args (list-operations lists each op’s schema)
  • give-write: Write op on GiveWP (donation forms, pricing, goals, and totals): operation + args per its schema (list-operations on the read half). Snapshot-first if possible (undo: rollback-operation); destructive ops need confirm:true
  • gravitytables-read: Read op on Gravity Tables (data tables built from Gravity Forms entries): operation + its args (list-operations lists each op’s schema)
  • gravitytables-write: Write op on Gravity Tables (data tables built from Gravity Forms entries): operation + args per its schema (list-operations on the read half). Snapshot-first if possible (undo: rollback-operation); destructive ops need confirm:true
  • mec-read: Read op on Modern Events Calendar (events and their schedules): operation + its args (list-operations lists each op’s schema)
  • mec-write: Write op on Modern Events Calendar (events and their schedules): operation + args per its schema (list-operations on the read half). Snapshot-first if possible (undo: rollback-operation); destructive ops need confirm:true
  • metabox-read: Read op on Meta Box (registered meta boxes and per-post custom field values): operation + its args (list-operations lists each op’s schema)
  • metabox-write: Write op on Meta Box (registered meta boxes and per-post custom field values): operation + args per its schema (list-operations on the read half). Snapshot-first if possible (undo: rollback-operation); destructive ops need confirm:true
  • metform-read: Read op on MetForm (forms, their Elementor field definitions, and entries): operation + its args (list-operations lists each op’s schema)
  • metform-write: Write op on MetForm (forms, their Elementor field definitions, and entries): operation + args per its schema (list-operations on the read half). Snapshot-first if possible (undo: rollback-operation); destructive ops need confirm:true
  • plugin-data-read: Read op on the plugin-data integration: operation + its args (list-operations lists each op’s schema)
  • plugin-data-write: Write op on the plugin-data integration: operation + args per its schema (list-operations on the read half). Snapshot-first if possible (undo: rollback-operation); destructive ops need confirm:true
  • pmpro-read: Read op on Paid Memberships Pro (membership levels, pricing, and member counts): operation + its args (list-operations lists each op’s schema)
  • pmpro-write: Write op on Paid Memberships Pro (membership levels, pricing, and member counts): operation + args per its schema (list-operations on the read half). Snapshot-first if possible (undo: rollback-operation); destructive ops need confirm:true
  • sureforms-read: Read op on SureForms (forms, their block field definitions, and entries): operation + its args (list-operations lists each op’s schema)
  • sureforms-write: Write op on SureForms (forms, their block field definitions, and entries): operation + args per its schema (list-operations on the read half). Snapshot-first if possible (undo: rollback-operation); destructive ops need confirm:true
  • tec-read: Read op on The Events Calendar (events and their schedules): operation + its args (list-operations lists each op’s schema)
  • tec-write: Write op on The Events Calendar (events and their schedules): operation + args per its schema (list-operations on the read half). Snapshot-first if possible (undo: rollback-operation); destructive ops need confirm:true
  • theme-read: Read op on the active theme (context, supports, theme mods), Redirection, and backup, security, analytics and cache status: operation + its args (list-operations lists each op’s schema)
  • theme-write: Write op on the active theme (context, supports, theme mods), Redirection, and backup, security, analytics and cache status: operation + args per its schema (list-operations on the read half). Snapshot-first if possible (undo: rollback-operation); destructive ops need confirm:true

Forms adapter pack (10 tools, 10 Pro)

  • fluentforms-read, Pro: Read op on Fluent Forms (forms, fields, notifications, entries): operation + its args (list-operations lists each op’s schema)
  • fluentforms-write, Pro: Write op on Fluent Forms (forms, fields, notifications, entries): operation + args per its schema (list-operations on the read half). Snapshot-first if possible (undo: rollback-operation); destructive ops need confirm:true
  • formidable-read, Pro: Read op on Formidable Forms (forms, fields, notifications, and entries): operation + its args (list-operations lists each op’s schema)
  • formidable-write, Pro: Write op on Formidable Forms (forms, fields, notifications, and entries): operation + args per its schema (list-operations on the read half). Snapshot-first if possible (undo: rollback-operation); destructive ops need confirm:true
  • gravityforms-read, Pro: Read op on Gravity Forms (forms, fields, notifications, entries, entry notes): operation + its args (list-operations lists each op’s schema)
  • gravityforms-write, Pro: Write op on Gravity Forms (forms, fields, notifications, entries, entry notes): operation + args per its schema (list-operations on the read half). Snapshot-first if possible (undo: rollback-operation); destructive ops need confirm:true
  • ninjaforms-read, Pro: Read op on Ninja Forms (forms, fields, notifications, submissions): operation + its args (list-operations lists each op’s schema)
  • ninjaforms-write, Pro: Write op on Ninja Forms (forms, fields, notifications, submissions): operation + args per its schema (list-operations on the read half). Snapshot-first if possible (undo: rollback-operation); destructive ops need confirm:true
  • wpforms-read, Pro: Read op on WPForms (forms, fields, notifications; entries with entry storage): operation + its args (list-operations lists each op’s schema)
  • wpforms-write, Pro: Write op on WPForms (forms, fields, notifications; entries with entry storage): operation + args per its schema (list-operations on the read half). Snapshot-first if possible (undo: rollback-operation); destructive ops need confirm:true

Internationalization (Polylang) (4 tools)

  • get-post-translations: Read a post’s translations (the translated post id and title, keyed by language code) via the active multilingual plugin (Polylang or WPML)
  • link-post-translations: Link posts as translations of one another from {language, post_id} pairs via Polylang or WPML. Only the primary (first) post is snapshotted, so rollback restores that post, not the others
  • list-languages: List the site’s languages (code, name, which is default) via the active multilingual plugin (Polylang or WPML)
  • set-post-language: Assign a post to a language (by code) via the active multilingual plugin (Polylang or WPML). Snapshotted as a post (a Polylang language is a term); rollback-operation restores the prior language assignment exactly

Multisite (4 tools)

  • get-network-info, requires an optional companion plugin or multisite: Report this network’s id, name, domain, total site count, and main site id, via get_network()/get_main_site_id()
  • get-site-details, requires an optional companion plugin or multisite: Report a single network site’s details (blog_id, url, name, last_updated) by blog_id, via get_site()/get_blog_details(). Returns an error for an unrecognized blog_id
  • is-multisite: Whether this install is a multisite network. Always registered, even on single-site, so network status can be checked before the other multisite tools
  • list-network-sites, requires an optional companion plugin or multisite: List sites on the network (blog_id, url, name, last_updated) via get_sites(), with optional limit (default 50) and offset for pagination. limit is capped at 500

Analytics & Search Console (5 tools)

  • get-analytics-connection-status: Whether an analytics provider (Google Site Kit or configured credentials) is active and appears connected. Always registered, so it can be checked before the other analytics tools
  • get-analytics-summary: Read-only sessions/users/pageviews summary over a date range (Y-m-d; default the 28 days ending yesterday) from the connected analytics provider. Errors when no provider is connected
  • get-search-console-queries: Read-only list of top search queries by clicks over a date range (Y-m-d; default the 28 days ending yesterday) from the connected Search Console provider; limit default 10, max 100. Errors when no provider is connected
  • get-search-console-summary: Read-only clicks/impressions/ctr/position summary over a date range (Y-m-d; default the 28 days ending yesterday) from the connected Search Console provider. Errors when no provider is connected
  • get-top-pages: Read-only list of top pages by pageviews over a date range (Y-m-d; default the 28 days ending yesterday) from the connected analytics provider; limit default 10, max 100. Errors when no provider is connected

Cron (4 tools)

  • list-cron-events: List the scheduled WP-Cron events (hook, next-run timestamp, recurrence/schedule, interval in seconds, callback args) from the cron array, plus the available schedules from wp_get_schedules(). Optional hook filter
  • run-event: Fire a scheduled cron hook now via do_action(), for debugging. Off until the wpmcp_enable_run_cron_event filter opts in; fires only hooks present in the cron array, always with their stored args. Not snapshotted: firing a hook is irreversible
  • schedule-event: Schedule a recurring (recurrence checked against wp_get_schedules()) or single event. Refuses core-critical hooks (wp_version_check, wp_update_plugins/themes, wp_scheduled_delete, delete_expired_transients, wp_privacy_delete_old_export_files). Snapshots the cron option; rollback-operation restores it
  • unschedule-event: Unschedule one occurrence (wp_unschedule_event, given timestamp and matching args) or every event for a hook (wp_clear_scheduled_hook). Core hooks included, made safe by snapshotting the cron option: rollback-operation restores it

Maintenance mode (3 tools)

  • disable-maintenance: Turn maintenance mode off: sets enabled=false on the wpmcp_maintenance option (message and retry_after are preserved for a later re-enable). Snapshotted; rollback-operation restores the prior state
  • enable-maintenance: Turn maintenance mode on (wpmcp_maintenance option: enabled, message, retry_after seconds). Logged-out visitors and users without manage_options get a 503 with the message until disabled. Snapshotted; rollback-operation restores the prior state
  • get-maintenance-status: Whether maintenance mode is on and, if so, its message and Retry-After seconds

Site context (3 tools)

  • get-page-snapshot: One-call page digest from stored post_content: structure counts, outline, media and link inventory, builder detection, SEO-lite signals (content_coverage flags builder page gaps). Heavy sections (global_tokens, responsive_overrides) opt-in via sections. Size-capped
  • get-rendered-html: Visitor-view HTML of a site page (post_id or url/path), chunked
  • get-site-context: Orientation payload: site name, URL, tagline, WordPress and PHP versions, theme, active plugins, public post types with counts, taxonomies, user count, locale, timezone, multisite and active integrations. No admin email

REST passthrough (2 tools)

  • call-rest: Run an internal REST request on any local route; returns status and body. The route’s permission_callback runs as the current user, so access cannot widen. GET/HEAD always run; writes need the wpmcp_enable_rest_writes filter (default off) and confirm:true, and are not snapshotted (recoverable:false)
  • list-rest-routes: List routes on this site’s REST server (core and plugin namespaces): path, methods and a short args summary. Optional namespace and search filter by substring; limit caps rows (default 50, max 200). Read-only: never executes a route

Onboarding & connection info (2 tools)

  • get-connection-info: How to connect an MCP client here: the MCP endpoint URL and ready-to-paste snippets for Claude Code, Cursor and Claude Desktop, each using an Application Password placeholder. Never returns a real credential. Read-only
  • list-tool-catalog: List every wpmcp ability here grouped by domain, with tier (free/pro), operation, required capability and read-only/destructive hints, plus per-domain counts. Optional domain and tier filters. Read-only

Tool dispatch (compact mode) (3 tools)

  • call-tool: Invoke any wpmcp tool by name with an arguments object (reaches tools compact mode hides). The target’s own checks (capability, governance, identity scope, license, rate limit, input validation, snapshot/rollback) apply as if called directly, never widening access. Refuses non-wpmcp tools and the meta-tools
  • get-tool-schema: Read one wpmcp tool’s full contract by name: exact input schema, complete description, MCP annotations and domain/operation/tier. Read-only; list-tools finds names
  • list-tools: List every tool this install registers: name, summary, domain, operation and tier, by name. Optional domain filter; full:true adds full descriptions and MCP annotations; schemas are behind get-tool-schema. Read-only. In compact mode, the discovery entry point for unlisted tools

Governance & scoped identities (6 tools)

  • create-identity: Create or overwrite (by name) a scoped identity that, once active (wpmcp_current_identity filter), narrows abilities beyond capability and Governance. Optional domains/operations/abilities allowlists, mode (allow default, or deny), exposure (full/compact, overrides site-wide mode), allowed_ips (IPs/CIDRs; other clients refused)
  • delete-identity: Delete a scoped identity by name. Returns an error if no identity with that name exists
  • get-governance-settings: Return the stored governance toggle maps (ability, domain, operation) and site_wide mode: explicit enable/disable decisions layered on top of the wpmcp_{ability,domain,operation}_enabled filters
  • list-governance-audit-log: List governance-decision audit log entries (ability, active identity or “none”, allowed/denied, timestamp, source: mcp/rest/server/cli/php), newest first. Optional limit (default 20) and source filter
  • list-identities: List every registered scoped identity
  • update-governance-settings: Batch-update stored governance toggles by ability, domain and operation, e.g. {ability: {“wpmcp/delete-post”: false}, domain: {“database”: false}}. Invalid entries are skipped and reported; only empty input throws. site_wide audits ability calls outside MCP; enforce also refuses abilities toggled off by name

Project memory (3 tools, 3 Pro)

  • memory-propose, Pro: Propose one durable memory entry, stored PENDING and inert (not injected into sessions; severity=block not enforced) until an admin publishes it in wp-admin. severity=block needs at least one target (tool:, post_id:, post_type:); once published, matching calls are refused (enforced, not advisory)
  • memory-recall, Pro: Read this site’s APPROVED project memory: published facts, conventions, guardrails, session summaries and enforced block rules. Pending proposals are never returned (only their count), so unapproved suggestions never read back as policy
  • memory-save-summary, Pro: Record what a session changed as a pending session-summary entry. Facts come from that session’s snapshot rows, not the supplied prose or any LLM, so it cannot over- or under-claim. Optional takeaways are filed as separate pending proposals

Skills (2 tools)

  • get-skill: Load one skill’s full instructions by slug (from list-skills), returned exactly as written. Unknown slugs return a structured error listing the installed slugs
  • list-skills: List installed agent skills (versioned markdown playbooks): slug, name, description, version, tags. Bodies via get-skill. Skills whose required tools are missing are hidden unless include_unavailable is set

Ability bridge (other plugins’ abilities) (3 tools)

  • execute-site-ability: Invoke one third-party ability by name with an arguments object. Its own permission callback always runs, plus wpmcp governance, identity scope and rate limiting. Results are reversible:false (bridged writes are outside wpmcp rollback). Refuses wpmcp’s own abilities; needs the site bridge opt-in (default off)
  • get-site-ability: Read one third-party ability’s full contract by name: description, input and output schemas, meta where provided, and owning plugin. Refuses wpmcp’s own abilities (use get-tool-schema). Read-only; list-site-abilities finds names
  • list-site-abilities: Abilities OTHER plugins register via the Abilities API: name, summary, owning plugin, whether an input schema exists and reversible:false (bridged results are outside wpmcp rollback). Optional plugin filter. Needs the ability bridge opt-in (default off)

Gateway credentials (3 tools)

  • gateway-provision: Provision or rotate the site gateway credential. Returns client_id, client_secret and refresh_token once; the previous credential dies immediately. Carries the calling user’s capabilities. Requires confirm: true
  • gateway-revoke: Revoke the site gateway credential and every token bound to it. Local, idempotent. Requires confirm: true
  • gateway-status: Whether the site gateway credential exists, its client_id and whether OAuth is enabled. Never returns secrets

WP MCP Cloud: sync & marketplace (12 tools, 12 Pro)

  • cloud-apply-settings, Pro: Apply a posture: a cloud-sync-settings map, or (settings omitted) the last pushed one. Re-filtered to the allowlist; toggles and identities merge, scope fields only; never changes the MCP exposure switch or disables rollback-operation. Paid. Each write snapshotted; applied[i] pairs with operation_ids[i]; matching options listed as unchanged
  • cloud-connect, Pro: Connect this site to WP MCP Cloud: store the cloud url + api key and verify them by fetching the account, which is returned. gateway_consent (default false) permits a gateway credential upload; false withdraws it and kills any the cloud holds
  • cloud-gateway-provision, Pro: Mint the gateway credential bound to a scoped identity (MCP connection, that allowlist only) and upload it. Needs consent=true; replace=true kills a live one. Upload needs cloud-connect gateway_consent. Secrets shown ONCE. Revoke: gateway-revoke
  • cloud-gateway-status, Pro: Gateway credential’s bound identity, upload and consent state. No secrets
  • cloud-list-assets, Pro: List the assets (widget/block specs) in this site’s WP MCP Cloud account
  • cloud-marketplace-browse, Pro: Browse WP MCP Cloud marketplace widget and block specs, optionally by type and search
  • cloud-marketplace-install, Pro: Install a WP MCP Cloud marketplace listing by slug: validated like validate-widget-spec / validate-block-spec, template run through wp_kses_post, lands INACTIVE until set-widget-status / set-block-status. Refuses a name colliding with a local spec
  • cloud-pull-assets, Pro: Pull this site’s WP MCP Cloud builder assets and recreate them as local custom widget/block specs (each validated first; refusals listed under skipped with a reason)
  • cloud-push-assets, Pro: Push this site’s custom widget and block specs up to WP MCP Cloud (backup + reuse across sites). Optional type filter (widget|block)
  • cloud-push-settings, Pro: Push the cloud-sync-settings posture plus identity scopes (no secrets) to WP MCP Cloud for cloud-apply-settings elsewhere. Paid. Changes nothing here
  • cloud-status, Pro: Whether this site is connected to WP MCP Cloud, and where
  • cloud-sync-settings, Pro: Preview what would sync to WP MCP Cloud: governance toggles, MCP exposure switch, tool-exposure mode, skills switch. Never secrets or code-level gates (db writes, php exec, cli allowlist)

PHP snippet validation (1 tool)

  • validate-php-snippet: Statically check a PHP snippet without running it: syntax validity (error message and line) and severity-tagged findings (eval, exec, shell_exec, backticks, obfuscation decoders, request-driven input, outbound HTTP)

PHP snippet store (6 tools)

  • create-php-snippet: Store a named PHP snippet, inactive (not Elementor custom code: see create-code-snippet). Never executed; syntax errors or critical static findings refuse it (advisory, not a security boundary). Activation is separate and gated. Snapshot-first, reversible per snippet
  • deactivate-php-snippet: Deactivate a stored PHP snippet by id (reverse of activate-php-snippet). Not gated on the PHP execution opt-in, so an active snippet can always be revoked. Snapshot-first; never executes anything
  • delete-php-snippet: Delete a stored PHP snippet by id (not an Elementor custom-code post: see delete-code-snippet). Snapshot-first, reversible per snippet (restored inactive); never executes anything
  • get-php-snippet: Fetch one STORED PHP SNIPPET by id: code, status and last static-check report (advisory, not proof of safety). Not Elementor custom code (see get-code-snippet)
  • list-php-snippets: List STORED PHP SNIPPETS as summaries (id, name, status, static-check flag, timestamps) without code. Not Elementor custom code (see list-code-snippets)
  • update-php-snippet: Update a stored PHP snippet’s name and/or code. New code is re-checked statically (advisory, not a security boundary) and never executed; a code change resets it to inactive. Snapshot-first, reversible per snippet

Guarded WP-CLI (1 tool, 1 Pro)

  • run-wp-cli, Pro: Run an allowlisted wp-cli subcommand (e.g. “core version”); returns stdout, stderr, exit code. Off by default (WPMCP_ALLOW_WP_CLI or wpmcp_allow_wp_cli); refused on production without a separate override; only subcommands on the wpmcp_wp_cli_allowlist filter run; shell metacharacters are rejected first

Async WP-CLI jobs (4 tools, 4 Pro)

  • cancel-cli-job, Pro: Cancel a queued background CLI job: unschedule its WP-Cron event and mark it canceled so it never runs. Errors if the job is unknown or no longer queued (running or terminal)
  • dispatch-cli-job, Pro: Queue an allowlisted wp-cli command as a background job; returns its id (poll get-cli-job). Same gates as run-wp-cli (off by default: WPMCP_ALLOW_WP_CLI or wpmcp_allow_wp_cli; production needs a separate override; allowlisted subcommands and flags; no shell metacharacters), re-checked at run time, so closing them stops queued jobs. timeout seconds (default 300, max 900). Refused while too many jobs are queued or running
  • get-cli-job, Pro: Return a background CLI job by id: status (queued/running/completed/failed/canceled), the dispatched command and, once run, size-capped stdout/stderr, exit code and timed-out flag, or the error that stopped it
  • list-cli-jobs, Pro: List background CLI jobs, newest first, with an optional status filter (queued/running/completed/failed/canceled)

Guarded PHP execution (2 tools, 2 Pro)

  • activate-php-snippet, Pro: Activate a STORED PHP SNIPPET by id (not Elementor custom code): flips the status flag, never executes it. Refused unless PHP execution is enabled (WPMCP_ALLOW_PHP_EXEC or wpmcp_allow_php_exec, default off) and the environment permits it (as run-php-snippet). Every attempt audited. Snapshot-first; rollback restores it inactive
  • run-php-snippet, Pro: Run a guarded arbitrary PHP snippet; returns its return value, output and any thrown error. REMOTE CODE EXECUTION: off by default (WPMCP_ALLOW_PHP_EXEC or wpmcp_allow_php_exec); refused on production or an unrecognized environment unless WPMCP_ALLOW_PHP_EXEC_ON_PRODUCTION is also set; the static validator rejects flagged snippets first (a speed-bump, not a security boundary). Not snapshotted; cannot be undone.

Misc (9 tools, 2 Pro)

  • create-comment: Comment on a post as you. status: approved (moderate_comments) or unapproved. Reversible
  • find-unused-media: Attachments nothing references (featured image, site logo/icon, content, builder data, post/term meta, options) with size on disk and checks run. Pass next_cursor as cursor until done; unattached:true lists parent-0 ones apart
  • get-preview-link: Preview URL for a draft, pending or scheduled post you can edit
  • incident-response: list-app-passwords (no secrets); revoke-app-password (uuid or all); end-sessions (user_id or all); rotate-salts (wp-config.php, backup kept; refused if unwritable or defined elsewhere); reinstall-core-files (files scan-security flags, from the checksum-verified official package, never wp-content; undo: rollback-operation). Writes need confirm:true. Cannot be undone: revoke, end-sessions, rotate-salts (logs out everyone, your cookie session too, not app passwords). Your session and app password stay unless include_current. Other users take edit_users
  • install-package-from-zip, Pro: Install or replace a plugin/theme from a Media Library ZIP. Off by default (wpmcp_enable_zip_install); needs confirm:true and its sha256. Rollbackable
  • manage-updates: Update WordPress core or set plugin/theme auto-updates. type=core applies the offered core update (or pending DB upgrade); needs confirm:true and a full backup (trigger-backup type=full) under 1h old; refuses if expected_version differs from the offer. A core update cannot be snapshot-rolled-back; restore that backup to revert. type=plugin|theme + item + enabled sets auto-update (undoable)
  • optimize-media, Pro: Recompress JPEG/PNG at quality (82), cap the full size at max_edge px and write webp/avif copies of it and each size with the server image editor. No media_id: batches by cursor, or background:true queues a job under one session_id (job_id: progress; +cancel: stop). dry_run estimates. Defers to an optimizer plugin unless force. Undo: rollback-operation. Settings: wpmcp_optimize_uploads, wpmcp_serve_modern_images
  • reply-to-comment: Reply to comment id; as create-comment
  • search-themes: Search wordpress.org themes; filters as in search-plugins

Block Suite (2 tools, 2 Pro)

  • block-suites-read, Pro: Read op on block suite blocks and patterns: operation + its args (list-operations lists each op’s schema)
  • block-suites-write, Pro: Write op on block suite blocks and patterns: operation + args per its schema (list-operations on the read half). Snapshot-first if possible (undo: rollback-operation); destructive ops need confirm:true

Bundle (2 tools)

  • export-bundle: Export PHP snippets, plus custom block/widget specs where present, as a checksummed JSON bundle for import-bundle; types and ids filter
  • import-bundle: Import an export-bundle: items are re-validated and created inactive; name clashes skip unless on_conflict=rename. Undo with rollback-session

Site Template (2 tools)

  • site-templates-read: List the block theme’s templates and parts (source theme/custom, area, customized) or wp_navigation menus; entity plus id reads one as parsed blocks and content_hash. global_styles: theme/user/merged theme.json, variations, fonts (id: dot path narrows). Reports a classic theme
  • site-templates-write: Edit a block theme template or part (id: slug or theme//slug) or wp_navigation menu (id). save: content or blocks, customizing over the theme file; add_block (content: one block)/update_block/remove_block: by path with expected_hash; revert: delete the customization. global_styles (no id): save attrs {dot.path: value|null} to user styles, preset entries by slug; variation: title; revert: reset. Snapshot-first

Free vs Pro

The split today

The plugin registers 407 abilities: 274 free and 133 Pro. Everything is GPL-2.0 licensed either way, the Pro tier gates which abilities register on your site, not the license of the code. The Tools Reference lists every one, generated from the plugin source.

The free tier includes the entire safety net and the broad site-management surface:

  • The full safety engine: snapshot, apply, verify, rollback (both operation- and session-level), plus the wp-admin wpmcp history and one-click restore screen.
  • All content, taxonomy, site search, Gutenberg block, and surgical block-editing tools, and the build-page composite.
  • Media (including stock-image search/import, sanitized SVG upload and uploads from raw bytes), settings, users, comments, navigation menus, plugins and themes, plus the theme builder for headers, footers and 404 pages.
  • The core WooCommerce surface: products, variations, bulk updates, a two-step product import (plan-product-import, then apply-product-import), orders, notes, coupons, tax rates and sales reports.
  • SEO metadata through Yoast SEO, Rank Math, The SEO Framework, SureRank, SEOPress or Slim SEO, redirects (including the Redirection plugin’s own redirects) and broken-link checks, internal-link analysis, performance and security scanners.
  • Backups with restore, site-wide find and replace, site URL rewrites, site-to-site archive push, and local-to-live change sets.
  • The PHP snippet store (snippets are always stored inactive), the rendered page HTML reader, and Elementor CSS regeneration.
  • Governance, scoped identities, the audit log, gateway credentials, the ability bridge, onboarding/connection tools, multisite reads, analytics/Search Console (through Site Kit), cron, maintenance mode, REST passthrough, and compact tool dispatch.
  • The integration dispatcher pairs: ACF (values, plus field groups, ACF post types and ACF taxonomies), Meta Box, JetEngine and Pods fields, TranslatePress strings, BuddyPress groups, activity and profile fields (plugin-data-read / plugin-data-write), Contact Form 7 with Flamingo entries, Forminator, SureForms and MetForm, events, donations and memberships, Polylang and WPML, and the theme pair (theme-read / theme-write) with the Astra and Kadence theme settings packs, child theme scaffolding, and read-only status for UpdraftPlus, Duplicator, Solid Security, MonsterInsights and W3 Total Cache plus the W3 Total Cache purge.
  • Operation history: the last 20 operations.

The 133 Pro abilities are:

  • Elementor deep editing (33): widget, element, template, theme-template, popup, dynamic-tag, global color and typography, custom CSS and code-snippet tools, built on a curated 44-widget catalog with typed, validated settings schemas.
  • Elementor structural suite (8): add-container, update-container, duplicate-element, find-element, reorder-elements, set-element-label, update-page-settings, batch-update.
  • Elementor v4 (12): atomic elements (add-atomic-widget, add-div-block, add-flexbox, update-atomic-widget), global classes (4) and global variables (4).
  • Custom widget builder (9) and custom block builder (8): build Elementor widgets and Gutenberg blocks from a validated data spec.
  • Brand kits (4) and scoped custom CSS and opt-in custom JavaScript (2).
  • Builders (3): detect-builder, get-builder-content and update-builder-content, covering Bricks, Divi, WPBakery, Beaver Builder, Breakdance, Oxygen 6, classic Oxygen, Avada and Thrive Architect.
  • Block suite packs (2): block-suites-read and block-suites-write for Kadence Blocks, GenerateBlocks, Spectra, Otter Blocks and the Blocksy companion blocks, with pattern import from the registry or the WordPress.org Pattern Directory.
  • Content analysis (7): accessibility, SEO and contrast analysis plus fixes such as alt text from context, color contrast and link text.
  • SEO depth (6): term-level SEO, social images and meta, and generated meta tags and JSON-LD schema.
  • SEO data provider (1): set-seo-data-key, which stores your own DataForSEO credentials (encrypted, never returned) for the keyword and backlink ops of analyze-seo.
  • WooCommerce operations catalog (3): woo-ops, woo-read, woo-write: order create and edit, shipping zones and methods, webhooks, payment gateways (secrets masked), review moderation, totals reports, system status and brands, plus the raw catalog of wc/v3 routes. See WooCommerce Tools.
  • Forms adapter pack (10): read/write pairs for WPForms, Gravity Forms, Formidable, Ninja Forms and Fluent Forms: forms, fields, notifications, entries and entry status.
  • WP MCP Cloud (12): settings sync, the widget and block marketplace, and cloud gateway provisioning and status.
  • Project memory (3): memory-recall, memory-propose, memory-save-summary.
  • Guarded execution (7): run-wp-cli, the async WP-CLI jobs (4), run-php-snippet and activate-php-snippet, all additionally default-off; see the safety model.
  • Stock placement (1): insert-stock-image (searching and importing stock images stays free).
  • Media optimization (1): optimize-media, which compresses Media Library JPEG and PNG images, caps dimensions and writes WebP and AVIF copies with the server’s own image editor, over the whole library in the background if asked, with one undo for the run.
  • Package install from ZIP (1): install-package-from-zip (installing from the WordPress.org directory stays free).

Some Pro capabilities are operations inside a free dispatcher pair rather than abilities of their own, so they do not change the counts above: the Essential Addons, Premium Addons and Ultimate Addons suite packs, the GeneratePress and Blocksy theme settings packs, FunnelKit funnel reads and dynamic single, archive and search templates (all on theme-read / theme-write), ACF batch-update-fields, and All in One SEO support in the SEO tools. list-operations on each pair shows which operations this install offers.

Plus, on Pro: unlimited operation history instead of the 20-operation cap.

The Gate

Free/Pro gating lives in WPMCP\Pro\Gate: a single boolean, Gate::is_pro(), which asks the Freemius SDK’s can_use_premium_code() (an active paid licence or trial) when the SDK is loaded, and fails closed to false when the SDK is absent or answers anything other than a strict true (no fatal error either way). It deliberately does not use the __premium_only variant: the Pro code ships in the same tree and is gated at runtime, and Freemius’ free-build processor strips __premium_only call sites, which would corrupt the check in a free zip.

The MCP\Registrar skips registering any ability tagged 'pro' when Gate::is_pro() is false, so Pro tools do not appear in a free-tier site’s tool list. The other concrete difference is history retention. Snapshot_Store::history_limit() has no licence check: it returns 20 unless the public wpmcp_snapshot_history_limit filter says otherwise. The Pro build adds that filter itself, returning unlimited (PHP_INT_MAX) while the licence is active:

public static function limit($default): int
{
    return Gate::is_pro() ? PHP_INT_MAX : (int) $default;
}

If a licence that was active ends, the site’s current history depth is held rather than pruned, and the admin shows a notice so the owner can trim it or choose a depth. A lapse never deletes undo points on its own.

Free tier: 20 operations of history by default, pruned after every write by whole sessions: the most recent run is always kept whole however large it is, and a run that has been dropped is refused by rollback-session with a reason rather than undone in part. Pro: unlimited while the licence is active. See Safety model for the detail.

Buying Pro

Pro is licensed through Freemius (annual plans from $26.99/year for a single site, with lifetime plans alongside). Buy on the pricing page; the licence activates in the plugin and the Pro abilities register from the next request. The free plugin is fully functional without one.

Privacy default: the Freemius opt-in

Bootstrap::config() keeps Freemius’ anonymous_mode off and pins enable_anonymous on, deliberately:

// anonymous_mode would suppress the SDK's stock opt-in screen and decide
// on the user's behalf, so it stays false and the SDK ships its
// default-off connect screen. Nothing is transmitted before the user
// affirmatively opts in.
'anonymous_mode'   => false,
// The Skip link on the connect screen is what makes the opt-in
// declinable, so the value it depends on is stated here.
'enable_anonymous' => true,

So on activation Freemius shows its stock opt-in screen, which defaults to off and carries a Skip link. Skip or decline it and no connection is made, no licence data is exchanged, and the plugin keeps working. The one path that can still reach Freemius afterwards is the optional deactivation feedback form on the Plugins screen, which the plugin readme’s External services section spells out. Opting in later, from the WP MCP > Account page, is how a Pro licence is activated.

WooCommerce Tools

wpmcp treats a WooCommerce store as a first-class surface, not an afterthought bolted onto the content tools. An AI agent can read products, orders, and sales data, and make the day-to-day changes a shop owner actually asks for: updating a price, marking an order as shipped, adjusting stock. Every one of those changes goes through the same snapshot/rollback engine documented in The safety model, so a bad price edit or a wrong order-status change is a one-click undo, not a support ticket.

What a shop owner gets

  • Read the whole store. list-products and get-product for the catalog, list-product-categories for how it is organized, list-orders and get-order for what customers have bought, get-sales-report for a date-range summary of gross sales, order count, items sold, and top products by quantity.
  • Change products safely. update-product edits price, stock, description, SKU, and status. A product is a WordPress post under the hood, so the change is snapshotted the same way an ordinary page edit is: rollback-operation puts the exact prior price and stock quantity back, not an approximation.
  • Change order status safely. update-order-status moves an order through the store’s registered statuses (processing, completed, refunded, and so on), validated against what the store actually supports. It is snapshotted via a dedicated wc_order object type, so rollback-operation restores the prior status exactly, whether the store uses WooCommerce’s classic post-based orders or the newer High-Performance Order Storage (HPOS) tables. Every order tool is HPOS- and CPT-safe: it does not assume one storage backend, so the same tools work whether or not a store has migrated to HPOS.
  • Create products, and add order notes. create-product and add-order-note are additive: a new product with no prior state to snapshot, or a note appended to an order’s history. A mistaken product is removed with delete-product.
  • Variations, coupons and tax rates. list-variations, create-variation, update-variation and delete-variation; list-coupons, get-coupon, create-coupon, update-coupon, validate-coupon and delete-coupon; list-tax-rates, create-tax-rate, update-tax-rate and delete-tax-rate. Bulk price and stock passes go through bulk-update-products, and list-low-stock-products finds what needs reordering.

Product import in two steps

plan-product-import is read-only: it validates every row, matches it to an existing product by SKU, and returns a plan in which each row is a create, an update (with a field-level diff), a skip (with a reason) or an error (with every problem found). Nothing is written, no image is fetched, and category and attribute terms must already exist. The plan carries a hash of the rows, the plan and the current state of every matched product.

apply-product-import writes exactly that plan. It rebuilds the plan first and refuses if anything moved in between (a matched product edited, a SKU taken, a category removed), refuses a plan with error rows, and needs confirm: true above its product threshold. Writes go through the same paths as the single-product tools, image URLs go through the guarded remote media download, and everything shares one session_id, so rollback-session undoes the whole import. A row that fails part way rolls back the whole session: an import is all or nothing. Both steps are free.

The Pro operations catalog

WP MCP Pro adds three abilities that reach much more of the store without adding a tool per endpoint: woo-ops lists the catalog (every op with its mode, route, capability, whether it needs confirm: true, and which snapshot backs it), woo-read runs read ops, and woo-write runs write and destructive ops, singly or as a batch under one session_id. Ops are named by domain, for example orders.update or shipping.add-method.

  • Orders. orders.create and orders.update run through WooCommerce’s order CRUD, so HPOS and the legacy post store both work. No payment gateway is ever called: set_paid and transaction_id are refused, and the payment method is a plain label. An edit is snapshotted as the full order and rolls back exactly; a create is rolled back by trashing the order. Responses leave out order meta, the transaction id, and the customer IP and user agent.
  • Shipping. Zones (shipping.create-zone, shipping.update-zone, shipping.delete-zone) and the three core methods, flat rate, free shipping and local pickup (shipping.add-method, shipping.update-method, shipping.remove-method), validated field by field against each method’s own settings form. Every write except a zone create is snapshotted as the whole zone; a create’s rollback deletes the zone, and only while it is still the zone that was created. Third-party methods are listed without their settings, which can hold carrier credentials.
  • Webhooks. webhooks.create, webhooks.update, webhooks.pause and webhooks.delete. The signing secret is write-only and masked in every response, the delivery URL must be a public https address that passes an SSRF guard, custom action.* topics are refused, and a webhook always delivers as the user who made it.
  • Payment gateways. gateways.list and gateways.get mask every key, secret, password, token, signature or credential field and every password-type field. gateways.update changes the enabled flag, title, description, order and non-secret settings, and refuses secret fields outright: the store admin screen stays the place to set a credential. Each write is snapshotted as the one option it changes, and restoring it takes manage_woocommerce, because the snapshot holds the gateway’s secrets.
  • Reviews. reviews.list (never the reviewer’s email or IP), reviews.approve, reviews.unapprove, reviews.spam, reviews.update, reviews.reply and reviews.trash. Every review op needs moderate_comments plus edit_product on the review’s product. Each change is snapshotted as the full comment with its rating and verified flag; a reply’s rollback trashes it.
  • Reports. reports.sales (optionally by day, week or month), reports.top-sellers, reports.orders, reports.customers and reports.coupons, from WooCommerce’s analytics tables when they agree with the order store and from order queries otherwise. Aggregate figures only, never a customer’s name, email or address.
  • System status. system-status.get reads the status report by section with secret-like values masked; system-status.tools lists the maintenance tools; system-status.run-tool runs an allowlisted one (clearing transients, regenerating lookup tables and similar). Tools that delete store rows need confirm: true, and every run says why it cannot be rolled back.
  • Brands. brands.list, brands.get, brands.create, brands.update, brands.delete, brands.assign and brands.unassign, available when the store registers the brands taxonomy.
  • Everything else in the catalog: products, variations, coupons, customers, refunds, tax rates and classes, and store settings through the store’s own wc/v3 routes.

What is honestly not undoable: refunds.create is snapshotted but reports recoverable: false (the order status comes back, the money does not), status tools rebuild derived data and are not snapshotted, and plain creates report the op that removes what they made. Destructive ops are off until a site opts in with the wpmcp_woo_op_enabled filter and always need confirm: true.

The one-click undo, and why it matters here specifically

Most AI-for-WooCommerce integrations either refuse to touch products and orders at all, or let an agent write directly with no way back. That is a real risk on a live store: an agent that fat-fingers a price from $49 to $4.90, or flips a paid order to “refunded” by mistake, has just cost real money in the minutes before a human notices.

wpmcp’s answer is the same safety engine used everywhere else in the plugin:

  1. update-product and update-order-status snapshot the object before the change applies.
  2. The change happens.
  3. If it turns out to be wrong, rollback-operation (from the agent, or a one-click Restore button on the wpmcp wp-admin screen) restores the exact prior state: the old price, the old stock count, the old order status.

This is the detail worth underlining for anyone evaluating AI tools against a live store: product and order-status changes are undoable by construction, not by promise. Competing WooCommerce AI plugins that offer direct database or REST access typically have no equivalent undo path once a write lands; recovering means restoring from a nightly backup (losing every order placed since), or fixing it by hand from memory. wpmcp’s snapshot happens at the moment of the change, so the fix is a single click, seconds later, with no data loss for anything else that happened on the store in between.

Destructive and disabled-by-default tools

  • delete-product is disabled by default (a site must opt in via the wpmcp_enable_delete_product filter) and always requires confirm: true. Trash is the default path; force: true permanently deletes, and even that is snapshotted, a force-deleted product resurrects at its original ID with its price, stock, and category terms intact.

Capabilities

Read tools (list-products, get-product, list-product-categories, get-sales-report) and product writes require manage_woocommerce, matching WooCommerce’s own admin gate. Order tools (list-orders, get-order, update-order-status, add-order-note) require edit_shop_orders. The Pro catalog checks a capability per op before dispatch and is never looser than the free tool covering the same data: order, note and refund reads keep edit_shop_orders, customer reads need list_users and customer writes edit_users or create_users, review ops need moderate_comments plus edit_product, and everything else needs manage_woocommerce. The store endpoint’s own permission check still runs on top. It defers to the store’s existing WooCommerce roles, so a shop owner does not need to reason about a second permissions model.

See the WooCommerce section of the Tools reference for the full argument-level detail on every WooCommerce tool.

Contributing and Tests

Dev setup

composer install       # install dev dependencies (PHPUnit, WP test scaffolding, phpcs)
composer test          # run the full PHPUnit + WordPress integration suite
composer test:free     # run only the free-tier test suite
composer test:pro      # run only the Pro/Freemius test suite
composer lint          # PSR-12 with WordPress-idiomatic naming, via phpcs

The suite needs a MySQL or MariaDB database for the WordPress integration harness (tests/bootstrap.php sets it up via the standard WordPress PHPUnit scaffolding). CI installs it with bin/install-wp-tests.sh wordpress_test root root 127.0.0.1 6.9 against a MariaDB 10.6 service container; see .github/workflows/ci.yml for the exact steps if you need to reproduce it locally.

CI runs on every push and pull request across a PHP 8.1 / 8.2 matrix: composer install, composer lint, install the WordPress test scaffolding, then composer test.

Test structure

Tests are split into two PHPUnit test suites, matching the free/Pro split described in Free vs Pro:

  • tests/free/, autoloaded as WPMCP\Tests\Free\, mirrors the src/ structure: Admin/, Content/, MCP/, Media/, Safety/, Settings/, Tools/, plus root-level smoke and bootstrap tests. This is where the bulk of the coverage lives, including the safety engine’s heaviest tests (Safety/SafeMutationTest.php, Safety/RollbackServiceTest.php, Safety/SnapshotCaptureTest.php, Safety/SnapshotStoreCrudTest.php, Safety/SnapshotStoreInstallTest.php).
  • tests/pro/, autoloaded as WPMCP\Tests\Pro\, covers the Pro ability surface: Elementor/, Builders/, Analysis/, Cli/, Code/, Compose/, Media/, Freemius/, plus GateTest.php for the gate itself.

phpunit.xml.dist wires both suites (free -> tests/free, pro -> tests/pro) and defines the WPMCP_TESTING constant used by the bootstrap files to skip the plugin’s normal ABSPATH guard during tests.

The safety invariant

The project’s core rule, stated plainly in the README’s Contributing section: no tool may write to the database except through Safe_Mutation::run(), and every change ships with a test.

This is not enforced by a single “no bypass” test; it’s enforced structurally and by convention across src/Tools/. Every WordPress write call (wp_update_post, wp_delete_post, wp_insert_post, update_option, etc.) in a tool handler either:

  • sits inside a Safe_Mutation::run() closure, or
  • has an explicit code comment justifying why it is exempt (only two categories qualify: pure-creation tools like create-post and sideload-image, where there is nothing pre-existing to snapshot; and WordPress’s own trash for delete-post’s default path and delete-media’s MEDIA_TRASH-covered path, which is already reversible without a redundant snapshot).

When you add a new tool that mutates existing state, route the mutation through Safe_Mutation::run() with an object_type, object_id, session_id, and tool_name in its context array, the same pattern every existing write tool follows (see src/Tools/Content/Update_Post.php for a representative example, or src/Tools/Settings/Update_Settings.php for the per-key variant used by batch writes).

If a mutation genuinely creates a brand-new object with nothing to overwrite, or is already covered by WordPress’s own reversible trash, document that exemption inline with a comment, the same way the existing exempt tools do, so the next contributor (or reviewer) understands it was a deliberate decision and not an oversight.

How to document a new feature

When a new tool or test lands in this codebase:

  1. Add or extend a section in 40-tools-reference.md for the new tool: its registered ability name (wpmcp/<name>), purpose, key args, whether it’s safe-wrapped (and why, if not), and any capability or opt-in requirements (disabled-by-default filters, confirm flags, allowlists).
  2. If it changes safety behavior, adds a new object type to snapshot/restore, changes pruning/retention logic, changes the meta-purge, or touches the force-delete/resurrection path, also update 30-safety-model.md. This file exists specifically to describe actual mechanics, so keep it in sync with the code rather than the aspirational design spec.
  3. Prefer describing what the code does over what a design doc says it should do. This documentation set intentionally verified every claim against src/ directly; a design spec drifting out of date from the shipped implementation is a normal part of development, not a bug, but the docs here should track the code, not the spec.
  4. If a feature is Pro-gated or merely planned, say so explicitly (“planned”) rather than implying it ships today. See 50-free-vs-pro.md for the current free/Pro boundary.

Release notes, straight to your inbox.

One email per release, what shipped, what's verified, what's production-only. No drip campaigns. Unsubscribe in one click.