PluginProbe
Code Engine – PHP Snippets, AI Functions & Automation for WordPress / 0.4.3
Code Engine – PHP Snippets, AI Functions & Automation for WordPress v0.4.3
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 +10 -158 0.5.00.4.3 View file →
@@ -13,116 +13,20 @@
13 13
14 14 public function init() {
15 15 global $mwcode, $mwai;
16 16 $this->api = $mwcode;
17 -
18 - // Nothing to do without AI Engine's MCP server.
19 - if ( !isset( $mwai ) ) {
20 - return;
17 +
18 + // Only register MCP if enabled AND AI Engine is available
19 + if ( $this->core->get_option( 'mcp_support', false ) && isset( $mwai ) ) {
20 + // Register MCP tools
21 + add_filter( 'mwai_mcp_tools', array( $this, 'register_tools' ) );
22 +
23 + // Handle MCP tool execution
24 + add_filter( 'mwai_mcp_callback', array( $this, 'handle_tool_execution' ), 10, 4 );
21 25 }
22 -
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)
26 - // Either one alone is enough to justify hooking the filters.
27 - add_filter( 'mwai_mcp_tools', array( $this, 'register_tools' ) );
28 - add_filter( 'mwai_mcp_callback', array( $this, 'handle_tool_execution' ), 10, 4 );
29 26 }
30 -
27 +
31 28 public function register_tools( $tools ) {
32 - // Individual Callable functions that opted in to MCP, exposed as first-class tools.
33 - $tools = $this->register_function_tools( $tools );
34 -
35 - // Code Engine's management/internal API (Settings > For Developers > MCP Support).
36 - if ( $this->core->get_option( 'mcp_support', false ) ) {
37 - $tools = $this->register_management_tools( $tools );
38 - }
39 -
40 - return $tools;
41 - }
42 -
43 - /**
44 - * Map a Code Engine argument type to a JSON Schema type.
45 - * Returns null for 'mixed'/unknown so the schema leaves the type open.
46 - */
47 - private function mcp_type( $type ) {
48 - switch ( $type ) {
49 - case 'number': return 'number';
50 - case 'boolean': return 'boolean';
51 - case 'array': return 'array';
52 - case 'object': return 'object';
53 - case 'string': return 'string';
54 - default: return null;
55 - }
56 - }
57 -
58 - /**
59 - * Return the active Callable (function) snippets that opted in to MCP exposure.
60 - */
61 - private function get_mcp_functions() {
62 - global $mwcode;
63 - if ( !isset( $mwcode ) || !method_exists( $mwcode, 'getSnippets' ) ) {
64 - return [];
65 - }
66 - $functions = $mwcode->getSnippets( true, 'function' );
67 - if ( empty( $functions ) ) {
68 - return [];
69 - }
70 - return array_values( array_filter( $functions, function ( $fn ) {
71 - return !empty( $fn['functionMcp'] ) && !empty( $fn['functionName'] );
72 - } ) );
73 - }
74 -
75 - /**
76 - * Register each opted-in Callable function as its own MCP tool, named after the
77 - * function, with an input schema derived from its declared arguments.
78 - */
79 - public function register_function_tools( $tools ) {
80 - foreach ( $this->get_mcp_functions() as $fn ) {
81 - $properties = [];
82 - $required = [];
83 -
84 - $argsDict = isset( $fn['functionArgsDict'] ) && is_array( $fn['functionArgsDict'] ) ? $fn['functionArgsDict'] : [];
85 - foreach ( $argsDict as $argName => $arg ) {
86 - $name = ltrim( $argName, '$' );
87 - if ( $name === '' ) {
88 - continue;
89 - }
90 - $prop = [];
91 - $type = $this->mcp_type( $arg['type'] ?? 'string' );
92 - if ( $type !== null ) {
93 - $prop['type'] = $type;
94 - }
95 - if ( !empty( $arg['desc'] ) ) {
96 - $prop['description'] = $arg['desc'];
97 - }
98 - $properties[ $name ] = $prop;
99 - if ( !empty( $arg['required'] ) ) {
100 - $required[] = $name;
101 - }
102 - }
103 -
104 - $schema = [
105 - 'type' => 'object',
106 - // Cast so an argument-less function still serializes as {} and not [].
107 - 'properties' => (object) $properties,
108 - ];
109 - if ( !empty( $required ) ) {
110 - $schema['required'] = $required;
111 - }
112 -
113 - $tools[] = [
114 - 'name' => $fn['functionName'],
115 - 'description' => !empty( $fn['description'] ) ? $fn['description'] : ( 'Code Engine function: ' . $fn['functionName'] ),
116 - 'category' => 'Code Engine (Functions)',
117 - 'inputSchema' => $schema,
118 - 'annotations' => [ 'openWorldHint' => true ],
119 - ];
120 - }
121 - return $tools;
122 - }
123 -
124 - public function register_management_tools( $tools ) {
125 29 // Get Snippet
126 30 $tools[] = [
127 31 'name' => 'mwcode_get_snippet',
128 32 'description' => 'Get a Code Engine snippet by its ID',
@@ -298,12 +202,8 @@
298 202 'type' => 'string',
299 203 'enum' => ['dynamic', 'static'],
300 204 'description' => 'Behavior for function snippets'
301 205 ],
302 - 'mcp' => [
303 - 'type' => 'boolean',
304 - 'description' => 'For function snippets: expose this function as its own MCP tool in AI Engine, named after the function and callable directly by external agents. Defaults to false.'
305 - ],
306 206 'tags' => [
307 207 'type' => 'array',
308 208 'description' => 'Array of tags',
309 209 'items' => [ 'type' => 'string' ]
@@ -389,12 +289,8 @@
389 289 'behavior' => [
390 290 'type' => 'string',
391 291 'enum' => ['dynamic', 'static']
392 292 ],
393 - 'mcp' => [
394 - 'type' => 'boolean',
395 - 'description' => 'For function snippets: expose this function as its own MCP tool in AI Engine, named after the function and callable directly by external agents.'
396 - ],
397 293 'intervalHours' => [ 'type' => 'integer' ],
398 294 'intervalMinutes' => [ 'type' => 'integer' ]
399 295 ]
400 296 ]
@@ -580,58 +476,14 @@
580 476
581 477 return $tools;
582 478 }
583 479
584 - /**
585 - * Execute a Callable function exposed via MCP. Returns $result unchanged when the
586 - * tool name does not match one of our opted-in functions, so the filter chain
587 - * continues to the management tools (and other plugins).
588 - */
589 - private function handle_function_execution( $result, $tool, $args ) {
590 - $match = null;
591 - foreach ( $this->get_mcp_functions() as $fn ) {
592 - if ( $fn['functionName'] === $tool ) {
593 - $match = $fn;
594 - break;
595 - }
596 - }
597 - if ( $match === null ) {
598 - return $result;
599 - }
600 -
601 - if ( !$this->api ) {
602 - return [ 'success' => false, 'error' => 'Code Engine API not initialized' ];
603 - }
604 -
605 - // getSnippets() rows expose the database id as 'id' (function metadata uses 'snippetId').
606 - $snippetId = $match['id'] ?? $match['snippetId'] ?? null;
607 -
608 - try {
609 - $output = $this->api->executeSnippet( $snippetId, is_array( $args ) ? $args : [] );
610 - return [ 'success' => true, 'data' => $output ];
611 - }
612 - catch ( Exception $e ) {
613 - return [ 'success' => false, 'error' => $e->getMessage() ];
614 - }
615 - }
616 -
617 480 public function handle_tool_execution( $result, $tool, $args, $id ) {
618 - // Individual Callable functions exposed via MCP take priority (named after the function).
619 - $handled = $this->handle_function_execution( $result, $tool, $args );
620 - if ( $handled !== $result ) {
621 - return $handled;
622 - }
623 -
624 - // Management/internal API tools are gated behind the master switch.
625 - if ( !$this->core->get_option( 'mcp_support', false ) ) {
626 - return $result;
627 - }
628 -
629 481 // Only handle our tools
630 482 if ( strpos( $tool, 'mwcode_' ) !== 0 ) {
631 483 return $result;
632 484 }
633 -
485 +
634 486 // Ensure API is initialized
635 487 if ( !$this->api ) {
636 488 return [ 'success' => false, 'error' => 'Code Engine API not initialized' ];
637 489 }