*/ private const WOOCOMMERCE_CATALOG_CANDIDATES = array( 'woocommerce/products-list', 'woocommerce/products-query', ); /** * Optional WooCommerce read-only product abilities exposed for visitor chat. * * @var list */ private const WOOCOMMERCE_VISITOR_READONLY = array( 'woocommerce/products-list', 'woocommerce/products-query', 'woocommerce/products-get', ); /** @var bool */ private static $executing = false; /** @var bool */ private static $mcp_authenticated = false; public static function set_mcp_authenticated( bool $authenticated ): void { self::$mcp_authenticated = $authenticated; } /** * True while a tools/call is being dispatched by the plugin's authenticated MCP route. */ public static function is_mcp_authenticated(): bool { return self::$mcp_authenticated; } public static function is_executing(): bool { return self::$executing; } public static function has_registered_ability( string $name ): bool { if ( ! function_exists( 'wp_has_ability' ) ) { return false; } return wp_has_ability( $name ); } public static function resolve_woocommerce_catalog_ability(): ?string { foreach ( self::WOOCOMMERCE_CATALOG_CANDIDATES as $ability_name ) { if ( self::has_registered_ability( $ability_name ) ) { return $ability_name; } } return null; } public static function should_register_fallback_catalog(): bool { return null === self::resolve_woocommerce_catalog_ability(); } /** * @return list */ public static function get_exposed_names(): array { $names = array(); $catalog = self::resolve_woocommerce_catalog_ability(); if ( null !== $catalog ) { $names[] = $catalog; } foreach ( self::WOOCOMMERCE_VISITOR_READONLY as $ability_name ) { if ( null !== $catalog && $ability_name === $catalog ) { continue; } if ( self::has_registered_ability( $ability_name ) ) { $names[] = $ability_name; } } return array_values( array_unique( $names ) ); } public static function is_delegated( string $name ): bool { return in_array( $name, self::get_exposed_names(), true ); } /** * @return array|null */ public static function get_definition( string $name ): ?array { if ( ! self::is_delegated( $name ) || ! function_exists( 'wp_get_ability' ) ) { return null; } $ability = wp_get_ability( $name ); if ( null === $ability ) { return null; } $input_schema = array( 'type' => 'object', 'properties' => new stdClass(), ); if ( method_exists( $ability, 'get_input_schema' ) ) { $schema = $ability->get_input_schema(); if ( is_array( $schema ) ) { $input_schema = $schema; } } $description = ''; if ( method_exists( $ability, 'get_description' ) ) { $description = (string) $ability->get_description(); } return array( 'name' => $name, 'description' => $description, 'input_schema' => $input_schema, 'delegated' => true, ); } /** * @param array|null $params * @return array */ public static function execute( string $name, $params = null ): array { if ( ! self::is_delegated( $name ) ) { return array( 'success' => false, 'message' => sprintf( /* translators: %s: ability name */ __( 'Ability is not delegated: %s', 'agentmatica-mcp-connector' ), $name ), ); } if ( ! self::$mcp_authenticated ) { return array( 'success' => false, 'message' => __( 'Delegated abilities require an authenticated MCP request.', 'agentmatica-mcp-connector' ), ); } if ( ! function_exists( 'wp_get_ability' ) ) { return array( 'success' => false, 'message' => __( 'Abilities API is not available.', 'agentmatica-mcp-connector' ), ); } $ability = wp_get_ability( $name ); if ( null === $ability || ! method_exists( $ability, 'execute' ) ) { return array( 'success' => false, 'message' => sprintf( /* translators: %s: ability name */ __( 'Delegated ability is not available: %s', 'agentmatica-mcp-connector' ), $name ), ); } self::$executing = true; add_filter( 'user_has_cap', array( __CLASS__, 'grant_read_capability_during_execute' ), 10, 4 ); try { $input = is_array( $params ) ? $params : array(); $result = $ability->execute( $input ); } finally { remove_filter( 'user_has_cap', array( __CLASS__, 'grant_read_capability_during_execute' ), 10 ); self::$executing = false; } if ( is_wp_error( $result ) ) { return array( 'success' => false, 'message' => $result->get_error_message(), ); } return is_array( $result ) ? $result : array( 'result' => $result ); } /** * @param array $allcaps All capabilities for the user. * @param array $caps Required capabilities. * @param array $args Capability check arguments. * @param WP_User $user User object. * @return array */ public static function grant_read_capability_during_execute( array $allcaps, array $caps, array $args, WP_User $user ): array { unset( $user ); if ( ! self::$executing || ! self::$mcp_authenticated ) { return $allcaps; } if ( in_array( 'read', $caps, true ) || in_array( 'read_private_posts', $caps, true ) ) { $allcaps['read'] = true; } return $allcaps; } }