2025-03-18 19:00:50 +08:00
|
|
|
# Unity MCP Server
|
|
|
|
|
|
|
|
|
|
This directory contains the Unity MCP Server implementation, which provides a bridge between Python and Unity Editor functionality.
|
|
|
|
|
|
|
|
|
|
## Adding New Tools
|
|
|
|
|
|
|
|
|
|
To add a new tool to the MCP Server, follow these steps:
|
|
|
|
|
|
|
|
|
|
### 1. Create the C# Command Handler
|
|
|
|
|
|
|
|
|
|
First, create or modify a command handler in the `Editor/Commands` directory:
|
|
|
|
|
|
|
|
|
|
```csharp
|
|
|
|
|
// Example: NewCommandHandler.cs
|
|
|
|
|
public static class NewCommandHandler
|
|
|
|
|
{
|
|
|
|
|
public static object HandleNewCommand(JObject @params)
|
|
|
|
|
{
|
|
|
|
|
// Extract parameters
|
|
|
|
|
string param1 = (string)@params["param1"];
|
|
|
|
|
int param2 = (int)@params["param2"];
|
|
|
|
|
|
|
|
|
|
// Implement the Unity-side functionality
|
|
|
|
|
// ...
|
|
|
|
|
|
|
|
|
|
// Return results
|
|
|
|
|
return new {
|
|
|
|
|
message = "Operation successful",
|
|
|
|
|
result = someResult
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### 2. Register the Command Handler
|
|
|
|
|
|
|
|
|
|
Add your command handler to the `CommandRegistry.cs` in the `Editor/Commands` directory:
|
|
|
|
|
|
|
|
|
|
```csharp
|
|
|
|
|
public static class CommandRegistry
|
|
|
|
|
{
|
|
|
|
|
private static readonly Dictionary<string, Func<JObject, object>> _handlers = new()
|
|
|
|
|
{
|
|
|
|
|
// ... existing handlers ...
|
|
|
|
|
{ "NEW_COMMAND", NewCommandHandler.HandleNewCommand }
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### 3. Create the Python Tool
|
|
|
|
|
|
|
|
|
|
Add your tool to the appropriate Python module in the `Python/tools` directory:
|
|
|
|
|
|
|
|
|
|
```python
|
|
|
|
|
@mcp.tool()
|
|
|
|
|
def new_tool(
|
|
|
|
|
ctx: Context,
|
|
|
|
|
param1: str,
|
|
|
|
|
param2: int
|
|
|
|
|
) -> str:
|
|
|
|
|
"""Description of what the tool does.
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
ctx: The MCP context
|
|
|
|
|
param1: Description of param1
|
|
|
|
|
param2: Description of param2
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
str: Success message or error details
|
|
|
|
|
"""
|
|
|
|
|
try:
|
|
|
|
|
response = get_unity_connection().send_command("NEW_COMMAND", {
|
|
|
|
|
"param1": param1,
|
|
|
|
|
"param2": param2
|
|
|
|
|
})
|
|
|
|
|
return response.get("message", "Operation successful")
|
|
|
|
|
except Exception as e:
|
|
|
|
|
return f"Error executing operation: {str(e)}"
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### 4. Register the Tool
|
|
|
|
|
|
|
|
|
|
Ensure your tool is registered in the appropriate registration function:
|
|
|
|
|
|
|
|
|
|
```python
|
|
|
|
|
# In Python/tools/__init__.py
|
|
|
|
|
def register_all_tools(mcp):
|
|
|
|
|
register_scene_tools(mcp)
|
|
|
|
|
register_script_tools(mcp)
|
|
|
|
|
register_material_tools(mcp)
|
|
|
|
|
# Add your new tool registration if needed
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### 5. Update the Prompt
|
|
|
|
|
|
|
|
|
|
If your tool should be exposed to users, update the prompt in `Python/server.py`:
|
|
|
|
|
|
|
|
|
|
```python
|
|
|
|
|
@mcp.prompt()
|
|
|
|
|
def asset_creation_strategy() -> str:
|
|
|
|
|
return (
|
|
|
|
|
"Follow these Unity best practices:\n\n"
|
|
|
|
|
"1. **Your Category**:\n"
|
|
|
|
|
" - Use `new_tool(param1, param2)` to do something\n"
|
|
|
|
|
# ... rest of the prompt ...
|
|
|
|
|
)
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## Best Practices
|
|
|
|
|
|
2025-03-18 21:52:41 +08:00
|
|
|
1. **Existence Checking**:
|
|
|
|
|
|
|
|
|
|
- ALWAYS check if objects, scripts, assets, or materials exist before creating or updating them
|
|
|
|
|
- Use appropriate search tools (`find_objects_by_name`, `list_scripts`, `get_asset_list`) to verify existence
|
|
|
|
|
- Handle both cases: creation when it doesn't exist and updating when it does
|
|
|
|
|
- Implement proper error handling when an expected resource is not found
|
|
|
|
|
|
|
|
|
|
2. **Error Handling**:
|
2025-03-18 19:00:50 +08:00
|
|
|
|
|
|
|
|
- Always include try-catch blocks in Python tools
|
|
|
|
|
- Validate parameters in C# handlers
|
|
|
|
|
- Return meaningful error messages
|
|
|
|
|
|
2025-03-18 21:52:41 +08:00
|
|
|
3. **Documentation**:
|
2025-03-18 19:00:50 +08:00
|
|
|
|
|
|
|
|
- Add XML documentation to C# handlers
|
|
|
|
|
- Include detailed docstrings in Python tools
|
|
|
|
|
- Update the prompt with clear usage instructions
|
|
|
|
|
|
2025-03-18 21:52:41 +08:00
|
|
|
4. **Parameter Validation**:
|
2025-03-18 19:00:50 +08:00
|
|
|
|
|
|
|
|
- Validate parameters on both Python and C# sides
|
|
|
|
|
- Use appropriate types (str, int, float, List, etc.)
|
|
|
|
|
- Provide default values when appropriate
|
|
|
|
|
|
2025-03-18 21:52:41 +08:00
|
|
|
5. **Testing**:
|
2025-03-18 19:00:50 +08:00
|
|
|
|
|
|
|
|
- Test the tool in both Unity Editor and Python environments
|
|
|
|
|
- Verify error handling works as expected
|
|
|
|
|
- Check that the tool integrates well with existing functionality
|
|
|
|
|
|
2025-03-18 21:52:41 +08:00
|
|
|
6. **Code Organization**:
|
2025-03-18 19:00:50 +08:00
|
|
|
- Group related tools in appropriate handler classes
|
|
|
|
|
- Keep tools focused and single-purpose
|
|
|
|
|
- Follow existing naming conventions
|
|
|
|
|
|
|
|
|
|
## Example Implementation
|
|
|
|
|
|
|
|
|
|
Here's a complete example of adding a new tool:
|
|
|
|
|
|
|
|
|
|
1. **C# Handler** (`Editor/Commands/ExampleHandler.cs`):
|
|
|
|
|
|
|
|
|
|
```csharp
|
|
|
|
|
public static class ExampleHandler
|
|
|
|
|
{
|
|
|
|
|
public static object CreatePrefab(JObject @params)
|
|
|
|
|
{
|
|
|
|
|
string prefabName = (string)@params["prefab_name"];
|
|
|
|
|
string template = (string)@params["template"];
|
2025-03-18 21:52:41 +08:00
|
|
|
bool overwrite = @params["overwrite"] != null ? (bool)@params["overwrite"] : false;
|
|
|
|
|
|
|
|
|
|
// Check if the prefab already exists
|
|
|
|
|
string prefabPath = $"Assets/Prefabs/{prefabName}.prefab";
|
|
|
|
|
bool prefabExists = System.IO.File.Exists(prefabPath);
|
|
|
|
|
|
|
|
|
|
if (prefabExists && !overwrite)
|
|
|
|
|
{
|
|
|
|
|
return new {
|
|
|
|
|
message = $"Prefab already exists: {prefabName}. Use overwrite=true to replace it.",
|
|
|
|
|
exists = true,
|
|
|
|
|
path = prefabPath
|
|
|
|
|
};
|
|
|
|
|
}
|
2025-03-18 19:00:50 +08:00
|
|
|
|
|
|
|
|
// Implementation
|
|
|
|
|
GameObject prefab = new GameObject(prefabName);
|
|
|
|
|
// ... setup prefab ...
|
|
|
|
|
|
|
|
|
|
return new {
|
2025-03-18 21:52:41 +08:00
|
|
|
message = prefabExists ? $"Updated prefab: {prefabName}" : $"Created prefab: {prefabName}",
|
|
|
|
|
exists = prefabExists,
|
|
|
|
|
path = prefabPath
|
2025-03-18 19:00:50 +08:00
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
2. **Python Tool** (`Python/tools/example_tools.py`):
|
|
|
|
|
|
|
|
|
|
```python
|
|
|
|
|
@mcp.tool()
|
|
|
|
|
def create_prefab(
|
|
|
|
|
ctx: Context,
|
|
|
|
|
prefab_name: str,
|
2025-03-18 21:52:41 +08:00
|
|
|
template: str = "default",
|
|
|
|
|
overwrite: bool = False
|
2025-03-18 19:00:50 +08:00
|
|
|
) -> str:
|
2025-03-18 21:52:41 +08:00
|
|
|
"""Create a new prefab in the project or update if it exists.
|
2025-03-18 19:00:50 +08:00
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
ctx: The MCP context
|
|
|
|
|
prefab_name: Name for the new prefab
|
|
|
|
|
template: Template to use (default: "default")
|
2025-03-18 21:52:41 +08:00
|
|
|
overwrite: Whether to overwrite an existing prefab (default: False)
|
2025-03-18 19:00:50 +08:00
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
str: Success message or error details
|
|
|
|
|
"""
|
|
|
|
|
try:
|
2025-03-18 21:52:41 +08:00
|
|
|
# First check if the prefab already exists
|
|
|
|
|
assets = get_unity_connection().send_command("GET_ASSET_LIST", {
|
|
|
|
|
"type": "Prefab",
|
|
|
|
|
"search_pattern": prefab_name,
|
|
|
|
|
"folder": "Assets/Prefabs"
|
|
|
|
|
}).get("assets", [])
|
|
|
|
|
|
|
|
|
|
prefab_exists = any(asset.get("name") == prefab_name for asset in assets)
|
|
|
|
|
|
|
|
|
|
if prefab_exists and not overwrite:
|
|
|
|
|
return f"Prefab '{prefab_name}' already exists. Use overwrite=True to replace it."
|
|
|
|
|
|
|
|
|
|
# Create or update the prefab
|
2025-03-18 19:00:50 +08:00
|
|
|
response = get_unity_connection().send_command("CREATE_PREFAB", {
|
|
|
|
|
"prefab_name": prefab_name,
|
2025-03-18 21:52:41 +08:00
|
|
|
"template": template,
|
|
|
|
|
"overwrite": overwrite
|
2025-03-18 19:00:50 +08:00
|
|
|
})
|
2025-03-18 21:52:41 +08:00
|
|
|
|
|
|
|
|
return response.get("message", "Prefab operation completed successfully")
|
2025-03-18 19:00:50 +08:00
|
|
|
except Exception as e:
|
2025-03-18 21:52:41 +08:00
|
|
|
return f"Error with prefab operation: {str(e)}"
|
2025-03-18 19:00:50 +08:00
|
|
|
```
|
|
|
|
|
|
|
|
|
|
3. **Update Prompt**:
|
|
|
|
|
|
|
|
|
|
```python
|
|
|
|
|
"1. **Prefab Management**:\n"
|
2025-03-18 21:52:41 +08:00
|
|
|
" - ALWAYS check if a prefab exists before creating it\n"
|
|
|
|
|
" - Create or update prefabs with `create_prefab(prefab_name, template, overwrite=False)`\n"
|
2025-03-18 19:00:50 +08:00
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## Troubleshooting
|
|
|
|
|
|
|
|
|
|
If you encounter issues:
|
|
|
|
|
|
|
|
|
|
1. Check the Unity Console for C# errors
|
|
|
|
|
2. Verify the command name matches between Python and C#
|
|
|
|
|
3. Ensure all parameters are properly serialized
|
|
|
|
|
4. Check the Python logs for connection issues
|
|
|
|
|
5. Verify the tool is properly registered in both environments
|