using System; using MinecraftClient.Mcp; using MinecraftClient.Scripting; using Tomlet.Attributes; namespace MinecraftClient.ChatBots { public class McpServer : ChatBot { public static Configs Config = new(); [TomlDoNotInlineObject] public class Configs { [NonSerialized] private const string BotName = "McpServer"; [TomlInlineComment("$ChatBot.McpServer.Enabled$")] public bool Enabled = false; [TomlPrecedingComment("$ChatBot.McpServer.Transport$")] public MccMcpTransportConfig Transport = new(); [TomlPrecedingComment("$ChatBot.McpServer.Capabilities$")] public MccMcpCapabilityToggles Capabilities = new(); public void OnSettingUpdate() { Transport ??= new MccMcpTransportConfig(); Capabilities ??= new MccMcpCapabilityToggles(); if (Transport.Port is < 1 or > 65535) Transport.Port = 33333; if (string.IsNullOrWhiteSpace(Transport.BindHost)) Transport.BindHost = "127.0.0.1"; if (string.IsNullOrWhiteSpace(Transport.Route)) Transport.Route = "/mcp"; if (!Transport.Route.StartsWith('/')) Transport.Route = "/" + Transport.Route; if (string.IsNullOrWhiteSpace(Transport.AuthTokenEnvVar)) Transport.AuthTokenEnvVar = "MCC_MCP_AUTH_TOKEN"; } } private MccEmbeddedMcpHost? host; public override void Initialize() { Config.OnSettingUpdate(); } public override void AfterGameJoined() { if (!Config.Enabled) return; MccMcpChatHistoryStore.Clear(); MccMcpConfig mcpConfig = new() { Enabled = Config.Enabled, Transport = Config.Transport, Capabilities = Config.Capabilities }; host ??= new MccEmbeddedMcpHost(mcpConfig, new MccMcpCapabilities(() => Config.Capabilities)); if (host.IsRunning) return; LogToConsole(Translations.bot_mcpserver_starting); if (!host.Start(out string? error)) { if (error == "missing_auth_token") LogToConsole(string.Format(Translations.bot_mcpserver_missing_auth_token, Config.Transport.AuthTokenEnvVar)); LogToConsole(string.Format(Translations.bot_mcpserver_start_failed, error ?? "unknown")); return; } LogToConsole(string.Format(Translations.bot_mcpserver_started, host.Endpoint)); } public override bool OnDisconnect(DisconnectReason reason, string message) { StopHost(); MccMcpChatHistoryStore.Clear(); return false; } public override void OnUnload() { StopHost(); MccMcpChatHistoryStore.Clear(); } public override void GetText(string text, string? json) { string clean = GetVerbatim(text); if (string.IsNullOrWhiteSpace(clean)) return; string kind = "system"; string? sender = null; string? message = null; string parsedMessage = string.Empty; string parsedSender = string.Empty; if (IsPrivateMessage(clean, ref parsedMessage, ref parsedSender)) { kind = "private"; sender = parsedSender; message = parsedMessage; } else if (IsChatMessage(clean, ref parsedMessage, ref parsedSender)) { kind = "chat"; sender = parsedSender; message = parsedMessage; } MccMcpChatHistoryStore.Add(new MccMcpChatHistoryEntry { TimestampUtc = DateTimeOffset.UtcNow, Kind = kind, Text = clean, Sender = sender, Message = message, Json = json }); } private void StopHost() { if (host is null || !host.IsRunning) return; if (host.Stop(out string? error)) LogToConsole(Translations.bot_mcpserver_stopped); else LogToConsole(string.Format(Translations.bot_mcpserver_stop_failed, error ?? "unknown")); } } }