Created
June 28, 2026 06:29
-
-
Save adammyhre/df4a1d98b131b1ef4df30a787a532e40 to your computer and use it in GitHub Desktop.
Example Custom MCP Tool for Unity
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| using Unity.AI.MCP.Editor.Helpers; | |
| using Unity.AI.MCP.Editor.ToolRegistry; | |
| using UnityEditor; | |
| using UnityEngine; | |
| using UnityEngine.Rendering; | |
| public enum MaterialConversionScope { All, Selected } | |
| public class ConvertBuiltinMaterialsParams { | |
| [McpDescription( | |
| "Which materials to convert: 'All' runs the project-wide upgrade; 'Selected' converts only selected Material assets.", | |
| Required = true, | |
| EnumType = typeof(MaterialConversionScope))] | |
| public string Scope { get; set; } = nameof(MaterialConversionScope.All); | |
| } | |
| public static class ConvertBuiltinMaterialsTool { | |
| public const string ToolName = "Unity.ConvertBuiltinMaterials"; | |
| public const string Description = | |
| "Converts Built-in Render Pipeline materials to the active Scriptable Render Pipeline (URP or HDRP). " + | |
| "Use Scope='All' for a project-wide upgrade, or Scope='Selected' when Material assets are selected in the Project window."; | |
| const string MenuPathAll = "Edit/Rendering/Materials/Convert All Built-In Materials to Current SRP"; | |
| const string MenuPathSelected = "Edit/Rendering/Materials/Convert Selected Built-In Materials to Current SRP"; | |
| [McpTool(ToolName, Description, Groups = new[] { "assets", "rendering" }, EnabledByDefault = true)] | |
| public static object Handle(ConvertBuiltinMaterialsParams parameters) { | |
| if (parameters == null) | |
| return Response.Error("INVALID_PARAMETERS", new { reason = "Parameters object was null." }); | |
| if (!TryParseScope(parameters.Scope, out var scope, out string scopeError)) | |
| return Response.Error("INVALID_SCOPE", new { field = nameof(parameters.Scope), reason = scopeError }); | |
| if (EditorApplication.isPlaying) | |
| return Response.Error("PLAY_MODE", new { reason = "Material conversion is editor-only. Exit Play Mode first." }); | |
| if (!GraphicsSettings.isScriptableRenderPipelineEnabled) { | |
| return Response.Error("NO_SRP", new { | |
| reason = "No Scriptable Render Pipeline is assigned.", | |
| hint = "Assign a URP or HDRP asset under Edit > Project Settings > Graphics." | |
| }); | |
| } | |
| if (scope == MaterialConversionScope.Selected) { | |
| var validation = ValidateSelection(); | |
| if (validation != null) | |
| return validation; | |
| } | |
| string menuPath = scope == MaterialConversionScope.All ? MenuPathAll : MenuPathSelected; | |
| bool executed = EditorApplication.ExecuteMenuItem(menuPath); | |
| if (!executed) { | |
| return Response.Error("MENU_ITEM_FAILED", new { | |
| menuPath, | |
| scope = scope.ToString(), | |
| hint = "Confirm the menu item exists and preconditions are met (e.g. materials selected)." | |
| }); | |
| } | |
| return Response.Success($"Material conversion started via menu: {menuPath}", new { | |
| scope = scope.ToString(), | |
| menuPath, | |
| renderPipeline = GraphicsSettings.currentRenderPipeline?.GetType().Name | |
| ?? GraphicsSettings.defaultRenderPipeline?.GetType().Name | |
| }); | |
| } | |
| static bool TryParseScope(string raw, out MaterialConversionScope scope, out string error) { | |
| scope = MaterialConversionScope.All; | |
| error = null; | |
| if (string.IsNullOrWhiteSpace(raw)) { | |
| error = "Scope is required. Use 'All' or 'Selected'."; | |
| return false; | |
| } | |
| if (!System.Enum.TryParse(raw, ignoreCase: true, out scope)) { | |
| error = $"Unknown scope '{raw}'. Use 'All' or 'Selected'."; | |
| return false; | |
| } | |
| return true; | |
| } | |
| static object ValidateSelection() { | |
| if (Selection.objects == null || Selection.objects.Length == 0) { | |
| return Response.Error("NO_SELECTION", new { | |
| reason = "Scope is 'Selected' but nothing is selected.", | |
| hint = "Select one or more Material assets in the Project window." | |
| }); | |
| } | |
| for (int i = 0; i < Selection.objects.Length; i++) { | |
| if (Selection.objects[i] is not Material) { | |
| return Response.Error("INVALID_SELECTION", new { | |
| reason = "Scope is 'Selected' but the selection includes non-Material objects.", | |
| selectedType = Selection.objects[i]?.GetType().Name, | |
| hint = "Select only Material assets." | |
| }); | |
| } | |
| } | |
| return null; | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment