PluginProbe
Code Engine – PHP Snippets, AI Functions & Automation for WordPress / 0.5.0
Code Engine – PHP Snippets, AI Functions & Automation for WordPress v0.5.0
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 0.3.8 All 32 releases
← All changes | classes/mcp.php +25 -59 trunk0.5.0 View file →
@@ -2,20 +2,9 @@
2 2
3 3 class Meow_MWCODE_MCP {
4 4 private $core;
5 5 private $api;
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 -
6 +
18 7 public function __construct( $core ) {
19 8 $this->core = $core;
20 9
21 10 // Initialize everything on 'init' to ensure options are loaded
@@ -30,13 +19,11 @@
30 19 if ( !isset( $mwai ) ) {
31 20 return;
32 21 }
33 22
34 - // Two independent surfaces share AI Engine's MCP server, each behind its own
35 - // global master switch (and AI Engine's own MCP auth gate upstream):
36 - // - the management/internal API (the 'mcp_support' option)
37 - // - individual Callable functions, opted-in per snippet via 'functionMcp' and
38 - // only exposed when the 'mcp_functions' option is enabled
23 + // Two independent surfaces share AI Engine's MCP server:
24 + // - the management/internal API (master switch: the 'mcp_support' option)
25 + // - individual Callable functions opted-in per snippet (the 'functionMcp' flag)
39 26 // Either one alone is enough to justify hooking the filters.
40 27 add_filter( 'mwai_mcp_tools', array( $this, 'register_tools' ) );
41 28 add_filter( 'mwai_mcp_callback', array( $this, 'handle_tool_execution' ), 10, 4 );
42 29 }
@@ -42,13 +29,9 @@
42 29 }
43 30
44 31 public function register_tools( $tools ) {
45 32 // Individual Callable functions that opted in to MCP, exposed as first-class tools.
46 - // Gated behind a global master switch (Settings > For Developers > MCP Functions)
47 - // in addition to each snippet's per-function opt-in.
48 - if ( $this->core->get_option( 'mcp_functions', false ) ) {
49 - $tools = $this->register_function_tools( $tools );
50 - }
33 + $tools = $this->register_function_tools( $tools );
51 34
52 35 // Code Engine's management/internal API (Settings > For Developers > MCP Support).
53 36 if ( $this->core->get_option( 'mcp_support', false ) ) {
54 37 $tools = $this->register_management_tools( $tools );
@@ -75,22 +58,19 @@
75 58 /**
76 59 * Return the active Callable (function) snippets that opted in to MCP exposure.
77 60 */
78 61 private function get_mcp_functions() {
79 - if ( $this->mcp_functions_cache !== null ) {
80 - return $this->mcp_functions_cache;
81 - }
82 62 global $mwcode;
83 63 if ( !isset( $mwcode ) || !method_exists( $mwcode, 'getSnippets' ) ) {
84 - return ( $this->mcp_functions_cache = [] );
64 + return [];
85 65 }
86 66 $functions = $mwcode->getSnippets( true, 'function' );
87 67 if ( empty( $functions ) ) {
88 - return ( $this->mcp_functions_cache = [] );
68 + return [];
89 69 }
90 - return ( $this->mcp_functions_cache = array_values( array_filter( $functions, function ( $fn ) {
70 + return array_values( array_filter( $functions, function ( $fn ) {
91 71 return !empty( $fn['functionMcp'] ) && !empty( $fn['functionName'] );
92 - } ) ) );
72 + } ) );
93 73 }
94 74
95 75 /**
96 76 * Register each opted-in Callable function as its own MCP tool, named after the
@@ -159,9 +139,9 @@
159 139 'description' => 'Optional filtering options',
160 140 'properties' => [
161 141 'php_ready_args' => [
162 142 'type' => 'boolean',
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").'
143 + 'description' => 'If false, arguments will not be formatted for PHP (no $ before names)'
164 144 ]
165 145 ]
166 146 ]
167 147 ],
@@ -186,9 +166,9 @@
186 166 'description' => 'Optional filtering options',
187 167 'properties' => [
188 168 'php_ready_args' => [
189 169 'type' => 'boolean',
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").'
170 + 'description' => 'If false, arguments will not be formatted for PHP (no $ before names)'
191 171 ]
192 172 ]
193 173 ]
194 174 ],
@@ -205,20 +185,20 @@
205 185 'type' => 'object',
206 186 'properties' => [
207 187 'safe' => [
208 188 'type' => 'boolean',
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.',
189 + 'description' => 'Whether to filter out snippets with invalid names',
210 190 'default' => true
211 191 ],
212 192 'scope' => [
213 193 'type' => 'string',
214 - 'description' => 'Optional scope filter. ' . self::SCOPE_DESC,
215 - 'enum' => self::SCOPES
194 + 'description' => 'Optional scope filter',
195 + 'enum' => ['function', 'backend', 'frontend', 'scheduled', 'persistent']
216 196 ]
217 197 ]
218 198 ]
219 199 ];
220 -
200 +
221 201 // Execute Snippet
222 202 $tools[] = [
223 203 'name' => 'mwcode_execute_snippet',
224 204 'description' => 'Execute a Code Engine snippet by its ID',
@@ -279,10 +259,10 @@
279 259 'description' => 'Code of the snippet'
280 260 ],
281 261 'scope' => [
282 262 'type' => 'string',
283 - 'description' => 'Scope of the snippet. ' . self::SCOPE_DESC . ' Defaults to "function".',
284 - 'enum' => self::SCOPES,
263 + 'description' => 'Scope of the snippet',
264 + 'enum' => ['function', 'backend', 'frontend', 'scheduled', 'persistent'],
285 265 'default' => 'function'
286 266 ],
287 267 'options' => [
288 268 'type' => 'object',
@@ -379,10 +359,9 @@
379 359 'name' => [ 'type' => 'string' ],
380 360 'code' => [ 'type' => 'string' ],
381 361 'scope' => [
382 362 'type' => 'string',
383 - 'description' => self::SCOPE_DESC,
384 - 'enum' => self::SCOPES
363 + 'enum' => ['function', 'backend', 'frontend', 'scheduled', 'persistent']
385 364 ],
386 365 'description' => [ 'type' => 'string' ],
387 366 'active' => [ 'type' => 'boolean' ],
388 367 'priority' => [ 'type' => 'integer' ],
@@ -501,10 +480,10 @@
501 480 'type' => 'object',
502 481 'properties' => [
503 482 'scope' => [
504 483 'type' => 'string',
505 - 'description' => 'The scope to filter by. ' . self::SCOPE_DESC,
506 - 'enum' => self::SCOPES
484 + 'description' => 'The scope to filter by',
485 + 'enum' => ['function', 'backend', 'frontend', 'scheduled', 'persistent']
507 486 ],
508 487 'filters' => [
509 488 'type' => 'object',
510 489 'description' => 'Additional filters',
@@ -534,15 +513,15 @@
534 513 'type' => 'object',
535 514 'properties' => [
536 515 'scope' => [
537 516 'type' => 'string',
538 - 'description' => 'Optional scope filter. ' . self::SCOPE_DESC,
539 - 'enum' => self::SCOPES
517 + 'description' => 'Optional scope filter',
518 + 'enum' => ['function', 'backend', 'frontend', 'scheduled', 'persistent']
540 519 ]
541 520 ]
542 521 ]
543 522 ];
544 -
523 +
545 524 // Snippet Exists
546 525 $tools[] = [
547 526 'name' => 'mwcode_snippet_exists',
548 527 'description' => 'Check if a Code Engine snippet exists by ID',
@@ -607,15 +586,8 @@
607 586 * tool name does not match one of our opted-in functions, so the filter chain
608 587 * continues to the management tools (and other plugins).
609 588 */
610 589 private function handle_function_execution( $result, $tool, $args ) {
611 - // Master switch: even an opted-in Callable is unreachable via MCP unless the
612 - // site has explicitly enabled function exposure. Returning $result unchanged
613 - // lets the filter chain fall through to the management tools and other plugins.
614 - if ( !$this->core->get_option( 'mcp_functions', false ) ) {
615 - return $result;
616 - }
617 -
618 590 $match = null;
619 591 foreach ( $this->get_mcp_functions() as $fn ) {
620 592 if ( $fn['functionName'] === $tool ) {
621 593 $match = $fn;
@@ -636,12 +608,9 @@
636 608 try {
637 609 $output = $this->api->executeSnippet( $snippetId, is_array( $args ) ? $args : [] );
638 610 return [ 'success' => true, 'data' => $output ];
639 611 }
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 ) {
612 + catch ( Exception $e ) {
644 613 return [ 'success' => false, 'error' => $e->getMessage() ];
645 614 }
646 615 }
647 616
@@ -738,12 +707,9 @@
738 707 $validation = $this->api->validateSnippetCode( $args['code'], $args['target'] ?? 'php' );
739 708 return [ 'success' => true, 'data' => $validation ];
740 709 }
741 710 }
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 ) {
711 + catch ( Exception $e ) {
746 712 return [ 'success' => false, 'error' => $e->getMessage() ];
747 713 }
748 714
749 715 return $result;