#
tokens: 36149/50000 13/13 files
lines: on (toggle) GitHub
raw markdown copy reset
# Directory Structure

```
├── .gitignore
├── GH_MCP
│   ├── GH_MCP
│   │   ├── Commands
│   │   │   ├── ComponentCommandHandler.cs
│   │   │   ├── ConnectionCommandHandler.cs
│   │   │   ├── DocumentCommandHandler.cs
│   │   │   ├── GeometryCommandHandler.cs
│   │   │   ├── GrasshopperCommandRegistry.cs
│   │   │   └── IntentCommandHandler.cs
│   │   ├── GH_MCP.csproj
│   │   ├── GH_MCPComponent.cs
│   │   ├── GH_MCPInfo.cs
│   │   ├── Models
│   │   │   ├── Connection.cs
│   │   │   └── GrasshopperCommand.cs
│   │   ├── Properties
│   │   │   └── launchSettings.json
│   │   ├── Resources
│   │   │   └── ComponentKnowledgeBase.json
│   │   └── Utils
│   │       ├── FuzzyMatcher.cs
│   │       └── IntentRecognizer.cs
│   └── GH_MCP.sln
├── grasshopper_mcp
│   ├── __init__.py
│   └── bridge.py
├── grasshopper_prompt_template.txt
├── LICENSE
├── README.md
├── releases
│   └── GH_MCP.gha
└── setup.py
```

# Files

--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------

```
 1 | # Python
 2 | __pycache__/
 3 | *.py[cod]
 4 | *$py.class
 5 | *.so
 6 | .Python
 7 | build/
 8 | develop-eggs/
 9 | dist/
10 | downloads/
11 | eggs/
12 | .eggs/
13 | lib/
14 | lib64/
15 | parts/
16 | sdist/
17 | var/
18 | wheels/
19 | *.egg-info/
20 | .installed.cfg
21 | *.egg
22 | MANIFEST
23 | 
24 | # Visual Studio
25 | .vs/
26 | bin/
27 | obj/
28 | *.user
29 | *.userosscache
30 | *.suo
31 | *.userprefs
32 | *.dbmdl
33 | *.dbproj.schemaview
34 | *.jfm
35 | *.pfx
36 | *.publishsettings
37 | orleans.codegen.cs
38 | 
39 | # Rhino and Grasshopper
40 | *.rhi
41 | *.ghx
42 | *.gh~
43 | *.3dm.rhl
44 | *.3dmbak
45 | *.3dm.rhl
46 | *.3dm.bak
47 | 
48 | # IDE
49 | .idea/
50 | .vscode/
51 | *.swp
52 | *.swo
53 | 
54 | # OS specific
55 | .DS_Store
56 | Thumbs.db
57 | ehthumbs.db
58 | Desktop.ini
59 | $RECYCLE.BIN/
60 | 
61 | # Project specific
62 | Grasshopper Tutorial for Beginners.pdf
63 | grasshopper_mcp_bridge.py
64 | 
```

--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------

```markdown
  1 | # Grasshopper MCP Bridge
  2 | 
  3 | Grasshopper MCP Bridge is a bridging server that connects Grasshopper and Claude Desktop using the Model Context Protocol (MCP) standard.
  4 | 
  5 | ## Features
  6 | 
  7 | - Connects Grasshopper and Claude Desktop through the MCP protocol
  8 | - Provides intuitive tool functions for creating and connecting Grasshopper components
  9 | - Supports high-level intent recognition, automatically creating complex component patterns from simple descriptions
 10 | - Includes a component knowledge base that understands parameters and connection rules for common components
 11 | - Provides component guidance resources to help Claude Desktop correctly connect components
 12 | 
 13 | ## System Architecture
 14 | 
 15 | The system consists of the following parts:
 16 | 
 17 | 1. **Grasshopper MCP Component (GH_MCP.gha)**: A plugin installed in Grasshopper that provides a TCP server to receive commands
 18 | 2. **Python MCP Bridge Server**: A bridge server that connects Claude Desktop and the Grasshopper MCP component
 19 | 3. **Component Knowledge Base**: JSON files containing component information, patterns, and intents
 20 | 
 21 | ## Installation Instructions
 22 | 
 23 | ### Prerequisites
 24 | 
 25 | - Rhino 7 or higher
 26 | - Grasshopper
 27 | - Python 3.8 or higher
 28 | - Claude Desktop
 29 | 
 30 | ### Installation Steps
 31 | 
 32 | 1. **Install the Grasshopper MCP Component**
 33 | 
 34 |    **Method 1: Download the pre-compiled GH_MCP.gha file (Recommended)**
 35 |    
 36 |    Download the [GH_MCP.gha](https://github.com/alfredatnycu/grasshopper-mcp/raw/master/releases/GH_MCP.gha) file directly from the GitHub repository and copy it to the Grasshopper components folder:
 37 |    ```
 38 |    %APPDATA%\Grasshopper\Libraries\
 39 |    ```
 40 | 
 41 |    **Method 2: Build from source**
 42 |    
 43 |    If you prefer to build from source, clone the repository and build the C# project using Visual Studio.
 44 | 
 45 | 2. **Install the Python MCP Bridge Server**
 46 | 
 47 |    **Method 1: Install from PyPI (Recommended)**
 48 |    
 49 |    The simplest method is to install directly from PyPI using pip:
 50 |    ```
 51 |    pip install grasshopper-mcp
 52 |    ```
 53 |    
 54 |    **Method 2: Install from GitHub**
 55 |    
 56 |    You can also install the latest version from GitHub:
 57 |    ```
 58 |    pip install git+https://github.com/alfredatnycu/grasshopper-mcp.git
 59 |    ```
 60 |    
 61 |    **Method 3: Install from Source Code**
 62 |    
 63 |    If you need to modify the code or develop new features, you can clone the repository and install:
 64 |    ```
 65 |    git clone https://github.com/alfredatnycu/grasshopper-mcp.git
 66 |    cd grasshopper-mcp
 67 |    pip install -e .
 68 |    ```
 69 | 
 70 |    **Install a Specific Version**
 71 |    
 72 |    If you need to install a specific version, you can use:
 73 |    ```
 74 |    pip install grasshopper-mcp==0.1.0
 75 |    ```
 76 |    Or install from a specific GitHub tag:
 77 |    ```
 78 |    pip install git+https://github.com/alfredatnycu/[email protected]
 79 |    ```
 80 | 
 81 | ## Usage
 82 | 
 83 | 1. **Start Rhino and Grasshopper**
 84 | 
 85 |    Launch Rhino and open Grasshopper.
 86 | 
 87 | 2. **Add the GH_MCP Component to Your Canvas**
 88 | 
 89 |    Find the GH_MCP component in the Grasshopper component panel and add it to your canvas.
 90 | 
 91 | 3. **Start the Python MCP Bridge Server**
 92 | 
 93 |    Open a terminal and run:
 94 |    ```
 95 |    python -m grasshopper_mcp.bridge
 96 |    ```
 97 |    
 98 |    > **Note**: The command `grasshopper-mcp` might not work directly due to Python script path issues. Using `python -m grasshopper_mcp.bridge` is the recommended and more reliable method.
 99 | 
100 | 4. **Connect Claude Desktop to the MCP Bridge**
101 | 
102 |    **Method 1: Manual Connection**
103 |    
104 |    In Claude Desktop, connect to the MCP Bridge server using the following settings:
105 |    - Protocol: MCP
106 |    - Host: localhost
107 |    - Port: 8080
108 | 
109 |    **Method 2: Configure Claude Desktop to Auto-Start the Bridge**
110 |    
111 |    You can configure Claude Desktop to automatically start the MCP Bridge server by modifying its configuration:
112 |    
113 |    ```json
114 |    "grasshopper": {
115 |      "command": "python",
116 |      "args": ["-m", "grasshopper_mcp.bridge"]
117 |    }
118 |    ```
119 |    
120 |    This configuration tells Claude Desktop to use the command `python -m grasshopper_mcp.bridge` to start the MCP server.
121 | 
122 | 5. **Start Using Grasshopper with Claude Desktop**
123 | 
124 |    You can now use Claude Desktop to control Grasshopper through natural language commands.
125 | 
126 | ## Example Commands
127 | 
128 | Here are some example commands you can use with Claude Desktop:
129 | 
130 | - "Create a circle with radius 5 at point (0,0,0)"
131 | - "Connect the circle to a extrude component with a height of 10"
132 | - "Create a grid of points with 5 rows and 5 columns"
133 | - "Apply a random rotation to all selected objects"
134 | 
135 | ## Troubleshooting
136 | 
137 | If you encounter issues, check the following:
138 | 
139 | 1. **GH_MCP Component Not Loading**
140 |    - Ensure the .gha file is in the correct location
141 |    - In Grasshopper, go to File > Preferences > Libraries and click "Unblock" to unblock new components
142 |    - Restart Rhino and Grasshopper
143 | 
144 | 2. **Bridge Server Won't Start**
145 |    - If `grasshopper-mcp` command doesn't work, use `python -m grasshopper_mcp.bridge` instead
146 |    - Ensure all required Python dependencies are installed
147 |    - Check if port 8080 is already in use by another application
148 | 
149 | 3. **Claude Desktop Can't Connect**
150 |    - Ensure the bridge server is running
151 |    - Verify you're using the correct connection settings (localhost:8080)
152 |    - Check the console output of the bridge server for any error messages
153 | 
154 | 4. **Commands Not Executing**
155 |    - Verify the GH_MCP component is on your Grasshopper canvas
156 |    - Check the bridge server console for error messages
157 |    - Ensure Claude Desktop is properly connected to the bridge server
158 | 
159 | ## Development
160 | 
161 | ### Project Structure
162 | 
163 | ```
164 | grasshopper-mcp/
165 | ├── grasshopper_mcp/       # Python bridge server
166 | │   ├── __init__.py
167 | │   └── bridge.py          # Main bridge server implementation
168 | ├── GH_MCP/                # Grasshopper component (C#)
169 | │   └── ...
170 | ├── releases/              # Pre-compiled binaries
171 | │   └── GH_MCP.gha         # Compiled Grasshopper component
172 | ├── setup.py               # Python package setup
173 | └── README.md              # This file
174 | ```
175 | 
176 | ### Contributing
177 | 
178 | Contributions are welcome! Please feel free to submit a Pull Request.
179 | 
180 | ## License
181 | 
182 | This project is licensed under the MIT License - see the LICENSE file for details.
183 | 
184 | ## Acknowledgments
185 | 
186 | - Thanks to the Rhino and Grasshopper community for their excellent tools
187 | - Thanks to Anthropic for Claude Desktop and the MCP protocol
188 | 
189 | ## Contact
190 | 
191 | For questions or support, please open an issue on the GitHub repository.
192 | 
```

--------------------------------------------------------------------------------
/grasshopper_mcp/__init__.py:
--------------------------------------------------------------------------------

```python
1 | """
2 | Grasshopper MCP Bridge Server
3 | """
4 | 
5 | __version__ = "0.1.0"
6 | 
```

--------------------------------------------------------------------------------
/GH_MCP/GH_MCP/Properties/launchSettings.json:
--------------------------------------------------------------------------------

```json
 1 | {
 2 |   "profiles": {
 3 |     "Rhino 8 - netcore": {
 4 |       "commandName": "Executable",
 5 |       "executablePath": "C:\\Program Files\\Rhino 8\\System\\Rhino.exe",
 6 |       "commandLineArgs": "/netcore /runscript=\"_Grasshopper\"",
 7 |       "environmentVariables": {
 8 |         "RHINO_PACKAGE_DIRS": "$(ProjectDir)$(OutputPath)\\"
 9 |       }
10 |     },
11 |     "Rhino 8 - netfx": {
12 |       "commandName": "Executable",
13 |       "executablePath": "C:\\Program Files\\Rhino 8\\System\\Rhino.exe",
14 |       "commandLineArgs": "/netfx /runscript=\"_Grasshopper\"",
15 |       "environmentVariables": {
16 |         "RHINO_PACKAGE_DIRS": "$(ProjectDir)$(OutputPath)\\"
17 |       }
18 |     },
19 |   }
20 | }
```

--------------------------------------------------------------------------------
/GH_MCP/GH_MCP/GH_MCPInfo.cs:
--------------------------------------------------------------------------------

```csharp
 1 | using System;
 2 | using System.Drawing;
 3 | using Grasshopper;
 4 | using Grasshopper.Kernel;
 5 | 
 6 | namespace GrasshopperMCP
 7 | {
 8 |   public class GH_MCPInfo : GH_AssemblyInfo
 9 |   {
10 |     public override string Name => "GH_MCP";
11 | 
12 |     //Return a 24x24 pixel bitmap to represent this GHA library.
13 |     public override Bitmap Icon => null;
14 | 
15 |     //Return a short string describing the purpose of this GHA library.
16 |     public override string Description => "";
17 | 
18 |     public override Guid Id => new Guid("1b472cf6-015c-496a-a0a1-7ced4df994a3");
19 | 
20 |     //Return a string identifying you or your company.
21 |     public override string AuthorName => "";
22 | 
23 |     //Return a string representing your preferred contact details.
24 |     public override string AuthorContact => "";
25 | 
26 |     //Return a string representing the version.  This returns the same version as the assembly.
27 |     public override string AssemblyVersion => GetType().Assembly.GetName().Version.ToString();
28 |   }
29 | }
```

--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------

```python
 1 | from setuptools import setup, find_packages
 2 | import os
 3 | 
 4 | # 讀取 README.md 作為長描述
 5 | with open("README.md", "r", encoding="utf-8") as fh:
 6 |     long_description = fh.read()
 7 | 
 8 | setup(
 9 |     name="grasshopper-mcp",
10 |     version="0.1.0",
11 |     packages=find_packages(),
12 |     include_package_data=True,
13 |     install_requires=[
14 |         "mcp>=0.1.0",
15 |         "websockets>=10.0",
16 |         "aiohttp>=3.8.0",
17 |     ],
18 |     entry_points={
19 |         "console_scripts": [
20 |             "grasshopper-mcp=grasshopper_mcp.bridge:main",
21 |         ],
22 |     },
23 |     author="Alfred Chen",
24 |     author_email="[email protected]",
25 |     description="Grasshopper MCP Bridge Server",
26 |     long_description=long_description,
27 |     long_description_content_type="text/markdown",
28 |     keywords="grasshopper, mcp, bridge, server",
29 |     url="https://github.com/alfredatnycu/grasshopper-mcp",
30 |     classifiers=[
31 |         "Development Status :: 3 - Alpha",
32 |         "Intended Audience :: Developers",
33 |         "Programming Language :: Python :: 3",
34 |         "Programming Language :: Python :: 3.8",
35 |         "Programming Language :: Python :: 3.9",
36 |     ],
37 |     python_requires=">=3.8",
38 | )
39 | 
```

--------------------------------------------------------------------------------
/GH_MCP/GH_MCP/Commands/GeometryCommandHandler.cs:
--------------------------------------------------------------------------------

```csharp
  1 | using System;
  2 | using System.Collections.Generic;
  3 | using GrasshopperMCP.Models;
  4 | using Grasshopper.Kernel;
  5 | using Rhino.Geometry;
  6 | using Newtonsoft.Json.Linq;
  7 | using System.Linq;
  8 | using Rhino;
  9 | 
 10 | namespace GrasshopperMCP.Commands
 11 | {
 12 |     /// <summary>
 13 |     /// 處理幾何相關命令的處理器
 14 |     /// </summary>
 15 |     public static class GeometryCommandHandler
 16 |     {
 17 |         /// <summary>
 18 |         /// 創建點
 19 |         /// </summary>
 20 |         /// <param name="command">包含點坐標的命令</param>
 21 |         /// <returns>創建的點信息</returns>
 22 |         public static object CreatePoint(Command command)
 23 |         {
 24 |             double x = command.GetParameter<double>("x");
 25 |             double y = command.GetParameter<double>("y");
 26 |             double z = command.GetParameter<double>("z");
 27 |             
 28 |             // 創建點
 29 |             Point3d point = new Point3d(x, y, z);
 30 |             
 31 |             // 返回點信息
 32 |             return new
 33 |             {
 34 |                 id = Guid.NewGuid().ToString(),
 35 |                 x = point.X,
 36 |                 y = point.Y,
 37 |                 z = point.Z
 38 |             };
 39 |         }
 40 |         
 41 |         /// <summary>
 42 |         /// 創建曲線
 43 |         /// </summary>
 44 |         /// <param name="command">包含曲線點的命令</param>
 45 |         /// <returns>創建的曲線信息</returns>
 46 |         public static object CreateCurve(Command command)
 47 |         {
 48 |             var pointsData = command.GetParameter<JArray>("points");
 49 |             
 50 |             if (pointsData == null || pointsData.Count < 2)
 51 |             {
 52 |                 throw new ArgumentException("At least 2 points are required to create a curve");
 53 |             }
 54 |             
 55 |             // 將 JSON 點數據轉換為 Point3d 列表
 56 |             List<Point3d> points = new List<Point3d>();
 57 |             foreach (var pointData in pointsData)
 58 |             {
 59 |                 double x = pointData["x"].Value<double>();
 60 |                 double y = pointData["y"].Value<double>();
 61 |                 double z = pointData["z"]?.Value<double>() ?? 0.0;
 62 |                 
 63 |                 points.Add(new Point3d(x, y, z));
 64 |             }
 65 |             
 66 |             // 創建曲線
 67 |             Curve curve;
 68 |             if (points.Count == 2)
 69 |             {
 70 |                 // 如果只有兩個點,創建一條直線
 71 |                 curve = new LineCurve(points[0], points[1]);
 72 |             }
 73 |             else
 74 |             {
 75 |                 // 如果有多個點,創建一條內插曲線
 76 |                 curve = Curve.CreateInterpolatedCurve(points, 3);
 77 |             }
 78 |             
 79 |             // 返回曲線信息
 80 |             return new
 81 |             {
 82 |                 id = Guid.NewGuid().ToString(),
 83 |                 pointCount = points.Count,
 84 |                 length = curve.GetLength()
 85 |             };
 86 |         }
 87 |         
 88 |         /// <summary>
 89 |         /// 創建圓
 90 |         /// </summary>
 91 |         /// <param name="command">包含圓心和半徑的命令</param>
 92 |         /// <returns>創建的圓信息</returns>
 93 |         public static object CreateCircle(Command command)
 94 |         {
 95 |             var centerData = command.GetParameter<JObject>("center");
 96 |             double radius = command.GetParameter<double>("radius");
 97 |             
 98 |             if (centerData == null)
 99 |             {
100 |                 throw new ArgumentException("Center point is required");
101 |             }
102 |             
103 |             if (radius <= 0)
104 |             {
105 |                 throw new ArgumentException("Radius must be greater than 0");
106 |             }
107 |             
108 |             // 解析圓心
109 |             double x = centerData["x"].Value<double>();
110 |             double y = centerData["y"].Value<double>();
111 |             double z = centerData["z"]?.Value<double>() ?? 0.0;
112 |             
113 |             Point3d center = new Point3d(x, y, z);
114 |             
115 |             // 創建圓
116 |             Circle circle = new Circle(center, radius);
117 |             
118 |             // 返回圓信息
119 |             return new
120 |             {
121 |                 id = Guid.NewGuid().ToString(),
122 |                 center = new { x = center.X, y = center.Y, z = center.Z },
123 |                 radius = circle.Radius,
124 |                 circumference = circle.Circumference
125 |             };
126 |         }
127 |     }
128 | }
129 | 
```

--------------------------------------------------------------------------------
/GH_MCP/GH_MCP/Commands/IntentCommandHandler.cs:
--------------------------------------------------------------------------------

```csharp
  1 | using System;
  2 | using System.Collections.Generic;
  3 | using System.Linq;
  4 | using System.Threading;
  5 | using GrasshopperMCP.Models;
  6 | using GrasshopperMCP.Commands;
  7 | using GH_MCP.Models;
  8 | using GH_MCP.Utils;
  9 | using Rhino;
 10 | using Newtonsoft.Json;
 11 | 
 12 | namespace GH_MCP.Commands
 13 | {
 14 |     /// <summary>
 15 |     /// 處理高層次意圖命令的處理器
 16 |     /// </summary>
 17 |     public class IntentCommandHandler
 18 |     {
 19 |         private static Dictionary<string, string> _componentIdMap = new Dictionary<string, string>();
 20 | 
 21 |         /// <summary>
 22 |         /// 處理創建模式命令
 23 |         /// </summary>
 24 |         /// <param name="command">命令對象</param>
 25 |         /// <returns>命令執行結果</returns>
 26 |         public static object CreatePattern(Command command)
 27 |         {
 28 |             // 獲取模式名稱或描述
 29 |             if (!command.Parameters.TryGetValue("description", out object descriptionObj) || descriptionObj == null)
 30 |             {
 31 |                 return Response.CreateError("Missing required parameter: description");
 32 |             }
 33 |             string description = descriptionObj.ToString();
 34 | 
 35 |             // 識別意圖
 36 |             string patternName = IntentRecognizer.RecognizeIntent(description);
 37 |             if (string.IsNullOrEmpty(patternName))
 38 |             {
 39 |                 return Response.CreateError($"Could not recognize intent from description: {description}");
 40 |             }
 41 | 
 42 |             RhinoApp.WriteLine($"Recognized intent: {patternName}");
 43 | 
 44 |             // 獲取模式詳細信息
 45 |             var (components, connections) = IntentRecognizer.GetPatternDetails(patternName);
 46 |             if (components.Count == 0)
 47 |             {
 48 |                 return Response.CreateError($"Pattern '{patternName}' has no components defined");
 49 |             }
 50 | 
 51 |             // 清空組件 ID 映射
 52 |             _componentIdMap.Clear();
 53 | 
 54 |             // 創建所有組件
 55 |             foreach (var component in components)
 56 |             {
 57 |                 try
 58 |                 {
 59 |                     // 創建組件命令
 60 |                     var addCommand = new Command(
 61 |                         "add_component",
 62 |                         new Dictionary<string, object>
 63 |                         {
 64 |                             { "type", component.Type },
 65 |                             { "x", component.X },
 66 |                             { "y", component.Y }
 67 |                         }
 68 |                     );
 69 | 
 70 |                     // 如果有設置,添加設置
 71 |                     if (component.Settings != null)
 72 |                     {
 73 |                         foreach (var setting in component.Settings)
 74 |                         {
 75 |                             addCommand.Parameters.Add(setting.Key, setting.Value);
 76 |                         }
 77 |                     }
 78 | 
 79 |                     // 執行添加組件命令
 80 |                     var result = ComponentCommandHandler.AddComponent(addCommand);
 81 |                     if (result is Response response && response.Success && response.Data != null)
 82 |                     {
 83 |                         // 保存組件 ID 映射
 84 |                         string componentId = response.Data.ToString();
 85 |                         _componentIdMap[component.Id] = componentId;
 86 |                         RhinoApp.WriteLine($"Created component {component.Type} with ID {componentId}");
 87 |                     }
 88 |                     else
 89 |                     {
 90 |                         RhinoApp.WriteLine($"Failed to create component {component.Type}");
 91 |                     }
 92 |                 }
 93 |                 catch (Exception ex)
 94 |                 {
 95 |                     RhinoApp.WriteLine($"Error creating component {component.Type}: {ex.Message}");
 96 |                 }
 97 | 
 98 |                 // 添加短暫延遲,確保組件創建完成
 99 |                 Thread.Sleep(100);
100 |             }
101 | 
102 |             // 創建所有連接
103 |             foreach (var connection in connections)
104 |             {
105 |                 try
106 |                 {
107 |                     // 檢查源和目標組件 ID 是否存在
108 |                     if (!_componentIdMap.TryGetValue(connection.SourceId, out string sourceId) ||
109 |                         !_componentIdMap.TryGetValue(connection.TargetId, out string targetId))
110 |                     {
111 |                         RhinoApp.WriteLine($"Could not find component IDs for connection {connection.SourceId} -> {connection.TargetId}");
112 |                         continue;
113 |                     }
114 | 
115 |                     // 創建連接命令
116 |                     var connectCommand = new Command(
117 |                         "connect_components",
118 |                         new Dictionary<string, object>
119 |                         {
120 |                             { "sourceId", sourceId },
121 |                             { "sourceParam", connection.SourceParam },
122 |                             { "targetId", targetId },
123 |                             { "targetParam", connection.TargetParam }
124 |                         }
125 |                     );
126 | 
127 |                     // 執行連接命令
128 |                     var result = ConnectionCommandHandler.ConnectComponents(connectCommand);
129 |                     if (result is Response response && response.Success)
130 |                     {
131 |                         RhinoApp.WriteLine($"Connected {connection.SourceId}.{connection.SourceParam} -> {connection.TargetId}.{connection.TargetParam}");
132 |                     }
133 |                     else
134 |                     {
135 |                         RhinoApp.WriteLine($"Failed to connect {connection.SourceId}.{connection.SourceParam} -> {connection.TargetId}.{connection.TargetParam}");
136 |                     }
137 |                 }
138 |                 catch (Exception ex)
139 |                 {
140 |                     RhinoApp.WriteLine($"Error creating connection: {ex.Message}");
141 |                 }
142 | 
143 |                 // 添加短暫延遲,確保連接創建完成
144 |                 Thread.Sleep(100);
145 |             }
146 | 
147 |             // 返回成功結果
148 |             return Response.Ok(new
149 |             {
150 |                 Pattern = patternName,
151 |                 ComponentCount = components.Count,
152 |                 ConnectionCount = connections.Count
153 |             });
154 |         }
155 | 
156 |         /// <summary>
157 |         /// 獲取可用的模式列表
158 |         /// </summary>
159 |         /// <param name="command">命令對象</param>
160 |         /// <returns>命令執行結果</returns>
161 |         public static object GetAvailablePatterns(Command command)
162 |         {
163 |             // 初始化意圖識別器
164 |             IntentRecognizer.Initialize();
165 | 
166 |             // 獲取所有可用的模式
167 |             var patterns = new List<string>();
168 |             if (command.Parameters.TryGetValue("query", out object queryObj) && queryObj != null)
169 |             {
170 |                 string query = queryObj.ToString();
171 |                 string patternName = IntentRecognizer.RecognizeIntent(query);
172 |                 if (!string.IsNullOrEmpty(patternName))
173 |                 {
174 |                     patterns.Add(patternName);
175 |                 }
176 |             }
177 |             else
178 |             {
179 |                 // 如果沒有查詢,返回所有模式
180 |                 // 這裡需要擴展 IntentRecognizer 以支持獲取所有模式
181 |                 // 暫時返回空列表
182 |             }
183 | 
184 |             // 返回成功結果
185 |             return Response.Ok(patterns);
186 |         }
187 |     }
188 | }
189 | 
```

--------------------------------------------------------------------------------
/GH_MCP/GH_MCP/GH_MCPComponent.cs:
--------------------------------------------------------------------------------

```csharp
  1 | using System;
  2 | using System.Collections.Generic;
  3 | using System.Drawing;
  4 | using System.Net;
  5 | using System.Net.Sockets;
  6 | using System.Text;
  7 | using System.Threading;
  8 | using System.Threading.Tasks;
  9 | using GH_MCP.Commands;
 10 | using GrasshopperMCP.Models;
 11 | using Grasshopper.Kernel;
 12 | using Rhino;
 13 | using Newtonsoft.Json;
 14 | using System.IO;
 15 | 
 16 | namespace GrasshopperMCP
 17 | {
 18 |     /// <summary>
 19 |     /// Grasshopper MCP 組件,用於與 Python 伺服器通信
 20 |     /// </summary>
 21 |     public class GrasshopperMCPComponent : GH_Component
 22 |     {
 23 |         private static TcpListener listener;
 24 |         private static bool isRunning = false;
 25 |         private static int grasshopperPort = 8080;
 26 |         
 27 |         /// <summary>
 28 |         /// 初始化 GrasshopperMCPComponent 類的新實例
 29 |         /// </summary>
 30 |         public GrasshopperMCPComponent()
 31 |             : base("Grasshopper MCP", "MCP", "Machine Control Protocol for Grasshopper", "Params", "Util")
 32 |         {
 33 |         }
 34 |         
 35 |         /// <summary>
 36 |         /// 註冊輸入參數
 37 |         /// </summary>
 38 |         protected override void RegisterInputParams(GH_Component.GH_InputParamManager pManager)
 39 |         {
 40 |             pManager.AddBooleanParameter("Enabled", "E", "Enable or disable the MCP server", GH_ParamAccess.item, false);
 41 |             pManager.AddIntegerParameter("Port", "P", "Port to listen on", GH_ParamAccess.item, grasshopperPort);
 42 |         }
 43 |         
 44 |         /// <summary>
 45 |         /// 註冊輸出參數
 46 |         /// </summary>
 47 |         protected override void RegisterOutputParams(GH_Component.GH_OutputParamManager pManager)
 48 |         {
 49 |             pManager.AddTextParameter("Status", "S", "Server status", GH_ParamAccess.item);
 50 |             pManager.AddTextParameter("LastCommand", "C", "Last received command", GH_ParamAccess.item);
 51 |         }
 52 |         
 53 |         /// <summary>
 54 |         /// 解決組件
 55 |         /// </summary>
 56 |         protected override void SolveInstance(IGH_DataAccess DA)
 57 |         {
 58 |             bool enabled = false;
 59 |             int port = grasshopperPort;
 60 |             
 61 |             // 獲取輸入參數
 62 |             if (!DA.GetData(0, ref enabled)) return;
 63 |             if (!DA.GetData(1, ref port)) return;
 64 |             
 65 |             // 更新端口
 66 |             grasshopperPort = port;
 67 |             
 68 |             // 根據啟用狀態啟動或停止伺服器
 69 |             if (enabled && !isRunning)
 70 |             {
 71 |                 Start();
 72 |                 DA.SetData(0, $"Running on port {grasshopperPort}");
 73 |             }
 74 |             else if (!enabled && isRunning)
 75 |             {
 76 |                 Stop();
 77 |                 DA.SetData(0, "Stopped");
 78 |             }
 79 |             else if (enabled && isRunning)
 80 |             {
 81 |                 DA.SetData(0, $"Running on port {grasshopperPort}");
 82 |             }
 83 |             else
 84 |             {
 85 |                 DA.SetData(0, "Stopped");
 86 |             }
 87 |             
 88 |             // 設置最後接收的命令
 89 |             DA.SetData(1, LastCommand);
 90 |         }
 91 |         
 92 |         /// <summary>
 93 |         /// 組件 GUID
 94 |         /// </summary>
 95 |         public override Guid ComponentGuid => new Guid("12345678-1234-1234-1234-123456789012");
 96 |         
 97 |         /// <summary>
 98 |         /// 暴露圖標
 99 |         /// </summary>
100 |         protected override Bitmap Icon => null;
101 |         
102 |         /// <summary>
103 |         /// 最後接收的命令
104 |         /// </summary>
105 |         public static string LastCommand { get; private set; } = "None";
106 |         
107 |         /// <summary>
108 |         /// 啟動 MCP 伺服器
109 |         /// </summary>
110 |         public static void Start()
111 |         {
112 |             if (isRunning) return;
113 |             
114 |             // 初始化命令註冊表
115 |             GrasshopperCommandRegistry.Initialize();
116 |             
117 |             // 啟動 TCP 監聽器
118 |             isRunning = true;
119 |             listener = new TcpListener(IPAddress.Loopback, grasshopperPort);
120 |             listener.Start();
121 |             RhinoApp.WriteLine($"GrasshopperMCPBridge started on port {grasshopperPort}.");
122 |             
123 |             // 開始接收連接
124 |             Task.Run(ListenerLoop);
125 |         }
126 |         
127 |         /// <summary>
128 |         /// 停止 MCP 伺服器
129 |         /// </summary>
130 |         public static void Stop()
131 |         {
132 |             if (!isRunning) return;
133 |             
134 |             isRunning = false;
135 |             listener.Stop();
136 |             RhinoApp.WriteLine("GrasshopperMCPBridge stopped.");
137 |         }
138 |         
139 |         /// <summary>
140 |         /// 監聽循環,處理傳入的連接
141 |         /// </summary>
142 |         private static async Task ListenerLoop()
143 |         {
144 |             try
145 |             {
146 |                 while (isRunning)
147 |                 {
148 |                     // 等待客戶端連接
149 |                     var client = await listener.AcceptTcpClientAsync();
150 |                     RhinoApp.WriteLine("GrasshopperMCPBridge: Client connected.");
151 |                     
152 |                     // 處理客戶端連接
153 |                     _ = Task.Run(() => HandleClient(client));
154 |                 }
155 |             }
156 |             catch (Exception ex)
157 |             {
158 |                 if (isRunning)
159 |                 {
160 |                     RhinoApp.WriteLine($"GrasshopperMCPBridge error: {ex.Message}");
161 |                     isRunning = false;
162 |                 }
163 |             }
164 |         }
165 |         
166 |         /// <summary>
167 |         /// 處理客戶端連接
168 |         /// </summary>
169 |         /// <param name="client">TCP 客戶端</param>
170 |         private static async Task HandleClient(TcpClient client)
171 |         {
172 |             using (client)
173 |             using (var stream = client.GetStream())
174 |             using (var reader = new StreamReader(stream, Encoding.UTF8))
175 |             using (var writer = new StreamWriter(stream, Encoding.UTF8) { AutoFlush = true })
176 |             {
177 |                 try
178 |                 {
179 |                     // 讀取命令
180 |                     string commandJson = await reader.ReadLineAsync();
181 |                     if (string.IsNullOrEmpty(commandJson))
182 |                     {
183 |                         return;
184 |                     }
185 |                     
186 |                     // 更新最後接收的命令
187 |                     LastCommand = commandJson;
188 |                     
189 |                     // 解析命令
190 |                     Command command = JsonConvert.DeserializeObject<Command>(commandJson);
191 |                     RhinoApp.WriteLine($"GrasshopperMCPBridge: Received command: {command.Type}");
192 |                     
193 |                     // 執行命令
194 |                     Response response = GrasshopperCommandRegistry.ExecuteCommand(command);
195 |                     
196 |                     // 發送響應
197 |                     string responseJson = JsonConvert.SerializeObject(response);
198 |                     await writer.WriteLineAsync(responseJson);
199 |                     
200 |                     RhinoApp.WriteLine($"GrasshopperMCPBridge: Command {command.Type} executed with result: {(response.Success ? "Success" : "Error")}");
201 |                 }
202 |                 catch (Exception ex)
203 |                 {
204 |                     RhinoApp.WriteLine($"GrasshopperMCPBridge error handling client: {ex.Message}");
205 |                     
206 |                     // 發送錯誤響應
207 |                     Response errorResponse = Response.CreateError($"Server error: {ex.Message}");
208 |                     string errorResponseJson = JsonConvert.SerializeObject(errorResponse);
209 |                     await writer.WriteLineAsync(errorResponseJson);
210 |                 }
211 |             }
212 |         }
213 |     }
214 | }
215 | 
```

--------------------------------------------------------------------------------
/GH_MCP/GH_MCP/Resources/ComponentKnowledgeBase.json:
--------------------------------------------------------------------------------

```json
  1 | {
  2 |   "components": [
  3 |     {
  4 |       "name": "Point",
  5 |       "category": "Params",
  6 |       "subcategory": "Geometry",
  7 |       "description": "Creates a point at the specified coordinates",
  8 |       "inputs": [
  9 |         {"name": "X", "type": "Number", "description": "X coordinate"},
 10 |         {"name": "Y", "type": "Number", "description": "Y coordinate"},
 11 |         {"name": "Z", "type": "Number", "description": "Z coordinate"}
 12 |       ],
 13 |       "outputs": [
 14 |         {"name": "Pt", "type": "Point", "description": "Point"}
 15 |       ]
 16 |     },
 17 |     {
 18 |       "name": "XY Plane",
 19 |       "category": "Vector",
 20 |       "subcategory": "Plane",
 21 |       "description": "Creates an XY plane at the world origin or at a specified point",
 22 |       "inputs": [
 23 |         {"name": "Origin", "type": "Point", "description": "Origin point", "optional": true}
 24 |       ],
 25 |       "outputs": [
 26 |         {"name": "Plane", "type": "Plane", "description": "XY plane"}
 27 |       ]
 28 |     },
 29 |     {
 30 |       "name": "Box",
 31 |       "category": "Surface",
 32 |       "subcategory": "Primitive",
 33 |       "description": "Creates a box from a base plane and dimensions",
 34 |       "inputs": [
 35 |         {"name": "Base", "type": "Plane", "description": "Base plane"},
 36 |         {"name": "X Size", "type": "Number", "description": "Size in X direction"},
 37 |         {"name": "Y Size", "type": "Number", "description": "Size in Y direction"},
 38 |         {"name": "Z Size", "type": "Number", "description": "Size in Z direction"}
 39 |       ],
 40 |       "outputs": [
 41 |         {"name": "Box", "type": "Brep", "description": "Box geometry"}
 42 |       ]
 43 |     },
 44 |     {
 45 |       "name": "Circle",
 46 |       "category": "Curve",
 47 |       "subcategory": "Primitive",
 48 |       "description": "Creates a circle from a plane and radius",
 49 |       "inputs": [
 50 |         {"name": "Plane", "type": "Plane", "description": "Circle plane"},
 51 |         {"name": "Radius", "type": "Number", "description": "Circle radius"}
 52 |       ],
 53 |       "outputs": [
 54 |         {"name": "Circle", "type": "Curve", "description": "Circle curve"}
 55 |       ]
 56 |     },
 57 |     {
 58 |       "name": "Number Slider",
 59 |       "category": "Params",
 60 |       "subcategory": "Input",
 61 |       "description": "Slider for numeric input",
 62 |       "inputs": [],
 63 |       "outputs": [
 64 |         {"name": "Number", "type": "Number", "description": "Slider value"}
 65 |       ],
 66 |       "defaultSettings": {
 67 |         "min": 0,
 68 |         "max": 10,
 69 |         "value": 5
 70 |       }
 71 |     },
 72 |     {
 73 |       "name": "Panel",
 74 |       "category": "Params",
 75 |       "subcategory": "Input",
 76 |       "description": "Text panel for input or output",
 77 |       "inputs": [
 78 |         {"name": "Input", "type": "Any", "description": "Any input", "optional": true}
 79 |       ],
 80 |       "outputs": [
 81 |         {"name": "Output", "type": "Text", "description": "Panel text"}
 82 |       ]
 83 |     },
 84 |     {
 85 |       "name": "Voronoi",
 86 |       "category": "Surface",
 87 |       "subcategory": "Triangulation",
 88 |       "description": "Creates a Voronoi diagram from points",
 89 |       "inputs": [
 90 |         {"name": "Points", "type": "Point", "description": "Input points"},
 91 |         {"name": "Radius", "type": "Number", "description": "Cell radius", "optional": true},
 92 |         {"name": "Plane", "type": "Plane", "description": "Base plane", "optional": true}
 93 |       ],
 94 |       "outputs": [
 95 |         {"name": "Cells", "type": "Curve", "description": "Voronoi cells"},
 96 |         {"name": "Vertices", "type": "Point", "description": "Voronoi vertices"}
 97 |       ]
 98 |     },
 99 |     {
100 |       "name": "Populate 3D",
101 |       "category": "Vector",
102 |       "subcategory": "Grid",
103 |       "description": "Creates a 3D grid of points",
104 |       "inputs": [
105 |         {"name": "Base", "type": "Plane", "description": "Base plane"},
106 |         {"name": "Size X", "type": "Number", "description": "Size in X direction"},
107 |         {"name": "Size Y", "type": "Number", "description": "Size in Y direction"},
108 |         {"name": "Size Z", "type": "Number", "description": "Size in Z direction"},
109 |         {"name": "Count X", "type": "Integer", "description": "Count in X direction"},
110 |         {"name": "Count Y", "type": "Integer", "description": "Count in Y direction"},
111 |         {"name": "Count Z", "type": "Integer", "description": "Count in Z direction"}
112 |       ],
113 |       "outputs": [
114 |         {"name": "Points", "type": "Point", "description": "3D grid of points"}
115 |       ]
116 |     },
117 |     {
118 |       "name": "Boundary Surfaces",
119 |       "category": "Surface",
120 |       "subcategory": "Freeform",
121 |       "description": "Creates boundary surfaces from curves",
122 |       "inputs": [
123 |         {"name": "Curves", "type": "Curve", "description": "Input curves"}
124 |       ],
125 |       "outputs": [
126 |         {"name": "Surfaces", "type": "Surface", "description": "Boundary surfaces"}
127 |       ]
128 |     },
129 |     {
130 |       "name": "Extrude",
131 |       "category": "Surface",
132 |       "subcategory": "Freeform",
133 |       "description": "Extrudes curves or surfaces",
134 |       "inputs": [
135 |         {"name": "Base", "type": "Geometry", "description": "Base geometry"},
136 |         {"name": "Direction", "type": "Vector", "description": "Extrusion direction"},
137 |         {"name": "Distance", "type": "Number", "description": "Extrusion distance"}
138 |       ],
139 |       "outputs": [
140 |         {"name": "Result", "type": "Brep", "description": "Extruded geometry"}
141 |       ]
142 |     }
143 |   ],
144 |   "patterns": [
145 |     {
146 |       "name": "3D Box",
147 |       "description": "Creates a simple 3D box",
148 |       "components": [
149 |         {"type": "XY Plane", "x": 100, "y": 100, "id": "plane"},
150 |         {"type": "Number Slider", "x": 100, "y": 200, "id": "sliderX", "settings": {"min": 0, "max": 50, "value": 20}},
151 |         {"type": "Number Slider", "x": 100, "y": 250, "id": "sliderY", "settings": {"min": 0, "max": 50, "value": 20}},
152 |         {"type": "Number Slider", "x": 100, "y": 300, "id": "sliderZ", "settings": {"min": 0, "max": 50, "value": 20}},
153 |         {"type": "Box", "x": 400, "y": 200, "id": "box"}
154 |       ],
155 |       "connections": [
156 |         {"source": "plane", "sourceParam": "Plane", "target": "box", "targetParam": "Base"},
157 |         {"source": "sliderX", "sourceParam": "Number", "target": "box", "targetParam": "X Size"},
158 |         {"source": "sliderY", "sourceParam": "Number", "target": "box", "targetParam": "Y Size"},
159 |         {"source": "sliderZ", "sourceParam": "Number", "target": "box", "targetParam": "Z Size"}
160 |       ]
161 |     },
162 |     {
163 |       "name": "3D Voronoi",
164 |       "description": "Creates a 3D Voronoi pattern within a box",
165 |       "components": [
166 |         {"type": "XY Plane", "x": 100, "y": 100, "id": "plane"},
167 |         {"type": "Number Slider", "x": 100, "y": 200, "id": "sizeX", "settings": {"min": 0, "max": 100, "value": 50}},
168 |         {"type": "Number Slider", "x": 100, "y": 250, "id": "sizeY", "settings": {"min": 0, "max": 100, "value": 50}},
169 |         {"type": "Number Slider", "x": 100, "y": 300, "id": "sizeZ", "settings": {"min": 0, "max": 100, "value": 50}},
170 |         {"type": "Number Slider", "x": 100, "y": 350, "id": "countX", "settings": {"min": 1, "max": 20, "value": 10}},
171 |         {"type": "Number Slider", "x": 100, "y": 400, "id": "countY", "settings": {"min": 1, "max": 20, "value": 10}},
172 |         {"type": "Number Slider", "x": 100, "y": 450, "id": "countZ", "settings": {"min": 1, "max": 20, "value": 10}},
173 |         {"type": "Populate 3D", "x": 400, "y": 250, "id": "populate"},
174 |         {"type": "Voronoi", "x": 600, "y": 250, "id": "voronoi"}
175 |       ],
176 |       "connections": [
177 |         {"source": "plane", "sourceParam": "Plane", "target": "populate", "targetParam": "Base"},
178 |         {"source": "sizeX", "sourceParam": "Number", "target": "populate", "targetParam": "Size X"},
179 |         {"source": "sizeY", "sourceParam": "Number", "target": "populate", "targetParam": "Size Y"},
180 |         {"source": "sizeZ", "sourceParam": "Number", "target": "populate", "targetParam": "Size Z"},
181 |         {"source": "countX", "sourceParam": "Number", "target": "populate", "targetParam": "Count X"},
182 |         {"source": "countY", "sourceParam": "Number", "target": "populate", "targetParam": "Count Y"},
183 |         {"source": "countZ", "sourceParam": "Number", "target": "populate", "targetParam": "Count Z"},
184 |         {"source": "populate", "sourceParam": "Points", "target": "voronoi", "targetParam": "Points"}
185 |       ]
186 |     },
187 |     {
188 |       "name": "Circle",
189 |       "description": "Creates a simple circle",
190 |       "components": [
191 |         {"type": "XY Plane", "x": 100, "y": 100, "id": "plane"},
192 |         {"type": "Number Slider", "x": 100, "y": 200, "id": "radius", "settings": {"min": 0, "max": 50, "value": 10}},
193 |         {"type": "Circle", "x": 400, "y": 150, "id": "circle"}
194 |       ],
195 |       "connections": [
196 |         {"source": "plane", "sourceParam": "Plane", "target": "circle", "targetParam": "Plane"},
197 |         {"source": "radius", "sourceParam": "Number", "target": "circle", "targetParam": "Radius"}
198 |       ]
199 |     }
200 |   ],
201 |   "intents": [
202 |     {
203 |       "keywords": ["box", "cube", "rectangular", "prism"],
204 |       "pattern": "3D Box"
205 |     },
206 |     {
207 |       "keywords": ["voronoi", "cell", "diagram", "3d", "cellular"],
208 |       "pattern": "3D Voronoi"
209 |     },
210 |     {
211 |       "keywords": ["circle", "round", "disc"],
212 |       "pattern": "Circle"
213 |     }
214 |   ]
215 | }
216 | 
```

--------------------------------------------------------------------------------
/GH_MCP/GH_MCP/Commands/ConnectionCommandHandler.cs:
--------------------------------------------------------------------------------

```csharp
  1 | using System;
  2 | using System.Collections.Generic;
  3 | using System.Linq;
  4 | using System.Threading;
  5 | using GrasshopperMCP.Models;
  6 | using GH_MCP.Models;
  7 | using Grasshopper;
  8 | using Grasshopper.Kernel;
  9 | using Grasshopper.Kernel.Parameters;
 10 | using Rhino;
 11 | using Newtonsoft.Json;
 12 | using GH_MCP.Utils;
 13 | 
 14 | namespace GH_MCP.Commands
 15 | {
 16 |     /// <summary>
 17 |     /// 處理組件連接相關的命令
 18 |     /// </summary>
 19 |     public class ConnectionCommandHandler
 20 |     {
 21 |         /// <summary>
 22 |         /// 連接兩個組件
 23 |         /// </summary>
 24 |         /// <param name="command">命令對象</param>
 25 |         /// <returns>命令執行結果</returns>
 26 |         public static object ConnectComponents(Command command)
 27 |         {
 28 |             // 獲取源組件 ID
 29 |             if (!command.Parameters.TryGetValue("sourceId", out object sourceIdObj) || sourceIdObj == null)
 30 |             {
 31 |                 return Response.CreateError("Missing required parameter: sourceId");
 32 |             }
 33 |             string sourceId = sourceIdObj.ToString();
 34 | 
 35 |             // 獲取源參數名稱或索引
 36 |             string sourceParam = null;
 37 |             int? sourceParamIndex = null;
 38 |             if (command.Parameters.TryGetValue("sourceParam", out object sourceParamObj) && sourceParamObj != null)
 39 |             {
 40 |                 sourceParam = sourceParamObj.ToString();
 41 |                 // 使用模糊匹配獲取標準化的參數名稱
 42 |                 sourceParam = FuzzyMatcher.GetClosestParameterName(sourceParam);
 43 |             }
 44 |             else if (command.Parameters.TryGetValue("sourceParamIndex", out object sourceParamIndexObj) && sourceParamIndexObj != null)
 45 |             {
 46 |                 if (int.TryParse(sourceParamIndexObj.ToString(), out int index))
 47 |                 {
 48 |                     sourceParamIndex = index;
 49 |                 }
 50 |             }
 51 | 
 52 |             // 獲取目標組件 ID
 53 |             if (!command.Parameters.TryGetValue("targetId", out object targetIdObj) || targetIdObj == null)
 54 |             {
 55 |                 return Response.CreateError("Missing required parameter: targetId");
 56 |             }
 57 |             string targetId = targetIdObj.ToString();
 58 | 
 59 |             // 獲取目標參數名稱或索引
 60 |             string targetParam = null;
 61 |             int? targetParamIndex = null;
 62 |             if (command.Parameters.TryGetValue("targetParam", out object targetParamObj) && targetParamObj != null)
 63 |             {
 64 |                 targetParam = targetParamObj.ToString();
 65 |                 // 使用模糊匹配獲取標準化的參數名稱
 66 |                 targetParam = FuzzyMatcher.GetClosestParameterName(targetParam);
 67 |             }
 68 |             else if (command.Parameters.TryGetValue("targetParamIndex", out object targetParamIndexObj) && targetParamIndexObj != null)
 69 |             {
 70 |                 if (int.TryParse(targetParamIndexObj.ToString(), out int index))
 71 |                 {
 72 |                     targetParamIndex = index;
 73 |                 }
 74 |             }
 75 | 
 76 |             // 記錄連接信息
 77 |             RhinoApp.WriteLine($"Connecting: sourceId={sourceId}, sourceParam={sourceParam}, targetId={targetId}, targetParam={targetParam}");
 78 | 
 79 |             // 創建連接對象
 80 |             var connection = new ConnectionPairing
 81 |             {
 82 |                 Source = new Connection
 83 |                 {
 84 |                     ComponentId = sourceId,
 85 |                     ParameterName = sourceParam,
 86 |                     ParameterIndex = sourceParamIndex
 87 |                 },
 88 |                 Target = new Connection
 89 |                 {
 90 |                     ComponentId = targetId,
 91 |                     ParameterName = targetParam,
 92 |                     ParameterIndex = targetParamIndex
 93 |                 }
 94 |             };
 95 | 
 96 |             // 檢查連接是否有效
 97 |             if (!connection.IsValid())
 98 |             {
 99 |                 return Response.CreateError("Invalid connection parameters");
100 |             }
101 | 
102 |             // 在 UI 線程上執行連接操作
103 |             object result = null;
104 |             Exception exception = null;
105 | 
106 |             RhinoApp.InvokeOnUiThread(new Action(() =>
107 |             {
108 |                 try
109 |                 {
110 |                     // 獲取當前文檔
111 |                     var doc = Instances.ActiveCanvas?.Document;
112 |                     if (doc == null)
113 |                     {
114 |                         exception = new InvalidOperationException("No active Grasshopper document");
115 |                         return;
116 |                     }
117 | 
118 |                     // 查找源組件
119 |                     Guid sourceGuid;
120 |                     if (!Guid.TryParse(connection.Source.ComponentId, out sourceGuid))
121 |                     {
122 |                         exception = new ArgumentException($"Invalid source component ID: {connection.Source.ComponentId}");
123 |                         return;
124 |                     }
125 | 
126 |                     var sourceComponent = doc.FindObject(sourceGuid, true);
127 |                     if (sourceComponent == null)
128 |                     {
129 |                         exception = new ArgumentException($"Source component not found: {connection.Source.ComponentId}");
130 |                         return;
131 |                     }
132 | 
133 |                     // 查找目標組件
134 |                     Guid targetGuid;
135 |                     if (!Guid.TryParse(connection.Target.ComponentId, out targetGuid))
136 |                     {
137 |                         exception = new ArgumentException($"Invalid target component ID: {connection.Target.ComponentId}");
138 |                         return;
139 |                     }
140 | 
141 |                     var targetComponent = doc.FindObject(targetGuid, true);
142 |                     if (targetComponent == null)
143 |                     {
144 |                         exception = new ArgumentException($"Target component not found: {connection.Target.ComponentId}");
145 |                         return;
146 |                     }
147 | 
148 |                     // 檢查源組件是否為輸入參數組件
149 |                     if (sourceComponent is IGH_Param && ((IGH_Param)sourceComponent).Kind == GH_ParamKind.input)
150 |                     {
151 |                         exception = new ArgumentException("Source component cannot be an input parameter");
152 |                         return;
153 |                     }
154 | 
155 |                     // 檢查目標組件是否為輸出參數組件
156 |                     if (targetComponent is IGH_Param && ((IGH_Param)targetComponent).Kind == GH_ParamKind.output)
157 |                     {
158 |                         exception = new ArgumentException("Target component cannot be an output parameter");
159 |                         return;
160 |                     }
161 | 
162 |                     // 獲取源參數
163 |                     IGH_Param sourceParameter = GetParameter(sourceComponent, connection.Source, false);
164 |                     if (sourceParameter == null)
165 |                     {
166 |                         exception = new ArgumentException($"Source parameter not found: {connection.Source.ParameterName ?? connection.Source.ParameterIndex.ToString()}");
167 |                         return;
168 |                     }
169 | 
170 |                     // 獲取目標參數
171 |                     IGH_Param targetParameter = GetParameter(targetComponent, connection.Target, true);
172 |                     if (targetParameter == null)
173 |                     {
174 |                         exception = new ArgumentException($"Target parameter not found: {connection.Target.ParameterName ?? connection.Target.ParameterIndex.ToString()}");
175 |                         return;
176 |                     }
177 | 
178 |                     // 檢查參數類型相容性
179 |                     if (!AreParametersCompatible(sourceParameter, targetParameter))
180 |                     {
181 |                         exception = new ArgumentException($"Parameters are not compatible: {sourceParameter.GetType().Name} cannot connect to {targetParameter.GetType().Name}");
182 |                         return;
183 |                     }
184 | 
185 |                     // 移除現有連接(如果需要)
186 |                     if (targetParameter.SourceCount > 0)
187 |                     {
188 |                         targetParameter.RemoveAllSources();
189 |                     }
190 | 
191 |                     // 連接參數
192 |                     targetParameter.AddSource(sourceParameter);
193 |                     
194 |                     // 刷新數據
195 |                     targetParameter.CollectData();
196 |                     targetParameter.ComputeData();
197 |                     
198 |                     // 刷新畫布
199 |                     doc.NewSolution(false);
200 | 
201 |                     // 返回結果
202 |                     result = new
203 |                     {
204 |                         success = true,
205 |                         message = "Connection created successfully",
206 |                         sourceId = connection.Source.ComponentId,
207 |                         targetId = connection.Target.ComponentId,
208 |                         sourceParam = sourceParameter.Name,
209 |                         targetParam = targetParameter.Name,
210 |                         sourceType = sourceParameter.GetType().Name,
211 |                         targetType = targetParameter.GetType().Name,
212 |                         sourceDescription = sourceParameter.Description,
213 |                         targetDescription = targetParameter.Description
214 |                     };
215 |                 }
216 |                 catch (Exception ex)
217 |                 {
218 |                     exception = ex;
219 |                     RhinoApp.WriteLine($"Error in ConnectComponents: {ex.Message}");
220 |                 }
221 |             }));
222 | 
223 |             // 等待 UI 線程操作完成
224 |             while (result == null && exception == null)
225 |             {
226 |                 Thread.Sleep(10);
227 |             }
228 | 
229 |             // 如果有異常,拋出
230 |             if (exception != null)
231 |             {
232 |                 return Response.CreateError($"Error executing command 'connect_components': {exception.Message}");
233 |             }
234 | 
235 |             return Response.Ok(result);
236 |         }
237 | 
238 |         /// <summary>
239 |         /// 獲取組件的參數
240 |         /// </summary>
241 |         /// <param name="docObj">文檔對象</param>
242 |         /// <param name="connection">連接信息</param>
243 |         /// <param name="isInput">是否為輸入參數</param>
244 |         /// <returns>參數對象</returns>
245 |         private static IGH_Param GetParameter(IGH_DocumentObject docObj, Connection connection, bool isInput)
246 |         {
247 |             // 處理參數組件
248 |             if (docObj is IGH_Param param)
249 |             {
250 |                 return param;
251 |             }
252 |             
253 |             // 處理一般組件
254 |             if (docObj is IGH_Component component)
255 |             {
256 |                 // 獲取參數集合
257 |                 IList<IGH_Param> parameters = isInput ? component.Params.Input : component.Params.Output;
258 |                 
259 |                 // 檢查參數集合是否為空
260 |                 if (parameters == null || parameters.Count == 0)
261 |                 {
262 |                     return null;
263 |                 }
264 |                 
265 |                 // 如果只有一個參數,直接返回(只有在未指定名稱或索引時)
266 |                 if (parameters.Count == 1 && string.IsNullOrEmpty(connection.ParameterName) && !connection.ParameterIndex.HasValue)
267 |                 {
268 |                     return parameters[0];
269 |                 }
270 |                 
271 |                 // 按名稱查找參數
272 |                 if (!string.IsNullOrEmpty(connection.ParameterName))
273 |                 {
274 |                     // 精確匹配
275 |                     foreach (var p in parameters)
276 |                     {
277 |                         if (string.Equals(p.Name, connection.ParameterName, StringComparison.OrdinalIgnoreCase))
278 |                         {
279 |                             return p;
280 |                         }
281 |                     }
282 |                     
283 |                     // 模糊匹配
284 |                     foreach (var p in parameters)
285 |                     {
286 |                         if (p.Name.IndexOf(connection.ParameterName, StringComparison.OrdinalIgnoreCase) >= 0)
287 |                         {
288 |                             return p;
289 |                         }
290 |                     }
291 | 
292 |                     // 嘗試匹配 NickName
293 |                     foreach (var p in parameters)
294 |                     {
295 |                         if (string.Equals(p.NickName, connection.ParameterName, StringComparison.OrdinalIgnoreCase))
296 |                         {
297 |                             return p;
298 |                         }
299 |                     }
300 |                 }
301 |                 
302 |                 // 按索引查找參數
303 |                 if (connection.ParameterIndex.HasValue)
304 |                 {
305 |                     int index = connection.ParameterIndex.Value;
306 |                     if (index >= 0 && index < parameters.Count)
307 |                     {
308 |                         return parameters[index];
309 |                     }
310 |                 }
311 |             }
312 |             
313 |             return null;
314 |         }
315 | 
316 |         /// <summary>
317 |         /// 檢查兩個參數是否相容
318 |         /// </summary>
319 |         /// <param name="source">源參數</param>
320 |         /// <param name="target">目標參數</param>
321 |         /// <returns>是否相容</returns>
322 |         private static bool AreParametersCompatible(IGH_Param source, IGH_Param target)
323 |         {
324 |             // 如果參數類型完全匹配,則相容
325 |             if (source.GetType() == target.GetType())
326 |             {
327 |                 return true;
328 |             }
329 | 
330 |             // 檢查數據類型是否兼容
331 |             var sourceType = source.Type;
332 |             var targetType = target.Type;
333 |             
334 |             // 記錄參數類型信息,用於調試
335 |             RhinoApp.WriteLine($"Parameter types: source={sourceType.Name}, target={targetType.Name}");
336 |             RhinoApp.WriteLine($"Parameter names: source={source.Name}, target={target.Name}");
337 |             
338 |             // 檢查數字類型的兼容性
339 |             bool isSourceNumeric = IsNumericType(source);
340 |             bool isTargetNumeric = IsNumericType(target);
341 |             
342 |             if (isSourceNumeric && isTargetNumeric)
343 |             {
344 |                 return true;
345 |             }
346 | 
347 |             // 曲線和幾何體之間的特殊處理
348 |             bool isSourceCurve = source is Param_Curve;
349 |             bool isTargetCurve = target is Param_Curve;
350 |             bool isSourceGeometry = source is Param_Geometry;
351 |             bool isTargetGeometry = target is Param_Geometry;
352 | 
353 |             if ((isSourceCurve && isTargetGeometry) || (isSourceGeometry && isTargetCurve))
354 |             {
355 |                 return true;
356 |             }
357 | 
358 |             // 點和向量之間的特殊處理
359 |             bool isSourcePoint = source is Param_Point;
360 |             bool isTargetPoint = target is Param_Point;
361 |             bool isSourceVector = source is Param_Vector;
362 |             bool isTargetVector = target is Param_Vector;
363 | 
364 |             if ((isSourcePoint && isTargetVector) || (isSourceVector && isTargetPoint))
365 |             {
366 |                 return true;
367 |             }
368 | 
369 |             // 檢查組件的 GUID,確保連接到正確的元件類型
370 |             // 獲取參數所屬的組件
371 |             var sourceDoc = source.OnPingDocument();
372 |             var targetDoc = target.OnPingDocument();
373 |             
374 |             if (sourceDoc != null && targetDoc != null)
375 |             {
376 |                 // 嘗試查找參數所屬的組件
377 |                 IGH_Component sourceComponent = FindComponentForParam(sourceDoc, source);
378 |                 IGH_Component targetComponent = FindComponentForParam(targetDoc, target);
379 |                 
380 |                 // 如果找到了源組件和目標組件
381 |                 if (sourceComponent != null && targetComponent != null)
382 |                 {
383 |                     // 記錄組件信息,用於調試
384 |                     RhinoApp.WriteLine($"Components: source={sourceComponent.Name}, target={targetComponent.Name}");
385 |                     RhinoApp.WriteLine($"Component GUIDs: source={sourceComponent.ComponentGuid}, target={targetComponent.ComponentGuid}");
386 |                     
387 |                     // 特殊處理平面到幾何元件的連接
388 |                     if (IsPlaneComponent(sourceComponent) && RequiresPlaneInput(targetComponent))
389 |                     {
390 |                         RhinoApp.WriteLine("Connecting plane component to geometry component that requires plane input");
391 |                         return true;
392 |                     }
393 |                     
394 |                     // 如果源是滑塊且目標是圓,確保目標是創建圓的組件
395 |                     if (sourceComponent.Name.Contains("Number") && targetComponent.Name.Contains("Circle"))
396 |                     {
397 |                         // 檢查目標是否為正確的圓元件 (使用 GUID 或描述)
398 |                         if (targetComponent.ComponentGuid.ToString() == "d1028c72-ff86-4057-9eb0-36c687a4d98c")
399 |                         {
400 |                             // 這是錯誤的圓元件 (參數容器)
401 |                             RhinoApp.WriteLine("Detected connection to Circle parameter container instead of Circle component");
402 |                             return false;
403 |                         }
404 |                         if (targetComponent.ComponentGuid.ToString() == "807b86e3-be8d-4970-92b5-f8cdcb45b06b")
405 |                         {
406 |                             // 這是正確的圓元件 (創建圓)
407 |                             return true;
408 |                         }
409 |                     }
410 |                     
411 |                     // 如果源是平面且目標是立方體,允許連接
412 |                     if (IsPlaneComponent(sourceComponent) && targetComponent.Name.Contains("Box"))
413 |                     {
414 |                         RhinoApp.WriteLine("Connecting plane component to box component");
415 |                         return true;
416 |                     }
417 |                 }
418 |             }
419 | 
420 |             // 默認允許連接,讓 Grasshopper 在運行時決定是否相容
421 |             return true;
422 |         }
423 | 
424 |         /// <summary>
425 |         /// 檢查參數是否為數字類型
426 |         /// </summary>
427 |         /// <param name="param">參數</param>
428 |         /// <returns>是否為數字類型</returns>
429 |         private static bool IsNumericType(IGH_Param param)
430 |         {
431 |             return param is Param_Integer || 
432 |                    param is Param_Number || 
433 |                    param is Param_Time;
434 |         }
435 | 
436 |         /// <summary>
437 |         /// 查找參數所屬的組件
438 |         /// </summary>
439 |         /// <param name="doc">文檔</param>
440 |         /// <param name="param">參數</param>
441 |         /// <returns>參數所屬的組件</returns>
442 |         private static IGH_Component FindComponentForParam(GH_Document doc, IGH_Param param)
443 |         {
444 |             foreach (var obj in doc.Objects)
445 |             {
446 |                 if (obj is IGH_Component comp)
447 |                 {
448 |                     // 檢查輸出參數
449 |                     foreach (var outParam in comp.Params.Output)
450 |                     {
451 |                         if (outParam.InstanceGuid == param.InstanceGuid)
452 |                         {
453 |                             return comp;
454 |                         }
455 |                     }
456 |                     
457 |                     // 檢查輸入參數
458 |                     foreach (var inParam in comp.Params.Input)
459 |                     {
460 |                         if (inParam.InstanceGuid == param.InstanceGuid)
461 |                         {
462 |                             return comp;
463 |                         }
464 |                     }
465 |                 }
466 |             }
467 |             
468 |             return null;
469 |         }
470 |         
471 |         /// <summary>
472 |         /// 檢查組件是否為平面組件
473 |         /// </summary>
474 |         /// <param name="component">組件</param>
475 |         /// <returns>是否為平面組件</returns>
476 |         private static bool IsPlaneComponent(IGH_Component component)
477 |         {
478 |             if (component == null)
479 |                 return false;
480 |                 
481 |             // 檢查組件名稱
482 |             string name = component.Name.ToLowerInvariant();
483 |             if (name.Contains("plane"))
484 |                 return true;
485 |                 
486 |             // 檢查 XY Plane 組件的 GUID
487 |             if (component.ComponentGuid.ToString() == "896a1e5e-c2ac-4996-a6d8-5b61157080b3")
488 |                 return true;
489 |                 
490 |             return false;
491 |         }
492 |         
493 |         /// <summary>
494 |         /// 檢查組件是否需要平面輸入
495 |         /// </summary>
496 |         /// <param name="component">組件</param>
497 |         /// <returns>是否需要平面輸入</returns>
498 |         private static bool RequiresPlaneInput(IGH_Component component)
499 |         {
500 |             if (component == null)
501 |                 return false;
502 |                 
503 |             // 檢查組件是否有名為 "Plane" 或 "Base" 的輸入參數
504 |             foreach (var param in component.Params.Input)
505 |             {
506 |                 string paramName = param.Name.ToLowerInvariant();
507 |                 if (paramName.Contains("plane") || paramName.Contains("base"))
508 |                     return true;
509 |             }
510 |             
511 |             // 檢查特定類型的組件
512 |             string name = component.Name.ToLowerInvariant();
513 |             return name.Contains("box") || 
514 |                    name.Contains("rectangle") || 
515 |                    name.Contains("circle") || 
516 |                    name.Contains("cylinder") || 
517 |                    name.Contains("cone");
518 |         }
519 |     }
520 | 
521 |     public class ConnectionPairing
522 |     {
523 |         public Connection Source { get; set; }
524 |         public Connection Target { get; set; }
525 | 
526 |         public bool IsValid()
527 |         {
528 |             return Source != null && Target != null;
529 |         }
530 |     }
531 | 
532 |     public class Connection
533 |     {
534 |         public string ComponentId { get; set; }
535 |         public string ParameterName { get; set; }
536 |         public int? ParameterIndex { get; set; }
537 |     }
538 | }
539 | 
```

--------------------------------------------------------------------------------
/GH_MCP/GH_MCP/Commands/ComponentCommandHandler.cs:
--------------------------------------------------------------------------------

```csharp
  1 | using System;
  2 | using System.Collections.Generic;
  3 | using GrasshopperMCP.Models;
  4 | using Grasshopper.Kernel;
  5 | using Grasshopper.Kernel.Parameters;
  6 | using Grasshopper.Kernel.Special;
  7 | using Rhino;
  8 | using Rhino.Geometry;
  9 | using Grasshopper;
 10 | using System.Linq;
 11 | using Grasshopper.Kernel.Components;
 12 | using System.Threading;
 13 | using GH_MCP.Utils;
 14 | 
 15 | namespace GrasshopperMCP.Commands
 16 | {
 17 |     /// <summary>
 18 |     /// 處理組件相關命令的處理器
 19 |     /// </summary>
 20 |     public static class ComponentCommandHandler
 21 |     {
 22 |         /// <summary>
 23 |         /// 添加組件
 24 |         /// </summary>
 25 |         /// <param name="command">包含組件類型和位置的命令</param>
 26 |         /// <returns>添加的組件信息</returns>
 27 |         public static object AddComponent(Command command)
 28 |         {
 29 |             string type = command.GetParameter<string>("type");
 30 |             double x = command.GetParameter<double>("x");
 31 |             double y = command.GetParameter<double>("y");
 32 |             
 33 |             if (string.IsNullOrEmpty(type))
 34 |             {
 35 |                 throw new ArgumentException("Component type is required");
 36 |             }
 37 |             
 38 |             // 使用模糊匹配獲取標準化的元件名稱
 39 |             string normalizedType = FuzzyMatcher.GetClosestComponentName(type);
 40 |             
 41 |             // 記錄請求信息
 42 |             RhinoApp.WriteLine($"AddComponent request: type={type}, normalized={normalizedType}, x={x}, y={y}");
 43 |             
 44 |             object result = null;
 45 |             Exception exception = null;
 46 |             
 47 |             // 在 UI 線程上執行
 48 |             RhinoApp.InvokeOnUiThread(new Action(() =>
 49 |             {
 50 |                 try
 51 |                 {
 52 |                     // 獲取 Grasshopper 文檔
 53 |                     var doc = Grasshopper.Instances.ActiveCanvas?.Document;
 54 |                     if (doc == null)
 55 |                     {
 56 |                         throw new InvalidOperationException("No active Grasshopper document");
 57 |                     }
 58 |                     
 59 |                     // 創建組件
 60 |                     IGH_DocumentObject component = null;
 61 |                     
 62 |                     // 記錄可用的組件類型(僅在第一次調用時記錄)
 63 |                     bool loggedComponentTypes = false;
 64 |                     if (!loggedComponentTypes)
 65 |                     {
 66 |                         var availableTypes = Grasshopper.Instances.ComponentServer.ObjectProxies
 67 |                             .Select(p => p.Desc.Name)
 68 |                             .OrderBy(n => n)
 69 |                             .ToList();
 70 |                         
 71 |                         RhinoApp.WriteLine($"Available component types: {string.Join(", ", availableTypes.Take(50))}...");
 72 |                         loggedComponentTypes = true;
 73 |                     }
 74 |                     
 75 |                     // 根據類型創建不同的組件
 76 |                     switch (normalizedType.ToLowerInvariant())
 77 |                     {
 78 |                         // 平面元件
 79 |                         case "xy plane":
 80 |                             component = CreateComponentByName("XY Plane");
 81 |                             break;
 82 |                         case "xz plane":
 83 |                             component = CreateComponentByName("XZ Plane");
 84 |                             break;
 85 |                         case "yz plane":
 86 |                             component = CreateComponentByName("YZ Plane");
 87 |                             break;
 88 |                         case "plane 3pt":
 89 |                             component = CreateComponentByName("Plane 3Pt");
 90 |                             break;
 91 |                             
 92 |                         // 基本幾何元件
 93 |                         case "box":
 94 |                             component = CreateComponentByName("Box");
 95 |                             break;
 96 |                         case "sphere":
 97 |                             component = CreateComponentByName("Sphere");
 98 |                             break;
 99 |                         case "cylinder":
100 |                             component = CreateComponentByName("Cylinder");
101 |                             break;
102 |                         case "cone":
103 |                             component = CreateComponentByName("Cone");
104 |                             break;
105 |                         case "circle":
106 |                             component = CreateComponentByName("Circle");
107 |                             break;
108 |                         case "rectangle":
109 |                             component = CreateComponentByName("Rectangle");
110 |                             break;
111 |                         case "line":
112 |                             component = CreateComponentByName("Line");
113 |                             break;
114 |                             
115 |                         // 參數元件
116 |                         case "point":
117 |                         case "pt":
118 |                         case "pointparam":
119 |                         case "param_point":
120 |                             component = new Param_Point();
121 |                             break;
122 |                         case "curve":
123 |                         case "crv":
124 |                         case "curveparam":
125 |                         case "param_curve":
126 |                             component = new Param_Curve();
127 |                             break;
128 |                         case "circleparam":
129 |                         case "param_circle":
130 |                             component = new Param_Circle();
131 |                             break;
132 |                         case "lineparam":
133 |                         case "param_line":
134 |                             component = new Param_Line();
135 |                             break;
136 |                         case "panel":
137 |                         case "gh_panel":
138 |                             component = new GH_Panel();
139 |                             break;
140 |                         case "slider":
141 |                         case "numberslider":
142 |                         case "gh_numberslider":
143 |                             var slider = new GH_NumberSlider();
144 |                             slider.SetInitCode("0.0 < 0.5 < 1.0");
145 |                             component = slider;
146 |                             break;
147 |                         case "number":
148 |                         case "num":
149 |                         case "integer":
150 |                         case "int":
151 |                         case "param_number":
152 |                         case "param_integer":
153 |                             component = new Param_Number();
154 |                             break;
155 |                         case "construct point":
156 |                         case "constructpoint":
157 |                         case "pt xyz":
158 |                         case "xyz":
159 |                             // 嘗試查找構造點組件
160 |                             var pointProxy = Grasshopper.Instances.ComponentServer.ObjectProxies
161 |                                 .FirstOrDefault(p => p.Desc.Name.Equals("Construct Point", StringComparison.OrdinalIgnoreCase));
162 |                             if (pointProxy != null)
163 |                             {
164 |                                 component = pointProxy.CreateInstance();
165 |                             }
166 |                             else
167 |                             {
168 |                                 throw new ArgumentException("Construct Point component not found");
169 |                             }
170 |                             break;
171 |                         default:
172 |                             // 嘗試通過 Guid 查找組件
173 |                             Guid componentGuid;
174 |                             if (Guid.TryParse(type, out componentGuid))
175 |                             {
176 |                                 component = Grasshopper.Instances.ComponentServer.EmitObject(componentGuid);
177 |                                 RhinoApp.WriteLine($"Attempting to create component by GUID: {componentGuid}");
178 |                             }
179 |                             
180 |                             if (component == null)
181 |                             {
182 |                                 // 嘗試通過名稱查找組件(不區分大小寫)
183 |                                 RhinoApp.WriteLine($"Attempting to find component by name: {type}");
184 |                                 var obj = Grasshopper.Instances.ComponentServer.ObjectProxies
185 |                                     .FirstOrDefault(p => p.Desc.Name.Equals(type, StringComparison.OrdinalIgnoreCase));
186 |                                     
187 |                                 if (obj != null)
188 |                                 {
189 |                                     RhinoApp.WriteLine($"Found component: {obj.Desc.Name}");
190 |                                     component = obj.CreateInstance();
191 |                                 }
192 |                                 else
193 |                                 {
194 |                                     // 嘗試通過部分名稱匹配
195 |                                     RhinoApp.WriteLine($"Attempting to find component by partial name match: {type}");
196 |                                     obj = Grasshopper.Instances.ComponentServer.ObjectProxies
197 |                                         .FirstOrDefault(p => p.Desc.Name.IndexOf(type, StringComparison.OrdinalIgnoreCase) >= 0);
198 |                                         
199 |                                     if (obj != null)
200 |                                     {
201 |                                         RhinoApp.WriteLine($"Found component by partial match: {obj.Desc.Name}");
202 |                                         component = obj.CreateInstance();
203 |                                     }
204 |                                 }
205 |                             }
206 |                             
207 |                             if (component == null)
208 |                             {
209 |                                 // 記錄一些可能的組件類型
210 |                                 var possibleMatches = Grasshopper.Instances.ComponentServer.ObjectProxies
211 |                                     .Where(p => p.Desc.Name.IndexOf(type, StringComparison.OrdinalIgnoreCase) >= 0)
212 |                                     .Select(p => p.Desc.Name)
213 |                                     .Take(10)
214 |                                     .ToList();
215 |                                 
216 |                                 var errorMessage = $"Unknown component type: {type}";
217 |                                 if (possibleMatches.Any())
218 |                                 {
219 |                                     errorMessage += $". Possible matches: {string.Join(", ", possibleMatches)}";
220 |                                 }
221 |                                 
222 |                                 throw new ArgumentException(errorMessage);
223 |                             }
224 |                             break;
225 |                     }
226 |                     
227 |                     // 設置組件位置
228 |                     if (component != null)
229 |                     {
230 |                         // 確保組件有有效的屬性對象
231 |                         if (component.Attributes == null)
232 |                         {
233 |                             RhinoApp.WriteLine("Component attributes are null, creating new attributes");
234 |                             component.CreateAttributes();
235 |                         }
236 |                         
237 |                         // 設置位置
238 |                         component.Attributes.Pivot = new System.Drawing.PointF((float)x, (float)y);
239 |                         
240 |                         // 添加到文檔
241 |                         doc.AddObject(component, false);
242 |                         
243 |                         // 刷新畫布
244 |                         doc.NewSolution(false);
245 |                         
246 |                         // 返回組件信息
247 |                         result = new
248 |                         {
249 |                             id = component.InstanceGuid.ToString(),
250 |                             type = component.GetType().Name,
251 |                             name = component.NickName,
252 |                             x = component.Attributes.Pivot.X,
253 |                             y = component.Attributes.Pivot.Y
254 |                         };
255 |                     }
256 |                     else
257 |                     {
258 |                         throw new InvalidOperationException("Failed to create component");
259 |                     }
260 |                 }
261 |                 catch (Exception ex)
262 |                 {
263 |                     exception = ex;
264 |                     RhinoApp.WriteLine($"Error in AddComponent: {ex.Message}");
265 |                 }
266 |             }));
267 |             
268 |             // 等待 UI 線程操作完成
269 |             while (result == null && exception == null)
270 |             {
271 |                 Thread.Sleep(10);
272 |             }
273 |             
274 |             // 如果有異常,拋出
275 |             if (exception != null)
276 |             {
277 |                 throw exception;
278 |             }
279 |             
280 |             return result;
281 |         }
282 |         
283 |         /// <summary>
284 |         /// 連接組件
285 |         /// </summary>
286 |         /// <param name="command">包含源和目標組件信息的命令</param>
287 |         /// <returns>連接信息</returns>
288 |         public static object ConnectComponents(Command command)
289 |         {
290 |             var fromData = command.GetParameter<Dictionary<string, object>>("from");
291 |             var toData = command.GetParameter<Dictionary<string, object>>("to");
292 |             
293 |             if (fromData == null || toData == null)
294 |             {
295 |                 throw new ArgumentException("Source and target component information are required");
296 |             }
297 |             
298 |             object result = null;
299 |             Exception exception = null;
300 |             
301 |             // 在 UI 線程上執行
302 |             RhinoApp.InvokeOnUiThread(new Action(() =>
303 |             {
304 |                 try
305 |                 {
306 |                     // 獲取 Grasshopper 文檔
307 |                     var doc = Grasshopper.Instances.ActiveCanvas?.Document;
308 |                     if (doc == null)
309 |                     {
310 |                         throw new InvalidOperationException("No active Grasshopper document");
311 |                     }
312 |                     
313 |                     // 解析源組件信息
314 |                     string fromIdStr = fromData["id"].ToString();
315 |                     string fromParamName = fromData["parameterName"].ToString();
316 |                     
317 |                     // 解析目標組件信息
318 |                     string toIdStr = toData["id"].ToString();
319 |                     string toParamName = toData["parameterName"].ToString();
320 |                     
321 |                     // 將字符串 ID 轉換為 Guid
322 |                     Guid fromId, toId;
323 |                     if (!Guid.TryParse(fromIdStr, out fromId) || !Guid.TryParse(toIdStr, out toId))
324 |                     {
325 |                         throw new ArgumentException("Invalid component ID format");
326 |                     }
327 |                     
328 |                     // 查找源和目標組件
329 |                     IGH_Component fromComponent = doc.FindComponent(fromId) as IGH_Component;
330 |                     IGH_Component toComponent = doc.FindComponent(toId) as IGH_Component;
331 |                     
332 |                     if (fromComponent == null || toComponent == null)
333 |                     {
334 |                         throw new ArgumentException("Source or target component not found");
335 |                     }
336 |                     
337 |                     // 查找源輸出參數
338 |                     IGH_Param fromParam = null;
339 |                     foreach (var param in fromComponent.Params.Output)
340 |                     {
341 |                         if (param.Name.Equals(fromParamName, StringComparison.OrdinalIgnoreCase))
342 |                         {
343 |                             fromParam = param;
344 |                             break;
345 |                         }
346 |                     }
347 |                     
348 |                     // 查找目標輸入參數
349 |                     IGH_Param toParam = null;
350 |                     foreach (var param in toComponent.Params.Input)
351 |                     {
352 |                         if (param.Name.Equals(toParamName, StringComparison.OrdinalIgnoreCase))
353 |                         {
354 |                             toParam = param;
355 |                             break;
356 |                         }
357 |                     }
358 |                     
359 |                     if (fromParam == null || toParam == null)
360 |                     {
361 |                         throw new ArgumentException("Source or target parameter not found");
362 |                     }
363 |                     
364 |                     // 連接參數
365 |                     toParam.AddSource(fromParam);
366 |                     
367 |                     // 刷新畫布
368 |                     doc.NewSolution(false);
369 |                     
370 |                     // 返回連接信息
371 |                     result = new
372 |                     {
373 |                         from = new
374 |                         {
375 |                             id = fromComponent.InstanceGuid.ToString(),
376 |                             name = fromComponent.NickName,
377 |                             parameter = fromParam.Name
378 |                         },
379 |                         to = new
380 |                         {
381 |                             id = toComponent.InstanceGuid.ToString(),
382 |                             name = toComponent.NickName,
383 |                             parameter = toParam.Name
384 |                         }
385 |                     };
386 |                 }
387 |                 catch (Exception ex)
388 |                 {
389 |                     exception = ex;
390 |                     RhinoApp.WriteLine($"Error in ConnectComponents: {ex.Message}");
391 |                 }
392 |             }));
393 |             
394 |             // 等待 UI 線程操作完成
395 |             while (result == null && exception == null)
396 |             {
397 |                 Thread.Sleep(10);
398 |             }
399 |             
400 |             // 如果有異常,拋出
401 |             if (exception != null)
402 |             {
403 |                 throw exception;
404 |             }
405 |             
406 |             return result;
407 |         }
408 |         
409 |         /// <summary>
410 |         /// 設置組件值
411 |         /// </summary>
412 |         /// <param name="command">包含組件 ID 和值的命令</param>
413 |         /// <returns>操作結果</returns>
414 |         public static object SetComponentValue(Command command)
415 |         {
416 |             string idStr = command.GetParameter<string>("id");
417 |             string value = command.GetParameter<string>("value");
418 |             
419 |             if (string.IsNullOrEmpty(idStr))
420 |             {
421 |                 throw new ArgumentException("Component ID is required");
422 |             }
423 |             
424 |             object result = null;
425 |             Exception exception = null;
426 |             
427 |             // 在 UI 線程上執行
428 |             RhinoApp.InvokeOnUiThread(new Action(() =>
429 |             {
430 |                 try
431 |                 {
432 |                     // 獲取 Grasshopper 文檔
433 |                     var doc = Grasshopper.Instances.ActiveCanvas?.Document;
434 |                     if (doc == null)
435 |                     {
436 |                         throw new InvalidOperationException("No active Grasshopper document");
437 |                     }
438 |                     
439 |                     // 將字符串 ID 轉換為 Guid
440 |                     Guid id;
441 |                     if (!Guid.TryParse(idStr, out id))
442 |                     {
443 |                         throw new ArgumentException("Invalid component ID format");
444 |                     }
445 |                     
446 |                     // 查找組件
447 |                     IGH_DocumentObject component = doc.FindObject(id, true);
448 |                     if (component == null)
449 |                     {
450 |                         throw new ArgumentException($"Component with ID {idStr} not found");
451 |                     }
452 |                     
453 |                     // 根據組件類型設置值
454 |                     if (component is GH_Panel panel)
455 |                     {
456 |                         panel.UserText = value;
457 |                     }
458 |                     else if (component is GH_NumberSlider slider)
459 |                     {
460 |                         double doubleValue;
461 |                         if (double.TryParse(value, out doubleValue))
462 |                         {
463 |                             slider.SetSliderValue((decimal)doubleValue);
464 |                         }
465 |                         else
466 |                         {
467 |                             throw new ArgumentException("Invalid slider value format");
468 |                         }
469 |                     }
470 |                     else if (component is IGH_Component ghComponent)
471 |                     {
472 |                         // 嘗試設置第一個輸入參數的值
473 |                         if (ghComponent.Params.Input.Count > 0)
474 |                         {
475 |                             var param = ghComponent.Params.Input[0];
476 |                             if (param is Param_String stringParam)
477 |                             {
478 |                                 stringParam.PersistentData.Clear();
479 |                                 stringParam.PersistentData.Append(new Grasshopper.Kernel.Types.GH_String(value));
480 |                             }
481 |                             else if (param is Param_Number numberParam)
482 |                             {
483 |                                 double doubleValue;
484 |                                 if (double.TryParse(value, out doubleValue))
485 |                                 {
486 |                                     numberParam.PersistentData.Clear();
487 |                                     numberParam.PersistentData.Append(new Grasshopper.Kernel.Types.GH_Number(doubleValue));
488 |                                 }
489 |                                 else
490 |                                 {
491 |                                     throw new ArgumentException("Invalid number value format");
492 |                                 }
493 |                             }
494 |                             else
495 |                             {
496 |                                 throw new ArgumentException($"Cannot set value for parameter type {param.GetType().Name}");
497 |                             }
498 |                         }
499 |                         else
500 |                         {
501 |                             throw new ArgumentException("Component has no input parameters");
502 |                         }
503 |                     }
504 |                     else
505 |                     {
506 |                         throw new ArgumentException($"Cannot set value for component type {component.GetType().Name}");
507 |                     }
508 |                     
509 |                     // 刷新畫布
510 |                     doc.NewSolution(false);
511 |                     
512 |                     // 返回操作結果
513 |                     result = new
514 |                     {
515 |                         id = component.InstanceGuid.ToString(),
516 |                         type = component.GetType().Name,
517 |                         value = value
518 |                     };
519 |                 }
520 |                 catch (Exception ex)
521 |                 {
522 |                     exception = ex;
523 |                     RhinoApp.WriteLine($"Error in SetComponentValue: {ex.Message}");
524 |                 }
525 |             }));
526 |             
527 |             // 等待 UI 線程操作完成
528 |             while (result == null && exception == null)
529 |             {
530 |                 Thread.Sleep(10);
531 |             }
532 |             
533 |             // 如果有異常,拋出
534 |             if (exception != null)
535 |             {
536 |                 throw exception;
537 |             }
538 |             
539 |             return result;
540 |         }
541 |         
542 |         /// <summary>
543 |         /// 獲取組件信息
544 |         /// </summary>
545 |         /// <param name="command">包含組件 ID 的命令</param>
546 |         /// <returns>組件信息</returns>
547 |         public static object GetComponentInfo(Command command)
548 |         {
549 |             string idStr = command.GetParameter<string>("id");
550 |             
551 |             if (string.IsNullOrEmpty(idStr))
552 |             {
553 |                 throw new ArgumentException("Component ID is required");
554 |             }
555 |             
556 |             object result = null;
557 |             Exception exception = null;
558 |             
559 |             // 在 UI 線程上執行
560 |             RhinoApp.InvokeOnUiThread(new Action(() =>
561 |             {
562 |                 try
563 |                 {
564 |                     // 獲取 Grasshopper 文檔
565 |                     var doc = Grasshopper.Instances.ActiveCanvas?.Document;
566 |                     if (doc == null)
567 |                     {
568 |                         throw new InvalidOperationException("No active Grasshopper document");
569 |                     }
570 |                     
571 |                     // 將字符串 ID 轉換為 Guid
572 |                     Guid id;
573 |                     if (!Guid.TryParse(idStr, out id))
574 |                     {
575 |                         throw new ArgumentException("Invalid component ID format");
576 |                     }
577 |                     
578 |                     // 查找組件
579 |                     IGH_DocumentObject component = doc.FindObject(id, true);
580 |                     if (component == null)
581 |                     {
582 |                         throw new ArgumentException($"Component with ID {idStr} not found");
583 |                     }
584 |                     
585 |                     // 收集組件信息
586 |                     var componentInfo = new Dictionary<string, object>
587 |                     {
588 |                         { "id", component.InstanceGuid.ToString() },
589 |                         { "type", component.GetType().Name },
590 |                         { "name", component.NickName },
591 |                         { "description", component.Description }
592 |                     };
593 |                     
594 |                     // 如果是 IGH_Component,收集輸入和輸出參數信息
595 |                     if (component is IGH_Component ghComponent)
596 |                     {
597 |                         var inputs = new List<Dictionary<string, object>>();
598 |                         foreach (var param in ghComponent.Params.Input)
599 |                         {
600 |                             inputs.Add(new Dictionary<string, object>
601 |                             {
602 |                                 { "name", param.Name },
603 |                                 { "nickname", param.NickName },
604 |                                 { "description", param.Description },
605 |                                 { "type", param.GetType().Name },
606 |                                 { "dataType", param.TypeName }
607 |                             });
608 |                         }
609 |                         componentInfo["inputs"] = inputs;
610 |                         
611 |                         var outputs = new List<Dictionary<string, object>>();
612 |                         foreach (var param in ghComponent.Params.Output)
613 |                         {
614 |                             outputs.Add(new Dictionary<string, object>
615 |                             {
616 |                                 { "name", param.Name },
617 |                                 { "nickname", param.NickName },
618 |                                 { "description", param.Description },
619 |                                 { "type", param.GetType().Name },
620 |                                 { "dataType", param.TypeName }
621 |                             });
622 |                         }
623 |                         componentInfo["outputs"] = outputs;
624 |                     }
625 |                     
626 |                     // 如果是 GH_Panel,獲取其文本值
627 |                     if (component is GH_Panel panel)
628 |                     {
629 |                         componentInfo["value"] = panel.UserText;
630 |                     }
631 |                     
632 |                     // 如果是 GH_NumberSlider,獲取其值和範圍
633 |                     if (component is GH_NumberSlider slider)
634 |                     {
635 |                         componentInfo["value"] = (double)slider.CurrentValue;
636 |                         componentInfo["minimum"] = (double)slider.Slider.Minimum;
637 |                         componentInfo["maximum"] = (double)slider.Slider.Maximum;
638 |                     }
639 |                     
640 |                     result = componentInfo;
641 |                 }
642 |                 catch (Exception ex)
643 |                 {
644 |                     exception = ex;
645 |                     RhinoApp.WriteLine($"Error in GetComponentInfo: {ex.Message}");
646 |                 }
647 |             }));
648 |             
649 |             // 等待 UI 線程操作完成
650 |             while (result == null && exception == null)
651 |             {
652 |                 Thread.Sleep(10);
653 |             }
654 |             
655 |             // 如果有異常,拋出
656 |             if (exception != null)
657 |             {
658 |                 throw exception;
659 |             }
660 |             
661 |             return result;
662 |         }
663 |         
664 |         private static IGH_DocumentObject CreateComponentByName(string name)
665 |         {
666 |             var obj = Grasshopper.Instances.ComponentServer.ObjectProxies
667 |                 .FirstOrDefault(p => p.Desc.Name.Equals(name, StringComparison.OrdinalIgnoreCase));
668 |                 
669 |             if (obj != null)
670 |             {
671 |                 return obj.CreateInstance();
672 |             }
673 |             else
674 |             {
675 |                 throw new ArgumentException($"Component with name {name} not found");
676 |             }
677 |         }
678 |     }
679 | }
680 | 
```

--------------------------------------------------------------------------------
/grasshopper_mcp/bridge.py:
--------------------------------------------------------------------------------

```python
   1 | import socket
   2 | import json
   3 | import os
   4 | import sys
   5 | import traceback
   6 | from typing import Dict, Any, Optional, List
   7 | 
   8 | # 使用 MCP 服務器
   9 | from mcp.server.fastmcp import FastMCP
  10 | 
  11 | # 設置 Grasshopper MCP 連接參數
  12 | GRASSHOPPER_HOST = "localhost"
  13 | GRASSHOPPER_PORT = 8080  # 默認端口,可以根據需要修改
  14 | 
  15 | # 創建 MCP 服務器
  16 | server = FastMCP("Grasshopper Bridge")
  17 | 
  18 | def send_to_grasshopper(command_type: str, params: Optional[Dict[str, Any]] = None) -> Dict[str, Any]:
  19 |     """向 Grasshopper MCP 發送命令"""
  20 |     if params is None:
  21 |         params = {}
  22 |     
  23 |     # 創建命令
  24 |     command = {
  25 |         "type": command_type,
  26 |         "parameters": params
  27 |     }
  28 |     
  29 |     try:
  30 |         print(f"Sending command to Grasshopper: {command_type} with params: {params}", file=sys.stderr)
  31 |         
  32 |         # 連接到 Grasshopper MCP
  33 |         client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  34 |         client.connect((GRASSHOPPER_HOST, GRASSHOPPER_PORT))
  35 |         
  36 |         # 發送命令
  37 |         command_json = json.dumps(command)
  38 |         client.sendall((command_json + "\n").encode("utf-8"))
  39 |         print(f"Command sent: {command_json}", file=sys.stderr)
  40 |         
  41 |         # 接收響應
  42 |         response_data = b""
  43 |         while True:
  44 |             chunk = client.recv(4096)
  45 |             if not chunk:
  46 |                 break
  47 |             response_data += chunk
  48 |             if response_data.endswith(b"\n"):
  49 |                 break
  50 |         
  51 |         # 處理可能的 BOM
  52 |         response_str = response_data.decode("utf-8-sig").strip()
  53 |         print(f"Response received: {response_str}", file=sys.stderr)
  54 |         
  55 |         # 解析 JSON 響應
  56 |         response = json.loads(response_str)
  57 |         client.close()
  58 |         return response
  59 |     except Exception as e:
  60 |         print(f"Error communicating with Grasshopper: {str(e)}", file=sys.stderr)
  61 |         traceback.print_exc(file=sys.stderr)
  62 |         return {
  63 |             "success": False,
  64 |             "error": f"Error communicating with Grasshopper: {str(e)}"
  65 |         }
  66 | 
  67 | # 註冊 MCP 工具
  68 | @server.tool("add_component")
  69 | def add_component(component_type: str, x: float, y: float):
  70 |     """
  71 |     Add a component to the Grasshopper canvas
  72 |     
  73 |     Args:
  74 |         component_type: Component type (point, curve, circle, line, panel, slider)
  75 |         x: X coordinate on the canvas
  76 |         y: Y coordinate on the canvas
  77 |     
  78 |     Returns:
  79 |         Result of adding the component
  80 |     """
  81 |     # 處理常見的組件名稱混淆問題
  82 |     component_mapping = {
  83 |         # Number Slider 的各種可能輸入方式
  84 |         "number slider": "Number Slider",
  85 |         "numeric slider": "Number Slider",
  86 |         "num slider": "Number Slider",
  87 |         "slider": "Number Slider",  # 當只提到 slider 且上下文是數值時,預設為 Number Slider
  88 |         
  89 |         # 其他組件的標準化名稱
  90 |         "md slider": "MD Slider",
  91 |         "multidimensional slider": "MD Slider",
  92 |         "multi-dimensional slider": "MD Slider",
  93 |         "graph mapper": "Graph Mapper",
  94 |         
  95 |         # 數學運算組件
  96 |         "add": "Addition",
  97 |         "addition": "Addition",
  98 |         "plus": "Addition",
  99 |         "sum": "Addition",
 100 |         "subtract": "Subtraction",
 101 |         "subtraction": "Subtraction",
 102 |         "minus": "Subtraction",
 103 |         "difference": "Subtraction",
 104 |         "multiply": "Multiplication",
 105 |         "multiplication": "Multiplication",
 106 |         "times": "Multiplication",
 107 |         "product": "Multiplication",
 108 |         "divide": "Division",
 109 |         "division": "Division",
 110 |         
 111 |         # 輸出組件
 112 |         "panel": "Panel",
 113 |         "text panel": "Panel",
 114 |         "output panel": "Panel",
 115 |         "display": "Panel"
 116 |     }
 117 |     
 118 |     # 檢查並修正組件類型
 119 |     normalized_type = component_type.lower()
 120 |     if normalized_type in component_mapping:
 121 |         component_type = component_mapping[normalized_type]
 122 |         print(f"Component type normalized from '{normalized_type}' to '{component_mapping[normalized_type]}'", file=sys.stderr)
 123 |     
 124 |     params = {
 125 |         "type": component_type,
 126 |         "x": x,
 127 |         "y": y
 128 |     }
 129 |     
 130 |     return send_to_grasshopper("add_component", params)
 131 | 
 132 | @server.tool("clear_document")
 133 | def clear_document():
 134 |     """Clear the Grasshopper document"""
 135 |     return send_to_grasshopper("clear_document")
 136 | 
 137 | @server.tool("save_document")
 138 | def save_document(path: str):
 139 |     """
 140 |     Save the Grasshopper document
 141 |     
 142 |     Args:
 143 |         path: Save path
 144 |     
 145 |     Returns:
 146 |         Result of the save operation
 147 |     """
 148 |     params = {
 149 |         "path": path
 150 |     }
 151 |     
 152 |     return send_to_grasshopper("save_document", params)
 153 | 
 154 | @server.tool("load_document")
 155 | def load_document(path: str):
 156 |     """
 157 |     Load a Grasshopper document
 158 |     
 159 |     Args:
 160 |         path: Document path
 161 |     
 162 |     Returns:
 163 |         Result of the load operation
 164 |     """
 165 |     params = {
 166 |         "path": path
 167 |     }
 168 |     
 169 |     return send_to_grasshopper("load_document", params)
 170 | 
 171 | @server.tool("get_document_info")
 172 | def get_document_info():
 173 |     """Get information about the Grasshopper document"""
 174 |     return send_to_grasshopper("get_document_info")
 175 | 
 176 | @server.tool("connect_components")
 177 | def connect_components(source_id: str, target_id: str, source_param: str = None, target_param: str = None, source_param_index: int = None, target_param_index: int = None):
 178 |     """
 179 |     Connect two components in the Grasshopper canvas
 180 |     
 181 |     Args:
 182 |         source_id: ID of the source component (output)
 183 |         target_id: ID of the target component (input)
 184 |         source_param: Name of the source parameter (optional)
 185 |         target_param: Name of the target parameter (optional)
 186 |         source_param_index: Index of the source parameter (optional, used if source_param is not provided)
 187 |         target_param_index: Index of the target parameter (optional, used if target_param is not provided)
 188 |     
 189 |     Returns:
 190 |         Result of connecting the components
 191 |     """
 192 |     # 獲取目標組件的信息,檢查是否已有連接
 193 |     target_info = send_to_grasshopper("get_component_info", {"componentId": target_id})
 194 |     
 195 |     # 檢查組件類型,如果是需要多個輸入的組件(如 Addition, Subtraction 等),智能分配輸入
 196 |     if target_info and "result" in target_info and "type" in target_info["result"]:
 197 |         component_type = target_info["result"]["type"]
 198 |         
 199 |         # 獲取現有連接
 200 |         connections = send_to_grasshopper("get_connections")
 201 |         existing_connections = []
 202 |         
 203 |         if connections and "result" in connections:
 204 |             for conn in connections["result"]:
 205 |                 if conn.get("targetId") == target_id:
 206 |                     existing_connections.append(conn)
 207 |         
 208 |         # 對於特定需要多個輸入的組件,自動選擇正確的輸入端口
 209 |         if component_type in ["Addition", "Subtraction", "Multiplication", "Division", "Math"]:
 210 |             # 如果沒有指定目標參數,且已有連接到第一個輸入,則自動連接到第二個輸入
 211 |             if target_param is None and target_param_index is None:
 212 |                 # 檢查第一個輸入是否已被佔用
 213 |                 first_input_occupied = False
 214 |                 for conn in existing_connections:
 215 |                     if conn.get("targetParam") == "A" or conn.get("targetParamIndex") == 0:
 216 |                         first_input_occupied = True
 217 |                         break
 218 |                 
 219 |                 # 如果第一個輸入已被佔用,則連接到第二個輸入
 220 |                 if first_input_occupied:
 221 |                     target_param = "B"  # 第二個輸入通常命名為 B
 222 |                 else:
 223 |                     target_param = "A"  # 否則連接到第一個輸入
 224 |     
 225 |     params = {
 226 |         "sourceId": source_id,
 227 |         "targetId": target_id
 228 |     }
 229 |     
 230 |     if source_param is not None:
 231 |         params["sourceParam"] = source_param
 232 |     elif source_param_index is not None:
 233 |         params["sourceParamIndex"] = source_param_index
 234 |         
 235 |     if target_param is not None:
 236 |         params["targetParam"] = target_param
 237 |     elif target_param_index is not None:
 238 |         params["targetParamIndex"] = target_param_index
 239 |     
 240 |     return send_to_grasshopper("connect_components", params)
 241 | 
 242 | @server.tool("create_pattern")
 243 | def create_pattern(description: str):
 244 |     """
 245 |     Create a pattern of components based on a high-level description
 246 |     
 247 |     Args:
 248 |         description: High-level description of what to create (e.g., '3D voronoi cube')
 249 |     
 250 |     Returns:
 251 |         Result of creating the pattern
 252 |     """
 253 |     params = {
 254 |         "description": description
 255 |     }
 256 |     
 257 |     return send_to_grasshopper("create_pattern", params)
 258 | 
 259 | @server.tool("get_available_patterns")
 260 | def get_available_patterns(query: str):
 261 |     """
 262 |     Get a list of available patterns that match a query
 263 |     
 264 |     Args:
 265 |         query: Query to search for patterns
 266 |     
 267 |     Returns:
 268 |         List of available patterns
 269 |     """
 270 |     params = {
 271 |         "query": query
 272 |     }
 273 |     
 274 |     return send_to_grasshopper("get_available_patterns", params)
 275 | 
 276 | @server.tool("get_component_info")
 277 | def get_component_info(component_id: str):
 278 |     """
 279 |     Get detailed information about a specific component
 280 |     
 281 |     Args:
 282 |         component_id: ID of the component to get information about
 283 |     
 284 |     Returns:
 285 |         Detailed information about the component, including inputs, outputs, and current values
 286 |     """
 287 |     params = {
 288 |         "componentId": component_id
 289 |     }
 290 |     
 291 |     result = send_to_grasshopper("get_component_info", params)
 292 |     
 293 |     # 增強返回結果,添加更多參數信息
 294 |     if result and "result" in result:
 295 |         component_data = result["result"]
 296 |         
 297 |         # 獲取組件類型
 298 |         if "type" in component_data:
 299 |             component_type = component_data["type"]
 300 |             
 301 |             # 查詢組件庫,獲取該類型組件的詳細參數信息
 302 |             component_library = get_component_library()
 303 |             if "components" in component_library:
 304 |                 for lib_component in component_library["components"]:
 305 |                     if lib_component.get("name") == component_type or lib_component.get("fullName") == component_type:
 306 |                         # 將組件庫中的參數信息合併到返回結果中
 307 |                         if "settings" in lib_component:
 308 |                             component_data["availableSettings"] = lib_component["settings"]
 309 |                         if "inputs" in lib_component:
 310 |                             component_data["inputDetails"] = lib_component["inputs"]
 311 |                         if "outputs" in lib_component:
 312 |                             component_data["outputDetails"] = lib_component["outputs"]
 313 |                         if "usage_examples" in lib_component:
 314 |                             component_data["usageExamples"] = lib_component["usage_examples"]
 315 |                         if "common_issues" in lib_component:
 316 |                             component_data["commonIssues"] = lib_component["common_issues"]
 317 |                         break
 318 |             
 319 |             # 特殊處理某些組件類型
 320 |             if component_type == "Number Slider":
 321 |                 # 嘗試從組件數據中獲取當前滑桿的實際設置
 322 |                 if "currentSettings" not in component_data:
 323 |                     component_data["currentSettings"] = {
 324 |                         "min": component_data.get("min", 0),
 325 |                         "max": component_data.get("max", 10),
 326 |                         "value": component_data.get("value", 5),
 327 |                         "rounding": component_data.get("rounding", 0.1),
 328 |                         "type": component_data.get("type", "float")
 329 |                     }
 330 |             
 331 |             # 添加組件的連接信息
 332 |             connections = send_to_grasshopper("get_connections")
 333 |             if connections and "result" in connections:
 334 |                 # 查找與該組件相關的所有連接
 335 |                 related_connections = []
 336 |                 for conn in connections["result"]:
 337 |                     if conn.get("sourceId") == component_id or conn.get("targetId") == component_id:
 338 |                         related_connections.append(conn)
 339 |                 
 340 |                 if related_connections:
 341 |                     component_data["connections"] = related_connections
 342 |     
 343 |     return result
 344 | 
 345 | @server.tool("get_all_components")
 346 | def get_all_components():
 347 |     """
 348 |     Get a list of all components in the current document
 349 |     
 350 |     Returns:
 351 |         List of all components in the document with their IDs, types, and positions
 352 |     """
 353 |     result = send_to_grasshopper("get_all_components")
 354 |     
 355 |     # 增強返回結果,為每個組件添加更多參數信息
 356 |     if result and "result" in result:
 357 |         components = result["result"]
 358 |         component_library = get_component_library()
 359 |         
 360 |         # 獲取所有連接信息
 361 |         connections = send_to_grasshopper("get_connections")
 362 |         connections_data = connections.get("result", []) if connections else []
 363 |         
 364 |         # 為每個組件添加詳細信息
 365 |         for component in components:
 366 |             if "id" in component and "type" in component:
 367 |                 component_id = component["id"]
 368 |                 component_type = component["type"]
 369 |                 
 370 |                 # 添加組件的詳細參數信息
 371 |                 if "components" in component_library:
 372 |                     for lib_component in component_library["components"]:
 373 |                         if lib_component.get("name") == component_type or lib_component.get("fullName") == component_type:
 374 |                             # 將組件庫中的參數信息合併到組件數據中
 375 |                             if "settings" in lib_component:
 376 |                                 component["availableSettings"] = lib_component["settings"]
 377 |                             if "inputs" in lib_component:
 378 |                                 component["inputDetails"] = lib_component["inputs"]
 379 |                             if "outputs" in lib_component:
 380 |                                 component["outputDetails"] = lib_component["outputs"]
 381 |                             break
 382 |                 
 383 |                 # 添加組件的連接信息
 384 |                 related_connections = []
 385 |                 for conn in connections_data:
 386 |                     if conn.get("sourceId") == component_id or conn.get("targetId") == component_id:
 387 |                         related_connections.append(conn)
 388 |                 
 389 |                 if related_connections:
 390 |                     component["connections"] = related_connections
 391 |                 
 392 |                 # 特殊處理某些組件類型
 393 |                 if component_type == "Number Slider":
 394 |                     # 嘗試獲取滑桿的當前設置
 395 |                     component_info = send_to_grasshopper("get_component_info", {"componentId": component_id})
 396 |                     if component_info and "result" in component_info:
 397 |                         info_data = component_info["result"]
 398 |                         component["currentSettings"] = {
 399 |                             "min": info_data.get("min", 0),
 400 |                             "max": info_data.get("max", 10),
 401 |                             "value": info_data.get("value", 5),
 402 |                             "rounding": info_data.get("rounding", 0.1)
 403 |                         }
 404 |     
 405 |     return result
 406 | 
 407 | @server.tool("get_connections")
 408 | def get_connections():
 409 |     """
 410 |     Get a list of all connections between components in the current document
 411 |     
 412 |     Returns:
 413 |         List of all connections between components
 414 |     """
 415 |     return send_to_grasshopper("get_connections")
 416 | 
 417 | @server.tool("search_components")
 418 | def search_components(query: str):
 419 |     """
 420 |     Search for components by name or category
 421 |     
 422 |     Args:
 423 |         query: Search query
 424 |     
 425 |     Returns:
 426 |         List of components matching the search query
 427 |     """
 428 |     params = {
 429 |         "query": query
 430 |     }
 431 |     
 432 |     return send_to_grasshopper("search_components", params)
 433 | 
 434 | @server.tool("get_component_parameters")
 435 | def get_component_parameters(component_type: str):
 436 |     """
 437 |     Get a list of parameters for a specific component type
 438 |     
 439 |     Args:
 440 |         component_type: Type of component to get parameters for
 441 |     
 442 |     Returns:
 443 |         List of input and output parameters for the component type
 444 |     """
 445 |     params = {
 446 |         "componentType": component_type
 447 |     }
 448 |     
 449 |     return send_to_grasshopper("get_component_parameters", params)
 450 | 
 451 | @server.tool("validate_connection")
 452 | def validate_connection(source_id: str, target_id: str, source_param: str = None, target_param: str = None):
 453 |     """
 454 |     Validate if a connection between two components is possible
 455 |     
 456 |     Args:
 457 |         source_id: ID of the source component (output)
 458 |         target_id: ID of the target component (input)
 459 |         source_param: Name of the source parameter (optional)
 460 |         target_param: Name of the target parameter (optional)
 461 |     
 462 |     Returns:
 463 |         Whether the connection is valid and any potential issues
 464 |     """
 465 |     params = {
 466 |         "sourceId": source_id,
 467 |         "targetId": target_id
 468 |     }
 469 |     
 470 |     if source_param is not None:
 471 |         params["sourceParam"] = source_param
 472 |         
 473 |     if target_param is not None:
 474 |         params["targetParam"] = target_param
 475 |     
 476 |     return send_to_grasshopper("validate_connection", params)
 477 | 
 478 | # 註冊 MCP 資源
 479 | @server.resource("grasshopper://status")
 480 | def get_grasshopper_status():
 481 |     """Get Grasshopper status"""
 482 |     try:
 483 |         # 獲取文檔信息
 484 |         doc_info = send_to_grasshopper("get_document_info")
 485 |         
 486 |         # 獲取所有組件(使用增強版的 get_all_components)
 487 |         components_result = get_all_components()
 488 |         components = components_result.get("result", []) if components_result else []
 489 |         
 490 |         # 獲取所有連接
 491 |         connections = send_to_grasshopper("get_connections")
 492 |         
 493 |         # 添加常用組件的提示信息
 494 |         component_hints = {
 495 |             "Number Slider": {
 496 |                 "description": "Single numeric value slider with adjustable range",
 497 |                 "common_usage": "Use for single numeric inputs like radius, height, count, etc.",
 498 |                 "parameters": ["min", "max", "value", "rounding", "type"],
 499 |                 "NOT_TO_BE_CONFUSED_WITH": "MD Slider (which is for multi-dimensional values)"
 500 |             },
 501 |             "MD Slider": {
 502 |                 "description": "Multi-dimensional slider for vector input",
 503 |                 "common_usage": "Use for vector inputs, NOT for simple numeric values",
 504 |                 "NOT_TO_BE_CONFUSED_WITH": "Number Slider (which is for single numeric values)"
 505 |             },
 506 |             "Panel": {
 507 |                 "description": "Displays text or numeric data",
 508 |                 "common_usage": "Use for displaying outputs and debugging"
 509 |             },
 510 |             "Addition": {
 511 |                 "description": "Adds two or more numbers",
 512 |                 "common_usage": "Connect two Number Sliders to inputs A and B",
 513 |                 "parameters": ["A", "B"],
 514 |                 "connection_tip": "First slider should connect to input A, second to input B"
 515 |             }
 516 |         }
 517 |         
 518 |         # 為每個組件添加當前參數值的摘要
 519 |         component_summaries = []
 520 |         for component in components:
 521 |             summary = {
 522 |                 "id": component.get("id", ""),
 523 |                 "type": component.get("type", ""),
 524 |                 "position": {
 525 |                     "x": component.get("x", 0),
 526 |                     "y": component.get("y", 0)
 527 |                 }
 528 |             }
 529 |             
 530 |             # 添加組件特定的參數信息
 531 |             if "currentSettings" in component:
 532 |                 summary["settings"] = component["currentSettings"]
 533 |             elif component.get("type") == "Number Slider":
 534 |                 # 嘗試從組件信息中提取滑桿設置
 535 |                 summary["settings"] = {
 536 |                     "min": component.get("min", 0),
 537 |                     "max": component.get("max", 10),
 538 |                     "value": component.get("value", 5),
 539 |                     "rounding": component.get("rounding", 0.1)
 540 |                 }
 541 |             
 542 |             # 添加連接信息摘要
 543 |             if "connections" in component:
 544 |                 conn_summary = []
 545 |                 for conn in component["connections"]:
 546 |                     if conn.get("sourceId") == component.get("id"):
 547 |                         conn_summary.append({
 548 |                             "type": "output",
 549 |                             "to": conn.get("targetId", ""),
 550 |                             "sourceParam": conn.get("sourceParam", ""),
 551 |                             "targetParam": conn.get("targetParam", "")
 552 |                         })
 553 |                     else:
 554 |                         conn_summary.append({
 555 |                             "type": "input",
 556 |                             "from": conn.get("sourceId", ""),
 557 |                             "sourceParam": conn.get("sourceParam", ""),
 558 |                             "targetParam": conn.get("targetParam", "")
 559 |                         })
 560 |                 
 561 |                 if conn_summary:
 562 |                     summary["connections"] = conn_summary
 563 |             
 564 |             component_summaries.append(summary)
 565 |         
 566 |         return {
 567 |             "status": "Connected to Grasshopper",
 568 |             "document": doc_info.get("result", {}),
 569 |             "components": component_summaries,
 570 |             "connections": connections.get("result", []),
 571 |             "component_hints": component_hints,
 572 |             "recommendations": [
 573 |                 "When needing a simple numeric input control, ALWAYS use 'Number Slider', not MD Slider",
 574 |                 "For vector inputs (like 3D points), use 'MD Slider' or 'Construct Point' with multiple Number Sliders",
 575 |                 "Use 'Panel' to display outputs and debug values",
 576 |                 "When connecting multiple sliders to Addition, first slider goes to input A, second to input B"
 577 |             ],
 578 |             "canvas_summary": f"Current canvas has {len(component_summaries)} components and {len(connections.get('result', []))} connections"
 579 |         }
 580 |     except Exception as e:
 581 |         print(f"Error getting Grasshopper status: {str(e)}", file=sys.stderr)
 582 |         traceback.print_exc(file=sys.stderr)
 583 |         return {
 584 |             "status": f"Error: {str(e)}",
 585 |             "document": {},
 586 |             "components": [],
 587 |             "connections": []
 588 |         }
 589 | 
 590 | @server.resource("grasshopper://component_guide")
 591 | def get_component_guide():
 592 |     """Get guide for Grasshopper components and connections"""
 593 |     return {
 594 |         "title": "Grasshopper Component Guide",
 595 |         "description": "Guide for creating and connecting Grasshopper components",
 596 |         "components": [
 597 |             {
 598 |                 "name": "Point",
 599 |                 "category": "Params",
 600 |                 "description": "Creates a point at specific coordinates",
 601 |                 "inputs": [
 602 |                     {"name": "X", "type": "Number"},
 603 |                     {"name": "Y", "type": "Number"},
 604 |                     {"name": "Z", "type": "Number"}
 605 |                 ],
 606 |                 "outputs": [
 607 |                     {"name": "Pt", "type": "Point"}
 608 |                 ]
 609 |             },
 610 |             {
 611 |                 "name": "Circle",
 612 |                 "category": "Curve",
 613 |                 "description": "Creates a circle",
 614 |                 "inputs": [
 615 |                     {"name": "Plane", "type": "Plane", "description": "Base plane for the circle"},
 616 |                     {"name": "Radius", "type": "Number", "description": "Circle radius"}
 617 |                 ],
 618 |                 "outputs": [
 619 |                     {"name": "C", "type": "Circle"}
 620 |                 ]
 621 |             },
 622 |             {
 623 |                 "name": "XY Plane",
 624 |                 "category": "Vector",
 625 |                 "description": "Creates an XY plane at the world origin or at a specified point",
 626 |                 "inputs": [
 627 |                     {"name": "Origin", "type": "Point", "description": "Origin point", "optional": True}
 628 |                 ],
 629 |                 "outputs": [
 630 |                     {"name": "Plane", "type": "Plane", "description": "XY plane"}
 631 |                 ]
 632 |             },
 633 |             {
 634 |                 "name": "Addition",
 635 |                 "fullName": "Addition",
 636 |                 "description": "Adds two or more numbers",
 637 |                 "inputs": [
 638 |                     {"name": "A", "type": "Number", "description": "First input value"},
 639 |                     {"name": "B", "type": "Number", "description": "Second input value"}
 640 |                 ],
 641 |                 "outputs": [
 642 |                     {"name": "Result", "type": "Number", "description": "Sum of inputs"}
 643 |                 ],
 644 |                 "usage_examples": [
 645 |                     "Connect two Number Sliders to inputs A and B to add their values",
 646 |                     "Connect multiple values to add them all together"
 647 |                 ],
 648 |                 "common_issues": [
 649 |                     "When connecting multiple sliders, ensure they connect to different inputs (A and B)",
 650 |                     "The first slider should connect to input A, the second to input B"
 651 |                 ]
 652 |             },
 653 |             {
 654 |                 "name": "Number Slider",
 655 |                 "fullName": "Number Slider",
 656 |                 "description": "Creates a slider for numeric input with adjustable range and precision",
 657 |                 "inputs": [],
 658 |                 "outputs": [
 659 |                     {"name": "N", "type": "Number", "description": "Number output"}
 660 |                 ],
 661 |                 "settings": {
 662 |                     "min": {"description": "Minimum value of the slider", "default": 0},
 663 |                     "max": {"description": "Maximum value of the slider", "default": 10},
 664 |                     "value": {"description": "Current value of the slider", "default": 5},
 665 |                     "rounding": {"description": "Rounding precision (0.01, 0.1, 1, etc.)", "default": 0.1},
 666 |                     "type": {"description": "Slider type (integer, floating point)", "default": "float"},
 667 |                     "name": {"description": "Custom name for the slider", "default": ""}
 668 |                 },
 669 |                 "usage_examples": [
 670 |                     "Create a Number Slider with min=0, max=100, value=50",
 671 |                     "Create a Number Slider for radius with min=0.1, max=10, value=2.5, rounding=0.1"
 672 |                 ],
 673 |                 "common_issues": [
 674 |                     "Confusing with other slider types",
 675 |                     "Not setting appropriate min/max values for the intended use"
 676 |                 ],
 677 |                 "disambiguation": {
 678 |                     "similar_components": [
 679 |                         {
 680 |                             "name": "MD Slider",
 681 |                             "description": "Multi-dimensional slider for vector input, NOT for simple numeric values",
 682 |                             "how_to_distinguish": "Use Number Slider for single numeric values; use MD Slider only when you need multi-dimensional control"
 683 |                         },
 684 |                         {
 685 |                             "name": "Graph Mapper",
 686 |                             "description": "Maps values through a graph function, NOT a simple slider",
 687 |                             "how_to_distinguish": "Use Number Slider for direct numeric input; use Graph Mapper only for function-based mapping"
 688 |                         }
 689 |                     ],
 690 |                     "correct_usage": "When needing a simple numeric input control, ALWAYS use 'Number Slider', not MD Slider or other variants"
 691 |                 }
 692 |             },
 693 |             {
 694 |                 "name": "Panel",
 695 |                 "fullName": "Panel",
 696 |                 "description": "Displays text or numeric data",
 697 |                 "inputs": [
 698 |                     {"name": "Input", "type": "Any"}
 699 |                 ],
 700 |                 "outputs": []
 701 |             },
 702 |             {
 703 |                 "name": "Math",
 704 |                 "fullName": "Mathematics",
 705 |                 "description": "Performs mathematical operations",
 706 |                 "inputs": [
 707 |                     {"name": "A", "type": "Number"},
 708 |                     {"name": "B", "type": "Number"}
 709 |                 ],
 710 |                 "outputs": [
 711 |                     {"name": "Result", "type": "Number"}
 712 |                 ],
 713 |                 "operations": ["Addition", "Subtraction", "Multiplication", "Division", "Power", "Modulo"]
 714 |             },
 715 |             {
 716 |                 "name": "Construct Point",
 717 |                 "fullName": "Construct Point",
 718 |                 "description": "Constructs a point from X, Y, Z coordinates",
 719 |                 "inputs": [
 720 |                     {"name": "X", "type": "Number"},
 721 |                     {"name": "Y", "type": "Number"},
 722 |                     {"name": "Z", "type": "Number"}
 723 |                 ],
 724 |                 "outputs": [
 725 |                     {"name": "Pt", "type": "Point"}
 726 |                 ]
 727 |             },
 728 |             {
 729 |                 "name": "Line",
 730 |                 "fullName": "Line",
 731 |                 "description": "Creates a line between two points",
 732 |                 "inputs": [
 733 |                     {"name": "Start", "type": "Point"},
 734 |                     {"name": "End", "type": "Point"}
 735 |                 ],
 736 |                 "outputs": [
 737 |                     {"name": "L", "type": "Line"}
 738 |                 ]
 739 |             },
 740 |             {
 741 |                 "name": "Extrude",
 742 |                 "fullName": "Extrude",
 743 |                 "description": "Extrudes a curve to create a surface or a solid",
 744 |                 "inputs": [
 745 |                     {"name": "Base", "type": "Curve"},
 746 |                     {"name": "Direction", "type": "Vector"},
 747 |                     {"name": "Height", "type": "Number"}
 748 |                 ],
 749 |                 "outputs": [
 750 |                     {"name": "Brep", "type": "Brep"}
 751 |                 ]
 752 |             }
 753 |         ],
 754 |         "connectionRules": [
 755 |             {
 756 |                 "from": "Number",
 757 |                 "to": "Circle.Radius",
 758 |                 "description": "Connect a number to the radius input of a circle"
 759 |             },
 760 |             {
 761 |                 "from": "Point",
 762 |                 "to": "Circle.Plane",
 763 |                 "description": "Connect a point to the plane input of a circle (not recommended, use XY Plane instead)"
 764 |             },
 765 |             {
 766 |                 "from": "XY Plane",
 767 |                 "to": "Circle.Plane",
 768 |                 "description": "Connect an XY Plane to the plane input of a circle (recommended)"
 769 |             },
 770 |             {
 771 |                 "from": "Number",
 772 |                 "to": "Math.A",
 773 |                 "description": "Connect a number to the first input of a Math component"
 774 |             },
 775 |             {
 776 |                 "from": "Number",
 777 |                 "to": "Math.B",
 778 |                 "description": "Connect a number to the second input of a Math component"
 779 |             },
 780 |             {
 781 |                 "from": "Number",
 782 |                 "to": "Construct Point.X",
 783 |                 "description": "Connect a number to the X input of a Construct Point component"
 784 |             },
 785 |             {
 786 |                 "from": "Number",
 787 |                 "to": "Construct Point.Y",
 788 |                 "description": "Connect a number to the Y input of a Construct Point component"
 789 |             },
 790 |             {
 791 |                 "from": "Number",
 792 |                 "to": "Construct Point.Z",
 793 |                 "description": "Connect a number to the Z input of a Construct Point component"
 794 |             },
 795 |             {
 796 |                 "from": "Point",
 797 |                 "to": "Line.Start",
 798 |                 "description": "Connect a point to the start input of a Line component"
 799 |             },
 800 |             {
 801 |                 "from": "Point",
 802 |                 "to": "Line.End",
 803 |                 "description": "Connect a point to the end input of a Line component"
 804 |             },
 805 |             {
 806 |                 "from": "Circle",
 807 |                 "to": "Extrude.Base",
 808 |                 "description": "Connect a circle to the base input of an Extrude component"
 809 |             },
 810 |             {
 811 |                 "from": "Number",
 812 |                 "to": "Extrude.Height",
 813 |                 "description": "Connect a number to the height input of an Extrude component"
 814 |             }
 815 |         ],
 816 |         "commonIssues": [
 817 |             "Using Point component instead of XY Plane for inputs that require planes",
 818 |             "Not specifying parameter names when connecting components",
 819 |             "Using incorrect component names (e.g., 'addition' instead of 'Math' with Addition operation)",
 820 |             "Trying to connect incompatible data types",
 821 |             "Not providing all required inputs for a component",
 822 |             "Using incorrect parameter names (e.g., 'A' and 'B' for Math component instead of the actual parameter names)",
 823 |             "Not checking if a connection was successful before proceeding"
 824 |         ],
 825 |         "tips": [
 826 |             "Always use XY Plane component for plane inputs",
 827 |             "Specify parameter names when connecting components",
 828 |             "For Circle components, make sure to use the correct inputs (Plane and Radius)",
 829 |             "Test simple connections before creating complex geometry",
 830 |             "Avoid using components that require selection from Rhino",
 831 |             "Use get_component_info to check the actual parameter names of a component",
 832 |             "Use get_connections to verify if connections were established correctly",
 833 |             "Use search_components to find the correct component name before adding it",
 834 |             "Use validate_connection to check if a connection is possible before attempting it"
 835 |         ]
 836 |     }
 837 | 
 838 | @server.resource("grasshopper://component_library")
 839 | def get_component_library():
 840 |     """Get a comprehensive library of Grasshopper components"""
 841 |     # 這個資源提供了一個更全面的組件庫,包括常用組件的詳細信息
 842 |     return {
 843 |         "categories": [
 844 |             {
 845 |                 "name": "Params",
 846 |                 "components": [
 847 |                     {
 848 |                         "name": "Point",
 849 |                         "fullName": "Point Parameter",
 850 |                         "description": "Creates a point parameter",
 851 |                         "inputs": [
 852 |                             {"name": "X", "type": "Number", "description": "X coordinate"},
 853 |                             {"name": "Y", "type": "Number", "description": "Y coordinate"},
 854 |                             {"name": "Z", "type": "Number", "description": "Z coordinate"}
 855 |                         ],
 856 |                         "outputs": [
 857 |                             {"name": "Pt", "type": "Point", "description": "Point output"}
 858 |                         ]
 859 |                     },
 860 |                     {
 861 |                         "name": "Number Slider",
 862 |                         "fullName": "Number Slider",
 863 |                         "description": "Creates a slider for numeric input with adjustable range and precision",
 864 |                         "inputs": [],
 865 |                         "outputs": [
 866 |                             {"name": "N", "type": "Number", "description": "Number output"}
 867 |                         ],
 868 |                         "settings": {
 869 |                             "min": {"description": "Minimum value of the slider", "default": 0},
 870 |                             "max": {"description": "Maximum value of the slider", "default": 10},
 871 |                             "value": {"description": "Current value of the slider", "default": 5},
 872 |                             "rounding": {"description": "Rounding precision (0.01, 0.1, 1, etc.)", "default": 0.1},
 873 |                             "type": {"description": "Slider type (integer, floating point)", "default": "float"},
 874 |                             "name": {"description": "Custom name for the slider", "default": ""}
 875 |                         },
 876 |                         "usage_examples": [
 877 |                             "Create a Number Slider with min=0, max=100, value=50",
 878 |                             "Create a Number Slider for radius with min=0.1, max=10, value=2.5, rounding=0.1"
 879 |                         ],
 880 |                         "common_issues": [
 881 |                             "Confusing with other slider types",
 882 |                             "Not setting appropriate min/max values for the intended use"
 883 |                         ],
 884 |                         "disambiguation": {
 885 |                             "similar_components": [
 886 |                                 {
 887 |                                     "name": "MD Slider",
 888 |                                     "description": "Multi-dimensional slider for vector input, NOT for simple numeric values",
 889 |                                     "how_to_distinguish": "Use Number Slider for single numeric values; use MD Slider only when you need multi-dimensional control"
 890 |                                 },
 891 |                                 {
 892 |                                     "name": "Graph Mapper",
 893 |                                     "description": "Maps values through a graph function, NOT a simple slider",
 894 |                                     "how_to_distinguish": "Use Number Slider for direct numeric input; use Graph Mapper only for function-based mapping"
 895 |                                 }
 896 |                             ],
 897 |                             "correct_usage": "When needing a simple numeric input control, ALWAYS use 'Number Slider', not MD Slider or other variants"
 898 |                         }
 899 |                     },
 900 |                     {
 901 |                         "name": "Panel",
 902 |                         "fullName": "Panel",
 903 |                         "description": "Displays text or numeric data",
 904 |                         "inputs": [
 905 |                             {"name": "Input", "type": "Any", "description": "Any input data"}
 906 |                         ],
 907 |                         "outputs": []
 908 |                     }
 909 |                 ]
 910 |             },
 911 |             {
 912 |                 "name": "Maths",
 913 |                 "components": [
 914 |                     {
 915 |                         "name": "Math",
 916 |                         "fullName": "Mathematics",
 917 |                         "description": "Performs mathematical operations",
 918 |                         "inputs": [
 919 |                             {"name": "A", "type": "Number", "description": "First number"},
 920 |                             {"name": "B", "type": "Number", "description": "Second number"}
 921 |                         ],
 922 |                         "outputs": [
 923 |                             {"name": "Result", "type": "Number", "description": "Result of the operation"}
 924 |                         ],
 925 |                         "operations": ["Addition", "Subtraction", "Multiplication", "Division", "Power", "Modulo"]
 926 |                     }
 927 |                 ]
 928 |             },
 929 |             {
 930 |                 "name": "Vector",
 931 |                 "components": [
 932 |                     {
 933 |                         "name": "XY Plane",
 934 |                         "fullName": "XY Plane",
 935 |                         "description": "Creates an XY plane at the world origin or at a specified point",
 936 |                         "inputs": [
 937 |                             {"name": "Origin", "type": "Point", "description": "Origin point", "optional": True}
 938 |                         ],
 939 |                         "outputs": [
 940 |                             {"name": "Plane", "type": "Plane", "description": "XY plane"}
 941 |                         ]
 942 |                     },
 943 |                     {
 944 |                         "name": "Construct Point",
 945 |                         "fullName": "Construct Point",
 946 |                         "description": "Constructs a point from X, Y, Z coordinates",
 947 |                         "inputs": [
 948 |                             {"name": "X", "type": "Number", "description": "X coordinate"},
 949 |                             {"name": "Y", "type": "Number", "description": "Y coordinate"},
 950 |                             {"name": "Z", "type": "Number", "description": "Z coordinate"}
 951 |                         ],
 952 |                         "outputs": [
 953 |                             {"name": "Pt", "type": "Point", "description": "Constructed point"}
 954 |                         ]
 955 |                     }
 956 |                 ]
 957 |             },
 958 |             {
 959 |                 "name": "Curve",
 960 |                 "components": [
 961 |                     {
 962 |                         "name": "Circle",
 963 |                         "fullName": "Circle",
 964 |                         "description": "Creates a circle",
 965 |                         "inputs": [
 966 |                             {"name": "Plane", "type": "Plane", "description": "Base plane for the circle"},
 967 |                             {"name": "Radius", "type": "Number", "description": "Circle radius"}
 968 |                         ],
 969 |                         "outputs": [
 970 |                             {"name": "C", "type": "Circle", "description": "Circle output"}
 971 |                         ]
 972 |                     },
 973 |                     {
 974 |                         "name": "Line",
 975 |                         "fullName": "Line",
 976 |                         "description": "Creates a line between two points",
 977 |                         "inputs": [
 978 |                             {"name": "Start", "type": "Point", "description": "Start point"},
 979 |                             {"name": "End", "type": "Point", "description": "End point"}
 980 |                         ],
 981 |                         "outputs": [
 982 |                             {"name": "L", "type": "Line", "description": "Line output"}
 983 |                         ]
 984 |                     }
 985 |                 ]
 986 |             },
 987 |             {
 988 |                 "name": "Surface",
 989 |                 "components": [
 990 |                     {
 991 |                         "name": "Extrude",
 992 |                         "fullName": "Extrude",
 993 |                         "description": "Extrudes a curve to create a surface or a solid",
 994 |                         "inputs": [
 995 |                             {"name": "Base", "type": "Curve", "description": "Base curve to extrude"},
 996 |                             {"name": "Direction", "type": "Vector", "description": "Direction of extrusion", "optional": True},
 997 |                             {"name": "Height", "type": "Number", "description": "Height of extrusion"}
 998 |                         ],
 999 |                         "outputs": [
1000 |                             {"name": "Brep", "type": "Brep", "description": "Extruded brep"}
1001 |                         ]
1002 |                     }
1003 |                 ]
1004 |             }
1005 |         ],
1006 |         "dataTypes": [
1007 |             {
1008 |                 "name": "Number",
1009 |                 "description": "A numeric value",
1010 |                 "compatibleWith": ["Number", "Integer", "Double"]
1011 |             },
1012 |             {
1013 |                 "name": "Point",
1014 |                 "description": "A 3D point in space",
1015 |                 "compatibleWith": ["Point3d", "Point"]
1016 |             },
1017 |             {
1018 |                 "name": "Vector",
1019 |                 "description": "A 3D vector",
1020 |                 "compatibleWith": ["Vector3d", "Vector"]
1021 |             },
1022 |             {
1023 |                 "name": "Plane",
1024 |                 "description": "A plane in 3D space",
1025 |                 "compatibleWith": ["Plane"]
1026 |             },
1027 |             {
1028 |                 "name": "Circle",
1029 |                 "description": "A circle curve",
1030 |                 "compatibleWith": ["Circle", "Curve"]
1031 |             },
1032 |             {
1033 |                 "name": "Line",
1034 |                 "description": "A line segment",
1035 |                 "compatibleWith": ["Line", "Curve"]
1036 |             },
1037 |             {
1038 |                 "name": "Curve",
1039 |                 "description": "A curve object",
1040 |                 "compatibleWith": ["Curve", "Circle", "Line", "Arc", "Polyline"]
1041 |             },
1042 |             {
1043 |                 "name": "Brep",
1044 |                 "description": "A boundary representation object",
1045 |                 "compatibleWith": ["Brep", "Surface", "Solid"]
1046 |             }
1047 |         ]
1048 |     }
1049 | 
1050 | def main():
1051 |     """Main entry point for the Grasshopper MCP Bridge Server"""
1052 |     try:
1053 |         # 啟動 MCP 服務器
1054 |         print("Starting Grasshopper MCP Bridge Server...", file=sys.stderr)
1055 |         print("Please add this MCP server to Claude Desktop", file=sys.stderr)
1056 |         server.run()
1057 |     except Exception as e:
1058 |         print(f"Error starting MCP server: {str(e)}", file=sys.stderr)
1059 |         traceback.print_exc(file=sys.stderr)
1060 |         sys.exit(1)
1061 | 
1062 | if __name__ == "__main__":
1063 |     main()
1064 | 
```