Files
Cortex_Engine/src/Engine.AI/Mcp/EngineMcpTools.cs
T
emil28092005 a9c783d204 feat: Step 6 MCP server bridge for AI agents
- Add ModelContextProtocol + ModelContextProtocol.AspNetCore 1.4.0 to Engine.AI.
- Expose AI commands as MCP tools: spawn_model, set_transform, delete_entity, list_entities.
- Add AiCommandQueue to marshal commands from the MCP server thread to the main thread.
- Start in-process HTTP MCP server in Program.cs (Debug/Release only; excluded in ReleaseAOT).
- Add --mcp-port CLI argument to configure the MCP server port.
- Fix Flecs.NET package conditions to include ReleaseAOT config.
2026-06-16 19:59:19 +03:00

125 lines
4.2 KiB
C#

using System.ComponentModel;
using System.Numerics;
using Engine.AI.Commands;
using ModelContextProtocol.Server;
namespace Engine.AI.Mcp;
/// <summary>
/// MCP tools that expose engine commands to AI agents.
/// </summary>
[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<string> SpawnModel(
string name,
string modelPath,
[Description("Optional position as [x, y, z] (default: 0,0,0)")] IReadOnlyList<double>? position = null,
[Description("Optional rotation as [x, y, z, w] quaternion (default: identity)")] IReadOnlyList<double>? rotation = null,
[Description("Optional scale as [x, y, z] (default: 1,1,1)")] IReadOnlyList<double>? 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<string> SetTransform(
string name,
[Description("Optional position as [x, y, z]")] IReadOnlyList<double>? position = null,
[Description("Optional rotation as [x, y, z, w] quaternion")] IReadOnlyList<double>? rotation = null,
[Description("Optional scale as [x, y, z]")] IReadOnlyList<double>? 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<string> DeleteEntity(string name)
{
var cmd = new DeleteEntityCommand { Name = name };
return EnqueueAndReturnMessage(cmd);
}
[McpServerTool, Description("List all named entities in the ECS world.")]
public Task<string> ListEntities()
{
var cmd = new ListEntitiesCommand();
return EnqueueAndReturnMessage(cmd);
}
private async Task<string> 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<double>? 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<double>? 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<double>? 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<double>? 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]);
}
}