%s', esc_url( admin_url( 'tools.php?page=alfmcp-requests' ) ), esc_html__( 'MCP Logs', 'activity-log-for-mcp' ) ); $settings_link = sprintf( '%s', esc_url( admin_url( 'tools.php?page=alfmcp-requests#settings' ) ), esc_html__( 'Settings', 'activity-log-for-mcp' ) ); array_unshift( $links, $logs_link, $settings_link ); return $links; } /** * Show a site-wide admin notice when MCP access is disabled. */ public static function maybe_show_mcp_disabled_notice() { if ( ! current_user_can( 'manage_options' ) ) { return; } if ( ! \ALFMCP\Classes\ALFMCP_Security::is_mcp_disabled() ) { return; } // Skip on the Logs page — the React app renders its own notice there. $screen = get_current_screen(); if ( $screen && 'tools_page_alfmcp-requests' === $screen->id ) { return; } $settings_url = admin_url( 'tools.php?page=alfmcp-requests#settings' ); printf( '

%s %s %s

', esc_html__( 'MCP Logs:', 'activity-log-for-mcp' ), esc_html__( 'MCP access is currently disabled. AI agents cannot make requests to this site.', 'activity-log-for-mcp' ), esc_url( $settings_url ), esc_html__( 'Re-enable MCP Access', 'activity-log-for-mcp' ) ); } /** * Enqueue admin assets. * * @param string $hook Current admin page hook. */ public static function enqueue_assets( $hook ) { if ( 'tools_page_alfmcp-requests' !== $hook ) { return; } $asset_file = ALFMCP_PLUGIN_PATH . 'build/index.asset.php'; $asset_data = file_exists( $asset_file ) ? require $asset_file : array( 'dependencies' => array(), 'version' => ALFMCP_VERSION, ); wp_enqueue_script( 'alfmcp-admin', ALFMCP_PLUGIN_URL . 'build/index.js', $asset_data['dependencies'], $asset_data['version'], true ); // The bundled @wordpress/dataviews source calls __() without a text // domain (e.g. "Actions", "Sort by", "Order"), which resolves to the // "default" domain at runtime. WordPress only ships JS translation // JSON files for its own script paths, so wp_set_script_translations() // can't load translations for our bundled file. Instead, read our // plugin's already-loaded PHP translations and inline them as JED // locale data under the default domain so wp.i18n.__('Sort by') // resolves against our MO. // // Force-load the textdomain first so get_translations_for_domain() // returns the real handler instead of NOOP_Translations. load_plugin_textdomain( 'activity-log-for-mcp', false, dirname( ALFMCP_PLUGIN_BASENAME ) . '/languages' ); if ( is_textdomain_loaded( 'activity-log-for-mcp' ) ) { $translations = get_translations_for_domain( 'activity-log-for-mcp' ); $locale_data = array( '' => array( 'domain' => 'default', 'lang' => determine_locale(), ), ); $headers = $translations->headers; if ( ! empty( $headers['Plural-Forms'] ) ) { $locale_data['']['plural-forms'] = $headers['Plural-Forms']; } // In WP 6.5+, $translations->entries is a numerically-indexed // array of Translation_Entry objects (returned by WP_Translations // via __get). The legacy MO_Translations class returned a // msgid-keyed array. Either way, $entry->key() reliably yields // the JED-format key (with msgctxt prefix where present). foreach ( $translations->entries as $entry ) { $key = $entry->key(); if ( false === $key ) { continue; } $locale_data[ $key ] = $entry->translations; } wp_add_inline_script( 'alfmcp-admin', sprintf( 'wp.i18n.setLocaleData( %s, "default" );', wp_json_encode( $locale_data ) ), 'before' ); } $style_deps = array( 'wp-components' ); if ( file_exists( ALFMCP_PLUGIN_PATH . 'build/style-index.css' ) ) { wp_enqueue_style( 'alfmcp-admin-vendor', ALFMCP_PLUGIN_URL . 'build/style-index.css', array( 'wp-components' ), ALFMCP_VERSION ); $style_deps[] = 'alfmcp-admin-vendor'; } wp_enqueue_style( 'alfmcp-admin', ALFMCP_PLUGIN_URL . 'build/index.css', $style_deps, ALFMCP_VERSION ); wp_localize_script( 'alfmcp-admin', 'alfmcp', array( 'apiUrl' => rest_url( 'activity-log-for-mcp/v1' ), 'nonce' => wp_create_nonce( 'wp_rest' ), 'siteUrl' => get_site_url(), 'adminUrl' => admin_url(), 'pluginUrl' => ALFMCP_PLUGIN_URL, 'securitySettings' => \ALFMCP\Classes\ALFMCP_Security::get_settings(), 'i18n' => array( 'title' => __( 'MCP Logs', 'activity-log-for-mcp' ), 'description' => __( 'Monitor, secure, and govern AI agent activity on your WordPress site.', 'activity-log-for-mcp' ), 'dateFrom' => __( 'Date From', 'activity-log-for-mcp' ), 'dateTo' => __( 'Date To', 'activity-log-for-mcp' ), 'tool' => __( 'Ability', 'activity-log-for-mcp' ), 'user' => __( 'User', 'activity-log-for-mcp' ), 'filter' => __( 'Filter', 'activity-log-for-mcp' ), 'resetFilters' => __( 'Reset Filters', 'activity-log-for-mcp' ), 'date' => __( 'Date / Time', 'activity-log-for-mcp' ), 'toolName' => __( 'Ability', 'activity-log-for-mcp' ), 'status' => __( 'Response', 'activity-log-for-mcp' ), 'method' => __( 'Method', 'activity-log-for-mcp' ), 'route' => __( 'API Endpoint', 'activity-log-for-mcp' ), 'action' => __( 'Details', 'activity-log-for-mcp' ), 'viewDetails' => __( 'View', 'activity-log-for-mcp' ), 'noRequests' => __( 'No MCP requests found', 'activity-log-for-mcp' ), 'noRequestsDesc' => __( 'MCP requests will appear here when they are made to your WordPress site.', 'activity-log-for-mcp' ), 'loading' => __( 'Loading...', 'activity-log-for-mcp' ), 'error' => __( 'Error loading data', 'activity-log-for-mcp' ), 'requestDetails' => __( 'Request Details', 'activity-log-for-mcp' ), 'close' => __( 'Close', 'activity-log-for-mcp' ), 'sessionId' => __( 'Session ID', 'activity-log-for-mcp' ), 'requestInfo' => __( 'Request Information', 'activity-log-for-mcp' ), 'responseInfo' => __( 'Response Information', 'activity-log-for-mcp' ), 'requestBody' => __( 'Request Body', 'activity-log-for-mcp' ), 'responseBody' => __( 'Response Data', 'activity-log-for-mcp' ), 'serverNamespace' => __( 'Server Namespace', 'activity-log-for-mcp' ), 'guest' => __( 'Guest', 'activity-log-for-mcp' ), 'allTools' => __( 'All Abilities', 'activity-log-for-mcp' ), 'allUsers' => __( 'All Users', 'activity-log-for-mcp' ), 'page' => __( 'Page', 'activity-log-for-mcp' ), 'of' => __( 'of', 'activity-log-for-mcp' ), 'previous' => __( 'Previous', 'activity-log-for-mcp' ), 'next' => __( 'Next', 'activity-log-for-mcp' ), 'copy' => __( 'Copy', 'activity-log-for-mcp' ), 'copied' => __( 'Copied!', 'activity-log-for-mcp' ), 'toolStatus' => __( 'Status', 'activity-log-for-mcp' ), 'toolStatusSuccess' => __( 'Success', 'activity-log-for-mcp' ), 'toolStatusFailed' => __( 'Failed', 'activity-log-for-mcp' ), 'toolStatusUnknown' => __( 'Unknown', 'activity-log-for-mcp' ), 'mcpMethod' => __( 'MCP Method', 'activity-log-for-mcp' ), 'toolExecError' => __( 'The request succeeded, but the MCP Ability failed during execution.', 'activity-log-for-mcp' ), 'clearLogs' => __( 'Clear All Logs', 'activity-log-for-mcp' ), 'clearing' => __( 'Clearing...', 'activity-log-for-mcp' ), 'clearLogsConfirm' => __( 'Are you sure you want to delete all logs? This cannot be undone.', 'activity-log-for-mcp' ), 'clearLogsError' => __( 'Failed to clear logs. Please try again.', 'activity-log-for-mcp' ), 'exportCsv' => __( 'Export CSV', 'activity-log-for-mcp' ), 'exporting' => __( 'Exporting...', 'activity-log-for-mcp' ), 'perPage' => __( 'per page', 'activity-log-for-mcp' ), 'sortAsc' => __( 'Sort ascending', 'activity-log-for-mcp' ), 'sortDesc' => __( 'Sort descending', 'activity-log-for-mcp' ), 'statusSuccess' => __( 'Success', 'activity-log-for-mcp' ), 'statusRedirect' => __( 'Redirect', 'activity-log-for-mcp' ), 'statusClientError' => __( 'Client Error', 'activity-log-for-mcp' ), 'statusServerError' => __( 'Server Error', 'activity-log-for-mcp' ), 'statusPending' => __( 'Pending', 'activity-log-for-mcp' ), // Security i18n. 'securityEvents' => __( 'Security Events', 'activity-log-for-mcp' ), 'settings' => __( 'Settings', 'activity-log-for-mcp' ), 'logs' => __( 'Logs', 'activity-log-for-mcp' ), 'alerts7d' => __( 'Alerts (7d)', 'activity-log-for-mcp' ), 'activeSessions7d' => __( 'Active sessions (7d)', 'activity-log-for-mcp' ), 'topRule7d' => __( 'Top rule (7d)', 'activity-log-for-mcp' ), 'successRate7d' => __( 'Success rate (7d)', 'activity-log-for-mcp' ), 'ruleName' => __( 'Rule', 'activity-log-for-mcp' ), 'severity' => __( 'Severity', 'activity-log-for-mcp' ), 'critical' => __( 'Critical', 'activity-log-for-mcp' ), 'warning' => __( 'Warning', 'activity-log-for-mcp' ), 'acknowledge' => __( 'Acknowledge', 'activity-log-for-mcp' ), 'acknowledged' => __( 'Acknowledged', 'activity-log-for-mcp' ), 'clearReviewed' => __( 'Clear reviewed', 'activity-log-for-mcp' ), 'noSecurityEvents' => __( 'No security events yet', 'activity-log-for-mcp' ), 'noSecurityEventsDesc' => __( 'Enable monitoring in Settings to get started.', 'activity-log-for-mcp' ), 'enableMonitoring' => __( 'Enable Security Monitoring', 'activity-log-for-mcp' ), 'emailAlerts' => __( 'Send email alerts', 'activity-log-for-mcp' ), 'alertEmail' => __( 'Alert email address', 'activity-log-for-mcp' ), 'dailyDigest' => __( 'Daily digest email', 'activity-log-for-mcp' ), 'velocityAlert' => __( 'Velocity Alert', 'activity-log-for-mcp' ), 'destructiveToolAlert' => __( 'Destructive Tool Alert', 'activity-log-for-mcp' ), 'errorStormAlert' => __( 'Error Storm Alert', 'activity-log-for-mcp' ), 'threshold' => __( 'Threshold', 'activity-log-for-mcp' ), 'windowMinutes' => __( 'Window (minutes)', 'activity-log-for-mcp' ), 'destructiveTools' => __( 'Monitored tools (one per line)', 'activity-log-for-mcp' ), 'killSwitch' => __( 'MCP Access', 'activity-log-for-mcp' ), 'mcpEnabled' => __( 'Enabled', 'activity-log-for-mcp' ), 'mcpDisabled' => __( 'Disabled', 'activity-log-for-mcp' ), 'mcpDisabledWarning' => __( 'MCP access is currently disabled. AI agents cannot make requests to this site.', 'activity-log-for-mcp' ), 'mcpDisabledReEnable' => __( 'Re-enable MCP Access', 'activity-log-for-mcp' ), 'saveSettings' => __( 'Save Settings', 'activity-log-for-mcp' ), 'saving' => __( 'Saving...', 'activity-log-for-mcp' ), 'settingsSaved' => __( 'Settings saved!', 'activity-log-for-mcp' ), 'settingsSaveError' => __( 'Failed to save settings.', 'activity-log-for-mcp' ), 'testAlert' => __( 'Test Alert', 'activity-log-for-mcp' ), 'testAlertSent' => __( 'Test email sent!', 'activity-log-for-mcp' ), 'testAlertFailed' => __( 'Email delivery failed.', 'activity-log-for-mcp' ), 'sessionReplay' => __( 'Session Replay', 'activity-log-for-mcp' ), 'details' => __( 'Details', 'activity-log-for-mcp' ), 'viewActivity' => __( 'View activity', 'activity-log-for-mcp' ), 'markReviewed' => __( 'Mark as reviewed', 'activity-log-for-mcp' ), 'markUnreviewed' => __( 'Mark as new', 'activity-log-for-mcp' ), 'reviewed' => __( 'Reviewed', 'activity-log-for-mcp' ), 'new' => __( 'New', 'activity-log-for-mcp' ), 'sessionActivity' => __( 'Session activity', 'activity-log-for-mcp' ), 'activityLog' => __( 'Activity log', 'activity-log-for-mcp' ), 'noSessionData' => __( 'No data for this session.', 'activity-log-for-mcp' ), 'time' => __( 'Time', 'activity-log-for-mcp' ), 'allRules' => __( 'All Rules', 'activity-log-for-mcp' ), 'allSeverities' => __( 'All Severities', 'activity-log-for-mcp' ), 'goToSettings' => __( 'Go to Settings', 'activity-log-for-mcp' ), 'loadingEvents' => __( 'Loading security events...', 'activity-log-for-mcp' ), 'loadError' => __( 'Failed to load. Try again.', 'activity-log-for-mcp' ), 'remove' => __( 'Remove', 'activity-log-for-mcp' ), 'addEmail' => __( 'Add email', 'activity-log-for-mcp' ), 'general' => __( 'General', 'activity-log-for-mcp' ), 'generalDesc' => __( 'Core monitoring and access controls.', 'activity-log-for-mcp' ), 'mcpAccess' => __( 'MCP Access', 'activity-log-for-mcp' ), 'mcpAccessDesc' => __( 'Controls all MCP requests site-wide.', 'activity-log-for-mcp' ), 'allowMcpAccess' => __( 'Allow MCP access', 'activity-log-for-mcp' ), 'emailNotifications' => __( 'Email Notifications', 'activity-log-for-mcp' ), 'emailNotificationsDesc' => __( 'Defaults to site admin email if none set.', 'activity-log-for-mcp' ), 'alertRecipients' => __( 'Alert recipients', 'activity-log-for-mcp' ), 'digestRecipients' => __( 'Digest recipients', 'activity-log-for-mcp' ), 'velocityAlertDesc' => __( 'Fires when a session exceeds a request threshold.', 'activity-log-for-mcp' ), 'enableVelocityAlert' => __( 'Enable velocity alert', 'activity-log-for-mcp' ), 'autoDisableMcp' => __( 'Auto-disable MCP when triggered', 'activity-log-for-mcp' ), 'destructiveToolAlertDesc' => __( 'Fires when an agent calls a monitored tool.', 'activity-log-for-mcp' ), 'enableDestructiveAlert' => __( 'Enable destructive tool alert', 'activity-log-for-mcp' ), 'errorStormAlertDesc' => __( 'Fires when too many tool errors occur in a window.', 'activity-log-for-mcp' ), 'enableErrorStormAlert' => __( 'Enable error storm alert', 'activity-log-for-mcp' ), ), ) ); } /** * Render admin page. */ public static function render_admin_page() { if ( ! current_user_can( 'manage_options' ) ) { wp_die( esc_html__( 'You do not have sufficient permissions to access this page.', 'activity-log-for-mcp' ) ); } require_once ALFMCP_PLUGIN_PATH . 'admin/views/app-root.php'; } }