PluginProbe
ZIP AI – AI Website Builder & AI Agent / 0.0.13
ZIP AI – AI Website Builder & AI Agent v0.0.13
0.0.13 0.0.12 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
← All changes | inc/api/external-mcp.php +0 -98 trunk → 0.0.13 View file →
@@ -35,10 +35,8 @@
35 35
36 36 namespace ZipAI\MCP\Classes\Api;
37 37
38 38 use ZipAI\MCP\Classes\Core\External_Tool_Policy;
39 -use ZipAI\MCP\Classes\Core\Utils;
40 -use ZipAI\MCP\Classes\Security\Protected_Options_Filter;
41 39
42 40 defined( 'ABSPATH' ) || exit;
43 41
44 42 /**
@@ -91,12 +89,8 @@
91 89 // masking below cannot be corrected — which is precisely why the two
92 90 // error-carrying import abilities are ADVERTISED as their own tools
93 91 // rather than reached through the dispatcher.
94 92 add_filter( 'mcp_adapter_tool_call_result', array( $this, 'unmask_failed_dispatch' ), 10, 3 );
95 - // The protected-options backstop holds on this transport too: entered last
96 - // before execute, left first after it.
97 - add_filter( 'mcp_adapter_pre_tool_call', array( $this, 'enter_protected_context' ), PHP_INT_MAX, 4 );
98 - add_filter( 'mcp_adapter_tool_call_result', array( $this, 'exit_protected_context' ), PHP_INT_MIN );
99 93 }
100 94
101 95 /**
102 96 * Whether this instance rebranded the adapter's default server.
@@ -206,88 +200,8 @@
206 200 return is_string( $capability ) && '' !== $capability ? $capability : self::CAPABILITY;
207 201 }
208 202
209 203 /**
210 - * Whether this instance entered the protected-options MCP context.
211 - *
212 - * @var bool
213 - */
214 - private $entered_protected_context = false;
215 -
216 - /**
217 - * Enter the protected-options MCP context for a tool call on this server, and
218 - * for a call on ANY server that runs one of this plugin's `zipai/*` abilities:
219 - * they are public to every MCP server's dispatcher, so the server id alone
220 - * would leave them unguarded there.
221 - *
222 - * McpTool::execute turns a Throwable from the ability into a WP_Error, so
223 - * the result filter that leaves the context still runs; anything that ends
224 - * the request first is cleared by the shutdown hook, which fails closed.
225 - *
226 - * @param mixed $args Tool arguments, or a WP_Error from an earlier filter.
227 - * @param string $tool_name MCP tool name (slashes become hyphens).
228 - * @param mixed $mcp_tool MCP tool instance.
229 - * @param mixed $server The MCP server handling the call.
230 - * @return mixed
231 - */
232 - public function enter_protected_context( $args, $tool_name, $mcp_tool, $server ) {
233 - if ( is_wp_error( $args ) ) {
234 - return $args;
235 - }
236 - $ours = $server instanceof \WP\MCP\Core\McpServer && self::SERVER_ID === $server->get_server_id();
237 - if ( $ours || self::runs_zipai_ability( $args, (string) $tool_name, $mcp_tool ) ) {
238 - Protected_Options_Filter::enter_mcp();
239 - $this->entered_protected_context = true;
240 - }
241 - return $args;
242 - }
243 -
244 - /**
245 - * Whether a tool call runs one of this plugin's abilities (a namespace
246 - * `External_Tool_Policy::is_allowed()` admits): the tool's own ability, or
247 - * the one the adapter's dispatcher (`mcp-adapter/execute-ability`) is asked
248 - * to run. An adapter without the tool's ability name falls back to the tool
249 - * name, where the ability's slash reads as a hyphen: matched by namespace
250 - * prefix, so a namespace with a hyphen of its own is still read whole.
251 - *
252 - * @param mixed $args Tool arguments.
253 - * @param string $tool_name MCP tool name.
254 - * @param mixed $mcp_tool MCP tool instance.
255 - * @return bool
256 - */
257 - private static function runs_zipai_ability( $args, $tool_name, $mcp_tool ) {
258 - $context = is_object( $mcp_tool ) && method_exists( $mcp_tool, 'get_observability_context' ) ? $mcp_tool->get_observability_context() : array();
259 - $ability = is_array( $context ) && isset( $context['ability_name'] ) && is_string( $context['ability_name'] ) ? $context['ability_name'] : '';
260 - if ( 'mcp-adapter/execute-ability' === $ability || 'mcp-adapter-execute-ability' === $tool_name ) {
261 - $ability = is_array( $args ) && isset( $args['ability_name'] ) && is_string( $args['ability_name'] ) ? $args['ability_name'] : '';
262 - return '' !== $ability && External_Tool_Policy::is_allowed( $ability );
263 - }
264 - if ( '' !== $ability ) {
265 - return External_Tool_Policy::is_allowed( $ability );
266 - }
267 - foreach ( External_Tool_Policy::ALLOWED_NAMESPACES as $namespace ) {
268 - if ( str_starts_with( $tool_name, $namespace . '-' ) ) {
269 - return true;
270 - }
271 - }
272 - return false;
273 - }
274 -
275 - /**
276 - * Leave the protected-options MCP context entered for this call.
277 - *
278 - * @param mixed $result The tool call result.
279 - * @return mixed
280 - */
281 - public function exit_protected_context( $result ) {
282 - if ( $this->entered_protected_context ) {
283 - $this->entered_protected_context = false;
284 - Protected_Options_Filter::exit_mcp();
285 - }
286 - return $result;
287 - }
288 -
289 - /**
290 204 * Surface a failure that the adapter's dispatcher hides.
291 205 *
292 206 * `mcp-adapter/execute-ability` wraps the target result as
293 207 * `{success: true, data: <inner>}`. This means "the dispatch worked". The
@@ -367,20 +281,8 @@
367 281 $stored = get_option( self::OPTION_KEY, self::DISABLED );
368 282 // A legacy boolean/empty value (written before this key was normalised to
369 283 // a string) is treated as an explicit OFF, matching what the admin chose.
370 284 $enabled = is_string( $stored ) ? self::ENABLED === $stored : (bool) $stored;
371 -
372 - // The protected-options guard hooks filters added in MCP Adapter 0.5.0;
373 - // an older standalone adapter would run every tool unguarded.
374 - $adapter = defined( 'WP_MCP_VERSION' ) ? constant( 'WP_MCP_VERSION' ) : null;
375 - if ( $enabled && is_string( $adapter ) && version_compare( $adapter, '0.5.0', '<' ) ) {
376 - static $logged = false;
377 - if ( ! $logged ) {
378 - $logged = true;
379 - Utils::debug_log( 'External MCP endpoint disabled', 'MCP Adapter ' . $adapter . ' is older than 0.5.0' );
380 - }
381 - $enabled = false;
382 - }
383 285
384 286 /**
385 287 * Filter whether the external MCP endpoint is available. Return false to
386 288 * force it off site-wide regardless of the stored setting.