How Multi-Instance Routing Works in Unity MCP: A Complete Technical Guide
Unity MCP routes requests to specific Unity editor instances using a session-based middleware that derives stable keys from client_id or user credentials, maintains per-session active instance mappings, and validates explicit unity_instance arguments before injecting resolved identifiers into the request context.
The Unity MCP server from CoplayDev/unity-mcp supports concurrent connections to multiple Unity editor instances, enabling collaborative workflows where different users or projects require isolated execution contexts. This multi-instance routing system is implemented in the UnityInstanceMiddleware class, which deterministically maps each incoming tool call to the correct Unity process based on session identity and automatic instance discovery.
Understanding the UnityInstanceMiddleware Architecture
The core routing logic resides in Server/src/transport/unity_instance_middleware.py. This middleware intercepts all tool calls, resource requests, and tool listing operations to provide a consistent abstraction over both std-IO (single-process, local) and HTTP (multi-user, remote) deployment modes. The middleware maintains an internal dictionary _active_by_key that maps session keys to selected Unity instance identifiers formatted as Name@hash.
Session Key Derivation and Client Isolation
The routing algorithm begins with stable session identification. According to the source code, the get_session_key method (lines 71-88) implements a three-tier fallback strategy to ensure deterministic routing even in varying deployment contexts.
The Three-Tier Fallback Strategy
When resolving a session key, the middleware evaluates the request context in the following priority order:
- Explicit
client_id– If the request context contains a non-emptyclient_idfield, this string is used directly as the session key. - Remote user identity – In HTTP mode with remote hosting enabled (controlled by flags in
core/config.py), the middleware derives the key from the API key'suser_id, prefixing it asuser:<id>. - Global fallback – For single-user local installations, the literal string
"global"serves as the default session key.
This hierarchy ensures that multi-tenant deployments achieve proper isolation while maintaining simplicity for local development.
Active Instance Management
Once the session key is established, the middleware manages the relationship between the client and Unity instances through a structured lifecycle.
Per-Session Instance Mapping
The middleware stores active instance references in _active_by_key, a dictionary keyed by session identifiers. Three methods govern this state:
set_active_instance(ctx, instance_id)– Stores the mapping between session andName@hashidentifier (lines 91-107).get_active_instance(ctx)– Retrieves the currently selected instance for the session.clear_active_instance(ctx)– Removes the association, allowing the client to switch projects or instances.
Auto-Selection and Discovery
When no active instance is recorded for a session, the middleware triggers automatic discovery via _discover_instances (lines 109-146). The implementation branches based on transport mode:
- HTTP mode: Queries
PluginHub.get_sessions(defined intransport/plugin_hub.py) to enumerate connected Unity sessions via the Plugin Hub service. - StdIO mode: Invokes
transport.legacy.unity_connection.get_unity_connection_pool().discover_all_instances()to find local Unity processes.
If exactly one instance is discovered, the _maybe_autoselect_instance method (lines 27-66) automatically selects it, stores the mapping via set_active_instance, and logs the selection for the session.
Instance Resolution and Validation
Clients can explicitly target specific Unity instances using the unity_instance argument, which undergoes rigorous validation before execution.
Explicit Instance Arguments
Tool calls may specify a target instance through three formats accepted by _resolve_instance_value (lines 153-224):
- Raw port number (StdIO mode only) – Automatically resolved to the corresponding
Name@hashidentifier. - Full identifier – Complete
Name@hashstring matching the discovered instance. - Hash prefix – Short prefix that must uniquely match exactly one running instance.
If validation fails, the middleware raises descriptive errors preventing ambiguous or invalid routing.
Context Injection for Tool Execution
Before any tool handler executes, the middleware invokes _inject_unity_instance to prepare the execution context. This method:
- Resolves the session key using the fallback strategy.
- Checks for explicit
unity_instancearguments and validates them. - Falls back to the stored active instance or auto-selected default.
- Injects the resolved identifier into FastMCP state via
ctx.set_state("unity_instance", ...). - In HTTP mode, additionally resolves and stores the
unity_session_idfor Plugin Hub communication.
This injection mechanism ensures that tool implementations receive consistent instance identifiers without manual session management.
Tool Visibility Filtering by Instance
Multi-instance routing extends beyond request handling to tool discovery. The on_list_tools method filters the available toolset based on the active Unity instance and its project hash. By querying PluginHub.get_tools_for_project and comparing against _resolve_enabled_tool_names_for_context (lines 70-96), the middleware exposes only tools actually registered in the target Unity session.
This prevents "orphaned" tools—resources defined in other Unity instances—from appearing in the client's tool list, ensuring type safety and preventing execution errors.
Code Examples for Multi-Instance Routing
The following patterns demonstrate how to interact with the routing system from Python tool implementations.
Setting an active instance programmatically:
from services.middleware import get_unity_instance_middleware
async def select_project_tool(ctx, project_hash: str) -> dict:
middleware = get_unity_instance_middleware()
await middleware.set_active_instance(ctx, f"MyProject@{project_hash}")
return {"status": "instance selected", "target": project_hash}
Targeting a specific instance via tool arguments:
{
"tool_name": "manage_scene",
"arguments": {
"action": "create",
"unity_instance": "MyProject@a1b2c3d4"
}
}
Retrieving the resolved instance within tool execution:
async def execute_scene_operation(ctx, ...) -> dict:
unity_id = await ctx.get_state("unity_instance")
# unity_id contains the resolved Name@hash identifier
return {"unity_instance": unity_id, "result": "success"}
Clearing the active instance to switch contexts:
middleware = get_unity_instance_middleware()
await middleware.clear_active_instance(ctx)
Summary
- Unity MCP implements multi-instance routing through the
UnityInstanceMiddlewareclass inServer/src/transport/unity_instance_middleware.py. - Session keys derive from
client_id,user_id(asuser:<id>), or fall back to"global"for local deployments. - The
_active_by_keydictionary maintains per-session mappings to Unity instances formatted asName@hash. - Automatic discovery via
_discover_instancessupports both HTTP (Plugin Hub) and StdIO (legacy connection pool) modes. - Explicit
unity_instancearguments undergo validation in_resolve_instance_value, accepting ports, full identifiers, or hash prefixes. - The middleware injects resolved instance IDs into request context via
ctx.set_state("unity_instance", ...)before tool execution. - Tool listings are filtered by project hash to prevent cross-instance pollution.
Frequently Asked Questions
How does Unity MCP handle multiple Unity editors simultaneously?
Unity MCP uses a session-based routing system where each client connection receives a unique session key. The UnityInstanceMiddleware maintains separate active instance mappings for each session in _active_by_key, allowing different users to target different Unity editors concurrently. When running in HTTP mode, the system leverages the Plugin Hub service (transport/plugin_hub.py) to discover and manage multiple sessions, while std-IO mode uses the legacy connection pool for single-machine deployments.
What is the difference between HTTP mode and StdIO mode for instance discovery?
In HTTP mode, _discover_instances queries PluginHub.get_sessions to retrieve connected Unity sessions from a central hub service, enabling remote multi-user scenarios. In std-IO mode, the middleware calls transport.legacy.unity_connection.get_unity_connection_pool().discover_all_instances() to scan for local Unity processes. HTTP mode supports user-based isolation via user:<id> session keys, while std-IO typically operates with a global session key for single-user local development.
How do I specify which Unity instance to target in a tool call?
You can provide the unity_instance argument in your tool call payload, which accepts three formats: a raw port number (stdio only), a full Name@hash string, or a unique hash prefix. The middleware validates this input in _resolve_instance_value (lines 153-224) before routing. Alternatively, use set_active_instance to persistently set a default instance for your session, which subsequent calls will use automatically.
What happens if no Unity instance is explicitly selected?
If no active instance is stored for the session and no unity_instance argument is provided, the middleware executes _maybe_autoselect_instance. If exactly one Unity instance is discovered through the discovery mechanism, it is automatically selected and associated with the session. If multiple instances are running without explicit selection, the system raises an error requiring the user to specify the target instance.
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 →