Skip to content
ClaudeUnreal
GitHub

Landscape & Foliage

33 tools — 0 typed MCP · 33 via cu CLI.

  • Landscape — Terrain — 13 tools
  • Foliage — Vegetation — 9 tools
  • PCG — Procedural Content Generation — 11 tools

Sculpt terrain height with circular brush tools — raise, lower, smooth, or flatten.

@mcp.tool()
@showcase(
"Sculpt terrain height with circular brush tools — raise, lower, smooth, or flatten.",
featured=True,
)
def sculpt_landscape(
ctx: Context,
landscape_name: str,
location: List[float],
radius: float = None,
strength: float = None,
tool_type: str = None,
target_height: float = None,
) -> ToolResult:
"""[Landscape] Sculpt terrain height at a specific location with a circular brush.
Anti-patterns:
- Do not call in headless / nullrhi mode — handler returns "Cannot
sculpt landscape in headless/nullrhi mode."
- Do not pass a brush ``location`` outside the landscape's footprint —
handler returns "Brush location is outside landscape bounds" once
the clamped region collapses to zero.
- Do not pass an unknown ``tool_type`` — only ``Raise``, ``Lower``,
``Smooth``, ``Flatten`` are valid. Anything else is rejected.
- Do not target a landscape that doesn't exist — call
``create_landscape`` first.
Args:
ctx: The MCP context
landscape_name: Name or label of the Landscape actor
location: World-space position [x, y] where the brush is centered (cm)
radius: Brush radius in centimeters (default 1000)
strength: Brush strength 0.0 to 1.0 (default 0.5)
tool_type: Sculpt operation — "Raise", "Lower", "Smooth", or "Flatten" (default "Raise")
target_height: Target height in cm for Flatten mode (default 0)
Returns:
Dict with sculpt result and number of modified vertices
Examples:
sculpt_landscape(landscape_name="Landscape", location=[5000, 5000], tool_type="Raise")
sculpt_landscape(landscape_name="Landscape", location=[5000, 5000], radius=2000, strength=0.8, tool_type="Lower")
sculpt_landscape(landscape_name="Landscape", location=[5000, 5000], tool_type="Flatten", target_height=500)
"""
from claude_unreal_server import get_unreal_connection
try:
unreal = get_unreal_connection()
if not unreal:
return err("Failed to connect to Unreal Engine")
params = {
"landscape_name": landscape_name,
"location": location,
}
if radius is not None:
params["radius"] = radius
if strength is not None:
params["strength"] = strength
if tool_type is not None:
params["tool_type"] = tool_type
if target_height is not None:
params["target_height"] = target_height
response = unreal.send_command("sculpt_landscape", params)
if not response:
return err("Failed to sculpt landscape: no response from Unreal Engine")
if response.get("status") == "error":
return err(f"Failed to sculpt '{landscape_name}'", error=response.get("error", "Unknown error"))
result = response.get("result", response)
return ok(f"Sculpted '{landscape_name}'", **result)
except Exception as e:
logger.error(f"Error in sculpt_landscape: {e}")
return err("Failed to sculpt landscape", error=str(e))

Scatter foliage instances (trees, grass, rocks) in a circular area with scale randomization.

@mcp.tool()
@showcase(
"Scatter foliage instances (trees, grass, rocks) in a circular area with scale randomization.",
featured=True,
)
def paint_foliage(
ctx: Context,
static_mesh: str,
location: List[float],
radius: float = None,
count: int = None,
min_scale: float = None,
max_scale: float = None,
align_to_normal: bool = None,
random_yaw: bool = None,
) -> ToolResult:
"""[Foliage] Paint foliage instances in a circular area around a location.
Instances snap to ground via vertical line trace against ECC_WorldStatic —
place over valid geometry. Auto-registers mesh if not yet a foliage type.
location in cm, radius in cm (default 500), scale defaults 1.0.
Example: `paint_foliage("/Game/Meshes/Tree.Tree", [0,0,0], radius=1000, count=50)`
"""
from claude_unreal_server import get_unreal_connection
try:
unreal = get_unreal_connection()
if not unreal:
return err("Failed to connect to Unreal Engine")
params = {
"static_mesh": static_mesh,
"location": location,
}
if radius is not None:
params["radius"] = radius
if count is not None:
params["count"] = count
if min_scale is not None:
params["min_scale"] = min_scale
if max_scale is not None:
params["max_scale"] = max_scale
if align_to_normal is not None:
params["align_to_normal"] = align_to_normal
if random_yaw is not None:
params["random_yaw"] = random_yaw
response = unreal.send_command("paint_foliage", params)
if not response:
return err("Failed to paint foliage: no response from Unreal Engine")
if response.get("status") == "error":
return err(f"Failed to paint foliage for '{static_mesh}'", error=response.get("error", "Unknown error"))
result = response.get("result", response)
return ok(f"Painted foliage for '{static_mesh}'", **result)
except Exception as e:
logger.error(f"Error in paint_foliage: {e}")
return err("Failed to paint foliage", error=str(e))

import_landscape_layer_weightmap CLI

Section titled “import_landscape_layer_weightmap ”

create_procedural_foliage_spawner CLI

Section titled “create_procedural_foliage_spawner ”
@mcp.tool()
def spawn_pcg_volume(
ctx: Context,
name: str,
location: List[float] = None,
extent: List[float] = None,
graph_path: str = None,
) -> ToolResult:
"""[PCG] Spawn a PCG Volume actor that can execute a PCG graph inside its bounds.
The volume is built with a box brush of the given extent. Default extent
is [10000, 10000, 6000] cm (200m x 200m x 120m). Requires the PCG plugin.
Anti-patterns:
- Do not pass ``name=""`` — the spawn rejects empty actor labels (#472).
- Do not pass ``graph_path`` outside ``/Game/`` — engine/plugin mounts are
rejected.
- Do not pass zero or negative ``extent`` components; PCG sampling requires
a positive volume.
Args:
ctx: The MCP context
name: Actor label for the new volume (must be non-empty)
location: Optional [x, y, z] world location in cm (default origin)
extent: Optional [x, y, z] half-extent in cm (default [10000, 10000, 6000])
graph_path: Optional PCG graph asset to assign immediately (must start with `/Game/`)
Examples:
spawn_pcg_volume(name="ForestVolume", location=[0, 0, 0])
spawn_pcg_volume(name="Scatter", extent=[5000, 5000, 2000],
graph_path="/Game/PCG/ForestGraph")
"""
from claude_unreal_server import get_unreal_connection
try:
unreal = get_unreal_connection()
if not unreal:
return err("Failed to connect to Unreal Engine")
params = {"name": name}
if location is not None:
params["location"] = location
if extent is not None:
params["extent"] = extent
if graph_path is not None:
params["graph_path"] = graph_path
response = unreal.send_command("spawn_pcg_volume", params)
if not response:
return err("Failed to spawn PCG volume: no response")
if response.get("status") == "error":
return err(f"Failed to spawn PCG volume '{name}'",
error=response.get("error", "Unknown error"))
result = response.get("result", response)
return ok(f"Spawned PCG volume '{name}'", **result)
except Exception as e:
logger.error(f"Error in spawn_pcg_volume: {e}")
return err("Failed to spawn PCG volume", error=str(e))