How I Connected ChatGPT to WordPress with a Secure, Self-Hosted MCP Server

Have questions, ideas to share, or just want to connect? I’d love to hear from you! Check out my About Page to learn more about me or connect with me.

I recently wanted a faster and more consistent way to work with my WordPress sites from ChatGPT.

Using the WordPress editor in a browser works, of course, but it becomes repetitive when the workflow is always the same: prepare an article, create the post, make sure it remains a draft, select the right category, and then open WordPress to review everything manually.

I also work from more than one computer. A local script with credentials stored on one machine would solve only part of the problem. I wanted something available wherever I used ChatGPT, without copying WordPress passwords between computers or giving an AI unrestricted access to publish content.

The solution was a small, self-hosted Model Context Protocol server—an MCP server—that sits between ChatGPT and WordPress.

The result is a private WordPress manager that can:

  • list the WordPress sites I have configured;
  • verify a site’s WordPress connection;
  • retrieve categories;
  • create new posts as drafts only;
  • preview category changes before applying them;
  • support multiple WordPress sites through one secure endpoint.
Publishing is deliberately not implemented. Even if I accidentally ask ChatGPT to publish something, the server simply has no publishing function available.

Why an MCP server?

MCP provides a standard way for ChatGPT and other compatible AI clients to discover and call tools exposed by a remote service. Instead of giving ChatGPT a general-purpose shell, direct database access, or my WordPress administrator password, I can expose a small set of carefully designed actions.

That separation matters.

ChatGPT decides when an available tool may help with a request, but the MCP server remains responsible for authentication, authorization, validation, and the actual WordPress operation. OpenAI’s guidance similarly emphasizes that authorization must be enforced by the MCP server rather than left to the model.

For my implementation, every operation is intentionally narrow. The create_draft tool always sends status: draft to WordPress. The taxonomy workflow separates previewing a change from applying it. Credentials remain on my server and are never returned to ChatGPT.

The options I considered

There are several reasonable ways to connect an AI assistant to WordPress.

Use the WordPress browser interface
This is the simplest option and requires no development. It is ideal for occasional use, but it is slower for repeatable workflows and still requires manually moving content between ChatGPT and WordPress.

Call the WordPress REST API from a local script
A local Python, PowerShell, or command-line script can create drafts very efficiently. WordPress Application Passwords make this possible without using the account’s normal login password.

The limitation is portability. Credentials and scripts must be configured separately on every computer, and a web-based ChatGPT session cannot automatically access environment variables stored on a different machine.

Use an automation platform
Tools such as n8n, Make, or Zapier can receive content and send it to WordPress. This is a good choice for fixed workflows, scheduled jobs, and integrations involving several services. It can become less natural when I want an interactive conversation in which the assistant first reads categories, asks for confirmation, and then performs one controlled action.

Host a private MCP server
This requires the most initial setup, but it provides the experience I wanted:

  • one HTTPS endpoint available from multiple computers;
  • one service capable of managing multiple WordPress sites;
  • OAuth login instead of sharing WordPress credentials with ChatGPT;
  • narrowly defined read and write tools;
  • draft-only safeguards enforced in code;
  • centralized logging and configuration.
For my situation, the hosted MCP approach was the best long-term fit.

The architecture

The finished service uses these components:

  • ChatGPT Developer Mode connects to the public MCP endpoint.
  • Auth0 provides OAuth authentication and Google sign-in.
  • Nginx Proxy Manager terminates HTTPS and forwards requests to the MCP container.
  • FastMCP implements the MCP protocol and exposes the WordPress tools.
  • Redis stores encrypted OAuth client and session state.
  • WordPress REST API performs the final site operations using an Application Password.
The request path looks like this:
ChatGPT → HTTPS MCP endpoint → Auth0 authentication → FastMCP tool → WordPress REST API

The MCP and Redis containers share a private Docker network. Only Nginx Proxy Manager can reach the MCP service from outside that internal network. Redis is never exposed publicly.

What you need before starting

You will need:

  • a server capable of running Docker Compose;
  • a domain or subdomain pointed to that server;
  • Nginx Proxy Manager or another HTTPS reverse proxy;
  • an Auth0 tenant;
  • administrator access to each WordPress site during setup;
  • a WordPress user with only the permissions the integration needs;
  • ChatGPT access that supports Developer Mode and custom MCP apps.
I used a dedicated subdomain similar to wpmcp.example.com. The examples below use that placeholder—replace it with your own hostname.

Step 1: Prepare WordPress access

WordPress includes Application Passwords for API authentication. An Application Password is separate from the user’s regular WordPress password and can be revoked independently.

In WordPress:

  1. Create or select the user the integration will use.
  2. Give that user only the role and capabilities required for the intended operations.
  3. Open the user’s profile.
  4. Find Application Passwords.
  5. Create a password named something recognizable, such as WordPress MCP.
  6. Store the generated value securely.
Do not place this password in source control, a public document, or a ChatGPT prompt. Repeat this process for each WordPress site you want the MCP server to manage.

Step 2: Create the DNS record

Create an A record for the MCP hostname and point it to the public IP address of the Docker server.

For example:

  • Type: A
  • Name: wpmcp
  • Value: YOUR_SERVER_IP
Wait until the hostname resolves publicly before requesting the TLS certificate.

Step 3: Configure Auth0

Auth0 acts as the identity provider. The WordPress credentials never pass through Auth0; Auth0 only determines who is allowed to use the MCP service.

Create a custom Auth0 API with an identifier matching the public MCP origin, for example:
[https://wpmcp.example.com](https://wpmcp.example.com)

I defined these application permissions to document the service’s intended capabilities:

  • wordpress:read
  • wordpress:draft
  • wordpress:taxonomy
Next, create a Regular Web Application for the MCP server. A server-side application is appropriate because the MCP service securely stores an Auth0 Client Secret.

Configure these URLs:

  • Allowed Callback URL: [https://wpmcp.example.com/auth/callback](https://wpmcp.example.com/auth/callback)
  • Allowed Logout URL: [https://wpmcp.example.com](https://wpmcp.example.com)
  • Allowed Web Origin: [https://wpmcp.example.com](https://wpmcp.example.com)
Enable the Authorization Code flow and refresh tokens. I also enabled Google as an Auth0 social connection, although another supported identity provider could be used.

Finally, identify the immutable Auth0 sub value for each person who should be allowed to use the service. The server uses these subject IDs as an explicit allowlist. This is more reliable than authorizing by a changeable email address.

Step 4: Create the multi-site configuration

The MCP server reads its WordPress sites from a JSON configuration file mounted read-only into the container.

Here is a simplified example:

JSON
 
{
  "sites": [
    {
      "id": "main-blog",
      "label": "My Main Blog",
      "base_url": "https://example.com/blog",
      "username": "api-user",
      "app_password": "WORDPRESS_APPLICATION_PASSWORD",
      "primary_categories": {
        "AI & Emerging Technology": 101,
        "Modern Workplace & IT": 102,
        "Perspectives": 103,
        "Guides & Resources": 104,
        "My Journey": 105
      }
    }
  ]
}
Each site has a short id used in tool calls, a human-readable label, its WordPress base URL, its API credentials, and its approved primary categories.

The category map also lets the server enforce a one-primary-category rule. When a primary category changes, the server removes any other category from the designated primary set before adding the selected one.

Protect this file carefully:

Bash
 
chmod 600 config/sites.json

Step 5: Configure the environment

Keep operational secrets in a local .env file that is never committed to Git.

The configuration includes:

Code snippet
 
AUTH0_DOMAIN=YOUR_TENANT.us.auth0.com
AUTH0_CLIENT_ID=YOUR_AUTH0_CLIENT_ID
AUTH0_CLIENT_SECRET=YOUR_AUTH0_CLIENT_SECRET
AUTH0_AUDIENCE=https://wpmcp.example.com
PUBLIC_BASE_URL=https://wpmcp.example.com
AUTHORIZED_SUBJECTS=AUTH0_SUBJECT_ID_1,AUTH0_SUBJECT_ID_2

JWT_SIGNING_KEY=GENERATED_RANDOM_VALUE
STORAGE_ENCRYPTION_KEY=GENERATED_FERNET_KEY
PREVIEW_SIGNING_KEY=GENERATED_RANDOM_VALUE
Generate each cryptographic value independently. The JWT key signs the tokens issued by the MCP authorization proxy, the storage key encrypts OAuth state stored in Redis, and the preview key signs short-lived taxonomy confirmation tokens.

Protect the environment file:

Bash
 
chmod 600 .env

Step 6: Run the service with Docker Compose

My Docker Compose stack contains two services:

  • wpmcp-server, running the Python/FastMCP application;
  • wpmcp-redis, storing encrypted OAuth state.
The MCP service listens internally on port 8134. You may use another unused port, but the reverse proxy and health check must match it.

The important deployment characteristics are:

  • Redis is attached only to an internal Docker network.
  • The MCP server joins both the internal network and the existing Nginx Proxy Manager network.
  • No MCP port is published directly on the Docker host.
  • The WordPress site configuration is mounted read-only.
  • The containers run with restricted Linux capabilities.
  • Health checks verify both Redis and the MCP application.
Start the stack:

Bash
 
docker compose config
docker compose up -d --build
docker compose ps
The MCP health endpoint should return a small JSON response indicating that the service is healthy.

Step 7: Add the proxy host in Nginx Proxy Manager

In Nginx Proxy Manager, create a new Proxy Host:

  • Domain: wpmcp.example.com
  • Scheme: http
  • Forward Hostname: wpmcp-server
  • Forward Port: 8134
Enable WebSocket support and the usual protection options. Request a Let’s Encrypt certificate, then enable Force SSL and HTTP/2.

The public endpoints will include:

  • [https://wpmcp.example.com/health](https://wpmcp.example.com/health)
  • [https://wpmcp.example.com/mcp](https://wpmcp.example.com/mcp)
Only the /mcp endpoint is entered into ChatGPT.

Step 8: Design tools with safety built in

The server exposes six focused tools:

  • list_sites
  • test_site
  • list_categories
  • create_draft
  • preview_taxonomy
  • apply_taxonomy
The safety design is as important as the functionality.

  • list_sites never returns credentials.
  • test_site performs only a read operation.
  • create_draft hard-codes the WordPress status to draft and verifies the status returned by WordPress. There is no publish tool.
Taxonomy changes use a two-stage process. preview_taxonomy reads the current post, calculates the proposed categories, and returns a signed token that expires after a short period. apply_taxonomy accepts only that token, verifies that it belongs to the same authenticated user, and refuses to proceed if the post changed after the preview.

Every write is also recorded in a server-side audit log.

Step 9: Connect the MCP server to ChatGPT

In ChatGPT on the web:

  1. Enable Developer Mode in Settings.
  2. Open the Plugins area.
  3. Create a custom app using the MCP URL: [https://wpmcp.example.com/mcp](https://wpmcp.example.com/mcp)
  4. Select OAuth authentication.
  5. Complete the Auth0 sign-in and consent process.
  6. Allow ChatGPT to scan the MCP tools.
  7. Confirm that the six expected actions are displayed.
  8. Create or save the custom app.
The custom WordPress manager can then be selected in a Work conversation with an @ mention.

Step 10: Validate safely

Begin with a read-only request:

List my configured WordPress sites. Do not create or change anything.
Then test the WordPress connection for one site and list its categories. Only after those read-only checks succeed should you create a disposable draft.

Confirm in WordPress that:

  • the post status is Draft;
  • the title and content are correct;
  • only the intended primary category is selected;
  • no post was published;
  • the write appears in the MCP audit log.

What I gained from this approach

The biggest benefit is not merely speed. It is having a repeatable boundary between conversation and action.

I can discuss and refine an article in ChatGPT, then ask the same assistant to create a draft without copying credentials or opening the WordPress editor. I can work from another computer because the integration lives on my server rather than one workstation. I can add more WordPress sites by extending one protected configuration file.

Most importantly, the server—not the prompt—enforces the rules that matter to me. Publishing is unavailable. Credentials stay private. Taxonomy changes require confirmation. Access is restricted to approved Auth0 identities. Writes are logged.

That is the real value of MCP for me: not giving AI unlimited access, but giving it a small set of useful, well-governed tools.

Reference documentation