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 / Prompts / McpPromptBuilder.php

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

346 lines 8.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Abstract base class for building MCP prompts.
4 *
5 * @package McpAdapter
6 */
7
8 declare( strict_types=1 );
9
10 namespace WP\MCP\Domain\Prompts;
11
12 use WP\MCP\Domain\Prompts\Contracts\McpPromptBuilderInterface;
13 use WP\MCP\Domain\Utils\McpValidator;
14 use WP\McpSchema\Server\Prompts\DTO\Prompt as PromptDto;
15 use WP\McpSchema\Server\Prompts\DTO\PromptArgument;
16
17 /**
18 * Abstract base class for building MCP prompts.
19 *
20 * @deprecated 0.5.0 Use {@see \WP\MCP\Domain\Prompts\McpPrompt} instead for creating prompts.
21 *
22 * The fluent API or array configuration provided by McpPrompt is simpler
23 * and more flexible than subclassing this abstract class:
24 *
25 * ```php
26 * // Fluent API (preferred)
27 * $prompt = McpPrompt::create('my-prompt')
28 * ->title('My Prompt')
29 * ->argument('input', 'Input text', true)
30 * ->handler(function(array $args): array {
31 * return ['messages' => [...]];
32 * });
33 *
34 * // Array configuration (WordPress-style)
35 * $prompt = McpPrompt::fromArray([
36 * 'name' => 'my-prompt',
37 * 'title' => 'My Prompt',
38 * 'arguments' => [['name' => 'input', 'description' => 'Input text', 'required' => true]],
39 * 'handler' => function(array $args): array { return [...]; },
40 * ]);
41 * ```
42 *
43 * This class remains functional for backward compatibility but will be
44 * removed in a future major version.
45 *
46 * @see McpPrompt For the preferred prompt creation API.
47 */
48 abstract class McpPromptBuilder implements McpPromptBuilderInterface {
49
50 /**
51 * The prompt name (unique identifier).
52 *
53 * @var string
54 */
55 protected string $name = '';
56
57 /**
58 * The prompt title (human-readable display name).
59 *
60 * @var string|null
61 */
62 protected ?string $title = null;
63
64 /**
65 * The prompt description.
66 *
67 * @var string|null
68 */
69 protected ?string $description = null;
70
71 /**
72 * The prompt arguments.
73 *
74 * @var list<array{name: string, title?: string, description?: string, required?: bool}>
75 */
76 protected array $arguments = array();
77
78 /**
79 * The prompt icons for UI display.
80 *
81 * @since 0.5.0
82 *
83 * @var list<array{src: string, mimeType?: string, sizes?: array<string>, theme?: string}>|null
84 */
85 protected ?array $icons = null;
86
87 /**
88 * Additional metadata for MCP clients.
89 *
90 * Use this to attach purpose-specific metadata that MCP clients can consume.
91 * Key names are kept as written. MCP declares `_meta` an object, so {@see self::build()}
92 * emits this only when it is a non-empty associative array; a list or an empty array
93 * would serialize as a JSON array and is omitted instead.
94 *
95 * @since 0.5.0
96 *
97 * @var array<string, mixed>
98 */
99 protected array $meta = array();
100
101 /**
102 * Constructor - automatically configures the prompt.
103 *
104 * Configuration happens exactly once during construction, ensuring
105 * idempotent behavior. Subclasses should NOT override this constructor;
106 * instead, implement the configure() method.
107 *
108 * @since 0.5.0
109 */
110 final public function __construct() {
111 $this->configure();
112 }
113
114 /**
115 * Configure the prompt properties.
116 *
117 * Subclasses must implement this method to set the name, title,
118 * description, and arguments for the prompt. This method is called
119 * exactly once during construction.
120 *
121 * @return void
122 */
123 abstract protected function configure(): void;
124
125 /**
126 * Build and return the Prompt DTO instance.
127 *
128 * This method converts the configured state into an MCP Prompt DTO.
129 * Safe to call multiple times - always returns a fresh DTO based on
130 * the current (immutable after construction) state.
131 *
132 * @return \WP\McpSchema\Server\Prompts\DTO\Prompt The built prompt DTO.
133 */
134 public function build(): PromptDto {
135 $argument_dtos = null;
136 if ( ! empty( $this->arguments ) ) {
137 $argument_dtos = array_map(
138 static function ( array $arg ): PromptArgument {
139 return PromptArgument::fromArray(
140 array(
141 'name' => $arg['name'],
142 'title' => $arg['title'] ?? null,
143 'description' => $arg['description'] ?? null,
144 'required' => $arg['required'] ?? null,
145 )
146 );
147 },
148 $this->arguments
149 );
150 }
151
152 // Validate and prepare icons if set.
153 $valid_icons = null;
154 if ( ! empty( $this->icons ) ) {
155 $icons_result = McpValidator::validate_icons_array( $this->icons );
156 if ( ! empty( $icons_result['valid'] ) ) {
157 $valid_icons = $icons_result['valid'];
158 }
159 }
160
161 $prompt_data = array(
162 'name' => $this->name,
163 'title' => $this->title,
164 'description' => $this->description,
165 'arguments' => $argument_dtos,
166 );
167
168 $prompt_meta = McpValidator::normalize_meta( $this->meta );
169 if ( null !== $prompt_meta ) {
170 $prompt_data['_meta'] = $prompt_meta;
171 }
172
173 // Only include icons if valid ones exist.
174 if ( null !== $valid_icons ) {
175 $prompt_data['icons'] = $valid_icons;
176 }
177
178 return PromptDto::fromArray( $prompt_data );
179 }
180
181 /**
182 * Get the unique name for this prompt.
183 *
184 * @return string The prompt name.
185 */
186 public function get_name(): string {
187 return $this->name;
188 }
189
190 /**
191 * Get the prompt title.
192 *
193 * @return string|null The prompt title.
194 */
195 public function get_title(): ?string {
196 return $this->title;
197 }
198
199 /**
200 * Get the prompt description.
201 *
202 * @return string|null The prompt description.
203 */
204 public function get_description(): ?string {
205 return $this->description;
206 }
207
208 /**
209 * Get the prompt arguments.
210 *
211 * @return list<array{name: string, title?: string, description?: string, required?: bool}> The prompt arguments.
212 */
213 public function get_arguments(): array {
214 return $this->arguments;
215 }
216
217 /**
218 * Get the prompt icons.
219 *
220 * @return list<array{src: string, mimeType?: string, sizes?: array<string>, theme?: string}> The prompt icons.
221 * @since 0.5.0
222 *
223 */
224 public function get_icons(): array {
225 return $this->icons ?? array();
226 }
227
228 /**
229 * Set the prompt icons for UI display.
230 *
231 * Icons are validated during build() using McpValidator::validate_icons_array().
232 * Invalid icons are filtered out with warnings (graceful degradation).
233 *
234 * Per MCP 2025-11-25:
235 * - MUST support: image/png, image/jpeg, image/jpg
236 * - SHOULD support: image/svg+xml, image/webp
237 *
238 * @param list<array{src: string, mimeType?: string, sizes?: array<string>, theme?: string}> $icons Array of icon definitions.
239 *
240 * @return self
241 * @since 0.5.0
242 *
243 */
244 protected function set_icons( array $icons ): self {
245 $this->icons = $icons;
246
247 return $this;
248 }
249
250 /**
251 * Get the additional metadata.
252 *
253 * @return array<string, mixed> The additional metadata.
254 * @since 0.5.0
255 *
256 */
257 public function get_meta(): array {
258 return $this->meta;
259 }
260
261 /**
262 * Set additional metadata.
263 *
264 * Key names are kept as written. MCP declares `_meta` an object, so {@see self::build()}
265 * emits this only when it is a non-empty associative array.
266 *
267 * @param array<string, mixed> $meta Additional metadata key-value pairs.
268 *
269 * @return self
270 * @since 0.5.0
271 *
272 */
273 protected function set_meta( array $meta ): self {
274 $this->meta = $meta;
275
276 return $this;
277 }
278
279 /**
280 * Handle the prompt execution when called.
281 *
282 * Subclasses must implement this method to handle the prompt logic.
283 *
284 * @param array<string, mixed> $arguments The arguments passed to the prompt.
285 *
286 * @return array<string, mixed> The prompt response.
287 */
288 abstract public function handle( array $arguments ): array;
289
290 /**
291 * Check if the current user has permission to execute this prompt.
292 *
293 * Default implementation allows all executions. Override this method
294 * to implement custom permission logic.
295 *
296 * @param array<string, mixed> $arguments The arguments passed to the prompt.
297 *
298 * @return bool True if execution is allowed, false otherwise.
299 */
300 public function has_permission( array $arguments ): bool {
301 // Default: allow all executions
302 // Override this method to implement custom permission logic
303 return true;
304 }
305
306 /**
307 * Helper method to add an argument to the prompt.
308 *
309 * @param string $name The argument name.
310 * @param string|null $description Optional argument description.
311 * @param bool $required Whether the argument is required.
312 *
313 * @return self
314 */
315 protected function add_argument( string $name, ?string $description = null, bool $required = false ): self {
316 $this->arguments[] = $this->create_argument( $name, $description, $required );
317
318 return $this;
319 }
320
321 /**
322 * Helper method to create an argument definition.
323 *
324 * @param string $name The argument name.
325 * @param string|null $description Optional argument description.
326 * @param bool $required Whether the argument is required.
327 *
328 * @return array{name: string, description?: string, required?: true} The argument definition.
329 */
330 protected function create_argument( string $name, ?string $description = null, bool $required = false ): array {
331 $argument = array(
332 'name' => $name,
333 );
334
335 if ( null !== $description ) {
336 $argument['description'] = $description;
337 }
338
339 if ( $required ) {
340 $argument['required'] = true;
341 }
342
343 return $argument;
344 }
345 }
346