# How Multi-Instance Routing Works in Unity MCP: A Complete Technical Guide

> Unlock multi-instance routing in Unity MCP with this technical guide. Discover how session-based middleware and stable keys ensure efficient request routing to the correct Unity editor instance.

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

---

**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`](https://github.com/CoplayDev/unity-mcp/blob/main/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:

1. **Explicit `client_id`** – If the request context contains a non-empty `client_id` field, this string is used directly as the session key.
2. **Remote user identity** – In HTTP mode with remote hosting enabled (controlled by flags in [`core/config.py`](https://github.com/CoplayDev/unity-mcp/blob/main/core/config.py)), the middleware derives the key from the API key's `user_id`, prefixing it as `user:<id>`.
3. **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 and `Name@hash` identifier (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 in [`transport/plugin_hub.py`](https://github.com/CoplayDev/unity-mcp/blob/main/transport/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):

1. **Raw port number** (StdIO mode only) – Automatically resolved to the corresponding `Name@hash` identifier.
2. **Full identifier** – Complete `Name@hash` string matching the discovered instance.
3. **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:

1. Resolves the session key using the fallback strategy.
2. Checks for explicit `unity_instance` arguments and validates them.
3. Falls back to the stored active instance or auto-selected default.
4. Injects the resolved identifier into FastMCP state via `ctx.set_state("unity_instance", ...)`.
5. In HTTP mode, additionally resolves and stores the `unity_session_id` for 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:

```python
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:

```json
{
  "tool_name": "manage_scene",
  "arguments": {
    "action": "create",
    "unity_instance": "MyProject@a1b2c3d4"
  }
}

```

Retrieving the resolved instance within tool execution:

```python
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:

```python
middleware = get_unity_instance_middleware()
await middleware.clear_active_instance(ctx)

```

## Summary

- Unity MCP implements multi-instance routing through the `UnityInstanceMiddleware` class in [`Server/src/transport/unity_instance_middleware.py`](https://github.com/CoplayDev/unity-mcp/blob/main/Server/src/transport/unity_instance_middleware.py).
- Session keys derive from `client_id`, `user_id` (as `user:<id>`), or fall back to `"global"` for local deployments.
- The `_active_by_key` dictionary maintains per-session mappings to Unity instances formatted as `Name@hash`.
- Automatic discovery via `_discover_instances` supports both HTTP (Plugin Hub) and StdIO (legacy connection pool) modes.
- Explicit `unity_instance` arguments 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`](https://github.com/CoplayDev/unity-mcp/blob/main/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.