Files
heretek-control-core/includes/class-admin-bar.php
T
Mian Shahzad Raza 0ce420601b style(copy): remove em dashes from plugin copy, fix mcpb display name
Sweeps shipped copy clean of em dashes: readme.txt, README.md,
CHANGELOG.md, the bundled sample prompts, and user-facing PHP strings
(tool descriptions, admin labels). PHP was swept on non-comment lines
only, so developer docblocks and inline comments are untouched.
Internal dev docs (CLAUDE.md, docs/) were deliberately left alone.

Replacements are contextual: colons for label/definition patterns,
commas for asides, en dashes for numeric ranges.

The .mcpb bundle display_name now reads "EMCP Tools - <host>" with a
plain hyphen; its test is updated to match. Fixing CHANGELOG.md also
clears the em dashes from the website's generated changelog on the
next site build, since that file is its source.

Verified: php -l clean on every touched file, pro suite 1559 pass,
public suite 102 pass. Bumps the pro submodule for the Pro-side sweep.
2026-07-21 08:03:19 +05:00

210 lines
6.1 KiB
PHP

<?php
/**
* MCP status + exposure toggle in the WordPress admin bar.
*
* A small, admin-only quick menu (front-end + wp-admin) showing whether the
* Abilities API is available and whether the EMCP MCP server is exposed, with a
* one-click nonce toggle that flips the SAME option the Connection tab writes
* (emcp_tools_server_enabled). Free; loaded unconditionally because the admin bar
* renders on the front end too. All display logic derives from the pure status()
* snapshot; the two environment seams (api_available/active_tool_count) are
* protected so status() is unit-testable without WordPress.
*
* @package EMCP_Tools
* @since 3.1.0
*/
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* @since 3.1.0
*/
class EMCP_Tools_Admin_Bar {
const TOGGLE_ACTION = 'emcp_tools_toggle_server';
/**
* @since 3.1.0
*/
public function init(): void {
add_action( 'admin_bar_menu', array( $this, 'render_node' ), 90 );
add_action( 'admin_post_' . self::TOGGLE_ACTION, array( $this, 'handle_toggle' ) );
}
/**
* Pure status snapshot for the menu.
*
* @since 3.1.0
* @return array{api_available:bool, enabled:bool, tool_count:int, color:string}
*/
public function status(): array {
$api = $this->api_available();
$enabled = $this->is_enabled();
$count = ( $api && $enabled ) ? $this->active_tool_count() : 0;
if ( ! $api ) {
$color = 'red';
} elseif ( $enabled ) {
$color = 'green';
} else {
$color = 'grey';
}
return array(
'api_available' => (bool) $api,
'enabled' => (bool) $enabled,
'tool_count' => (int) $count,
'color' => $color,
);
}
/**
* Flip the exposure option; returns the new value ('1'|'0').
*
* @since 3.1.0
* @return string
*/
public function flip(): string {
$new = $this->is_enabled() ? '0' : '1';
update_option( 'emcp_tools_server_enabled', $new );
return $new;
}
/**
* Whether MCP exposure is on. Prefers the plugin's canonical accessor; falls
* back to reading the option directly (identical logic) so this class is
* decoupled and unit-testable without booting the plugin singleton.
*
* @since 3.1.0
* @return bool
*/
private function is_enabled(): bool {
if ( class_exists( 'EMCP_Tools_Plugin' ) ) {
return EMCP_Tools_Plugin::is_server_enabled();
}
return '1' === (string) get_option( 'emcp_tools_server_enabled', '1' );
}
/**
* Whether the WordPress Abilities API is present (WP 6.9+). Seam for tests.
*
* @since 3.1.0
* @return bool
*/
protected function api_available(): bool {
return function_exists( 'wp_register_ability' );
}
/**
* Count of currently-exposed tools. Seam for tests (avoids booting the
* plugin singleton in unit tests).
*
* @since 3.1.0
* @return int
*/
protected function active_tool_count(): int {
if ( ! class_exists( 'EMCP_Tools_Plugin' ) ) {
return 0;
}
$names = EMCP_Tools_Plugin::instance()->get_active_ability_names();
return is_array( $names ) ? count( $names ) : 0;
}
/**
* Dot color hex for a status color.
*
* @since 3.1.0
* @param string $color green|red|grey.
* @return string
*/
private function dot_hex( string $color ): string {
switch ( $color ) {
case 'green':
return '#22c55e';
case 'red':
return '#ef4444';
default:
return '#94a3b8';
}
}
/**
* Render the admin-bar node + children. manage_options only; front-end + admin.
*
* @since 3.1.0
* @param \WP_Admin_Bar $wp_admin_bar Admin bar instance.
*/
public function render_node( $wp_admin_bar ): void {
if ( ! is_admin_bar_showing() || ! current_user_can( 'manage_options' ) ) {
return;
}
$s = $this->status();
$settings = admin_url( 'admin.php?page=emcp-tools-connection' );
$dot = '<span style="display:inline-block;width:9px;height:9px;border-radius:50%;margin-right:6px;background:' . esc_attr( $this->dot_hex( $s['color'] ) ) . '"></span>';
$wp_admin_bar->add_node( array(
'id' => 'emcp-tools-mcp',
'title' => $dot . esc_html__( 'MCP', 'emcp-tools' ),
'href' => esc_url( $settings ),
'meta' => array( 'title' => esc_attr__( 'EMCP Tools, MCP status', 'emcp-tools' ) ),
) );
$wp_admin_bar->add_node( array(
'parent' => 'emcp-tools-mcp',
'id' => 'emcp-tools-mcp-api',
'title' => esc_html__( 'Abilities API:', 'emcp-tools' ) . ' ' . ( $s['api_available'] ? esc_html__( 'Available', 'emcp-tools' ) : esc_html__( 'Unavailable', 'emcp-tools' ) ),
) );
$wp_admin_bar->add_node( array(
'parent' => 'emcp-tools-mcp',
'id' => 'emcp-tools-mcp-state',
'title' => esc_html__( 'MCP exposure:', 'emcp-tools' ) . ' ' . ( $s['enabled'] ? esc_html__( 'Enabled', 'emcp-tools' ) : esc_html__( 'Disabled', 'emcp-tools' ) ),
) );
if ( $s['api_available'] && $s['enabled'] && $s['tool_count'] > 0 ) {
$wp_admin_bar->add_node( array(
'parent' => 'emcp-tools-mcp',
'id' => 'emcp-tools-mcp-count',
/* translators: %d: number of active MCP tools */
'title' => sprintf( esc_html__( 'Active tools: %d', 'emcp-tools' ), (int) $s['tool_count'] ),
) );
}
if ( $s['api_available'] ) {
$toggle_url = wp_nonce_url( admin_url( 'admin-post.php?action=' . self::TOGGLE_ACTION ), self::TOGGLE_ACTION );
$wp_admin_bar->add_node( array(
'parent' => 'emcp-tools-mcp',
'id' => 'emcp-tools-mcp-toggle',
'title' => $s['enabled'] ? esc_html__( 'Turn MCP off', 'emcp-tools' ) : esc_html__( 'Turn MCP on', 'emcp-tools' ),
'href' => esc_url( $toggle_url ),
) );
}
$wp_admin_bar->add_node( array(
'parent' => 'emcp-tools-mcp',
'id' => 'emcp-tools-mcp-settings',
'title' => esc_html__( 'Connection settings', 'emcp-tools' ) . ' →',
'href' => esc_url( $settings ),
) );
}
/**
* admin-post handler: flip exposure (cap + nonce guarded) and redirect back.
*
* @since 3.1.0
*/
public function handle_toggle(): void {
if ( ! current_user_can( 'manage_options' ) ) {
wp_die( esc_html__( 'You are not allowed to do this.', 'emcp-tools' ), '', array( 'response' => 403 ) );
}
check_admin_referer( self::TOGGLE_ACTION );
$this->flip();
$back = wp_get_referer();
wp_safe_redirect( $back ? $back : admin_url() );
exit;
}
}