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!