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