# How UDP Discovery Enables Automatic Server Detection in Unity MCP

> Discover how Unity MCP uses UDP broadcast discovery on port 27183 to automatically find TypeScript MCP servers without needing manual IP addresses. Learn how JSON announcements enable seamless TCP connections and simplify serve...

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

---

**Unity MCP uses UDP broadcast discovery on port 27183 to automatically detect running TypeScript MCP servers without manual IP configuration, parsing JSON announcements to establish TCP connections.**

The Unity MCP package (isuzu-shiranui/unitymcp) implements zero-configuration networking through UDP discovery. This mechanism allows the Unity Editor to locate and connect to MCP servers dynamically, eliminating the need for developers to hardcode host addresses or ports. The implementation spans the settings system and core server infrastructure, utilizing asynchronous socket operations for non-blocking network communication.

## How UDP Discovery Works in Unity MCP

The discovery system operates through a broadcast listener that parses JSON packets to extract connection parameters. When enabled, the `McpServer` class maintains a background UDP listener that updates connection settings in real-time.

### Settings Configuration in McpSettings.cs

Discovery behavior is controlled through the `McpSettings` ScriptableObject, which persists editor preferences. The `useUdpDiscovery` boolean flag enables the feature, while `udpDiscoveryPort` defines the broadcast port (default 27183).

```csharp
// jp.shiranui-isuzu.unity-mcp/Editor/Settings/McpSettings.cs
[SerializeField] public bool useUdpDiscovery = true;      // Enable discovery
[SerializeField] public int udpDiscoveryPort = 27183;     // Port for broadcast packets

```

These settings are checked during `McpServer` initialization. When `useUdpDiscovery` is true, the server automatically invokes `StartUdpListener()` to begin monitoring for broadcasts.

### Starting the UDP Listener in McpServer.cs

The `StartUdpListener()` method in [`Editor/Core/McpServer.cs`](https://github.com/isuzu-shiranui/unitymcp/blob/main/Editor/Core/McpServer.cs) initializes a reusable UDP socket bound to all network interfaces. It configures the `UdpClient` with `SO_REUSEADDR` to prevent port binding conflicts and begins an asynchronous receive loop.

```csharp
// jp.shiranui-isuzu.unity-mcp/Editor/Core/McpServer.cs
private void StartUdpListener() {
    lock (this.udpLock) {
        if (this.isUdpListening) return;

        this.udpListener = new UdpClient();
        this.udpListener.Client.SetSocketOption(
            SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
        this.udpListener.Client.Bind(new IPEndPoint(IPAddress.Any, this.broadcastPort));
        this.isUdpListening = true;

        // Async callback for every packet
        this.udpListener.BeginReceive(this.ReceiveCallback, this.udpListener);
    }
}

```

This implementation uses `IPAddress.Any` to listen on all available network interfaces, ensuring the editor receives broadcasts regardless of which network adapter the MCP server uses.

### Processing Broadcast Packets via ReceiveCallback

When a UDP datagram arrives, the `ReceiveCallback` method decodes the UTF-8 payload and parses it as JSON. The system distinguishes between legacy announcements and modern MCP-TS broadcasts using the `type` field.

```csharp
private void ReceiveCallback(IAsyncResult ar) {
    // ... receive data into `data` ...

    var jsonStr = Encoding.UTF8.GetString(data);
    var serverInfo = JObject.Parse(jsonStr);
    var messageType = serverInfo["type"]?.ToString();

    switch (messageType) {
        case "listClients":
            // Legacy path – updates host/port and tries to connect
            break;
        case "mcp_server_announce":
            // Modern MCP-TS announcement
            var host = serverInfo["host"]?.ToString();
            var port = serverInfo["port"]?.Value<int>() ?? 0;
            var version = serverInfo["version"]?.ToString();

            if (!string.IsNullOrEmpty(host) && port > 0) {
                ExecuteOnMainThread(() => {
                    this.host = host;
                    this.port = port;
                    this.TryConnect();      // Initiate TCP connection
                });
            }
            break;
    }
}

```

The callback extracts three critical fields from the announcement: `host` (string), `port` (integer), and `version` (string). Valid packets trigger a main-thread update to prevent Unity API threading violations.

### Establishing the TCP Connection

All UI-thread sensitive operations execute through `ExecuteOnMainThread`, which marshals the connection update to Unity's main execution loop. The `TryConnect()` method then initiates the TCP handshake to the advertised endpoint, raising the `Connected` event upon successful establishment.

This architecture ensures that network I/O remains asynchronous while Unity-specific operations (updating inspector fields, logging) occur on the appropriate thread.

### Cleanup and Shutdown

When the editor shuts down or the server stops, `StopUdpListener()` closes the UDP socket and halts the background receive loop. This method is invoked from the `McpServer` destructor or `Dispose()` pattern to prevent resource leaks.

```csharp
private void StopUdpListener() {
    lock (this.udpLock) {
        if (!this.isUdpListening) return;
        this.udpListener?.Close();
        this.isUdpListening = false;
        this.udpListener = null;
    }
}

```

The `lock` statement ensures thread-safe cleanup even if a packet arrives during shutdown.

## Practical Implementation Examples

### Enabling Discovery via Editor Script

Toggle UDP discovery programmatically by modifying the settings instance:

```csharp
using UnityMCP.Editor.Settings;

public static void SetDiscovery(bool enabled) {
    McpSettings.instance.useUdpDiscovery = enabled;
    McpSettings.instance.Save();
}

```

Changes persist to disk via Unity's ScriptableObject serialization system and take effect the next time `McpServer` initializes.

### Manual Server Startup with Discovery

Instantiate the server directly to trigger automatic discovery:

```csharp
using UnityMCP.Editor.Core;

// Reads settings, starts UDP listener if enabled
var server = new McpServer();
server.Start();  // Begins TCP connection attempts when broadcasts arrive

```

The constructor automatically checks `McpSettings.instance.useUdpDiscovery` and invokes `StartUdpListener()` when the flag is true.

### Simulating Server Broadcasts for Testing

Send fake announcements to test client behavior without a running TypeScript server:

```csharp
using System.Net;
using System.Net.Sockets;
using System.Text;
using Newtonsoft.Json.Linq;

void SendFakeAnnouncement(string host, int port) {
    var socket = new UdpClient();
    socket.EnableBroadcast = true;
    var payload = new JObject {
        ["type"] = "mcp_server_announce",
        ["host"] = host,
        ["port"] = port,
        ["version"] = "1.0.0"
    };
    var bytes = Encoding.UTF8.GetBytes(payload.ToString());
    socket.Send(bytes, bytes.Length, new IPEndPoint(IPAddress.Broadcast, 27183));
    socket.Close();
}

```

This utility is useful for integration testing or verifying that the Unity client correctly parses JSON payloads and updates connection parameters.

## Summary

- **UDP discovery is enabled by default** via `McpSettings.useUdpDiscovery` and listens on port 27183 as defined in [`Editor/Settings/McpSettings.cs`](https://github.com/isuzu-shiranui/unitymcp/blob/main/Editor/Settings/McpSettings.cs).
- **The `StartUdpListener()` method** in [`Editor/Core/McpServer.cs`](https://github.com/isuzu-shiranui/unitymcp/blob/main/Editor/Core/McpServer.cs) binds to `IPAddress.Any` with socket reuse enabled to prevent port conflicts.
- **JSON broadcasts** with `type: "mcp_server_announce"` trigger automatic TCP connection attempts via `TryConnect()` after marshaling to the main thread.
- **Thread safety** is maintained through `ExecuteOnMainThread` for Unity API operations and `lock` statements for UDP socket management.
- **Cleanup occurs** through `StopUdpListener()`, which closes the `UdpClient` and terminates the async receive loop when the editor shuts down.

## Frequently Asked Questions

### What port does Unity MCP use for UDP discovery?

Unity MCP uses **port 27183** by default for UDP broadcast discovery. This value is configurable through the `udpDiscoveryPort` field in [`McpSettings.cs`](https://github.com/isuzu-shiranui/unitymcp/blob/main/McpSettings.cs). The server binds to this port with `SO_REUSEADDR` enabled, allowing multiple instances or rapid restarts without "address already in use" errors.

### How does the Unity editor handle UDP packets on the main thread?

The `ReceiveCallback` method uses `ExecuteOnMainThread` to marshal connection updates onto Unity's main execution loop. This pattern prevents threading violations since Unity API operations (such as updating inspector values or logging) must occur on the main thread, while the UDP listener operates on a background thread pool.

### Can UDP discovery be disabled if I want manual configuration?

Yes. Set `McpSettings.instance.useUdpDiscovery = false` and call `Save()`. When disabled, the `McpServer` constructor skips the `StartUdpListener()` call, and you must manually configure the `host` and `port` fields before calling `Start()`. This is useful for connecting to remote servers outside the local broadcast domain.

### What JSON format does the MCP server broadcast?

The TypeScript MCP server broadcasts a JSON object containing at minimum: `{"type": "mcp_server_announce", "host": "127.0.0.1", "port": 8080, "version": "1.0.0"}`. The Unity client specifically looks for the `mcp_server_announce` type to distinguish modern announcements from legacy `listClients` packets. Missing or invalid fields (null host or zero port) cause the packet to be ignored.