Skip to main content
Glama
Singtaa
by Singtaa
ResourceRegistry.cs7.18 kB
using System; using System.Collections.Generic; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using UnityEngine; using UnityEngine.SceneManagement; namespace UnityMcp { /// <summary> /// Registry and dispatcher for MCP resources. /// Resources provide read-only access to Unity state like console logs, hierarchy, etc. /// </summary> public static class ResourceRegistry { public delegate ResourceResult ResourceHandler(string uri, JObject args); static bool _init; static Dictionary<string, ResourceHandler> _handlers; public static int Count => _handlers?.Count ?? 0; public static void EnsureInitialized() { if (_init) return; _init = true; _handlers = new Dictionary<string, ResourceHandler>(StringComparer.Ordinal) { // Console logs ["unity.resource.console.logs"] = ReadConsoleLogs, // Scene hierarchy ["unity.resource.hierarchy"] = ReadHierarchy, // Test results ["unity.resource.tests.results"] = ReadTestResults, // Project files ["unity.resource.project.files"] = ReadProjectFiles, }; } public static void HandleResourceCall(McpTcpClient client, string id, string tool, JObject args) { EnsureInitialized(); ResourceResult result; try { var uri = args?.Value<string>("uri") ?? ""; if (string.IsNullOrEmpty(tool) || !_handlers.TryGetValue(tool, out var handler) || handler == null) { result = ResourceResultUtil.Error(uri, $"Unknown resource: {tool}"); } else { result = handler(uri, args ?? new JObject()) ?? ResourceResultUtil.Error(uri, "Null resource result"); } } catch (Exception e) { Debug.LogException(e); result = ResourceResultUtil.Error("", $"Resource read failed: {e.GetType().Name}: {e.Message}"); } try { client.SendResourceResponse(id, result); } catch (Exception e) { Debug.LogWarning($"[UnityMcp] failed sending resource response: {e.Message}"); } } public static IEnumerable<string> GetResourceNames() { EnsureInitialized(); return _handlers.Keys; } // MARK: Resource Handlers static ResourceResult ReadConsoleLogs(string uri, JObject args) { var maxEntries = args?.Value<int?>("maxEntries") ?? 500; maxEntries = Mathf.Clamp(maxEntries, 1, 5000); string logsText; if (ConsoleCapture.TryReadUnityConsole(maxEntries, out var text)) { logsText = text; } else { logsText = ConsoleCapture.GetFallbackText(maxEntries); } return ResourceResultUtil.Text("unity://console/logs", logsText); } static ResourceResult ReadHierarchy(string uri, JObject args) { var sceneName = args?.Value<string>("scene"); var list = new List<object>(4096); for (var si = 0; si < SceneManager.sceneCount; si++) { var scene = SceneManager.GetSceneAt(si); if (!scene.isLoaded) continue; // Filter by scene name if provided if (!string.IsNullOrEmpty(sceneName) && !scene.name.Equals(sceneName, StringComparison.OrdinalIgnoreCase)) { continue; } var roots = scene.GetRootGameObjects(); foreach (var go in roots) { TraverseHierarchy(go.transform, scene.name, list); } } var resultUri = string.IsNullOrEmpty(sceneName) ? "unity://hierarchy" : $"unity://hierarchy/{sceneName}"; return ResourceResultUtil.Json(resultUri, JsonConvert.SerializeObject(new { sceneCount = SceneManager.sceneCount, objectCount = list.Count, hierarchy = list }, Formatting.Indented)); } static void TraverseHierarchy(Transform t, string sceneName, List<object> list) { if (t == null) return; list.Add(new { scene = sceneName, path = GameObjectResolver.GetPath(t.gameObject), name = t.name, activeSelf = t.gameObject.activeSelf, activeInHierarchy = t.gameObject.activeInHierarchy, instanceId = t.gameObject.GetInstanceID(), childCount = t.childCount }); for (var i = 0; i < t.childCount; i++) { TraverseHierarchy(t.GetChild(i), sceneName, list); } } static ResourceResult ReadTestResults(string uri, JObject args) { // Get test results from Tools_Test var result = Tools_Test.GetResults(args); return new ResourceResult { contents = new[] { new ResourceContent { uri = "unity://tests/results", mimeType = "application/json", text = result.content[0].text } }, isError = result.isError }; } static ResourceResult ReadProjectFiles(string uri, JObject args) { var path = args?.Value<string>("path"); var ignore = GitIgnoreCache.Get(); if (!string.IsNullOrEmpty(path)) { // Read specific file if (!ProjectPaths.TryResolveAllowedPath(path, isDirectory: false, ignore, out var fullPath, out var error)) { return ResourceResultUtil.Error($"unity://project/files/{path}", error); } try { var bytes = System.IO.File.ReadAllBytes(fullPath); if (bytes.Length > 1024 * 1024) { return ResourceResultUtil.Error($"unity://project/files/{path}", "File too large (>1MB)"); } var text = System.Text.Encoding.UTF8.GetString(bytes); return ResourceResultUtil.Text($"unity://project/files/{path}", text); } catch (Exception e) { return ResourceResultUtil.Error($"unity://project/files/{path}", e.Message); } } // List all files var entries = new List<object>(4096); foreach (var e in ProjectPaths.EnumerateProjectEntries(ignore)) { entries.Add(new { path = e.path, kind = e.kind }); } return ResourceResultUtil.Json("unity://project/files", JsonConvert.SerializeObject(new { projectRoot = ProjectPaths.ProjectRoot, fileCount = entries.Count, files = entries }, Formatting.Indented)); } } }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/Singtaa/UnityMCP'

If you have feedback or need assistance with the MCP directory API, please join our Discord server