2025-03-18 19:00:50 +08:00
|
|
|
from typing import Optional
|
|
|
|
|
from mcp.server.fastmcp import FastMCP, Context
|
|
|
|
|
from unity_connection import get_unity_connection
|
|
|
|
|
|
|
|
|
|
def register_asset_tools(mcp: FastMCP):
|
|
|
|
|
"""Register all asset management tools with the MCP server."""
|
|
|
|
|
|
|
|
|
|
@mcp.tool()
|
|
|
|
|
def import_asset(
|
|
|
|
|
ctx: Context,
|
|
|
|
|
source_path: str,
|
2025-03-18 21:52:41 +08:00
|
|
|
target_path: str,
|
|
|
|
|
overwrite: bool = False
|
2025-03-18 19:00:50 +08:00
|
|
|
) -> str:
|
|
|
|
|
"""Import an asset (e.g., 3D model, texture) into the Unity project.
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
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)
|
2025-03-18 21:52:41 +08:00
|
|
|
overwrite: Whether to overwrite if an asset already exists at the target path (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 19:00:50 +08:00
|
|
|
# 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"
|
2025-03-18 21:52:41 +08:00
|
|
|
|
|
|
|
|
# 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."
|
2025-03-18 19:00:50 +08:00
|
|
|
|
2025-03-18 21:52:41 +08:00
|
|
|
response = unity.send_command("IMPORT_ASSET", {
|
2025-03-18 19:00:50 +08:00
|
|
|
"source_path": source_path,
|
2025-03-18 21:52:41 +08:00
|
|
|
"target_path": target_path,
|
|
|
|
|
"overwrite": overwrite
|
2025-03-18 19:00:50 +08:00
|
|
|
})
|
|
|
|
|
|
|
|
|
|
if not response.get("success", False):
|
|
|
|
|
return f"Error importing asset: {response.get('error', 'Unknown error')} (Source: {source_path}, Target: {target_path})"
|
|
|
|
|
|
|
|
|
|
return response.get("message", "Asset imported successfully")
|
|
|
|
|
except Exception as e:
|
|
|
|
|
return f"Error importing asset: {str(e)} (Source: {source_path}, Target: {target_path})"
|
|
|
|
|
|
|
|
|
|
@mcp.tool()
|
|
|
|
|
def instantiate_prefab(
|
|
|
|
|
ctx: Context,
|
|
|
|
|
prefab_path: str,
|
|
|
|
|
position_x: float = 0.0,
|
|
|
|
|
position_y: float = 0.0,
|
|
|
|
|
position_z: float = 0.0,
|
|
|
|
|
rotation_x: float = 0.0,
|
|
|
|
|
rotation_y: float = 0.0,
|
|
|
|
|
rotation_z: float = 0.0
|
|
|
|
|
) -> str:
|
|
|
|
|
"""Instantiate a prefab into the current scene at a specified location.
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
ctx: The MCP context
|
|
|
|
|
prefab_path: Path to the prefab asset (relative to Assets folder)
|
|
|
|
|
position_x: X position in world space (default: 0.0)
|
|
|
|
|
position_y: Y position in world space (default: 0.0)
|
|
|
|
|
position_z: Z position in world space (default: 0.0)
|
|
|
|
|
rotation_x: X rotation in degrees (default: 0.0)
|
|
|
|
|
rotation_y: Y rotation in degrees (default: 0.0)
|
|
|
|
|
rotation_z: Z rotation in degrees (default: 0.0)
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
str: Success message or error details
|
|
|
|
|
"""
|
|
|
|
|
try:
|
2025-03-18 21:52:41 +08:00
|
|
|
unity = get_unity_connection()
|
|
|
|
|
|
2025-03-18 19:00:50 +08:00
|
|
|
# Parameter validation
|
|
|
|
|
if not prefab_path or not isinstance(prefab_path, str):
|
|
|
|
|
return f"Error instantiating prefab: prefab_path must be a valid string"
|
|
|
|
|
|
|
|
|
|
# Validate numeric parameters
|
|
|
|
|
position_params = {
|
|
|
|
|
"position_x": position_x,
|
|
|
|
|
"position_y": position_y,
|
|
|
|
|
"position_z": position_z,
|
|
|
|
|
"rotation_x": rotation_x,
|
|
|
|
|
"rotation_y": rotation_y,
|
|
|
|
|
"rotation_z": rotation_z
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for param_name, param_value in position_params.items():
|
|
|
|
|
if not isinstance(param_value, (int, float)):
|
|
|
|
|
return f"Error instantiating prefab: {param_name} must be a number"
|
|
|
|
|
|
2025-03-18 21:52:41 +08:00
|
|
|
# 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", {
|
2025-03-18 19:00:50 +08:00
|
|
|
"prefab_path": prefab_path,
|
|
|
|
|
"position_x": position_x,
|
|
|
|
|
"position_y": position_y,
|
|
|
|
|
"position_z": position_z,
|
|
|
|
|
"rotation_x": rotation_x,
|
|
|
|
|
"rotation_y": rotation_y,
|
|
|
|
|
"rotation_z": rotation_z
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
if not response.get("success", False):
|
|
|
|
|
return f"Error instantiating prefab: {response.get('error', 'Unknown error')} (Path: {prefab_path})"
|
|
|
|
|
|
|
|
|
|
return f"Prefab instantiated successfully as '{response.get('instance_name', 'unknown')}'"
|
|
|
|
|
except Exception as e:
|
|
|
|
|
return f"Error instantiating prefab: {str(e)} (Path: {prefab_path})"
|
|
|
|
|
|
|
|
|
|
@mcp.tool()
|
|
|
|
|
def create_prefab(
|
|
|
|
|
ctx: Context,
|
|
|
|
|
object_name: str,
|
2025-03-18 21:52:41 +08:00
|
|
|
prefab_path: str,
|
|
|
|
|
overwrite: bool = False
|
2025-03-18 19:00:50 +08:00
|
|
|
) -> str:
|
|
|
|
|
"""Create a new prefab asset from a GameObject in the scene.
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
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)
|
2025-03-18 21:52:41 +08:00
|
|
|
overwrite: Whether to overwrite if a prefab already exists at the path (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 19:00:50 +08:00
|
|
|
# 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"
|
2025-03-18 21:52:41 +08:00
|
|
|
|
|
|
|
|
# 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."
|
2025-03-18 19:00:50 +08:00
|
|
|
|
|
|
|
|
# Verify prefab path has proper extension
|
|
|
|
|
if not prefab_path.lower().endswith('.prefab'):
|
|
|
|
|
prefab_path = f"{prefab_path}.prefab"
|
|
|
|
|
|
2025-03-18 21:52:41 +08:00
|
|
|
# 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", {
|
2025-03-18 19:00:50 +08:00
|
|
|
"object_name": object_name,
|
2025-03-18 21:52:41 +08:00
|
|
|
"prefab_path": prefab_path,
|
|
|
|
|
"overwrite": overwrite
|
2025-03-18 19:00:50 +08:00
|
|
|
})
|
|
|
|
|
|
|
|
|
|
if not response.get("success", False):
|
|
|
|
|
return f"Error creating prefab: {response.get('error', 'Unknown error')} (Object: {object_name}, Path: {prefab_path})"
|
|
|
|
|
|
|
|
|
|
return f"Prefab created successfully at {response.get('path', prefab_path)}"
|
|
|
|
|
except Exception as e:
|
|
|
|
|
return f"Error creating prefab: {str(e)} (Object: {object_name}, Path: {prefab_path})"
|
|
|
|
|
|
|
|
|
|
@mcp.tool()
|
|
|
|
|
def apply_prefab(
|
|
|
|
|
ctx: Context,
|
|
|
|
|
object_name: str
|
|
|
|
|
) -> str:
|
|
|
|
|
"""Apply changes made to a prefab instance back to the original prefab asset.
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
ctx: The MCP context
|
|
|
|
|
object_name: Name of the prefab instance in the scene
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
str: Success message or error details
|
|
|
|
|
"""
|
|
|
|
|
try:
|
2025-03-18 21:52:41 +08:00
|
|
|
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", {
|
2025-03-18 19:00:50 +08:00
|
|
|
"object_name": object_name
|
|
|
|
|
})
|
|
|
|
|
return response.get("message", "Prefab changes applied successfully")
|
|
|
|
|
except Exception as e:
|
|
|
|
|
return f"Error applying prefab changes: {str(e)}"
|