From 12b74a108e4c8c9764e6d17c442cb8a5a24880ca Mon Sep 17 00:00:00 2001 From: waleed Date: Wed, 1 Jul 2026 23:51:35 -0700 Subject: [PATCH 1/8] feat(wordpress): add category/tag CRUD tools, fix delete-status and dead-field bugs - Add wordpress_{get,update,delete}_category and _tag tools for full taxonomy parity - Fix `deleted: data.deleted || true` always evaluating true across all 6 delete tools (posts/pages/media/comments/categories/tags) - Remove dead `force` param from delete_media (endpoint always force-deletes; param had zero effect) - Remove unwired `hideEmpty` block input - Normalize search_content perPage/page visibility to user-or-llm for consistency --- apps/sim/blocks/blocks/wordpress.ts | 139 +++++++++++++++++--- apps/sim/tools/registry.ts | 12 ++ apps/sim/tools/wordpress/delete_category.ts | 96 ++++++++++++++ apps/sim/tools/wordpress/delete_comment.ts | 2 +- apps/sim/tools/wordpress/delete_media.ts | 10 +- apps/sim/tools/wordpress/delete_page.ts | 2 +- apps/sim/tools/wordpress/delete_post.ts | 2 +- apps/sim/tools/wordpress/delete_tag.ts | 91 +++++++++++++ apps/sim/tools/wordpress/get_category.ts | 86 ++++++++++++ apps/sim/tools/wordpress/get_tag.ts | 83 ++++++++++++ apps/sim/tools/wordpress/index.ts | 12 ++ apps/sim/tools/wordpress/search_content.ts | 4 +- apps/sim/tools/wordpress/types.ts | 82 +++++++++++- apps/sim/tools/wordpress/update_category.ts | 122 +++++++++++++++++ apps/sim/tools/wordpress/update_tag.ts | 110 ++++++++++++++++ 15 files changed, 823 insertions(+), 30 deletions(-) create mode 100644 apps/sim/tools/wordpress/delete_category.ts create mode 100644 apps/sim/tools/wordpress/delete_tag.ts create mode 100644 apps/sim/tools/wordpress/get_category.ts create mode 100644 apps/sim/tools/wordpress/get_tag.ts create mode 100644 apps/sim/tools/wordpress/update_category.ts create mode 100644 apps/sim/tools/wordpress/update_tag.ts diff --git a/apps/sim/blocks/blocks/wordpress.ts b/apps/sim/blocks/blocks/wordpress.ts index b451b6a1a41..599a7de4a77 100644 --- a/apps/sim/blocks/blocks/wordpress.ts +++ b/apps/sim/blocks/blocks/wordpress.ts @@ -11,7 +11,7 @@ export const WordPressBlock: BlockConfig = { description: 'Manage WordPress content', authMode: AuthMode.OAuth, longDescription: - 'Integrate with WordPress to create, update, and manage posts, pages, media, comments, categories, tags, and users. Supports WordPress.com sites via OAuth and self-hosted WordPress sites using Application Passwords authentication.', + 'Integrate with WordPress.com to create, update, and manage posts, pages, media, comments, categories, tags, and users. Connects to WordPress.com sites via OAuth.', docsLink: 'https://docs.sim.ai/integrations/wordpress', category: 'tools', integrationType: IntegrationType.Marketing, @@ -49,9 +49,15 @@ export const WordPressBlock: BlockConfig = { { label: 'Delete Comment', id: 'wordpress_delete_comment' }, // Categories { label: 'Create Category', id: 'wordpress_create_category' }, + { label: 'Update Category', id: 'wordpress_update_category' }, + { label: 'Delete Category', id: 'wordpress_delete_category' }, + { label: 'Get Category', id: 'wordpress_get_category' }, { label: 'List Categories', id: 'wordpress_list_categories' }, // Tags { label: 'Create Tag', id: 'wordpress_create_tag' }, + { label: 'Update Tag', id: 'wordpress_update_tag' }, + { label: 'Delete Tag', id: 'wordpress_delete_tag' }, + { label: 'Get Tag', id: 'wordpress_get_tag' }, { label: 'List Tags', id: 'wordpress_list_tags' }, // Users { label: 'Get Current User', id: 'wordpress_get_current_user' }, @@ -429,12 +435,29 @@ export const WordPressBlock: BlockConfig = { }, // Category Operations + { + id: 'categoryId', + title: 'Category ID', + type: 'short-input', + placeholder: 'Enter category ID', + condition: { + field: 'operation', + value: ['wordpress_get_category', 'wordpress_update_category', 'wordpress_delete_category'], + }, + required: { + field: 'operation', + value: ['wordpress_get_category', 'wordpress_update_category', 'wordpress_delete_category'], + }, + }, { id: 'categoryName', title: 'Category Name', type: 'short-input', placeholder: 'Category name', - condition: { field: 'operation', value: 'wordpress_create_category' }, + condition: { + field: 'operation', + value: ['wordpress_create_category', 'wordpress_update_category'], + }, required: { field: 'operation', value: 'wordpress_create_category' }, }, { @@ -443,7 +466,10 @@ export const WordPressBlock: BlockConfig = { type: 'long-input', placeholder: 'Category description', mode: 'advanced', - condition: { field: 'operation', value: 'wordpress_create_category' }, + condition: { + field: 'operation', + value: ['wordpress_create_category', 'wordpress_update_category'], + }, }, { id: 'categoryParent', @@ -451,7 +477,10 @@ export const WordPressBlock: BlockConfig = { type: 'short-input', placeholder: 'Parent category ID', mode: 'advanced', - condition: { field: 'operation', value: 'wordpress_create_category' }, + condition: { + field: 'operation', + value: ['wordpress_create_category', 'wordpress_update_category'], + }, }, { id: 'categorySlug', @@ -459,16 +488,36 @@ export const WordPressBlock: BlockConfig = { type: 'short-input', placeholder: 'URL slug (optional)', mode: 'advanced', - condition: { field: 'operation', value: 'wordpress_create_category' }, + condition: { + field: 'operation', + value: ['wordpress_create_category', 'wordpress_update_category'], + }, }, // Tag Operations + { + id: 'tagId', + title: 'Tag ID', + type: 'short-input', + placeholder: 'Enter tag ID', + condition: { + field: 'operation', + value: ['wordpress_get_tag', 'wordpress_update_tag', 'wordpress_delete_tag'], + }, + required: { + field: 'operation', + value: ['wordpress_get_tag', 'wordpress_update_tag', 'wordpress_delete_tag'], + }, + }, { id: 'tagName', title: 'Tag Name', type: 'short-input', placeholder: 'Tag name', - condition: { field: 'operation', value: 'wordpress_create_tag' }, + condition: { + field: 'operation', + value: ['wordpress_create_tag', 'wordpress_update_tag'], + }, required: { field: 'operation', value: 'wordpress_create_tag' }, }, { @@ -477,7 +526,10 @@ export const WordPressBlock: BlockConfig = { type: 'long-input', placeholder: 'Tag description', mode: 'advanced', - condition: { field: 'operation', value: 'wordpress_create_tag' }, + condition: { + field: 'operation', + value: ['wordpress_create_tag', 'wordpress_update_tag'], + }, }, { id: 'tagSlug', @@ -485,7 +537,10 @@ export const WordPressBlock: BlockConfig = { type: 'short-input', placeholder: 'URL slug (optional)', mode: 'advanced', - condition: { field: 'operation', value: 'wordpress_create_tag' }, + condition: { + field: 'operation', + value: ['wordpress_create_tag', 'wordpress_update_tag'], + }, }, // User Operations @@ -665,12 +720,7 @@ export const WordPressBlock: BlockConfig = { mode: 'advanced', condition: { field: 'operation', - value: [ - 'wordpress_delete_post', - 'wordpress_delete_page', - 'wordpress_delete_media', - 'wordpress_delete_comment', - ], + value: ['wordpress_delete_post', 'wordpress_delete_page', 'wordpress_delete_comment'], }, }, ], @@ -696,8 +746,14 @@ export const WordPressBlock: BlockConfig = { 'wordpress_delete_comment', 'wordpress_create_category', 'wordpress_list_categories', + 'wordpress_get_category', + 'wordpress_update_category', + 'wordpress_delete_category', 'wordpress_create_tag', 'wordpress_list_tags', + 'wordpress_get_tag', + 'wordpress_update_tag', + 'wordpress_delete_tag', 'wordpress_get_current_user', 'wordpress_list_users', 'wordpress_get_user', @@ -837,7 +893,6 @@ export const WordPressBlock: BlockConfig = { return { ...baseParams, mediaId: Number(params.mediaId), - force: params.force, } case 'wordpress_create_comment': return { @@ -884,6 +939,25 @@ export const WordPressBlock: BlockConfig = { search: params.search, order: params.order, } + case 'wordpress_get_category': + return { + ...baseParams, + categoryId: Number(params.categoryId), + } + case 'wordpress_update_category': + return { + ...baseParams, + categoryId: Number(params.categoryId), + name: params.categoryName, + description: params.categoryDescription, + parent: params.categoryParent ? Number(params.categoryParent) : undefined, + slug: params.categorySlug, + } + case 'wordpress_delete_category': + return { + ...baseParams, + categoryId: Number(params.categoryId), + } case 'wordpress_create_tag': return { ...baseParams, @@ -899,6 +973,24 @@ export const WordPressBlock: BlockConfig = { search: params.search, order: params.order, } + case 'wordpress_get_tag': + return { + ...baseParams, + tagId: Number(params.tagId), + } + case 'wordpress_update_tag': + return { + ...baseParams, + tagId: Number(params.tagId), + name: params.tagName, + description: params.tagDescription, + slug: params.tagSlug, + } + case 'wordpress_delete_tag': + return { + ...baseParams, + tagId: Number(params.tagId), + } case 'wordpress_get_current_user': return baseParams case 'wordpress_list_users': @@ -961,11 +1053,13 @@ export const WordPressBlock: BlockConfig = { commentId: { type: 'number', description: 'Comment ID' }, commentStatus: { type: 'string', description: 'Comment status' }, // Category inputs + categoryId: { type: 'number', description: 'Category ID' }, categoryName: { type: 'string', description: 'Category name' }, categoryDescription: { type: 'string', description: 'Category description' }, categoryParent: { type: 'number', description: 'Parent category ID' }, categorySlug: { type: 'string', description: 'Category slug' }, // Tag inputs + tagId: { type: 'number', description: 'Tag ID' }, tagName: { type: 'string', description: 'Tag name' }, tagDescription: { type: 'string', description: 'Tag description' }, tagSlug: { type: 'string', description: 'Tag slug' }, @@ -983,7 +1077,6 @@ export const WordPressBlock: BlockConfig = { order: { type: 'string', description: 'Order direction' }, listStatus: { type: 'string', description: 'Status filter' }, force: { type: 'boolean', description: 'Force delete' }, - hideEmpty: { type: 'boolean', description: 'Hide empty taxonomies' }, }, outputs: { // Post outputs @@ -1114,5 +1207,19 @@ export const WordPressBlockMeta = { content: '# Moderate WordPress Comments\n\nKeep the comment queue clean and on-policy.\n\n## Steps\n1. List comments, optionally filtering by status such as hold.\n2. For each comment, judge it against the moderation policy: legitimate, spam, or abusive.\n3. Update each comment to the right status: approved, hold, spam, or trash.\n\n## Output\nReturn a summary of how many comments were approved, held, marked spam, or trashed, with the comment IDs grouped by action taken.', }, + { + name: 'organize-taxonomy', + description: + 'Clean up WordPress categories and tags: rename, re-slug, re-parent, or remove unused ones.', + content: + '# Organize WordPress Taxonomy\n\nKeep categories and tags tidy and consistent.\n\n## Steps\n1. List existing categories and tags to see the current taxonomy, including each item post count.\n2. Decide the target structure: rename to a consistent style, fix slugs, set parents for hierarchy, or remove duplicates and empties.\n3. For renames or re-parenting, update the category or tag by ID with the new name, slug, or parent.\n4. To remove one, get it first to confirm it is the right term and low-usage, then delete it (deletion is permanent for terms).\n\n## Output\nReport what changed for each term: renamed, re-slugged, re-parented, or deleted, with the term IDs. Note any term skipped because it still had many posts.', + }, + { + name: 'audit-site-content', + description: + 'Inventory WordPress content by searching and listing posts and pages to find gaps or issues.', + content: + '# Audit WordPress Content\n\nBuild an inventory of what is on the site.\n\n## Steps\n1. Use search across content, or list posts and pages with filters such as status and date order.\n2. Page through results using the total and totalPages counts so nothing is missed.\n3. Group findings by status, category, or age to spot drafts left unpublished, stale posts, or thin content.\n\n## Output\nReturn a structured inventory: counts by status and type, plus a list of flagged items (IDs, titles, and URLs) that need attention.', + }, ], } as const satisfies BlockMeta diff --git a/apps/sim/tools/registry.ts b/apps/sim/tools/registry.ts index 4adbd38fb46..4c9fe89ed76 100644 --- a/apps/sim/tools/registry.ts +++ b/apps/sim/tools/registry.ts @@ -4123,14 +4123,18 @@ import { wordpressCreatePageTool, wordpressCreatePostTool, wordpressCreateTagTool, + wordpressDeleteCategoryTool, wordpressDeleteCommentTool, wordpressDeleteMediaTool, wordpressDeletePageTool, wordpressDeletePostTool, + wordpressDeleteTagTool, + wordpressGetCategoryTool, wordpressGetCurrentUserTool, wordpressGetMediaTool, wordpressGetPageTool, wordpressGetPostTool, + wordpressGetTagTool, wordpressGetUserTool, wordpressListCategoriesTool, wordpressListCommentsTool, @@ -4140,9 +4144,11 @@ import { wordpressListTagsTool, wordpressListUsersTool, wordpressSearchContentTool, + wordpressUpdateCategoryTool, wordpressUpdateCommentTool, wordpressUpdatePageTool, wordpressUpdatePostTool, + wordpressUpdateTagTool, wordpressUploadMediaTool, } from '@/tools/wordpress' import { @@ -7421,8 +7427,14 @@ export const tools: Record = { wordpress_delete_comment: wordpressDeleteCommentTool, wordpress_create_category: wordpressCreateCategoryTool, wordpress_list_categories: wordpressListCategoriesTool, + wordpress_get_category: wordpressGetCategoryTool, + wordpress_update_category: wordpressUpdateCategoryTool, + wordpress_delete_category: wordpressDeleteCategoryTool, wordpress_create_tag: wordpressCreateTagTool, wordpress_list_tags: wordpressListTagsTool, + wordpress_get_tag: wordpressGetTagTool, + wordpress_update_tag: wordpressUpdateTagTool, + wordpress_delete_tag: wordpressDeleteTagTool, wordpress_get_current_user: wordpressGetCurrentUserTool, wordpress_list_users: wordpressListUsersTool, wordpress_get_user: wordpressGetUserTool, diff --git a/apps/sim/tools/wordpress/delete_category.ts b/apps/sim/tools/wordpress/delete_category.ts new file mode 100644 index 00000000000..ad251093639 --- /dev/null +++ b/apps/sim/tools/wordpress/delete_category.ts @@ -0,0 +1,96 @@ +import type { ToolConfig } from '@/tools/types' +import { + WORDPRESS_COM_API_BASE, + type WordPressDeleteCategoryParams, + type WordPressDeleteCategoryResponse, +} from '@/tools/wordpress/types' + +export const deleteCategoryTool: ToolConfig< + WordPressDeleteCategoryParams, + WordPressDeleteCategoryResponse +> = { + id: 'wordpress_delete_category', + name: 'WordPress Delete Category', + description: 'Delete a category from WordPress.com', + version: '1.0.0', + + oauth: { + required: true, + provider: 'wordpress', + requiredScopes: ['global'], + }, + + params: { + siteId: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'WordPress.com site ID or domain (e.g., 12345678 or mysite.wordpress.com)', + }, + categoryId: { + type: 'number', + required: true, + visibility: 'user-or-llm', + description: 'The ID of the category to delete', + }, + }, + + request: { + url: (params) => { + // Terms do not support trashing, so force=true is required to delete. + return `${WORDPRESS_COM_API_BASE}/${params.siteId}/categories/${params.categoryId}?force=true` + }, + method: 'DELETE', + headers: (params) => ({ + 'Content-Type': 'application/json', + Authorization: `Bearer ${params.accessToken}`, + }), + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const error = await response.json().catch(() => ({})) + throw new Error(error.message || `WordPress API error: ${response.status}`) + } + + const data = await response.json() + + return { + success: true, + output: { + deleted: data.deleted ?? true, + category: { + id: data.id || data.previous?.id, + count: data.count || data.previous?.count, + description: data.description || data.previous?.description, + link: data.link || data.previous?.link, + name: data.name || data.previous?.name, + slug: data.slug || data.previous?.slug, + taxonomy: data.taxonomy || data.previous?.taxonomy, + parent: data.parent || data.previous?.parent, + }, + }, + } + }, + + outputs: { + deleted: { + type: 'boolean', + description: 'Whether the category was deleted', + }, + category: { + type: 'object', + description: 'The deleted category', + properties: { + id: { type: 'number', description: 'Category ID' }, + count: { type: 'number', description: 'Number of posts in this category' }, + description: { type: 'string', description: 'Category description' }, + link: { type: 'string', description: 'Category archive URL' }, + name: { type: 'string', description: 'Category name' }, + slug: { type: 'string', description: 'Category slug' }, + taxonomy: { type: 'string', description: 'Taxonomy name' }, + parent: { type: 'number', description: 'Parent category ID' }, + }, + }, + }, +} diff --git a/apps/sim/tools/wordpress/delete_comment.ts b/apps/sim/tools/wordpress/delete_comment.ts index 5aec306e5d8..a82eb622d7c 100644 --- a/apps/sim/tools/wordpress/delete_comment.ts +++ b/apps/sim/tools/wordpress/delete_comment.ts @@ -64,7 +64,7 @@ export const deleteCommentTool: ToolConfig< return { success: true, output: { - deleted: data.deleted || true, + deleted: data.deleted ?? true, comment: { id: data.id || data.previous?.id, post: data.post || data.previous?.post, diff --git a/apps/sim/tools/wordpress/delete_media.ts b/apps/sim/tools/wordpress/delete_media.ts index 3b680919e3d..33c11dd26e6 100644 --- a/apps/sim/tools/wordpress/delete_media.ts +++ b/apps/sim/tools/wordpress/delete_media.ts @@ -31,17 +31,11 @@ export const deleteMediaTool: ToolConfig { - // Media deletion requires force=true to actually delete + // Media has no trash — deletion always requires force=true to take effect return `${WORDPRESS_COM_API_BASE}/${params.siteId}/media/${params.mediaId}?force=true` }, method: 'DELETE', @@ -62,7 +56,7 @@ export const deleteMediaTool: ToolConfig = { + id: 'wordpress_delete_tag', + name: 'WordPress Delete Tag', + description: 'Delete a tag from WordPress.com', + version: '1.0.0', + + oauth: { + required: true, + provider: 'wordpress', + requiredScopes: ['global'], + }, + + params: { + siteId: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'WordPress.com site ID or domain (e.g., 12345678 or mysite.wordpress.com)', + }, + tagId: { + type: 'number', + required: true, + visibility: 'user-or-llm', + description: 'The ID of the tag to delete', + }, + }, + + request: { + url: (params) => { + // Terms do not support trashing, so force=true is required to delete. + return `${WORDPRESS_COM_API_BASE}/${params.siteId}/tags/${params.tagId}?force=true` + }, + method: 'DELETE', + headers: (params) => ({ + 'Content-Type': 'application/json', + Authorization: `Bearer ${params.accessToken}`, + }), + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const error = await response.json().catch(() => ({})) + throw new Error(error.message || `WordPress API error: ${response.status}`) + } + + const data = await response.json() + + return { + success: true, + output: { + deleted: data.deleted ?? true, + tag: { + id: data.id || data.previous?.id, + count: data.count || data.previous?.count, + description: data.description || data.previous?.description, + link: data.link || data.previous?.link, + name: data.name || data.previous?.name, + slug: data.slug || data.previous?.slug, + taxonomy: data.taxonomy || data.previous?.taxonomy, + }, + }, + } + }, + + outputs: { + deleted: { + type: 'boolean', + description: 'Whether the tag was deleted', + }, + tag: { + type: 'object', + description: 'The deleted tag', + properties: { + id: { type: 'number', description: 'Tag ID' }, + count: { type: 'number', description: 'Number of posts with this tag' }, + description: { type: 'string', description: 'Tag description' }, + link: { type: 'string', description: 'Tag archive URL' }, + name: { type: 'string', description: 'Tag name' }, + slug: { type: 'string', description: 'Tag slug' }, + taxonomy: { type: 'string', description: 'Taxonomy name' }, + }, + }, + }, +} diff --git a/apps/sim/tools/wordpress/get_category.ts b/apps/sim/tools/wordpress/get_category.ts new file mode 100644 index 00000000000..968dafe7fbc --- /dev/null +++ b/apps/sim/tools/wordpress/get_category.ts @@ -0,0 +1,86 @@ +import type { ToolConfig } from '@/tools/types' +import { + WORDPRESS_COM_API_BASE, + type WordPressGetCategoryParams, + type WordPressGetCategoryResponse, +} from '@/tools/wordpress/types' + +export const getCategoryTool: ToolConfig = + { + id: 'wordpress_get_category', + name: 'WordPress Get Category', + description: 'Get a single category from WordPress.com by ID', + version: '1.0.0', + + oauth: { + required: true, + provider: 'wordpress', + requiredScopes: ['global'], + }, + + params: { + siteId: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'WordPress.com site ID or domain (e.g., 12345678 or mysite.wordpress.com)', + }, + categoryId: { + type: 'number', + required: true, + visibility: 'user-or-llm', + description: 'The ID of the category to retrieve', + }, + }, + + request: { + url: (params) => `${WORDPRESS_COM_API_BASE}/${params.siteId}/categories/${params.categoryId}`, + method: 'GET', + headers: (params) => ({ + 'Content-Type': 'application/json', + Authorization: `Bearer ${params.accessToken}`, + }), + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const error = await response.json().catch(() => ({})) + throw new Error(error.message || `WordPress API error: ${response.status}`) + } + + const data = await response.json() + + return { + success: true, + output: { + category: { + id: data.id, + count: data.count, + description: data.description, + link: data.link, + name: data.name, + slug: data.slug, + taxonomy: data.taxonomy, + parent: data.parent, + }, + }, + } + }, + + outputs: { + category: { + type: 'object', + description: 'The retrieved category', + properties: { + id: { type: 'number', description: 'Category ID' }, + count: { type: 'number', description: 'Number of posts in this category' }, + description: { type: 'string', description: 'Category description' }, + link: { type: 'string', description: 'Category archive URL' }, + name: { type: 'string', description: 'Category name' }, + slug: { type: 'string', description: 'Category slug' }, + taxonomy: { type: 'string', description: 'Taxonomy name' }, + parent: { type: 'number', description: 'Parent category ID' }, + }, + }, + }, + } diff --git a/apps/sim/tools/wordpress/get_tag.ts b/apps/sim/tools/wordpress/get_tag.ts new file mode 100644 index 00000000000..bc4645c4fcd --- /dev/null +++ b/apps/sim/tools/wordpress/get_tag.ts @@ -0,0 +1,83 @@ +import type { ToolConfig } from '@/tools/types' +import { + WORDPRESS_COM_API_BASE, + type WordPressGetTagParams, + type WordPressGetTagResponse, +} from '@/tools/wordpress/types' + +export const getTagTool: ToolConfig = { + id: 'wordpress_get_tag', + name: 'WordPress Get Tag', + description: 'Get a single tag from WordPress.com by ID', + version: '1.0.0', + + oauth: { + required: true, + provider: 'wordpress', + requiredScopes: ['global'], + }, + + params: { + siteId: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'WordPress.com site ID or domain (e.g., 12345678 or mysite.wordpress.com)', + }, + tagId: { + type: 'number', + required: true, + visibility: 'user-or-llm', + description: 'The ID of the tag to retrieve', + }, + }, + + request: { + url: (params) => `${WORDPRESS_COM_API_BASE}/${params.siteId}/tags/${params.tagId}`, + method: 'GET', + headers: (params) => ({ + 'Content-Type': 'application/json', + Authorization: `Bearer ${params.accessToken}`, + }), + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const error = await response.json().catch(() => ({})) + throw new Error(error.message || `WordPress API error: ${response.status}`) + } + + const data = await response.json() + + return { + success: true, + output: { + tag: { + id: data.id, + count: data.count, + description: data.description, + link: data.link, + name: data.name, + slug: data.slug, + taxonomy: data.taxonomy, + }, + }, + } + }, + + outputs: { + tag: { + type: 'object', + description: 'The retrieved tag', + properties: { + id: { type: 'number', description: 'Tag ID' }, + count: { type: 'number', description: 'Number of posts with this tag' }, + description: { type: 'string', description: 'Tag description' }, + link: { type: 'string', description: 'Tag archive URL' }, + name: { type: 'string', description: 'Tag name' }, + slug: { type: 'string', description: 'Tag slug' }, + taxonomy: { type: 'string', description: 'Taxonomy name' }, + }, + }, + }, +} diff --git a/apps/sim/tools/wordpress/index.ts b/apps/sim/tools/wordpress/index.ts index 3889208a544..ab48592b46d 100644 --- a/apps/sim/tools/wordpress/index.ts +++ b/apps/sim/tools/wordpress/index.ts @@ -4,14 +4,18 @@ import { createCommentTool } from '@/tools/wordpress/create_comment' import { createPageTool } from '@/tools/wordpress/create_page' import { createPostTool } from '@/tools/wordpress/create_post' import { createTagTool } from '@/tools/wordpress/create_tag' +import { deleteCategoryTool } from '@/tools/wordpress/delete_category' import { deleteCommentTool } from '@/tools/wordpress/delete_comment' import { deleteMediaTool } from '@/tools/wordpress/delete_media' import { deletePageTool } from '@/tools/wordpress/delete_page' import { deletePostTool } from '@/tools/wordpress/delete_post' +import { deleteTagTool } from '@/tools/wordpress/delete_tag' +import { getCategoryTool } from '@/tools/wordpress/get_category' import { getCurrentUserTool } from '@/tools/wordpress/get_current_user' import { getMediaTool } from '@/tools/wordpress/get_media' import { getPageTool } from '@/tools/wordpress/get_page' import { getPostTool } from '@/tools/wordpress/get_post' +import { getTagTool } from '@/tools/wordpress/get_tag' import { getUserTool } from '@/tools/wordpress/get_user' import { listCategoriesTool } from '@/tools/wordpress/list_categories' import { listCommentsTool } from '@/tools/wordpress/list_comments' @@ -21,9 +25,11 @@ import { listPostsTool } from '@/tools/wordpress/list_posts' import { listTagsTool } from '@/tools/wordpress/list_tags' import { listUsersTool } from '@/tools/wordpress/list_users' import { searchContentTool } from '@/tools/wordpress/search_content' +import { updateCategoryTool } from '@/tools/wordpress/update_category' import { updateCommentTool } from '@/tools/wordpress/update_comment' import { updatePageTool } from '@/tools/wordpress/update_page' import { updatePostTool } from '@/tools/wordpress/update_post' +import { updateTagTool } from '@/tools/wordpress/update_tag' import { uploadMediaTool } from '@/tools/wordpress/upload_media' // Post operations @@ -55,10 +61,16 @@ export const wordpressDeleteCommentTool = deleteCommentTool // Category operations export const wordpressCreateCategoryTool = createCategoryTool export const wordpressListCategoriesTool = listCategoriesTool +export const wordpressGetCategoryTool = getCategoryTool +export const wordpressUpdateCategoryTool = updateCategoryTool +export const wordpressDeleteCategoryTool = deleteCategoryTool // Tag operations export const wordpressCreateTagTool = createTagTool export const wordpressListTagsTool = listTagsTool +export const wordpressGetTagTool = getTagTool +export const wordpressUpdateTagTool = updateTagTool +export const wordpressDeleteTagTool = deleteTagTool // User operations export const wordpressGetCurrentUserTool = getCurrentUserTool diff --git a/apps/sim/tools/wordpress/search_content.ts b/apps/sim/tools/wordpress/search_content.ts index 2d20340810c..357607a9a94 100644 --- a/apps/sim/tools/wordpress/search_content.ts +++ b/apps/sim/tools/wordpress/search_content.ts @@ -36,13 +36,13 @@ export const searchContentTool: ToolConfig< perPage: { type: 'number', required: false, - visibility: 'user-only', + visibility: 'user-or-llm', description: 'Number of results per request (default: 10, max: 100)', }, page: { type: 'number', required: false, - visibility: 'user-only', + visibility: 'user-or-llm', description: 'Page number for pagination', }, type: { diff --git a/apps/sim/tools/wordpress/types.ts b/apps/sim/tools/wordpress/types.ts index 81bc115e414..4c840247e18 100644 --- a/apps/sim/tools/wordpress/types.ts +++ b/apps/sim/tools/wordpress/types.ts @@ -325,7 +325,6 @@ export interface WordPressListMediaResponse extends ToolResponse { // Delete Media export interface WordPressDeleteMediaParams extends WordPressBaseParams { mediaId: number - force?: boolean } export interface WordPressDeleteMediaResponse extends ToolResponse { @@ -472,6 +471,44 @@ export interface WordPressListCategoriesResponse extends ToolResponse { } } +// Get Category +export interface WordPressGetCategoryParams extends WordPressBaseParams { + categoryId: number +} + +export interface WordPressGetCategoryResponse extends ToolResponse { + output: { + category: WordPressCategory + } +} + +// Update Category +export interface WordPressUpdateCategoryParams extends WordPressBaseParams { + categoryId: number + name?: string + description?: string + parent?: number + slug?: string +} + +export interface WordPressUpdateCategoryResponse extends ToolResponse { + output: { + category: WordPressCategory + } +} + +// Delete Category +export interface WordPressDeleteCategoryParams extends WordPressBaseParams { + categoryId: number +} + +export interface WordPressDeleteCategoryResponse extends ToolResponse { + output: { + deleted: boolean + category: WordPressCategory + } +} + // Create Tag export interface WordPressCreateTagParams extends WordPressBaseParams { name: string @@ -511,6 +548,43 @@ export interface WordPressListTagsResponse extends ToolResponse { } } +// Get Tag +export interface WordPressGetTagParams extends WordPressBaseParams { + tagId: number +} + +export interface WordPressGetTagResponse extends ToolResponse { + output: { + tag: WordPressTag + } +} + +// Update Tag +export interface WordPressUpdateTagParams extends WordPressBaseParams { + tagId: number + name?: string + description?: string + slug?: string +} + +export interface WordPressUpdateTagResponse extends ToolResponse { + output: { + tag: WordPressTag + } +} + +// Delete Tag +export interface WordPressDeleteTagParams extends WordPressBaseParams { + tagId: number +} + +export interface WordPressDeleteTagResponse extends ToolResponse { + output: { + deleted: boolean + tag: WordPressTag + } +} + // ============================================ // USER OPERATIONS // ============================================ @@ -620,8 +694,14 @@ export type WordPressResponse = | WordPressDeleteCommentResponse | WordPressCreateCategoryResponse | WordPressListCategoriesResponse + | WordPressGetCategoryResponse + | WordPressUpdateCategoryResponse + | WordPressDeleteCategoryResponse | WordPressCreateTagResponse | WordPressListTagsResponse + | WordPressGetTagResponse + | WordPressUpdateTagResponse + | WordPressDeleteTagResponse | WordPressGetCurrentUserResponse | WordPressListUsersResponse | WordPressGetUserResponse diff --git a/apps/sim/tools/wordpress/update_category.ts b/apps/sim/tools/wordpress/update_category.ts new file mode 100644 index 00000000000..1b5f22d484c --- /dev/null +++ b/apps/sim/tools/wordpress/update_category.ts @@ -0,0 +1,122 @@ +import type { ToolConfig } from '@/tools/types' +import { + WORDPRESS_COM_API_BASE, + type WordPressUpdateCategoryParams, + type WordPressUpdateCategoryResponse, +} from '@/tools/wordpress/types' + +export const updateCategoryTool: ToolConfig< + WordPressUpdateCategoryParams, + WordPressUpdateCategoryResponse +> = { + id: 'wordpress_update_category', + name: 'WordPress Update Category', + description: 'Update an existing category in WordPress.com', + version: '1.0.0', + + oauth: { + required: true, + provider: 'wordpress', + requiredScopes: ['global'], + }, + + params: { + siteId: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'WordPress.com site ID or domain (e.g., 12345678 or mysite.wordpress.com)', + }, + categoryId: { + type: 'number', + required: true, + visibility: 'user-or-llm', + description: 'The ID of the category to update', + }, + name: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Category name', + }, + description: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Category description', + }, + parent: { + type: 'number', + required: false, + visibility: 'user-only', + description: 'Parent category ID for hierarchical categories', + }, + slug: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'URL slug for the category', + }, + }, + + request: { + url: (params) => `${WORDPRESS_COM_API_BASE}/${params.siteId}/categories/${params.categoryId}`, + method: 'POST', + headers: (params) => ({ + 'Content-Type': 'application/json', + Authorization: `Bearer ${params.accessToken}`, + }), + body: (params) => { + const body: Record = {} + + if (params.name) body.name = params.name + if (params.description !== undefined) body.description = params.description + if (params.parent !== undefined) body.parent = params.parent + if (params.slug) body.slug = params.slug + + return body + }, + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const error = await response.json().catch(() => ({})) + throw new Error(error.message || `WordPress API error: ${response.status}`) + } + + const data = await response.json() + + return { + success: true, + output: { + category: { + id: data.id, + count: data.count, + description: data.description, + link: data.link, + name: data.name, + slug: data.slug, + taxonomy: data.taxonomy, + parent: data.parent, + }, + }, + } + }, + + outputs: { + category: { + type: 'object', + description: 'The updated category', + properties: { + id: { type: 'number', description: 'Category ID' }, + count: { type: 'number', description: 'Number of posts in this category' }, + description: { type: 'string', description: 'Category description' }, + link: { type: 'string', description: 'Category archive URL' }, + name: { type: 'string', description: 'Category name' }, + slug: { type: 'string', description: 'Category slug' }, + taxonomy: { type: 'string', description: 'Taxonomy name' }, + parent: { type: 'number', description: 'Parent category ID' }, + }, + }, + }, +} diff --git a/apps/sim/tools/wordpress/update_tag.ts b/apps/sim/tools/wordpress/update_tag.ts new file mode 100644 index 00000000000..2e5d4171be9 --- /dev/null +++ b/apps/sim/tools/wordpress/update_tag.ts @@ -0,0 +1,110 @@ +import type { ToolConfig } from '@/tools/types' +import { + WORDPRESS_COM_API_BASE, + type WordPressUpdateTagParams, + type WordPressUpdateTagResponse, +} from '@/tools/wordpress/types' + +export const updateTagTool: ToolConfig = { + id: 'wordpress_update_tag', + name: 'WordPress Update Tag', + description: 'Update an existing tag in WordPress.com', + version: '1.0.0', + + oauth: { + required: true, + provider: 'wordpress', + requiredScopes: ['global'], + }, + + params: { + siteId: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'WordPress.com site ID or domain (e.g., 12345678 or mysite.wordpress.com)', + }, + tagId: { + type: 'number', + required: true, + visibility: 'user-or-llm', + description: 'The ID of the tag to update', + }, + name: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Tag name', + }, + description: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Tag description', + }, + slug: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'URL slug for the tag', + }, + }, + + request: { + url: (params) => `${WORDPRESS_COM_API_BASE}/${params.siteId}/tags/${params.tagId}`, + method: 'POST', + headers: (params) => ({ + 'Content-Type': 'application/json', + Authorization: `Bearer ${params.accessToken}`, + }), + body: (params) => { + const body: Record = {} + + if (params.name) body.name = params.name + if (params.description !== undefined) body.description = params.description + if (params.slug) body.slug = params.slug + + return body + }, + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const error = await response.json().catch(() => ({})) + throw new Error(error.message || `WordPress API error: ${response.status}`) + } + + const data = await response.json() + + return { + success: true, + output: { + tag: { + id: data.id, + count: data.count, + description: data.description, + link: data.link, + name: data.name, + slug: data.slug, + taxonomy: data.taxonomy, + }, + }, + } + }, + + outputs: { + tag: { + type: 'object', + description: 'The updated tag', + properties: { + id: { type: 'number', description: 'Tag ID' }, + count: { type: 'number', description: 'Number of posts with this tag' }, + description: { type: 'string', description: 'Tag description' }, + link: { type: 'string', description: 'Tag archive URL' }, + name: { type: 'string', description: 'Tag name' }, + slug: { type: 'string', description: 'Tag slug' }, + taxonomy: { type: 'string', description: 'Taxonomy name' }, + }, + }, + }, +} From ebfdb2f4053b68c8edbc911dd8dedf01d4bbdd45 Mon Sep 17 00:00:00 2001 From: waleed Date: Wed, 1 Jul 2026 23:57:56 -0700 Subject: [PATCH 2/8] fix(wordpress): use ?? instead of || for zero-valued numeric fields in delete_category/tag count and parent can legitimately be 0 (empty term, top-level category); || was dropping those values, same antipattern already fixed for `deleted` in this PR. Flagged independently by Greptile and Cursor Bugbot. --- apps/sim/tools/wordpress/delete_category.ts | 6 +++--- apps/sim/tools/wordpress/delete_tag.ts | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/apps/sim/tools/wordpress/delete_category.ts b/apps/sim/tools/wordpress/delete_category.ts index ad251093639..fc420454444 100644 --- a/apps/sim/tools/wordpress/delete_category.ts +++ b/apps/sim/tools/wordpress/delete_category.ts @@ -60,14 +60,14 @@ export const deleteCategoryTool: ToolConfig< output: { deleted: data.deleted ?? true, category: { - id: data.id || data.previous?.id, - count: data.count || data.previous?.count, + id: data.id ?? data.previous?.id, + count: data.count ?? data.previous?.count, description: data.description || data.previous?.description, link: data.link || data.previous?.link, name: data.name || data.previous?.name, slug: data.slug || data.previous?.slug, taxonomy: data.taxonomy || data.previous?.taxonomy, - parent: data.parent || data.previous?.parent, + parent: data.parent ?? data.previous?.parent, }, }, } diff --git a/apps/sim/tools/wordpress/delete_tag.ts b/apps/sim/tools/wordpress/delete_tag.ts index fecb6948b88..593243ff55e 100644 --- a/apps/sim/tools/wordpress/delete_tag.ts +++ b/apps/sim/tools/wordpress/delete_tag.ts @@ -57,8 +57,8 @@ export const deleteTagTool: ToolConfig Date: Thu, 2 Jul 2026 00:19:35 -0700 Subject: [PATCH 3/8] fix(wordpress): use ?? for zero-valued numeric fields across all delete tools MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Same || antipattern already fixed for category/tag delete tools was still present in delete_post/page/comment/media for id, author, featured_media, menu_order, parent, post — all legitimately 0 in common cases (no featured image, top-level page/comment). Found by a final independent validation pass. --- apps/sim/tools/wordpress/delete_comment.ts | 8 ++++---- apps/sim/tools/wordpress/delete_media.ts | 2 +- apps/sim/tools/wordpress/delete_page.ts | 10 +++++----- apps/sim/tools/wordpress/delete_post.ts | 6 +++--- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/apps/sim/tools/wordpress/delete_comment.ts b/apps/sim/tools/wordpress/delete_comment.ts index a82eb622d7c..03d4b6081df 100644 --- a/apps/sim/tools/wordpress/delete_comment.ts +++ b/apps/sim/tools/wordpress/delete_comment.ts @@ -66,10 +66,10 @@ export const deleteCommentTool: ToolConfig< output: { deleted: data.deleted ?? true, comment: { - id: data.id || data.previous?.id, - post: data.post || data.previous?.post, - parent: data.parent || data.previous?.parent, - author: data.author || data.previous?.author, + id: data.id ?? data.previous?.id, + post: data.post ?? data.previous?.post, + parent: data.parent ?? data.previous?.parent, + author: data.author ?? data.previous?.author, author_name: data.author_name || data.previous?.author_name, author_email: data.author_email || data.previous?.author_email, author_url: data.author_url || data.previous?.author_url, diff --git a/apps/sim/tools/wordpress/delete_media.ts b/apps/sim/tools/wordpress/delete_media.ts index 33c11dd26e6..0dfc8e0b1de 100644 --- a/apps/sim/tools/wordpress/delete_media.ts +++ b/apps/sim/tools/wordpress/delete_media.ts @@ -58,7 +58,7 @@ export const deleteMediaTool: ToolConfig Date: Thu, 2 Jul 2026 00:25:27 -0700 Subject: [PATCH 4/8] fix(wordpress): don't drop categoryParent=0 (root-level category) in block param mapping Truthy check on params.categoryParent treated a resolved numeric 0 (root-level, no parent) as unset. Flagged by Cursor Bugbot on create_category/update_category. --- apps/sim/blocks/blocks/wordpress.ts | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/apps/sim/blocks/blocks/wordpress.ts b/apps/sim/blocks/blocks/wordpress.ts index 599a7de4a77..92e7d639a6e 100644 --- a/apps/sim/blocks/blocks/wordpress.ts +++ b/apps/sim/blocks/blocks/wordpress.ts @@ -928,7 +928,10 @@ export const WordPressBlock: BlockConfig = { ...baseParams, name: params.categoryName, description: params.categoryDescription, - parent: params.categoryParent ? Number(params.categoryParent) : undefined, + parent: + params.categoryParent !== undefined && params.categoryParent !== '' + ? Number(params.categoryParent) + : undefined, slug: params.categorySlug, } case 'wordpress_list_categories': @@ -950,7 +953,10 @@ export const WordPressBlock: BlockConfig = { categoryId: Number(params.categoryId), name: params.categoryName, description: params.categoryDescription, - parent: params.categoryParent ? Number(params.categoryParent) : undefined, + parent: + params.categoryParent !== undefined && params.categoryParent !== '' + ? Number(params.categoryParent) + : undefined, slug: params.categorySlug, } case 'wordpress_delete_category': From 1d508b7cb42542de494090cd76c1ba69e938f04e Mon Sep 17 00:00:00 2001 From: waleed Date: Thu, 2 Jul 2026 00:31:20 -0700 Subject: [PATCH 5/8] fix(wordpress): don't clear category/tag description on update when field left blank description used !== undefined (numeric-field convention) instead of a truthy check (string-field convention used everywhere else in this codebase, e.g. update_post/update_page excerpt), so an untouched empty description field silently wiped existing text on every update. Flagged by Cursor Bugbot. --- apps/sim/tools/wordpress/update_category.ts | 2 +- apps/sim/tools/wordpress/update_tag.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/sim/tools/wordpress/update_category.ts b/apps/sim/tools/wordpress/update_category.ts index 1b5f22d484c..cbb47510c21 100644 --- a/apps/sim/tools/wordpress/update_category.ts +++ b/apps/sim/tools/wordpress/update_category.ts @@ -70,7 +70,7 @@ export const updateCategoryTool: ToolConfig< const body: Record = {} if (params.name) body.name = params.name - if (params.description !== undefined) body.description = params.description + if (params.description) body.description = params.description if (params.parent !== undefined) body.parent = params.parent if (params.slug) body.slug = params.slug diff --git a/apps/sim/tools/wordpress/update_tag.ts b/apps/sim/tools/wordpress/update_tag.ts index 2e5d4171be9..3f52dfb926b 100644 --- a/apps/sim/tools/wordpress/update_tag.ts +++ b/apps/sim/tools/wordpress/update_tag.ts @@ -61,7 +61,7 @@ export const updateTagTool: ToolConfig = {} if (params.name) body.name = params.name - if (params.description !== undefined) body.description = params.description + if (params.description) body.description = params.description if (params.slug) body.slug = params.slug return body From 3fc3fe245757b89f01f7d29b6e0a32fd4b2eca36 Mon Sep 17 00:00:00 2001 From: waleed Date: Thu, 2 Jul 2026 09:03:31 -0700 Subject: [PATCH 6/8] fix(wordpress): fix search type/subtype mislabeling, complete I/O exposure gaps - search_content.ts: type param was mislabeled with subtype's vocabulary (post/page/attachment); real WP type enum is post/term/post-format. Rewired the block's Content Type dropdown to map to subtype (which is what post/page/attachment actually filter), not type. - Widened subBlock conditions so params already read by tools.config.params are actually reachable in the UI: commentPostId for list_comments, categories/tags for list_posts, parent for list_pages. - Added missing subBlocks for tool params with no UI path: comment parent/authorName/authorEmail/authorUrl (create_comment), media description (upload_media), author filter (list_posts). - Extended the ?? / !== undefined fix (already applied to categoryParent) to the same class of param across the block: featuredMedia, page parent, menuOrder, and the new commentParent/listAuthor mappings. - Fixed featuredMedia/parent truthy-check inconsistency in create_post, update_post, create_page, update_page, create_category, create_comment body builders to match the !== undefined convention used elsewhere. Found by an independent final validation pass across 3 parallel agents. --- apps/sim/blocks/blocks/wordpress.ts | 137 +++++++++++++++++--- apps/sim/tools/wordpress/create_category.ts | 2 +- apps/sim/tools/wordpress/create_comment.ts | 2 +- apps/sim/tools/wordpress/create_page.ts | 6 +- apps/sim/tools/wordpress/create_post.ts | 2 +- apps/sim/tools/wordpress/search_content.ts | 12 +- apps/sim/tools/wordpress/types.ts | 2 +- apps/sim/tools/wordpress/update_page.ts | 2 +- apps/sim/tools/wordpress/update_post.ts | 2 +- 9 files changed, 136 insertions(+), 31 deletions(-) diff --git a/apps/sim/blocks/blocks/wordpress.ts b/apps/sim/blocks/blocks/wordpress.ts index 92e7d639a6e..4c1e073c8ab 100644 --- a/apps/sim/blocks/blocks/wordpress.ts +++ b/apps/sim/blocks/blocks/wordpress.ts @@ -214,7 +214,7 @@ export const WordPressBlock: BlockConfig = { }, }, - // Categories (for posts only) + // Categories (for posts) { id: 'categories', title: 'Categories', @@ -223,11 +223,11 @@ export const WordPressBlock: BlockConfig = { mode: 'advanced', condition: { field: 'operation', - value: ['wordpress_create_post', 'wordpress_update_post'], + value: ['wordpress_create_post', 'wordpress_update_post', 'wordpress_list_posts'], }, }, - // Tags (for posts only) + // Tags (for posts) { id: 'tags', title: 'Tags', @@ -236,10 +236,20 @@ export const WordPressBlock: BlockConfig = { mode: 'advanced', condition: { field: 'operation', - value: ['wordpress_create_post', 'wordpress_update_post'], + value: ['wordpress_create_post', 'wordpress_update_post', 'wordpress_list_posts'], }, }, + // List Posts: Author filter + { + id: 'listAuthor', + title: 'Author ID', + type: 'short-input', + placeholder: 'Filter by author ID', + mode: 'advanced', + condition: { field: 'operation', value: 'wordpress_list_posts' }, + }, + // Featured Media ID { id: 'featuredMedia', @@ -283,7 +293,7 @@ export const WordPressBlock: BlockConfig = { mode: 'advanced', condition: { field: 'operation', - value: ['wordpress_create_page', 'wordpress_update_page'], + value: ['wordpress_create_page', 'wordpress_update_page', 'wordpress_list_pages'], }, }, @@ -355,6 +365,14 @@ export const WordPressBlock: BlockConfig = { mode: 'advanced', condition: { field: 'operation', value: 'wordpress_upload_media' }, }, + { + id: 'mediaDescription', + title: 'Description', + type: 'long-input', + placeholder: 'Media description', + mode: 'advanced', + condition: { field: 'operation', value: 'wordpress_upload_media' }, + }, { id: 'mediaId', title: 'Media ID', @@ -391,7 +409,10 @@ export const WordPressBlock: BlockConfig = { title: 'Post ID', type: 'short-input', placeholder: 'Post ID to comment on', - condition: { field: 'operation', value: 'wordpress_create_comment' }, + condition: { + field: 'operation', + value: ['wordpress_create_comment', 'wordpress_list_comments'], + }, required: { field: 'operation', value: 'wordpress_create_comment' }, }, { @@ -405,6 +426,38 @@ export const WordPressBlock: BlockConfig = { }, required: { field: 'operation', value: 'wordpress_create_comment' }, }, + { + id: 'commentParent', + title: 'Parent Comment ID', + type: 'short-input', + placeholder: 'Parent comment ID (for replies)', + mode: 'advanced', + condition: { field: 'operation', value: 'wordpress_create_comment' }, + }, + { + id: 'commentAuthorName', + title: 'Author Name', + type: 'short-input', + placeholder: 'Comment author display name', + mode: 'advanced', + condition: { field: 'operation', value: 'wordpress_create_comment' }, + }, + { + id: 'commentAuthorEmail', + title: 'Author Email', + type: 'short-input', + placeholder: 'Comment author email', + mode: 'advanced', + condition: { field: 'operation', value: 'wordpress_create_comment' }, + }, + { + id: 'commentAuthorUrl', + title: 'Author URL', + type: 'short-input', + placeholder: 'Comment author URL', + mode: 'advanced', + condition: { field: 'operation', value: 'wordpress_create_comment' }, + }, { id: 'commentId', title: 'Comment ID', @@ -571,7 +624,7 @@ export const WordPressBlock: BlockConfig = { required: { field: 'operation', value: 'wordpress_search_content' }, }, { - id: 'searchType', + id: 'searchSubtype', title: 'Content Type', type: 'dropdown', options: [ @@ -779,7 +832,10 @@ export const WordPressBlock: BlockConfig = { slug: params.slug, categories: params.categories, tags: params.tags, - featuredMedia: params.featuredMedia ? Number(params.featuredMedia) : undefined, + featuredMedia: + params.featuredMedia !== undefined && params.featuredMedia !== '' + ? Number(params.featuredMedia) + : undefined, } case 'wordpress_update_post': return { @@ -792,7 +848,10 @@ export const WordPressBlock: BlockConfig = { slug: params.slug, categories: params.categories, tags: params.tags, - featuredMedia: params.featuredMedia ? Number(params.featuredMedia) : undefined, + featuredMedia: + params.featuredMedia !== undefined && params.featuredMedia !== '' + ? Number(params.featuredMedia) + : undefined, } case 'wordpress_delete_post': return { @@ -816,6 +875,10 @@ export const WordPressBlock: BlockConfig = { order: params.order, categories: params.categories, tags: params.tags, + author: + params.listAuthor !== undefined && params.listAuthor !== '' + ? Number(params.listAuthor) + : undefined, } case 'wordpress_create_page': return { @@ -825,9 +888,18 @@ export const WordPressBlock: BlockConfig = { status: params.status, excerpt: params.excerpt, slug: params.slug, - parent: params.parent ? Number(params.parent) : undefined, - menuOrder: params.menuOrder ? Number(params.menuOrder) : undefined, - featuredMedia: params.featuredMedia ? Number(params.featuredMedia) : undefined, + parent: + params.parent !== undefined && params.parent !== '' + ? Number(params.parent) + : undefined, + menuOrder: + params.menuOrder !== undefined && params.menuOrder !== '' + ? Number(params.menuOrder) + : undefined, + featuredMedia: + params.featuredMedia !== undefined && params.featuredMedia !== '' + ? Number(params.featuredMedia) + : undefined, } case 'wordpress_update_page': return { @@ -838,9 +910,18 @@ export const WordPressBlock: BlockConfig = { status: params.status, excerpt: params.excerpt, slug: params.slug, - parent: params.parent ? Number(params.parent) : undefined, - menuOrder: params.menuOrder ? Number(params.menuOrder) : undefined, - featuredMedia: params.featuredMedia ? Number(params.featuredMedia) : undefined, + parent: + params.parent !== undefined && params.parent !== '' + ? Number(params.parent) + : undefined, + menuOrder: + params.menuOrder !== undefined && params.menuOrder !== '' + ? Number(params.menuOrder) + : undefined, + featuredMedia: + params.featuredMedia !== undefined && params.featuredMedia !== '' + ? Number(params.featuredMedia) + : undefined, } case 'wordpress_delete_page': return { @@ -862,7 +943,10 @@ export const WordPressBlock: BlockConfig = { search: params.search, orderBy: params.orderBy, order: params.order, - parent: params.parent ? Number(params.parent) : undefined, + parent: + params.parent !== undefined && params.parent !== '' + ? Number(params.parent) + : undefined, } case 'wordpress_upload_media': // file is the canonical param for both basic (fileUpload) and advanced modes @@ -873,6 +957,7 @@ export const WordPressBlock: BlockConfig = { title: params.mediaTitle, caption: params.caption, altText: params.altText, + description: params.mediaDescription || undefined, } case 'wordpress_get_media': return { @@ -899,6 +984,13 @@ export const WordPressBlock: BlockConfig = { ...baseParams, postId: Number(params.commentPostId), content: params.commentContent, + parent: + params.commentParent !== undefined && params.commentParent !== '' + ? Number(params.commentParent) + : undefined, + authorName: params.commentAuthorName || undefined, + authorEmail: params.commentAuthorEmail || undefined, + authorUrl: params.commentAuthorUrl || undefined, } case 'wordpress_list_comments': return { @@ -1019,7 +1111,7 @@ export const WordPressBlock: BlockConfig = { query: params.query, perPage: params.perPage ? Number(params.perPage) : undefined, page: params.page ? Number(params.page) : undefined, - type: params.searchType || undefined, + subtype: params.searchSubtype || undefined, } default: return baseParams @@ -1040,6 +1132,7 @@ export const WordPressBlock: BlockConfig = { slug: { type: 'string', description: 'URL slug' }, categories: { type: 'string', description: 'Category IDs (comma-separated)' }, tags: { type: 'string', description: 'Tag IDs (comma-separated)' }, + listAuthor: { type: 'string', description: 'Filter posts by author ID' }, featuredMedia: { type: 'number', description: 'Featured media ID' }, // Page inputs pageId: { type: 'number', description: 'Page ID' }, @@ -1051,11 +1144,16 @@ export const WordPressBlock: BlockConfig = { mediaTitle: { type: 'string', description: 'Media title' }, caption: { type: 'string', description: 'Media caption' }, altText: { type: 'string', description: 'Alt text' }, + mediaDescription: { type: 'string', description: 'Media description' }, mediaId: { type: 'number', description: 'Media ID' }, mediaType: { type: 'string', description: 'Media type filter' }, // Comment inputs commentPostId: { type: 'number', description: 'Post ID for comment' }, commentContent: { type: 'string', description: 'Comment content' }, + commentParent: { type: 'number', description: 'Parent comment ID for replies' }, + commentAuthorName: { type: 'string', description: 'Comment author display name' }, + commentAuthorEmail: { type: 'string', description: 'Comment author email' }, + commentAuthorUrl: { type: 'string', description: 'Comment author URL' }, commentId: { type: 'number', description: 'Comment ID' }, commentStatus: { type: 'string', description: 'Comment status' }, // Category inputs @@ -1074,7 +1172,10 @@ export const WordPressBlock: BlockConfig = { roles: { type: 'string', description: 'User roles filter' }, // Search inputs query: { type: 'string', description: 'Search query' }, - searchType: { type: 'string', description: 'Content type filter' }, + searchSubtype: { + type: 'string', + description: 'Content subtype filter (post, page, attachment)', + }, // List inputs perPage: { type: 'number', description: 'Results per page' }, page: { type: 'number', description: 'Page number' }, diff --git a/apps/sim/tools/wordpress/create_category.ts b/apps/sim/tools/wordpress/create_category.ts index 61bb4076a34..f787b60b186 100644 --- a/apps/sim/tools/wordpress/create_category.ts +++ b/apps/sim/tools/wordpress/create_category.ts @@ -66,7 +66,7 @@ export const createCategoryTool: ToolConfig< } if (params.description) body.description = params.description - if (params.parent) body.parent = params.parent + if (params.parent !== undefined) body.parent = params.parent if (params.slug) body.slug = params.slug return body diff --git a/apps/sim/tools/wordpress/create_comment.ts b/apps/sim/tools/wordpress/create_comment.ts index d4f473105f1..dd6fb5898b1 100644 --- a/apps/sim/tools/wordpress/create_comment.ts +++ b/apps/sim/tools/wordpress/create_comment.ts @@ -78,7 +78,7 @@ export const createCommentTool: ToolConfig< content: params.content, } - if (params.parent) body.parent = params.parent + if (params.parent !== undefined) body.parent = params.parent if (params.authorName) body.author_name = params.authorName if (params.authorEmail) body.author_email = params.authorEmail if (params.authorUrl) body.author_url = params.authorUrl diff --git a/apps/sim/tools/wordpress/create_page.ts b/apps/sim/tools/wordpress/create_page.ts index 161cd10fb8d..2a7f7658f74 100644 --- a/apps/sim/tools/wordpress/create_page.ts +++ b/apps/sim/tools/wordpress/create_page.ts @@ -90,9 +90,9 @@ export const createPageTool: ToolConfig Date: Thu, 2 Jul 2026 09:07:22 -0700 Subject: [PATCH 7/8] fix(wordpress): keep searchType subBlock id to preserve saved-workflow compat MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The type/subtype fix should only change which API param the field feeds (subtype, not type) — renaming the subBlock id to searchSubtype broke already-saved workflows with a search content-type filter set, since the block would stop reading the old searchType key. Reverted the id rename, kept the underlying subtype mapping fix. Flagged by Cursor Bugbot. --- apps/sim/blocks/blocks/wordpress.ts | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/apps/sim/blocks/blocks/wordpress.ts b/apps/sim/blocks/blocks/wordpress.ts index 4c1e073c8ab..db6044be247 100644 --- a/apps/sim/blocks/blocks/wordpress.ts +++ b/apps/sim/blocks/blocks/wordpress.ts @@ -624,7 +624,7 @@ export const WordPressBlock: BlockConfig = { required: { field: 'operation', value: 'wordpress_search_content' }, }, { - id: 'searchSubtype', + id: 'searchType', title: 'Content Type', type: 'dropdown', options: [ @@ -1111,7 +1111,7 @@ export const WordPressBlock: BlockConfig = { query: params.query, perPage: params.perPage ? Number(params.perPage) : undefined, page: params.page ? Number(params.page) : undefined, - subtype: params.searchSubtype || undefined, + subtype: params.searchType || undefined, } default: return baseParams @@ -1172,9 +1172,10 @@ export const WordPressBlock: BlockConfig = { roles: { type: 'string', description: 'User roles filter' }, // Search inputs query: { type: 'string', description: 'Search query' }, - searchSubtype: { + searchType: { type: 'string', - description: 'Content subtype filter (post, page, attachment)', + description: + 'Content subtype filter (post, page, attachment) — maps to the API subtype param', }, // List inputs perPage: { type: 'number', description: 'Results per page' }, From 4bc8d280b5d56eea7ec3f1f8f3721dd74dc0f6b6 Mon Sep 17 00:00:00 2001 From: waleed Date: Thu, 2 Jul 2026 10:02:48 -0700 Subject: [PATCH 8/8] fix(wordpress): remove invalid Attachment search subtype, fix listAuthor input type MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Search Content's "Content Type" dropdown offered Attachment, which maps to subtype=attachment. WP core's WP_REST_Post_Search_Handler explicitly excludes attachment from valid subtypes (media isn't searchable via /search) — selecting it guaranteed a 400 rest_invalid_param. Removed the option; only Post/Page (the only valid subtypes) remain. - listAuthor was declared type: 'string' in the inputs catalog despite being Number()-coerced before use, inconsistent with every other ID-like field (postId, pageId, categoryId, commentParent, etc. are all 'number'). Found by an independent final pre-merge validation pass, requested before merge to be certain of full API alignment. --- apps/sim/blocks/blocks/wordpress.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/apps/sim/blocks/blocks/wordpress.ts b/apps/sim/blocks/blocks/wordpress.ts index db6044be247..cb38487859e 100644 --- a/apps/sim/blocks/blocks/wordpress.ts +++ b/apps/sim/blocks/blocks/wordpress.ts @@ -631,7 +631,6 @@ export const WordPressBlock: BlockConfig = { { label: 'All Types', id: '' }, { label: 'Post', id: 'post' }, { label: 'Page', id: 'page' }, - { label: 'Attachment', id: 'attachment' }, ], value: () => '', mode: 'advanced', @@ -1132,7 +1131,7 @@ export const WordPressBlock: BlockConfig = { slug: { type: 'string', description: 'URL slug' }, categories: { type: 'string', description: 'Category IDs (comma-separated)' }, tags: { type: 'string', description: 'Tag IDs (comma-separated)' }, - listAuthor: { type: 'string', description: 'Filter posts by author ID' }, + listAuthor: { type: 'number', description: 'Filter posts by author ID' }, featuredMedia: { type: 'number', description: 'Featured media ID' }, // Page inputs pageId: { type: 'number', description: 'Page ID' }, @@ -1174,8 +1173,7 @@ export const WordPressBlock: BlockConfig = { query: { type: 'string', description: 'Search query' }, searchType: { type: 'string', - description: - 'Content subtype filter (post, page, attachment) — maps to the API subtype param', + description: 'Content subtype filter (post, page) — maps to the API subtype param', }, // List inputs perPage: { type: 'number', description: 'Results per page' },