Skip to content

Commit 6e6bd61

Browse files
committed
Added the possibility to add the unity packages and assets to the vscode ides workspace. Allowing this way for the AI to index all the necessary code to properly function inside Unity
Added the name of the node.js server connecting to Unity. This way is easier to debug issues with Unity MCP Server Window
1 parent 3c8eebc commit 6e6bd61

File tree

13 files changed

+261
-60
lines changed

13 files changed

+261
-60
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,3 +113,6 @@ GvhProjectSettings.xml
113113
#Project Specific
114114
**/package-lock.json
115115
**/package-lock.json.meta
116+
.codeiumignore
117+
Server/log.txt
118+
Server/log.txt.meta

Editor/UnityBridge/McpUnityEditorWindow.cs

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
using McpUnity.Utils;
12
using UnityEngine;
23
using UnityEditor;
34

@@ -128,8 +129,8 @@ private void DrawServerTab()
128129

129130
EditorGUILayout.LabelField("Connected Clients", EditorStyles.boldLabel);
130131
EditorGUILayout.BeginVertical("box"); // Keep the default gray box for the container
131-
132-
var clients = mcpUnityServer.GetConnectedClients();
132+
133+
var clients = mcpUnityServer.Clients;
133134

134135
if (clients.Count > 0)
135136
{
@@ -139,12 +140,12 @@ private void DrawServerTab()
139140

140141
EditorGUILayout.BeginHorizontal();
141142
EditorGUILayout.LabelField("ID:", _connectedClientLabelStyle, GUILayout.Width(50));
142-
EditorGUILayout.LabelField(client.ID, EditorStyles.boldLabel);
143+
EditorGUILayout.LabelField(client.Key, EditorStyles.boldLabel);
143144
EditorGUILayout.EndHorizontal();
144145

145146
EditorGUILayout.BeginHorizontal();
146147
EditorGUILayout.LabelField("Name:", _connectedClientLabelStyle, GUILayout.Width(50));
147-
EditorGUILayout.LabelField(client.Name, _connectedClientLabelStyle);
148+
EditorGUILayout.LabelField(client.Value, _connectedClientLabelStyle);
148149
EditorGUILayout.EndHorizontal();
149150

150151
EditorGUILayout.EndVertical();
@@ -157,9 +158,31 @@ private void DrawServerTab()
157158
}
158159

159160
EditorGUILayout.EndVertical();
161+
162+
// IDE Integration settings
163+
string ideIntegrationTooltip = "Add the Library/PackedCache folder to VSCode-like IDE workspaces so code can be indexed for the AI to access it. This improves code intelligence for Unity packages in VSCode, Cursor, and similar IDEs.";
160164
EditorGUILayout.Space();
165+
EditorGUILayout.LabelField(new GUIContent("Improve VSCode-like IDEs code intelligence", ideIntegrationTooltip), EditorStyles.boldLabel);
166+
167+
// Add button to manually update workspace
168+
EditorGUILayout.BeginHorizontal();
169+
if (GUILayout.Button(new GUIContent("Update Workspace Cache Now", ideIntegrationTooltip), GUILayout.Height(24)))
170+
{
171+
bool updated = VsCodeWorkspaceUtils.AddPackageCacheToWorkspace();
172+
if (updated)
173+
{
174+
EditorUtility.DisplayDialog("Workspace Updated", "Successfully added Library/PackedCache to workspace files. Please restart your IDE and open the workspace.", "OK");
175+
}
176+
else
177+
{
178+
EditorUtility.DisplayDialog("Workspace Update Failed", "No workspace files were found or needed updating.", "OK");
179+
}
180+
}
181+
182+
EditorGUILayout.EndHorizontal();
161183

162184
// MCP Config generation section
185+
EditorGUILayout.Space();
163186
EditorGUILayout.LabelField("MCP Configuration", EditorStyles.boldLabel);
164187

165188
var before = _tabsIndentationJson;

Editor/UnityBridge/McpUnityServer.cs

Lines changed: 6 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ public class McpUnityServer
2929
private WebSocketServer _webSocketServer;
3030
private CancellationTokenSource _cts;
3131
private TestRunnerService _testRunnerService;
32-
private List<ClientInfo> _clients = new List<ClientInfo>();
32+
private Dictionary<string, string> _clients = new Dictionary<string, string>();
3333

3434
/// <summary>
3535
/// Static constructor that gets called when Unity loads due to InitializeOnLoad attribute
@@ -60,6 +60,11 @@ public static McpUnityServer Instance
6060
/// Current Listening state
6161
/// </summary>
6262
public bool IsListening => _webSocketServer?.IsListening ?? false;
63+
64+
/// <summary>
65+
/// Dictionary of connected clients with this server
66+
/// </summary>
67+
public Dictionary<string, string> Clients => _clients;
6368

6469
/// <summary>
6570
/// Private constructor to enforce singleton pattern
@@ -207,44 +212,5 @@ private void InitializeServices()
207212
// Create TestRunnerService
208213
_testRunnerService = new TestRunnerService();
209214
}
210-
211-
/// <summary>
212-
/// Gets the list of clients currently connected to the WebSocket server
213-
/// </summary>
214-
/// <returns>List of client information</returns>
215-
public List<ClientInfo> GetConnectedClients()
216-
{
217-
_clients.Clear();
218-
219-
if (!IsListening)
220-
return _clients;
221-
222-
// Get all services
223-
var services = _webSocketServer.WebSocketServices.Hosts;
224-
225-
foreach (var servicePath in services)
226-
{
227-
// Get sessions from the service
228-
foreach (var session in servicePath.Sessions.Sessions)
229-
{
230-
_clients.Add(new ClientInfo
231-
{
232-
ID = session.ID,
233-
Name = session.Context.Origin ?? "Unknown"
234-
});
235-
}
236-
}
237-
238-
return _clients;
239-
}
240-
}
241-
242-
/// <summary>
243-
/// Simple class to hold client connection information
244-
/// </summary>
245-
public struct ClientInfo
246-
{
247-
public string ID { get; set; }
248-
public string Name { get; set; }
249215
}
250216
}

Editor/UnityBridge/McpUnitySettings.cs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System;
22
using System.IO;
3+
using McpUnity.Utils;
34
using UnityEngine;
45
using UnityEditor;
56

@@ -31,7 +32,6 @@ public static McpUnitySettings Instance
3132
if (_instance == null)
3233
{
3334
_instance = new McpUnitySettings();
34-
_instance.LoadSettings();
3535
}
3636
return _instance;
3737
}
@@ -40,7 +40,11 @@ public static McpUnitySettings Instance
4040
/// <summary>
4141
/// Private constructor for singleton
4242
/// </summary>
43-
private McpUnitySettings() { }
43+
private McpUnitySettings()
44+
{
45+
LoadSettings();
46+
VsCodeWorkspaceUtils.AddPackageCacheToWorkspace();
47+
}
4448

4549
/// <summary>
4650
/// Load settings from disk

Editor/UnityBridge/McpUnitySocketHandler.cs

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
using McpUnity.Resources;
1111
using Unity.EditorCoroutines.Editor;
1212
using System.Collections;
13+
using System.Collections.Specialized;
1314

1415
namespace McpUnity.Unity
1516
{
@@ -104,15 +105,33 @@ protected override async void OnMessage(MessageEventArgs e)
104105
/// </summary>
105106
protected override void OnOpen()
106107
{
107-
Debug.Log("[MCP Unity] WebSocket client connected");
108+
// Extract client name from the X-Client-Name header
109+
string clientName = "";
110+
NameValueCollection headers = Context.Headers;
111+
if (headers != null && headers.Contains("X-Client-Name"))
112+
{
113+
clientName = headers["X-Client-Name"];
114+
115+
// Add the client name on the server
116+
_server.Clients.Add(ID, clientName);
117+
}
118+
119+
//Debug.Log(Context);
120+
121+
Debug.Log($"[MCP Unity] WebSocket client '{clientName}' connected");
108122
}
109123

110124
/// <summary>
111125
/// Handle WebSocket connection close
112126
/// </summary>
113127
protected override void OnClose(CloseEventArgs e)
114128
{
115-
Debug.Log($"[MCP Unity] WebSocket client disconnected: {e.Reason}");
129+
_server.Clients.TryGetValue(ID, out string clientName);
130+
131+
// Remove the client from the server
132+
_server.Clients.Remove(ID);
133+
134+
Debug.Log($"[MCP Unity] WebSocket client '{clientName}' disconnected: {e.Reason}");
116135
}
117136

118137
/// <summary>

Editor/Utils.meta

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
using System;
22
using System.IO;
33
using System.Collections.Generic;
4+
using McpUnity.Unity;
45
using UnityEngine;
56
using UnityEditor;
67
using Newtonsoft.Json;
78
using Newtonsoft.Json.Linq;
89

9-
namespace McpUnity.Unity
10+
namespace McpUnity.Utils
1011
{
1112
/// <summary>
1213
/// Utility class for MCP configuration operations
Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
using System;
2+
using System.IO;
3+
using System.Collections.Generic;
4+
using UnityEngine;
5+
using UnityEditor;
6+
using Newtonsoft.Json;
7+
using Newtonsoft.Json.Linq;
8+
using System.Linq;
9+
10+
namespace McpUnity.Utils
11+
{
12+
/// <summary>
13+
/// Manages VSCode-like IDE workspace integration for Unity projects
14+
/// </summary>
15+
public class VsCodeWorkspaceUtils
16+
{
17+
/// <summary>
18+
/// The default folder structure for code-workspace files
19+
/// </summary>
20+
private static readonly JArray DefaultFolders = JArray.Parse(@"[
21+
{
22+
""path"": ""Assets""
23+
},
24+
{
25+
""path"": ""Packages""
26+
},
27+
{
28+
""path"": ""Library/PackageCache""
29+
}
30+
]");
31+
32+
/// <summary>
33+
/// Add the Library/PackageCache folder to the .code-workspace file if not already present
34+
/// This ensures that the Unity cache is available to code intelligence tools
35+
/// </summary>
36+
public static bool AddPackageCacheToWorkspace()
37+
{
38+
try
39+
{
40+
// Get the project root directory
41+
string projectRoot = Directory.GetParent(Application.dataPath).FullName;
42+
43+
// Determine the workspace filename based on the project directory name
44+
string projectDirName = new DirectoryInfo(projectRoot).Name;
45+
string workspaceFilename = $"{projectDirName}.code-workspace";
46+
string workspacePath = Path.Combine(projectRoot, workspaceFilename);
47+
JObject workspaceConfig = new JObject
48+
{
49+
["folders"] = DefaultFolders.DeepClone(),
50+
["settings"] = new JObject()
51+
};
52+
53+
// If file exists, update it rather than overwriting
54+
if (File.Exists(workspacePath))
55+
{
56+
string existingContent = File.ReadAllText(workspacePath);
57+
JObject existingWorkspace = JObject.Parse(existingContent);
58+
59+
// Merge the new config with the existing one
60+
MergeWorkspaceConfigs(existingWorkspace, workspaceConfig);
61+
workspaceConfig = existingWorkspace;
62+
}
63+
64+
// Write the updated workspace file
65+
File.WriteAllText(workspacePath, workspaceConfig.ToString(Formatting.Indented));
66+
Debug.Log($"[MCP Unity] Updated workspace configuration in {workspacePath}");
67+
return true;
68+
}
69+
catch (Exception ex)
70+
{
71+
Debug.LogError($"[MCP Unity] Error updating workspace file: {ex.Message}");
72+
return false;
73+
}
74+
}
75+
76+
/// <summary>
77+
/// Merges a source workspace config into a target workspace config
78+
/// Ensures folders are uniquely added based on path properties
79+
/// </summary>
80+
private static void MergeWorkspaceConfigs(JObject target, JObject source)
81+
{
82+
// Merge folders array if both exist
83+
if (source["folders"] != null && source["folders"].Type == JTokenType.Array)
84+
{
85+
if (target["folders"] == null || target["folders"].Type != JTokenType.Array)
86+
{
87+
target["folders"] = new JArray();
88+
}
89+
90+
// Get existing folder paths
91+
var existingPaths = new HashSet<string>();
92+
foreach (var folder in target["folders"])
93+
{
94+
if (folder.Type == JTokenType.Object && folder["path"] != null)
95+
{
96+
existingPaths.Add(folder["path"].ToString());
97+
}
98+
}
99+
100+
// Add new folders if they don't exist
101+
foreach (var folder in source["folders"])
102+
{
103+
if (folder.Type == JTokenType.Object && folder["path"] != null)
104+
{
105+
string path = folder["path"].ToString();
106+
if (!existingPaths.Contains(path))
107+
{
108+
((JArray)target["folders"]).Add(folder.DeepClone());
109+
existingPaths.Add(path);
110+
}
111+
}
112+
}
113+
}
114+
115+
// Merge settings if both exist
116+
if (source["settings"] != null && source["settings"].Type == JTokenType.Object)
117+
{
118+
if (target["settings"] == null || target["settings"].Type != JTokenType.Object)
119+
{
120+
target["settings"] = new JObject();
121+
}
122+
123+
// Deep merge settings
124+
foreach (var property in (JObject)source["settings"])
125+
{
126+
target["settings"][property.Key] = property.Value.DeepClone();
127+
}
128+
}
129+
130+
// Merge any other top-level properties
131+
foreach (var property in source)
132+
{
133+
if (property.Key != "folders" && property.Key != "settings")
134+
{
135+
target[property.Key] = property.Value.DeepClone();
136+
}
137+
}
138+
}
139+
}
140+
}

Editor/Utils/VsCodeWorkspaceUtils.cs.meta

Lines changed: 11 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)