- Add MCP server with real Unreal Remote Execution Protocol (UDP 6766 + TCP 6776) - Implement 12 MCP tools: project intelligence, scene manipulation, debug/profiling, blueprint ops - Add enhanced .uasset parser with UE4/UE5 support - Create /blueprint-workflow skill (analyze, bp-to-cpp, cpp-to-bp, transform, optimize) - Include 21 passing tests - Add complete user documentation Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
113 lines
2.8 KiB
Python
113 lines
2.8 KiB
Python
"""Validation utilities for Unreal MCP Server."""
|
|
|
|
import re
|
|
from pathlib import Path
|
|
|
|
|
|
def validate_path(path: str | Path, must_exist: bool = False) -> Path:
|
|
"""Validate and normalize a file path.
|
|
|
|
Args:
|
|
path: Path to validate
|
|
must_exist: If True, check that the path exists
|
|
|
|
Returns:
|
|
Normalized Path object
|
|
|
|
Raises:
|
|
ValueError: If path is invalid or doesn't exist (when must_exist=True)
|
|
"""
|
|
path = Path(path)
|
|
|
|
if must_exist and not path.exists():
|
|
raise ValueError(f"Path does not exist: {path}")
|
|
|
|
return path
|
|
|
|
|
|
def validate_class_name(name: str) -> str:
|
|
"""Validate an Unreal class name.
|
|
|
|
Args:
|
|
name: Class name to validate (e.g., 'BP_Enemy', 'AWeapon')
|
|
|
|
Returns:
|
|
Validated class name
|
|
|
|
Raises:
|
|
ValueError: If class name is invalid
|
|
"""
|
|
# Unreal class names should be alphanumeric with underscores
|
|
# Typically prefixed with A (Actor), U (Object), F (Struct), E (Enum)
|
|
# Or BP_ for Blueprints
|
|
pattern = r"^[A-Z][A-Za-z0-9_]*$"
|
|
|
|
if not re.match(pattern, name):
|
|
raise ValueError(
|
|
f"Invalid class name: {name}. "
|
|
"Must start with uppercase letter and contain only alphanumeric characters and underscores."
|
|
)
|
|
|
|
return name
|
|
|
|
|
|
def validate_unreal_path(path: str) -> str:
|
|
"""Validate an Unreal content path.
|
|
|
|
Args:
|
|
path: Unreal path (e.g., '/Game/Weapons/BP_Rifle')
|
|
|
|
Returns:
|
|
Validated path
|
|
|
|
Raises:
|
|
ValueError: If path is invalid
|
|
"""
|
|
if not path.startswith("/"):
|
|
raise ValueError(f"Unreal path must start with '/': {path}")
|
|
|
|
# Common valid prefixes
|
|
valid_prefixes = ["/Game/", "/Engine/", "/Script/"]
|
|
if not any(path.startswith(prefix) for prefix in valid_prefixes):
|
|
raise ValueError(
|
|
f"Unreal path must start with one of {valid_prefixes}: {path}"
|
|
)
|
|
|
|
return path
|
|
|
|
|
|
def validate_location(location: list[float]) -> tuple[float, float, float]:
|
|
"""Validate a 3D location.
|
|
|
|
Args:
|
|
location: List of [X, Y, Z] coordinates
|
|
|
|
Returns:
|
|
Tuple of (X, Y, Z)
|
|
|
|
Raises:
|
|
ValueError: If location is invalid
|
|
"""
|
|
if len(location) != 3:
|
|
raise ValueError(f"Location must have 3 components [X, Y, Z], got {len(location)}")
|
|
|
|
return tuple(float(v) for v in location) # type: ignore
|
|
|
|
|
|
def validate_rotation(rotation: list[float]) -> tuple[float, float, float]:
|
|
"""Validate a 3D rotation.
|
|
|
|
Args:
|
|
rotation: List of [Pitch, Yaw, Roll] angles in degrees
|
|
|
|
Returns:
|
|
Tuple of (Pitch, Yaw, Roll)
|
|
|
|
Raises:
|
|
ValueError: If rotation is invalid
|
|
"""
|
|
if len(rotation) != 3:
|
|
raise ValueError(f"Rotation must have 3 components [Pitch, Yaw, Roll], got {len(rotation)}")
|
|
|
|
return tuple(float(v) for v in rotation) # type: ignore
|