Skip to main content
Glama

WordPress MCP — Site & Elementor Automation for AI Clients

26 tools for managing WordPress sites — users, posts/pages, media, ACF, plugin settings, writable Elementor layouts, and any other REST route — over the WP REST API, for any MCP client.

by Jan Ivan Simoy


What is this?

WordPress MCP is a Model Context Protocol server that exposes WordPress site management over the WP REST API. One server instance manages one WordPress site — pick which site by pointing --config at that site's config file, so a single MCP client can run several instances for several sites.

Beyond the standard content-management surface, it can read, back up, and write Elementor page layouts directly — something most WordPress tooling doesn't expose, because Elementor registers those fields for REST itself rather than through a public API. It can also reach any other REST route a plugin exposes, and change plugin settings safely through per-plugin adapters that handle the read-modify-write traps those routes tend to have.


Related MCP server: wp-mcp

Tools

Category

Tools

What you can do

Site

1

Basic site info (name, URL, WP version)

Users

5

List, get, create, update, delete

Posts & Pages

5

List, get, create, update, delete — any post type by REST base slug

ACF Fields

2

Read/write ACF field data where REST-exposed

Media

5

List, get, find by filename, upload, delete

Elementor

4

Read a layout, back it up, clear the CSS cache, and deploy a layout between environments with attachment IDs remapped

Generic REST

1

Call any other REST route a plugin exposes

Plugin Settings

2

Read/change a plugin's settings safely through a per-plugin adapter

Diagnostics

1

How many requests this server has sent to the site so far

Tool

Description

wp_site_info

Basic site info (name, URL, WP version, description)

wp_list_users / wp_get_user / wp_create_user / wp_update_user / wp_delete_user

User management

wp_list_posts / wp_get_post / wp_create_post / wp_update_post / wp_delete_post

Works with any post type by REST base slug (pages, posts, or a custom post type)

wp_get_acf_fields / wp_update_acf_fields

Read/write ACF field data, when a site has ACF fields exposed via REST

wp_list_media / wp_get_media / wp_find_media_by_filename / wp_upload_media / wp_delete_media

Media library management

wp_get_elementor_page

Read a page's parsed layout, page settings (incl. custom_css), template, and referenced attachment IDs

wp_backup_elementor_page

Save a page's current Elementor layout and settings to a timestamped local JSON file before any overwrite

wp_clear_elementor_cache

Clear Elementor's generated CSS cache site-wide — required after writing _elementor_page_settings

wp_deploy_elementor_page

Deploy a layout to a page: back up what's there, upload media, remap attachment IDs/URLs for the target site, validate, then write

wp_rest_request

Generic authenticated call to any REST route on the site (a plugin's own namespace, or a core route the tools above don't cover). Route only; the /wp-json prefix is added and full URLs, query strings in the path and traversal are refused

wp_plugin_settings_get / wp_plugin_settings_update

Read and safely change a plugin's settings through a per-plugin adapter (see Plugin settings adapters). update is a dry run unless you pass dry_run: false

wp_request_count

How many HTTP requests this server has sent to the site so far, login included, so the count AGENTS.md asks for after touching a live or staging site is a lookup


Elementor pages

wp_create_post / wp_update_post accept a meta object, which is what makes Elementor layouts writable — Elementor registers these keys for REST itself (elementor/modules/wp-rest/classes/elementor-post-meta.php, hooked unconditionally on rest_api_init):

Key

Type

Notes

_elementor_data

string

the whole layout, as a JSON string

_elementor_edit_mode

string

set to builder

_elementor_template_type

string

e.g. wp-page

_elementor_page_settings

object

accepts custom_css, hide_title

_elementor_conditions

array

Pro only

Pair with template: "elementor_canvas" for a page with no theme header/footer. Elementor regenerates its per-post CSS file automatically after the write.

Two things worth knowing before pointing this at anything that matters:

  • There is no undo. wp_backup_elementor_page saves the existing layout before you overwrite it — REST writes replace it wholesale and WordPress keeps no revision of post meta.

  • sanitize_elementor_data() runs the payload through kses_post_deep for any user without the unfiltered_html capability, silently stripping attributes. Confirm the target user has it before trusting a write.

Media IDs and URLs are embedded in the layout JSON, so a layout moved between environments needs its attachment IDs remapped — wp_deploy_elementor_page handles this by uploading media first (idempotent by filename) and rewriting the IDs before writing the page.

See ELEMENTOR_FINDINGS.md for the underlying research this was built from.


Plugin settings adapters

Plenty of plugins keep their settings behind their own REST routes, and those routes rarely behave like "PATCH these keys". Typical traps: a save replaces the stored object from what you send plus the plugin's defaults (so a partial POST silently resets everything else), read-only counters come back in the GET and must not be posted, text fields are sanitised in ways that change line breaks, and even a read can have a side effect. An adapter is where one plugin's route shape and its traps live, behind one interface:

  • wp_plugin_settings_get(plugin, group?, keys?) — no group lists the groups (no request sent).

  • wp_plugin_settings_update(plugin, changes, dry_run=true, fix_line_breaks=false) — changes is {group: {setting: value}}. The adapter reads the full current object, rejects unknown keys and wrong-typed values, changes only what you asked, saves the whole object back, and verifies the result from the plugin's own save response. The dry run shows the exact plan and writes nothing. Both report requests_sent.

Adapters so far:

plugin

Covers

wcdn

Print Invoice & Delivery Notes for WooCommerce v7+: settings and templates.<document>

To add one, subclass SettingsAdapter in plugin_settings/, register it in plugin_settings/__init__.py, and test it against a fake client — tests/test_wcdn_adapter.py shows the pattern (the fake reproduces the plugin's quirks, so the tests fail if the adapter stops handling them). plugin_settings/wcdn.py documents each quirk it handles and how it was found. For anything an adapter doesn't cover yet, wp_rest_request reaches any route — read the object first and send it back whole.


Auth modes

Two, and which you need depends on how the site is protected.

Mode

Use when

Config keys

app_password

Normal site, no HTTP Basic auth in front of it

wp_user, wp_app_password

cookie

Site sits behind htpasswd, or you only have a normal login

wp_user, wp_password, http_basic_auth, optional wp_login_url

Application passwords cannot be used behind HTTP Basic auth. Both rely on the Authorization: Basic header and the basic-auth layer consumes it first. That is what cookie mode exists for — it logs in via the form and authenticates with a REST nonce, leaving Authorization free for htpasswd.

wp_login_url covers sites that move or hide wp-login.php (WPS Hide Login and similar); give it the slug or a full URL. Cookie logins also append a cache-buster automatically, because managed hosts will serve a cached login page with Set-Cookie stripped — see AGENTS.md for why that surfaces as a misleading "Incorrect username or password".

Safety rails (optional, per site)

Key

Default

Effect

read_only

false

Refuse every request that is not a GET, whichever tool sends it. Set it on live sites you only need to inspect.

request_delay

0

Minimum seconds between requests, login included. Set e.g. 4 on staging so multi-request tools stay paced, as AGENTS.md asks.


Requirements

Requirement

Version

Python

3.9 or later

WordPress

Any version with the REST API enabled (default since WP 4.7)


Installation

git clone https://github.com/jimsimoy/wordpress-mcp.git
cd wordpress-mcp
pip install -r requirements.txt
cp config.example.json configs/my-site.json
# edit configs/my-site.json with your site's URL and credentials

Site configs live in configs/*.json and are gitignored — they hold live credentials and should never be committed. See config.example.json for the full config schema.


Client Setup

python3 server.py --config configs/my-site.json

This starts an MCP server over stdio. Point an MCP client at it, e.g.:

{
  "mcpServers": {
    "wordpress-my-site": {
      "command": "python3",
      "args": ["/path/to/wordpress-mcp/server.py", "--config", "/path/to/configs/my-site.json"]
    }
  }
}

Security

  • configs/*.json is gitignored — those files hold live credentials and should never be committed. Keep one config file per site.

  • This repo is public, so the included git helpers scan before they act:

    ./git-pull-current.sh              pull the current branch, then show status
    ./git-push-current.sh              scan, then push the current branch
    ./git-commit.sh "message"          scan the message, stage all, commit, push

    They check the outgoing diff and the commit message for credentials, private keys, user:pass pairs, .local hostnames, IPs, and email addresses, and refuse to push if anything matches — a force-push cannot unpublish a mistake, since old objects stay fetchable by SHA, so the only reliable moment to catch one is before it leaves. For names that must never appear here, add a pattern to .git-deny-patterns (gitignored, never ships).

  • Using this against live or staging sites: if an AI agent is driving this tool, see AGENTS.md first — it sets the rate-limiting policy for any config pointed at a live or staging site (space out requests, no retry loops, no bulk calls) and requires reporting a hit-count summary at the end of a task.


Tests

python3 -m unittest discover -s tests -v

No network and no WordPress needed: the client guards (path validation, read_only, pacing, request counting) and the adapters are tested against fakes.


Notes

  • ACF tools only work if the target field group(s) have show_in_rest enabled; otherwise the tools report that no ACF data is visible rather than guessing.


Project Structure

server.py            # MCP server entry point and tool definitions
wp_client.py          # WordPress REST API client
plugin_settings/      # Per-plugin settings adapters
configs/               # Per-site config files (gitignored)

The server communicates over stdio using JSON-RPC 2.0, the standard MCP transport.


License

MIT — free to use, modify, and distribute.


Report a Bug · Request a Feature

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    C
    maintenance
    MCP server for WordPress content management via REST API, supporting posts, pages, media, comments, and terms through natural language interfaces like Cursor, ChatGPT, Codex, and Claude.
    16 npm
    MIT
  • A
    license
    Not graded
    quality
    C
    maintenance
    An MCP server for managing multiple WordPress sites via the WordPress REST API. It supports posts, categories, tags, media, comments, and site configuration through natural language.
    67 npm
    ISC
  • A
    license
    B
    quality
    C
    maintenance
    Enables MCP-compatible clients to securely operate a WordPress site through a signed REST API, managing plugins, content, themes, menus, media, users, WooCommerce, Elementor, audits, and allowlisted WP-CLI commands.
    58
    55 npm
    MIT