From a08da86dff1cbd2d6a19aadee89cdef8b7fe5ef9 Mon Sep 17 00:00:00 2001 From: Waleed Date: Thu, 2 Jul 2026 10:47:31 -0700 Subject: [PATCH] feat(wordpress): add category/tag CRUD tools, fix delete-status and dead-field bugs (#5360) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 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 * 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. * fix(wordpress): use ?? for zero-valued numeric fields across all delete tools 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. * 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. * 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. * 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. * fix(wordpress): keep searchType subBlock id to preserve saved-workflow compat 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. * fix(wordpress): remove invalid Attachment search subtype, fix listAuthor input type - 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 | 283 +++++++++++++++++--- apps/sim/tools/registry.ts | 12 + 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/delete_category.ts | 96 +++++++ apps/sim/tools/wordpress/delete_comment.ts | 10 +- apps/sim/tools/wordpress/delete_media.ts | 12 +- apps/sim/tools/wordpress/delete_page.ts | 12 +- apps/sim/tools/wordpress/delete_post.ts | 8 +- 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 | 16 +- apps/sim/tools/wordpress/types.ts | 84 +++++- apps/sim/tools/wordpress/update_category.ts | 122 +++++++++ apps/sim/tools/wordpress/update_page.ts | 2 +- apps/sim/tools/wordpress/update_post.ts | 2 +- apps/sim/tools/wordpress/update_tag.ts | 110 ++++++++ 21 files changed, 978 insertions(+), 75 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 b451b6a1a4..cb38487859 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' }, @@ -208,7 +214,7 @@ export const WordPressBlock: BlockConfig = { }, }, - // Categories (for posts only) + // Categories (for posts) { id: 'categories', title: 'Categories', @@ -217,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', @@ -230,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', @@ -277,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'], }, }, @@ -349,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', @@ -385,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' }, }, { @@ -399,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', @@ -429,12 +488,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 +519,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 +530,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 +541,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 +579,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 +590,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 @@ -523,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', @@ -665,12 +772,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 +798,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', @@ -723,7 +831,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 { @@ -736,7 +847,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 { @@ -760,6 +874,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 { @@ -769,9 +887,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 { @@ -782,9 +909,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 { @@ -806,7 +942,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 @@ -817,6 +956,7 @@ export const WordPressBlock: BlockConfig = { title: params.mediaTitle, caption: params.caption, altText: params.altText, + description: params.mediaDescription || undefined, } case 'wordpress_get_media': return { @@ -837,13 +977,19 @@ export const WordPressBlock: BlockConfig = { return { ...baseParams, mediaId: Number(params.mediaId), - force: params.force, } case 'wordpress_create_comment': return { ...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 { @@ -873,7 +1019,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': @@ -884,6 +1033,28 @@ 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 !== undefined && 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 +1070,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': @@ -921,7 +1110,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.searchType || undefined, } default: return baseParams @@ -942,6 +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: 'number', description: 'Filter posts by author ID' }, featuredMedia: { type: 'number', description: 'Featured media ID' }, // Page inputs pageId: { type: 'number', description: 'Page ID' }, @@ -953,19 +1143,26 @@ 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 + 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' }, @@ -974,7 +1171,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' }, + searchType: { + type: 'string', + description: 'Content subtype filter (post, page) — maps to the API subtype param', + }, // List inputs perPage: { type: 'number', description: 'Results per page' }, page: { type: 'number', description: 'Page number' }, @@ -983,7 +1183,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 +1313,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 50c24a36e4..c13f19830a 100644 --- a/apps/sim/tools/registry.ts +++ b/apps/sim/tools/registry.ts @@ -4163,14 +4163,18 @@ import { wordpressCreatePageTool, wordpressCreatePostTool, wordpressCreateTagTool, + wordpressDeleteCategoryTool, wordpressDeleteCommentTool, wordpressDeleteMediaTool, wordpressDeletePageTool, wordpressDeletePostTool, + wordpressDeleteTagTool, + wordpressGetCategoryTool, wordpressGetCurrentUserTool, wordpressGetMediaTool, wordpressGetPageTool, wordpressGetPostTool, + wordpressGetTagTool, wordpressGetUserTool, wordpressListCategoriesTool, wordpressListCommentsTool, @@ -4180,9 +4184,11 @@ import { wordpressListTagsTool, wordpressListUsersTool, wordpressSearchContentTool, + wordpressUpdateCategoryTool, wordpressUpdateCommentTool, wordpressUpdatePageTool, wordpressUpdatePostTool, + wordpressUpdateTagTool, wordpressUploadMediaTool, } from '@/tools/wordpress' import { @@ -7490,8 +7496,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/create_category.ts b/apps/sim/tools/wordpress/create_category.ts index 61bb4076a3..f787b60b18 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 d4f473105f..dd6fb5898b 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 161cd10fb8..2a7f7658f7 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 = { + 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 5aec306e5d..03d4b6081d 100644 --- a/apps/sim/tools/wordpress/delete_comment.ts +++ b/apps/sim/tools/wordpress/delete_comment.ts @@ -64,12 +64,12 @@ 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, - 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 3b680919e3..0dfc8e0b1d 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,9 +56,9 @@ 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 0000000000..968dafe7fb --- /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 0000000000..bc4645c4fc --- /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 3889208a54..ab48592b46 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 2d20340810..706b510991 100644 --- a/apps/sim/tools/wordpress/search_content.ts +++ b/apps/sim/tools/wordpress/search_content.ts @@ -36,26 +36,27 @@ 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: { type: 'string', required: false, visibility: 'user-only', - description: 'Filter by content type: post, page, attachment', + description: 'Filter by search index type: post, term, or post-format', }, subtype: { type: 'string', required: false, visibility: 'user-only', - description: 'Filter by post type slug (e.g., post, page)', + description: + 'Filter by subtype within the selected type (e.g., post or page when type is post)', }, }, @@ -114,8 +115,11 @@ export const searchContentTool: ToolConfig< id: { type: 'number', description: 'Content ID' }, title: { type: 'string', description: 'Content title' }, url: { type: 'string', description: 'Content URL' }, - type: { type: 'string', description: 'Content type (post, page, attachment)' }, - subtype: { type: 'string', description: 'Post type slug' }, + type: { type: 'string', description: 'Content type (post, term, or post-format)' }, + subtype: { + type: 'string', + description: 'Subtype within the content type (e.g., post, page)', + }, }, }, }, diff --git a/apps/sim/tools/wordpress/types.ts b/apps/sim/tools/wordpress/types.ts index 81bc115e41..146b72905c 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 // ============================================ @@ -576,7 +650,7 @@ export interface WordPressSearchContentParams extends WordPressBaseParams { query: string perPage?: number page?: number - type?: 'post' | 'page' | 'attachment' + type?: 'post' | 'term' | 'post-format' subtype?: string } @@ -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 0000000000..cbb47510c2 --- /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) 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_page.ts b/apps/sim/tools/wordpress/update_page.ts index 37d3dc1d0f..0c7fe51b79 100644 --- a/apps/sim/tools/wordpress/update_page.ts +++ b/apps/sim/tools/wordpress/update_page.ts @@ -97,7 +97,7 @@ export const updatePageTool: 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) 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' }, + }, + }, + }, +}