# code-engine/trunk/classes/mcp.php

Code Engine – PHP Snippets, AI Functions &amp; Automation for WordPress, version trunk. 751 lines.

- Page: https://pluginprobe.com/plugins/code-engine/trunk/code/classes/mcp.php
- Raw: https://pluginprobe.com/plugins/code-engine/trunk/raw/classes/mcp.php
- Modified: 2026-07-06T11:32:28+00:00

Line numbers below start at 1. Link to a line or a range by appending a fragment to the
page URL, for example `https://pluginprobe.com/plugins/code-engine/trunk/code/classes/mcp.php#L10-L20`.

```php
<?php

class Meow_MWCODE_MCP {
  private $core;
  private $api;
  // Per-request memo for the opted-in Callable list, so listing tools and executing
  // one don't each reload every snippet from the database. Reset on any mutation.
  private $mcp_functions_cache = null;

  // Shared, model-facing explanation of what each scope means. Without this an agent
  // sees a bare enum and has to guess which scope to pick.
  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.";

  // Scope enums that appear across every management tool. Kept in one place so the
  // list can never drift between tools (all seven scopes the API actually accepts).
  const SCOPES = ['function', 'backend', 'frontend', 'scheduled', 'persistent', 'content_php', 'content_js'];

  public function __construct( $core ) {
    $this->core = $core;
    
    // Initialize everything on 'init' to ensure options are loaded
    add_action( 'init', array( $this, 'init' ), 20 );
  }
  
  public function init() {
    global $mwcode, $mwai;
    $this->api = $mwcode;

    // Nothing to do without AI Engine's MCP server.
    if ( !isset( $mwai ) ) {
      return;
    }

    // Two independent surfaces share AI Engine's MCP server, each behind its own
    // global master switch (and AI Engine's own MCP auth gate upstream):
    //  - the management/internal API (the 'mcp_support' option)
    //  - individual Callable functions, opted-in per snippet via 'functionMcp' and
    //    only exposed when the 'mcp_functions' option is enabled
    // Either one alone is enough to justify hooking the filters.
    add_filter( 'mwai_mcp_tools', array( $this, 'register_tools' ) );
    add_filter( 'mwai_mcp_callback', array( $this, 'handle_tool_execution' ), 10, 4 );
  }

  public function register_tools( $tools ) {
    // Individual Callable functions that opted in to MCP, exposed as first-class tools.
    // Gated behind a global master switch (Settings > For Developers > MCP Functions)
    // in addition to each snippet's per-function opt-in.
    if ( $this->core->get_option( 'mcp_functions', false ) ) {
      $tools = $this->register_function_tools( $tools );
    }

    // Code Engine's management/internal API (Settings > For Developers > MCP Support).
    if ( $this->core->get_option( 'mcp_support', false ) ) {
      $tools = $this->register_management_tools( $tools );
    }

    return $tools;
  }

  /**
   * Map a Code Engine argument type to a JSON Schema type.
   * Returns null for 'mixed'/unknown so the schema leaves the type open.
   */
  private function mcp_type( $type ) {
    switch ( $type ) {
      case 'number':  return 'number';
      case 'boolean': return 'boolean';
      case 'array':   return 'array';
      case 'object':  return 'object';
      case 'string':  return 'string';
      default:        return null;
    }
  }

  /**
   * Return the active Callable (function) snippets that opted in to MCP exposure.
   */
  private function get_mcp_functions() {
    if ( $this->mcp_functions_cache !== null ) {
      return $this->mcp_functions_cache;
    }
    global $mwcode;
    if ( !isset( $mwcode ) || !method_exists( $mwcode, 'getSnippets' ) ) {
      return ( $this->mcp_functions_cache = [] );
    }
    $functions = $mwcode->getSnippets( true, 'function' );
    if ( empty( $functions ) ) {
      return ( $this->mcp_functions_cache = [] );
    }
    return ( $this->mcp_functions_cache = array_values( array_filter( $functions, function ( $fn ) {
      return !empty( $fn['functionMcp'] ) && !empty( $fn['functionName'] );
    } ) ) );
  }

  /**
   * Register each opted-in Callable function as its own MCP tool, named after the
   * function, with an input schema derived from its declared arguments.
   */
  public function register_function_tools( $tools ) {
    foreach ( $this->get_mcp_functions() as $fn ) {
      $properties = [];
      $required = [];

      $argsDict = isset( $fn['functionArgsDict'] ) && is_array( $fn['functionArgsDict'] ) ? $fn['functionArgsDict'] : [];
      foreach ( $argsDict as $argName => $arg ) {
        $name = ltrim( $argName, '$' );
        if ( $name === '' ) {
          continue;
        }
        $prop = [];
        $type = $this->mcp_type( $arg['type'] ?? 'string' );
        if ( $type !== null ) {
          $prop['type'] = $type;
        }
        if ( !empty( $arg['desc'] ) ) {
          $prop['description'] = $arg['desc'];
        }
        $properties[ $name ] = $prop;
        if ( !empty( $arg['required'] ) ) {
          $required[] = $name;
        }
      }

      $schema = [
        'type' => 'object',
        // Cast so an argument-less function still serializes as {} and not [].
        'properties' => (object) $properties,
      ];
      if ( !empty( $required ) ) {
        $schema['required'] = $required;
      }

      $tools[] = [
        'name' => $fn['functionName'],
        'description' => !empty( $fn['description'] ) ? $fn['description'] : ( 'Code Engine function: ' . $fn['functionName'] ),
        'category' => 'Code Engine (Functions)',
        'inputSchema' => $schema,
        'annotations' => [ 'openWorldHint' => true ],
      ];
    }
    return $tools;
  }

  public function register_management_tools( $tools ) {
    // Get Snippet
    $tools[] = [
      'name' => 'mwcode_get_snippet',
      'description' => 'Get a Code Engine snippet by its ID',
      'category' => 'Code Engine',
      'inputSchema' => [
        'type' => 'object',
        'properties' => [
          'id' => [
            'type' => 'integer',
            'description' => 'The snippet ID'
          ],
          'options' => [
            'type' => 'object',
            'description' => 'Optional filtering options',
            'properties' => [
              'php_ready_args' => [
                'type' => 'boolean',
                '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").'
              ]
            ]
          ]
        ],
        'required' => ['id']
      ]
    ];
    
    // Get Snippet by Name
    $tools[] = [
      'name' => 'mwcode_get_snippet_by_name',
      'description' => 'Get a Code Engine snippet by its name',
      'category' => 'Code Engine',
      'inputSchema' => [
        'type' => 'object',
        'properties' => [
          'name' => [
            'type' => 'string',
            'description' => 'The snippet name'
          ],
          'options' => [
            'type' => 'object',
            'description' => 'Optional filtering options',
            'properties' => [
              'php_ready_args' => [
                'type' => 'boolean',
                '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").'
              ]
            ]
          ]
        ],
        'required' => ['name']
      ]
    ];
    
    // Get Snippets
    $tools[] = [
      'name' => 'mwcode_get_snippets',
      'description' => 'Get all Code Engine snippets, optionally filtered by scope',
      'category' => 'Code Engine',
      'inputSchema' => [
        'type' => 'object',
        'properties' => [
          'safe' => [
            'type' => 'boolean',
            'description' => 'When true (default), skip function snippets whose function name is empty or invalid. Leave true unless you specifically need to inspect malformed snippets.',
            'default' => true
          ],
          'scope' => [
            'type' => 'string',
            'description' => 'Optional scope filter. ' . self::SCOPE_DESC,
            'enum' => self::SCOPES
          ]
        ]
      ]
    ];

    // Execute Snippet
    $tools[] = [
      'name' => 'mwcode_execute_snippet',
      'description' => 'Execute a Code Engine snippet by its ID',
      'category' => 'Code Engine',
      'inputSchema' => [
        'type' => 'object',
        'properties' => [
          'id' => [
            'type' => 'integer',
            'description' => 'The snippet ID'
          ],
          'args' => [
            'type' => 'object',
            'description' => 'Arguments to pass to the snippet (key-value pairs)',
            'additionalProperties' => true
          ]
        ],
        'required' => ['id']
      ]
    ];
    
    // Execute Snippet by Name
    $tools[] = [
      'name' => 'mwcode_execute_snippet_by_name',
      'description' => 'Execute a Code Engine snippet by its name',
      'category' => 'Code Engine',
      'inputSchema' => [
        'type' => 'object',
        'properties' => [
          'name' => [
            'type' => 'string',
            'description' => 'The snippet name'
          ],
          'args' => [
            'type' => 'object',
            'description' => 'Arguments to pass to the snippet (key-value pairs)',
            'additionalProperties' => true
          ]
        ],
        'required' => ['name']
      ]
    ];
    
    // Create Snippet
    $tools[] = [
      'name' => 'mwcode_create_snippet',
      'description' => 'Create a new Code Engine snippet',
      'category' => 'Code Engine',
      'inputSchema' => [
        'type' => 'object',
        'properties' => [
          'name' => [
            'type' => 'string',
            'description' => 'Name of the snippet'
          ],
          'code' => [
            'type' => 'string',
            'description' => 'Code of the snippet'
          ],
          'scope' => [
            'type' => 'string',
            'description' => 'Scope of the snippet. ' . self::SCOPE_DESC . ' Defaults to "function".',
            'enum' => self::SCOPES,
            'default' => 'function'
          ],
          'options' => [
            'type' => 'object',
            'description' => 'Additional options for the snippet',
            'properties' => [
              'target' => [
                'type' => 'string',
                'enum' => ['php', 'js'],
                'description' => 'Target language (for function snippets)'
              ],
              'description' => [
                'type' => 'string',
                'description' => 'Description of the snippet'
              ],
              'args' => [
                'type' => 'array',
                'description' => 'Arguments for function snippets',
                'items' => [ 'type' => 'string' ]
              ],
              'argsData' => [
                'type' => 'object',
                'description' => 'Argument data for function snippets',
                'additionalProperties' => [
                  'type' => 'object',
                  'properties' => [
                    'type' => [ 'type' => 'string' ],
                    'description' => [ 'type' => 'string' ],
                    'default' => [ 'type' => 'string' ]
                  ]
                ]
              ],
              'behavior' => [
                'type' => 'string',
                'enum' => ['dynamic', 'static'],
                'description' => 'Behavior for function snippets'
              ],
              'mcp' => [
                'type' => 'boolean',
                '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.'
              ],
              'tags' => [
                'type' => 'array',
                'description' => 'Array of tags',
                'items' => [ 'type' => 'string' ]
              ],
              'priority' => [
                'type' => 'integer',
                'description' => 'Execution priority'
              ],
              'active' => [
                'type' => 'boolean',
                'description' => 'Active status'
              ],
              'endpoint' => [
                'type' => 'string',
                'description' => 'REST endpoint'
              ],
              'method' => [
                'type' => 'string',
                'enum' => ['GET', 'POST'],
                'description' => 'HTTP method'
              ],
              'intervalHours' => [
                'type' => 'integer',
                'description' => 'Hours for scheduled snippets'
              ],
              'intervalMinutes' => [
                'type' => 'integer',
                'description' => 'Minutes for scheduled snippets'
              ]
            ]
          ]
        ],
        'required' => ['name', 'code']
      ]
    ];
    
    // Update Snippet
    $tools[] = [
      'name' => 'mwcode_update_snippet',
      'description' => 'Update an existing Code Engine snippet',
      'category' => 'Code Engine',
      'inputSchema' => [
        'type' => 'object',
        'properties' => [
          'id' => [
            'type' => 'integer',
            'description' => 'ID of the snippet to update'
          ],
          'params' => [
            'type' => 'object',
            'description' => 'Parameters to update',
            'properties' => [
              'name' => [ 'type' => 'string' ],
              'code' => [ 'type' => 'string' ],
              'scope' => [
                'type' => 'string',
                'description' => self::SCOPE_DESC,
                'enum' => self::SCOPES
              ],
              'description' => [ 'type' => 'string' ],
              'active' => [ 'type' => 'boolean' ],
              'priority' => [ 'type' => 'integer' ],
              'tags' => [
                'type' => 'array',
                'items' => [ 'type' => 'string' ]
              ],
              'endpoint' => [ 'type' => 'string' ],
              'method' => [
                'type' => 'string',
                'enum' => ['GET', 'POST']
              ],
              'target' => [
                'type' => 'string',
                'enum' => ['php', 'js']
              ],
              'args' => [
                'type' => 'array',
                'items' => [ 'type' => 'string' ]
              ],
              'argsData' => [
                'type' => 'object',
                'additionalProperties' => true
              ],
              'behavior' => [
                'type' => 'string',
                'enum' => ['dynamic', 'static']
              ],
              'mcp' => [
                'type' => 'boolean',
                '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.'
              ],
              'intervalHours' => [ 'type' => 'integer' ],
              'intervalMinutes' => [ 'type' => 'integer' ]
            ]
          ]
        ],
        'required' => ['id']
      ]
    ];
    
    // Delete Snippet
    $tools[] = [
      'name' => 'mwcode_delete_snippet',
      'description' => 'Delete a Code Engine snippet by its ID',
      'category' => 'Code Engine',
      'inputSchema' => [
        'type' => 'object',
        'properties' => [
          'id' => [
            'type' => 'integer',
            'description' => 'The snippet ID'
          ]
        ],
        'required' => ['id']
      ]
    ];
    
    // Delete Snippet by Name
    $tools[] = [
      'name' => 'mwcode_delete_snippet_by_name',
      'description' => 'Delete a Code Engine snippet by its name',
      'category' => 'Code Engine',
      'inputSchema' => [
        'type' => 'object',
        'properties' => [
          'name' => [
            'type' => 'string',
            'description' => 'The snippet name'
          ]
        ],
        'required' => ['name']
      ]
    ];
    
    // Activate Snippet
    $tools[] = [
      'name' => 'mwcode_activate_snippet',
      'description' => 'Activate a Code Engine snippet',
      'category' => 'Code Engine',
      'inputSchema' => [
        'type' => 'object',
        'properties' => [
          'id' => [
            'type' => 'integer',
            'description' => 'The snippet ID'
          ]
        ],
        'required' => ['id']
      ]
    ];
    
    // Deactivate Snippet
    $tools[] = [
      'name' => 'mwcode_deactivate_snippet',
      'description' => 'Deactivate a Code Engine snippet',
      'category' => 'Code Engine',
      'inputSchema' => [
        'type' => 'object',
        'properties' => [
          'id' => [
            'type' => 'integer',
            'description' => 'The snippet ID'
          ]
        ],
        'required' => ['id']
      ]
    ];
    
    // Get Snippets by Scope
    $tools[] = [
      'name' => 'mwcode_get_snippets_by_scope',
      'description' => 'Get Code Engine snippets filtered by scope',
      'category' => 'Code Engine',
      'inputSchema' => [
        'type' => 'object',
        'properties' => [
          'scope' => [
            'type' => 'string',
            'description' => 'The scope to filter by. ' . self::SCOPE_DESC,
            'enum' => self::SCOPES
          ],
          'filters' => [
            'type' => 'object',
            'description' => 'Additional filters',
            'properties' => [
              'active' => [
                'type' => 'boolean',
                'description' => 'Filter by active status'
              ],
              'tags' => [
                'type' => 'array',
                'description' => 'Filter by tags',
                'items' => [ 'type' => 'string' ]
              ]
            ]
          ]
        ],
        'required' => ['scope']
      ]
    ];
    
    // Get Active Snippets
    $tools[] = [
      'name' => 'mwcode_get_active_snippets',
      'description' => 'Get all active Code Engine snippets',
      'category' => 'Code Engine',
      'inputSchema' => [
        'type' => 'object',
        'properties' => [
          'scope' => [
            'type' => 'string',
            'description' => 'Optional scope filter. ' . self::SCOPE_DESC,
            'enum' => self::SCOPES
          ]
        ]
      ]
    ];

    // Snippet Exists
    $tools[] = [
      'name' => 'mwcode_snippet_exists',
      'description' => 'Check if a Code Engine snippet exists by ID',
      'category' => 'Code Engine',
      'inputSchema' => [
        'type' => 'object',
        'properties' => [
          'id' => [
            'type' => 'integer',
            'description' => 'The snippet ID'
          ]
        ],
        'required' => ['id']
      ]
    ];
    
    // Snippet Exists by Name
    $tools[] = [
      'name' => 'mwcode_snippet_exists_by_name',
      'description' => 'Check if a Code Engine snippet exists by name',
      'category' => 'Code Engine',
      'inputSchema' => [
        'type' => 'object',
        'properties' => [
          'name' => [
            'type' => 'string',
            'description' => 'The snippet name'
          ]
        ],
        'required' => ['name']
      ]
    ];
    
    // Validate Snippet Code
    $tools[] = [
      'name' => 'mwcode_validate_snippet_code',
      'description' => 'Validate snippet code syntax',
      'category' => 'Code Engine',
      'inputSchema' => [
        'type' => 'object',
        'properties' => [
          'code' => [
            'type' => 'string',
            'description' => 'The snippet code to validate'
          ],
          'target' => [
            'type' => 'string',
            'description' => 'The target language',
            'enum' => ['php', 'js'],
            'default' => 'php'
          ]
        ],
        'required' => ['code']
      ]
    ];
    
    return $tools;
  }
  
  /**
   * Execute a Callable function exposed via MCP. Returns $result unchanged when the
   * tool name does not match one of our opted-in functions, so the filter chain
   * continues to the management tools (and other plugins).
   */
  private function handle_function_execution( $result, $tool, $args ) {
    // Master switch: even an opted-in Callable is unreachable via MCP unless the
    // site has explicitly enabled function exposure. Returning $result unchanged
    // lets the filter chain fall through to the management tools and other plugins.
    if ( !$this->core->get_option( 'mcp_functions', false ) ) {
      return $result;
    }

    $match = null;
    foreach ( $this->get_mcp_functions() as $fn ) {
      if ( $fn['functionName'] === $tool ) {
        $match = $fn;
        break;
      }
    }
    if ( $match === null ) {
      return $result;
    }

    if ( !$this->api ) {
      return [ 'success' => false, 'error' => 'Code Engine API not initialized' ];
    }

    // getSnippets() rows expose the database id as 'id' (function metadata uses 'snippetId').
    $snippetId = $match['id'] ?? $match['snippetId'] ?? null;

    try {
      $output = $this->api->executeSnippet( $snippetId, is_array( $args ) ? $args : [] );
      return [ 'success' => true, 'data' => $output ];
    }
    // Snippet code is arbitrary PHP: a fatal surfaces as Error/TypeError/ParseError,
    // none of which are Exceptions. Catch Throwable so a bad snippet can never take
    // down the MCP request.
    catch ( \Throwable $e ) {
      return [ 'success' => false, 'error' => $e->getMessage() ];
    }
  }

  public function handle_tool_execution( $result, $tool, $args, $id ) {
    // Individual Callable functions exposed via MCP take priority (named after the function).
    $handled = $this->handle_function_execution( $result, $tool, $args );
    if ( $handled !== $result ) {
      return $handled;
    }

    // Management/internal API tools are gated behind the master switch.
    if ( !$this->core->get_option( 'mcp_support', false ) ) {
      return $result;
    }

    // Only handle our tools
    if ( strpos( $tool, 'mwcode_' ) !== 0 ) {
      return $result;
    }

    // Ensure API is initialized
    if ( !$this->api ) {
      return [ 'success' => false, 'error' => 'Code Engine API not initialized' ];
    }
    
    try {
      switch ( $tool ) {
        case 'mwcode_get_snippet':
          $data = $this->api->getSnippet( $args['id'], $args['options'] ?? [] );
          return [ 'success' => true, 'data' => $data ];
          
        case 'mwcode_get_snippet_by_name':
          $data = $this->api->getSnippetByName( $args['name'], $args['options'] ?? [] );
          return [ 'success' => true, 'data' => $data ];
          
        case 'mwcode_get_snippets':
          $data = $this->api->getSnippets( $args['safe'] ?? true, $args['scope'] ?? null );
          return [ 'success' => true, 'data' => $data ];
          
        case 'mwcode_execute_snippet':
          $data = $this->api->executeSnippet( $args['id'], $args['args'] ?? [] );
          return [ 'success' => true, 'data' => $data ];
          
        case 'mwcode_execute_snippet_by_name':
          $data = $this->api->executeSnippetByName( $args['name'], $args['args'] ?? [] );
          return [ 'success' => true, 'data' => $data ];
          
        case 'mwcode_create_snippet':
          $data = $this->api->createSnippet( 
            $args['name'], 
            $args['code'], 
            $args['scope'] ?? 'function', 
            $args['options'] ?? [] 
          );
          return [ 'success' => true, 'data' => $data ];
          
        case 'mwcode_update_snippet':
          $data = $this->api->updateSnippet( $args['id'], $args['params'] ?? [] );
          return [ 'success' => true, 'data' => $data ];
          
        case 'mwcode_delete_snippet':
          $success = $this->api->deleteSnippet( $args['id'] );
          return [ 'success' => true, 'data' => [ 'deleted' => $success ] ];
          
        case 'mwcode_delete_snippet_by_name':
          $success = $this->api->deleteSnippetByName( $args['name'] );
          return [ 'success' => true, 'data' => [ 'deleted' => $success ] ];
          
        case 'mwcode_activate_snippet':
          $success = $this->api->activateSnippet( $args['id'] );
          return [ 'success' => true, 'data' => [ 'activated' => $success ] ];
          
        case 'mwcode_deactivate_snippet':
          $success = $this->api->deactivateSnippet( $args['id'] );
          return [ 'success' => true, 'data' => [ 'deactivated' => $success ] ];
          
        case 'mwcode_get_snippets_by_scope':
          $data = $this->api->getSnippetsByScope( $args['scope'], $args['filters'] ?? [] );
          return [ 'success' => true, 'data' => $data ];
          
        case 'mwcode_get_active_snippets':
          $data = $this->api->getActiveSnippets( $args['scope'] ?? null );
          return [ 'success' => true, 'data' => $data ];
          
        case 'mwcode_snippet_exists':
          $exists = $this->api->snippetExists( $args['id'] );
          return [ 'success' => true, 'data' => [ 'exists' => $exists ] ];
          
        case 'mwcode_snippet_exists_by_name':
          $exists = $this->api->snippetExistsByName( $args['name'] );
          return [ 'success' => true, 'data' => [ 'exists' => $exists ] ];
          
        case 'mwcode_validate_snippet_code':
          $validation = $this->api->validateSnippetCode( $args['code'], $args['target'] ?? 'php' );
          return [ 'success' => true, 'data' => $validation ];
      }
    }
    // executeSnippet() runs arbitrary snippet PHP, whose fatals are Errors, not
    // Exceptions. Catch Throwable so a broken snippet returns a clean error rather
    // than crashing the MCP request.
    catch ( \Throwable $e ) {
      return [ 'success' => false, 'error' => $e->getMessage() ];
    }
    
    return $result;
  }
}
```
