register_tools(); } // ── Admin ────────────────────────────────────────────────────────────── if ( is_admin() ) { new AICOM_Admin(); } // ── Admin Bar (fires on both admin and frontend for logged-in admins) ── add_action( 'admin_bar_menu', [ 'AICOM_Admin', 'register_admin_bar' ], 100 ); add_action( 'wp_ajax_aicom_toolbar_toggle', [ 'AICOM_Admin', 'ajax_toolbar_toggle' ] ); add_action( 'wp_ajax_aicom_toolbar_lock', [ 'AICOM_Admin', 'ajax_toolbar_lock' ] ); add_action( 'wp_ajax_aicom_save_preset', [ 'AICOM_Admin', 'ajax_save_preset' ] ); add_action( 'wp_ajax_aicom_delete_preset', [ 'AICOM_Admin', 'ajax_delete_preset' ] ); add_action( 'wp_ajax_aicom_rename_preset', [ 'AICOM_Admin', 'ajax_rename_preset' ] ); add_action( 'wp_ajax_aicom_duplicate_preset', [ 'AICOM_Admin', 'ajax_duplicate_preset' ] ); // Enqueue toolbar JS/CSS on frontend too (admin_enqueue_scripts only fires in /wp-admin) add_action( 'wp_enqueue_scripts', function (): void { if ( ! is_admin_bar_showing() || ! current_user_can( 'manage_options' ) ) { return; } wp_enqueue_style( 'aicom-admin', AICOM_URL . 'assets/admin.css', [], AICOM_VERSION ); wp_enqueue_script( 'aicom-admin', AICOM_URL . 'assets/admin.js', [ 'jquery' ], AICOM_VERSION, true ); wp_localize_script( 'aicom-admin', 'AICOM_MCP', [ 'ajaxUrl' => admin_url( 'admin-ajax.php' ), ] ); } ); // ── REST Endpoints ───────────────────────────────────────────────────── add_action( 'rest_api_init', function (): void { // Main MCP endpoint register_rest_route( 'aicom/v1', '/mcp', [ 'methods' => [ 'POST', 'GET' ], 'callback' => 'aicom_rest_handler', // permission_callback is intentionally open — authentication and authorization // are fully handled inside AICOM_Tool_Router::dispatch() via API key validation, // scope checks, and lock-state enforcement before any operation is executed. 'permission_callback' => '__return_true', ] ); // OpenAPI schema — for ChatGPT Custom GPT Actions import register_rest_route( 'aicom/v1', '/schema', [ 'methods' => 'GET', 'callback' => 'aicom_schema_handler', 'permission_callback' => '__return_true', ] ); // Individual tool endpoints — REST wrapper around the MCP dispatcher register_rest_route( 'aicom/v1', '/tools/(?P[a-zA-Z0-9._-]+)', [ 'methods' => 'POST', 'callback' => 'aicom_tool_handler', 'permission_callback' => '__return_true', 'args' => [ 'tool' => [ 'required' => true, 'sanitize_callback' => 'sanitize_text_field' ], ], ] ); // Hub ↔ Local management channel (PRD §16) — /pair and /management. AICOM_Hub_Channel::register_routes(); } ); // ── Fallback Endpoint (/index.php?aicom=1) ───────────────────────── add_action( 'init', function (): void { if ( ! empty( $_GET['aicom'] ) && ( $_GET['aicom'] === '1' ) ) { if ( isset( $_SERVER['REQUEST_METHOD'] ) && $_SERVER['REQUEST_METHOD'] === 'GET' ) { // Health check wp_send_json( [ 'ok' => true, 'server' => 'AICOM - AI Commander', 'version' => AICOM_VERSION ] ); } $body = file_get_contents( 'php://input' ); $result = AICOM_Tool_Router::dispatch( $body ?: '{}' ); wp_send_json( $result ); } } ); } // ── REST Handlers ────────────────────────────────────────────────────────── function aicom_rest_handler( WP_REST_Request $request ): WP_REST_Response { // GET = health/status endpoint if ( $request->get_method() === 'GET' ) { return new WP_REST_Response( [ 'ok' => true, 'server' => 'AICOM - AI Commander', 'version' => AICOM_VERSION, 'lock' => AICOM_Lock_Manager::get_state(), ] ); } // POST = MCP dispatch $body = $request->get_body(); $result = AICOM_Tool_Router::dispatch( $body ?: '{}' ); $http_status = isset( $result['error'] ) ? ( $result['error']['http_status'] ?? 400 ) : 200; unset( $result['error']['http_status'] ); return new WP_REST_Response( $result, 200 ); // Always HTTP 200; errors encoded in body per MCP convention } function aicom_schema_handler( WP_REST_Request $request ): WP_REST_Response { $schema = AICOM_Schema_Generator::generate(); $response = new WP_REST_Response( $schema, 200 ); $response->header( 'Content-Type', 'application/json' ); return $response; } function aicom_tool_handler( WP_REST_Request $request ): WP_REST_Response { $tool = $request->get_param( 'tool' ); $args = $request->get_json_params() ?: []; $body = wp_json_encode( [ 'jsonrpc' => '2.0', 'method' => 'tools/call', 'params' => [ 'name' => $tool, 'arguments' => $args ], 'id' => 1, ] ); $result = AICOM_Tool_Router::dispatch( $body ); return new WP_REST_Response( $result, 200 ); }