rhinomcp 0.1.1.1__tar.gz → 0.1.1.3__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.
Files changed (24) hide show
  1. {rhinomcp-0.1.1.1 → rhinomcp-0.1.1.3}/PKG-INFO +1 -1
  2. {rhinomcp-0.1.1.1 → rhinomcp-0.1.1.3}/pyproject.toml +1 -1
  3. rhinomcp-0.1.1.3/src/rhinomcp/__init__.py +19 -0
  4. rhinomcp-0.1.1.3/src/rhinomcp/prompts/assert_creation_strategy.py +21 -0
  5. rhinomcp-0.1.1.3/src/rhinomcp/prompts/assert_query_strategy.py +10 -0
  6. {rhinomcp-0.1.1.1 → rhinomcp-0.1.1.3}/src/rhinomcp/server.py +8 -213
  7. rhinomcp-0.1.1.3/src/rhinomcp/tools/create_object.py +77 -0
  8. rhinomcp-0.1.1.3/src/rhinomcp/tools/create_objects.py +56 -0
  9. rhinomcp-0.1.1.3/src/rhinomcp/tools/delete_object.py +25 -0
  10. rhinomcp-0.1.1.3/src/rhinomcp/tools/execute_rhinoscript_python_code.py +54 -0
  11. rhinomcp-0.1.1.3/src/rhinomcp/tools/get_document_info.py +16 -0
  12. rhinomcp-0.1.1.3/src/rhinomcp/tools/get_object_info.py +24 -0
  13. rhinomcp-0.1.1.3/src/rhinomcp/tools/get_selected_objects_info.py +15 -0
  14. rhinomcp-0.1.1.3/src/rhinomcp/tools/modify_object.py +59 -0
  15. {rhinomcp-0.1.1.1 → rhinomcp-0.1.1.3}/src/rhinomcp.egg-info/PKG-INFO +1 -1
  16. rhinomcp-0.1.1.3/src/rhinomcp.egg-info/SOURCES.txt +20 -0
  17. rhinomcp-0.1.1.1/src/rhinomcp/__init__.py +0 -6
  18. rhinomcp-0.1.1.1/src/rhinomcp.egg-info/SOURCES.txt +0 -10
  19. {rhinomcp-0.1.1.1 → rhinomcp-0.1.1.3}/README.md +0 -0
  20. {rhinomcp-0.1.1.1 → rhinomcp-0.1.1.3}/setup.cfg +0 -0
  21. {rhinomcp-0.1.1.1 → rhinomcp-0.1.1.3}/src/rhinomcp.egg-info/dependency_links.txt +0 -0
  22. {rhinomcp-0.1.1.1 → rhinomcp-0.1.1.3}/src/rhinomcp.egg-info/entry_points.txt +0 -0
  23. {rhinomcp-0.1.1.1 → rhinomcp-0.1.1.3}/src/rhinomcp.egg-info/requires.txt +0 -0
  24. {rhinomcp-0.1.1.1 → rhinomcp-0.1.1.3}/src/rhinomcp.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: rhinomcp
3
- Version: 0.1.1.1
3
+ Version: 0.1.1.3
4
4
  Summary: Rhino integration through the Model Context Protocol
5
5
  Author-email: Jingcheng Chen <mail@jchen.ch>
6
6
  License: MIT
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "rhinomcp"
3
- version = "0.1.1.1"
3
+ version = "0.1.1.3"
4
4
  description = "Rhino integration through the Model Context Protocol"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.10"
@@ -0,0 +1,19 @@
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.execute_rhinoscript_python_code import execute_rhinoscript_python_code
19
+
@@ -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] = None) -> 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:
@@ -200,28 +204,10 @@ mcp = FastMCP(
200
204
 
201
205
  # Global connection for resources (since resources can't access context)
202
206
  _rhino_connection = None
203
- _polyhaven_enabled = False # Add this global variable
204
207
 
205
208
  def get_rhino_connection():
206
209
  """Get or create a persistent Rhino connection"""
207
- global _rhino_connection, _polyhaven_enabled # Add _polyhaven_enabled to globals
208
-
209
- # If we have an existing connection, check if it's still valid
210
- if _rhino_connection is not None:
211
- try:
212
- # First check if PolyHaven is enabled by sending a ping command
213
- result = _rhino_connection.send_command("get_polyhaven_status")
214
- # Store the PolyHaven status globally
215
- _polyhaven_enabled = result.get("enabled", False)
216
- return _rhino_connection
217
- except Exception as e:
218
- # Connection is dead, close it and create a new one
219
- logger.warning(f"Existing connection is no longer valid: {str(e)}")
220
- try:
221
- _rhino_connection.disconnect()
222
- except:
223
- pass
224
- _rhino_connection = None
210
+ global _rhino_connection
225
211
 
226
212
  # Create a new connection if needed
227
213
  if _rhino_connection is None:
@@ -234,198 +220,7 @@ def get_rhino_connection():
234
220
 
235
221
  return _rhino_connection
236
222
 
237
-
238
- @mcp.tool()
239
- def get_scene_info(ctx: Context) -> str:
240
- """Get detailed information about the current Rhino scene"""
241
- try:
242
- rhino = get_rhino_connection()
243
- result = rhino.send_command("get_scene_info")
244
-
245
- # Just return the JSON representation of what Rhino sent us
246
- return json.dumps(result, indent=2)
247
- except Exception as e:
248
- logger.error(f"Error getting scene info from Rhino: {str(e)}")
249
- return f"Error getting scene info: {str(e)}"
250
-
251
- @mcp.tool()
252
- def get_object_info(ctx: Context, object_name: str) -> str:
253
- """
254
- Get detailed information about a specific object in the Rhino scene.
255
-
256
- Parameters:
257
- - object_name: The name of the object to get information about
258
- """
259
- try:
260
- rhino = get_rhino_connection()
261
- result = rhino.send_command("get_object_info", {"name": object_name})
262
-
263
- # Just return the JSON representation of what Rhino sent us
264
- return json.dumps(result, indent=2)
265
- except Exception as e:
266
- logger.error(f"Error getting object info from Rhino: {str(e)}")
267
- return f"Error getting object info: {str(e)}"
268
-
269
-
270
-
271
- @mcp.tool()
272
- def create_object(
273
- ctx: Context,
274
- type: str = "CUBE",
275
- name: str = None,
276
- location: List[float] = None,
277
- rotation: List[float] = None,
278
- scale: List[float] = None,
279
- # Torus-specific parameters
280
- align: str = "WORLD",
281
- major_segments: int = 48,
282
- minor_segments: int = 12,
283
- mode: str = "MAJOR_MINOR",
284
- major_radius: float = 1.0,
285
- minor_radius: float = 0.25,
286
- abso_major_rad: float = 1.25,
287
- abso_minor_rad: float = 0.75,
288
- generate_uvs: bool = True
289
- ) -> str:
290
- """
291
- Create a new object in the Rhino scene.
292
-
293
- Parameters:
294
- - type: Object type (CUBE, SPHERE, CYLINDER, PLANE, CONE, TORUS, EMPTY, CAMERA, LIGHT)
295
- - name: Optional name for the object
296
- - location: Optional [x, y, z] location coordinates
297
- - rotation: Optional [x, y, z] rotation in radians
298
- - scale: Optional [x, y, z] scale factors (not used for TORUS)
299
-
300
- Torus-specific parameters (only used when type == "TORUS"):
301
- - align: How to align the torus ('WORLD', 'VIEW', or 'CURSOR')
302
- - major_segments: Number of segments for the main ring
303
- - minor_segments: Number of segments for the cross-section
304
- - mode: Dimension mode ('MAJOR_MINOR' or 'EXT_INT')
305
- - major_radius: Radius from the origin to the center of the cross sections
306
- - minor_radius: Radius of the torus' cross section
307
- - abso_major_rad: Total exterior radius of the torus
308
- - abso_minor_rad: Total interior radius of the torus
309
- - generate_uvs: Whether to generate a default UV map
310
-
311
- Returns:
312
- A message indicating the created object name.
313
- """
314
- try:
315
- # Get the global connection
316
- rhino = get_rhino_connection()
317
-
318
- # Set default values for missing parameters
319
- loc = location or [0, 0, 0]
320
- rot = rotation or [0, 0, 0]
321
- sc = scale or [1, 1, 1]
322
-
323
- params = {
324
- "type": type,
325
- "location": loc,
326
- "rotation": rot,
327
- }
328
-
329
- if name:
330
- params["name"] = name
331
-
332
- if type == "TORUS":
333
- # For torus, the scale is not used.
334
- params.update({
335
- "align": align,
336
- "major_segments": major_segments,
337
- "minor_segments": minor_segments,
338
- "mode": mode,
339
- "major_radius": major_radius,
340
- "minor_radius": minor_radius,
341
- "abso_major_rad": abso_major_rad,
342
- "abso_minor_rad": abso_minor_rad,
343
- "generate_uvs": generate_uvs
344
- })
345
- result = rhino.send_command("create_object", params)
346
- return f"Created {type} object: {result['name']}"
347
- else:
348
- # For non-torus objects, include scale
349
- params["scale"] = sc
350
- result = rhino.send_command("create_object", params)
351
- return f"Created {type} object: {result['name']}"
352
- except Exception as e:
353
- logger.error(f"Error creating object: {str(e)}")
354
- return f"Error creating object: {str(e)}"
355
-
356
- @mcp.tool()
357
- def modify_object(
358
- ctx: Context,
359
- name: str,
360
- location: List[float] = None,
361
- rotation: List[float] = None,
362
- scale: List[float] = None,
363
- visible: bool = None
364
- ) -> str:
365
- """
366
- Modify an existing object in the Rhino scene.
367
-
368
- Parameters:
369
- - name: Name of the object to modify
370
- - location: Optional [x, y, z] location coordinates
371
- - rotation: Optional [x, y, z] rotation in radians
372
- - scale: Optional [x, y, z] scale factors
373
- - visible: Optional boolean to set visibility
374
- """
375
- try:
376
- # Get the global connection
377
- rhino = get_rhino_connection()
378
-
379
- params = {"name": name}
380
-
381
- if location is not None:
382
- params["location"] = location
383
- if rotation is not None:
384
- params["rotation"] = rotation
385
- if scale is not None:
386
- params["scale"] = scale
387
- if visible is not None:
388
- params["visible"] = visible
389
-
390
- result = rhino.send_command("modify_object", params)
391
- return f"Modified object: {result['name']}"
392
- except Exception as e:
393
- logger.error(f"Error modifying object: {str(e)}")
394
- return f"Error modifying object: {str(e)}"
395
-
396
- @mcp.tool()
397
- def delete_object(ctx: Context, name: str) -> str:
398
- """
399
- Delete an object from the Rhino scene.
400
-
401
- Parameters:
402
- - name: Name of the object to delete
403
- """
404
- try:
405
- # Get the global connection
406
- rhino = get_rhino_connection()
407
-
408
- result = rhino.send_command("delete_object", {"name": name})
409
- return f"Deleted object: {name}"
410
- except Exception as e:
411
- logger.error(f"Error deleting object: {str(e)}")
412
- return f"Error deleting object: {str(e)}"
413
-
414
- @mcp.prompt()
415
- def asset_creation_strategy() -> str:
416
- """Defines the preferred strategy for creating assets in Rhino"""
417
- return """When creating 3D content in Rhino, always start by checking if integrations are available:
418
-
419
- 0. Before anything, always check the scene from get_scene_info()
420
- 1. Use the method create_object() for basic primitives (CUBE, SPHERE, etc.)
421
- 2. When including an object into scene, ALWAYS make sure that the name of the object is meanful.
422
- 3. After giving the tool location/scale/rotation information (via create_object() and modify_object()),
423
- double check the related object's location, scale, rotation, and world_bounding_box using get_object_info(),
424
- so that the object is in the desired location.
425
- """
426
-
427
223
  # Main execution
428
-
429
224
  def main():
430
225
  """Run the MCP server"""
431
226
  mcp.run()
@@ -0,0 +1,77 @@
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 ("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 BOX, the params dictionary should contain the following keys:
31
+ - width: Width of the box along X axis of the object
32
+ - length: Length of the box along Y axis of the object
33
+ - height: Height of the box along Z axis of the object
34
+
35
+ Returns:
36
+ A message indicating the created object name.
37
+
38
+ Examples of params:
39
+ - BOX: {"width": 1.0, "length": 1.0, "height": 1.0}
40
+ - SPHERE: {"radius": 1.0}
41
+ """
42
+ try:
43
+ # Get the global connection
44
+ rhino = get_rhino_connection()
45
+ # Set default values for missing parameters
46
+ trans = translation or [0, 0, 0]
47
+ rot = rotation or [0, 0, 0]
48
+ sc = scale or [1, 1, 1]
49
+
50
+ command_params = {
51
+ "type": type,
52
+ "translation": trans,
53
+ "rotation": rot,
54
+ "scale": sc
55
+ }
56
+
57
+ if name: command_params["name"] = name
58
+ if color: command_params["color"] = color
59
+
60
+ # Create the object
61
+ result = {}
62
+ if (type == "BOX"):
63
+ command_params["width"] = params["width"]
64
+ command_params["length"] = params["length"]
65
+ command_params["height"] = params["height"]
66
+ result = rhino.send_command("create_object", command_params)
67
+ elif (type == "SPHERE"):
68
+ result = rhino.send_command("create_object", command_params)
69
+ # elif (type == "CYLINDER"):
70
+ # result = rhino.send_command("create_cylinder", command_params)
71
+
72
+
73
+ return f"Created {type} object: {result['name']}"
74
+ except Exception as e:
75
+ logger.error(f"Error creating object: {str(e)}")
76
+ return f"Error creating object: {str(e)}"
77
+
@@ -0,0 +1,56 @@
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 keys:
19
+ - type: Object type ("BOX")
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)
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": "BOX",
34
+ "name": "Box 1",
35
+ "color": [255, 0, 0],
36
+ "params": {"width": 1.0, "length": 1.0, "height": 1.0},
37
+ "translation": [0, 0, 0],
38
+ "rotation": [0, 0, 0],
39
+ "scale": [1, 1, 1]
40
+ }
41
+ ]
42
+ """
43
+ try:
44
+ # Get the global connection
45
+ rhino = get_rhino_connection()
46
+ command_params = {}
47
+ for obj in objects:
48
+ command_params[obj["name"]] = obj
49
+ result = rhino.send_command("create_objects", command_params)
50
+
51
+
52
+ return f"Created {len(result)} objects"
53
+ except Exception as e:
54
+ logger.error(f"Error creating object: {str(e)}")
55
+ return f"Error creating object: {str(e)}"
56
+
@@ -0,0 +1,25 @@
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) -> 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
+ result = rhino.send_command("delete_object", {"id": id, "name": name})
22
+ return f"Deleted object: {result['name']}"
23
+ except Exception as e:
24
+ logger.error(f"Error deleting object: {str(e)}")
25
+ 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)}"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: rhinomcp
3
- Version: 0.1.1.1
3
+ Version: 0.1.1.3
4
4
  Summary: Rhino integration through the Model Context Protocol
5
5
  Author-email: Jingcheng Chen <mail@jchen.ch>
6
6
  License: MIT
@@ -0,0 +1,20 @@
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
@@ -1,6 +0,0 @@
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
@@ -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