From 79153f4d83a52978494b489b3ce3f7ca5880ffbc Mon Sep 17 00:00:00 2001
From: CoderGamester <game.gamester@gmail.com>
Date: Sat, 26 Apr 2025 03:05:37 +0300
Subject: [PATCH 01/17] docs(readme): synchronize troubleshooting and support
 sections across all languages EN, CN & JA change: renamed notify_message tool
 to send_console_log

---
 ...fyMessageTool.cs => SendConsoleLogTool.cs} |  6 +-
 ...ool.cs.meta => SendConsoleLogTool.cs.meta} |  2 +-
 Editor/UnityBridge/McpUnityServer.cs          |  6 +-
 README-ja.md                                  | 63 ++++++++++++++-----
 README.md                                     | 15 +----
 README_zh-CN.md                               | 59 ++++++++++++-----
 Server/build/index.js                         |  4 +-
 Server/build/tools/sendConsoleLogTool.d.ts    | 12 ++++
 .../tools/sendConsoleLogTool.d.ts.meta}       |  2 +-
 Server/build/tools/sendConsoleLogTool.js      | 61 ++++++++++++++++++
 Server/build/tools/sendConsoleLogTool.js.meta |  7 +++
 Server/src/index.ts                           |  4 +-
 ...fyMessageTool.ts => sendConsoleLogTool.ts} |  8 +--
 Server/src/tools/sendConsoleLogTool.ts.meta   |  7 +++
 14 files changed, 198 insertions(+), 58 deletions(-)
 rename Editor/Tools/{NotifyMessageTool.cs => SendConsoleLogTool.cs} (93%)
 rename Editor/Tools/{NotifyMessageTool.cs.meta => SendConsoleLogTool.cs.meta} (83%)
 create mode 100644 Server/build/tools/sendConsoleLogTool.d.ts
 rename Server/{src/tools/notifyMessageTool.ts.meta => build/tools/sendConsoleLogTool.d.ts.meta} (74%)
 create mode 100644 Server/build/tools/sendConsoleLogTool.js
 create mode 100644 Server/build/tools/sendConsoleLogTool.js.meta
 rename Server/src/tools/{notifyMessageTool.ts => sendConsoleLogTool.ts} (88%)
 create mode 100644 Server/src/tools/sendConsoleLogTool.ts.meta

diff --git a/Editor/Tools/NotifyMessageTool.cs b/Editor/Tools/SendConsoleLogTool.cs
similarity index 93%
rename from Editor/Tools/NotifyMessageTool.cs
rename to Editor/Tools/SendConsoleLogTool.cs
index cd9b93e8..25757fc0 100644
--- a/Editor/Tools/NotifyMessageTool.cs
+++ b/Editor/Tools/SendConsoleLogTool.cs
@@ -8,11 +8,11 @@ namespace McpUnity.Tools
     /// <summary>
     /// Tool for sending notification messages to the Unity console
     /// </summary>
-    public class NotifyMessageTool : McpToolBase
+    public class SendConsoleLogTool : McpToolBase
     {
-        public NotifyMessageTool()
+        public SendConsoleLogTool()
         {
-            Name = "notify_message";
+            Name = "send_console_log";
             Description = "Sends a message to the Unity console";
         }
         
diff --git a/Editor/Tools/NotifyMessageTool.cs.meta b/Editor/Tools/SendConsoleLogTool.cs.meta
similarity index 83%
rename from Editor/Tools/NotifyMessageTool.cs.meta
rename to Editor/Tools/SendConsoleLogTool.cs.meta
index bfeecfaf..59b80e28 100644
--- a/Editor/Tools/NotifyMessageTool.cs.meta
+++ b/Editor/Tools/SendConsoleLogTool.cs.meta
@@ -1,5 +1,5 @@
 fileFormatVersion: 2
-guid: a1aaf613f7e9c0341a389b261a93c72e
+guid: 35bb1bb57a358064c9ca8f2c8ba3caf2
 MonoImporter:
   externalObjects: {}
   serializedVersion: 2
diff --git a/Editor/UnityBridge/McpUnityServer.cs b/Editor/UnityBridge/McpUnityServer.cs
index 06c4c7cb..72a4db69 100644
--- a/Editor/UnityBridge/McpUnityServer.cs
+++ b/Editor/UnityBridge/McpUnityServer.cs
@@ -163,9 +163,9 @@ private void RegisterTools()
             RunTestsTool runTestsTool = new RunTestsTool(_testRunnerService);
             _tools.Add(runTestsTool.Name, runTestsTool);
             
-            // Register NotifyMessageTool
-            NotifyMessageTool notifyMessageTool = new NotifyMessageTool();
-            _tools.Add(notifyMessageTool.Name, notifyMessageTool);
+            // Register SendConsoleLogTool
+            SendConsoleLogTool sendConsoleLogTool = new SendConsoleLogTool();
+            _tools.Add(sendConsoleLogTool.Name, sendConsoleLogTool);
             
             // Register UpdateComponentTool
             UpdateComponentTool updateComponentTool = new UpdateComponentTool();
diff --git a/README-ja.md b/README-ja.md
index 826bea2c..93c0fcb6 100644
--- a/README-ja.md
+++ b/README-ja.md
@@ -76,8 +76,8 @@ MCP Unityは、Unityの`Library/PackedCache`フォルダーをワークスペー
 - `run_tests`: Unityテストランナーを使用してテストを実行
   > **例:** "プロジェクト内のすべてのEditModeテストを実行"
 
-- `notify_message`: Unityエディターにメッセージを表示
-  > **例:** "タスクが完了したことをUnityに通知"
+- `send_console_log`: Unityにコンソールログを送信
+  > **例:** "Unity Editorにコンソールログを送信"
 
 - `add_asset_to_scene`: AssetDatabaseからアセットをUnityシーンに追加
   > **例:** "プロジェクトからPlayerプレハブを現在のシーンに追加"
@@ -230,30 +230,61 @@ MCP Unityサーバーをデバッグするには、以下の方法を使用で
 
 ## トラブルシューティング
 
-### <a name="common-issues"></a>よくある問題
-
-#### サーバーが起動しない
+<details>
+<summary><span style="font-size: 1.1em; font-weight: bold;">接続の問題</span></summary>
 
-- Node.js 18以降がインストールされていることを確認
-- npm 9以降がインストールされていることを確認
-- MCP Unityサーバーディレクトリが正しいことを確認
+- WebSocketサーバーが実行中であることを確認してください（UnityのServer Windowを確認）
+- ファイアウォールの制限が接続を妨げていないか確認してください
+- ポート番号が正しいことを確認してください（デフォルトは8080）
+- UnityエディターのMCP Serverウィンドウでポート番号を変更できます（ツール > MCP Unity > Server Window）
+</details>
 
-#### メニュー項目が実行されない
+<details>
+<summary><span style="font-size: 1.1em; font-weight: bold;">サーバーが起動しない</span></summary>
 
-- メニュー項目のパスが正しいことを確認（大文字小文字を区別）
-- メニュー項目が確認を必要とするかどうかを確認
-- メニュー項目が現在のコンテキストで利用可能であることを確認
+- Unityコンソールにエラーメッセージがないか確認してください
+- Node.jsが正しくインストールされ、PATHで利用可能であることを確認してください
+- Serverディレクトリ内の依存関係がすべてインストールされていることを確認してください
+</details>
 
-#### Play Modeテスト実行時に `Connection failed` エラー
+<details>
+<summary><span style="font-size: 1.1em; font-weight: bold;">Play Modeテスト実行時の接続失敗</span></summary>
 
-`run_tests` ツールの実行が次のエラーを返します：
+`run_tests` ツールは以下の応答を返します：
 ```
 Error:
 Connection failed: Unknown error
 ```
 
-このエラーは、Play Modeに切り替わる際にドメインがリロードされるため、ブリッジ接続が失われることで発生します。  
-回避方法は、**Edit > Project Settings > Editor > "Enter Play Mode Settings"** で **Reload Domain** をオフにすることです。
+このエラーは、Play Modeへ切り替える際にドメインリロードが発生し、ブリッジ接続が失われるために発生します。  
+回避策は、**Edit > Project Settings > Editor > "Enter Play Mode Settings"** で **Reload Domain** をオフにすることです。
+</details>
+
+## サポート・フィードバック
+
+ご質問やサポートが必要な場合は、このリポジトリの[issue](https://github.com/CoderGamester/mcp-unity/issues)をご利用ください。
+
+また、以下でも連絡可能です：
+- Linkedin: [![](https://img.shields.io/badge/LinkedIn-0077B5?style=flat&logo=linkedin&logoColor=white 'LinkedIn')](https://www.linkedin.com/in/miguel-tomas/)
+- Discord: gamester7178
+- メール: game.gamester@gmail.com
+
+## コントリビューション
+
+コントリビューションは大歓迎です！Pull Requestの送信やIssueの提出をお待ちしています。
+
+**変更は [Conventional Commits](https://www.conventionalcommits.org/ja/v1.0.0/) フォーマットに従ってください。**
+
+## ライセンス
+
+本プロジェクトは [MIT License](License.md) の下で提供されています。
+
+## 謝辞
+
+- [Model Context Protocol](https://modelcontextprotocol.io)
+- [Unity Technologies](https://unity.com)
+- [Node.js](https://nodejs.org)
+- [WebSocket-Sharp](https://github.com/sta/websocket-sharp)
 
 ## 貢献
 
diff --git a/README.md b/README.md
index 719f0fec..c15a39bf 100644
--- a/README.md
+++ b/README.md
@@ -76,8 +76,8 @@ MCP Unity provides automatic integration with VSCode-like IDEs (Visual Studio Co
 - `run_tests`: Runs tests using the Unity Test Runner
   > **Example prompt:** "Run all the EditMode tests in my project"
 
-- `notify_message`: Displays messages in the Unity Editor
-  > **Example prompt:** "Send a notification to Unity that the task has been completed"
+- `send_console_log`: Send a console log to Unity
+  > **Example prompt:** "Send a console log to Unity Editor"
 
 - `add_asset_to_scene`: Adds an asset from the AssetDatabase to the Unity scene
   > **Example prompt:** "Add the Player prefab from my project to the current scene"
@@ -306,8 +306,7 @@ Don't forget to shutdown the server with `Ctrl + C` before closing the terminal
 <summary><span style="font-size: 1.1em; font-weight: bold;">Connection Issues</span></summary>
 
 - Ensure the WebSocket server is running (check the Server Window in Unity)
-- Check if there are any firewall restrictions blocking the connection
-- Make sure the port number is correct (default is 8080)
+- Send a console log message from MCP client to force a reconnection between MCP client and Unity server
 - Change the port number in the Unity Editor MCP Server window. (Tools > MCP Unity > Server Window)
 </details>
 
@@ -319,14 +318,6 @@ Don't forget to shutdown the server with `Ctrl + C` before closing the terminal
 - Verify that all dependencies are installed in the Server directory
 </details>
 
-<details>
-<summary><span style="font-size: 1.1em; font-weight: bold;">Menu Items Not Executing</span></summary>
-
-- Ensure the menu item path is correct (case-sensitive)
-- Check if the menu item requires confirmation
-- Verify that the menu item is available in the current context
-</details>
-
 <details>
 <summary><span style="font-size: 1.1em; font-weight: bold;">Connection failed when running Play Mode tests</span></summary>
 
diff --git a/README_zh-CN.md b/README_zh-CN.md
index a277a1fd..be21ec0a 100644
--- a/README_zh-CN.md
+++ b/README_zh-CN.md
@@ -78,8 +78,8 @@ MCP Unity 通过将 Unity `Library/PackedCache` 文件夹添加到您的工作
 - `run_tests`: 使用 Unity 测试运行器运行测试
   > **示例提示:** "运行我项目中所有的 EditMode 测试"
 
-- `notify_message`: 在 Unity 编辑器中显示消息
-  > **示例提示:** "发送通知到 Unity 告知任务已完成"
+- `send_console_log`: 发送控制台日志到 Unity
+  > **示例提示:** "发送控制台日志到 Unity 编辑器"
 
 - `add_asset_to_scene`: 将 AssetDatabase 中的资源添加到 Unity 场景中
   > **示例提示:** "将我的项目中的 Player 预制体添加到当前场景"
@@ -232,21 +232,25 @@ MCP Unity 通过将 Unity `Library/PackedCache` 文件夹添加到您的工作
 
 ## 故障排除
 
-### <a name="common-issues"></a>常见问题
-
-#### 服务器无法启动
+<details>
+<summary><span style="font-size: 1.1em; font-weight: bold;">连接问题</span></summary>
 
-- 确保 Node.js 18 或更高版本已安装
-- 验证 npm 9 或更高版本已安装
-- 检查 MCP Unity 服务器目录是否正确
+- 确保 WebSocket 服务器正在运行（检查 Unity 的 Server Window）
+- 检查是否有防火墙限制阻止连接
+- 确认端口号正确（默认是 8080）
+- 可在 Unity 编辑器 MCP Server 窗口更改端口号。(工具 > MCP Unity > Server Window)
+</details>
 
-#### 菜单项无法执行
+<details>
+<summary><span style="font-size: 1.1em; font-weight: bold;">服务器无法启动</span></summary>
 
-- 确保菜单项路径正确（区分大小写）
-- 检查菜单项是否需要确认
-- 验证菜单项在当前上下文中是否可用
+- 检查 Unity 控制台是否有错误消息
+- 确保 Node.js 已正确安装并可在 PATH 中访问
+- 验证 Server 目录下所有依赖均已安装
+</details>
 
-#### 运行播放模式测试时连接失败
+<details>
+<summary><span style="font-size: 1.1em; font-weight: bold;">运行播放模式测试时连接失败</span></summary>
 
 `run_tests` 工具返回以下响应：
 ```
@@ -254,8 +258,35 @@ Error:
 Connection failed: Unknown error
 ```
 
-发生此错误的原因是在切换到播放模式触发域重新加载时，桥接连接会丢失。  
+发生此错误的原因是在切换到播放模式时域重新加载，导致桥接连接丢失。  
 解决方法是在 **Edit > Project Settings > Editor > "Enter Play Mode Settings"** 中关闭 **Reload Domain**。
+</details>
+
+## 支持与反馈
+
+如有任何问题或需要支持，请在本仓库[提交 issue](https://github.com/CoderGamester/mcp-unity/issues)。
+
+你也可以通过以下方式联系：
+- Linkedin: [![](https://img.shields.io/badge/LinkedIn-0077B5?style=flat&logo=linkedin&logoColor=white 'LinkedIn')](https://www.linkedin.com/in/miguel-tomas/)
+- Discord: gamester7178
+- 邮箱: game.gamester@gmail.com
+
+## 贡献
+
+欢迎贡献！请随时提交 Pull Request 或提出 Issue。
+
+**请遵循 [Conventional Commits](https://www.conventionalcommits.org/zh-hans/v1.0.0/) 格式提交更改。**
+
+## 许可证
+
+本项目采用 [MIT License](License.md) 授权。
+
+## 鸣谢
+
+- [Model Context Protocol](https://modelcontextprotocol.io)
+- [Unity Technologies](https://unity.com)
+- [Node.js](https://nodejs.org)
+- [WebSocket-Sharp](https://github.com/sta/websocket-sharp)
 
 ## 贡献
 
diff --git a/Server/build/index.js b/Server/build/index.js
index e0faf2fa..44c35174 100644
--- a/Server/build/index.js
+++ b/Server/build/index.js
@@ -7,7 +7,7 @@ import { createMenuItemTool } from './tools/menuItemTool.js';
 import { createSelectGameObjectTool } from './tools/selectGameObjectTool.js';
 import { createAddPackageTool } from './tools/addPackageTool.js';
 import { createRunTestsTool } from './tools/runTestsTool.js';
-import { createNotifyMessageTool } from './tools/notifyMessageTool.js';
+import { createSendConsoleLogTool } from './tools/sendConsoleLogTool.js';
 import { createUpdateComponentTool } from './tools/updateComponentTool.js';
 import { createAddAssetToSceneTool } from './tools/addAssetToSceneTool.js';
 import { createGetMenuItemsResource } from './resources/getMenuItemResource.js';
@@ -39,7 +39,7 @@ createMenuItemTool(server, mcpUnity, toolLogger);
 createSelectGameObjectTool(server, mcpUnity, toolLogger);
 createAddPackageTool(server, mcpUnity, toolLogger);
 createRunTestsTool(server, mcpUnity, toolLogger);
-createNotifyMessageTool(server, mcpUnity, toolLogger);
+createSendConsoleLogTool(server, mcpUnity, toolLogger);
 createUpdateComponentTool(server, mcpUnity, toolLogger);
 createAddAssetToSceneTool(server, mcpUnity, toolLogger);
 // Create and register all resources with the MCP server
diff --git a/Server/build/tools/sendConsoleLogTool.d.ts b/Server/build/tools/sendConsoleLogTool.d.ts
new file mode 100644
index 00000000..8eaba712
--- /dev/null
+++ b/Server/build/tools/sendConsoleLogTool.d.ts
@@ -0,0 +1,12 @@
+import { Logger } from '../utils/logger.js';
+import { McpUnity } from '../unity/mcpUnity.js';
+import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
+/**
+ * Creates and registers the Send Console Log tool with the MCP server
+ * This tool allows sending messages to the Unity console
+ *
+ * @param server The MCP server instance to register with
+ * @param mcpUnity The McpUnity instance to communicate with Unity
+ * @param logger The logger instance for diagnostic information
+ */
+export declare function createSendConsoleLogTool(server: McpServer, mcpUnity: McpUnity, logger: Logger): void;
diff --git a/Server/src/tools/notifyMessageTool.ts.meta b/Server/build/tools/sendConsoleLogTool.d.ts.meta
similarity index 74%
rename from Server/src/tools/notifyMessageTool.ts.meta
rename to Server/build/tools/sendConsoleLogTool.d.ts.meta
index f98967dd..5c8ced54 100644
--- a/Server/src/tools/notifyMessageTool.ts.meta
+++ b/Server/build/tools/sendConsoleLogTool.d.ts.meta
@@ -1,5 +1,5 @@
 fileFormatVersion: 2
-guid: 46b2a32d3d3a076479aadb9728235e9c
+guid: 0963564453d22894c9db3e635922fd1c
 DefaultImporter:
   externalObjects: {}
   userData: 
diff --git a/Server/build/tools/sendConsoleLogTool.js b/Server/build/tools/sendConsoleLogTool.js
new file mode 100644
index 00000000..0b152b1a
--- /dev/null
+++ b/Server/build/tools/sendConsoleLogTool.js
@@ -0,0 +1,61 @@
+import { z } from 'zod';
+import { McpUnityError, ErrorType } from '../utils/errors.js';
+// Constants for the tool
+const toolName = 'send_console_log';
+const toolDescription = 'Sends console log messages to the Unity console';
+const paramsSchema = z.object({
+    message: z.string().describe('The message to display in the Unity console'),
+    type: z.string().optional().describe('The type of message (info, warning, error) - defaults to info (optional)')
+});
+/**
+ * Creates and registers the Send Console Log tool with the MCP server
+ * This tool allows sending messages to the Unity console
+ *
+ * @param server The MCP server instance to register with
+ * @param mcpUnity The McpUnity instance to communicate with Unity
+ * @param logger The logger instance for diagnostic information
+ */
+export function createSendConsoleLogTool(server, mcpUnity, logger) {
+    logger.info(`Registering tool: ${toolName}`);
+    // Register this tool with the MCP server
+    server.tool(toolName, toolDescription, paramsSchema.shape, async (params) => {
+        try {
+            logger.info(`Executing tool: ${toolName}`, params);
+            const result = await toolHandler(mcpUnity, params);
+            logger.info(`Tool execution successful: ${toolName}`);
+            return result;
+        }
+        catch (error) {
+            logger.error(`Tool execution failed: ${toolName}`, error);
+            throw error;
+        }
+    });
+}
+/**
+ * Handles notification message requests
+ *
+ * @param mcpUnity The McpUnity instance to communicate with Unity
+ * @param params The parameters for the tool
+ * @returns A promise that resolves to the tool execution result
+ * @throws McpUnityError if the request to Unity fails
+ */
+async function toolHandler(mcpUnity, params) {
+    const { message, type = 'info' } = params;
+    // Send to Unity
+    const response = await mcpUnity.sendRequest({
+        method: toolName,
+        params: {
+            message,
+            type
+        }
+    });
+    if (!response.success) {
+        throw new McpUnityError(ErrorType.TOOL_EXECUTION, response.message || `Failed to send message to Unity console`);
+    }
+    return {
+        content: [{
+                type: response.type,
+                text: response.message
+            }]
+    };
+}
diff --git a/Server/build/tools/sendConsoleLogTool.js.meta b/Server/build/tools/sendConsoleLogTool.js.meta
new file mode 100644
index 00000000..3f1a35d9
--- /dev/null
+++ b/Server/build/tools/sendConsoleLogTool.js.meta
@@ -0,0 +1,7 @@
+fileFormatVersion: 2
+guid: 212342734ce3429469b615bf02b08f08
+DefaultImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Server/src/index.ts b/Server/src/index.ts
index c8c697a9..6aad3fab 100644
--- a/Server/src/index.ts
+++ b/Server/src/index.ts
@@ -7,7 +7,7 @@ import { createMenuItemTool } from './tools/menuItemTool.js';
 import { createSelectGameObjectTool } from './tools/selectGameObjectTool.js';
 import { createAddPackageTool } from './tools/addPackageTool.js';
 import { createRunTestsTool } from './tools/runTestsTool.js';
-import { createNotifyMessageTool } from './tools/notifyMessageTool.js';
+import { createSendConsoleLogTool } from './tools/sendConsoleLogTool.js';
 import { createUpdateComponentTool } from './tools/updateComponentTool.js';
 import { createAddAssetToSceneTool } from './tools/addAssetToSceneTool.js';
 import { createGetMenuItemsResource } from './resources/getMenuItemResource.js';
@@ -46,7 +46,7 @@ createMenuItemTool(server, mcpUnity, toolLogger);
 createSelectGameObjectTool(server, mcpUnity, toolLogger);
 createAddPackageTool(server, mcpUnity, toolLogger);
 createRunTestsTool(server, mcpUnity, toolLogger);
-createNotifyMessageTool(server, mcpUnity, toolLogger);
+createSendConsoleLogTool(server, mcpUnity, toolLogger);
 createUpdateComponentTool(server, mcpUnity, toolLogger);
 createAddAssetToSceneTool(server, mcpUnity, toolLogger);
 
diff --git a/Server/src/tools/notifyMessageTool.ts b/Server/src/tools/sendConsoleLogTool.ts
similarity index 88%
rename from Server/src/tools/notifyMessageTool.ts
rename to Server/src/tools/sendConsoleLogTool.ts
index b53975fc..492e72f9 100644
--- a/Server/src/tools/notifyMessageTool.ts
+++ b/Server/src/tools/sendConsoleLogTool.ts
@@ -6,22 +6,22 @@ import { McpUnityError, ErrorType } from '../utils/errors.js';
 import { CallToolResult } from '@modelcontextprotocol/sdk/types.js';
 
 // Constants for the tool
-const toolName = 'notify_message';
-const toolDescription = 'Sends a message to the Unity console';
+const toolName = 'send_console_log';
+const toolDescription = 'Sends console log messages to the Unity console';
 const paramsSchema = z.object({
   message: z.string().describe('The message to display in the Unity console'),
   type: z.string().optional().describe('The type of message (info, warning, error) - defaults to info (optional)')
 });
 
 /**
- * Creates and registers the Notify Message tool with the MCP server
+ * Creates and registers the Send Console Log tool with the MCP server
  * This tool allows sending messages to the Unity console
  * 
  * @param server The MCP server instance to register with
  * @param mcpUnity The McpUnity instance to communicate with Unity
  * @param logger The logger instance for diagnostic information
  */
-export function createNotifyMessageTool(server: McpServer, mcpUnity: McpUnity, logger: Logger) {
+export function createSendConsoleLogTool(server: McpServer, mcpUnity: McpUnity, logger: Logger) {
   logger.info(`Registering tool: ${toolName}`);
   
   // Register this tool with the MCP server
diff --git a/Server/src/tools/sendConsoleLogTool.ts.meta b/Server/src/tools/sendConsoleLogTool.ts.meta
new file mode 100644
index 00000000..4a578be9
--- /dev/null
+++ b/Server/src/tools/sendConsoleLogTool.ts.meta
@@ -0,0 +1,7 @@
+fileFormatVersion: 2
+guid: 55291baeb7210f64888ab4e7668b5fbc
+DefaultImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

From de5b69455ad55525d6cb2342085304e3c36ef723 Mon Sep 17 00:00:00 2001
From: Koji Hasegawa <hasegawa@hubsys.co.jp>
Date: Sat, 26 Apr 2025 12:50:02 +0900
Subject: [PATCH 02/17] Add env UNITY_REQUEST_TIMEOUT to allow setting the
 timeout in seconds for requests to the Unity Editor Bridge.

---
 Server/build/unity/mcpUnity.js | 6 +++++-
 Server/src/unity/mcpUnity.ts   | 8 ++++++--
 2 files changed, 11 insertions(+), 3 deletions(-)

diff --git a/Server/build/unity/mcpUnity.js b/Server/build/unity/mcpUnity.js
index 44fd6935..7013a8f8 100644
--- a/Server/build/unity/mcpUnity.js
+++ b/Server/build/unity/mcpUnity.js
@@ -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;
@@ -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
diff --git a/Server/src/unity/mcpUnity.ts b/Server/src/unity/mcpUnity.ts
index 3b552635..83838c00 100644
--- a/Server/src/unity/mcpUnity.ts
+++ b/Server/src/unity/mcpUnity.ts
@@ -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) {
@@ -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`);
   }
   
   /**

From 96e1101c50307daaf31967d606ea5389d70af0dd Mon Sep 17 00:00:00 2001
From: Koji Hasegawa <hasegawa@hubsys.co.jp>
Date: Sat, 26 Apr 2025 13:12:02 +0900
Subject: [PATCH 03/17] Add Test Timeout server settings

---
 Editor/Services/ITestRunnerService.cs      |  4 +---
 Editor/Services/TestRunnerService.cs       | 11 ++++++-----
 Editor/UnityBridge/McpUnityEditorWindow.cs | 18 ++++++++++++++++++
 Editor/UnityBridge/McpUnitySettings.cs     |  4 ++++
 4 files changed, 29 insertions(+), 8 deletions(-)

diff --git a/Editor/Services/ITestRunnerService.cs b/Editor/Services/ITestRunnerService.cs
index 780ba3f3..d23f7cc7 100644
--- a/Editor/Services/ITestRunnerService.cs
+++ b/Editor/Services/ITestRunnerService.cs
@@ -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);
     }
 }
diff --git a/Editor/Services/TestRunnerService.cs b/Editor/Services/TestRunnerService.cs
index 7b7fc87d..bfe188e7 100644
--- a/Editor/Services/TestRunnerService.cs
+++ b/Editor/Services/TestRunnerService.cs
@@ -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
@@ -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.TestTimeoutSeconds;
+            
             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"
                 ));
             }
diff --git a/Editor/UnityBridge/McpUnityEditorWindow.cs b/Editor/UnityBridge/McpUnityEditorWindow.cs
index 86585300..772b57d6 100644
--- a/Editor/UnityBridge/McpUnityEditorWindow.cs
+++ b/Editor/UnityBridge/McpUnityEditorWindow.cs
@@ -107,6 +107,24 @@ private void DrawServerTab()
             
             EditorGUILayout.Space();
             
+            // Test timeout setting
+            EditorGUILayout.BeginHorizontal();
+            int newTimeout = EditorGUILayout.IntField(new GUIContent("Test Timeout (seconds)", "Timeout in seconds for test execution"), settings.TestTimeoutSeconds);
+            if (newTimeout < 60)
+            {
+                newTimeout = 60;
+                Debug.LogError("Test timeout must be at least 60 seconds.");
+            }
+            
+            if (newTimeout != settings.TestTimeoutSeconds)
+            {
+                settings.TestTimeoutSeconds = 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)
diff --git a/Editor/UnityBridge/McpUnitySettings.cs b/Editor/UnityBridge/McpUnitySettings.cs
index 821101ab..d3c340d6 100644
--- a/Editor/UnityBridge/McpUnitySettings.cs
+++ b/Editor/UnityBridge/McpUnitySettings.cs
@@ -3,6 +3,7 @@
 using McpUnity.Utils;
 using UnityEngine;
 using UnityEditor;
+using UnityEngine.Serialization;
 
 namespace McpUnity.Unity
 {
@@ -27,6 +28,9 @@ public class McpUnitySettings
         
         [Tooltip("Whether to show info logs in the Unity console")]
         public bool EnableInfoLogs = true;
+        
+        [Tooltip("Timeout in seconds for test execution")]
+        public int TestTimeoutSeconds = 60;
 
         /// <summary>
         /// Singleton instance of settings

From 438ebd5c415217f593393f7154d24e2482aac197 Mon Sep 17 00:00:00 2001
From: Koji Hasegawa <hasegawa@hubsys.co.jp>
Date: Sat, 26 Apr 2025 14:07:30 +0900
Subject: [PATCH 04/17] Add about timeout settings

---
 README-ja.md    | 32 ++++++++++++++++++++++++++++++++
 README.md       | 32 ++++++++++++++++++++++++++++++++
 README_zh-CN.md | 32 ++++++++++++++++++++++++++++++++
 3 files changed, 96 insertions(+)

diff --git a/README-ja.md b/README-ja.md
index 93c0fcb6..106e8751 100644
--- a/README-ja.md
+++ b/README-ja.md
@@ -211,6 +211,38 @@ MCP Unityサーバーを起動するには2つの方法があります：
    node Server/build/index.js
    ```
 
+## オプション: タイムアウト設定
+
+### WebSocketタイムアウト
+
+デフォルトでは、MCPサーバーとWebSocket間のタイムアウトは 10 秒です。
+MCP構成ファイルで、次のように環境変数 `UNITY_REQUEST_TIMEOUT` として指定できます。
+
+```json
+{
+  "mcpServers": {
+    "mcp-unity": {
+      "command": "node",
+      "args": [
+        "ABSOLUTE/PATH/TO/mcp-unity/Server/build/index.js"
+      ],
+      "env": {
+        "UNITY_PORT": "8090",
+        "UNITY_REQUEST_TIMEOUT": "300"
+      }
+    }
+  }
+}
+```
+
+> [!TIP]  
+> AIコーディングIDE（Claude Desktop、Cursor IDE、Windsurf IDE など）とMCPサーバー間のタイムアウトは、AIコーディングIDEによって異なります。
+
+### テスト実行タイムアウト
+
+デフォルトでは、Unityエディター側の `run_tests` ツールの実行タイムアウトは 60 秒です。
+**Tools > MCP Unity > Server Window** の **Test Timeout (seconds)** で指定できます。
+
 ## <a name="debug-server"></a>サーバーのデバッグ
 
 MCP Unityサーバーをデバッグするには、以下の方法を使用できます：
diff --git a/README.md b/README.md
index c15a39bf..98d678a1 100644
--- a/README.md
+++ b/README.md
@@ -236,6 +236,38 @@ By default, the WebSocket server runs on port 8090. You can change this port in
 
 </details>
 
+## Optional: Set Timeout
+
+### WebSocket Timeout
+
+By default, the timeout between the MCP server and the WebSocket is 10 seconds.
+You can specify it as an environment variable `UNITY_REQUEST_TIMEOUT` in your MCP configuration file as follows:
+
+```json
+{
+  "mcpServers": {
+    "mcp-unity": {
+      "command": "node",
+      "args": [
+        "ABSOLUTE/PATH/TO/mcp-unity/Server/build/index.js"
+      ],
+      "env": {
+        "UNITY_PORT": "8090",
+        "UNITY_REQUEST_TIMEOUT": "300"
+      }
+    }
+  }
+}
+```
+
+> [!TIP]  
+> The timeout between your AI Coding IDE (e.g., Claude Desktop, Cursor IDE, Windsurf IDE) and the MCP Server depends on the AI coding IDE.
+
+### Test Execution Timeout
+
+By default, the execution timeout for the `run_tests` tool in the Unity editor is 60 seconds.
+You can specify it in **Test Timeout (seconds)** in the **Tools > MCP Unity > Server Window**.
+
 ## <a name="debug-server"></a>Debugging the Server
 
 <details>
diff --git a/README_zh-CN.md b/README_zh-CN.md
index be21ec0a..2e6f71e8 100644
--- a/README_zh-CN.md
+++ b/README_zh-CN.md
@@ -213,6 +213,38 @@ MCP Unity 通过将 Unity `Library/PackedCache` 文件夹添加到您的工作
    node Server/build/index.js
    ```
 
+## 可选：设置超时
+
+### WebSocket 超时
+
+默认情况下，MCP 服务器与 WebSocket 之间的超时时间为 10 秒。
+您可以在 MCP 配置文件中将其指定为环境变量 `UNITY_REQUEST_TIMEOUT`，如下所示：
+
+```json
+{
+  "mcpServers": {
+    "mcp-unity": {
+      "command": "node",
+      "args": [
+        "ABSOLUTE/PATH/TO/mcp-unity/Server/build/index.js"
+      ],
+      "env": {
+        "UNITY_PORT": "8090",
+        "UNITY_REQUEST_TIMEOUT": "300"
+      }
+    }
+  }
+}
+```
+
+> [!TIP]  
+> 您的 AI 编码 IDE（例如，Claude Desktop、Cursor IDE、Windsurf IDE）和 MCP 服务器之间的超时取决于 AI 编码 IDE。
+
+### 测试执行超时
+
+默认情况下，Unity 编辑器中 `run_tests` 工具的执行超时时间为 60 秒。
+您可以在 **Tools > MCP Unity > Server Window** 中的 **Test Timeout (seconds)** 中指定它。
+
 ## <a name="debug-server"></a>调试服务器
 
 要调试 MCP Unity 服务器，您可以使用以下方法：

From 22cc6780cf67dcca87a6ab5e4b32e7c248c8bc44 Mon Sep 17 00:00:00 2001
From: Koji Hasegawa <hasegawa@nowsprinting.com>
Date: Sat, 26 Apr 2025 15:03:43 +0900
Subject: [PATCH 05/17] Update Editor/UnityBridge/McpUnitySettings.cs

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
---
 Editor/UnityBridge/McpUnitySettings.cs | 1 -
 1 file changed, 1 deletion(-)

diff --git a/Editor/UnityBridge/McpUnitySettings.cs b/Editor/UnityBridge/McpUnitySettings.cs
index d3c340d6..0f9514c2 100644
--- a/Editor/UnityBridge/McpUnitySettings.cs
+++ b/Editor/UnityBridge/McpUnitySettings.cs
@@ -3,7 +3,6 @@
 using McpUnity.Utils;
 using UnityEngine;
 using UnityEditor;
-using UnityEngine.Serialization;
 
 namespace McpUnity.Unity
 {

From f7720260012778544cd6b9f0cd9ac9ee66ca7164 Mon Sep 17 00:00:00 2001
From: Koji Hasegawa <hasegawa@hubsys.co.jp>
Date: Sat, 26 Apr 2025 16:21:21 +0900
Subject: [PATCH 06/17] Add returnsOnlyFailures option into run_tests tool

---
 Editor/Tools/RunTestsTool.cs       | 9 +++++++++
 Server/build/tools/runTestsTool.js | 9 ++++++---
 Server/src/tools/runTestsTool.ts   | 9 ++++++---
 3 files changed, 21 insertions(+), 6 deletions(-)

diff --git a/Editor/Tools/RunTestsTool.cs b/Editor/Tools/RunTestsTool.cs
index 08757ef5..eee2d9f3 100644
--- a/Editor/Tools/RunTestsTool.cs
+++ b/Editor/Tools/RunTestsTool.cs
@@ -19,6 +19,7 @@ public class RunTestsTool : McpToolBase, ICallbacks
         private readonly ITestRunnerService _testRunnerService;
         
         private bool _isRunning = false;
+        private bool _returnsOnlyFailures = true;
         private TaskCompletionSource<JObject> _testRunCompletionSource;
         private List<TestResult> _testResults = new List<TestResult>();
         
@@ -31,6 +32,7 @@ private class TestResult
             public string Message { get; set; }
             public double Duration { get; set; }
             public bool Passed => ResultState == "Passed";
+            public bool Skipped => ResultState.StartsWith("Skipped");
         }
         
         public RunTestsTool(ITestRunnerService testRunnerService)
@@ -65,6 +67,7 @@ public override void ExecuteAsync(JObject parameters, TaskCompletionSource<JObje
             // Extract parameters
             string testModeStr = parameters["testMode"]?.ToObject<string>() ?? "editmode";
             string testFilter = parameters["testFilter"]?.ToObject<string>() ?? "";
+            _returnsOnlyFailures = parameters["returnsOnlyFailures"]?.ToObject<bool>() ?? true;
             
             // Parse test mode
             TestMode testMode;
@@ -157,6 +160,12 @@ public void RunFinished(ITestResultAdaptor result)
             var resultArray = new JArray();
             foreach (var testResult in _testResults)
             {
+                // If returnsOnlyFailures is true, only include failed tests
+                if (_returnsOnlyFailures && (testResult.Passed || testResult.Skipped))
+                {
+                    continue;
+                }
+                
                 resultArray.Add(new JObject
                 {
                     ["name"] = testResult.Name,
diff --git a/Server/build/tools/runTestsTool.js b/Server/build/tools/runTestsTool.js
index cd2c00d0..acdc76e6 100644
--- a/Server/build/tools/runTestsTool.js
+++ b/Server/build/tools/runTestsTool.js
@@ -5,7 +5,8 @@ const toolName = 'run_tests';
 const toolDescription = 'Runs Unity\'s Test Runner tests';
 const paramsSchema = z.object({
     testMode: z.string().optional().describe('The test mode to run (EditMode or PlayMode) - defaults to EditMode (optional)'),
-    testFilter: z.string().optional().describe('The specific test filter to run (e.g. specific test name or namespace) (optional)')
+    testFilter: z.string().optional().describe('The specific test filter to run (e.g. specific test name or namespace) (optional)'),
+    returnsOnlyFailures: z.boolean().optional().default(true).describe('Whether to show only failed tests in the results (optional)')
 });
 /**
  * Creates and registers the Run Tests tool with the MCP server
@@ -40,13 +41,14 @@ export function createRunTestsTool(server, mcpUnity, logger) {
  * @throws McpUnityError if the request to Unity fails
  */
 async function toolHandler(mcpUnity, params) {
-    const { testMode = 'EditMode', testFilter } = params;
+    const { testMode = 'EditMode', testFilter, returnsOnlyFailures = true } = params;
     // Create and wait for the test run
     const response = await mcpUnity.sendRequest({
         method: toolName,
         params: {
             testMode,
-            testFilter
+            testFilter,
+            returnsOnlyFailures
         }
     });
     // Process the test results
@@ -65,6 +67,7 @@ async function toolHandler(mcpUnity, params) {
     if (testCount > 0 && passCount < testCount) {
         resultMessage += `. Failed tests: ${testResults
             .filter((r) => r.result !== 'Passed')
+            .filter((r) => !r.result.startsWith('Skipped'))
             .map((r) => r.name)
             .join(', ')}`;
     }
diff --git a/Server/src/tools/runTestsTool.ts b/Server/src/tools/runTestsTool.ts
index b8de19a3..2d510fea 100644
--- a/Server/src/tools/runTestsTool.ts
+++ b/Server/src/tools/runTestsTool.ts
@@ -10,7 +10,8 @@ const toolName = 'run_tests';
 const toolDescription = 'Runs Unity\'s Test Runner tests';
 const paramsSchema = z.object({
   testMode: z.string().optional().describe('The test mode to run (EditMode or PlayMode) - defaults to EditMode (optional)'),
-  testFilter: z.string().optional().describe('The specific test filter to run (e.g. specific test name or namespace) (optional)')
+  testFilter: z.string().optional().describe('The specific test filter to run (e.g. specific test name or namespace) (optional)'),
+  returnsOnlyFailures: z.boolean().optional().default(true).describe('Whether to show only failed tests in the results (optional)')
 });
 
 /**
@@ -52,14 +53,15 @@ export function createRunTestsTool(server: McpServer, mcpUnity: McpUnity, logger
  * @throws McpUnityError if the request to Unity fails
  */
 async function toolHandler(mcpUnity: McpUnity, params: any): Promise<CallToolResult> {
-  const { testMode = 'EditMode', testFilter } = params;
+  const { testMode = 'EditMode', testFilter, returnsOnlyFailures = true } = params;
 
   // Create and wait for the test run
   const response = await mcpUnity.sendRequest({
     method: toolName,
     params: { 
       testMode,
-      testFilter
+      testFilter,
+      returnsOnlyFailures
     }
   });
   
@@ -84,6 +86,7 @@ async function toolHandler(mcpUnity: McpUnity, params: any): Promise<CallToolRes
   if (testCount > 0 && passCount < testCount) {
     resultMessage += `. Failed tests: ${testResults
       .filter((r: any) => r.result !== 'Passed')
+      .filter((r: any) => !r.result.startsWith('Skipped'))
       .map((r: any) => r.name)
       .join(', ')}`;
   }

From 3a5d17a99cad6c0165d0f827f09be7ef857d8507 Mon Sep 17 00:00:00 2001
From: Koji Hasegawa <hasegawa@hubsys.co.jp>
Date: Sun, 27 Apr 2025 00:30:19 +0900
Subject: [PATCH 07/17] Fix to persist WebSocket ports on non-Windows OS

---
 Editor/UnityBridge/McpUnitySettings.cs | 11 ++++++++++-
 1 file changed, 10 insertions(+), 1 deletion(-)

diff --git a/Editor/UnityBridge/McpUnitySettings.cs b/Editor/UnityBridge/McpUnitySettings.cs
index 0f9514c2..00778b84 100644
--- a/Editor/UnityBridge/McpUnitySettings.cs
+++ b/Editor/UnityBridge/McpUnitySettings.cs
@@ -20,6 +20,9 @@ public class McpUnitySettings
         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;
         
         [Tooltip("Whether to automatically start the MCP server when Unity opens")]
@@ -69,12 +72,14 @@ public void LoadSettings()
                     JsonUtility.FromJsonOverwrite(json, this);
                 }
                 
+#if UNITY_EDITOR_WIN
                 // Check for environment variable PORT
                 string envPort = System.Environment.GetEnvironmentVariable("UNITY_PORT");
                 if (!string.IsNullOrEmpty(envPort) && int.TryParse(envPort, out int port))
                 {
                     Port = port;
                 }
+#endif
             }
             catch (Exception ex)
             {
@@ -93,10 +98,14 @@ 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
+                // 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
                 System.Environment.SetEnvironmentVariable("UNITY_PORT", Port.ToString(), System.EnvironmentVariableTarget.User);
+#endif
             }
             catch (Exception ex)
             {

From fea6e1070fbe3514ee5be76f2f78bba72b7289f0 Mon Sep 17 00:00:00 2001
From: Koji Hasegawa <hasegawa@hubsys.co.jp>
Date: Sun, 27 Apr 2025 01:08:35 +0900
Subject: [PATCH 08/17] Fix to persist request timeout seconds as an
 environment variable on Windows OS

---
 Editor/Services/TestRunnerService.cs       |  2 +-
 Editor/UnityBridge/McpUnityEditorWindow.cs |  8 ++++----
 Editor/UnityBridge/McpUnitySettings.cs     | 24 +++++++++++++++++-----
 3 files changed, 24 insertions(+), 10 deletions(-)

diff --git a/Editor/Services/TestRunnerService.cs b/Editor/Services/TestRunnerService.cs
index bfe188e7..6d21e5ae 100644
--- a/Editor/Services/TestRunnerService.cs
+++ b/Editor/Services/TestRunnerService.cs
@@ -71,7 +71,7 @@ public async void ExecuteTests(
             _testRunnerApi.Execute(new ExecutionSettings(filter));
 
             // Use timeout from settings if not specified
-            var timeoutSeconds =  McpUnitySettings.Instance.TestTimeoutSeconds;
+            var timeoutSeconds =  McpUnitySettings.Instance.RequestTimeoutSeconds;
             
             Task completedTask = await Task.WhenAny(
                 completionSource.Task,
diff --git a/Editor/UnityBridge/McpUnityEditorWindow.cs b/Editor/UnityBridge/McpUnityEditorWindow.cs
index 772b57d6..59663e28 100644
--- a/Editor/UnityBridge/McpUnityEditorWindow.cs
+++ b/Editor/UnityBridge/McpUnityEditorWindow.cs
@@ -109,16 +109,16 @@ private void DrawServerTab()
             
             // Test timeout setting
             EditorGUILayout.BeginHorizontal();
-            int newTimeout = EditorGUILayout.IntField(new GUIContent("Test Timeout (seconds)", "Timeout in seconds for test execution"), settings.TestTimeoutSeconds);
+            int newTimeout = EditorGUILayout.IntField(new GUIContent("Request Timeout (seconds)", "Timeout in seconds for tool request"), settings.RequestTimeoutSeconds);
             if (newTimeout < 60)
             {
                 newTimeout = 60;
-                Debug.LogError("Test timeout must be at least 60 seconds.");
+                Debug.LogError("Request timeout must be at least 60 seconds.");
             }
             
-            if (newTimeout != settings.TestTimeoutSeconds)
+            if (newTimeout != settings.RequestTimeoutSeconds)
             {
-                settings.TestTimeoutSeconds = newTimeout;
+                settings.RequestTimeoutSeconds = newTimeout;
                 settings.SaveSettings();
             }
             EditorGUILayout.EndHorizontal();
diff --git a/Editor/UnityBridge/McpUnitySettings.cs b/Editor/UnityBridge/McpUnitySettings.cs
index 00778b84..37963021 100644
--- a/Editor/UnityBridge/McpUnitySettings.cs
+++ b/Editor/UnityBridge/McpUnitySettings.cs
@@ -15,6 +15,11 @@ public class McpUnitySettings
         // Constants
         public const string ServerVersion = "1.0.0";
         public const string PackageName = "com.gamelovers.mcp-unity";
+
+#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";
@@ -25,14 +30,17 @@ public class McpUnitySettings
 #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; } = 60;
+        
         [Tooltip("Whether to automatically start the MCP server when Unity opens")]
         public bool AutoStartServer = true;
         
         [Tooltip("Whether to show info logs in the Unity console")]
         public bool EnableInfoLogs = true;
-        
-        [Tooltip("Timeout in seconds for test execution")]
-        public int TestTimeoutSeconds = 60;
 
         /// <summary>
         /// Singleton instance of settings
@@ -74,11 +82,16 @@ public void LoadSettings()
                 
 #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)
@@ -104,7 +117,8 @@ public void SaveSettings()
                 // Note: This will only affect processes started after this point
                 // 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
-                System.Environment.SetEnvironmentVariable("UNITY_PORT", Port.ToString(), System.EnvironmentVariableTarget.User);
+                Environment.SetEnvironmentVariable(EnvUnityPort, Port.ToString(), EnvironmentVariableTarget.User);
+                Environment.SetEnvironmentVariable(EnvUnityRequestTimeout, RequestTimeoutSeconds.ToString(), EnvironmentVariableTarget.User);
 #endif
             }
             catch (Exception ex)

From 6b24edf8ccd97ba3eba6af3dcba62736e753f1af Mon Sep 17 00:00:00 2001
From: Koji Hasegawa <hasegawa@hubsys.co.jp>
Date: Sun, 27 Apr 2025 01:27:24 +0900
Subject: [PATCH 09/17] Fix minimum timeout

---
 Editor/UnityBridge/McpUnityEditorWindow.cs | 6 +++---
 Editor/UnityBridge/McpUnitySettings.cs     | 3 ++-
 2 files changed, 5 insertions(+), 4 deletions(-)

diff --git a/Editor/UnityBridge/McpUnityEditorWindow.cs b/Editor/UnityBridge/McpUnityEditorWindow.cs
index 59663e28..ded9242f 100644
--- a/Editor/UnityBridge/McpUnityEditorWindow.cs
+++ b/Editor/UnityBridge/McpUnityEditorWindow.cs
@@ -110,10 +110,10 @@ private void DrawServerTab()
             // Test timeout setting
             EditorGUILayout.BeginHorizontal();
             int newTimeout = EditorGUILayout.IntField(new GUIContent("Request Timeout (seconds)", "Timeout in seconds for tool request"), settings.RequestTimeoutSeconds);
-            if (newTimeout < 60)
+            if (newTimeout < McpUnitySettings.RequestTimeoutMinimum)
             {
-                newTimeout = 60;
-                Debug.LogError("Request timeout must be at least 60 seconds.");
+                newTimeout = McpUnitySettings.RequestTimeoutMinimum;
+                Debug.LogError($"Request timeout must be at least {McpUnitySettings.RequestTimeoutMinimum} seconds.");
             }
             
             if (newTimeout != settings.RequestTimeoutSeconds)
diff --git a/Editor/UnityBridge/McpUnitySettings.cs b/Editor/UnityBridge/McpUnitySettings.cs
index 37963021..056cd431 100644
--- a/Editor/UnityBridge/McpUnitySettings.cs
+++ b/Editor/UnityBridge/McpUnitySettings.cs
@@ -15,6 +15,7 @@ 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";
@@ -34,7 +35,7 @@ public class McpUnitySettings
         [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; } = 60;
+        public int RequestTimeoutSeconds { get; set; } = RequestTimeoutMinimum;
         
         [Tooltip("Whether to automatically start the MCP server when Unity opens")]
         public bool AutoStartServer = true;

From 59e28918ceec9baa485b952e6263ee63652f3579 Mon Sep 17 00:00:00 2001
From: Koji Hasegawa <hasegawa@hubsys.co.jp>
Date: Sun, 27 Apr 2025 01:57:04 +0900
Subject: [PATCH 10/17] Fix READMEs

---
 README-ja.md    | 63 ++++++++++++++++++++++++++++++-------------------
 README.md       | 63 ++++++++++++++++++++++++++++++-------------------
 README_zh-CN.md | 63 ++++++++++++++++++++++++++++++-------------------
 3 files changed, 117 insertions(+), 72 deletions(-)

diff --git a/README-ja.md b/README-ja.md
index 106e8751..cc4a75ef 100644
--- a/README-ja.md
+++ b/README-ja.md
@@ -213,35 +213,50 @@ MCP Unityサーバーを起動するには2つの方法があります：
 
 ## オプション: タイムアウト設定
 
-### WebSocketタイムアウト
-
 デフォルトでは、MCPサーバーとWebSocket間のタイムアウトは 10 秒です。
-MCP構成ファイルで、次のように環境変数 `UNITY_REQUEST_TIMEOUT` として指定できます。
+お使いのOSに応じて変更できます。
 
-```json
-{
-  "mcpServers": {
-    "mcp-unity": {
-      "command": "node",
-      "args": [
-        "ABSOLUTE/PATH/TO/mcp-unity/Server/build/index.js"
-      ],
-      "env": {
-        "UNITY_PORT": "8090",
-        "UNITY_REQUEST_TIMEOUT": "300"
-      }
-    }
-  }
-}
-```
+<details>
+<summary><span style="font-size: 1.1em; font-weight: bold;">Option 1: Windows OS</span></summary>
 
-> [!TIP]  
-> AIコーディングIDE（Claude Desktop、Cursor IDE、Windsurf IDE など）とMCPサーバー間のタイムアウトは、AIコーディングIDEによって異なります。
+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のタイムアウト
 
-デフォルトでは、Unityエディター側の `run_tests` ツールの実行タイムアウトは 60 秒です。
-**Tools > MCP Unity > Server Window** の **Test Timeout (seconds)** で指定できます。
+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>サーバーのデバッグ
 
diff --git a/README.md b/README.md
index 98d678a1..800ec710 100644
--- a/README.md
+++ b/README.md
@@ -238,35 +238,50 @@ By default, the WebSocket server runs on port 8090. You can change this port in
 
 ## Optional: Set Timeout
 
-### WebSocket Timeout
-
 By default, the timeout between the MCP server and the WebSocket is 10 seconds.
-You can specify it as an environment variable `UNITY_REQUEST_TIMEOUT` in your MCP configuration file as follows:
+You can change depending on the OS you are using:
 
-```json
-{
-  "mcpServers": {
-    "mcp-unity": {
-      "command": "node",
-      "args": [
-        "ABSOLUTE/PATH/TO/mcp-unity/Server/build/index.js"
-      ],
-      "env": {
-        "UNITY_PORT": "8090",
-        "UNITY_REQUEST_TIMEOUT": "300"
-      }
-    }
-  }
-}
-```
+<details>
+<summary><span style="font-size: 1.1em; font-weight: bold;">Option 1: Windows OS</span></summary>
 
-> [!TIP]  
-> The timeout between your AI Coding IDE (e.g., Claude Desktop, Cursor IDE, Windsurf IDE) and the MCP Server depends on the AI coding IDE.
+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>
 
-### Test Execution Timeout
+For non-Windows OS, you need to configure two places:
 
-By default, the execution timeout for the `run_tests` tool in the Unity editor is 60 seconds.
-You can specify it in **Test Timeout (seconds)** in the **Tools > MCP Unity > Server Window**.
+### 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
 
diff --git a/README_zh-CN.md b/README_zh-CN.md
index 2e6f71e8..f1839213 100644
--- a/README_zh-CN.md
+++ b/README_zh-CN.md
@@ -215,35 +215,50 @@ MCP Unity 通过将 Unity `Library/PackedCache` 文件夹添加到您的工作
 
 ## 可选：设置超时
 
-### WebSocket 超时
-
 默认情况下，MCP 服务器与 WebSocket 之间的超时时间为 10 秒。
-您可以在 MCP 配置文件中将其指定为环境变量 `UNITY_REQUEST_TIMEOUT`，如下所示：
+您可以根据所使用的操作系统进行更改：
 
-```json
-{
-  "mcpServers": {
-    "mcp-unity": {
-      "command": "node",
-      "args": [
-        "ABSOLUTE/PATH/TO/mcp-unity/Server/build/index.js"
-      ],
-      "env": {
-        "UNITY_PORT": "8090",
-        "UNITY_REQUEST_TIMEOUT": "300"
-      }
-    }
-  }
-}
-```
+<details>
+<summary><span style="font-size: 1.1em; font-weight: bold;">Option 1: Windows OS</span></summary>
 
-> [!TIP]  
-> 您的 AI 编码 IDE（例如，Claude Desktop、Cursor IDE、Windsurf IDE）和 MCP 服务器之间的超时取决于 AI 编码 IDE。
+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操作系统，需要配置两个地方：
 
-默认情况下，Unity 编辑器中 `run_tests` 工具的执行超时时间为 60 秒。
-您可以在 **Tools > MCP Unity > Server Window** 中的 **Test Timeout (seconds)** 中指定它。
+### 编辑器进程超时
+
+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 服务器
+
+</details>
+
+> [!TIP]  
+> 您的 AI 编码 IDE（例如，Claude Desktop、Cursor IDE、Windsurf IDE）和 MCP 服务器之间的超时取决于 IDE。
 
 ## <a name="debug-server"></a>调试服务器
 

From 129d7e3213108fc01b9c16384384b34f58954c9b Mon Sep 17 00:00:00 2001
From: CoderGamester <game.gamester@gmail.com>
Date: Sun, 27 Apr 2025 21:32:07 +0300
Subject: [PATCH 11/17] Added option to filter tests by test mode as it is
 defined in the resource

---
 Editor/Resources/GetTestsResource.cs       | 18 ++++--------------
 Editor/Services/ConsoleLogsService.cs      |  7 +++++--
 Editor/Services/IConsoleLogsService.cs     |  7 ++++---
 Editor/Services/ITestRunnerService.cs      |  6 ++++--
 Editor/Services/TestRunnerService.cs       | 20 +++++++++++++++-----
 Server/build/resources/getTestsResource.js | 16 +++++++++++-----
 Server/src/resources/getTestsResource.ts   | 17 +++++++++++------
 7 files changed, 54 insertions(+), 37 deletions(-)

diff --git a/Editor/Resources/GetTestsResource.cs b/Editor/Resources/GetTestsResource.cs
index d3982ef4..252de3a9 100644
--- a/Editor/Resources/GetTestsResource.cs
+++ b/Editor/Resources/GetTestsResource.cs
@@ -14,12 +14,12 @@ namespace McpUnity.Resources
     /// </summary>
     public class GetTestsResource : McpResourceBase
     {
-        private readonly TestRunnerService _testRunnerService;
+        private readonly ITestRunnerService _testRunnerService;
         
         /// <summary>
         /// Constructor
         /// </summary>
-        public GetTestsResource(TestRunnerService testRunnerService)
+        public GetTestsResource(ITestRunnerService testRunnerService)
         {
             Name = "get_tests";
             Description = "Gets available tests from Unity Test Runner";
@@ -38,15 +38,7 @@ public override JObject Fetch(JObject parameters)
             string testModeFilter = parameters["testMode"]?.ToObject<string>();
             
             // Get all tests from the service
-            var allTests = _testRunnerService.GetAllTests();
-            
-            // Apply test mode filter if provided
-            if (!string.IsNullOrEmpty(testModeFilter))
-            {
-                allTests = allTests.Where(t => 
-                    t.TestMode.Equals(testModeFilter, StringComparison.OrdinalIgnoreCase)
-                ).ToList();
-            }
+            var allTests = _testRunnerService.GetAllTests(testModeFilter);
             
             // Create the results array
             var results = new JArray();
@@ -67,9 +59,7 @@ public override JObject Fetch(JObject parameters)
             {
                 ["success"] = true,
                 ["message"] = $"Retrieved {allTests.Count} tests",
-                ["tests"] = results,
-                ["editModeCount"] = allTests.Count(t => t.TestMode.Equals("EditMode", StringComparison.OrdinalIgnoreCase)),
-                ["playModeCount"] = allTests.Count(t => t.TestMode.Equals("PlayMode", StringComparison.OrdinalIgnoreCase))
+                ["tests"] = results
             };
         }
     }
diff --git a/Editor/Services/ConsoleLogsService.cs b/Editor/Services/ConsoleLogsService.cs
index 418a96fc..e3063c17 100644
--- a/Editor/Services/ConsoleLogsService.cs
+++ b/Editor/Services/ConsoleLogsService.cs
@@ -70,15 +70,18 @@ public void StopListening()
         /// Get all logs as a JSON array
         /// </summary>
         /// <returns>JArray containing all logs</returns>
-        public JArray GetAllLogsAsJson()
+        public JArray GetAllLogsAsJson(string logType = "")
         {
-            // Convert log entries to a JSON array
+            // Convert log entries to a JSON array, filtering by logType if provided
             JArray logsArray = new JArray();
+            bool filter = !string.IsNullOrEmpty(logType);
             
             lock (_logEntries)
             {
                 foreach (var entry in _logEntries)
                 {
+                    if (filter && !entry.Type.ToString().Equals(logType, System.StringComparison.OrdinalIgnoreCase))
+                        continue;
                     logsArray.Add(new JObject
                     {
                         ["message"] = entry.Message,
diff --git a/Editor/Services/IConsoleLogsService.cs b/Editor/Services/IConsoleLogsService.cs
index 75127570..ac96d50c 100644
--- a/Editor/Services/IConsoleLogsService.cs
+++ b/Editor/Services/IConsoleLogsService.cs
@@ -11,10 +11,11 @@ namespace McpUnity.Services
     public interface IConsoleLogsService
     {
         /// <summary>
-        /// Get all logs as a JSON array
+        /// Get all logs as a JSON array, optionally filtered by log type
         /// </summary>
-        /// <returns>JArray containing all logs</returns>
-        JArray GetAllLogsAsJson();
+        /// <param name="logType">UnityEngine.LogType as string (e.g. "Error", "Warning", "Log"). Empty string for all logs.</param>
+        /// <returns>JArray containing filtered logs</returns>
+        JArray GetAllLogsAsJson(string logType = "");
         
         /// <summary>
         /// Start listening for logs
diff --git a/Editor/Services/ITestRunnerService.cs b/Editor/Services/ITestRunnerService.cs
index 780ba3f3..d7a7151b 100644
--- a/Editor/Services/ITestRunnerService.cs
+++ b/Editor/Services/ITestRunnerService.cs
@@ -16,9 +16,11 @@ public interface ITestRunnerService
         TestRunnerApi TestRunnerApi { get; }
         
         /// <summary>
-        /// Get a list of all available tests
+        /// Get a list of available tests, optionally filtered by test mode
         /// </summary>
-        List<TestItemInfo> GetAllTests();
+        /// <param name="testMode">Optional test mode filter (EditMode, PlayMode, or empty for all)</param>
+        /// <returns>List of test items matching the specified test mode, or all tests if no mode specified</returns>
+        List<TestItemInfo> GetAllTests(string testMode = "");
 
         /// <summary>
         /// Execute tests with the provided parameters
diff --git a/Editor/Services/TestRunnerService.cs b/Editor/Services/TestRunnerService.cs
index 7b7fc87d..38f73900 100644
--- a/Editor/Services/TestRunnerService.cs
+++ b/Editor/Services/TestRunnerService.cs
@@ -30,15 +30,25 @@ public TestRunnerService()
         }
         
         /// <summary>
-        /// Get a list of all available tests
+        /// Get a list of available tests, optionally filtered by test mode
         /// </summary>
-        public List<TestItemInfo> GetAllTests()
+        /// <param name="testMode">Optional test mode filter (EditMode, PlayMode, or empty for all)</param>
+        /// <returns>List of test items matching the specified test mode, or all tests if no mode specified</returns>
+        public List<TestItemInfo> GetAllTests(string testMode = "")
         {
             var tests = new List<TestItemInfo>();
             
-            // Get tests for both edit mode and play mode
-            _testRunnerApi.RetrieveTestList(TestMode.EditMode, adaptor => CollectTestItems(adaptor, tests));
-            _testRunnerApi.RetrieveTestList(TestMode.PlayMode, adaptor => CollectTestItems(adaptor, tests));
+            // Check if we need to retrieve EditMode tests
+            if (string.IsNullOrEmpty(testMode) || testMode.Equals("EditMode", StringComparison.OrdinalIgnoreCase))
+            {
+                _testRunnerApi.RetrieveTestList(TestMode.EditMode, adaptor => CollectTestItems(adaptor, tests));
+            }
+            
+            // Check if we need to retrieve PlayMode tests
+            if (string.IsNullOrEmpty(testMode) || testMode.Equals("PlayMode", StringComparison.OrdinalIgnoreCase))
+            {
+                _testRunnerApi.RetrieveTestList(TestMode.PlayMode, adaptor => CollectTestItems(adaptor, tests));
+            }
             
             return tests;
         }
diff --git a/Server/build/resources/getTestsResource.js b/Server/build/resources/getTestsResource.js
index 5c0114e2..74f7a2f6 100644
--- a/Server/build/resources/getTestsResource.js
+++ b/Server/build/resources/getTestsResource.js
@@ -17,14 +17,20 @@ function listTestModes(resourceMimeType) {
         resources: [
             {
                 uri: `unity://tests/EditMode`,
-                name: "EditMode tests",
-                description: "List of all EditMode tests in Unity's test runner",
+                name: "List only 'EditMode' tests",
+                description: "List only 'EditMode' tests from Unity's test runner",
                 mimeType: resourceMimeType
             },
             {
                 uri: `unity://tests/PlayMode`,
-                name: "PlayMode tests",
-                description: "List of all PlayMode tests in Unity's test runner",
+                name: "List only 'PlayMode' tests",
+                description: "List only 'PlayMode' tests from Unity's test runner",
+                mimeType: resourceMimeType
+            },
+            {
+                uri: `unity://tests/`,
+                name: "List all tests",
+                description: "List of all tests in Unity's test runner, this includes PlayMode and EditMode tests",
                 mimeType: resourceMimeType
             }
         ]
@@ -66,7 +72,7 @@ export function createGetTestsResource(server, mcpUnity, logger) {
  */
 async function resourceHandler(mcpUnity, uri, variables) {
     // Convert the new handler signature to work with our existing code
-    const testMode = variables["testMode"] || 'EditMode';
+    const testMode = variables["testMode"];
     const response = await mcpUnity.sendRequest({
         method: resourceName,
         params: {
diff --git a/Server/src/resources/getTestsResource.ts b/Server/src/resources/getTestsResource.ts
index 279cb190..2926bec3 100644
--- a/Server/src/resources/getTestsResource.ts
+++ b/Server/src/resources/getTestsResource.ts
@@ -31,14 +31,20 @@ function listTestModes(resourceMimeType: string) {
     resources: [
       { 
         uri: `unity://tests/EditMode`, 
-        name: "EditMode tests",
-        description: "List of all EditMode tests in Unity's test runner",
+        name: "List only 'EditMode' tests",
+        description: "List only 'EditMode' tests from Unity's test runner",
         mimeType: resourceMimeType
       },
       { 
         uri: `unity://tests/PlayMode`, 
-        name: "PlayMode tests",
-        description: "List of all PlayMode tests in Unity's test runner",
+        name: "List only 'PlayMode' tests",
+        description: "List only 'PlayMode' tests from Unity's test runner",
+        mimeType: resourceMimeType
+      },
+      { 
+        uri: `unity://tests/`, 
+        name: "List all tests",
+        description: "List of all tests in Unity's test runner, this includes PlayMode and EditMode tests",
         mimeType: resourceMimeType
       }
     ]
@@ -75,7 +81,6 @@ export function createGetTestsResource(server: McpServer, mcpUnity: McpUnity, lo
   );
 }
 
-
 /**
  * Handles requests for test information from Unity's Test Runner
  * Retrieves tests filtered by test mode (EditMode or PlayMode)
@@ -88,7 +93,7 @@ export function createGetTestsResource(server: McpServer, mcpUnity: McpUnity, lo
  */
 async function resourceHandler(mcpUnity: McpUnity, uri: URL, variables: Variables): Promise<ReadResourceResult> {
   // Convert the new handler signature to work with our existing code
-  const testMode = variables["testMode"] || 'EditMode';
+  const testMode = variables["testMode"];
   
   const response = await mcpUnity.sendRequest({
     method: resourceName,

From 1b3769813712f1d69cdb5de29f8d4b743ddce883 Mon Sep 17 00:00:00 2001
From: Koji Hasegawa <hasegawa@hubsys.co.jp>
Date: Mon, 28 Apr 2025 03:47:39 +0900
Subject: [PATCH 12/17] Fix server auto-restart logic on domain reloading

---
 Editor/UnityBridge/McpUnityServer.cs | 9 +++------
 1 file changed, 3 insertions(+), 6 deletions(-)

diff --git a/Editor/UnityBridge/McpUnityServer.cs b/Editor/UnityBridge/McpUnityServer.cs
index 72a4db69..a9274cbb 100644
--- a/Editor/UnityBridge/McpUnityServer.cs
+++ b/Editor/UnityBridge/McpUnityServer.cs
@@ -38,13 +38,10 @@ static McpUnityServer()
             EditorApplication.quitting += Instance.StopServer;
 
             // Auto-restart server after domain reload
-            EditorApplication.delayCall += () =>
+            if (McpUnitySettings.Instance.AutoStartServer)
             {
-                if (McpUnitySettings.Instance.AutoStartServer)
-                {
-                    Instance.StartServer();
-                }
-            };
+                Instance.StartServer();
+            }
         }
         
         /// <summary>

From c98534fc541b4053ad66ab1a7b9005895c628164 Mon Sep 17 00:00:00 2001
From: CoderGamester <game.gamester@gmail.com>
Date: Tue, 29 Apr 2025 02:25:36 +0300
Subject: [PATCH 13/17] Fixed the issue preventing GetTestsResource to work
 Added capacity for McpResources to work asynchronously

---
 .windsurfrules                              | 13 +++++-
 Editor/Resources/GetTestsResource.cs        | 27 ++++-------
 Editor/Resources/McpResourceBase.cs         | 32 +++++++++++--
 Editor/Services/ITestRunnerService.cs       |  6 +--
 Editor/Services/TestRunnerService.cs        | 51 +++++++++++++++++----
 Editor/Tools/RunTestsTool.cs                |  3 ++
 Editor/UnityBridge/McpUnitySocketHandler.cs | 14 ++++--
 7 files changed, 107 insertions(+), 39 deletions(-)

diff --git a/.windsurfrules b/.windsurfrules
index 6457e212..6bf8a4bb 100644
--- a/.windsurfrules
+++ b/.windsurfrules
@@ -55,10 +55,21 @@
 
 - MCP Protocol: https://modelcontextprotocol.io
 - TypeScript SDK: https://github.com/modelcontextprotocol/typescript-sdk
+- Inspector: https://github.com/modelcontextprotocol/inspector
 
 ## 9. Conventions
 
 - Use WebSockets for all cross-process communication.
 - Follow the MCP protocol for all tool/resource definitions.
 - All new tools/resources should be registered in both Unity and Node.js server entry points.
-- Follow Conventional Commits for all commit messages.
\ No newline at end of file
+- Follow Conventional Commits for all commit messages.
+
+## 10. Debugging with MCP Inspector
+
+To debug the MCP Node.js server using the Model Context Protocol Inspector, run the following command from the project root:
+
+```shell
+npx @modelcontextprotocol/inspector node Server/build/index.js
+```
+
+This will launch the MCP Inspector, allowing you to inspect and debug live MCP traffic between the Node.js server and connected clients (such as Unity or LLM IDEs).
\ No newline at end of file
diff --git a/Editor/Resources/GetTestsResource.cs b/Editor/Resources/GetTestsResource.cs
index 252de3a9..28c0a501 100644
--- a/Editor/Resources/GetTestsResource.cs
+++ b/Editor/Resources/GetTestsResource.cs
@@ -1,11 +1,7 @@
-using System;
-using System.Linq;
 using System.Collections.Generic;
 using System.Threading.Tasks;
-using UnityEngine;
-using Newtonsoft.Json.Linq;
-using McpUnity.Unity;
 using McpUnity.Services;
+using Newtonsoft.Json.Linq;
 
 namespace McpUnity.Resources
 {
@@ -24,25 +20,23 @@ public GetTestsResource(ITestRunnerService testRunnerService)
             Name = "get_tests";
             Description = "Gets available tests from Unity Test Runner";
             Uri = "unity://tests/{testMode}";
-            
+            IsAsync = true;
             _testRunnerService = testRunnerService;
         }
         
         /// <summary>
-        /// Fetch tests based on provided parameters
+        /// Asynchronously fetch tests based on provided parameters
         /// </summary>
         /// <param name="parameters">Resource parameters as a JObject</param>
-        public override JObject Fetch(JObject parameters)
+        /// <param name="tcs">TaskCompletionSource to set the result or exception</param>
+        public override async void FetchAsync(JObject parameters, TaskCompletionSource<JObject> tcs)
         {
             // Get filter parameters
             string testModeFilter = parameters["testMode"]?.ToObject<string>();
-            
-            // Get all tests from the service
-            var allTests = _testRunnerService.GetAllTests(testModeFilter);
-            
-            // Create the results array
+            List<TestItemInfo> allTests = await _testRunnerService.GetAllTestsAsync(testModeFilter);
             var results = new JArray();
-            foreach (var test in allTests)
+            
+            foreach (TestItemInfo test in allTests)
             {
                 results.Add(new JObject
                 {
@@ -54,13 +48,12 @@ public override JObject Fetch(JObject parameters)
                 });
             }
             
-            // Return the results
-            return new JObject
+            tcs.SetResult(new JObject
             {
                 ["success"] = true,
                 ["message"] = $"Retrieved {allTests.Count} tests",
                 ["tests"] = results
-            };
+            });
         }
     }
 }
diff --git a/Editor/Resources/McpResourceBase.cs b/Editor/Resources/McpResourceBase.cs
index 49a6eadc..08843e36 100644
--- a/Editor/Resources/McpResourceBase.cs
+++ b/Editor/Resources/McpResourceBase.cs
@@ -1,5 +1,6 @@
 using System;
 using System.Collections.Generic;
+using System.Threading.Tasks;
 using UnityEngine;
 using Newtonsoft.Json.Linq;
 
@@ -31,10 +32,33 @@ public abstract class McpResourceBase
         public bool IsEnabled { get; protected set; } = true;
         
         /// <summary>
-        /// Fetch the resource data with the provided parameters
+        /// Indicates if the fetch operation is asynchronous.
         /// </summary>
-        /// <param name="parameters">Resource parameters as a JObject</param>
-        /// <returns>The result of the resource fetch as a JObject</returns>
-        public abstract JObject Fetch(JObject parameters);
+        public bool IsAsync { get; protected set; } = false;
+
+        /// <summary>
+        /// Synchronously fetch the resource data.
+        /// Implement this for synchronous resources (IsAsync = false).
+        /// </summary>
+        /// <param name="parameters">Parameters extracted from the URI or query.</param>
+        /// <returns>Result as JObject.</returns>
+        public virtual JObject Fetch(JObject parameters)
+        {
+            // Default implementation throws, forcing sync resources to override.
+            throw new NotImplementedException($"Synchronous Fetch not implemented for resource '{Name}'. Mark IsAsync=true and implement FetchAsync, or override Fetch.");
+        }
+
+        /// <summary>
+        /// Asynchronously fetch the resource data.
+        /// Implement this for asynchronous resources (IsAsync = true).
+        /// The implementation MUST eventually call tcs.SetResult() or tcs.SetException().
+        /// </summary>
+        /// <param name="parameters">Parameters extracted from the URI or query.</param>
+        /// <param name="tcs">TaskCompletionSource to set the result on.</param>
+        public virtual void FetchAsync(JObject parameters, TaskCompletionSource<JObject> tcs)
+        {
+            // Default implementation throws, forcing async resources to override.
+            tcs.SetException(new NotImplementedException($"Asynchronous FetchAsync not implemented for resource '{Name}'. Mark IsAsync=false and implement Fetch, or override FetchAsync."));
+        }
     }
 }
diff --git a/Editor/Services/ITestRunnerService.cs b/Editor/Services/ITestRunnerService.cs
index 8a01c7cf..cf168b37 100644
--- a/Editor/Services/ITestRunnerService.cs
+++ b/Editor/Services/ITestRunnerService.cs
@@ -14,13 +14,13 @@ public interface ITestRunnerService
         /// Get the TestRunnerApi instance
         /// </summary>
         TestRunnerApi TestRunnerApi { get; }
-        
+
         /// <summary>
-        /// Get a list of available tests, optionally filtered by test mode
+        /// Async retrieval of all tests using TestRunnerApi callbacks
         /// </summary>
         /// <param name="testMode">Optional test mode filter (EditMode, PlayMode, or empty for all)</param>
         /// <returns>List of test items matching the specified test mode, or all tests if no mode specified</returns>
-        List<TestItemInfo> GetAllTests(string testMode = "");
+        Task<List<TestItemInfo>> GetAllTestsAsync(string testMode = "");
 
         /// <summary>
         /// Execute tests with the provided parameters
diff --git a/Editor/Services/TestRunnerService.cs b/Editor/Services/TestRunnerService.cs
index 26ce3819..08f35f92 100644
--- a/Editor/Services/TestRunnerService.cs
+++ b/Editor/Services/TestRunnerService.cs
@@ -4,6 +4,7 @@
 using System.Threading.Tasks;
 using McpUnity.Unity;
 using UnityEngine;
+using UnityEditor;
 using UnityEditor.TestTools.TestRunner.Api;
 using Newtonsoft.Json.Linq;
 
@@ -28,29 +29,59 @@ public TestRunnerService()
         {
             _testRunnerApi = ScriptableObject.CreateInstance<TestRunnerApi>();
         }
-        
+
+        [MenuItem("Tools/MCP Unity/Debug call path")]
+        public static async void DebugCallGetAllTests()
+        {
+            var service = new TestRunnerService();
+            var tests = await service.GetAllTestsAsync();
+            Debug.Log($"Retrieved {tests.Count} tests:");
+            foreach (var t in tests)
+                Debug.Log($"Test: {t.FullName} ({t.TestMode}) - State: {t.RunState}");
+        }
+
         /// <summary>
-        /// Get a list of available tests, optionally filtered by test mode
+        /// Async retrieval of all tests using TestRunnerApi callbacks
         /// </summary>
         /// <param name="testMode">Optional test mode filter (EditMode, PlayMode, or empty for all)</param>
         /// <returns>List of test items matching the specified test mode, or all tests if no mode specified</returns>
-        public List<TestItemInfo> GetAllTests(string testMode = "")
+        public async Task<List<TestItemInfo>> GetAllTestsAsync(string testMode = "")
         {
             var tests = new List<TestItemInfo>();
-            
-            // Check if we need to retrieve EditMode tests
+            var tcs = new TaskCompletionSource<bool>();
+            int pending = 0;
+
             if (string.IsNullOrEmpty(testMode) || testMode.Equals("EditMode", StringComparison.OrdinalIgnoreCase))
             {
-                _testRunnerApi.RetrieveTestList(TestMode.EditMode, adaptor => CollectTestItems(adaptor, tests));
+                Interlocked.Increment(ref pending);
+                _testRunnerApi.RetrieveTestList(TestMode.EditMode, adaptor =>
+                {
+                    CollectTestItems(adaptor, tests);
+                    CheckDone();
+                });
             }
-            
-            // Check if we need to retrieve PlayMode tests
             if (string.IsNullOrEmpty(testMode) || testMode.Equals("PlayMode", StringComparison.OrdinalIgnoreCase))
             {
-                _testRunnerApi.RetrieveTestList(TestMode.PlayMode, adaptor => CollectTestItems(adaptor, tests));
+                Interlocked.Increment(ref pending);
+                _testRunnerApi.RetrieveTestList(TestMode.PlayMode, adaptor =>
+                {
+                    CollectTestItems(adaptor, tests);
+                    CheckDone();
+                });
             }
-            
+
+            if (pending == 0)
+                tcs.SetResult(true);
+
+            await tcs.Task;
+
             return tests;
+
+            void CheckDone()
+            {
+                if (Interlocked.Decrement(ref pending) == 0)
+                    tcs.TrySetResult(true);
+            }
         }
 
         /// <summary>
diff --git a/Editor/Tools/RunTestsTool.cs b/Editor/Tools/RunTestsTool.cs
index eee2d9f3..7daf40ef 100644
--- a/Editor/Tools/RunTestsTool.cs
+++ b/Editor/Tools/RunTestsTool.cs
@@ -145,12 +145,15 @@ public void RunFinished(ITestResultAdaptor result)
             // Create test results summary
             var summary = new JObject
             {
+                ["name"] = result.Name,
+                ["fullName"] = result.FullName,
                 ["testCount"] = result.PassCount + result.FailCount + result.SkipCount + result.InconclusiveCount,
                 ["passCount"] = result.PassCount,
                 ["failCount"] = result.FailCount,
                 ["skipCount"] = result.SkipCount,
                 ["inconclusiveCount"] = result.InconclusiveCount,
                 ["duration"] = result.Duration,
+                ["resultState"] = result.ResultState,
                 ["success"] = result.ResultState == "Passed",
                 ["status"] = "completed",
                 ["message"] = $"Test run completed: {result.Test.Name} - {result.ResultState}"
diff --git a/Editor/UnityBridge/McpUnitySocketHandler.cs b/Editor/UnityBridge/McpUnitySocketHandler.cs
index ed6a48d1..63c429f0 100644
--- a/Editor/UnityBridge/McpUnitySocketHandler.cs
+++ b/Editor/UnityBridge/McpUnitySocketHandler.cs
@@ -177,18 +177,24 @@ private IEnumerator FetchResourceCoroutine(McpResourceBase resource, JObject par
         {
             try
             {
-                var result = resource.Fetch(parameters);
-                tcs.SetResult(result);
+                if (resource.IsAsync)
+                {
+                    resource.FetchAsync(parameters, tcs);
+                }
+                else
+                {
+                    var result = resource.Fetch(parameters);
+                    tcs.SetResult(result);
+                }
             }
             catch (Exception ex)
             {
-                McpLogger.LogError($"Error fetching resource {resource.Name}: {ex.Message}");
+                McpLogger.LogError($"Error fetching resource {resource.Name}: {ex}");
                 tcs.SetResult(CreateErrorResponse(
                     $"Failed to fetch resource {resource.Name}: {ex.Message}",
                     "resource_fetch_error"
                 ));
             }
-            
             yield return null;
         }
         

From 3547d2ce7fd9274d9e7d8a7bae12472454a776a1 Mon Sep 17 00:00:00 2001
From: Miguel Tomas <game.gamester@gmail.com>
Date: Wed, 30 Apr 2025 01:18:00 +0300
Subject: [PATCH 14/17] refactor(TestRunnerService): improve GetAllTestsAsync
 method readability and organization

---
 Editor/Services/TestRunnerService.cs | 173 ---------------------------
 1 file changed, 173 deletions(-)

diff --git a/Editor/Services/TestRunnerService.cs b/Editor/Services/TestRunnerService.cs
index 08f35f92..e69de29b 100644
--- a/Editor/Services/TestRunnerService.cs
+++ b/Editor/Services/TestRunnerService.cs
@@ -1,173 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Threading;
-using System.Threading.Tasks;
-using McpUnity.Unity;
-using UnityEngine;
-using UnityEditor;
-using UnityEditor.TestTools.TestRunner.Api;
-using Newtonsoft.Json.Linq;
-
-namespace McpUnity.Services
-{
-    /// <summary>
-    /// Service for accessing Unity Test Runner functionality
-    /// </summary>
-    public class TestRunnerService : ITestRunnerService
-    {
-        private readonly TestRunnerApi _testRunnerApi;
-        
-        /// <summary>
-        /// Get the TestRunnerApi instance
-        /// </summary>
-        public TestRunnerApi TestRunnerApi => _testRunnerApi;
-        
-        /// <summary>
-        /// Constructor
-        /// </summary>
-        public TestRunnerService()
-        {
-            _testRunnerApi = ScriptableObject.CreateInstance<TestRunnerApi>();
-        }
-
-        [MenuItem("Tools/MCP Unity/Debug call path")]
-        public static async void DebugCallGetAllTests()
-        {
-            var service = new TestRunnerService();
-            var tests = await service.GetAllTestsAsync();
-            Debug.Log($"Retrieved {tests.Count} tests:");
-            foreach (var t in tests)
-                Debug.Log($"Test: {t.FullName} ({t.TestMode}) - State: {t.RunState}");
-        }
-
-        /// <summary>
-        /// Async retrieval of all tests using TestRunnerApi callbacks
-        /// </summary>
-        /// <param name="testMode">Optional test mode filter (EditMode, PlayMode, or empty for all)</param>
-        /// <returns>List of test items matching the specified test mode, or all tests if no mode specified</returns>
-        public async Task<List<TestItemInfo>> GetAllTestsAsync(string testMode = "")
-        {
-            var tests = new List<TestItemInfo>();
-            var tcs = new TaskCompletionSource<bool>();
-            int pending = 0;
-
-            if (string.IsNullOrEmpty(testMode) || testMode.Equals("EditMode", StringComparison.OrdinalIgnoreCase))
-            {
-                Interlocked.Increment(ref pending);
-                _testRunnerApi.RetrieveTestList(TestMode.EditMode, adaptor =>
-                {
-                    CollectTestItems(adaptor, tests);
-                    CheckDone();
-                });
-            }
-            if (string.IsNullOrEmpty(testMode) || testMode.Equals("PlayMode", StringComparison.OrdinalIgnoreCase))
-            {
-                Interlocked.Increment(ref pending);
-                _testRunnerApi.RetrieveTestList(TestMode.PlayMode, adaptor =>
-                {
-                    CollectTestItems(adaptor, tests);
-                    CheckDone();
-                });
-            }
-
-            if (pending == 0)
-                tcs.SetResult(true);
-
-            await tcs.Task;
-
-            return tests;
-
-            void CheckDone()
-            {
-                if (Interlocked.Decrement(ref pending) == 0)
-                    tcs.TrySetResult(true);
-            }
-        }
-
-        /// <summary>
-        /// Execute tests with the provided parameters
-        /// </summary>
-        /// <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>
-        /// <returns>Task that resolves with test results when tests are complete</returns>
-        public async void ExecuteTests(
-            TestMode testMode, 
-            string testFilter, 
-            TaskCompletionSource<JObject> completionSource)
-        {
-            // Create filter
-            var filter = new Filter
-            {
-                testMode = testMode
-            };
-                
-            // Apply name filter if provided
-            if (!string.IsNullOrEmpty(testFilter))
-            {
-                filter.testNames = new[] { testFilter };
-            }
-                
-            // 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.FromSeconds(timeoutSeconds))
-            );
-
-            if (completedTask != completionSource.Task)
-            {
-                completionSource.SetResult(McpUnitySocketHandler.CreateErrorResponse(
-                    $"Test run timed out after {timeoutSeconds} seconds",
-                    "test_runner_timeout"
-                ));
-            }
-        }
-        
-        /// <summary>
-        /// Recursively collect test items from test adaptors
-        /// </summary>
-        private void CollectTestItems(ITestAdaptor testAdaptor, List<TestItemInfo> tests, string parentPath = "")
-        {
-            if (testAdaptor.IsSuite)
-            {
-                // For suites (namespaces, classes), collect all children
-                foreach (var child in testAdaptor.Children)
-                {
-                    string currentPath = string.IsNullOrEmpty(parentPath) ? testAdaptor.Name : $"{parentPath}.{testAdaptor.Name}";
-                    CollectTestItems(child, tests, currentPath);
-                }
-            }
-            else
-            {
-                // For individual tests, add to the list
-                string fullPath = string.IsNullOrEmpty(parentPath) ? testAdaptor.Name : $"{parentPath}.{testAdaptor.Name}";
-                
-                tests.Add(new TestItemInfo
-                {
-                    Name = testAdaptor.Name,
-                    FullName = testAdaptor.FullName,
-                    Path = fullPath,
-                    TestMode = testAdaptor.TestMode.ToString(),
-                    RunState = testAdaptor.RunState.ToString()
-                });
-            }
-        }
-    }
-    
-    /// <summary>
-    /// Information about a test item
-    /// </summary>
-    public class TestItemInfo
-    {
-        public string Name { get; set; }
-        public string FullName { get; set; }
-        public string Path { get; set; }
-        public string TestMode { get; set; }
-        public string RunState { get; set; }
-    }
-}

From 423ae566aa4e2a4e48dae0bb1a748336cd1f5453 Mon Sep 17 00:00:00 2001
From: Miguel Tomas <game.gamester@gmail.com>
Date: Wed, 30 Apr 2025 01:19:15 +0300
Subject: [PATCH 15/17] refactor(TestRunnerService): improve GetAllTestsAsync
 readability with helper methods

---
 Editor/Services/TestRunnerService.cs | 262 +++++++++++++++++++++++++++
 1 file changed, 262 insertions(+)

diff --git a/Editor/Services/TestRunnerService.cs b/Editor/Services/TestRunnerService.cs
index e69de29b..e299f776 100644
--- a/Editor/Services/TestRunnerService.cs
+++ b/Editor/Services/TestRunnerService.cs
@@ -0,0 +1,262 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+using McpUnity.Unity;
+using McpUnity.Utils;
+using UnityEngine;
+using UnityEditor;
+using UnityEditor.TestTools.TestRunner.Api;
+using Newtonsoft.Json.Linq;
+
+namespace McpUnity.Services
+{
+    /// <summary>
+    /// Service for accessing Unity Test Runner functionality
+    /// Implements ICallbacks for TestRunnerApi.
+    /// </summary>
+    public class TestRunnerService : ITestRunnerService, ICallbacks
+    {
+        private readonly TestRunnerApi _testRunnerApi;
+        private TaskCompletionSource<JObject> _tcs;
+        private bool _returnOnlyFailures;
+        private List<ITestResultAdaptor> _results;
+
+        /// <summary>
+        /// Constructor
+        /// </summary>
+        public TestRunnerService()
+        {
+            _testRunnerApi = ScriptableObject.CreateInstance<TestRunnerApi>();
+            
+            _testRunnerApi.RegisterCallbacks(this);
+        }
+
+        [MenuItem("Tools/MCP Unity/Debug call path")]
+        public static async void DebugCallGetAllTests()
+        {
+            var service = new TestRunnerService();
+            var tests = await service.GetAllTestsAsync();
+            Debug.Log($"Retrieved {tests.Count} tests:");
+            foreach (var t in tests)
+                Debug.Log($"Test: {t.FullName} ({t.TestMode}) - State: {t.RunState}");
+        }
+
+        /// <summary>
+        /// Asynchronously retrieves all available tests using the TestRunnerApi.
+        /// </summary>
+        /// <param name="testModeFilter">Optional test mode filter (EditMode, PlayMode, or empty for all)</param>
+        /// <returns>List of test items matching the specified test mode, or all tests if no mode specified</returns>
+        public async Task<List<ITestAdaptor>> GetAllTestsAsync(string testModeFilter = "")
+        {
+            McpLogger.LogInfo($"Retrieving tests with filter: {testModeFilter ?? "All"}");
+            
+            var tests = new List<ITestAdaptor>();
+            var tcs = new TaskCompletionSource<bool>();
+            var pendingOperations = 0;
+
+            // Determine which test modes to retrieve based on the filter
+            var modesToRetrieve = GetTestModesToRetrieve(testModeFilter);
+            
+            // Exit early if no modes match the filter
+            if (modesToRetrieve.Count == 0)
+            {
+                McpLogger.LogInfo("No matching test modes found for filter");
+                return tests;
+            }
+
+            // Start retrieval for each test mode
+            foreach (var mode in modesToRetrieve)
+            {
+                RetrieveTestsForMode(mode, tests, ref pendingOperations, tcs);
+            }
+
+            // Wait for all retrievals to complete
+            await tcs.Task;
+            McpLogger.LogInfo($"Retrieved {tests.Count} tests matching filter: {testModeFilter ?? "All"}");
+            
+            return tests;
+        }
+
+        /// <summary>
+        /// Determines which test modes to retrieve based on the provided filter.
+        /// </summary>
+        private List<TestMode> GetTestModesToRetrieve(string testModeFilter)
+        {
+            var modes = new List<TestMode>();
+            
+            // If no filter or explicitly requesting EditMode tests
+            if (string.IsNullOrEmpty(testModeFilter) || 
+                testModeFilter.Equals("EditMode", StringComparison.OrdinalIgnoreCase))
+            {
+                modes.Add(TestMode.EditMode);
+            }
+            
+            // If no filter or explicitly requesting PlayMode tests
+            if (string.IsNullOrEmpty(testModeFilter) || 
+                testModeFilter.Equals("PlayMode", StringComparison.OrdinalIgnoreCase))
+            {
+                modes.Add(TestMode.PlayMode);
+            }
+            
+            return modes;
+        }
+
+        /// <summary>
+        /// Retrieves tests for a specific test mode and adds them to the results collection.
+        /// </summary>
+        private void RetrieveTestsForMode(
+            TestMode mode, 
+            List<ITestAdaptor> tests, 
+            ref int pendingOperations, 
+            TaskCompletionSource<bool> tcs)
+        {
+            Interlocked.Increment(ref pendingOperations);
+            
+            McpLogger.LogInfo($"Retrieving {mode} tests...");
+            _testRunnerApi.RetrieveTestList(mode, adaptor =>
+            {
+                // Process the test results
+                CollectTestItems(adaptor, tests);
+                
+                // Check if this was the last pending operation
+                if (Interlocked.Decrement(ref pendingOperations) == 0)
+                {
+                    McpLogger.LogInfo($"All test retrievals completed");
+                    tcs.TrySetResult(true);
+                }
+            });
+        }
+
+        /// <summary>
+        /// Executes tests and returns a JSON summary.
+        /// </summary>
+        /// <param name="testMode">The test mode to run (EditMode or PlayMode).</param>
+        /// <param name="returnOnlyFailures">If true, only failed test results are included in the output.</param>
+        /// <param name="testFilter">A filter string to select specific tests to run.</param>
+        /// <returns>Task that resolves with test results when tests are complete</returns>
+        public async Task<JObject> ExecuteTestsAsync(TestMode testMode, bool returnOnlyFailures, string testFilter = "")
+        {
+            _tcs = new TaskCompletionSource<JObject>();
+            _results = new List<ITestResultAdaptor>();
+            _returnOnlyFailures = returnOnlyFailures;
+            var filter = new Filter { testMode = testMode };
+
+            if (!string.IsNullOrEmpty(testFilter))
+            {
+                filter.testNames = new[] { testFilter };
+            }
+
+            _testRunnerApi.Execute(new ExecutionSettings(filter));
+
+            return await WaitForCompletionAsync(
+                McpUnitySettings.Instance.RequestTimeoutSeconds);
+        }
+        
+        /// <summary>
+        /// Recursively collect test items from test adaptors
+        /// </summary>
+        private void CollectTestItems(ITestAdaptor testAdaptor, List<ITestAdaptor> tests)
+        {
+            if (testAdaptor.IsSuite)
+            {
+                // For suites (namespaces, classes), collect all children
+                foreach (var child in testAdaptor.Children)
+                {
+                    CollectTestItems(child, tests);
+                }
+            }
+            else
+            {
+                tests.Add(testAdaptor);
+            }
+        }
+
+        #region ICallbacks Implementation
+
+        /// <summary>
+        /// Called when the test run starts.
+        /// </summary>
+        public void RunStarted(ITestAdaptor testsToRun)
+        {
+            McpLogger.LogInfo($"Test run started: {testsToRun?.Name}");
+        }
+
+        /// <summary>
+        /// Called when an individual test starts.
+        /// </summary>
+        public void TestStarted(ITestAdaptor test)
+        {
+            // Optionally implement per-test start logic or logging.
+        }
+
+        /// <summary>
+        /// Called when an individual test finishes.
+        /// </summary>
+        public void TestFinished(ITestResultAdaptor result)
+        {
+            _results.Add(result);
+        }
+
+        /// <summary>
+        /// Called when the test run finishes.
+        /// </summary>
+        public void RunFinished(ITestResultAdaptor result)
+        {
+            var summary = BuildResultJson(_results, result);
+            _tcs?.TrySetResult(summary);
+        }
+
+        #endregion
+
+        #region Helpers
+
+        private async Task<JObject> WaitForCompletionAsync(int timeoutSeconds)
+        {
+            var delayTask = Task.Delay(TimeSpan.FromSeconds(timeoutSeconds));
+            var winner = await Task.WhenAny(_tcs.Task, delayTask);
+            
+            if (winner != _tcs.Task)
+            {
+                _tcs.TrySetResult(
+                    McpUnitySocketHandler.CreateErrorResponse(
+                        $"Test run timed out after {timeoutSeconds} seconds",
+                        "test_runner_timeout"));
+            }
+            return await _tcs.Task;
+        }
+
+        private JObject BuildResultJson(List<ITestResultAdaptor> results, ITestResultAdaptor result)
+        {
+            int pass = results.Count(r => r.ResultState == "Passed");
+            int fail = results.Count(r => r.ResultState == "Failed");
+            int skip = results.Count(r => r.ResultState == "Skipped");
+
+            var arr = new JArray(results
+                .Where(r => !_returnOnlyFailures || r.ResultState == "Failed")
+                .Select(r => new JObject {
+                    ["name"]      = r.Name,
+                    ["fullName"]  = r.FullName,
+                    ["state"]     = r.ResultState,
+                    ["message"]   = r.Message,
+                    ["duration"]  = r.Duration
+                }));
+
+            return new JObject { 
+                ["success"]           = true,
+                ["type"]              = "text",
+                ["message"]           = $"{result.Test.Name} test run completed: {pass}/{results.Count} passed - {fail}/{results.Count} failed - {skip}/{results.Count} skipped",
+                ["resultState"]       = result.ResultState,
+                ["durationSeconds"]   = result.Duration,
+                ["testCount"]         = results.Count,
+                ["passCount"]         = pass,
+                ["failCount"]         = fail,
+                ["skipCount"]         = skip,
+                ["results"]           = arr
+            };
+        }
+
+        #endregion
+    }
+}
\ No newline at end of file

From 1124a5c85b856a58735894a86cff3cb2b9e70e39 Mon Sep 17 00:00:00 2001
From: CoderGamester <game.gamester@gmail.com>
Date: Wed, 30 Apr 2025 01:48:19 +0300
Subject: [PATCH 16/17] Fix GetTests Resource and Run tests tool to work as
 described in the Readme file

---
 Editor/Resources/GetTestsResource.cs  |  10 +-
 Editor/Services/ITestRunnerService.cs |  24 +--
 Editor/Services/TestRunnerService.cs  | 233 ++++++++++++++++----------
 Editor/Tools/RunTestsTool.cs          | 191 ++-------------------
 Server/build/tools/runTestsTool.js    |  27 +--
 Server/src/tools/runTestsTool.ts      |  28 ++--
 6 files changed, 190 insertions(+), 323 deletions(-)

diff --git a/Editor/Resources/GetTestsResource.cs b/Editor/Resources/GetTestsResource.cs
index 28c0a501..ac099d11 100644
--- a/Editor/Resources/GetTestsResource.cs
+++ b/Editor/Resources/GetTestsResource.cs
@@ -2,6 +2,7 @@
 using System.Threading.Tasks;
 using McpUnity.Services;
 using Newtonsoft.Json.Linq;
+using UnityEditor.TestTools.TestRunner.Api;
 
 namespace McpUnity.Resources
 {
@@ -33,18 +34,17 @@ public override async void FetchAsync(JObject parameters, TaskCompletionSource<J
         {
             // Get filter parameters
             string testModeFilter = parameters["testMode"]?.ToObject<string>();
-            List<TestItemInfo> allTests = await _testRunnerService.GetAllTestsAsync(testModeFilter);
+            List<ITestAdaptor> allTests = await _testRunnerService.GetAllTestsAsync(testModeFilter);
             var results = new JArray();
             
-            foreach (TestItemInfo test in allTests)
+            foreach (ITestAdaptor test in allTests)
             {
                 results.Add(new JObject
                 {
                     ["name"] = test.Name,
                     ["fullName"] = test.FullName,
-                    ["path"] = test.Path,
-                    ["testMode"] = test.TestMode,
-                    ["runState"] = test.RunState
+                    ["testMode"] = test.TestMode.ToString(),
+                    ["runState"] = test.RunState.ToString()
                 });
             }
             
diff --git a/Editor/Services/ITestRunnerService.cs b/Editor/Services/ITestRunnerService.cs
index cf168b37..b5666099 100644
--- a/Editor/Services/ITestRunnerService.cs
+++ b/Editor/Services/ITestRunnerService.cs
@@ -11,27 +11,19 @@ namespace McpUnity.Services
     public interface ITestRunnerService
     {
         /// <summary>
-        /// Get the TestRunnerApi instance
+        /// Asynchronously retrieves all available tests using the TestRunnerApi.
         /// </summary>
-        TestRunnerApi TestRunnerApi { get; }
-
-        /// <summary>
-        /// Async retrieval of all tests using TestRunnerApi callbacks
-        /// </summary>
-        /// <param name="testMode">Optional test mode filter (EditMode, PlayMode, or empty for all)</param>
+        /// <param name="testModeFilter">Optional test mode filter (EditMode, PlayMode, or empty for all)</param>
         /// <returns>List of test items matching the specified test mode, or all tests if no mode specified</returns>
-        Task<List<TestItemInfo>> GetAllTestsAsync(string testMode = "");
+        Task<List<ITestAdaptor>> GetAllTestsAsync(string testModeFilter = "");
 
         /// <summary>
-        /// Execute tests with the provided parameters
+        /// Executes tests using the TestRunnerApi and returns the results as a JSON object.
         /// </summary>
-        /// <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="testMode">The test mode to run (EditMode or PlayMode).</param>
+        /// <param name="returnOnlyFailures">If true, only failed test results are included in the output.</param>
+        /// <param name="testFilter">A filter string to select specific tests to run.</param>
         /// <returns>Task that resolves with test results when tests are complete</returns>
-        void ExecuteTests(
-            TestMode testMode,
-            string testFilter,
-            TaskCompletionSource<JObject> completionSource);
+        Task<JObject> ExecuteTestsAsync(TestMode testMode, bool returnOnlyFailures, string testFilter);
     }
 }
diff --git a/Editor/Services/TestRunnerService.cs b/Editor/Services/TestRunnerService.cs
index 08f35f92..a91b9866 100644
--- a/Editor/Services/TestRunnerService.cs
+++ b/Editor/Services/TestRunnerService.cs
@@ -1,8 +1,10 @@
 using System;
 using System.Collections.Generic;
+using System.Linq;
 using System.Threading;
 using System.Threading.Tasks;
 using McpUnity.Unity;
+using McpUnity.Utils;
 using UnityEngine;
 using UnityEditor;
 using UnityEditor.TestTools.TestRunner.Api;
@@ -12,22 +14,23 @@ namespace McpUnity.Services
 {
     /// <summary>
     /// Service for accessing Unity Test Runner functionality
+    /// Implements ICallbacks for TestRunnerApi.
     /// </summary>
-    public class TestRunnerService : ITestRunnerService
+    public class TestRunnerService : ITestRunnerService, ICallbacks
     {
         private readonly TestRunnerApi _testRunnerApi;
-        
-        /// <summary>
-        /// Get the TestRunnerApi instance
-        /// </summary>
-        public TestRunnerApi TestRunnerApi => _testRunnerApi;
-        
+        private TaskCompletionSource<JObject> _tcs;
+        private bool _returnOnlyFailures;
+        private List<ITestResultAdaptor> _results;
+
         /// <summary>
         /// Constructor
         /// </summary>
         public TestRunnerService()
         {
             _testRunnerApi = ScriptableObject.CreateInstance<TestRunnerApi>();
+            
+            _testRunnerApi.RegisterCallbacks(this);
         }
 
         [MenuItem("Tools/MCP Unity/Debug call path")]
@@ -43,131 +46,179 @@ public static async void DebugCallGetAllTests()
         /// <summary>
         /// Async retrieval of all tests using TestRunnerApi callbacks
         /// </summary>
-        /// <param name="testMode">Optional test mode filter (EditMode, PlayMode, or empty for all)</param>
+        /// <param name="testModeFilter">Optional test mode filter (EditMode, PlayMode, or empty for all)</param>
         /// <returns>List of test items matching the specified test mode, or all tests if no mode specified</returns>
-        public async Task<List<TestItemInfo>> GetAllTestsAsync(string testMode = "")
+        public async Task<List<ITestAdaptor>> GetAllTestsAsync(string testModeFilter = "")
         {
-            var tests = new List<TestItemInfo>();
-            var tcs = new TaskCompletionSource<bool>();
-            int pending = 0;
+            var tests = new List<ITestAdaptor>();
+            var tasks = new List<Task<List<ITestAdaptor>>>();
 
-            if (string.IsNullOrEmpty(testMode) || testMode.Equals("EditMode", StringComparison.OrdinalIgnoreCase))
+            if (string.IsNullOrEmpty(testModeFilter) || testModeFilter.Equals("EditMode", StringComparison.OrdinalIgnoreCase))
             {
-                Interlocked.Increment(ref pending);
-                _testRunnerApi.RetrieveTestList(TestMode.EditMode, adaptor =>
-                {
-                    CollectTestItems(adaptor, tests);
-                    CheckDone();
-                });
+                tasks.Add(RetrieveTestsAsync(TestMode.EditMode));
             }
-            if (string.IsNullOrEmpty(testMode) || testMode.Equals("PlayMode", StringComparison.OrdinalIgnoreCase))
+            if (string.IsNullOrEmpty(testModeFilter) || testModeFilter.Equals("PlayMode", StringComparison.OrdinalIgnoreCase))
             {
-                Interlocked.Increment(ref pending);
-                _testRunnerApi.RetrieveTestList(TestMode.PlayMode, adaptor =>
-                {
-                    CollectTestItems(adaptor, tests);
-                    CheckDone();
-                });
+                tasks.Add(RetrieveTestsAsync(TestMode.PlayMode));
             }
 
-            if (pending == 0)
-                tcs.SetResult(true);
-
-            await tcs.Task;
+            var results = await Task.WhenAll(tasks);
 
-            return tests;
-
-            void CheckDone()
+            foreach (var result in results)
             {
-                if (Interlocked.Decrement(ref pending) == 0)
-                    tcs.TrySetResult(true);
+                tests.AddRange(result);
             }
+
+            return tests;
         }
 
         /// <summary>
-        /// Execute tests with the provided parameters
+        /// Executes tests and returns a JSON summary.
         /// </summary>
-        /// <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="testMode">The test mode to run (EditMode or PlayMode).</param>
+        /// <param name="returnOnlyFailures">If true, only failed test results are included in the output.</param>
+        /// <param name="testFilter">A filter string to select specific tests to run.</param>
         /// <returns>Task that resolves with test results when tests are complete</returns>
-        public async void ExecuteTests(
-            TestMode testMode, 
-            string testFilter, 
-            TaskCompletionSource<JObject> completionSource)
+        public async Task<JObject> ExecuteTestsAsync(TestMode testMode, bool returnOnlyFailures, string testFilter = "")
         {
-            // Create filter
-            var filter = new Filter
-            {
-                testMode = testMode
-            };
-                
-            // Apply name filter if provided
+            _tcs = new TaskCompletionSource<JObject>();
+            _results = new List<ITestResultAdaptor>();
+            _returnOnlyFailures = returnOnlyFailures;
+            var filter = new Filter { testMode = testMode };
+
             if (!string.IsNullOrEmpty(testFilter))
             {
                 filter.testNames = new[] { testFilter };
             }
-                
-            // 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.FromSeconds(timeoutSeconds))
-            );
+            return await WaitForCompletionAsync(
+                McpUnitySettings.Instance.RequestTimeoutSeconds);
+        }
+        
+        /// <summary>
+        /// Asynchronously retrieves all test adaptors for the specified test mode.
+        /// </summary>
+        /// <param name="mode">The test mode to retrieve tests for (EditMode or PlayMode).</param>
+        /// <returns>A task that resolves to a list of ITestAdaptor representing all tests in the given mode.</returns>
+        private Task<List<ITestAdaptor>> RetrieveTestsAsync(TestMode mode)
+        {
+            var tcs = new TaskCompletionSource<List<ITestAdaptor>>();
+            var tests = new List<ITestAdaptor>();
 
-            if (completedTask != completionSource.Task)
+            _testRunnerApi.RetrieveTestList(mode, adaptor =>
             {
-                completionSource.SetResult(McpUnitySocketHandler.CreateErrorResponse(
-                    $"Test run timed out after {timeoutSeconds} seconds",
-                    "test_runner_timeout"
-                ));
-            }
+                CollectTestItems(adaptor, tests);
+                tcs.SetResult(tests);
+            });
+
+            return tcs.Task;
         }
         
         /// <summary>
         /// Recursively collect test items from test adaptors
         /// </summary>
-        private void CollectTestItems(ITestAdaptor testAdaptor, List<TestItemInfo> tests, string parentPath = "")
+        private void CollectTestItems(ITestAdaptor testAdaptor, List<ITestAdaptor> tests)
         {
             if (testAdaptor.IsSuite)
             {
                 // For suites (namespaces, classes), collect all children
                 foreach (var child in testAdaptor.Children)
                 {
-                    string currentPath = string.IsNullOrEmpty(parentPath) ? testAdaptor.Name : $"{parentPath}.{testAdaptor.Name}";
-                    CollectTestItems(child, tests, currentPath);
+                    CollectTestItems(child, tests);
                 }
             }
             else
             {
-                // For individual tests, add to the list
-                string fullPath = string.IsNullOrEmpty(parentPath) ? testAdaptor.Name : $"{parentPath}.{testAdaptor.Name}";
-                
-                tests.Add(new TestItemInfo
-                {
-                    Name = testAdaptor.Name,
-                    FullName = testAdaptor.FullName,
-                    Path = fullPath,
-                    TestMode = testAdaptor.TestMode.ToString(),
-                    RunState = testAdaptor.RunState.ToString()
-                });
+                tests.Add(testAdaptor);
             }
         }
-    }
-    
-    /// <summary>
-    /// Information about a test item
-    /// </summary>
-    public class TestItemInfo
-    {
-        public string Name { get; set; }
-        public string FullName { get; set; }
-        public string Path { get; set; }
-        public string TestMode { get; set; }
-        public string RunState { get; set; }
+
+        #region ICallbacks Implementation
+
+        /// <summary>
+        /// Called when the test run starts.
+        /// </summary>
+        public void RunStarted(ITestAdaptor testsToRun)
+        {
+            McpLogger.LogInfo($"Test run started: {testsToRun?.Name}");
+        }
+
+        /// <summary>
+        /// Called when an individual test starts.
+        /// </summary>
+        public void TestStarted(ITestAdaptor test)
+        {
+            // Optionally implement per-test start logic or logging.
+        }
+
+        /// <summary>
+        /// Called when an individual test finishes.
+        /// </summary>
+        public void TestFinished(ITestResultAdaptor result)
+        {
+            _results.Add(result);
+        }
+
+        /// <summary>
+        /// Called when the test run finishes.
+        /// </summary>
+        public void RunFinished(ITestResultAdaptor result)
+        {
+            var summary = BuildResultJson(_results, result);
+            _tcs?.TrySetResult(summary);
+        }
+
+        #endregion
+
+        #region Helpers
+
+        private async Task<JObject> WaitForCompletionAsync(int timeoutSeconds)
+        {
+            var delayTask = Task.Delay(TimeSpan.FromSeconds(timeoutSeconds));
+            var winner = await Task.WhenAny(_tcs.Task, delayTask);
+            
+            if (winner != _tcs.Task)
+            {
+                _tcs.TrySetResult(
+                    McpUnitySocketHandler.CreateErrorResponse(
+                        $"Test run timed out after {timeoutSeconds} seconds",
+                        "test_runner_timeout"));
+            }
+            return await _tcs.Task;
+        }
+
+        private JObject BuildResultJson(List<ITestResultAdaptor> results, ITestResultAdaptor result)
+        {
+            int pass = results.Count(r => r.ResultState == "Passed");
+            int fail = results.Count(r => r.ResultState == "Failed");
+            int skip = results.Count(r => r.ResultState == "Skipped");
+
+            var arr = new JArray(results
+                .Where(r => !_returnOnlyFailures || r.ResultState == "Failed")
+                .Select(r => new JObject {
+                    ["name"]      = r.Name,
+                    ["fullName"]  = r.FullName,
+                    ["state"]     = r.ResultState,
+                    ["message"]   = r.Message,
+                    ["duration"]  = r.Duration
+                }));
+
+            return new JObject { 
+                ["success"]           = true,
+                ["type"]              = "text",
+                ["message"]           = $"{result.Test.Name} test run completed: {pass}/{results.Count} passed - {fail}/{results.Count} failed - {skip}/{results.Count} skipped",
+                ["resultState"]       = result.ResultState,
+                ["durationSeconds"]   = result.Duration,
+                ["testCount"]         = results.Count,
+                ["passCount"]         = pass,
+                ["failCount"]         = fail,
+                ["skipCount"]         = skip,
+                ["results"]           = arr
+            };
+        }
+
+        #endregion
     }
 }
diff --git a/Editor/Tools/RunTestsTool.cs b/Editor/Tools/RunTestsTool.cs
index 7daf40ef..857eb31a 100644
--- a/Editor/Tools/RunTestsTool.cs
+++ b/Editor/Tools/RunTestsTool.cs
@@ -14,37 +14,16 @@ namespace McpUnity.Tools
     /// <summary>
     /// Tool for running Unity Test Runner tests
     /// </summary>
-    public class RunTestsTool : McpToolBase, ICallbacks
+    public class RunTestsTool : McpToolBase
     {
         private readonly ITestRunnerService _testRunnerService;
-        
-        private bool _isRunning = false;
-        private bool _returnsOnlyFailures = true;
-        private TaskCompletionSource<JObject> _testRunCompletionSource;
-        private List<TestResult> _testResults = new List<TestResult>();
-        
-        // Structure to store test results
-        private class TestResult
-        {
-            public string Name { get; set; }
-            public string FullName { get; set; }
-            public string ResultState { get; set; }
-            public string Message { get; set; }
-            public double Duration { get; set; }
-            public bool Passed => ResultState == "Passed";
-            public bool Skipped => ResultState.StartsWith("Skipped");
-        }
-        
+
         public RunTestsTool(ITestRunnerService testRunnerService)
         {
             Name = "run_tests";
             Description = "Runs tests using Unity's Test Runner";
             IsAsync = true;
-            
             _testRunnerService = testRunnerService;
-            
-            // Register callbacks with the TestRunnerApi
-            _testRunnerService.TestRunnerApi.RegisterCallbacks(this);
         }
         
         /// <summary>
@@ -52,161 +31,25 @@ public RunTestsTool(ITestRunnerService testRunnerService)
         /// </summary>
         /// <param name="parameters">Tool parameters, including optional 'testMode' and 'testFilter'.</param>
         /// <param name="tcs">TaskCompletionSource to set the result or exception.</param>
-        public override void ExecuteAsync(JObject parameters, TaskCompletionSource<JObject> tcs)
+        public override async void ExecuteAsync(JObject parameters, TaskCompletionSource<JObject> tcs)
         {
-            // Check if tests are already running
-            if (_isRunning)
-            {
-                tcs.SetResult(McpUnitySocketHandler.CreateErrorResponse(
-                    "Tests are already running. Please wait for them to complete.",
-                    "test_runner_busy"
-                ));
-                return;
-            }
-            
-            // Extract parameters
-            string testModeStr = parameters["testMode"]?.ToObject<string>() ?? "editmode";
-            string testFilter = parameters["testFilter"]?.ToObject<string>() ?? "";
-            _returnsOnlyFailures = parameters["returnsOnlyFailures"]?.ToObject<bool>() ?? true;
-            
-            // Parse test mode
-            TestMode testMode;
-            switch (testModeStr.ToLowerInvariant())
-            {
-                case "playmode":
-                    testMode = TestMode.PlayMode;
-                    break;
-                case "editmode":
-                    testMode = TestMode.EditMode;
-                    break;
-                default:
-                    testMode = TestMode.EditMode;
-                    break;
-            }
-            
-            // Log the execution
-            McpLogger.LogInfo($"[MCP Unity] Running tests: Mode={testMode}, Filter={testFilter}");
-            
-            // Reset state
-            _isRunning = true;
-            _testResults.Clear();
-            _testRunCompletionSource = tcs;
-            
-            // Execute tests using the TestRunnerService
-            _testRunnerService.ExecuteTests(
-                testMode, 
-                testFilter, 
-                tcs
-            );
-        }
-        
-        #region ICallbacks Implementation
-        
-        // Called when a test run starts
-        public void RunStarted(ITestAdaptor testsToRun)
-        {
-            McpLogger.LogInfo($"[MCP Unity] Test run started: {testsToRun.Name}");
-        }
-        
-        // Called when a test runs
-        public void TestStarted(ITestAdaptor test)
-        {
-            // Nothing to do here
-        }
-        
-        // Called when a test finishes
-        public void TestFinished(ITestResultAdaptor result)
-        {
-            // Skip test suites (tests with children)
-            if (result.Test.HasChildren)
-            {
-                return;
-            }
-            
-            _testResults.Add(new TestResult
-            {
-                Name = result.Test.Name,
-                FullName = result.FullName,
-                ResultState = result.ResultState,
-                Message = result.Message,
-                Duration = result.Duration
-            });
-            
-            McpLogger.LogInfo($"[MCP Unity] Test finished: {result.Test.Name} - {result.ResultState}");
-        }
-        
-        // Called when a test run completes
-        public void RunFinished(ITestResultAdaptor result)
-        {
-            McpLogger.LogInfo($"[MCP Unity] {_testResults.Count} tests completed in {result.Duration}: {result.Test.Name} - {result.ResultState}");
-            
-            _isRunning = false;
-            
-            // Create test results summary
-            var summary = new JObject
-            {
-                ["name"] = result.Name,
-                ["fullName"] = result.FullName,
-                ["testCount"] = result.PassCount + result.FailCount + result.SkipCount + result.InconclusiveCount,
-                ["passCount"] = result.PassCount,
-                ["failCount"] = result.FailCount,
-                ["skipCount"] = result.SkipCount,
-                ["inconclusiveCount"] = result.InconclusiveCount,
-                ["duration"] = result.Duration,
-                ["resultState"] = result.ResultState,
-                ["success"] = result.ResultState == "Passed",
-                ["status"] = "completed",
-                ["message"] = $"Test run completed: {result.Test.Name} - {result.ResultState}"
-            };
-            
-            // Add test results array
-            var resultArray = new JArray();
-            foreach (var testResult in _testResults)
-            {
-                // If returnsOnlyFailures is true, only include failed tests
-                if (_returnsOnlyFailures && (testResult.Passed || testResult.Skipped))
-                {
-                    continue;
-                }
-                
-                resultArray.Add(new JObject
-                {
-                    ["name"] = testResult.Name,
-                    ["fullName"] = testResult.FullName,
-                    ["result"] = testResult.ResultState,
-                    ["message"] = testResult.Message,
-                    ["duration"] = testResult.Duration
-                });
-            }
-            summary["results"] = resultArray;
+            // Parse parameters
+            string testModeStr = parameters?["testMode"]?.ToObject<string>() ?? "EditMode";
+            string testFilter = parameters?["testFilter"]?.ToObject<string>(); // Optional
+            bool returnOnlyFailures = parameters?["returnOnlyFailures"]?.ToObject<bool>() ?? false; // Optional
+
+            TestMode testMode = TestMode.EditMode;
             
-            // Set the test run completion result
-            try
-            {
-                _testRunCompletionSource?.SetResult(new JObject
-                {
-                    ["success"] = true,
-                    ["type"] = "text",
-                    ["message"] = summary["message"].Value<string>(),
-                    ["testCount"] = summary["testCount"],
-                    ["passCount"] = summary["passCount"],
-                    ["failCount"] = summary["failCount"],
-                    ["skipCount"] = summary["skipCount"],
-                    ["inconclusiveCount"] = summary["inconclusiveCount"],
-                    ["results"] = summary["results"]
-                });
-            }
-            catch (Exception ex)
-            {
-                McpLogger.LogError($"[MCP Unity] Failed to set test results: {ex.Message}");
-                _testRunCompletionSource?.TrySetException(ex);
-            }
-            finally
+            if (Enum.TryParse(testModeStr, true, out TestMode parsedMode))
             {
-                _testRunCompletionSource = null;
+                testMode = parsedMode;
             }
+
+            McpLogger.LogInfo($"Executing RunTestsTool: Mode={testMode}, Filter={testFilter ?? "(none)"}");
+
+            // Call the service to run tests
+            JObject result = await _testRunnerService.ExecuteTestsAsync(testMode, returnOnlyFailures, testFilter);
+            tcs.SetResult(result);
         }
-        
-        #endregion
     }
 }
diff --git a/Server/build/tools/runTestsTool.js b/Server/build/tools/runTestsTool.js
index acdc76e6..4b461a13 100644
--- a/Server/build/tools/runTestsTool.js
+++ b/Server/build/tools/runTestsTool.js
@@ -4,9 +4,9 @@ import { McpUnityError, ErrorType } from '../utils/errors.js';
 const toolName = 'run_tests';
 const toolDescription = 'Runs Unity\'s Test Runner tests';
 const paramsSchema = z.object({
-    testMode: z.string().optional().describe('The test mode to run (EditMode or PlayMode) - defaults to EditMode (optional)'),
-    testFilter: z.string().optional().describe('The specific test filter to run (e.g. specific test name or namespace) (optional)'),
-    returnsOnlyFailures: z.boolean().optional().default(true).describe('Whether to show only failed tests in the results (optional)')
+    testMode: z.string().optional().default('EditMode').describe('The test mode to run (EditMode or PlayMode) - defaults to EditMode (optional)'),
+    testFilter: z.string().optional().default('').describe('The specific test filter to run (e.g. specific test name or namespace) (optional)'),
+    returnOnlyFailures: z.boolean().optional().default(true).describe('Whether to show only failed tests in the results (optional)')
 });
 /**
  * Creates and registers the Run Tests tool with the MCP server
@@ -19,7 +19,7 @@ const paramsSchema = z.object({
 export function createRunTestsTool(server, mcpUnity, logger) {
     logger.info(`Registering tool: ${toolName}`);
     // Register this tool with the MCP server
-    server.tool(toolName, toolDescription, paramsSchema.shape, async (params) => {
+    server.tool(toolName, toolDescription, paramsSchema.shape, async (params = {}) => {
         try {
             logger.info(`Executing tool: ${toolName}`, params);
             const result = await toolHandler(mcpUnity, params);
@@ -40,15 +40,15 @@ export function createRunTestsTool(server, mcpUnity, logger) {
  * @returns A promise that resolves to the tool execution result
  * @throws McpUnityError if the request to Unity fails
  */
-async function toolHandler(mcpUnity, params) {
-    const { testMode = 'EditMode', testFilter, returnsOnlyFailures = true } = params;
+async function toolHandler(mcpUnity, params = {}) {
+    const { testMode = 'EditMode', testFilter = '', returnOnlyFailures = true } = params;
     // Create and wait for the test run
     const response = await mcpUnity.sendRequest({
         method: toolName,
         params: {
             testMode,
             testFilter,
-            returnsOnlyFailures
+            returnOnlyFailures
         }
     });
     // Process the test results
@@ -60,22 +60,12 @@ async function toolHandler(mcpUnity, params) {
     const testCount = response.testCount || 0;
     const passCount = response.passCount || 0;
     const failCount = response.failCount || 0;
-    const inconclusiveCount = response.inconclusiveCount || 0;
     const skipCount = response.skipCount || 0;
-    // Format the result message
-    let resultMessage = `${passCount}/${testCount} tests passed`;
-    if (testCount > 0 && passCount < testCount) {
-        resultMessage += `. Failed tests: ${testResults
-            .filter((r) => r.result !== 'Passed')
-            .filter((r) => !r.result.startsWith('Skipped'))
-            .map((r) => r.name)
-            .join(', ')}`;
-    }
     return {
         content: [
             {
                 type: 'text',
-                text: resultMessage || response.message
+                text: response.message
             },
             {
                 type: 'text',
@@ -83,7 +73,6 @@ async function toolHandler(mcpUnity, params) {
                     testCount,
                     passCount,
                     failCount,
-                    inconclusiveCount,
                     skipCount,
                     results: testResults
                 }, null, 2)
diff --git a/Server/src/tools/runTestsTool.ts b/Server/src/tools/runTestsTool.ts
index 2d510fea..90573198 100644
--- a/Server/src/tools/runTestsTool.ts
+++ b/Server/src/tools/runTestsTool.ts
@@ -11,7 +11,7 @@ const toolDescription = 'Runs Unity\'s Test Runner tests';
 const paramsSchema = z.object({
   testMode: z.string().optional().describe('The test mode to run (EditMode or PlayMode) - defaults to EditMode (optional)'),
   testFilter: z.string().optional().describe('The specific test filter to run (e.g. specific test name or namespace) (optional)'),
-  returnsOnlyFailures: z.boolean().optional().default(true).describe('Whether to show only failed tests in the results (optional)')
+  returnOnlyFailures: z.boolean().optional().default(true).describe('Whether to show only failed tests in the results (optional)')
 });
 
 /**
@@ -30,7 +30,7 @@ export function createRunTestsTool(server: McpServer, mcpUnity: McpUnity, logger
     toolName,
     toolDescription,
     paramsSchema.shape,
-    async (params: any) => {
+    async (params: any = {}) => {
       try {
         logger.info(`Executing tool: ${toolName}`, params);
         const result = await toolHandler(mcpUnity, params);
@@ -52,8 +52,12 @@ export function createRunTestsTool(server: McpServer, mcpUnity: McpUnity, logger
  * @returns A promise that resolves to the tool execution result
  * @throws McpUnityError if the request to Unity fails
  */
-async function toolHandler(mcpUnity: McpUnity, params: any): Promise<CallToolResult> {
-  const { testMode = 'EditMode', testFilter, returnsOnlyFailures = true } = params;
+async function toolHandler(mcpUnity: McpUnity, params: any = {}): Promise<CallToolResult> {
+  const {
+    testMode = 'EditMode',
+    testFilter = '',
+    returnOnlyFailures = true
+  } = params;
 
   // Create and wait for the test run
   const response = await mcpUnity.sendRequest({
@@ -61,7 +65,7 @@ async function toolHandler(mcpUnity: McpUnity, params: any): Promise<CallToolRes
     params: { 
       testMode,
       testFilter,
-      returnsOnlyFailures
+      returnOnlyFailures
     }
   });
   
@@ -78,24 +82,13 @@ async function toolHandler(mcpUnity: McpUnity, params: any): Promise<CallToolRes
   const testCount = response.testCount || 0;
   const passCount = response.passCount || 0;
   const failCount = response.failCount || 0;
-  const inconclusiveCount = response.inconclusiveCount || 0;
   const skipCount = response.skipCount || 0;
   
-  // Format the result message
-  let resultMessage = `${passCount}/${testCount} tests passed`;
-  if (testCount > 0 && passCount < testCount) {
-    resultMessage += `. Failed tests: ${testResults
-      .filter((r: any) => r.result !== 'Passed')
-      .filter((r: any) => !r.result.startsWith('Skipped'))
-      .map((r: any) => r.name)
-      .join(', ')}`;
-  }
-  
   return {
     content: [
       {
         type: 'text',
-        text: resultMessage || response.message
+        text: response.message
       },
       {
         type: 'text',
@@ -103,7 +96,6 @@ async function toolHandler(mcpUnity: McpUnity, params: any): Promise<CallToolRes
           testCount,
           passCount,
           failCount,
-          inconclusiveCount,
           skipCount,
           results: testResults
         }, null, 2)

From 6a10bf1fb9fd7251f7c20ea5a976e5ac4d326510 Mon Sep 17 00:00:00 2001
From: CoderGamester <game.gamester@gmail.com>
Date: Wed, 30 Apr 2025 01:51:49 +0300
Subject: [PATCH 17/17] Project tag to 1.0.2

---
 package.json | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/package.json b/package.json
index c4bf5bfb..54230587 100644
--- a/package.json
+++ b/package.json
@@ -2,7 +2,7 @@
   "name": "com.gamelovers.mcp-unity",
   "displayName": "MCP Unity Server",
   "author": "CoderGamester",
-  "version": "1.0.1",
+  "version": "1.0.2",
   "unity": "2022.3",
   "license": "MIT",
   "description": "The purpose of this package is to provide a MCP Unity Server for executing Unity operations and request Editor information from AI MCP enabled hosts",
