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,68 @@
# UnrealAgentLink 工具能力(增量)
## 服务端请求Actor Management
- 统一变换接口 `actor.set_transform`
- 结构:`targets`(选择器) + `operation`(操作)
- `targets` 字段:
- `names`: 字符串数组,指定 Actor 名称。
- `paths`: 字符串数组,指定 Actor 路径。
- `filter`: 筛选器对象,支持 `class` (包含匹配), `name_pattern` (通配符), `exclude_classes` (排除类名数组)。
- `operation` 字段:
- `space`: `"World"` (默认) 或 `"Local"`
- `snap_to_floor`: `true` (执行贴地)。
- `set`: 绝对值设置 (`location`, `rotation`, `scale`)。
- `add`: 增量设置 (`location`, `rotation`, `scale`),支持负数。
- `multiply`: 倍乘设置 (`location`, `rotation`, `scale`)。
- 示例 1单体绝对设置Z=200
```json
{
"ver":"1.0","type":"req","id":"t1","method":"actor.set_transform",
"params":{
"targets": {"names": ["MyCube"]},
"operation": {
"set": {"location": {"z": 200}}
}
}
}
```
- 示例 2批量增量所有灯光 Z 轴上移 500局部坐标系
```json
{
"ver":"1.0","type":"req","id":"t2","method":"actor.set_transform",
"params":{
"targets": {
"filter": {"class": "Light"}
},
"operation": {
"space": "Local",
"add": {"location": {"z": 500}}
}
}
}
```
- 示例 3多选倍乘Cube_1 和 Sphere_2 放大 2 倍)
```json
{
"ver":"1.0","type":"req","id":"t3","method":"actor.set_transform",
"params":{
"targets": {
"names": ["Cube_1", "Sphere_2"]
},
"operation": {
"multiply": {"scale": {"x": 2, "y": 2, "z": 2}}
}
}
}
```
- 响应code 200
```json
{"ver":"1.0","type":"res","id":"t1","code":200,"result":{"count":1,"actors":[{"name":"MyCube",...}]}}
```

View File

@@ -0,0 +1,420 @@
"""
Blueprint Tools for Unreal MCP.
This module provides tools for creating and manipulating Blueprint assets in Unreal Engine.
"""
import logging
from typing import Dict, List, Any
from mcp.server.fastmcp import FastMCP, Context
# Get logger
logger = logging.getLogger("UnrealMCP")
def register_blueprint_tools(mcp: FastMCP):
"""Register Blueprint tools with the MCP server."""
@mcp.tool()
def create_blueprint(
ctx: Context,
name: str,
parent_class: str
) -> Dict[str, Any]:
"""Create a new Blueprint class."""
# Import inside function to avoid circular imports
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("create_blueprint", {
"name": name,
"parent_class": parent_class
})
if not response:
logger.error("No response from Unreal Engine")
return {"success": False, "message": "No response from Unreal Engine"}
logger.info(f"Blueprint creation response: {response}")
return response or {}
except Exception as e:
error_msg = f"Error creating blueprint: {e}"
logger.error(error_msg)
return {"success": False, "message": error_msg}
@mcp.tool()
def add_component_to_blueprint(
ctx: Context,
blueprint_name: str,
component_type: str,
component_name: str,
location: List[float] = [],
rotation: List[float] = [],
scale: List[float] = [],
component_properties: Dict[str, Any] = {}
) -> Dict[str, Any]:
"""
Add a component to a Blueprint.
Args:
blueprint_name: Name of the target Blueprint
component_type: Type of component to add (use component class name without U prefix)
component_name: Name for the new component
location: [X, Y, Z] coordinates for component's position
rotation: [Pitch, Yaw, Roll] values for component's rotation
scale: [X, Y, Z] values for component's scale
component_properties: Additional properties to set on the component
Returns:
Information about the added component
"""
from unreal_mcp_server import get_unreal_connection
try:
# Ensure all parameters are properly formatted
params = {
"blueprint_name": blueprint_name,
"component_type": component_type,
"component_name": component_name,
"location": location or [0.0, 0.0, 0.0],
"rotation": rotation or [0.0, 0.0, 0.0],
"scale": scale or [1.0, 1.0, 1.0]
}
# Add component_properties if provided
if component_properties and len(component_properties) > 0:
params["component_properties"] = component_properties
# Validate location, rotation, and scale formats
for param_name in ["location", "rotation", "scale"]:
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]
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"}
logger.info(f"Adding component to blueprint with params: {params}")
response = unreal.send_command("add_component_to_blueprint", params)
if not response:
logger.error("No response from Unreal Engine")
return {"success": False, "message": "No response from Unreal Engine"}
logger.info(f"Component addition response: {response}")
return response
except Exception as e:
error_msg = f"Error adding component to blueprint: {e}"
logger.error(error_msg)
return {"success": False, "message": error_msg}
@mcp.tool()
def set_static_mesh_properties(
ctx: Context,
blueprint_name: str,
component_name: str,
static_mesh: str = "/Engine/BasicShapes/Cube.Cube"
) -> Dict[str, Any]:
"""
Set static mesh properties on a StaticMeshComponent.
Args:
blueprint_name: Name of the target Blueprint
component_name: Name of the StaticMeshComponent
static_mesh: Path to the static mesh asset (e.g., "/Engine/BasicShapes/Cube.Cube")
Returns:
Response indicating success or failure
"""
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 = {
"blueprint_name": blueprint_name,
"component_name": component_name,
"static_mesh": static_mesh
}
logger.info(f"Setting static mesh properties with params: {params}")
response = unreal.send_command("set_static_mesh_properties", params)
if not response:
logger.error("No response from Unreal Engine")
return {"success": False, "message": "No response from Unreal Engine"}
logger.info(f"Set static mesh properties response: {response}")
return response
except Exception as e:
error_msg = f"Error setting static mesh properties: {e}"
logger.error(error_msg)
return {"success": False, "message": error_msg}
@mcp.tool()
def set_component_property(
ctx: Context,
blueprint_name: str,
component_name: str,
property_name: str,
property_value,
) -> Dict[str, Any]:
"""Set a property on a component in a Blueprint."""
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 = {
"blueprint_name": blueprint_name,
"component_name": component_name,
"property_name": property_name,
"property_value": property_value
}
logger.info(f"Setting component property with params: {params}")
response = unreal.send_command("set_component_property", params)
if not response:
logger.error("No response from Unreal Engine")
return {"success": False, "message": "No response from Unreal Engine"}
logger.info(f"Set component property response: {response}")
return response
except Exception as e:
error_msg = f"Error setting component property: {e}"
logger.error(error_msg)
return {"success": False, "message": error_msg}
@mcp.tool()
def set_physics_properties(
ctx: Context,
blueprint_name: str,
component_name: str,
simulate_physics: bool = True,
gravity_enabled: bool = True,
mass: float = 1.0,
linear_damping: float = 0.01,
angular_damping: float = 0.0
) -> Dict[str, Any]:
"""Set physics properties on a component."""
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 = {
"blueprint_name": blueprint_name,
"component_name": component_name,
"simulate_physics": simulate_physics,
"gravity_enabled": gravity_enabled,
"mass": float(mass),
"linear_damping": float(linear_damping),
"angular_damping": float(angular_damping)
}
logger.info(f"Setting physics properties with params: {params}")
response = unreal.send_command("set_physics_properties", params)
if not response:
logger.error("No response from Unreal Engine")
return {"success": False, "message": "No response from Unreal Engine"}
logger.info(f"Set physics properties response: {response}")
return response
except Exception as e:
error_msg = f"Error setting physics properties: {e}"
logger.error(error_msg)
return {"success": False, "message": error_msg}
@mcp.tool()
def compile_blueprint(
ctx: Context,
blueprint_name: str
) -> Dict[str, Any]:
"""Compile a Blueprint."""
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 = {
"blueprint_name": blueprint_name
}
logger.info(f"Compiling blueprint: {blueprint_name}")
response = unreal.send_command("compile_blueprint", params)
if not response:
logger.error("No response from Unreal Engine")
return {"success": False, "message": "No response from Unreal Engine"}
logger.info(f"Compile blueprint response: {response}")
return response
except Exception as e:
error_msg = f"Error compiling blueprint: {e}"
logger.error(error_msg)
return {"success": False, "message": error_msg}
@mcp.tool()
def set_blueprint_property(
ctx: Context,
blueprint_name: str,
property_name: str,
property_value
) -> Dict[str, Any]:
"""
Set a property on a Blueprint class default object.
Args:
blueprint_name: Name of the target Blueprint
property_name: Name of the property to set
property_value: Value to set the property to
Returns:
Response indicating success or failure
"""
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 = {
"blueprint_name": blueprint_name,
"property_name": property_name,
"property_value": property_value
}
logger.info(f"Setting blueprint property with params: {params}")
response = unreal.send_command("set_blueprint_property", params)
if not response:
logger.error("No response from Unreal Engine")
return {"success": False, "message": "No response from Unreal Engine"}
logger.info(f"Set blueprint property response: {response}")
return response
except Exception as e:
error_msg = f"Error setting blueprint property: {e}"
logger.error(error_msg)
return {"success": False, "message": error_msg}
# @mcp.tool() commented out, just use set_component_property instead
def set_pawn_properties(
ctx: Context,
blueprint_name: str,
auto_possess_player: str = "",
use_controller_rotation_yaw: bool = None,
use_controller_rotation_pitch: bool = None,
use_controller_rotation_roll: bool = None,
can_be_damaged: bool = None
) -> Dict[str, Any]:
"""
Set common Pawn properties on a Blueprint.
This is a utility function that sets multiple pawn-related properties at once.
Args:
blueprint_name: Name of the target Blueprint (must be a Pawn or Character)
auto_possess_player: Auto possess player setting (None, "Disabled", "Player0", "Player1", etc.)
use_controller_rotation_yaw: Whether the pawn should use the controller's yaw rotation
use_controller_rotation_pitch: Whether the pawn should use the controller's pitch rotation
use_controller_rotation_roll: Whether the pawn should use the controller's roll rotation
can_be_damaged: Whether the pawn can be damaged
Returns:
Response indicating success or failure with detailed results for each property
"""
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"}
# Define the properties to set
properties = {}
if auto_possess_player and auto_possess_player != "":
properties["auto_possess_player"] = auto_possess_player
# Only include boolean properties if they were explicitly set
if use_controller_rotation_yaw is not None:
properties["bUseControllerRotationYaw"] = use_controller_rotation_yaw
if use_controller_rotation_pitch is not None:
properties["bUseControllerRotationPitch"] = use_controller_rotation_pitch
if use_controller_rotation_roll is not None:
properties["bUseControllerRotationRoll"] = use_controller_rotation_roll
if can_be_damaged is not None:
properties["bCanBeDamaged"] = can_be_damaged
if not properties:
logger.warning("No properties specified to set")
return {"success": True, "message": "No properties specified to set", "results": {}}
# Set each property using the generic set_blueprint_property function
results = {}
overall_success = True
for prop_name, prop_value in properties.items():
params = {
"blueprint_name": blueprint_name,
"property_name": prop_name,
"property_value": prop_value
}
logger.info(f"Setting pawn property {prop_name} to {prop_value}")
response = unreal.send_command("set_blueprint_property", params)
if not response:
logger.error(f"No response from Unreal Engine for property {prop_name}")
results[prop_name] = {"success": False, "message": "No response from Unreal Engine"}
overall_success = False
continue
results[prop_name] = response
if not response.get("success", False):
overall_success = False
return {
"success": overall_success,
"message": "Pawn properties set" if overall_success else "Some pawn properties failed to set",
"results": results
}
except Exception as e:
error_msg = f"Error setting pawn properties: {e}"
logger.error(error_msg)
return {"success": False, "message": error_msg}
logger.info("Blueprint tools registered successfully")

View File

@@ -0,0 +1,430 @@
"""
Blueprint Node Tools for Unreal MCP.
This module provides tools for manipulating Blueprint graph nodes and connections.
"""
import logging
from typing import Dict, List, Any, Optional
from mcp.server.fastmcp import FastMCP, Context
# Get logger
logger = logging.getLogger("UnrealMCP")
def register_blueprint_node_tools(mcp: FastMCP):
"""Register Blueprint node manipulation tools with the MCP server."""
@mcp.tool()
def add_blueprint_event_node(
ctx: Context,
blueprint_name: str,
event_name: str,
node_position = None
) -> Dict[str, Any]:
"""
Add an event node to a Blueprint's event graph.
Args:
blueprint_name: Name of the target Blueprint
event_name: Name of the event. Use 'Receive' prefix for standard events:
- 'ReceiveBeginPlay' for Begin Play
- 'ReceiveTick' for Tick
- etc.
node_position: Optional [X, Y] position in the graph
Returns:
Response containing the node ID and success status
"""
from unreal_mcp_server import get_unreal_connection
try:
# Handle default value within the method body
if node_position is None:
node_position = [0, 0]
params = {
"blueprint_name": blueprint_name,
"event_name": event_name,
"node_position": node_position
}
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"}
logger.info(f"Adding event node '{event_name}' to blueprint '{blueprint_name}'")
response = unreal.send_command("add_blueprint_event_node", params)
if not response:
logger.error("No response from Unreal Engine")
return {"success": False, "message": "No response from Unreal Engine"}
logger.info(f"Event node creation response: {response}")
return response
except Exception as e:
error_msg = f"Error adding event node: {e}"
logger.error(error_msg)
return {"success": False, "message": error_msg}
@mcp.tool()
def add_blueprint_input_action_node(
ctx: Context,
blueprint_name: str,
action_name: str,
node_position = None
) -> Dict[str, Any]:
"""
Add an input action event node to a Blueprint's event graph.
Args:
blueprint_name: Name of the target Blueprint
action_name: Name of the input action to respond to
node_position: Optional [X, Y] position in the graph
Returns:
Response containing the node ID and success status
"""
from unreal_mcp_server import get_unreal_connection
try:
# Handle default value within the method body
if node_position is None:
node_position = [0, 0]
params = {
"blueprint_name": blueprint_name,
"action_name": action_name,
"node_position": node_position
}
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"}
logger.info(f"Adding input action node for '{action_name}' to blueprint '{blueprint_name}'")
response = unreal.send_command("add_blueprint_input_action_node", params)
if not response:
logger.error("No response from Unreal Engine")
return {"success": False, "message": "No response from Unreal Engine"}
logger.info(f"Input action node creation response: {response}")
return response
except Exception as e:
error_msg = f"Error adding input action node: {e}"
logger.error(error_msg)
return {"success": False, "message": error_msg}
@mcp.tool()
def add_blueprint_function_node(
ctx: Context,
blueprint_name: str,
target: str,
function_name: str,
params = None,
node_position = None
) -> Dict[str, Any]:
"""
Add a function call node to a Blueprint's event graph.
Args:
blueprint_name: Name of the target Blueprint
target: Target object for the function (component name or self)
function_name: Name of the function to call
params: Optional parameters to set on the function node
node_position: Optional [X, Y] position in the graph
Returns:
Response containing the node ID and success status
"""
from unreal_mcp_server import get_unreal_connection
try:
# Handle default values within the method body
if params is None:
params = {}
if node_position is None:
node_position = [0, 0]
command_params = {
"blueprint_name": blueprint_name,
"target": target,
"function_name": function_name,
"params": params,
"node_position": node_position
}
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"}
logger.info(f"Adding function node '{function_name}' to blueprint '{blueprint_name}'")
response = unreal.send_command("add_blueprint_function_node", command_params)
if not response:
logger.error("No response from Unreal Engine")
return {"success": False, "message": "No response from Unreal Engine"}
logger.info(f"Function node creation response: {response}")
return response
except Exception as e:
error_msg = f"Error adding function node: {e}"
logger.error(error_msg)
return {"success": False, "message": error_msg}
@mcp.tool()
def connect_blueprint_nodes(
ctx: Context,
blueprint_name: str,
source_node_id: str,
source_pin: str,
target_node_id: str,
target_pin: str
) -> Dict[str, Any]:
"""
Connect two nodes in a Blueprint's event graph.
Args:
blueprint_name: Name of the target Blueprint
source_node_id: ID of the source node
source_pin: Name of the output pin on the source node
target_node_id: ID of the target node
target_pin: Name of the input pin on the target node
Returns:
Response indicating success or failure
"""
from unreal_mcp_server import get_unreal_connection
try:
params = {
"blueprint_name": blueprint_name,
"source_node_id": source_node_id,
"source_pin": source_pin,
"target_node_id": target_node_id,
"target_pin": target_pin
}
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"}
logger.info(f"Connecting nodes in blueprint '{blueprint_name}'")
response = unreal.send_command("connect_blueprint_nodes", params)
if not response:
logger.error("No response from Unreal Engine")
return {"success": False, "message": "No response from Unreal Engine"}
logger.info(f"Node connection response: {response}")
return response
except Exception as e:
error_msg = f"Error connecting nodes: {e}"
logger.error(error_msg)
return {"success": False, "message": error_msg}
@mcp.tool()
def add_blueprint_variable(
ctx: Context,
blueprint_name: str,
variable_name: str,
variable_type: str,
is_exposed: bool = False
) -> Dict[str, Any]:
"""
Add a variable to a Blueprint.
Args:
blueprint_name: Name of the target Blueprint
variable_name: Name of the variable
variable_type: Type of the variable (Boolean, Integer, Float, Vector, etc.)
is_exposed: Whether to expose the variable to the editor
Returns:
Response indicating success or failure
"""
from unreal_mcp_server import get_unreal_connection
try:
params = {
"blueprint_name": blueprint_name,
"variable_name": variable_name,
"variable_type": variable_type,
"is_exposed": is_exposed
}
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"}
logger.info(f"Adding variable '{variable_name}' to blueprint '{blueprint_name}'")
response = unreal.send_command("add_blueprint_variable", params)
if not response:
logger.error("No response from Unreal Engine")
return {"success": False, "message": "No response from Unreal Engine"}
logger.info(f"Variable creation response: {response}")
return response
except Exception as e:
error_msg = f"Error adding variable: {e}"
logger.error(error_msg)
return {"success": False, "message": error_msg}
@mcp.tool()
def add_blueprint_get_self_component_reference(
ctx: Context,
blueprint_name: str,
component_name: str,
node_position = None
) -> Dict[str, Any]:
"""
Add a node that gets a reference to a component owned by the current Blueprint.
This creates a node similar to what you get when dragging a component from the Components panel.
Args:
blueprint_name: Name of the target Blueprint
component_name: Name of the component to get a reference to
node_position: Optional [X, Y] position in the graph
Returns:
Response containing the node ID and success status
"""
from unreal_mcp_server import get_unreal_connection
try:
# Handle None case explicitly in the function
if node_position is None:
node_position = [0, 0]
params = {
"blueprint_name": blueprint_name,
"component_name": component_name,
"node_position": node_position
}
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"}
logger.info(f"Adding self component reference node for '{component_name}' to blueprint '{blueprint_name}'")
response = unreal.send_command("add_blueprint_get_self_component_reference", params)
if not response:
logger.error("No response from Unreal Engine")
return {"success": False, "message": "No response from Unreal Engine"}
logger.info(f"Self component reference node creation response: {response}")
return response
except Exception as e:
error_msg = f"Error adding self component reference node: {e}"
logger.error(error_msg)
return {"success": False, "message": error_msg}
@mcp.tool()
def add_blueprint_self_reference(
ctx: Context,
blueprint_name: str,
node_position = None
) -> Dict[str, Any]:
"""
Add a 'Get Self' node to a Blueprint's event graph that returns a reference to this actor.
Args:
blueprint_name: Name of the target Blueprint
node_position: Optional [X, Y] position in the graph
Returns:
Response containing the node ID and success status
"""
from unreal_mcp_server import get_unreal_connection
try:
if node_position is None:
node_position = [0, 0]
params = {
"blueprint_name": blueprint_name,
"node_position": node_position
}
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"}
logger.info(f"Adding self reference node to blueprint '{blueprint_name}'")
response = unreal.send_command("add_blueprint_self_reference", params)
if not response:
logger.error("No response from Unreal Engine")
return {"success": False, "message": "No response from Unreal Engine"}
logger.info(f"Self reference node creation response: {response}")
return response
except Exception as e:
error_msg = f"Error adding self reference node: {e}"
logger.error(error_msg)
return {"success": False, "message": error_msg}
@mcp.tool()
def find_blueprint_nodes(
ctx: Context,
blueprint_name: str,
node_type = None,
event_type = None
) -> Dict[str, Any]:
"""
Find nodes in a Blueprint's event graph.
Args:
blueprint_name: Name of the target Blueprint
node_type: Optional type of node to find (Event, Function, Variable, etc.)
event_type: Optional specific event type to find (BeginPlay, Tick, etc.)
Returns:
Response containing array of found node IDs and success status
"""
from unreal_mcp_server import get_unreal_connection
try:
params = {
"blueprint_name": blueprint_name,
"node_type": node_type,
"event_type": event_type
}
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"}
logger.info(f"Finding nodes in blueprint '{blueprint_name}'")
response = unreal.send_command("find_blueprint_nodes", params)
if not response:
logger.error("No response from Unreal Engine")
return {"success": False, "message": "No response from Unreal Engine"}
logger.info(f"Node find response: {response}")
return response
except Exception as e:
error_msg = f"Error finding nodes: {e}"
logger.error(error_msg)
return {"success": False, "message": error_msg}
logger.info("Blueprint node tools registered successfully")

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")

View File

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

View File

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

View File

@@ -0,0 +1,85 @@
"""
World Tools for Unreal MCP.
This module provides tools for runtime world operations in Unreal Engine.
Following Epic's official structure: Engine/World 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_world_tools(mcp: FastMCP):
"""Register world runtime tools with the MCP server."""
@mcp.tool()
def get_current_level_info(ctx: Context) -> Dict[str, Any]:
"""Get information about the current level and world.
Returns:
Dictionary containing current world and level information
Example:
get_current_level_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 {"error": "Failed to connect to Unreal Engine"}
response = unreal.send_command("get_current_level_info", {})
if not response:
logger.warning("No response from Unreal Engine")
return {"error": "No response from Unreal Engine"}
logger.info("Retrieved current level info")
return response
except Exception as e:
logger.error(f"Error getting level info: {e}")
return {"error": f"Error getting level info: {str(e)}"}
@mcp.tool()
def query_assets(
ctx: Context,
scope: Dict[str, Any] = None,
conditions: Dict[str, Any] = None,
sort_by: str = None,
limit: int = 20
) -> Dict[str, Any]:
"""Query assets in the current level/selection with performance filters.
Args:
scope: {"type": "Level"|"Selection"|"ContentBrowser", "path": "..."}
conditions: filter object (min_triangles, nanite_enabled, missing_collision, etc.)
sort_by: "TriangleCount" | "TextureMemory" | "DiskSize"
limit: max results
"""
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"}
params = {
"scope": scope or {"type": "Level"},
"conditions": conditions or {},
"sort_by": sort_by or "TriangleCount",
"limit": limit,
}
response = unreal.send_command("level.query_assets", params)
return response or {"error": "No response from Unreal Engine"}
except Exception as e:
logger.error(f"Error querying assets: {e}")
return {"error": f"Error querying assets: {str(e)}"}

View File

@@ -0,0 +1,64 @@
"""
Project Tools for Unreal MCP.
This module provides tools for managing project-wide settings and configuration.
"""
import logging
from typing import Dict, Any
from mcp.server.fastmcp import FastMCP, Context
# Get logger
logger = logging.getLogger("UnrealMCP")
def register_project_tools(mcp: FastMCP):
"""Register project tools with the MCP server."""
@mcp.tool()
def create_input_mapping(
ctx: Context,
action_name: str,
key: str,
input_type: str = "Action"
) -> Dict[str, Any]:
"""
Create an input mapping for the project.
Args:
action_name: Name of the input action
key: Key to bind (SpaceBar, LeftMouseButton, etc.)
input_type: Type of input mapping (Action or Axis)
Returns:
Response indicating success or failure
"""
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 = {
"action_name": action_name,
"key": key,
"input_type": input_type
}
logger.info(f"Creating input mapping '{action_name}' with key '{key}'")
response = unreal.send_command("create_input_mapping", params)
if not response:
logger.error("No response from Unreal Engine")
return {"success": False, "message": "No response from Unreal Engine"}
logger.info(f"Input mapping creation response: {response}")
return response
except Exception as e:
error_msg = f"Error creating input mapping: {e}"
logger.error(error_msg)
return {"success": False, "message": error_msg}
logger.info("Project tools registered successfully")

View File

@@ -0,0 +1,333 @@
"""
UMG Tools for Unreal MCP.
This module provides tools for creating and manipulating UMG Widget Blueprints in Unreal Engine.
"""
import logging
from typing import Dict, List, Any
from mcp.server.fastmcp import FastMCP, Context
# Get logger
logger = logging.getLogger("UnrealMCP")
def register_umg_tools(mcp: FastMCP):
"""Register UMG tools with the MCP server."""
@mcp.tool()
def create_umg_widget_blueprint(
ctx: Context,
widget_name: str,
parent_class: str = "UserWidget",
path: str = "/Game/UI"
) -> Dict[str, Any]:
"""
Create a new UMG Widget Blueprint.
Args:
widget_name: Name of the widget blueprint to create
parent_class: Parent class for the widget (default: UserWidget)
path: Content browser path where the widget should be created
Returns:
Dict containing success status and widget path
"""
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 = {
"widget_name": widget_name,
"parent_class": parent_class,
"path": path
}
logger.info(f"Creating UMG Widget Blueprint with params: {params}")
response = unreal.send_command("create_umg_widget_blueprint", params)
if not response:
logger.error("No response from Unreal Engine")
return {"success": False, "message": "No response from Unreal Engine"}
logger.info(f"Create UMG Widget Blueprint response: {response}")
return response
except Exception as e:
error_msg = f"Error creating UMG Widget Blueprint: {e}"
logger.error(error_msg)
return {"success": False, "message": error_msg}
@mcp.tool()
def add_text_block_to_widget(
ctx: Context,
widget_name: str,
text_block_name: str,
text: str = "",
position: List[float] = [0.0, 0.0],
size: List[float] = [200.0, 50.0],
font_size: int = 12,
color: List[float] = [1.0, 1.0, 1.0, 1.0]
) -> Dict[str, Any]:
"""
Add a Text Block widget to a UMG Widget Blueprint.
Args:
widget_name: Name of the target Widget Blueprint
text_block_name: Name to give the new Text Block
text: Initial text content
position: [X, Y] position in the canvas panel
size: [Width, Height] of the text block
font_size: Font size in points
color: [R, G, B, A] color values (0.0 to 1.0)
Returns:
Dict containing success status and text block 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"}
params = {
"widget_name": widget_name,
"text_block_name": text_block_name,
"text": text,
"position": position,
"size": size,
"font_size": font_size,
"color": color
}
logger.info(f"Adding Text Block to widget with params: {params}")
response = unreal.send_command("add_text_block_to_widget", params)
if not response:
logger.error("No response from Unreal Engine")
return {"success": False, "message": "No response from Unreal Engine"}
logger.info(f"Add Text Block response: {response}")
return response
except Exception as e:
error_msg = f"Error adding Text Block to widget: {e}"
logger.error(error_msg)
return {"success": False, "message": error_msg}
@mcp.tool()
def add_button_to_widget(
ctx: Context,
widget_name: str,
button_name: str,
text: str = "",
position: List[float] = [0.0, 0.0],
size: List[float] = [200.0, 50.0],
font_size: int = 12,
color: List[float] = [1.0, 1.0, 1.0, 1.0],
background_color: List[float] = [0.1, 0.1, 0.1, 1.0]
) -> Dict[str, Any]:
"""
Add a Button widget to a UMG Widget Blueprint.
Args:
widget_name: Name of the target Widget Blueprint
button_name: Name to give the new Button
text: Text to display on the button
position: [X, Y] position in the canvas panel
size: [Width, Height] of the button
font_size: Font size for button text
color: [R, G, B, A] text color values (0.0 to 1.0)
background_color: [R, G, B, A] button background color values (0.0 to 1.0)
Returns:
Dict containing success status and button 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"}
params = {
"widget_name": widget_name,
"button_name": button_name,
"text": text,
"position": position,
"size": size,
"font_size": font_size,
"color": color,
"background_color": background_color
}
logger.info(f"Adding Button to widget with params: {params}")
response = unreal.send_command("add_button_to_widget", params)
if not response:
logger.error("No response from Unreal Engine")
return {"success": False, "message": "No response from Unreal Engine"}
logger.info(f"Add Button response: {response}")
return response
except Exception as e:
error_msg = f"Error adding Button to widget: {e}"
logger.error(error_msg)
return {"success": False, "message": error_msg}
@mcp.tool()
def bind_widget_event(
ctx: Context,
widget_name: str,
widget_component_name: str,
event_name: str,
function_name: str = ""
) -> Dict[str, Any]:
"""
Bind an event on a widget component to a function.
Args:
widget_name: Name of the target Widget Blueprint
widget_component_name: Name of the widget component (button, etc.)
event_name: Name of the event to bind (OnClicked, etc.)
function_name: Name of the function to create/bind to (defaults to f"{widget_component_name}_{event_name}")
Returns:
Dict containing success status and binding 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"}
# If no function name provided, create one from component and event names
if not function_name:
function_name = f"{widget_component_name}_{event_name}"
params = {
"widget_name": widget_name,
"widget_component_name": widget_component_name,
"event_name": event_name,
"function_name": function_name
}
logger.info(f"Binding widget event with params: {params}")
response = unreal.send_command("bind_widget_event", params)
if not response:
logger.error("No response from Unreal Engine")
return {"success": False, "message": "No response from Unreal Engine"}
logger.info(f"Bind widget event response: {response}")
return response
except Exception as e:
error_msg = f"Error binding widget event: {e}"
logger.error(error_msg)
return {"success": False, "message": error_msg}
@mcp.tool()
def add_widget_to_viewport(
ctx: Context,
widget_name: str,
z_order: int = 0
) -> Dict[str, Any]:
"""
Add a Widget Blueprint instance to the viewport.
Args:
widget_name: Name of the Widget Blueprint to add
z_order: Z-order for the widget (higher numbers appear on top)
Returns:
Dict containing success status and widget instance 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 = {
"widget_name": widget_name,
"z_order": z_order
}
logger.info(f"Adding widget to viewport with params: {params}")
response = unreal.send_command("add_widget_to_viewport", params)
if not response:
logger.error("No response from Unreal Engine")
return {"success": False, "message": "No response from Unreal Engine"}
logger.info(f"Add widget to viewport response: {response}")
return response
except Exception as e:
error_msg = f"Error adding widget to viewport: {e}"
logger.error(error_msg)
return {"success": False, "message": error_msg}
@mcp.tool()
def set_text_block_binding(
ctx: Context,
widget_name: str,
text_block_name: str,
binding_property: str,
binding_type: str = "Text"
) -> Dict[str, Any]:
"""
Set up a property binding for a Text Block widget.
Args:
widget_name: Name of the target Widget Blueprint
text_block_name: Name of the Text Block to bind
binding_property: Name of the property to bind to
binding_type: Type of binding (Text, Visibility, etc.)
Returns:
Dict containing success status and binding 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 = {
"widget_name": widget_name,
"text_block_name": text_block_name,
"binding_property": binding_property,
"binding_type": binding_type
}
logger.info(f"Setting text block binding with params: {params}")
response = unreal.send_command("set_text_block_binding", params)
if not response:
logger.error("No response from Unreal Engine")
return {"success": False, "message": "No response from Unreal Engine"}
logger.info(f"Set text block binding response: {response}")
return response
except Exception as e:
error_msg = f"Error setting text block binding: {e}"
logger.error(error_msg)
return {"success": False, "message": error_msg}
logger.info("UMG tools registered successfully")