claude-plugin-wordpress-manager 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (142) hide show
  1. package/.claude-plugin/plugin.json +19 -0
  2. package/.mcp.json +19 -0
  3. package/CHANGELOG.md +62 -0
  4. package/LICENSE +69 -0
  5. package/README.md +213 -0
  6. package/agents/wp-content-strategist.md +148 -0
  7. package/agents/wp-deployment-engineer.md +93 -0
  8. package/agents/wp-performance-optimizer.md +198 -0
  9. package/agents/wp-security-auditor.md +161 -0
  10. package/agents/wp-site-manager.md +109 -0
  11. package/commands/wp-audit.md +37 -0
  12. package/commands/wp-backup.md +45 -0
  13. package/commands/wp-deploy.md +38 -0
  14. package/commands/wp-setup.md +64 -0
  15. package/commands/wp-status.md +53 -0
  16. package/docs/GUIDE.md +1190 -0
  17. package/hooks/hooks.json +57 -0
  18. package/hooks/scripts/backup-reminder.sh +29 -0
  19. package/hooks/scripts/pre-deploy-check.sh +49 -0
  20. package/package.json +46 -0
  21. package/scripts/health-check.sh +110 -0
  22. package/scripts/validate-wp-operation.sh +115 -0
  23. package/servers/wp-rest-bridge/build/server.d.ts +2 -0
  24. package/servers/wp-rest-bridge/build/server.js +74 -0
  25. package/servers/wp-rest-bridge/build/tools/comments.d.ts +227 -0
  26. package/servers/wp-rest-bridge/build/tools/comments.js +192 -0
  27. package/servers/wp-rest-bridge/build/tools/index.d.ts +919 -0
  28. package/servers/wp-rest-bridge/build/tools/index.js +30 -0
  29. package/servers/wp-rest-bridge/build/tools/media.d.ts +174 -0
  30. package/servers/wp-rest-bridge/build/tools/media.js +247 -0
  31. package/servers/wp-rest-bridge/build/tools/plugin-repository.d.ts +62 -0
  32. package/servers/wp-rest-bridge/build/tools/plugin-repository.js +149 -0
  33. package/servers/wp-rest-bridge/build/tools/plugins.d.ts +153 -0
  34. package/servers/wp-rest-bridge/build/tools/plugins.js +175 -0
  35. package/servers/wp-rest-bridge/build/tools/search.d.ts +44 -0
  36. package/servers/wp-rest-bridge/build/tools/search.js +44 -0
  37. package/servers/wp-rest-bridge/build/tools/unified-content.d.ts +328 -0
  38. package/servers/wp-rest-bridge/build/tools/unified-content.js +628 -0
  39. package/servers/wp-rest-bridge/build/tools/unified-taxonomies.d.ts +244 -0
  40. package/servers/wp-rest-bridge/build/tools/unified-taxonomies.js +492 -0
  41. package/servers/wp-rest-bridge/build/tools/users.d.ts +269 -0
  42. package/servers/wp-rest-bridge/build/tools/users.js +226 -0
  43. package/servers/wp-rest-bridge/build/types.d.ts +151 -0
  44. package/servers/wp-rest-bridge/build/types.js +2 -0
  45. package/servers/wp-rest-bridge/build/wordpress.d.ts +48 -0
  46. package/servers/wp-rest-bridge/build/wordpress.js +305 -0
  47. package/servers/wp-rest-bridge/package.json +27 -0
  48. package/skills/wordpress-router/SKILL.md +78 -0
  49. package/skills/wordpress-router/references/decision-tree.md +88 -0
  50. package/skills/wp-abilities-api/SKILL.md +97 -0
  51. package/skills/wp-abilities-api/references/php-registration.md +67 -0
  52. package/skills/wp-abilities-api/references/rest-api.md +13 -0
  53. package/skills/wp-audit/SKILL.md +114 -0
  54. package/skills/wp-audit/references/performance-checklist.md +113 -0
  55. package/skills/wp-audit/references/security-checklist.md +95 -0
  56. package/skills/wp-audit/references/seo-checklist.md +128 -0
  57. package/skills/wp-backup/SKILL.md +87 -0
  58. package/skills/wp-backup/references/backup-strategies.md +116 -0
  59. package/skills/wp-backup/references/restore-procedures.md +129 -0
  60. package/skills/wp-block-development/SKILL.md +176 -0
  61. package/skills/wp-block-development/references/attributes-and-serialization.md +22 -0
  62. package/skills/wp-block-development/references/block-json.md +49 -0
  63. package/skills/wp-block-development/references/creating-new-blocks.md +46 -0
  64. package/skills/wp-block-development/references/debugging.md +36 -0
  65. package/skills/wp-block-development/references/deprecations.md +24 -0
  66. package/skills/wp-block-development/references/dynamic-rendering.md +23 -0
  67. package/skills/wp-block-development/references/inner-blocks.md +25 -0
  68. package/skills/wp-block-development/references/registration.md +30 -0
  69. package/skills/wp-block-development/references/supports-and-wrappers.md +18 -0
  70. package/skills/wp-block-development/references/tooling-and-testing.md +21 -0
  71. package/skills/wp-block-development/scripts/list_blocks.mjs +121 -0
  72. package/skills/wp-block-themes/SKILL.md +118 -0
  73. package/skills/wp-block-themes/references/creating-new-block-theme.md +37 -0
  74. package/skills/wp-block-themes/references/debugging.md +24 -0
  75. package/skills/wp-block-themes/references/patterns.md +18 -0
  76. package/skills/wp-block-themes/references/style-variations.md +14 -0
  77. package/skills/wp-block-themes/references/templates-and-parts.md +16 -0
  78. package/skills/wp-block-themes/references/theme-json.md +59 -0
  79. package/skills/wp-block-themes/scripts/detect_block_themes.mjs +117 -0
  80. package/skills/wp-content/SKILL.md +103 -0
  81. package/skills/wp-content/references/content-templates.md +230 -0
  82. package/skills/wp-content/references/seo-optimization.md +169 -0
  83. package/skills/wp-deploy/SKILL.md +52 -0
  84. package/skills/wp-deploy/references/hostinger-deploy.md +51 -0
  85. package/skills/wp-deploy/references/ssh-deploy.md +63 -0
  86. package/skills/wp-interactivity-api/SKILL.md +181 -0
  87. package/skills/wp-interactivity-api/references/debugging.md +29 -0
  88. package/skills/wp-interactivity-api/references/directives-quickref.md +30 -0
  89. package/skills/wp-interactivity-api/references/server-side-rendering.md +310 -0
  90. package/skills/wp-migrate/SKILL.md +100 -0
  91. package/skills/wp-migrate/references/cross-platform.md +104 -0
  92. package/skills/wp-migrate/references/hostinger-migration.md +86 -0
  93. package/skills/wp-performance/SKILL.md +148 -0
  94. package/skills/wp-performance/references/autoload-options.md +24 -0
  95. package/skills/wp-performance/references/cron.md +20 -0
  96. package/skills/wp-performance/references/database.md +20 -0
  97. package/skills/wp-performance/references/http-api.md +15 -0
  98. package/skills/wp-performance/references/measurement.md +21 -0
  99. package/skills/wp-performance/references/object-cache.md +24 -0
  100. package/skills/wp-performance/references/query-monitor-headless.md +38 -0
  101. package/skills/wp-performance/references/server-timing.md +22 -0
  102. package/skills/wp-performance/references/wp-cli-doctor.md +24 -0
  103. package/skills/wp-performance/references/wp-cli-profile.md +32 -0
  104. package/skills/wp-performance/scripts/perf_inspect.mjs +128 -0
  105. package/skills/wp-phpstan/SKILL.md +99 -0
  106. package/skills/wp-phpstan/references/configuration.md +52 -0
  107. package/skills/wp-phpstan/references/third-party-classes.md +76 -0
  108. package/skills/wp-phpstan/references/wordpress-annotations.md +124 -0
  109. package/skills/wp-phpstan/scripts/phpstan_inspect.mjs +263 -0
  110. package/skills/wp-playground/SKILL.md +103 -0
  111. package/skills/wp-playground/references/blueprints.md +36 -0
  112. package/skills/wp-playground/references/cli-commands.md +39 -0
  113. package/skills/wp-playground/references/debugging.md +16 -0
  114. package/skills/wp-plugin-development/SKILL.md +114 -0
  115. package/skills/wp-plugin-development/references/data-and-cron.md +19 -0
  116. package/skills/wp-plugin-development/references/debugging.md +19 -0
  117. package/skills/wp-plugin-development/references/lifecycle.md +33 -0
  118. package/skills/wp-plugin-development/references/security.md +29 -0
  119. package/skills/wp-plugin-development/references/settings-api.md +22 -0
  120. package/skills/wp-plugin-development/references/structure.md +16 -0
  121. package/skills/wp-plugin-development/scripts/detect_plugins.mjs +122 -0
  122. package/skills/wp-project-triage/SKILL.md +40 -0
  123. package/skills/wp-project-triage/references/triage.schema.json +143 -0
  124. package/skills/wp-project-triage/scripts/detect_wp_project.mjs +592 -0
  125. package/skills/wp-rest-api/SKILL.md +116 -0
  126. package/skills/wp-rest-api/references/authentication.md +18 -0
  127. package/skills/wp-rest-api/references/custom-content-types.md +20 -0
  128. package/skills/wp-rest-api/references/discovery-and-params.md +20 -0
  129. package/skills/wp-rest-api/references/responses-and-fields.md +30 -0
  130. package/skills/wp-rest-api/references/routes-and-endpoints.md +36 -0
  131. package/skills/wp-rest-api/references/schema.md +22 -0
  132. package/skills/wp-wpcli-and-ops/SKILL.md +125 -0
  133. package/skills/wp-wpcli-and-ops/references/automation.md +30 -0
  134. package/skills/wp-wpcli-and-ops/references/cron-and-cache.md +23 -0
  135. package/skills/wp-wpcli-and-ops/references/debugging.md +17 -0
  136. package/skills/wp-wpcli-and-ops/references/multisite.md +22 -0
  137. package/skills/wp-wpcli-and-ops/references/packages-and-updates.md +22 -0
  138. package/skills/wp-wpcli-and-ops/references/safety.md +30 -0
  139. package/skills/wp-wpcli-and-ops/references/search-replace.md +40 -0
  140. package/skills/wp-wpcli-and-ops/scripts/wpcli_inspect.mjs +90 -0
  141. package/skills/wpds/SKILL.md +60 -0
  142. package/skills/wpds/references/wpds-mcp-setup.md +59 -0
@@ -0,0 +1,492 @@
1
+ import { makeWordPressRequest, logToFile, getActiveSite } from '../wordpress.js';
2
+ import { z } from 'zod';
3
+ // Site-aware cache for taxonomies to reduce API calls
4
+ const taxonomiesCache = new Map();
5
+ const CACHE_DURATION = 5 * 60 * 1000; // 5 minutes
6
+ // Helper function to get all taxonomies with caching (per-site)
7
+ async function getTaxonomies(forceRefresh = false) {
8
+ const siteId = getActiveSite();
9
+ const now = Date.now();
10
+ const cached = taxonomiesCache.get(siteId);
11
+ if (!forceRefresh && cached && (now - cached.timestamp) < CACHE_DURATION) {
12
+ logToFile(`Using cached taxonomies for site: ${siteId}`);
13
+ return cached.data;
14
+ }
15
+ try {
16
+ logToFile(`Fetching taxonomies from API for site: ${siteId}`);
17
+ const response = await makeWordPressRequest('GET', 'taxonomies');
18
+ taxonomiesCache.set(siteId, { data: response, timestamp: now });
19
+ return response;
20
+ }
21
+ catch (error) {
22
+ const errorMessage = error.response?.data?.message || error.message;
23
+ logToFile(`Error fetching taxonomies: ${errorMessage}`);
24
+ throw error;
25
+ }
26
+ }
27
+ // Helper function to get the correct endpoint for a taxonomy
28
+ function getTaxonomyEndpoint(taxonomy) {
29
+ const endpointMap = {
30
+ 'category': 'categories',
31
+ 'post_tag': 'tags',
32
+ 'nav_menu': 'menus',
33
+ 'link_category': 'link_categories'
34
+ };
35
+ return endpointMap[taxonomy] || taxonomy;
36
+ }
37
+ // Helper function to get the correct content endpoint
38
+ function getContentEndpoint(contentType) {
39
+ const endpointMap = {
40
+ 'post': 'posts',
41
+ 'page': 'pages'
42
+ };
43
+ return endpointMap[contentType] || contentType;
44
+ }
45
+ // Schema definitions
46
+ const discoverTaxonomiesSchema = z.object({
47
+ content_type: z.string().optional().describe("Limit results to taxonomies associated with a specific content type"),
48
+ refresh_cache: z.boolean().optional().describe("Force refresh the taxonomies cache")
49
+ });
50
+ const listTermsSchema = z.object({
51
+ taxonomy: z.string().describe("The taxonomy slug (e.g., 'category', 'post_tag', or custom taxonomies)"),
52
+ page: z.number().optional().describe("Page number (default 1)"),
53
+ per_page: z.number().min(1).max(100).optional().describe("Items per page (default 10, max 100)"),
54
+ search: z.string().optional().describe("Search term for term name"),
55
+ parent: z.number().optional().describe("Parent term ID to retrieve direct children"),
56
+ slug: z.string().optional().describe("Limit result to terms with a specific slug"),
57
+ hide_empty: z.boolean().optional().describe("Whether to hide terms not assigned to any content"),
58
+ orderby: z.enum(['id', 'include', 'name', 'slug', 'term_group', 'description', 'count']).optional().describe("Sort terms by parameter"),
59
+ order: z.enum(['asc', 'desc']).optional().describe("Order sort attribute"),
60
+ _embed: z.boolean().optional().describe("Inline related resources"),
61
+ _fields: z.string().optional().describe("Comma-separated list of fields to return"),
62
+ include_pagination: z.boolean().optional().describe("Include pagination metadata in response")
63
+ });
64
+ const getTermSchema = z.object({
65
+ taxonomy: z.string().describe("The taxonomy slug"),
66
+ id: z.number().describe("Term ID"),
67
+ _embed: z.boolean().optional().describe("Inline related resources"),
68
+ _fields: z.string().optional().describe("Comma-separated list of fields to return")
69
+ });
70
+ const createTermSchema = z.object({
71
+ taxonomy: z.string().describe("The taxonomy slug"),
72
+ name: z.string().describe("Term name"),
73
+ slug: z.string().optional().describe("Term slug"),
74
+ parent: z.number().optional().describe("Parent term ID"),
75
+ description: z.string().optional().describe("Term description"),
76
+ meta: z.record(z.any()).optional().describe("Term meta fields")
77
+ });
78
+ const updateTermSchema = z.object({
79
+ taxonomy: z.string().describe("The taxonomy slug"),
80
+ id: z.number().describe("Term ID"),
81
+ name: z.string().optional().describe("Term name"),
82
+ slug: z.string().optional().describe("Term slug"),
83
+ parent: z.number().optional().describe("Parent term ID"),
84
+ description: z.string().optional().describe("Term description"),
85
+ meta: z.record(z.any()).optional().describe("Term meta fields")
86
+ });
87
+ const deleteTermSchema = z.object({
88
+ taxonomy: z.string().describe("The taxonomy slug"),
89
+ id: z.number().describe("Term ID"),
90
+ force: z.boolean().optional().describe("Required to be true, as terms do not support trashing")
91
+ });
92
+ const assignTermsToContentSchema = z.object({
93
+ content_id: z.number().describe("The content ID"),
94
+ content_type: z.string().describe("The content type slug"),
95
+ taxonomy: z.string().describe("The taxonomy slug"),
96
+ terms: z.array(z.union([z.number(), z.string()])).describe("Array of term IDs or slugs to assign"),
97
+ append: z.boolean().optional().describe("If true, append terms to existing ones. If false, replace all terms")
98
+ });
99
+ const getContentTermsSchema = z.object({
100
+ content_id: z.number().describe("The content ID"),
101
+ content_type: z.string().describe("The content type slug"),
102
+ taxonomy: z.string().optional().describe("Specific taxonomy to retrieve terms from (if not specified, returns all)")
103
+ });
104
+ export const unifiedTaxonomyTools = [
105
+ {
106
+ name: "discover_taxonomies",
107
+ description: "Discovers all available taxonomies (built-in and custom) in the WordPress site",
108
+ inputSchema: { type: "object", properties: discoverTaxonomiesSchema.shape }
109
+ },
110
+ {
111
+ name: "list_terms",
112
+ description: "Lists terms in any taxonomy (categories, tags, or custom taxonomies) with filtering and pagination",
113
+ inputSchema: { type: "object", properties: listTermsSchema.shape }
114
+ },
115
+ {
116
+ name: "get_term",
117
+ description: "Gets a specific term by ID from any taxonomy",
118
+ inputSchema: { type: "object", properties: getTermSchema.shape }
119
+ },
120
+ {
121
+ name: "create_term",
122
+ description: "Creates a new term in any taxonomy",
123
+ inputSchema: { type: "object", properties: createTermSchema.shape }
124
+ },
125
+ {
126
+ name: "update_term",
127
+ description: "Updates an existing term in any taxonomy",
128
+ inputSchema: { type: "object", properties: updateTermSchema.shape }
129
+ },
130
+ {
131
+ name: "delete_term",
132
+ description: "Deletes a term from any taxonomy",
133
+ inputSchema: { type: "object", properties: deleteTermSchema.shape }
134
+ },
135
+ {
136
+ name: "assign_terms_to_content",
137
+ description: "Assigns taxonomy terms to content of any type",
138
+ inputSchema: { type: "object", properties: assignTermsToContentSchema.shape }
139
+ },
140
+ {
141
+ name: "get_content_terms",
142
+ description: "Gets all taxonomy terms assigned to content of any type",
143
+ inputSchema: { type: "object", properties: getContentTermsSchema.shape }
144
+ }
145
+ ];
146
+ export const unifiedTaxonomyHandlers = {
147
+ discover_taxonomies: async (params) => {
148
+ try {
149
+ const taxonomies = await getTaxonomies(params.refresh_cache || false);
150
+ // Filter by content type if specified
151
+ let filteredTaxonomies = taxonomies;
152
+ if (params.content_type) {
153
+ filteredTaxonomies = Object.fromEntries(Object.entries(taxonomies).filter(([_, tax]) => tax.types && tax.types.includes(params.content_type)));
154
+ }
155
+ // Format the response to be more readable
156
+ const formattedTaxonomies = Object.entries(filteredTaxonomies).map(([slug, tax]) => ({
157
+ slug,
158
+ name: tax.name,
159
+ description: tax.description,
160
+ types: tax.types,
161
+ hierarchical: tax.hierarchical,
162
+ rest_base: tax.rest_base,
163
+ labels: tax.labels
164
+ }));
165
+ return {
166
+ toolResult: {
167
+ content: [{
168
+ type: 'text',
169
+ text: JSON.stringify(formattedTaxonomies, null, 2)
170
+ }],
171
+ isError: false
172
+ }
173
+ };
174
+ }
175
+ catch (error) {
176
+ return {
177
+ toolResult: {
178
+ content: [{
179
+ type: 'text',
180
+ text: `Error discovering taxonomies: ${error.response?.data?.message || error.message}`
181
+ }],
182
+ isError: true
183
+ }
184
+ };
185
+ }
186
+ },
187
+ list_terms: async (params) => {
188
+ try {
189
+ const endpoint = getTaxonomyEndpoint(params.taxonomy);
190
+ const { taxonomy, include_pagination, ...queryParams } = params;
191
+ const response = await makeWordPressRequest('GET', endpoint, queryParams, {
192
+ includePagination: include_pagination,
193
+ });
194
+ return {
195
+ toolResult: {
196
+ content: [{
197
+ type: 'text',
198
+ text: JSON.stringify(response, null, 2)
199
+ }],
200
+ isError: false
201
+ }
202
+ };
203
+ }
204
+ catch (error) {
205
+ return {
206
+ toolResult: {
207
+ content: [{
208
+ type: 'text',
209
+ text: `Error listing terms: ${error.response?.data?.message || error.message}`
210
+ }],
211
+ isError: true
212
+ }
213
+ };
214
+ }
215
+ },
216
+ get_term: async (params) => {
217
+ try {
218
+ const endpoint = getTaxonomyEndpoint(params.taxonomy);
219
+ const queryParams = {};
220
+ if (params._embed)
221
+ queryParams._embed = true;
222
+ if (params._fields)
223
+ queryParams._fields = params._fields;
224
+ const response = await makeWordPressRequest('GET', `${endpoint}/${params.id}`, queryParams);
225
+ return {
226
+ toolResult: {
227
+ content: [{
228
+ type: 'text',
229
+ text: JSON.stringify(response, null, 2)
230
+ }],
231
+ isError: false
232
+ }
233
+ };
234
+ }
235
+ catch (error) {
236
+ return {
237
+ toolResult: {
238
+ content: [{
239
+ type: 'text',
240
+ text: `Error getting term: ${error.response?.data?.message || error.message}`
241
+ }],
242
+ isError: true
243
+ }
244
+ };
245
+ }
246
+ },
247
+ create_term: async (params) => {
248
+ try {
249
+ const endpoint = getTaxonomyEndpoint(params.taxonomy);
250
+ const termData = {
251
+ name: params.name
252
+ };
253
+ if (params.slug !== undefined)
254
+ termData.slug = params.slug;
255
+ if (params.parent !== undefined)
256
+ termData.parent = params.parent;
257
+ if (params.description !== undefined)
258
+ termData.description = params.description;
259
+ if (params.meta !== undefined)
260
+ termData.meta = params.meta;
261
+ const response = await makeWordPressRequest('POST', endpoint, termData);
262
+ return {
263
+ toolResult: {
264
+ content: [{
265
+ type: 'text',
266
+ text: JSON.stringify(response, null, 2)
267
+ }],
268
+ isError: false
269
+ }
270
+ };
271
+ }
272
+ catch (error) {
273
+ return {
274
+ toolResult: {
275
+ content: [{
276
+ type: 'text',
277
+ text: `Error creating term: ${error.response?.data?.message || error.message}`
278
+ }],
279
+ isError: true
280
+ }
281
+ };
282
+ }
283
+ },
284
+ update_term: async (params) => {
285
+ try {
286
+ const endpoint = getTaxonomyEndpoint(params.taxonomy);
287
+ const updateData = {};
288
+ if (params.name !== undefined)
289
+ updateData.name = params.name;
290
+ if (params.slug !== undefined)
291
+ updateData.slug = params.slug;
292
+ if (params.parent !== undefined)
293
+ updateData.parent = params.parent;
294
+ if (params.description !== undefined)
295
+ updateData.description = params.description;
296
+ if (params.meta !== undefined)
297
+ updateData.meta = params.meta;
298
+ const response = await makeWordPressRequest('POST', `${endpoint}/${params.id}`, updateData);
299
+ return {
300
+ toolResult: {
301
+ content: [{
302
+ type: 'text',
303
+ text: JSON.stringify(response, null, 2)
304
+ }],
305
+ isError: false
306
+ }
307
+ };
308
+ }
309
+ catch (error) {
310
+ return {
311
+ toolResult: {
312
+ content: [{
313
+ type: 'text',
314
+ text: `Error updating term: ${error.response?.data?.message || error.message}`
315
+ }],
316
+ isError: true
317
+ }
318
+ };
319
+ }
320
+ },
321
+ delete_term: async (params) => {
322
+ try {
323
+ const endpoint = getTaxonomyEndpoint(params.taxonomy);
324
+ const response = await makeWordPressRequest('DELETE', `${endpoint}/${params.id}`, {
325
+ force: true // Terms require force to be true
326
+ });
327
+ return {
328
+ toolResult: {
329
+ content: [{
330
+ type: 'text',
331
+ text: JSON.stringify(response, null, 2)
332
+ }],
333
+ isError: false
334
+ }
335
+ };
336
+ }
337
+ catch (error) {
338
+ return {
339
+ toolResult: {
340
+ content: [{
341
+ type: 'text',
342
+ text: `Error deleting term: ${error.response?.data?.message || error.message}`
343
+ }],
344
+ isError: true
345
+ }
346
+ };
347
+ }
348
+ },
349
+ assign_terms_to_content: async (params) => {
350
+ try {
351
+ // Determine the content endpoint
352
+ const contentEndpoint = getContentEndpoint(params.content_type);
353
+ // Prepare the update data
354
+ const updateData = {};
355
+ // The field name depends on the taxonomy
356
+ if (params.taxonomy === 'category') {
357
+ updateData.categories = params.terms;
358
+ }
359
+ else if (params.taxonomy === 'post_tag') {
360
+ updateData.tags = params.terms;
361
+ }
362
+ else {
363
+ // For custom taxonomies, use the taxonomy slug as the field name
364
+ updateData[params.taxonomy] = params.terms;
365
+ }
366
+ // If appending, we need to get current terms first
367
+ if (params.append) {
368
+ try {
369
+ const currentContent = await makeWordPressRequest('GET', `${contentEndpoint}/${params.content_id}`);
370
+ const currentTerms = currentContent[params.taxonomy === 'category' ? 'categories' :
371
+ params.taxonomy === 'post_tag' ? 'tags' :
372
+ params.taxonomy] || [];
373
+ // Merge current terms with new terms (remove duplicates)
374
+ const allTerms = [...new Set([...currentTerms, ...params.terms])];
375
+ updateData[params.taxonomy === 'category' ? 'categories' :
376
+ params.taxonomy === 'post_tag' ? 'tags' :
377
+ params.taxonomy] = allTerms;
378
+ }
379
+ catch (error) {
380
+ // If we can't get current terms, just set the new ones
381
+ logToFile(`Warning: Could not get current terms for append operation: ${error}`);
382
+ }
383
+ }
384
+ const response = await makeWordPressRequest('POST', `${contentEndpoint}/${params.content_id}`, updateData);
385
+ return {
386
+ toolResult: {
387
+ content: [{
388
+ type: 'text',
389
+ text: JSON.stringify({
390
+ success: true,
391
+ content_id: params.content_id,
392
+ content_type: params.content_type,
393
+ taxonomy: params.taxonomy,
394
+ assigned_terms: params.terms,
395
+ appended: params.append || false,
396
+ content: response
397
+ }, null, 2)
398
+ }],
399
+ isError: false
400
+ }
401
+ };
402
+ }
403
+ catch (error) {
404
+ return {
405
+ toolResult: {
406
+ content: [{
407
+ type: 'text',
408
+ text: `Error assigning terms to content: ${error.response?.data?.message || error.message}`
409
+ }],
410
+ isError: true
411
+ }
412
+ };
413
+ }
414
+ },
415
+ get_content_terms: async (params) => {
416
+ try {
417
+ // First, get the content to see what taxonomies are assigned
418
+ const contentEndpoint = getContentEndpoint(params.content_type);
419
+ const content = await makeWordPressRequest('GET', `${contentEndpoint}/${params.content_id}`);
420
+ // Get all available taxonomies
421
+ const taxonomies = await getTaxonomies();
422
+ const terms = {};
423
+ // If specific taxonomy requested
424
+ if (params.taxonomy) {
425
+ const taxonomyField = params.taxonomy === 'category' ? 'categories' :
426
+ params.taxonomy === 'post_tag' ? 'tags' :
427
+ params.taxonomy;
428
+ if (content[taxonomyField]) {
429
+ // Get full term details
430
+ const endpoint = getTaxonomyEndpoint(params.taxonomy);
431
+ const termDetails = await Promise.all(content[taxonomyField].map(async (termId) => {
432
+ try {
433
+ return await makeWordPressRequest('GET', `${endpoint}/${termId}`);
434
+ }
435
+ catch {
436
+ return { id: termId, error: 'Could not fetch term details' };
437
+ }
438
+ }));
439
+ terms[params.taxonomy] = termDetails;
440
+ }
441
+ }
442
+ else {
443
+ // Get all taxonomy terms for this content
444
+ for (const [taxonomySlug, taxonomyInfo] of Object.entries(taxonomies)) {
445
+ const tax = taxonomyInfo;
446
+ // Check if this taxonomy applies to this content type
447
+ if (tax.types && tax.types.includes(params.content_type)) {
448
+ const taxonomyField = taxonomySlug === 'category' ? 'categories' :
449
+ taxonomySlug === 'post_tag' ? 'tags' :
450
+ taxonomySlug;
451
+ if (content[taxonomyField] && Array.isArray(content[taxonomyField]) && content[taxonomyField].length > 0) {
452
+ const endpoint = getTaxonomyEndpoint(taxonomySlug);
453
+ const termDetails = await Promise.all(content[taxonomyField].map(async (termId) => {
454
+ try {
455
+ return await makeWordPressRequest('GET', `${endpoint}/${termId}`);
456
+ }
457
+ catch {
458
+ return { id: termId, error: 'Could not fetch term details' };
459
+ }
460
+ }));
461
+ terms[taxonomySlug] = termDetails;
462
+ }
463
+ }
464
+ }
465
+ }
466
+ return {
467
+ toolResult: {
468
+ content: [{
469
+ type: 'text',
470
+ text: JSON.stringify({
471
+ content_id: params.content_id,
472
+ content_type: params.content_type,
473
+ terms: terms
474
+ }, null, 2)
475
+ }],
476
+ isError: false
477
+ }
478
+ };
479
+ }
480
+ catch (error) {
481
+ return {
482
+ toolResult: {
483
+ content: [{
484
+ type: 'text',
485
+ text: `Error getting content terms: ${error.response?.data?.message || error.message}`
486
+ }],
487
+ isError: true
488
+ }
489
+ };
490
+ }
491
+ }
492
+ };