mirror of
https://github.com/simstudioai/sim.git
synced 2026-09-24 15:45:35 +08:00
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:
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
},
|
||||
|
||||
@@ -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' },
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -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 || [],
|
||||
},
|
||||
|
||||
@@ -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' },
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -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' },
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -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' },
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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)',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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' },
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -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
|
||||
},
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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' },
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
Reference in New Issue
Block a user