Skip to content

Commit 8211d85

Browse files
authored
Merge pull request #126 from xsodus/feature/vscode-ghcs-support
Feature: VSCode GitHub Copilot Support
2 parents 558b051 + a880bf4 commit 8211d85

File tree

7 files changed

+499
-56
lines changed

7 files changed

+499
-56
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,3 +31,4 @@ CONTRIBUTING.md.meta
3131
.idea/
3232
.vscode/
3333
.aider*
34+
.DS_Store*

UnityMcpBridge/Editor/Data/McpClients.cs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,26 @@ public class McpClients
4343
mcpType = McpTypes.Cursor,
4444
configStatus = "Not Configured",
4545
},
46+
new()
47+
{
48+
name = "VSCode GitHub Copilot",
49+
windowsConfigPath = Path.Combine(
50+
Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
51+
"Code",
52+
"User",
53+
"settings.json"
54+
),
55+
linuxConfigPath = Path.Combine(
56+
Environment.GetFolderPath(Environment.SpecialFolder.UserProfile),
57+
"Library",
58+
"Application Support",
59+
"Code",
60+
"User",
61+
"settings.json"
62+
),
63+
mcpType = McpTypes.VSCode,
64+
configStatus = "Not Configured",
65+
},
4666
};
4767

4868
// Initialize status enums after construction

UnityMcpBridge/Editor/Models/McpTypes.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ public enum McpTypes
44
{
55
ClaudeDesktop,
66
Cursor,
7+
VSCode,
78
}
89
}
910

UnityMcpBridge/Editor/Windows/ManualConfigEditorWindow.cs

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,13 @@ namespace UnityMcpBridge.Editor.Windows
88
// Editor window to display manual configuration instructions
99
public class ManualConfigEditorWindow : EditorWindow
1010
{
11-
private string configPath;
12-
private string configJson;
13-
private Vector2 scrollPos;
14-
private bool pathCopied = false;
15-
private bool jsonCopied = false;
16-
private float copyFeedbackTimer = 0;
17-
private McpClient mcpClient;
11+
protected string configPath;
12+
protected string configJson;
13+
protected Vector2 scrollPos;
14+
protected bool pathCopied = false;
15+
protected bool jsonCopied = false;
16+
protected float copyFeedbackTimer = 0;
17+
protected McpClient mcpClient;
1818

1919
public static void ShowWindow(string configPath, string configJson, McpClient mcpClient)
2020
{
@@ -26,7 +26,7 @@ public static void ShowWindow(string configPath, string configJson, McpClient mc
2626
window.Show();
2727
}
2828

29-
private void OnGUI()
29+
protected virtual void OnGUI()
3030
{
3131
scrollPos = EditorGUILayout.BeginScrollView(scrollPos);
3232

@@ -245,7 +245,7 @@ private void OnGUI()
245245
EditorGUILayout.EndScrollView();
246246
}
247247

248-
private void Update()
248+
protected virtual void Update()
249249
{
250250
// Handle the feedback message timer
251251
if (copyFeedbackTimer > 0)

UnityMcpBridge/Editor/Windows/UnityMcpEditorWindow.cs

Lines changed: 171 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -137,24 +137,75 @@ private void ConfigurationSection(McpClient mcpClient)
137137
// Create muted button style for Manual Setup
138138
GUIStyle mutedButtonStyle = new(buttonStyle);
139139

140-
if (
141-
GUILayout.Button(
142-
$"Auto Configure {mcpClient.name}",
143-
buttonStyle,
144-
GUILayout.Height(28)
145-
)
146-
)
140+
if (mcpClient.mcpType == McpTypes.VSCode)
147141
{
148-
ConfigureMcpClient(mcpClient);
149-
}
142+
// Special handling for VSCode GitHub Copilot
143+
if (
144+
GUILayout.Button(
145+
"Auto Configure VSCode with GitHub Copilot",
146+
buttonStyle,
147+
GUILayout.Height(28)
148+
)
149+
)
150+
{
151+
ConfigureMcpClient(mcpClient);
152+
}
150153

151-
if (GUILayout.Button("Manual Setup", mutedButtonStyle, GUILayout.Height(28)))
154+
if (GUILayout.Button("Manual Setup", mutedButtonStyle, GUILayout.Height(28)))
155+
{
156+
// Show VSCode specific manual setup window
157+
string configPath = RuntimeInformation.IsOSPlatform(OSPlatform.Windows)
158+
? mcpClient.windowsConfigPath
159+
: mcpClient.linuxConfigPath;
160+
161+
// Get the Python directory path
162+
string pythonDir = FindPackagePythonDirectory();
163+
164+
// Create VSCode-specific configuration
165+
var vscodeConfig = new
166+
{
167+
mcp = new
168+
{
169+
servers = new
170+
{
171+
unityMCP = new
172+
{
173+
command = "uv",
174+
args = new[] { "--directory", pythonDir, "run", "server.py" }
175+
}
176+
}
177+
}
178+
};
179+
180+
JsonSerializerSettings jsonSettings = new() { Formatting = Formatting.Indented };
181+
string manualConfigJson = JsonConvert.SerializeObject(vscodeConfig, jsonSettings);
182+
183+
// Use the VSCodeManualSetupWindow directly since we're in the same namespace
184+
VSCodeManualSetupWindow.ShowWindow(configPath, manualConfigJson);
185+
}
186+
}
187+
else
152188
{
153-
// Get the appropriate config path based on OS
154-
string configPath = RuntimeInformation.IsOSPlatform(OSPlatform.Windows)
155-
? mcpClient.windowsConfigPath
156-
: mcpClient.linuxConfigPath;
157-
ShowManualInstructionsWindow(configPath, mcpClient);
189+
// Standard client buttons
190+
if (
191+
GUILayout.Button(
192+
$"Auto Configure {mcpClient.name}",
193+
buttonStyle,
194+
GUILayout.Height(28)
195+
)
196+
)
197+
{
198+
ConfigureMcpClient(mcpClient);
199+
}
200+
201+
if (GUILayout.Button("Manual Setup", mutedButtonStyle, GUILayout.Height(28)))
202+
{
203+
// Get the appropriate config path based on OS
204+
string configPath = RuntimeInformation.IsOSPlatform(OSPlatform.Windows)
205+
? mcpClient.windowsConfigPath
206+
: mcpClient.linuxConfigPath;
207+
ShowManualInstructionsWindow(configPath, mcpClient);
208+
}
158209
}
159210
EditorGUILayout.Space(5);
160211

@@ -274,7 +325,7 @@ private void ToggleUnityBridge()
274325
isUnityBridgeRunning = !isUnityBridgeRunning;
275326
}
276327

277-
private string WriteToConfig(string pythonDir, string configPath)
328+
private string WriteToConfig(string pythonDir, string configPath, McpClient mcpClient = null)
278329
{
279330
// Create configuration object for unityMCP
280331
McpConfigServer unityMCPConfig = new()
@@ -303,14 +354,36 @@ private string WriteToConfig(string pythonDir, string configPath)
303354
dynamic existingConfig = JsonConvert.DeserializeObject(existingJson);
304355
existingConfig ??= new Newtonsoft.Json.Linq.JObject();
305356

306-
// Ensure mcpServers object exists
307-
if (existingConfig.mcpServers == null)
357+
// Handle different client types with a switch statement
358+
switch (mcpClient?.mcpType)
308359
{
309-
existingConfig.mcpServers = new Newtonsoft.Json.Linq.JObject();
360+
case McpTypes.VSCode:
361+
// VSCode specific configuration
362+
// Ensure mcp object exists
363+
if (existingConfig.mcp == null)
364+
{
365+
existingConfig.mcp = new Newtonsoft.Json.Linq.JObject();
366+
}
367+
368+
// Ensure mcp.servers object exists
369+
if (existingConfig.mcp.servers == null)
370+
{
371+
existingConfig.mcp.servers = new Newtonsoft.Json.Linq.JObject();
372+
}
373+
break;
374+
375+
default:
376+
// Standard MCP configuration (Claude Desktop, Cursor, etc.)
377+
// Ensure mcpServers object exists
378+
if (existingConfig.mcpServers == null)
379+
{
380+
existingConfig.mcpServers = new Newtonsoft.Json.Linq.JObject();
381+
}
382+
break;
310383
}
311384

312-
// Add/update unityMCP while preserving other servers
313-
existingConfig.mcpServers.unityMCP =
385+
// Add/update UnityMCP server in VSCode settings
386+
existingConfig.mcp.servers.unityMCP =
314387
JsonConvert.DeserializeObject<Newtonsoft.Json.Linq.JToken>(
315388
JsonConvert.SerializeObject(unityMCPConfig)
316389
);
@@ -334,22 +407,49 @@ private void ShowManualInstructionsWindow(string configPath, McpClient mcpClient
334407
{
335408
// Get the Python directory path using Package Manager API
336409
string pythonDir = FindPackagePythonDirectory();
337-
338-
// Create the manual configuration message
339-
McpConfig jsonConfig = new()
410+
string manualConfigJson;
411+
412+
// Create common JsonSerializerSettings
413+
JsonSerializerSettings jsonSettings = new() { Formatting = Formatting.Indented };
414+
415+
// Use switch statement to handle different client types
416+
switch (mcpClient.mcpType)
340417
{
341-
mcpServers = new McpConfigServers
342-
{
343-
unityMCP = new McpConfigServer
418+
case McpTypes.VSCode:
419+
// Create VSCode-specific configuration with proper format
420+
var vscodeConfig = new
344421
{
345-
command = "uv",
346-
args = new[] { "--directory", pythonDir, "run", "server.py" },
347-
},
348-
},
349-
};
350-
351-
JsonSerializerSettings jsonSettings = new() { Formatting = Formatting.Indented };
352-
string manualConfigJson = JsonConvert.SerializeObject(jsonConfig, jsonSettings);
422+
mcp = new
423+
{
424+
servers = new
425+
{
426+
unityMCP = new
427+
{
428+
command = "uv",
429+
args = new[] { "--directory", pythonDir, "run", "server.py" }
430+
}
431+
}
432+
}
433+
};
434+
manualConfigJson = JsonConvert.SerializeObject(vscodeConfig, jsonSettings);
435+
break;
436+
437+
default:
438+
// Create standard MCP configuration for other clients
439+
McpConfig jsonConfig = new()
440+
{
441+
mcpServers = new McpConfigServers
442+
{
443+
unityMCP = new McpConfigServer
444+
{
445+
command = "uv",
446+
args = new[] { "--directory", pythonDir, "run", "server.py" },
447+
},
448+
},
449+
};
450+
manualConfigJson = JsonConvert.SerializeObject(jsonConfig, jsonSettings);
451+
break;
452+
}
353453

354454
ManualConfigEditorWindow.ShowWindow(configPath, manualConfigJson, mcpClient);
355455
}
@@ -450,7 +550,7 @@ private string ConfigureMcpClient(McpClient mcpClient)
450550
return "Manual Configuration Required";
451551
}
452552

453-
string result = WriteToConfig(pythonDir, configPath);
553+
string result = WriteToConfig(pythonDir, configPath, mcpClient);
454554

455555
// Update the client status after successful configuration
456556
if (result == "Configured successfully")
@@ -542,18 +642,42 @@ private void CheckMcpConfiguration(McpClient mcpClient)
542642
}
543643

544644
string configJson = File.ReadAllText(configPath);
545-
McpConfig config = JsonConvert.DeserializeObject<McpConfig>(configJson);
546-
547-
if (config?.mcpServers?.unityMCP != null)
645+
string pythonDir = ServerInstaller.GetServerPath();
646+
647+
// Use switch statement to handle different client types, extracting common logic
648+
string[] args = null;
649+
bool configExists = false;
650+
651+
switch (mcpClient.mcpType)
548652
{
549-
string pythonDir = ServerInstaller.GetServerPath();
550-
if (
551-
pythonDir != null
552-
&& Array.Exists(
553-
config.mcpServers.unityMCP.args,
554-
arg => arg.Contains(pythonDir, StringComparison.Ordinal)
555-
)
556-
)
653+
case McpTypes.VSCode:
654+
dynamic config = JsonConvert.DeserializeObject(configJson);
655+
656+
if (config?.mcp?.servers?.unityMCP != null)
657+
{
658+
// Extract args from VSCode config format
659+
args = config.mcp.servers.unityMCP.args.ToObject<string[]>();
660+
configExists = true;
661+
}
662+
break;
663+
664+
default:
665+
// Standard MCP configuration check for Claude Desktop, Cursor, etc.
666+
McpConfig standardConfig = JsonConvert.DeserializeObject<McpConfig>(configJson);
667+
668+
if (standardConfig?.mcpServers?.unityMCP != null)
669+
{
670+
args = standardConfig.mcpServers.unityMCP.args;
671+
configExists = true;
672+
}
673+
break;
674+
}
675+
676+
// Common logic for checking configuration status
677+
if (configExists)
678+
{
679+
if (pythonDir != null &&
680+
Array.Exists(args, arg => arg.Contains(pythonDir, StringComparison.Ordinal)))
557681
{
558682
mcpClient.SetStatus(McpStatus.Configured);
559683
}

0 commit comments

Comments
 (0)