Initial commit: MCP Hub Community Edition v3.0.0
Community edition generated from private repo via sync pipeline. Includes 9 plugins (WordPress, WooCommerce, WP Advanced, Gitea, n8n, Supabase, OpenPanel, Appwrite, Directus) with ~587 tools. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
689
plugins/wordpress/handlers/taxonomy.py
Normal file
689
plugins/wordpress/handlers/taxonomy.py
Normal file
@@ -0,0 +1,689 @@
|
||||
"""Taxonomy Handler - manages WordPress categories, tags, and custom taxonomies"""
|
||||
|
||||
import json
|
||||
from typing import Any
|
||||
|
||||
from plugins.wordpress.client import WordPressClient
|
||||
|
||||
def get_tool_specifications() -> list[dict[str, Any]]:
|
||||
"""Return tool specifications for ToolGenerator"""
|
||||
return [
|
||||
# === CATEGORIES ===
|
||||
{
|
||||
"name": "list_categories",
|
||||
"method_name": "list_categories",
|
||||
"description": "List WordPress post categories. Returns hierarchical list of categories with post counts.",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"per_page": {
|
||||
"type": "integer",
|
||||
"description": "Number of categories per page (1-100)",
|
||||
"default": 100,
|
||||
"minimum": 1,
|
||||
"maximum": 100,
|
||||
},
|
||||
"page": {
|
||||
"type": "integer",
|
||||
"description": "Page number",
|
||||
"default": 1,
|
||||
"minimum": 1,
|
||||
},
|
||||
"hide_empty": {
|
||||
"type": "boolean",
|
||||
"description": "Hide categories with no posts",
|
||||
"default": False,
|
||||
},
|
||||
},
|
||||
},
|
||||
"scope": "read",
|
||||
},
|
||||
{
|
||||
"name": "create_category",
|
||||
"method_name": "create_category",
|
||||
"description": "Create a new WordPress category. Supports hierarchical categories with parent relationships.",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {"type": "string", "description": "Category name", "minLength": 1},
|
||||
"description": {
|
||||
"anyOf": [{"type": "string"}, {"type": "null"}],
|
||||
"description": "Category description",
|
||||
},
|
||||
"parent": {
|
||||
"anyOf": [{"type": "integer"}, {"type": "null"}],
|
||||
"description": "Parent category ID for hierarchy",
|
||||
},
|
||||
},
|
||||
"required": ["name"],
|
||||
},
|
||||
"scope": "write",
|
||||
},
|
||||
{
|
||||
"name": "update_category",
|
||||
"method_name": "update_category",
|
||||
"description": "Update an existing WordPress category. Can modify name, description, and parent category.",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"category_id": {
|
||||
"type": "integer",
|
||||
"description": "Category ID to update",
|
||||
"minimum": 1,
|
||||
},
|
||||
"name": {
|
||||
"anyOf": [{"type": "string"}, {"type": "null"}],
|
||||
"description": "New category name",
|
||||
},
|
||||
"description": {
|
||||
"anyOf": [{"type": "string"}, {"type": "null"}],
|
||||
"description": "New category description",
|
||||
},
|
||||
"parent": {
|
||||
"anyOf": [{"type": "integer"}, {"type": "null"}],
|
||||
"description": "New parent category ID",
|
||||
},
|
||||
},
|
||||
"required": ["category_id"],
|
||||
},
|
||||
"scope": "write",
|
||||
},
|
||||
{
|
||||
"name": "delete_category",
|
||||
"method_name": "delete_category",
|
||||
"description": "Delete a WordPress category. Can permanently delete or reassign posts to another category.",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"category_id": {
|
||||
"type": "integer",
|
||||
"description": "Category ID to delete",
|
||||
"minimum": 1,
|
||||
},
|
||||
"force": {
|
||||
"type": "boolean",
|
||||
"description": "Force permanent deletion",
|
||||
"default": False,
|
||||
},
|
||||
},
|
||||
"required": ["category_id"],
|
||||
},
|
||||
"scope": "write",
|
||||
},
|
||||
# === TAGS ===
|
||||
{
|
||||
"name": "list_tags",
|
||||
"method_name": "list_tags",
|
||||
"description": "List WordPress post tags. Returns all tags with usage counts and metadata.",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"per_page": {
|
||||
"type": "integer",
|
||||
"description": "Number of tags per page (1-100)",
|
||||
"default": 100,
|
||||
"minimum": 1,
|
||||
"maximum": 100,
|
||||
},
|
||||
"page": {
|
||||
"type": "integer",
|
||||
"description": "Page number",
|
||||
"default": 1,
|
||||
"minimum": 1,
|
||||
},
|
||||
"hide_empty": {
|
||||
"type": "boolean",
|
||||
"description": "Hide tags with no posts",
|
||||
"default": False,
|
||||
},
|
||||
},
|
||||
},
|
||||
"scope": "read",
|
||||
},
|
||||
{
|
||||
"name": "create_tag",
|
||||
"method_name": "create_tag",
|
||||
"description": "Create a new WordPress tag. Tag slug is auto-generated from name if not provided.",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {"type": "string", "description": "Tag name", "minLength": 1},
|
||||
"description": {
|
||||
"anyOf": [{"type": "string"}, {"type": "null"}],
|
||||
"description": "Tag description",
|
||||
},
|
||||
},
|
||||
"required": ["name"],
|
||||
},
|
||||
"scope": "write",
|
||||
},
|
||||
{
|
||||
"name": "update_tag",
|
||||
"method_name": "update_tag",
|
||||
"description": "Update an existing WordPress tag. Can modify name and description.",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"tag_id": {"type": "integer", "description": "Tag ID to update", "minimum": 1},
|
||||
"name": {
|
||||
"anyOf": [{"type": "string"}, {"type": "null"}],
|
||||
"description": "New tag name",
|
||||
},
|
||||
"description": {
|
||||
"anyOf": [{"type": "string"}, {"type": "null"}],
|
||||
"description": "New tag description",
|
||||
},
|
||||
},
|
||||
"required": ["tag_id"],
|
||||
},
|
||||
"scope": "write",
|
||||
},
|
||||
{
|
||||
"name": "delete_tag",
|
||||
"method_name": "delete_tag",
|
||||
"description": "Delete a WordPress tag. Permanently removes the tag from all posts.",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"tag_id": {"type": "integer", "description": "Tag ID to delete", "minimum": 1},
|
||||
"force": {
|
||||
"type": "boolean",
|
||||
"description": "Force permanent deletion",
|
||||
"default": False,
|
||||
},
|
||||
},
|
||||
"required": ["tag_id"],
|
||||
},
|
||||
"scope": "write",
|
||||
},
|
||||
# === CUSTOM TAXONOMIES ===
|
||||
{
|
||||
"name": "list_taxonomies",
|
||||
"method_name": "list_taxonomies",
|
||||
"description": "List all registered taxonomies including built-in (category, post_tag) and custom taxonomies. Returns configuration and post type associations.",
|
||||
"schema": {"type": "object", "properties": {}},
|
||||
"scope": "read",
|
||||
},
|
||||
{
|
||||
"name": "list_taxonomy_terms",
|
||||
"method_name": "list_taxonomy_terms",
|
||||
"description": "List terms of a specific taxonomy. Works with any registered taxonomy including categories, tags, and custom taxonomies.",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"taxonomy": {
|
||||
"type": "string",
|
||||
"description": "Taxonomy slug (e.g., 'category', 'post_tag', 'product_cat')",
|
||||
},
|
||||
"per_page": {
|
||||
"type": "integer",
|
||||
"description": "Number of terms per page",
|
||||
"default": 100,
|
||||
"minimum": 1,
|
||||
"maximum": 100,
|
||||
},
|
||||
"page": {
|
||||
"type": "integer",
|
||||
"description": "Page number",
|
||||
"default": 1,
|
||||
"minimum": 1,
|
||||
},
|
||||
"hide_empty": {
|
||||
"type": "boolean",
|
||||
"description": "Hide terms with no posts",
|
||||
"default": False,
|
||||
},
|
||||
"parent": {
|
||||
"anyOf": [{"type": "integer"}, {"type": "null"}],
|
||||
"description": "Filter by parent term ID (for hierarchical taxonomies)",
|
||||
},
|
||||
},
|
||||
"required": ["taxonomy"],
|
||||
},
|
||||
"scope": "read",
|
||||
},
|
||||
{
|
||||
"name": "create_taxonomy_term",
|
||||
"method_name": "create_taxonomy_term",
|
||||
"description": "Create a new term in a taxonomy. Supports hierarchical taxonomies with parent terms. Works with any registered taxonomy.",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"taxonomy": {
|
||||
"type": "string",
|
||||
"description": "Taxonomy slug (e.g., 'category', 'product_cat')",
|
||||
},
|
||||
"name": {"type": "string", "description": "Term name", "minLength": 1},
|
||||
"description": {
|
||||
"anyOf": [{"type": "string"}, {"type": "null"}],
|
||||
"description": "Term description",
|
||||
},
|
||||
"parent": {
|
||||
"anyOf": [{"type": "integer"}, {"type": "null"}],
|
||||
"description": "Parent term ID for hierarchical taxonomies (e.g., subcategories)",
|
||||
},
|
||||
},
|
||||
"required": ["taxonomy", "name"],
|
||||
},
|
||||
"scope": "write",
|
||||
},
|
||||
]
|
||||
|
||||
class TaxonomyHandler:
|
||||
"""Handle taxonomy-related operations for WordPress"""
|
||||
|
||||
def __init__(self, client: WordPressClient):
|
||||
"""
|
||||
Initialize taxonomy handler.
|
||||
|
||||
Args:
|
||||
client: WordPress API client instance
|
||||
"""
|
||||
self.client = client
|
||||
|
||||
# === CATEGORIES ===
|
||||
|
||||
async def list_categories(
|
||||
self, per_page: int = 100, page: int = 1, hide_empty: bool = False
|
||||
) -> str:
|
||||
"""
|
||||
List WordPress post categories.
|
||||
|
||||
Args:
|
||||
per_page: Number of categories per page (1-100)
|
||||
page: Page number
|
||||
hide_empty: Hide categories with no posts
|
||||
|
||||
Returns:
|
||||
JSON string with categories list
|
||||
"""
|
||||
try:
|
||||
params = {
|
||||
"per_page": per_page,
|
||||
"page": page,
|
||||
"hide_empty": "true" if hide_empty else "false",
|
||||
}
|
||||
|
||||
categories = await self.client.get("categories", params=params)
|
||||
|
||||
result = {
|
||||
"total": len(categories),
|
||||
"categories": [
|
||||
{
|
||||
"id": cat["id"],
|
||||
"name": cat["name"],
|
||||
"slug": cat["slug"],
|
||||
"description": cat.get("description", ""),
|
||||
"count": cat.get("count", 0),
|
||||
"parent": cat.get("parent", 0),
|
||||
}
|
||||
for cat in categories
|
||||
],
|
||||
}
|
||||
|
||||
return json.dumps(result, indent=2)
|
||||
except Exception as e:
|
||||
return json.dumps(
|
||||
{"error": str(e), "message": f"Failed to list categories: {str(e)}"}, indent=2
|
||||
)
|
||||
|
||||
async def create_category(
|
||||
self, name: str, description: str | None = None, parent: int | None = None
|
||||
) -> str:
|
||||
"""
|
||||
Create a new WordPress category.
|
||||
|
||||
Args:
|
||||
name: Category name
|
||||
description: Category description
|
||||
parent: Parent category ID for hierarchy
|
||||
|
||||
Returns:
|
||||
JSON string with created category data
|
||||
"""
|
||||
try:
|
||||
data = {"name": name}
|
||||
if description:
|
||||
data["description"] = description
|
||||
if parent:
|
||||
data["parent"] = parent
|
||||
|
||||
category = await self.client.post("categories", json_data=data)
|
||||
|
||||
result = {
|
||||
"id": category["id"],
|
||||
"name": category["name"],
|
||||
"slug": category["slug"],
|
||||
"message": f"Category '{name}' created successfully with ID {category['id']}",
|
||||
}
|
||||
|
||||
return json.dumps(result, indent=2)
|
||||
except Exception as e:
|
||||
return json.dumps(
|
||||
{"error": str(e), "message": f"Failed to create category: {str(e)}"}, indent=2
|
||||
)
|
||||
|
||||
async def update_category(
|
||||
self,
|
||||
category_id: int,
|
||||
name: str | None = None,
|
||||
description: str | None = None,
|
||||
parent: int | None = None,
|
||||
) -> str:
|
||||
"""
|
||||
Update an existing WordPress category.
|
||||
|
||||
Args:
|
||||
category_id: Category ID to update
|
||||
name: New category name
|
||||
description: New category description
|
||||
parent: New parent category ID
|
||||
|
||||
Returns:
|
||||
JSON string with updated category data
|
||||
"""
|
||||
try:
|
||||
# Build data dict with only provided values
|
||||
data = {}
|
||||
if name is not None:
|
||||
data["name"] = name
|
||||
if description is not None:
|
||||
data["description"] = description
|
||||
if parent is not None:
|
||||
data["parent"] = parent
|
||||
|
||||
category = await self.client.post(f"categories/{category_id}", json_data=data)
|
||||
|
||||
result = {
|
||||
"id": category["id"],
|
||||
"name": category["name"],
|
||||
"slug": category["slug"],
|
||||
"message": f"Category {category_id} updated successfully",
|
||||
}
|
||||
|
||||
return json.dumps(result, indent=2)
|
||||
except Exception as e:
|
||||
return json.dumps(
|
||||
{"error": str(e), "message": f"Failed to update category {category_id}: {str(e)}"},
|
||||
indent=2,
|
||||
)
|
||||
|
||||
async def delete_category(self, category_id: int, force: bool = False) -> str:
|
||||
"""
|
||||
Delete a WordPress category.
|
||||
|
||||
Args:
|
||||
category_id: Category ID to delete
|
||||
force: Force permanent deletion
|
||||
|
||||
Returns:
|
||||
JSON string with deletion result
|
||||
"""
|
||||
try:
|
||||
params = {"force": "true" if force else "false"}
|
||||
result = await self.client.delete(f"categories/{category_id}", params=params)
|
||||
|
||||
message = f"Category {category_id} deleted successfully"
|
||||
return json.dumps({"success": True, "message": message, "result": result}, indent=2)
|
||||
except Exception as e:
|
||||
return json.dumps(
|
||||
{"error": str(e), "message": f"Failed to delete category {category_id}: {str(e)}"},
|
||||
indent=2,
|
||||
)
|
||||
|
||||
# === TAGS ===
|
||||
|
||||
async def list_tags(self, per_page: int = 100, page: int = 1, hide_empty: bool = False) -> str:
|
||||
"""
|
||||
List WordPress post tags.
|
||||
|
||||
Args:
|
||||
per_page: Number of tags per page (1-100)
|
||||
page: Page number
|
||||
hide_empty: Hide tags with no posts
|
||||
|
||||
Returns:
|
||||
JSON string with tags list
|
||||
"""
|
||||
try:
|
||||
params = {
|
||||
"per_page": per_page,
|
||||
"page": page,
|
||||
"hide_empty": "true" if hide_empty else "false",
|
||||
}
|
||||
|
||||
tags = await self.client.get("tags", params=params)
|
||||
|
||||
result = {
|
||||
"total": len(tags),
|
||||
"tags": [
|
||||
{
|
||||
"id": tag["id"],
|
||||
"name": tag["name"],
|
||||
"slug": tag["slug"],
|
||||
"description": tag.get("description", ""),
|
||||
"count": tag.get("count", 0),
|
||||
}
|
||||
for tag in tags
|
||||
],
|
||||
}
|
||||
|
||||
return json.dumps(result, indent=2)
|
||||
except Exception as e:
|
||||
return json.dumps(
|
||||
{"error": str(e), "message": f"Failed to list tags: {str(e)}"}, indent=2
|
||||
)
|
||||
|
||||
async def create_tag(self, name: str, description: str | None = None) -> str:
|
||||
"""
|
||||
Create a new WordPress tag.
|
||||
|
||||
Args:
|
||||
name: Tag name
|
||||
description: Tag description
|
||||
|
||||
Returns:
|
||||
JSON string with created tag data
|
||||
"""
|
||||
try:
|
||||
data = {"name": name}
|
||||
if description:
|
||||
data["description"] = description
|
||||
|
||||
tag = await self.client.post("tags", json_data=data)
|
||||
|
||||
result = {
|
||||
"id": tag["id"],
|
||||
"name": tag["name"],
|
||||
"slug": tag["slug"],
|
||||
"message": f"Tag '{name}' created successfully with ID {tag['id']}",
|
||||
}
|
||||
|
||||
return json.dumps(result, indent=2)
|
||||
except Exception as e:
|
||||
return json.dumps(
|
||||
{"error": str(e), "message": f"Failed to create tag: {str(e)}"}, indent=2
|
||||
)
|
||||
|
||||
async def update_tag(
|
||||
self, tag_id: int, name: str | None = None, description: str | None = None
|
||||
) -> str:
|
||||
"""
|
||||
Update an existing WordPress tag.
|
||||
|
||||
Args:
|
||||
tag_id: Tag ID to update
|
||||
name: New tag name
|
||||
description: New tag description
|
||||
|
||||
Returns:
|
||||
JSON string with updated tag data
|
||||
"""
|
||||
try:
|
||||
# Build data dict with only provided values
|
||||
data = {}
|
||||
if name is not None:
|
||||
data["name"] = name
|
||||
if description is not None:
|
||||
data["description"] = description
|
||||
|
||||
tag = await self.client.post(f"tags/{tag_id}", json_data=data)
|
||||
|
||||
result = {
|
||||
"id": tag["id"],
|
||||
"name": tag["name"],
|
||||
"slug": tag["slug"],
|
||||
"message": f"Tag {tag_id} updated successfully",
|
||||
}
|
||||
|
||||
return json.dumps(result, indent=2)
|
||||
except Exception as e:
|
||||
return json.dumps(
|
||||
{"error": str(e), "message": f"Failed to update tag {tag_id}: {str(e)}"}, indent=2
|
||||
)
|
||||
|
||||
async def delete_tag(self, tag_id: int, force: bool = False) -> str:
|
||||
"""
|
||||
Delete a WordPress tag.
|
||||
|
||||
Args:
|
||||
tag_id: Tag ID to delete
|
||||
force: Force permanent deletion
|
||||
|
||||
Returns:
|
||||
JSON string with deletion result
|
||||
"""
|
||||
try:
|
||||
params = {"force": "true" if force else "false"}
|
||||
result = await self.client.delete(f"tags/{tag_id}", params=params)
|
||||
|
||||
message = f"Tag {tag_id} deleted successfully"
|
||||
return json.dumps({"success": True, "message": message, "result": result}, indent=2)
|
||||
except Exception as e:
|
||||
return json.dumps(
|
||||
{"error": str(e), "message": f"Failed to delete tag {tag_id}: {str(e)}"}, indent=2
|
||||
)
|
||||
|
||||
# === CUSTOM TAXONOMIES ===
|
||||
|
||||
async def list_taxonomies(self) -> str:
|
||||
"""
|
||||
List all registered taxonomies.
|
||||
|
||||
Returns list of all taxonomies including built-in (category, post_tag)
|
||||
and custom taxonomies (portfolio_category, etc.).
|
||||
|
||||
Returns:
|
||||
JSON string with total count and taxonomies list
|
||||
"""
|
||||
try:
|
||||
taxonomies_data = await self.client.get("taxonomies")
|
||||
|
||||
# Convert dict to list
|
||||
taxonomies = []
|
||||
if isinstance(taxonomies_data, dict):
|
||||
for slug, data in taxonomies_data.items():
|
||||
taxonomies.append(
|
||||
{
|
||||
"slug": slug,
|
||||
"name": data.get("name", slug),
|
||||
"description": data.get("description", ""),
|
||||
"types": data.get("types", []),
|
||||
"hierarchical": data.get("hierarchical", False),
|
||||
"rest_base": data.get("rest_base", slug),
|
||||
}
|
||||
)
|
||||
|
||||
result = {"total": len(taxonomies), "taxonomies": taxonomies}
|
||||
|
||||
return json.dumps(result, indent=2)
|
||||
except Exception as e:
|
||||
return json.dumps(
|
||||
{"error": str(e), "message": f"Failed to list taxonomies: {str(e)}"}, indent=2
|
||||
)
|
||||
|
||||
async def list_taxonomy_terms(
|
||||
self,
|
||||
taxonomy: str,
|
||||
per_page: int = 100,
|
||||
page: int = 1,
|
||||
hide_empty: bool = False,
|
||||
parent: int | None = None,
|
||||
) -> str:
|
||||
"""
|
||||
List terms of a specific taxonomy.
|
||||
|
||||
Args:
|
||||
taxonomy: Taxonomy slug (e.g., 'category', 'product_category')
|
||||
per_page: Number of terms per page
|
||||
page: Page number
|
||||
hide_empty: Hide terms with no posts
|
||||
parent: Filter by parent term ID
|
||||
|
||||
Returns:
|
||||
JSON string with terms list
|
||||
"""
|
||||
try:
|
||||
params = {
|
||||
"per_page": per_page,
|
||||
"page": page,
|
||||
"hide_empty": "true" if hide_empty else "false",
|
||||
}
|
||||
|
||||
if parent is not None:
|
||||
params["parent"] = parent
|
||||
|
||||
terms = await self.client.get(taxonomy, params=params)
|
||||
|
||||
result = {
|
||||
"total": len(terms) if isinstance(terms, list) else 0,
|
||||
"taxonomy": taxonomy,
|
||||
"page": page,
|
||||
"per_page": per_page,
|
||||
"terms": terms,
|
||||
}
|
||||
|
||||
return json.dumps(result, indent=2)
|
||||
except Exception as e:
|
||||
return json.dumps(
|
||||
{
|
||||
"error": str(e),
|
||||
"message": f"Failed to list taxonomy terms for '{taxonomy}': {str(e)}",
|
||||
},
|
||||
indent=2,
|
||||
)
|
||||
|
||||
async def create_taxonomy_term(
|
||||
self, taxonomy: str, name: str, description: str | None = None, parent: int | None = None
|
||||
) -> str:
|
||||
"""
|
||||
Create a new term in a taxonomy.
|
||||
|
||||
Args:
|
||||
taxonomy: Taxonomy slug (e.g., 'category', 'product_category')
|
||||
name: Term name
|
||||
description: Term description
|
||||
parent: Parent term ID for hierarchical taxonomies
|
||||
|
||||
Returns:
|
||||
JSON string with created term details
|
||||
"""
|
||||
try:
|
||||
data = {"name": name}
|
||||
|
||||
if description:
|
||||
data["description"] = description
|
||||
if parent:
|
||||
data["parent"] = parent
|
||||
|
||||
term = await self.client.post(taxonomy, json_data=data)
|
||||
|
||||
return json.dumps(term, indent=2)
|
||||
except Exception as e:
|
||||
return json.dumps(
|
||||
{
|
||||
"error": str(e),
|
||||
"message": f"Failed to create taxonomy term in '{taxonomy}': {str(e)}",
|
||||
},
|
||||
indent=2,
|
||||
)
|
||||
Reference in New Issue
Block a user