# How Unity MCP Implements TCP/IP Communication with TypeScript MCP Servers

> Discover how Unity MCP implements TCP/IP communication with TypeScript MCP servers using a lightweight JSON protocol for seamless data exchange and automatic reconnection.

- Repository: [いすず/unitymcp](https://github.com/isuzu-shiranui/unitymcp)
- Tags: internals
- Published: 2026-03-04

---

**The Unity-MCP package establishes a persistent TCP connection to a companion TypeScript MCP server, using a lightweight newline-delimited JSON protocol over a socket with automatic reconnection, UDP discovery, and thread-safe dispatch to Unity's main thread.**

The [unitymcp](https://github.com/isuzu-shiranui/unitymcp) repository provides a bridge between the Unity Editor and Model Context Protocol (MCP) servers written in TypeScript. Understanding how this C# client manages **TCP/IP communication between Unity and TypeScript MCP server** implementations is essential for building reliable editor extensions that require live command execution and resource fetching.

## UDP Server Discovery and TCP Connection Establishment

The communication workflow begins with optional discovery via UDP broadcast. In [`jp.shiranui-isuzu.unity-mcp/Editor/Core/McpServer.cs`](https://github.com/isuzu-shiranui/unitymcp/blob/main/jp.shiranui-isuzu.unity-mcp/Editor/Core/McpServer.cs), the client initializes a UDP listener on port `27183` (defined in `McpSettings` as `broadcastPort`). When a TypeScript MCP server broadcasts a `mcp_server_announce` message, the listener extracts the host and port, updates the internal configuration, and immediately invokes `TryConnect()` to establish the TCP link.

The `TryConnect()` method (lines 58-70) creates a new `TcpClient`, initiates an asynchronous connection with a **5-second timeout**, and upon success stores the socket reference, raises the `Connected` event, and transmits a registration packet via `SendClientRegistration()`. This packet contains the client ID and basic Unity project metadata, identifying the editor instance to the MCP server.

## JSON Message Protocol and Request Routing

Once connected, a background thread `RunClient` continuously polls the socket for incoming data. The `ProcessIncomingData()` method (lines 85-92) reads the byte stream into a buffer, converts bytes to a UTF-8 string, and forwards the content to `ProcessData()` for parsing.

The protocol expects **newline-delimited JSON objects**. Each incoming message must contain a `"type"` field that determines routing logic:

- **Resource requests** (`"type": "resource"`) are delegated to registered `IMcpResourceHandler` implementations via `ProcessResourceRequest`
- **Command requests** are dispatched to matching `IMcpCommandHandler` implementations via `ExecuteCommand`

Both handlers are resolved based on command prefixes and action names extracted from the JSON payload.

## Thread Safety and Main Thread Execution

Unity API calls are not thread-safe, so the MCP server must marshal all handler execution to Unity's main thread. In [`McpServer.cs`](https://github.com/isuzu-shiranui/unitymcp/blob/main/McpServer.cs) (lines 87-95 and 124-132), both `ProcessResourceRequest` and `ExecuteCommand` wrap their logic in `ExecuteOnMainThread` delegates.

This ensures that when a TypeScript MCP server requests scene modifications, asset imports, or GameObject manipulations, the actual execution occurs on Unity's main thread while the background TCP thread remains responsive. After processing completes, the server serializes the response object to JSON, appends a newline delimiter, and writes it directly to the TCP stream via `stream.Write`.

## Automatic Reconnection with Exponential Backoff

The TCP client includes robust reconnection logic for handling network instability. If `TryConnect()` fails or the connection drops, the system triggers exponential back-off using the `currentReconnectDelay` field (lines 36-44 and 92-99).

The client repeatedly attempts reconnection at increasing intervals until the TCP link is restored. This ensures that temporary network interruptions or server restarts do not require manual intervention in the Unity Editor—the client automatically recovers and re-registers with the MCP server.

## Practical Implementation Examples

### Establishing the Client Connection

Normally initialized automatically by `McpEditorInitializer`, you can also manage the server manually:

```csharp
var server = new McpServer();   // Reads host/port from McpSettings
server.Start();                // Launches UDP listener and TCP client thread

```

### Sending a Custom Command from Unity

Construct a JSON command matching a registered handler prefix:

```csharp
var command = new JObject
{
    ["type"] = "command",
    ["command"] = "unity.log",
    ["params"] = new JObject { ["message"] = "Hello from Unity!" },
    ["id"] = Guid.NewGuid().ToString()
};

var bytes = Encoding.UTF8.GetBytes(command.ToString(Formatting.None) + "\n");
server.GetTcpClient().GetStream().Write(bytes, 0, bytes.Length);

```

### Receiving Command Responses

Subscribe to execution events to handle responses asynchronously:

```csharp
server.CommandExecuted += (s, e) =>
{
    Debug.Log($"Command '{e.Prefix}.{e.Action}' finished with result: {e.Result}");
};

```

### Registering a Custom Handler

Implement `IMcpCommandHandler` to expose Unity functionality to the MCP server:

```csharp
public class MyLogHandler : IMcpCommandHandler
{
    public string CommandPrefix => "unity";
    public string Description => "Handles Unity-specific commands";

    public JObject Execute(string action, JObject parameters)
    {
        if (action == "log")
        {
            Debug.Log(parameters["message"]?.ToString());
            return new JObject { ["status"] = "success" };
        }
        return new JObject { ["status"] = "error", ["message"] = "Unknown action" };
    }
}

// Register during editor initialization:
server.RegisterHandler(new MyLogHandler());

```

## Summary

- **Discovery**: UDP broadcast on port `27183` enables automatic server detection, with `TryConnect()` establishing the TCP socket
- **Protocol**: Newline-delimited JSON messages route to `IMcpCommandHandler` or `IMcpResourceHandler` based on the `"type"` field
- **Thread Safety**: All Unity API interactions execute via `ExecuteOnMainThread` to prevent cross-thread access violations
- **Resilience**: Exponential back-off reconnection logic maintains persistent connectivity without manual intervention
- **Key Files**: [`McpServer.cs`](https://github.com/isuzu-shiranui/unitymcp/blob/main/McpServer.cs) manages the socket, [`McpSettings.cs`](https://github.com/isuzu-shiranui/unitymcp/blob/main/McpSettings.cs) stores configuration, and handlers in `Editor/Handlers/` implement the business logic

## Frequently Asked Questions

### What port does Unity MCP use for TCP communication?

The TCP port is configurable via [`McpSettings.cs`](https://github.com/isuzu-shiranui/unitymcp/blob/main/McpSettings.cs) and typically discovered through UDP broadcast on port `27183`. If UDP discovery is disabled, the client falls back to manually configured host/port combinations stored in Unity Editor preferences.

### How does the MCP server handle Unity API thread safety?

All incoming commands and resource requests are queued to Unity's main thread using `ExecuteOnMainThread` before invoking handler methods. This ensures that `IMcpCommandHandler` implementations can safely manipulate GameObjects, assets, and scene data without causing Unity thread errors.

### What happens if the TypeScript MCP server disconnects unexpectedly?

The `McpServer` class implements automatic reconnection with exponential back-off. When the connection drops, the client enters a retry loop that increases the delay between attempts (`currentReconnectDelay`) until the TCP connection is re-established and the client re-registers automatically.

### Can I send custom commands from Unity to the TypeScript server?

Yes. While the typical flow involves the TypeScript server sending commands to Unity, you can write JSON payloads directly to the TCP stream obtained via `server.GetTcpClient().GetStream()`. Ensure you terminate each JSON object with a newline character to comply with the protocol delimiter expected by the TypeScript server.