WordPress Self-Hosted MCP Server
by mdsakib-dr
README.md
# WordPress MCP Server (for Prefect Horizon)
An MCP server that lets an AI client **draft, publish, update, and search posts**
on your self-hosted WordPress site (the kind hosted on Hostinger). It uses the
built-in WordPress REST API and authenticates with an **Application Password** —
no plugins to install on WordPress.
Built with the standalone [`fastmcp`](https://gofastmcp.com) package so it deploys
cleanly on **Prefect Horizon** (`fastmcp.app`). Horizon serves the HTTP transport
for you; you just point it at the `mcp` object.
- **Entrypoint:** `main.py:mcp`
## Tools
| Tool | What it does |
|------|--------------|
| `wordpress_create_draft` | Create a new post saved as a **draft** (private, for review) |
| `wordpress_publish_post` | Create a new post and **publish** it immediately (live) |
| `wordpress_update_post` | Edit an existing post by ID, or change its status (draft → publish) |
| `wordpress_list_posts` | List / search posts to find IDs, review drafts, or check content |
---
## Step 1 — Create a WordPress Application Password
Application Passwords are built into WordPress 5.6+ and let an app authenticate
without your real password.
1. Log in to WordPress admin (`https://yoursite.com/wp-admin`).
2. Go to **Users → Profile** (or **Users → All Users → your user → Edit**).
3. Scroll to **Application Passwords**.
4. Enter a name like `Horizon MCP` and click **Add New Application Password**.
5. Copy the generated password (e.g. `abcd EFGH ijkl MNOP qrst UVWX`). You only see
it once. Spaces are fine — the server strips them.
Your account needs the **Editor** or **Administrator** role to publish.
**If the Application Passwords section is missing on Hostinger:** it's usually
because the site isn't on HTTPS, or a security plugin (Wordfence, iThemes, etc.)
disabled the feature or the REST API. Make sure the site loads over `https://`
and check the plugin's REST API / Application Password settings.
---
## Step 2 — Put this repo on GitHub
Horizon deploys from a GitHub repository.
```bash
cd wordpress-mcp
git init
git add main.py requirements.txt pyproject.toml README.md .gitignore
git commit -m "WordPress MCP server for Horizon"
# create a repo on GitHub, then:
git remote add origin https://github.com/<you>/wordpress-mcp.git
git push -u origin main
```
> Do **not** commit real credentials. There are none in the repo — they're supplied
> as environment variables in Horizon (Step 3). The included `.gitignore` keeps
> `.env` and the local virtualenv out.
---
## Step 3 — Deploy on Prefect Horizon
1. Go to **https://horizon.prefect.io** and sign in with GitHub.
2. Click to create/deploy a new server and **select this repository**.
3. Configure:
- **Name:** e.g. `wordpress` (your URL becomes `https://wordpress.fastmcp.app/mcp`)
- **Entrypoint:** `main.py:mcp`
- **Authentication:** toggle on if you want only your org's authenticated users
to be able to connect (recommended).
- **Environment variables / secrets:**
| Key | Value |
|-----|-------|
| `WORDPRESS_URL` | `https://yoursite.com` |
| `WORDPRESS_USERNAME` | your WordPress login name (not the email) |
| `WORDPRESS_APP_PASSWORD` | the Application Password from Step 1 |
4. Click **Deploy**. Horizon clones the repo, installs `requirements.txt`, and
brings the server up (~1 minute).
5. Test it with Horizon's built-in **Inspector** (call `wordpress_list_posts`) or
**ChatMCP** before wiring up other clients.
Your MCP endpoint will be **`https://<name>.fastmcp.app/mcp`**. Add that URL as a
remote MCP server in any client that supports remote MCP (including Claude).
---
## Run it locally (optional)
```bash
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
export WORDPRESS_URL="https://yoursite.com"
export WORDPRESS_USERNAME="your_wp_login"
export WORDPRESS_APP_PASSWORD="abcd EFGH ijkl MNOP qrst UVWX"
python main.py # runs over stdio for a local client
```
---
## Notes
- **Content accepts HTML.** `<h2>…</h2><p>…</p>` produces formatted posts; plain
text works too.
- **Categories and tags are numeric IDs**, not names. (Adding name→ID lookup is an
easy future addition.)
- **Publishing vs. drafting:** `wordpress_create_draft` stages content; flip a draft
live later with `wordpress_update_post` (`status: "publish"`).
- **Security:** the Application Password lives only in Horizon's env/secrets, never
in the repo. Revoke it anytime under **Users → Profile** without affecting login.
## Troubleshooting
| Symptom | Likely cause / fix |
|---------|--------------------|
| `Authentication failed (401)` | Wrong username, or you used your login password instead of an Application Password. |
| `Permission denied (403)` | User role too low to publish, or a security plugin blocks the REST API. |
| `Not found (404)` on every call | `/wp-json/` disabled/blocked, or `WORDPRESS_URL` wrong. Open `https://yoursite.com/wp-json/` in a browser — you should see JSON. |
| `credentials are not configured` | One of the three env vars isn't set in Horizon's server settings. |
| Build fails on Horizon | Confirm the entrypoint is exactly `main.py:mcp` and `requirements.txt` is in the repo root. |
This server cannot be deployed
Maintenance
ActivitySlowing
ResponsivenessNo issues