using System;
using System.IO;
using UnityEditor;
using UnityEngine;
namespace LocalMcp.UnityServer
{
/// <summary>
/// Unity MCP Setup Window - Provides one-click setup for Claude Code connection
/// </summary>
public class McpSetupWindow : EditorWindow
{
private string _serverPath;
private string _projectPath;
private string _configPath;
private string _runtimeConfigPath;
private bool _isServerPathValid;
private bool _isConfigGenerated;
private Vector2 _scrollPosition;
private GUIStyle _headerStyle;
private GUIStyle _successStyle;
private GUIStyle _errorStyle;
private GUIStyle _codeStyle;
private bool _stylesInitialized;
[MenuItem("Window/Unity MCP/Setup Claude Code", false, 100)]
public static void ShowWindow()
{
var window = GetWindow<McpSetupWindow>("MCP Setup");
window.minSize = new Vector2(500, 400);
window.Show();
}
private void OnEnable()
{
RefreshPaths();
}
private void InitStyles()
{
if (_stylesInitialized) return;
_headerStyle = new GUIStyle(EditorStyles.boldLabel)
{
fontSize = 14,
margin = new RectOffset(0, 0, 10, 10)
};
_successStyle = new GUIStyle(EditorStyles.label)
{
normal = { textColor = new Color(0.2f, 0.8f, 0.2f) }
};
_errorStyle = new GUIStyle(EditorStyles.label)
{
normal = { textColor = new Color(0.9f, 0.3f, 0.3f) }
};
_codeStyle = new GUIStyle(EditorStyles.textArea)
{
wordWrap = true,
padding = new RectOffset(8, 8, 8, 8)
};
_stylesInitialized = true;
}
private void RefreshPaths()
{
// Find Server~ path in package
_serverPath = FindServerPath();
_isServerPathValid = !string.IsNullOrEmpty(_serverPath) && Directory.Exists(_serverPath);
// Project paths
_projectPath = Path.GetDirectoryName(Application.dataPath);
_configPath = Path.Combine(_projectPath, ".mcp.json");
_runtimeConfigPath = Path.Combine(_projectPath, ".unity-mcp-runtime.json");
// Check if config exists
_isConfigGenerated = File.Exists(_configPath);
}
private string FindServerPath()
{
// Try to find Server~/mcp-bridge in package cache
var packagePath = GetPackagePath();
if (!string.IsNullOrEmpty(packagePath))
{
var serverPath = Path.Combine(packagePath, "Server~", "mcp-bridge");
if (Directory.Exists(serverPath))
{
return serverPath;
}
}
// Fallback: check common locations
var possiblePaths = new[]
{
Path.Combine(Application.dataPath, "..", "Packages", "com.local.mcp.unityserver", "Server~", "mcp-bridge"),
Path.Combine(Application.dataPath, "..", "Library", "PackageCache")
};
foreach (var basePath in possiblePaths)
{
if (Directory.Exists(basePath))
{
// Search for Server~/mcp-bridge
try
{
var dirs = Directory.GetDirectories(basePath, "com.local.mcp.unityserver*", SearchOption.TopDirectoryOnly);
foreach (var dir in dirs)
{
var serverPath = Path.Combine(dir, "Server~", "mcp-bridge");
if (Directory.Exists(serverPath))
{
return serverPath;
}
}
}
catch { }
}
}
return null;
}
private string GetPackagePath()
{
// Use PackageInfo to find our package
var packageInfo = UnityEditor.PackageManager.PackageInfo.FindForAssembly(typeof(McpSetupWindow).Assembly);
return packageInfo?.resolvedPath;
}
private void OnGUI()
{
InitStyles();
_scrollPosition = EditorGUILayout.BeginScrollView(_scrollPosition);
// Header
EditorGUILayout.LabelField("Unity MCP - Claude Code Setup", _headerStyle);
EditorGUILayout.Space(5);
// Status Section
DrawStatusSection();
EditorGUILayout.Space(10);
// Setup Actions
DrawSetupSection();
EditorGUILayout.Space(10);
// Configuration Preview
DrawConfigSection();
EditorGUILayout.Space(10);
// Help Section
DrawHelpSection();
EditorGUILayout.EndScrollView();
}
private void DrawStatusSection()
{
EditorGUILayout.BeginVertical(EditorStyles.helpBox);
EditorGUILayout.LabelField("Status", EditorStyles.boldLabel);
// Server Path
EditorGUILayout.BeginHorizontal();
EditorGUILayout.LabelField("MCP Bridge:", GUILayout.Width(100));
if (_isServerPathValid)
{
EditorGUILayout.LabelField("Found", _successStyle);
}
else
{
EditorGUILayout.LabelField("Not Found", _errorStyle);
}
EditorGUILayout.EndHorizontal();
// Config File
EditorGUILayout.BeginHorizontal();
EditorGUILayout.LabelField("Config File:", GUILayout.Width(100));
if (_isConfigGenerated)
{
EditorGUILayout.LabelField(".mcp.json exists", _successStyle);
}
else
{
EditorGUILayout.LabelField("Not generated", _errorStyle);
}
EditorGUILayout.EndHorizontal();
// Runtime Config
var hasRuntimeConfig = File.Exists(_runtimeConfigPath);
EditorGUILayout.BeginHorizontal();
EditorGUILayout.LabelField("Runtime:", GUILayout.Width(100));
if (hasRuntimeConfig)
{
EditorGUILayout.LabelField("Server running", _successStyle);
}
else
{
EditorGUILayout.LabelField("Server not started", _errorStyle);
}
EditorGUILayout.EndHorizontal();
EditorGUILayout.EndVertical();
}
private void DrawSetupSection()
{
EditorGUILayout.BeginVertical(EditorStyles.helpBox);
EditorGUILayout.LabelField("Setup Actions", EditorStyles.boldLabel);
EditorGUILayout.Space(5);
// Main setup button
GUI.enabled = _isServerPathValid;
if (GUILayout.Button("Generate Configuration", GUILayout.Height(30)))
{
GenerateConfiguration();
}
GUI.enabled = true;
EditorGUILayout.Space(5);
// Additional actions
EditorGUILayout.BeginHorizontal();
if (GUILayout.Button("Refresh Status"))
{
RefreshPaths();
}
if (GUILayout.Button("Open Config Folder"))
{
EditorUtility.RevealInFinder(_projectPath);
}
if (GUILayout.Button("Install npm packages"))
{
InstallNpmPackages();
}
EditorGUILayout.EndHorizontal();
EditorGUILayout.EndVertical();
}
private void DrawConfigSection()
{
EditorGUILayout.BeginVertical(EditorStyles.helpBox);
EditorGUILayout.LabelField("Configuration Preview", EditorStyles.boldLabel);
EditorGUILayout.Space(5);
var config = GenerateConfigJson();
EditorGUILayout.TextArea(config, _codeStyle, GUILayout.Height(120));
EditorGUILayout.Space(5);
EditorGUILayout.BeginHorizontal();
if (GUILayout.Button("Copy to Clipboard"))
{
GUIUtility.systemCopyBuffer = config;
Debug.Log("[MCP Setup] Configuration copied to clipboard");
}
if (_isConfigGenerated && GUILayout.Button("View .mcp.json"))
{
EditorUtility.RevealInFinder(_configPath);
}
EditorGUILayout.EndHorizontal();
EditorGUILayout.EndVertical();
}
private void DrawHelpSection()
{
EditorGUILayout.BeginVertical(EditorStyles.helpBox);
EditorGUILayout.LabelField("Setup Instructions", EditorStyles.boldLabel);
EditorGUILayout.HelpBox(
"1. Click 'Generate Configuration' to create .mcp.json\n" +
"2. Run 'npm install' in the mcp-bridge folder (or click Install npm packages)\n" +
"3. Restart Claude Code\n" +
"4. Claude Code will automatically detect the Unity MCP server",
MessageType.Info
);
EditorGUILayout.Space(5);
if (!string.IsNullOrEmpty(_serverPath))
{
EditorGUILayout.LabelField("Server Path:", EditorStyles.miniLabel);
EditorGUILayout.SelectableLabel(_serverPath, EditorStyles.textField, GUILayout.Height(18));
}
EditorGUILayout.EndVertical();
}
private string GenerateConfigJson()
{
var indexPath = _isServerPathValid
? Path.Combine(_serverPath, "index.js").Replace("\\", "/")
: "<path-to-package>/Server~/mcp-bridge/index.js";
var projectPathEscaped = _projectPath.Replace("\\", "/");
return "{\n" +
" \"mcpServers\": {\n" +
" \"unity\": {\n" +
" \"command\": \"node\",\n" +
" \"args\": [\"" + indexPath + "\"],\n" +
" \"cwd\": \"" + projectPathEscaped + "\"\n" +
" }\n" +
" }\n" +
"}";
}
private void GenerateConfiguration()
{
try
{
var config = GenerateConfigJson();
File.WriteAllText(_configPath, config);
_isConfigGenerated = true;
Debug.Log("[MCP Setup] Configuration generated: " + _configPath);
EditorUtility.DisplayDialog(
"MCP Setup Complete",
"Configuration file generated at:\n" + _configPath + "\n\n" +
"Next steps:\n" +
"1. Run 'npm install' in Server~/mcp-bridge (or click 'Install npm packages')\n" +
"2. Restart Claude Code\n" +
"3. The Unity MCP server will be available",
"OK"
);
}
catch (Exception e)
{
Debug.LogError("[MCP Setup] Failed to generate configuration: " + e.Message);
EditorUtility.DisplayDialog(
"MCP Setup Error",
"Failed to generate configuration:\n" + e.Message,
"OK"
);
}
}
private void InstallNpmPackages()
{
if (!_isServerPathValid)
{
EditorUtility.DisplayDialog("Error", "Server path not found", "OK");
return;
}
try
{
var startInfo = new System.Diagnostics.ProcessStartInfo
{
FileName = "npm",
Arguments = "install",
WorkingDirectory = _serverPath,
UseShellExecute = false,
RedirectStandardOutput = true,
RedirectStandardError = true,
CreateNoWindow = true
};
var process = System.Diagnostics.Process.Start(startInfo);
var output = process.StandardOutput.ReadToEnd();
var error = process.StandardError.ReadToEnd();
process.WaitForExit();
if (process.ExitCode == 0)
{
Debug.Log("[MCP Setup] npm install completed:\n" + output);
EditorUtility.DisplayDialog(
"npm install Complete",
"Dependencies installed successfully.\n\nYou can now restart Claude Code.",
"OK"
);
}
else
{
Debug.LogError("[MCP Setup] npm install failed:\n" + error);
EditorUtility.DisplayDialog(
"npm install Failed",
"Error:\n" + error + "\n\nTry running manually:\ncd \"" + _serverPath + "\"\nnpm install",
"OK"
);
}
}
catch (Exception e)
{
Debug.LogError("[MCP Setup] Failed to run npm: " + e.Message);
EditorUtility.DisplayDialog(
"Error",
"Failed to run npm:\n" + e.Message + "\n\nMake sure Node.js is installed.",
"OK"
);
}
}
}
}