Manage your entire WordPress site through conversation with AI. This complete guide includes all the code, step-by-step instructions, and troubleshooting you need. Expected setup time: 30 minutes.
๐ Table of Contents
๐คWhat is an MCP Server?
MCP (Model Context Protocol) is an open standard that allows AI assistants like Claude to connect directly to external systems.
Simple Explanation:
- Without MCP: You ask Claude to write content, then YOU manually copy/paste it into WordPress
- With MCP: You ask Claude to write and publish content, and Claude does EVERYTHING automatically
Think of it like giving Claude a key to your WordPress admin panel – but in a secure, controlled way.
๐ฏWhat You’ll Build
By the end of this guide, you’ll be able to tell Claude things like:
“Create a blog post titled ‘Top 10 AI Tools’ with an introduction, 10 tools with descriptions, and publish it”
And Claude will:
- Write the entire post
- Format it properly
- Publish it to your WordPress site
- Give you the link
๐Create Content
Write and publish blog posts and pages through conversation
โ๏ธEdit Posts
Update existing content, change titles, or fix typos instantly
๐Manage Content
List posts, check status, view publication dates
๐๏ธDelete Content
Remove outdated posts or clean up drafts
๐Prerequisites Checklist
Before you begin, make sure you have everything you need. Check each item:
Test by running:
python --version in your terminalDownload from python.org if needed
Download from claude.ai/download
Most WordPress sites support this by default
You need to be able to log into wp-admin
To complete the setup without rushing
You’ll need to run a few simple commands
Verify Your Python Installation
Open your terminal (Command Prompt on Windows, Terminal on Mac) and run:
python --version
You should see something like: Python 3.8.0 or higher
๐ก Tip: If python --version doesn’t work, try python3 --version on Mac
โ๏ธComplete Setup Guide
Step 1: Create Your Project Folder
First, create a folder where all your MCP server files will live.
Create a folder at:
C:\Users\YourName\wordpress-mcp\
Replace YourName with your actual Windows username
Create a folder at:
/Users/YourName/wordpress-mcp/
Or use the terminal:
mkdir -p ~/wordpress-mcp
cd ~/wordpress-mcp
Step 2: Create the Server Files
You need to create 2 files in your wordpress-mcp folder. I’ll provide the complete code for both.
File 1: requirements.txt
This file lists the Python libraries we need.
mcp>=1.0.0
httpx>=0.27.0
python-dotenv>=1.0.0
๐ก How to create this file:
- Open Notepad (Windows) or TextEdit (Mac) or any text editor
- Copy the 3 lines above
- Paste them into the editor
- Save as
requirements.txtin your wordpress-mcp folder
File 2: wordpress_mcp_server.py
This is the main server code. Copy ALL of this code:
#!/usr/bin/env python3
"""
WordPress MCP Server
Connects to WordPress REST API to manage content
"""
import asyncio
import os
import httpx
from typing import Any
from mcp.server import Server
from mcp.types import Tool, TextContent
import mcp.server.stdio
from dotenv import load_dotenv
# Load environment variables from .env file
load_dotenv()
# WordPress configuration
WP_SITE_URL = os.getenv("WP_SITE_URL", "")
WP_USERNAME = os.getenv("WP_USERNAME", "")
WP_APP_PASSWORD = os.getenv("WP_APP_PASSWORD", "")
server = Server("wordpress-mcp")
async def wp_request(method: str, endpoint: str, data: dict = None) -> dict:
"""Make authenticated request to WordPress REST API"""
url = f"{WP_SITE_URL}/wp-json/wp/v2/{endpoint}"
auth = (WP_USERNAME, WP_APP_PASSWORD)
async with httpx.AsyncClient(timeout=30.0) as client:
if method == "GET":
response = await client.get(url, auth=auth)
elif method == "POST":
response = await client.post(url, auth=auth, json=data)
elif method == "PUT":
response = await client.put(url, auth=auth, json=data)
elif method == "DELETE":
response = await client.delete(url, auth=auth)
response.raise_for_status()
return response.json()
@server.list_tools()
async def list_tools() -> list[Tool]:
"""List available WordPress management tools"""
return [
Tool(
name="list_posts",
description="List WordPress posts with optional filters",
inputSchema={
"type": "object",
"properties": {
"per_page": {
"type": "number",
"description": "Number of posts to retrieve (max 100)",
"default": 10
},
"status": {
"type": "string",
"description": "Post status (publish, draft, pending, etc.)",
"default": "publish"
}
}
}
),
Tool(
name="get_post",
description="Get a specific WordPress post by ID",
inputSchema={
"type": "object",
"properties": {
"post_id": {
"type": "number",
"description": "The ID of the post to retrieve"
}
},
"required": ["post_id"]
}
),
Tool(
name="create_post",
description="Create a new WordPress post",
inputSchema={
"type": "object",
"properties": {
"title": {
"type": "string",
"description": "Post title"
},
"content": {
"type": "string",
"description": "Post content (HTML allowed)"
},
"status": {
"type": "string",
"description": "Post status: draft, publish, pending",
"default": "draft"
},
"excerpt": {
"type": "string",
"description": "Post excerpt (optional)"
}
},
"required": ["title", "content"]
}
),
Tool(
name="update_post",
description="Update an existing WordPress post",
inputSchema={
"type": "object",
"properties": {
"post_id": {
"type": "number",
"description": "The ID of the post to update"
},
"title": {
"type": "string",
"description": "New post title"
},
"content": {
"type": "string",
"description": "New post content"
},
"status": {
"type": "string",
"description": "New post status"
}
},
"required": ["post_id"]
}
),
Tool(
name="delete_post",
description="Delete a WordPress post",
inputSchema={
"type": "object",
"properties": {
"post_id": {
"type": "number",
"description": "The ID of the post to delete"
},
"force": {
"type": "boolean",
"description": "Whether to bypass trash and force deletion",
"default": False
}
},
"required": ["post_id"]
}
),
Tool(
name="list_pages",
description="List WordPress pages",
inputSchema={
"type": "object",
"properties": {
"per_page": {
"type": "number",
"description": "Number of pages to retrieve",
"default": 10
}
}
}
),
Tool(
name="create_page",
description="Create a new WordPress page",
inputSchema={
"type": "object",
"properties": {
"title": {
"type": "string",
"description": "Page title"
},
"content": {
"type": "string",
"description": "Page content (HTML allowed)"
},
"status": {
"type": "string",
"description": "Page status: draft, publish, pending",
"default": "draft"
}
},
"required": ["title", "content"]
}
),
Tool(
name="list_media",
description="List media files in WordPress library",
inputSchema={
"type": "object",
"properties": {
"per_page": {
"type": "number",
"description": "Number of media items to retrieve",
"default": 20
}
}
}
),
Tool(
name="get_site_info",
description="Get WordPress site information and settings",
inputSchema={
"type": "object",
"properties": {}
}
)
]
@server.call_tool()
async def call_tool(name: str, arguments: Any) -> list[TextContent]:
"""Execute WordPress management tools"""
try:
if name == "list_posts":
per_page = arguments.get("per_page", 10)
status = arguments.get("status", "publish")
posts = await wp_request("GET", f"posts?per_page={per_page}&status={status}")
result = f"Found {len(posts)} posts:\\n\\n"
for post in posts:
result += f"ID: {post['id']}\\n"
result += f"Title: {post['title']['rendered']}\\n"
result += f"Status: {post['status']}\\n"
result += f"Date: {post['date']}\\n"
result += f"Link: {post['link']}\\n\\n"
return [TextContent(type="text", text=result)]
elif name == "get_post":
post_id = arguments["post_id"]
post = await wp_request("GET", f"posts/{post_id}")
result = f"Post ID: {post['id']}\\n"
result += f"Title: {post['title']['rendered']}\\n"
result += f"Status: {post['status']}\\n"
result += f"Date: {post['date']}\\n"
result += f"Modified: {post['modified']}\\n"
result += f"Link: {post['link']}\\n\\n"
result += f"Content:\\n{post['content']['rendered']}\\n"
return [TextContent(type="text", text=result)]
elif name == "create_post":
data = {
"title": arguments["title"],
"content": arguments["content"],
"status": arguments.get("status", "draft")
}
if "excerpt" in arguments:
data["excerpt"] = arguments["excerpt"]
post = await wp_request("POST", "posts", data)
result = f"Post created successfully!\\n\\n"
result += f"ID: {post['id']}\\n"
result += f"Title: {post['title']['rendered']}\\n"
result += f"Status: {post['status']}\\n"
result += f"Link: {post['link']}\\n"
return [TextContent(type="text", text=result)]
elif name == "update_post":
post_id = arguments["post_id"]
data = {}
if "title" in arguments:
data["title"] = arguments["title"]
if "content" in arguments:
data["content"] = arguments["content"]
if "status" in arguments:
data["status"] = arguments["status"]
post = await wp_request("POST", f"posts/{post_id}", data)
result = f"Post updated successfully!\\n\\n"
result += f"ID: {post['id']}\\n"
result += f"Title: {post['title']['rendered']}\\n"
result += f"Status: {post['status']}\\n"
return [TextContent(type="text", text=result)]
elif name == "delete_post":
post_id = arguments["post_id"]
force = arguments.get("force", False)
endpoint = f"posts/{post_id}"
if force:
endpoint += "?force=true"
result_data = await wp_request("DELETE", endpoint)
result = f"Post deleted successfully!\\n"
result += f"ID: {post_id}\\n"
return [TextContent(type="text", text=result)]
elif name == "list_pages":
per_page = arguments.get("per_page", 10)
pages = await wp_request("GET", f"pages?per_page={per_page}")
result = f"Found {len(pages)} pages:\\n\\n"
for page in pages:
result += f"ID: {page['id']}\\n"
result += f"Title: {page['title']['rendered']}\\n"
result += f"Status: {page['status']}\\n"
result += f"Link: {page['link']}\\n\\n"
return [TextContent(type="text", text=result)]
elif name == "create_page":
data = {
"title": arguments["title"],
"content": arguments["content"],
"status": arguments.get("status", "draft")
}
page = await wp_request("POST", "pages", data)
result = f"Page created successfully!\\n\\n"
result += f"ID: {page['id']}\\n"
result += f"Title: {page['title']['rendered']}\\n"
result += f"Status: {page['status']}\\n"
result += f"Link: {page['link']}\\n"
return [TextContent(type="text", text=result)]
elif name == "list_media":
per_page = arguments.get("per_page", 20)
media = await wp_request("GET", f"media?per_page={per_page}")
result = f"Found {len(media)} media items:\\n\\n"
for item in media:
result += f"ID: {item['id']}\\n"
result += f"Title: {item['title']['rendered']}\\n"
result += f"Type: {item['mime_type']}\\n"
result += f"URL: {item['source_url']}\\n\\n"
return [TextContent(type="text", text=result)]
elif name == "get_site_info":
async with httpx.AsyncClient(timeout=30.0) as client:
response = await client.get(f"{WP_SITE_URL}/wp-json/")
site_info = response.json()
result = f"WordPress Site Information:\\n\\n"
result += f"Name: {site_info.get('name', 'N/A')}\\n"
result += f"Description: {site_info.get('description', 'N/A')}\\n"
result += f"URL: {site_info.get('url', 'N/A')}\\n"
result += f"Home: {site_info.get('home', 'N/A')}\\n"
return [TextContent(type="text", text=result)]
else:
return [TextContent(type="text", text=f"Unknown tool: {name}")]
except httpx.HTTPStatusError as e:
return [TextContent(type="text", text=f"HTTP Error: {e.response.status_code} - {e.response.text}")]
except Exception as e:
return [TextContent(type="text", text=f"Error: {str(e)}")]
async def main():
"""Run the MCP server"""
async with mcp.server.stdio.stdio_server() as (read_stream, write_stream):
await server.run(
read_stream,
write_stream,
server.create_initialization_options()
)
if __name__ == "__main__":
asyncio.run(main())
๐ก How to create this file:
- Open Notepad (Windows) or TextEdit (Mac) or VS Code
- Copy ALL the code above (scroll through the entire code block)
- Paste it into the editor
- Save as
wordpress_mcp_server.pyin your wordpress-mcp folder - Important: Make sure it’s saved as .py, not .txt!
Your Folder Should Now Look Like This:
Step 3: Install Python Dependencies
Now we need to install the Python libraries that the server needs.
- Open your terminal/command prompt
- Navigate to your wordpress-mcp folder:
(Replace with your actual path)cd C:\Users\YourName\wordpress-mcp - Run this command:
pip install -r requirements.txt - Wait for installation to complete (about 30 seconds)
โ Success looks like:
Successfully installed mcp-1.0.0 httpx-0.27.0 python-dotenv-1.0.0
โ ๏ธ If you see errors:
- Try using
pip3instead ofpip - Make sure you’re in the correct folder
- Check that Python is properly installed
Step 4: Generate WordPress Application Password
Now we need a special password from WordPress that the MCP server will use to connect.
Important: This is NOT your normal WordPress login password. It’s a special “Application Password” that’s more secure.
- Log into WordPress Admin
- Go to
yoursite.com/wp-admin - Enter your username and password
- Go to
- Go to Your Profile
- Click Users in the left sidebar
- Click on your username
- Or click Edit Profile at the top
- Scroll Down to Application Passwords
- Look for a section called Application Passwords
- It’s usually near the bottom of the page
- Create New Password
- In the “New Application Password Name” field, type:
Claude MCP Server - Click Add New Application Password
- In the “New Application Password Name” field, type:
- COPY THE PASSWORD IMMEDIATELY
- WordPress will show a password like:
xxxx xxxx xxxx xxxx xxxx xxxx - THIS IS VERY IMPORTANT: Copy it right away! You won’t see it again!
- Save it in a text file temporarily – you’ll need it in the next step
- WordPress will show a password like:
โ ๏ธ Can’t find Application Passwords?
- Make sure you’re running WordPress 5.6 or higher
- Some hosting providers disable this – contact their support
- Alternative: Install the “Application Passwords” plugin from WordPress.org
Step 5: Configure Claude Desktop
Now we’ll tell Claude Desktop about your MCP server.
- Windows: Press
Win + R, type%APPDATA%\Claude, press Enter
Then openclaude_desktop_config.json - Mac: In Finder, press
Cmd + Shift + G, paste:
~/Library/Application Support/Claude/
Then openclaude_desktop_config.json
Open it with Notepad (Windows) or TextEdit (Mac). You’ll see JSON code.
Add this configuration (replace the placeholder values with YOUR actual information):
{
"mcpServers": {
"wordpress": {
"command": "python",
"args": ["C:\\Users\\YourName\\wordpress-mcp\\wordpress_mcp_server.py"],
"env": {
"WP_SITE_URL": "https://yoursite.com",
"WP_USERNAME": "your_wordpress_username",
"WP_APP_PASSWORD": "xxxx xxxx xxxx xxxx xxxx xxxx"
}
}
}
}
โ ๏ธ IMPORTANT – Replace These Values:
C:\\Users\\YourName\\wordpress-mcp\\wordpress_mcp_server.pyโ Your ACTUAL full path
Note: On Windows, use double backslashes (\\)
On Mac: Use forward slashes:/Users/YourName/wordpress-mcp/wordpress_mcp_server.pyhttps://yoursite.comโ Your WordPress site URL (include https://)your_wordpress_usernameโ Your WordPress username (NOT email)xxxx xxxx xxxx xxxx xxxx xxxxโ The Application Password you copied
๐ก If you already have other MCP servers configured:
Add the wordpress section inside the existing mcpServers object, like this:
{
"mcpServers": {
"existing-server": {
...existing config...
},
"wordpress": {
...new wordpress config...
}
}
}
Make sure to save your changes!
Step 6: Restart Claude Desktop
- Completely quit Claude Desktop (don’t just close the window)
- On Windows: Right-click the system tray icon and choose “Quit”
- On Mac: Press
Cmd + Q - Wait 5 seconds
- Restart Claude Desktop
๐งชTesting Your Setup
Let’s make sure everything is working!
Test 1: Check Site Connection
Open a new conversation with Claude and type:
What’s my WordPress site information?
โ If it works, Claude will respond with:
- Your site name
- Your site description
- Your site URL
Congrats! Your MCP server is working! ๐
Test 2: List Posts
Try this command:
List my recent WordPress posts
Claude should show you a list of your posts with titles, dates, and links.
Test 3: Create a Draft Post
Try creating a test post:
Create a draft post titled “Test Post” with the content “This is a test from Claude”
Then check your WordPress admin to see if the draft appears!
โ All three tests passed? You’re ready to use your MCP server!
๐งTroubleshooting Common Issues
Problem: “HTTP Error: 401”
This means authentication failed.
Solutions:
- Double-check your Application Password – make sure you copied it correctly (no extra spaces)
- Verify your WordPress username is correct (it’s NOT your email)
- Make sure you’re using the Application Password, not your regular password
- Try generating a new Application Password
Problem: “Connection Error” or “Cannot connect to site”
The server can’t reach your WordPress site.
Solutions:
- Make sure your site URL includes
https://(not just www) - Verify your WordPress site is online and accessible
- Check if your hosting has a firewall blocking API requests
- Try accessing
yoursite.com/wp-json/in your browser – you should see JSON data
Problem: Claude doesn’t seem to have the WordPress tools
The MCP server didn’t load properly.
Solutions:
- Make sure you completely quit and restarted Claude Desktop (not just minimized)
- Check the file path in your config – it must be the FULL path to wordpress_mcp_server.py
- Verify Python is installed: run
python --versionin terminal - Check for JSON syntax errors in claude_desktop_config.json (missing commas, brackets)
- Look at Claude Desktop logs for error messages
Problem: “Module not found” or Import Errors
Python dependencies aren’t installed.
Solutions:
- Navigate to your wordpress-mcp folder in terminal
- Run
pip install -r requirements.txtagain - Try using
pip3instead ofpip - Make sure you’re in the correct folder when installing
Problem: Can’t find the Claude config file
Windows:
- Press
Win + R - Type:
%APPDATA% - Press Enter
- Look for a “Claude” folder
Mac:
- In Finder, click “Go” menu
- Hold Option key – “Library” appears
- Click Library
- Go to Application Support โ Claude
Common Beginner Mistakes
Watch out for these common errors:
- โ Using login password instead of Application Password
- โ Forgetting
https://in site URL - โ Using relative paths instead of absolute paths in config
- โ Not restarting Claude Desktop after config changes
- โ JSON syntax errors (missing commas, mismatched brackets)
- โ Saving wordpress_mcp_server.py as a .txt file instead of .py
- โ Not copying the entire server code (missing the bottom part)
๐Using Your WordPress MCP Server
Now that it’s working, here are powerful ways to use it:
Content Creation
“Create a blog post titled ‘Top 10 AI Tools for Developers’ with an introduction, 10 tools with descriptions, and a conclusion. Publish it.”
Content Management
“List all my posts from the last month and tell me which ones need updating”
Quick Edits
“Update post #42 – change the title to ‘Complete Guide to Python’ and add a note at the beginning saying it was updated today”
Content Audits
“Show me all my draft posts and help me decide which ones to publish first”
Batch Operations
“Create 5 draft posts about machine learning topics, each with an outline”
๐Next Steps
Master Your New Workflow
Now that your MCP server is set up, explore what’s possible:
- Content Strategy: Use Claude to plan your editorial calendar
- SEO Optimization: Let Claude analyze and improve your posts
- Batch Creation: Generate multiple posts at once
- Content Updates: Keep old posts fresh and relevant
Advanced Tips
- Combine with other MCP servers for powerful workflows
- Use Claude to migrate content from other platforms
- Automate content quality checks before publishing
- Generate documentation from your code and publish it
๐Conclusion
Congratulations! You’ve successfully connected Claude to your WordPress site through an MCP server. You can now manage your entire site through natural conversation.
This workflow saves hours of manual work and makes content management feel effortless. Instead of context-switching between tools, you can do everything in one place through conversation.
Join the Community
Share what you’ve built! Connect with other developers using MCP servers and discover new use cases.
Having trouble? Questions about the setup? Drop a comment below and I’ll help you troubleshoot!