# How Handler Discovery Works in Unity MCP for C#: Automatic Registration Explained

> Discover how Unity MCP automatically registers C# command and resource handlers via assembly scanning at startup saving you manual setup time.

- Repository: [いすず/unitymcp](https://github.com/isuzu-shiranui/unitymcp)
- Tags: internals
- Published: 2026-03-04

---

**Unity MCP automatically discovers and registers command and resource handlers at editor startup by scanning loaded assemblies for types implementing `IMcpCommandHandler` or `IMcpResourceHandler`, then instantiating them via `Activator.CreateInstance` without requiring manual registration code.**

Unity MCP (Meta-Command-Protocol) eliminates boilerplate registration by employing a reflection-based discovery pipeline. When the Unity editor loads, the system automatically locates and wires up C# handlers according to the isuzu-shiranui/unitymcp source code, enabling plug-and-play extensibility for editor automation.

## The Generic Discovery Engine

The discovery process centers on **`McpHandlerDiscovery<T>`**, a generic class defined in [`Editor/Core/McpHandlerDiscovery.cs`](https://github.com/isuzu-shiranui/unitymcp/blob/main/Editor/Core/McpHandlerDiscovery.cs) that uses reflection to locate concrete implementations of handler interfaces. The class constructor accepts an `Action<T>` registration callback that executes for every valid handler discovered:

```csharp
// From Editor/Core/McpHandlerDiscovery.cs (lines 13-21)
public McpHandlerDiscovery(Action<T> registerAction)
{
    _registerAction = registerAction ?? throw new ArgumentNullException(nameof(registerAction));
}

```

The **`DiscoverAndRegister()`** method orchestrates the scanning process, returning an integer count of registered handlers for diagnostic purposes.

## Assembly Scanning and Type Filtering

During discovery, the system iterates over all assemblies in the current AppDomain while excluding system and Unity-specific namespaces to optimize performance. As implemented in [`Editor/Core/McpHandlerDiscovery.cs`](https://github.com/isuzu-shiranui/unitymcp/blob/main/Editor/Core/McpHandlerDiscovery.cs) (lines 34-44), the scanner skips assemblies whose names start with `System.`, `Unity.`, or `UnityEditor.`.

For each remaining assembly, the algorithm filters types using three criteria (lines 50-55):

1. **Interface implementation** – `typeof(T).IsAssignableFrom(t)` ensures the type implements the target interface (e.g., `IMcpCommandHandler`)
2. **Concrete class requirement** – `!t.IsAbstract` verifies the type is instantiable
3. **Non-interface check** – `!t.IsInterface` excludes the interface definition itself

## Instantiation and Registration Pipeline

Once the scanner identifies valid handler types, it instantiates each using **`Activator.CreateInstance`** and immediately invokes the registration callback. The implementation in [`Editor/Core/McpHandlerDiscovery.cs`](https://github.com/isuzu-shiranui/unitymcp/blob/main/Editor/Core/McpHandlerDiscovery.cs) (lines 61-64) follows this pattern:

```csharp
var instance = (T)Activator.CreateInstance(handlerType);
_registerAction(instance);

```

For command handlers, the registration callback passes the instance to `McpServer.RegisterHandler`, which stores it in a dictionary keyed by `CommandPrefix`. Resource handlers follow an identical pattern via `RegisterResourceHandler`, using `ResourceName` as the dictionary key.

## Editor Initialization Trigger

Discovery triggers automatically when the Unity editor loads through the **`McpEditorInitializer`** class in [`Editor/Core/McpEditorInitializer.cs`](https://github.com/isuzu-shiranui/unitymcp/blob/main/Editor/Core/McpEditorInitializer.cs). The static constructor schedules initialization using `EditorApplication.delayCall`:

```csharp
// Editor/Core/McpEditorInitializer.cs
static McpEditorInitializer()
{
    EditorApplication.delayCall += Initialize;
}

```

The **`Initialize()`** method executes two separate discovery passes:

```csharp
// Register command handlers
var commandDiscovery = new McpHandlerDiscovery<IMcpCommandHandler>(
    handler => server.RegisterHandler(handler));
var commandCount = commandDiscovery.DiscoverAndRegister();

// Register resource handlers  
var resourceDiscovery = new McpHandlerDiscovery<IMcpResourceHandler>(
    handler => server.RegisterResourceHandler(handler));
var resourceCount = resourceDiscovery.DiscoverAndRegister();

```

This dual-pass approach ensures both command and resource handlers are available before the MCP server begins accepting requests.

## Handler Interface Contracts

Unity MCP defines two distinct handler contracts that enable the discovery system to identify capable classes:

**`IMcpCommandHandler`** (defined in [`Editor/Core/IMcpCommandHandler.cs`](https://github.com/isuzu-shiranui/unitymcp/blob/main/Editor/Core/IMcpCommandHandler.cs)) requires:
- `string CommandPrefix` – unique identifier for routing
- `string Description` – human-readable documentation
- `JObject Execute(string action, JObject parameters)` – execution logic

**`IMcpResourceHandler`** (defined in [`Editor/Core/IMcpResourceHandler.cs`](https://github.com/isuzu-shiranui/unitymcp/blob/main/Editor/Core/IMcpResourceHandler.cs)) requires:
- `string ResourceName` – unique resource identifier
- `string Description` – documentation string
- `string ResourceUri` – URI scheme for the resource
- `JObject FetchResource(JObject parameters)` – data retrieval logic

## Implementing Custom Handlers

Developers implement these interfaces without writing registration code. The discovery system automatically picks up any concrete class in the project assemblies.

### Example: Command Handler

The following handler responds to `ping` commands:

```csharp
using Newtonsoft.Json.Linq;
using UnityMCP.Editor.Core;

public sealed class PingCommandHandler : IMcpCommandHandler
{
    public string CommandPrefix => "ping";
    public string Description => "Simple health-check command";

    public JObject Execute(string action, JObject parameters)
    {
        return new JObject
        {
            ["success"] = true,
            ["message"] = "pong"
        };
    }
}

```

When the editor starts, `McpHandlerDiscovery<IMcpCommandHandler>` instantiates this class and registers it with the server automatically.

### Example: Resource Handler

Resource handlers expose project data via the MCP protocol:

```csharp
using Newtonsoft.Json.Linq;
using UnityMCP.Editor.Resources;

public sealed class ProjectInfoResourceHandler : IMcpResourceHandler
{
    public string ResourceName => "projectInfo";
    public string Description => "Provides basic Unity project metadata";
    public string ResourceUri => "mcp://resource/projectInfo";

    public JObject FetchResource(JObject parameters)
    {
        return new JObject
        {
            ["unityVersion"] = UnityEngine.Application.unityVersion,
            ["platform"] = UnityEngine.Application.platform.ToString(),
            ["productName"] = UnityEngine.Application.productName
        };
    }
}

```

## Error Handling and Diagnostics

The discovery pipeline includes defensive error handling to prevent individual handler failures from crashing the initialization process. As shown in [`Editor/Core/McpHandlerDiscovery.cs`](https://github.com/isuzu-shiranui/unitymcp/blob/main/Editor/Core/McpHandlerDiscovery.cs) (lines 66-75), the system catches exceptions at both the assembly level and the individual type level, logging detailed error messages to the Unity console while continuing to process remaining handlers.

The **`DiscoverAndRegister()`** method returns the total count of successfully registered handlers (lines 78-86), enabling diagnostic logging that reports how many commands and resources are available after initialization.

## Summary

- **Reflection-based scanning**: `McpHandlerDiscovery<T>` scans all non-system assemblies for interface implementations
- **Automatic instantiation**: Valid handlers are instantiated via `Activator.CreateInstance` without manual registration
- **Editor-triggered startup**: `McpEditorInitializer` triggers discovery via `EditorApplication.delayCall` when the Unity editor loads
- **Dual handler types**: Separate discovery passes register both `IMcpCommandHandler` and `IMcpResourceHandler` implementations
- **Fault isolation**: Per-type and per-assembly try-catch blocks ensure one bad handler doesn't break the entire system

## Frequently Asked Questions

### How does Unity MCP know which assemblies to scan for handlers?

The discovery system iterates over `AppDomain.CurrentDomain.GetAssemblies()` but filters out assemblies starting with `System.`, `Unity.`, or `UnityEditor.` to avoid scanning framework code. This optimization is implemented in [`Editor/Core/McpHandlerDiscovery.cs`](https://github.com/isuzu-shiranui/unitymcp/blob/main/Editor/Core/McpHandlerDiscovery.cs) lines 34-44.

### Can abstract base classes implement handler interfaces without being registered?

Yes. The discovery algorithm explicitly checks `!t.IsAbstract` before instantiation, ensuring only concrete classes are registered. Abstract implementations are filtered out during the type selection phase in [`Editor/Core/McpHandlerDiscovery.cs`](https://github.com/isuzu-shiranui/unitymcp/blob/main/Editor/Core/McpHandlerDiscovery.cs) lines 50-55.

### What happens if a handler constructor throws an exception?

The discovery pipeline wraps each instantiation attempt in a try-catch block. If `Activator.CreateInstance` fails for a specific type, the error is logged to the Unity console and the system continues processing remaining handlers, as shown in lines 66-75 of [`McpHandlerDiscovery.cs`](https://github.com/isuzu-shiranui/unitymcp/blob/main/McpHandlerDiscovery.cs).

### Do handlers require specific namespaces or folder locations to be discovered?

No. The discovery system scans all loaded assemblies regardless of namespace or file location. Any concrete class implementing `IMcpCommandHandler` or `IMcpResourceHandler` anywhere in the project will be automatically registered, provided it resides in a non-excluded assembly.