Skip to content

Commit 193bcfc

Browse files
authored
Merge pull request #1000 from Scriptwonder/feat/manage-profiler
feat: add manage_profiler tool with session control, counters, memory snapshots, and Frame Debugger
2 parents 6907bad + 864d37e commit 193bcfc

34 files changed

Lines changed: 1832 additions & 56 deletions

.claude/skills/unity-mcp-skill/references/tools-reference.md

Lines changed: 204 additions & 39 deletions
Large diffs are not rendered by default.

CLAUDE.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ from services.registry import mcp_for_unity_tool
7070

7171
@mcp_for_unity_tool(
7272
description="Does something in Unity.",
73-
group="core", # core (default), vfx, animation, ui, scripting_ext, testing, probuilder
73+
group="core", # core (default), vfx, animation, ui, scripting_ext, testing, probuilder, profiling, docs
7474
)
7575
async def manage_something(
7676
ctx: Context,

MCPForUnity/Editor/Helpers/McpLogRecord.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,9 @@ namespace MCPForUnity.Editor.Helpers
1010
{
1111
internal static class McpLogRecord
1212
{
13-
private static readonly string LogPath = Path.Combine(Application.dataPath, "mcp.log");
14-
private static readonly string ErrorLogPath = Path.Combine(Application.dataPath, "mcpError.log");
13+
private static readonly string LogDir = Path.Combine(Application.dataPath, "UnityMCP", "Log");
14+
private static readonly string LogPath = Path.Combine(LogDir, "mcp.log");
15+
private static readonly string ErrorLogPath = Path.Combine(LogDir, "mcpError.log");
1516
private const long MaxLogSizeBytes = 1024 * 1024; // 1 MB
1617
private static bool _sessionStarted;
1718
private static readonly object _logLock = new();
@@ -79,6 +80,7 @@ internal static void Log(string commandType, JObject parameters, string type, st
7980

8081
private static void RotateAndAppend(string path, string line)
8182
{
83+
Directory.CreateDirectory(LogDir);
8284
RotateIfNeeded(path);
8385
File.AppendAllText(path, line + Environment.NewLine);
8486
}

MCPForUnity/Editor/Services/IPackageUpdateService.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,12 @@ public interface IPackageUpdateService
2424
/// </summary>
2525
UpdateCheckResult FetchAndCompare(string currentVersion);
2626

27+
/// <summary>
28+
/// Performs only the network fetch and version comparison using pre-computed installation info.
29+
/// Use this overload when calling from a background thread to avoid main-thread-only API calls.
30+
/// </summary>
31+
UpdateCheckResult FetchAndCompare(string currentVersion, bool isGitInstallation, string gitBranch);
32+
2733
/// <summary>
2834
/// Caches a successful fetch result in EditorPrefs. Must be called from the main thread.
2935
/// </summary>
@@ -43,6 +49,12 @@ public interface IPackageUpdateService
4349
/// <returns>True if installed via Git, false if Asset Store or unknown</returns>
4450
bool IsGitInstallation();
4551

52+
/// <summary>
53+
/// Determines the Git branch to check for updates (e.g. "main" or "beta").
54+
/// Must be called from the main thread (uses Unity PackageManager APIs).
55+
/// </summary>
56+
string GetGitUpdateBranch(string currentVersion);
57+
4658
/// <summary>
4759
/// Clears the cached update check data, forcing a fresh check on next request
4860
/// </summary>

MCPForUnity/Editor/Services/PackageUpdateService.cs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,12 @@ public UpdateCheckResult FetchAndCompare(string currentVersion)
118118
{
119119
bool isGitInstallation = IsGitInstallation();
120120
string gitBranch = isGitInstallation ? GetGitUpdateBranch(currentVersion) : "main";
121+
return FetchAndCompare(currentVersion, isGitInstallation, gitBranch);
122+
}
121123

124+
/// <inheritdoc/>
125+
public UpdateCheckResult FetchAndCompare(string currentVersion, bool isGitInstallation, string gitBranch)
126+
{
122127
string latestVersion = isGitInstallation
123128
? FetchLatestVersionFromGitHub(gitBranch)
124129
: FetchLatestVersionFromAssetStoreJson();
@@ -279,7 +284,8 @@ private static bool IsPreReleaseVersion(string version)
279284
return version.IndexOf('-', StringComparison.Ordinal) >= 0;
280285
}
281286

282-
private static string GetGitUpdateBranch(string currentVersion)
287+
/// <inheritdoc/>
288+
public string GetGitUpdateBranch(string currentVersion)
283289
{
284290
try
285291
{

MCPForUnity/Editor/Tools/Graphics/RenderingStatsOps.cs

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
using Unity.Profiling;
99
using Unity.Profiling.LowLevel.Unsafe;
1010
using UnityEngine.Profiling;
11+
using UProfiler = UnityEngine.Profiling.Profiler;
1112

1213
namespace MCPForUnity.Editor.Tools.Graphics
1314
{
@@ -126,12 +127,12 @@ internal static object GetMemory(JObject @params)
126127
{
127128
var data = new Dictionary<string, object>
128129
{
129-
["totalAllocatedMB"] = Math.Round(Profiler.GetTotalAllocatedMemoryLong() / (1024.0 * 1024.0), 2),
130-
["totalReservedMB"] = Math.Round(Profiler.GetTotalReservedMemoryLong() / (1024.0 * 1024.0), 2),
131-
["totalUnusedReservedMB"] = Math.Round(Profiler.GetTotalUnusedReservedMemoryLong() / (1024.0 * 1024.0), 2),
132-
["monoUsedMB"] = Math.Round(Profiler.GetMonoUsedSizeLong() / (1024.0 * 1024.0), 2),
133-
["monoHeapMB"] = Math.Round(Profiler.GetMonoHeapSizeLong() / (1024.0 * 1024.0), 2),
134-
["graphicsDriverMB"] = Math.Round(Profiler.GetAllocatedMemoryForGraphicsDriver() / (1024.0 * 1024.0), 2),
130+
["totalAllocatedMB"] = Math.Round(UProfiler.GetTotalAllocatedMemoryLong() / (1024.0 * 1024.0), 2),
131+
["totalReservedMB"] = Math.Round(UProfiler.GetTotalReservedMemoryLong() / (1024.0 * 1024.0), 2),
132+
["totalUnusedReservedMB"] = Math.Round(UProfiler.GetTotalUnusedReservedMemoryLong() / (1024.0 * 1024.0), 2),
133+
["monoUsedMB"] = Math.Round(UProfiler.GetMonoUsedSizeLong() / (1024.0 * 1024.0), 2),
134+
["monoHeapMB"] = Math.Round(UProfiler.GetMonoHeapSizeLong() / (1024.0 * 1024.0), 2),
135+
["graphicsDriverMB"] = Math.Round(UProfiler.GetAllocatedMemoryForGraphicsDriver() / (1024.0 * 1024.0), 2),
135136
};
136137

137138
return new

MCPForUnity/Editor/Tools/Profiler.meta

Lines changed: 8 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
using System;
2+
using System.Threading.Tasks;
3+
using MCPForUnity.Editor.Helpers;
4+
using Newtonsoft.Json.Linq;
5+
6+
namespace MCPForUnity.Editor.Tools.Profiler
7+
{
8+
[McpForUnityTool("manage_profiler", AutoRegister = false, Group = "profiling")]
9+
public static class ManageProfiler
10+
{
11+
public static async Task<object> HandleCommand(JObject @params)
12+
{
13+
if (@params == null)
14+
return new ErrorResponse("Parameters cannot be null.");
15+
16+
var p = new ToolParams(@params);
17+
string action = p.Get("action")?.ToLowerInvariant();
18+
19+
if (string.IsNullOrEmpty(action))
20+
return new ErrorResponse("'action' parameter is required.");
21+
22+
try
23+
{
24+
switch (action)
25+
{
26+
// Session
27+
case "profiler_start":
28+
return SessionOps.Start(@params);
29+
case "profiler_stop":
30+
return SessionOps.Stop(@params);
31+
case "profiler_status":
32+
return SessionOps.Status(@params);
33+
case "profiler_set_areas":
34+
return SessionOps.SetAreas(@params);
35+
36+
// Counters
37+
case "get_frame_timing":
38+
return FrameTimingOps.GetFrameTiming(@params);
39+
case "get_counters":
40+
return await CounterOps.GetCountersAsync(@params);
41+
case "get_object_memory":
42+
return ObjectMemoryOps.GetObjectMemory(@params);
43+
44+
// Memory Snapshot
45+
case "memory_take_snapshot":
46+
return await MemorySnapshotOps.TakeSnapshotAsync(@params);
47+
case "memory_list_snapshots":
48+
return MemorySnapshotOps.ListSnapshots(@params);
49+
case "memory_compare_snapshots":
50+
return MemorySnapshotOps.CompareSnapshots(@params);
51+
52+
// Frame Debugger
53+
case "frame_debugger_enable":
54+
return FrameDebuggerOps.Enable(@params);
55+
case "frame_debugger_disable":
56+
return FrameDebuggerOps.Disable(@params);
57+
case "frame_debugger_get_events":
58+
return FrameDebuggerOps.GetEvents(@params);
59+
60+
// Utility
61+
case "ping":
62+
return new SuccessResponse("manage_profiler is available.", new
63+
{
64+
tool = "manage_profiler",
65+
group = "profiling"
66+
});
67+
68+
default:
69+
return new ErrorResponse(
70+
$"Unknown action: '{action}'. Valid actions: "
71+
+ "profiler_start, profiler_stop, profiler_status, profiler_set_areas, "
72+
+ "get_frame_timing, get_counters, get_object_memory, "
73+
+ "memory_take_snapshot, memory_list_snapshots, memory_compare_snapshots, "
74+
+ "frame_debugger_enable, frame_debugger_disable, frame_debugger_get_events, "
75+
+ "ping.");
76+
}
77+
}
78+
catch (Exception ex)
79+
{
80+
McpLog.Error($"[ManageProfiler] Action '{action}' failed: {ex}");
81+
return new ErrorResponse($"Error in action '{action}': {ex.Message}");
82+
}
83+
}
84+
}
85+
}

MCPForUnity/Editor/Tools/Profiler/ManageProfiler.cs.meta

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

MCPForUnity/Editor/Tools/Profiler/Operations.meta

Lines changed: 8 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)