PluginProbe ʕ •ᴥ•ʔ
Booking for Appointments and Events Calendar – Amelia / trunk
Booking for Appointments and Events Calendar – Amelia vtrunk
2.4.4 2.4.3 2.4.2 2.4.1 2.4 trunk 1.2.1 1.2.10 1.2.11 1.2.12 1.2.13 1.2.14 1.2.15 1.2.16 1.2.17 1.2.18 1.2.19 1.2.2 1.2.20 1.2.21 1.2.22 1.2.23 1.2.24 1.2.25 1.2.26 1.2.27 1.2.28 1.2.29 1.2.3 1.2.30 1.2.31 1.2.32 1.2.33 1.2.34 1.2.35 1.2.36 1.2.37 1.2.38 1.2.4 1.2.5 1.2.6 1.2.7 1.2.8 1.2.9 2.0 2.0.1 2.0.2 2.1 2.1.1 2.1.2 2.1.3 2.2 2.2.1 2.3
ameliabooking / vendor / wordpress / mcp-adapter / includes / Domain / Prompts / McpPromptBuilder.php
ameliabooking / vendor / wordpress / mcp-adapter / includes / Domain / Prompts Last commit date
Contracts 2 months ago McpPrompt.php 2 months ago McpPromptBuilder.php 2 months ago McpPromptValidator.php 2 months ago RegisterAbilityAsMcpPrompt.php 2 months ago
McpPromptBuilder.php
342 lines
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 passed through to MCP clients.
89 *
90 * Use this to attach purpose-specific metadata that MCP clients can consume.
91 * Keys are passed through unchanged.
92 *
93 * @since 0.5.0
94 *
95 * @var array<string, mixed>
96 */
97 protected array $meta = array();
98
99 /**
100 * Constructor - automatically configures the prompt.
101 *
102 * Configuration happens exactly once during construction, ensuring
103 * idempotent behavior. Subclasses should NOT override this constructor;
104 * instead, implement the configure() method.
105 *
106 * @since 0.5.0
107 */
108 final public function __construct() {
109 $this->configure();
110 }
111
112 /**
113 * Configure the prompt properties.
114 *
115 * Subclasses must implement this method to set the name, title,
116 * description, and arguments for the prompt. This method is called
117 * exactly once during construction.
118 *
119 * @return void
120 */
121 abstract protected function configure(): void;
122
123 /**
124 * Build and return the Prompt DTO instance.
125 *
126 * This method converts the configured state into an MCP Prompt DTO.
127 * Safe to call multiple times - always returns a fresh DTO based on
128 * the current (immutable after construction) state.
129 *
130 * @return \WP\McpSchema\Server\Prompts\DTO\Prompt The built prompt DTO.
131 */
132 public function build(): PromptDto {
133 $argument_dtos = null;
134 if ( ! empty( $this->arguments ) ) {
135 $argument_dtos = array_map(
136 static function ( array $arg ): PromptArgument {
137 return PromptArgument::fromArray(
138 array(
139 'name' => $arg['name'],
140 'title' => $arg['title'] ?? null,
141 'description' => $arg['description'] ?? null,
142 'required' => $arg['required'] ?? null,
143 )
144 );
145 },
146 $this->arguments
147 );
148 }
149
150 // Validate and prepare icons if set.
151 $valid_icons = null;
152 if ( ! empty( $this->icons ) ) {
153 $icons_result = McpValidator::validate_icons_array( $this->icons );
154 if ( ! empty( $icons_result['valid'] ) ) {
155 $valid_icons = $icons_result['valid'];
156 }
157 }
158
159 $prompt_data = array(
160 'name' => $this->name,
161 'title' => $this->title,
162 'description' => $this->description,
163 'arguments' => $argument_dtos,
164 );
165
166 if ( ! empty( $this->meta ) ) {
167 $prompt_data['_meta'] = $this->meta;
168 }
169
170 // Only include icons if valid ones exist.
171 if ( null !== $valid_icons ) {
172 $prompt_data['icons'] = $valid_icons;
173 }
174
175 return PromptDto::fromArray( $prompt_data );
176 }
177
178 /**
179 * Get the unique name for this prompt.
180 *
181 * @return string The prompt name.
182 */
183 public function get_name(): string {
184 return $this->name;
185 }
186
187 /**
188 * Get the prompt title.
189 *
190 * @return string|null The prompt title.
191 */
192 public function get_title(): ?string {
193 return $this->title;
194 }
195
196 /**
197 * Get the prompt description.
198 *
199 * @return string|null The prompt description.
200 */
201 public function get_description(): ?string {
202 return $this->description;
203 }
204
205 /**
206 * Get the prompt arguments.
207 *
208 * @return list<array{name: string, title?: string, description?: string, required?: bool}> The prompt arguments.
209 */
210 public function get_arguments(): array {
211 return $this->arguments;
212 }
213
214 /**
215 * Get the prompt icons.
216 *
217 * @return list<array{src: string, mimeType?: string, sizes?: array<string>, theme?: string}> The prompt icons.
218 * @since 0.5.0
219 *
220 */
221 public function get_icons(): array {
222 return $this->icons ?? array();
223 }
224
225 /**
226 * Set the prompt icons for UI display.
227 *
228 * Icons are validated during build() using McpValidator::validate_icons_array().
229 * Invalid icons are filtered out with warnings (graceful degradation).
230 *
231 * Per MCP 2025-11-25:
232 * - MUST support: image/png, image/jpeg, image/jpg
233 * - SHOULD support: image/svg+xml, image/webp
234 *
235 * @param list<array{src: string, mimeType?: string, sizes?: array<string>, theme?: string}> $icons Array of icon definitions.
236 *
237 * @return self
238 * @since 0.5.0
239 *
240 */
241 protected function set_icons( array $icons ): self {
242 $this->icons = $icons;
243
244 return $this;
245 }
246
247 /**
248 * Get the additional metadata.
249 *
250 * @return array<string, mixed> The additional metadata.
251 * @since 0.5.0
252 *
253 */
254 public function get_meta(): array {
255 return $this->meta;
256 }
257
258 /**
259 * Set additional metadata.
260 *
261 * This metadata is passed through to MCP clients unchanged.
262 *
263 * @param array<string, mixed> $meta Additional metadata key-value pairs.
264 *
265 * @return self
266 * @since 0.5.0
267 *
268 */
269 protected function set_meta( array $meta ): self {
270 $this->meta = $meta;
271
272 return $this;
273 }
274
275 /**
276 * Handle the prompt execution when called.
277 *
278 * Subclasses must implement this method to handle the prompt logic.
279 *
280 * @param array<string, mixed> $arguments The arguments passed to the prompt.
281 *
282 * @return array<string, mixed> The prompt response.
283 */
284 abstract public function handle( array $arguments ): array;
285
286 /**
287 * Check if the current user has permission to execute this prompt.
288 *
289 * Default implementation allows all executions. Override this method
290 * to implement custom permission logic.
291 *
292 * @param array<string, mixed> $arguments The arguments passed to the prompt.
293 *
294 * @return bool True if execution is allowed, false otherwise.
295 */
296 public function has_permission( array $arguments ): bool {
297 // Default: allow all executions
298 // Override this method to implement custom permission logic
299 return true;
300 }
301
302 /**
303 * Helper method to add an argument to the prompt.
304 *
305 * @param string $name The argument name.
306 * @param string|null $description Optional argument description.
307 * @param bool $required Whether the argument is required.
308 *
309 * @return self
310 */
311 protected function add_argument( string $name, ?string $description = null, bool $required = false ): self {
312 $this->arguments[] = $this->create_argument( $name, $description, $required );
313
314 return $this;
315 }
316
317 /**
318 * Helper method to create an argument definition.
319 *
320 * @param string $name The argument name.
321 * @param string|null $description Optional argument description.
322 * @param bool $required Whether the argument is required.
323 *
324 * @return array{name: string, description?: string, required?: true} The argument definition.
325 */
326 protected function create_argument( string $name, ?string $description = null, bool $required = false ): array {
327 $argument = array(
328 'name' => $name,
329 );
330
331 if ( null !== $description ) {
332 $argument['description'] = $description;
333 }
334
335 if ( $required ) {
336 $argument['required'] = true;
337 }
338
339 return $argument;
340 }
341 }
342