# Unity MCP PluginRegistry and PluginHub Architecture: How the Bridge Connects AI Clients to Unity

> Explore the Unity MCP PluginRegistry and PluginHub architecture. Discover how this bridge enables seamless bidirectional communication between Python MCP servers and the Unity Editor using JSON commands and reflection.

- Repository: [Coplay/unity-mcp](https://github.com/CoplayDev/unity-mcp)
- Tags: architecture
- Published: 2026-07-06

---

**The Unity MCP PluginRegistry auto-discovers AI client configurators via reflection, while the PluginHub (`StdioBridgeHost`) hosts a TCP bridge that frames JSON commands between Python MCP servers and the Unity Editor, enabling bidirectional communication via a heartbeat file and queued command processing.**

The CoplayDev/unity-mcp repository implements a two-layer architecture that enables AI assistants like Claude, Cursor, and VS Code to control the Unity Editor through the Model Context Protocol. Central to this system are the **PluginRegistry**, which manages client discovery through `McpClientRegistry`, and the **PluginHub**, which handles transport and command execution via `StdioBridgeHost`. Understanding how these components interact reveals how external MCP clients establish and maintain connections with Unity.

## Client Discovery via PluginRegistry (McpClientRegistry)

The `McpClientRegistry` class in [`MCPForUnity/Editor/Clients/McpClientRegistry.cs`](https://github.com/CoplayDev/unity-mcp/blob/main/MCPForUnity/Editor/Clients/McpClientRegistry.cs) serves as the discovery mechanism for AI client integrations. It maintains a static `All` property that caches available client configurators to optimize UI performance and prevent redundant reflection calls during domain reloads.

### Reflection-Based Discovery of IMcpClientConfigurator

The registry uses Unity's `TypeCache` system to locate all implementations of `IMcpClientConfigurator` without manual registration. During initialization, it calls `TypeCache.GetTypesDerivedFrom<IMcpClientConfigurator>()` to retrieve every concrete configurator class found in the assembly, eliminating the need for hardcoded client lists.

### Validation and Caching Mechanism

Only public, non-abstract classes with parameterless constructors are instantiated via `Activator.CreateInstance`. The registry sorts these instances alphabetically by `DisplayName` and stores them in the static `All` collection, making the list available immediately after domain reload for UI components like `MCPSetupWindow` and `MCPForUnityEditorWindow`.

## The Bridge Hub Architecture (StdioBridgeHost)

The `StdioBridgeHost` class in [`MCPForUnity/Editor/Services/Transport/Transports/StdioBridgeHost.cs`](https://github.com/CoplayDev/unity-mcp/blob/main/MCPForUnity/Editor/Services/Transport/Transports/StdioBridgeHost.cs) functions as the **PluginHub**, hosting a TCP listener that bridges the gap between Python MCP servers and Unity's main thread. It manages connection lifecycle, port allocation, command serialization, and graceful shutdown procedures.

### TCP Listener and Port Management

The bridge creates a configured listener via `CreateConfiguredListener(port)`, binding to `IPAddress.Loopback` and setting `ExclusiveAddressUse` on macOS to prevent port-sharing conflicts. `PortManager.GetPortWithFallback()` selects the default port (6400) and automatically falls back to an available port if the original remains busy, as determined by `PortManager.ShouldAbandonBusyPort` in [`MCPForUnity/Editor/Dependencies/PlatformDetectors/PortManager.cs`](https://github.com/CoplayDev/unity-mcp/blob/main/MCPForUnity/Editor/Dependencies/PlatformDetectors/PortManager.cs).

### Heartbeat File and External Discovery

To enable external process discovery, the bridge writes a JSON heartbeat file to `~/.unity-mcp/unity-mcp-status-<hash>.json` via `WriteHeartbeat(bool reloading, string reason)`. This file contains the current TCP port, Unity version, project name, and a ready/reloading flag that Python clients poll to locate the active bridge and determine if the Editor is currently recompiling scripts.

### Command Framing and Queue Processing

The bridge implements length-prefixed JSON framing using 8-byte big-endian headers written by `WriteUInt64BigEndian`. Incoming commands are queued in `commandQueue` (a `Dictionary<string, QueuedCommand>`) and processed on the main thread via `ProcessCommands`, which invokes `TransportCommandDispatcher.ExecuteCommandJsonAsync`. Commands exceeding `2 × FrameIOTimeoutMs` are evicted with an error response to prevent UI blocking.

## Transport Modes and Command Dispatch

The architecture supports dual transport modes controlled by `EditorConfigurationCache.UseHttpTransport` in [`MCPForUnity/Editor/Constants/EditorConfigurationCache.cs`](https://github.com/CoplayDev/unity-mcp/blob/main/MCPForUnity/Editor/Constants/EditorConfigurationCache.cs), allowing the system to adapt to different integration scenarios.

### STDIO vs HTTP Transport

When `UseHttpTransport` is false, the `StdioBridgeHost` starts automatically via `EditorApplication.update` and operates in STDIO mode optimized for single-client connections. When enabled, the system switches to HTTP mode using `TransportCommandDispatcher`, which hosts an HTTP server capable of handling multiple simultaneous MCP clients while reusing the same command execution logic.

### TransportCommandDispatcher Integration

Located in [`MCPForUnity/Editor/Services/Transport/TransportCommandDispatcher.cs`](https://github.com/CoplayDev/unity-mcp/blob/main/MCPForUnity/Editor/Services/Transport/TransportCommandDispatcher.cs), this class executes commands received from either transport on Unity's main thread. It ensures thread-safe marshaling of results back to the requesting client, whether communicating through the TCP bridge or HTTP endpoint, and handles the actual invocation of tools decorated with `[McpForUnityTool]`.

## Practical Implementation Examples

The following examples demonstrate how to interact with the PluginRegistry and PluginHub programmatically.

Enumerate all detected MCP clients:

```csharp
using MCPForUnity.Editor.Clients;

// In an editor script:
foreach (var cfg in McpClientRegistry.All)
{
    Debug.Log($"Client: {cfg.DisplayName}");
    Debug.Log($"  Installed: {cfg.IsInstalled}");
    Debug.Log($"  Config path: {cfg.GetConfigPath()}");
}

```

Start the STDIO bridge manually:

```csharp
using MCPForUnity.Editor.Services;

// Force bridge restart after configuration changes:
StdioBridgeHost.StartAutoConnect();

```

Send a command from Python:

```python
import socket, json, struct

def send_command(cmd_dict, host='127.0.0.1', port=6400):
    payload = json.dumps(cmd_dict).encode('utf-8')
    header = struct.pack('>Q', len(payload))  # 8-byte big-endian length

    with socket.create_connection((host, port)) as s:
        s.sendall(header + payload)
        length = struct.unpack('>Q', s.recv(8))[0]
        response = s.recv(length).decode('utf-8')
        return json.loads(response)

# Create a cube at origin

resp = send_command({
    "command": "manage_create_gameobject",
    "params": {"name": "Cube", "position": [0,0,0]}
})

```

Read the bridge heartbeat file:

```csharp
using System.IO;
using UnityEngine;

string statusDir = Environment.GetEnvironmentVariable("UNITY_MCP_STATUS_DIR")
    ?? Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".unity-mcp");
string hash = ComputeProjectHash(Application.dataPath);
string path = Path.Combine(statusDir, $"unity-mcp-status-{hash}.json");

if (File.Exists(path))
{
    string json = File.ReadAllText(path);
    var info = JsonUtility.FromJson<BridgeStatus>(json);
    Debug.Log($"Bridge on port {info.unity_port}, ready={!info.reloading}");
}

```

## Summary

- **McpClientRegistry** uses reflection to auto-discover AI client configurators and caches them in `McpClientRegistry.All` for immediate UI access without manual registration.
- **StdioBridgeHost** acts as the PluginHub, hosting a TCP listener with automatic port fallback via `PortManager` and writing heartbeat files to `~/.unity-mcp/` for external discovery.
- **Command framing** uses 8-byte big-endian length prefixes to ensure reliable JSON transport between Python MCP servers and the Unity bridge.
- **Dual transport modes** support both STDIO (single-client) and HTTP (multi-client) via `EditorConfigurationCache.UseHttpTransport`, with `TransportCommandDispatcher` handling execution on the main thread.
- **Timeout protection** automatically evicts commands stalled longer than `2 × FrameIOTimeoutMs` to prevent editor UI blocking.

## Frequently Asked Questions

### How does the Unity MCP PluginRegistry discover new AI clients without manual registration?

The registry automatically discovers clients by calling `TypeCache.GetTypesDerivedFrom<IMcpClientConfigurator>()` to find all classes implementing the configurator interface. It validates that each type is concrete, public, and has a parameterless constructor before instantiating via `Activator.CreateInstance`. This reflection-based approach eliminates the need for manual registration when adding support for new AI clients like Claude Desktop or VS Code.

### What is the purpose of the heartbeat file written by StdioBridgeHost?

The heartbeat file at `~/.unity-mcp/unity-mcp-status-<hash>.json` serves as a discovery mechanism for external Python MCP servers. It contains the active TCP port, Unity version, project identifier, and a reloading flag that indicates whether the Editor is currently recompiling scripts. External processes poll this file to determine where to connect and whether the bridge is ready to accept commands.

### How does the bridge handle port conflicts when the default port 6400 is unavailable?

The `PortManager.GetPortWithFallback()` method detects busy ports using `PortManager.ShouldAbandonBusyPort` and automatically selects an available alternative. The bridge then writes the new port to the heartbeat file, ensuring external clients can still locate the service without manual configuration changes.

### Can the Unity MCP architecture support multiple simultaneous AI clients?

Yes, by setting `EditorConfigurationCache.UseHttpTransport` to true, the system switches from STDIO mode to HTTP mode using `TransportCommandDispatcher`. This enables the HTTP server to handle multiple concurrent connections from different AI assistants, while maintaining the same command execution pipeline and thread-safety guarantees on Unity's main thread.