ceshi
This commit is contained in:
@@ -0,0 +1 @@
|
||||
# Editor tools package
|
||||
@@ -0,0 +1 @@
|
||||
# Asset tools package
|
||||
@@ -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")
|
||||
@@ -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")
|
||||
@@ -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")
|
||||
@@ -0,0 +1 @@
|
||||
# Landscape tools package
|
||||
@@ -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 []
|
||||
@@ -0,0 +1 @@
|
||||
# Level Editor tools package
|
||||
@@ -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)}"}
|
||||
@@ -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")
|
||||
Reference in New Issue
Block a user