rhinomcp

star 0

Control Rhino 3D via RhinoMCP plugin. Use when creating, modifying, or deleting 3D geometry (spheres, boxes, cylinders, lines, curves, meshes), managing layers and materials (including PBR), boolean operations, transforms, file import/export, groups/blocks, or querying document information. Requires Rhino running on Windows with RhinoMCP plugin started (`tcpstart` command for WSL access).

McMuff86 By McMuff86 schedule Updated 1/28/2026

name: rhinomcp description: Control Rhino 3D via RhinoMCP plugin. Use when creating, modifying, or deleting 3D geometry (spheres, boxes, cylinders, lines, curves, meshes), managing layers and materials (including PBR), boolean operations, transforms, file import/export, groups/blocks, or querying document information. Requires Rhino running on Windows with RhinoMCP plugin started (tcpstart command for WSL access).

RhinoMCP Skill

Control Rhino 3D directly from Clawdbot via TCP socket connection to the RhinoMCP plugin.

Prerequisites

  1. Rhino 7/8 running on Windows
  2. RhinoMCP 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

Edit config.json:

{
  "connection": {
    "host": "172.31.96.1",
    "port": 1999,
    "timeout": 15.0
  },
  "screenshots": {
    "linux_dir": "/home/mcmuff/clawd/projects/rhinomcp-dev/captures",
    "windows_dir": "\\\\wsl.localhost\\Ubuntu\\home\\mcmuff\\clawd\\projects\\rhinomcp-dev\\captures"
  }
}

Quick Test

cd ~/clawd/skills/rhinomcp/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
grasshopper.py Grasshopper Player automation (run/info/preset with custom params)
presets.py Preset & Template manager for GH definitions
scene.py Document info, batch operations

🌿 Grasshopper Player Automation

Run Grasshopper definitions with custom parameters directly from CLI.

Basic Usage

# 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/Rahmentuer_UD4.gh"
# Output:
# Available Parameters (26):
# ----------------------------------------------------------
#   --Lichthoehe = 2100.0 [1800.0 - 2600.0]  (Number)
#   --Lichtbreite = 900.0 [600.0 - 1400.0]  (Number)
#   --Rahmendicke = 53.0  (Number)
#   --Tuerstaerke = 58.0  (Number)
#   --DichtNut_Rahmen = True  (Boolean)
#   ...

Parameter Aliases

GH nicknames are automatically mapped to Player prompt names:

GH Nickname Maps to
Pt Point
Punkt Point
Position Point
Pos Point

Add custom aliases in PARAM_ALIASES dict in grasshopper.py.

How It Works

  1. info loads the GH file via Grasshopper SDK to extract parameter metadata (names, types, defaults, min/max)
  2. run starts Rhino's GrasshopperPlayer command via SendKeystrokes (non-blocking)
  3. Script monitors command prompts and sends parameter values
  4. Prompts like Lichthoehe <2100> get the custom or default value
  5. Get Point prompts receive the --Point coordinate or default 0,0,0

Example: Create Multiple Doors

# Door 1: Standard at origin
python3 grasshopper.py run "C:/path/to/Rahmentuer_UD4.gh" \
  --Lichthoehe 2100 --Lichtbreite 900 --Point 0,0,0

# Door 2: Wider door offset 1500mm
python3 grasshopper.py run "C:/path/to/Rahmentuer_UD4.gh" \
  --Lichthoehe 2100 --Lichtbreite 1000 --Point 1500,0,0

# Door 3: Taller door offset 3000mm
python3 grasshopper.py run "C:/path/to/Rahmentuer_UD4.gh" \
  --Lichthoehe 2300 --Lichtbreite 900 --Point 3000,0,0

Debugging

# Enable verbose logging
export RHINOMCP_DEBUG=1
python3 grasshopper.py run "C:/path/to/file.gh" --Lichthoehe 2200

🎯 Presets & Templates

Quick access to common Grasshopper configurations.

List Presets

python3 grasshopper.py presets
# Output:
# Available Presets (5):
# ------------------------------------------------------------
#   standard_900           Standard Innentür 900mm
#   standard_800           Standard Innentür 800mm
#   brandschutz_t30        Brandschutztür T30 (EI30)
#   nasszelle_750          Nasszelltür 750mm (WC/Bad)
#   aussentuer_schwelle    Aussentür mit Schwelle

Run a Preset

# Run with defaults
python3 grasshopper.py preset standard_900 --Point 0,0,0

# Run with parameter override
python3 grasshopper.py preset standard_900 --Lichthoehe 2200 --Point 1500,0,0

# Show preset details
python3 grasshopper.py preset standard_900 --info

# Validate only
python3 grasshopper.py preset brandschutz_t30 --validate

Templates

Templates map friendly names to GH files with defaults and validation:

python3 grasshopper.py templates

Adding Custom Presets

Edit config/presets.yaml to add your own:

presets:
  my_custom_door:
    description: "My custom door preset"
    template: rahmentuer_ud4
    params:
      Lichtbreite: 1200
      Lichthoehe: 2400

Templates support inheritance — see config/templates.yaml.


🎯 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

🔄 Transform Operations

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

# Rotate object (degrees around axis through point)
python3 transforms.py rotate <id> --angle 45 --axis 0,0,1 --center 0,0,0

# Scale object
python3 transforms.py scale <id> --factor 2.0 --center 0,0,0

# Copy with offset
python3 transforms.py copy <id> --offset 10,0,0

# Mirror across plane
python3 transforms.py mirror <id> --origin 0,0,0 --normal 1,0,0

# Linear array: 5 copies along X
python3 transforms.py linear <id> --direction 1,0,0 --count 5 --distance 10

# Polar array: 8 copies around Z axis
python3 transforms.py polar <id> --center 0,0,0 --axis 0,0,1 --count 8

⚡ Boolean Operations

# Union multiple solids
python3 booleans.py union <id1> <id2> <id3>

# Difference: subtract cutter(s) from base
python3 booleans.py difference <base_id> <cutter_id>

# Intersection
python3 booleans.py intersection <id1> <id2>

# Keep input objects (don't delete)
python3 booleans.py union <id1> <id2> --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 <id1> <id2> <id3>

# Combined filters
python3 selection.py filter --layer "Parts" --type solid

📊 Object Analysis

# Basic object info
python3 analysis.py info <object_id>

# Detailed properties (bounding box, area, volume, centroid)
python3 analysis.py properties <object_id>

# Info about selected objects
python3 analysis.py selected

# Document summary
python3 analysis.py document

〰️ Curve Operations

# Offset curve
python3 curves.py offset <curve_id> --distance 5

# Fillet two curves
python3 curves.py fillet <curve1_id> <curve2_id> --radius 2

# Chamfer two curves
python3 curves.py chamfer <curve1_id> <curve2_id> --distance 3

# Join curves into polycurve
python3 curves.py join <id1> <id2> <id3>

# Explode polycurve into segments
python3 curves.py explode <polycurve_id>

# Keep input (don't delete)
python3 curves.py join <id1> <id2> --keep

🏔️ Surface Operations

# Loft through curves
python3 surfaces.py loft <curve1_id> <curve2_id> <curve3_id>

# Extrude curve along vector
python3 surfaces.py extrude <curve_id> --direction 0,0,10

# Revolve curve around axis
python3 surfaces.py revolve <curve_id> --axis-start 0,0,0 --axis-end 0,0,1 --angle 360

# Sweep curve along rail
python3 surfaces.py sweep <profile_id> <rail_id>

# Create planar surface from closed curve
python3 surfaces.py planar <closed_curve_id>

📁 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" <material_id>

📷 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 <id1> <id2>
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 <id1> <id2> --name "MyGroup"

# Ungroup
python3 groups.py ungroup --name "MyGroup"

# Create block definition
python3 groups.py block-create "MyBlock" <id1> <id2> --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 <instance_id>

📜 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:

# View recent log entries
tail -30 "/mnt/c/Users/Adi.Muff/AppData/Local/Temp/rhinomcp.log"

# Continuous monitoring
tail -f "/mnt/c/Users/Adi.Muff/AppData/Local/Temp/rhinomcp.log" &

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" <material_id>
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 <box_id> <cylinder_id>

# 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

Troubleshooting

Problem Solution
Connection refused Run tcpstart in Rhino
Timeout Increase timeout in config.json
Boolean failed Ensure objects are closed solids
Screenshot path issues Check windows_dir UNC path in config
Command not found Rebuild plugin after C# changes
Install via CLI
npx skills add https://github.com/McMuff86/rhinomcp --skill rhinomcp
Repository Details
star Stars 0
call_split Forks 0
navigation Branch main
article Path SKILL.md
More from Creator