Skip to content

[lldb] Move MCP protocol into its own library (NFC) #152059

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

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,15 @@
//
//===----------------------------------------------------------------------===//

#ifndef LLDB_PLUGINS_PROTOCOL_MCP_PROTOCOL_H
#define LLDB_PLUGINS_PROTOCOL_MCP_PROTOCOL_H
#ifndef LLDB_PROTOCOL_MCP_PROTOCOL_H
#define LLDB_PROTOCOL_MCP_PROTOCOL_H

#include "llvm/Support/JSON.h"
#include <optional>
#include <string>
#include <variant>

namespace lldb_private::mcp::protocol {
namespace lldb_protocol::mcp {

static llvm::StringLiteral kVersion = "2024-11-05";

Expand Down Expand Up @@ -183,6 +183,6 @@ llvm::json::Value toJSON(const Message &);

using ToolArguments = std::variant<std::monostate, llvm::json::Value>;

} // namespace lldb_private::mcp::protocol
} // namespace lldb_protocol::mcp

#endif
1 change: 1 addition & 0 deletions lldb/source/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ add_subdirectory(Host)
add_subdirectory(Initialization)
add_subdirectory(Interpreter)
add_subdirectory(Plugins)
add_subdirectory(Protocol)
add_subdirectory(Symbol)
add_subdirectory(Target)
add_subdirectory(Utility)
Expand Down
2 changes: 1 addition & 1 deletion lldb/source/Plugins/Protocol/MCP/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
add_lldb_library(lldbPluginProtocolServerMCP PLUGIN
MCPError.cpp
Protocol.cpp
ProtocolServerMCP.cpp
Resource.cpp
Tool.cpp
Expand All @@ -10,5 +9,6 @@ add_lldb_library(lldbPluginProtocolServerMCP PLUGIN

LINK_LIBS
lldbHost
lldbProtocolMCP
lldbUtility
)
4 changes: 2 additions & 2 deletions lldb/source/Plugins/Protocol/MCP/MCPError.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ std::error_code MCPError::convertToErrorCode() const {
return llvm::inconvertibleErrorCode();
}

protocol::Error MCPError::toProtcolError() const {
protocol::Error error;
lldb_protocol::mcp::Error MCPError::toProtcolError() const {
lldb_protocol::mcp::Error error;
error.error.code = m_error_code;
error.error.message = m_message;
return error;
Expand Down
4 changes: 2 additions & 2 deletions lldb/source/Plugins/Protocol/MCP/MCPError.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
//
//===----------------------------------------------------------------------===//

#include "Protocol.h"
#include "lldb/Protocol/MCP/Protocol.h"
#include "llvm/Support/Error.h"
#include "llvm/Support/FormatVariadic.h"
#include <string>
Expand All @@ -24,7 +24,7 @@ class MCPError : public llvm::ErrorInfo<MCPError> {

const std::string &getMessage() const { return m_message; }

protocol::Error toProtcolError() const;
lldb_protocol::mcp::Error toProtcolError() const;

static constexpr int64_t kResourceNotFound = -32002;
static constexpr int64_t kInternalError = -32603;
Expand Down
90 changes: 48 additions & 42 deletions lldb/source/Plugins/Protocol/MCP/ProtocolServerMCP.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,11 @@ ProtocolServerMCP::ProtocolServerMCP() : ProtocolServer() {
AddRequestHandler("resources/read",
std::bind(&ProtocolServerMCP::ResourcesReadHandler, this,
std::placeholders::_1));
AddNotificationHandler(
"notifications/initialized", [](const protocol::Notification &) {
LLDB_LOG(GetLog(LLDBLog::Host), "MCP initialization complete");
});
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."));
Expand All @@ -72,11 +73,11 @@ llvm::StringRef ProtocolServerMCP::GetPluginDescriptionStatic() {
return "MCP Server.";
}

llvm::Expected<protocol::Response>
ProtocolServerMCP::Handle(protocol::Request request) {
llvm::Expected<lldb_protocol::mcp::Response>
ProtocolServerMCP::Handle(lldb_protocol::mcp::Request request) {
auto it = m_request_handlers.find(request.method);
if (it != m_request_handlers.end()) {
llvm::Expected<protocol::Response> response = it->second(request);
llvm::Expected<lldb_protocol::mcp::Response> response = it->second(request);
if (!response)
return response;
response->id = request.id;
Expand All @@ -87,7 +88,7 @@ ProtocolServerMCP::Handle(protocol::Request request) {
llvm::formatv("no handler for request: {0}", request.method).str());
}

void ProtocolServerMCP::Handle(protocol::Notification notification) {
void ProtocolServerMCP::Handle(lldb_protocol::mcp::Notification notification) {
auto it = m_notification_handlers.find(notification.method);
if (it != m_notification_handlers.end()) {
it->second(notification);
Expand Down Expand Up @@ -133,7 +134,7 @@ llvm::Error ProtocolServerMCP::ReadCallback(Client &client) {

for (std::string::size_type pos;
(pos = client.buffer.find('\n')) != std::string::npos;) {
llvm::Expected<std::optional<protocol::Message>> message =
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)
Expand Down Expand Up @@ -208,19 +209,19 @@ llvm::Error ProtocolServerMCP::Stop() {
return llvm::Error::success();
}

llvm::Expected<std::optional<protocol::Message>>
llvm::Expected<std::optional<lldb_protocol::mcp::Message>>
ProtocolServerMCP::HandleData(llvm::StringRef data) {
auto message = llvm::json::parse<protocol::Message>(/*JSON=*/data);
auto message = llvm::json::parse<lldb_protocol::mcp::Message>(/*JSON=*/data);
if (!message)
return message.takeError();

if (const protocol::Request *request =
std::get_if<protocol::Request>(&(*message))) {
llvm::Expected<protocol::Response> response = Handle(*request);
if (const lldb_protocol::mcp::Request *request =
std::get_if<lldb_protocol::mcp::Request>(&(*message))) {
llvm::Expected<lldb_protocol::mcp::Response> response = Handle(*request);

// Handle failures by converting them into an Error message.
if (!response) {
protocol::Error protocol_error;
lldb_protocol::mcp::Error protocol_error;
llvm::handleAllErrors(
response.takeError(),
[&](const MCPError &err) { protocol_error = err.toProtcolError(); },
Expand All @@ -235,23 +236,23 @@ ProtocolServerMCP::HandleData(llvm::StringRef data) {
return *response;
}

if (const protocol::Notification *notification =
std::get_if<protocol::Notification>(&(*message))) {
if (const lldb_protocol::mcp::Notification *notification =
std::get_if<lldb_protocol::mcp::Notification>(&(*message))) {
Handle(*notification);
return std::nullopt;
}

if (std::get_if<protocol::Error>(&(*message)))
if (std::get_if<lldb_protocol::mcp::Error>(&(*message)))
return llvm::createStringError("unexpected MCP message: error");

if (std::get_if<protocol::Response>(&(*message)))
if (std::get_if<lldb_protocol::mcp::Response>(&(*message)))
return llvm::createStringError("unexpected MCP message: response");

llvm_unreachable("all message types handled");
}

protocol::Capabilities ProtocolServerMCP::GetCapabilities() {
protocol::Capabilities capabilities;
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.
Expand Down Expand Up @@ -288,20 +289,22 @@ void ProtocolServerMCP::AddNotificationHandler(llvm::StringRef method,
m_notification_handlers[method] = std::move(handler);
}

llvm::Expected<protocol::Response>
ProtocolServerMCP::InitializeHandler(const protocol::Request &request) {
protocol::Response response;
llvm::Expected<lldb_protocol::mcp::Response>
ProtocolServerMCP::InitializeHandler(
const lldb_protocol::mcp::Request &request) {
lldb_protocol::mcp::Response response;
response.result.emplace(llvm::json::Object{
{"protocolVersion", protocol::kVersion},
{"protocolVersion", lldb_protocol::mcp::kVersion},
{"capabilities", GetCapabilities()},
{"serverInfo",
llvm::json::Object{{"name", kName}, {"version", kVersion}}}});
return response;
}

llvm::Expected<protocol::Response>
ProtocolServerMCP::ToolsListHandler(const protocol::Request &request) {
protocol::Response response;
llvm::Expected<lldb_protocol::mcp::Response>
ProtocolServerMCP::ToolsListHandler(
const lldb_protocol::mcp::Request &request) {
lldb_protocol::mcp::Response response;

llvm::json::Array tools;
for (const auto &tool : m_tools)
Expand All @@ -312,9 +315,10 @@ ProtocolServerMCP::ToolsListHandler(const protocol::Request &request) {
return response;
}

llvm::Expected<protocol::Response>
ProtocolServerMCP::ToolsCallHandler(const protocol::Request &request) {
protocol::Response response;
llvm::Expected<lldb_protocol::mcp::Response>
ProtocolServerMCP::ToolsCallHandler(
const lldb_protocol::mcp::Request &request) {
lldb_protocol::mcp::Response response;

if (!request.params)
return llvm::createStringError("no tool parameters");
Expand All @@ -335,11 +339,11 @@ ProtocolServerMCP::ToolsCallHandler(const protocol::Request &request) {
if (it == m_tools.end())
return llvm::createStringError(llvm::formatv("no tool \"{0}\"", tool_name));

protocol::ToolArguments tool_args;
lldb_protocol::mcp::ToolArguments tool_args;
if (const json::Value *args = param_obj->get("arguments"))
tool_args = *args;

llvm::Expected<protocol::TextResult> text_result =
llvm::Expected<lldb_protocol::mcp::TextResult> text_result =
it->second->Call(tool_args);
if (!text_result)
return text_result.takeError();
Expand All @@ -349,16 +353,17 @@ ProtocolServerMCP::ToolsCallHandler(const protocol::Request &request) {
return response;
}

llvm::Expected<protocol::Response>
ProtocolServerMCP::ResourcesListHandler(const protocol::Request &request) {
protocol::Response response;
llvm::Expected<lldb_protocol::mcp::Response>
ProtocolServerMCP::ResourcesListHandler(
const lldb_protocol::mcp::Request &request) {
lldb_protocol::mcp::Response response;

llvm::json::Array resources;

std::lock_guard<std::mutex> guard(m_server_mutex);
for (std::unique_ptr<ResourceProvider> &resource_provider_up :
m_resource_providers) {
for (const protocol::Resource &resource :
for (const lldb_protocol::mcp::Resource &resource :
resource_provider_up->GetResources())
resources.push_back(resource);
}
Expand All @@ -368,9 +373,10 @@ ProtocolServerMCP::ResourcesListHandler(const protocol::Request &request) {
return response;
}

llvm::Expected<protocol::Response>
ProtocolServerMCP::ResourcesReadHandler(const protocol::Request &request) {
protocol::Response response;
llvm::Expected<lldb_protocol::mcp::Response>
ProtocolServerMCP::ResourcesReadHandler(
const lldb_protocol::mcp::Request &request) {
lldb_protocol::mcp::Response response;

if (!request.params)
return llvm::createStringError("no resource parameters");
Expand All @@ -390,7 +396,7 @@ ProtocolServerMCP::ResourcesReadHandler(const protocol::Request &request) {
std::lock_guard<std::mutex> guard(m_server_mutex);
for (std::unique_ptr<ResourceProvider> &resource_provider_up :
m_resource_providers) {
llvm::Expected<protocol::ResourceResult> result =
llvm::Expected<lldb_protocol::mcp::ResourceResult> result =
resource_provider_up->ReadResource(uri_str);
if (result.errorIsA<UnsupportedURI>()) {
llvm::consumeError(result.takeError());
Expand All @@ -399,7 +405,7 @@ ProtocolServerMCP::ResourcesReadHandler(const protocol::Request &request) {
if (!result)
return result.takeError();

protocol::Response response;
lldb_protocol::mcp::Response response;
response.result.emplace(std::move(*result));
return response;
}
Expand Down
38 changes: 20 additions & 18 deletions lldb/source/Plugins/Protocol/MCP/ProtocolServerMCP.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,12 @@
#ifndef LLDB_PLUGINS_PROTOCOL_MCP_PROTOCOLSERVERMCP_H
#define LLDB_PLUGINS_PROTOCOL_MCP_PROTOCOLSERVERMCP_H

#include "Protocol.h"
#include "Resource.h"
#include "Tool.h"
#include "lldb/Core/ProtocolServer.h"
#include "lldb/Host/MainLoop.h"
#include "lldb/Host/Socket.h"
#include "lldb/Protocol/MCP/Protocol.h"
#include "llvm/ADT/StringMap.h"
#include <thread>

Expand All @@ -41,10 +41,11 @@ class ProtocolServerMCP : public ProtocolServer {
Socket *GetSocket() const override { return m_listener.get(); }

protected:
using RequestHandler = std::function<llvm::Expected<protocol::Response>(
const protocol::Request &)>;
using RequestHandler =
std::function<llvm::Expected<lldb_protocol::mcp::Response>(
const lldb_protocol::mcp::Request &)>;
using NotificationHandler =
std::function<void(const protocol::Notification &)>;
std::function<void(const lldb_protocol::mcp::Notification &)>;

void AddTool(std::unique_ptr<Tool> tool);
void AddResourceProvider(std::unique_ptr<ResourceProvider> resource_provider);
Expand All @@ -56,26 +57,27 @@ class ProtocolServerMCP : public ProtocolServer {
private:
void AcceptCallback(std::unique_ptr<Socket> socket);

llvm::Expected<std::optional<protocol::Message>>
llvm::Expected<std::optional<lldb_protocol::mcp::Message>>
HandleData(llvm::StringRef data);

llvm::Expected<protocol::Response> Handle(protocol::Request request);
void Handle(protocol::Notification notification);
llvm::Expected<lldb_protocol::mcp::Response>
Handle(lldb_protocol::mcp::Request request);
void Handle(lldb_protocol::mcp::Notification notification);

llvm::Expected<protocol::Response>
InitializeHandler(const protocol::Request &);
llvm::Expected<lldb_protocol::mcp::Response>
InitializeHandler(const lldb_protocol::mcp::Request &);

llvm::Expected<protocol::Response>
ToolsListHandler(const protocol::Request &);
llvm::Expected<protocol::Response>
ToolsCallHandler(const protocol::Request &);
llvm::Expected<lldb_protocol::mcp::Response>
ToolsListHandler(const lldb_protocol::mcp::Request &);
llvm::Expected<lldb_protocol::mcp::Response>
ToolsCallHandler(const lldb_protocol::mcp::Request &);

llvm::Expected<protocol::Response>
ResourcesListHandler(const protocol::Request &);
llvm::Expected<protocol::Response>
ResourcesReadHandler(const protocol::Request &);
llvm::Expected<lldb_protocol::mcp::Response>
ResourcesListHandler(const lldb_protocol::mcp::Request &);
llvm::Expected<lldb_protocol::mcp::Response>
ResourcesReadHandler(const lldb_protocol::mcp::Request &);

protocol::Capabilities GetCapabilities();
lldb_protocol::mcp::Capabilities GetCapabilities();

llvm::StringLiteral kName = "lldb-mcp";
llvm::StringLiteral kVersion = "0.1.0";
Expand Down
Loading
Loading