# How UnityInstanceMiddleware Enables Session-Based Routing in MCP

> Learn how UnityInstanceMiddleware enables session-based routing in MCP. It maps client IDs to Unity instance identifiers for isolated, concurrent request routing in multiple Unity Editor sessions.

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

---

**UnityInstanceMiddleware is a FastMCP middleware that maintains a session-scoped mapping of client IDs to Unity instance identifiers, injecting the active instance into each request's context to enable isolated, concurrent routing across multiple Unity Editor sessions.**

The CoplayDev/unity-mcp repository implements a FastMCP-based bridge between AI assistants and Unity Editor instances. At the core of this architecture lies the **UnityInstanceMiddleware**, which solves the critical challenge of routing MCP tool calls to the correct Unity session when multiple editors or projects are active simultaneously.

## Middleware Architecture and Registration

UnityInstanceMiddleware sits between every MCP tool or resource invocation and the underlying Unity instance. According to the architecture documentation in [`website/docs/architecture/remote-auth.md`](https://github.com/CoplayDev/unity-mcp/blob/main/website/docs/architecture/remote-auth.md) (lines 119-124), this middleware registers itself in the FastMCP middleware chain to intercept requests before they reach tool handlers. This positioning allows the middleware to inspect the incoming request context and determine which Unity instance should handle the operation.

## Session-Based Instance Resolution

### Computing the Session Key

The middleware computes a unique session key via `get_session_key(ctx)`, which prioritizes the `client_id` supplied by the remote HTTP transport or falls back to the Stdio session ID. As implemented in the test suite at [`Server/tests/test_transport_characterization.py`](https://github.com/CoplayDev/unity-mcp/blob/main/Server/tests/test_transport_characterization.py) (lines 124-131), this approach ensures that two concurrent clients maintain distinct active instances even when targeting the same Unity Editor pool. The session key derivation ensures that requests from different clients never share instance state unless explicitly configured.

### Thread-Safe Storage Mechanism

Internally, the middleware uses an async-safe in-memory dictionary keyed by session keys. The test suite validates concurrent updates at [`Server/tests/test_transport_characterization.py`](https://github.com/CoplayDev/unity-mcp/blob/main/Server/tests/test_transport_characterization.py) (lines 190-206), confirming that the middleware safely handles parallel requests from multiple agents without race conditions. The last-write-wins semantics ensure that the most recent instance assignment for a given session is always the one used for routing.

## Active Instance Lifecycle Management

The middleware exposes explicit methods for managing the session-to-instance mapping. When a client attaches to a specific Unity project, the bridge calls `set_active_instance(context, instance_id)`, which stores the mapping in the session-scoped store. Conversely, `clear_active_instance(context)` removes the entry when a client disconnects or switches projects. Tests in [`Server/tests/test_transport_characterization.py`](https://github.com/CoplayDev/unity-mcp/blob/main/Server/tests/test_transport_characterization.py) (lines 173-188) verify that instances can be set, retrieved, and cleared correctly per session, ensuring clean isolation between client connections.

## Request Context Injection

### Per-Request Instance Injection

For every tool or resource invocation, the middleware's `on_call_tool` and `on_read_resource` hooks invoke the internal `_inject_unity_instance` method. This method looks up the session key and, if an instance mapping exists, injects it into the FastMCP context at `fastmcp_context.state["unity_instance"] = instance_id` (referenced in [`Server/tests/test_transport_characterization.py`](https://github.com/CoplayDev/unity-mcp/blob/main/Server/tests/test_transport_characterization.py), lines 216-224). Tool implementations then read this value to determine which Unity editor to communicate with.

### Fallback to Global Instance

If no instance is stored for the current session, the middleware passes the request through without modification, allowing the tool to fall back to a global default instance. This behavior ensures backward compatibility for single-instance deployments while enabling multi-session support when explicitly configured.

## Tool Visibility and Project Filtering

Because the middleware executes before tool visibility checks, it can filter available tools based on the active project configuration. As noted in [`Server/tests/test_transport_characterization.py`](https://github.com/CoplayDev/unity-mcp/blob/main/Server/tests/test_transport_characterization.py) (lines 336-352), the middleware integrates with `PluginHub.get_tools_for_project` to ensure clients only see tools belonging to the specific Unity instance they are attached to. This prevents cross-contamination between projects and ensures that tool schemas match the capabilities of the targeted Unity version.

## Implementation Example

The following pattern demonstrates how to configure the middleware and manage instance routing in practice:

```python
from unity_mcp.transport import UnityInstanceMiddleware

# Initialize the middleware

middleware = UnityInstanceMiddleware()

# Set the active instance after client selects a project

await middleware.set_active_instance(ctx, "MyProject@hash123")

# Inside a tool handler, retrieve the injected instance

instance_id = ctx.state.get("unity_instance")

# Use instance_id to route commands to the correct Unity editor

# Clean up when client disconnects

await middleware.clear_active_instance(ctx)

```

## Summary

- **UnityInstanceMiddleware** registers with FastMCP to intercept all tool and resource calls before they reach handlers.
- **Session keys** are derived from `client_id` or transport session IDs, ensuring isolated routing per client connection.
- **Thread-safe storage** uses an async-safe dictionary keyed by session keys, validated for concurrent access.
- **Automatic injection** populates `ctx.state["unity_instance"]` via `_inject_unity_instance`, allowing tools to route commands correctly.
- **Lifecycle methods** `set_active_instance` and `clear_active_instance` enable dynamic attachment and detachment from Unity projects.
- **Tool filtering** ensures clients only see tools compatible with their active Unity instance.

## Frequently Asked Questions

### What is UnityInstanceMiddleware?

UnityInstanceMiddleware is a FastMCP middleware component in the CoplayDev/unity-mcp repository that manages the relationship between MCP client sessions and Unity Editor instances. It maintains a session-scoped mapping and injects the correct instance identifier into each request's context, enabling multiple AI assistants to work with different Unity projects simultaneously.

### How does session isolation work between concurrent clients?

The middleware computes a unique session key via `get_session_key(ctx)`, which prioritizes the `client_id` from the HTTP transport or Stdio session. Each client's requests are routed to their own slot in the internal storage dictionary, meaning two clients can maintain different active instances against the same Unity Editor pool without interfering with each other.

### Can multiple clients connect to the same Unity instance?

Yes, while the default behavior isolates clients by session key, the middleware supports scenarios where multiple clients share an instance by explicitly setting the same instance ID via `set_active_instance`. The routing logic simply retrieves whatever instance ID is stored for that client's session key, allowing both shared and isolated usage patterns.

### What happens if no Unity instance is set for the current session?

If `_inject_unity_instance` finds no mapping for the computed session key, the middleware passes the request through without modification. This allows the tool implementation to fall back to a global default instance, ensuring backward compatibility for deployments where only one Unity instance is available.