Woodpresser
This server exposes a WordPress site’s core content and administration through 19 REST-backed tools; no PHP, SQL, filesystem, snippet, or Elementor tools are present in this schema.
Inspect site identity, capabilities, versions, and layered connectivity/auth/bridge health.
List, fetch, create, update, trash or force-delete posts and pages, including meta, terms, templates, and featured media.
List and upload media, using a local path or remote URL sideloading.
List and create categories/tags.
List users with roles.
List plugins and activate, deactivate, install, or delete them; deletion requires confirmation.
List themes and switch the active theme; activation requires confirmation.
Read and update REST-exposed site settings such as title, description, front page, posts per page, and timezone.
Search across posts, pages, and media.
Provides tools for reading and manipulating Elementor page data stored in _elementor_data: inspecting available widgets and page status, building full page trees from a compact spec (sections/columns/widgets or flexbox containers), deep-merging patches into existing elements by path, and taking/restoring five-deep snapshots of previous page data, plus cache clearing.
Provides typed, Zod-validated tools for interacting with a real WordPress site through the REST API and a companion bridge plugin: managing posts, pages and content (create, read, update, delete, search), media uploads and listings, terms and taxonomy, users, plugins and themes, and site settings. It can also inspect site info and health, manage post meta, and with the bridge read/write files inside wp-content, run SQL statements, execute PHP (as programs or templates with captured output), manage snippet-like PHP, and read the audit log.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@Woodpresser list the 5 most recent draft posts on my WordPress site"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
Woodpresser
A WordPress MCP server for coding agents. Give Claude, opencode, Cursor or any MCP client structured access to a real WordPress site — content, plugins, PHP, the database, the filesystem, and Elementor page data — through one typed tool surface.
npx woodpresser-mcp initYou need three things: the site URL, a username, and an Application Password. Everything else is automated.
Why
WordPress is where a huge share of the web lives, but agents are bad at it. They guess at REST routes, paste PHP into the wrong file, or rewrite an Elementor page by hand-editing JSON they do not understand. Woodpresser gives them:
A typed tool surface — 39 tools, Zod-validated, with descriptions written for a model, not a human.
Mode gating — a content-only site never gets a PHP execution tool registered. No dead tools, no accidental damage.
A companion bridge plugin for the things REST cannot do: evaluating PHP with captured output, running SQL, reading and writing files inside
wp-content, managing snippet-like PHP, and reading/writing Elementor's_elementor_data.Safety rails — destructive tools require an explicit
confirmliteral,wp_el_builddefaults to a dry run, every write is audited locally and on the site, and secrets are scrubbed from every payload before it reaches the transcript.
Related MCP server: WordPress MCP Server
Install
npm i -g github:a-saeedia/woodpresser-mcp
woodpresser initThat form works today and always builds from source (prepare runs tsc), so it needs git on PATH and about a minute. Once woodpresser-mcp is on the registry (tracked in the changelog), npm i -g woodpresser-mcp and npx woodpresser-mcp init do the same thing faster.
Node >= 22 is the only prerequisite.
Install it globally. The MCP entry install-mcp writes contains an absolute path to the install, so a clone inside a temp directory works until something cleans temp and then your client cannot start the server.
After your credentials, init asks whether to enable php + elementor. The default is no: you get 19 content tools and nothing that can execute code. Answer y when you actually want PHP, SQL and filesystem access, or be explicit in a script with --modes core,php,elementor.
init walks through:
Credentials — verifies the Application Password against
/wp/v2/users/me.Bridge detection — probes
/wp-json/woodpresser/v1/ping.Claim — binds the bridge to your token (constant-time compared on the server).
Config — writes
~/.woodpresser/config.json(mode0600).MCP registration — adds the server to opencode, Claude Code and/or VS Code, preserving your comments and formatting.
If the bridge is not installed yet, init writes woodpresser-bridge.php to the current directory and prints the three-step upload. Re-run woodpresser bridge claim once it is active.
Register only
woodpresser install-mcp --target all --scope user
woodpresser install-mcp --target opencode --scope project
woodpresser uninstall-mcp --target opencodeClient | File | Key |
opencode |
|
|
Claude Code |
|
|
VS Code |
|
|
The entry looks like this:
{
"mcp": {
"woodpresser": {
"type": "local",
"command": ["C:/Program Files/nodejs/node.exe", "/path/to/woodpresser/dist/cli.js", "mcp", "--site", "example-com"],
"enabled": true
}
}
}Restart the client afterwards.
The bridge plugin
The core REST API covers posts, media, terms, users, plugins, themes and settings. It cannot evaluate PHP, inspect the database, or touch files. The bridge fills that gap.
Single file.
bridge/woodpresser-bridge.php, no build step, no Composer.Upload once.
woodpresser initwriteswoodpresser-bridge.zipnext to you when the bridge is missing; upload it at wp-admin → Plugins → Add New → Upload Plugin and activate. The archive already contains thewoodpresser-bridge/folder wp-admin expects. No SFTP needed;npm run bridge:zipbuilds the same file intodist/.Or pin the token in
wp-config.phpand skip the claim flow entirely:
define( 'WOODPRESSER_TOKEN', 'your-token' );The token source is reported by wp_bridge_status as constant or option.
Authentication
Three factors, all required:
A valid WordPress user with
manage_options(Application Password over Basic auth).A matching
X-Woodpresser-Token.A per-user rate limit of 240 requests/minute.
The token is compared with hash_equals. Without WOODPRESSER_TOKEN, the first POST /claim stores it in an option; the route requires manage_options, so only an administrator can claim a fresh bridge.
Modes
Set per site. A new site starts in core only; add php and/or elementor at init time (--modes core,php,elementor), in ~/.woodpresser/config.json, or via WOODPRESSER_MODES.
A mode you did not enable registers no tools at all. The model cannot call what is not in its tool list, so the safest configuration is also the one with the smallest prompt.
Mode | Tools | Use when |
| 19 | Content editing, media, users, plugins, settings. No code execution. |
| 11 | PHP eval, SQL, filesystem, snippets, post meta, audit log. Requires the bridge. |
| 9 | Read, build, patch, snapshot and restore Elementor pages. |
Tools from a mode you did not enable are not registered — the model never sees them.
Tools
Core (core)
wp_site_info · wp_health_check · wp_content_list · wp_content_get · wp_content_create · wp_content_update · wp_content_delete · wp_media_list · wp_media_upload · wp_terms_list · wp_term_create · wp_users_list · wp_plugins_list · wp_plugin_action · wp_themes_list · wp_theme_activate · wp_settings_get · wp_settings_set · wp_search
PHP / site (php)
wp_bridge_status · wp_bridge_flags · wp_php_run · wp_db_query · wp_fs · wp_snippet_list · wp_snippet_save · wp_snippet_run · wp_snippet_delete · wp_post_meta · wp_audit_log
wp_php_run takes code plus mode: php (a PHP program; return is serialized back) or template (HTML with inline <?php ?> blocks). Output buffering, fatal-error capture, a 512 KB source cap and a shell-function blocklist apply in both.
wp_fs is jailed to wp-content. Reads are broad; writes are limited to an extension allowlist, and .htaccess, .user.ini and index.php are never writable. Overwrites are backed up to wp-content/woodpresser-backups (last 40 kept).
Elementor (elementor)
wp_el_status · wp_el_widgets · wp_el_get · wp_el_build · wp_el_set · wp_el_patch · wp_el_snapshots · wp_el_restore · wp_el_clear_cache
wp_el_build accepts a compact spec and expands it into Elementor's tree (section → column → widget, or flexbox containers), generating 7-hex ids:
{
"layout": "section",
"page_settings": { "template": "elementor_canvas" },
"sections": [
{
"settings": { "layout": "boxed", "gap": "extended" },
"columns": [
{ "size": 60, "widgets": [{ "type": "heading", "settings": { "title": "Hello", "header_size": "h1" } }] },
{ "size": 40, "widgets": [{ "type": "button", "settings": { "text": "Go", "link": { "url": "#" } } }] }
]
}
]
}It defaults to dry_run: true and returns the tree without writing. Set dry_run: false and confirm: "overwrite" to persist. Every write snapshots the previous data (five deep) into woodpresser_el_snap_<post_id>, so wp_el_restore can put it back.
wp_el_patch takes paths from wp_el_get (e.g. 0/1/2) and deep-merges settings, so changing typography_font_size.size does not erase unit.
Safety model
Control | Behaviour |
|
|
Dry run |
|
Feature flags |
|
SQL | Single statement only; |
Filesystem | Realpath jail under |
Secrets |
|
Audit | Local |
The bridge is a deliberate remote-code-execution surface guarded by an administrator credential. Treat the token like a root password, keep allow_* flags off on production until you need them, and read wp_audit_log after an agent session.
Configuration
Precedence, highest first:
Environment:
WOODPRESSER_SITE_URL,WOODPRESSER_USER,WOODPRESSER_APP_PASSWORD,WOODPRESSER_BRIDGE_TOKEN,WOODPRESSER_MODES,WOODPRESSER_SITE,WOODPRESSER_CONFIG,WOODPRESSER_BRIDGE_FILE.Project file:
./.woodpresser.json.User file:
~/.woodpresser/config.json.
Multiple sites live side by side; each MCP entry pins one with --site <name>.
woodpresser sites
woodpresser sites default clinic
woodpresser doctor --site clinicdoctor reports REST, auth, bridge, WordPress and PHP versions, Elementor presence and every flag — the fastest way to tell whether a failure is credentials, a plugin, or a permission.
Troubleshooting
Symptom | Cause | Fix |
| Login password instead of an Application Password, or the | Create one at Users → Profile → Application Passwords; on LiteSpeed/Apache check that |
| Config and bridge disagree after a re-init or restore |
|
| A second machine pointed at a site whose bridge another machine already claimed; the bridge only rotates its token for the current holder | On the owning machine run |
|
| Put that same value in the client config ( |
| Bridge installed but not activated, or the plugin folder was uploaded inside another folder | Activate it in Plugins; the plugin file must be |
| Bridge is active but no token was ever claimed |
|
| More than 240 requests in a minute from one administrator | Batch your calls; the bucket resets after 60s |
| The site, usually via a security plugin, turned Application Passwords off | Enable them in Users → Profile, or define |
| Path resolved outside the jail, or a symlinked upload directory | Pass paths relative to |
Tools missing in the client | Mode not enabled, or the client has not reloaded the server |
|
| Node older than 22 |
|
| Application Passwords are only offered over HTTPS unless the site sets | Use the |
You must point a second machine at a site already configured | Re-running | Run |
woodpresser doctor --site <name> prints each layer — REST, auth, bridge, versions, flags — and says which one failed. On a rejected login it also prints the one curl command that separates a wrong Application Password from a host that strips the Authorization header.
Requirements
Node.js >= 22 (uses
zlib.crc32,node:testglobbing,AbortSignal.timeout). Older runtimes exit at startup with an upgrade line.WordPress >= 6.0 with Application Passwords (HTTPS or a
localenvironment).PHP >= 7.4 for the bridge.
Development
npm install
npm run build # tsc
npm test # unit tests (node:test)
npm run test:integration
npm run bridge:ziptest:integration boots a real WordPress in WordPress Playground (PHP-WASM, no Docker, no PHP install), mounts bridge/, activates the plugin, and drives the actual MCP tool handlers through 50+ assertions: auth, claim, PHP eval, SQL guard rails, filesystem jail, snippets, content CRUD, Elementor build/patch/snapshot/restore, meta and audit.
@wp-playground/cli is a dev dependency, so npm run test:integration works after npm install. To use a different build, set WOODPRESSER_PLAYGROUND_CLI to its path.
License
MIT (c) Arman Saeedi
Change history: CHANGELOG.md. Contributing: CONTRIBUTING.md. Security: SECURITY.md.
Available Tools
19 toolswp_content_createCreate a post or pageC
Create content. Returns the new ID and link. Supports meta, taxonomy terms and page templates.
Inputs: title (required), date, meta, slug, +10 more.
wp_content_deleteDelete (trash) a post or pageADestructiveIdempotent
Moves to trash by default. force=true bypasses trash forever and requires confirm:"delete".
Inputs: id (required), type, force, confirm.
wp_content_getGet a post or pageBRead-only
Fetch a single item by ID or slug, including raw and rendered content plus open Graph-ish fields.
Inputs: id, slug, type, context.
wp_content_listList posts or pagesARead-only
List content with filters. Returns compact rows by default (no full content). Use wp_content_get for the body.
Inputs: page, slug, tags, type, +9 more.
wp_content_updateUpdate a post or pageBIdempotent
Patch an existing item. Only supplied fields change. For a full Elementor rewrite use wp_el_set instead.
Inputs: id (required), meta, slug, tags, +10 more.
wp_health_checkWordPress health checkARead-only
Diagnose connectivity in layers: REST reachable, credentials accepted, bridge claimed, Elementor present, permalinks, and debug log size. Use before blaming the code.
Inputs: no named parameters.
wp_media_listList mediaCRead-only
List attachments with optional MIME filter and search.
Inputs: page, search, per_page, mime_type.
wp_media_uploadUpload mediaB
Upload a local file to the media library. Provide path, or url to sideload a remote file.
Inputs: url, path, title, alt_text, +1 more.
wp_plugin_actionPlugin actionADestructive
Activate, deactivate, install (from wordpress.org by slug) or delete a plugin. Deletion requires confirm:"delete". Activating an untrusted plugin is a security decision.
Inputs: action (required), slug, plugin, confirm.
wp_plugins_listList pluginsARead-only
List installed plugins with status and available updates.
Inputs: no named parameters.
wp_searchSearch the siteBRead-only
Search across posts, pages and media in one call. Use this to locate content before editing it.
Inputs: query (required), subtype, per_page.
wp_settings_getRead site settingsARead-only
Read the REST-exposed site settings (title, description, timezone, permalinks, front page).
Inputs: no named parameters.
wp_settings_setUpdate site settingsBIdempotent
Update REST-exposed settings. Arbitrary options need wp_php_run or wp_db_query.
Inputs: title, description, page_on_front, show_on_front, +3 more.
wp_site_infoWordPress site infoARead-only
Identify the connected site: name, description, URL, REST namespaces, the authenticated user and their capabilities, plus bridge/Elementor/plugin versions. Start here when you inherit a project.
Inputs: no named parameters.
wp_term_createCreate a termC
Create a category or tag.
Inputs: name (required), slug, parent, taxonomy, +1 more.
wp_terms_listList taxonomy termsCRead-only
List categories, tags or any public taxonomy.
Inputs: page, search, per_page, taxonomy.
wp_theme_activateActivate a themeADestructive
Switch the active theme. Changing themes on a live site is high-impact; prefer a staging copy.
Inputs: stylesheet (required), confirm.
wp_themes_listList themesARead-only
List installed themes and which one is active.
Inputs: no named parameters.
wp_users_listList usersCRead-only
List users with roles. Requires administrator credentials.
Inputs: page, roles, search, per_page.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
TDQS
Scored across 19 tools
Most tools map cleanly to a resource+action pair (content CRUD, media, terms, plugins, themes, settings), so selection is usually obvious. Minor overlaps exist: wp_search vs wp_content_list both locate content, and wp_site_info vs wp_settings_get both expose title/description, which could cause a misselection. Descriptions do enough disambiguating work to keep this mostly clean.
A consistent wp_ + resource + action convention runs through nearly all tools (wp_content_get, wp_media_upload, wp_plugin_action, wp_settings_set). Deviations are small: wp_terms_list is plural while wp_term_create is singular, and a few names are noun-only (wp_site_info, wp_health_check, wp_search). Still readable and predictable overall.
19 tools is on the heavy side but each covers a distinct slice of WordPress administration (content, media, terms, users, plugins, themes, settings, diagnostics, search). No obvious filler tools; the count is driven by genuine domain breadth rather than redundancy.
Content has full CRUD, but media and taxonomy are half-finished: no wp_media_get/delete and no wp_term_update/delete, leaving dead ends. Worse, descriptions reference tools that do not exist (wp_el_set, wp_php_run, wp_db_query), which will send an agent chasing phantom operations, especially for the Elementor workflow the server advertises.
Maintenance
Related MCP Connectors
Manage WordPress blogs and WooCommerce shops from Claude, ChatGPT, Cursor and other MCP apps.
Run all of WordPress from Claude, ChatGPT or Cursor: SEO, security, your WooCommerce shop and Elementor pages, on up to 10 websites in one connector. Start free. SEO: titles, descriptions and focus keywords for every page, post and product. Security & updates: core, plugin and theme updates with a live test, site health and HTTPS checks, users and roles. WooCommerce shop: products, prices, orders, coupons and customers. Elementor and many more page builders: texts, links and images. Blog posts, pages, media, comments and menus. 10 websites in one connector: all your sites in the same chat. Deploy (Agency): build a site locally and push it to your own server. Just say it: "Write SEO titles for every product." "Install all updates and check that the site still works." "Show today's orders and create a weekend coupon." You choose what your AI may change, with one switch. Install the free AlphaBridge MCP plugin from WordPress.org, connect and approve on your own site. No account, no key to copy.
Run every WordPress site you look after from Claude or ChatGPT. Nothing goes live until you approve it, every change gets a receipt you can undo, and the whole fleet sits in one tamper-evident log.
Manage a WordPress site from your AI assistant: read, search, propose edits, Search Console data.
Related MCP Servers
- FlicenseNot gradedqualityDmaintenanceEnables AI agents to manage WordPress sites via ~74 capabilities including content, media, plugins, themes, and more.-
- AlicenseNot gradedqualityCmaintenanceEnables AI agents to manage WordPress sites through the WordPress REST API, supporting content, media, users, settings, and Elementor management with safety policies for production use.MIT
- AlicenseAqualityCmaintenanceEnables AI assistants to read and edit WordPress sites over the REST API, including Divi 4, Divi 5, and Gutenberg content, with safety features like round-trip verification, draft-based editing, and dry-run previews.45MIT
- AlicenseAqualityCmaintenanceEnables an AI design agent to connect to any WordPress site, inspect its real rendered CSS/JS/theme, and create or update pages through the WordPress REST API and optional companion plugin routes.617 npmMIT