| @@ -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. |