%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(
'
',
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';
}
}