How UDP Discovery Enables Automatic Server Detection in Unity MCP
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).
// 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 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.
// 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.
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.
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:
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:
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:
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.useUdpDiscoveryand listens on port 27183 as defined inEditor/Settings/McpSettings.cs. - The
StartUdpListener()method inEditor/Core/McpServer.csbinds toIPAddress.Anywith socket reuse enabled to prevent port conflicts. - JSON broadcasts with
type: "mcp_server_announce"trigger automatic TCP connection attempts viaTryConnect()after marshaling to the main thread. - Thread safety is maintained through
ExecuteOnMainThreadfor Unity API operations andlockstatements for UDP socket management. - Cleanup occurs through
StopUdpListener(), which closes theUdpClientand 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. 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.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →