rhinomcp 0.1.1.2__tar.gz → 0.1.1.4__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {rhinomcp-0.1.1.2 → rhinomcp-0.1.1.4}/PKG-INFO +1 -1
- {rhinomcp-0.1.1.2 → rhinomcp-0.1.1.4}/pyproject.toml +1 -1
- rhinomcp-0.1.1.4/src/rhinomcp/__init__.py +20 -0
- rhinomcp-0.1.1.4/src/rhinomcp/prompts/assert_creation_strategy.py +21 -0
- rhinomcp-0.1.1.4/src/rhinomcp/prompts/assert_query_strategy.py +10 -0
- {rhinomcp-0.1.1.2 → rhinomcp-0.1.1.4}/src/rhinomcp/server.py +7 -214
- rhinomcp-0.1.1.4/src/rhinomcp/tools/create_object.py +88 -0
- rhinomcp-0.1.1.4/src/rhinomcp/tools/create_objects.py +85 -0
- rhinomcp-0.1.1.4/src/rhinomcp/tools/delete_object.py +34 -0
- rhinomcp-0.1.1.4/src/rhinomcp/tools/execute_rhinoscript_python_code.py +54 -0
- rhinomcp-0.1.1.4/src/rhinomcp/tools/get_document_info.py +16 -0
- rhinomcp-0.1.1.4/src/rhinomcp/tools/get_object_info.py +24 -0
- rhinomcp-0.1.1.4/src/rhinomcp/tools/get_selected_objects_info.py +15 -0
- rhinomcp-0.1.1.4/src/rhinomcp/tools/modify_object.py +59 -0
- rhinomcp-0.1.1.4/src/rhinomcp/tools/modify_objects.py +45 -0
- {rhinomcp-0.1.1.2 → rhinomcp-0.1.1.4}/src/rhinomcp.egg-info/PKG-INFO +1 -1
- rhinomcp-0.1.1.4/src/rhinomcp.egg-info/SOURCES.txt +21 -0
- rhinomcp-0.1.1.2/src/rhinomcp/__init__.py +0 -6
- rhinomcp-0.1.1.2/src/rhinomcp.egg-info/SOURCES.txt +0 -10
- {rhinomcp-0.1.1.2 → rhinomcp-0.1.1.4}/README.md +0 -0
- {rhinomcp-0.1.1.2 → rhinomcp-0.1.1.4}/setup.cfg +0 -0
- {rhinomcp-0.1.1.2 → rhinomcp-0.1.1.4}/src/rhinomcp.egg-info/dependency_links.txt +0 -0
- {rhinomcp-0.1.1.2 → rhinomcp-0.1.1.4}/src/rhinomcp.egg-info/entry_points.txt +0 -0
- {rhinomcp-0.1.1.2 → rhinomcp-0.1.1.4}/src/rhinomcp.egg-info/requires.txt +0 -0
- {rhinomcp-0.1.1.2 → rhinomcp-0.1.1.4}/src/rhinomcp.egg-info/top_level.txt +0 -0
@@ -0,0 +1,20 @@
|
|
1
|
+
"""Rhino integration through the Model Context Protocol."""
|
2
|
+
|
3
|
+
__version__ = "0.1.0"
|
4
|
+
|
5
|
+
# Expose key classes and functions for easier imports
|
6
|
+
from .server import RhinoConnection, get_rhino_connection, mcp, logger
|
7
|
+
|
8
|
+
from .prompts.assert_creation_strategy import asset_creation_strategy
|
9
|
+
from .prompts.assert_query_strategy import assert_query_strategy
|
10
|
+
|
11
|
+
from .tools.create_object import create_object
|
12
|
+
from .tools.create_objects import create_objects
|
13
|
+
from .tools.delete_object import delete_object
|
14
|
+
from .tools.get_document_info import get_document_info
|
15
|
+
from .tools.get_object_info import get_object_info
|
16
|
+
from .tools.get_selected_objects_info import get_selected_objects_info
|
17
|
+
from .tools.modify_object import modify_object
|
18
|
+
from .tools.modify_objects import modify_objects
|
19
|
+
from .tools.execute_rhinoscript_python_code import execute_rhinoscript_python_code
|
20
|
+
|
@@ -0,0 +1,21 @@
|
|
1
|
+
from rhinomcp.server import mcp
|
2
|
+
|
3
|
+
|
4
|
+
@mcp.prompt()
|
5
|
+
def asset_creation_strategy() -> str:
|
6
|
+
"""Defines the preferred strategy for creating assets in Rhino"""
|
7
|
+
return """When creating 3D content in Rhino, always start by checking if integrations are available:
|
8
|
+
|
9
|
+
0. Before anything, always check the document from get_document_info()
|
10
|
+
1. Please ALWAYS try to create the objects using the tool execute_rhinoscript_python_code() first.
|
11
|
+
2. If there are multiple objects, use the method create_objects() to create multiple objects at once. Do not attempt to create them one by one if they are more than 10.
|
12
|
+
3. When including an object into document, ALWAYS make sure that the name of the object is meanful.
|
13
|
+
4. Try to include as many objects as possible accurately and efficiently. If the command is not able to include
|
14
|
+
so many data, try to create the objects in batches.
|
15
|
+
|
16
|
+
When creating rhinoscript python code:
|
17
|
+
- do not hallucinate, only use the syntax that is supported by rhinoscriptsyntax or Rhino,Geometry.
|
18
|
+
- document the code that you are writing.
|
19
|
+
- when creating objects, ALWAYS make sure that the name of the object is meanful.
|
20
|
+
- double check the code if any of the code is not correct, and fix it.
|
21
|
+
"""
|
@@ -0,0 +1,10 @@
|
|
1
|
+
from rhinomcp.server import mcp
|
2
|
+
|
3
|
+
|
4
|
+
@mcp.prompt()
|
5
|
+
def assert_query_strategy() -> str:
|
6
|
+
"""Defines the preferred strategy for querying Object or Objects in Rhino"""
|
7
|
+
return """When querying Object or Objects in Rhino:
|
8
|
+
- if the id of the object is known, use the id to query the object.
|
9
|
+
- if the id is not known, use the name of the object to query the object.
|
10
|
+
"""
|
@@ -12,6 +12,7 @@ from pathlib import Path
|
|
12
12
|
import base64
|
13
13
|
from urllib.parse import urlparse
|
14
14
|
|
15
|
+
|
15
16
|
# Configure logging
|
16
17
|
logging.basicConfig(level=logging.INFO,
|
17
18
|
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
|
@@ -21,7 +22,7 @@ logger = logging.getLogger("RhinoMCPServer")
|
|
21
22
|
class RhinoConnection:
|
22
23
|
host: str
|
23
24
|
port: int
|
24
|
-
sock: socket.socket = None # Changed from 'socket' to 'sock' to avoid naming conflict
|
25
|
+
sock: socket.socket | None = None # Changed from 'socket' to 'sock' to avoid naming conflict
|
25
26
|
|
26
27
|
def connect(self) -> bool:
|
27
28
|
"""Connect to the Rhino addon socket server"""
|
@@ -104,7 +105,7 @@ class RhinoConnection:
|
|
104
105
|
else:
|
105
106
|
raise Exception("No data received")
|
106
107
|
|
107
|
-
def send_command(self, command_type: str, params: Dict[str, Any] =
|
108
|
+
def send_command(self, command_type: str, params: Dict[str, Any] = {}) -> Dict[str, Any]:
|
108
109
|
"""Send a command to Rhino and return the response"""
|
109
110
|
if not self.sock and not self.connect():
|
110
111
|
raise ConnectionError("Not connected to Rhino")
|
@@ -117,6 +118,9 @@ class RhinoConnection:
|
|
117
118
|
try:
|
118
119
|
# Log the command being sent
|
119
120
|
logger.info(f"Sending command: {command_type} with params: {params}")
|
121
|
+
|
122
|
+
if self.sock is None:
|
123
|
+
raise Exception("Socket is not connected")
|
120
124
|
|
121
125
|
# Send the command
|
122
126
|
self.sock.sendall(json.dumps(command).encode('utf-8'))
|
@@ -150,7 +154,7 @@ class RhinoConnection:
|
|
150
154
|
except json.JSONDecodeError as e:
|
151
155
|
logger.error(f"Invalid JSON response from Rhino: {str(e)}")
|
152
156
|
# Try to log what was received
|
153
|
-
if 'response_data' in locals() and response_data:
|
157
|
+
if 'response_data' in locals() and response_data: # type: ignore
|
154
158
|
logger.error(f"Raw response (first 200 bytes): {response_data[:200]}")
|
155
159
|
raise Exception(f"Invalid response from Rhino: {str(e)}")
|
156
160
|
except Exception as e:
|
@@ -216,218 +220,7 @@ def get_rhino_connection():
|
|
216
220
|
|
217
221
|
return _rhino_connection
|
218
222
|
|
219
|
-
|
220
|
-
@mcp.tool()
|
221
|
-
def get_document_info(ctx: Context) -> str:
|
222
|
-
"""Get detailed information about the current Rhino document"""
|
223
|
-
try:
|
224
|
-
rhino = get_rhino_connection()
|
225
|
-
result = rhino.send_command("get_document_info")
|
226
|
-
|
227
|
-
# Just return the JSON representation of what Rhino sent us
|
228
|
-
return json.dumps(result, indent=2)
|
229
|
-
except Exception as e:
|
230
|
-
logger.error(f"Error getting document info from Rhino: {str(e)}")
|
231
|
-
return f"Error getting document info: {str(e)}"
|
232
|
-
|
233
|
-
@mcp.tool()
|
234
|
-
def get_object_info(ctx: Context, id: str = None, name: str = None) -> str:
|
235
|
-
"""
|
236
|
-
Get detailed information about a specific object in the Rhino document.
|
237
|
-
You can either provide the id or the object_name of the object to get information about.
|
238
|
-
If both are provided, the id will be used.
|
239
|
-
|
240
|
-
Parameters:
|
241
|
-
- id: The id of the object to get information about
|
242
|
-
- name: The name of the object to get information about
|
243
|
-
"""
|
244
|
-
try:
|
245
|
-
rhino = get_rhino_connection()
|
246
|
-
result = rhino.send_command("get_object_info", {"id": id, "name": name})
|
247
|
-
|
248
|
-
# Just return the JSON representation of what Rhino sent us
|
249
|
-
return json.dumps(result, indent=2)
|
250
|
-
except Exception as e:
|
251
|
-
logger.error(f"Error getting object info from Rhino: {str(e)}")
|
252
|
-
return f"Error getting object info: {str(e)}"
|
253
|
-
|
254
|
-
@mcp.tool()
|
255
|
-
def get_selected_objects_info(ctx: Context) -> str:
|
256
|
-
"""Get detailed information about the currently selected objects in Rhino"""
|
257
|
-
try:
|
258
|
-
rhino = get_rhino_connection()
|
259
|
-
result = rhino.send_command("get_selected_objects_info")
|
260
|
-
return json.dumps(result, indent=2)
|
261
|
-
except Exception as e:
|
262
|
-
logger.error(f"Error getting selected objects from Rhino: {str(e)}")
|
263
|
-
return f"Error getting selected objects: {str(e)}"
|
264
|
-
|
265
|
-
|
266
|
-
@mcp.tool()
|
267
|
-
def create_object(
|
268
|
-
ctx: Context,
|
269
|
-
type: str = "CUBE",
|
270
|
-
name: str = None,
|
271
|
-
location: List[float] = None,
|
272
|
-
rotation: List[float] = None,
|
273
|
-
scale: List[float] = None,
|
274
|
-
# Torus-specific parameters
|
275
|
-
align: str = "WORLD",
|
276
|
-
major_segments: int = 48,
|
277
|
-
minor_segments: int = 12,
|
278
|
-
mode: str = "MAJOR_MINOR",
|
279
|
-
major_radius: float = 1.0,
|
280
|
-
minor_radius: float = 0.25,
|
281
|
-
abso_major_rad: float = 1.25,
|
282
|
-
abso_minor_rad: float = 0.75,
|
283
|
-
generate_uvs: bool = True
|
284
|
-
) -> str:
|
285
|
-
"""
|
286
|
-
Create a new object in the Rhino document.
|
287
|
-
|
288
|
-
Parameters:
|
289
|
-
- type: Object type (CUBE, SPHERE, CYLINDER, PLANE, CONE, TORUS, EMPTY, CAMERA, LIGHT)
|
290
|
-
- name: Optional name for the object
|
291
|
-
- location: Optional [x, y, z] location coordinates
|
292
|
-
- rotation: Optional [x, y, z] rotation in radians
|
293
|
-
- scale: Optional [x, y, z] scale factors (not used for TORUS)
|
294
|
-
|
295
|
-
Torus-specific parameters (only used when type == "TORUS"):
|
296
|
-
- align: How to align the torus ('WORLD', 'VIEW', or 'CURSOR')
|
297
|
-
- major_segments: Number of segments for the main ring
|
298
|
-
- minor_segments: Number of segments for the cross-section
|
299
|
-
- mode: Dimension mode ('MAJOR_MINOR' or 'EXT_INT')
|
300
|
-
- major_radius: Radius from the origin to the center of the cross sections
|
301
|
-
- minor_radius: Radius of the torus' cross section
|
302
|
-
- abso_major_rad: Total exterior radius of the torus
|
303
|
-
- abso_minor_rad: Total interior radius of the torus
|
304
|
-
- generate_uvs: Whether to generate a default UV map
|
305
|
-
|
306
|
-
Returns:
|
307
|
-
A message indicating the created object name.
|
308
|
-
"""
|
309
|
-
try:
|
310
|
-
# Get the global connection
|
311
|
-
rhino = get_rhino_connection()
|
312
|
-
|
313
|
-
# Set default values for missing parameters
|
314
|
-
loc = location or [0, 0, 0]
|
315
|
-
rot = rotation or [0, 0, 0]
|
316
|
-
sc = scale or [1, 1, 1]
|
317
|
-
|
318
|
-
params = {
|
319
|
-
"type": type,
|
320
|
-
"location": loc,
|
321
|
-
"rotation": rot,
|
322
|
-
}
|
323
|
-
|
324
|
-
if name:
|
325
|
-
params["name"] = name
|
326
|
-
|
327
|
-
if type == "TORUS":
|
328
|
-
# For torus, the scale is not used.
|
329
|
-
params.update({
|
330
|
-
"align": align,
|
331
|
-
"major_segments": major_segments,
|
332
|
-
"minor_segments": minor_segments,
|
333
|
-
"mode": mode,
|
334
|
-
"major_radius": major_radius,
|
335
|
-
"minor_radius": minor_radius,
|
336
|
-
"abso_major_rad": abso_major_rad,
|
337
|
-
"abso_minor_rad": abso_minor_rad,
|
338
|
-
"generate_uvs": generate_uvs
|
339
|
-
})
|
340
|
-
result = rhino.send_command("create_object", params)
|
341
|
-
return f"Created {type} object: {result['name']}"
|
342
|
-
else:
|
343
|
-
# For non-torus objects, include scale
|
344
|
-
params["scale"] = sc
|
345
|
-
result = rhino.send_command("create_object", params)
|
346
|
-
return f"Created {type} object: {result['name']}"
|
347
|
-
except Exception as e:
|
348
|
-
logger.error(f"Error creating object: {str(e)}")
|
349
|
-
return f"Error creating object: {str(e)}"
|
350
|
-
|
351
|
-
@mcp.tool()
|
352
|
-
def modify_object(
|
353
|
-
ctx: Context,
|
354
|
-
id: str = None,
|
355
|
-
name: str = None,
|
356
|
-
new_name: str = None,
|
357
|
-
location: List[float] = None,
|
358
|
-
rotation: List[float] = None,
|
359
|
-
scale: List[float] = None,
|
360
|
-
visible: bool = None
|
361
|
-
) -> str:
|
362
|
-
"""
|
363
|
-
Modify an existing object in the Rhino document.
|
364
|
-
|
365
|
-
Parameters:
|
366
|
-
- id: The id of the object to modify
|
367
|
-
- name: The name of the object to modify
|
368
|
-
- new_name: Optional new name for the object
|
369
|
-
- location: Optional [x, y, z] location coordinates
|
370
|
-
- rotation: Optional [x, y, z] rotation in radians
|
371
|
-
- scale: Optional [x, y, z] scale factors
|
372
|
-
- visible: Optional boolean to set visibility
|
373
|
-
"""
|
374
|
-
try:
|
375
|
-
# Get the global connection
|
376
|
-
rhino = get_rhino_connection()
|
377
|
-
|
378
|
-
params = {"id": id, "name": name}
|
379
|
-
|
380
|
-
if new_name is not None:
|
381
|
-
params["new_name"] = new_name
|
382
|
-
if location is not None:
|
383
|
-
params["location"] = location
|
384
|
-
if rotation is not None:
|
385
|
-
params["rotation"] = rotation
|
386
|
-
if scale is not None:
|
387
|
-
params["scale"] = scale
|
388
|
-
if visible is not None:
|
389
|
-
params["visible"] = visible
|
390
|
-
|
391
|
-
result = rhino.send_command("modify_object", params)
|
392
|
-
return f"Modified object: {result['name']}"
|
393
|
-
except Exception as e:
|
394
|
-
logger.error(f"Error modifying object: {str(e)}")
|
395
|
-
return f"Error modifying object: {str(e)}"
|
396
|
-
|
397
|
-
@mcp.tool()
|
398
|
-
def delete_object(ctx: Context, id: str = None, name: str = None) -> str:
|
399
|
-
"""
|
400
|
-
Delete an object from the Rhino document.
|
401
|
-
|
402
|
-
Parameters:
|
403
|
-
- id: The id of the object to delete
|
404
|
-
- name: The name of the object to delete
|
405
|
-
"""
|
406
|
-
try:
|
407
|
-
# Get the global connection
|
408
|
-
rhino = get_rhino_connection()
|
409
|
-
|
410
|
-
result = rhino.send_command("delete_object", {"id": id, "name": name})
|
411
|
-
return f"Deleted object: {result['name']}"
|
412
|
-
except Exception as e:
|
413
|
-
logger.error(f"Error deleting object: {str(e)}")
|
414
|
-
return f"Error deleting object: {str(e)}"
|
415
|
-
|
416
|
-
@mcp.prompt()
|
417
|
-
def asset_creation_strategy() -> str:
|
418
|
-
"""Defines the preferred strategy for creating assets in Rhino"""
|
419
|
-
return """When creating 3D content in Rhino, always start by checking if integrations are available:
|
420
|
-
|
421
|
-
0. Before anything, always check the document from get_document_info()
|
422
|
-
1. Use the method create_object() for basic primitives (CUBE, SPHERE, etc.)
|
423
|
-
2. When including an object into document, ALWAYS make sure that the name of the object is meanful.
|
424
|
-
3. After giving the tool location/scale/rotation information (via create_object() and modify_object()),
|
425
|
-
double check the related object's location, scale, rotation, and world_bounding_box using get_object_info(),
|
426
|
-
so that the object is in the desired location.
|
427
|
-
"""
|
428
|
-
|
429
223
|
# Main execution
|
430
|
-
|
431
224
|
def main():
|
432
225
|
"""Run the MCP server"""
|
433
226
|
mcp.run()
|
@@ -0,0 +1,88 @@
|
|
1
|
+
from mcp.server.fastmcp import Context
|
2
|
+
import json
|
3
|
+
from rhinomcp.server import get_rhino_connection, mcp, logger
|
4
|
+
from typing import Any, List, Dict
|
5
|
+
|
6
|
+
@mcp.tool()
|
7
|
+
def create_object(
|
8
|
+
ctx: Context,
|
9
|
+
type: str = "BOX",
|
10
|
+
name: str = None,
|
11
|
+
color: List[int]= None,
|
12
|
+
params: Dict[str, Any] = {},
|
13
|
+
translation: List[float]= None,
|
14
|
+
rotation: List[float]= None,
|
15
|
+
scale: List[float]= None,
|
16
|
+
) -> str:
|
17
|
+
"""
|
18
|
+
Create a new object in the Rhino document.
|
19
|
+
|
20
|
+
Parameters:
|
21
|
+
- type: Object type ("POINT", "LINE", "POLYLINE", "CURVE", "BOX", "SPHERE")
|
22
|
+
- name: Optional name for the object
|
23
|
+
- color: Optional [r, g, b] color values (0-255) for the object
|
24
|
+
- params: Type-specific parameters dictionary (see documentation for each type)
|
25
|
+
- translation: Optional [x, y, z] translation vector
|
26
|
+
- rotation: Optional [x, y, z] rotation in radians
|
27
|
+
- scale: Optional [x, y, z] scale factors
|
28
|
+
|
29
|
+
The params dictionary is type-specific.
|
30
|
+
For POINT, the params dictionary should be empty.
|
31
|
+
|
32
|
+
For LINE, the params dictionary should contain the following keys:
|
33
|
+
- start: [x, y, z] start point of the line
|
34
|
+
- end: [x, y, z] end point of the line
|
35
|
+
|
36
|
+
For POLYLINE, the params dictionary should contain the following keys:
|
37
|
+
- points: List of [x, y, z] points that define the polyline
|
38
|
+
|
39
|
+
For CURVE, the params dictionary should contain the following keys:
|
40
|
+
- points: List of [x, y, z] control points that define the curve
|
41
|
+
- degree: Degree of the curve (default is 3, if user asked for smoother curve, degree can be higher)
|
42
|
+
|
43
|
+
For BOX, the params dictionary should contain the following keys:
|
44
|
+
- width: Width of the box along X axis of the object
|
45
|
+
- length: Length of the box along Y axis of the object
|
46
|
+
- height: Height of the box along Z axis of the object
|
47
|
+
|
48
|
+
For SPHERE, the params dictionary should contain the following key:
|
49
|
+
- radius: Radius of the sphere
|
50
|
+
|
51
|
+
Returns:
|
52
|
+
A message indicating the created object name.
|
53
|
+
|
54
|
+
Examples of params:
|
55
|
+
- POINT: {} (no additional parameters because the point location is defined by the translation vector)
|
56
|
+
- LINE: {"start": [0, 0, 0], "end": [1, 1, 1]}
|
57
|
+
- POLYLINE: {"points": [[0, 0, 0], [1, 1, 1], [2, 2, 2]]}
|
58
|
+
- CURVE: {"points": [[0, 0, 0], [1, 1, 1], [2, 2, 2]], "degree": 3}
|
59
|
+
- BOX: {"width": 1.0, "length": 1.0, "height": 1.0}
|
60
|
+
- SPHERE: {"radius": 1.0}
|
61
|
+
"""
|
62
|
+
try:
|
63
|
+
# Get the global connection
|
64
|
+
rhino = get_rhino_connection()
|
65
|
+
# Set default values for missing parameters
|
66
|
+
trans = translation or [0, 0, 0]
|
67
|
+
rot = rotation or [0, 0, 0]
|
68
|
+
sc = scale or [1, 1, 1]
|
69
|
+
|
70
|
+
command_params = {
|
71
|
+
"type": type,
|
72
|
+
"translation": trans,
|
73
|
+
"rotation": rot,
|
74
|
+
"scale": sc,
|
75
|
+
"params": params
|
76
|
+
}
|
77
|
+
|
78
|
+
if name: command_params["name"] = name
|
79
|
+
if color: command_params["color"] = color
|
80
|
+
|
81
|
+
# Create the object
|
82
|
+
result = result = rhino.send_command("create_object", command_params)
|
83
|
+
|
84
|
+
return f"Created {type} object: {result['name']}"
|
85
|
+
except Exception as e:
|
86
|
+
logger.error(f"Error creating object: {str(e)}")
|
87
|
+
return f"Error creating object: {str(e)}"
|
88
|
+
|
@@ -0,0 +1,85 @@
|
|
1
|
+
from mcp.server.fastmcp import Context
|
2
|
+
import json
|
3
|
+
from rhinomcp.server import get_rhino_connection, mcp, logger
|
4
|
+
from typing import Any, List, Dict
|
5
|
+
|
6
|
+
|
7
|
+
@mcp.tool()
|
8
|
+
def create_objects(
|
9
|
+
ctx: Context,
|
10
|
+
objects: List[Dict[str, Any]]
|
11
|
+
) -> str:
|
12
|
+
"""
|
13
|
+
Create multiple objects at once in the Rhino document.
|
14
|
+
|
15
|
+
Parameters:
|
16
|
+
- objects: A list of dictionaries, each containing the parameters for a single object
|
17
|
+
|
18
|
+
Each object should have the following values:
|
19
|
+
- type: Object type ("POINT", "LINE", "POLYLINE", "BOX", "SPHERE", etc.)
|
20
|
+
- name: Optional name for the object
|
21
|
+
- color: Optional [r, g, b] color values (0-255) for the object
|
22
|
+
- params: Type-specific parameters dictionary (see documentation for each type in create_object() function)
|
23
|
+
- translation: Optional [x, y, z] translation vector
|
24
|
+
- rotation: Optional [x, y, z] rotation in radians
|
25
|
+
- scale: Optional [x, y, z] scale factors
|
26
|
+
|
27
|
+
Returns:
|
28
|
+
A message indicating the created objects.
|
29
|
+
|
30
|
+
Examples of params:
|
31
|
+
[
|
32
|
+
{
|
33
|
+
"type": "POINT",
|
34
|
+
"name": "Point 1",
|
35
|
+
"translation": [0, 0, 0]
|
36
|
+
},
|
37
|
+
{
|
38
|
+
"type": "LINE",
|
39
|
+
"name": "Line 1",
|
40
|
+
"params": {"start": [0, 0, 0], "end": [1, 1, 1]}
|
41
|
+
},
|
42
|
+
{
|
43
|
+
"type": "POLYLINE",
|
44
|
+
"name": "Polyline 1",
|
45
|
+
"params": {"points": [[0, 0, 0], [1, 1, 1], [2, 2, 2]]}
|
46
|
+
},
|
47
|
+
{
|
48
|
+
"type": "CURVE",
|
49
|
+
"name": "Curve 1",
|
50
|
+
"params": {"points": [[0, 0, 0], [1, 1, 1], [2, 2, 2]], "degree": 3}
|
51
|
+
},
|
52
|
+
{
|
53
|
+
"type": "BOX",
|
54
|
+
"name": "Box 1",
|
55
|
+
"color": [255, 0, 0],
|
56
|
+
"params": {"width": 1.0, "length": 1.0, "height": 1.0},
|
57
|
+
"translation": [0, 0, 0],
|
58
|
+
"rotation": [0, 0, 0],
|
59
|
+
"scale": [1, 1, 1]
|
60
|
+
},
|
61
|
+
{
|
62
|
+
"type": "SPHERE",
|
63
|
+
"name": "Sphere 1",
|
64
|
+
"color": [0, 255, 0],
|
65
|
+
"params": {"radius": 1.0},
|
66
|
+
"translation": [0, 0, 0],
|
67
|
+
"rotation": [0, 0, 0],
|
68
|
+
"scale": [1, 1, 1]
|
69
|
+
}
|
70
|
+
]
|
71
|
+
"""
|
72
|
+
try:
|
73
|
+
# Get the global connection
|
74
|
+
rhino = get_rhino_connection()
|
75
|
+
command_params = {}
|
76
|
+
for obj in objects:
|
77
|
+
command_params[obj["name"]] = obj
|
78
|
+
result = rhino.send_command("create_objects", command_params)
|
79
|
+
|
80
|
+
|
81
|
+
return f"Created {len(result)} objects"
|
82
|
+
except Exception as e:
|
83
|
+
logger.error(f"Error creating object: {str(e)}")
|
84
|
+
return f"Error creating object: {str(e)}"
|
85
|
+
|
@@ -0,0 +1,34 @@
|
|
1
|
+
from mcp.server.fastmcp import Context
|
2
|
+
import json
|
3
|
+
from rhinomcp.server import get_rhino_connection, mcp, logger
|
4
|
+
from typing import Any, List, Dict
|
5
|
+
|
6
|
+
|
7
|
+
|
8
|
+
@mcp.tool()
|
9
|
+
def delete_object(ctx: Context, id: str = None, name: str = None, all: bool = None) -> str:
|
10
|
+
"""
|
11
|
+
Delete an object from the Rhino document.
|
12
|
+
|
13
|
+
Parameters:
|
14
|
+
- id: The id of the object to delete
|
15
|
+
- name: The name of the object to delete
|
16
|
+
"""
|
17
|
+
try:
|
18
|
+
# Get the global connection
|
19
|
+
rhino = get_rhino_connection()
|
20
|
+
|
21
|
+
commandParams = {}
|
22
|
+
if id is not None:
|
23
|
+
commandParams["id"] = id
|
24
|
+
if name is not None:
|
25
|
+
commandParams["name"] = name
|
26
|
+
if all:
|
27
|
+
commandParams["all"] = all
|
28
|
+
|
29
|
+
result = rhino.send_command("delete_object", commandParams)
|
30
|
+
|
31
|
+
return f"Deleted object: {result['name']}"
|
32
|
+
except Exception as e:
|
33
|
+
logger.error(f"Error deleting object: {str(e)}")
|
34
|
+
return f"Error deleting object: {str(e)}"
|
@@ -0,0 +1,54 @@
|
|
1
|
+
from mcp.server.fastmcp import Context
|
2
|
+
import json
|
3
|
+
from rhinomcp.server import get_rhino_connection, mcp, logger
|
4
|
+
from typing import Any, List, Dict
|
5
|
+
|
6
|
+
|
7
|
+
@mcp.tool()
|
8
|
+
def execute_rhinoscript_python_code(ctx: Context, code: str) -> str:
|
9
|
+
"""
|
10
|
+
Execute arbitrary RhinoScript code in Rhino.
|
11
|
+
|
12
|
+
Parameters:
|
13
|
+
- code: The RhinoScript code to execute
|
14
|
+
|
15
|
+
References:
|
16
|
+
|
17
|
+
AddBox(corners)
|
18
|
+
Adds a box shaped polysurface to the document
|
19
|
+
Parameters:
|
20
|
+
corners ([point, point, point ,point, point, point ,point,point]) 8 points that define the corners of the box. Points need to
|
21
|
+
be in counter-clockwise order starting with the bottom rectangle of the box
|
22
|
+
Returns:
|
23
|
+
guid: identifier of the new object on success
|
24
|
+
Example:
|
25
|
+
import rhinoscriptsyntax as rs
|
26
|
+
box = rs.GetBox()
|
27
|
+
if box: rs.AddBox(box)
|
28
|
+
|
29
|
+
AddSphere(center_or_plane, radius)
|
30
|
+
Add a spherical surface to the document
|
31
|
+
Parameters:
|
32
|
+
center_or_plane (point|plane): center point of the sphere. If a plane is input,
|
33
|
+
the origin of the plane will be the center of the sphere
|
34
|
+
radius (number): radius of the sphere in the current model units
|
35
|
+
Returns:
|
36
|
+
guid: identifier of the new object on success
|
37
|
+
None: on error
|
38
|
+
Example:
|
39
|
+
import rhinoscriptsyntax as rs
|
40
|
+
radius = 2
|
41
|
+
center = rs.GetPoint("Center of sphere")
|
42
|
+
if center: rs.AddSphere(center, radius)
|
43
|
+
|
44
|
+
|
45
|
+
"""
|
46
|
+
try:
|
47
|
+
# Get the global connection
|
48
|
+
rhino = get_rhino_connection()
|
49
|
+
|
50
|
+
result = rhino.send_command("execute_rhinoscript_python_code", {"code": code})
|
51
|
+
return f"Code executed successfully: {result.get('result', '')}"
|
52
|
+
except Exception as e:
|
53
|
+
logger.error(f"Error executing code: {str(e)}")
|
54
|
+
return f"Error executing code: {str(e)}"
|
@@ -0,0 +1,16 @@
|
|
1
|
+
from mcp.server.fastmcp import Context
|
2
|
+
import json
|
3
|
+
from rhinomcp import get_rhino_connection, mcp, logger
|
4
|
+
|
5
|
+
@mcp.tool()
|
6
|
+
def get_document_info(ctx: Context) -> str:
|
7
|
+
"""Get detailed information about the current Rhino document"""
|
8
|
+
try:
|
9
|
+
rhino = get_rhino_connection()
|
10
|
+
result = rhino.send_command("get_document_info")
|
11
|
+
|
12
|
+
# Just return the JSON representation of what Rhino sent us
|
13
|
+
return json.dumps(result, indent=2)
|
14
|
+
except Exception as e:
|
15
|
+
logger.error(f"Error getting document info from Rhino: {str(e)}")
|
16
|
+
return f"Error getting document info: {str(e)}"
|
@@ -0,0 +1,24 @@
|
|
1
|
+
from mcp.server.fastmcp import Context
|
2
|
+
import json
|
3
|
+
from rhinomcp import get_rhino_connection, mcp, logger
|
4
|
+
|
5
|
+
@mcp.tool()
|
6
|
+
def get_object_info(ctx: Context, id: str = None, name: str = None) -> str:
|
7
|
+
"""
|
8
|
+
Get detailed information about a specific object in the Rhino document.
|
9
|
+
You can either provide the id or the object_name of the object to get information about.
|
10
|
+
If both are provided, the id will be used.
|
11
|
+
|
12
|
+
Parameters:
|
13
|
+
- id: The id of the object to get information about
|
14
|
+
- name: The name of the object to get information about
|
15
|
+
"""
|
16
|
+
try:
|
17
|
+
rhino = get_rhino_connection()
|
18
|
+
result = rhino.send_command("get_object_info", {"id": id, "name": name})
|
19
|
+
|
20
|
+
# Just return the JSON representation of what Rhino sent us
|
21
|
+
return json.dumps(result, indent=2)
|
22
|
+
except Exception as e:
|
23
|
+
logger.error(f"Error getting object info from Rhino: {str(e)}")
|
24
|
+
return f"Error getting object info: {str(e)}"
|
@@ -0,0 +1,15 @@
|
|
1
|
+
from mcp.server.fastmcp import Context
|
2
|
+
import json
|
3
|
+
from rhinomcp import get_rhino_connection, mcp, logger
|
4
|
+
|
5
|
+
@mcp.tool()
|
6
|
+
def get_selected_objects_info(ctx: Context) -> str:
|
7
|
+
"""Get detailed information about the currently selected objects in Rhino"""
|
8
|
+
try:
|
9
|
+
rhino = get_rhino_connection()
|
10
|
+
result = rhino.send_command("get_selected_objects_info")
|
11
|
+
return json.dumps(result, indent=2)
|
12
|
+
except Exception as e:
|
13
|
+
logger.error(f"Error getting selected objects from Rhino: {str(e)}")
|
14
|
+
return f"Error getting selected objects: {str(e)}"
|
15
|
+
|
@@ -0,0 +1,59 @@
|
|
1
|
+
from mcp.server.fastmcp import Context
|
2
|
+
import json
|
3
|
+
from rhinomcp.server import get_rhino_connection, mcp, logger
|
4
|
+
from typing import Any, List, Dict
|
5
|
+
|
6
|
+
|
7
|
+
@mcp.tool()
|
8
|
+
def modify_object(
|
9
|
+
ctx: Context,
|
10
|
+
id: str = None,
|
11
|
+
name: str = None,
|
12
|
+
new_name: str = None,
|
13
|
+
new_color: List[int] = None,
|
14
|
+
translation: List[float] = None,
|
15
|
+
rotation: List[float] = None,
|
16
|
+
scale: List[float] = None,
|
17
|
+
visible: bool = None
|
18
|
+
) -> str:
|
19
|
+
"""
|
20
|
+
Modify an existing object in the Rhino document.
|
21
|
+
|
22
|
+
Parameters:
|
23
|
+
- id: The id of the object to modify
|
24
|
+
- name: The name of the object to modify
|
25
|
+
- new_name: Optional new name for the object
|
26
|
+
- new_color: Optional [r, g, b] color values (0-255) for the object
|
27
|
+
- translation: Optional [x, y, z] translation vector
|
28
|
+
- rotation: Optional [x, y, z] rotation in radians
|
29
|
+
- scale: Optional [x, y, z] scale factors
|
30
|
+
- visible: Optional boolean to set visibility
|
31
|
+
"""
|
32
|
+
try:
|
33
|
+
# Get the global connection
|
34
|
+
rhino = get_rhino_connection()
|
35
|
+
|
36
|
+
params : Dict[str, Any] = {}
|
37
|
+
|
38
|
+
if id is not None:
|
39
|
+
params["id"] = id
|
40
|
+
if name is not None:
|
41
|
+
params["name"] = name
|
42
|
+
if new_name is not None:
|
43
|
+
params["new_name"] = new_name
|
44
|
+
if new_color is not None:
|
45
|
+
params["new_color"] = new_color
|
46
|
+
if translation is not None:
|
47
|
+
params["translation"] = translation
|
48
|
+
if rotation is not None:
|
49
|
+
params["rotation"] = rotation
|
50
|
+
if scale is not None:
|
51
|
+
params["scale"] = scale
|
52
|
+
if visible is not None:
|
53
|
+
params["visible"] = visible
|
54
|
+
|
55
|
+
result = rhino.send_command("modify_object", params)
|
56
|
+
return f"Modified object: {result['name']}"
|
57
|
+
except Exception as e:
|
58
|
+
logger.error(f"Error modifying object: {str(e)}")
|
59
|
+
return f"Error modifying object: {str(e)}"
|
@@ -0,0 +1,45 @@
|
|
1
|
+
from mcp.server.fastmcp import Context
|
2
|
+
import json
|
3
|
+
from rhinomcp.server import get_rhino_connection, mcp, logger
|
4
|
+
from typing import Any, List, Dict
|
5
|
+
|
6
|
+
|
7
|
+
@mcp.tool()
|
8
|
+
def modify_objects(
|
9
|
+
ctx: Context,
|
10
|
+
objects: List[Dict[str, Any]],
|
11
|
+
all: bool = None
|
12
|
+
) -> str:
|
13
|
+
"""
|
14
|
+
Create multiple objects at once in the Rhino document.
|
15
|
+
|
16
|
+
Parameters:
|
17
|
+
- objects: A List of objects, each containing the parameters for a single object modification
|
18
|
+
- all: Optional boolean to modify all objects, if true, only one object is required in the objects dictionary
|
19
|
+
|
20
|
+
Each object can have the following parameters:
|
21
|
+
- id: The id of the object to modify
|
22
|
+
- new_color: Optional [r, g, b] color values (0-255) for the object
|
23
|
+
- translation: Optional [x, y, z] translation vector
|
24
|
+
- rotation: Optional [x, y, z] rotation in radians
|
25
|
+
- scale: Optional [x, y, z] scale factors
|
26
|
+
- visible: Optional boolean to set visibility
|
27
|
+
|
28
|
+
Returns:
|
29
|
+
A message indicating the modified objects.
|
30
|
+
"""
|
31
|
+
try:
|
32
|
+
# Get the global connection
|
33
|
+
rhino = get_rhino_connection()
|
34
|
+
command_params = {}
|
35
|
+
command_params["objects"] = objects
|
36
|
+
if all:
|
37
|
+
command_params["all"] = all
|
38
|
+
result = rhino.send_command("modify_objects", command_params)
|
39
|
+
|
40
|
+
|
41
|
+
return f"Modified {result['modified']} objects"
|
42
|
+
except Exception as e:
|
43
|
+
logger.error(f"Error modifying objects: {str(e)}")
|
44
|
+
return f"Error modifying objects: {str(e)}"
|
45
|
+
|
@@ -0,0 +1,21 @@
|
|
1
|
+
README.md
|
2
|
+
pyproject.toml
|
3
|
+
src/rhinomcp/__init__.py
|
4
|
+
src/rhinomcp/server.py
|
5
|
+
src/rhinomcp.egg-info/PKG-INFO
|
6
|
+
src/rhinomcp.egg-info/SOURCES.txt
|
7
|
+
src/rhinomcp.egg-info/dependency_links.txt
|
8
|
+
src/rhinomcp.egg-info/entry_points.txt
|
9
|
+
src/rhinomcp.egg-info/requires.txt
|
10
|
+
src/rhinomcp.egg-info/top_level.txt
|
11
|
+
src/rhinomcp/prompts/assert_creation_strategy.py
|
12
|
+
src/rhinomcp/prompts/assert_query_strategy.py
|
13
|
+
src/rhinomcp/tools/create_object.py
|
14
|
+
src/rhinomcp/tools/create_objects.py
|
15
|
+
src/rhinomcp/tools/delete_object.py
|
16
|
+
src/rhinomcp/tools/execute_rhinoscript_python_code.py
|
17
|
+
src/rhinomcp/tools/get_document_info.py
|
18
|
+
src/rhinomcp/tools/get_object_info.py
|
19
|
+
src/rhinomcp/tools/get_selected_objects_info.py
|
20
|
+
src/rhinomcp/tools/modify_object.py
|
21
|
+
src/rhinomcp/tools/modify_objects.py
|
@@ -1,10 +0,0 @@
|
|
1
|
-
README.md
|
2
|
-
pyproject.toml
|
3
|
-
src/rhinomcp/__init__.py
|
4
|
-
src/rhinomcp/server.py
|
5
|
-
src/rhinomcp.egg-info/PKG-INFO
|
6
|
-
src/rhinomcp.egg-info/SOURCES.txt
|
7
|
-
src/rhinomcp.egg-info/dependency_links.txt
|
8
|
-
src/rhinomcp.egg-info/entry_points.txt
|
9
|
-
src/rhinomcp.egg-info/requires.txt
|
10
|
-
src/rhinomcp.egg-info/top_level.txt
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|