"""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,
)