Skip to content

Add your robot

Shape the prompt

Every field is optional. What you leave blank, the prompt tells your agent to ask you for rather than guess.

Floating: legs or wheels. Fixed: bolted down.

PD stiffness, if you know it.

PD damping, likewise.

A link or a path your agent can open.

The prompt

Paste it into Claude Code, Cursor, ChatGPT or any agent that can write files. It comes back with a zip, and a note.

I want to add my robot to MouseMouse (https://mousemouse.ai) and I do not have a robot model file yet. Write one for me: a URDF or an MJCF (MuJoCo XML) with its meshes, zipped, that the hub's drop at https://mousemouse.ai/robots/new accepts without guessing. Everything below is what the hub actually checks; it refuses with reasons and repairs nothing.

## My robot
- Name: my robot
- Kind: legged — quadruped or biped; base: floating (legs or wheels — a free joint on the root body)
- Joints a policy drives: unknown — ask me, or derive it from the reference below
- Motors: not stated — gains not stated (I will declare kp/kd at the drop, or the MJCF's <position> actuators can carry them)
- Control rate: not stated (I declare it at the drop; no model file carries it)
- Reference (datasheet, CAD, an existing URDF): none
Ask me before you invent a mass, a joint range, a gear ratio or a gain: a guessed number passes the check and fails on the real robot.

## What a Lucen robot file is
1. One model file — a URDF (root element `<robot>`) or an MJCF (root element `<mujoco>`) — and every mesh and texture it references, in one folder named after the robot, zipped. Starting from nothing, write an MJCF: it can carry the keyframe, the actuators with their gains and a floor, and a URDF cannot. One top-level folder around everything is fine (it is stripped). Paths inside the zip are relative: `..`, an absolute path, a symlink, an encrypted entry, `__MACOSX/` and `.DS_Store` are refused and named, never silently fixed.
2. Limits: at most 4,096 files, 256 MiB per file, 1 GiB unpacked; an MJCF file is read up to 4 MiB. Decimate meshes rather than ship CAD tessellation.
3. Meshes are STL or OBJ (MSH also compiles), in metres — CAD exported in millimetres gets `scale="0.001 0.001 0.001"` on the `<mesh>`. `.dae` is refused: MuJoCo cannot read it. A URDF's `package://pkg/path` meshes are resolved by walking up from the URDF, so put them next to it or under a folder named after the package; `file://` and relative paths work too. In an MJCF, `<compiler meshdir="…"/>` is relative to the main model's directory, and an `<include file="…"/>` is relative to the including file. Give each link simple collision geometry (capsules, boxes) beside its visual mesh where you can: mesh-on-mesh contact at a joint is what `model.self_collision` catches.
4. A URDF is converted by MuJoCo's own compiler, and the conversion drops what MJCF has no place for: actuators (URDF has none — gains, action semantics and the control rate are declared at the drop), velocity limits (kept in the card's `limits`), `<transmission>`, `<gazebo>` blocks, and `<mimic>` (the joint moves independently; couple it with an `<equality>` in an MJCF if the robot does). Every moving link needs an `<inertial>` with a positive mass and inertia — MuJoCo invents one at 1000 kg/m³ for a link without, which is almost never right and is named in the report.
5. Units are SI: metres, kilograms, seconds, radians for a hinge and metres for a slide. Name every joint, meaningfully and consistently (`FL_hip`, `shoulder_pan`, `wheel_left`): letters, digits and `_ . : - /`, up to 128 characters. The joint order is the order the joints appear in the file (the body tree, depth first); it is the policy's index space and its sha256 is the fingerprint every policy and dataset is checked against — so the names and their order are a contract, not decoration. Only hinge and slide (one-DoF) joints can be in that order; a ball joint cannot. Give every joint a range (`<limit lower="…" upper="…" effort="…" velocity="…"/>` in a URDF; `range="lo hi"` on the joint in an MJCF) and an effort limit (`forcerange="-E E"` on an MJCF actuator).
6. A default pose the robot starts from, with every joint inside its range and not on a limit: in an MJCF, a `<keyframe><key name="home" qpos="…"/></keyframe>` with one value per joint in model order (a floating base adds seven in front: position xyz and quaternion wxyz); the first keyframe is read, and the base height of a floating robot is read from it. A URDF has no keyframe, so the robot starts at every joint's zero; where that puts a joint outside its range the drop proposes a pose from the ranges — so make the ranges right.
7. In an MJCF, `<position>` actuators with `kp` and `kv` let the gains be read off the model; torque `<motor>`s or no `<actuator>` block mean I declare kp/kd at the drop. Set `<option timestep="…"/>` (0.002 s is typical; the control rate above is then every 1/(rate × timestep) steps): the card's physics timestep is the model's.
- Floating base: in an MJCF, a `<freejoint/>` on the root body and a floor (`<geom type="plane"/>`) in the scene so the robot has something to stand on — a floating base with no floor falls forever and `validate` says so (`model.floor`). In a URDF, no floating joint: the drop is told `base: floating` and the conversion adds the free joint and lifts the root so the lowest collision geometry rests on z = 0.
- The feet are the leaf bodies of each leg; name them (`FL_foot`). The default pose is the standing pose, with every joint inside its range.

## What the file cannot carry — I declare these at the drop
The card's `interface`, with the hub's own field names; the file decides the first three, so get them right:
- joint_order — The policy's index space. Hashed, unique, never reordered silently. (derived — the model's named hinge/slide joints, in model order)
- base · init_base_height_m — Fixed or floating, which decides the task templates and gate batteries that apply. (derived — a free joint means floating; a URDF must be told with --base)
- default_pose · default_keyframe — Where the robot starts, and what position_offset is relative to. (derived — keyframe 0, else qpos0 (and then listed under needs_you))
- control_rate_hz · physics_dt_s — The rates a policy is trained and run at. (--control-hz is required; the timestep is the model's)
- action — What one action vector means: position_offset, position_absolute, velocity or torque, with its scale and clip. (--action is required; the scale is asked for)
- actuation[] — Per joint group: mode, kp/kd, effort and velocity limits, armature, friction — none of which a URDF carries. (derived from the MJCF's position actuators, else --kp/--kd/--armature, else blocking)
- proprioception[] — What the REAL robot can observe. A policy's observations must be a subset. (asked — suggested from the MJCF's sensors)
- parts[] · feet[] · end_effectors[] · cameras[] — Named slices of the joint order and the places tasks and image slots key on. (candidates from the kinematic tree and the MJCF's cameras — to confirm)
- command_space · safety — What a person or planner commands, and how the robot stops. (asked (--command); safety has documented defaults)

## The checks it will face
On the hub, statically (a failing check is a 422 naming the code; nothing is written and nothing is repaired):
- mjcf.exists: mjcf_path is a file of this repo — push first, then set the card
- mjcf.parses: it is MuJoCo XML; parsed with a hardened parser, capped in size
- assets.closure: every referenced asset and include is in the repo
- interface.joint_order: every name is a 1-DoF joint of the MJCF
- interface.base: fixed/floating agrees with the model's free joint
- interface.keyframe: default_keyframe exists in the model
Then `lucen robot init` + `lucen robot validate` run with real MuJoCo 3.12.0 — as the hub's hosted check, or on a machine I start a worker on — and the report has these codes:
- mjcf.compiles: MuJoCo's own verdict, in its own words
- assets.closure: every mesh, texture and include is in the directory; ../ out of it is refused
- interface.joint_order · interface.base: names exist and are 1-DoF; fixed/floating matches the free joint
- interface.default_pose · interface.actuation: the pose is inside the joint ranges (and warned about outside the soft range the sim gate scores); every joint is driven by exactly one group, or flagged
- model.inertia · model.gains: no massless moving body; kp and kd are plausible against each joint's inertia and the timestep
- model.self_collision: no bodies already interpenetrating at the default pose
- stability.settle: N seconds under gravity on the declared gains: joint drift, base drop, penetration, NaNs
- stability.joint_response: every driven joint stepped a little on the declared gains: one that barely moves is jammed
- sweep.joints: every joint through its full range: finite, no explosion
Pre-check your own output before handing it over, if you can run Python: `pip install mujoco==3.12.0`, then `mujoco.MjModel.from_xml_path("<file>")` must compile it (a URDF too), and the pose you chose must sit inside every joint's range (`jnt_range`).

## How to finish
- Hand me the zip. I drop it at https://mousemouse.ai/robots/new, pick the model file, declare the control rate and the action (and the base for a URDF), the hub uploads it into a private repo and runs the check, and I publish from the report.
- Or, if you can run a terminal: install the CLI with `curl -fsSL https://api.mousemouse.ai/install.sh | sh -s -- "cli[rollout]"`, then:
    lucen robot init PATH --control-hz HZ --action position_offset|position_absolute|velocity|torque [--base fixed|floating] [--kp KP --kd KD]
    lucen robot validate DIR
    lucen robot publish OWNER/SLUG DIR
- Or, if you have the Lucen MCP server (`lucen-mcp`), use its tools: `init_robot` on the model file, then `publish_robot` with `dry_run=true` for the MuJoCo report and `dry_run=false` to publish; `validate_robot` asks the hub for its static checks without writing.
- With the zip, give me a short note: the joint order you chose and why, the default pose, the ranges and masses you took from the reference versus the ones you assumed, and anything from the list above I still have to declare.

## Rules
- Fix the model, never the check: do not rename a joint, flip the base type, delete a mesh or widen a range just to make a check pass.
- Do not invent numbers silently. Ask, or mark each assumption in the note as an assumption.
- A STEP or other CAD export is not a robot file: the hub does not read it. Derive the links, joints and meshes from it and write the URDF or MJCF.

What comes back: a zip of the URDF or MJCF with its meshes, and a note on what was taken from your reference and what was assumed. Drop the zip on the first path, declare the rate and the action, and the hub checks it the same way as any other.