PluginProbe
ZIP AI – AI Website Builder & AI Agent (Beta) / 0.0.8
ZIP AI – AI Website Builder & AI Agent (Beta) v0.0.8
0.0.11 0.0.10 0.0.9 trunk 0.0.4 0.0.5 0.0.6 0.0.7 0.0.8
zip-ai / inc / core / tool-registry.php

tool-registry.php in ZIP AI – AI Website Builder & AI Agent (Beta) 0.0.8, at inc/core/tool-registry.php

299 lines 7.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Tool Registry - Central registry for MCP tools with execution mode support
4 *
5 * @package zip-ai
6 */
7
8 namespace ZipAI\MCP\Classes\Core;
9
10 // Exit if accessed directly.
11 if ( ! defined( 'ABSPATH' ) ) {
12 exit;
13 }
14
15 /**
16 * The Tool_Registry Class.
17 * Provides API for third-party plugins to register MCP tools with different execution modes.
18 */
19 class Tool_Registry {
20
21 /**
22 * Registered tools with execution modes.
23 *
24 * @since 1.0.0
25 * @var array<string, array<string, mixed>>
26 */
27 private $tools = array();
28
29 /**
30 * Constructor of this class.
31 *
32 * @since 1.0.0
33 * @return void
34 */
35 public function __construct() {
36 // Allow third-party plugins to register tools after abilities API is ready.
37 add_action( 'wp_abilities_api_init', array( $this, 'trigger_tool_registration' ), 999 );
38
39 // Enqueue tool metadata for JavaScript.
40 add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_tool_metadata' ), 20 );
41 add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_tool_metadata' ), 20 );
42 }
43
44 /**
45 * Trigger the tool registration action for third-party plugins.
46 *
47 * @since 1.0.0
48 * @return void
49 */
50 public function trigger_tool_registration() {
51 /**
52 * Fires when ZipWP MCP is ready to accept tool registrations.
53 *
54 * Third-party plugins should hook into this action to register their tools.
55 *
56 * @since 1.0.0
57 */
58 do_action( 'zip_ai_register_tools', $this );
59 }
60
61 /**
62 * Register a new MCP tool.
63 *
64 * @param string $tool_name Unique tool name (e.g., 'myplugin/my-action').
65 * @param array<string, mixed> $args Tool configuration arguments.
66 * @return bool True on success, false on failure.
67 *
68 * @since 1.0.0
69 */
70 public function register_tool( $tool_name, $args ) {
71 // Validate required parameters.
72 if ( empty( $tool_name ) ) {
73 return false;
74 }
75
76 // Default arguments.
77 $defaults = array(
78 'description' => '',
79 'execution_mode' => 'rest_api', // 'rest_api' or 'js_hook'
80 'input_schema' => array(),
81 'examples' => array(),
82 'keywords' => array(),
83 'api_endpoint' => null, // For rest_api mode
84 'js_handler' => null, // For js_hook mode (JavaScript function name)
85 'capabilities' => array( 'edit_posts' ), // WordPress capabilities required
86 'callback' => null, // PHP callback for rest_api mode
87 'preview_mode' => 'none', // 'none', 'client', 'server'
88 );
89
90 /**
91 * Narrowed type for `$args`.
92 *
93 * @var array<string, mixed> $args
94 */
95 $args = wp_parse_args( $args, $defaults );
96
97 // Validate execution_mode.
98 if ( ! in_array( $args['execution_mode'], array( 'rest_api', 'js_hook' ), true ) ) {
99 return false;
100 }
101
102 // For rest_api mode, callback is required.
103 if ( 'rest_api' === $args['execution_mode'] && empty( $args['callback'] ) && empty( $args['api_endpoint'] ) ) {
104 return false;
105 }
106
107 // For js_hook mode, js_handler is required.
108 if ( 'js_hook' === $args['execution_mode'] && empty( $args['js_handler'] ) ) {
109 return false;
110 }
111
112 // Store the tool.
113 $this->tools[ $tool_name ] = $args;
114
115 // Register with WordPress Abilities API if available.
116 if ( class_exists( 'WP_Abilities_Registry' ) ) {
117 $this->register_with_abilities_api( $tool_name, $args );
118 }
119
120 return true;
121 }
122
123 /**
124 * Register tool with WordPress Abilities API.
125 *
126 * @param string $tool_name Tool name.
127 * @param array<string, mixed> $args Tool arguments.
128 * @return void
129 *
130 * @since 1.0.0
131 */
132 private function register_with_abilities_api( $tool_name, $args ) {
133 $registry = \WP_Abilities_Registry::get_instance();
134
135 if ( null === $registry ) {
136 return;
137 }
138
139 // Create ability configuration.
140 $ability_config = array(
141 'description' => $args['description'],
142 'input_schema' => $args['input_schema'],
143 'meta' => array(
144 'examples' => $args['examples'],
145 'keywords' => $args['keywords'],
146 'execution_mode' => $args['execution_mode'],
147 'js_handler' => $args['js_handler'],
148 'preview_mode' => $args['preview_mode'],
149 ),
150 );
151
152 // Add API endpoint for rest_api mode.
153 if ( 'rest_api' === $args['execution_mode'] && ! empty( $args['api_endpoint'] ) ) {
154 $ability_config['meta']['api_endpoint'] = $args['api_endpoint'];
155 }
156
157 // Add callback executor if provided.
158 if ( ! empty( $args['callback'] ) ) {
159 $ability_config['executor'] = $args['callback'];
160 }
161
162 // Register the ability.
163 $registry->register( $tool_name, $ability_config );
164 }
165
166 /**
167 * Get all registered tools.
168 *
169 * @return array<string, array<string, mixed>> Array of registered tools.
170 *
171 * @since 1.0.0
172 */
173 public function get_all_tools() {
174 return $this->tools;
175 }
176
177 /**
178 * Get a specific tool by name.
179 *
180 * @param string $tool_name Tool name.
181 * @return array<string, mixed>|null Tool configuration or null if not found.
182 *
183 * @since 1.0.0
184 */
185 public function get_tool( $tool_name ) {
186 return isset( $this->tools[ $tool_name ] ) ? $this->tools[ $tool_name ] : null;
187 }
188
189 /**
190 * Get tools by execution mode.
191 *
192 * @param string $mode Execution mode ('rest_api' or 'js_hook').
193 * @return array<string, array<string, mixed>> Array of tools matching the execution mode.
194 *
195 * @since 1.0.0
196 */
197 public function get_tools_by_mode( $mode ) {
198 return array_filter(
199 $this->tools,
200 function ( $tool ) use ( $mode ) {
201 return isset( $tool['execution_mode'] ) && $tool['execution_mode'] === $mode;
202 }
203 );
204 }
205
206 /**
207 * Enqueue tool metadata for JavaScript.
208 *
209 * @since 1.0.0
210 * @return void
211 */
212 public function enqueue_tool_metadata() {
213 if ( ! is_user_logged_in() ) {
214 return;
215 }
216
217 // Sync tools from WordPress Abilities API
218 $this->sync_from_abilities_api();
219
220 // Prepare tool metadata for JavaScript (only execution-related info).
221 $tool_metadata = array();
222 foreach ( $this->tools as $tool_name => $tool_config ) {
223 $tool_metadata[ $tool_name ] = array(
224 'execution_mode' => $tool_config['execution_mode'],
225 'js_handler' => $tool_config['js_handler'] ?? null,
226 'preview_mode' => $tool_config['preview_mode'] ?? 'none',
227 );
228 }
229
230 // Add inline script with tool metadata.
231 wp_add_inline_script(
232 'zip-ai-tool-hooks',
233 'window.zipwpMcpTools = ' . wp_json_encode( $tool_metadata ) . ';',
234 'before'
235 );
236 }
237
238 /**
239 * Sync tools from WordPress Abilities API.
240 * This ensures all abilities registered via wp_register_ability() are included.
241 *
242 * @since 1.0.0
243 * @return void
244 */
245 private function sync_from_abilities_api() {
246 if ( ! class_exists( 'WP_Abilities_Registry' ) ) {
247 return;
248 }
249
250 $registry = \WP_Abilities_Registry::get_instance();
251
252 if ( null === $registry ) {
253 return;
254 }
255
256 $abilities = $registry->get_all_registered();
257
258 foreach ( $abilities as $ability_name => $ability ) {
259 $ability_name = (string) $ability_name;
260 // Skip if already registered via register_tool()
261 if ( isset( $this->tools[ $ability_name ] ) ) {
262 continue;
263 }
264
265 // Get metadata from ability (WP_Ability has get_meta(), not get_meta_data())
266 /**
267 * Narrowed type for `$meta`.
268 *
269 * @var array<string, mixed> $meta
270 */
271 $meta = $ability->get_meta();
272
273 // Determine execution mode
274 $execution_mode = $meta['execution_mode'] ?? 'rest_api';
275
276 // Add to tools array
277 $this->tools[ $ability_name ] = array(
278 'execution_mode' => $execution_mode,
279 'js_handler' => $meta['js_handler'] ?? null,
280 'preview_mode' => $meta['preview_mode'] ?? 'none',
281 'description' => $ability->get_description(),
282 );
283 }
284 }
285
286 /**
287 * Check if a tool uses JavaScript hook execution.
288 *
289 * @param string $tool_name Tool name.
290 * @return bool True if tool uses JS hooks, false otherwise.
291 *
292 * @since 1.0.0
293 */
294 public function is_js_hook_tool( $tool_name ) {
295 $tool = $this->get_tool( $tool_name );
296 return $tool && 'js_hook' === ( $tool['execution_mode'] ?? '' );
297 }
298 }
299