diff --git a/Editor/Commands/ScriptCommandHandler.cs b/Editor/Commands/ScriptCommandHandler.cs
index 6b6af57..666976c 100644
--- a/Editor/Commands/ScriptCommandHandler.cs
+++ b/Editor/Commands/ScriptCommandHandler.cs
@@ -1,10 +1,10 @@
-using UnityEngine;
-using UnityEditor;
using System;
using System.IO;
-using System.Text;
using System.Linq;
+using System.Text;
using Newtonsoft.Json.Linq;
+using UnityEditor;
+using UnityEngine;
namespace UnityMCP.Editor.Commands
{
@@ -18,7 +18,9 @@ namespace UnityMCP.Editor.Commands
///
public static object ViewScript(JObject @params)
{
- string scriptPath = (string)@params["script_path"] ?? throw new Exception("Parameter 'script_path' is required.");
+ string scriptPath =
+ (string)@params["script_path"]
+ ?? throw new Exception("Parameter 'script_path' is required.");
bool requireExists = (bool?)@params["require_exists"] ?? true;
// Handle path correctly to avoid double "Assets" folder issue
@@ -47,7 +49,16 @@ namespace UnityMCP.Editor.Commands
}
}
- return new { exists = true, content = File.ReadAllText(fullPath) };
+ string content = File.ReadAllText(fullPath);
+ byte[] contentBytes = System.Text.Encoding.UTF8.GetBytes(content);
+ string base64Content = Convert.ToBase64String(contentBytes);
+
+ return new
+ {
+ exists = true,
+ content = base64Content,
+ encoding = "base64"
+ };
}
///
@@ -70,7 +81,9 @@ namespace UnityMCP.Editor.Commands
///
public static object CreateScript(JObject @params)
{
- string scriptName = (string)@params["script_name"] ?? throw new Exception("Parameter 'script_name' is required.");
+ string scriptName =
+ (string)@params["script_name"]
+ ?? throw new Exception("Parameter 'script_name' is required.");
string scriptType = (string)@params["script_type"] ?? "MonoBehaviour";
string namespaceName = (string)@params["namespace"];
string template = (string)@params["template"];
@@ -128,7 +141,9 @@ namespace UnityMCP.Editor.Commands
string fullFilePath = Path.Combine(folderPath, scriptName);
if (File.Exists(fullFilePath) && !overwrite)
{
- throw new Exception($"Script file '{scriptName}' already exists in '{scriptPath}' and overwrite is not enabled.");
+ throw new Exception(
+ $"Script file '{scriptName}' already exists in '{scriptPath}' and overwrite is not enabled."
+ );
}
try
@@ -157,7 +172,9 @@ namespace UnityMCP.Editor.Commands
// Add class definition with indent based on namespace
string indent = string.IsNullOrEmpty(namespaceName) ? "" : " ";
- contentBuilder.AppendLine($"{indent}public class {Path.GetFileNameWithoutExtension(scriptName)} : {scriptType}");
+ contentBuilder.AppendLine(
+ $"{indent}public class {Path.GetFileNameWithoutExtension(scriptName)} : {scriptType}"
+ );
contentBuilder.AppendLine($"{indent}{{");
// Add default Unity methods based on script type
@@ -165,7 +182,9 @@ namespace UnityMCP.Editor.Commands
{
contentBuilder.AppendLine($"{indent} private void Start()");
contentBuilder.AppendLine($"{indent} {{");
- contentBuilder.AppendLine($"{indent} // Initialize your component here");
+ contentBuilder.AppendLine(
+ $"{indent} // Initialize your component here"
+ );
contentBuilder.AppendLine($"{indent} }}");
contentBuilder.AppendLine();
contentBuilder.AppendLine($"{indent} private void Update()");
@@ -177,7 +196,9 @@ namespace UnityMCP.Editor.Commands
{
contentBuilder.AppendLine($"{indent} private void OnEnable()");
contentBuilder.AppendLine($"{indent} {{");
- contentBuilder.AppendLine($"{indent} // Initialize your ScriptableObject here");
+ contentBuilder.AppendLine(
+ $"{indent} // Initialize your ScriptableObject here"
+ );
contentBuilder.AppendLine($"{indent} }}");
}
@@ -222,8 +243,12 @@ namespace UnityMCP.Editor.Commands
///
public static object UpdateScript(JObject @params)
{
- string scriptPath = (string)@params["script_path"] ?? throw new Exception("Parameter 'script_path' is required.");
- string content = (string)@params["content"] ?? throw new Exception("Parameter 'content' is required.");
+ string scriptPath =
+ (string)@params["script_path"]
+ ?? throw new Exception("Parameter 'script_path' is required.");
+ string content =
+ (string)@params["content"]
+ ?? throw new Exception("Parameter 'content' is required.");
bool createIfMissing = (bool?)@params["create_if_missing"] ?? false;
bool createFolderIfMissing = (bool?)@params["create_folder_if_missing"] ?? false;
@@ -257,7 +282,9 @@ namespace UnityMCP.Editor.Commands
}
else if (!Directory.Exists(directory))
{
- throw new Exception($"Directory does not exist: {Path.GetDirectoryName(scriptPath)}");
+ throw new Exception(
+ $"Directory does not exist: {Path.GetDirectoryName(scriptPath)}"
+ );
}
// Create the file with content
@@ -308,7 +335,8 @@ namespace UnityMCP.Editor.Commands
if (!Directory.Exists(fullPath))
throw new Exception($"Folder not found: {folderPath}");
- string[] scripts = Directory.GetFiles(fullPath, "*.cs", SearchOption.AllDirectories)
+ string[] scripts = Directory
+ .GetFiles(fullPath, "*.cs", SearchOption.AllDirectories)
.Select(path => path.Replace(Application.dataPath, "Assets"))
.ToArray();
@@ -320,8 +348,12 @@ namespace UnityMCP.Editor.Commands
///
public static object AttachScript(JObject @params)
{
- string objectName = (string)@params["object_name"] ?? throw new Exception("Parameter 'object_name' is required.");
- string scriptName = (string)@params["script_name"] ?? throw new Exception("Parameter 'script_name' is required.");
+ string objectName =
+ (string)@params["object_name"]
+ ?? throw new Exception("Parameter 'object_name' is required.");
+ string scriptName =
+ (string)@params["script_name"]
+ ?? throw new Exception("Parameter 'script_name' is required.");
string scriptPath = (string)@params["script_path"]; // Optional
// Find the target object
@@ -343,7 +375,11 @@ namespace UnityMCP.Editor.Commands
if (!string.IsNullOrEmpty(scriptPath))
{
// If a specific path is provided, try that first
- if (File.Exists(Path.Combine(Application.dataPath, scriptPath.Replace("Assets/", ""))))
+ if (
+ File.Exists(
+ Path.Combine(Application.dataPath, scriptPath.Replace("Assets/", ""))
+ )
+ )
{
// Use the direct path if it exists
MonoScript scriptAsset = AssetDatabase.LoadAssetAtPath(scriptPath);
@@ -398,8 +434,18 @@ namespace UnityMCP.Editor.Commands
// Double check the file name to avoid false matches
string foundFileName = Path.GetFileName(path);
- if (!string.Equals(foundFileName, scriptFileName, StringComparison.OrdinalIgnoreCase) &&
- !string.Equals(Path.GetFileNameWithoutExtension(foundFileName), scriptNameWithoutExtension, StringComparison.OrdinalIgnoreCase))
+ if (
+ !string.Equals(
+ foundFileName,
+ scriptFileName,
+ StringComparison.OrdinalIgnoreCase
+ )
+ && !string.Equals(
+ Path.GetFileNameWithoutExtension(foundFileName),
+ scriptNameWithoutExtension,
+ StringComparison.OrdinalIgnoreCase
+ )
+ )
continue;
MonoScript scriptAsset = AssetDatabase.LoadAssetAtPath(path);
@@ -442,7 +488,9 @@ namespace UnityMCP.Editor.Commands
}
// If we've tried all possibilities and nothing worked
- throw new Exception($"Could not attach script '{scriptFileName}' to object '{objectName}'. No valid script found or component creation failed.");
+ throw new Exception(
+ $"Could not attach script '{scriptFileName}' to object '{objectName}'. No valid script found or component creation failed."
+ );
}
}
-}
\ No newline at end of file
+}
diff --git a/Python/tools/script_tools.py b/Python/tools/script_tools.py
index f67a963..b7dee85 100644
--- a/Python/tools/script_tools.py
+++ b/Python/tools/script_tools.py
@@ -1,19 +1,21 @@
from mcp.server.fastmcp import FastMCP, Context
from typing import List
from unity_connection import get_unity_connection
+import base64
+
def register_script_tools(mcp: FastMCP):
"""Register all script-related tools with the MCP server."""
-
+
@mcp.tool()
def view_script(ctx: Context, script_path: str, require_exists: bool = True) -> str:
"""View the contents of a Unity script file.
-
+
Args:
ctx: The MCP context
script_path: Path to the script file relative to the Assets folder
require_exists: Whether to raise an error if the file doesn't exist (default: True)
-
+
Returns:
str: The contents of the script file or error message
"""
@@ -21,17 +23,22 @@ def register_script_tools(mcp: FastMCP):
# Normalize script path to ensure it has the correct format
if not script_path.startswith("Assets/"):
script_path = f"Assets/{script_path}"
-
+
# Debug to help diagnose issues
print(f"ViewScript - Using normalized script path: {script_path}")
-
+
# Send command to Unity to read the script file
- response = get_unity_connection().send_command("VIEW_SCRIPT", {
- "script_path": script_path,
- "require_exists": require_exists
- })
-
+ response = get_unity_connection().send_command(
+ "VIEW_SCRIPT",
+ {"script_path": script_path, "require_exists": require_exists},
+ )
+
if response.get("exists", True):
+ if response.get("encoding", "base64") == "base64":
+ decoded_content = base64.b64decode(response.get("content")).decode(
+ "utf-8"
+ )
+ return decoded_content
return response.get("content", "Script contents not available")
else:
return response.get("message", "Script not found")
@@ -47,10 +54,10 @@ def register_script_tools(mcp: FastMCP):
template: str = None,
script_folder: str = None,
overwrite: bool = False,
- content: str = None
+ content: str = None,
) -> str:
"""Create a new Unity script file.
-
+
Args:
ctx: The MCP context
script_name: Name of the script (without .cs extension)
@@ -60,13 +67,13 @@ def register_script_tools(mcp: FastMCP):
script_folder: Optional folder path within Assets to create the script
overwrite: Whether to overwrite if script already exists (default: False)
content: Optional custom content for the script
-
+
Returns:
str: Success message or error details
"""
try:
unity = get_unity_connection()
-
+
# Determine script path based on script_folder parameter
if script_folder:
# Use provided folder path
@@ -75,13 +82,13 @@ def register_script_tools(mcp: FastMCP):
normalized_folder = script_folder
else:
normalized_folder = f"Assets/{script_folder}"
-
+
# Create the full path
if normalized_folder.endswith("/"):
script_path = f"{normalized_folder}{script_name}.cs"
else:
script_path = f"{normalized_folder}/{script_name}.cs"
-
+
# Debug to help diagnose issues
print(f"CreateScript - Folder: {script_folder}")
print(f"CreateScript - Normalized folder: {normalized_folder}")
@@ -90,7 +97,7 @@ def register_script_tools(mcp: FastMCP):
# Default to Scripts folder when no folder is provided
script_path = f"Assets/Scripts/{script_name}.cs"
print(f"CreateScript - Using default script path: {script_path}")
-
+
# Send command to Unity to create the script directly
# The C# handler will handle the file existence check
params = {
@@ -98,17 +105,17 @@ def register_script_tools(mcp: FastMCP):
"script_type": script_type,
"namespace": namespace,
"template": template,
- "overwrite": overwrite
+ "overwrite": overwrite,
}
-
+
# Add script_folder if provided
if script_folder:
params["script_folder"] = script_folder
-
+
# Add content if provided
if content:
params["content"] = content
-
+
response = unity.send_command("CREATE_SCRIPT", params)
return response.get("message", "Script created successfully")
except Exception as e:
@@ -120,59 +127,58 @@ def register_script_tools(mcp: FastMCP):
script_path: str,
content: str,
create_if_missing: bool = False,
- create_folder_if_missing: bool = False
+ create_folder_if_missing: bool = False,
) -> str:
"""Update the contents of an existing Unity script.
-
+
Args:
ctx: The MCP context
script_path: Path to the script file relative to the Assets folder
content: New content for the script
create_if_missing: Whether to create the script if it doesn't exist (default: False)
create_folder_if_missing: Whether to create the parent directory if it doesn't exist (default: False)
-
+
Returns:
str: Success message or error details
"""
try:
unity = get_unity_connection()
-
+
# Normalize script path to ensure it has the correct format
# Make sure the path starts with Assets/ but not Assets/Assets/
if not script_path.startswith("Assets/"):
script_path = f"Assets/{script_path}"
-
+
# Debug to help diagnose issues
print(f"UpdateScript - Original path: {script_path}")
-
+
# Parse script path (for potential creation)
script_name = script_path.split("/")[-1]
if not script_name.endswith(".cs"):
script_name += ".cs"
script_path = f"{script_path}.cs"
-
+
if create_if_missing:
# When create_if_missing is true, we'll just try to update directly,
# and let Unity handle the creation if needed
params = {
"script_path": script_path,
"content": content,
- "create_if_missing": True
+ "create_if_missing": True,
}
-
+
# Add folder creation flag if requested
if create_folder_if_missing:
params["create_folder_if_missing"] = True
-
+
# Send command to Unity to update/create the script
response = unity.send_command("UPDATE_SCRIPT", params)
return response.get("message", "Script updated successfully")
else:
# Standard update without creation flags
- response = unity.send_command("UPDATE_SCRIPT", {
- "script_path": script_path,
- "content": content
- })
+ response = unity.send_command(
+ "UPDATE_SCRIPT", {"script_path": script_path, "content": content}
+ )
return response.get("message", "Script updated successfully")
except Exception as e:
return f"Error updating script: {str(e)}"
@@ -180,19 +186,19 @@ def register_script_tools(mcp: FastMCP):
@mcp.tool()
def list_scripts(ctx: Context, folder_path: str = "Assets") -> str:
"""List all script files in a specified folder.
-
+
Args:
ctx: The MCP context
folder_path: Path to the folder to search (default: Assets)
-
+
Returns:
str: List of script files or error message
"""
try:
# Send command to Unity to list scripts
- response = get_unity_connection().send_command("LIST_SCRIPTS", {
- "folder_path": folder_path
- })
+ response = get_unity_connection().send_command(
+ "LIST_SCRIPTS", {"folder_path": folder_path}
+ )
scripts = response.get("scripts", [])
if not scripts:
return "No scripts found in the specified folder"
@@ -202,79 +208,73 @@ def register_script_tools(mcp: FastMCP):
@mcp.tool()
def attach_script(
- ctx: Context,
- object_name: str,
- script_name: str,
- script_path: str = None
+ ctx: Context, object_name: str, script_name: str, script_path: str = None
) -> str:
"""Attach a script component to a GameObject.
-
+
Args:
ctx: The MCP context
object_name: Name of the target GameObject in the scene
script_name: Name of the script to attach (with or without .cs extension)
script_path: Optional full path to the script (if not in the default Scripts folder)
-
+
Returns:
str: Success message or error details
"""
try:
unity = get_unity_connection()
-
+
# Check if the object exists
- object_response = unity.send_command("FIND_OBJECTS_BY_NAME", {
- "name": object_name
- })
-
+ object_response = unity.send_command(
+ "FIND_OBJECTS_BY_NAME", {"name": object_name}
+ )
+
objects = object_response.get("objects", [])
if not objects:
return f"GameObject '{object_name}' not found in the scene."
-
- # Ensure script_name has .cs extension
+
+ # Ensure script_name has .cs extension
if not script_name.lower().endswith(".cs"):
script_name = f"{script_name}.cs"
-
+
# Remove any path information from script_name if it contains slashes
- script_basename = script_name.split('/')[-1]
-
+ script_basename = script_name.split("/")[-1]
+
# Determine the full script path if provided
if script_path is not None:
# Ensure script_path starts with Assets/
if not script_path.startswith("Assets/"):
script_path = f"Assets/{script_path}"
-
+
# If path is just a directory, append the script name
if not script_path.endswith(script_basename):
if script_path.endswith("/"):
script_path = f"{script_path}{script_basename}"
else:
script_path = f"{script_path}/{script_basename}"
-
+
# Check if the script is already attached
- object_props = unity.send_command("GET_OBJECT_PROPERTIES", {
- "name": object_name
- })
-
+ object_props = unity.send_command(
+ "GET_OBJECT_PROPERTIES", {"name": object_name}
+ )
+
# Extract script name without .cs and without path for component type checking
script_class_name = script_basename.replace(".cs", "")
-
+
# Check if component is already attached
components = object_props.get("components", [])
for component in components:
if component.get("type") == script_class_name:
return f"Script '{script_class_name}' is already attached to '{object_name}'."
-
+
# Send command to Unity to attach the script
- params = {
- "object_name": object_name,
- "script_name": script_basename
- }
-
+ params = {"object_name": object_name, "script_name": script_basename}
+
# Add script_path if provided
if script_path:
params["script_path"] = script_path
-
+
response = unity.send_command("ATTACH_SCRIPT", params)
return response.get("message", "Script attached successfully")
except Exception as e:
- return f"Error attaching script: {str(e)}"
\ No newline at end of file
+ return f"Error attaching script: {str(e)}"