Skip to content

Commit 73f2cc7

Browse files
committed
feat: implement WebSocket auto-restart after Unity domain reload and improved windsurfrules to better match this project needs
1 parent 2d40387 commit 73f2cc7

File tree

6 files changed

+85
-27
lines changed

6 files changed

+85
-27
lines changed

.windsurfrules

Lines changed: 61 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,64 @@
1-
This project is split in 2 parts:
2-
- The C# Unity Editor package in the Editor/ folder
3-
- The Node.js server in the Server/ folder.
1+
## 1. Project Structure
42

5-
Use WebSockets to communicate between the Editor and the Node.js server.
6-
For more documentation for the WebSocket library being used in Unity see https://github.com/sta/websocket-sharp/tree/master/websocket-sharp/Server
3+
- The project consists of two main parts:
4+
- Editor/: A C# Unity Editor package that exposes Unity Editor functionality via a WebSocket bridge.
5+
- Server/: A Node.js server implementing the Model Context Protocol (MCP), using the official TypeScript SDK.
76

8-
Minimum supported versions:
9-
- Unity 2022.3
10-
- Node.js 18.0.0
7+
## 2. Communication & Protocol
118

12-
The MCP Node.js server implements the typescript SDK from https://github.com/modelcontextprotocol/typescript-sdk
9+
- Communication between the Unity Editor and the Node.js MCP server is handled via WebSockets.
10+
- The Unity side uses the `websocket-sharp` library: https://github.com/sta/websocket-sharp/tree/master/websocket-sharp/Server
11+
- The Node.js server uses the `@modelcontextprotocol/sdk` for protocol implementation and exposes tools/resources to LLMs and AI clients.
12+
13+
## 3. Minimum Supported Versions
14+
15+
- Unity: 2022.3 or newer
16+
- Node.js: 18.0.0 or newer
17+
18+
## 4. Key Components
19+
20+
### Unity Editor Package (Editor/)
21+
22+
- **McpUnityServer**: Singleton entry point for the Unity-side server. Registers tools/resources, manages WebSocket connections, and handles requests from Node.js.
23+
- **McpUnitySocketHandler**: Handles WebSocket messages/events, dispatches tool/resource calls, and manages client connections.
24+
- **Tools**: Modular C# classes implementing various Unity actions (e.g., running tests, modifying the scene, package management).
25+
- **Resources**: Read-only endpoints for querying Unity state (e.g., assets, hierarchy, logs).
26+
- **Services**: Dependency-injected logic for test running, logging, etc., improving testability and maintainability.
27+
- **Utils**: Utility classes for configuration, logging, and workspace integration.
28+
29+
### Node.js Server (Server/)
30+
31+
- **index.ts**: Entry point. Sets up the MCP server, registers all tools/resources, and initializes the Unity bridge.
32+
- **unity/mcpUnity.js**: Handles low-level communication with the Unity Editor via WebSocket.
33+
- **tools/**: Implements MCP tool endpoints (e.g., run tests, add asset, select object).
34+
- **resources/**: Implements MCP resource endpoints (e.g., get hierarchy, get logs).
35+
- **utils/**: Logging and helper utilities.
36+
- **package.json**: Declares dependencies, including `@modelcontextprotocol/sdk`, `ws`, `express`, etc.
37+
38+
## 5. Integration & Usage
39+
40+
- The Unity Editor package is designed to be used as a package (via UPM or direct import).
41+
- The Node.js server can be started independently and connects to Unity via WebSocket (port configurable, default 8090).
42+
- The system is designed for use with LLM-based IDEs (e.g., Windsurf, Cursor, Claude Desktop) to enable AI-powered Unity Editor automation and queries.
43+
44+
## 6. Configuration
45+
46+
- Configuration utilities are provided for generating and injecting MCP config into various IDEs (Cursor, Claude Desktop, Windsurf).
47+
- Unity-side settings are persisted in `ProjectSettings/McpUnitySettings.json`.
48+
49+
## 7. Extensibility
50+
51+
- Tools and resources can be extended by adding new C# classes (Unity) or TypeScript modules (Node.js).
52+
- Dependency injection is used on the Unity side for improved modularity and testability.
53+
54+
## 8. References
55+
56+
- MCP Protocol: https://modelcontextprotocol.io
57+
- TypeScript SDK: https://github.com/modelcontextprotocol/typescript-sdk
58+
59+
## 9. Conventions
60+
61+
- Use WebSockets for all cross-process communication.
62+
- Follow the MCP protocol for all tool/resource definitions.
63+
- All new tools/resources should be registered in both Unity and Node.js server entry points.
64+
- Follow Conventional Commits for all commit messages.

Editor/UnityBridge/McpUnityEditorWindow.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,9 @@ private void DrawServerTab()
178178
}
179179
else
180180
{
181-
EditorGUILayout.LabelField("No clients connected", EditorStyles.centeredGreyMiniLabel);
181+
GUIStyle wrapStyle = new GUIStyle(EditorStyles.centeredGreyMiniLabel);
182+
wrapStyle.wordWrap = true;
183+
GUILayout.Label("No clients connected\nInvoke a tool from the MCP Client to connect", wrapStyle, GUILayout.ExpandWidth(true));
182184
}
183185

184186
EditorGUILayout.EndVertical();

Editor/UnityBridge/McpUnityServer.cs

Lines changed: 14 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,12 @@
11
using System;
22
using System.Collections.Generic;
33
using System.Threading;
4-
using System.Threading.Tasks;
54
using UnityEngine;
65
using UnityEditor;
7-
using Newtonsoft.Json;
8-
using Newtonsoft.Json.Linq;
96
using McpUnity.Tools;
107
using McpUnity.Resources;
118
using McpUnity.Services;
129
using McpUnity.Utils;
13-
using WebSocketSharp;
1410
using WebSocketSharp.Server;
1511

1612
namespace McpUnity.Unity
@@ -31,16 +27,24 @@ public class McpUnityServer
3127
private CancellationTokenSource _cts;
3228
private TestRunnerService _testRunnerService;
3329
private ConsoleLogsService _consoleLogsService;
34-
private Dictionary<string, string> _clients = new Dictionary<string, string>();
35-
30+
3631
/// <summary>
3732
/// Static constructor that gets called when Unity loads due to InitializeOnLoad attribute
3833
/// </summary>
3934
static McpUnityServer()
4035
{
4136
// Initialize the singleton instance when Unity loads
4237
// This ensures the bridge is available as soon as Unity starts
43-
EditorApplication.quitting += () => Instance.StopServer();
38+
EditorApplication.quitting += Instance.StopServer;
39+
40+
// Auto-restart server after domain reload
41+
EditorApplication.delayCall += () =>
42+
{
43+
if (McpUnitySettings.Instance.AutoStartServer)
44+
{
45+
Instance.StartServer();
46+
}
47+
};
4448
}
4549

4650
/// <summary>
@@ -66,8 +70,8 @@ public static McpUnityServer Instance
6670
/// <summary>
6771
/// Dictionary of connected clients with this server
6872
/// </summary>
69-
public Dictionary<string, string> Clients => _clients;
70-
73+
public Dictionary<string, string> Clients { get; } = new Dictionary<string, string>();
74+
7175
/// <summary>
7276
/// Private constructor to enforce singleton pattern
7377
/// </summary>
@@ -76,13 +80,6 @@ private McpUnityServer()
7680
InitializeServices();
7781
RegisterResources();
7882
RegisterTools();
79-
80-
McpLogger.LogInfo($"Created WebSocket server on port {McpUnitySettings.Instance.Port}");
81-
82-
if (McpUnitySettings.Instance.AutoStartServer || Application.internetReachability != NetworkReachability.NotReachable)
83-
{
84-
StartServer();
85-
}
8683
}
8784

8885
/// <summary>
@@ -102,7 +99,7 @@ public void StartServer()
10299
// Start the server
103100
_webSocketServer.Start();
104101

105-
McpLogger.LogInfo("WebSocket server started");
102+
McpLogger.LogInfo($"WebSocket server started on port {McpUnitySettings.Instance.Port}");
106103
}
107104
catch (Exception ex)
108105
{

Server/build/unity/mcpUnity.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ export declare class McpUnity {
1010
private ws;
1111
private pendingRequests;
1212
private readonly REQUEST_TIMEOUT;
13+
private retryDelay;
1314
constructor(logger: Logger);
1415
/**
1516
* Start the Unity connection

Server/build/unity/mcpUnity.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ export class McpUnity {
99
ws = null;
1010
pendingRequests = new Map();
1111
REQUEST_TIMEOUT = 10000;
12+
retryDelay = 1000;
1213
constructor(logger) {
1314
this.logger = logger;
1415
// Initialize port from environment variable or use default
@@ -87,6 +88,8 @@ export class McpUnity {
8788
this.ws.onclose = () => {
8889
this.logger.debug('WebSocket closed');
8990
this.disconnect();
91+
//this.logger.debug('WebSocket closed. Reconnecting in', this.retryDelay);
92+
//setTimeout(this.connect, this.retryDelay);
9093
};
9194
});
9295
}

Server/src/unity/mcpUnity.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ export class McpUnity {
3434
private ws: WebSocket | null = null;
3535
private pendingRequests: Map<string, PendingRequest> = new Map<string, PendingRequest>();
3636
private readonly REQUEST_TIMEOUT = 10000;
37+
private retryDelay = 1000;
3738

3839
constructor(logger: Logger) {
3940
this.logger = logger;
@@ -129,6 +130,8 @@ export class McpUnity {
129130
this.ws.onclose = () => {
130131
this.logger.debug('WebSocket closed');
131132
this.disconnect();
133+
//this.logger.debug('WebSocket closed. Reconnecting in', this.retryDelay);
134+
//setTimeout(this.connect, this.retryDelay);
132135
};
133136
});
134137
}

0 commit comments

Comments
 (0)