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]);
}
}