Unravel Engine C++ Reference
Loading...
Searching...
No Matches
mcp_panel.cpp
Go to the documentation of this file.
1#include "mcp_panel.h"
2
3#include "../panels_defs.h"
4
6
7#include <imgui/imgui.h>
8#include <imgui_widgets/tooltips.h>
9
10#include <chrono>
11#include <ctime>
12#include <iomanip>
13#include <sstream>
14
15namespace unravel
16{
17namespace
18{
19auto format_timestamp(std::chrono::system_clock::time_point tp) -> std::string
20{
21 const auto time = std::chrono::system_clock::to_time_t(tp);
22 std::tm local_tm{};
23#ifdef _WIN32
24 localtime_s(&local_tm, &time);
25#else
26 localtime_r(&time, &local_tm);
27#endif
28 std::ostringstream oss;
29 oss << std::put_time(&local_tm, "%H:%M:%S");
30 return oss.str();
31}
32} // namespace
33
35{
36}
37
39{
40}
41
45
47{
48 if(show_request_)
49 {
50 show_request_ = false;
51 show_ = true;
52 ImGui::SetNextWindowSize(ImGui::GetMainViewport()->Size * 0.45f, ImGuiCond_Once);
53 }
54
55 if(!show_)
56 {
57 return;
58 }
59
60 if(ImGui::Begin(name, &show_))
61 {
62 draw_ui(ctx);
63 }
64 ImGui::End();
65}
66
67void mcp_panel::show(bool s)
68{
69 show_request_ = s;
70}
71
72void mcp_panel::draw_ui(rtti::context& ctx)
73{
74 auto& mcp = ctx.get_cached<mcp_manager>();
75 const bool running = mcp.is_running();
76 const auto endpoint = mcp.get_endpoint_url();
77 const auto health = mcp.get_health_url();
78
79 ImGui::TextUnformatted("Status");
80 ImGui::SameLine();
81 if(running)
82 {
83 ImGui::TextColored(ImVec4(0.35f, 0.85f, 0.45f, 1.0f), ICON_MDI_LAN_CONNECT " Listening");
84 }
85 else
86 {
87 ImGui::TextColored(ImVec4(0.9f, 0.4f, 0.35f, 1.0f), ICON_MDI_LAN_DISCONNECT " Stopped");
88 }
89
90 ImGui::Separator();
91
92 ImGui::BeginTable("##mcp_info", 2, ImGuiTableFlags_SizingStretchProp | ImGuiTableFlags_NoSavedSettings);
93 ImGui::TableSetupColumn("label", ImGuiTableColumnFlags_WidthFixed, 110.0f);
94 ImGui::TableSetupColumn("value", ImGuiTableColumnFlags_WidthStretch);
95
96 ImGui::TableNextRow();
97 ImGui::TableSetColumnIndex(0);
98 ImGui::TextUnformatted("Endpoint");
99 ImGui::TableSetColumnIndex(1);
100 ImGui::TextUnformatted(endpoint.c_str());
101 ImGui::SameLine();
102 if(ImGui::SmallButton(ICON_MDI_CONTENT_COPY "##copy_endpoint"))
103 {
104 ImGui::SetClipboardText(endpoint.c_str());
105 }
106 ImGui::SetItemTooltipEx("Copy MCP endpoint URL");
107
108 ImGui::TableNextRow();
109 ImGui::TableSetColumnIndex(0);
110 ImGui::TextUnformatted("Health");
111 ImGui::TableSetColumnIndex(1);
112 ImGui::TextUnformatted(health.c_str());
113
114 ImGui::TableNextRow();
115 ImGui::TableSetColumnIndex(0);
116 ImGui::TextUnformatted("Bind");
117 ImGui::TableSetColumnIndex(1);
118 ImGui::Text("%s:%d (localhost only)", mcp.get_host(), mcp.get_port());
119
120 ImGui::TableNextRow();
121 ImGui::TableSetColumnIndex(0);
122 ImGui::TextUnformatted("Tools");
123 ImGui::TableSetColumnIndex(1);
124 ImGui::Text("%zu registered", mcp.get_tool_count());
125
126 ImGui::TableNextRow();
127 ImGui::TableSetColumnIndex(0);
128 ImGui::TextUnformatted("Requests");
129 ImGui::TableSetColumnIndex(1);
130 ImGui::Text("%llu | tool calls: %llu | errors: %llu",
131 static_cast<unsigned long long>(mcp.get_request_count()),
132 static_cast<unsigned long long>(mcp.get_tool_call_count()),
133 static_cast<unsigned long long>(mcp.get_error_count()));
134
135 ImGui::EndTable();
136
137 ImGui::Spacing();
138 if(running)
139 {
140 if(ImGui::Button(ICON_MDI_STOP " Stop"))
141 {
142 mcp.stop();
143 }
144 }
145 else
146 {
147 if(ImGui::Button(ICON_MDI_PLAY " Start"))
148 {
149 mcp.start();
150 }
151 }
152 ImGui::SameLine();
153 if(ImGui::Button(ICON_MDI_DELETE " Clear Log"))
154 {
155 mcp.clear_activity();
156 }
157 ImGui::SameLine();
158 ImGui::Checkbox("Auto-scroll", &auto_scroll_);
159
160 ImGui::Separator();
161 ImGui::TextUnformatted("Activity");
162
163 const auto activity = mcp.snapshot_activity();
164 ImGui::BeginChild("##mcp_activity", ImVec2(0, 0), ImGuiChildFlags_Borders, ImGuiWindowFlags_HorizontalScrollbar);
165
166 if(activity.empty())
167 {
168 ImGui::TextDisabled("No MCP activity yet. Connect a client to %s", endpoint.c_str());
169 }
170 else
171 {
172 ImGuiListClipper clipper;
173 clipper.Begin(static_cast<int>(activity.size()));
174 while(clipper.Step())
175 {
176 for(int i = clipper.DisplayStart; i < clipper.DisplayEnd; ++i)
177 {
178 const auto& entry = activity[static_cast<size_t>(i)];
179 ImGui::PushID(i);
180
181 const ImVec4 color = entry.is_error ? ImVec4(0.95f, 0.45f, 0.4f, 1.0f)
182 : ImVec4(0.85f, 0.85f, 0.85f, 1.0f);
183 ImGui::TextColored(ImVec4(0.55f, 0.55f, 0.6f, 1.0f), "%s", format_timestamp(entry.timestamp).c_str());
184 ImGui::SameLine();
185 ImGui::TextColored(ImVec4(0.45f, 0.7f, 0.95f, 1.0f), "[%s]", entry.category.c_str());
186 ImGui::SameLine();
187 ImGui::TextColored(color, "%s", entry.message.c_str());
188
189 ImGui::PopID();
190 }
191 }
192
193 if(auto_scroll_ && ImGui::GetScrollY() >= ImGui::GetScrollMaxY() - 1.0f)
194 {
195 ImGui::SetScrollHereY(1.0f);
196 }
197 }
198
199 ImGui::EndChild();
200}
201
202} // namespace unravel
bool running
auto is_running() const -> bool
void deinit(rtti::context &ctx)
Definition mcp_panel.cpp:42
void init(rtti::context &ctx)
Definition mcp_panel.cpp:38
mcp_panel(imgui_panels *parent)
Definition mcp_panel.cpp:34
void show(bool s)
Definition mcp_panel.cpp:67
void on_frame_ui_render(rtti::context &ctx, const char *name)
Definition mcp_panel.cpp:46
std::string name
Definition hub.cpp:33
#define ICON_MDI_STOP
#define ICON_MDI_CONTENT_COPY
#define ICON_MDI_LAN_CONNECT
#define ICON_MDI_DELETE
#define ICON_MDI_PLAY
#define ICON_MDI_LAN_DISCONNECT
std::vector< size_t > parent_
std::vector< math::color > color
auto get_cached() -> T &
Definition context.hpp:49