🧩 What's new
Feature 034 — MCP client subsystem refactor: self-describing subclasses + canonical filter-aware enumeration.
Every concrete MCP client class in includes/MCPClients/ now declares its own display metadata via six new methods on AbstractMCPClient — no more stranded metadata in the Renderer's private const. Enumeration collapses to a single canonical entry point that companion plugins can extend cleanly.
The six new methods on AbstractMCPClient
All backwards-compatible defaults — existing third-party subclasses that only implement the three original abstract methods (get_client_slug, get_client_name, get_config_snippet) continue to work unchanged:
| Method | Default | Purpose |
|---|---|---|
get_icon(): string | '' | Emoji / short display marker for sub-nav |
get_description(): string | '' | One-line translated description |
get_config_file(): string | '' | Config file path hint (untranslated) |
get_top_level_key(): string | '' | JSON/TOML top-level key (untranslated) |
get_instructions(): string | '' | Translated setup instructions |
get_priority(): int | 100 | Sub-nav slot preference (WP-idiomatic; lower runs earlier) |
Canonical enumeration
The eight built-in clients migrate their metadata verbatim from the (now-deleted) MCPClientsBlock::CLIENT_META const, with priorities 10, 20, 30, ..., 80 preserving byte-identical pre-refactor sub-nav order.
Enumeration collapses to AbstractMCPClient::get_all_registered_clients() — a single canonical entry point mirroring ConnectorProfileRegistry::get_profiles() (F021 pattern) line-for-line:
- Fires
acrossai_mcp_client_classesfilter withDEFAULT_CLIENT_CLASSESseed - Validates each contributed FQN (
is_string+class_exists+is_subclass_of→ silent skip per SEC-013-008) - Validates each subclass's slug against
/[a-z0-9-]{1,64}/(bad slug →_doing_it_wrongunderWP_DEBUG+ skip) - Dedups by slug (duplicate →
_doing_it_wrongunderWP_DEBUG+ later-wins) - Sorts by
(priority ASC, slug ASC)
The pre-F034 glob-based AbstractMCPClient::get_all_clients() (which ignored the filter) is deleted. MCPClientsBlock::render_body() shrinks from 32 lines to 6.
Third-party companion plugins
Add a new MCP client to the sub-nav in three steps:
namespace MyCompanyPlugin\MCPClients;
use AcrossAI_MCP_Manager\Includes\MCPClients\AbstractMCPClient;
class ZedClient extends AbstractMCPClient {
public function get_client_slug(): string { return 'zed'; }
public function get_client_name(): string { return __( 'Zed Editor', 'my-plugin' ); }
public function get_config_snippet( string $server_url, string $auth_token ): array { /* ... */ }
public function get_icon(): string { return '⚡'; }
public function get_description(): string { return __( 'Zed collaborative AI editor', 'my-plugin' ); }
public function get_config_file(): string { return '~/.config/zed/settings.json'; }
public function get_top_level_key(): string { return 'mcpServers'; }
public function get_instructions(): string { return __( '...', 'my-plugin' ); }
public function get_priority(): int { return 45; } // Between GitHubCopilot (40) and Codex (50)
}add_filter( 'acrossai_mcp_client_classes', function ( array $fqns ): array {
$fqns[] = \MyCompanyPlugin\MCPClients\ZedClient::class;
return $fqns;
} );The client appears in the server-edit → Clients tab sub-nav with the declared metadata in the declared position. Full walkthrough at specs/034-mcp-client-metadata-refactor/quickstart.md on the branch.
No breaking changes
- Zero admin-visible behaviour change (byte-identical sub-nav render for the eight built-in clients).
- Existing
acrossai_mcp_client_classesfilter contract preserved verbatim. - Existing third-party subclasses that only implement the three original abstract methods continue to work — the six new methods all default to safe empty values.
- No database schema changes, no admin UI changes, no REST route changes.
Safe to upgrade in place.
Test coverage
mcpclientsPHPUnit suite: 86 tests / 181 assertions (up from 74/119 pre-F034)- Three new test files:
GetAllRegisteredClientsTest.php— enumeration + validation + filter + priority sortConcreteClientMetadataTest.php— data-provider parameterized over all 8 built-ins × 6 metadata fieldsMCPClientsBlockRenderTest.php— DOM byte-identity + hostile third-party subclass XSS regression (fake subclass returning<script>alert(1)</script>verified to render escaped, not raw)
Durable memory captured
- D35 / DEC-F034-SELF-CONTAINED-SUBSYSTEM-CONTRACT — the architectural pattern: any subsystem with an abstract base + filter-based subclass contribution + per-subclass display metadata MUST declare metadata on the abstract (never as a private const on a Renderer) AND MUST expose enumeration via ONE canonical static method.
- A18 — scoped carve-out from A12 for
tests/bootstrap.phpin-memory WP function stubs pattern that lets pure-PHP suites cover code that fires WP hooks without a full WP bootstrap.
Follow-up work
- F036 (planned) — public discovery API
ConnectionMethodRegistryunderpublic/Discovery/for a planned BuddyBoss add-on. Depends on F034 (this release) as a hard prerequisite. Engineering brief atdocs/planings-tasks/036-connection-method-discovery-api.md.