From 619172ad9937fccd65c8346b52f3de66c6bdc27f Mon Sep 17 00:00:00 2001 From: xsodus Date: Mon, 12 May 2025 08:25:21 +0700 Subject: [PATCH 1/6] Add VSCode GitHub Copilot support and configuration window - Updated McpTypes to include VSCode. - Enhanced McpClients to handle VSCode configuration paths. - Implemented VSCodeManualSetupWindow for user instructions and JSON configuration. - Modified UnityMcpEditorWindow to support VSCode-specific setup and configuration. - Updated .gitignore to include .DS_Store files. --- .gitignore | 1 + UnityMcpBridge/Editor/Data/McpClients.cs | 20 ++ UnityMcpBridge/Editor/Models/McpTypes.cs | 1 + .../Editor/Windows/UnityMcpEditorWindow.cs | 237 ++++++++++---- .../Editor/Windows/VSCodeManualSetupWindow.cs | 297 ++++++++++++++++++ 5 files changed, 501 insertions(+), 55 deletions(-) create mode 100644 UnityMcpBridge/Editor/Windows/VSCodeManualSetupWindow.cs diff --git a/.gitignore b/.gitignore index 75612dc8..f1d2443b 100644 --- a/.gitignore +++ b/.gitignore @@ -31,3 +31,4 @@ CONTRIBUTING.md.meta .idea/ .vscode/ .aider* +.DS_Store* \ No newline at end of file diff --git a/UnityMcpBridge/Editor/Data/McpClients.cs b/UnityMcpBridge/Editor/Data/McpClients.cs index d370df87..2360ab1b 100644 --- a/UnityMcpBridge/Editor/Data/McpClients.cs +++ b/UnityMcpBridge/Editor/Data/McpClients.cs @@ -43,6 +43,26 @@ public class McpClients mcpType = McpTypes.Cursor, configStatus = "Not Configured", }, + new() + { + name = "VSCode GitHub Copilot", + windowsConfigPath = Path.Combine( + Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), + "Code", + "User", + "settings.json" + ), + linuxConfigPath = Path.Combine( + Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), + "Library", + "Application Support", + "Code", + "User", + "settings.json" + ), + mcpType = McpTypes.VSCode, + configStatus = "Not Configured", + }, }; // Initialize status enums after construction diff --git a/UnityMcpBridge/Editor/Models/McpTypes.cs b/UnityMcpBridge/Editor/Models/McpTypes.cs index 913ed47c..0259b9c2 100644 --- a/UnityMcpBridge/Editor/Models/McpTypes.cs +++ b/UnityMcpBridge/Editor/Models/McpTypes.cs @@ -4,6 +4,7 @@ public enum McpTypes { ClaudeDesktop, Cursor, + VSCode, } } diff --git a/UnityMcpBridge/Editor/Windows/UnityMcpEditorWindow.cs b/UnityMcpBridge/Editor/Windows/UnityMcpEditorWindow.cs index c794fb71..1971bc19 100644 --- a/UnityMcpBridge/Editor/Windows/UnityMcpEditorWindow.cs +++ b/UnityMcpBridge/Editor/Windows/UnityMcpEditorWindow.cs @@ -137,24 +137,74 @@ private void ConfigurationSection(McpClient mcpClient) // Create muted button style for Manual Setup GUIStyle mutedButtonStyle = new(buttonStyle); - if ( - GUILayout.Button( - $"Auto Configure {mcpClient.name}", - buttonStyle, - GUILayout.Height(28) - ) - ) + if (mcpClient.mcpType == McpTypes.VSCode) { - ConfigureMcpClient(mcpClient); - } + // Special handling for VSCode GitHub Copilot + if ( + GUILayout.Button( + "Auto Configure VSCode with GitHub Copilot", + buttonStyle, + GUILayout.Height(28) + ) + ) + { + ConfigureMcpClient(mcpClient); + } - if (GUILayout.Button("Manual Setup", mutedButtonStyle, GUILayout.Height(28))) + if (GUILayout.Button("Manual Setup", mutedButtonStyle, GUILayout.Height(28))) + { + // Show VSCode specific manual setup window + string configPath = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) + ? mcpClient.windowsConfigPath + : mcpClient.linuxConfigPath; + + // Get the Python directory path + string pythonDir = FindPackagePythonDirectory(); + + // Create VSCode-specific configuration + var vscodeConfig = new + { + mcp = new + { + servers = new + { + UnityMCP = new + { + command = "uv", + args = new[] { "--directory", pythonDir, "run", "server.py" } + } + } + } + }; + + JsonSerializerSettings jsonSettings = new() { Formatting = Formatting.Indented }; + string manualConfigJson = JsonConvert.SerializeObject(vscodeConfig, jsonSettings); + + VSCodeManualSetupWindow.ShowWindow(configPath, manualConfigJson); + } + } + else { - // Get the appropriate config path based on OS - string configPath = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) - ? mcpClient.windowsConfigPath - : mcpClient.linuxConfigPath; - ShowManualInstructionsWindow(configPath, mcpClient); + // Standard client buttons + if ( + GUILayout.Button( + $"Auto Configure {mcpClient.name}", + buttonStyle, + GUILayout.Height(28) + ) + ) + { + ConfigureMcpClient(mcpClient); + } + + if (GUILayout.Button("Manual Setup", mutedButtonStyle, GUILayout.Height(28))) + { + // Get the appropriate config path based on OS + string configPath = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) + ? mcpClient.windowsConfigPath + : mcpClient.linuxConfigPath; + ShowManualInstructionsWindow(configPath, mcpClient); + } } EditorGUILayout.Space(5); @@ -274,7 +324,7 @@ private void ToggleUnityBridge() isUnityBridgeRunning = !isUnityBridgeRunning; } - private string WriteToConfig(string pythonDir, string configPath) + private string WriteToConfig(string pythonDir, string configPath, McpClient mcpClient = null) { // Create configuration object for unityMCP McpConfigServer unityMCPConfig = new() @@ -303,14 +353,36 @@ private string WriteToConfig(string pythonDir, string configPath) dynamic existingConfig = JsonConvert.DeserializeObject(existingJson); existingConfig ??= new Newtonsoft.Json.Linq.JObject(); - // Ensure mcpServers object exists - if (existingConfig.mcpServers == null) + // Handle different client types with a switch statement + switch (mcpClient?.mcpType) { - existingConfig.mcpServers = new Newtonsoft.Json.Linq.JObject(); + case McpTypes.VSCode: + // VSCode specific configuration + // Ensure mcp object exists + if (existingConfig.mcp == null) + { + existingConfig.mcp = new Newtonsoft.Json.Linq.JObject(); + } + + // Ensure mcp.servers object exists + if (existingConfig.mcp.servers == null) + { + existingConfig.mcp.servers = new Newtonsoft.Json.Linq.JObject(); + } + break; + + default: + // Standard MCP configuration (Claude Desktop, Cursor, etc.) + // Ensure mcpServers object exists + if (existingConfig.mcpServers == null) + { + existingConfig.mcpServers = new Newtonsoft.Json.Linq.JObject(); + } + break; } - // Add/update unityMCP while preserving other servers - existingConfig.mcpServers.unityMCP = + // Add/update UnityMCP server in VSCode settings + existingConfig.mcp.servers.unityMCP = JsonConvert.DeserializeObject( JsonConvert.SerializeObject(unityMCPConfig) ); @@ -334,22 +406,47 @@ private void ShowManualInstructionsWindow(string configPath, McpClient mcpClient { // Get the Python directory path using Package Manager API string pythonDir = FindPackagePythonDirectory(); - - // Create the manual configuration message - McpConfig jsonConfig = new() + string manualConfigJson; + + if (mcpClient.mcpType == McpTypes.VSCode) { - mcpServers = new McpConfigServers + // Create VSCode-specific configuration with proper format + var vscodeConfig = new { - unityMCP = new McpConfigServer + mcp = new { - command = "uv", - args = new[] { "--directory", pythonDir, "run", "server.py" }, + servers = new + { + unityMCP = new + { + command = "uv", + args = new[] { "--directory", pythonDir, "run", "server.py" } + } + } + } + }; + + JsonSerializerSettings jsonSettings = new() { Formatting = Formatting.Indented }; + manualConfigJson = JsonConvert.SerializeObject(vscodeConfig, jsonSettings); + } + else + { + // Create standard MCP configuration for other clients + McpConfig jsonConfig = new() + { + mcpServers = new McpConfigServers + { + unityMCP = new McpConfigServer + { + command = "uv", + args = new[] { "--directory", pythonDir, "run", "server.py" }, + }, }, - }, - }; + }; - JsonSerializerSettings jsonSettings = new() { Formatting = Formatting.Indented }; - string manualConfigJson = JsonConvert.SerializeObject(jsonConfig, jsonSettings); + JsonSerializerSettings jsonSettings = new() { Formatting = Formatting.Indented }; + manualConfigJson = JsonConvert.SerializeObject(jsonConfig, jsonSettings); + } ManualConfigEditorWindow.ShowWindow(configPath, manualConfigJson, mcpClient); } @@ -450,7 +547,7 @@ private string ConfigureMcpClient(McpClient mcpClient) return "Manual Configuration Required"; } - string result = WriteToConfig(pythonDir, configPath); + string result = WriteToConfig(pythonDir, configPath, mcpClient); // Update the client status after successful configuration if (result == "Configured successfully") @@ -542,29 +639,59 @@ private void CheckMcpConfiguration(McpClient mcpClient) } string configJson = File.ReadAllText(configPath); - McpConfig config = JsonConvert.DeserializeObject(configJson); - - if (config?.mcpServers?.unityMCP != null) - { - string pythonDir = ServerInstaller.GetServerPath(); - if ( - pythonDir != null - && Array.Exists( - config.mcpServers.unityMCP.args, - arg => arg.Contains(pythonDir, StringComparison.Ordinal) - ) - ) - { - mcpClient.SetStatus(McpStatus.Configured); - } - else - { - mcpClient.SetStatus(McpStatus.IncorrectPath); - } - } - else + string pythonDir = ServerInstaller.GetServerPath(); + + // Use switch statement to handle different client types + switch (mcpClient.mcpType) { - mcpClient.SetStatus(McpStatus.MissingConfig); + case McpTypes.VSCode: + dynamic config = JsonConvert.DeserializeObject(configJson); + + if (config?.mcp?.servers?.unityMCP != null) + { + // Extract args from VSCode config format + var args = config.mcp.servers.unityMCP.args.ToObject(); + + if (pythonDir != null && + Array.Exists(args, arg => arg.Contains(pythonDir, StringComparison.Ordinal))) + { + mcpClient.SetStatus(McpStatus.Configured); + } + else + { + mcpClient.SetStatus(McpStatus.IncorrectPath); + } + } + else + { + mcpClient.SetStatus(McpStatus.MissingConfig); + } + break; + + default: + // Standard MCP configuration check for Claude Desktop, Cursor, etc. + McpConfig standardConfig = JsonConvert.DeserializeObject(configJson); + + if (standardConfig?.mcpServers?.unityMCP != null) + { + if (pythonDir != null + && Array.Exists( + standardConfig.mcpServers.unityMCP.args, + arg => arg.Contains(pythonDir, StringComparison.Ordinal) + )) + { + mcpClient.SetStatus(McpStatus.Configured); + } + else + { + mcpClient.SetStatus(McpStatus.IncorrectPath); + } + } + else + { + mcpClient.SetStatus(McpStatus.MissingConfig); + } + break; } } catch (Exception e) diff --git a/UnityMcpBridge/Editor/Windows/VSCodeManualSetupWindow.cs b/UnityMcpBridge/Editor/Windows/VSCodeManualSetupWindow.cs new file mode 100644 index 00000000..bfdbe92a --- /dev/null +++ b/UnityMcpBridge/Editor/Windows/VSCodeManualSetupWindow.cs @@ -0,0 +1,297 @@ +using System.Runtime.InteropServices; +using UnityEditor; +using UnityEngine; +using UnityMcpBridge.Editor.Models; + +namespace UnityMcpBridge.Editor.Windows +{ + public class VSCodeManualSetupWindow : EditorWindow + { + private string configPath; + private string configJson; + private Vector2 scrollPos; + private bool pathCopied = false; + private bool jsonCopied = false; + private float copyFeedbackTimer = 0; + + public static void ShowWindow(string configPath, string configJson) + { + VSCodeManualSetupWindow window = GetWindow("VSCode GitHub Copilot Setup"); + window.configPath = configPath; + window.configJson = configJson; + window.minSize = new Vector2(550, 500); + window.Show(); + } + + private void OnGUI() + { + scrollPos = EditorGUILayout.BeginScrollView(scrollPos); + + // Header with improved styling + EditorGUILayout.Space(10); + Rect titleRect = EditorGUILayout.GetControlRect(false, 30); + EditorGUI.DrawRect( + new Rect(titleRect.x, titleRect.y, titleRect.width, titleRect.height), + new Color(0.2f, 0.2f, 0.2f, 0.1f) + ); + GUI.Label( + new Rect(titleRect.x + 10, titleRect.y + 6, titleRect.width - 20, titleRect.height), + "VSCode GitHub Copilot MCP Setup", + EditorStyles.boldLabel + ); + EditorGUILayout.Space(10); + + // Instructions with improved styling + EditorGUILayout.BeginVertical(EditorStyles.helpBox); + + Rect headerRect = EditorGUILayout.GetControlRect(false, 24); + EditorGUI.DrawRect( + new Rect(headerRect.x, headerRect.y, headerRect.width, headerRect.height), + new Color(0.1f, 0.1f, 0.1f, 0.2f) + ); + GUI.Label( + new Rect( + headerRect.x + 8, + headerRect.y + 4, + headerRect.width - 16, + headerRect.height + ), + "Setting up GitHub Copilot in VSCode with Unity MCP", + EditorStyles.boldLabel + ); + EditorGUILayout.Space(10); + + GUIStyle instructionStyle = new(EditorStyles.wordWrappedLabel) + { + margin = new RectOffset(10, 10, 5, 5), + }; + + EditorGUILayout.LabelField( + "1. Prerequisites", + EditorStyles.boldLabel + ); + EditorGUILayout.LabelField( + "• Ensure you have VSCode installed", + instructionStyle + ); + EditorGUILayout.LabelField( + "• Ensure you have GitHub Copilot extension installed in VSCode", + instructionStyle + ); + EditorGUILayout.LabelField( + "• Ensure you have a valid GitHub Copilot subscription", + instructionStyle + ); + EditorGUILayout.Space(5); + + EditorGUILayout.LabelField( + "2. Steps to Configure", + EditorStyles.boldLabel + ); + EditorGUILayout.LabelField( + "a) Open VSCode Settings (File > Preferences > Settings)", + instructionStyle + ); + EditorGUILayout.LabelField( + "b) Click on the 'Open Settings (JSON)' button in the top right", + instructionStyle + ); + EditorGUILayout.LabelField( + "c) Add the MCP configuration shown below to your settings.json file", + instructionStyle + ); + EditorGUILayout.LabelField( + "d) Save the file and restart VSCode", + instructionStyle + ); + EditorGUILayout.Space(5); + + EditorGUILayout.LabelField( + "3. VSCode settings.json location:", + EditorStyles.boldLabel + ); + + // Path section with improved styling + EditorGUILayout.BeginVertical(EditorStyles.helpBox); + string displayPath; + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + displayPath = System.IO.Path.Combine( + System.Environment.GetFolderPath(System.Environment.SpecialFolder.ApplicationData), + "Code", + "User", + "settings.json" + ); + } + else + { + displayPath = System.IO.Path.Combine( + System.Environment.GetFolderPath(System.Environment.SpecialFolder.UserProfile), + "Library", + "Application Support", + "Code", + "User", + "settings.json" + ); + } + + // Prevent text overflow by allowing the text field to wrap + GUIStyle pathStyle = new(EditorStyles.textField) { wordWrap = true }; + + EditorGUILayout.TextField( + displayPath, + pathStyle, + GUILayout.Height(EditorGUIUtility.singleLineHeight) + ); + + // Copy button with improved styling + EditorGUILayout.BeginHorizontal(); + GUILayout.FlexibleSpace(); + GUIStyle copyButtonStyle = new(GUI.skin.button) + { + padding = new RectOffset(15, 15, 5, 5), + margin = new RectOffset(10, 10, 5, 5), + }; + + if ( + GUILayout.Button( + "Copy Path", + copyButtonStyle, + GUILayout.Height(25), + GUILayout.Width(100) + ) + ) + { + EditorGUIUtility.systemCopyBuffer = displayPath; + pathCopied = true; + copyFeedbackTimer = 2f; + } + + if ( + GUILayout.Button( + "Open File", + copyButtonStyle, + GUILayout.Height(25), + GUILayout.Width(100) + ) + ) + { + // Open the file using the system's default application + System.Diagnostics.Process.Start( + new System.Diagnostics.ProcessStartInfo + { + FileName = displayPath, + UseShellExecute = true, + } + ); + } + + if (pathCopied) + { + GUIStyle feedbackStyle = new(EditorStyles.label); + feedbackStyle.normal.textColor = Color.green; + EditorGUILayout.LabelField("Copied!", feedbackStyle, GUILayout.Width(60)); + } + + EditorGUILayout.EndHorizontal(); + EditorGUILayout.EndVertical(); + EditorGUILayout.Space(10); + + EditorGUILayout.LabelField( + "4. Add this configuration to your settings.json:", + EditorStyles.boldLabel + ); + + // JSON section with improved styling + EditorGUILayout.BeginVertical(EditorStyles.helpBox); + + // Improved text area for JSON with syntax highlighting colors + GUIStyle jsonStyle = new(EditorStyles.textArea) + { + font = EditorStyles.boldFont, + wordWrap = true, + }; + jsonStyle.normal.textColor = new Color(0.3f, 0.6f, 0.9f); // Syntax highlighting blue + + // Draw the JSON in a text area with a taller height for better readability + EditorGUILayout.TextArea(configJson, jsonStyle, GUILayout.Height(200)); + + // Copy JSON button with improved styling + EditorGUILayout.BeginHorizontal(); + GUILayout.FlexibleSpace(); + + if ( + GUILayout.Button( + "Copy JSON", + copyButtonStyle, + GUILayout.Height(25), + GUILayout.Width(100) + ) + ) + { + EditorGUIUtility.systemCopyBuffer = configJson; + jsonCopied = true; + copyFeedbackTimer = 2f; + } + + if (jsonCopied) + { + GUIStyle feedbackStyle = new(EditorStyles.label); + feedbackStyle.normal.textColor = Color.green; + EditorGUILayout.LabelField("Copied!", feedbackStyle, GUILayout.Width(60)); + } + + EditorGUILayout.EndHorizontal(); + EditorGUILayout.EndVertical(); + + EditorGUILayout.Space(10); + EditorGUILayout.LabelField( + "5. After configuration:", + EditorStyles.boldLabel + ); + EditorGUILayout.LabelField( + "• Restart VSCode", + instructionStyle + ); + EditorGUILayout.LabelField( + "• GitHub Copilot will now be able to interact with your Unity project through the MCP protocol", + instructionStyle + ); + EditorGUILayout.LabelField( + "• Remember to have the Unity MCP Bridge running in Unity Editor", + instructionStyle + ); + + EditorGUILayout.EndVertical(); + + EditorGUILayout.Space(10); + + // Close button at the bottom + EditorGUILayout.BeginHorizontal(); + GUILayout.FlexibleSpace(); + if (GUILayout.Button("Close", GUILayout.Height(30), GUILayout.Width(100))) + { + Close(); + } + GUILayout.FlexibleSpace(); + EditorGUILayout.EndHorizontal(); + + EditorGUILayout.EndScrollView(); + } + + private void Update() + { + // Handle the feedback message timer + if (copyFeedbackTimer > 0) + { + copyFeedbackTimer -= Time.deltaTime; + if (copyFeedbackTimer <= 0) + { + pathCopied = false; + jsonCopied = false; + Repaint(); + } + } + } + } +} From b4910be508b81139cee89399b478977659199bee Mon Sep 17 00:00:00 2001 From: xsodus Date: Mon, 12 May 2025 09:24:10 +0700 Subject: [PATCH 2/6] Refactor VSCode configuration to use lowercase 'unityMCP' and improve argument checking --- UnityMcpBridge/Editor/Windows/UnityMcpEditorWindow.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/UnityMcpBridge/Editor/Windows/UnityMcpEditorWindow.cs b/UnityMcpBridge/Editor/Windows/UnityMcpEditorWindow.cs index 1971bc19..45394f92 100644 --- a/UnityMcpBridge/Editor/Windows/UnityMcpEditorWindow.cs +++ b/UnityMcpBridge/Editor/Windows/UnityMcpEditorWindow.cs @@ -168,7 +168,7 @@ private void ConfigurationSection(McpClient mcpClient) { servers = new { - UnityMCP = new + unityMCP = new { command = "uv", args = new[] { "--directory", pythonDir, "run", "server.py" } @@ -180,6 +180,7 @@ private void ConfigurationSection(McpClient mcpClient) JsonSerializerSettings jsonSettings = new() { Formatting = Formatting.Indented }; string manualConfigJson = JsonConvert.SerializeObject(vscodeConfig, jsonSettings); + // Use the VSCodeManualSetupWindow directly since we're in the same namespace VSCodeManualSetupWindow.ShowWindow(configPath, manualConfigJson); } } @@ -653,7 +654,7 @@ private void CheckMcpConfiguration(McpClient mcpClient) var args = config.mcp.servers.unityMCP.args.ToObject(); if (pythonDir != null && - Array.Exists(args, arg => arg.Contains(pythonDir, StringComparison.Ordinal))) + Array.Exists(args, new Predicate(arg => arg.Contains(pythonDir, StringComparison.Ordinal)))) { mcpClient.SetStatus(McpStatus.Configured); } From d63eb7d4e4cee485e65bc379659ad08273f1acc3 Mon Sep 17 00:00:00 2001 From: xsodus Date: Mon, 12 May 2025 09:25:01 +0700 Subject: [PATCH 3/6] Add metadata file for VSCodeManualSetupWindow with file format version and GUID --- UnityMcpBridge/Editor/Windows/VSCodeManualSetupWindow.cs.meta | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 UnityMcpBridge/Editor/Windows/VSCodeManualSetupWindow.cs.meta diff --git a/UnityMcpBridge/Editor/Windows/VSCodeManualSetupWindow.cs.meta b/UnityMcpBridge/Editor/Windows/VSCodeManualSetupWindow.cs.meta new file mode 100644 index 00000000..437ccab6 --- /dev/null +++ b/UnityMcpBridge/Editor/Windows/VSCodeManualSetupWindow.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 377fe73d52cf0435fabead5f50a0d204 \ No newline at end of file From 861e6bc62cc387428ef7c3c78ca988f539a275fa Mon Sep 17 00:00:00 2001 From: xsodus Date: Mon, 12 May 2025 09:34:04 +0700 Subject: [PATCH 4/6] Refactor VSCodeManualSetupWindow to inherit from ManualConfigEditorWindow and override methods for improved functionality --- .../Windows/ManualConfigEditorWindow.cs | 18 +++---- .../Editor/Windows/VSCodeManualSetupWindow.cs | 52 +++++++++++-------- 2 files changed, 38 insertions(+), 32 deletions(-) diff --git a/UnityMcpBridge/Editor/Windows/ManualConfigEditorWindow.cs b/UnityMcpBridge/Editor/Windows/ManualConfigEditorWindow.cs index 17c93e0b..07b7da3f 100644 --- a/UnityMcpBridge/Editor/Windows/ManualConfigEditorWindow.cs +++ b/UnityMcpBridge/Editor/Windows/ManualConfigEditorWindow.cs @@ -8,13 +8,13 @@ namespace UnityMcpBridge.Editor.Windows // Editor window to display manual configuration instructions public class ManualConfigEditorWindow : EditorWindow { - private string configPath; - private string configJson; - private Vector2 scrollPos; - private bool pathCopied = false; - private bool jsonCopied = false; - private float copyFeedbackTimer = 0; - private McpClient mcpClient; + protected string configPath; + protected string configJson; + protected Vector2 scrollPos; + protected bool pathCopied = false; + protected bool jsonCopied = false; + protected float copyFeedbackTimer = 0; + protected McpClient mcpClient; public static void ShowWindow(string configPath, string configJson, McpClient mcpClient) { @@ -26,7 +26,7 @@ public static void ShowWindow(string configPath, string configJson, McpClient mc window.Show(); } - private void OnGUI() + protected virtual void OnGUI() { scrollPos = EditorGUILayout.BeginScrollView(scrollPos); @@ -245,7 +245,7 @@ private void OnGUI() EditorGUILayout.EndScrollView(); } - private void Update() + protected virtual void Update() { // Handle the feedback message timer if (copyFeedbackTimer > 0) diff --git a/UnityMcpBridge/Editor/Windows/VSCodeManualSetupWindow.cs b/UnityMcpBridge/Editor/Windows/VSCodeManualSetupWindow.cs index bfdbe92a..4eb2eb1c 100644 --- a/UnityMcpBridge/Editor/Windows/VSCodeManualSetupWindow.cs +++ b/UnityMcpBridge/Editor/Windows/VSCodeManualSetupWindow.cs @@ -5,25 +5,34 @@ namespace UnityMcpBridge.Editor.Windows { - public class VSCodeManualSetupWindow : EditorWindow + public class VSCodeManualSetupWindow : ManualConfigEditorWindow { - private string configPath; - private string configJson; - private Vector2 scrollPos; - private bool pathCopied = false; - private bool jsonCopied = false; - private float copyFeedbackTimer = 0; - - public static void ShowWindow(string configPath, string configJson) + // Not defining fields that are inherited from ManualConfigEditorWindow: + // protected string configPath; + // protected string configJson; + // protected Vector2 scrollPos; + // protected bool pathCopied; + // protected bool jsonCopied; + // protected float copyFeedbackTimer; + // protected McpClient mcpClient; + public static new void ShowWindow(string configPath, string configJson) { - VSCodeManualSetupWindow window = GetWindow("VSCode GitHub Copilot Setup"); + var window = GetWindow("VSCode GitHub Copilot Setup"); window.configPath = configPath; window.configJson = configJson; window.minSize = new Vector2(550, 500); + + // Create a McpClient for VSCode + window.mcpClient = new McpClient + { + name = "VSCode GitHub Copilot", + mcpType = McpTypes.VSCode + }; + window.Show(); } - private void OnGUI() + protected override void OnGUI() { scrollPos = EditorGUILayout.BeginScrollView(scrollPos); @@ -135,6 +144,12 @@ private void OnGUI() ); } + // Store the path in the base class config path + if (string.IsNullOrEmpty(configPath)) + { + configPath = displayPath; + } + // Prevent text overflow by allowing the text field to wrap GUIStyle pathStyle = new(EditorStyles.textField) { wordWrap = true }; @@ -279,19 +294,10 @@ private void OnGUI() EditorGUILayout.EndScrollView(); } - private void Update() + protected override void Update() { - // Handle the feedback message timer - if (copyFeedbackTimer > 0) - { - copyFeedbackTimer -= Time.deltaTime; - if (copyFeedbackTimer <= 0) - { - pathCopied = false; - jsonCopied = false; - Repaint(); - } - } + // Call the base implementation which handles the copy feedback timer + base.Update(); } } } From 48a865d3862d5f7f029f46ec2a5ba71a411ad54f Mon Sep 17 00:00:00 2001 From: xsodus Date: Mon, 12 May 2025 09:34:27 +0700 Subject: [PATCH 5/6] Remove redundant field definitions inherited from ManualConfigEditorWindow in VSCodeManualSetupWindow --- UnityMcpBridge/Editor/Windows/VSCodeManualSetupWindow.cs | 8 -------- 1 file changed, 8 deletions(-) diff --git a/UnityMcpBridge/Editor/Windows/VSCodeManualSetupWindow.cs b/UnityMcpBridge/Editor/Windows/VSCodeManualSetupWindow.cs index 4eb2eb1c..a0b78e2d 100644 --- a/UnityMcpBridge/Editor/Windows/VSCodeManualSetupWindow.cs +++ b/UnityMcpBridge/Editor/Windows/VSCodeManualSetupWindow.cs @@ -7,14 +7,6 @@ namespace UnityMcpBridge.Editor.Windows { public class VSCodeManualSetupWindow : ManualConfigEditorWindow { - // Not defining fields that are inherited from ManualConfigEditorWindow: - // protected string configPath; - // protected string configJson; - // protected Vector2 scrollPos; - // protected bool pathCopied; - // protected bool jsonCopied; - // protected float copyFeedbackTimer; - // protected McpClient mcpClient; public static new void ShowWindow(string configPath, string configJson) { var window = GetWindow("VSCode GitHub Copilot Setup"); From a880bf485b16512243d5e8b946030090dd49d251 Mon Sep 17 00:00:00 2001 From: xsodus Date: Mon, 12 May 2025 15:05:56 +0700 Subject: [PATCH 6/6] Refactor configuration handling in UnityMcpEditorWindow to streamline JSON serialization and improve client type management --- .../Editor/Windows/UnityMcpEditorWindow.cs | 120 +++++++++--------- 1 file changed, 58 insertions(+), 62 deletions(-) diff --git a/UnityMcpBridge/Editor/Windows/UnityMcpEditorWindow.cs b/UnityMcpBridge/Editor/Windows/UnityMcpEditorWindow.cs index 45394f92..bf11f1fd 100644 --- a/UnityMcpBridge/Editor/Windows/UnityMcpEditorWindow.cs +++ b/UnityMcpBridge/Editor/Windows/UnityMcpEditorWindow.cs @@ -409,44 +409,46 @@ private void ShowManualInstructionsWindow(string configPath, McpClient mcpClient string pythonDir = FindPackagePythonDirectory(); string manualConfigJson; - if (mcpClient.mcpType == McpTypes.VSCode) + // Create common JsonSerializerSettings + JsonSerializerSettings jsonSettings = new() { Formatting = Formatting.Indented }; + + // Use switch statement to handle different client types + switch (mcpClient.mcpType) { - // Create VSCode-specific configuration with proper format - var vscodeConfig = new - { - mcp = new + case McpTypes.VSCode: + // Create VSCode-specific configuration with proper format + var vscodeConfig = new { - servers = new + mcp = new { - unityMCP = new + servers = new { - command = "uv", - args = new[] { "--directory", pythonDir, "run", "server.py" } + unityMCP = new + { + command = "uv", + args = new[] { "--directory", pythonDir, "run", "server.py" } + } } } - } - }; - - JsonSerializerSettings jsonSettings = new() { Formatting = Formatting.Indented }; - manualConfigJson = JsonConvert.SerializeObject(vscodeConfig, jsonSettings); - } - else - { - // Create standard MCP configuration for other clients - McpConfig jsonConfig = new() - { - mcpServers = new McpConfigServers + }; + manualConfigJson = JsonConvert.SerializeObject(vscodeConfig, jsonSettings); + break; + + default: + // Create standard MCP configuration for other clients + McpConfig jsonConfig = new() { - unityMCP = new McpConfigServer + mcpServers = new McpConfigServers { - command = "uv", - args = new[] { "--directory", pythonDir, "run", "server.py" }, + unityMCP = new McpConfigServer + { + command = "uv", + args = new[] { "--directory", pythonDir, "run", "server.py" }, + }, }, - }, - }; - - JsonSerializerSettings jsonSettings = new() { Formatting = Formatting.Indented }; - manualConfigJson = JsonConvert.SerializeObject(jsonConfig, jsonSettings); + }; + manualConfigJson = JsonConvert.SerializeObject(jsonConfig, jsonSettings); + break; } ManualConfigEditorWindow.ShowWindow(configPath, manualConfigJson, mcpClient); @@ -642,7 +644,10 @@ private void CheckMcpConfiguration(McpClient mcpClient) string configJson = File.ReadAllText(configPath); string pythonDir = ServerInstaller.GetServerPath(); - // Use switch statement to handle different client types + // Use switch statement to handle different client types, extracting common logic + string[] args = null; + bool configExists = false; + switch (mcpClient.mcpType) { case McpTypes.VSCode: @@ -651,49 +656,40 @@ private void CheckMcpConfiguration(McpClient mcpClient) if (config?.mcp?.servers?.unityMCP != null) { // Extract args from VSCode config format - var args = config.mcp.servers.unityMCP.args.ToObject(); - - if (pythonDir != null && - Array.Exists(args, new Predicate(arg => arg.Contains(pythonDir, StringComparison.Ordinal)))) - { - mcpClient.SetStatus(McpStatus.Configured); - } - else - { - mcpClient.SetStatus(McpStatus.IncorrectPath); - } - } - else - { - mcpClient.SetStatus(McpStatus.MissingConfig); + args = config.mcp.servers.unityMCP.args.ToObject(); + configExists = true; } break; default: // Standard MCP configuration check for Claude Desktop, Cursor, etc. McpConfig standardConfig = JsonConvert.DeserializeObject(configJson); - + if (standardConfig?.mcpServers?.unityMCP != null) { - if (pythonDir != null - && Array.Exists( - standardConfig.mcpServers.unityMCP.args, - arg => arg.Contains(pythonDir, StringComparison.Ordinal) - )) - { - mcpClient.SetStatus(McpStatus.Configured); - } - else - { - mcpClient.SetStatus(McpStatus.IncorrectPath); - } - } - else - { - mcpClient.SetStatus(McpStatus.MissingConfig); + args = standardConfig.mcpServers.unityMCP.args; + configExists = true; } break; } + + // Common logic for checking configuration status + if (configExists) + { + if (pythonDir != null && + Array.Exists(args, arg => arg.Contains(pythonDir, StringComparison.Ordinal))) + { + mcpClient.SetStatus(McpStatus.Configured); + } + else + { + mcpClient.SetStatus(McpStatus.IncorrectPath); + } + } + else + { + mcpClient.SetStatus(McpStatus.MissingConfig); + } } catch (Exception e) {