# How the MCP Server Handles Unity Domain Reload and Script Compilation

> Learn how the MCP server handles Unity domain reloads and script compilation. Discover automatic reconnection, state persistence, and expected disconnect management for a seamless workflow.

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

---

**The MCP server treats domain reload disconnects as expected events, implements automatic reconnection with a 20-second timeout, and persists job state to survive Unity's scripting domain teardown.**

When Unity recompiles scripts or reloads its scripting domain, the entire Editor process restarts the internal C# runtime, wiping static state and closing every TCP socket. The **MCP server** (the Python side of the bridge in the CoplayDev/unity-mcp repository) must survive this abrupt connection loss, re-establish a fresh session, and continue serving the same logical client without losing pending work.

## Understanding Unity Domain Reload Impact

A **domain reload** occurs whenever Unity recompiles scripts, imports assets, or explicitly resets its scripting environment. This process destroys all static variables, closes active network connections, and resets the Unity Editor API. For the MCP bridge, this means the Python server loses its TCP socket or WebSocket connection to Unity instantly.

The server handles this not as a fatal error, but as a routine operational condition. Both the legacy TCP bridge in [`Server/src/transport/legacy/unity_connection.py`](https://github.com/CoplayDev/unity-mcp/blob/main/Server/src/transport/legacy/unity_connection.py) (around line 134) and the WebSocket hub in [`Server/src/transport/plugin_hub.py`](https://github.com/CoplayDev/unity-mcp/blob/main/Server/src/transport/plugin_hub.py) (around line 833) explicitly recognize this disconnection pattern. By treating the closed socket as expected rather than exceptional, the server avoids crashing and enters recovery mode immediately.

## Three-Layer Resilience Strategy

The MCP server implements three coordinated mechanisms to maintain continuity across domain reloads.

### Graceful TCP and WebSocket Teardown

When Unity initiates a reload, every open socket closes. The transport layer detects this condition immediately. In [`unity_connection.py`](https://github.com/CoplayDev/unity-mcp/blob/main/unity_connection.py), the connection logic monitors for socket closure specifically during the reload window. Similarly, [`plugin_hub.py`](https://github.com/CoplayDev/unity-mcp/blob/main/plugin_hub.py) handles the WebSocket disconnect without raising fatal exceptions. This graceful teardown prevents resource leaks and leaves the server in a clean state ready for reconnection.

### Automatic Reconnection with Back-Off

After detecting a disconnect, the Python side enters a retry loop with a capped wait time of **20 seconds**. This window covers the typical 10–20 second compilation period for scripts and assets. The reconnection logic appears in [`unity_connection.py`](https://github.com/CoplayDev/unity-mcp/blob/main/unity_connection.py) (lines 815–819) and [`plugin_hub.py`](https://github.com/CoplayDev/unity-mcp/blob/main/plugin_hub.py) (lines 846–849), where the server attempts to reconnect with exponential back-off until the timeout expires.

```python
async def ensure_connection():
    timeout = time.time() + 20   # 20-second retry window

    while not unity_bridge.is_connected():
        try:
            await unity_bridge.connect()
        except ConnectionError:
            await asyncio.sleep(0.5)   # back-off

        if time.time() > timeout:
            raise RuntimeError("Failed to reconnect after domain reload")

```

### State Persistence Across Reloads

Any state that must survive a reload is stored in Unity's `SessionState` or on disk. Tools that perform long-running work (such as `RefreshUnity`, `RunTests`, or custom user tools) persist their progress via `McpJobStateStore`. This mechanism, documented in [`website/docs/guides/custom-tools.md`](https://github.com/CoplayDev/unity-mcp/blob/main/website/docs/guides/custom-tools.md) (line 207), ensures that when the connection re-establishes, the server can resume the previous job rather than starting over.

```csharp
// C# helper used by long-running tools

McpJobStateStore.Save(jobId, jobState);   // writes into Library/

```

## Script Compilation Workflow

When editing scripts through MCP tools like `manage_script` or `script_apply_edits`, the server follows a specific pattern to handle the inevitable domain reload.

### Detecting Compilation Start

The client polls the `editor_state` resource to monitor the `isCompiling` flag. This allows the server to know exactly when Unity enters the compilation phase. The documentation in [`website/docs/reference/tools/core/manage_script.md`](https://github.com/CoplayDev/unity-mcp/blob/main/website/docs/reference/tools/core/manage_script.md) (line 87) outlines this polling pattern.

```python

# Python side – typical pattern used by many tools

while True:
    editor_state = await read_resource("editor_state")
    if not editor_state["isCompiling"]:
        break
    await asyncio.sleep(0.2)   # brief poll interval

```

### Waiting for Domain Reload

Tools do not assume the connection remains alive during compilation. Instead, they rely on the reconnection logic described above. The `RefreshUnity` tool explicitly tolerates disconnects during compile requests, logging them as expected events rather than errors (see [`Server/src/services/tools/refresh_unity.py`](https://github.com/CoplayDev/unity-mcp/blob/main/Server/src/services/tools/refresh_unity.py), lines 78–84, and the C# counterpart [`RefreshUnity.cs`](https://github.com/CoplayDev/unity-mcp/blob/main/RefreshUnity.cs)).

### Verifying Success Post-Reload

After the reload completes, the server reads the Unity console via `read_console` to surface any compilation errors before returning success. This verification step appears in [`Server/src/services/tools/manage_script.py`](https://github.com/CoplayDev/unity-mcp/blob/main/Server/src/services/tools/manage_script.py) (line 331) and ensures that callers receive a definitive result only after the domain has stabilized. The final response includes a "verified after domain reload" flag, making the post-reload success explicit (lines 331, 435, 470 in [`manage_script.py`](https://github.com/CoplayDev/unity-mcp/blob/main/manage_script.py)).

## Unity Plugin Integration

The Unity-side plugin participates in reload handling through defensive coding practices. Static fields clear automatically on reload, so tools that rely on static caches (such as `BuildJob` and `CommandRegistry`) guard against half-loaded types ([`CommandRegistry.cs`](https://github.com/CoplayDev/unity-mcp/blob/main/CommandRegistry.cs), line 116). UI windows including `MCPForUnityEditorWindow` and `McpConnectionSection` prevent duplicated `CreateGUI` calls and re-initialize their transport selection after a reload (lines 149 and 327–332 in the Editor Windows directory).

## Integration Testing for Reliability

The integration test [`Server/tests/integration/test_domain_reload_resilience.py`](https://github.com/CoplayDev/unity-mcp/blob/main/Server/tests/integration/test_domain_reload_resilience.py) validates this resilience by simulating dozens of concurrent MCP calls while a script is created (forcing a domain reload). The test asserts that every request either succeeds or is retried transparently, confirming the server survives the brief period when Unity's bridge is unavailable.

## Summary

- **Domain reloads are expected**: The MCP server treats Unity's scripting domain teardown as a normal operational condition rather than a connection failure.
- **Automatic reconnection**: The Python server implements a 20-second retry window with back-off to cover typical compilation periods.
- **State persistence**: Long-running jobs use `McpJobStateStore` to save progress to disk, enabling seamless continuation after reconnect.
- **Script workflow**: Tools like `manage_script` poll `isCompiling`, tolerate disconnects, and verify console output post-reload to ensure success.
- **Cross-platform resilience**: Both legacy TCP and WebSocket transports implement graceful teardown and reconnection logic.

## Frequently Asked Questions

### How long does the MCP server wait to reconnect after a domain reload?

The server caps its reconnection attempts at **20 seconds**. This timeout, implemented in [`unity_connection.py`](https://github.com/CoplayDev/unity-mcp/blob/main/unity_connection.py) (lines 815–819) and [`plugin_hub.py`](https://github.com/CoplayDev/unity-mcp/blob/main/plugin_hub.py) (lines 846–849), covers the typical 10–20 second window required for Unity to recompile scripts and reload the domain.

### What happens to active MCP jobs when Unity recompiles scripts?

Active jobs persist their state using `McpJobStateStore`, which writes to Unity's `SessionState` or disk. When the connection re-establishes after the reload, the server retrieves this persisted state and resumes the job from where it left off rather than restarting.

### Does the MCP server crash when Unity closes the socket during compilation?

No. The server explicitly recognizes the socket closure as an expected event during domain reload. Both the legacy TCP bridge ([`unity_connection.py`](https://github.com/CoplayDev/unity-mcp/blob/main/unity_connection.py), line 134) and WebSocket hub ([`plugin_hub.py`](https://github.com/CoplayDev/unity-mcp/blob/main/plugin_hub.py), line 833) handle this gracefully without entering fatal exception paths.

### How do MCP tools verify that script edits succeeded after a domain reload?

Tools like `manage_script` poll the `editor_state` resource to detect when `isCompiling` returns to false. After reconnection, the server reads the Unity console via `read_console` to check for compilation errors before returning a success response flagged as "verified after domain reload."