Skip to content

Commit 150a396

Browse files
improved claude config error handling
1 parent 3e55b23 commit 150a396

File tree

1 file changed

+247
-18
lines changed

1 file changed

+247
-18
lines changed

Editor/MCPEditorWindow.cs

Lines changed: 247 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
using System.Net.Sockets;
99
using System.Threading.Tasks;
1010
using System.Text;
11+
using System.Collections.Generic;
1112

1213
public class DefaultServerConfig : ServerConfig
1314
{
@@ -277,36 +278,80 @@ private void ConfigureClaudeDesktop()
277278
string serverPath = null;
278279
string pythonDir = null;
279280

280-
// First try to find it in the current project's Assets folder
281-
string localServerPath = Path.GetFullPath(Path.Combine(Application.dataPath, "unity-mcp", "Python", "server.py"));
282-
if (File.Exists(localServerPath))
281+
// List of possible locations to search
282+
var possiblePaths = new List<string>
283283
{
284-
serverPath = localServerPath;
285-
pythonDir = Path.GetDirectoryName(serverPath);
284+
// Search in Assets folder - Manual installation
285+
Path.GetFullPath(Path.Combine(Application.dataPath, "unity-mcp", "Python", "server.py")),
286+
Path.GetFullPath(Path.Combine(Application.dataPath, "Packages", "com.justinpbarnett.unity-mcp", "Python", "server.py")),
287+
288+
// Search in package cache - Package manager installation
289+
Path.GetFullPath(Path.Combine(Application.dataPath, "..", "Library", "PackageCache", "com.justinpbarnett.unity-mcp@*", "Python", "server.py")),
290+
291+
// Search in package manager packages - Git installation
292+
Path.GetFullPath(Path.Combine(Application.dataPath, "..", "Packages", "com.justinpbarnett.unity-mcp", "Python", "server.py"))
293+
};
294+
295+
UnityEngine.Debug.Log("Searching for server.py in the following locations:");
296+
297+
// First try with explicit paths
298+
foreach (var path in possiblePaths)
299+
{
300+
// Skip wildcard paths for now
301+
if (path.Contains("*")) continue;
302+
303+
UnityEngine.Debug.Log($"Checking: {path}");
304+
if (File.Exists(path))
305+
{
306+
serverPath = path;
307+
pythonDir = Path.GetDirectoryName(serverPath);
308+
UnityEngine.Debug.Log($"Found server.py at: {serverPath}");
309+
break;
310+
}
286311
}
287-
else
312+
313+
// If not found, try with wildcard paths (package cache with version)
314+
if (serverPath == null)
288315
{
289-
// If not found locally, try to find it in the package cache
290-
string packageName = "com.justinpbarnett.unity-mcp";
291-
string packageCachePath = Path.Combine(Application.dataPath, "..", "Library", "PackageCache", packageName);
292-
if (Directory.Exists(packageCachePath))
316+
foreach (var path in possiblePaths)
293317
{
294-
serverPath = Path.GetFullPath(Path.Combine(packageCachePath, "Python", "server.py"));
295-
if (File.Exists(serverPath))
318+
if (!path.Contains("*")) continue;
319+
320+
string directoryPath = Path.GetDirectoryName(path);
321+
string searchPattern = Path.GetFileName(Path.GetDirectoryName(path));
322+
string parentDir = Path.GetDirectoryName(directoryPath);
323+
324+
if (Directory.Exists(parentDir))
296325
{
297-
pythonDir = Path.GetDirectoryName(serverPath);
326+
var matchingDirs = Directory.GetDirectories(parentDir, searchPattern);
327+
UnityEngine.Debug.Log($"Searching in: {parentDir} for pattern: {searchPattern}, found {matchingDirs.Length} matches");
328+
329+
foreach (var dir in matchingDirs)
330+
{
331+
string candidatePath = Path.Combine(dir, "Python", "server.py");
332+
UnityEngine.Debug.Log($"Checking: {candidatePath}");
333+
334+
if (File.Exists(candidatePath))
335+
{
336+
serverPath = candidatePath;
337+
pythonDir = Path.GetDirectoryName(serverPath);
338+
UnityEngine.Debug.Log($"Found server.py at: {serverPath}");
339+
break;
340+
}
341+
}
342+
343+
if (serverPath != null) break;
298344
}
299345
}
300346
}
301347

302348
if (serverPath == null || !File.Exists(serverPath))
303349
{
304-
claudeConfigStatus = "Error: Could not find server.py";
305-
UnityEngine.Debug.LogError("Could not find server.py in either local project or package cache");
350+
ShowManualConfigurationInstructions(configPath);
306351
return;
307352
}
308353

309-
UnityEngine.Debug.Log($"Found server.py at: {serverPath}");
354+
UnityEngine.Debug.Log($"Using server.py at: {serverPath}");
310355
UnityEngine.Debug.Log($"Python directory: {pythonDir}");
311356

312357
// Create configuration object
@@ -342,8 +387,192 @@ private void ConfigureClaudeDesktop()
342387
}
343388
catch (Exception e)
344389
{
345-
claudeConfigStatus = "Configuration failed";
346-
UnityEngine.Debug.LogError($"Failed to configure Claude Desktop: {e.Message}");
390+
// Determine the config file path based on OS for error message
391+
string configPath = "";
392+
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
393+
{
394+
configPath = Path.Combine(
395+
Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
396+
"Claude",
397+
"claude_desktop_config.json"
398+
);
399+
}
400+
else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
401+
{
402+
configPath = Path.Combine(
403+
Environment.GetFolderPath(Environment.SpecialFolder.UserProfile),
404+
"Library",
405+
"Application Support",
406+
"Claude",
407+
"claude_desktop_config.json"
408+
);
409+
}
410+
411+
ShowManualConfigurationInstructions(configPath);
412+
UnityEngine.Debug.LogError($"Failed to configure Claude Desktop: {e.Message}\n{e.StackTrace}");
413+
}
414+
}
415+
416+
private void ShowManualConfigurationInstructions(string configPath)
417+
{
418+
claudeConfigStatus = "Error: Manual configuration required";
419+
420+
// Get the Python directory path that's likely to exist
421+
string pythonDir = "";
422+
string[] possibleDirs = {
423+
Path.GetFullPath(Path.Combine(Application.dataPath, "unity-mcp", "Python"))
424+
};
425+
426+
foreach (var dir in possibleDirs)
427+
{
428+
if (Directory.Exists(dir) && File.Exists(Path.Combine(dir, "server.py")))
429+
{
430+
pythonDir = dir;
431+
break;
432+
}
433+
}
434+
435+
// If we couldn't find a Python directory, try to get the Assets path as a fallback
436+
if (string.IsNullOrEmpty(pythonDir))
437+
{
438+
pythonDir = "/path/to/your/unity-mcp/Python";
439+
}
440+
441+
// Create the manual configuration message
442+
var jsonConfig = new MCPConfig
443+
{
444+
mcpServers = new MCPConfigServers
445+
{
446+
unityMCP = new MCPConfigServer
447+
{
448+
command = "uv",
449+
args = new[]
450+
{
451+
"--directory",
452+
pythonDir,
453+
"run",
454+
"server.py"
455+
}
456+
}
457+
}
458+
};
459+
460+
var jsonSettings = new JsonSerializerSettings
461+
{
462+
Formatting = Formatting.Indented
463+
};
464+
string manualConfigJson = JsonConvert.SerializeObject(jsonConfig, jsonSettings);
465+
466+
// Show a dedicated configuration window instead of console logs
467+
ManualConfigWindow.ShowWindow(configPath, manualConfigJson);
468+
}
469+
}
470+
471+
// Editor window to display manual configuration instructions
472+
public class ManualConfigWindow : EditorWindow
473+
{
474+
private string configPath;
475+
private string configJson;
476+
private Vector2 scrollPos;
477+
private bool pathCopied = false;
478+
private bool jsonCopied = false;
479+
private float copyFeedbackTimer = 0;
480+
481+
public static void ShowWindow(string configPath, string configJson)
482+
{
483+
var window = GetWindow<ManualConfigWindow>("Manual Configuration");
484+
window.configPath = configPath;
485+
window.configJson = configJson;
486+
window.minSize = new Vector2(500, 400);
487+
window.Show();
488+
}
489+
490+
private void OnGUI()
491+
{
492+
scrollPos = EditorGUILayout.BeginScrollView(scrollPos);
493+
494+
// Header
495+
EditorGUILayout.Space(10);
496+
EditorGUILayout.LabelField("Claude Desktop Manual Configuration", EditorStyles.boldLabel);
497+
EditorGUILayout.Space(10);
498+
499+
// Instructions
500+
EditorGUILayout.LabelField("The automatic configuration failed. Please follow these steps:", EditorStyles.boldLabel);
501+
EditorGUILayout.Space(5);
502+
503+
EditorGUILayout.LabelField("1. Open Claude Desktop and go to Settings > Developer > Edit Config", EditorStyles.wordWrappedLabel);
504+
EditorGUILayout.LabelField("2. Create or edit the configuration file at:", EditorStyles.wordWrappedLabel);
505+
506+
// Config path section with copy button
507+
EditorGUILayout.BeginHorizontal();
508+
EditorGUILayout.SelectableLabel(configPath, EditorStyles.textField, GUILayout.Height(EditorGUIUtility.singleLineHeight));
509+
510+
if (GUILayout.Button("Copy Path", GUILayout.Width(80)))
511+
{
512+
EditorGUIUtility.systemCopyBuffer = configPath;
513+
pathCopied = true;
514+
copyFeedbackTimer = 2f;
515+
}
516+
517+
EditorGUILayout.EndHorizontal();
518+
519+
if (pathCopied)
520+
{
521+
EditorGUILayout.LabelField("Path copied to clipboard!", EditorStyles.miniLabel);
522+
}
523+
524+
EditorGUILayout.Space(10);
525+
526+
// JSON configuration
527+
EditorGUILayout.LabelField("3. Paste the following JSON configuration:", EditorStyles.wordWrappedLabel);
528+
EditorGUILayout.Space(5);
529+
530+
EditorGUILayout.LabelField("Make sure to replace the Python path if necessary:", EditorStyles.wordWrappedLabel);
531+
EditorGUILayout.Space(5);
532+
533+
// JSON text area with copy button
534+
GUIStyle textAreaStyle = new GUIStyle(EditorStyles.textArea)
535+
{
536+
wordWrap = true,
537+
richText = true
538+
};
539+
540+
EditorGUILayout.BeginHorizontal();
541+
EditorGUILayout.SelectableLabel(configJson, textAreaStyle, GUILayout.MinHeight(200));
542+
EditorGUILayout.EndHorizontal();
543+
544+
if (GUILayout.Button("Copy JSON Configuration"))
545+
{
546+
EditorGUIUtility.systemCopyBuffer = configJson;
547+
jsonCopied = true;
548+
copyFeedbackTimer = 2f;
549+
}
550+
551+
if (jsonCopied)
552+
{
553+
EditorGUILayout.LabelField("JSON copied to clipboard!", EditorStyles.miniLabel);
554+
}
555+
556+
EditorGUILayout.Space(10);
557+
558+
// Additional note
559+
EditorGUILayout.HelpBox("After configuring, restart Claude Desktop to apply the changes.", MessageType.Info);
560+
561+
EditorGUILayout.EndScrollView();
562+
}
563+
564+
private void Update()
565+
{
566+
// Handle the feedback message timer
567+
if (copyFeedbackTimer > 0)
568+
{
569+
copyFeedbackTimer -= Time.deltaTime;
570+
if (copyFeedbackTimer <= 0)
571+
{
572+
pathCopied = false;
573+
jsonCopied = false;
574+
Repaint();
575+
}
347576
}
348577
}
349578
}

0 commit comments

Comments
 (0)