838 lines
31 KiB
C#
838 lines
31 KiB
C#
using System;
|
|
using System.Diagnostics;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Runtime.InteropServices;
|
|
using UnityEditor;
|
|
using UnityEditor.UIElements; // For Unity 2021 compatibility
|
|
using UnityEngine;
|
|
using UnityEngine.UIElements;
|
|
using MCPForUnity.Editor.Data;
|
|
using MCPForUnity.Editor.Helpers;
|
|
using MCPForUnity.Editor.Models;
|
|
using MCPForUnity.Editor.Services;
|
|
|
|
namespace MCPForUnity.Editor.Windows
|
|
{
|
|
public class MCPForUnityEditorWindowNew : EditorWindow
|
|
{
|
|
// Protocol enum for future HTTP support
|
|
private enum ConnectionProtocol
|
|
{
|
|
Stdio,
|
|
// HTTPStreaming // Future
|
|
}
|
|
|
|
// Settings UI Elements
|
|
private Label versionLabel;
|
|
private Toggle debugLogsToggle;
|
|
private EnumField validationLevelField;
|
|
private Label validationDescription;
|
|
private Foldout advancedSettingsFoldout;
|
|
private TextField mcpServerPathOverride;
|
|
private TextField uvPathOverride;
|
|
private Button browsePythonButton;
|
|
private Button clearPythonButton;
|
|
private Button browseUvButton;
|
|
private Button clearUvButton;
|
|
private VisualElement mcpServerPathStatus;
|
|
private VisualElement uvPathStatus;
|
|
|
|
// Connection UI Elements
|
|
private EnumField protocolDropdown;
|
|
private TextField unityPortField;
|
|
private TextField serverPortField;
|
|
private VisualElement statusIndicator;
|
|
private Label connectionStatusLabel;
|
|
private Button connectionToggleButton;
|
|
private VisualElement healthIndicator;
|
|
private Label healthStatusLabel;
|
|
private Button testConnectionButton;
|
|
private VisualElement serverStatusBanner;
|
|
private Label serverStatusMessage;
|
|
private Button downloadServerButton;
|
|
private Button rebuildServerButton;
|
|
|
|
// Client UI Elements
|
|
private DropdownField clientDropdown;
|
|
private Button configureAllButton;
|
|
private VisualElement clientStatusIndicator;
|
|
private Label clientStatusLabel;
|
|
private Button configureButton;
|
|
private VisualElement claudeCliPathRow;
|
|
private TextField claudeCliPath;
|
|
private Button browseClaudeButton;
|
|
private Foldout manualConfigFoldout;
|
|
private TextField configPathField;
|
|
private Button copyPathButton;
|
|
private Button openFileButton;
|
|
private TextField configJsonField;
|
|
private Button copyJsonButton;
|
|
private Label installationStepsLabel;
|
|
|
|
// Data
|
|
private readonly McpClients mcpClients = new();
|
|
private int selectedClientIndex = 0;
|
|
private ValidationLevel currentValidationLevel = ValidationLevel.Standard;
|
|
|
|
// Validation levels matching the existing enum
|
|
private enum ValidationLevel
|
|
{
|
|
Basic,
|
|
Standard,
|
|
Comprehensive,
|
|
Strict
|
|
}
|
|
|
|
public static void ShowWindow()
|
|
{
|
|
var window = GetWindow<MCPForUnityEditorWindowNew>("MCP For Unity");
|
|
window.minSize = new Vector2(500, 600);
|
|
}
|
|
public void CreateGUI()
|
|
{
|
|
// Determine base path (Package Manager vs Asset Store install)
|
|
string basePath = AssetPathUtility.GetMcpPackageRootPath();
|
|
|
|
// Load UXML
|
|
var visualTree = AssetDatabase.LoadAssetAtPath<VisualTreeAsset>(
|
|
$"{basePath}/Editor/Windows/MCPForUnityEditorWindowNew.uxml"
|
|
);
|
|
|
|
if (visualTree == null)
|
|
{
|
|
McpLog.Error($"Failed to load UXML at: {basePath}/Editor/Windows/MCPForUnityEditorWindowNew.uxml");
|
|
return;
|
|
}
|
|
|
|
visualTree.CloneTree(rootVisualElement);
|
|
|
|
// Load USS
|
|
var styleSheet = AssetDatabase.LoadAssetAtPath<StyleSheet>(
|
|
$"{basePath}/Editor/Windows/MCPForUnityEditorWindowNew.uss"
|
|
);
|
|
|
|
if (styleSheet != null)
|
|
{
|
|
rootVisualElement.styleSheets.Add(styleSheet);
|
|
}
|
|
|
|
// Cache UI elements
|
|
CacheUIElements();
|
|
|
|
// Initialize UI
|
|
InitializeUI();
|
|
|
|
// Register callbacks
|
|
RegisterCallbacks();
|
|
|
|
// Initial update
|
|
UpdateConnectionStatus();
|
|
UpdateServerStatusBanner();
|
|
UpdateClientStatus();
|
|
UpdatePathOverrides();
|
|
// Technically not required to connect, but if we don't do this, the UI will be blank
|
|
UpdateManualConfiguration();
|
|
UpdateClaudeCliPathVisibility();
|
|
}
|
|
|
|
private void OnEnable()
|
|
{
|
|
EditorApplication.update += OnEditorUpdate;
|
|
}
|
|
|
|
private void OnDisable()
|
|
{
|
|
EditorApplication.update -= OnEditorUpdate;
|
|
}
|
|
|
|
private void OnFocus()
|
|
{
|
|
// Only refresh data if UI is built
|
|
if (rootVisualElement == null || rootVisualElement.childCount == 0)
|
|
return;
|
|
|
|
RefreshAllData();
|
|
}
|
|
|
|
private void OnEditorUpdate()
|
|
{
|
|
// Only update UI if it's built
|
|
if (rootVisualElement == null || rootVisualElement.childCount == 0)
|
|
return;
|
|
|
|
UpdateConnectionStatus();
|
|
}
|
|
|
|
private void RefreshAllData()
|
|
{
|
|
// Update connection status
|
|
UpdateConnectionStatus();
|
|
|
|
// Auto-verify bridge health if connected
|
|
if (MCPServiceLocator.Bridge.IsRunning)
|
|
{
|
|
VerifyBridgeConnection();
|
|
}
|
|
|
|
// Update path overrides
|
|
UpdatePathOverrides();
|
|
|
|
// Refresh selected client (may have been configured externally)
|
|
if (selectedClientIndex >= 0 && selectedClientIndex < mcpClients.clients.Count)
|
|
{
|
|
var client = mcpClients.clients[selectedClientIndex];
|
|
MCPServiceLocator.Client.CheckClientStatus(client);
|
|
UpdateClientStatus();
|
|
UpdateManualConfiguration();
|
|
UpdateClaudeCliPathVisibility();
|
|
}
|
|
}
|
|
|
|
private void CacheUIElements()
|
|
{
|
|
// Settings
|
|
versionLabel = rootVisualElement.Q<Label>("version-label");
|
|
debugLogsToggle = rootVisualElement.Q<Toggle>("debug-logs-toggle");
|
|
validationLevelField = rootVisualElement.Q<EnumField>("validation-level");
|
|
validationDescription = rootVisualElement.Q<Label>("validation-description");
|
|
advancedSettingsFoldout = rootVisualElement.Q<Foldout>("advanced-settings-foldout");
|
|
mcpServerPathOverride = rootVisualElement.Q<TextField>("python-path-override");
|
|
uvPathOverride = rootVisualElement.Q<TextField>("uv-path-override");
|
|
browsePythonButton = rootVisualElement.Q<Button>("browse-python-button");
|
|
clearPythonButton = rootVisualElement.Q<Button>("clear-python-button");
|
|
browseUvButton = rootVisualElement.Q<Button>("browse-uv-button");
|
|
clearUvButton = rootVisualElement.Q<Button>("clear-uv-button");
|
|
mcpServerPathStatus = rootVisualElement.Q<VisualElement>("mcp-server-path-status");
|
|
uvPathStatus = rootVisualElement.Q<VisualElement>("uv-path-status");
|
|
|
|
// Connection
|
|
protocolDropdown = rootVisualElement.Q<EnumField>("protocol-dropdown");
|
|
unityPortField = rootVisualElement.Q<TextField>("unity-port");
|
|
serverPortField = rootVisualElement.Q<TextField>("server-port");
|
|
statusIndicator = rootVisualElement.Q<VisualElement>("status-indicator");
|
|
connectionStatusLabel = rootVisualElement.Q<Label>("connection-status");
|
|
connectionToggleButton = rootVisualElement.Q<Button>("connection-toggle");
|
|
healthIndicator = rootVisualElement.Q<VisualElement>("health-indicator");
|
|
healthStatusLabel = rootVisualElement.Q<Label>("health-status");
|
|
testConnectionButton = rootVisualElement.Q<Button>("test-connection-button");
|
|
serverStatusBanner = rootVisualElement.Q<VisualElement>("server-status-banner");
|
|
serverStatusMessage = rootVisualElement.Q<Label>("server-status-message");
|
|
downloadServerButton = rootVisualElement.Q<Button>("download-server-button");
|
|
rebuildServerButton = rootVisualElement.Q<Button>("rebuild-server-button");
|
|
|
|
// Client
|
|
clientDropdown = rootVisualElement.Q<DropdownField>("client-dropdown");
|
|
configureAllButton = rootVisualElement.Q<Button>("configure-all-button");
|
|
clientStatusIndicator = rootVisualElement.Q<VisualElement>("client-status-indicator");
|
|
clientStatusLabel = rootVisualElement.Q<Label>("client-status");
|
|
configureButton = rootVisualElement.Q<Button>("configure-button");
|
|
claudeCliPathRow = rootVisualElement.Q<VisualElement>("claude-cli-path-row");
|
|
claudeCliPath = rootVisualElement.Q<TextField>("claude-cli-path");
|
|
browseClaudeButton = rootVisualElement.Q<Button>("browse-claude-button");
|
|
manualConfigFoldout = rootVisualElement.Q<Foldout>("manual-config-foldout");
|
|
configPathField = rootVisualElement.Q<TextField>("config-path");
|
|
copyPathButton = rootVisualElement.Q<Button>("copy-path-button");
|
|
openFileButton = rootVisualElement.Q<Button>("open-file-button");
|
|
configJsonField = rootVisualElement.Q<TextField>("config-json");
|
|
copyJsonButton = rootVisualElement.Q<Button>("copy-json-button");
|
|
installationStepsLabel = rootVisualElement.Q<Label>("installation-steps");
|
|
}
|
|
|
|
private void InitializeUI()
|
|
{
|
|
// Settings Section
|
|
versionLabel.text = AssetPathUtility.GetPackageVersion();
|
|
debugLogsToggle.value = EditorPrefs.GetBool("MCPForUnity.DebugLogs", false);
|
|
|
|
validationLevelField.Init(ValidationLevel.Standard);
|
|
int savedLevel = EditorPrefs.GetInt("MCPForUnity.ValidationLevel", 1);
|
|
currentValidationLevel = (ValidationLevel)Mathf.Clamp(savedLevel, 0, 3);
|
|
validationLevelField.value = currentValidationLevel;
|
|
UpdateValidationDescription();
|
|
|
|
// Advanced settings starts collapsed
|
|
advancedSettingsFoldout.value = false;
|
|
|
|
// Connection Section
|
|
protocolDropdown.Init(ConnectionProtocol.Stdio);
|
|
protocolDropdown.SetEnabled(false); // Disabled for now, only stdio supported
|
|
|
|
unityPortField.value = MCPServiceLocator.Bridge.CurrentPort.ToString();
|
|
serverPortField.value = "6500";
|
|
|
|
// Client Configuration
|
|
var clientNames = mcpClients.clients.Select(c => c.name).ToList();
|
|
clientDropdown.choices = clientNames;
|
|
if (clientNames.Count > 0)
|
|
{
|
|
clientDropdown.index = 0;
|
|
}
|
|
|
|
// Manual config starts collapsed
|
|
manualConfigFoldout.value = false;
|
|
|
|
// Claude CLI path row hidden by default
|
|
claudeCliPathRow.style.display = DisplayStyle.None;
|
|
}
|
|
|
|
private void RegisterCallbacks()
|
|
{
|
|
// Settings callbacks
|
|
debugLogsToggle.RegisterValueChangedCallback(evt =>
|
|
{
|
|
EditorPrefs.SetBool("MCPForUnity.DebugLogs", evt.newValue);
|
|
});
|
|
|
|
validationLevelField.RegisterValueChangedCallback(evt =>
|
|
{
|
|
currentValidationLevel = (ValidationLevel)evt.newValue;
|
|
EditorPrefs.SetInt("MCPForUnity.ValidationLevel", (int)currentValidationLevel);
|
|
UpdateValidationDescription();
|
|
});
|
|
|
|
// Advanced settings callbacks
|
|
browsePythonButton.clicked += OnBrowsePythonClicked;
|
|
clearPythonButton.clicked += OnClearPythonClicked;
|
|
browseUvButton.clicked += OnBrowseUvClicked;
|
|
clearUvButton.clicked += OnClearUvClicked;
|
|
|
|
// Connection callbacks
|
|
connectionToggleButton.clicked += OnConnectionToggleClicked;
|
|
testConnectionButton.clicked += OnTestConnectionClicked;
|
|
downloadServerButton.clicked += OnDownloadServerClicked;
|
|
rebuildServerButton.clicked += OnRebuildServerClicked;
|
|
|
|
// Client callbacks
|
|
clientDropdown.RegisterValueChangedCallback(evt =>
|
|
{
|
|
selectedClientIndex = clientDropdown.index;
|
|
UpdateClientStatus();
|
|
UpdateManualConfiguration();
|
|
UpdateClaudeCliPathVisibility();
|
|
});
|
|
|
|
configureAllButton.clicked += OnConfigureAllClientsClicked;
|
|
configureButton.clicked += OnConfigureClicked;
|
|
browseClaudeButton.clicked += OnBrowseClaudeClicked;
|
|
copyPathButton.clicked += OnCopyPathClicked;
|
|
openFileButton.clicked += OnOpenFileClicked;
|
|
copyJsonButton.clicked += OnCopyJsonClicked;
|
|
}
|
|
|
|
private void UpdateValidationDescription()
|
|
{
|
|
validationDescription.text = GetValidationLevelDescription((int)currentValidationLevel);
|
|
}
|
|
|
|
private string GetValidationLevelDescription(int index)
|
|
{
|
|
return index switch
|
|
{
|
|
0 => "Only basic syntax checks (braces, quotes, comments)",
|
|
1 => "Syntax checks + Unity best practices and warnings",
|
|
2 => "All checks + semantic analysis and performance warnings",
|
|
3 => "Full semantic validation with namespace/type resolution (requires Roslyn)",
|
|
_ => "Standard validation"
|
|
};
|
|
}
|
|
|
|
private void UpdateConnectionStatus()
|
|
{
|
|
var bridgeService = MCPServiceLocator.Bridge;
|
|
bool isRunning = bridgeService.IsRunning;
|
|
|
|
if (isRunning)
|
|
{
|
|
connectionStatusLabel.text = "Connected";
|
|
statusIndicator.RemoveFromClassList("disconnected");
|
|
statusIndicator.AddToClassList("connected");
|
|
connectionToggleButton.text = "Stop";
|
|
}
|
|
else
|
|
{
|
|
connectionStatusLabel.text = "Disconnected";
|
|
statusIndicator.RemoveFromClassList("connected");
|
|
statusIndicator.AddToClassList("disconnected");
|
|
connectionToggleButton.text = "Start";
|
|
|
|
// Reset health status when disconnected
|
|
healthStatusLabel.text = "Unknown";
|
|
healthIndicator.RemoveFromClassList("healthy");
|
|
healthIndicator.RemoveFromClassList("warning");
|
|
healthIndicator.AddToClassList("unknown");
|
|
}
|
|
|
|
// Update ports
|
|
unityPortField.value = bridgeService.CurrentPort.ToString();
|
|
}
|
|
|
|
private void UpdateClientStatus()
|
|
{
|
|
if (selectedClientIndex < 0 || selectedClientIndex >= mcpClients.clients.Count)
|
|
return;
|
|
|
|
var client = mcpClients.clients[selectedClientIndex];
|
|
MCPServiceLocator.Client.CheckClientStatus(client);
|
|
|
|
clientStatusLabel.text = client.GetStatusDisplayString();
|
|
|
|
// Reset inline color style (clear error state from OnConfigureClicked)
|
|
clientStatusLabel.style.color = StyleKeyword.Null;
|
|
|
|
// Update status indicator color
|
|
clientStatusIndicator.RemoveFromClassList("configured");
|
|
clientStatusIndicator.RemoveFromClassList("not-configured");
|
|
clientStatusIndicator.RemoveFromClassList("warning");
|
|
|
|
switch (client.status)
|
|
{
|
|
case McpStatus.Configured:
|
|
case McpStatus.Running:
|
|
case McpStatus.Connected:
|
|
clientStatusIndicator.AddToClassList("configured");
|
|
break;
|
|
case McpStatus.IncorrectPath:
|
|
case McpStatus.CommunicationError:
|
|
case McpStatus.NoResponse:
|
|
clientStatusIndicator.AddToClassList("warning");
|
|
break;
|
|
default:
|
|
clientStatusIndicator.AddToClassList("not-configured");
|
|
break;
|
|
}
|
|
|
|
// Update configure button text for Claude Code
|
|
if (client.mcpType == McpTypes.ClaudeCode)
|
|
{
|
|
bool isConfigured = client.status == McpStatus.Configured;
|
|
configureButton.text = isConfigured ? "Unregister" : "Register";
|
|
}
|
|
else
|
|
{
|
|
configureButton.text = "Configure";
|
|
}
|
|
}
|
|
|
|
private void UpdateManualConfiguration()
|
|
{
|
|
if (selectedClientIndex < 0 || selectedClientIndex >= mcpClients.clients.Count)
|
|
return;
|
|
|
|
var client = mcpClients.clients[selectedClientIndex];
|
|
|
|
// Get config path
|
|
string configPath = MCPServiceLocator.Client.GetConfigPath(client);
|
|
configPathField.value = configPath;
|
|
|
|
// Get config JSON
|
|
string configJson = MCPServiceLocator.Client.GenerateConfigJson(client);
|
|
configJsonField.value = configJson;
|
|
|
|
// Get installation steps
|
|
string steps = MCPServiceLocator.Client.GetInstallationSteps(client);
|
|
installationStepsLabel.text = steps;
|
|
}
|
|
|
|
private void UpdateClaudeCliPathVisibility()
|
|
{
|
|
if (selectedClientIndex < 0 || selectedClientIndex >= mcpClients.clients.Count)
|
|
return;
|
|
|
|
var client = mcpClients.clients[selectedClientIndex];
|
|
|
|
// Show Claude CLI path only for Claude Code client
|
|
if (client.mcpType == McpTypes.ClaudeCode)
|
|
{
|
|
string claudePath = MCPServiceLocator.Paths.GetClaudeCliPath();
|
|
if (string.IsNullOrEmpty(claudePath))
|
|
{
|
|
// Show path selector if not found
|
|
claudeCliPathRow.style.display = DisplayStyle.Flex;
|
|
claudeCliPath.value = "Not found - click Browse to select";
|
|
}
|
|
else
|
|
{
|
|
// Show detected path
|
|
claudeCliPathRow.style.display = DisplayStyle.Flex;
|
|
claudeCliPath.value = claudePath;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
claudeCliPathRow.style.display = DisplayStyle.None;
|
|
}
|
|
}
|
|
|
|
private void UpdatePathOverrides()
|
|
{
|
|
var pathService = MCPServiceLocator.Paths;
|
|
|
|
// MCP Server Path
|
|
string mcpServerPath = pathService.GetMcpServerPath();
|
|
if (pathService.HasMcpServerOverride)
|
|
{
|
|
mcpServerPathOverride.value = mcpServerPath ?? "(override set but invalid)";
|
|
}
|
|
else
|
|
{
|
|
mcpServerPathOverride.value = mcpServerPath ?? "(auto-detected)";
|
|
}
|
|
|
|
// Update status indicator
|
|
mcpServerPathStatus.RemoveFromClassList("valid");
|
|
mcpServerPathStatus.RemoveFromClassList("invalid");
|
|
if (!string.IsNullOrEmpty(mcpServerPath) && File.Exists(Path.Combine(mcpServerPath, "server.py")))
|
|
{
|
|
mcpServerPathStatus.AddToClassList("valid");
|
|
}
|
|
else
|
|
{
|
|
mcpServerPathStatus.AddToClassList("invalid");
|
|
}
|
|
|
|
// UV Path
|
|
string uvPath = pathService.GetUvPath();
|
|
if (pathService.HasUvPathOverride)
|
|
{
|
|
uvPathOverride.value = uvPath ?? "(override set but invalid)";
|
|
}
|
|
else
|
|
{
|
|
uvPathOverride.value = uvPath ?? "(auto-detected)";
|
|
}
|
|
|
|
// Update status indicator
|
|
uvPathStatus.RemoveFromClassList("valid");
|
|
uvPathStatus.RemoveFromClassList("invalid");
|
|
if (!string.IsNullOrEmpty(uvPath) && File.Exists(uvPath))
|
|
{
|
|
uvPathStatus.AddToClassList("valid");
|
|
}
|
|
else
|
|
{
|
|
uvPathStatus.AddToClassList("invalid");
|
|
}
|
|
}
|
|
|
|
// Button callbacks
|
|
private void OnConnectionToggleClicked()
|
|
{
|
|
var bridgeService = MCPServiceLocator.Bridge;
|
|
|
|
if (bridgeService.IsRunning)
|
|
{
|
|
bridgeService.Stop();
|
|
}
|
|
else
|
|
{
|
|
bridgeService.Start();
|
|
|
|
// Verify connection after starting (Option C: verify on connect)
|
|
EditorApplication.delayCall += () =>
|
|
{
|
|
if (bridgeService.IsRunning)
|
|
{
|
|
VerifyBridgeConnection();
|
|
}
|
|
};
|
|
}
|
|
|
|
UpdateConnectionStatus();
|
|
}
|
|
|
|
private void OnTestConnectionClicked()
|
|
{
|
|
VerifyBridgeConnection();
|
|
}
|
|
|
|
private void VerifyBridgeConnection()
|
|
{
|
|
var bridgeService = MCPServiceLocator.Bridge;
|
|
|
|
if (!bridgeService.IsRunning)
|
|
{
|
|
healthStatusLabel.text = "Disconnected";
|
|
healthIndicator.RemoveFromClassList("healthy");
|
|
healthIndicator.RemoveFromClassList("warning");
|
|
healthIndicator.AddToClassList("unknown");
|
|
McpLog.Warn("Cannot verify connection: Bridge is not running");
|
|
return;
|
|
}
|
|
|
|
var result = bridgeService.Verify(bridgeService.CurrentPort);
|
|
|
|
healthIndicator.RemoveFromClassList("healthy");
|
|
healthIndicator.RemoveFromClassList("warning");
|
|
healthIndicator.RemoveFromClassList("unknown");
|
|
|
|
if (result.Success && result.PingSucceeded)
|
|
{
|
|
healthStatusLabel.text = "Healthy";
|
|
healthIndicator.AddToClassList("healthy");
|
|
McpLog.Info("Bridge verification successful");
|
|
}
|
|
else if (result.HandshakeValid)
|
|
{
|
|
healthStatusLabel.text = "Ping Failed";
|
|
healthIndicator.AddToClassList("warning");
|
|
McpLog.Warn($"Bridge verification warning: {result.Message}");
|
|
}
|
|
else
|
|
{
|
|
healthStatusLabel.text = "Unhealthy";
|
|
healthIndicator.AddToClassList("warning");
|
|
McpLog.Error($"Bridge verification failed: {result.Message}");
|
|
}
|
|
}
|
|
|
|
private void OnDownloadServerClicked()
|
|
{
|
|
if (ServerInstaller.DownloadAndInstallServer())
|
|
{
|
|
UpdateServerStatusBanner();
|
|
UpdatePathOverrides();
|
|
EditorUtility.DisplayDialog(
|
|
"Download Complete",
|
|
"Server installed successfully! Start your connection and configure your MCP clients to begin.",
|
|
"OK"
|
|
);
|
|
}
|
|
}
|
|
|
|
private void OnRebuildServerClicked()
|
|
{
|
|
try
|
|
{
|
|
bool success = ServerInstaller.RebuildMcpServer();
|
|
if (success)
|
|
{
|
|
EditorUtility.DisplayDialog("MCP For Unity", "Server rebuilt successfully.", "OK");
|
|
UpdateServerStatusBanner();
|
|
UpdatePathOverrides();
|
|
}
|
|
else
|
|
{
|
|
EditorUtility.DisplayDialog("MCP For Unity", "Rebuild failed. Please check Console for details.", "OK");
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
McpLog.Error($"Failed to rebuild server: {ex.Message}");
|
|
EditorUtility.DisplayDialog("MCP For Unity", $"Rebuild failed: {ex.Message}", "OK");
|
|
}
|
|
}
|
|
|
|
private void UpdateServerStatusBanner()
|
|
{
|
|
bool hasEmbedded = ServerInstaller.HasEmbeddedServer();
|
|
string installedVer = ServerInstaller.GetInstalledServerVersion();
|
|
string packageVer = AssetPathUtility.GetPackageVersion();
|
|
|
|
// Show/hide download vs rebuild buttons
|
|
if (hasEmbedded)
|
|
{
|
|
downloadServerButton.style.display = DisplayStyle.None;
|
|
rebuildServerButton.style.display = DisplayStyle.Flex;
|
|
}
|
|
else
|
|
{
|
|
downloadServerButton.style.display = DisplayStyle.Flex;
|
|
rebuildServerButton.style.display = DisplayStyle.None;
|
|
}
|
|
|
|
// Update banner
|
|
if (!hasEmbedded && string.IsNullOrEmpty(installedVer))
|
|
{
|
|
serverStatusMessage.text = "\u26A0 Server not installed. Click 'Download & Install Server' to get started.";
|
|
serverStatusBanner.style.display = DisplayStyle.Flex;
|
|
}
|
|
else if (!hasEmbedded && !string.IsNullOrEmpty(installedVer) && installedVer != packageVer)
|
|
{
|
|
serverStatusMessage.text = $"\u26A0 Server update available (v{installedVer} \u2192 v{packageVer}). Update recommended.";
|
|
serverStatusBanner.style.display = DisplayStyle.Flex;
|
|
}
|
|
else
|
|
{
|
|
serverStatusBanner.style.display = DisplayStyle.None;
|
|
}
|
|
}
|
|
|
|
private void OnConfigureAllClientsClicked()
|
|
{
|
|
try
|
|
{
|
|
var summary = MCPServiceLocator.Client.ConfigureAllDetectedClients();
|
|
|
|
// Build detailed message
|
|
string message = summary.GetSummaryMessage() + "\n\n";
|
|
foreach (var msg in summary.Messages)
|
|
{
|
|
message += msg + "\n";
|
|
}
|
|
|
|
EditorUtility.DisplayDialog("Configure All Clients", message, "OK");
|
|
|
|
// Refresh current client status
|
|
if (selectedClientIndex >= 0 && selectedClientIndex < mcpClients.clients.Count)
|
|
{
|
|
UpdateClientStatus();
|
|
UpdateManualConfiguration();
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
EditorUtility.DisplayDialog("Configuration Failed", ex.Message, "OK");
|
|
}
|
|
}
|
|
|
|
private void OnConfigureClicked()
|
|
{
|
|
if (selectedClientIndex < 0 || selectedClientIndex >= mcpClients.clients.Count)
|
|
return;
|
|
|
|
var client = mcpClients.clients[selectedClientIndex];
|
|
|
|
try
|
|
{
|
|
if (client.mcpType == McpTypes.ClaudeCode)
|
|
{
|
|
bool isConfigured = client.status == McpStatus.Configured;
|
|
if (isConfigured)
|
|
{
|
|
MCPServiceLocator.Client.UnregisterClaudeCode();
|
|
}
|
|
else
|
|
{
|
|
MCPServiceLocator.Client.RegisterClaudeCode();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
MCPServiceLocator.Client.ConfigureClient(client);
|
|
}
|
|
|
|
UpdateClientStatus();
|
|
UpdateManualConfiguration();
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
clientStatusLabel.text = "Error";
|
|
clientStatusLabel.style.color = Color.red;
|
|
McpLog.Error($"Configuration failed: {ex.Message}");
|
|
EditorUtility.DisplayDialog("Configuration Failed", ex.Message, "OK");
|
|
}
|
|
}
|
|
|
|
private void OnBrowsePythonClicked()
|
|
{
|
|
string picked = EditorUtility.OpenFolderPanel("Select MCP Server Directory", Application.dataPath, "");
|
|
if (!string.IsNullOrEmpty(picked))
|
|
{
|
|
try
|
|
{
|
|
MCPServiceLocator.Paths.SetMcpServerOverride(picked);
|
|
UpdatePathOverrides();
|
|
McpLog.Info($"MCP server path override set to: {picked}");
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
EditorUtility.DisplayDialog("Invalid Path", ex.Message, "OK");
|
|
}
|
|
}
|
|
}
|
|
|
|
private void OnClearPythonClicked()
|
|
{
|
|
MCPServiceLocator.Paths.ClearMcpServerOverride();
|
|
UpdatePathOverrides();
|
|
McpLog.Info("MCP server path override cleared");
|
|
}
|
|
|
|
private void OnBrowseUvClicked()
|
|
{
|
|
string suggested = RuntimeInformation.IsOSPlatform(OSPlatform.OSX)
|
|
? "/opt/homebrew/bin"
|
|
: Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles);
|
|
string picked = EditorUtility.OpenFilePanel("Select UV Executable", suggested, "");
|
|
if (!string.IsNullOrEmpty(picked))
|
|
{
|
|
try
|
|
{
|
|
MCPServiceLocator.Paths.SetUvPathOverride(picked);
|
|
UpdatePathOverrides();
|
|
McpLog.Info($"UV path override set to: {picked}");
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
EditorUtility.DisplayDialog("Invalid Path", ex.Message, "OK");
|
|
}
|
|
}
|
|
}
|
|
|
|
private void OnClearUvClicked()
|
|
{
|
|
MCPServiceLocator.Paths.ClearUvPathOverride();
|
|
UpdatePathOverrides();
|
|
McpLog.Info("UV path override cleared");
|
|
}
|
|
|
|
private void OnBrowseClaudeClicked()
|
|
{
|
|
string suggested = RuntimeInformation.IsOSPlatform(OSPlatform.OSX)
|
|
? "/opt/homebrew/bin"
|
|
: Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles);
|
|
string picked = EditorUtility.OpenFilePanel("Select Claude CLI", suggested, "");
|
|
if (!string.IsNullOrEmpty(picked))
|
|
{
|
|
try
|
|
{
|
|
MCPServiceLocator.Paths.SetClaudeCliPathOverride(picked);
|
|
UpdateClaudeCliPathVisibility();
|
|
UpdateClientStatus();
|
|
McpLog.Info($"Claude CLI path override set to: {picked}");
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
EditorUtility.DisplayDialog("Invalid Path", ex.Message, "OK");
|
|
}
|
|
}
|
|
}
|
|
|
|
private void OnCopyPathClicked()
|
|
{
|
|
EditorGUIUtility.systemCopyBuffer = configPathField.value;
|
|
McpLog.Info("Config path copied to clipboard");
|
|
}
|
|
|
|
private void OnOpenFileClicked()
|
|
{
|
|
string path = configPathField.value;
|
|
try
|
|
{
|
|
if (!File.Exists(path))
|
|
{
|
|
EditorUtility.DisplayDialog("Open File", "The configuration file path does not exist.", "OK");
|
|
return;
|
|
}
|
|
|
|
Process.Start(new ProcessStartInfo
|
|
{
|
|
FileName = path,
|
|
UseShellExecute = true
|
|
});
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
McpLog.Error($"Failed to open file: {ex.Message}");
|
|
}
|
|
}
|
|
|
|
private void OnCopyJsonClicked()
|
|
{
|
|
EditorGUIUtility.systemCopyBuffer = configJsonField.value;
|
|
McpLog.Info("Configuration copied to clipboard");
|
|
}
|
|
}
|
|
}
|