@@ -26,10 +26,24 @@ using namespace llvm;
2626
2727LLDB_PLUGIN_DEFINE (ProtocolServerMCP)
2828
29+ static constexpr size_t kChunkSize = 1024;
2930static constexpr llvm::StringLiteral kName = " lldb-mcp" ;
3031static constexpr llvm::StringLiteral kVersion = " 0.1.0" ;
3132
32- ProtocolServerMCP::ProtocolServerMCP () : ProtocolServer() {}
33+ ProtocolServerMCP::ProtocolServerMCP ()
34+ : ProtocolServer(),
35+ lldb_protocol::mcp::Server(std::string(kName ), std::string(kVersion )) {
36+ AddNotificationHandler (" notifications/initialized" ,
37+ [](const lldb_protocol::mcp::Notification &) {
38+ LLDB_LOG (GetLog (LLDBLog::Host),
39+ " MCP initialization complete" );
40+ });
41+
42+ AddTool (
43+ std::make_unique<CommandTool>(" lldb_command" , " Run an lldb command." ));
44+
45+ AddResourceProvider (std::make_unique<DebuggerResourceProvider>());
46+ }
3347
3448ProtocolServerMCP::~ProtocolServerMCP () { llvm::consumeError (Stop ()); }
3549
@@ -50,37 +64,57 @@ llvm::StringRef ProtocolServerMCP::GetPluginDescriptionStatic() {
5064 return " MCP Server." ;
5165}
5266
53- void ProtocolServerMCP::Extend (lldb_protocol::mcp::Server &server) const {
54- server.AddNotificationHandler (" notifications/initialized" ,
55- [](const lldb_protocol::mcp::Notification &) {
56- LLDB_LOG (GetLog (LLDBLog::Host),
57- " MCP initialization complete" );
58- });
59- server.AddTool (
60- std::make_unique<CommandTool>(" lldb_command" , " Run an lldb command." ));
61- server.AddResourceProvider (std::make_unique<DebuggerResourceProvider>());
62- }
63-
6467void ProtocolServerMCP::AcceptCallback (std::unique_ptr<Socket> socket) {
65- Log *log = GetLog (LLDBLog::Host);
66- std::string client_name = llvm::formatv (" client_{0}" , m_instances.size () + 1 );
67- LLDB_LOG (log, " New MCP client connected: {0}" , client_name);
68+ LLDB_LOG (GetLog (LLDBLog::Host), " New MCP client ({0}) connected" ,
69+ m_clients.size () + 1 );
6870
6971 lldb::IOObjectSP io_sp = std::move (socket);
70- auto transport_up = std::make_unique<lldb_protocol::mcp::MCPTransport>(
71- io_sp, io_sp, std::move (client_name), [&](llvm::StringRef message) {
72- LLDB_LOG (GetLog (LLDBLog::Host), " {0}" , message);
73- });
74- auto instance_up = std::make_unique<lldb_protocol::mcp::Server>(
75- std::string (kName ), std::string (kVersion ), std::move (transport_up),
76- m_loop);
77- Extend (*instance_up);
78- llvm::Error error = instance_up->Run ();
79- if (error) {
80- LLDB_LOG_ERROR (log, std::move (error), " Failed to run MCP server: {0}" );
72+ auto client_up = std::make_unique<Client>();
73+ client_up->io_sp = io_sp;
74+ Client *client = client_up.get ();
75+
76+ Status status;
77+ auto read_handle_up = m_loop.RegisterReadObject (
78+ io_sp,
79+ [this , client](MainLoopBase &loop) {
80+ if (llvm::Error error = ReadCallback (*client)) {
81+ LLDB_LOG_ERROR (GetLog (LLDBLog::Host), std::move (error), " {0}" );
82+ client->read_handle_up .reset ();
83+ }
84+ },
85+ status);
86+ if (status.Fail ())
8187 return ;
88+
89+ client_up->read_handle_up = std::move (read_handle_up);
90+ m_clients.emplace_back (std::move (client_up));
91+ }
92+
93+ llvm::Error ProtocolServerMCP::ReadCallback (Client &client) {
94+ char chunk[kChunkSize ];
95+ size_t bytes_read = sizeof (chunk);
96+ if (Status status = client.io_sp ->Read (chunk, bytes_read); status.Fail ())
97+ return status.takeError ();
98+ client.buffer .append (chunk, bytes_read);
99+
100+ for (std::string::size_type pos;
101+ (pos = client.buffer .find (' \n ' )) != std::string::npos;) {
102+ llvm::Expected<std::optional<lldb_protocol::mcp::Message>> message =
103+ HandleData (StringRef (client.buffer .data (), pos));
104+ client.buffer = client.buffer .erase (0 , pos + 1 );
105+ if (!message)
106+ return message.takeError ();
107+
108+ if (*message) {
109+ std::string Output;
110+ llvm::raw_string_ostream OS (Output);
111+ OS << llvm::formatv (" {0}" , toJSON (**message)) << ' \n ' ;
112+ size_t num_bytes = Output.size ();
113+ return client.io_sp ->Write (Output.data (), num_bytes).takeError ();
114+ }
82115 }
83- m_instances.push_back (std::move (instance_up));
116+
117+ return llvm::Error::success ();
84118}
85119
86120llvm::Error ProtocolServerMCP::Start (ProtocolServer::Connection connection) {
@@ -124,11 +158,27 @@ llvm::Error ProtocolServerMCP::Stop() {
124158
125159 // Stop the main loop.
126160 m_loop.AddPendingCallback (
127- [](lldb_private:: MainLoopBase &loop) { loop.RequestTermination (); });
161+ [](MainLoopBase &loop) { loop.RequestTermination (); });
128162
129163 // Wait for the main loop to exit.
130164 if (m_loop_thread.joinable ())
131165 m_loop_thread.join ();
132166
167+ {
168+ std::lock_guard<std::mutex> guard (m_mutex);
169+ m_listener.reset ();
170+ m_listen_handlers.clear ();
171+ m_clients.clear ();
172+ }
173+
133174 return llvm::Error::success ();
134175}
176+
177+ lldb_protocol::mcp::Capabilities ProtocolServerMCP::GetCapabilities () {
178+ lldb_protocol::mcp::Capabilities capabilities;
179+ capabilities.tools .listChanged = true ;
180+ // FIXME: Support sending notifications when a debugger/target are
181+ // added/removed.
182+ capabilities.resources .listChanged = false ;
183+ return capabilities;
184+ }
0 commit comments