A complete worked example of assembling a cabinet with a rigid frame and 3 sliding drawers using the Onshape MCP server.
Cabinet frame (6 parts from one Part Studio):
- Back panel (reference part — grounded)
- Left panel, Right panel
- Bottom shelf, Top shelf
- Door
Each drawer (5 parts per drawer, 3 drawers, from separate Part Studios):
- Front panel
- Left side, Right side
- Back panel
- Bottom panel
Total: 21 instances, 60 features (51 fastened mates + 9 slider features)
- Ground the back panel as the reference
- Attach frame parts with fastened mates (5 mates = 15 features: 10 MCs + 5 mates)
- Add drawer instances and position them
- Attach drawer parts internally with fastened mates (12 mates per drawer = 36 features)
- Create slider mates between drawers and cabinet (3 sliders = 9 features)
1. create_document → get documentId
2. create_assembly → get assemblyId (elementId)
3. Add all instances from Part Studios using add_assembly_instance
The first instance added (back panel) is automatically grounded.
Before creating any mates, get the face IDs from each Part Studio:
get_body_details(documentId, workspaceId, partStudioElementId)
This returns face deterministic IDs like "JHW", "JKG", etc. These IDs are stable — the same face ID works for every instance of that part in the assembly.
Tip: Map out which faces you'll use for each mate before starting. For the cabinet:
- Back panel front face → mates with Left, Right, Bottom, Top
- Left panel inner face → mates with drawer left panels (for sliders)
Position frame parts near their targets, then create fastened mates:
create_fastened_mate(
firstInstanceId=back_panel_id,
secondInstanceId=left_panel_id,
firstFaceId="JHW", # back panel left edge face
secondFaceId="JKG", # left panel back edge face
firstOffsetX=0, firstOffsetY=0, firstOffsetZ=0,
secondOffsetX=0, secondOffsetY=0, secondOffsetZ=0,
)
After each batch of mates, verify:
get_assembly_features → all features should be "OK"
get_assembly_positions → check relative positions against design
When mating to a grounded part (e.g., the back panel):
- Anti-parallel faces (face-to-face contact) work fine since the grounded part anchors the solver
- Pick the touching faces and the solver rotates the free part into position
When mating two free-floating parts (e.g., drawer front → drawer left):
- Do NOT use anti-parallel faces — the solver rotates parts 180° to align opposite MC Z-axes
- Instead, use same-direction faces (same outward normal on both parts)
- Use
offsetZto bridge the gap between the non-coplanar parallel faces
# Same-direction technique: both faces have normal (0, -1, 0)
create_fastened_mate(
firstInstanceId=drawer_front_id,
secondInstanceId=drawer_left_id,
firstFaceId="JHK", # -Y face on front panel
secondFaceId="JHK", # -Y face on left panel (same direction!)
firstOffsetZ=-0.75, # Bridge the 0.75" gap
)
See knowledge_base/assembly_workflow_guide.md → "Fastened Mate Face Selection" for full explanation.
Each drawer has 4 internal fastened mates (front→left, front→right, front→back, front→bottom).
Since all 3 drawers use the same Part Studio, the face IDs are identical:
# Same face IDs work for all 3 drawers
DRAWER_FRONT_LEFT_FACE = "ABC"
DRAWER_LEFT_BACK_FACE = "DEF"Workflow for each drawer:
- Position the drawer front panel at the correct absolute location
- Create fastened mates from front → left, right, back, bottom
- Verify all 5 drawer parts are correctly positioned
This is the most nuanced step. Key decisions:
For a drawer sliding in/out of a cabinet:
- Cabinet face: Inner face of the left (or right) panel — the face the drawer slides against
- Drawer face: Outer face of the drawer's left (or right) panel — the face that touches the cabinet
This is critical. The first instance slides relative to the second:
# Drawer first → positive travel = drawer slides OUT (forward)
create_slider_mate(
firstInstanceId=drawer_left_id, # ← moving part
secondInstanceId=cabinet_left_id, # ← fixed reference
firstFaceId=drawer_left_face,
secondFaceId=cabinet_left_face,
)
If you put the cabinet first, positive travel pushes the drawer backward (into the cabinet wall).
When multiple drawers slide against the same cabinet panel, each needs a different MC position on that panel. Use offsets to place the cabinet MC at the correct height for each drawer:
# D1 (bottom drawer): offset to lower position on cabinet left face
create_mate_connector(
instanceId=cabinet_left_id,
faceId=cabinet_left_face,
offsetX=0.875, # in from edge
offsetY=10.5, # height for bottom drawer (in MC local coords)
offsetZ=0,
)
# D2 (middle drawer): different Y offset
create_mate_connector(..., offsetY=1.0, ...)
# D3 (top drawer): different Y offset
create_mate_connector(..., offsetY=-8.5, ...)
Remember: Offsets are in the MC's local coordinate system, not world space. The Y offset on a face pointing in the +X world direction maps to a different world axis than Y offset on a face pointing in +Z.
Create slider mates without minLimit/maxLimit first. Limits trigger the Onshape solver to re-evaluate from scratch, which can flip parts to unexpected orientations.
# Good: no limits, verify animation works first
create_slider_mate(
firstInstanceId=drawer_left, secondInstanceId=cabinet_left,
firstFaceId="JHG", secondFaceId="JKG",
name="D1 Slider",
# minLimit and maxLimit omitted
)
After verifying the slider works correctly (drawer slides in the right direction, animation works), add limits manually in the Onshape UI if needed.
get_assembly_features → expect all 60 features "OK"
get_assembly_positions → for each instance:
relative_pos = instance_pos - back_panel_pos
compare against expected design positions
Expected relative positions (from back panel):
Left: ( 0.00, 16.00, 0.00) Right: (23.25, 16.00, 0.00)
Bottom: ( 0.00, 0.00, 0.00) Top: ( 0.00, 0.00, 29.25)
D1-Front: ( 0.75, 16.75, 0.75) D1-Left: ( 1.00, 16.00, 0.75)
D2-Front: ( 0.75, 16.75, 10.25) D2-Left: ( 1.00, 16.00, 10.25)
D3-Front: ( 0.75, 16.75, 19.75) D3-Left: ( 1.00, 16.00, 19.75)
In the Onshape UI:
- Right-click a slider mate → "Animate"
- Set the end value (e.g., 10 inches)
- Verify the drawer slides forward (out of the cabinet)
- If it slides backward, swap the instance order in the slider mate
The order of instances in create_slider_mate determines which direction is "positive." For drawers, put the drawer first (moving part) and cabinet second (fixed reference) so positive = outward.
API-set limits cause the solver to re-evaluate from scratch. Without limits, the solver uses the current position as a hint. Create mates without limits, verify everything works, then add limits in the UI.
If the reference part is at the wrong position and is grounded, you can't move it with transform_instance or set_instance_position. Unfix it in the Onshape UI first.
When the back panel is at (100, 200, 300) instead of (0, 0, 0), a drawer at relative (0.75, 16.75, 0.75) needs absolute position (100.75, 216.75, 300.75).
Parts from the same Part Studio share face IDs. If the drawer left panel's outer face is "JHG" for drawer 1, it's also "JHG" for drawers 2 and 3.
Don't create all 60 features at once. Create the frame mates (15 features), verify, then drawer mates (36 features), verify, then sliders (9 features), verify. Much easier to debug 3 features than 60.
If you click "Solve" or add limits, the solver may find a valid but wrong configuration (e.g., drawer upside down). This happens because face-centered MCs on symmetric faces don't uniquely determine orientation.
Using face-to-face contact surfaces (opposite normals) for fastened mates between two free-floating parts causes the solver to rotate one part 180° to align the MC coordinate frames. A drawer left panel at Y=[-16, -2] can flip to Y=[-30, -16]. Fix: Use same-direction faces (same outward normal on both parts) with offsetZ to bridge the gap. Anti-parallel is only safe when one part is grounded.