Skip to content

Add timeout settings for the WebSocket and the test execution. #22

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Apr 28, 2025
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
4 changes: 1 addition & 3 deletions Editor/Services/ITestRunnerService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,10 @@ public interface ITestRunnerService
/// <param name="testMode">Test mode to run</param>
/// <param name="testFilter">Optional test filter</param>
/// <param name="completionSource">TaskCompletionSource to resolve when tests are complete</param>
/// <param name="timeoutMinutes">Timeout in minutes, defaults to 10</param>
/// <returns>Task that resolves with test results when tests are complete</returns>
void ExecuteTests(
TestMode testMode,
string testFilter,
TaskCompletionSource<JObject> completionSource,
int timeoutMinutes = 1);
TaskCompletionSource<JObject> completionSource);
}
}
11 changes: 6 additions & 5 deletions Editor/Services/TestRunnerService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -49,13 +49,11 @@ public List<TestItemInfo> GetAllTests()
/// <param name="testMode">Test mode to run</param>
/// <param name="testFilter">Optional test filter</param>
/// <param name="completionSource">TaskCompletionSource to resolve when tests are complete</param>
/// <param name="timeoutMinutes">Timeout in minutes, defaults to 10</param>
/// <returns>Task that resolves with test results when tests are complete</returns>
public async void ExecuteTests(
TestMode testMode,
string testFilter,
TaskCompletionSource<JObject> completionSource,
int timeoutMinutes = 1)
TaskCompletionSource<JObject> completionSource)
{
// Create filter
var filter = new Filter
Expand All @@ -72,15 +70,18 @@ public async void ExecuteTests(
// Execute tests
_testRunnerApi.Execute(new ExecutionSettings(filter));

// Use timeout from settings if not specified
var timeoutSeconds = McpUnitySettings.Instance.RequestTimeoutSeconds;

Task completedTask = await Task.WhenAny(
completionSource.Task,
Task.Delay(TimeSpan.FromMinutes(timeoutMinutes))
Task.Delay(TimeSpan.FromSeconds(timeoutSeconds))
);

if (completedTask != completionSource.Task)
{
completionSource.SetResult(McpUnitySocketHandler.CreateErrorResponse(
$"Test run timed out after {timeoutMinutes} minutes",
$"Test run timed out after {timeoutSeconds} seconds",
"test_runner_timeout"
));
}
Expand Down
18 changes: 18 additions & 0 deletions Editor/UnityBridge/McpUnityEditorWindow.cs
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,24 @@ private void DrawServerTab()

EditorGUILayout.Space();

// Test timeout setting
EditorGUILayout.BeginHorizontal();
int newTimeout = EditorGUILayout.IntField(new GUIContent("Request Timeout (seconds)", "Timeout in seconds for tool request"), settings.RequestTimeoutSeconds);
if (newTimeout < McpUnitySettings.RequestTimeoutMinimum)
{
newTimeout = McpUnitySettings.RequestTimeoutMinimum;
Debug.LogError($"Request timeout must be at least {McpUnitySettings.RequestTimeoutMinimum} seconds.");
}

if (newTimeout != settings.RequestTimeoutSeconds)
{
settings.RequestTimeoutSeconds = newTimeout;
settings.SaveSettings();
}
EditorGUILayout.EndHorizontal();

EditorGUILayout.Space();

// Auto start server toggle
bool autoStartServer = EditorGUILayout.Toggle(new GUIContent("Auto Start Server", "Automatically starts the MCP server when Unity opens"), settings.AutoStartServer);
if (autoStartServer != settings.AutoStartServer)
Expand Down
33 changes: 30 additions & 3 deletions Editor/UnityBridge/McpUnitySettings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,28 @@ public class McpUnitySettings
// Constants
public const string ServerVersion = "1.0.0";
public const string PackageName = "com.gamelovers.mcp-unity";
public const int RequestTimeoutMinimum = 10;

#if UNITY_EDITOR_WIN
private const string EnvUnityPort = "UNITY_PORT";
private const string EnvUnityRequestTimeout = "UNITY_REQUEST_TIMEOUT";
#endif

private static McpUnitySettings _instance;
private static readonly string SettingsPath = "ProjectSettings/McpUnitySettings.json";

// Server settings
#if !UNITY_EDITOR_WIN
[field: SerializeField] // Note: On Windows, this property is persisted in per-user environment variables.
#endif
public int Port { get; set; } = 8090;

#if !UNITY_EDITOR_WIN
[field: SerializeField] // Note: On Windows, this property is persisted in per-user environment variables.
#endif
[Tooltip("Timeout in seconds for tool request")]
public int RequestTimeoutSeconds { get; set; } = RequestTimeoutMinimum;

[Tooltip("Whether to automatically start the MCP server when Unity opens")]
public bool AutoStartServer = true;

Expand Down Expand Up @@ -66,12 +81,19 @@ public void LoadSettings()
JsonUtility.FromJsonOverwrite(json, this);
}

#if UNITY_EDITOR_WIN
// Check for environment variable PORT
string envPort = System.Environment.GetEnvironmentVariable("UNITY_PORT");
string envPort = System.Environment.GetEnvironmentVariable(EnvUnityPort);
if (!string.IsNullOrEmpty(envPort) && int.TryParse(envPort, out int port))
{
Port = port;
}
string envTimeout = System.Environment.GetEnvironmentVariable(EnvUnityRequestTimeout);
if (!string.IsNullOrEmpty(envTimeout) && int.TryParse(envTimeout, out int timeout))
{
RequestTimeoutSeconds = timeout;
}
#endif
}
catch (Exception ex)
{
Expand All @@ -90,10 +112,15 @@ public void SaveSettings()
// Save settings to McpUnitySettings.json
string json = JsonUtility.ToJson(this, true);
File.WriteAllText(SettingsPath, json);


#if UNITY_EDITOR_WIN
// Set environment variable PORT for the Node.js process
// Note: This will only affect processes started after this point
System.Environment.SetEnvironmentVariable("UNITY_PORT", Port.ToString(), System.EnvironmentVariableTarget.User);
// Note: EnvironmentVariableTarget.User should be used on .NET implementations running on Windows systems only.
// see: https://learn.microsoft.com/ja-jp/dotnet/api/system.environmentvariabletarget?view=net-8.0#fields
Environment.SetEnvironmentVariable(EnvUnityPort, Port.ToString(), EnvironmentVariableTarget.User);
Environment.SetEnvironmentVariable(EnvUnityRequestTimeout, RequestTimeoutSeconds.ToString(), EnvironmentVariableTarget.User);
#endif
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

doesn't this code run on Mac machines?

Asking as I don't have one at the moment to test

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On a macOS, it behaves the same as Process.
So, I use SerializeField to persist data when not on Windows.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can confirm it works properly on windows.
This needs to have a new solution for Mac, but that should be done in a separate PR as it was a problem present before

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks!

}
catch (Exception ex)
{
Expand Down
47 changes: 47 additions & 0 deletions README-ja.md
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,53 @@ MCP Unityサーバーを起動するには2つの方法があります:
node Server/build/index.js
```

## オプション: タイムアウト設定

デフォルトでは、MCPサーバーとWebSocket間のタイムアウトは 10 秒です。
お使いのOSに応じて変更できます。

<details>
<summary><span style="font-size: 1.1em; font-weight: bold;">Option 1: Windows OS</span></summary>

1. Unityエディターを開きます
2. **Tools > MCP Unity > Server Window** に移動します
3. **Request Timeout (seconds)** の値を希望のタイムアウト秒数に変更します
4. Unityはシステム環境変数UNITY_REQUEST_TIMEOUTに新しいタイムアウト値を設定します
5. Node.jsサーバーを再起動します
6. **Start Server** をもう一度クリックして、UnityエディターのWebソケットをNode.js MCPサーバーに再接続します

</details>

<details>
<summary><span style="font-size: 1.1em; font-weight: bold;">Option 2: Windows以外のOS</span></summary>

Windows 以外の OS の場合は、次の 2 か所で設定する必要があります。

### エディター内プロセスのタイムアウト

1. Unityエディターを開きます
2. **Tools > MCP Unity > Server Window** に移動します
3. **Request Timeout (seconds)** の値を希望のタイムアウト秒数に変更します

### WebSocketのタイムアウト

1. ターミナルで UNITY_REQUEST_TIMEOUT 環境変数を設定します
- Powershell
```powershell
$env:UNITY_REQUEST_TIMEOUT = "300"
```
- Command Prompt/Terminal
```cmd
set UNITY_REQUEST_TIMEOUT=300
```
2. Node.jsサーバーを再起動します
3. **Start Server** をもう一度クリックして、UnityエディターのWebソケットをNode.js MCPサーバーに再接続します

</details>

> [!TIP]
> AIコーディングIDE(Claude Desktop、Cursor IDE、Windsurf IDE など)とMCPサーバー間のタイムアウト設定は、IDEによって異なります。

## <a name="debug-server"></a>サーバーのデバッグ

MCP Unityサーバーをデバッグするには、以下の方法を使用できます:
Expand Down
47 changes: 47 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,53 @@ By default, the WebSocket server runs on port 8090. You can change this port in

</details>

## Optional: Set Timeout

By default, the timeout between the MCP server and the WebSocket is 10 seconds.
You can change depending on the OS you are using:

<details>
<summary><span style="font-size: 1.1em; font-weight: bold;">Option 1: Windows OS</span></summary>

1. Open the Unity Editor
2. Navigate to Tools > MCP Unity > Server Window
3. Change the "Request Timeout (seconds)" value to your desired timeout seconds
4. Unity will setup the system environment variable UNITY_REQUEST_TIMEOUT to the new timeout value
5. Restart the Node.js server
6. Click again on "Start Server" to reconnect the Unity Editor web socket to the Node.js MCP Server

</details>

<details>
<summary><span style="font-size: 1.1em; font-weight: bold;">Option 2: Non-Windows OS</span></summary>

For non-Windows OS, you need to configure two places:

### In Editor Process Timeout

1. Open the Unity Editor
2. Navigate to Tools > MCP Unity > Server Window
3. Change the "Request Timeout (seconds)" value to your desired timeout seconds

### WebSocket Timeout

1. Set the UNITY_REQUEST_TIMEOUT environment variable in the terminal
- Powershell
```powershell
$env:UNITY_REQUEST_TIMEOUT = "300"
```
- Command Prompt/Terminal
```cmd
set UNITY_REQUEST_TIMEOUT=300
```
2. Restart the Node.js server
3. Click again on "Start Server" to reconnect the Unity Editor web socket to the Node.js MCP Server

</details>

> [!TIP]
> The timeout between your AI Coding IDE (e.g., Claude Desktop, Cursor IDE, Windsurf IDE) and the MCP Server depends on the IDE.

## <a name="debug-server"></a>Debugging the Server

<details>
Expand Down
47 changes: 47 additions & 0 deletions README_zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,53 @@ MCP Unity 通过将 Unity `Library/PackedCache` 文件夹添加到您的工作
node Server/build/index.js
```

## 可选:设置超时

默认情况下,MCP 服务器与 WebSocket 之间的超时时间为 10 秒。
您可以根据所使用的操作系统进行更改:

<details>
<summary><span style="font-size: 1.1em; font-weight: bold;">Option 1: Windows OS</span></summary>

1. 打开 Unity 编辑器
2. 导航至 Tools > MCP Unity > Server Window
3. 将 "Request Timeout (seconds)" 值更改为所需的超时秒数
4. Unity 会将系统环境变量 UNITY_REQUEST_TIMEOUT 设置为新的超时值
5. 重启 Node.js 服务器
6. 再次点击“启动服务器”,将 Unity 编辑器 Web 套接字重新连接到 Node.js MCP 服务器

</details>

<details>
<summary><span style="font-size: 1.1em; font-weight: bold;">Option 2: 非Windows操作系统</span></summary>

对于非Windows操作系统,需要配置两个地方:

### 编辑器进程超时

1. 打开 Unity 编辑器
2. 导航至 Tools > MCP Unity > Server Window
3. 将 "Request Timeout (seconds)" 值更改为所需的超时秒数

### WebSocket 超时

1. 在终端中设置 UNITY_REQUEST_TIMEOUT 环境变量
- Powershell
```powershell
$env:UNITY_REQUEST_TIMEOUT = "300"
```
- Command Prompt/Terminal
```cmd
set UNITY_REQUEST_TIMEOUT=300
```
2. 重启 Node.js 服务器
3. 再次点击“启动服务器”,将 Unity 编辑器 Web 套接字重新连接到 Node.js MCP 服务器

Comment on lines +216 to +257
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

统一通过编辑器界面配置超时
为简化用户体验,可考虑移除非 Windows 环境下对 UNITY_REQUEST_TIMEOUT 的手动设置,仅在 “Request Timeout (seconds)” 字段中统一管理所有请求(包括测试执行与 WebSocket)。这样可避免跨平台环境变量差异,且无需重启或手工维护。

🧰 Tools
🪛 LanguageTool

[uncategorized] ~227-~227: 动词的修饰一般为‘形容词(副词)+地+动词’。您的意思是否是:新"地"超时
Context: ...nity 会将系统环境变量 UNITY_REQUEST_TIMEOUT 设置为新的超时值 5. 重启 Node.js 服务器 6. 再次点击“启动服务器”,将 U...

(wb4)

</details>

> [!TIP]
> 您的 AI 编码 IDE(例如,Claude Desktop、Cursor IDE、Windsurf IDE)和 MCP 服务器之间的超时取决于 IDE。

## <a name="debug-server"></a>调试服务器

要调试 MCP Unity 服务器,您可以使用以下方法:
Expand Down
6 changes: 5 additions & 1 deletion Server/build/unity/mcpUnity.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ export class McpUnity {
port;
ws = null;
pendingRequests = new Map();
REQUEST_TIMEOUT = 10000;
REQUEST_TIMEOUT;
retryDelay = 1000;
constructor(logger) {
this.logger = logger;
Expand All @@ -19,6 +19,10 @@ export class McpUnity {
const envPort = process.env.UNITY_PORT || envRegistry;
this.port = envPort ? parseInt(envPort, 10) : 8090;
this.logger.info(`Using port: ${this.port} for Unity WebSocket connection`);
// Initialize timeout from environment variable (in seconds; it is the same as Cline) or use default (10 seconds)
const envTimeout = process.env.UNITY_REQUEST_TIMEOUT;
this.REQUEST_TIMEOUT = envTimeout ? parseInt(envTimeout, 10) * 1000 : 10000;
this.logger.info(`Using request timeout: ${this.REQUEST_TIMEOUT / 1000} seconds`);
}
/**
* Start the Unity connection
Expand Down
8 changes: 6 additions & 2 deletions Server/src/unity/mcpUnity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ export class McpUnity {
private port: number;
private ws: WebSocket | null = null;
private pendingRequests: Map<string, PendingRequest> = new Map<string, PendingRequest>();
private readonly REQUEST_TIMEOUT = 10000;
private readonly REQUEST_TIMEOUT: number;
private retryDelay = 1000;

constructor(logger: Logger) {
Expand All @@ -46,8 +46,12 @@ export class McpUnity {

const envPort = process.env.UNITY_PORT || envRegistry;
this.port = envPort ? parseInt(envPort, 10) : 8090;

this.logger.info(`Using port: ${this.port} for Unity WebSocket connection`);

// Initialize timeout from environment variable (in seconds; it is the same as Cline) or use default (10 seconds)
const envTimeout = process.env.UNITY_REQUEST_TIMEOUT;
this.REQUEST_TIMEOUT = envTimeout ? parseInt(envTimeout, 10) * 1000 : 10000;
this.logger.info(`Using request timeout: ${this.REQUEST_TIMEOUT / 1000} seconds`);
}

/**
Expand Down