Skip to content

Instantly share code, notes, and snippets.

@adammyhre
Created June 28, 2026 06:29
Show Gist options
  • Select an option

  • Save adammyhre/df4a1d98b131b1ef4df30a787a532e40 to your computer and use it in GitHub Desktop.

Select an option

Save adammyhre/df4a1d98b131b1ef4df30a787a532e40 to your computer and use it in GitHub Desktop.
Example Custom MCP Tool for Unity
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