LEARN · UPDATED 2026-09-21

WordPress MCP without installing a plugin

An MCP server does not have to run inside WordPress. Here is what the protocol actually needs, the three places the server can live, and the trade you make by keeping the site untouched.

Almost every guide to connecting Claude or ChatGPT to WordPress opens the same way: install this plugin. That is a reasonable default, and for a site you own it is usually the shortest path. It stops being reasonable the moment the site belongs to a client, sits behind a managed host with a plugin allowlist, or has to pass someone else's security review before anything new goes on it.

So it is worth being precise about what the Model Context Protocol requires, because a plugin is not on the list.

What MCP actually needs from WordPress

MCP standardises the conversation between an AI client and a server that exposes tools. The client is Claude, ChatGPT, Claude Code or Cursor. The server is whatever answers with a tool list and executes the calls. Nothing in the specification says that server has to be the WordPress installation, or has to live on the same machine, or has to be PHP.

What the server needs is a way to read and change WordPress. WordPress has shipped one since version 4.7 in December 2016, when the content endpoints of the REST API landed in core, and it has shipped authentication for it since 5.6 in December 2020, when application passwords arrived. Every modern WordPress install already answers at /wp-json/ and can already mint a revocable credential from Users → Profile.

That combination is the whole requirement. A plugin that turns the site into an MCP server is one way to meet it. It is not the only way, and on someone else's site it is often the hardest one to get approved.

Three places the server can live

Where the server runsExamplesWhat goes on the WordPress site
Inside WordPressAI Engine, the official MCP Adapter, miniOrangeA plugin, plus whatever it activates
On your own machinestdio servers, npx packagesNothing
On a remote servicehosted connectors, including wpreefNothing, or an optional add-on

The first row is the one the search results are full of. WordPress 6.9 added the Abilities API to core and the WordPress/mcp-adapter plugin bridges those abilities to MCP, which makes it the closest thing the project has to an official answer. Automattic's earlier Automattic/wordpress-mcp was archived in January 2026 and points at the adapter now. If you run your own sites and you are happy adding a plugin, start there — it is free, it is maintained by the people who maintain WordPress, and this page is not trying to talk you out of it.

The second row runs the server as a process on your laptop. It reaches the site over the REST API with an application password held in your environment, so the site stays untouched. The catch is distribution: a local server is configured per machine, and a credential per site per machine multiplies quickly. Five clients across two laptops is ten places a password lives. That is fine for one developer and unpleasant for a team.

The third row moves the same REST-API approach to a server you connect over HTTP with OAuth. The site still gets nothing installed, the credential lives in one place, and ChatGPT and claude.ai can reach it without anything running locally — which matters, because the web clients cannot spawn a local process at all. This is what wpreef does.

What you give up

A REST bridge can only do what the REST API exposes, and the REST API does not expose everything. This is the honest ceiling on the no-plugin approach, and most articles on the subject skip it. The full line — every area core covers, and the short list where it stops — is set out in what the REST API can and cannot change.

Posts, pages, custom post types, media, taxonomies, comments and users are all covered. Block themes are covered, because templates and template parts are post types. What is not reachable over core REST routes: switching the active theme, running plugin or core updates, reading and writing Customizer values, and most options that no plugin registered a route for. There is no REST endpoint for them, so no bridge can reach them, however it is built.

The second gap is the interesting one. When a plugin registers an ability through the Abilities API, an in-site MCP server can offer it immediately. A remote bridge cannot see that ability unless the plugin also exposed a REST route. So if your workflow depends on WooCommerce products, ACF field groups, or a form plugin's submissions, an in-site server will reach them sooner.

wpreef's answer to the first gap is a small optional plugin, wpreef-bridge, GPL-2.0-or-later, that adds routes for theme switching, updates, Customizer values and settings. It is optional on purpose: everything in the tool list that is not marked as a companion ability works with nothing installed. If you never need to switch a theme from a chat window, never install it.

Setting it up with nothing installed

Four things have to be true, and they are all on the WordPress side.

  1. The site is on HTTPS.

    WordPress refuses to issue application passwords over plain HTTP unless WP_ENVIRONMENT_TYPE is set to local. If the certificate is broken, fix that before anything else — nothing downstream will work.

  2. /wp-json/ returns JSON.

    Open it in a browser. A 404, a login page, or an HTML error means something is refusing REST requests. Usually that is a security plugin with a "disable REST API" switch, or a host-level rule.

  3. An application password exists.

    Users → Profile → Application Passwords, on the account you want the assistant to act as. It is shown once. The username is the WordPress login name — not the display name, and not the email address.

  4. The role is the smallest one that does the job.

    More on this below.

Then point the client at the server. For a remote connector that means pasting an HTTPS URL; for wpreef it ends in /mcp and never /sse. That suffix is worth knowing about because getting it wrong produces a connection error that does not name the cause: /sse was the older MCP transport, Streamable HTTP replaced it, and a URL with the old suffix simply fails.

Which role the credential should have

Give it Editor if the work is posts, pages, media and comments. That is most work.

Give it Administrator only if design is in scope. Templates, menus, navigation and widgets all sit behind edit_theme_options, which is an administrator capability — an Editor account will be refused, and the error names the missing capability rather than failing vaguely. There is no middle role that covers block-theme design without also covering plugin installation, which is a real limitation of the WordPress capability model and not something a connector can work around.

The credential is revocable from the same screen that issued it, which is the practical argument when a client asks what happens if they change their mind. Revoking the application password ends the access immediately and independently of whatever else the account can do.

The four failures that actually happen

Three of these are on the WordPress side, and one of them is genuinely hard to diagnose. Each one, with the error codes and the server-side fixes, is worked through in why the REST API reads but will not write.

The REST API is blocked. Covered above. A security plugin or a host rule. Allow requests that carry an Authorization header, at minimum.

The Authorization header is stripped before PHP sees it. Some Apache and FastCGI configurations drop it. Every request then arrives unauthenticated, and WordPress answers with the permissions of a logged-out visitor — so everything reads and nothing writes, which looks like a permissions problem and is not one. The test that separates them: send a request with a deliberately wrong application password. If WordPress says the credential is invalid, the header arrived and the problem is elsewhere. If it says the credential is missing, the header was stripped.

The role is too small. See above. The error names the capability.

The connection is read-only. If the write scope was not granted, a well-built server registers no write tools at all rather than offering tools that fail later. Ask the assistant for site information; it should report the read-only state rather than leaving you guessing.

When you should install a plugin anyway

Use an in-site MCP server when you need abilities that other plugins register, when you are the site owner and the plugin count is your own business, or when you want the official path and are willing to follow core's release cycle to get it.

Keep the site untouched when the site is a client's, when the host maintains an allowlist, when a security reviewer has to sign off on new code, or when you are looking after enough sites that "install and configure a plugin on each one" is the actual cost rather than a footnote. That last case has a page of its own: managing client WordPress sites with an assistant.

Neither is the clever answer. They are different trades, and which one is right depends entirely on whose site it is.

Questions people ask

Does connecting an AI assistant to WordPress require a plugin?

No. The REST API has been in core since WordPress 4.7 and application passwords since 5.6, so an external MCP server can read and write a site with nothing installed. A plugin is required only for things core exposes no REST route for, such as switching themes or running updates.

Is an application password safe to hand to an AI assistant?

It is safer than the account password, because it authenticates only REST requests, never wp-admin login, and can be revoked on its own from Users → Profile without touching the account. The real boundary is the role you attach it to, so issue it on an account with the smallest role that does the work.

Can ChatGPT and Claude use a local MCP server?

Claude Code and Cursor can, because they run on your machine and can start a process. The ChatGPT and Claude web clients cannot — they need a remote server reachable over HTTPS, which is why hosted connectors exist.

Why does my connector read but never write?

Usually the Authorization header is being stripped by the web server before PHP receives it. Test with a deliberately wrong password: "invalid" proves the header arrived, "missing" proves it did not. The other two causes are a read-only connection scope and a role without the capability the tool needs.

← All articles