diff --git a/MCPForUnity/Editor/Tools/Sprite2D.meta b/MCPForUnity/Editor/Tools/Sprite2D.meta
new file mode 100644
index 000000000..574f9695a
--- /dev/null
+++ b/MCPForUnity/Editor/Tools/Sprite2D.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: e7ba99f77eb524525964129bb1fcd94c
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/MCPForUnity/Editor/Tools/Sprite2D/ManageSprite.cs b/MCPForUnity/Editor/Tools/Sprite2D/ManageSprite.cs
new file mode 100644
index 000000000..79074be0c
--- /dev/null
+++ b/MCPForUnity/Editor/Tools/Sprite2D/ManageSprite.cs
@@ -0,0 +1,47 @@
+using Newtonsoft.Json.Linq;
+using MCPForUnity.Editor.Helpers;
+
+namespace MCPForUnity.Editor.Tools.Sprite2D
+{
+ [McpForUnityTool("manage_sprite", AutoRegister = false, Group = "animation")]
+ public static class ManageSprite
+ {
+ private static readonly string[] ValidActions =
+ {
+ "get_info", "slice_sheet", "setup_clips",
+ "setup_controller", "full_setup"
+ };
+
+ public static object HandleCommand(JObject @params)
+ {
+ string action = @params["action"]?.ToString()?.ToLowerInvariant();
+ if (string.IsNullOrEmpty(action))
+ return new ErrorResponse(
+ "'action' is required. Valid: " + string.Join(", ", ValidActions));
+
+ var diagnostics = new SpriteDiagnosticBuilder();
+
+ switch (action)
+ {
+ case "get_info":
+ return SpriteImportSetup.GetInfo(@params);
+
+ case "slice_sheet":
+ return SpriteImportSetup.SliceSheet(@params, diagnostics);
+
+ case "setup_clips":
+ return SpriteClipBuilder.SetupClips(@params, diagnostics);
+
+ case "setup_controller":
+ return SpriteControllerBuilder.Build(@params, diagnostics);
+
+ case "full_setup":
+ return SpriteFullSetup.Run(@params);
+
+ default:
+ return new ErrorResponse(
+ $"Unknown action '{action}'. Valid: " + string.Join(", ", ValidActions));
+ }
+ }
+ }
+}
diff --git a/MCPForUnity/Editor/Tools/Sprite2D/ManageSprite.cs.meta b/MCPForUnity/Editor/Tools/Sprite2D/ManageSprite.cs.meta
new file mode 100644
index 000000000..9445f93fa
--- /dev/null
+++ b/MCPForUnity/Editor/Tools/Sprite2D/ManageSprite.cs.meta
@@ -0,0 +1,2 @@
+fileFormatVersion: 2
+guid: 636047e62387a46a39a2cdfd31172f8a
\ No newline at end of file
diff --git a/MCPForUnity/Editor/Tools/Sprite2D/SpriteClipBuilder.cs b/MCPForUnity/Editor/Tools/Sprite2D/SpriteClipBuilder.cs
new file mode 100644
index 000000000..1d48d13c3
--- /dev/null
+++ b/MCPForUnity/Editor/Tools/Sprite2D/SpriteClipBuilder.cs
@@ -0,0 +1,254 @@
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using Newtonsoft.Json.Linq;
+using UnityEditor;
+using UnityEngine;
+using MCPForUnity.Editor.Helpers;
+
+namespace MCPForUnity.Editor.Tools.Sprite2D
+{
+ internal static class SpriteClipBuilder
+ {
+ ///
+ /// Builds AnimationClips out of sliced sprites and saves them as .anim assets.
+ /// params:
+ /// path - sprite texture asset path
+ /// clips - [{name, start_frame, end_frame, fps (opt, def=12), loop (opt)}]
+ /// output_dir - where the clips are written (default: the sprite's own folder)
+ /// overwrite - bool (default false); an existing clip is kept unless this is true
+ ///
+ public static object SetupClips(JObject @params, SpriteDiagnosticBuilder diagnostics)
+ {
+ string path = @params["path"]?.ToString();
+ if (string.IsNullOrEmpty(path))
+ return new ErrorResponse("'path' is required.");
+
+ path = AssetPathUtility.SanitizeAssetPath(path);
+ if (path == null)
+ return new ErrorResponse("'path' must stay under Assets/ and cannot contain '..'.");
+
+ var allSprites = AssetDatabase.LoadAllAssetsAtPath(path)
+ .OfType()
+ .OrderBy(s => NaturalSortKey(s.name))
+ .ToArray();
+
+ if (allSprites.Length == 0)
+ return new ErrorResponse($"No sprites found at '{path}'. Run slice_sheet first.");
+
+ var clipsToken = @params["clips"] as JArray;
+ if (clipsToken == null || clipsToken.Count == 0)
+ return new ErrorResponse("'clips' array is required.");
+
+ string outputDir = @params["output_dir"]?.ToString()
+ ?? Path.GetDirectoryName(path)?.Replace('\\', '/') ?? "Assets";
+
+ // SanitizeAssetPath returns null when it refuses a path, so falling back to the
+ // raw value would hand traversal sequences straight through.
+ outputDir = AssetPathUtility.SanitizeAssetPath(outputDir);
+ if (outputDir == null)
+ return new ErrorResponse("'output_dir' must stay under Assets/ and cannot contain '..'.");
+ if (!AssetDatabase.IsValidFolder(outputDir))
+ CreateFolders(outputDir);
+
+ bool overwrite = @params["overwrite"]?.ToObject() ?? false;
+
+ var createdClips = new List