rhinomcp 0.1.1.2__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.2 → rhinomcp-0.1.1.3}/PKG-INFO +1 -1
  2. {rhinomcp-0.1.1.2 → 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.2 → rhinomcp-0.1.1.3}/src/rhinomcp/server.py +7 -214
  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.2 → 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.2/src/rhinomcp/__init__.py +0 -6
  18. rhinomcp-0.1.1.2/src/rhinomcp.egg-info/SOURCES.txt +0 -10
  19. {rhinomcp-0.1.1.2 → rhinomcp-0.1.1.3}/README.md +0 -0
  20. {rhinomcp-0.1.1.2 → rhinomcp-0.1.1.3}/setup.cfg +0 -0
  21. {rhinomcp-0.1.1.2 → rhinomcp-0.1.1.3}/src/rhinomcp.egg-info/dependency_links.txt +0 -0
  22. {rhinomcp-0.1.1.2 → rhinomcp-0.1.1.3}/src/rhinomcp.egg-info/entry_points.txt +0 -0
  23. {rhinomcp-0.1.1.2 → rhinomcp-0.1.1.3}/src/rhinomcp.egg-info/requires.txt +0 -0
  24. {rhinomcp-0.1.1.2 → 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.2
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.2"
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:
@@ -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,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.2
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