Creating Custom Tools
You can extend the MCP Server with your own tools by adding a simple attribute to any C# method. No Python changes, no server restart, no tool registration — just recompile in Unity and the AI agent sees your new tool.
Basic Example
Section titled “Basic Example”using realvirtual.MCP;
public static class MyTools{ [McpTool("Get current time")] public static string GetTime() { return $"{{\"time\":\"{System.DateTime.Now}\"}}"; }
[McpTool("Add two numbers")] public static string Add( [McpParam("First number")] float a, [McpParam("Second number")] float b) { return $"{{\"result\":{a + b}}}"; }}That’s it. After recompilation, get_time and add are available to any connected AI agent.
Attributes
Section titled “Attributes”McpTool
Section titled “McpTool”Add [McpTool("description")] to any public static method that returns a string. The description tells the AI agent what the tool does.
[McpTool("Spawn an enemy at position")]public static string SpawnEnemy( [McpParam("Prefab name")] string prefab, [McpParam("X position")] float x, [McpParam("Z position")] float z){ // Your Unity code here - runs on main thread return ToolHelpers.Ok("Enemy spawned");}McpParam
Section titled “McpParam”Add [McpParam("description")] to parameters to give the AI agent context about what values to provide. Optional parameters need default values.
[McpTool("Set weather")]public static string SetWeather( [McpParam("Weather type: sunny, rainy, cloudy")] string weather, [McpParam("Wind speed in m/s")] float windSpeed = 0f){ // windSpeed is optional, defaults to 0 return ToolHelpers.Ok($"Weather set to {weather}");}- Method must be
public staticand returnstring(JSON) - Tool names auto-convert from PascalCase to snake_case (
SpawnEnemy→spawn_enemy) - Methods can be in any class, in any assembly — they are found via reflection
- All tool methods run on Unity’s main thread
- Return JSON strings — use
ToolHelpers.Ok()andToolHelpers.Error()for standard responses
Helper Utilities
Section titled “Helper Utilities”The ToolHelpers class provides common patterns:
// Find a GameObject by hierarchy pathvar go = ToolHelpers.FindGameObject("Robot/Arm/Gripper");
// Return successreturn ToolHelpers.Ok("Operation completed");
// Return success with datareturn ToolHelpers.Ok(new { position = transform.position });
// Return errorreturn ToolHelpers.Error("GameObject not found");Example: Custom Sensor Tool
Section titled “Example: Custom Sensor Tool”using realvirtual.MCP;using UnityEngine;
public static class FactoryTools{ [McpTool("Get temperature from all sensors in the factory")] public static string GetTemperatures() { var sensors = Object.FindObjectsByType<TemperatureSensor>( FindObjectsSortMode.None);
var results = sensors.Select(s => new { name = s.gameObject.name, temperature = s.CurrentTemperature, unit = "celsius" }).ToArray();
return JsonUtility.ToJson(new { sensors = results }); }}After recompiling, the AI agent can call get_temperatures and receive structured data about all temperature sensors in the scene.