editor_tools.py•14 kB
"""
Editor Tools for Unreal MCP.
This module provides tools for controlling the Unreal Editor viewport and other editor functionality.
"""
import logging
from typing import Dict, List, Any, Optional
from mcp.server.fastmcp import FastMCP, Context
# Get logger
logger = logging.getLogger("UnrealMCP")
def register_editor_tools(mcp: FastMCP):
"""Register editor tools with the MCP server."""
@mcp.tool()
def get_actors_in_level(ctx: Context) -> List[Dict[str, Any]]:
"""Get a list of all actors in the current level."""
from unreal_mcp_server import get_unreal_connection
try:
unreal = get_unreal_connection()
if not unreal:
logger.warning("Failed to connect to Unreal Engine")
return []
response = unreal.send_command("get_actors_in_level", {})
if not response:
logger.warning("No response from Unreal Engine")
return []
# Log the complete response for debugging
logger.info(f"Complete response from Unreal: {response}")
# Check response format
if "result" in response and "actors" in response["result"]:
actors = response["result"]["actors"]
logger.info(f"Found {len(actors)} actors in level")
return actors
elif "actors" in response:
actors = response["actors"]
logger.info(f"Found {len(actors)} actors in level")
return actors
logger.warning(f"Unexpected response format: {response}")
return []
except Exception as e:
logger.error(f"Error getting actors: {e}")
return []
@mcp.tool()
def find_actors_by_name(ctx: Context, pattern: str) -> List[str]:
"""Find actors by name pattern."""
from unreal_mcp_server import get_unreal_connection
try:
unreal = get_unreal_connection()
if not unreal:
logger.warning("Failed to connect to Unreal Engine")
return []
response = unreal.send_command("find_actors_by_name", {
"pattern": pattern
})
if not response:
return []
return response.get("actors", [])
except Exception as e:
logger.error(f"Error finding actors: {e}")
return []
@mcp.tool()
def spawn_actor(
ctx: Context,
name: str,
type: str,
location: List[float] = [0.0, 0.0, 0.0],
rotation: List[float] = [0.0, 0.0, 0.0]
) -> Dict[str, Any]:
"""Create a new actor in the current level.
Args:
ctx: The MCP context
name: The name to give the new actor (must be unique)
type: The type of actor to create (e.g. StaticMeshActor, PointLight)
location: The [x, y, z] world location to spawn at
rotation: The [pitch, yaw, roll] rotation in degrees
Returns:
Dict containing the created actor's properties
"""
from unreal_mcp_server import get_unreal_connection
try:
unreal = get_unreal_connection()
if not unreal:
logger.error("Failed to connect to Unreal Engine")
return {"success": False, "message": "Failed to connect to Unreal Engine"}
# Ensure all parameters are properly formatted
params = {
"name": name,
"type": type.upper(), # Make sure type is uppercase
"location": location,
"rotation": rotation
}
# Validate location and rotation formats
for param_name in ["location", "rotation"]:
param_value = params[param_name]
if not isinstance(param_value, list) or len(param_value) != 3:
logger.error(f"Invalid {param_name} format: {param_value}. Must be a list of 3 float values.")
return {"success": False, "message": f"Invalid {param_name} format. Must be a list of 3 float values."}
# Ensure all values are float
params[param_name] = [float(val) for val in param_value]
logger.info(f"Creating actor '{name}' of type '{type}' with params: {params}")
response = unreal.send_command("spawn_actor", params)
if not response:
logger.error("No response from Unreal Engine")
return {"success": False, "message": "No response from Unreal Engine"}
# Log the complete response for debugging
logger.info(f"Actor creation response: {response}")
# Handle error responses correctly
if response.get("status") == "error":
error_message = response.get("error", "Unknown error")
logger.error(f"Error creating actor: {error_message}")
return {"success": False, "message": error_message}
return response
except Exception as e:
error_msg = f"Error creating actor: {e}"
logger.error(error_msg)
return {"success": False, "message": error_msg}
@mcp.tool()
def delete_actor(ctx: Context, name: str) -> Dict[str, Any]:
"""Delete an actor by name."""
from unreal_mcp_server import get_unreal_connection
try:
unreal = get_unreal_connection()
if not unreal:
logger.error("Failed to connect to Unreal Engine")
return {"success": False, "message": "Failed to connect to Unreal Engine"}
response = unreal.send_command("delete_actor", {
"name": name
})
return response or {}
except Exception as e:
logger.error(f"Error deleting actor: {e}")
return {}
@mcp.tool()
def set_actor_transform(
ctx: Context,
name: str,
location: List[float] = None,
rotation: List[float] = None,
scale: List[float] = None
) -> Dict[str, Any]:
"""Set the transform of an actor."""
from unreal_mcp_server import get_unreal_connection
try:
unreal = get_unreal_connection()
if not unreal:
logger.error("Failed to connect to Unreal Engine")
return {"success": False, "message": "Failed to connect to Unreal Engine"}
params = {"name": name}
if location is not None:
params["location"] = location
if rotation is not None:
params["rotation"] = rotation
if scale is not None:
params["scale"] = scale
response = unreal.send_command("set_actor_transform", params)
return response or {}
except Exception as e:
logger.error(f"Error setting transform: {e}")
return {}
@mcp.tool()
def get_actor_properties(ctx: Context, name: str) -> Dict[str, Any]:
"""Get all properties of an actor."""
from unreal_mcp_server import get_unreal_connection
try:
unreal = get_unreal_connection()
if not unreal:
logger.error("Failed to connect to Unreal Engine")
return {"success": False, "message": "Failed to connect to Unreal Engine"}
response = unreal.send_command("get_actor_properties", {
"name": name
})
return response or {}
except Exception as e:
logger.error(f"Error getting properties: {e}")
return {}
@mcp.tool()
def set_actor_property(
ctx: Context,
name: str,
property_name: str,
property_value,
) -> Dict[str, Any]:
"""
Set a property on an actor.
Args:
name: Name of the actor
property_name: Name of the property to set
property_value: Value to set the property to
Returns:
Dict containing response from Unreal with operation status
"""
from unreal_mcp_server import get_unreal_connection
try:
unreal = get_unreal_connection()
if not unreal:
logger.error("Failed to connect to Unreal Engine")
return {"success": False, "message": "Failed to connect to Unreal Engine"}
response = unreal.send_command("set_actor_property", {
"name": name,
"property_name": property_name,
"property_value": property_value
})
if not response:
logger.error("No response from Unreal Engine")
return {"success": False, "message": "No response from Unreal Engine"}
logger.info(f"Set actor property response: {response}")
return response
except Exception as e:
error_msg = f"Error setting actor property: {e}"
logger.error(error_msg)
return {"success": False, "message": error_msg}
# @mcp.tool() commented out because it's buggy
def focus_viewport(
ctx: Context,
target: str = None,
location: List[float] = None,
distance: float = 1000.0,
orientation: List[float] = None
) -> Dict[str, Any]:
"""
Focus the viewport on a specific actor or location.
Args:
target: Name of the actor to focus on (if provided, location is ignored)
location: [X, Y, Z] coordinates to focus on (used if target is None)
distance: Distance from the target/location
orientation: Optional [Pitch, Yaw, Roll] for the viewport camera
Returns:
Response from Unreal Engine
"""
from unreal_mcp_server import get_unreal_connection
try:
unreal = get_unreal_connection()
if not unreal:
logger.error("Failed to connect to Unreal Engine")
return {"success": False, "message": "Failed to connect to Unreal Engine"}
params = {}
if target:
params["target"] = target
elif location:
params["location"] = location
if distance:
params["distance"] = distance
if orientation:
params["orientation"] = orientation
response = unreal.send_command("focus_viewport", params)
return response or {}
except Exception as e:
logger.error(f"Error focusing viewport: {e}")
return {"status": "error", "message": str(e)}
@mcp.tool()
def spawn_blueprint_actor(
ctx: Context,
blueprint_name: str,
actor_name: str,
location: List[float] = [0.0, 0.0, 0.0],
rotation: List[float] = [0.0, 0.0, 0.0]
) -> Dict[str, Any]:
"""Spawn an actor from a Blueprint.
Args:
ctx: The MCP context
blueprint_name: Name of the Blueprint to spawn from
actor_name: Name to give the spawned actor
location: The [x, y, z] world location to spawn at
rotation: The [pitch, yaw, roll] rotation in degrees
Returns:
Dict containing the spawned actor's properties
"""
from unreal_mcp_server import get_unreal_connection
try:
unreal = get_unreal_connection()
if not unreal:
logger.error("Failed to connect to Unreal Engine")
return {"success": False, "message": "Failed to connect to Unreal Engine"}
# Ensure all parameters are properly formatted
params = {
"blueprint_name": blueprint_name,
"actor_name": actor_name,
"location": location or [0.0, 0.0, 0.0],
"rotation": rotation or [0.0, 0.0, 0.0]
}
# Validate location and rotation formats
for param_name in ["location", "rotation"]:
param_value = params[param_name]
if not isinstance(param_value, list) or len(param_value) != 3:
logger.error(f"Invalid {param_name} format: {param_value}. Must be a list of 3 float values.")
return {"success": False, "message": f"Invalid {param_name} format. Must be a list of 3 float values."}
# Ensure all values are float
params[param_name] = [float(val) for val in param_value]
logger.info(f"Spawning blueprint actor with params: {params}")
response = unreal.send_command("spawn_blueprint_actor", params)
if not response:
logger.error("No response from Unreal Engine")
return {"success": False, "message": "No response from Unreal Engine"}
logger.info(f"Spawn blueprint actor response: {response}")
return response
except Exception as e:
error_msg = f"Error spawning blueprint actor: {e}"
logger.error(error_msg)
return {"success": False, "message": error_msg}
logger.info("Editor tools registered successfully")