🎁 Get the FREE AI Skills Starter Guide β€” Subscribe β†’
BytesAgainBytesAgain
πŸ¦€ ClawHub

RhinoClaw

by @mcmuff86

Control Rhino 3D via AI agents. 72+ tools for geometry, transforms, booleans, PBR materials, Grasshopper automation, VisualARQ BIM objects, and viewport cont...

Versionv0.2.6
Downloads757
TERMINAL
clawhub install rhinoclaw

πŸ“– About This Skill


name: rhinoclaw version: 0.2.5 description: > Control Rhino 3D via AI agents. 72+ tools for geometry, transforms, booleans, PBR materials, Grasshopper automation, VisualARQ BIM objects, and viewport control. Create parametric models, architectural layouts, and export to IFC β€” all from your terminal. Optional VisualARQ integration for walls, doors, windows, levels, and IFC workflows. Requires RhinoClaw plugin running in Rhino 7/8 on Windows. author: McMuff86 repository: https://github.com/McMuff86/RhinoClaw tags: - rhino - 3d - cad - bim - grasshopper - visualarq - parametric - architecture - manufacturing - ifc

RhinoClaw Skill

Control Rhino 3D directly via TCP socket connection to the RhinoClaw plugin. 72+ tools for geometry creation, BIM workflows, Grasshopper automation, and more.

Plugin: github.com/McMuff86/RhinoClaw Β· Author: Solid AI

Prerequisites

1. Rhino 7/8 running on Windows 2. RhinoClaw plugin installed and built 3. Plugin started: In Rhino command line, type tcpstart (for WSL/remote access)

> Note: Use mcpstart for local-only access (Cursor, Claude Desktop), tcpstart for WSL/Clawdbot.

Configuration

Copy config.example.json to config.json and edit:

cp config.example.json config.json

{
  "connection": {
    "host": "YOUR_RHINO_HOST_IP",
    "port": 1999,
    "timeout": 15.0
  },
  "screenshots": {
    "linux_dir": "./captures",
    "windows_dir": ""
  }
}

Host IP depends on your setup:

  • Same machine: 127.0.0.1
  • WSL2 β†’ Windows: Your gateway IP (ip route show default | awk '{print $3}')
  • Remote (Tailscale/LAN): The IP of the Windows machine running Rhino
  • Quick Test

    cd ~/clawd/skills/rhinoclaw/scripts
    python3 rhino_client.py ping
    


    Scripts Reference

    | Script | Purpose | |--------|---------| | rhino_client.py | Base TCP client, raw commands | | geometry.py | Create primitives (box, sphere, cylinder, curves...) | | transforms.py | Move, rotate, scale, copy, mirror, arrays | | booleans.py | Union, difference, intersection | | selection.py | Select by layer, type, name, IDs | | analysis.py | Object info, properties, bounding box, volume | | curves.py | Offset, fillet, chamfer, join, explode | | surfaces.py | Loft, extrude, revolve, sweep | | layers.py | Create, set, list, delete layers | | materials.py | PBR materials, assign to layers | | viewport.py | Views, camera, screenshots | | render.py | Lights, render settings, render to file | | files.py | Open, save, import, export (STEP, OBJ, STL...) | | groups.py | Groups and block definitions | | scene.py | Document info, batch operations | | presets.py | Preset & Template manager for GH definitions | | ghscript.py | GH Definition builder + Compute Platform deployer | | visualarq.py | VisualARQ BIM objects (walls, doors, windows, levels, IFC) | | utils.py | Shared utilities (parse_coords, parse_color, format_result) |


    🎯 Geometry Creation

    # Primitives
    python3 geometry.py sphere --radius 5 --position 0,0,0 --name "Ball"
    python3 geometry.py box --width 10 --length 10 --height 5 --color 255,0,0
    python3 geometry.py cylinder --radius 2 --height 8 --layer "Parts"
    python3 geometry.py cone --radius 3 --height 6
    python3 geometry.py line --start 0,0,0 --end 10,10,0
    python3 geometry.py circle --radius 5
    python3 geometry.py arc --radius 5 --angle 90
    python3 geometry.py polyline --points "0,0,0 10,0,0 10,10,0 0,10,0"
    

    Supported Geometry Types

    | Type | Key Parameters | |------|----------------| | POINT | location | | LINE | start, end | | POLYLINE | points | | CIRCLE | center, radius | | ARC | center, radius, angle | | ELLIPSE | center, radius_x, radius_y | | CURVE | points, degree | | BOX | width, length, height | | SPHERE | radius | | CONE | radius, height | | CYLINDER | radius, height | | MESH | vertices, faces |


    πŸ”¨ Solid Operations

    # Fillet (round) edges of a solid
    python3 solids.py fillet  --radius 2.0

    Fillet specific edges only

    python3 solids.py fillet --radius 2.0 --edges 0,1,3

    Chamfer edges

    python3 solids.py chamfer --distance 1.5

    Keep original (don't delete input)

    python3 solids.py fillet --radius 2.0 --keep


    πŸ”„ Transform Operations

    # Move object
    python3 transforms.py move  --vector 10,0,0

    Rotate object (degrees around axis through point)

    python3 transforms.py rotate --angle 45 --axis 0,0,1 --center 0,0,0

    Scale object

    python3 transforms.py scale --factor 2.0 --center 0,0,0

    Copy with offset

    python3 transforms.py copy --offset 10,0,0

    Mirror across plane

    python3 transforms.py mirror --origin 0,0,0 --normal 1,0,0

    Linear array: 5 copies along X

    python3 transforms.py linear --direction 1,0,0 --count 5 --distance 10

    Polar array: 8 copies around Z axis

    python3 transforms.py polar --center 0,0,0 --axis 0,0,1 --count 8


    ⚑ Boolean Operations

    # Union multiple solids
    python3 booleans.py union   

    Difference: subtract cutter(s) from base

    python3 booleans.py difference

    Intersection

    python3 booleans.py intersection

    Keep input objects (don't delete)

    python3 booleans.py union --keep

    > Note: Objects must be closed solids (Breps).


    🎯 Selection

    # Select all
    python3 selection.py all

    Clear selection

    python3 selection.py none

    Get info about selected objects

    python3 selection.py get

    Select by layer

    python3 selection.py layer "MyLayer"

    Select by object type

    python3 selection.py type solid # solid, curve, surface, mesh, point, etc.

    Select by name (partial match)

    python3 selection.py name "Box"

    Select specific IDs

    python3 selection.py ids

    Combined filters

    python3 selection.py filter --layer "Parts" --type solid


    πŸ“Š Object Analysis

    # Basic object info
    python3 analysis.py info 

    Detailed properties (bounding box, area, volume, centroid)

    python3 analysis.py properties

    Info about selected objects

    python3 analysis.py selected

    Document summary

    python3 analysis.py document


    〰️ Curve Operations

    # Offset curve
    python3 curves.py offset  --distance 5

    Fillet two curves

    python3 curves.py fillet --radius 2

    Chamfer two curves

    python3 curves.py chamfer --distance 3

    Join curves into polycurve

    python3 curves.py join

    Explode polycurve into segments

    python3 curves.py explode

    Keep input (don't delete)

    python3 curves.py join --keep


    πŸ”οΈ Surface Operations

    # Loft through curves
    python3 surfaces.py loft   

    Extrude curve along vector

    python3 surfaces.py extrude --direction 0,0,10

    Revolve curve around axis

    python3 surfaces.py revolve --axis-start 0,0,0 --axis-end 0,0,1 --angle 360

    Sweep curve along rail

    python3 surfaces.py sweep

    Create planar surface from closed curve

    python3 surfaces.py planar


    πŸ“ Layers

    python3 layers.py create "MyLayer" --color 255,100,100
    python3 layers.py set "MyLayer"
    python3 layers.py list
    python3 layers.py delete "OldLayer"
    


    🎨 Materials (PBR)

    # Metal presets
    python3 materials.py preset gold
    python3 materials.py preset silver
    python3 materials.py preset copper

    Custom PBR material

    python3 materials.py pbr "Chrome" --color 200,200,210 --metallic 0.95 --roughness 0.02

    Assign material to layer

    python3 materials.py assign "MyLayer"


    πŸ“· Viewport & Screenshots

    # Set standard view
    python3 viewport.py view Perspective
    python3 viewport.py view Top

    Zoom to fit all

    python3 viewport.py zoom

    Zoom to selection

    python3 viewport.py zoom --selected

    Orbit camera

    python3 viewport.py orbit --yaw 45 --pitch 30

    Set camera position

    python3 viewport.py camera --position 100,100,50 --target 0,0,0 --lens 35

    Capture screenshot (saves to linux_dir, returns linux_path)

    python3 viewport.py screenshot --width 1920 --height 1080 python3 viewport.py screenshot --output myrender.png

    Render with materials

    python3 viewport.py render --output render.png

    > Screenshots are saved directly to the Linux filesystem via WSL UNC path. The returned linux_path can be read directly.


    πŸ’‘ Render & Lighting

    # Set render quality
    python3 render.py settings --width 1920 --height 1080 --quality high
    python3 render.py settings --background 50,50,50

    Add lights

    python3 render.py light point --position 50,50,100 --intensity 1.5 python3 render.py light directional --direction -1,-1,-1 python3 render.py light spot --position 0,0,100 --target 0,0,0

    Render to file

    python3 render.py render --output scene.png


    πŸ“¦ Files (Import/Export)

    # Open 3DM file
    python3 files.py open "/path/to/file.3dm"

    Save current document

    python3 files.py save python3 files.py save --path "/path/to/new.3dm"

    Export to various formats

    python3 files.py export output.step python3 files.py export output.obj --ids python3 files.py export output.stl --format stl

    Import mesh

    python3 files.py import model.obj

    Supported Export Formats

    STEP, IGES, OBJ, STL, DXF, DWG, 3DS, FBX, DAE


    πŸ“¦ Groups & Blocks

    # Create group
    python3 groups.py group   --name "MyGroup"

    Ungroup

    python3 groups.py ungroup --name "MyGroup"

    Create block definition

    python3 groups.py block-create "MyBlock" --base 0,0,0

    Insert block instance

    python3 groups.py block-insert "MyBlock" --position 10,0,0 --scale 2 --rotation 45

    Explode block

    python3 groups.py block-explode


    🌿 Grasshopper Player Automation

    Run Grasshopper definitions with custom parameters directly from CLI.

    # Show available parameters in a GH file
    python3 grasshopper.py info "C:/path/to/definition.gh"

    Run with default parameters

    python3 grasshopper.py run "C:/path/to/definition.gh"

    Run with custom parameters

    python3 grasshopper.py run "C:/path/to/definition.gh" --Lichthoehe 2200 --Lichtbreite 1000

    Set insertion point

    python3 grasshopper.py run "C:/path/to/definition.gh" --Point 100,200,0

    Parameter Discovery

    python3 grasshopper.py info "C:/path/to/definition.gh"
    

    Output shows: parameter name, default value, min/max range, type

    How It Works

    1. info loads the GH file via Grasshopper SDK to extract parameter metadata 2. run starts Rhino's GrasshopperPlayer via SendKeystrokes (non-blocking) 3. Script monitors command prompts and sends parameter values 4. Get Point prompts receive the --Point coordinate or default 0,0,0

    Presets (YAML-based)

    # List available presets
    python3 grasshopper.py preset --list

    Run a preset

    python3 grasshopper.py preset door_standard

    Override preset parameters

    python3 grasshopper.py preset door_standard --Lichthoehe 2200

    Presets are defined in config/presets.yaml, templates in config/templates.yaml.


    πŸ—οΈ GH Definition Builder (NEW)

    Programmatically create Grasshopper definitions with Python script components. The full pipeline: Generate Code β†’ Build .gh β†’ Deploy to Compute Platform β†’ Solve via API

    Build a Definition via RhinoClaw

    # Build a .gh file with inputs + Python script
    python3 ghscript.py build \
      --name "ParametricBox" \
      --script my_script.py \
      --inputs inputs.json \
      --output "C:/temp/gh_definitions/ParametricBox.gh"
    

    Build + Deploy to Rhino Compute Platform

    # Build .gh and deploy to ~/projects/rhino-compute-platform/definitions/
    python3 ghscript.py deploy \
      --name "ParametricBox" \
      --script my_script.py \
      --inputs inputs.json
    

    After deploy, the definition is immediately solvable:

    POST http://localhost:8100/solve
    { "definition": "ParametricBox.gh", "inputs": { "Width": 200, "Height": 100 } }
    

    Input Spec Format (inputs.json)

    [
      { "name": "Width",  "type": "number",  "default": 200, "min": 10, "max": 1000 },
      { "name": "Height", "type": "number",  "default": 100, "min": 10, "max": 500 },
      { "name": "Count",  "type": "integer", "default": 5,   "min": 1,  "max": 20 },
      { "name": "Label",  "type": "string",  "default": "Part-A" },
      { "name": "Mirror", "type": "boolean", "default": false }
    ]
    

    GHPython Script Template

    The script receives inputs as variables matching the input names:

    # Inputs: Width, Height (injected by Grasshopper)
    

    Output: Geometry (assigned to 'a' output)

    import Rhino.Geometry as rg

    plane = rg.Plane.WorldXY box = rg.Box(plane, rg.Interval(-Width/2, Width/2), rg.Interval(-Height/2, Height/2), rg.Interval(0, Height)) Geometry = box.ToBrep()

    Execute Python Directly in Rhino

    # Quick test without building a GH definition
    python3 ghscript.py exec --code "import Rhino; print(Rhino.RhinoApp.Version)"
    python3 ghscript.py exec --file my_script.py
    

    How It Works

    1. build_gh_definition command in RhinoClaw creates a GH document with: - Number Sliders / Boolean Toggles / Panels for each input - A Python 3 Script component with the provided code - Wired connections between inputs and script 2. The document is saved as binary .gh file 3. deploy copies to Compute Platform definitions/ and writes .meta.json 4. /solve endpoint picks it up automatically


    πŸ“œ RhinoScript Execution

    # Execute inline code
    python3 script_exec.py -c "import rhinoscriptsyntax as rs; rs.AddSphere([0,0,0], 10)"

    Execute script file

    python3 script_exec.py -f ~/scripts/my_script.py


    πŸ” Log Monitoring

    Check the Rhino log for debugging (requires Windows filesystem access / TOTP):

    # View recent log entries (needs TOTP unlock for /mnt/c access)
    tail -30 "/mnt/c/Users/YOUR_USERNAME/AppData/Local/Temp/rhinoclaw.log"

    Alternative: use rhino_client to query logs via TCP

    python3 rhino_client.py get_logs


    Example: Complete PBR Scene Workflow

    # 1. Create layer with material
    python3 layers.py create "Gold_Parts" --color 255,215,0
    python3 materials.py preset gold
    

    β†’ Note material_id

    2. Assign material to layer

    python3 materials.py assign "Gold_Parts" python3 layers.py set "Gold_Parts"

    3. Create geometry

    python3 geometry.py sphere --radius 5 --name "Gold_Ball" python3 geometry.py box --width 10 --length 10 --height 2 --position 0,0,-3

    4. Boolean difference (cut hole)

    python3 geometry.py cylinder --radius 2 --height 5 --position 0,0,-3 python3 booleans.py difference

    5. Set camera and capture

    python3 viewport.py camera --position 30,30,20 --target 0,0,0 --lens 35 python3 viewport.py screenshot --width 1920 --height 1080

    β†’ Returns linux_path, read directly with Read tool


    ⚠️ Error Handling

    The rhino_client.py provides custom exceptions for robust error handling:

    | Exception | When | |-----------|------| | RhinoConnectionError | Can't connect or connection lost | | RhinoTimeoutError | No response within timeout | | RhinoCommandError | Rhino returned an error | | ValidationError | Invalid parameters |

    Auto-retry: The client has a @with_retry decorator for automatic reconnection with exponential backoff (default: 3 retries).

    Context Manager: Use with RhinoClient() as client: for automatic connect/disconnect.


    πŸ—οΈ VisualARQ BIM Objects (OPTIONAL)

    Control VisualARQ BIM objects (walls, doors, windows, columns, beams, etc.).

    > Note: VisualARQ is an optional add-on. All commands gracefully degrade if not installed - no crashes or errors.

    Check Availability

    # Check if VisualARQ is installed
    python3 visualarq.py check

    Get available styles, levels, and buildings

    python3 visualarq.py info

    Walls

    # Create a wall
    python3 visualarq.py wall --style "Generic - 200mm" \
      --start 0,0,0 --end 10,0,0 --height 3.0 \
      --layer "Walls" --name "ExtWall_01"

    List available wall styles

    python3 visualarq.py wall-styles

    Add a new wall style

    python3 visualarq.py add-wall-style --name "Custom 300mm" --width 0.3

    Doors & Windows

    # Create a door (requires existing wall)
    python3 visualarq.py door --style "Single Swing" \
      --wall-id  --position 0.5 \
      --width 0.9 --height 2.1 --name "MainEntry"

    Create a window

    python3 visualarq.py window --style "Fixed" \ --wall-id --position 0.3 \ --width 1.2 --height 1.5 --name "Window_01"

    List available styles

    python3 visualarq.py door-styles python3 visualarq.py window-styles

    Position Parameter: 0.0 = start of wall, 1.0 = end of wall, 0.5 = middle

    Structural Elements

    # Create a column
    python3 visualarq.py column --style "Rectangular" \
      --position 0,0,0 --height 3.0 --name "Col_A1"

    Create a beam

    python3 visualarq.py beam --style "Rectangular" \ --start 0,0,3 --end 5,0,3 --name "Beam_B1"

    Create a slab (requires boundary curves)

    python3 visualarq.py slab --boundary , \ --thickness 0.25 --name "Slab_01"

    Levels & Buildings

    # List all levels
    python3 visualarq.py levels

    Add a new level

    python3 visualarq.py add-level --name "OG1" --elevation 3.0

    Add a building

    python3 visualarq.py add-building --name "Haus A"

    Custom BIM Parameters

    # Add a custom parameter to an object
    python3 visualarq.py add-param --name "FireRating" --type text \
      --object-id 

    Set parameter value

    python3 visualarq.py set-param --name "FireRating" \ --value "EI30" --object-id

    Get parameter value

    python3 visualarq.py get-param --name "FireRating" --object-id

    Parameter Types: text, number, integer, boolean, length

    IFC Import/Export

    # Export to IFC
    python3 visualarq.py ifc-export --path "output.ifc" --version "IFC4"

    Import IFC file

    python3 visualarq.py ifc-import --path "model.ifc"

    Supported IFC Versions: IFC2x3, IFC4, IFC4.3

    Query VisualARQ Objects

    # List all walls with properties
    python3 visualarq.py list-walls

    List all doors with properties

    python3 visualarq.py list-doors

    List all windows

    python3 visualarq.py list-windows

    Overview of all VisualARQ objects by type

    python3 visualarq.py list-objects

    Example: Complete BIM Workflow

    # 1. Check if VisualARQ is available
    python3 visualarq.py check
    

    β†’ {"available": true, "version": "detected"}

    2. Create building structure

    python3 visualarq.py add-building --name "Office Building" python3 visualarq.py add-level --name "Ground Floor" --elevation 0.0 python3 visualarq.py add-level --name "First Floor" --elevation 3.5

    3. Create walls

    python3 visualarq.py wall --style "Generic - 200mm" \ --start 0,0,0 --end 10,0,0 --height 3.0 --name "SouthWall"

    β†’ Note wall_id for door/window insertion

    4. Add openings

    python3 visualarq.py door --style "Single Swing" \ --wall-id --position 0.7 \ --width 0.9 --height 2.1 --name "MainEntry"

    python3 visualarq.py window --style "Fixed" \ --wall-id --position 0.3 \ --width 1.5 --height 1.2 --name "SouthWindow"

    5. Add custom BIM data

    python3 visualarq.py add-param --name "FireRating" --type text \ --object-id python3 visualarq.py set-param --name "FireRating" \ --value "EI90" --object-id

    6. Export to IFC

    python3 visualarq.py ifc-export --path "office_building.ifc" --version "IFC4"


    Troubleshooting

    | Problem | Solution | |---------|----------| | Connection refused | Run tcpstart in Rhino command line | | Timeout | Increase timeout in config.json (max 120s) | | Boolean failed | Ensure objects are closed solids (Breps) | | Screenshot path issues | Check windows_dir UNC path in config | | Command not found | Rebuild plugin after C# changes | | Objects on wrong layer | Set layer first with layers.py set "LayerName" | | PBR material not visible | Materials use RenderMaterials table (fixed in 0.1.3.7+) | | Script timeout | Use --timeout 60 for large scripts | | RhinoConnectionError | Auto-retry is built in (3 attempts), check if plugin running | | VisualARQ not available | Install VisualARQ plugin or gracefully ignore VA commands |

    βš™οΈ Configuration

    Copy config.example.json to config.json and edit:

    cp config.example.json config.json
    

    {
      "connection": {
        "host": "YOUR_RHINO_HOST_IP",
        "port": 1999,
        "timeout": 15.0
      },
      "screenshots": {
        "linux_dir": "./captures",
        "windows_dir": ""
      }
    }
    

    Host IP depends on your setup:

  • Same machine: 127.0.0.1
  • WSL2 β†’ Windows: Your gateway IP (ip route show default | awk '{print $3}')
  • Remote (Tailscale/LAN): The IP of the Windows machine running Rhino
  • πŸ“‹ Tips & Best Practices

    | Problem | Solution | |---------|----------| | Connection refused | Run tcpstart in Rhino command line | | Timeout | Increase timeout in config.json (max 120s) | | Boolean failed | Ensure objects are closed solids (Breps) | | Screenshot path issues | Check windows_dir UNC path in config | | Command not found | Rebuild plugin after C# changes | | Objects on wrong layer | Set layer first with layers.py set "LayerName" | | PBR material not visible | Materials use RenderMaterials table (fixed in 0.1.3.7+) | | Script timeout | Use --timeout 60 for large scripts | | RhinoConnectionError | Auto-retry is built in (3 attempts), check if plugin running | | VisualARQ not available | Install VisualARQ plugin or gracefully ignore VA commands |