Skip to content

Commit 6d8329b

Browse files
committed
docs: update README with new client configurations and build instructions
refactor: simplify connection state management in McpUnityBridge chore: remove outdated restructuring plan document
1 parent 8b6a769 commit 6d8329b

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

52 files changed

+889
-1127
lines changed

Diff for: .gitignore

-4
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,6 @@
44
#
55
**/[Ll]ibrary/
66
**/[Tt]emp/
7-
**/[Bb]uild/
8-
**/[Bb]uilds/
97
**/[Ll]ogs/
108
**/[Mm]emoryCaptures/
119
**/[Uu]ser[Ss]ettings/
@@ -71,8 +69,6 @@ sysinfo.txt
7169
**/.utmp/
7270
**/BuildReports/
7371
**/BuildReports.meta
74-
**/Build/
75-
**/Build.meta
7672
**/Editor Default Resources/
7773
**/Editor Default Resources.meta
7874
**/WebGL Builds/

Diff for: Editor.meta

+8
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Diff for: Editor/Unity/McpUnityBridge.cs

+51-15
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,16 @@
1212

1313
namespace McpUnity.Unity
1414
{
15+
/// <summary>
16+
/// Connection state for the MCP Unity Bridge
17+
/// </summary>
18+
public enum ConnectionState
19+
{
20+
Disconnected,
21+
Connecting,
22+
Connected
23+
}
24+
1525
/// <summary>
1626
/// Bridge between Unity and Node.js MCP server.
1727
/// Handles WebSocket communication and request processing.
@@ -22,7 +32,6 @@ public class McpUnityBridge
2232
private static McpUnityBridge _instance;
2333
private ClientWebSocket _webSocket;
2434
private CancellationTokenSource _cts;
25-
private bool _isConnected = false;
2635
private Dictionary<string, TaskCompletionSource<JObject>> _pendingRequests = new Dictionary<string, TaskCompletionSource<JObject>>();
2736

2837
// Dictionary to store tool instances
@@ -31,6 +40,7 @@ public class McpUnityBridge
3140
// Events
3241
public static event Action OnConnected;
3342
public static event Action OnDisconnected;
43+
public static event Action OnConnecting;
3444
public static event Action<string> OnError;
3545

3646
/// <summary>
@@ -63,9 +73,31 @@ public static McpUnityBridge Instance
6373
}
6474

6575
/// <summary>
66-
/// Whether the bridge is currently connected
76+
/// Current connection state mapped from WebSocket state
6777
/// </summary>
68-
public bool IsConnected => _isConnected;
78+
public ConnectionState ConnectionState
79+
{
80+
get
81+
{
82+
if (_webSocket == null)
83+
return ConnectionState.Disconnected;
84+
85+
switch (_webSocket.State)
86+
{
87+
case WebSocketState.Open:
88+
return ConnectionState.Connected;
89+
case WebSocketState.Connecting:
90+
return ConnectionState.Connecting;
91+
case WebSocketState.CloseSent: // Still finalizing connection
92+
case WebSocketState.CloseReceived: // Still finalizing connection
93+
case WebSocketState.None:
94+
case WebSocketState.Closed:
95+
case WebSocketState.Aborted:
96+
default:
97+
return ConnectionState.Disconnected;
98+
}
99+
}
100+
}
69101

70102
/// <summary>
71103
/// Private constructor to enforce singleton pattern
@@ -102,15 +134,19 @@ private void RegisterTools()
102134
/// </summary>
103135
public async Task Connect(string url)
104136
{
105-
if (_isConnected) return;
137+
// Don't try to connect if already connected or connecting
138+
if (ConnectionState != ConnectionState.Disconnected) return;
106139

107140
try
108141
{
142+
// Notify that we're connecting
143+
OnConnecting?.Invoke();
144+
Debug.Log($"[MCP Unity] Connecting to server at {url}...");
145+
109146
_cts = new CancellationTokenSource();
110147
_webSocket = new ClientWebSocket();
111148

112149
await _webSocket.ConnectAsync(new Uri(url), _cts.Token);
113-
_isConnected = true;
114150

115151
Debug.Log($"[MCP Unity] Connected to server at {url}");
116152
OnConnected?.Invoke();
@@ -130,13 +166,14 @@ public async Task Connect(string url)
130166
/// </summary>
131167
public async Task Disconnect()
132168
{
133-
if (!_isConnected) return;
169+
// Only disconnect if connected
170+
if (ConnectionState != ConnectionState.Connected) return;
134171

135172
try
136173
{
137-
_cts.Cancel();
174+
_cts = null;
175+
138176
await _webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, "Closing", CancellationToken.None);
139-
_isConnected = false;
140177

141178
Debug.Log("[MCP Unity] Disconnected from server");
142179
OnDisconnected?.Invoke();
@@ -153,7 +190,7 @@ public async Task Disconnect()
153190
/// </summary>
154191
public async Task<JObject> SendRequest(string method, JObject parameters)
155192
{
156-
if (!_isConnected)
193+
if (ConnectionState != ConnectionState.Connected)
157194
{
158195
throw new InvalidOperationException("Not connected to server");
159196
}
@@ -206,9 +243,9 @@ public JArray GetToolsMetadata()
206243
/// </summary>
207244
private async Task SendMessage(string message)
208245
{
209-
if (!_isConnected || _webSocket.State != WebSocketState.Open)
246+
if (ConnectionState != ConnectionState.Connected)
210247
{
211-
throw new InvalidOperationException("WebSocket is not connected");
248+
throw new InvalidOperationException("Not connected to server");
212249
}
213250

214251
byte[] buffer = Encoding.UTF8.GetBytes(message);
@@ -225,7 +262,7 @@ private async Task ReceiveLoop()
225262

226263
try
227264
{
228-
while (_isConnected && !_cts.Token.IsCancellationRequested)
265+
while (ConnectionState == ConnectionState.Connected && !_cts.Token.IsCancellationRequested)
229266
{
230267
StringBuilder messageBuilder = new StringBuilder();
231268
WebSocketReceiveResult result;
@@ -259,10 +296,9 @@ private async Task ReceiveLoop()
259296
Debug.LogError($"[MCP Unity] WebSocket error: {ex.Message}");
260297
OnError?.Invoke(ex.Message);
261298

262-
if (_isConnected)
299+
if (ConnectionState == ConnectionState.Connected)
263300
{
264-
_isConnected = false;
265-
OnDisconnected?.Invoke();
301+
await Disconnect();
266302
}
267303
}
268304
}

Diff for: Editor/Unity/UnityMCPEditorWindow.cs renamed to Editor/Unity/McpUnityEditorWindow.cs

+31-58
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,6 @@ namespace McpUnity.Unity
1414
public class McpUnityEditorWindow : EditorWindow
1515
{
1616
private string _statusMessage = "";
17-
private MessageType _statusMessageType = MessageType.Info;
18-
private double _statusMessageExpireTime = 0;
1917
private GUIStyle _headerStyle;
2018
private GUIStyle _subHeaderStyle;
2119
private GUIStyle _boxStyle;
@@ -35,52 +33,53 @@ public static void ShowWindow()
3533

3634
private void OnEnable()
3735
{
36+
titleContent = new GUIContent("MCP Unity");
37+
_statusMessage = "Ready";
38+
3839
// Subscribe to McpUnityBridge events
3940
McpUnityBridge.OnConnected += HandleConnected;
4041
McpUnityBridge.OnDisconnected += HandleDisconnected;
42+
McpUnityBridge.OnConnecting += HandleConnecting;
4143
McpUnityBridge.OnError += HandleError;
4244

43-
// Set up regular UI updates
44-
EditorApplication.update += OnEditorUpdate;
45+
InitializeStyles();
4546
}
4647

4748
private void OnDisable()
4849
{
4950
// Unsubscribe from McpUnityBridge events
5051
McpUnityBridge.OnConnected -= HandleConnected;
5152
McpUnityBridge.OnDisconnected -= HandleDisconnected;
53+
McpUnityBridge.OnConnecting -= HandleConnecting;
5254
McpUnityBridge.OnError -= HandleError;
53-
54-
// Remove editor update callback
55-
EditorApplication.update -= OnEditorUpdate;
5655
}
5756

5857
private void HandleConnected()
5958
{
60-
SetStatusMessage("Connected to server", MessageType.Info);
59+
_statusMessage = "Connected";
60+
6161
Repaint();
6262
}
6363

6464
private void HandleDisconnected()
6565
{
66-
SetStatusMessage("Disconnected from server", MessageType.Info);
66+
_statusMessage = "Disconnected";
67+
6768
Repaint();
6869
}
6970

70-
private void HandleError(string errorMessage)
71+
private void HandleConnecting()
7172
{
72-
SetStatusMessage($"Error: {errorMessage}", MessageType.Error);
73+
_statusMessage = "Connecting...";
74+
7375
Repaint();
7476
}
7577

76-
private void OnEditorUpdate()
78+
private void HandleError(string errorMessage)
7779
{
78-
// Check if status message has expired
79-
if (!string.IsNullOrEmpty(_statusMessage) && EditorApplication.timeSinceStartup > _statusMessageExpireTime)
80-
{
81-
_statusMessage = "";
82-
Repaint();
83-
}
80+
_statusMessage = $"Error: {errorMessage}";
81+
82+
Repaint();
8483
}
8584

8685
private void OnGUI()
@@ -111,7 +110,7 @@ private void OnGUI()
111110
// Bottom Status bar
112111
if (!string.IsNullOrEmpty(_statusMessage))
113112
{
114-
EditorGUILayout.HelpBox(_statusMessage, _statusMessageType);
113+
EditorGUILayout.HelpBox(_statusMessage, MessageType.Info);
115114
}
116115

117116
// Version info at the bottom
@@ -131,8 +130,10 @@ private void DrawServerTab()
131130
EditorGUILayout.BeginHorizontal();
132131
EditorGUILayout.LabelField("Server Status:", GUILayout.Width(120));
133132

134-
string statusText = McpUnityBridge.Instance.IsConnected ? "Connected" : "Disconnected";
135-
Color statusColor = McpUnityBridge.Instance.IsConnected ? Color.green : Color.red;
133+
var connectionState = McpUnityBridge.Instance.ConnectionState;
134+
var statusText = connectionState == ConnectionState.Connected ? "Connected" : "Disconnected";
135+
var statusColor = connectionState == ConnectionState.Connected ? Color.green : Color.red;
136+
statusColor = connectionState == ConnectionState.Connecting ? Color.yellow : statusColor;
136137

137138
GUIStyle statusStyle = new GUIStyle(EditorStyles.boldLabel);
138139
statusStyle.normal.textColor = statusColor;
@@ -155,16 +156,21 @@ private void DrawServerTab()
155156
// Server control buttons
156157
EditorGUILayout.BeginHorizontal();
157158

158-
GUI.enabled = !McpUnityBridge.Instance.IsConnected;
159+
// Determine button states based on connection state
160+
ConnectionState currentState = McpUnityBridge.Instance.ConnectionState;
161+
162+
// Connect button - enabled only when disconnected
163+
GUI.enabled = currentState == ConnectionState.Disconnected;
159164
if (GUILayout.Button("Start Server", GUILayout.Height(30)))
160165
{
161-
StartServer();
166+
_ = McpUnityBridge.Instance.Connect(McpUnitySettings.Instance.WebSocketUrl);
162167
}
163168

164-
GUI.enabled = McpUnityBridge.Instance.IsConnected;
169+
// Disconnect button - enabled only when connected
170+
GUI.enabled = currentState == ConnectionState.Connected;
165171
if (GUILayout.Button("Stop Server", GUILayout.Height(30)))
166172
{
167-
StopServer();
173+
_ = McpUnityBridge.Instance.Disconnect();
168174
}
169175

170176
GUI.enabled = true;
@@ -248,32 +254,6 @@ private void DrawHelpTab()
248254
EditorGUILayout.EndVertical();
249255
}
250256

251-
private async void StartServer()
252-
{
253-
try
254-
{
255-
await McpUnityBridge.Instance.Connect(McpUnitySettings.Instance.WebSocketUrl);
256-
}
257-
catch (Exception ex)
258-
{
259-
Debug.LogError($"[MCP Unity] Failed to start server: {ex.Message}");
260-
EditorUtility.DisplayDialog("Error", $"Failed to start server: {ex.Message}", "OK");
261-
}
262-
}
263-
264-
private async void StopServer()
265-
{
266-
try
267-
{
268-
await McpUnityBridge.Instance.Disconnect();
269-
}
270-
catch (Exception ex)
271-
{
272-
Debug.LogError($"[MCP Unity] Failed to stop server: {ex.Message}");
273-
EditorUtility.DisplayDialog("Error", $"Failed to stop server: {ex.Message}", "OK");
274-
}
275-
}
276-
277257
private void GenerateMcpConfigJson()
278258
{
279259
var config = new Dictionary<string, object>
@@ -400,13 +380,6 @@ private void InitializeStyles()
400380

401381
_isInitialized = true;
402382
}
403-
404-
private void SetStatusMessage(string message, MessageType type)
405-
{
406-
_statusMessage = message;
407-
_statusMessageType = type;
408-
_statusMessageExpireTime = EditorApplication.timeSinceStartup + 5;
409-
}
410383

411384
/// <summary>
412385
/// Creates a label with text that properly wraps based on available width

0 commit comments

Comments
 (0)