PluginProbe
Code Engine – PHP Snippets, AI Functions & Automation for WordPress / 0.5.7
Code Engine – PHP Snippets, AI Functions & Automation for WordPress v0.5.7
0.5.7 0.5.6 0.5.5 0.5.4 0.5.3 0.5.2 0.5.1 0.5.0 0.4.9 0.4.8 0.4.7 0.4.6 trunk 0.0.1 0.0.2 0.2.8 0.2.9 0.3.0 0.3.1 0.3.2 0.3.3 0.3.4 0.3.5 0.3.6 0.3.7 All 33 releases
← All changes | classes/mcp.php +42 -21 0.5.20.5.7 View file →
@@ -2,9 +2,20 @@
2 2
3 3 class Meow_MWCODE_MCP {
4 4 private $core;
5 5 private $api;
6 -
6 + // Per-request memo for the opted-in Callable list, so listing tools and executing
7 + // one don't each reload every snippet from the database. Reset on any mutation.
8 + private $mcp_functions_cache = null;
9 +
10 + // Shared, model-facing explanation of what each scope means. Without this an agent
11 + // sees a bare enum and has to guess which scope to pick.
12 + const SCOPE_DESC = "Where the snippet lives and runs: 'function' = Callable, run on demand (via REST, AI Engine, MCP); 'persistent' = Global, always loaded on both the front-end and wp-admin; 'frontend' = loaded on the front-end only; 'backend' = loaded in wp-admin only; 'scheduled' = run automatically on a schedule (cron); 'content_php' = a PHP snippet output where its [code-engine id=...] shortcode/block is placed; 'content_js' = a JavaScript snippet emitted as a <script> tag via the same shortcode/block.";
13 +
14 + // Scope enums that appear across every management tool. Kept in one place so the
15 + // list can never drift between tools (all seven scopes the API actually accepts).
16 + const SCOPES = ['function', 'backend', 'frontend', 'scheduled', 'persistent', 'content_php', 'content_js'];
17 +
7 18 public function __construct( $core ) {
8 19 $this->core = $core;
9 20
10 21 // Initialize everything on 'init' to ensure options are loaded
@@ -64,19 +75,22 @@
64 75 /**
65 76 * Return the active Callable (function) snippets that opted in to MCP exposure.
66 77 */
67 78 private function get_mcp_functions() {
79 + if ( $this->mcp_functions_cache !== null ) {
80 + return $this->mcp_functions_cache;
81 + }
68 82 global $mwcode;
69 83 if ( !isset( $mwcode ) || !method_exists( $mwcode, 'getSnippets' ) ) {
70 - return [];
84 + return ( $this->mcp_functions_cache = [] );
71 85 }
72 86 $functions = $mwcode->getSnippets( true, 'function' );
73 87 if ( empty( $functions ) ) {
74 - return [];
88 + return ( $this->mcp_functions_cache = [] );
75 89 }
76 - return array_values( array_filter( $functions, function ( $fn ) {
90 + return ( $this->mcp_functions_cache = array_values( array_filter( $functions, function ( $fn ) {
77 91 return !empty( $fn['functionMcp'] ) && !empty( $fn['functionName'] );
78 - } ) );
92 + } ) ) );
79 93 }
80 94
81 95 /**
82 96 * Register each opted-in Callable function as its own MCP tool, named after the
@@ -145,9 +159,9 @@
145 159 'description' => 'Optional filtering options',
146 160 'properties' => [
147 161 'php_ready_args' => [
148 162 'type' => 'boolean',
149 - 'description' => 'If false, arguments will not be formatted for PHP (no $ before names)'
163 + 'description' => 'When true (default), function argument names are returned PHP-ready with a leading $ (e.g. "$id"). Set false to get plain names (e.g. "id").'
150 164 ]
151 165 ]
152 166 ]
153 167 ],
@@ -172,9 +186,9 @@
172 186 'description' => 'Optional filtering options',
173 187 'properties' => [
174 188 'php_ready_args' => [
175 189 'type' => 'boolean',
176 - 'description' => 'If false, arguments will not be formatted for PHP (no $ before names)'
190 + 'description' => 'When true (default), function argument names are returned PHP-ready with a leading $ (e.g. "$id"). Set false to get plain names (e.g. "id").'
177 191 ]
178 192 ]
179 193 ]
180 194 ],
@@ -191,20 +205,20 @@
191 205 'type' => 'object',
192 206 'properties' => [
193 207 'safe' => [
194 208 'type' => 'boolean',
195 - 'description' => 'Whether to filter out snippets with invalid names',
209 + 'description' => 'When true (default), skip function snippets whose function name is empty or invalid. Leave true unless you specifically need to inspect malformed snippets.',
196 210 'default' => true
197 211 ],
198 212 'scope' => [
199 213 'type' => 'string',
200 - 'description' => 'Optional scope filter',
201 - 'enum' => ['function', 'backend', 'frontend', 'scheduled', 'persistent']
214 + 'description' => 'Optional scope filter. ' . self::SCOPE_DESC,
215 + 'enum' => self::SCOPES
202 216 ]
203 217 ]
204 218 ]
205 219 ];
206 -
220 +
207 221 // Execute Snippet
208 222 $tools[] = [
209 223 'name' => 'mwcode_execute_snippet',
210 224 'description' => 'Execute a Code Engine snippet by its ID',
@@ -265,10 +279,10 @@
265 279 'description' => 'Code of the snippet'
266 280 ],
267 281 'scope' => [
268 282 'type' => 'string',
269 - 'description' => 'Scope of the snippet',
270 - 'enum' => ['function', 'backend', 'frontend', 'scheduled', 'persistent'],
283 + 'description' => 'Scope of the snippet. ' . self::SCOPE_DESC . ' Defaults to "function".',
284 + 'enum' => self::SCOPES,
271 285 'default' => 'function'
272 286 ],
273 287 'options' => [
274 288 'type' => 'object',
@@ -365,9 +379,10 @@
365 379 'name' => [ 'type' => 'string' ],
366 380 'code' => [ 'type' => 'string' ],
367 381 'scope' => [
368 382 'type' => 'string',
369 - 'enum' => ['function', 'backend', 'frontend', 'scheduled', 'persistent']
383 + 'description' => self::SCOPE_DESC,
384 + 'enum' => self::SCOPES
370 385 ],
371 386 'description' => [ 'type' => 'string' ],
372 387 'active' => [ 'type' => 'boolean' ],
373 388 'priority' => [ 'type' => 'integer' ],
@@ -486,10 +501,10 @@
486 501 'type' => 'object',
487 502 'properties' => [
488 503 'scope' => [
489 504 'type' => 'string',
490 - 'description' => 'The scope to filter by',
491 - 'enum' => ['function', 'backend', 'frontend', 'scheduled', 'persistent']
505 + 'description' => 'The scope to filter by. ' . self::SCOPE_DESC,
506 + 'enum' => self::SCOPES
492 507 ],
493 508 'filters' => [
494 509 'type' => 'object',
495 510 'description' => 'Additional filters',
@@ -519,15 +534,15 @@
519 534 'type' => 'object',
520 535 'properties' => [
521 536 'scope' => [
522 537 'type' => 'string',
523 - 'description' => 'Optional scope filter',
524 - 'enum' => ['function', 'backend', 'frontend', 'scheduled', 'persistent']
538 + 'description' => 'Optional scope filter. ' . self::SCOPE_DESC,
539 + 'enum' => self::SCOPES
525 540 ]
526 541 ]
527 542 ]
528 543 ];
529 -
544 +
530 545 // Snippet Exists
531 546 $tools[] = [
532 547 'name' => 'mwcode_snippet_exists',
533 548 'description' => 'Check if a Code Engine snippet exists by ID',
@@ -621,9 +636,12 @@
621 636 try {
622 637 $output = $this->api->executeSnippet( $snippetId, is_array( $args ) ? $args : [] );
623 638 return [ 'success' => true, 'data' => $output ];
624 639 }
625 - catch ( Exception $e ) {
640 + // Snippet code is arbitrary PHP: a fatal surfaces as Error/TypeError/ParseError,
641 + // none of which are Exceptions. Catch Throwable so a bad snippet can never take
642 + // down the MCP request.
643 + catch ( \Throwable $e ) {
626 644 return [ 'success' => false, 'error' => $e->getMessage() ];
627 645 }
628 646 }
629 647
@@ -720,9 +738,12 @@
720 738 $validation = $this->api->validateSnippetCode( $args['code'], $args['target'] ?? 'php' );
721 739 return [ 'success' => true, 'data' => $validation ];
722 740 }
723 741 }
724 - catch ( Exception $e ) {
742 + // executeSnippet() runs arbitrary snippet PHP, whose fatals are Errors, not
743 + // Exceptions. Catch Throwable so a broken snippet returns a clean error rather
744 + // than crashing the MCP request.
745 + catch ( \Throwable $e ) {
725 746 return [ 'success' => false, 'error' => $e->getMessage() ];
726 747 }
727 748
728 749 return $result;