app_passwords = new ApplicationPasswords(); add_action( 'admin_init', array( $this, 'handle_actions' ), 5 ); add_action( 'admin_init', array( $this, 'register_settings' ) ); add_action( 'admin_menu', array( $this, 'register_menu' ) ); add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_assets' ) ); // Show notice if the MCP adapter package is not installed. if ( ! class_exists( '\WP\MCP\Plugin' ) ) { add_action( 'admin_notices', array( $this, 'render_missing_adapter_notice' ) ); } } // ------------------------------------------------------------------------- // Hooks // ------------------------------------------------------------------------- /** * Process actions (toggle_status) before any HTML output. * * @since 1.0.0 * * @return void */ public function handle_actions() { // phpcs:disable WordPress.Security.NonceVerification if ( ! isset( $_GET['page'] ) || 'acrossai_mcp_manager' !== sanitize_text_field( wp_unslash( $_GET['page'] ) ) ) { return; } $action = isset( $_GET['action'] ) ? sanitize_key( $_GET['action'] ) : ''; // phpcs:enable WordPress.Security.NonceVerification if ( ! in_array( $action, array( 'toggle_status', 'delete', 'create', 'update', 'save_claude_connector' ), true ) ) { return; } if ( ! current_user_can( 'manage_options' ) ) { wp_die( esc_html__( 'You do not have permission to perform this action.', 'acrossai-mcp-manager' ) ); } // ── toggle_status ──────────────────────────────────────────────────────── if ( 'toggle_status' === $action ) { $server_id = isset( $_GET['server'] ) ? absint( $_GET['server'] ) : 0; // phpcs:ignore WordPress.Security.NonceVerification $redirect_to = isset( $_GET['redirect_to'] ) ? sanitize_key( $_GET['redirect_to'] ) : 'list'; // phpcs:ignore WordPress.Security.NonceVerification check_admin_referer( 'acrossai_mcp_toggle_' . $server_id ); if ( $server_id > 0 ) { MCPServerTable::toggle_status( $server_id ); } if ( 'edit' === $redirect_to ) { $active_tab = isset( $_GET['tab'] ) ? sanitize_key( $_GET['tab'] ) : 'overview'; // phpcs:ignore WordPress.Security.NonceVerification wp_safe_redirect( add_query_arg( array( 'page' => 'acrossai_mcp_manager', 'action' => 'edit', 'server' => $server_id, 'tab' => $active_tab, 'updated' => '1', ), admin_url( 'admin.php' ) ) ); } else { wp_safe_redirect( add_query_arg( array( 'page' => 'acrossai_mcp_manager', 'updated' => '1', ), admin_url( 'admin.php' ) ) ); } exit; } // ── delete ─────────────────────────────────────────────────────────────── if ( 'delete' === $action ) { $server_id = isset( $_GET['server'] ) ? absint( $_GET['server'] ) : 0; // phpcs:ignore WordPress.Security.NonceVerification check_admin_referer( 'acrossai_mcp_delete_' . $server_id ); if ( $server_id > 0 ) { MCPServerTable::delete_server( $server_id ); } wp_safe_redirect( add_query_arg( array( 'page' => 'acrossai_mcp_manager', 'deleted' => '1', ), admin_url( 'admin.php' ) ) ); exit; } // ── create (POST) ──────────────────────────────────────────────────────── if ( 'create' === $action && isset( $_SERVER['REQUEST_METHOD'] ) && 'post' === sanitize_key( wp_unslash( $_SERVER['REQUEST_METHOD'] ) ) ) { check_admin_referer( 'acrossai_mcp_create_server' ); $name = isset( $_POST['server_name'] ) ? sanitize_text_field( wp_unslash( $_POST['server_name'] ) ) : ''; $description = isset( $_POST['description'] ) ? sanitize_text_field( wp_unslash( $_POST['description'] ) ) : ''; $namespace = isset( $_POST['server_route_namespace'] ) ? sanitize_text_field( wp_unslash( $_POST['server_route_namespace'] ) ) : 'mcp'; $route = isset( $_POST['server_route'] ) ? sanitize_text_field( wp_unslash( $_POST['server_route'] ) ) : ''; $version = isset( $_POST['server_version'] ) ? sanitize_text_field( wp_unslash( $_POST['server_version'] ) ) : 'v1.0.0'; if ( empty( $name ) ) { wp_safe_redirect( add_query_arg( array( 'page' => 'acrossai_mcp_manager', 'action' => 'create', 'error' => 'empty_name', ), admin_url( 'admin.php' ) ) ); exit; } $slug = sanitize_title( $name ); if ( MCPServerTable::slug_exists( $slug ) ) { wp_safe_redirect( add_query_arg( array( 'page' => 'acrossai_mcp_manager', 'action' => 'create', 'error' => 'slug_conflict', 'slug' => rawurlencode( $slug ), ), admin_url( 'admin.php' ) ) ); exit; } // Route defaults to the slug when left blank. if ( '' === $route ) { $route = $slug; } $new_id = MCPServerTable::create_server( $name, $description, $namespace, $route, $version ); if ( ! $new_id ) { wp_safe_redirect( add_query_arg( array( 'page' => 'acrossai_mcp_manager', 'action' => 'create', 'error' => 'db_error', ), admin_url( 'admin.php' ) ) ); exit; } wp_safe_redirect( add_query_arg( array( 'page' => 'acrossai_mcp_manager', 'action' => 'edit', 'server' => $new_id, 'tab' => 'overview', 'created' => '1', ), admin_url( 'admin.php' ) ) ); exit; } // ── update (POST) ──────────────────────────────────────────────────────── if ( 'update' === $action && isset( $_SERVER['REQUEST_METHOD'] ) && 'post' === sanitize_key( wp_unslash( $_SERVER['REQUEST_METHOD'] ) ) ) { $server_id = isset( $_GET['server'] ) ? absint( $_GET['server'] ) : 0; // phpcs:ignore WordPress.Security.NonceVerification check_admin_referer( 'acrossai_mcp_update_' . $server_id ); $server = $server_id ? MCPServerTable::get_by_id( $server_id ) : null; if ( ! $server || 'database' !== $server['registered_from'] ) { wp_die( esc_html__( 'Invalid server or this server cannot be edited.', 'acrossai-mcp-manager' ) ); } $data = array( 'server_name' => isset( $_POST['server_name'] ) ? sanitize_text_field( wp_unslash( $_POST['server_name'] ) ) : '', 'description' => isset( $_POST['description'] ) ? sanitize_textarea_field( wp_unslash( $_POST['description'] ) ) : '', 'server_route_namespace' => isset( $_POST['server_route_namespace'] ) ? sanitize_text_field( wp_unslash( $_POST['server_route_namespace'] ) ) : 'mcp', 'server_route' => isset( $_POST['server_route'] ) ? sanitize_text_field( wp_unslash( $_POST['server_route'] ) ) : '', 'server_version' => isset( $_POST['server_version'] ) ? sanitize_text_field( wp_unslash( $_POST['server_version'] ) ) : 'v1.0.0', ); // Fallback: keep existing route if submitted value is blank. if ( '' === $data['server_route'] ) { $data['server_route'] = $server['server_slug']; } if ( empty( $data['server_name'] ) ) { wp_safe_redirect( add_query_arg( array( 'page' => 'acrossai_mcp_manager', 'action' => 'edit', 'server' => $server_id, 'tab' => 'overview', 'error' => 'empty_name', ), admin_url( 'admin.php' ) ) ); exit; } MCPServerTable::update_server( $server_id, $data ); wp_safe_redirect( add_query_arg( array( 'page' => 'acrossai_mcp_manager', 'action' => 'edit', 'server' => $server_id, 'tab' => 'update-server', 'updated' => '1', ), admin_url( 'admin.php' ) ) ); exit; } // ── save_claude_connector (POST) ──────────────────────────────────────── if ( 'save_claude_connector' === $action && isset( $_SERVER['REQUEST_METHOD'] ) && 'post' === sanitize_key( wp_unslash( $_SERVER['REQUEST_METHOD'] ) ) ) { $server_id = isset( $_GET['server'] ) ? absint( $_GET['server'] ) : 0; // phpcs:ignore WordPress.Security.NonceVerification check_admin_referer( 'acrossai_mcp_claude_connector_' . $server_id ); $server = $server_id ? MCPServerTable::get_by_id( $server_id ) : null; if ( ! $server ) { wp_die( esc_html__( 'Invalid server.', 'acrossai-mcp-manager' ) ); } $connector_data = array( 'claude_connector_client_id' => isset( $_POST['claude_connector_client_id'] ) ? sanitize_text_field( wp_unslash( $_POST['claude_connector_client_id'] ) ) : '', 'claude_connector_client_secret' => isset( $_POST['claude_connector_client_secret'] ) ? sanitize_text_field( wp_unslash( $_POST['claude_connector_client_secret'] ) ) : '', 'claude_connector_redirect_uri' => isset( $_POST['claude_connector_redirect_uri'] ) ? esc_url_raw( wp_unslash( $_POST['claude_connector_redirect_uri'] ) ) : '', ); if ( '' !== $connector_data['claude_connector_client_id'] && $this->has_duplicate_claude_connector_client_id( $server_id, $connector_data['claude_connector_client_id'] ) ) { wp_safe_redirect( add_query_arg( array( 'page' => 'acrossai_mcp_manager', 'action' => 'edit', 'server' => $server_id, 'tab' => 'claude-connector', 'error' => 'connector_duplicate_client', ), admin_url( 'admin.php' ) ) ); exit; } $has_any_value = '' !== $connector_data['claude_connector_client_id'] || '' !== $connector_data['claude_connector_client_secret'] || '' !== $connector_data['claude_connector_redirect_uri']; if ( $has_any_value && ( '' === $connector_data['claude_connector_client_id'] || '' === $connector_data['claude_connector_redirect_uri'] ) ) { wp_safe_redirect( add_query_arg( array( 'page' => 'acrossai_mcp_manager', 'action' => 'edit', 'server' => $server_id, 'tab' => 'claude-connector', 'error' => 'connector_incomplete', ), admin_url( 'admin.php' ) ) ); exit; } MCPServerTable::update_claude_connector_settings( $server_id, $connector_data ); $redirect_args = array( 'page' => 'acrossai_mcp_manager', 'action' => 'edit', 'server' => $server_id, 'tab' => 'claude-connector', ); if ( $has_any_value ) { $redirect_args['connector_saved'] = '1'; } else { $redirect_args['connector_cleared'] = '1'; } wp_safe_redirect( add_query_arg( $redirect_args, admin_url( 'admin.php' ) ) ); exit; } } /** * Register the top-level admin menu page. * * @since 1.0.0 * * @return void */ public function register_menu() { add_menu_page( __( 'AcrossAI MCP Manager', 'acrossai-mcp-manager' ), __( 'MCP Manager', 'acrossai-mcp-manager' ), 'manage_options', 'acrossai_mcp_manager', array( $this, 'render_settings_page' ), 'dashicons-hammer', 99 ); add_submenu_page( 'acrossai_mcp_manager', __( 'MCP Manager Settings', 'acrossai-mcp-manager' ), __( 'Settings', 'acrossai-mcp-manager' ), 'manage_options', 'acrossai_mcp_manager_settings', array( $this, 'render_plugin_settings_page' ) ); } /** * Register WP Settings API options. * * @since 1.0.0 * * @return void */ public function register_settings() { register_setting( 'acrossai_mcp_manager_settings', 'acrossai_mcp_npm_login_enabled', array( 'type' => 'boolean', 'default' => false, 'sanitize_callback' => 'rest_sanitize_boolean', ) ); add_settings_section( 'acrossai_mcp_npm_section', __( 'npm / CLI Settings', 'acrossai-mcp-manager' ), array( $this, 'render_npm_section_description' ), 'acrossai_mcp_manager_settings' ); add_settings_field( 'acrossai_mcp_npm_login_enabled', __( 'Enable CLI Connections', 'acrossai-mcp-manager' ), array( $this, 'render_npm_login_field' ), 'acrossai_mcp_manager_settings', 'acrossai_mcp_npm_section' ); register_setting( 'acrossai_mcp_manager_settings', 'acrossai_mcp_claude_connectors_enabled', array( 'type' => 'boolean', 'default' => false, 'sanitize_callback' => 'rest_sanitize_boolean', ) ); add_settings_section( 'acrossai_mcp_claude_connectors_section', __( 'Claude Connectors Screen (Experimental)', 'acrossai-mcp-manager' ), array( $this, 'render_claude_connectors_section_description' ), 'acrossai_mcp_manager_settings' ); add_settings_field( 'acrossai_mcp_claude_connectors_enabled', __( 'Enable direct Claude Connectors mode', 'acrossai-mcp-manager' ), array( $this, 'render_claude_connectors_enabled_field' ), 'acrossai_mcp_manager_settings', 'acrossai_mcp_claude_connectors_section' ); } /** * Render the description for the npm settings section. * * @since 1.0.0 * * @return void */ public function render_npm_section_description() { $auth_url = \ACROSSAI_MCP_MANAGER\Frontend\FrontendAuth::get_base_url(); ?>
%s contains time-sensitive auth codes and nonces. If your hosting, CDN, or caching plugin caches this URL, authentication will silently fail. Exclude this path from all page-caching rules.', 'acrossai-mcp-manager' ) ), esc_html( $auth_url ) ); ?>
%1$s
Authorize URL: %2$s
Token endpoint: %3$s', 'acrossai-mcp-manager' ) ),
esc_html( ClaudeConnectors::get_authorization_server_metadata_url() ),
esc_html( ClaudeConnectors::get_authorize_url() ),
esc_html( ClaudeConnectors::get_token_endpoint_url() )
);
?>
|
|
|
|
|
|
|
|
|
profile page.', 'acrossai-mcp-manager' ) ), esc_url( admin_url( 'profile.php' ) ) ); ?>
enable CLI Connections in Settings first.', 'acrossai-mcp-manager' ) ), esc_url( $settings_url ) ); ?>
|
|
|
|
|
|
settings page first.', 'acrossai-mcp-manager' ) ), esc_url( admin_url( 'admin.php?page=acrossai_mcp_manager_settings' ) ) ); ?>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
'wp', 'args' => array( 'mcp-adapter', 'serve', '--server=' . $server_slug, '--user=admin', '--path=' . $wp_root, ), ); $stdio_config_json = wp_json_encode( $stdio_config, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES ); ?>
|
get_name() ); ?> |
get_label() ); ?> | get_category() ); ?> |
get_description() ); ?> |
get_name() ); ?> |
get_label() ); ?> | get_category() ); ?> |
get_description() ); ?> |
wordpress/mcp-adapter package is not installed. Please run composer install inside the plugin directory.', 'acrossai-mcp-manager' )
);
?>