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
Landscape — Terrain
Section titled “Landscape — Terrain”sculpt_landscape CLI
Section titled “sculpt_landscape ”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))paint_foliage CLI
Section titled “paint_foliage ”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))create_landscape CLI
Section titled “create_landscape ”import_heightmap CLI
Section titled “import_heightmap ”export_heightmap CLI
Section titled “export_heightmap ”paint_landscape_layer CLI
Section titled “paint_landscape_layer ”set_landscape_material CLI
Section titled “set_landscape_material ”create_landscape_layer_info CLI
Section titled “create_landscape_layer_info ”get_landscape_layers CLI
Section titled “get_landscape_layers ”import_landscape_layer_weightmap CLI
Section titled “import_landscape_layer_weightmap ”add_landscape_spline CLI
Section titled “add_landscape_spline ”set_landscape_spline_mesh CLI
Section titled “set_landscape_spline_mesh ”apply_landscape_splines CLI
Section titled “apply_landscape_splines ”Foliage — Vegetation
Section titled “Foliage — Vegetation”add_foliage_type CLI
Section titled “add_foliage_type ”remove_foliage CLI
Section titled “remove_foliage ”get_foliage_types CLI
Section titled “get_foliage_types ”set_foliage_density CLI
Section titled “set_foliage_density ”set_foliage_scale_range CLI
Section titled “set_foliage_scale_range ”set_foliage_placement_rules CLI
Section titled “set_foliage_placement_rules ”set_foliage_lod CLI
Section titled “set_foliage_lod ”create_procedural_foliage_spawner CLI
Section titled “create_procedural_foliage_spawner ”set_procedural_foliage_settings CLI
Section titled “set_procedural_foliage_settings ”PCG — Procedural Content Generation
Section titled “PCG — Procedural Content Generation”spawn_pcg_volume CLI
Section titled “spawn_pcg_volume ”@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))