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() ) ); ?>

id ) { return; } wp_enqueue_style( 'acrossai-mcp-manager-admin', ACROSSAI_MCP_MANAGER_URL . 'assets/admin.css', array(), ACROSSAI_MCP_MANAGER_VERSION ); wp_enqueue_script( 'acrossai-mcp-manager-admin', ACROSSAI_MCP_MANAGER_URL . 'assets/admin.js', array( 'wp-api' ), ACROSSAI_MCP_MANAGER_VERSION, true ); wp_localize_script( 'acrossai-mcp-manager-admin', 'acrossaiMcpManagerData', array( 'nonce' => wp_create_nonce( 'wp_rest' ), 'rest_url' => rest_url( 'acrossai-mcp-manager/v1/' ), 'current_user' => wp_get_current_user(), 'server_id' => isset( $_GET['server'] ) ? absint( $_GET['server'] ) : 0, // phpcs:ignore WordPress.Security.NonceVerification 'clients' => array_keys( $this->app_passwords->get_clients() ), 'slug_label' => __( 'Slug:', 'acrossai-mcp-manager' ), ) ); // Enqueue library access-control assets (CSS + JS for provider-rendered UI). $this->get_access_control_ui()->enqueue_assets(); } // ------------------------------------------------------------------------- // Page routing // ------------------------------------------------------------------------- /** * Route to list or edit view based on the ?action= param. * * @since 1.0.0 * * @return void */ public function render_settings_page() { if ( ! current_user_can( 'manage_options' ) ) { wp_die( esc_html__( 'You do not have permission to access this page.', 'acrossai-mcp-manager' ) ); } $action = isset( $_GET['action'] ) ? sanitize_key( $_GET['action'] ) : ''; // phpcs:ignore WordPress.Security.NonceVerification if ( 'edit' === $action ) { $this->render_edit_page(); } elseif ( 'create' === $action ) { $this->render_create_page(); } else { $this->render_list_page(); } } // ------------------------------------------------------------------------- // List page // ------------------------------------------------------------------------- /** * Render the MCP server list (WP_List_Table). * * @since 1.0.0 * * @return void */ private function render_list_page() { $list_table = new MCPServerListTable(); $list_table->prepare_items(); $updated = isset( $_GET['updated'] ) && '1' === sanitize_key( wp_unslash( $_GET['updated'] ) ); // phpcs:ignore WordPress.Security.NonceVerification $deleted = isset( $_GET['deleted'] ) && '1' === sanitize_key( wp_unslash( $_GET['deleted'] ) ); // phpcs:ignore WordPress.Security.NonceVerification $add_new_url = add_query_arg( array( 'page' => 'acrossai_mcp_manager', 'action' => 'create', ), admin_url( 'admin.php' ) ); ?>


display(); ?>
'acrossai_mcp_manager', 'action' => 'create', ), admin_url( 'admin.php' ) ); // Error messages from redirect. $error = isset( $_GET['error'] ) ? sanitize_key( $_GET['error'] ) : ''; // phpcs:ignore WordPress.Security.NonceVerification $slug = isset( $_GET['slug'] ) ? sanitize_text_field( wp_unslash( $_GET['slug'] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification ?>

←

app_passwords->get_clients(); $back_url = admin_url( 'admin.php?page=acrossai_mcp_manager' ); $updated = isset( $_GET['updated'] ) && '1' === sanitize_key( wp_unslash( $_GET['updated'] ) ); // phpcs:ignore WordPress.Security.NonceVerification $created = isset( $_GET['created'] ) && '1' === sanitize_key( wp_unslash( $_GET['created'] ) ); // phpcs:ignore WordPress.Security.NonceVerification $connector_saved = isset( $_GET['connector_saved'] ) && '1' === sanitize_key( wp_unslash( $_GET['connector_saved'] ) ); // phpcs:ignore WordPress.Security.NonceVerification $connector_cleared = isset( $_GET['connector_cleared'] ) && '1' === sanitize_key( wp_unslash( $_GET['connector_cleared'] ) ); // phpcs:ignore WordPress.Security.NonceVerification $edit_error = isset( $_GET['error'] ) ? sanitize_key( $_GET['error'] ) : ''; // phpcs:ignore WordPress.Security.NonceVerification if ( 'connector' === $active_tab ) { $active_tab = 'claude-connector'; } // Default to first client when the clients tab is active but no sub-tab chosen. if ( 'clients' === $active_tab && '' === $active_client ) { $active_client = (string) array_key_first( $clients ); } $make_tab_url = function( $tab ) use ( $server_id ) { return add_query_arg( array( 'page' => 'acrossai_mcp_manager', 'action' => 'edit', 'server' => $server_id, 'tab' => $tab, ), admin_url( 'admin.php' ) ); }; ?>

←

'acrossai_mcp_manager', 'action' => 'edit', 'server' => $server_id, 'tab' => 'clients', 'client' => $client_id, ), admin_url( 'admin.php' ) ); }; // "clients" tab is considered active for any individual client sub-tab too. $clients_tab_active = in_array( $active_tab, array( 'clients' ), true ); ?>
render_overview_tab( $server ); } elseif ( 'npm' === $active_tab ) { $this->render_npm_tab( $server ); } elseif ( 'clients' === $active_tab ) { $this->render_clients_tab( $server, $clients, $active_client, $server_id, $make_client_url ); } elseif ( 'claude-connector' === $active_tab ) { $this->render_claude_connector_tab( $server ); } elseif ( 'wp-cli' === $active_tab ) { $this->render_wpcli_tab( $server ); } elseif ( 'tools' === $active_tab ) { $this->render_tools_tab( $server ); } elseif ( 'abilities' === $active_tab ) { $this->render_abilities_tab( $server ); } elseif ( 'access-control' === $active_tab ) { $this->render_access_control_tab( $server ); } elseif ( 'mcp-tracker' === $active_tab ) { $this->render_mcp_tracker_tab( $server ); } elseif ( 'update-server' === $active_tab ) { $this->render_update_server_tab( $server ); } elseif ( 'danger-zone' === $active_tab ) { $this->render_danger_zone_tab( $server ); } ?>
'acrossai_mcp_manager', 'action' => 'toggle_status', 'server' => $server_id, 'redirect_to' => 'edit', 'tab' => 'overview', '_wpnonce' => $nonce, ), admin_url( 'admin.php' ) ); // Derive MCP URL from stored values; fall back gracefully for legacy rows. $slug = ! empty( $server['server_slug'] ) ? $server['server_slug'] : sanitize_title( $server['server_name'] ); $namespace = ! empty( $server['server_route_namespace'] ) ? $server['server_route_namespace'] : 'mcp'; $route = ! empty( $server['server_route'] ) ? $server['server_route'] : $slug; $version = ! empty( $server['server_version'] ) ? $server['server_version'] : 'v1.0.0'; $mcp_url = rest_url( $namespace . '/' . $route ); $source_labels = array( 'plugin' => __( 'Plugin', 'acrossai-mcp-manager' ), 'database' => __( 'Database', 'acrossai-mcp-manager' ), 'theme' => __( 'Theme', 'acrossai-mcp-manager' ), 'core' => __( 'Core', 'acrossai-mcp-manager' ), ); $source_label = isset( $source_labels[ $registered_from ] ) ? $source_labels[ $registered_from ] : esc_html( $registered_from ); $update_url = add_query_arg( array( 'page' => 'acrossai_mcp_manager', 'action' => 'update', 'server' => $server_id, ), admin_url( 'admin.php' ) ); $delete_url = add_query_arg( array( 'page' => 'acrossai_mcp_manager', 'action' => 'delete', 'server' => $server_id, '_wpnonce' => wp_create_nonce( 'acrossai_mcp_delete_' . $server_id ), ), admin_url( 'admin.php' ) ); ?>

profile page.', 'acrossai-mcp-manager' ) ), esc_url( admin_url( 'profile.php' ) ) ); ?>

prepare_items(); ?>

enable CLI Connections in Settings first.', 'acrossai-mcp-manager' ) ), esc_url( $settings_url ) ); ?>


display(); ?>
$client_data ) : ?>
render_client_tab( $active_client, $clients[ $active_client ], $server_id ); } ?>

build_claude_connector_name( $server ); $oauth_client_id = (string) ( $server['claude_connector_client_id'] ?? '' ); $oauth_secret = (string) ( $server['claude_connector_client_secret'] ?? '' ); $redirect_uri = (string) ( $server['claude_connector_redirect_uri'] ?? '' ); $connector_ready = ClaudeConnectors::is_server_configured( $server ); $save_action = add_query_arg( array( 'page' => 'acrossai_mcp_manager', 'action' => 'save_claude_connector', 'server' => $server_id, ), admin_url( 'admin.php' ) ); $log_table = new ConnectorAuditLogListTable( $server_id ); $log_table->prepare_items(); ?>

settings page first.', 'acrossai-mcp-manager' ) ), esc_url( admin_url( 'admin.php?page=acrossai_mcp_manager_settings' ) ) ); ?>


display(); ?>
'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 ); ?>

'mcp-adapter/discover-abilities', 'label' => __( 'Discover Abilities', 'acrossai-mcp-manager' ), 'description' => __( 'Lists all publicly available WordPress abilities registered on this site. AI clients use this to discover what actions the server can perform.', 'acrossai-mcp-manager' ), ), array( 'name' => 'mcp-adapter/get-ability-info', 'label' => __( 'Get Ability Info', 'acrossai-mcp-manager' ), 'description' => __( 'Returns detailed information about a specific ability, including its input/output schema and description. Used by AI clients before executing an ability.', 'acrossai-mcp-manager' ), ), array( 'name' => 'mcp-adapter/execute-ability', 'label' => __( 'Execute Ability', 'acrossai-mcp-manager' ), 'description' => __( 'Executes a WordPress ability with the provided input parameters and returns the result. This is the primary tool used by AI clients to interact with WordPress.', 'acrossai-mcp-manager' ), ), ); ?>

get_meta(); $slug = $ability->get_name(); $is_public = ! empty( $meta['mcp']['public'] ); if ( $is_public && $has_manager && ! empty( $server_slug ) ) { // Apply per-server filtering from the Abilities Manager when active. if ( \AcrossAI_Abilities_Manager\Runtime\Override_Applier::has_server_restriction( $slug ) ) { // Specific-server mode: only include if this server is in the allowlist. $is_public = \AcrossAI_Abilities_Manager\Runtime\Override_Applier::should_expose_to_mcp_server( $slug, $server_slug ); } // else: no restriction means "Allow in all servers" — keep $is_public=true. } if ( $is_public ) { $mcp_public[] = $ability; } else { $mcp_private[] = $ability; } } ?>

get_meta(); $mcp_type = $meta['mcp']['type'] ?? 'tool'; ?>
get_name() ); ?> get_label() ); ?> get_category() ); ?> get_description() ); ?>

get_name() ); ?> get_label() ); ?> get_category() ); ?> get_description() ); ?>
get_access_control_ui(); echo '
'; $ui->render( $ns, $route, array( 'submit_label' => __( 'Save Access Control', 'acrossai-mcp-manager' ), 'description' => __( 'Control which users are allowed to connect to this MCP server. Administrators always have access regardless of this setting.', 'acrossai-mcp-manager' ), ) ); echo '
'; } /** * Return a configured access-control UI instance for this plugin. * * Assets (CSS + JS) are bundled inside the plugin at assets/access-control/ * so the UI works regardless of whether the vendor package includes them. * * @since 1.6.0 * * @return AccessControlUI */ private function get_access_control_ui(): AccessControlUI { $manager = \ACROSSAI_MCP_MANAGER\Core\Plugin::instance()->get_access_control_manager(); $ui = new AccessControlUI( $manager ); $ui->set_assets_url( plugins_url( 'assets/access-control', ACROSSAI_MCP_MANAGER_FILE ) ); return $ui; } /** * Render the MCP Tracker tab. * * Promotes the MCP Tracker plugin (wordpress.org/plugins/mcp-tracker/) and * links directly to its request log filtered to this server's slug when the * plugin is active. * * @since 1.5.0 * * @param array $server DB row for the server being viewed. * * @return void */ private function render_mcp_tracker_tab( array $server ) { $server_slug = ! empty( $server['server_slug'] ) ? $server['server_slug'] : sanitize_title( $server['server_name'] ); $tracker_active = defined( 'WPVMCPT_PLUGIN_VERSION' ) || class_exists( 'WPVMCPT\Plugin' ); // Build the tracker URL using the MCP server slug as the server parameter. $tracker_url = admin_url( 'admin.php?page=wpvmcpt-requests-list&server=' . rawurlencode( $server_slug ) ); // WordPress.org plugin page URL — hardcoded, no user input. $wporg_url = 'https://wordpress.org/plugins/mcp-tracker/'; ?>

' . esc_html( $server_slug ) . '' ); ?>

'acrossai_mcp_manager', 'action' => 'update', 'server' => $server_id, ), admin_url( 'admin.php' ) ); ?>

'acrossai_mcp_manager', 'action' => 'delete', 'server' => $server_id, '_wpnonce' => wp_create_nonce( 'acrossai_mcp_delete_' . $server_id ), ), admin_url( 'admin.php' ) ); $slug = ! empty( $server['server_slug'] ) ? $server['server_slug'] : sanitize_title( $server['server_name'] ); $ns = ! empty( $server['server_route_namespace'] ) ? $server['server_route_namespace'] : 'mcp'; $route = ! empty( $server['server_route'] ) ? $server['server_route'] : $slug; $mcp_url = rest_url( $ns . '/' . $route ); ?>

')">

wordpress/mcp-adapter package is not installed. Please run composer install inside the plugin directory.', 'acrossai-mcp-manager' ) ); ?>