feat(wordpress): add category/tag CRUD tools, fix delete-status and dead-field bugs (#5360)

* 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.
This commit is contained in:
Waleed
2026-07-02 10:47:31 -07:00
committed by GitHub
parent 7a31871471
commit a08da86dff
21 changed files with 978 additions and 75 deletions
+248 -35
View File
@@ -11,7 +11,7 @@ export const WordPressBlock: BlockConfig<WordPressResponse> = {
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<WordPressResponse> = {
{ 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<WordPressResponse> = {
},
},
// Categories (for posts only)
// Categories (for posts)
{
id: 'categories',
title: 'Categories',
@@ -217,11 +223,11 @@ export const WordPressBlock: BlockConfig<WordPressResponse> = {
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<WordPressResponse> = {
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<WordPressResponse> = {
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<WordPressResponse> = {
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<WordPressResponse> = {
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<WordPressResponse> = {
},
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<WordPressResponse> = {
},
// 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<WordPressResponse> = {
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<WordPressResponse> = {
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<WordPressResponse> = {
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<WordPressResponse> = {
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<WordPressResponse> = {
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<WordPressResponse> = {
{ 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<WordPressResponse> = {
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<WordPressResponse> = {
'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<WordPressResponse> = {
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<WordPressResponse> = {
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<WordPressResponse> = {
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<WordPressResponse> = {
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<WordPressResponse> = {
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<WordPressResponse> = {
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<WordPressResponse> = {
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<WordPressResponse> = {
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<WordPressResponse> = {
...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<WordPressResponse> = {
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<WordPressResponse> = {
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<WordPressResponse> = {
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<WordPressResponse> = {
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<WordPressResponse> = {
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<WordPressResponse> = {
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<WordPressResponse> = {
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
+12
View File
@@ -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<string, ToolConfig> = {
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,
+1 -1
View File
@@ -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
+1 -1
View File
@@ -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
+3 -3
View File
@@ -90,9 +90,9 @@ export const createPageTool: ToolConfig<WordPressCreatePageParams, WordPressCrea
if (params.status) body.status = params.status
if (params.excerpt) body.excerpt = params.excerpt
if (params.slug) body.slug = params.slug
if (params.parent) body.parent = params.parent
if (params.menuOrder) body.menu_order = params.menuOrder
if (params.featuredMedia) body.featured_media = params.featuredMedia
if (params.parent !== undefined) body.parent = params.parent
if (params.menuOrder !== undefined) body.menu_order = params.menuOrder
if (params.featuredMedia !== undefined) body.featured_media = params.featuredMedia
return body
},
+1 -1
View File
@@ -90,7 +90,7 @@ export const createPostTool: ToolConfig<WordPressCreatePostParams, WordPressCrea
if (params.status) body.status = params.status
if (params.excerpt) body.excerpt = params.excerpt
if (params.slug) body.slug = params.slug
if (params.featuredMedia) body.featured_media = params.featuredMedia
if (params.featuredMedia !== undefined) body.featured_media = params.featuredMedia
if (params.categories) {
body.categories = params.categories
@@ -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' },
},
},
},
}
+5 -5
View File
@@ -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,
+3 -9
View File
@@ -31,17 +31,11 @@ export const deleteMediaTool: ToolConfig<WordPressDeleteMediaParams, WordPressDe
visibility: 'user-or-llm',
description: 'The ID of the media item to delete',
},
force: {
type: 'boolean',
required: false,
visibility: 'user-only',
description: 'Force delete (media has no trash, so deletion is permanent)',
},
},
request: {
url: (params) => {
// 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<WordPressDeleteMediaParams, WordPressDe
return {
success: true,
output: {
deleted: data.deleted || true,
deleted: data.deleted ?? true,
media: {
id: data.id || data.previous?.id,
id: data.id ?? data.previous?.id,
date: data.date || data.previous?.date,
slug: data.slug || data.previous?.slug,
type: data.type || data.previous?.type,
+6 -6
View File
@@ -61,9 +61,9 @@ export const deletePageTool: ToolConfig<WordPressDeletePageParams, WordPressDele
return {
success: true,
output: {
deleted: data.deleted || true,
deleted: data.deleted ?? true,
page: {
id: data.id || data.previous?.id,
id: data.id ?? data.previous?.id,
date: data.date || data.previous?.date,
modified: data.modified || data.previous?.modified,
slug: data.slug || data.previous?.slug,
@@ -73,10 +73,10 @@ export const deletePageTool: ToolConfig<WordPressDeletePageParams, WordPressDele
title: data.title || data.previous?.title,
content: data.content || data.previous?.content,
excerpt: data.excerpt || data.previous?.excerpt,
author: data.author || data.previous?.author,
featured_media: data.featured_media || data.previous?.featured_media,
parent: data.parent || data.previous?.parent,
menu_order: data.menu_order || data.previous?.menu_order,
author: data.author ?? data.previous?.author,
featured_media: data.featured_media ?? data.previous?.featured_media,
parent: data.parent ?? data.previous?.parent,
menu_order: data.menu_order ?? data.previous?.menu_order,
},
},
}
+4 -4
View File
@@ -61,9 +61,9 @@ export const deletePostTool: ToolConfig<WordPressDeletePostParams, WordPressDele
return {
success: true,
output: {
deleted: data.deleted || true,
deleted: data.deleted ?? true,
post: {
id: data.id || data.previous?.id,
id: data.id ?? data.previous?.id,
date: data.date || data.previous?.date,
modified: data.modified || data.previous?.modified,
slug: data.slug || data.previous?.slug,
@@ -73,8 +73,8 @@ export const deletePostTool: ToolConfig<WordPressDeletePostParams, WordPressDele
title: data.title || data.previous?.title,
content: data.content || data.previous?.content,
excerpt: data.excerpt || data.previous?.excerpt,
author: data.author || data.previous?.author,
featured_media: data.featured_media || data.previous?.featured_media,
author: data.author ?? data.previous?.author,
featured_media: data.featured_media ?? data.previous?.featured_media,
categories: data.categories || data.previous?.categories || [],
tags: data.tags || data.previous?.tags || [],
},
+91
View File
@@ -0,0 +1,91 @@
import type { ToolConfig } from '@/tools/types'
import {
WORDPRESS_COM_API_BASE,
type WordPressDeleteTagParams,
type WordPressDeleteTagResponse,
} from '@/tools/wordpress/types'
export const deleteTagTool: ToolConfig<WordPressDeleteTagParams, WordPressDeleteTagResponse> = {
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' },
},
},
},
}
+86
View File
@@ -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<WordPressGetCategoryParams, WordPressGetCategoryResponse> =
{
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' },
},
},
},
}
+83
View File
@@ -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<WordPressGetTagParams, WordPressGetTagResponse> = {
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' },
},
},
},
}
+12
View File
@@ -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
+10 -6
View File
@@ -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)',
},
},
},
},
+82 -2
View File
@@ -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
+122
View File
@@ -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<string, any> = {}
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' },
},
},
},
}
+1 -1
View File
@@ -97,7 +97,7 @@ export const updatePageTool: ToolConfig<WordPressUpdatePageParams, WordPressUpda
if (params.slug) body.slug = params.slug
if (params.parent !== undefined) body.parent = params.parent
if (params.menuOrder !== undefined) body.menu_order = params.menuOrder
if (params.featuredMedia) body.featured_media = params.featuredMedia
if (params.featuredMedia !== undefined) body.featured_media = params.featuredMedia
return body
},
+1 -1
View File
@@ -95,7 +95,7 @@ export const updatePostTool: ToolConfig<WordPressUpdatePostParams, WordPressUpda
if (params.status) body.status = params.status
if (params.excerpt) body.excerpt = params.excerpt
if (params.slug) body.slug = params.slug
if (params.featuredMedia) body.featured_media = params.featuredMedia
if (params.featuredMedia !== undefined) body.featured_media = params.featuredMedia
if (params.categories) {
body.categories = params.categories
+110
View File
@@ -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<WordPressUpdateTagParams, WordPressUpdateTagResponse> = {
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<string, any> = {}
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' },
},
},
},
}