Skip to content
Open
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
2 changes: 2 additions & 0 deletions MCPForUnity/Editor/Constants/EditorPrefKeys.cs
Original file line number Diff line number Diff line change
Expand Up @@ -71,5 +71,7 @@ internal static class EditorPrefKeys
internal const string LogRecordEnabled = "MCPForUnity.LogRecordEnabled";

internal const string ExecuteCodeCompiler = "MCPForUnity.ExecuteCode.Compiler";

internal const string MacOSTerminalApp = "MCPForUnity.MacOSTerminalApp";
}
}
20 changes: 19 additions & 1 deletion MCPForUnity/Editor/Services/Server/TerminalLauncher.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
using System;
using System.IO;
using MCPForUnity.Editor.Constants;
using MCPForUnity.Editor.Helpers;
using UnityEditor;
using UnityEngine;

namespace MCPForUnity.Editor.Services.Server
Expand All @@ -25,6 +27,21 @@ public string GetProjectRootPath()
}
}

/// <summary>
/// Resolves the macOS terminal application name, falling back to "Terminal"
/// when the configured value is unset, empty, or whitespace-only.
/// </summary>
internal static string ResolveMacTerminalApp(string configuredApp)
=> string.IsNullOrWhiteSpace(configuredApp) ? "Terminal" : configuredApp.Trim();

/// <summary>
/// Builds the argument string for <c>/usr/bin/open</c> to launch the given
/// script in the configured macOS terminal application. The app name is quoted
/// so values containing spaces (e.g. "iTerm 2") resolve correctly.
/// </summary>
internal static string BuildMacOpenArguments(string configuredApp, string scriptPath)
=> $"-a \"{ResolveMacTerminalApp(configuredApp)}\" \"{scriptPath}\"";

/// <inheritdoc/>
public System.Diagnostics.ProcessStartInfo CreateTerminalProcessStartInfo(string command)
{
Expand All @@ -45,10 +62,11 @@ public System.Diagnostics.ProcessStartInfo CreateTerminalProcessStartInfo(string
"clear\n" +
$"{command}\n");
ExecPath.TryRun("/bin/chmod", $"+x \"{scriptPath}\"", Application.dataPath, out _, out _, 3000);
string configuredApp = EditorPrefs.GetString(EditorPrefKeys.MacOSTerminalApp, "Terminal");
return new System.Diagnostics.ProcessStartInfo
{
FileName = "/usr/bin/open",
Arguments = $"-a Terminal \"{scriptPath}\"",
Arguments = BuildMacOpenArguments(configuredApp, scriptPath),
UseShellExecute = false,
CreateNoWindow = true
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ public class EditorPrefsWindow : EditorWindow
{ EditorPrefKeys.LastLocalHttpServerPidArgsHash, EditorPrefType.String },
{ EditorPrefKeys.LastLocalHttpServerPidFilePath, EditorPrefType.String },
{ EditorPrefKeys.LastLocalHttpServerInstanceToken, EditorPrefType.String },
{ EditorPrefKeys.MacOSTerminalApp, EditorPrefType.String },
};

// Templates
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,65 @@ public void TerminalLauncher_CanBeUsedViaInterface()

#endregion

#region macOS Terminal App Resolution Tests

[Test]
public void ResolveMacTerminalApp_NullOrEmpty_FallsBackToTerminal()
{
Assert.AreEqual("Terminal", TerminalLauncher.ResolveMacTerminalApp(null));
Assert.AreEqual("Terminal", TerminalLauncher.ResolveMacTerminalApp(string.Empty));
}

[Test]
public void ResolveMacTerminalApp_WhitespaceOnly_FallsBackToTerminal()
{
Assert.AreEqual("Terminal", TerminalLauncher.ResolveMacTerminalApp(" "));
Assert.AreEqual("Terminal", TerminalLauncher.ResolveMacTerminalApp("\t\n"));
}

[Test]
public void ResolveMacTerminalApp_CustomValue_IsUsed()
{
Assert.AreEqual("iTerm", TerminalLauncher.ResolveMacTerminalApp("iTerm"));
Assert.AreEqual("Warp", TerminalLauncher.ResolveMacTerminalApp("Warp"));
}

[Test]
public void ResolveMacTerminalApp_TrimsSurroundingWhitespace()
{
Assert.AreEqual("iTerm", TerminalLauncher.ResolveMacTerminalApp(" iTerm "));
}

[Test]
public void BuildMacOpenArguments_DefaultsToTerminal_WhenUnset()
{
string args = TerminalLauncher.BuildMacOpenArguments("", "/tmp/mcp-terminal.command");
Assert.AreEqual("-a \"Terminal\" \"/tmp/mcp-terminal.command\"", args);
}

[Test]
public void BuildMacOpenArguments_QuotesCustomApp()
{
string args = TerminalLauncher.BuildMacOpenArguments("iTerm", "/tmp/mcp-terminal.command");
Assert.AreEqual("-a \"iTerm\" \"/tmp/mcp-terminal.command\"", args);
}

[Test]
public void BuildMacOpenArguments_QuotingHandlesAppNameWithSpaces()
{
string args = TerminalLauncher.BuildMacOpenArguments("iTerm 2", "/tmp/mcp-terminal.command");
Assert.AreEqual("-a \"iTerm 2\" \"/tmp/mcp-terminal.command\"", args);
}

[Test]
public void BuildMacOpenArguments_QuotingHandlesScriptPathWithSpaces()
{
string args = TerminalLauncher.BuildMacOpenArguments("Terminal", "/Users/me/My Project/mcp-terminal.command");
Assert.AreEqual("-a \"Terminal\" \"/Users/me/My Project/mcp-terminal.command\"", args);
}

#endregion

#region Platform-Specific Behavior Tests

[Test]
Expand Down