A Model Context Protocol (MCP) server for interacting with the WordPress REST API. This server provides tools for managing WordPress posts, pages, categories, tags, comments, and site search through a standardized MCP interface.
- Posts Management: Create, read, update, and delete WordPress posts with bulk publishing
- Pages Management: Create, read, update, and delete WordPress pages with hierarchical support and bulk publishing
- Categories Management: Create, read, update, and delete WordPress categories with parent-child relationships
- Tags Management: Create, read, update, and delete WordPress tags for content labeling
- Comments Management: Create, read, update, moderate, and delete WordPress comments with bulk approval
- Site Search: Search across posts, pages, categories, and tags
- Authentication: Basic authentication using WordPress Application Passwords
- Resources: Quick access to posts, pages, categories, tags, and comments
- Prompts: Pre-built prompts for content creation and SEO optimization
- Configure environment variables:
cp .env.example .env
# Edit .env with your WordPress credentials- Build and run with Docker:
# Build the image
./build-image.sh
# Or with docker-compose
docker-compose up -d- The server will be available at
http://localhost:8000
- Install dependencies:
pip install -r requirements.txt-
Configure environment variables (see Configuration section below)
-
Run the server:
python wordpress_server.pyThe server requires the following environment variables:
Create a .env file or set these in your environment:
# WordPress site URL (without trailing slash)
WP_URL=https://example.com
# WordPress username
WP_USERNAME=admin
# WordPress Application Password (create in WP Admin > Users > Profile > Application Passwords)
WP_APP_PASSWORD=abcd-efgh-ijkl-mnop-1234-5678- Log in to your WordPress admin dashboard
- Go to Users > Profile
- Scroll down to Application Passwords section
- Enter a name (e.g., "MCP Server")
- Click Add New Application Password
- Copy the generated password (you'll only see it once!)
| Tool | Description |
|---|---|
list_posts |
List posts with filtering options |
get_post |
Retrieve a single post by ID |
create_post |
Create a new post |
update_post |
Update an existing post |
delete_post |
Delete a post (move to trash or force delete) |
list_draft_posts |
List all draft posts |
publish_post |
Publish a single draft post |
publish_bulk_posts |
Publish multiple posts at once |
publish_all_drafts |
Publish ALL draft posts at once |
| Tool | Description |
|---|---|
list_pages |
List pages with filtering options |
get_page |
Retrieve a single page by ID |
create_page |
Create a new page |
update_page |
Update an existing page |
delete_page |
Delete a page (move to trash or force delete) |
list_draft_pages |
List all draft pages |
publish_page |
Publish a single draft page |
publish_bulk_pages |
Publish multiple pages at once |
publish_all_draft_pages |
Publish ALL draft pages at once |
| Tool | Description |
|---|---|
list_categories |
List categories with filtering options |
get_category |
Retrieve a single category by ID |
create_category |
Create a new category |
update_category |
Update an existing category |
delete_category |
Delete a category |
| Tool | Description |
|---|---|
list_tags |
List tags with filtering options |
get_tag |
Retrieve a single tag by ID |
create_tag |
Create a new tag |
update_tag |
Update an existing tag |
delete_tag |
Delete a tag |
| Tool | Description |
|---|---|
list_comments |
List comments with filtering options |
get_comment |
Retrieve a single comment by ID |
create_comment |
Create a new comment |
update_comment |
Update an existing comment |
delete_comment |
Delete a comment |
approve_comment |
Approve a comment immediately |
spam_comment |
Mark a comment as spam |
list_pending_comments |
List pending comments for moderation |
bulk_approve_comments |
Approve multiple comments at once |
| Tool | Description |
|---|---|
search_site |
Site-wide search across all content |
search_posts |
Search only posts |
search_pages |
Search only pages |
# Via MCP client
result = await create_post(
title="My New Post",
content="This is the post content with <strong>HTML</strong>.",
status="draft",
categories=[5, 10],
tags=[15]
)# 1. Create posts as drafts
post1 = await create_post(
title="First Draft",
content="Content for first post",
status="draft"
)
post2 = await create_post(
title="Second Draft",
content="Content for second post",
status="draft"
)
# 2. Review your draft posts
drafts = await list_draft_posts()
print(f"Found {len(drafts)} draft posts to review")
# 3. Publish a single post
await publish_post(post_id=post1["id"])
# 4. Or publish multiple specific posts
await publish_bulk_posts(post_ids=[post1["id"], post2["id"]])
# 5. Or publish ALL drafts at once
result = await publish_all_drafts()
print(f"Published {len(result['successful'])} posts")
if result['failed']:
print(f"Failed to publish {len(result['failed'])} posts")# Create parent page
parent = await create_page(
title="Services",
content="Our services overview",
status="publish"
)
# Create child pages
await create_page(
title="Web Development",
content="Web development services",
parent=parent["id"],
menu_order=1
)# Create a category with children
tech = await create_category(
name="Technology",
description="Tech-related posts"
)
await create_category(
name="Python",
parent=tech["id"]
)# Create multiple tags for content organization
javascript = await create_tag(
name="JavaScript",
description="JavaScript programming posts"
)
tutorial = await create_tag(
name="Tutorial",
description="Tutorial and how-to content"
)
# Assign tags to posts
await update_post(
post_id=42,
tags=[javascript["id"], tutorial["id"]]
)# Get comments for moderation
pending = await list_pending_comments()
# Approve a comment
await approve_comment(comment_id=157)
# Create a reply to a comment
reply = await create_comment(
post=42,
content="Thanks for your feedback!",
parent=157
)
# Bulk approve comments
result = await bulk_approve_comments(comment_ids=[157, 158, 159])
print(f"Approved {len(result['successful'])} comments")# Search all content
results = await search_site(search="wordpress tutorial")
# Search only posts
posts = await search_posts(search="javascript")
# Search only pages
pages = await search_pages(search="about")
# Filter by content type
categories = await search_site(
search="technology",
type="term",
subtype="category"
)The server uses FastMCP with streamable HTTP transport by default. You can customize the host and port by modifying the server initialization:
mcp = FastMCP("WordPress", host="0.0.0.0", port=8000, json_response=True)- Always use HTTPS for your WordPress site URL
- Keep Application Passwords secure and never commit them to version control
- Create dedicated Application Passwords for each integration
- Rotate Application Passwords periodically
- The server requires authentication for write operations (POST, PUT, DELETE)
The server returns error information in the following format:
{
"error": true,
"status_code": 401,
"message": "Sorry, you are not allowed to do that."
}Common HTTP status codes:
401- Authentication failed or missing400- Invalid request parameters404- Resource not found403- Forbidden (insufficient permissions)
wp-mcp/
├── wordpress_server.py # Main server implementation
├── pyproject.toml # Project configuration
├── requirements.txt # Python dependencies
├── Dockerfile # Docker image configuration
├── docker-compose.yml # Docker Compose configuration
├── build-image.sh # Build script for Docker image
├── .dockerignore # Files to exclude from Docker build
├── README.md # This file
├── .env.example # Example environment variables
└── docs/ # API documentation
The Docker setup includes:
- Health checks: Monitors server availability
- Auto-restart: Container restarts on failure
- Environment variables: Loaded from
.envfile - Exposed port: 8000 (configurable via docker-compose.yml)
# Build the image
docker build -t wp-mcp:latest .
# Run with docker-compose
docker-compose up -d
# View logs
docker-compose logs -f
# Stop the container
docker-compose down
# Rebuild after changes
docker-compose up -d --buildTo add a new tool, decorate a function with @mcp.tool():
@mcp.tool()
async def my_new_tool(param: str) -> Dict[str, Any]:
"""Tool description."""
# Implementation
return resultMIT