PluginProbe
Kit (formerly ConvertKit) – Email Newsletter, Email Marketing, Membership, Subscribers and Landing Pages / 3.4.4
Kit (formerly ConvertKit) – Email Newsletter, Email Marketing, Membership, Subscribers and Landing Pages v3.4.4
3.4.4 3.4.3 3.4.2 3.4.1 3.4.0 3.3.9 3.3.8 3.3.7 3.3.6 3.3.5 3.3.4 3.3.3 3.3.2 3.3.1 2.2.0 2.2.1 2.2.2 2.2.3 2.2.4 2.2.5 2.2.6 2.2.7 2.2.8 2.2.9 2.3.0 All 197 releases
convertkit / vendor / wordpress / mcp-adapter / includes / Domain / Resources / McpResourceValidator.php

McpResourceValidator.php in Kit (formerly ConvertKit) – Email Newsletter, Email Marketing, Membership, Subscribers and Landing Pages 3.4.4, at vendor/wordpress/mcp-adapter/includes/Domain/Resources/McpResourceValidator.php

192 lines 6.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * MCP Resource Validator class for validating MCP resources according to the specification.
4 *
5 * @package McpAdapter
6 */
7
8 declare( strict_types=1 );
9
10 namespace WP\MCP\Domain\Resources;
11
12 use WP\MCP\Domain\Utils\McpValidator;
13 use WP\McpSchema\Server\Resources\DTO\Resource as ResourceDto;
14 use WP_Error;
15
16 /**
17 * Validates MCP resources against the Model Context Protocol specification.
18 *
19 * Provides minimal, resource-efficient validation to ensure resources conform
20 * to the MCP schema requirements without heavy processing overhead.
21 *
22 * @link https://modelcontextprotocol.io/specification/2025-11-25/server/resources
23 */
24 class McpResourceValidator {
25
26 /**
27 * Validate the MCP resource data array against the MCP schema.
28 *
29 * @param array $resource_data The resource data to validate.
30 * @param string $context Optional context for error messages.
31 *
32 * @return bool|\WP_Error True if valid, WP_Error if validation fails.
33 */
34 public static function validate_resource_data( array $resource_data, string $context = '' ) {
35 $validation_errors = self::get_validation_errors( $resource_data );
36
37 if ( ! empty( $validation_errors ) ) {
38 $error_message = $context ? "[{$context}] " : '';
39 $error_message .= sprintf(
40 /* translators: %s: comma-separated list of validation errors */
41 __( 'Resource validation failed: %s', 'mcp-adapter' ),
42 implode( ', ', $validation_errors )
43 );
44 return new WP_Error( 'mcp_resource_validation_failed', esc_html( $error_message ) );
45 }
46
47 return true;
48 }
49
50 /**
51 * Validate a Resource DTO against the MCP schema.
52 *
53 * @param \WP\McpSchema\Server\Resources\DTO\Resource $resource_dto The resource DTO to validate.
54 *
55 * @return bool|\WP_Error True if valid, WP_Error otherwise.
56 */
57 public static function validate_resource_dto( ResourceDto $resource_dto ) {
58 $errors = array();
59
60 // Validate URI.
61 if ( ! McpValidator::validate_resource_uri( $resource_dto->getUri() ) ) {
62 $errors[] = __( 'Resource URI must be a valid URI string', 'mcp-adapter' );
63 }
64
65 // Validate icons if present.
66 $icons = $resource_dto->getIcons();
67 if ( ! empty( $icons ) ) {
68 $icons_array = array_map( static fn( $icon ) => $icon->toArray(), $icons );
69 $icons_result = McpValidator::validate_icons_array( $icons_array );
70 $icons_errors = self::format_icon_validation_errors( $icons_result );
71 $errors = array_merge( $errors, $icons_errors );
72 }
73
74 // Validate annotations if present.
75 $annotations = $resource_dto->getAnnotations();
76 if ( $annotations ) {
77 $annotation_errors = McpValidator::get_annotation_validation_errors( $annotations->toArray() );
78 $errors = array_merge( $errors, $annotation_errors );
79 }
80
81 if ( ! empty( $errors ) ) {
82 return new WP_Error(
83 'mcp_resource_validation_failed',
84 sprintf(
85 /* translators: %s: list of validation errors */
86 __( 'Resource validation failed: %s', 'mcp-adapter' ),
87 implode( '; ', $errors )
88 )
89 );
90 }
91
92 return true;
93 }
94
95 /**
96 * Validate an McpResource instance against the MCP schema.
97 *
98 * @param \WP\MCP\Domain\Resources\McpResource $the_resource The resource instance to validate.
99 *
100 * @return bool|\WP_Error True if valid, WP_Error if validation fails.
101 */
102 public static function validate_resource_instance( McpResource $the_resource ) {
103 return self::validate_resource_dto( $the_resource->get_protocol_dto() );
104 }
105
106 /**
107 * Get validation errors for MCP resource contents.
108 *
109 * NOTE: This validates the `resource` object used by:
110 * - `resources/read` results (`TextResourceContents` / `BlobResourceContents`)
111 * - `content` blocks of type `resource` (`EmbeddedResource.resource`)
112 *
113 * It does NOT validate the `Resource` metadata object returned by `resources/list`.
114 * For `Resource` DTO validation (resources/list), use validate_resource_dto() instead.
115 *
116 * This validator focuses on the MCP-required fields and ignores unknown fields to remain
117 * forward-compatible with future schema versions.
118 *
119 * @param array $resource_data The resource contents object to validate.
120 *
121 * @return array Array of validation errors, empty if valid.
122 */
123 public static function get_validation_errors( array $resource_data ): array {
124 $errors = array();
125
126 // Validate the required URI field.
127 if ( empty( $resource_data['uri'] ) || ! is_string( $resource_data['uri'] ) ) {
128 $errors[] = __( 'Resource URI is required and must be a non-empty string', 'mcp-adapter' );
129 } elseif ( ! McpValidator::validate_resource_uri( $resource_data['uri'] ) ) {
130 $errors[] = __( 'Resource URI must be a valid URI format', 'mcp-adapter' );
131 }
132
133 // Validate content: at least one of text/blob must be present and correctly typed.
134 // Use array_key_exists to allow empty strings as valid content.
135 $has_text_key = array_key_exists( 'text', $resource_data );
136 $has_blob_key = array_key_exists( 'blob', $resource_data );
137
138 $has_text = $has_text_key && is_string( $resource_data['text'] );
139 $has_blob = $has_blob_key && is_string( $resource_data['blob'] );
140
141 if ( ! $has_text && ! $has_blob ) {
142 $errors[] = __( 'Resource contents must include at least one of: text (string) or blob (base64 string)', 'mcp-adapter' );
143 }
144
145 if ( $has_text_key && ! is_string( $resource_data['text'] ) ) {
146 $errors[] = __( 'Resource text content must be a string when provided', 'mcp-adapter' );
147 }
148
149 if ( $has_blob_key && ! is_string( $resource_data['blob'] ) ) {
150 $errors[] = __( 'Resource blob content must be a string when provided', 'mcp-adapter' );
151 }
152
153 // Validate blob content if present and typed.
154 if ( $has_blob && ! McpValidator::validate_base64( $resource_data['blob'] ) ) {
155 $errors[] = __( 'Resource blob content must be valid base64-encoded data', 'mcp-adapter' );
156 }
157
158 // mimeType is optional. Only its type is checked.
159 if ( isset( $resource_data['mimeType'] ) && ! is_string( $resource_data['mimeType'] ) ) {
160 $errors[] = __( 'Resource mimeType must be a string if provided', 'mcp-adapter' );
161 }
162
163 return $errors;
164 }
165
166 /**
167 * Format icon validation errors from the validation result.
168 *
169 * @param array{valid: array, errors: array} $icons_result The result from validate_icons_array.
170 *
171 * @return array Array of formatted error messages.
172 */
173 private static function format_icon_validation_errors( array $icons_result ): array {
174 $errors = array();
175
176 if ( ! empty( $icons_result['errors'] ) ) {
177 foreach ( $icons_result['errors'] as $error_group ) {
178 foreach ( $error_group['errors'] as $error ) {
179 $errors[] = sprintf(
180 /* translators: 1: icon index, 2: error message */
181 __( 'Icon at index %1$d: %2$s', 'mcp-adapter' ),
182 $error_group['index'],
183 $error
184 );
185 }
186 }
187 }
188
189 return $errors;
190 }
191 }
192