Skip to main content
Glama

editor_create_object

Create new actors or objects in Unreal Engine with specified class, name, transform, and properties to populate your scene.

Instructions

Create a new object/actor in the world

Example output: {'success': true, 'actor_name': 'StaticMeshActor_1', 'actor_label': 'MyCube', 'class': 'StaticMeshActor', 'location': {'x': 100.0, 'y': 200.0, 'z': 0.0}, 'rotation': {'pitch': 0.0, 'yaw': 45.0, 'roll': 0.0}, 'scale': {'x': 1.0, 'y': 1.0, 'z': 1.0}}

Returns created actor details with final transform values.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
object_classYesUnreal class name (e.g., 'StaticMeshActor', 'DirectionalLight')
object_nameYesName/label for the created object
locationNoWorld position coordinates
rotationNoRotation in degrees
scaleNoScale multipliers
propertiesNoAdditional actor properties. For StaticMeshActor: use 'StaticMesh' for mesh path, 'Material' for single material path, or 'Materials' for array of material paths. Example: {"StaticMesh": "/Game/Meshes/Cube", "Material": "/Game/Materials/M_Basic"}

Implementation Reference

  • MCP tool handler that generates Python code via editorTools.UECreateObject and executes it in the Unreal Editor using tryRunCommand, returning the result as text.
    async ({ object_class, object_name, location, rotation, scale, properties }) => {
    	const result = await tryRunCommand(
    		editorTools.UECreateObject(object_class, object_name, location, rotation, scale, properties),
    	)
    	return {
    		content: [
    			{
    				type: "text",
    				text: result,
    			},
    		],
    	}
    },
  • Zod schema defining the input parameters for the editor_create_object tool, including object class, name, optional transforms and properties.
    {
    	object_class: z.string().describe("Unreal class name (e.g., 'StaticMeshActor', 'DirectionalLight')"),
    	object_name: z.string().describe("Name/label for the created object"),
    	location: z
    		.object({
    			x: z.number().default(0),
    			y: z.number().default(0),
    			z: z.number().default(0),
    		})
    		.optional()
    		.describe("World position coordinates"),
    	rotation: z
    		.object({
    			pitch: z.number().default(0),
    			yaw: z.number().default(0),
    			roll: z.number().default(0),
    		})
    		.optional()
    		.describe("Rotation in degrees"),
    	scale: z
    		.object({
    			x: z.number().default(1),
    			y: z.number().default(1),
    			z: z.number().default(1),
    		})
    		.optional()
    		.describe("Scale multipliers"),
    	properties: z
    		.record(z.any())
    		.optional()
    		.describe(
    			'Additional actor properties. For StaticMeshActor: use \'StaticMesh\' for mesh path, \'Material\' for single material path, or \'Materials\' for array of material paths. Example: {"StaticMesh": "/Game/Meshes/Cube", "Material": "/Game/Materials/M_Basic"}',
    		),
    },
  • Registration of the editor_create_object tool on the McpServer instance, specifying name, description, input schema, and handler.
    server.tool(
    	"editor_create_object",
    	"Create a new object/actor in the world\n\nExample output: {'success': true, 'actor_name': 'StaticMeshActor_1', 'actor_label': 'MyCube', 'class': 'StaticMeshActor', 'location': {'x': 100.0, 'y': 200.0, 'z': 0.0}, 'rotation': {'pitch': 0.0, 'yaw': 45.0, 'roll': 0.0}, 'scale': {'x': 1.0, 'y': 1.0, 'z': 1.0}}\n\nReturns created actor details with final transform values.",
    	{
    		object_class: z.string().describe("Unreal class name (e.g., 'StaticMeshActor', 'DirectionalLight')"),
    		object_name: z.string().describe("Name/label for the created object"),
    		location: z
    			.object({
    				x: z.number().default(0),
    				y: z.number().default(0),
    				z: z.number().default(0),
    			})
    			.optional()
    			.describe("World position coordinates"),
    		rotation: z
    			.object({
    				pitch: z.number().default(0),
    				yaw: z.number().default(0),
    				roll: z.number().default(0),
    			})
    			.optional()
    			.describe("Rotation in degrees"),
    		scale: z
    			.object({
    				x: z.number().default(1),
    				y: z.number().default(1),
    				z: z.number().default(1),
    			})
    			.optional()
    			.describe("Scale multipliers"),
    		properties: z
    			.record(z.any())
    			.optional()
    			.describe(
    				'Additional actor properties. For StaticMeshActor: use \'StaticMesh\' for mesh path, \'Material\' for single material path, or \'Materials\' for array of material paths. Example: {"StaticMesh": "/Game/Meshes/Cube", "Material": "/Game/Materials/M_Basic"}',
    			),
    	},
    	async ({ object_class, object_name, location, rotation, scale, properties }) => {
    		const result = await tryRunCommand(
    			editorTools.UECreateObject(object_class, object_name, location, rotation, scale, properties),
    		)
    		return {
    			content: [
    				{
    					type: "text",
    					text: result,
    				},
    			],
    		}
    	},
    )
  • Helper function that reads the ue_create_object.py template and fills it with JSON-stringified parameters to produce executable Python code.
    export const UECreateObject = (
    	object_class: string,
    	object_name: string,
    	location?: { x: number; y: number; z: number },
    	rotation?: { pitch: number; yaw: number; roll: number },
    	scale?: { x: number; y: number; z: number },
    	properties?: Record<string, any>,
    ) => {
    	return Template(read("./scripts/ue_create_object.py"), {
    		object_class,
    		object_name,
    		location: location ? JSON.stringify(location) : "null",
    		rotation: rotation ? JSON.stringify(rotation) : "null",
    		scale: scale ? JSON.stringify(scale) : "null",
    		properties: properties ? JSON.stringify(properties) : "null",
    	})
    }
  • Core Unreal Python implementation that resolves the actor class, spawns the actor in the editor world, applies location/rotation/scale/properties (including special handling for StaticMeshActor meshes and materials), and returns detailed JSON output.
    def create_object(
        object_class: str,
        object_name: str,
        location: Optional[Dict[str, float]] = None,
        rotation: Optional[Dict[str, float]] = None,
        scale: Optional[Dict[str, float]] = None,
        properties: Optional[Dict[str, Any]] = None,
    ) -> Dict[str, Any]:
        try:
            world = unreal.get_editor_subsystem(
                unreal.UnrealEditorSubsystem
            ).get_editor_world()
            if not world:
                return {"error": "No world loaded"}
    
            actor_class = None
    
            class_mappings = {
                "StaticMeshActor": unreal.StaticMeshActor,
                "SkeletalMeshActor": unreal.SkeletalMeshActor,
                "DirectionalLight": unreal.DirectionalLight,
                "PointLight": unreal.PointLight,
                "SpotLight": unreal.SpotLight,
                "Camera": unreal.CameraActor,
                "CameraActor": unreal.CameraActor,
                "Pawn": unreal.Pawn,
                "Character": unreal.Character,
                "PlayerStart": unreal.PlayerStart,
            }
    
            actor_class = class_mappings.get(object_class)
    
            # If not found, try loading as native class
            if not actor_class:
                try:
                    actor_class = unreal.load_class(None, object_class)
                except Exception:
                    pass
    
            # If still not found, try finding by name
            if not actor_class:
                try:
                    actor_class = unreal.find_class(object_class)
                except Exception:
                    pass
    
            # If still not found, try loading as blueprint class last
            if not actor_class:
                try:
                    actor_class = unreal.EditorAssetLibrary.load_blueprint_class(
                        object_class
                    )
                except Exception:
                    pass
    
            if not actor_class:
                return {"error": f"Could not find class: {object_class}"}
    
            spawn_location = unreal.Vector(
                x=location.get("x", 0.0) if location else 0.0,
                y=location.get("y", 0.0) if location else 0.0,
                z=location.get("z", 0.0) if location else 0.0,
            )
    
            spawn_rotation = unreal.Rotator(
                pitch=rotation.get("pitch", 0.0) if rotation else 0.0,
                yaw=rotation.get("yaw", 0.0) if rotation else 0.0,
                roll=rotation.get("roll", 0.0) if rotation else 0.0,
            )
    
            spawn_scale = unreal.Vector(
                x=scale.get("x", 1.0) if scale else 1.0,
                y=scale.get("y", 1.0) if scale else 1.0,
                z=scale.get("z", 1.0) if scale else 1.0,
            )
    
            actor = unreal.EditorLevelLibrary.spawn_actor_from_class(
                actor_class, spawn_location, spawn_rotation
            )
    
            if not actor:
                return {"error": "Failed to spawn actor"}
    
            if object_name:
                actor.set_actor_label(object_name)
    
            actor.set_actor_scale3d(spawn_scale)
    
            # Apply default mesh and material for StaticMeshActor if no properties provided
            if actor.get_class().get_name() == "StaticMeshActor" and not properties:
                mesh_component = actor.get_component_by_class(unreal.StaticMeshComponent)
                if mesh_component:
                    name_lower = object_name.lower()
                    mesh_path = "/Engine/BasicShapes/Cube"  # Default fallback
    
                    if "sphere" in name_lower or "ball" in name_lower:
                        mesh_path = "/Engine/BasicShapes/Sphere"
                    elif "cylinder" in name_lower:
                        mesh_path = "/Engine/BasicShapes/Cylinder"
                    elif "cone" in name_lower:
                        mesh_path = "/Engine/BasicShapes/Cone"
                    elif "plane" in name_lower:
                        mesh_path = "/Engine/BasicShapes/Plane"
    
                    mesh = unreal.EditorAssetLibrary.load_asset(mesh_path)
                    if mesh:
                        mesh_component.set_static_mesh(mesh)
    
                    # Apply default material
                    default_material = unreal.EditorAssetLibrary.load_asset(
                        "/Engine/BasicShapes/BasicShapeMaterial"
                    )
                    if default_material:
                        mesh_component.set_material(0, default_material)
    
            if properties:
                for prop_name, prop_value in properties.items():
                    try:
                        if (
                            prop_name == "StaticMesh"
                            and actor.get_class().get_name() == "StaticMeshActor"
                        ):
                            static_mesh = unreal.EditorAssetLibrary.load_asset(prop_value)
                            if static_mesh:
                                mesh_component = actor.get_component_by_class(
                                    unreal.StaticMeshComponent
                                )
                                if mesh_component:
                                    mesh_component.set_static_mesh(static_mesh)
                        elif (
                            prop_name == "Material"
                            and actor.get_class().get_name() == "StaticMeshActor"
                        ):
                            material = unreal.EditorAssetLibrary.load_asset(prop_value)
                            if material:
                                mesh_component = actor.get_component_by_class(
                                    unreal.StaticMeshComponent
                                )
                                if mesh_component:
                                    mesh_component.set_material(0, material)
                        elif (
                            prop_name == "Materials"
                            and actor.get_class().get_name() == "StaticMeshActor"
                            and isinstance(prop_value, list)
                        ):
                            mesh_component = actor.get_component_by_class(
                                unreal.StaticMeshComponent
                            )
                            if mesh_component:
                                for i, material_path in enumerate(prop_value):
                                    if material_path:
                                        material = unreal.EditorAssetLibrary.load_asset(
                                            material_path
                                        )
                                        if material:
                                            mesh_component.set_material(i, material)
                        elif hasattr(actor, prop_name):
                            setattr(actor, prop_name, prop_value)
                    except Exception as e:
                        continue
    
            return {
                "success": True,
                "actor_name": actor.get_name(),
                "actor_label": actor.get_actor_label(),
                "class": actor.get_class().get_name(),
                "location": {
                    "x": actor.get_actor_location().x,
                    "y": actor.get_actor_location().y,
                    "z": actor.get_actor_location().z,
                },
                "rotation": {
                    "pitch": actor.get_actor_rotation().pitch,
                    "yaw": actor.get_actor_rotation().yaw,
                    "roll": actor.get_actor_rotation().roll,
                },
                "scale": {
                    "x": actor.get_actor_scale3d().x,
                    "y": actor.get_actor_scale3d().y,
                    "z": actor.get_actor_scale3d().z,
                },
            }
    
        except Exception as e:
            return {"error": f"Failed to create object: {str(e)}"}
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries full burden. It discloses the creation behavior and example output format, which is helpful. However, it lacks critical details like whether this requires specific permissions, if it's destructive to existing world state, error handling, or performance implications (e.g., rate limits). The output example adds some value but doesn't fully compensate for missing behavioral context.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is appropriately sized with two sentences: the first states the purpose, and the second describes the return format. The example output is detailed but relevant. However, it could be more front-loaded by integrating the return details into the first sentence for better clarity.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool's complexity (6 parameters with nested objects) and no annotations or output schema, the description is moderately complete. It covers the basic purpose and return format but lacks context on mutations, error cases, or integration with sibling tools. For a creation tool in a world editor, more guidance on typical workflows and limitations would improve completeness.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so the schema already documents all 6 parameters thoroughly. The description adds no additional parameter semantics beyond what's in the schema—it doesn't explain parameter interactions, default behaviors, or usage examples for the parameters themselves. This meets the baseline of 3 when schema does the heavy lifting.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the action ('Create a new object/actor in the world') and specifies the resource ('object/actor'), which is more specific than just restating the name. However, it doesn't explicitly differentiate from sibling tools like 'editor_update_object' or 'editor_delete_object', which would require a 5.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides no guidance on when to use this tool versus alternatives like 'editor_update_object' for modifying existing objects or 'editor_delete_object' for removal. It mentions no prerequisites, constraints, or typical use cases, leaving the agent to infer usage from context alone.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/runreal/unreal-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server