added object existance validation
parent
276ac8166f
commit
36b5b224b3
|
|
@ -108,31 +108,38 @@ def asset_creation_strategy() -> str:
|
|||
|
||||
## Best Practices
|
||||
|
||||
1. **Error Handling**:
|
||||
1. **Existence Checking**:
|
||||
|
||||
- ALWAYS check if objects, scripts, assets, or materials exist before creating or updating them
|
||||
- Use appropriate search tools (`find_objects_by_name`, `list_scripts`, `get_asset_list`) to verify existence
|
||||
- Handle both cases: creation when it doesn't exist and updating when it does
|
||||
- Implement proper error handling when an expected resource is not found
|
||||
|
||||
2. **Error Handling**:
|
||||
|
||||
- Always include try-catch blocks in Python tools
|
||||
- Validate parameters in C# handlers
|
||||
- Return meaningful error messages
|
||||
|
||||
2. **Documentation**:
|
||||
3. **Documentation**:
|
||||
|
||||
- Add XML documentation to C# handlers
|
||||
- Include detailed docstrings in Python tools
|
||||
- Update the prompt with clear usage instructions
|
||||
|
||||
3. **Parameter Validation**:
|
||||
4. **Parameter Validation**:
|
||||
|
||||
- Validate parameters on both Python and C# sides
|
||||
- Use appropriate types (str, int, float, List, etc.)
|
||||
- Provide default values when appropriate
|
||||
|
||||
4. **Testing**:
|
||||
5. **Testing**:
|
||||
|
||||
- Test the tool in both Unity Editor and Python environments
|
||||
- Verify error handling works as expected
|
||||
- Check that the tool integrates well with existing functionality
|
||||
|
||||
5. **Code Organization**:
|
||||
6. **Code Organization**:
|
||||
- Group related tools in appropriate handler classes
|
||||
- Keep tools focused and single-purpose
|
||||
- Follow existing naming conventions
|
||||
|
|
@ -150,14 +157,29 @@ public static class ExampleHandler
|
|||
{
|
||||
string prefabName = (string)@params["prefab_name"];
|
||||
string template = (string)@params["template"];
|
||||
bool overwrite = @params["overwrite"] != null ? (bool)@params["overwrite"] : false;
|
||||
|
||||
// Check if the prefab already exists
|
||||
string prefabPath = $"Assets/Prefabs/{prefabName}.prefab";
|
||||
bool prefabExists = System.IO.File.Exists(prefabPath);
|
||||
|
||||
if (prefabExists && !overwrite)
|
||||
{
|
||||
return new {
|
||||
message = $"Prefab already exists: {prefabName}. Use overwrite=true to replace it.",
|
||||
exists = true,
|
||||
path = prefabPath
|
||||
};
|
||||
}
|
||||
|
||||
// Implementation
|
||||
GameObject prefab = new GameObject(prefabName);
|
||||
// ... setup prefab ...
|
||||
|
||||
return new {
|
||||
message = $"Created prefab: {prefabName}",
|
||||
path = $"Assets/Prefabs/{prefabName}.prefab"
|
||||
message = prefabExists ? $"Updated prefab: {prefabName}" : $"Created prefab: {prefabName}",
|
||||
exists = prefabExists,
|
||||
path = prefabPath
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -170,33 +192,51 @@ public static class ExampleHandler
|
|||
def create_prefab(
|
||||
ctx: Context,
|
||||
prefab_name: str,
|
||||
template: str = "default"
|
||||
template: str = "default",
|
||||
overwrite: bool = False
|
||||
) -> str:
|
||||
"""Create a new prefab in the project.
|
||||
"""Create a new prefab in the project or update if it exists.
|
||||
|
||||
Args:
|
||||
ctx: The MCP context
|
||||
prefab_name: Name for the new prefab
|
||||
template: Template to use (default: "default")
|
||||
overwrite: Whether to overwrite an existing prefab (default: False)
|
||||
|
||||
Returns:
|
||||
str: Success message or error details
|
||||
"""
|
||||
try:
|
||||
# First check if the prefab already exists
|
||||
assets = get_unity_connection().send_command("GET_ASSET_LIST", {
|
||||
"type": "Prefab",
|
||||
"search_pattern": prefab_name,
|
||||
"folder": "Assets/Prefabs"
|
||||
}).get("assets", [])
|
||||
|
||||
prefab_exists = any(asset.get("name") == prefab_name for asset in assets)
|
||||
|
||||
if prefab_exists and not overwrite:
|
||||
return f"Prefab '{prefab_name}' already exists. Use overwrite=True to replace it."
|
||||
|
||||
# Create or update the prefab
|
||||
response = get_unity_connection().send_command("CREATE_PREFAB", {
|
||||
"prefab_name": prefab_name,
|
||||
"template": template
|
||||
"template": template,
|
||||
"overwrite": overwrite
|
||||
})
|
||||
return response.get("message", "Prefab created successfully")
|
||||
|
||||
return response.get("message", "Prefab operation completed successfully")
|
||||
except Exception as e:
|
||||
return f"Error creating prefab: {str(e)}"
|
||||
return f"Error with prefab operation: {str(e)}"
|
||||
```
|
||||
|
||||
3. **Update Prompt**:
|
||||
|
||||
```python
|
||||
"1. **Prefab Management**:\n"
|
||||
" - Create prefabs with `create_prefab(prefab_name, template)`\n"
|
||||
" - ALWAYS check if a prefab exists before creating it\n"
|
||||
" - Create or update prefabs with `create_prefab(prefab_name, template, overwrite=False)`\n"
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
|
|
|||
|
|
@ -56,6 +56,7 @@ def asset_creation_strategy() -> str:
|
|||
" - `open_scene(path)`, `save_scene(path)` - Open/save scenes\n"
|
||||
" - `new_scene(path)`, `change_scene(path, save_current)` - Create/switch scenes\n\n"
|
||||
"3. **Object Management**\n"
|
||||
" - ALWAYS use `find_objects_by_name(name)` to check if an object exists before creating or modifying it\n"
|
||||
" - `create_object(name, type)` - Create objects (e.g. `CUBE`, `SPHERE`, `EMPTY`, `CAMERA`)\n"
|
||||
" - `delete_object(name)` - Remove objects\n"
|
||||
" - `set_object_transform(name, location, rotation, scale)` - Modify object position, rotation, and scale\n"
|
||||
|
|
@ -65,11 +66,13 @@ def asset_creation_strategy() -> str:
|
|||
" - `find_objects_by_name(name)` - Find objects by name\n"
|
||||
" - `get_hierarchy()` - Get object hierarchy\n"
|
||||
"4. **Script Management**\n"
|
||||
" - ALWAYS use `list_scripts(folder_path)` or `view_script(path)` to check if a script exists before creating or updating it\n"
|
||||
" - `create_script(name, type, namespace, template)` - Create scripts\n"
|
||||
" - `view_script(path)`, `update_script(path, content)` - View/modify scripts\n"
|
||||
" - `attach_script(object_name, script_name)` - Add scripts to objects\n"
|
||||
" - `list_scripts(folder_path)` - List scripts in folder\n\n"
|
||||
"5. **Asset Management**\n"
|
||||
" - ALWAYS use `get_asset_list(type, search_pattern, folder)` to check if an asset exists before creating or importing it\n"
|
||||
" - `import_asset(source_path, target_path)` - Import external assets\n"
|
||||
" - `instantiate_prefab(path, pos_x, pos_y, pos_z, rot_x, rot_y, rot_z)` - Create prefab instances\n"
|
||||
" - `create_prefab(object_name, path)`, `apply_prefab(object_name, path)` - Manage prefabs\n"
|
||||
|
|
@ -77,9 +80,11 @@ def asset_creation_strategy() -> str:
|
|||
" - Use relative paths for Unity assets (e.g., 'Assets/Models/MyModel.fbx')\n"
|
||||
" - Use absolute paths for external files\n\n"
|
||||
"6. **Material Management**\n"
|
||||
" - ALWAYS check if a material exists before creating or modifying it\n"
|
||||
" - `set_material(object_name, material_name, color)` - Apply/create materials\n"
|
||||
" - Use RGB colors (0.0-1.0 range)\n\n"
|
||||
"7. **Best Practices**\n"
|
||||
" - ALWAYS verify existence before creating or updating any objects, scripts, assets, or materials\n"
|
||||
" - Use meaningful names for objects and scripts\n"
|
||||
" - Keep scripts organized in folders with namespaces\n"
|
||||
" - Verify changes after modifications\n"
|
||||
|
|
|
|||
|
|
@ -9,7 +9,8 @@ def register_asset_tools(mcp: FastMCP):
|
|||
def import_asset(
|
||||
ctx: Context,
|
||||
source_path: str,
|
||||
target_path: str
|
||||
target_path: str,
|
||||
overwrite: bool = False
|
||||
) -> str:
|
||||
"""Import an asset (e.g., 3D model, texture) into the Unity project.
|
||||
|
||||
|
|
@ -17,21 +18,45 @@ def register_asset_tools(mcp: FastMCP):
|
|||
ctx: The MCP context
|
||||
source_path: Path to the source file on disk
|
||||
target_path: Path where the asset should be imported in the Unity project (relative to Assets folder)
|
||||
overwrite: Whether to overwrite if an asset already exists at the target path (default: False)
|
||||
|
||||
Returns:
|
||||
str: Success message or error details
|
||||
"""
|
||||
try:
|
||||
unity = get_unity_connection()
|
||||
|
||||
# Parameter validation
|
||||
if not source_path or not isinstance(source_path, str):
|
||||
return f"Error importing asset: source_path must be a valid string"
|
||||
|
||||
if not target_path or not isinstance(target_path, str):
|
||||
return f"Error importing asset: target_path must be a valid string"
|
||||
|
||||
# Check if the source file exists (on local disk)
|
||||
import os
|
||||
if not os.path.exists(source_path):
|
||||
return f"Error importing asset: Source file '{source_path}' does not exist"
|
||||
|
||||
# Extract the target directory and filename
|
||||
target_dir = '/'.join(target_path.split('/')[:-1])
|
||||
target_filename = target_path.split('/')[-1]
|
||||
|
||||
# Check if an asset already exists at the target path
|
||||
existing_assets = unity.send_command("GET_ASSET_LIST", {
|
||||
"search_pattern": target_filename,
|
||||
"folder": target_dir or "Assets"
|
||||
}).get("assets", [])
|
||||
|
||||
# Check if any asset matches the exact path
|
||||
asset_exists = any(asset.get("path") == target_path for asset in existing_assets)
|
||||
if asset_exists and not overwrite:
|
||||
return f"Asset already exists at '{target_path}'. Use overwrite=True to replace it."
|
||||
|
||||
response = get_unity_connection().send_command("IMPORT_ASSET", {
|
||||
response = unity.send_command("IMPORT_ASSET", {
|
||||
"source_path": source_path,
|
||||
"target_path": target_path
|
||||
"target_path": target_path,
|
||||
"overwrite": overwrite
|
||||
})
|
||||
|
||||
if not response.get("success", False):
|
||||
|
|
@ -68,6 +93,8 @@ def register_asset_tools(mcp: FastMCP):
|
|||
str: Success message or error details
|
||||
"""
|
||||
try:
|
||||
unity = get_unity_connection()
|
||||
|
||||
# Parameter validation
|
||||
if not prefab_path or not isinstance(prefab_path, str):
|
||||
return f"Error instantiating prefab: prefab_path must be a valid string"
|
||||
|
|
@ -86,7 +113,26 @@ def register_asset_tools(mcp: FastMCP):
|
|||
if not isinstance(param_value, (int, float)):
|
||||
return f"Error instantiating prefab: {param_name} must be a number"
|
||||
|
||||
response = get_unity_connection().send_command("INSTANTIATE_PREFAB", {
|
||||
# Check if the prefab exists
|
||||
prefab_dir = '/'.join(prefab_path.split('/')[:-1]) or "Assets"
|
||||
prefab_name = prefab_path.split('/')[-1]
|
||||
|
||||
# Ensure prefab has .prefab extension for searching
|
||||
if not prefab_name.lower().endswith('.prefab'):
|
||||
prefab_name = f"{prefab_name}.prefab"
|
||||
prefab_path = f"{prefab_path}.prefab"
|
||||
|
||||
prefab_assets = unity.send_command("GET_ASSET_LIST", {
|
||||
"type": "Prefab",
|
||||
"search_pattern": prefab_name,
|
||||
"folder": prefab_dir
|
||||
}).get("assets", [])
|
||||
|
||||
prefab_exists = any(asset.get("path") == prefab_path for asset in prefab_assets)
|
||||
if not prefab_exists:
|
||||
return f"Prefab '{prefab_path}' not found in the project."
|
||||
|
||||
response = unity.send_command("INSTANTIATE_PREFAB", {
|
||||
"prefab_path": prefab_path,
|
||||
"position_x": position_x,
|
||||
"position_y": position_y,
|
||||
|
|
@ -107,7 +153,8 @@ def register_asset_tools(mcp: FastMCP):
|
|||
def create_prefab(
|
||||
ctx: Context,
|
||||
object_name: str,
|
||||
prefab_path: str
|
||||
prefab_path: str,
|
||||
overwrite: bool = False
|
||||
) -> str:
|
||||
"""Create a new prefab asset from a GameObject in the scene.
|
||||
|
||||
|
|
@ -115,25 +162,51 @@ def register_asset_tools(mcp: FastMCP):
|
|||
ctx: The MCP context
|
||||
object_name: Name of the GameObject in the scene to create prefab from
|
||||
prefab_path: Path where the prefab should be saved (relative to Assets folder)
|
||||
overwrite: Whether to overwrite if a prefab already exists at the path (default: False)
|
||||
|
||||
Returns:
|
||||
str: Success message or error details
|
||||
"""
|
||||
try:
|
||||
unity = get_unity_connection()
|
||||
|
||||
# Parameter validation
|
||||
if not object_name or not isinstance(object_name, str):
|
||||
return f"Error creating prefab: object_name must be a valid string"
|
||||
|
||||
if not prefab_path or not isinstance(prefab_path, str):
|
||||
return f"Error creating prefab: prefab_path must be a valid string"
|
||||
|
||||
# Check if the GameObject exists
|
||||
found_objects = unity.send_command("FIND_OBJECTS_BY_NAME", {
|
||||
"name": object_name
|
||||
}).get("objects", [])
|
||||
|
||||
if not found_objects:
|
||||
return f"GameObject '{object_name}' not found in the scene."
|
||||
|
||||
# Verify prefab path has proper extension
|
||||
if not prefab_path.lower().endswith('.prefab'):
|
||||
prefab_path = f"{prefab_path}.prefab"
|
||||
|
||||
response = get_unity_connection().send_command("CREATE_PREFAB", {
|
||||
# Check if a prefab already exists at this path
|
||||
prefab_dir = '/'.join(prefab_path.split('/')[:-1]) or "Assets"
|
||||
prefab_name = prefab_path.split('/')[-1]
|
||||
|
||||
prefab_assets = unity.send_command("GET_ASSET_LIST", {
|
||||
"type": "Prefab",
|
||||
"search_pattern": prefab_name,
|
||||
"folder": prefab_dir
|
||||
}).get("assets", [])
|
||||
|
||||
prefab_exists = any(asset.get("path") == prefab_path for asset in prefab_assets)
|
||||
if prefab_exists and not overwrite:
|
||||
return f"Prefab already exists at '{prefab_path}'. Use overwrite=True to replace it."
|
||||
|
||||
response = unity.send_command("CREATE_PREFAB", {
|
||||
"object_name": object_name,
|
||||
"prefab_path": prefab_path
|
||||
"prefab_path": prefab_path,
|
||||
"overwrite": overwrite
|
||||
})
|
||||
|
||||
if not response.get("success", False):
|
||||
|
|
@ -158,7 +231,27 @@ def register_asset_tools(mcp: FastMCP):
|
|||
str: Success message or error details
|
||||
"""
|
||||
try:
|
||||
response = get_unity_connection().send_command("APPLY_PREFAB", {
|
||||
unity = get_unity_connection()
|
||||
|
||||
# Check if the GameObject exists
|
||||
found_objects = unity.send_command("FIND_OBJECTS_BY_NAME", {
|
||||
"name": object_name
|
||||
}).get("objects", [])
|
||||
|
||||
if not found_objects:
|
||||
return f"GameObject '{object_name}' not found in the scene."
|
||||
|
||||
# Check if the object is a prefab instance
|
||||
object_props = unity.send_command("GET_OBJECT_PROPERTIES", {
|
||||
"name": object_name
|
||||
})
|
||||
|
||||
# Try to extract prefab status from properties
|
||||
is_prefab_instance = object_props.get("isPrefabInstance", False)
|
||||
if not is_prefab_instance:
|
||||
return f"GameObject '{object_name}' is not a prefab instance."
|
||||
|
||||
response = unity.send_command("APPLY_PREFAB", {
|
||||
"object_name": object_name
|
||||
})
|
||||
return response.get("message", "Prefab changes applied successfully")
|
||||
|
|
|
|||
|
|
@ -92,6 +92,34 @@ def register_editor_tools(mcp: FastMCP):
|
|||
str: Success message or error details
|
||||
"""
|
||||
try:
|
||||
# Validate platform
|
||||
valid_platforms = ["windows", "mac", "linux", "android", "ios", "webgl"]
|
||||
if platform.lower() not in valid_platforms:
|
||||
return f"Error: '{platform}' is not a valid platform. Valid platforms are: {', '.join(valid_platforms)}"
|
||||
|
||||
# Check if build_path exists and is writable
|
||||
import os
|
||||
|
||||
# Check if the directory exists
|
||||
build_dir = os.path.dirname(build_path)
|
||||
if not os.path.exists(build_dir):
|
||||
return f"Error: Build directory '{build_dir}' does not exist. Please create it first."
|
||||
|
||||
# Check if the directory is writable
|
||||
if not os.access(build_dir, os.W_OK):
|
||||
return f"Error: Build directory '{build_dir}' is not writable."
|
||||
|
||||
# If the build path itself exists, check if it's a file or directory
|
||||
if os.path.exists(build_path):
|
||||
if os.path.isfile(build_path):
|
||||
# If it's a file, check if it's writable
|
||||
if not os.access(build_path, os.W_OK):
|
||||
return f"Error: Existing build file '{build_path}' is not writable."
|
||||
elif os.path.isdir(build_path):
|
||||
# If it's a directory, check if it's writable
|
||||
if not os.access(build_path, os.W_OK):
|
||||
return f"Error: Existing build directory '{build_path}' is not writable."
|
||||
|
||||
response = get_unity_connection().send_command("EDITOR_CONTROL", {
|
||||
"command": "BUILD",
|
||||
"params": {
|
||||
|
|
@ -104,17 +132,41 @@ def register_editor_tools(mcp: FastMCP):
|
|||
return f"Error building project: {str(e)}"
|
||||
|
||||
@mcp.tool()
|
||||
def execute_command(ctx: Context, command_name: str) -> str:
|
||||
def execute_command(ctx: Context, command_name: str, validate_command: bool = True) -> str:
|
||||
"""Execute a specific editor command or custom script within the Unity editor.
|
||||
|
||||
Args:
|
||||
command_name: Name of the editor command to execute (e.g., "Edit/Preferences")
|
||||
validate_command: Whether to validate the command existence before executing (default: True)
|
||||
|
||||
Returns:
|
||||
str: Success message or error details
|
||||
"""
|
||||
try:
|
||||
response = get_unity_connection().send_command("EDITOR_CONTROL", {
|
||||
unity = get_unity_connection()
|
||||
|
||||
# Optionally validate if the command exists
|
||||
if validate_command:
|
||||
# Get a list of available commands from Unity
|
||||
available_commands = unity.send_command("EDITOR_CONTROL", {
|
||||
"command": "GET_AVAILABLE_COMMANDS"
|
||||
}).get("commands", [])
|
||||
|
||||
# Check if the command exists in the list
|
||||
if available_commands and command_name not in available_commands:
|
||||
# If command doesn't exist, try to find similar commands as suggestions
|
||||
similar_commands = [cmd for cmd in available_commands if command_name.lower() in cmd.lower()]
|
||||
suggestion_msg = ""
|
||||
if similar_commands:
|
||||
suggestion_msg = f" Did you mean one of these: {', '.join(similar_commands[:5])}"
|
||||
if len(similar_commands) > 5:
|
||||
suggestion_msg += " or others?"
|
||||
else:
|
||||
suggestion_msg += "?"
|
||||
|
||||
return f"Error: Command '{command_name}' not found.{suggestion_msg}"
|
||||
|
||||
response = unity.send_command("EDITOR_CONTROL", {
|
||||
"command": "EXECUTE_COMMAND",
|
||||
"params": {
|
||||
"commandName": command_name
|
||||
|
|
|
|||
|
|
@ -10,7 +10,8 @@ def register_material_tools(mcp: FastMCP):
|
|||
ctx: Context,
|
||||
object_name: str,
|
||||
material_name: str = None,
|
||||
color: List[float] = None
|
||||
color: List[float] = None,
|
||||
create_if_missing: bool = True
|
||||
) -> str:
|
||||
"""
|
||||
Apply or create a material for a game object.
|
||||
|
|
@ -19,14 +20,56 @@ def register_material_tools(mcp: FastMCP):
|
|||
object_name: Target game object.
|
||||
material_name: Optional material name.
|
||||
color: Optional [R, G, B] values (0.0-1.0).
|
||||
create_if_missing: Whether to create the material if it doesn't exist (default: True).
|
||||
"""
|
||||
try:
|
||||
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."
|
||||
|
||||
# If a material name is specified, check if it exists
|
||||
if material_name:
|
||||
material_assets = unity.send_command("GET_ASSET_LIST", {
|
||||
"type": "Material",
|
||||
"search_pattern": material_name,
|
||||
"folder": "Assets/Materials"
|
||||
}).get("assets", [])
|
||||
|
||||
material_exists = any(asset.get("name") == material_name for asset in material_assets)
|
||||
|
||||
if not material_exists and not create_if_missing:
|
||||
return f"Material '{material_name}' not found. Use create_if_missing=True to create it."
|
||||
|
||||
# Validate color values if provided
|
||||
if color:
|
||||
# Check if color has the right number of components (RGB or RGBA)
|
||||
if not (len(color) == 3 or len(color) == 4):
|
||||
return f"Error: Color must have 3 (RGB) or 4 (RGBA) components, but got {len(color)}."
|
||||
|
||||
# Check if all color values are in the 0-1 range
|
||||
for i, value in enumerate(color):
|
||||
if not isinstance(value, (int, float)):
|
||||
return f"Error: Color component at index {i} is not a number."
|
||||
|
||||
if value < 0.0 or value > 1.0:
|
||||
channel = "RGBA"[i] if i < 4 else f"component {i}"
|
||||
return f"Error: Color {channel} value must be in the range 0.0-1.0, but got {value}."
|
||||
|
||||
# Set up parameters for the command
|
||||
params = {"object_name": object_name}
|
||||
if material_name:
|
||||
params["material_name"] = material_name
|
||||
params["create_if_missing"] = create_if_missing
|
||||
if color:
|
||||
params["color"] = color
|
||||
|
||||
result = unity.send_command("SET_MATERIAL", params)
|
||||
return f"Applied material to {object_name}: {result.get('material_name', 'unknown')}"
|
||||
except Exception as e:
|
||||
|
|
|
|||
|
|
@ -28,6 +28,19 @@ def register_scene_tools(mcp: FastMCP):
|
|||
"""
|
||||
try:
|
||||
unity = get_unity_connection()
|
||||
|
||||
# Check if the scene exists in the project
|
||||
scenes = unity.send_command("GET_ASSET_LIST", {
|
||||
"type": "Scene",
|
||||
"search_pattern": scene_path.split('/')[-1],
|
||||
"folder": '/'.join(scene_path.split('/')[:-1]) or "Assets"
|
||||
}).get("assets", [])
|
||||
|
||||
# Check if any scene matches the exact path
|
||||
scene_exists = any(scene.get("path") == scene_path for scene in scenes)
|
||||
if not scene_exists:
|
||||
return f"Scene at '{scene_path}' not found in the project."
|
||||
|
||||
result = unity.send_command("OPEN_SCENE", {"scene_path": scene_path})
|
||||
return result.get("message", "Scene opened successfully")
|
||||
except Exception as e:
|
||||
|
|
@ -48,19 +61,36 @@ def register_scene_tools(mcp: FastMCP):
|
|||
return f"Error saving scene: {str(e)}"
|
||||
|
||||
@mcp.tool()
|
||||
def new_scene(ctx: Context, scene_path: str) -> str:
|
||||
def new_scene(ctx: Context, scene_path: str, overwrite: bool = False) -> str:
|
||||
"""Create a new empty scene in the Unity editor.
|
||||
|
||||
Args:
|
||||
scene_path: Full path where the new scene should be saved (e.g., "Assets/Scenes/NewScene.unity")
|
||||
overwrite: Whether to overwrite if scene already exists (default: False)
|
||||
|
||||
Returns:
|
||||
str: Success message or error details
|
||||
"""
|
||||
try:
|
||||
unity = get_unity_connection()
|
||||
|
||||
# Check if a scene with this path already exists
|
||||
scenes = unity.send_command("GET_ASSET_LIST", {
|
||||
"type": "Scene",
|
||||
"search_pattern": scene_path.split('/')[-1],
|
||||
"folder": '/'.join(scene_path.split('/')[:-1]) or "Assets"
|
||||
}).get("assets", [])
|
||||
|
||||
# Check if any scene matches the exact path
|
||||
scene_exists = any(scene.get("path") == scene_path for scene in scenes)
|
||||
if scene_exists and not overwrite:
|
||||
return f"Scene at '{scene_path}' already exists. Use overwrite=True to replace it."
|
||||
|
||||
# Create new scene
|
||||
result = unity.send_command("NEW_SCENE", {"scene_path": scene_path})
|
||||
result = unity.send_command("NEW_SCENE", {
|
||||
"scene_path": scene_path,
|
||||
"overwrite": overwrite
|
||||
})
|
||||
|
||||
# Save the scene to ensure it's properly created
|
||||
unity.send_command("SAVE_SCENE")
|
||||
|
|
@ -115,7 +145,8 @@ def register_scene_tools(mcp: FastMCP):
|
|||
name: str = None,
|
||||
location: List[float] = None,
|
||||
rotation: List[float] = None,
|
||||
scale: List[float] = None
|
||||
scale: List[float] = None,
|
||||
replace_if_exists: bool = False
|
||||
) -> str:
|
||||
"""
|
||||
Create a game object in the Unity scene.
|
||||
|
|
@ -126,12 +157,27 @@ def register_scene_tools(mcp: FastMCP):
|
|||
location: [x, y, z] position (defaults to [0, 0, 0]).
|
||||
rotation: [x, y, z] rotation in degrees (defaults to [0, 0, 0]).
|
||||
scale: [x, y, z] scale factors (defaults to [1, 1, 1]).
|
||||
replace_if_exists: Whether to replace if an object with the same name exists (default: False)
|
||||
|
||||
Returns:
|
||||
Confirmation message with the created object's name.
|
||||
"""
|
||||
try:
|
||||
unity = get_unity_connection()
|
||||
|
||||
# Check if an object with the specified name already exists (if name is provided)
|
||||
if name:
|
||||
found_objects = unity.send_command("FIND_OBJECTS_BY_NAME", {
|
||||
"name": name
|
||||
}).get("objects", [])
|
||||
|
||||
if found_objects and not replace_if_exists:
|
||||
return f"Object with name '{name}' already exists. Use replace_if_exists=True to replace it."
|
||||
elif found_objects and replace_if_exists:
|
||||
# Delete the existing object
|
||||
unity.send_command("DELETE_OBJECT", {"name": name})
|
||||
|
||||
# Create the new object
|
||||
params = {
|
||||
"type": type.upper(),
|
||||
"location": location or [0, 0, 0],
|
||||
|
|
@ -140,6 +186,7 @@ def register_scene_tools(mcp: FastMCP):
|
|||
}
|
||||
if name:
|
||||
params["name"] = name
|
||||
|
||||
result = unity.send_command("CREATE_OBJECT", params)
|
||||
return f"Created {type} game object: {result['name']}"
|
||||
except Exception as e:
|
||||
|
|
@ -180,6 +227,48 @@ def register_scene_tools(mcp: FastMCP):
|
|||
"""
|
||||
try:
|
||||
unity = get_unity_connection()
|
||||
|
||||
# Check if the object exists
|
||||
found_objects = unity.send_command("FIND_OBJECTS_BY_NAME", {
|
||||
"name": name
|
||||
}).get("objects", [])
|
||||
|
||||
if not found_objects:
|
||||
return f"Object with name '{name}' not found in the scene."
|
||||
|
||||
# If set_parent is provided, check if parent object exists
|
||||
if set_parent is not None:
|
||||
parent_objects = unity.send_command("FIND_OBJECTS_BY_NAME", {
|
||||
"name": set_parent
|
||||
}).get("objects", [])
|
||||
|
||||
if not parent_objects:
|
||||
return f"Parent object '{set_parent}' not found in the scene."
|
||||
|
||||
# If we're adding a component, we could also check if it's already attached
|
||||
if add_component is not None:
|
||||
object_props = unity.send_command("GET_OBJECT_PROPERTIES", {
|
||||
"name": name
|
||||
})
|
||||
|
||||
components = object_props.get("components", [])
|
||||
component_exists = any(comp.get("type") == add_component for comp in components)
|
||||
|
||||
if component_exists:
|
||||
return f"Component '{add_component}' is already attached to '{name}'."
|
||||
|
||||
# If we're removing a component, check if it exists
|
||||
if remove_component is not None:
|
||||
object_props = unity.send_command("GET_OBJECT_PROPERTIES", {
|
||||
"name": name
|
||||
})
|
||||
|
||||
components = object_props.get("components", [])
|
||||
component_exists = any(comp.get("type") == remove_component for comp in components)
|
||||
|
||||
if not component_exists:
|
||||
return f"Component '{remove_component}' is not attached to '{name}'."
|
||||
|
||||
params = {"name": name}
|
||||
|
||||
# Add basic transform properties
|
||||
|
|
@ -212,15 +301,31 @@ def register_scene_tools(mcp: FastMCP):
|
|||
return f"Error modifying game object: {str(e)}"
|
||||
|
||||
@mcp.tool()
|
||||
def delete_object(ctx: Context, name: str) -> str:
|
||||
def delete_object(ctx: Context, name: str, ignore_missing: bool = False) -> str:
|
||||
"""
|
||||
Remove a game object from the scene.
|
||||
|
||||
Args:
|
||||
name: Name of the game object to delete.
|
||||
ignore_missing: Whether to silently ignore if the object doesn't exist (default: False)
|
||||
|
||||
Returns:
|
||||
str: Success message or error details
|
||||
"""
|
||||
try:
|
||||
unity = get_unity_connection()
|
||||
|
||||
# Check if the object exists
|
||||
found_objects = unity.send_command("FIND_OBJECTS_BY_NAME", {
|
||||
"name": name
|
||||
}).get("objects", [])
|
||||
|
||||
if not found_objects:
|
||||
if ignore_missing:
|
||||
return f"No object named '{name}' found to delete. Ignoring."
|
||||
else:
|
||||
return f"Error: Object '{name}' not found in the scene."
|
||||
|
||||
result = unity.send_command("DELETE_OBJECT", {"name": name})
|
||||
return f"Deleted game object: {name}"
|
||||
except Exception as e:
|
||||
|
|
|
|||
|
|
@ -31,7 +31,8 @@ def register_script_tools(mcp: FastMCP):
|
|||
script_name: str,
|
||||
script_type: str = "MonoBehaviour",
|
||||
namespace: str = None,
|
||||
template: str = None
|
||||
template: str = None,
|
||||
overwrite: bool = False
|
||||
) -> str:
|
||||
"""Create a new Unity script file.
|
||||
|
||||
|
|
@ -41,17 +42,32 @@ def register_script_tools(mcp: FastMCP):
|
|||
script_type: Type of script (e.g., MonoBehaviour, ScriptableObject)
|
||||
namespace: Optional namespace for the script
|
||||
template: Optional custom template to use
|
||||
overwrite: Whether to overwrite if script already exists (default: False)
|
||||
|
||||
Returns:
|
||||
str: Success message or error details
|
||||
"""
|
||||
try:
|
||||
# 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."
|
||||
|
||||
# Send command to Unity to create the script
|
||||
response = get_unity_connection().send_command("CREATE_SCRIPT", {
|
||||
response = unity.send_command("CREATE_SCRIPT", {
|
||||
"script_name": script_name,
|
||||
"script_type": script_type,
|
||||
"namespace": namespace,
|
||||
"template": template
|
||||
"template": template,
|
||||
"overwrite": overwrite
|
||||
})
|
||||
return response.get("message", "Script created successfully")
|
||||
except Exception as e:
|
||||
|
|
@ -61,7 +77,9 @@ def register_script_tools(mcp: FastMCP):
|
|||
def update_script(
|
||||
ctx: Context,
|
||||
script_path: str,
|
||||
content: str
|
||||
content: str,
|
||||
create_if_missing: bool = False,
|
||||
create_folder_if_missing: bool = False
|
||||
) -> str:
|
||||
"""Update the contents of an existing Unity script.
|
||||
|
||||
|
|
@ -69,13 +87,66 @@ def register_script_tools(mcp: FastMCP):
|
|||
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()
|
||||
|
||||
# Check if the script exists first
|
||||
existing_script_response = unity.send_command("VIEW_SCRIPT", {
|
||||
"script_path": script_path
|
||||
})
|
||||
|
||||
# If the script doesn't exist
|
||||
if "content" not in existing_script_response:
|
||||
if not create_if_missing:
|
||||
return f"Script at '{script_path}' not found. Use create_if_missing=True to create it."
|
||||
|
||||
# If we should create the missing script
|
||||
script_name = script_path.split("/")[-1].replace(".cs", "")
|
||||
script_folder = "/".join(script_path.split("/")[:-1])
|
||||
|
||||
# Check if the parent directory exists
|
||||
if script_folder:
|
||||
folder_exists = False
|
||||
try:
|
||||
# Try to list scripts in the folder to see if it exists
|
||||
folder_response = unity.send_command("LIST_SCRIPTS", {
|
||||
"folder_path": script_folder
|
||||
})
|
||||
# If we didn't get an error, the folder exists
|
||||
folder_exists = "error" not in folder_response
|
||||
except:
|
||||
folder_exists = False
|
||||
|
||||
if not folder_exists:
|
||||
if not create_folder_if_missing:
|
||||
return f"Parent directory '{script_folder}' does not exist. Use create_folder_if_missing=True to create it."
|
||||
|
||||
# Create the directory structure
|
||||
try:
|
||||
response = unity.send_command("CREATE_FOLDER", {
|
||||
"folder_path": script_folder
|
||||
})
|
||||
if "error" in response:
|
||||
return f"Error creating directory '{script_folder}': {response.get('error')}"
|
||||
except Exception as folder_error:
|
||||
return f"Error creating directory '{script_folder}': {str(folder_error)}"
|
||||
|
||||
# Create the script with the provided content
|
||||
response = unity.send_command("CREATE_SCRIPT", {
|
||||
"script_name": script_name,
|
||||
"script_folder": script_folder,
|
||||
"content": content
|
||||
})
|
||||
return response.get("message", "Script created successfully")
|
||||
|
||||
# Send command to Unity to update the script
|
||||
response = get_unity_connection().send_command("UPDATE_SCRIPT", {
|
||||
response = unity.send_command("UPDATE_SCRIPT", {
|
||||
"script_path": script_path,
|
||||
"content": content
|
||||
})
|
||||
|
|
@ -110,7 +181,8 @@ def register_script_tools(mcp: FastMCP):
|
|||
def attach_script(
|
||||
ctx: Context,
|
||||
object_name: str,
|
||||
script_name: str
|
||||
script_name: str,
|
||||
script_path: str = None
|
||||
) -> str:
|
||||
"""Attach a script component to a GameObject.
|
||||
|
||||
|
|
@ -118,15 +190,85 @@ def register_script_tools(mcp: FastMCP):
|
|||
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
|
||||
})
|
||||
|
||||
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}'."
|
||||
|
||||
# Send command to Unity to attach the script
|
||||
response = get_unity_connection().send_command("ATTACH_SCRIPT", {
|
||||
response = unity.send_command("ATTACH_SCRIPT", {
|
||||
"object_name": object_name,
|
||||
"script_name": script_name
|
||||
"script_name": script_name,
|
||||
"script_path": script_path
|
||||
})
|
||||
return response.get("message", "Script attached successfully")
|
||||
except Exception as e:
|
||||
|
|
|
|||
Loading…
Reference in New Issue