Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit c98534f

Browse filesBrowse files
committed
Fixed the issue preventing GetTestsResource to work
Added capacity for McpResources to work asynchronously
1 parent 73230c6 commit c98534f
Copy full SHA for c98534f

File tree

Expand file treeCollapse file tree

7 files changed

+107
-39
lines changed
Filter options
Expand file treeCollapse file tree

7 files changed

+107
-39
lines changed

‎.windsurfrules

Copy file name to clipboardExpand all lines: .windsurfrules
+12-1Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,10 +55,21 @@
5555

5656
- MCP Protocol: https://modelcontextprotocol.io
5757
- TypeScript SDK: https://github.com/modelcontextprotocol/typescript-sdk
58+
- Inspector: https://github.com/modelcontextprotocol/inspector
5859

5960
## 9. Conventions
6061

6162
- Use WebSockets for all cross-process communication.
6263
- Follow the MCP protocol for all tool/resource definitions.
6364
- All new tools/resources should be registered in both Unity and Node.js server entry points.
64-
- Follow Conventional Commits for all commit messages.
65+
- Follow Conventional Commits for all commit messages.
66+
67+
## 10. Debugging with MCP Inspector
68+
69+
To debug the MCP Node.js server using the Model Context Protocol Inspector, run the following command from the project root:
70+
71+
```shell
72+
npx @modelcontextprotocol/inspector node Server/build/index.js
73+
```
74+
75+
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).

‎Editor/Resources/GetTestsResource.cs

Copy file name to clipboard
+10-17Lines changed: 10 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,7 @@
1-
using System;
2-
using System.Linq;
31
using System.Collections.Generic;
42
using System.Threading.Tasks;
5-
using UnityEngine;
6-
using Newtonsoft.Json.Linq;
7-
using McpUnity.Unity;
83
using McpUnity.Services;
4+
using Newtonsoft.Json.Linq;
95

106
namespace McpUnity.Resources
117
{
@@ -24,25 +20,23 @@ public GetTestsResource(ITestRunnerService testRunnerService)
2420
Name = "get_tests";
2521
Description = "Gets available tests from Unity Test Runner";
2622
Uri = "unity://tests/{testMode}";
27-
23+
IsAsync = true;
2824
_testRunnerService = testRunnerService;
2925
}
3026

3127
/// <summary>
32-
/// Fetch tests based on provided parameters
28+
/// Asynchronously fetch tests based on provided parameters
3329
/// </summary>
3430
/// <param name="parameters">Resource parameters as a JObject</param>
35-
public override JObject Fetch(JObject parameters)
31+
/// <param name="tcs">TaskCompletionSource to set the result or exception</param>
32+
public override async void FetchAsync(JObject parameters, TaskCompletionSource<JObject> tcs)
3633
{
3734
// Get filter parameters
3835
string testModeFilter = parameters["testMode"]?.ToObject<string>();
39-
40-
// Get all tests from the service
41-
var allTests = _testRunnerService.GetAllTests(testModeFilter);
42-
43-
// Create the results array
36+
List<TestItemInfo> allTests = await _testRunnerService.GetAllTestsAsync(testModeFilter);
4437
var results = new JArray();
45-
foreach (var test in allTests)
38+
39+
foreach (TestItemInfo test in allTests)
4640
{
4741
results.Add(new JObject
4842
{
@@ -54,13 +48,12 @@ public override JObject Fetch(JObject parameters)
5448
});
5549
}
5650

57-
// Return the results
58-
return new JObject
51+
tcs.SetResult(new JObject
5952
{
6053
["success"] = true,
6154
["message"] = $"Retrieved {allTests.Count} tests",
6255
["tests"] = results
63-
};
56+
});
6457
}
6558
}
6659
}

‎Editor/Resources/McpResourceBase.cs

Copy file name to clipboardExpand all lines: Editor/Resources/McpResourceBase.cs
+28-4Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System;
22
using System.Collections.Generic;
3+
using System.Threading.Tasks;
34
using UnityEngine;
45
using Newtonsoft.Json.Linq;
56

@@ -31,10 +32,33 @@ public abstract class McpResourceBase
3132
public bool IsEnabled { get; protected set; } = true;
3233

3334
/// <summary>
34-
/// Fetch the resource data with the provided parameters
35+
/// Indicates if the fetch operation is asynchronous.
3536
/// </summary>
36-
/// <param name="parameters">Resource parameters as a JObject</param>
37-
/// <returns>The result of the resource fetch as a JObject</returns>
38-
public abstract JObject Fetch(JObject parameters);
37+
public bool IsAsync { get; protected set; } = false;
38+
39+
/// <summary>
40+
/// Synchronously fetch the resource data.
41+
/// Implement this for synchronous resources (IsAsync = false).
42+
/// </summary>
43+
/// <param name="parameters">Parameters extracted from the URI or query.</param>
44+
/// <returns>Result as JObject.</returns>
45+
public virtual JObject Fetch(JObject parameters)
46+
{
47+
// Default implementation throws, forcing sync resources to override.
48+
throw new NotImplementedException($"Synchronous Fetch not implemented for resource '{Name}'. Mark IsAsync=true and implement FetchAsync, or override Fetch.");
49+
}
50+
51+
/// <summary>
52+
/// Asynchronously fetch the resource data.
53+
/// Implement this for asynchronous resources (IsAsync = true).
54+
/// The implementation MUST eventually call tcs.SetResult() or tcs.SetException().
55+
/// </summary>
56+
/// <param name="parameters">Parameters extracted from the URI or query.</param>
57+
/// <param name="tcs">TaskCompletionSource to set the result on.</param>
58+
public virtual void FetchAsync(JObject parameters, TaskCompletionSource<JObject> tcs)
59+
{
60+
// Default implementation throws, forcing async resources to override.
61+
tcs.SetException(new NotImplementedException($"Asynchronous FetchAsync not implemented for resource '{Name}'. Mark IsAsync=false and implement Fetch, or override FetchAsync."));
62+
}
3963
}
4064
}

‎Editor/Services/ITestRunnerService.cs

Copy file name to clipboardExpand all lines: Editor/Services/ITestRunnerService.cs
+3-3Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,13 @@ public interface ITestRunnerService
1414
/// Get the TestRunnerApi instance
1515
/// </summary>
1616
TestRunnerApi TestRunnerApi { get; }
17-
17+
1818
/// <summary>
19-
/// Get a list of available tests, optionally filtered by test mode
19+
/// Async retrieval of all tests using TestRunnerApi callbacks
2020
/// </summary>
2121
/// <param name="testMode">Optional test mode filter (EditMode, PlayMode, or empty for all)</param>
2222
/// <returns>List of test items matching the specified test mode, or all tests if no mode specified</returns>
23-
List<TestItemInfo> GetAllTests(string testMode = "");
23+
Task<List<TestItemInfo>> GetAllTestsAsync(string testMode = "");
2424

2525
/// <summary>
2626
/// Execute tests with the provided parameters

‎Editor/Services/TestRunnerService.cs

Copy file name to clipboardExpand all lines: Editor/Services/TestRunnerService.cs
+41-10Lines changed: 41 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
using System.Threading.Tasks;
55
using McpUnity.Unity;
66
using UnityEngine;
7+
using UnityEditor;
78
using UnityEditor.TestTools.TestRunner.Api;
89
using Newtonsoft.Json.Linq;
910

@@ -28,29 +29,59 @@ public TestRunnerService()
2829
{
2930
_testRunnerApi = ScriptableObject.CreateInstance<TestRunnerApi>();
3031
}
31-
32+
33+
[MenuItem("Tools/MCP Unity/Debug call path")]
34+
public static async void DebugCallGetAllTests()
35+
{
36+
var service = new TestRunnerService();
37+
var tests = await service.GetAllTestsAsync();
38+
Debug.Log($"Retrieved {tests.Count} tests:");
39+
foreach (var t in tests)
40+
Debug.Log($"Test: {t.FullName} ({t.TestMode}) - State: {t.RunState}");
41+
}
42+
3243
/// <summary>
33-
/// Get a list of available tests, optionally filtered by test mode
44+
/// Async retrieval of all tests using TestRunnerApi callbacks
3445
/// </summary>
3546
/// <param name="testMode">Optional test mode filter (EditMode, PlayMode, or empty for all)</param>
3647
/// <returns>List of test items matching the specified test mode, or all tests if no mode specified</returns>
37-
public List<TestItemInfo> GetAllTests(string testMode = "")
48+
public async Task<List<TestItemInfo>> GetAllTestsAsync(string testMode = "")
3849
{
3950
var tests = new List<TestItemInfo>();
40-
41-
// Check if we need to retrieve EditMode tests
51+
var tcs = new TaskCompletionSource<bool>();
52+
int pending = 0;
53+
4254
if (string.IsNullOrEmpty(testMode) || testMode.Equals("EditMode", StringComparison.OrdinalIgnoreCase))
4355
{
44-
_testRunnerApi.RetrieveTestList(TestMode.EditMode, adaptor => CollectTestItems(adaptor, tests));
56+
Interlocked.Increment(ref pending);
57+
_testRunnerApi.RetrieveTestList(TestMode.EditMode, adaptor =>
58+
{
59+
CollectTestItems(adaptor, tests);
60+
CheckDone();
61+
});
4562
}
46-
47-
// Check if we need to retrieve PlayMode tests
4863
if (string.IsNullOrEmpty(testMode) || testMode.Equals("PlayMode", StringComparison.OrdinalIgnoreCase))
4964
{
50-
_testRunnerApi.RetrieveTestList(TestMode.PlayMode, adaptor => CollectTestItems(adaptor, tests));
65+
Interlocked.Increment(ref pending);
66+
_testRunnerApi.RetrieveTestList(TestMode.PlayMode, adaptor =>
67+
{
68+
CollectTestItems(adaptor, tests);
69+
CheckDone();
70+
});
5171
}
52-
72+
73+
if (pending == 0)
74+
tcs.SetResult(true);
75+
76+
await tcs.Task;
77+
5378
return tests;
79+
80+
void CheckDone()
81+
{
82+
if (Interlocked.Decrement(ref pending) == 0)
83+
tcs.TrySetResult(true);
84+
}
5485
}
5586

5687
/// <summary>

‎Editor/Tools/RunTestsTool.cs

Copy file name to clipboardExpand all lines: Editor/Tools/RunTestsTool.cs
+3Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,12 +145,15 @@ public void RunFinished(ITestResultAdaptor result)
145145
// Create test results summary
146146
var summary = new JObject
147147
{
148+
["name"] = result.Name,
149+
["fullName"] = result.FullName,
148150
["testCount"] = result.PassCount + result.FailCount + result.SkipCount + result.InconclusiveCount,
149151
["passCount"] = result.PassCount,
150152
["failCount"] = result.FailCount,
151153
["skipCount"] = result.SkipCount,
152154
["inconclusiveCount"] = result.InconclusiveCount,
153155
["duration"] = result.Duration,
156+
["resultState"] = result.ResultState,
154157
["success"] = result.ResultState == "Passed",
155158
["status"] = "completed",
156159
["message"] = $"Test run completed: {result.Test.Name} - {result.ResultState}"

‎Editor/UnityBridge/McpUnitySocketHandler.cs

Copy file name to clipboardExpand all lines: Editor/UnityBridge/McpUnitySocketHandler.cs
+10-4Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -177,18 +177,24 @@ private IEnumerator FetchResourceCoroutine(McpResourceBase resource, JObject par
177177
{
178178
try
179179
{
180-
var result = resource.Fetch(parameters);
181-
tcs.SetResult(result);
180+
if (resource.IsAsync)
181+
{
182+
resource.FetchAsync(parameters, tcs);
183+
}
184+
else
185+
{
186+
var result = resource.Fetch(parameters);
187+
tcs.SetResult(result);
188+
}
182189
}
183190
catch (Exception ex)
184191
{
185-
McpLogger.LogError($"Error fetching resource {resource.Name}: {ex.Message}");
192+
McpLogger.LogError($"Error fetching resource {resource.Name}: {ex}");
186193
tcs.SetResult(CreateErrorResponse(
187194
$"Failed to fetch resource {resource.Name}: {ex.Message}",
188195
"resource_fetch_error"
189196
));
190197
}
191-
192198
yield return null;
193199
}
194200

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.