-
Notifications
You must be signed in to change notification settings - Fork 14.9k
Revert "[lldb] Adopt JSONTransport in the MCP Server" #155280
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
+127
−190
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This reverts commit a49df8e.
@llvm/pr-subscribers-lldb Author: Jonas Devlieghere (JDevlieghere) ChangesReverts llvm/llvm-project#155034 Full diff: https://github.com/llvm/llvm-project/pull/155280.diff 5 Files Affected:
diff --git a/lldb/include/lldb/Protocol/MCP/Server.h b/lldb/include/lldb/Protocol/MCP/Server.h
index 382f9a4731dd4..2ac05880de86b 100644
--- a/lldb/include/lldb/Protocol/MCP/Server.h
+++ b/lldb/include/lldb/Protocol/MCP/Server.h
@@ -9,8 +9,6 @@
#ifndef LLDB_PROTOCOL_MCP_SERVER_H
#define LLDB_PROTOCOL_MCP_SERVER_H
-#include "lldb/Host/JSONTransport.h"
-#include "lldb/Host/MainLoop.h"
#include "lldb/Protocol/MCP/Protocol.h"
#include "lldb/Protocol/MCP/Resource.h"
#include "lldb/Protocol/MCP/Tool.h"
@@ -20,52 +18,26 @@
namespace lldb_protocol::mcp {
-class MCPTransport final
- : public lldb_private::JSONRPCTransport<Request, Response, Notification> {
+class Server {
public:
- using LogCallback = std::function<void(llvm::StringRef message)>;
-
- MCPTransport(lldb::IOObjectSP in, lldb::IOObjectSP out,
- std::string client_name, LogCallback log_callback = {})
- : JSONRPCTransport(in, out), m_client_name(std::move(client_name)),
- m_log_callback(log_callback) {}
- virtual ~MCPTransport() = default;
-
- void Log(llvm::StringRef message) override {
- if (m_log_callback)
- m_log_callback(llvm::formatv("{0}: {1}", m_client_name, message).str());
- }
-
-private:
- std::string m_client_name;
- LogCallback m_log_callback;
-};
-
-class Server : public MCPTransport::MessageHandler {
-public:
- Server(std::string name, std::string version,
- std::unique_ptr<MCPTransport> transport_up,
- lldb_private::MainLoop &loop);
- ~Server() = default;
-
- using NotificationHandler = std::function<void(const Notification &)>;
+ Server(std::string name, std::string version);
+ virtual ~Server() = default;
void AddTool(std::unique_ptr<Tool> tool);
void AddResourceProvider(std::unique_ptr<ResourceProvider> resource_provider);
- void AddNotificationHandler(llvm::StringRef method,
- NotificationHandler handler);
-
- llvm::Error Run();
protected:
- Capabilities GetCapabilities();
+ virtual Capabilities GetCapabilities() = 0;
using RequestHandler =
std::function<llvm::Expected<Response>(const Request &)>;
+ using NotificationHandler = std::function<void(const Notification &)>;
void AddRequestHandlers();
void AddRequestHandler(llvm::StringRef method, RequestHandler handler);
+ void AddNotificationHandler(llvm::StringRef method,
+ NotificationHandler handler);
llvm::Expected<std::optional<Message>> HandleData(llvm::StringRef data);
@@ -80,23 +52,12 @@ class Server : public MCPTransport::MessageHandler {
llvm::Expected<Response> ResourcesListHandler(const Request &);
llvm::Expected<Response> ResourcesReadHandler(const Request &);
- void Received(const Request &) override;
- void Received(const Response &) override;
- void Received(const Notification &) override;
- void OnError(llvm::Error) override;
- void OnClosed() override;
-
- void TerminateLoop();
-
std::mutex m_mutex;
private:
const std::string m_name;
const std::string m_version;
- std::unique_ptr<MCPTransport> m_transport_up;
- lldb_private::MainLoop &m_loop;
-
llvm::StringMap<std::unique_ptr<Tool>> m_tools;
std::vector<std::unique_ptr<ResourceProvider>> m_resource_providers;
diff --git a/lldb/source/Plugins/Protocol/MCP/ProtocolServerMCP.cpp b/lldb/source/Plugins/Protocol/MCP/ProtocolServerMCP.cpp
index 57132534cf680..c359663239dcc 100644
--- a/lldb/source/Plugins/Protocol/MCP/ProtocolServerMCP.cpp
+++ b/lldb/source/Plugins/Protocol/MCP/ProtocolServerMCP.cpp
@@ -26,10 +26,24 @@ using namespace llvm;
LLDB_PLUGIN_DEFINE(ProtocolServerMCP)
+static constexpr size_t kChunkSize = 1024;
static constexpr llvm::StringLiteral kName = "lldb-mcp";
static constexpr llvm::StringLiteral kVersion = "0.1.0";
-ProtocolServerMCP::ProtocolServerMCP() : ProtocolServer() {}
+ProtocolServerMCP::ProtocolServerMCP()
+ : ProtocolServer(),
+ lldb_protocol::mcp::Server(std::string(kName), std::string(kVersion)) {
+ AddNotificationHandler("notifications/initialized",
+ [](const lldb_protocol::mcp::Notification &) {
+ LLDB_LOG(GetLog(LLDBLog::Host),
+ "MCP initialization complete");
+ });
+
+ AddTool(
+ std::make_unique<CommandTool>("lldb_command", "Run an lldb command."));
+
+ AddResourceProvider(std::make_unique<DebuggerResourceProvider>());
+}
ProtocolServerMCP::~ProtocolServerMCP() { llvm::consumeError(Stop()); }
@@ -50,37 +64,57 @@ llvm::StringRef ProtocolServerMCP::GetPluginDescriptionStatic() {
return "MCP Server.";
}
-void ProtocolServerMCP::Extend(lldb_protocol::mcp::Server &server) const {
- server.AddNotificationHandler("notifications/initialized",
- [](const lldb_protocol::mcp::Notification &) {
- LLDB_LOG(GetLog(LLDBLog::Host),
- "MCP initialization complete");
- });
- server.AddTool(
- std::make_unique<CommandTool>("lldb_command", "Run an lldb command."));
- server.AddResourceProvider(std::make_unique<DebuggerResourceProvider>());
-}
-
void ProtocolServerMCP::AcceptCallback(std::unique_ptr<Socket> socket) {
- Log *log = GetLog(LLDBLog::Host);
- std::string client_name = llvm::formatv("client_{0}", m_instances.size() + 1);
- LLDB_LOG(log, "New MCP client connected: {0}", client_name);
+ LLDB_LOG(GetLog(LLDBLog::Host), "New MCP client ({0}) connected",
+ m_clients.size() + 1);
lldb::IOObjectSP io_sp = std::move(socket);
- auto transport_up = std::make_unique<lldb_protocol::mcp::MCPTransport>(
- io_sp, io_sp, std::move(client_name), [&](llvm::StringRef message) {
- LLDB_LOG(GetLog(LLDBLog::Host), "{0}", message);
- });
- auto instance_up = std::make_unique<lldb_protocol::mcp::Server>(
- std::string(kName), std::string(kVersion), std::move(transport_up),
- m_loop);
- Extend(*instance_up);
- llvm::Error error = instance_up->Run();
- if (error) {
- LLDB_LOG_ERROR(log, std::move(error), "Failed to run MCP server: {0}");
+ auto client_up = std::make_unique<Client>();
+ client_up->io_sp = io_sp;
+ Client *client = client_up.get();
+
+ Status status;
+ auto read_handle_up = m_loop.RegisterReadObject(
+ io_sp,
+ [this, client](MainLoopBase &loop) {
+ if (llvm::Error error = ReadCallback(*client)) {
+ LLDB_LOG_ERROR(GetLog(LLDBLog::Host), std::move(error), "{0}");
+ client->read_handle_up.reset();
+ }
+ },
+ status);
+ if (status.Fail())
return;
+
+ client_up->read_handle_up = std::move(read_handle_up);
+ m_clients.emplace_back(std::move(client_up));
+}
+
+llvm::Error ProtocolServerMCP::ReadCallback(Client &client) {
+ char chunk[kChunkSize];
+ size_t bytes_read = sizeof(chunk);
+ if (Status status = client.io_sp->Read(chunk, bytes_read); status.Fail())
+ return status.takeError();
+ client.buffer.append(chunk, bytes_read);
+
+ for (std::string::size_type pos;
+ (pos = client.buffer.find('\n')) != std::string::npos;) {
+ llvm::Expected<std::optional<lldb_protocol::mcp::Message>> message =
+ HandleData(StringRef(client.buffer.data(), pos));
+ client.buffer = client.buffer.erase(0, pos + 1);
+ if (!message)
+ return message.takeError();
+
+ if (*message) {
+ std::string Output;
+ llvm::raw_string_ostream OS(Output);
+ OS << llvm::formatv("{0}", toJSON(**message)) << '\n';
+ size_t num_bytes = Output.size();
+ return client.io_sp->Write(Output.data(), num_bytes).takeError();
+ }
}
- m_instances.push_back(std::move(instance_up));
+
+ return llvm::Error::success();
}
llvm::Error ProtocolServerMCP::Start(ProtocolServer::Connection connection) {
@@ -124,11 +158,27 @@ llvm::Error ProtocolServerMCP::Stop() {
// Stop the main loop.
m_loop.AddPendingCallback(
- [](lldb_private::MainLoopBase &loop) { loop.RequestTermination(); });
+ [](MainLoopBase &loop) { loop.RequestTermination(); });
// Wait for the main loop to exit.
if (m_loop_thread.joinable())
m_loop_thread.join();
+ {
+ std::lock_guard<std::mutex> guard(m_mutex);
+ m_listener.reset();
+ m_listen_handlers.clear();
+ m_clients.clear();
+ }
+
return llvm::Error::success();
}
+
+lldb_protocol::mcp::Capabilities ProtocolServerMCP::GetCapabilities() {
+ lldb_protocol::mcp::Capabilities capabilities;
+ capabilities.tools.listChanged = true;
+ // FIXME: Support sending notifications when a debugger/target are
+ // added/removed.
+ capabilities.resources.listChanged = false;
+ return capabilities;
+}
diff --git a/lldb/source/Plugins/Protocol/MCP/ProtocolServerMCP.h b/lldb/source/Plugins/Protocol/MCP/ProtocolServerMCP.h
index fc650ffe0dfa7..7fe909a728b85 100644
--- a/lldb/source/Plugins/Protocol/MCP/ProtocolServerMCP.h
+++ b/lldb/source/Plugins/Protocol/MCP/ProtocolServerMCP.h
@@ -18,7 +18,8 @@
namespace lldb_private::mcp {
-class ProtocolServerMCP : public ProtocolServer {
+class ProtocolServerMCP : public ProtocolServer,
+ public lldb_protocol::mcp::Server {
public:
ProtocolServerMCP();
virtual ~ProtocolServerMCP() override;
@@ -38,24 +39,26 @@ class ProtocolServerMCP : public ProtocolServer {
Socket *GetSocket() const override { return m_listener.get(); }
-protected:
- // This adds tools and resource providers that
- // are specific to this server. Overridable by the unit tests.
- virtual void Extend(lldb_protocol::mcp::Server &server) const;
-
private:
void AcceptCallback(std::unique_ptr<Socket> socket);
+ lldb_protocol::mcp::Capabilities GetCapabilities() override;
+
bool m_running = false;
- lldb_private::MainLoop m_loop;
+ MainLoop m_loop;
std::thread m_loop_thread;
- std::mutex m_mutex;
std::unique_ptr<Socket> m_listener;
-
std::vector<MainLoopBase::ReadHandleUP> m_listen_handlers;
- std::vector<std::unique_ptr<lldb_protocol::mcp::Server>> m_instances;
+
+ struct Client {
+ lldb::IOObjectSP io_sp;
+ MainLoopBase::ReadHandleUP read_handle_up;
+ std::string buffer;
+ };
+ llvm::Error ReadCallback(Client &client);
+ std::vector<std::unique_ptr<Client>> m_clients;
};
} // namespace lldb_private::mcp
diff --git a/lldb/source/Protocol/MCP/Server.cpp b/lldb/source/Protocol/MCP/Server.cpp
index 3713e8e46c5d6..a9c1482e3e378 100644
--- a/lldb/source/Protocol/MCP/Server.cpp
+++ b/lldb/source/Protocol/MCP/Server.cpp
@@ -12,11 +12,8 @@
using namespace lldb_protocol::mcp;
using namespace llvm;
-Server::Server(std::string name, std::string version,
- std::unique_ptr<MCPTransport> transport_up,
- lldb_private::MainLoop &loop)
- : m_name(std::move(name)), m_version(std::move(version)),
- m_transport_up(std::move(transport_up)), m_loop(loop) {
+Server::Server(std::string name, std::string version)
+ : m_name(std::move(name)), m_version(std::move(version)) {
AddRequestHandlers();
}
@@ -235,71 +232,3 @@ llvm::Expected<Response> Server::ResourcesReadHandler(const Request &request) {
llvm::formatv("no resource handler for uri: {0}", uri_str).str(),
MCPError::kResourceNotFound);
}
-
-Capabilities Server::GetCapabilities() {
- lldb_protocol::mcp::Capabilities capabilities;
- capabilities.tools.listChanged = true;
- // FIXME: Support sending notifications when a debugger/target are
- // added/removed.
- capabilities.resources.listChanged = false;
- return capabilities;
-}
-
-llvm::Error Server::Run() {
- auto handle = m_transport_up->RegisterMessageHandler(m_loop, *this);
- if (!handle)
- return handle.takeError();
-
- lldb_private::Status status = m_loop.Run();
- if (status.Fail())
- return status.takeError();
-
- return llvm::Error::success();
-}
-
-void Server::Received(const Request &request) {
- auto SendResponse = [this](const Response &response) {
- if (llvm::Error error = m_transport_up->Send(response))
- m_transport_up->Log(llvm::toString(std::move(error)));
- };
-
- llvm::Expected<Response> response = Handle(request);
- if (response)
- return SendResponse(*response);
-
- lldb_protocol::mcp::Error protocol_error;
- llvm::handleAllErrors(
- response.takeError(),
- [&](const MCPError &err) { protocol_error = err.toProtocolError(); },
- [&](const llvm::ErrorInfoBase &err) {
- protocol_error.code = MCPError::kInternalError;
- protocol_error.message = err.message();
- });
- Response error_response;
- error_response.id = request.id;
- error_response.result = std::move(protocol_error);
- SendResponse(error_response);
-}
-
-void Server::Received(const Response &response) {
- m_transport_up->Log("unexpected MCP message: response");
-}
-
-void Server::Received(const Notification ¬ification) {
- Handle(notification);
-}
-
-void Server::OnError(llvm::Error error) {
- m_transport_up->Log(llvm::toString(std::move(error)));
- TerminateLoop();
-}
-
-void Server::OnClosed() {
- m_transport_up->Log("EOF");
- TerminateLoop();
-}
-
-void Server::TerminateLoop() {
- m_loop.AddPendingCallback(
- [](lldb_private::MainLoopBase &loop) { loop.RequestTermination(); });
-}
diff --git a/lldb/unittests/ProtocolServer/ProtocolMCPServerTest.cpp b/lldb/unittests/ProtocolServer/ProtocolMCPServerTest.cpp
index 83a42bfb6970c..18112428950ce 100644
--- a/lldb/unittests/ProtocolServer/ProtocolMCPServerTest.cpp
+++ b/lldb/unittests/ProtocolServer/ProtocolMCPServerTest.cpp
@@ -39,20 +39,12 @@ using testing::_;
namespace {
class TestProtocolServerMCP : public lldb_private::mcp::ProtocolServerMCP {
public:
+ using ProtocolServerMCP::AddNotificationHandler;
+ using ProtocolServerMCP::AddRequestHandler;
+ using ProtocolServerMCP::AddResourceProvider;
+ using ProtocolServerMCP::AddTool;
using ProtocolServerMCP::GetSocket;
using ProtocolServerMCP::ProtocolServerMCP;
-
- using ExtendCallback =
- std::function<void(lldb_protocol::mcp::Server &server)>;
-
- virtual void Extend(lldb_protocol::mcp::Server &server) const override {
- if (m_extend_callback)
- m_extend_callback(server);
- };
-
- void Extend(ExtendCallback callback) { m_extend_callback = callback; }
-
- ExtendCallback m_extend_callback;
};
using Message = typename Transport<Request, Response, Notification>::Message;
@@ -191,10 +183,8 @@ class ProtocolServerMCPTest : public ::testing::Test {
connection.protocol = Socket::SocketProtocol::ProtocolTcp;
connection.name = llvm::formatv("{0}:0", k_localhost).str();
m_server_up = std::make_unique<TestProtocolServerMCP>();
- m_server_up->Extend([&](auto &server) {
- server.AddTool(std::make_unique<TestTool>("test", "test tool"));
- server.AddResourceProvider(std::make_unique<TestResourceProvider>());
- });
+ m_server_up->AddTool(std::make_unique<TestTool>("test", "test tool"));
+ m_server_up->AddResourceProvider(std::make_unique<TestResourceProvider>());
ASSERT_THAT_ERROR(m_server_up->Start(connection), llvm::Succeeded());
// Connect to the server over a TCP socket.
@@ -243,10 +233,20 @@ TEST_F(ProtocolServerMCPTest, ToolsList) {
test_tool.description = "test tool";
test_tool.inputSchema = json::Object{{"type", "object"}};
+ ToolDefinition lldb_command_tool;
+ lldb_command_tool.description = "Run an lldb command.";
+ lldb_command_tool.name = "lldb_command";
+ lldb_command_tool.inputSchema = json::Object{
+ {"type", "object"},
+ {"properties",
+ json::Object{{"arguments", json::Object{{"type", "string"}}},
+ {"debugger_id", json::Object{{"type", "number"}}}}},
+ {"required", json::Array{"debugger_id"}}};
Response response;
response.id = "one";
response.result = json::Object{
- {"tools", json::Array{std::move(test_tool)}},
+ {"tools",
+ json::Array{std::move(test_tool), std::move(lldb_command_tool)}},
};
ASSERT_THAT_ERROR(Write(request), llvm::Succeeded());
@@ -281,9 +281,7 @@ TEST_F(ProtocolServerMCPTest, ToolsCall) {
}
TEST_F(ProtocolServerMCPTest, ToolsCallError) {
- m_server_up->Extend([&](auto &server) {
- server.AddTool(std::make_unique<ErrorTool>("error", "error tool"));
- });
+ m_server_up->AddTool(std::make_unique<ErrorTool>("error", "error tool"));
llvm::StringLiteral request =
R"json({"method":"tools/call","params":{"name":"error","arguments":{"arguments":"foo","debugger_id":0}},"jsonrpc":"2.0","id":11})json";
@@ -298,9 +296,7 @@ TEST_F(ProtocolServerMCPTest, ToolsCallError) {
}
TEST_F(ProtocolServerMCPTest, ToolsCallFail) {
- m_server_up->Extend([&](auto &server) {
- server.AddTool(std::make_unique<FailTool>("fail", "fail tool"));
- });
+ m_server_up->AddTool(std::make_unique<FailTool>("fail", "fail tool"));
llvm::StringLiteral request =
R"json({"method":"tools/call","params":{"name":"fail","arguments":{"arguments":"foo","debugger_id":0}},"jsonrpc":"2.0","id":11})json";
@@ -319,16 +315,14 @@ TEST_F(ProtocolServerMCPTest, NotificationInitialized) {
std::condition_variable cv;
std::mutex mutex;
- m_server_up->Extend([&](auto &server) {
- server.AddNotificationHandler("notifications/initialized",
- [&](const Notification ¬ification) {
- {
- std::lock_guard<std::mutex> lock(mutex);
- handler_called = true;
- }
- cv.notify_all();
- });
- });
+ m_server_up->AddNotificationHandler(
+ "notifications/initialized", [&](const Notification ¬ification) {
+ {
+ std::lock_guard<std::mutex> lock(mutex);
+ handler_called = true;
+ }
+ cv.notify_all();
+ });
llvm::StringLiteral request =
R"json({"method":"notifications/initialized","jsonrpc":"2.0"})json";
|
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Reverts #155034 because the unit tests are flakey on the Debian bot: https://lab.llvm.org/buildbot/#/builders/162.