using System.ComponentModel; using System.Numerics; using Engine.AI.Commands; using ModelContextProtocol.Server; namespace Engine.AI.Mcp; /// /// MCP tools that expose engine commands to AI agents. /// [McpServerToolType] public sealed class EngineMcpTools { private readonly AiCommandQueue _queue; public EngineMcpTools(AiCommandQueue queue) { _queue = queue; } [McpServerTool, Description("Spawn a 3D model entity in the engine world.")] public Task SpawnModel( string name, string modelPath, [Description("Optional position as [x, y, z] (default: 0,0,0)")] IReadOnlyList? position = null, [Description("Optional rotation as [x, y, z, w] quaternion (default: identity)")] IReadOnlyList? rotation = null, [Description("Optional scale as [x, y, z] (default: 1,1,1)")] IReadOnlyList? scale = null) { var cmd = new SpawnModelCommand { Name = name, ModelPath = modelPath, Position = ToVector3(position, Vector3.Zero), Rotation = ToQuaternion(rotation, Quaternion.Identity), Scale = ToVector3(scale, Vector3.One) }; return EnqueueAndReturnMessage(cmd); } [McpServerTool, Description("Update the transform of an existing entity by name.")] public Task SetTransform( string name, [Description("Optional position as [x, y, z]")] IReadOnlyList? position = null, [Description("Optional rotation as [x, y, z, w] quaternion")] IReadOnlyList? rotation = null, [Description("Optional scale as [x, y, z]")] IReadOnlyList? scale = null) { var cmd = new SetTransformCommand { Name = name, Position = ToVector3(position), Rotation = ToQuaternion(rotation), Scale = ToVector3(scale) }; return EnqueueAndReturnMessage(cmd); } [McpServerTool, Description("Delete an entity by name.")] public Task DeleteEntity(string name) { var cmd = new DeleteEntityCommand { Name = name }; return EnqueueAndReturnMessage(cmd); } [McpServerTool, Description("List all named entities in the ECS world.")] public Task ListEntities() { var cmd = new ListEntitiesCommand(); return EnqueueAndReturnMessage(cmd); } private async Task EnqueueAndReturnMessage(AiCommand command) { var result = await _queue.EnqueueAsync(command).ConfigureAwait(false); return result.Success ? result.Message : throw new InvalidOperationException(result.Message); } private static Vector3 ToVector3(IReadOnlyList? values, Vector3 defaultValue) { if (values == null || values.Count == 0) return defaultValue; if (values.Count != 3) throw new ArgumentException("Expected 3 values for Vector3.", nameof(values)); return new Vector3((float)values[0], (float)values[1], (float)values[2]); } private static Vector3? ToVector3(IReadOnlyList? values) { if (values == null || values.Count == 0) return null; if (values.Count != 3) throw new ArgumentException("Expected 3 values for Vector3.", nameof(values)); return new Vector3((float)values[0], (float)values[1], (float)values[2]); } private static Quaternion ToQuaternion(IReadOnlyList? values, Quaternion defaultValue) { if (values == null || values.Count == 0) return defaultValue; if (values.Count != 4) throw new ArgumentException("Expected 4 values for Quaternion.", nameof(values)); return new Quaternion((float)values[0], (float)values[1], (float)values[2], (float)values[3]); } private static Quaternion? ToQuaternion(IReadOnlyList? values) { if (values == null || values.Count == 0) return null; if (values.Count != 4) throw new ArgumentException("Expected 4 values for Quaternion.", nameof(values)); return new Quaternion((float)values[0], (float)values[1], (float)values[2], (float)values[3]); } }