PluginProbe
Elementor Website Builder – more than just a page builder / 4.3.0-beta3
Elementor Website Builder – more than just a page builder v4.3.0-beta3
4.3.0-beta3 4.3.0-beta2 4.3.0-beta1 4.2.4 4.2.3 4.2.2 4.2.1 4.2.0 4.1.5 4.2.0-beta2 4.2.0-dev2 4.2.0-beta1 4.1.4 4.1.3 4.1.2 4.1.1 4.1.0 4.1.0-beta3 4.1.0-dev3 4.0.9 4.1.0-beta2 4.1.0-dev2 4.0.8 4.1.0-beta1 4.1.0-dev1 All 452 releases
← All changes | modules/mcp/abilities/abstract-ability.php +155 -3 4.2.24.3.0-beta3 View file →
@@ -1,8 +1,11 @@
1 1 <?php
2 2
3 3 namespace Elementor\Modules\Mcp\Abilities;
4 4
5 +use Elementor\Modules\Mcp\Utils\Editor_Sync_State;
6 +use Elementor\Modules\Mcp\Utils\Mcp_V4_Gate;
7 +
5 8 if ( ! defined( 'ABSPATH' ) ) {
6 9 exit;
7 10 }
8 11
@@ -7,8 +10,14 @@
7 10 }
8 11
9 12 abstract class Abstract_Ability {
10 13
14 + const KIND_TOOL = 'tool';
15 + const KIND_RESOURCE = 'resource';
16 + const ABILITY_ID_PREFIX = 'elementor/';
17 +
18 + private ?Ability_Definition $cached_definition = null;
19 +
11 20 abstract protected function get_ability_id(): string;
12 21
13 22 abstract protected function get_definition(): Ability_Definition;
14 23
@@ -13,10 +22,153 @@
13 22 abstract protected function get_definition(): Ability_Definition;
14 23
15 24 abstract public function execute( $input = [] );
16 25
26 + final public function execute_guarded( $input = [] ) {
27 + $availability = $this->is_available();
28 + if ( is_wp_error( $availability ) ) {
29 + return $availability;
30 + }
31 +
32 + $is_mutating = self::KIND_TOOL === $this->get_kind() && $this->is_destructive();
33 +
34 + if ( $is_mutating ) {
35 + $guard = apply_filters( 'elementor/mcp/pre_execute_guard', null, $input );
36 + if ( is_wp_error( $guard ) ) {
37 + return $guard;
38 + }
39 + }
40 +
41 + $result = $this->execute( $input );
42 + $post_id = isset( $input['post_id'] ) ? (int) $input['post_id'] : 0;
43 +
44 + $is_failed = is_wp_error( $result ) || ( is_array( $result ) && 'error' === ( $result['status'] ?? '' ) );
45 +
46 + if ( $is_mutating && $post_id > 0 && ! $is_failed ) {
47 + Editor_Sync_State::set_mcp_mutation( $post_id );
48 + }
49 +
50 + return $result;
51 + }
52 +
53 + public function check_permission(): bool {
54 + return (bool) call_user_func( $this->definition()->permission_callback );
55 + }
56 +
17 57 public function register(): void {
18 - $definition = $this->get_definition()->to_array();
19 - $definition['execute_callback'] = [ $this, 'execute' ];
20 - wp_register_ability( $this->get_ability_id(), $definition );
58 + $definition = $this->definition()->to_array();
59 + $definition['execute_callback'] = [ $this, 'execute_guarded' ];
60 + $definition['description'] = $this->get_description_for_llm();
61 +
62 + $meta = is_array( $definition['meta'] ?? null ) ? $definition['meta'] : [];
63 + $mcp = is_array( $meta['mcp'] ?? null ) ? $meta['mcp'] : [];
64 + $mcp['public'] = true;
65 + if ( isset( $mcp['description'] ) ) {
66 + $mcp['description'] = $this->maybe_append_unavailable_notice( (string) $mcp['description'] );
67 + }
68 + $meta['mcp'] = $mcp;
69 + $meta['show_in_rest'] = true;
70 + $definition['meta'] = $meta;
71 + wp_register_ability( $this->get_id(), $definition );
72 + }
73 +
74 + public function get_id(): string {
75 + return $this->get_ability_id();
76 + }
77 +
78 + public function get_kind(): string {
79 + return $this->is_resource() ? self::KIND_RESOURCE : self::KIND_TOOL;
80 + }
81 +
82 + public function get_uri(): ?string {
83 + return $this->mcp_meta()['uri'] ?? null;
84 + }
85 +
86 + public function get_mime_type(): ?string {
87 + return $this->mcp_meta()['mimeType'] ?? null;
88 + }
89 +
90 + public function get_resource_description(): ?string {
91 + $base = $this->mcp_meta()['description'] ?? $this->definition()->description;
92 +
93 + return $this->maybe_append_unavailable_notice( (string) $base );
94 + }
95 +
96 + public function get_description_for_llm(): string {
97 + return $this->maybe_append_unavailable_notice( (string) $this->definition()->description );
98 + }
99 +
100 + public function get_display_name(): string {
101 + return (string) ( $this->mcp_meta()['name'] ?? $this->definition()->label );
102 + }
103 +
104 + public function get_proxy_slug(): string {
105 + if ( self::KIND_RESOURCE === $this->get_kind() ) {
106 + return (string) $this->get_uri();
107 + }
108 +
109 + return substr( $this->get_id(), strlen( self::ABILITY_ID_PREFIX ) );
110 + }
111 +
112 + /**
113 + * Whether this ability may currently run.
114 + *
115 + * Subclasses may override to add reasons beyond the default Atomic Editor gate
116 + * (missing plugin, licence tier, post-type support, ...). Return `true`
117 + * when available, or a `\WP_Error` explaining why not. Include a
118 + * `description_notice` entry in the error data to add a short hint to
119 + * this ability's description in `tools/list` / `elementor/list-resources`.
120 + *
121 + * @return true|\WP_Error
122 + */
123 + public function is_available() {
124 + return Mcp_V4_Gate::is_available( $this->get_id() );
125 + }
126 +
127 + public function is_exposed_via_proxy(): bool {
128 + return true;
129 + }
130 +
131 + public function is_exposed_on_server(): bool {
132 + return true;
133 + }
134 +
135 + private function is_resource(): bool {
136 + return self::KIND_RESOURCE === ( $this->mcp_meta()['type'] ?? null );
137 + }
138 +
139 + private function is_destructive(): bool {
140 + return false !== ( $this->definition()->meta['annotations']['destructive'] ?? true );
141 + }
142 +
143 + protected function definition(): Ability_Definition {
144 + if ( null === $this->cached_definition ) {
145 + $this->cached_definition = $this->get_definition();
146 + }
147 +
148 + return $this->cached_definition;
149 + }
150 +
151 + private function mcp_meta(): array {
152 + $meta = $this->definition()->meta;
153 +
154 + return is_array( $meta['mcp'] ?? null ) ? $meta['mcp'] : [];
155 + }
156 +
157 + private function maybe_append_unavailable_notice( string $description ): string {
158 + $availability = $this->is_available();
159 + if ( ! is_wp_error( $availability ) ) {
160 + return $description;
161 + }
162 +
163 + $data = $availability->get_error_data();
164 + $notice = is_array( $data ) && isset( $data['description_notice'] )
165 + ? (string) $data['description_notice']
166 + : $availability->get_error_message();
167 +
168 + if ( '' === $notice ) {
169 + return $description;
170 + }
171 +
172 + return '' === $description ? $notice : rtrim( $description ) . ' ' . $notice;
21 173 }
22 174 }