Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,4 @@ CONTRIBUTING.md.meta
.idea/
.vscode/
.aider*
.DS_Store*
20 changes: 20 additions & 0 deletions UnityMcpBridge/Editor/Data/McpClients.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions UnityMcpBridge/Editor/Models/McpTypes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ public enum McpTypes
{
ClaudeDesktop,
Cursor,
VSCode,
}
}

18 changes: 9 additions & 9 deletions UnityMcpBridge/Editor/Windows/ManualConfigEditorWindow.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand All @@ -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);

Expand Down Expand Up @@ -245,7 +245,7 @@ private void OnGUI()
EditorGUILayout.EndScrollView();
}

private void Update()
protected virtual void Update()
{
// Handle the feedback message timer
if (copyFeedbackTimer > 0)
Expand Down
218 changes: 171 additions & 47 deletions UnityMcpBridge/Editor/Windows/UnityMcpEditorWindow.cs
Original file line number Diff line number Diff line change
Expand Up @@ -137,24 +137,75 @@ 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);

// Use the VSCodeManualSetupWindow directly since we're in the same namespace
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);

Expand Down Expand Up @@ -274,7 +325,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()
Expand Down Expand Up @@ -303,14 +354,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<Newtonsoft.Json.Linq.JToken>(
JsonConvert.SerializeObject(unityMCPConfig)
);
Expand All @@ -334,22 +407,49 @@ 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;

// Create common JsonSerializerSettings
JsonSerializerSettings jsonSettings = new() { Formatting = Formatting.Indented };

// Use switch statement to handle different client types
switch (mcpClient.mcpType)
{
mcpServers = new McpConfigServers
{
unityMCP = new McpConfigServer
case McpTypes.VSCode:
// Create VSCode-specific configuration with proper format
var vscodeConfig = new
{
command = "uv",
args = new[] { "--directory", pythonDir, "run", "server.py" },
},
},
};

JsonSerializerSettings jsonSettings = new() { Formatting = Formatting.Indented };
string manualConfigJson = JsonConvert.SerializeObject(jsonConfig, jsonSettings);
mcp = new
{
servers = new
{
unityMCP = new
{
command = "uv",
args = new[] { "--directory", pythonDir, "run", "server.py" }
}
}
}
};
manualConfigJson = JsonConvert.SerializeObject(vscodeConfig, jsonSettings);
break;

default:
// 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" },
},
},
};
manualConfigJson = JsonConvert.SerializeObject(jsonConfig, jsonSettings);
break;
}

ManualConfigEditorWindow.ShowWindow(configPath, manualConfigJson, mcpClient);
}
Expand Down Expand Up @@ -450,7 +550,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")
Expand Down Expand Up @@ -542,18 +642,42 @@ private void CheckMcpConfiguration(McpClient mcpClient)
}

string configJson = File.ReadAllText(configPath);
McpConfig config = JsonConvert.DeserializeObject<McpConfig>(configJson);

if (config?.mcpServers?.unityMCP != null)
string pythonDir = ServerInstaller.GetServerPath();

// Use switch statement to handle different client types, extracting common logic
string[] args = null;
bool configExists = false;

switch (mcpClient.mcpType)
{
string pythonDir = ServerInstaller.GetServerPath();
if (
pythonDir != null
&& Array.Exists(
config.mcpServers.unityMCP.args,
arg => arg.Contains(pythonDir, StringComparison.Ordinal)
)
)
case McpTypes.VSCode:
dynamic config = JsonConvert.DeserializeObject(configJson);

if (config?.mcp?.servers?.unityMCP != null)
{
// Extract args from VSCode config format
args = config.mcp.servers.unityMCP.args.ToObject<string[]>();
configExists = true;
}
break;

default:
// Standard MCP configuration check for Claude Desktop, Cursor, etc.
McpConfig standardConfig = JsonConvert.DeserializeObject<McpConfig>(configJson);

if (standardConfig?.mcpServers?.unityMCP != null)
{
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);
}
Expand Down
Loading