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-schema-markup.php

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

630 lines 21.2 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 Schema Markup Operations
4 *
5 * Provides MCP tools for managing schema markup on posts and pages.
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 * Get Schema Markup Tool
19 *
20 * Retrieves schema markup configuration for a post
21 */
22 class MCP_Tool_Get_Schema_Markup extends MCP_Tool_Base {
23
24 public function get_name() {
25 return 'wordpress_get_schema_markup';
26 }
27
28 public function get_description() {
29 return 'Get schema markup configuration for a post or page';
30 }
31
32 public function get_input_schema() {
33 return [
34 'type' => 'object',
35 'properties' => [
36 'post_id' => [
37 'type' => 'integer',
38 'description' => 'Post ID',
39 ],
40 ],
41 'required' => ['post_id'],
42 ];
43 }
44
45 public function execute($params) {
46 $this->validate_params($params);
47 $this->require_capability('edit_posts');
48
49 $post_id = intval($params['post_id']);
50
51 // Validate post exists
52 $post = $this->verify_post_exists($post_id);
53
54 // Get schema data
55 $schema_data = get_post_meta($post_id, 'metasync_schema_markup', true);
56 $validation_errors = get_post_meta($post_id, '_metasync_schema_validation_errors', true);
57
58 $result = [
59 'post_id' => $post_id,
60 'post_title' => $post->post_title,
61 'post_type' => $post->post_type,
62 'schema_enabled' => false,
63 'schema_types' => [],
64 'has_validation_errors' => false,
65 'validation_errors' => [],
66 ];
67
68 if (!empty($schema_data)) {
69 $result['schema_enabled'] = isset($schema_data['enabled']) ? (bool)$schema_data['enabled'] : false;
70 $result['schema_types'] = isset($schema_data['types']) ? $schema_data['types'] : [];
71 }
72
73 if (!empty($validation_errors) && is_array($validation_errors)) {
74 $result['has_validation_errors'] = true;
75 $result['validation_errors'] = $validation_errors;
76 }
77
78 return $this->success($result);
79 }
80 }
81
82 /**
83 * Update Schema Markup Tool
84 *
85 * Updates schema markup configuration for a post
86 */
87 class MCP_Tool_Update_Schema_Markup extends MCP_Tool_Base {
88
89 public function get_name() {
90 return 'wordpress_update_schema_markup';
91 }
92
93 public function get_description() {
94 return 'Update schema markup configuration for a post or page';
95 }
96
97 public function get_input_schema() {
98 return [
99 'type' => 'object',
100 'properties' => [
101 'post_id' => [
102 'type' => 'integer',
103 'description' => 'Post ID',
104 ],
105 'enabled' => [
106 'type' => 'boolean',
107 'description' => 'Enable or disable schema markup for this post',
108 ],
109 'types' => [
110 'type' => 'array',
111 'description' => 'Array of schema types with their configuration',
112 'items' => [
113 'type' => 'object',
114 'properties' => [
115 'type' => [
116 'type' => 'string',
117 'enum' => ['article', 'FAQPage', 'product', 'recipe', 'Event', 'JobPosting', 'Review', 'Course', 'Organization', 'Person', 'WebSite', 'NewsArticle', 'LocalBusiness', 'HowTo', 'VideoObject'],
118 ],
119 'fields' => [
120 'type' => 'object',
121 'description' => 'Schema-specific fields',
122 ],
123 ],
124 ],
125 ],
126 ],
127 'required' => ['post_id', 'enabled'],
128 ];
129 }
130
131 public function execute($params) {
132 $this->validate_params($params);
133 $this->require_capability('edit_posts');
134
135 $post_id = intval($params['post_id']);
136
137 // Validate post exists and user can edit it
138 $post = $this->verify_post_exists($post_id);
139
140 $this->check_post_permission($post_id);
141
142 // Build schema data
143 $schema_data = [
144 'enabled' => (bool)$params['enabled'],
145 'types' => [],
146 ];
147
148 // Process schema types
149 if (isset($params['types']) && is_array($params['types'])) {
150 foreach ($params['types'] as $type_data) {
151 if (!empty($type_data['type'])) {
152 $schema_data['types'][] = [
153 'type' => sanitize_text_field($type_data['type']),
154 'fields' => isset($type_data['fields']) ? $type_data['fields'] : [],
155 ];
156 }
157 }
158 }
159
160 // Save schema data
161 update_post_meta($post_id, 'metasync_schema_markup', $schema_data);
162
163 // Clear validation errors
164 delete_post_meta($post_id, '_metasync_schema_validation_errors');
165
166 return $this->success([
167 'post_id' => $post_id,
168 'schema_enabled' => $schema_data['enabled'],
169 'schema_types_count' => count($schema_data['types']),
170 'message' => 'Schema markup updated successfully',
171 ]);
172 }
173 }
174
175 /**
176 * Add Schema Type Tool
177 *
178 * Adds a new schema type to a post
179 */
180 class MCP_Tool_Add_Schema_Type extends MCP_Tool_Base {
181
182 public function get_name() {
183 return 'wordpress_add_schema_type';
184 }
185
186 public function get_description() {
187 return 'Add a new schema type (article, FAQ, product, recipe, Event, JobPosting, Review, Course, Organization, Person, WebSite, NewsArticle, LocalBusiness, HowTo, VideoObject) to a post';
188 }
189
190 public function get_input_schema() {
191 return [
192 'type' => 'object',
193 'properties' => [
194 'post_id' => [
195 'type' => 'integer',
196 'description' => 'Post ID',
197 ],
198 'schema_type' => [
199 'type' => 'string',
200 'enum' => ['article', 'FAQPage', 'product', 'recipe', 'Event', 'JobPosting', 'Review', 'Course', 'Organization', 'Person', 'WebSite', 'NewsArticle', 'LocalBusiness', 'HowTo', 'VideoObject'],
201 'description' => 'Schema type to add',
202 ],
203 'fields' => [
204 'type' => 'object',
205 'description' => 'Schema-specific fields configuration',
206 ],
207 ],
208 'required' => ['post_id', 'schema_type'],
209 ];
210 }
211
212 public function execute($params) {
213 $this->validate_params($params);
214 $this->require_capability('edit_posts');
215
216 $post_id = intval($params['post_id']);
217 $schema_type = sanitize_text_field($params['schema_type']);
218 $fields = isset($params['fields']) ? $params['fields'] : [];
219
220 // Validate post exists and user can edit it
221 $post = $this->verify_post_exists($post_id);
222
223 $this->check_post_permission($post_id);
224
225 // Get existing schema data
226 $schema_data = get_post_meta($post_id, 'metasync_schema_markup', true);
227 if (empty($schema_data)) {
228 $schema_data = ['enabled' => true, 'types' => []];
229 }
230
231 // Check if schema type already exists
232 foreach ($schema_data['types'] as $existing_type) {
233 if ($existing_type['type'] === $schema_type) {
234 throw new Exception(sprintf("Schema type '%s' already exists for this post", esc_html($schema_type)));
235 }
236 }
237
238 // Add new schema type
239 $schema_data['types'][] = [
240 'type' => $schema_type,
241 'fields' => $fields,
242 ];
243
244 // Save updated schema data
245 update_post_meta($post_id, 'metasync_schema_markup', $schema_data);
246
247 return $this->success([
248 'post_id' => $post_id,
249 'schema_type' => $schema_type,
250 'schema_types_count' => count($schema_data['types']),
251 'message' => "Schema type '{$schema_type}' added successfully",
252 ]);
253 }
254 }
255
256 /**
257 * Remove Schema Type Tool
258 *
259 * Removes a schema type from a post
260 */
261 class MCP_Tool_Remove_Schema_Type extends MCP_Tool_Base {
262
263 public function get_name() {
264 return 'wordpress_remove_schema_type';
265 }
266
267 public function get_description() {
268 return 'Remove a schema type from a post';
269 }
270
271 public function get_input_schema() {
272 return [
273 'type' => 'object',
274 'properties' => [
275 'post_id' => [
276 'type' => 'integer',
277 'description' => 'Post ID',
278 ],
279 'schema_type' => [
280 'type' => 'string',
281 'enum' => ['article', 'FAQPage', 'product', 'recipe', 'Event', 'JobPosting', 'Review', 'Course', 'Organization', 'Person', 'WebSite', 'NewsArticle', 'LocalBusiness', 'HowTo', 'VideoObject'],
282 'description' => 'Schema type to remove',
283 ],
284 ],
285 'required' => ['post_id', 'schema_type'],
286 ];
287 }
288
289 public function execute($params) {
290 $this->validate_params($params);
291 $this->require_capability('edit_posts');
292
293 $post_id = intval($params['post_id']);
294 $schema_type = sanitize_text_field($params['schema_type']);
295
296 // Validate post exists and user can edit it
297 $post = $this->verify_post_exists($post_id);
298
299 $this->check_post_permission($post_id);
300
301 // Get existing schema data
302 $schema_data = get_post_meta($post_id, 'metasync_schema_markup', true);
303 if (empty($schema_data) || empty($schema_data['types'])) {
304 throw new Exception('No schema types found for this post');
305 }
306
307 // Remove the specified schema type
308 $found = false;
309 $new_types = [];
310 foreach ($schema_data['types'] as $existing_type) {
311 if ($existing_type['type'] === $schema_type) {
312 $found = true;
313 continue; // Skip this type (remove it)
314 }
315 $new_types[] = $existing_type;
316 }
317
318 if (!$found) {
319 throw new Exception(sprintf("Schema type '%s' not found for this post", esc_html($schema_type)));
320 }
321
322 $schema_data['types'] = $new_types;
323
324 // Save updated schema data
325 update_post_meta($post_id, 'metasync_schema_markup', $schema_data);
326
327 // Clear validation errors
328 delete_post_meta($post_id, '_metasync_schema_validation_errors');
329
330 return $this->success([
331 'post_id' => $post_id,
332 'schema_type' => $schema_type,
333 'schema_types_count' => count($schema_data['types']),
334 'message' => "Schema type '{$schema_type}' removed successfully",
335 ]);
336 }
337 }
338
339 /**
340 * Validate Schema Tool
341 *
342 * Validates schema markup for a post
343 */
344 class MCP_Tool_Validate_Schema extends MCP_Tool_Base {
345
346 public function get_name() {
347 return 'wordpress_validate_schema';
348 }
349
350 public function get_description() {
351 return 'Validate schema markup configuration for a post';
352 }
353
354 public function get_input_schema() {
355 return [
356 'type' => 'object',
357 'properties' => [
358 'post_id' => [
359 'type' => 'integer',
360 'description' => 'Post ID',
361 ],
362 ],
363 'required' => ['post_id'],
364 ];
365 }
366
367 public function execute($params) {
368 $this->validate_params($params);
369 $this->require_capability('edit_posts');
370
371 $post_id = intval($params['post_id']);
372
373 // Validate post exists
374 $post = $this->verify_post_exists($post_id);
375
376 // Get schema data
377 $schema_data = get_post_meta($post_id, 'metasync_schema_markup', true);
378
379 if (empty($schema_data) || !isset($schema_data['enabled']) || !$schema_data['enabled']) {
380 return $this->success([
381 'post_id' => $post_id,
382 'valid' => true,
383 'message' => 'Schema markup is not enabled for this post',
384 ]);
385 }
386
387 if (empty($schema_data['types'])) {
388 return $this->success([
389 'post_id' => $post_id,
390 'valid' => false,
391 'errors' => [['message' => 'No schema types configured']],
392 ]);
393 }
394
395 // Get the schema markup class for validation
396 require_once plugin_dir_path(dirname(dirname(__FILE__))) . 'schema-markup/class-metasync-schema-markup.php';
397 $schema_markup = new Metasync_Schema_Markup('metasync', METASYNC_VERSION);
398
399 // Use reflection to access private validation method
400 $reflection = new ReflectionClass($schema_markup);
401 $validate_method = $reflection->getMethod('validate_schema_requirements');
402 $validate_method->setAccessible(true);
403
404 // Validate all schema types
405 $all_errors = [];
406 foreach ($schema_data['types'] as $schema_type_data) {
407 $errors = $validate_method->invoke(
408 $schema_markup,
409 $post_id,
410 $schema_type_data['type'],
411 $schema_type_data['fields']
412 );
413
414 if (!empty($errors)) {
415 $all_errors = array_merge($all_errors, $errors);
416 }
417 }
418
419 $is_valid = empty($all_errors);
420
421 // Update validation errors in post meta
422 if ($is_valid) {
423 delete_post_meta($post_id, '_metasync_schema_validation_errors');
424 } else {
425 update_post_meta($post_id, '_metasync_schema_validation_errors', $all_errors);
426 }
427
428 return $this->success([
429 'post_id' => $post_id,
430 'valid' => $is_valid,
431 'errors' => $all_errors,
432 'message' => $is_valid ? 'Schema markup is valid' : 'Schema markup has validation errors',
433 ]);
434 }
435 }
436
437 /**
438 * Get Schema Content Tool
439 *
440 * Returns the full content fields for a specific schema type on a post
441 */
442 class MCP_Tool_Get_Schema_Content extends MCP_Tool_Base {
443
444 public function get_name() {
445 return 'wordpress_get_schema_content';
446 }
447
448 public function get_description() {
449 return 'Get the full content fields for a specific schema type on a post';
450 }
451
452 public function get_input_schema() {
453 return [
454 'type' => 'object',
455 'properties' => [
456 'post_id' => [
457 'type' => 'integer',
458 'description' => 'Post ID',
459 ],
460 'schema_type' => [
461 'type' => 'string',
462 'enum' => ['article', 'FAQPage', 'product', 'recipe', 'Event', 'JobPosting', 'Review', 'Course', 'Organization', 'Person', 'WebSite', 'NewsArticle', 'LocalBusiness', 'HowTo', 'VideoObject'],
463 'description' => 'Schema type to retrieve content for',
464 ],
465 ],
466 'required' => ['post_id', 'schema_type'],
467 ];
468 }
469
470 public function execute($params) {
471 $this->validate_params($params);
472 $this->require_capability('edit_posts');
473
474 $post_id = intval($params['post_id']);
475 $schema_type = sanitize_text_field($params['schema_type']);
476
477 // Validate post exists
478 $post = $this->verify_post_exists($post_id);
479
480 // Get schema data
481 $schema_data = get_post_meta($post_id, 'metasync_schema_markup', true);
482
483 if (empty($schema_data) || empty($schema_data['types'])) {
484 return $this->success([
485 'post_id' => $post_id,
486 'schema_type' => $schema_type,
487 'fields' => null,
488 'message' => 'No schema types configured for this post',
489 'validation_warnings' => [],
490 ]);
491 }
492
493 // Find the requested schema type
494 $found_fields = null;
495 foreach ($schema_data['types'] as $type_data) {
496 if ($type_data['type'] === $schema_type) {
497 $found_fields = isset($type_data['fields']) ? $type_data['fields'] : [];
498 break;
499 }
500 }
501
502 if ($found_fields === null) {
503 return $this->success([
504 'post_id' => $post_id,
505 'schema_type' => $schema_type,
506 'fields' => null,
507 'message' => sprintf("Schema type '%s' not found for this post", esc_html($schema_type)),
508 'validation_warnings' => [],
509 ]);
510 }
511
512 // Run validation
513 require_once plugin_dir_path(dirname(dirname(__FILE__))) . 'schema-markup/class-metasync-schema-markup.php';
514 $schema_markup = Metasync_Schema_Markup::get_instance();
515 $validation_warnings = $schema_markup->validate_schema_requirements($post_id, $schema_type, $found_fields);
516
517 return $this->success([
518 'post_id' => $post_id,
519 'schema_type' => $schema_type,
520 'fields' => $found_fields,
521 'validation_warnings' => $validation_warnings,
522 ]);
523 }
524 }
525
526 /**
527 * Set Schema Content Tool
528 *
529 * Sets the content fields for a specific schema type on a post
530 */
531 class MCP_Tool_Set_Schema_Content extends MCP_Tool_Base {
532
533 public function get_name() {
534 return 'wordpress_set_schema_content';
535 }
536
537 public function get_description() {
538 return 'Set the content fields for a specific schema type on a post. Merges into existing schema markup data. For HowTo, steps must use the "instructions" key (not "text" or "instruction").';
539 }
540
541 public function get_input_schema() {
542 return [
543 'type' => 'object',
544 'properties' => [
545 'post_id' => [
546 'type' => 'integer',
547 'description' => 'Post ID',
548 ],
549 'schema_type' => [
550 'type' => 'string',
551 'enum' => ['article', 'FAQPage', 'product', 'recipe', 'Event', 'JobPosting', 'Review', 'Course', 'Organization', 'Person', 'WebSite', 'NewsArticle', 'LocalBusiness', 'HowTo', 'VideoObject'],
552 'description' => 'Schema type to set content for',
553 ],
554 'fields' => [
555 'type' => 'object',
556 'description' => 'Content fields for the schema type. FAQPage: faq_items array of {question, answer}. HowTo: steps array of {instructions, image?} (use "instructions" key, not "text"), plus total_time (minutes). product: price, currency, availability, condition, sku, brand. recipe: ingredients (string array), instructions (string array), prep_time, cook_time, total_time, calories, yield.',
557 ],
558 ],
559 'required' => ['post_id', 'schema_type', 'fields'],
560 ];
561 }
562
563 public function execute($params) {
564 $this->validate_params($params);
565 $this->require_capability('edit_posts');
566
567 $post_id = intval($params['post_id']);
568 $schema_type = sanitize_text_field($params['schema_type']);
569
570 // Validate post exists and user can edit it
571 $this->verify_post_exists($post_id);
572 $this->check_post_permission($post_id);
573
574 // Sanitize fields before any DB write (same as Classic Editor and REST paths)
575 require_once plugin_dir_path(dirname(dirname(__FILE__))) . 'schema-markup/class-metasync-schema-markup.php';
576 $schema_markup = Metasync_Schema_Markup::get_instance();
577 $content = $schema_markup->sanitize_schema_fields(
578 isset($params['fields']) && is_array($params['fields']) ? $params['fields'] : [],
579 $schema_type
580 );
581
582 // Get existing schema data
583 $schema_data = get_post_meta($post_id, 'metasync_schema_markup', true);
584 if (empty($schema_data)) {
585 $schema_data = ['enabled' => true, 'types' => []];
586 }
587
588 // Find and update or add the schema type
589 $found = false;
590 foreach ($schema_data['types'] as &$type_data) {
591 if ($type_data['type'] === $schema_type) {
592 $type_data['fields'] = $content;
593 $found = true;
594 break;
595 }
596 }
597 unset($type_data);
598
599 if (!$found) {
600 $schema_data['types'][] = [
601 'type' => $schema_type,
602 'fields' => $content,
603 ];
604 }
605
606 // Save updated schema data
607 update_post_meta($post_id, 'metasync_schema_markup', $schema_data);
608
609 // Validate
610 $validation_warnings = $schema_markup->validate_schema_requirements($post_id, $schema_type, $content);
611
612 // Update validation errors
613 if (!empty($validation_warnings)) {
614 update_post_meta($post_id, '_metasync_schema_validation_errors', $validation_warnings);
615 } else {
616 delete_post_meta($post_id, '_metasync_schema_validation_errors');
617 }
618
619 // Cross-plugin sync
620 $schema_markup->sync_schema_to_seo_plugins($post_id, $schema_type, $content);
621
622 return $this->success([
623 'post_id' => $post_id,
624 'schema_type' => $schema_type,
625 'message' => "Schema content for '{$schema_type}' updated successfully",
626 'validation_warnings' => $validation_warnings,
627 ]);
628 }
629 }
630