How Unity MCP Architecture Works: A Deep Dive into the C#-TypeScript Bridge
Unity MCP architecture consists of a bidirectional bridge where the Unity Editor (C#) acts as a TCP client that auto-discovers handler implementations via reflection, while a TypeScript process runs the MCP server and adapts Unity capabilities to the Model-Context-Protocol SDK, enabling AI assistants to execute editor commands, fetch project resources, and process prompts via JSON-RPC.
The Unity MCP architecture in the isuzu-shiranui/unitymcp repository establishes a lightweight, extensible integration pattern that exposes Unity Editor functionality to external AI models. Rather than embedding the MCP server inside Unity, the architecture splits responsibilities: Unity handles network plumbing and main-thread marshaling, while TypeScript implements the actual MCP protocol surface using the official SDK.
Core Components of the Unity MCP Architecture
The architecture is split into two tightly coupled runtimes that communicate over a persistent TCP connection.
C# Side: Unity Editor Infrastructure
The Unity side provides the transport layer, service management, and reflection-based discovery mechanisms:
- McpServer – A background-thread TCP client that manages the connection to the TypeScript MCP server, handles JSON-RPC message routing, UDP discovery, and automatic reconnection. It maintains dictionaries of
commandHandlersandresourceHandlersfor routing incoming requests. - McpHandlerDiscovery – Uses reflection to scan loaded assemblies (excluding Unity/System assemblies) and instantiates every concrete type implementing
IMcpCommandHandlerorIMcpResourceHandlerviaActivator.CreateInstance. - McpServiceManager – A simple dependency-injection container that stores the singleton
McpServerinstance and other shared services. - McpSettings – A
ScriptableSingletonpersisted in Unity preferences that stores connection parameters (host, port, auto-start flags, UDP discovery settings). - McpEditorInitializer – An
[InitializeOnLoad]entry point that wires upEditorApplication.delayCallto create the server instance and optionally start it when the editor loads.
TypeScript Side: MCP Protocol Adapter
The TypeScript side implements the protocol semantics using the @modelcontextprotocol/sdk:
- HandlerAdapter – Bridges C#-style handler contracts to the MCP SDK by registering tools with
server.tool(), resources withserver.resource(), and prompts withserver.prompt(). - ICommandHandler / IResourceHandler / IPromptHandler – Interface contracts defined in
unity-mcp-ts/src/core/interfacesthat TypeScript implementations must satisfy.
How the Unity MCP Architecture Handles Communication
The data flow follows a strict initialization and runtime pattern designed to keep Unity's main thread responsive while allowing async AI interactions.
Editor Initialization and Handler Discovery
When Unity starts, the static constructor in McpEditorInitializer attaches an Initialize method to EditorApplication.delayCall. This method constructs a McpServer using settings from McpSettings and registers it with McpServiceManager【/cache/repos/github.com/isuzu-shiranui/unitymcp/main/jp.shiranui-isuzu.unity-mcp/Editor/Core/McpEditorInitializer.cs#L27-L34】.
Immediately after server creation, two instances of McpHandlerDiscovery<T> execute:
McpHandlerDiscovery<IMcpCommandHandler>scans assemblies for command handlers.McpHandlerDiscovery<IMcpResourceHandler>scans for resource handlers.
Each discovery walker ignores Unity and System assemblies, then uses Activator.CreateInstance to instantiate found types【/cache/repos/github.com/isuzu-shiranui/unitymcp/main/jp.shiranui-isuzu.unity-mcp/Editor/Core/McpHandlerDiscovery.cs#L24-L72】. Discovered handlers are passed to McpServer.RegisterHandler (commands) or RegisterResourceHandler (resources), which store them in internal dictionaries keyed by command prefix or URI template.
If McpSettings.autoStartOnLaunch is enabled, the server begins its connection loop immediately. Additionally, the initializer subscribes to EditorApplication.playModeStateChanged to restart the server when play mode changes, provided autoRestartOnPlayModeChange is set【/cache/repos/github.com/isuzu-shiranui/unitymcp/main/jp.shiranui-isuzu.unity-mcp/Editor/Core/McpEditorInitializer.cs#L48-L84】.
TCP Client Loop and Request Routing
The McpServer runs its connection logic on a background thread. Upon successful TCP connection to the configured host/port, it sends a registration message containing the client ID, Unity version, product name, and project hash【/cache/repos/github.com/isuzu-shiranui/unitymcp/main/jp.shiranui-isuzu.unity-mcp/Editor/Core/McpServer.cs#L94-L106】.
Incoming JSON-RPC messages are deserialized and routed based on type:
- Commands: Routed to
ExecuteCommand, which looks up the handler by prefix incommandHandlers, queues execution on the main thread viamainThreadQueue, waits up to 5 seconds, and returns a JSON response. - Resources: Routed to
ProcessResourceRequest, which invokesFetchResourceDatato call the matching handler'sFetchResourcemethod, also main-thread marshaled.
All main-thread work is synchronized through ExecuteOnMainThread, ensuring thread-safe access to Unity APIs.
UDP Discovery Mechanism
When UDP discovery is enabled, McpServer opens a UdpClient listening on settings.udpDiscoveryPort. It parses broadcast packets formatted as "type":"mcp_server_announce" from the TypeScript server. Upon receipt, it dynamically updates the host/port configuration and triggers an immediate reconnection attempt【/cache/repos/github.com/isuzu-shiranui/unitymcp/main/jp.shiranui-isuzu.unity-mcp/Editor/Core/McpServer.cs#L165-L176】.
TypeScript Handler Adapter Registration
On the TypeScript side, the HandlerAdapter class consumes the connection and registers capabilities with the MCP SDK:
-
Commands: Iterates over
handler.getToolDefinitions()and registers each withserver.tool(), forwarding calls back to C# via the TCP bridge. -
Resources: Checks if the URI template contains parameters. If so, registers a dynamic resource template using
new ResourceTemplate(); otherwise registers a static string URI. The callback invokeshandler.fetchResource(uri). -
Prompts: Registers prompt definitions with
server.prompt(), constructing chat-style message payloads for the model.
The adapter logs registration events to console.error('[INFO] Registered …') for debugging visibility.
Implementing Custom Handlers in Unity MCP
Creating new capabilities requires only implementing the correct C# interface; no manual registration code is necessary.
Creating a Command Handler
using UnityEngine;
using UnityMCP.Editor.Core;
using Newtonsoft.Json.Linq;
public class HelloWorldCommandHandler : IMcpCommandHandler
{
public string CommandPrefix => "hello";
public string Description => "Simple hello-world command.";
public ICommandResult Execute(string action, JObject parameters)
{
if (action == "say")
{
var name = parameters?["name"]?.ToString() ?? "World";
return new CommandResult
{
Success = true,
Result = JObject.FromObject(new { message = $"Hello, {name}!" })
};
}
return new CommandResult { Success = false, Error = "Unknown action" };
}
}
When Unity loads, McpHandlerDiscovery<IMcpCommandHandler> automatically instantiates and registers this handler. External clients can then invoke hello.say via the MCP protocol.
Creating a Resource Handler
using UnityEngine;
using UnityMCP.Editor.Core;
using Newtonsoft.Json.Linq;
using System.Linq;
public class SceneListResourceHandler : IMcpResourceHandler
{
public string ResourceName => "scenes";
public string ResourceUri => "unity://scenes";
public JObject FetchResource(string uri, JObject parameters)
{
var scenes = UnityEngine.SceneManagement.SceneManager.GetAllScenes()
.Select(s => s.name)
.ToArray();
return JObject.FromObject(new { scenes });
}
}
Resource handlers allow AI models to query the Unity project state (like open scenes or asset lists) without executing commands.
Consuming from TypeScript
The TypeScript client requires no knowledge of the C# implementation:
import { McpClient } from '@modelcontextprotocol/sdk/client/mcp';
async function sayHello(client: McpClient, name: string) {
const response = await client.callTool('hello.say', { name });
console.log(response.result?.message); // → "Hello, Alice!"
}
Summary
The Unity MCP architecture decouples the Unity Editor from AI model integration through a clean separation of concerns:
- Auto-discovery: The
McpHandlerDiscoveryclass eliminates boilerplate by reflecting over assemblies to findIMcpCommandHandlerandIMcpResourceHandlerimplementations. - Thread safety: All Unity API interactions are marshaled to the main thread via
McpServer.ExecuteOnMainThread, preventing cross-thread exceptions. - Dynamic configuration:
McpSettingsprovides persistent user preferences for network configuration, while UDP discovery allows zero-config setup. - Protocol abstraction: TypeScript handlers implement business logic against standard MCP interfaces, making the system portable across different AI backends.
Key source files defining this architecture include Editor/Core/McpServer.cs for network logic, Editor/Core/McpHandlerDiscovery.cs for reflection-based registration, and unity-mcp-ts/src/core/HandlerAdapter.ts for SDK adaptation.
Frequently Asked Questions
What role does McpEditorInitializer play in the Unity MCP architecture?
McpEditorInitializer serves as the entry point marked with [InitializeOnLoad], ensuring the MCP server starts automatically when the Unity Editor opens. It creates the McpServer singleton, triggers handler discovery, and manages play-mode lifecycle events such as auto-restarting the connection when entering or exiting play mode【/cache/repos/github.com/isuzu-shiranui/unitymcp/main/jp.shiranui-isuzu.unity-mcp/Editor/Core/McpEditorInitializer.cs】.
How does the architecture handle automatic handler discovery?
The architecture uses generic McpHandlerDiscovery<T> classes that scan all loaded assemblies (excluding Unity and System libraries) for concrete implementations of IMcpCommandHandler or IMcpResourceHandler. It instantiates each found type using Activator.CreateInstance and immediately registers the instance with the active McpServer, requiring no manual wiring or configuration files【/cache/repos/github.com/isuzu-shiranui/unitymcp/main/jp.shiranui-isuzu.unity-mcp/Editor/Core/McpHandlerDiscovery.cs#L24-L72】.
What transport protocol does Unity MCP use for communication?
Unity MCP uses TCP sockets for primary communication, sending JSON-RPC 2.0 messages between the C# client and TypeScript server. Additionally, it supports UDP discovery on a configurable port (default defined in McpSettings) to allow the TypeScript server to broadcast its availability and enable automatic reconnection without manual IP configuration【/cache/repos/github.com/isuzu-shiranui/unitymcp/main/jp.shiranui-isuzu.unity-mcp/Editor/Core/McpServer.cs#L165-L176】.
How are Unity main-thread operations handled in the architecture?
All handler invocations that require Unity API access are queued to a mainThreadQueue managed by McpServer. The background TCP thread adds work items via ExecuteOnMainThread, then blocks waiting for completion (with a 5-second timeout). This pattern ensures that scene manipulation, asset loading, and other Unity-specific operations occur safely on the main thread while the server remains responsive to network events.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →