Unreal-mcp/src/unreal_mcp/utils/validation.py
StillHammer ee2092dada Implement complete Python MCP server with 12 tools and blueprint-workflow skill
- 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>
2026-01-20 18:59:43 +07:00

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