From 550fe8e4aae67e6f23034588f40f0d2da6e851c1 Mon Sep 17 00:00:00 2001 From: WerWolv Date: Wed, 7 Jan 2026 17:12:40 +0100 Subject: [PATCH] impr: Add MCP Client information to footer icon tooltip --- lib/libimhex/include/hex/mcp/server.hpp | 13 +++++- lib/libimhex/source/mcp/server.cpp | 26 ++++++++++- plugins/builtin/romfs/lang/en_US.json | 6 ++- plugins/builtin/source/content/ui_items.cpp | 50 +++++++++++++++++---- 4 files changed, 83 insertions(+), 12 deletions(-) diff --git a/lib/libimhex/include/hex/mcp/server.hpp b/lib/libimhex/include/hex/mcp/server.hpp index 7599449c6..febd72cbc 100644 --- a/lib/libimhex/include/hex/mcp/server.hpp +++ b/lib/libimhex/include/hex/mcp/server.hpp @@ -94,8 +94,18 @@ namespace hex::mcp { void addPrimitive(std::string type, std::string_view capabilities, std::function function); + struct ClientInfo { + std::string name; + std::string version; + std::string protocolVersion; + }; + + const ClientInfo& getClientInfo() const { + return m_clientInfo; + } + private: - nlohmann::json handleInitialize(); + nlohmann::json handleInitialize(const nlohmann::json ¶ms); void handleNotifications(const std::string &method, const nlohmann::json ¶ms); struct Primitive { @@ -107,6 +117,7 @@ namespace hex::mcp { wolv::net::SocketServer m_server; bool m_connected = false; + ClientInfo m_clientInfo; }; } diff --git a/lib/libimhex/source/mcp/server.cpp b/lib/libimhex/source/mcp/server.cpp index 8ed67fe30..5cc5fd4ac 100644 --- a/lib/libimhex/source/mcp/server.cpp +++ b/lib/libimhex/source/mcp/server.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include @@ -119,12 +120,13 @@ namespace hex::mcp { m_server.accept([this](auto, const std::vector &data) -> std::vector { std::string request(data.begin(), data.end()); + TaskManager::setCurrentThreadName("MCP Server"); log::debug("MCP ----> {}", request); JsonRpc rpc(request); auto response = rpc.execute([this](const std::string &method, const nlohmann::json ¶ms) -> nlohmann::json { if (method == "initialize") { - return handleInitialize(); + return handleInitialize(params); } else if (method.starts_with("notifications/")) { handleNotifications(method.substr(14), params); return {}; @@ -166,6 +168,7 @@ namespace hex::mcp { }, [this](auto) { log::info("MCP client disconnected"); m_connected = false; + m_clientInfo = {}; }, true); } @@ -188,10 +191,29 @@ namespace hex::mcp { } - nlohmann::json Server::handleInitialize() { + nlohmann::json Server::handleInitialize(const nlohmann::json ¶ms) { constexpr static auto ServerName = "ImHex"; constexpr static auto ProtocolVersion = "2025-06-18"; + m_clientInfo = {}; + + if (params.contains("protocolVersion")) { + auto clientProtocolVersion = params["protocolVersion"].get(); + m_clientInfo.protocolVersion = clientProtocolVersion; + } else { + throw JsonRpc::InvalidParametersException(); + } + + if (params.contains("clientInfo")) { + const auto &clientInfo = params["clientInfo"]; + m_clientInfo.name = clientInfo.value("name", "???"); + m_clientInfo.version = clientInfo.value("version", "???"); + + log::info("MCP client connected: {} v{} (protocol {})", m_clientInfo.name, m_clientInfo.version, m_clientInfo.protocolVersion); + } else { + log::info("MCP client connected: Unknown client info"); + } + return { { "protocolVersion", ProtocolVersion }, { diff --git a/plugins/builtin/romfs/lang/en_US.json b/plugins/builtin/romfs/lang/en_US.json index 316cc93d0..0937c189f 100644 --- a/plugins/builtin/romfs/lang/en_US.json +++ b/plugins/builtin/romfs/lang/en_US.json @@ -1255,5 +1255,9 @@ "hex.builtin.welcome.update.desc": "ImHex {0} just released!", "hex.builtin.welcome.update.link": "https://github.com/WerWolv/ImHex/releases/latest", "hex.builtin.welcome.update.title": "New Update available!", - "hex.builtin.welcome.quick_settings.simplified": "Simplified" + "hex.builtin.welcome.quick_settings.simplified": "Simplified", + "hex.builtin.footer.mcp.connected_to": "Connected to '{0}'", + "hex.builtin.footer.mcp.client_version": "Client Version", + "hex.builtin.footer.mcp.protocol_version": "Protocol Version", + "hex.builtin.footer.mcp.not_connected": "MCP Server enabled but no AI Client is connected.\n\nMake sure your AI Client settings are correct!" } diff --git a/plugins/builtin/source/content/ui_items.cpp b/plugins/builtin/source/content/ui_items.cpp index a1f3182c0..bfbe343f4 100644 --- a/plugins/builtin/source/content/ui_items.cpp +++ b/plugins/builtin/source/content/ui_items.cpp @@ -241,17 +241,51 @@ namespace hex::plugin::builtin { } ContentRegistry::UserInterface::addFooterItem([] { - if (ContentRegistry::MCP::isConnected()) { - ImGui::PushStyleColor(ImGuiCol_Text, ImGuiExt::GetCustomColorU32(ImGuiCustomCol_Highlight)); - } else { - ImGui::PushStyleColor(ImGuiCol_Text, ImGui::GetColorU32(ImGuiCol_TextDisabled)); - } - if (ContentRegistry::MCP::isEnabled()) { + const bool connected = ContentRegistry::MCP::isConnected(); + if (connected) { + ImGui::PushStyleColor(ImGuiCol_Text, ImGuiExt::GetCustomColorU32(ImGuiCustomCol_Highlight)); + } else { + ImGui::PushStyleColor(ImGuiCol_Text, ImGui::GetColorU32(ImGuiCol_TextDisabled)); + } ImGui::TextUnformatted(ICON_VS_MCP); - } - ImGui::PopStyleColor(); + ImGui::PopStyleColor(); + + if (ImGui::IsItemHovered()) { + if (ImGui::BeginTooltip()) { + if (connected) { + auto &clientInfo = ContentRegistry::MCP::impl::getMcpServerInstance()->getClientInfo(); + ImGuiExt::TextFormattedColored(ImGuiExt::GetCustomColorVec4(ImGuiCustomCol_Highlight), "hex.builtin.footer.mcp.connected_to"_lang, clientInfo.name); + ImGui::Separator(); + if (ImGui::BeginTable("##connection_info", 2, ImGuiTableFlags_RowBg)) { + ImGui::TableSetupColumn("##property", ImGuiTableColumnFlags_WidthStretch); + ImGui::TableSetupColumn("##value", ImGuiTableColumnFlags_WidthStretch); + + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + ImGuiExt::TextFormatted("hex.builtin.footer.mcp.client_version"_lang); + ImGui::TableNextColumn(); + ImGuiExt::TextFormatted("{}", clientInfo.version); + + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + ImGuiExt::TextFormatted("hex.builtin.footer.mcp.protocol_version"_lang); + ImGui::TableNextColumn(); + ImGuiExt::TextFormatted("{}", clientInfo.protocolVersion); + + ImGui::EndTable(); + } + } else { + ImGui::PushTextWrapPos(200_scaled); + ImGuiExt::TextFormattedWrapped("hex.builtin.footer.mcp.not_connected"_lang); + ImGui::PopTextWrapPos(); + } + + ImGui::EndTooltip(); + } + } + } }); if (dbg::debugModeEnabled()) {