This commit is contained in:
yjj
2026-02-26 23:45:31 +08:00
parent aa599ea653
commit dd642c8585
79 changed files with 6044 additions and 0 deletions

View File

@@ -0,0 +1 @@
# Editor tools package

View File

@@ -0,0 +1 @@
# Asset tools package

View File

@@ -0,0 +1,51 @@
"""Asset registry tools for Unreal Engine via MCP."""
from typing import Dict
import logging
logger = logging.getLogger(__name__)
def register_tools(mcp, connection=None):
"""Register asset registry tools with the MCP server."""
# Import get_unreal_connection from parent module
import sys
import os
sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))))
from unreal_mcp_server import get_unreal_connection
@mcp.tool()
async def get_asset_references(asset_path: str) -> Dict:
"""Get all assets that reference the specified asset.
Args:
asset_path: Full path to the asset
Returns:
Dictionary containing list of referencing assets
"""
conn = get_unreal_connection()
if not conn:
return {"status": "error", "error": "Failed to connect to Unreal Engine"}
return conn.send_command("get_asset_references", {
"asset_path": asset_path
})
@mcp.tool()
async def get_asset_dependencies(asset_path: str) -> Dict:
"""Get all assets that the specified asset depends on.
Args:
asset_path: Full path to the asset
Returns:
Dictionary containing list of dependency assets
"""
conn = get_unreal_connection()
if not conn:
return {"status": "error", "error": "Failed to connect to Unreal Engine"}
return conn.send_command("get_asset_dependencies", {
"asset_path": asset_path
})
logger.info("Asset registry tools registered")

View File

@@ -0,0 +1,165 @@
"""Core asset management tools for Unreal Engine via MCP."""
from typing import Dict
import logging
logger = logging.getLogger(__name__)
def register_tools(mcp, connection=None):
"""Register core asset tools with the MCP server."""
# Import get_unreal_connection from parent module
import sys
import os
sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))))
from unreal_mcp_server import get_unreal_connection
@mcp.tool()
async def load_asset(asset_path: str) -> Dict:
"""Load an asset into memory.
Args:
asset_path: Full path to the asset to load
Returns:
Dictionary with load status and asset information
"""
conn = get_unreal_connection()
if not conn:
return {"status": "error", "error": "Failed to connect to Unreal Engine"}
return conn.send_command("load_asset", {
"asset_path": asset_path
})
@mcp.tool()
async def save_asset(asset_path: str, only_if_dirty: bool = True) -> Dict:
"""Save an asset to disk.
Args:
asset_path: Full path to the asset to save
only_if_dirty: Only save if the asset has unsaved changes
Returns:
Dictionary with save status
"""
conn = get_unreal_connection()
if not conn:
return {"status": "error", "error": "Failed to connect to Unreal Engine"}
return conn.send_command("save_asset", {
"asset_path": asset_path,
"only_if_dirty": only_if_dirty
})
@mcp.tool()
async def duplicate_asset(source_path: str, destination_path: str) -> Dict:
"""Duplicate an existing asset.
Args:
source_path: Path to the asset to duplicate
destination_path: Path for the new duplicated asset
Returns:
Dictionary with duplication status
"""
conn = get_unreal_connection()
if not conn:
return {"status": "error", "error": "Failed to connect to Unreal Engine"}
return conn.send_command("duplicate_asset", {
"source_path": source_path,
"destination_path": destination_path
})
@mcp.tool()
async def delete_asset(asset_path: str) -> Dict:
"""Delete an asset from the project.
Args:
asset_path: Full path to the asset to delete
Returns:
Dictionary with deletion status
"""
conn = get_unreal_connection()
if not conn:
return {"status": "error", "error": "Failed to connect to Unreal Engine"}
return conn.send_command("delete_asset", {
"asset_path": asset_path
})
@mcp.tool()
async def rename_asset(source_path: str, new_name: str) -> Dict:
"""Rename an existing asset.
Args:
source_path: Current path to the asset
new_name: New name for the asset (without path)
Returns:
Dictionary with rename status and new path
"""
conn = get_unreal_connection()
if not conn:
return {"status": "error", "error": "Failed to connect to Unreal Engine"}
return conn.send_command("rename_asset", {
"source_path": source_path,
"new_name": new_name
})
@mcp.tool()
async def move_asset(source_path: str, destination_path: str) -> Dict:
"""Move an asset to a different location.
Args:
source_path: Current path to the asset
destination_path: New full path for the asset
Returns:
Dictionary with move status
"""
conn = get_unreal_connection()
if not conn:
return {"status": "error", "error": "Failed to connect to Unreal Engine"}
return conn.send_command("move_asset", {
"source_path": source_path,
"destination_path": destination_path
})
@mcp.tool()
async def import_asset(file_path: str, destination_path: str) -> Dict:
"""Import an external file as an asset.
Args:
file_path: Path to the file on disk to import
destination_path: Content browser path where to import the asset
Returns:
Dictionary with import status and imported asset information
"""
conn = get_unreal_connection()
if not conn:
return {"status": "error", "error": "Failed to connect to Unreal Engine"}
return conn.send_command("import_asset", {
"file_path": file_path,
"destination_path": destination_path
})
@mcp.tool()
async def export_asset(asset_path: str, export_path: str) -> Dict:
"""Export an asset to an external file.
Args:
asset_path: Full path to the asset to export
export_path: File path where to export the asset
Returns:
Dictionary with export status
"""
conn = get_unreal_connection()
if not conn:
return {"status": "error", "error": "Failed to connect to Unreal Engine"}
return conn.send_command("export_asset", {
"asset_path": asset_path,
"export_path": export_path
})
logger.info("Core asset tools registered")

View File

@@ -0,0 +1,78 @@
"""Content browser tools for Unreal Engine via MCP."""
from typing import Dict
import logging
logger = logging.getLogger(__name__)
def register_tools(mcp, connection=None):
"""Register content browser tools with the MCP server."""
# Import get_unreal_connection from parent module
import sys
import os
sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))))
from unreal_mcp_server import get_unreal_connection
@mcp.tool()
async def list_assets(path: str = "/Game", type_filter: str = "", recursive: bool = False) -> Dict:
"""List all assets in a given path.
Args:
path: The content browser path to list (e.g., "/Game/MyFolder")
type_filter: Optional asset type filter (e.g., "StaticMesh", "Material")
recursive: Whether to search recursively in subdirectories
Returns:
Dictionary containing list of assets and count
"""
params = {
"path": path,
"recursive": recursive
}
if type_filter:
params["type_filter"] = type_filter
conn = get_unreal_connection()
if not conn:
return {"status": "error", "error": "Failed to connect to Unreal Engine"}
return conn.send_command("list_assets", params)
@mcp.tool()
async def get_asset_metadata(asset_path: str) -> Dict:
"""Get detailed metadata for a specific asset.
Args:
asset_path: Full path to the asset (e.g., "/Game/MyFolder/MyAsset")
Returns:
Dictionary containing asset metadata including tags and properties
"""
conn = get_unreal_connection()
if not conn:
return {"status": "error", "error": "Failed to connect to Unreal Engine"}
return conn.send_command("get_asset_metadata", {
"asset_path": asset_path
})
@mcp.tool()
async def search_assets(search_text: str, type_filter: str = "") -> Dict:
"""Search for assets by name or path.
Args:
search_text: Text to search for in asset names and paths
type_filter: Optional asset type filter
Returns:
Dictionary containing matching assets
"""
params = {"search_text": search_text}
if type_filter:
params["type_filter"] = type_filter
conn = get_unreal_connection()
if not conn:
return {"status": "error", "error": "Failed to connect to Unreal Engine"}
return conn.send_command("search_assets", params)
logger.info("Content browser tools registered")

View File

@@ -0,0 +1 @@
# Landscape tools package

View File

@@ -0,0 +1,186 @@
"""
Landscape Tools for Unreal MCP.
This module provides tools for managing landscapes in Unreal Engine through the Landscape Editor module.
Following Epic's official structure: Editor/Landscape module patterns.
"""
import logging
from typing import Dict, List, Any, Optional
from mcp.server.fastmcp import FastMCP, Context
# Get logger
logger = logging.getLogger("UnrealMCP")
def register_landscape_tools(mcp: FastMCP):
"""Register landscape tools with the MCP server."""
@mcp.tool()
def create_landscape(ctx: Context,
size_x: int = 127,
size_y: int = 127,
sections_per_component: int = 1,
quads_per_section: int = 63,
location_x: float = 0.0,
location_y: float = 0.0,
location_z: float = 0.0) -> Dict[str, Any]:
"""Create a landscape in the current level.
Args:
size_x: Landscape size in X direction (default: 127)
size_y: Landscape size in Y direction (default: 127)
sections_per_component: Number of sections per component (default: 1)
quads_per_section: Number of quads per section (default: 63)
location_x: X location of the landscape (default: 0.0)
location_y: Y location of the landscape (default: 0.0)
location_z: Z location of the landscape (default: 0.0)
Returns:
Dictionary containing the landscape creation result
Example:
create_landscape(size_x=255, size_y=255, location_z=100.0)
"""
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 {"error": "Failed to connect to Unreal Engine"}
response = unreal.send_command("create_landscape", {
"size_x": size_x,
"size_y": size_y,
"sections_per_component": sections_per_component,
"quads_per_section": quads_per_section,
"location": {
"x": location_x,
"y": location_y,
"z": location_z
}
})
if not response:
logger.warning("No response from Unreal Engine")
return {"error": "No response from Unreal Engine"}
logger.info(f"Created landscape with size {size_x}x{size_y}")
return response
except Exception as e:
logger.error(f"Error creating landscape: {e}")
return {"error": f"Error creating landscape: {str(e)}"}
@mcp.tool()
def modify_landscape(ctx: Context, modification_type: str = "sculpt") -> Dict[str, Any]:
"""Modify the landscape heightmap.
Args:
modification_type: Type of modification to perform (default: "sculpt")
Returns:
Dictionary containing the landscape modification result
Example:
modify_landscape("sculpt")
"""
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 {"error": "Failed to connect to Unreal Engine"}
response = unreal.send_command("modify_landscape", {
"modification_type": modification_type
})
if not response:
logger.warning("No response from Unreal Engine")
return {"error": "No response from Unreal Engine"}
logger.info(f"Modified landscape with type: {modification_type}")
return response
except Exception as e:
logger.error(f"Error modifying landscape: {e}")
return {"error": f"Error modifying landscape: {str(e)}"}
@mcp.tool()
def paint_landscape_layer(ctx: Context, layer_name: str) -> Dict[str, Any]:
"""Paint a landscape material layer.
Args:
layer_name: Name of the landscape layer to paint
Returns:
Dictionary containing the landscape painting result
Example:
paint_landscape_layer("Grass")
"""
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 {"error": "Failed to connect to Unreal Engine"}
response = unreal.send_command("paint_landscape_layer", {
"layer_name": layer_name
})
if not response:
logger.warning("No response from Unreal Engine")
return {"error": "No response from Unreal Engine"}
logger.info(f"Painted landscape layer: {layer_name}")
return response
except Exception as e:
logger.error(f"Error painting landscape layer: {e}")
return {"error": f"Error painting landscape layer: {str(e)}"}
@mcp.tool()
def get_landscape_info(ctx: Context) -> List[Dict[str, Any]]:
"""Get information about all landscapes in the current level.
Returns:
List of dictionaries containing landscape information
Example:
get_landscape_info()
"""
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_landscape_info", {})
if not response:
logger.warning("No response from Unreal Engine")
return []
# Check response format
if "result" in response and "landscapes" in response["result"]:
landscapes = response["result"]["landscapes"]
logger.info(f"Found {len(landscapes)} landscapes in level")
return landscapes
elif "landscapes" in response:
landscapes = response["landscapes"]
logger.info(f"Found {len(landscapes)} landscapes in level")
return landscapes
logger.warning(f"Unexpected response format: {response}")
return []
except Exception as e:
logger.error(f"Error getting landscape info: {e}")
return []

View File

@@ -0,0 +1 @@
# Level Editor tools package

View File

@@ -0,0 +1,255 @@
"""
Level Editor Tools for Unreal MCP.
This module provides tools for managing levels in Unreal Engine through the Level Editor module.
Following Epic's official structure: Editor/LevelEditor module patterns.
"""
import logging
from typing import Dict, List, Any, Optional
from mcp.server.fastmcp import FastMCP, Context
# Get logger
logger = logging.getLogger("UnrealMCP")
def register_level_tools(mcp: FastMCP):
"""Register level editor tools with the MCP server."""
@mcp.tool()
def create_level(ctx: Context, level_name: str) -> Dict[str, Any]:
"""Create a new level.
Args:
level_name: Name of the new level to create
Returns:
Dictionary containing the created level information
Example:
create_level("MyNewLevel")
"""
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 {"error": "Failed to connect to Unreal Engine"}
response = unreal.send_command("create_level", {"level_name": level_name})
if not response:
logger.warning("No response from Unreal Engine")
return {"error": "No response from Unreal Engine"}
logger.info(f"Created level: {level_name}")
return response
except Exception as e:
logger.error(f"Error creating level: {e}")
return {"error": f"Error creating level: {str(e)}"}
@mcp.tool()
def save_level(ctx: Context) -> Dict[str, Any]:
"""Save the current level.
Returns:
Dictionary containing the save operation result
Example:
save_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 {"error": "Failed to connect to Unreal Engine"}
response = unreal.send_command("save_level", {})
if not response:
logger.warning("No response from Unreal Engine")
return {"error": "No response from Unreal Engine"}
logger.info("Level saved successfully")
return response
except Exception as e:
logger.error(f"Error saving level: {e}")
return {"error": f"Error saving level: {str(e)}"}
@mcp.tool()
def load_level(ctx: Context, level_path: str) -> Dict[str, Any]:
"""Load a level.
Args:
level_path: Path to the level to load (e.g., "/Game/Maps/MyLevel")
Returns:
Dictionary containing the load operation result
Example:
load_level("/Game/Maps/MyLevel")
"""
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 {"error": "Failed to connect to Unreal Engine"}
response = unreal.send_command("load_level", {"level_path": level_path})
if not response:
logger.warning("No response from Unreal Engine")
return {"error": "No response from Unreal Engine"}
logger.info(f"Loaded level: {level_path}")
return response
except Exception as e:
logger.error(f"Error loading level: {e}")
return {"error": f"Error loading level: {str(e)}"}
@mcp.tool()
def set_level_visibility(ctx: Context, level_name: str, visible: bool = True) -> Dict[str, Any]:
"""Set the visibility of a level.
Args:
level_name: Name of the level to set visibility for
visible: Whether the level should be visible (default: True)
Returns:
Dictionary containing the visibility operation result
Example:
set_level_visibility("MyLevel", True)
"""
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 {"error": "Failed to connect to Unreal Engine"}
response = unreal.send_command("set_level_visibility", {
"level_name": level_name,
"visible": visible
})
if not response:
logger.warning("No response from Unreal Engine")
return {"error": "No response from Unreal Engine"}
logger.info(f"Set level {level_name} visibility to {visible}")
return response
except Exception as e:
logger.error(f"Error setting level visibility: {e}")
return {"error": f"Error setting level visibility: {str(e)}"}
@mcp.tool()
def create_streaming_level(ctx: Context, level_path: str) -> Dict[str, Any]:
"""Create a streaming level.
Args:
level_path: Path to the level to add as streaming level
Returns:
Dictionary containing the streaming level creation result
Example:
create_streaming_level("/Game/Maps/StreamingLevel")
"""
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 {"error": "Failed to connect to Unreal Engine"}
response = unreal.send_command("create_streaming_level", {"level_path": level_path})
if not response:
logger.warning("No response from Unreal Engine")
return {"error": "No response from Unreal Engine"}
logger.info(f"Created streaming level: {level_path}")
return response
except Exception as e:
logger.error(f"Error creating streaming level: {e}")
return {"error": f"Error creating streaming level: {str(e)}"}
@mcp.tool()
def load_streaming_level(ctx: Context, level_name: str) -> Dict[str, Any]:
"""Load a streaming level.
Args:
level_name: Name of the streaming level to load
Returns:
Dictionary containing the streaming level load result
Example:
load_streaming_level("StreamingLevel")
"""
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 {"error": "Failed to connect to Unreal Engine"}
response = unreal.send_command("load_streaming_level", {"level_name": level_name})
if not response:
logger.warning("No response from Unreal Engine")
return {"error": "No response from Unreal Engine"}
logger.info(f"Loaded streaming level: {level_name}")
return response
except Exception as e:
logger.error(f"Error loading streaming level: {e}")
return {"error": f"Error loading streaming level: {str(e)}"}
@mcp.tool()
def unload_streaming_level(ctx: Context, level_name: str) -> Dict[str, Any]:
"""Unload a streaming level.
Args:
level_name: Name of the streaming level to unload
Returns:
Dictionary containing the streaming level unload result
Example:
unload_streaming_level("StreamingLevel")
"""
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 {"error": "Failed to connect to Unreal Engine"}
response = unreal.send_command("unload_streaming_level", {"level_name": level_name})
if not response:
logger.warning("No response from Unreal Engine")
return {"error": "No response from Unreal Engine"}
logger.info(f"Unloaded streaming level: {level_name}")
return response
except Exception as e:
logger.error(f"Error unloading streaming level: {e}")
return {"error": f"Error unloading streaming level: {str(e)}"}

View File

@@ -0,0 +1,424 @@
"""
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],
static_mesh: str = None
) -> 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
static_mesh: Optional path to static mesh for StaticMeshActor (e.g. /Engine/BasicShapes/Cube.Cube)
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
}
# Add static_mesh parameter if provided
if static_mesh:
params["static_mesh"] = static_mesh
# 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}
@mcp.tool()
def take_screenshot(
ctx: Context,
filename: str,
show_ui: bool = False,
resolution: List[int] = None
) -> Dict[str, Any]:
"""Take a screenshot of the current viewport.
Args:
ctx: The MCP context
filename: Name for the screenshot file (without extension)
show_ui: Whether to include UI in the screenshot
resolution: Optional [width, height] for the screenshot
Returns:
Dict containing screenshot information
"""
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 = {
"filepath": filename,
"show_ui": show_ui
}
if resolution:
if isinstance(resolution, list) and len(resolution) == 2:
params["resolution"] = resolution
logger.info(f"Taking screenshot: {filename}")
response = unreal.send_command("take_screenshot", params)
if not response:
logger.error("No response from Unreal Engine")
return {"success": False, "message": "No response from Unreal Engine"}
return response
except Exception as e:
error_msg = f"Error taking screenshot: {e}"
logger.error(error_msg)
return {"success": False, "message": error_msg}
logger.info("Editor tools registered successfully")