Skip to main content
Glama

Unity Editor MCP Server

using System; using System.Reflection; using McpUnity.Unity; using McpUnity.Utils; using UnityEngine; using UnityEditor; using Newtonsoft.Json.Linq; namespace McpUnity.Tools { /// <summary> /// Tool for updating component data in the Unity Editor /// </summary> public class UpdateComponentTool : McpToolBase { public UpdateComponentTool() { Name = "update_component"; Description = "Updates component fields on a GameObject or adds it to the GameObject if it does not contain the component"; } /// <summary> /// Execute the UpdateComponent tool with the provided parameters synchronously /// </summary> /// <param name="parameters">Tool parameters as a JObject</param> public override JObject Execute(JObject parameters) { // Extract parameters int? instanceId = parameters["instanceId"]?.ToObject<int?>(); string objectPath = parameters["objectPath"]?.ToObject<string>(); string componentName = parameters["componentName"]?.ToObject<string>(); JObject componentData = parameters["componentData"] as JObject; // Validate parameters - require either instanceId or objectPath if (!instanceId.HasValue && string.IsNullOrEmpty(objectPath)) { return McpUnitySocketHandler.CreateErrorResponse( "Either 'instanceId' or 'objectPath' must be provided", "validation_error" ); } if (string.IsNullOrEmpty(componentName)) { return McpUnitySocketHandler.CreateErrorResponse( "Required parameter 'componentName' not provided", "validation_error" ); } // Find the GameObject by instance ID or path GameObject gameObject = null; string identifier = "unknown"; if (instanceId.HasValue) { gameObject = EditorUtility.InstanceIDToObject(instanceId.Value) as GameObject; identifier = $"ID {instanceId.Value}"; } else { // Find by path gameObject = GameObject.Find(objectPath); identifier = $"path '{objectPath}'"; if (gameObject == null) { // Try to find using the Unity Scene hierarchy path gameObject = FindGameObjectByPath(objectPath); } } if (gameObject == null) { return McpUnitySocketHandler.CreateErrorResponse( $"GameObject with path '{objectPath}' or instance ID {instanceId} not found", "not_found_error" ); } McpLogger.LogInfo($"[MCP Unity] Updating component '{componentName}' on GameObject '{gameObject.name}' (found by {identifier})"); // Try to find the component by name Component component = gameObject.GetComponent(componentName); // If component not found, try to add it if (component == null) { Type componentType = FindComponentType(componentName); if (componentType == null) { return McpUnitySocketHandler.CreateErrorResponse( $"Component type '{componentName}' not found in Unity", "component_error" ); } component = Undo.AddComponent(gameObject, componentType); // Ensure changes are saved EditorUtility.SetDirty(gameObject); if (PrefabUtility.IsPartOfAnyPrefab(gameObject)) { PrefabUtility.RecordPrefabInstancePropertyModifications(component); } McpLogger.LogInfo($"[MCP Unity] Added component '{componentName}' to GameObject '{gameObject.name}'"); } // Update component fields if (componentData != null && componentData.Count > 0) { bool success = UpdateComponentData(component, componentData, out string errorMessage); // If update failed, return error if (!success) { return McpUnitySocketHandler.CreateErrorResponse(errorMessage, "update_error"); } // Ensure field changes are saved EditorUtility.SetDirty(gameObject); if (PrefabUtility.IsPartOfAnyPrefab(gameObject)) { PrefabUtility.RecordPrefabInstancePropertyModifications(component); } } // Create the response return new JObject { ["success"] = true, ["type"] = "text", ["message"] = $"Successfully updated component '{componentName}' on GameObject '{gameObject.name}'" }; } /// <summary> /// Find a GameObject by its hierarchy path /// </summary> /// <param name="path">The path to the GameObject (e.g. "Canvas/Panel/Button")</param> /// <returns>The GameObject if found, null otherwise</returns> private GameObject FindGameObjectByPath(string path) { // Split the path by '/' string[] pathParts = path.Split('/'); GameObject[] rootGameObjects = UnityEngine.SceneManagement.SceneManager.GetActiveScene().GetRootGameObjects(); // If the path is empty, return null if (pathParts.Length == 0) { return null; } // Search through all root GameObjects in all scenes foreach (GameObject rootObj in rootGameObjects) { if (rootObj.name == pathParts[0]) { // Found the root object, now traverse down the path GameObject current = rootObj; // Start from index 1 since we've already matched the root for (int i = 1; i < pathParts.Length; i++) { Transform child = current.transform.Find(pathParts[i]); if (child == null) { // Path segment not found return null; } // Move to the next level current = child.gameObject; } // If we got here, we found the full path return current; } } // Not found return null; } /// <summary> /// Find a component type by name /// </summary> /// <param name="componentName">The name of the component type</param> /// <returns>The component type, or null if not found</returns> private Type FindComponentType(string componentName) { // First try direct match Type type = Type.GetType(componentName); if (type != null && typeof(Component).IsAssignableFrom(type)) { return type; } // Try common Unity namespaces string[] commonNamespaces = new string[] { "UnityEngine", "UnityEngine.UI", "UnityEngine.EventSystems", "UnityEngine.Animations", "UnityEngine.Rendering", "TMPro" }; foreach (string ns in commonNamespaces) { type = Type.GetType($"{ns}.{componentName}, UnityEngine"); if (type != null && typeof(Component).IsAssignableFrom(type)) { return type; } } // Try assemblies search foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies()) { try { foreach (Type t in assembly.GetTypes()) { if (t.Name == componentName && typeof(Component).IsAssignableFrom(t)) { return t; } } } catch (Exception) { // Some assemblies might throw exceptions when getting types continue; } } return null; } /// <summary> /// Update component data based on the provided JObject /// </summary> /// <param name="component">The component to update</param> /// <param name="componentData">The data to apply to the component</param> /// <returns>True if the component was updated successfully</returns> private bool UpdateComponentData(Component component, JObject componentData, out string errorMessage) { errorMessage = ""; if (component == null || componentData == null) { errorMessage = "Component or component data is null"; return false; } Type componentType = component.GetType(); bool fullSuccess = true; // Record object for undo Undo.RecordObject(component, $"Update {componentType.Name} fields"); // Process each field or property in the component data foreach (var property in componentData.Properties()) { string fieldName = property.Name; JToken fieldValue = property.Value; // Skip null values if (string.IsNullOrEmpty(fieldName) || fieldValue.Type == JTokenType.Null) { continue; } // Try to update field FieldInfo fieldInfo = componentType.GetField(fieldName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); if (fieldInfo != null) { object value = ConvertJTokenToValue(fieldValue, fieldInfo.FieldType); fieldInfo.SetValue(component, value); continue; } // Try to update property if not found as a field PropertyInfo propertyInfo = componentType.GetProperty(fieldName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); if (propertyInfo != null) { object value = ConvertJTokenToValue(fieldValue, propertyInfo.PropertyType); propertyInfo.SetValue(component, value); continue; } fullSuccess = false; errorMessage = $"Field or Property with name '{fieldName}' not found on component '{componentType.Name}'"; } return fullSuccess; } /// <summary> /// Convert a JToken to a value of the specified type /// </summary> /// <param name="token">The JToken to convert</param> /// <param name="targetType">The target type to convert to</param> /// <returns>The converted value</returns> private object ConvertJTokenToValue(JToken token, Type targetType) { if (token == null) { return null; } // Handle Unity Vector types if (targetType == typeof(Vector2) && token.Type == JTokenType.Object) { JObject vector = (JObject)token; return new Vector2( vector["x"]?.ToObject<float>() ?? 0f, vector["y"]?.ToObject<float>() ?? 0f ); } if (targetType == typeof(Vector3) && token.Type == JTokenType.Object) { JObject vector = (JObject)token; return new Vector3( vector["x"]?.ToObject<float>() ?? 0f, vector["y"]?.ToObject<float>() ?? 0f, vector["z"]?.ToObject<float>() ?? 0f ); } if (targetType == typeof(Vector4) && token.Type == JTokenType.Object) { JObject vector = (JObject)token; return new Vector4( vector["x"]?.ToObject<float>() ?? 0f, vector["y"]?.ToObject<float>() ?? 0f, vector["z"]?.ToObject<float>() ?? 0f, vector["w"]?.ToObject<float>() ?? 0f ); } if (targetType == typeof(Quaternion) && token.Type == JTokenType.Object) { JObject quaternion = (JObject)token; return new Quaternion( quaternion["x"]?.ToObject<float>() ?? 0f, quaternion["y"]?.ToObject<float>() ?? 0f, quaternion["z"]?.ToObject<float>() ?? 0f, quaternion["w"]?.ToObject<float>() ?? 1f ); } if (targetType == typeof(Color) && token.Type == JTokenType.Object) { JObject color = (JObject)token; return new Color( color["r"]?.ToObject<float>() ?? 0f, color["g"]?.ToObject<float>() ?? 0f, color["b"]?.ToObject<float>() ?? 0f, color["a"]?.ToObject<float>() ?? 1f ); } if (targetType == typeof(Bounds) && token.Type == JTokenType.Object) { JObject bounds = (JObject)token; Vector3 center = bounds["center"]?.ToObject<Vector3>() ?? Vector3.zero; Vector3 size = bounds["size"]?.ToObject<Vector3>() ?? Vector3.one; return new Bounds(center, size); } if (targetType == typeof(Rect) && token.Type == JTokenType.Object) { JObject rect = (JObject)token; return new Rect( rect["x"]?.ToObject<float>() ?? 0f, rect["y"]?.ToObject<float>() ?? 0f, rect["width"]?.ToObject<float>() ?? 0f, rect["height"]?.ToObject<float>() ?? 0f ); } // Handle UnityEngine.Object types; if (targetType == typeof(UnityEngine.Object)) { return token.ToObject<UnityEngine.Object>(); } // Handle enum types if (targetType.IsEnum) { // If JToken is a string, try to parse as enum name if (token.Type == JTokenType.String) { string enumName = token.ToObject<string>(); if (Enum.TryParse(targetType, enumName, true, out object result)) { return result; } // If parsing fails, try to convert numeric value if (int.TryParse(enumName, out int enumValue)) { return Enum.ToObject(targetType, enumValue); } } // If JToken is a number, convert directly to enum else if (token.Type == JTokenType.Integer) { return Enum.ToObject(targetType, token.ToObject<int>()); } } // For other types, use JToken's ToObject method try { return token.ToObject(targetType); } catch (Exception ex) { McpLogger.LogError($"[MCP Unity] Error converting value to type {targetType.Name}: {ex.Message}"); return null; } } } }

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/CoderGamester/mcp-unity'

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