Skip to main content
Glama
kbangaru-cyber

RhinoMCP

README.md
# RhinoMCP: an MCP Server for Rhino 3D

> **105 tools** for AI-driven 3D modeling, analysis, diagnostics, and Grasshopper integration via the [Model Context Protocol](https://modelcontextprotocol.io/).

RhinoMCP connects Claude to Rhino 3D through a comprehensive set of tools covering everything from basic primitives to advanced surface modeling (loft, sweep, revolve), boolean operations, mesh diagnostics, block management, and parametric design with Grasshopper.

## Architecture

```
┌────────────────┐   stdio    ┌──────────────┐   TCP/JSON    ┌──────────────┐
│ Claude Desktop │◄──────────►│  rhino_mcp   │◄─────────────►│    Rhino 3D  │
│                │   (MCP)    │  (Python)    │  localhost:   │  listener.py │
│                │            │  server.py   │    9876       │  (inside     │
└────────────────┘            └──────────────┘               │   Rhino)     │
                                                             └──────────────┘
```

**MCP Server** (`rhino_mcp/`). Python package using FastMCP. Communicates with Claude via stdio and with Rhino via TCP.

**Rhino Listener** (`rhino/listener.py`). Python script that runs inside Rhino. Opens a TCP server, receives JSON commands, executes them using RhinoCommon/rhinoscriptsyntax, and returns results.

## Capabilities

### Geometry Creation (21 tools)
| Tool | Description |
|------|-------------|
| `create_point`, `create_points` | Single or batch point creation |
| `create_line`, `create_polyline` | Lines and polylines |
| `create_curve` | Interpolated NURBS curves |
| `create_circle`, `create_arc`, `create_ellipse` | Circular geometry |
| `create_rectangle` | Rectangular polylines |
| `create_sphere`, `create_box`, `create_cylinder`, `create_cone`, `create_torus` | Solid primitives |
| `create_extrusion`, `create_pipe` | Extrude and pipe along curves |
| `create_surface_from_points`, `create_mesh` | NURBS surfaces and meshes |
| `create_text` | Text dots |
| **`create_loft`** | Loft surfaces through cross-sections |
| **`create_sweep1`**, **`create_sweep2`** | Single and two-rail sweeps |
| **`create_revolve`** | Revolve profiles around an axis |
| **`create_planar_surface`**, **`create_patch`** | Planar and patch surfaces |

### Transforms (8 tools)
`move`, `rotate`, `scale`, **`scale_non_uniform`**, `mirror`, `copy`, **`array_linear`**, **`array_polar`**

### Boolean Operations (3 tools)
`boolean_union`, `boolean_difference`, `boolean_intersection`

### Modification (18 tools)
| Tool | Description |
|------|-------------|
| `delete`, `set_color`, `set_material`, `set_name`, `set_layer` | Basic modifications |
| `group`, **`ungroup`** | Grouping |
| `join`, `explode` | Join and explode geometry |
| **`trim`**, **`split`** | Trim and split curves |
| `offset_curve`, **`offset_surface`** | Offset geometry |
| **`fillet_edge`**, **`chamfer_edge`**, **`fillet_curves`** | Edge treatments |
| `cap_planar_holes` | Cap open breps |
| **`extend_curve`**, **`rebuild_curve`** | Curve editing |
| **`project_to_surface`** | Project curves onto surfaces |
| **`duplicate_edge`**, **`duplicate_border`** | Extract edges/borders |

### Blocks & Instances (4 tools)
**`create_block`**, **`insert_block`**, **`list_blocks`**, **`explode_block`**

### User Data (2 tools)
**`set_user_text`**, **`get_user_text`**: store metadata, BIM data, costs, etc.

### Layers (6 tools)
`list_layers`, `create_layer`, `delete_layer`, `set_layer_color`, `set_layer_visibility`, `set_current_layer`

### Materials (3 tools)
`list_materials`, `create_material`, `assign_material`

### Analysis (10 tools)
| Tool | Description |
|------|-------------|
| `get_bounding_box`, `get_area`, `get_volume`, `get_length` | Measurements |
| `distance`, `closest_point` | Spatial queries |
| **`intersect`** | Find intersections between objects |
| **`get_curvature`** | Curvature analysis |
| **`is_point_on_surface`**, **`is_point_in_solid`** | Point containment tests |

### Diagnostics (4 tools)
| Tool | Description |
|------|-------------|
| **`check_objects`** | Check validity, find bad geometry |
| **`get_naked_edges`** | Find gaps in surfaces |
| **`check_mesh`** | Mesh quality analysis |
| **`repair_mesh`** | Fix mesh issues automatically |

### View (5 tools)
`zoom_extents`, `set_view`, `capture_view` (with base64 option), **`list_views`**, **`set_display_mode`**

### Grasshopper (3 tools)
`gh_run_script`, `gh_set_slider`, `gh_get_outputs`

### Code Execution (2 tools)
`run_python`, `run_command`: full access to rhinoscriptsyntax and RhinoCommon

### Undo/Redo (2 tools)
`undo`, `redo`

### Scene (4 tools)
`scene_info`, `get_objects`, `get_object` (detailed with edge info), `select_objects`

### Import/Export (2 tools)
`export` (.3dm, .obj, .stl, .step, .iges, .fbx), `import_file`

**Bold** = new in v2.0

## Prerequisites

- **Rhino 7 or 8** (Windows or Mac)
- **Python 3.10+**
- **uv** package manager ([install](https://docs.astral.sh/uv/getting-started/installation/))

## Installation

### 1. Clone and install

```bash
git clone https://github.com/kbangaru-cyber/rhino-mcp.git
cd Rhino_MCP
uv sync
```

### 2. Start the Rhino listener

In Rhino:
1. Go to **Tools > PythonScript > Run**
2. Select `rhino/listener.py`
3. You should see:

```
============================================================
  RhinoMCP Listener v2.0.0
  Listening on 127.0.0.1:9876
  Registered handlers: 90
  Ready for MCP connection (Claude)
============================================================
```

### 3. Configure Claude Desktop

#### Claude Desktop

Edit `claude_desktop_config.json`:

```json
{
  "mcpServers": {
    "rhino": {
      "command": "uv",
      "args": ["--directory", "/ABSOLUTE/PATH/TO/Rhino_MCP", "run", "rhino-mcp"],
      "env": {
        "RHINO_MCP_HOST": "127.0.0.1",
        "RHINO_MCP_PORT": "9876",
        "RHINO_MCP_TIMEOUT": "30"
      }
    }
  }
}
```

## Usage Examples

### Create a simple building form

> "Create a 10x8x4 meter box for the building volume, then add a cylinder of radius 2 and height 6 at the center for a tower. Union them together and fillet all edges with radius 0.3."

### Diagnose model problems

> "Check all objects in the scene for problems. Find any naked edges and tell me what needs fixing."

### Parametric design

> "Create a vase shape by making a profile curve and revolving it. Then create a polar array of decorative cylinders around it."

## Built-in Prompts

RhinoMCP includes guided workflow prompts:

- **`model-from-description`**: structured workflow for creating 3D models from text descriptions
- **`diagnose-model`**: systematic approach to finding and fixing model problems
- **`parametric-design`**: guide for Grasshopper/parametric workflows

## Resources

The server exposes live data as MCP resources:

| URI | Description |
|-----|-------------|
| `rhino://scene/info` | Current scene overview |
| `rhino://layers` | Layer tree |
| `rhino://materials` | Material list |
| `rhino://blocks` | Block definitions |

## Configuration

| Environment Variable | Default | Description |
|---------------------|---------|-------------|
| `RHINO_MCP_HOST` | `127.0.0.1` | Rhino listener host |
| `RHINO_MCP_PORT` | `9876` | Rhino listener port |
| `RHINO_MCP_TIMEOUT` | `30` | Command timeout (seconds) |

## Development

```bash
# Install dev dependencies
uv sync --extra dev

# Run tests (no Rhino needed)
uv run pytest -v

# Lint
uv run ruff check .

# Type check
uv run mypy rhino_mcp
```

### Integration testing

With Rhino running and the listener active:

```bash
uv run pytest -v -m integration
```

## Protocol

Commands are JSON objects sent over TCP (newline-delimited):

```json
{"id": "abc123", "action": "create_sphere", "params": {"center": [0,0,0], "radius": 5}}
```

Responses:

```json
{"id": "abc123", "ok": true, "result": {"guid": "d3e4f5..."}}
```

## Thread Safety

The Rhino listener uses `Rhino.RhinoApp.InvokeOnUiThread()` on Rhino 8+ to ensure all geometry operations run on the UI thread. On Rhino 7, operations execute directly (which is safe for single-client connections).

## License

MIT. See [`LICENSE`](LICENSE).

## Author

**Karthick Raja B G**, [portfolio](https://sites.google.com/view/karthick-raja/home) · [GitHub](https://github.com/kbangaru-cyber)

Related work: [SPIDER](https://github.com/kbangaru-cyber/SPIDER), a Grasshopper plugin for
computational geometry and spatial analysis.

TDQS

C2.7/5.0

Scored across 105 tools

Disambiguation2/5

Most geometry creation/editing tools are distinguishable by their object type and operation, but the set contains direct duplicates (set_material/assign_material, duplicate_border/get_naked_edges) and overlapping checks (check_objects/check_mesh). With 105 tools, an agent must navigate many near-synonyms and the line between trim/split and copy/array is not always obvious.

Naming Consistency4/5

Tools consistently use the rhino_ prefix with snake_case verb_noun operations, making groups like create_* and get_* easy to scan. Minor inconsistencies exist (scene_info is noun-only, distance/intersect have no target, set_material vs assign_material) but they do not undermine the overall pattern.

Tool Count1/5

At 105 tools, this is an extreme mismatch for an MCP tool surface, far beyond the 25+ threshold where selection overhead becomes severe. Many narrow variants (e.g., point vs points, multiple sweep/loft surface creators) could be consolidated without losing capability.

Completeness4/5

The server covers the full Rhino modeling lifecycle: primitive creation, editing, transformations, booleans, analysis, layers, materials, blocks, Grasshopper, and file I/O. Minor gaps like layer rename/lock or block definition removal exist, but the universal run_python and run_command escape hatches prevent dead ends.

Maintenance

ActivityMaintained
ResponsivenessNo issues