WordPress MCP is useful when an agent can inspect a site. It becomes a production incident when the same agent can write, install, or delete, and nobody can say who approved the call.
This is not a generic “secure your tools” essay. WordPress already has roles, capabilities, application passwords, nonces, and a REST surface that will happily do damage if the authenticated user is an administrator. MCP is another client on that surface. Hardening it means making the WordPress side small, observable, and boring before you ever enable a write tool.
If you do not need writes, stop here. Run MCP on local and staging as read-only. Production can stay out of the agent’s config. That is the correct default for most agency retainers.
Assume the agent will call every tool you expose
Language models do not have judgement you can quote in an incident review. They have a tool list and a goal. If delete_post is available, it will be considered. If update_option is available, a prompt about “fix the site URL” can become a loop of option writes. If install_plugin is available, a suggestion from a blog post can become a production install.
So the tool catalogue is the security boundary, not the system prompt. Prompts are not access control.
Expose the minimum:
- Read tools for the objects you actually query: posts, plugins, site health, maybe field groups.
- No install, activate, delete, or user-management tools unless the environment is disposable.
- No arbitrary SQL. No
eval. No file-write tools that point atwp-content.
If a vendor’s WordPress MCP server ships with a wide catalogue, turn tools off. If you cannot turn them off, do not point it at anything you care about.
Do not authenticate as an administrator
Create a dedicated WordPress user for the MCP client. Do not reuse a founder account, a developer account with administrator, or a password manager login that also opens wp-admin in a browser.
Give that user a role that matches the tools. A read-only mapping is usually a custom role, not editor, because editor can still publish and delete other people’s posts. If you must use a built-in role, author or a trimmed custom role is closer than administrator.
Authenticate with an application password on that user, stored in the MCP client’s secret config, not in a repo. Rotate it when staff leave. Revoke it by deleting the application password in WordPress — you should not need to change the person’s login password to kill the agent.
If the MCP server supports a separate service account or token with its own capability map, use that and keep the WordPress user even tighter.
Capability checks must remain on. A server that bypasses current_user_can() to “make the agent more useful” is not useful. It is a backdoor.
Environment is the other boundary
Write tools belong on local and staging. Production, if it is in the picture at all, should be read-only until you have a reason that survives a meeting.
A workable split:
- Local: read and write. This is where the agent drafts. You review git and the database dump.
- Staging: read, plus writes you have explicitly allowed for a ticket. Staging should be a recent copy, not a playground with different plugins.
- Production: omit from the MCP config, or allow GET-equivalent tools only.
Pushing from staging to production remains a human step: Nought Sync, a deploy, or a carefully selected export. The agent does not get a production write URL “for convenience”.
If a host cannot give you staging, you do not have a place to enable write tools. Fix hosting before you fix MCP.
Writes need a human in the loop
An agent that can create_post will create posts. An agent that can update_post will overwrite. The mitigation is not a friendlier prompt. It is a workflow:
- Agent proposes a change on local or staging.
- Diff is visible — git for code, a preview or changelog for content.
- A named person approves.
- Only then does anything approach production.
For content, that person is an editor. For schema, plugins, or options, it is an engineer. Do not let the MCP client publish.
Where the protocol supports confirmation, require it for every mutating tool. Where it does not, keep mutating tools off and have the agent emit a patch or a WP-CLI command for a human to run.
This matches how we already treat other agent tool use: useful, scoped, and never the thing that directly mutates production because a model felt done.
Log the calls as if they were wp-admin
If an agent updates a page, the site must show it. WordPress last-modified and revision history help for posts. They do not help for options, user meta, or plugin installs.
Log at least:
- Timestamp, tool name, arguments (redact secrets).
- WordPress user ID used for the call.
- Object IDs touched.
- Success or error.
Ship those logs to the same place you keep deploy logs. “The agent did it” is not an audit trail if you cannot say which tool, which user, and which post.
Rate-limit tool calls. A runaway loop that lists and updates every post will look like an editor having a stroke, and it will lock tables the same way.
WordPress-specific footguns
Application passwords are per user. If that user can log into wp-admin, a leaked token is a leaked session. Disable wp-admin for the MCP user if you can (admin UI), or block it at the edge, and keep the user for REST/MCP only.
XML-RPC is unrelated and should stay disabled. Do not “turn on XML-RPC for the AI”.
Must-use plugins and drop-ins that the agent cannot see will still run. An agent that “fixes” a performance issue by writing to wp-config.php via a file tool is a host incident. File tools should not be on the WordPress MCP server.
Multisite multiplies the blast radius. A super admin token is never an MCP credential.
WooCommerce, membership, and booking data are personal data. Read tools that return orders, guests, or emails do not belong in an agent that lives in a developer laptop’s chat history. Scope tools to public content types, or run them only on anonymised staging.
ACF. Reading field groups is reasonable. Writing field keys is a migration. Use a dedicated ACF workflow — we use FieldCraft for moves, renames and link-and-sync — not an MCP update_post_meta loop.
A release checklist
Before any mutating WordPress MCP tool is enabled on a shared environment:
- Dedicated user, not administrator, application password stored outside git.
- Tool list written down; install/delete/option-write off unless the environment is local.
- Staging exists and is the only non-local write target.
- Logging on, including tool name and object ID.
- Backup taken the same day. Nought Backup or your existing off-site job.
- A named approver for publishes and schema.
- A revoke step that takes one minute (delete the application password).
If you cannot tick those, the agent can still help: it reads local, you paste. That is slower and much cheaper than a Friday rollback.
The rule
WordPress already knows how to restrict users. MCP does not get a special exemption because the client is a model. Give it a small role, a small catalogue, a non-production home, and a log. Enable writes only when a human is still the publisher.
Anything looser is production admin with extra steps.