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

This PR moves the MCP protocol code into its own library
(`lldbProtocolMCP`) so the code can be shared between the
`ProtocolServerMCP` plugin in LLDB as well as `lldb-mcp`. The goal is to
do the same thing for DAP (which, for now, would be used exclusively
from `lldb-dap`).

To make it clear that it's neither part of the `lldb` nor the
`lldb_private` namespace, I created a new `lldb_protocol` namespace.

Depending on how much code would be reused by lldb-dap, we may move more
code into the protocol library.
This commit is contained in:
Jonas Devlieghere 2025-08-05 09:48:26 -07:00 committed by GitHub
parent 1b651bf2b7
commit ed294c28ac
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
19 changed files with 167 additions and 140 deletions

View File

@ -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";
@ -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

View File

@ -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)

View File

@ -1,6 +1,5 @@
add_lldb_library(lldbPluginProtocolServerMCP PLUGIN
MCPError.cpp
Protocol.cpp
ProtocolServerMCP.cpp
Resource.cpp
Tool.cpp
@ -10,5 +9,6 @@ add_lldb_library(lldbPluginProtocolServerMCP PLUGIN
LINK_LIBS
lldbHost
lldbProtocolMCP
lldbUtility
)

View File

@ -11,7 +11,7 @@
#include "llvm/Support/raw_ostream.h"
#include <system_error>
namespace lldb_private::mcp {
using namespace lldb_private::mcp;
char MCPError::ID;
char UnsupportedURI::ID;
@ -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;
@ -41,5 +41,3 @@ void UnsupportedURI::log(llvm::raw_ostream &OS) const {
std::error_code UnsupportedURI::convertToErrorCode() const {
return llvm::inconvertibleErrorCode();
}
} // namespace lldb_private::mcp

View File

@ -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>
@ -24,7 +24,7 @@ public:
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;

View File

@ -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."));
@ -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;
@ -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);
@ -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)
@ -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(); },
@ -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.
@ -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)
@ -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");
@ -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();
@ -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);
}
@ -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");
@ -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());
@ -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;
}

View File

@ -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>
@ -41,10 +41,11 @@ public:
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);
@ -56,26 +57,27 @@ protected:
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";

View File

@ -10,6 +10,7 @@
#include "lldb/Core/Module.h"
#include "lldb/Target/Platform.h"
using namespace lldb_private;
using namespace lldb_private::mcp;
namespace {
@ -64,11 +65,11 @@ static llvm::Error createUnsupportedURIError(llvm::StringRef uri) {
return llvm::make_error<UnsupportedURI>(uri.str());
}
protocol::Resource
lldb_protocol::mcp::Resource
DebuggerResourceProvider::GetDebuggerResource(Debugger &debugger) {
const lldb::user_id_t debugger_id = debugger.GetID();
protocol::Resource resource;
lldb_protocol::mcp::Resource resource;
resource.uri = llvm::formatv("lldb://debugger/{0}", debugger_id);
resource.name = debugger.GetInstanceName();
resource.description =
@ -78,7 +79,7 @@ DebuggerResourceProvider::GetDebuggerResource(Debugger &debugger) {
return resource;
}
protocol::Resource
lldb_protocol::mcp::Resource
DebuggerResourceProvider::GetTargetResource(size_t target_idx, Target &target) {
const size_t debugger_id = target.GetDebugger().GetID();
@ -87,7 +88,7 @@ DebuggerResourceProvider::GetTargetResource(size_t target_idx, Target &target) {
if (Module *exe_module = target.GetExecutableModulePointer())
target_name = exe_module->GetFileSpec().GetFilename().GetString();
protocol::Resource resource;
lldb_protocol::mcp::Resource resource;
resource.uri =
llvm::formatv("lldb://debugger/{0}/target/{1}", debugger_id, target_idx);
resource.name = target_name;
@ -98,8 +99,9 @@ DebuggerResourceProvider::GetTargetResource(size_t target_idx, Target &target) {
return resource;
}
std::vector<protocol::Resource> DebuggerResourceProvider::GetResources() const {
std::vector<protocol::Resource> resources;
std::vector<lldb_protocol::mcp::Resource>
DebuggerResourceProvider::GetResources() const {
std::vector<lldb_protocol::mcp::Resource> resources;
const size_t num_debuggers = Debugger::GetNumDebuggers();
for (size_t i = 0; i < num_debuggers; ++i) {
@ -121,7 +123,7 @@ std::vector<protocol::Resource> DebuggerResourceProvider::GetResources() const {
return resources;
}
llvm::Expected<protocol::ResourceResult>
llvm::Expected<lldb_protocol::mcp::ResourceResult>
DebuggerResourceProvider::ReadResource(llvm::StringRef uri) const {
auto [protocol, path] = uri.split("://");
@ -158,7 +160,7 @@ DebuggerResourceProvider::ReadResource(llvm::StringRef uri) const {
return ReadDebuggerResource(uri, debugger_idx);
}
llvm::Expected<protocol::ResourceResult>
llvm::Expected<lldb_protocol::mcp::ResourceResult>
DebuggerResourceProvider::ReadDebuggerResource(llvm::StringRef uri,
lldb::user_id_t debugger_id) {
lldb::DebuggerSP debugger_sp = Debugger::FindDebuggerWithID(debugger_id);
@ -170,17 +172,17 @@ DebuggerResourceProvider::ReadDebuggerResource(llvm::StringRef uri,
debugger_resource.name = debugger_sp->GetInstanceName();
debugger_resource.num_targets = debugger_sp->GetTargetList().GetNumTargets();
protocol::ResourceContents contents;
lldb_protocol::mcp::ResourceContents contents;
contents.uri = uri;
contents.mimeType = kMimeTypeJSON;
contents.text = llvm::formatv("{0}", toJSON(debugger_resource));
protocol::ResourceResult result;
lldb_protocol::mcp::ResourceResult result;
result.contents.push_back(contents);
return result;
}
llvm::Expected<protocol::ResourceResult>
llvm::Expected<lldb_protocol::mcp::ResourceResult>
DebuggerResourceProvider::ReadTargetResource(llvm::StringRef uri,
lldb::user_id_t debugger_id,
size_t target_idx) {
@ -206,12 +208,12 @@ DebuggerResourceProvider::ReadTargetResource(llvm::StringRef uri,
if (lldb::PlatformSP platform_sp = target_sp->GetPlatform())
target_resource.platform = platform_sp->GetName();
protocol::ResourceContents contents;
lldb_protocol::mcp::ResourceContents contents;
contents.uri = uri;
contents.mimeType = kMimeTypeJSON;
contents.text = llvm::formatv("{0}", toJSON(target_resource));
protocol::ResourceResult result;
lldb_protocol::mcp::ResourceResult result;
result.contents.push_back(contents);
return result;
}

View File

@ -9,7 +9,7 @@
#ifndef LLDB_PLUGINS_PROTOCOL_MCP_RESOURCE_H
#define LLDB_PLUGINS_PROTOCOL_MCP_RESOURCE_H
#include "Protocol.h"
#include "lldb/Protocol/MCP/Protocol.h"
#include "lldb/lldb-private.h"
#include <vector>
@ -20,8 +20,8 @@ public:
ResourceProvider() = default;
virtual ~ResourceProvider() = default;
virtual std::vector<protocol::Resource> GetResources() const = 0;
virtual llvm::Expected<protocol::ResourceResult>
virtual std::vector<lldb_protocol::mcp::Resource> GetResources() const = 0;
virtual llvm::Expected<lldb_protocol::mcp::ResourceResult>
ReadResource(llvm::StringRef uri) const = 0;
};
@ -30,18 +30,19 @@ public:
using ResourceProvider::ResourceProvider;
virtual ~DebuggerResourceProvider() = default;
virtual std::vector<protocol::Resource> GetResources() const override;
virtual llvm::Expected<protocol::ResourceResult>
virtual std::vector<lldb_protocol::mcp::Resource>
GetResources() const override;
virtual llvm::Expected<lldb_protocol::mcp::ResourceResult>
ReadResource(llvm::StringRef uri) const override;
private:
static protocol::Resource GetDebuggerResource(Debugger &debugger);
static protocol::Resource GetTargetResource(size_t target_idx,
Target &target);
static lldb_protocol::mcp::Resource GetDebuggerResource(Debugger &debugger);
static lldb_protocol::mcp::Resource GetTargetResource(size_t target_idx,
Target &target);
static llvm::Expected<protocol::ResourceResult>
static llvm::Expected<lldb_protocol::mcp::ResourceResult>
ReadDebuggerResource(llvm::StringRef uri, lldb::user_id_t debugger_id);
static llvm::Expected<protocol::ResourceResult>
static llvm::Expected<lldb_protocol::mcp::ResourceResult>
ReadTargetResource(llvm::StringRef uri, lldb::user_id_t debugger_id,
size_t target_idx);
};

View File

@ -11,6 +11,8 @@
#include "lldb/Interpreter/CommandInterpreter.h"
#include "lldb/Interpreter/CommandReturnObject.h"
using namespace lldb_private;
using namespace lldb_protocol;
using namespace lldb_private::mcp;
using namespace llvm;
@ -28,11 +30,11 @@ bool fromJSON(const llvm::json::Value &V, CommandToolArguments &A,
}
/// Helper function to create a TextResult from a string output.
static lldb_private::mcp::protocol::TextResult
createTextResult(std::string output, bool is_error = false) {
lldb_private::mcp::protocol::TextResult text_result;
static lldb_protocol::mcp::TextResult createTextResult(std::string output,
bool is_error = false) {
lldb_protocol::mcp::TextResult text_result;
text_result.content.emplace_back(
lldb_private::mcp::protocol::TextContent{{std::move(output)}});
lldb_protocol::mcp::TextContent{{std::move(output)}});
text_result.isError = is_error;
return text_result;
}
@ -42,8 +44,8 @@ createTextResult(std::string output, bool is_error = false) {
Tool::Tool(std::string name, std::string description)
: m_name(std::move(name)), m_description(std::move(description)) {}
protocol::ToolDefinition Tool::GetDefinition() const {
protocol::ToolDefinition definition;
lldb_protocol::mcp::ToolDefinition Tool::GetDefinition() const {
lldb_protocol::mcp::ToolDefinition definition;
definition.name = m_name;
definition.description = m_description;
@ -53,8 +55,8 @@ protocol::ToolDefinition Tool::GetDefinition() const {
return definition;
}
llvm::Expected<protocol::TextResult>
CommandTool::Call(const protocol::ToolArguments &args) {
llvm::Expected<lldb_protocol::mcp::TextResult>
CommandTool::Call(const lldb_protocol::mcp::ToolArguments &args) {
if (!std::holds_alternative<json::Value>(args))
return createStringError("CommandTool requires arguments");

View File

@ -9,8 +9,8 @@
#ifndef LLDB_PLUGINS_PROTOCOL_MCP_TOOL_H
#define LLDB_PLUGINS_PROTOCOL_MCP_TOOL_H
#include "Protocol.h"
#include "lldb/Core/Debugger.h"
#include "lldb/Protocol/MCP/Protocol.h"
#include "llvm/Support/JSON.h"
#include <string>
@ -21,14 +21,14 @@ public:
Tool(std::string name, std::string description);
virtual ~Tool() = default;
virtual llvm::Expected<protocol::TextResult>
Call(const protocol::ToolArguments &args) = 0;
virtual llvm::Expected<lldb_protocol::mcp::TextResult>
Call(const lldb_protocol::mcp::ToolArguments &args) = 0;
virtual std::optional<llvm::json::Value> GetSchema() const {
return llvm::json::Object{{"type", "object"}};
}
protocol::ToolDefinition GetDefinition() const;
lldb_protocol::mcp::ToolDefinition GetDefinition() const;
const std::string &GetName() { return m_name; }
@ -42,8 +42,8 @@ public:
using mcp::Tool::Tool;
~CommandTool() = default;
virtual llvm::Expected<protocol::TextResult>
Call(const protocol::ToolArguments &args) override;
virtual llvm::Expected<lldb_protocol::mcp::TextResult>
Call(const lldb_protocol::mcp::ToolArguments &args) override;
virtual std::optional<llvm::json::Value> GetSchema() const override;
};

View File

@ -0,0 +1 @@
add_subdirectory(MCP)

View File

@ -0,0 +1,8 @@
add_lldb_library(lldbProtocolMCP NO_PLUGIN_DEPENDENCIES
Protocol.cpp
LINK_COMPONENTS
Support
LINK_LIBS
lldbUtility
)

View File

@ -6,12 +6,12 @@
//
//===----------------------------------------------------------------------===//
#include "Protocol.h"
#include "lldb/Protocol/MCP/Protocol.h"
#include "llvm/Support/JSON.h"
using namespace llvm;
namespace lldb_private::mcp::protocol {
namespace lldb_protocol::mcp {
static bool mapRaw(const json::Value &Params, StringLiteral Prop,
std::optional<json::Value> &V, json::Path P) {
@ -228,7 +228,7 @@ bool fromJSON(const llvm::json::Value &V, Message &M, llvm::json::Path P) {
// A message without an ID is a Notification.
if (!O->get("id")) {
protocol::Notification N;
Notification N;
if (!fromJSON(V, N, P))
return false;
M = std::move(N);
@ -236,7 +236,7 @@ bool fromJSON(const llvm::json::Value &V, Message &M, llvm::json::Path P) {
}
if (O->get("error")) {
protocol::Error E;
Error E;
if (!fromJSON(V, E, P))
return false;
M = std::move(E);
@ -244,7 +244,7 @@ bool fromJSON(const llvm::json::Value &V, Message &M, llvm::json::Path P) {
}
if (O->get("result")) {
protocol::Response R;
Response R;
if (!fromJSON(V, R, P))
return false;
M = std::move(R);
@ -252,7 +252,7 @@ bool fromJSON(const llvm::json::Value &V, Message &M, llvm::json::Path P) {
}
if (O->get("method")) {
protocol::Request R;
Request R;
if (!fromJSON(V, R, P))
return false;
M = std::move(R);
@ -263,4 +263,4 @@ bool fromJSON(const llvm::json::Value &V, Message &M, llvm::json::Path P) {
return false;
}
} // namespace lldb_private::mcp::protocol
} // namespace lldb_protocol::mcp

View File

@ -61,25 +61,26 @@ add_subdirectory(Disassembler)
add_subdirectory(Editline)
add_subdirectory(Expression)
add_subdirectory(Host)
add_subdirectory(Interpreter)
add_subdirectory(Instruction)
add_subdirectory(Interpreter)
add_subdirectory(Language)
add_subdirectory(ObjectFile)
add_subdirectory(Platform)
add_subdirectory(Process)
add_subdirectory(Protocol)
add_subdirectory(ScriptInterpreter)
add_subdirectory(Signals)
add_subdirectory(Symbol)
add_subdirectory(SymbolFile)
add_subdirectory(Target)
add_subdirectory(tools)
add_subdirectory(Thread)
add_subdirectory(UnwindAssembly)
add_subdirectory(Utility)
add_subdirectory(Thread)
add_subdirectory(ValueObject)
add_subdirectory(tools)
if(LLDB_ENABLE_PROTOCOL_SERVERS)
add_subdirectory(Protocol)
add_subdirectory(ProtocolServer)
endif()
if(LLDB_CAN_USE_DEBUGSERVER AND LLDB_TOOL_DEBUGSERVER_BUILD AND NOT LLDB_USE_SYSTEM_DEBUGSERVER)

View File

@ -1,12 +1,9 @@
add_lldb_unittest(ProtocolTests
ProtocolMCPTest.cpp
ProtocolMCPServerTest.cpp
LINK_LIBS
lldbCore
lldbUtility
lldbHost
lldbPluginPlatformMacOSX
lldbPluginProtocolServerMCP
lldbProtocolMCP
lldbUtility
LLVMTestingSupport
)

View File

@ -6,14 +6,14 @@
//
//===----------------------------------------------------------------------===//
#include "Plugins/Protocol/MCP/Protocol.h"
#include "TestingSupport/TestUtilities.h"
#include "lldb/Protocol/MCP/Protocol.h"
#include "llvm/Testing/Support/Error.h"
#include "gtest/gtest.h"
using namespace lldb;
using namespace lldb_private;
using namespace lldb_private::mcp::protocol;
using namespace lldb_protocol::mcp;
TEST(ProtocolMCPTest, Request) {
Request request;

View File

@ -0,0 +1,11 @@
add_lldb_unittest(ProtocolServerTests
ProtocolMCPServerTest.cpp
LINK_LIBS
lldbCore
lldbUtility
lldbHost
lldbPluginPlatformMacOSX
lldbPluginProtocolServerMCP
LLVMTestingSupport
)

View File

@ -16,13 +16,14 @@
#include "lldb/Host/HostInfo.h"
#include "lldb/Host/JSONTransport.h"
#include "lldb/Host/Socket.h"
#include "lldb/Protocol/MCP/Protocol.h"
#include "llvm/Testing/Support/Error.h"
#include "gtest/gtest.h"
using namespace llvm;
using namespace lldb;
using namespace lldb_private;
using namespace lldb_private::mcp::protocol;
using namespace lldb_protocol::mcp;
namespace {
class TestProtocolServerMCP : public lldb_private::mcp::ProtocolServerMCP {
@ -47,8 +48,7 @@ class TestTool : public mcp::Tool {
public:
using mcp::Tool::Tool;
virtual llvm::Expected<mcp::protocol::TextResult>
Call(const ToolArguments &args) override {
virtual llvm::Expected<TextResult> Call(const ToolArguments &args) override {
std::string argument;
if (const json::Object *args_obj =
std::get<json::Value>(args).getAsObject()) {
@ -57,8 +57,8 @@ public:
}
}
mcp::protocol::TextResult text_result;
text_result.content.emplace_back(mcp::protocol::TextContent{{argument}});
TextResult text_result;
text_result.content.emplace_back(TextContent{{argument}});
return text_result;
}
};
@ -100,8 +100,7 @@ class ErrorTool : public mcp::Tool {
public:
using mcp::Tool::Tool;
virtual llvm::Expected<mcp::protocol::TextResult>
Call(const ToolArguments &args) override {
virtual llvm::Expected<TextResult> Call(const ToolArguments &args) override {
return llvm::createStringError("error");
}
};
@ -111,10 +110,9 @@ class FailTool : public mcp::Tool {
public:
using mcp::Tool::Tool;
virtual llvm::Expected<mcp::protocol::TextResult>
Call(const ToolArguments &args) override {
mcp::protocol::TextResult text_result;
text_result.content.emplace_back(mcp::protocol::TextContent{{"failed"}});
virtual llvm::Expected<TextResult> Call(const ToolArguments &args) override {
TextResult text_result;
text_result.content.emplace_back(TextContent{{"failed"}});
text_result.isError = true;
return text_result;
}
@ -309,8 +307,7 @@ TEST_F(ProtocolServerMCPTest, NotificationInitialized) {
std::mutex mutex;
m_server_up->AddNotificationHandler(
"notifications/initialized",
[&](const mcp::protocol::Notification &notification) {
"notifications/initialized", [&](const Notification &notification) {
{
std::lock_guard<std::mutex> lock(mutex);
handler_called = true;