PluginProbe
Search Atlas SEO – OTTO AI SEO Automation for WordPress / 2.6.14
Search Atlas SEO – OTTO AI SEO Automation for WordPress v2.6.14
2.7.0 2.6.26 2.6.25 2.6.24 2.6.23 2.6.22 2.6.21 2.6.20 2.6.19 2.6.18 2.6.17 2.6.16 2.6.15 2.6.14 2.6.13 2.6.12 2.6.11 2.6.10 2.6.9 2.6.8 2.6.7 2.6.6 2.6.5 2.6.4 2.6.3 All 139 releases
metasync / wp-mcp-server / tools / class-mcp-tool-taxonomies.php

class-mcp-tool-taxonomies.php in Search Atlas SEO – OTTO AI SEO Automation for WordPress 2.6.14, at wp-mcp-server/tools/class-mcp-tool-taxonomies.php

1,052 lines 29.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * MCP Tools for Taxonomy Operations
4 *
5 * Provides MCP tools for managing categories and taxonomies.
6 *
7 * @package MetaSync
8 * @subpackage MCP_Server/Tools
9 */
10
11 if (!defined('ABSPATH')) {
12 exit;
13 }
14
15 require_once plugin_dir_path(dirname(__FILE__)) . 'class-mcp-tool-base.php';
16
17 /**
18 * List Categories Tool
19 *
20 * Lists all categories
21 */
22 class MCP_Tool_List_Categories extends MCP_Tool_Base {
23
24 public function get_name() {
25 return 'wordpress_list_categories';
26 }
27
28 public function get_description() {
29 return 'List all categories with their details';
30 }
31
32 public function get_input_schema() {
33 return [
34 'type' => 'object',
35 'properties' => [
36 'hide_empty' => [
37 'type' => 'boolean',
38 'description' => 'Whether to hide categories with no posts (default: false)',
39 ],
40 'orderby' => [
41 'type' => 'string',
42 'enum' => ['name', 'slug', 'count', 'id'],
43 'description' => 'Order by field (default: name)',
44 ],
45 'order' => [
46 'type' => 'string',
47 'enum' => ['ASC', 'DESC'],
48 'description' => 'Sort order (default: ASC)',
49 ],
50 ],
51 ];
52 }
53
54 public function execute($params) {
55 $this->validate_params($params);
56 $this->require_capability('manage_categories');
57
58 $args = [
59 'taxonomy' => 'category',
60 'hide_empty' => isset($params['hide_empty']) ? (bool)$params['hide_empty'] : false,
61 'orderby' => isset($params['orderby']) ? sanitize_text_field($params['orderby']) : 'name',
62 'order' => isset($params['order']) ? sanitize_text_field($params['order']) : 'ASC',
63 ];
64
65 $categories = get_terms($args);
66
67 if (is_wp_error($categories)) {
68 throw new Exception('Failed to retrieve categories: ' . $categories->get_error_message());
69 }
70
71 $categories_data = [];
72 foreach ($categories as $category) {
73 $categories_data[] = [
74 'term_id' => $category->term_id,
75 'name' => $category->name,
76 'slug' => $category->slug,
77 'description' => $category->description,
78 'count' => $category->count,
79 'parent' => $category->parent,
80 'parent_name' => $category->parent ? get_term($category->parent)->name : null,
81 ];
82 }
83
84 return $this->success([
85 'count' => count($categories_data),
86 'categories' => $categories_data,
87 ]);
88 }
89 }
90
91 /**
92 * Get Category Tool
93 *
94 * Gets a specific category
95 */
96 class MCP_Tool_Get_Category extends MCP_Tool_Base {
97
98 public function get_name() {
99 return 'wordpress_get_category';
100 }
101
102 public function get_description() {
103 return 'Get category details by ID or slug';
104 }
105
106 public function get_input_schema() {
107 return [
108 'type' => 'object',
109 'properties' => [
110 'category_id' => [
111 'type' => 'integer',
112 'description' => 'Category ID',
113 ],
114 'slug' => [
115 'type' => 'string',
116 'description' => 'Category slug',
117 ],
118 ],
119 ];
120 }
121
122 public function execute($params) {
123 $this->validate_params($params);
124 $this->require_capability('manage_categories');
125
126 $category_id = isset($params['category_id']) ? intval($params['category_id']) : null;
127 $slug = isset($params['slug']) ? sanitize_text_field($params['slug']) : null;
128
129 if (empty($category_id) && empty($slug)) {
130 throw new Exception('Either category_id or slug must be provided');
131 }
132
133 if (!empty($category_id)) {
134 $category = get_term($category_id, 'category');
135 } else {
136 $category = get_term_by('slug', $slug, 'category');
137 }
138
139 if (is_wp_error($category)) {
140 throw new Exception('Failed to retrieve category: ' . $category->get_error_message());
141 }
142
143 if (!$category) {
144 throw new Exception('Category not found');
145 }
146
147 return $this->success([
148 'term_id' => $category->term_id,
149 'name' => $category->name,
150 'slug' => $category->slug,
151 'description' => $category->description,
152 'count' => $category->count,
153 'parent' => $category->parent,
154 'parent_name' => $category->parent ? get_term($category->parent)->name : null,
155 ]);
156 }
157 }
158
159 /**
160 * Create Category Tool
161 *
162 * Creates a new category
163 */
164 class MCP_Tool_Create_Category extends MCP_Tool_Base {
165
166 public function get_name() {
167 return 'wordpress_create_category';
168 }
169
170 public function get_description() {
171 return 'Create a new category';
172 }
173
174 public function get_input_schema() {
175 return [
176 'type' => 'object',
177 'properties' => [
178 'name' => [
179 'type' => 'string',
180 'description' => 'Category name',
181 ],
182 'slug' => [
183 'type' => 'string',
184 'description' => 'Category slug (optional, auto-generated from name if not provided)',
185 ],
186 'description' => [
187 'type' => 'string',
188 'description' => 'Category description (optional)',
189 ],
190 'parent' => [
191 'type' => 'integer',
192 'description' => 'Parent category ID (optional)',
193 ],
194 ],
195 'required' => ['name'],
196 ];
197 }
198
199 public function execute($params) {
200 $this->validate_params($params);
201 $this->require_capability('manage_categories');
202
203 $args = [
204 'description' => isset($params['description']) ? sanitize_textarea_field($params['description']) : '',
205 'parent' => isset($params['parent']) ? intval($params['parent']) : 0,
206 ];
207
208 if (!empty($params['slug'])) {
209 $args['slug'] = sanitize_title($params['slug']);
210 }
211
212 $result = wp_insert_term(
213 sanitize_text_field($params['name']),
214 'category',
215 $args
216 );
217
218 if (is_wp_error($result)) {
219 throw new Exception('Failed to create category: ' . $result->get_error_message());
220 }
221
222 $category = get_term($result['term_id'], 'category');
223
224 return $this->success([
225 'term_id' => $category->term_id,
226 'name' => $category->name,
227 'slug' => $category->slug,
228 'description' => $category->description,
229 'parent' => $category->parent,
230 'message' => 'Category created successfully',
231 ]);
232 }
233 }
234
235 /**
236 * Update Category Tool
237 *
238 * Updates an existing category
239 */
240 class MCP_Tool_Update_Category extends MCP_Tool_Base {
241
242 public function get_name() {
243 return 'wordpress_update_category';
244 }
245
246 public function get_description() {
247 return 'Update an existing category';
248 }
249
250 public function get_input_schema() {
251 return [
252 'type' => 'object',
253 'properties' => [
254 'category_id' => [
255 'type' => 'integer',
256 'description' => 'Category ID to update',
257 ],
258 'name' => [
259 'type' => 'string',
260 'description' => 'Category name (optional)',
261 ],
262 'slug' => [
263 'type' => 'string',
264 'description' => 'Category slug (optional)',
265 ],
266 'description' => [
267 'type' => 'string',
268 'description' => 'Category description (optional)',
269 ],
270 'parent' => [
271 'type' => 'integer',
272 'description' => 'Parent category ID (optional)',
273 ],
274 ],
275 'required' => ['category_id'],
276 ];
277 }
278
279 public function execute($params) {
280 $this->validate_params($params);
281 $this->require_capability('manage_categories');
282
283 $category_id = intval($params['category_id']);
284
285 // Verify category exists
286 $category = get_term($category_id, 'category');
287 if (is_wp_error($category) || !$category) {
288 throw new Exception(sprintf("Category not found with ID: %d", absint($category_id)));
289 }
290
291 $args = [];
292
293 if (isset($params['name'])) {
294 $args['name'] = sanitize_text_field($params['name']);
295 }
296
297 if (isset($params['slug'])) {
298 $args['slug'] = sanitize_title($params['slug']);
299 }
300
301 if (isset($params['description'])) {
302 $args['description'] = sanitize_textarea_field($params['description']);
303 }
304
305 if (isset($params['parent'])) {
306 $args['parent'] = intval($params['parent']);
307 }
308
309 if (empty($args)) {
310 throw new Exception('No fields to update provided');
311 }
312
313 $result = wp_update_term($category_id, 'category', $args);
314
315 if (is_wp_error($result)) {
316 throw new Exception('Failed to update category: ' . $result->get_error_message());
317 }
318
319 $updated_category = get_term($result['term_id'], 'category');
320
321 return $this->success([
322 'term_id' => $updated_category->term_id,
323 'name' => $updated_category->name,
324 'slug' => $updated_category->slug,
325 'description' => $updated_category->description,
326 'parent' => $updated_category->parent,
327 'message' => 'Category updated successfully',
328 ]);
329 }
330 }
331
332 /**
333 * Delete Category Tool
334 *
335 * Deletes a category
336 */
337 class MCP_Tool_Delete_Category extends MCP_Tool_Base {
338
339 public function get_name() {
340 return 'wordpress_delete_category';
341 }
342
343 public function get_description() {
344 return 'Delete a category permanently';
345 }
346
347 public function get_input_schema() {
348 return [
349 'type' => 'object',
350 'properties' => [
351 'category_id' => [
352 'type' => 'integer',
353 'description' => 'Category ID to delete',
354 ],
355 ],
356 'required' => ['category_id'],
357 ];
358 }
359
360 public function execute($params) {
361 $this->validate_params($params);
362 $this->require_capability('manage_categories');
363
364 $category_id = intval($params['category_id']);
365
366 // Verify category exists
367 $category = get_term($category_id, 'category');
368 if (is_wp_error($category) || !$category) {
369 throw new Exception(sprintf("Category not found with ID: %d", absint($category_id)));
370 }
371
372 // Prevent deletion of default category
373 $default_category = get_option('default_category');
374 if ($category_id == $default_category) {
375 throw new Exception('Cannot delete the default category');
376 }
377
378 $name = $category->name;
379
380 $result = wp_delete_term($category_id, 'category');
381
382 if (is_wp_error($result)) {
383 throw new Exception('Failed to delete category: ' . $result->get_error_message());
384 }
385
386 if (!$result) {
387 throw new Exception('Failed to delete category');
388 }
389
390 return $this->success([
391 'category_id' => $category_id,
392 'name' => $name,
393 'message' => 'Category deleted successfully',
394 ]);
395 }
396 }
397
398 /**
399 * Get Post Categories Tool
400 *
401 * Gets categories assigned to a post
402 */
403 class MCP_Tool_Get_Post_Categories extends MCP_Tool_Base {
404
405 public function get_name() {
406 return 'wordpress_get_post_categories';
407 }
408
409 public function get_description() {
410 return 'Get all categories assigned to a post';
411 }
412
413 public function get_input_schema() {
414 return [
415 'type' => 'object',
416 'properties' => [
417 'post_id' => [
418 'type' => 'integer',
419 'description' => 'Post ID',
420 ],
421 ],
422 'required' => ['post_id'],
423 ];
424 }
425
426 public function execute($params) {
427 $this->validate_params($params);
428 $this->require_capability('edit_posts');
429
430 $post_id = intval($params['post_id']);
431
432 // Validate post exists
433 $post = $this->verify_post_exists($post_id);
434
435 $categories = get_the_category($post_id);
436
437 $categories_data = [];
438 foreach ($categories as $category) {
439 $categories_data[] = [
440 'term_id' => $category->term_id,
441 'name' => $category->name,
442 'slug' => $category->slug,
443 'description' => $category->description,
444 'parent' => $category->parent,
445 ];
446 }
447
448 return $this->success([
449 'post_id' => $post_id,
450 'post_title' => $post->post_title,
451 'count' => count($categories_data),
452 'categories' => $categories_data,
453 ]);
454 }
455 }
456
457 /**
458 * Set Post Categories Tool
459 *
460 * Sets categories for a post
461 */
462 class MCP_Tool_Set_Post_Categories extends MCP_Tool_Base {
463
464 public function get_name() {
465 return 'wordpress_set_post_categories';
466 }
467
468 public function get_description() {
469 return 'Set categories for a post (replaces existing categories)';
470 }
471
472 public function get_input_schema() {
473 return [
474 'type' => 'object',
475 'properties' => [
476 'post_id' => [
477 'type' => 'integer',
478 'description' => 'Post ID',
479 ],
480 'category_ids' => [
481 'type' => 'array',
482 'description' => 'Array of category IDs to assign',
483 'items' => [
484 'type' => 'integer',
485 ],
486 ],
487 ],
488 'required' => ['post_id', 'category_ids'],
489 ];
490 }
491
492 public function execute($params) {
493 $this->validate_params($params);
494 $this->require_capability('edit_posts');
495
496 $post_id = intval($params['post_id']);
497
498 // Validate post exists and user can edit it
499 $post = $this->verify_post_exists($post_id);
500
501 $this->check_post_permission($post_id);
502
503 $category_ids = array_map('intval', $params['category_ids']);
504
505 // Validate all categories exist
506 foreach ($category_ids as $category_id) {
507 $category = get_term($category_id, 'category');
508 if (is_wp_error($category) || !$category) {
509 throw new Exception(sprintf("Category not found with ID: %d", absint($category_id)));
510 }
511 }
512
513 $result = wp_set_post_categories($post_id, $category_ids);
514
515 if (is_wp_error($result)) {
516 throw new Exception('Failed to set categories: ' . $result->get_error_message());
517 }
518
519 // Get updated categories
520 $categories = get_the_category($post_id);
521 $categories_data = [];
522 foreach ($categories as $category) {
523 $categories_data[] = [
524 'term_id' => $category->term_id,
525 'name' => $category->name,
526 'slug' => $category->slug,
527 ];
528 }
529
530 return $this->success([
531 'post_id' => $post_id,
532 'post_title' => $post->post_title,
533 'categories' => $categories_data,
534 'message' => 'Categories updated successfully',
535 ]);
536 }
537 }
538
539 /**
540 * List Tags Tool
541 *
542 * Lists all tags
543 */
544 class MCP_Tool_List_Tags extends MCP_Tool_Base {
545
546 public function get_name() {
547 return 'wordpress_list_tags';
548 }
549
550 public function get_description() {
551 return 'List all tags with their details';
552 }
553
554 public function get_input_schema() {
555 return [
556 'type' => 'object',
557 'properties' => [
558 'hide_empty' => [
559 'type' => 'boolean',
560 'description' => 'Whether to hide tags with no posts (default: false)',
561 ],
562 'orderby' => [
563 'type' => 'string',
564 'enum' => ['name', 'slug', 'count', 'id'],
565 'description' => 'Order by field (default: name)',
566 ],
567 'order' => [
568 'type' => 'string',
569 'enum' => ['ASC', 'DESC'],
570 'description' => 'Sort order (default: ASC)',
571 ],
572 'number' => [
573 'type' => 'integer',
574 'description' => 'Maximum number of tags to return (default: all)',
575 ],
576 ],
577 ];
578 }
579
580 public function execute($params) {
581 $this->validate_params($params);
582 $this->require_capability('manage_categories'); // Same capability for tags
583
584 $args = [
585 'taxonomy' => 'post_tag',
586 'hide_empty' => isset($params['hide_empty']) ? (bool)$params['hide_empty'] : false,
587 'orderby' => isset($params['orderby']) ? sanitize_text_field($params['orderby']) : 'name',
588 'order' => isset($params['order']) ? sanitize_text_field($params['order']) : 'ASC',
589 ];
590
591 if (isset($params['number'])) {
592 $args['number'] = intval($params['number']);
593 }
594
595 $tags = get_terms($args);
596
597 if (is_wp_error($tags)) {
598 throw new Exception('Failed to retrieve tags: ' . $tags->get_error_message());
599 }
600
601 $tags_data = [];
602 foreach ($tags as $tag) {
603 $tags_data[] = [
604 'term_id' => $tag->term_id,
605 'name' => $tag->name,
606 'slug' => $tag->slug,
607 'description' => $tag->description,
608 'count' => $tag->count,
609 ];
610 }
611
612 return $this->success([
613 'count' => count($tags_data),
614 'tags' => $tags_data,
615 ]);
616 }
617 }
618
619 /**
620 * Get Tag Tool
621 *
622 * Gets a specific tag
623 */
624 class MCP_Tool_Get_Tag extends MCP_Tool_Base {
625
626 public function get_name() {
627 return 'wordpress_get_tag';
628 }
629
630 public function get_description() {
631 return 'Get tag details by ID or slug';
632 }
633
634 public function get_input_schema() {
635 return [
636 'type' => 'object',
637 'properties' => [
638 'tag_id' => [
639 'type' => 'integer',
640 'description' => 'Tag ID',
641 ],
642 'slug' => [
643 'type' => 'string',
644 'description' => 'Tag slug',
645 ],
646 ],
647 ];
648 }
649
650 public function execute($params) {
651 $this->validate_params($params);
652 $this->require_capability('manage_categories');
653
654 $tag_id = isset($params['tag_id']) ? intval($params['tag_id']) : null;
655 $slug = isset($params['slug']) ? sanitize_text_field($params['slug']) : null;
656
657 if (empty($tag_id) && empty($slug)) {
658 throw new Exception('Either tag_id or slug must be provided');
659 }
660
661 if (!empty($tag_id)) {
662 $tag = get_term($tag_id, 'post_tag');
663 } else {
664 $tag = get_term_by('slug', $slug, 'post_tag');
665 }
666
667 if (is_wp_error($tag)) {
668 throw new Exception('Failed to retrieve tag: ' . $tag->get_error_message());
669 }
670
671 if (!$tag) {
672 throw new Exception('Tag not found');
673 }
674
675 return $this->success([
676 'term_id' => $tag->term_id,
677 'name' => $tag->name,
678 'slug' => $tag->slug,
679 'description' => $tag->description,
680 'count' => $tag->count,
681 ]);
682 }
683 }
684
685 /**
686 * Create Tag Tool
687 *
688 * Creates a new tag
689 */
690 class MCP_Tool_Create_Tag extends MCP_Tool_Base {
691
692 public function get_name() {
693 return 'wordpress_create_tag';
694 }
695
696 public function get_description() {
697 return 'Create a new tag';
698 }
699
700 public function get_input_schema() {
701 return [
702 'type' => 'object',
703 'properties' => [
704 'name' => [
705 'type' => 'string',
706 'description' => 'Tag name',
707 ],
708 'slug' => [
709 'type' => 'string',
710 'description' => 'Tag slug (optional, auto-generated from name if not provided)',
711 ],
712 'description' => [
713 'type' => 'string',
714 'description' => 'Tag description (optional)',
715 ],
716 ],
717 'required' => ['name'],
718 ];
719 }
720
721 public function execute($params) {
722 $this->validate_params($params);
723 $this->require_capability('manage_categories');
724
725 $args = [
726 'description' => isset($params['description']) ? sanitize_textarea_field($params['description']) : '',
727 ];
728
729 if (!empty($params['slug'])) {
730 $args['slug'] = sanitize_title($params['slug']);
731 }
732
733 $result = wp_insert_term(
734 sanitize_text_field($params['name']),
735 'post_tag',
736 $args
737 );
738
739 if (is_wp_error($result)) {
740 throw new Exception('Failed to create tag: ' . $result->get_error_message());
741 }
742
743 $tag = get_term($result['term_id'], 'post_tag');
744
745 return $this->success([
746 'term_id' => $tag->term_id,
747 'name' => $tag->name,
748 'slug' => $tag->slug,
749 'description' => $tag->description,
750 'message' => 'Tag created successfully',
751 ]);
752 }
753 }
754
755 /**
756 * Update Tag Tool
757 *
758 * Updates an existing tag
759 */
760 class MCP_Tool_Update_Tag extends MCP_Tool_Base {
761
762 public function get_name() {
763 return 'wordpress_update_tag';
764 }
765
766 public function get_description() {
767 return 'Update an existing tag';
768 }
769
770 public function get_input_schema() {
771 return [
772 'type' => 'object',
773 'properties' => [
774 'tag_id' => [
775 'type' => 'integer',
776 'description' => 'Tag ID to update',
777 ],
778 'name' => [
779 'type' => 'string',
780 'description' => 'Tag name (optional)',
781 ],
782 'slug' => [
783 'type' => 'string',
784 'description' => 'Tag slug (optional)',
785 ],
786 'description' => [
787 'type' => 'string',
788 'description' => 'Tag description (optional)',
789 ],
790 ],
791 'required' => ['tag_id'],
792 ];
793 }
794
795 public function execute($params) {
796 $this->validate_params($params);
797 $this->require_capability('manage_categories');
798
799 $tag_id = intval($params['tag_id']);
800
801 // Verify tag exists
802 $tag = get_term($tag_id, 'post_tag');
803 if (is_wp_error($tag) || !$tag) {
804 throw new Exception(sprintf("Tag not found with ID: %d", absint($tag_id)));
805 }
806
807 $args = [];
808
809 if (isset($params['name'])) {
810 $args['name'] = sanitize_text_field($params['name']);
811 }
812
813 if (isset($params['slug'])) {
814 $args['slug'] = sanitize_title($params['slug']);
815 }
816
817 if (isset($params['description'])) {
818 $args['description'] = sanitize_textarea_field($params['description']);
819 }
820
821 if (empty($args)) {
822 throw new Exception('No fields to update provided');
823 }
824
825 $result = wp_update_term($tag_id, 'post_tag', $args);
826
827 if (is_wp_error($result)) {
828 throw new Exception('Failed to update tag: ' . $result->get_error_message());
829 }
830
831 $updated_tag = get_term($result['term_id'], 'post_tag');
832
833 return $this->success([
834 'term_id' => $updated_tag->term_id,
835 'name' => $updated_tag->name,
836 'slug' => $updated_tag->slug,
837 'description' => $updated_tag->description,
838 'message' => 'Tag updated successfully',
839 ]);
840 }
841 }
842
843 /**
844 * Delete Tag Tool
845 *
846 * Deletes a tag
847 */
848 class MCP_Tool_Delete_Tag extends MCP_Tool_Base {
849
850 public function get_name() {
851 return 'wordpress_delete_tag';
852 }
853
854 public function get_description() {
855 return 'Delete a tag permanently';
856 }
857
858 public function get_input_schema() {
859 return [
860 'type' => 'object',
861 'properties' => [
862 'tag_id' => [
863 'type' => 'integer',
864 'description' => 'Tag ID to delete',
865 ],
866 ],
867 'required' => ['tag_id'],
868 ];
869 }
870
871 public function execute($params) {
872 $this->validate_params($params);
873 $this->require_capability('manage_categories');
874
875 $tag_id = intval($params['tag_id']);
876
877 // Verify tag exists
878 $tag = get_term($tag_id, 'post_tag');
879 if (is_wp_error($tag) || !$tag) {
880 throw new Exception(sprintf("Tag not found with ID: %d", absint($tag_id)));
881 }
882
883 $name = $tag->name;
884
885 $result = wp_delete_term($tag_id, 'post_tag');
886
887 if (is_wp_error($result)) {
888 throw new Exception('Failed to delete tag: ' . $result->get_error_message());
889 }
890
891 if (!$result) {
892 throw new Exception('Failed to delete tag');
893 }
894
895 return $this->success([
896 'tag_id' => $tag_id,
897 'name' => $name,
898 'message' => 'Tag deleted successfully',
899 ]);
900 }
901 }
902
903 /**
904 * Get Post Tags Tool
905 *
906 * Gets tags assigned to a post
907 */
908 class MCP_Tool_Get_Post_Tags extends MCP_Tool_Base {
909
910 public function get_name() {
911 return 'wordpress_get_post_tags';
912 }
913
914 public function get_description() {
915 return 'Get all tags assigned to a post';
916 }
917
918 public function get_input_schema() {
919 return [
920 'type' => 'object',
921 'properties' => [
922 'post_id' => [
923 'type' => 'integer',
924 'description' => 'Post ID',
925 ],
926 ],
927 'required' => ['post_id'],
928 ];
929 }
930
931 public function execute($params) {
932 $this->validate_params($params);
933 $this->require_capability('edit_posts');
934
935 $post_id = intval($params['post_id']);
936
937 // Validate post exists
938 $post = $this->verify_post_exists($post_id);
939
940 $tags = get_the_tags($post_id);
941
942 // get_the_tags() returns false if no tags
943 if ($tags === false) {
944 $tags = [];
945 }
946
947 $tags_data = [];
948 foreach ($tags as $tag) {
949 $tags_data[] = [
950 'term_id' => $tag->term_id,
951 'name' => $tag->name,
952 'slug' => $tag->slug,
953 'description' => $tag->description,
954 ];
955 }
956
957 return $this->success([
958 'post_id' => $post_id,
959 'post_title' => $post->post_title,
960 'count' => count($tags_data),
961 'tags' => $tags_data,
962 ]);
963 }
964 }
965
966 /**
967 * Set Post Tags Tool
968 *
969 * Sets tags for a post
970 */
971 class MCP_Tool_Set_Post_Tags extends MCP_Tool_Base {
972
973 public function get_name() {
974 return 'wordpress_set_post_tags';
975 }
976
977 public function get_description() {
978 return 'Set tags for a post (replaces existing tags). Accepts tag IDs, names, or slugs.';
979 }
980
981 public function get_input_schema() {
982 return [
983 'type' => 'object',
984 'properties' => [
985 'post_id' => [
986 'type' => 'integer',
987 'description' => 'Post ID',
988 ],
989 'tags' => [
990 'type' => 'array',
991 'description' => 'Array of tag IDs, names, or slugs. Tags that don\'t exist will be created automatically.',
992 'items' => [
993 'oneOf' => [
994 ['type' => 'integer'],
995 ['type' => 'string'],
996 ],
997 ],
998 ],
999 'append' => [
1000 'type' => 'boolean',
1001 'description' => 'If true, append tags to existing tags instead of replacing (default: false)',
1002 ],
1003 ],
1004 'required' => ['post_id', 'tags'],
1005 ];
1006 }
1007
1008 public function execute($params) {
1009 $this->validate_params($params);
1010 $this->require_capability('edit_posts');
1011
1012 $post_id = intval($params['post_id']);
1013
1014 // Validate post exists and user can edit it
1015 $post = $this->verify_post_exists($post_id);
1016
1017 $this->check_post_permission($post_id);
1018
1019 $tags = $params['tags'];
1020 $append = isset($params['append']) ? (bool)$params['append'] : false;
1021
1022 // WordPress wp_set_post_tags() handles tag creation automatically
1023 $result = wp_set_post_tags($post_id, $tags, $append);
1024
1025 if (is_wp_error($result)) {
1026 throw new Exception('Failed to set tags: ' . $result->get_error_message());
1027 }
1028
1029 // Get updated tags
1030 $post_tags = get_the_tags($post_id);
1031 if ($post_tags === false) {
1032 $post_tags = [];
1033 }
1034
1035 $tags_data = [];
1036 foreach ($post_tags as $tag) {
1037 $tags_data[] = [
1038 'term_id' => $tag->term_id,
1039 'name' => $tag->name,
1040 'slug' => $tag->slug,
1041 ];
1042 }
1043
1044 return $this->success([
1045 'post_id' => $post_id,
1046 'post_title' => $post->post_title,
1047 'tags' => $tags_data,
1048 'message' => sprintf('%s successfully', $append ? 'Tags appended' : 'Tags updated'),
1049 ]);
1050 }
1051 }
1052