Back to Article List

WordPress MCP on a VPS: Let AI Act Without the Keys

WordPress MCP on a VPS: Let AI Act Without the Keys - WordPress MCP on a VPS: Let AI Act Without the Keys

You can let an AI agent draft posts, tidy tags and check your site's health without giving it your admin password or a wide-open API key. The trick is to run your own Model Context Protocol (MCP) server on a VPS you control, expose only the specific actions you want, and authenticate every request with a token you can revoke in seconds.

MCP is just a standard way for AI clients (Claude, ChatGPT desktop, Cursor and friends) to call tools on your behalf. On WordPress, that means an agent can say "publish this draft" or "list plugins needing updates" through a defined interface instead of poking at your dashboard. Self-hosting it keeps that interface, and your content, on infrastructure you own.

Here is how to set it up on a VPS, scope it tightly, and stop it leaking anything that shouldn't leave the building.

Why self-host the MCP server instead of using a hosted one

Self-hosting puts the boundary in your hands: you decide which tools exist, which user the agent acts as, and where the logs live. A managed MCP endpoint is convenient, but you're trusting someone else's scoping and someone else's idea of what "read-only" means.

There's a second reason this matters right now. Cloudflare has started classifying agent traffic at the edge and tagging it in your request logs — you'll see the bot category and a signed Signature-Agent header on well-behaved crawlers, and you can write a WAF rule that matches on cf.bot_management.verified_bot or the request's user-agent to sort real agents from noise. If agents are going to hit your site anyway, you want to be the one defining the front door, not reacting to it.

For a small business the calculus is simple. Your WordPress database holds customer emails, order notes, draft pricing pages and unpublished announcements. None of that should be reachable by an agent just because it can write a blog post. Running the server yourself, on a VPS, lets you draw that line explicitly. If you host with an EU provider like TPC Hosting, the data also stays inside the GDPR perimeter you already promised your customers.

What you need before you start

You need a VPS with root access, a WordPress install you can reach over SSH, and a way to create a scoped application password. That's the whole shopping list.

Concretely, get these in place first:

  • A VPS running a current Linux (Ubuntu 22.04/24.04 or Debian 12 are fine) with Node.js 20+ or PHP 8.2+, depending on which MCP bridge you pick.
  • WordPress 6.4 or newer with the REST API reachable at /wp-json/.
  • A dedicated WordPress user for the agent — not your admin account. Give it the lowest role that still gets the job done (Author for drafting, Editor if it must publish).
  • An Application Password for that user (Users → Profile → Application Passwords). This is a per-application credential you can revoke without touching the main login.
  • A subdomain and a TLS certificate for the MCP endpoint, so agent traffic never travels in the clear.

Do not reuse your personal admin password anywhere in this chain. The entire point is that the agent's credential is disposable and narrow.

Standing up the MCP server on your VPS

The fastest working setup is a thin MCP server that translates tool calls into scoped WordPress REST requests, running behind Nginx with TLS. You install it, point it at your site with the application password, and expose only the tools you've whitelisted.

You don't have to write the bridge yourself. Automattic ships an official wordpress-mcp plugin that exposes REST-backed tools over MCP, and there are solid community Node bridges such as server-wp-mcp that run entirely outside WordPress and speak to /wp-json/ with an application password. The Node route keeps everything off the WordPress box, so start there unless you need custom tools that only a plugin can register.

A typical Node install looks like this. Create an isolated directory and user, pull the bridge, then run it under a process manager so it survives reboots:

  • Create a system user: adduser --system --group mcp
  • Fetch the bridge into /opt/wp-mcp: cd /opt/wp-mcp && git clone https://github.com/Automattic/wordpress-mcp-clients.git . && npm ci (or npx server-wp-mcp if you prefer the community package). Set the site URL and application password as environment variables — never hardcode them in a tracked file.
  • Run it under systemd or pm2 bound to 127.0.0.1:3000, so it's not directly exposed to the internet.
  • Put Nginx in front as a reverse proxy on your MCP subdomain, terminate TLS there, and forward to localhost.

Keep the environment file at /opt/wp-mcp/.env with permissions locked to chmod 600 and owned by the mcp user. A sample:

VariableValue
WP_SITE_URLhttps://yoursite.com
WP_USERNAMEagent-bot
WP_APP_PASSWORDxxxx xxxx xxxx xxxx
MCP_ALLOWED_TOOLSlist_posts,create_draft,get_updates
MCP_BIND127.0.0.1:3000

Once it's running, confirm the bind actually took. Run ss -tlnp | grep 3000 — you want to see 127.0.0.1:3000, not 0.0.0.0:3000. If it shows the wildcard address, the process is listening on every interface and a scanner can reach it directly; fix the bind before you go further.

On the client side, you point your AI tool at the subdomain, not at raw WordPress. For Claude Desktop or Cursor, add an entry to the MCP config — ~/.config/claude/claude_desktop_config.json on Linux or the equivalent Cursor settings:

FieldValue
typesse (or streamable-http)
urlhttps://mcp.yoursite.com/sse
header: AuthorizationBearer YOUR_MCP_TOKEN

Restart the client and it lists exactly the tools you whitelisted — nothing else. Binding to localhost and proxying through Nginx matters more than it looks. It means the only way in is through TLS and your token check, and you can reuse that Cloudflare classification: an Access policy on the MCP subdomain that only admits your own service token or IPs turns the front door into one you control. On a TPC VPS you have full root access to configure exactly this, and our engineers are on support around the clock if the reverse proxy fights you.

Scoping what the agent can actually do

Scope at three layers — the WordPress role, the allowed-tools list, and the token — so no single mistake hands over the whole site. If an agent can only see the tools you named, and those tools run as a low-privilege user, the blast radius stays tiny.

Start from "deny everything" and add back only what you need. A drafting assistant almost never needs delete or user-management powers. Map it out plainly:

Task the agent doesTools to allowWP role
Write and revise draftslist_posts, get_post, create_draft, update_draftAuthor
Publish approved content+ publish_postEditor
Report on updates and healthget_updates, list_pluginsAuthor (read-only calls)
Never allow blindlydelete_post, manage_users, edit_options

Two rules save you from most trouble. Keep publish behind a human by default — let the agent create drafts and have a person hit publish, at least until you trust the workflow. And never expose edit_options or anything touching users; a compromised agent that can change your site URL or create an admin is a bad day.

Whitelist by name, not by wildcard. MCP_ALLOWED_TOOLS=list_posts,create_draft is a firewall you can read at a glance. A regex that "allows most things" is how the wrong tool slips through in six months when you've forgotten the details.

Keeping non-public data locked down

The agent should only ever see published, non-sensitive content — enforce that in the tool code, not just by trusting the role. Application passwords still authenticate as a real user, so an Author-level token can read private drafts and some meta unless you filter it.

Do the filtering where it can't be argued with:

  • Strip fields at the server: in your MCP bridge, append the REST _fields query parameter to every read — for example GET /wp-json/wp/v2/posts?status=publish&_fields=id,title,slug,content,status — so the response never carries author objects, private meta or emails. Whitelist those field names in code so a tool can't quietly ask for more.
  • Block sensitive post types: refuse tool calls for shop_order, attachment metadata, or any custom type holding personal data.
  • Rate-limit and log: cap requests per minute at Nginx and log every tool call with a timestamp and the tool name, so you can audit what the agent actually did.
  • Rotate the app password on a schedule and immediately if a client machine is lost. Revoking it in WordPress kills the agent's access without disturbing your own login.

Add a firewall rule too. Only your Nginx front end needs to reach port 3000, and only known clients need to reach the subdomain — an allowlist on the MCP endpoint (by IP or Cloudflare Access) means a random scanner never gets to try a token at all. Because a TPC VPS is EU-hosted, keeping this data on-server also keeps your GDPR story clean: personal data isn't quietly shipped to a third-party MCP host you can't audit.

A quick pre-launch checklist

Before you point a live agent at your production site, walk this list once — it takes ten minutes and prevents the obvious mistakes.

  • Agent uses a dedicated low-role user, never admin.
  • Credential is an Application Password, revocable and rotated.
  • MCP server binds to localhost (confirmed with ss -tlnp), TLS terminated at Nginx.
  • Allowed tools are a named whitelist, no wildcards.
  • Publish stays behind a human approval step.
  • Response fields are filtered via _fields; sensitive post types blocked.
  • Endpoint is IP-restricted or behind an access proxy.
  • Every tool call is logged with a timestamp.

Test against a staging copy first. Spin up a clone, run the agent through its real tasks, and read the logs to confirm it only touched what you expected. TPC gives you 30 days to back out and free migration if you want to move an existing site onto a VPS to try this properly, so there's no reason to experiment on the site paying your bills.

Where this is heading

Agent traffic is becoming ordinary, and the sites that handle it well will be the ones that decided the rules up front. Self-hosting your MCP server is the difference between offering the AI a labelled set of levers and leaving the whole control panel unlocked.

You don't need a big platform to do this safely. A modest VPS, a scoped token, and a whitelist of tools cover the real risk. Build the front door yourself, keep the private data behind it, and let the agents do the boring work.

FAQ

Do I need a plugin to run WordPress MCP, or is the VPS server enough?

The VPS-hosted MCP bridge is enough, because it talks to WordPress over the standard REST API using an application password. Community Node bridges like server-wp-mcp run entirely outside WordPress. Add Automattic's wordpress-mcp plugin only if you need custom tools the REST API doesn't cover — for drafting, listing and update checks the external bridge handles everything.

Is an Application Password safe to give an AI agent?

Yes, when it belongs to a dedicated low-privilege user and is scoped by your tool whitelist. Application passwords authenticate as that specific user only, can be revoked individually without touching your main login, and should be rotated on a schedule — treat them as disposable, not permanent.

Can the AI agent read my private drafts or customer data?

Only if you let it — filter the response fields and block sensitive post types in the MCP bridge itself. Don't rely on the WordPress role alone, since an Author-level token can still read private drafts; use the REST _fields parameter to return only title, slug, content and status, and strip emails and personal data at the server before anything reaches the model.

Why does Cloudflare detecting MCP traffic matter to me?

It puts a category and a Signature-Agent header on agent requests in your logs, so you can tell real agents from noise and write WAF or Access rules to match them. That's a reason to define your own MCP front door now — a locked-down, self-hosted endpoint fronted by an Access policy — rather than reacting later when agents are already hitting your site.

Should the agent be allowed to publish posts automatically?

Keep publishing behind a human by default until you trust the workflow. Let the agent create and revise drafts, then have a person approve and publish; you can grant a publish tool later once your logs show it behaves as expected.