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