unity-mcp/Python/tools/script_tools.py

247 lines
10 KiB
Python
Raw Normal View History

2025-03-18 19:00:50 +08:00
from mcp.server.fastmcp import FastMCP, Context
from typing import List
from unity_connection import get_unity_connection
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) -> 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
Returns:
str: The contents of the script file or error message
"""
try:
# Send command to Unity to read the script file
response = get_unity_connection().send_command("VIEW_SCRIPT", {
"script_path": script_path
})
return response.get("content", "Script not found")
except Exception as e:
return f"Error viewing script: {str(e)}"
@mcp.tool()
def create_script(
ctx: Context,
script_name: str,
script_type: str = "MonoBehaviour",
namespace: str = None,
2025-03-18 21:52:41 +08:00
template: str = None,
overwrite: bool = False
2025-03-18 19:00:50 +08:00
) -> str:
"""Create a new Unity script file.
Args:
ctx: The MCP context
script_name: Name of the script (without .cs extension)
script_type: Type of script (e.g., MonoBehaviour, ScriptableObject)
namespace: Optional namespace for the script
template: Optional custom template to use
2025-03-18 21:52:41 +08:00
overwrite: Whether to overwrite if script already exists (default: False)
2025-03-18 19:00:50 +08:00
Returns:
str: Success message or error details
"""
try:
2025-03-18 21:52:41 +08:00
# First check if a script with this name already exists
unity = get_unity_connection()
script_path = f"Assets/Scripts/{script_name}.cs"
# Try to view the script to check if it exists
existing_script_response = unity.send_command("VIEW_SCRIPT", {
"script_path": script_path
})
# If the script exists and overwrite is False, return a message
if "content" in existing_script_response and not overwrite:
return f"Script '{script_name}.cs' already exists. Use overwrite=True to replace it."
2025-03-18 19:00:50 +08:00
# Send command to Unity to create the script
2025-03-18 21:52:41 +08:00
response = unity.send_command("CREATE_SCRIPT", {
2025-03-18 19:00:50 +08:00
"script_name": script_name,
"script_type": script_type,
"namespace": namespace,
2025-03-18 21:52:41 +08:00
"template": template,
"overwrite": overwrite
2025-03-18 19:00:50 +08:00
})
return response.get("message", "Script created successfully")
except Exception as e:
return f"Error creating script: {str(e)}"
@mcp.tool()
def update_script(
ctx: Context,
script_path: str,
2025-03-18 21:52:41 +08:00
content: str,
create_if_missing: bool = False,
create_folder_if_missing: bool = False
2025-03-18 19:00:50 +08:00
) -> 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
2025-03-18 21:52:41 +08:00
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)
2025-03-18 19:00:50 +08:00
Returns:
str: Success message or error details
"""
try:
2025-03-18 21:52:41 +08:00
unity = get_unity_connection()
2025-03-18 22:01:51 +08:00
# Parse script path (for potential creation)
script_name = script_path.split("/")[-1].replace(".cs", "")
script_folder = "/".join(script_path.split("/")[:-1])
2025-03-18 21:52:41 +08:00
2025-03-18 22:01:51 +08:00
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
}
2025-03-18 21:52:41 +08:00
2025-03-18 22:01:51 +08:00
# Add folder creation flag if requested
if create_folder_if_missing:
params["create_folder_if_missing"] = True
2025-03-18 21:52:41 +08:00
2025-03-18 22:01:51 +08:00
# 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,
2025-03-18 21:52:41 +08:00
"content": content
})
2025-03-18 22:01:51 +08:00
return response.get("message", "Script updated successfully")
2025-03-18 19:00:50 +08:00
except Exception as e:
return f"Error updating script: {str(e)}"
@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
})
scripts = response.get("scripts", [])
if not scripts:
return "No scripts found in the specified folder"
return "\n".join(scripts)
except Exception as e:
return f"Error listing scripts: {str(e)}"
@mcp.tool()
def attach_script(
ctx: Context,
object_name: str,
2025-03-18 21:52:41 +08:00
script_name: str,
script_path: str = None
2025-03-18 19:00:50 +08:00
) -> 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)
2025-03-18 21:52:41 +08:00
script_path: Optional full path to the script (if not in the default Scripts folder)
2025-03-18 19:00:50 +08:00
Returns:
str: Success message or error details
"""
try:
2025-03-18 21:52:41 +08:00
unity = get_unity_connection()
# Check if the object exists
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
if not script_name.lower().endswith(".cs"):
script_name = f"{script_name}.cs"
# Determine the full script path
if script_path is None:
# Use default Scripts folder if no path provided
script_path = f"Assets/Scripts/{script_name}"
elif not script_path.endswith(script_name):
# If path is just a directory, append the script name
if script_path.endswith("/"):
script_path = f"{script_path}{script_name}"
else:
script_path = f"{script_path}/{script_name}"
# Check if the script exists by trying to view it
existing_script_response = unity.send_command("VIEW_SCRIPT", {
"script_path": script_path
})
if "content" not in existing_script_response:
# If not found at the specific path, try to search for it in the project
script_found = False
try:
# Search in the entire Assets folder
script_assets = unity.send_command("LIST_SCRIPTS", {
"folder_path": "Assets"
}).get("scripts", [])
# Look for matching script name in any folder
matching_scripts = [path for path in script_assets if path.endswith(f"/{script_name}") or path == script_name]
if matching_scripts:
script_path = matching_scripts[0]
script_found = True
if len(matching_scripts) > 1:
return f"Multiple scripts named '{script_name}' found in the project. Please specify script_path parameter."
except:
pass
if not script_found:
return f"Script '{script_name}' not found in the project."
# Check if the script is already attached
object_props = unity.send_command("GET_OBJECT_PROPERTIES", {
"name": object_name
})
# Extract script name without .cs and without path
script_class_name = script_name.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}'."
2025-03-18 19:00:50 +08:00
# Send command to Unity to attach the script
2025-03-18 21:52:41 +08:00
response = unity.send_command("ATTACH_SCRIPT", {
2025-03-18 19:00:50 +08:00
"object_name": object_name,
2025-03-18 21:52:41 +08:00
"script_name": script_name,
"script_path": script_path
2025-03-18 19:00:50 +08:00
})
return response.get("message", "Script attached successfully")
except Exception as e:
return f"Error attaching script: {str(e)}"