Skip to content

Commit 48eb7de

Browse files
committed
[lldb] Adding A new Binding helper for JSONTransport.
This adds a new Binding helper class to allow mapping of incoming and outgoing requests / events to specific handlers. This should make it easier to create new protocol implementations and allow us to create a relay in the lldb-mcp binary.
1 parent 5cc4193 commit 48eb7de

File tree

18 files changed

+1167
-599
lines changed

18 files changed

+1167
-599
lines changed

lldb/include/lldb/Host/JSONTransport.h

Lines changed: 356 additions & 21 deletions
Large diffs are not rendered by default.

lldb/include/lldb/Protocol/MCP/Protocol.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#ifndef LLDB_PROTOCOL_MCP_PROTOCOL_H
1515
#define LLDB_PROTOCOL_MCP_PROTOCOL_H
1616

17+
#include "llvm/ADT/StringRef.h"
1718
#include "llvm/Support/JSON.h"
1819
#include <optional>
1920
#include <string>
@@ -324,4 +325,11 @@ bool fromJSON(const llvm::json::Value &, CallToolResult &, llvm::json::Path);
324325

325326
} // namespace lldb_protocol::mcp
326327

328+
namespace llvm::json {
329+
inline Value toJSON(const lldb_protocol::mcp::Void &) { return Object(); }
330+
inline bool fromJSON(const Value &, lldb_protocol::mcp::Void &, Path) {
331+
return true;
332+
}
333+
} // namespace llvm::json
334+
327335
#endif

lldb/include/lldb/Protocol/MCP/Server.h

Lines changed: 32 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
#ifndef LLDB_PROTOCOL_MCP_SERVER_H
1010
#define LLDB_PROTOCOL_MCP_SERVER_H
1111

12-
#include "lldb/Host/JSONTransport.h"
1312
#include "lldb/Host/MainLoop.h"
1413
#include "lldb/Protocol/MCP/Protocol.h"
1514
#include "lldb/Protocol/MCP/Resource.h"
@@ -19,75 +18,67 @@
1918
#include "llvm/ADT/StringMap.h"
2019
#include "llvm/ADT/StringRef.h"
2120
#include "llvm/Support/Error.h"
21+
#include "llvm/Support/FormatVariadic.h"
2222
#include "llvm/Support/JSON.h"
2323
#include "llvm/Support/Signals.h"
24-
#include <functional>
2524
#include <memory>
2625
#include <string>
2726
#include <vector>
2827

2928
namespace lldb_protocol::mcp {
3029

31-
class Server : public MCPTransport::MessageHandler {
32-
using ClosedCallback = llvm::unique_function<void()>;
30+
31+
class Server {
32+
33+
using MCPTransportUP = std::unique_ptr<lldb_protocol::mcp::MCPTransport>;
34+
35+
using ReadHandleUP = lldb_private::MainLoop::ReadHandleUP;
3336

3437
public:
35-
Server(std::string name, std::string version, MCPTransport &client,
36-
LogCallback log_callback = {}, ClosedCallback closed_callback = {});
38+
Server(std::string name, std::string version, LogCallback log_callback = {});
3739
~Server() = default;
3840

39-
using NotificationHandler = std::function<void(const Notification &)>;
40-
4141
void AddTool(std::unique_ptr<Tool> tool);
4242
void AddResourceProvider(std::unique_ptr<ResourceProvider> resource_provider);
43-
void AddNotificationHandler(llvm::StringRef method,
44-
NotificationHandler handler);
45-
46-
protected:
47-
ServerCapabilities GetCapabilities();
4843

49-
using RequestHandler =
50-
std::function<llvm::Expected<Response>(const Request &)>;
44+
llvm::Error Accept(lldb_private::MainLoop &, MCPTransportUP);
5145

52-
void AddRequestHandlers();
53-
54-
void AddRequestHandler(llvm::StringRef method, RequestHandler handler);
55-
56-
llvm::Expected<std::optional<Message>> HandleData(llvm::StringRef data);
57-
58-
llvm::Expected<Response> Handle(const Request &request);
59-
void Handle(const Notification &notification);
46+
protected:
47+
MCPTransport::BinderUP Bind(MCPTransport &);
6048

61-
llvm::Expected<Response> InitializeHandler(const Request &);
49+
ServerCapabilities GetCapabilities();
6250

63-
llvm::Expected<Response> ToolsListHandler(const Request &);
64-
llvm::Expected<Response> ToolsCallHandler(const Request &);
51+
llvm::Expected<InitializeResult> InitializeHandler(const InitializeParams &);
6552

66-
llvm::Expected<Response> ResourcesListHandler(const Request &);
67-
llvm::Expected<Response> ResourcesReadHandler(const Request &);
53+
llvm::Expected<ListToolsResult> ToolsListHandler();
54+
llvm::Expected<CallToolResult> ToolsCallHandler(const CallToolParams &);
6855

69-
void Received(const Request &) override;
70-
void Received(const Response &) override;
71-
void Received(const Notification &) override;
72-
void OnError(llvm::Error) override;
73-
void OnClosed() override;
56+
llvm::Expected<ListResourcesResult> ResourcesListHandler();
57+
llvm::Expected<ReadResourceResult>
58+
ResourcesReadHandler(const ReadResourceParams &);
7459

75-
protected:
76-
void Log(llvm::StringRef);
60+
template <typename... Ts> inline auto Logv(const char *Fmt, Ts &&...Vals) {
61+
Log(llvm::formatv(Fmt, std::forward<Ts>(Vals)...).str());
62+
}
63+
void Log(llvm::StringRef message) {
64+
if (m_log_callback)
65+
m_log_callback(message);
66+
}
7767

7868
private:
7969
const std::string m_name;
8070
const std::string m_version;
8171

82-
MCPTransport &m_client;
8372
LogCallback m_log_callback;
84-
ClosedCallback m_closed_callback;
73+
struct Client {
74+
ReadHandleUP handle;
75+
MCPTransportUP transport;
76+
MCPTransport::BinderUP binder;
77+
};
78+
std::map<MCPTransport *, Client> m_instances;
8579

8680
llvm::StringMap<std::unique_ptr<Tool>> m_tools;
8781
std::vector<std::unique_ptr<ResourceProvider>> m_resource_providers;
88-
89-
llvm::StringMap<RequestHandler> m_request_handlers;
90-
llvm::StringMap<NotificationHandler> m_notification_handlers;
9182
};
9283

9384
class ServerInfoHandle;
@@ -121,7 +112,7 @@ class ServerInfoHandle {
121112
ServerInfoHandle &operator=(const ServerInfoHandle &) = delete;
122113
/// @}
123114

124-
/// Remove the file.
115+
/// Remove the file on disk, if one is tracked.
125116
void Remove();
126117

127118
private:

lldb/include/lldb/Protocol/MCP/Transport.h

Lines changed: 75 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,22 +10,95 @@
1010
#define LLDB_PROTOCOL_MCP_TRANSPORT_H
1111

1212
#include "lldb/Host/JSONTransport.h"
13+
#include "lldb/Protocol/MCP/MCPError.h"
1314
#include "lldb/Protocol/MCP/Protocol.h"
1415
#include "lldb/lldb-forward.h"
1516
#include "llvm/ADT/FunctionExtras.h"
1617
#include "llvm/ADT/StringRef.h"
18+
#include "llvm/Support/Error.h"
19+
20+
namespace lldb_private {
21+
/// Specializations of the JSONTransport protocol functions for MCP.
22+
/// @{
23+
template <>
24+
inline lldb_protocol::mcp::Request
25+
make_request(int64_t id, llvm::StringRef method,
26+
std::optional<llvm::json::Value> params) {
27+
return lldb_protocol::mcp::Request{id, method.str(), params};
28+
}
29+
template <>
30+
inline lldb_protocol::mcp::Response
31+
make_response(const lldb_protocol::mcp::Request &req, llvm::Error error) {
32+
lldb_protocol::mcp::Error protocol_error;
33+
llvm::handleAllErrors(
34+
std::move(error),
35+
[&](const lldb_protocol::mcp::MCPError &err) {
36+
protocol_error = err.toProtocolError();
37+
},
38+
[&](const llvm::ErrorInfoBase &err) {
39+
protocol_error.code = lldb_protocol::mcp::MCPError::kInternalError;
40+
protocol_error.message = err.message();
41+
});
42+
43+
return lldb_protocol::mcp::Response{req.id, std::move(protocol_error)};
44+
}
45+
template <>
46+
inline lldb_protocol::mcp::Response
47+
make_response(const lldb_protocol::mcp::Request &req,
48+
llvm::json::Value result) {
49+
return lldb_protocol::mcp::Response{req.id, std::move(result)};
50+
}
51+
template <>
52+
inline lldb_protocol::mcp::Notification
53+
make_event(llvm::StringRef method, std::optional<llvm::json::Value> params) {
54+
return lldb_protocol::mcp::Notification{method.str(), params};
55+
}
56+
template <>
57+
inline llvm::Expected<llvm::json::Value>
58+
get_result(const lldb_protocol::mcp::Response &resp) {
59+
if (const lldb_protocol::mcp::Error *error =
60+
std::get_if<lldb_protocol::mcp::Error>(&resp.result))
61+
return llvm::make_error<lldb_protocol::mcp::MCPError>(error->message,
62+
error->code);
63+
return std::get<llvm::json::Value>(resp.result);
64+
}
65+
template <> inline int64_t get_id(const lldb_protocol::mcp::Response &resp) {
66+
return std::get<int64_t>(resp.id);
67+
}
68+
template <>
69+
inline llvm::StringRef get_method(const lldb_protocol::mcp::Request &req) {
70+
return req.method;
71+
}
72+
template <>
73+
inline llvm::StringRef get_method(const lldb_protocol::mcp::Notification &evt) {
74+
return evt.method;
75+
}
76+
template <>
77+
inline llvm::json::Value get_params(const lldb_protocol::mcp::Request &req) {
78+
return req.params;
79+
}
80+
template <>
81+
inline llvm::json::Value
82+
get_params(const lldb_protocol::mcp::Notification &evt) {
83+
return evt.params;
84+
}
85+
/// @}
86+
87+
} // end namespace lldb_private
1788

1889
namespace lldb_protocol::mcp {
1990

2091
/// Generic transport that uses the MCP protocol.
21-
using MCPTransport = lldb_private::Transport<Request, Response, Notification>;
92+
using MCPTransport =
93+
lldb_private::JSONTransport<int64_t, Request, Response, Notification>;
2294

2395
/// Generic logging callback, to allow the MCP server / client / transport layer
2496
/// to be independent of the lldb log implementation.
2597
using LogCallback = llvm::unique_function<void(llvm::StringRef message)>;
2698

2799
class Transport final
28-
: public lldb_private::JSONRPCTransport<Request, Response, Notification> {
100+
: public lldb_private::JSONRPCTransport<int64_t, Request, Response,
101+
Notification> {
29102
public:
30103
Transport(lldb::IOObjectSP in, lldb::IOObjectSP out,
31104
LogCallback log_callback = {});

lldb/source/Host/common/JSONTransport.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,3 +30,13 @@ void TransportUnhandledContentsError::log(llvm::raw_ostream &OS) const {
3030
std::error_code TransportUnhandledContentsError::convertToErrorCode() const {
3131
return std::make_error_code(std::errc::bad_message);
3232
}
33+
34+
char InvalidParams::ID;
35+
36+
void InvalidParams::log(llvm::raw_ostream &OS) const {
37+
OS << "invalid parameters for method '" << m_method << "': '" << m_context
38+
<< "'";
39+
}
40+
std::error_code InvalidParams::convertToErrorCode() const {
41+
return std::make_error_code(std::errc::invalid_argument);
42+
}

lldb/source/Plugins/Protocol/MCP/ProtocolServerMCP.cpp

Lines changed: 18 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -52,11 +52,6 @@ llvm::StringRef ProtocolServerMCP::GetPluginDescriptionStatic() {
5252
}
5353

5454
void ProtocolServerMCP::Extend(lldb_protocol::mcp::Server &server) const {
55-
server.AddNotificationHandler("notifications/initialized",
56-
[](const lldb_protocol::mcp::Notification &) {
57-
LLDB_LOG(GetLog(LLDBLog::Host),
58-
"MCP initialization complete");
59-
});
6055
server.AddTool(
6156
std::make_unique<CommandTool>("command", "Run an lldb command."));
6257
server.AddTool(std::make_unique<DebuggerListTool>(
@@ -74,26 +69,9 @@ void ProtocolServerMCP::AcceptCallback(std::unique_ptr<Socket> socket) {
7469
io_sp, io_sp, [client_name](llvm::StringRef message) {
7570
LLDB_LOG(GetLog(LLDBLog::Host), "{0}: {1}", client_name, message);
7671
});
77-
MCPTransport *transport_ptr = transport_up.get();
78-
auto instance_up = std::make_unique<lldb_protocol::mcp::Server>(
79-
std::string(kName), std::string(kVersion), *transport_up,
80-
/*log_callback=*/
81-
[client_name](llvm::StringRef message) {
82-
LLDB_LOG(GetLog(LLDBLog::Host), "{0} Server: {1}", client_name,
83-
message);
84-
},
85-
/*closed_callback=*/
86-
[this, transport_ptr]() { m_instances.erase(transport_ptr); });
87-
Extend(*instance_up);
88-
llvm::Expected<MainLoop::ReadHandleUP> handle =
89-
transport_up->RegisterMessageHandler(m_loop, *instance_up);
90-
if (!handle) {
91-
LLDB_LOG_ERROR(log, handle.takeError(), "Failed to run MCP server: {0}");
92-
return;
93-
}
94-
m_instances[transport_ptr] =
95-
std::make_tuple<ServerUP, ReadHandleUP, TransportUP>(
96-
std::move(instance_up), std::move(*handle), std::move(transport_up));
72+
73+
if (auto error = m_server->Accept(m_loop, std::move(transport_up)))
74+
LLDB_LOG_ERROR(log, std::move(error), "{0}:");
9775
}
9876

9977
llvm::Error ProtocolServerMCP::Start(ProtocolServer::Connection connection) {
@@ -124,13 +102,20 @@ llvm::Error ProtocolServerMCP::Start(ProtocolServer::Connection connection) {
124102
llvm::join(m_listener->GetListeningConnectionURI(), ", ");
125103

126104
ServerInfo info{listening_uris[0]};
127-
llvm::Expected<ServerInfoHandle> handle = ServerInfo::Write(info);
128-
if (!handle)
129-
return handle.takeError();
105+
llvm::Expected<ServerInfoHandle> server_info_handle = ServerInfo::Write(info);
106+
if (!server_info_handle)
107+
return server_info_handle.takeError();
108+
109+
m_client_count = 0;
110+
m_server = std::make_unique<lldb_protocol::mcp::Server>(
111+
std::string(kName), std::string(kVersion), [](StringRef message) {
112+
LLDB_LOG(GetLog(LLDBLog::Host), "MCP Server: {0}", message);
113+
});
114+
Extend(*m_server);
130115

131116
m_running = true;
132-
m_server_info_handle = std::move(*handle);
133-
m_listen_handlers = std::move(*handles);
117+
m_server_info_handle = std::move(*server_info_handle);
118+
m_accept_handles = std::move(*handles);
134119
m_loop_thread = std::thread([=] {
135120
llvm::set_thread_name("protocol-server.mcp");
136121
m_loop.Run();
@@ -155,9 +140,10 @@ llvm::Error ProtocolServerMCP::Stop() {
155140
if (m_loop_thread.joinable())
156141
m_loop_thread.join();
157142

143+
m_accept_handles.clear();
144+
145+
m_server.reset(nullptr);
158146
m_server_info_handle.Remove();
159-
m_listen_handlers.clear();
160-
m_instances.clear();
161147

162148
return llvm::Error::success();
163149
}

lldb/source/Plugins/Protocol/MCP/ProtocolServerMCP.h

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -23,16 +23,17 @@
2323
namespace lldb_private::mcp {
2424

2525
class ProtocolServerMCP : public ProtocolServer {
26-
using ReadHandleUP = MainLoopBase::ReadHandleUP;
27-
using TransportUP = std::unique_ptr<lldb_protocol::mcp::MCPTransport>;
26+
2827
using ServerUP = std::unique_ptr<lldb_protocol::mcp::Server>;
2928

29+
using ReadHandleUP = MainLoop::ReadHandleUP;
30+
3031
public:
3132
ProtocolServerMCP();
32-
virtual ~ProtocolServerMCP() override;
33+
~ProtocolServerMCP() override;
3334

34-
virtual llvm::Error Start(ProtocolServer::Connection connection) override;
35-
virtual llvm::Error Stop() override;
35+
llvm::Error Start(ProtocolServer::Connection connection) override;
36+
llvm::Error Stop() override;
3637

3738
static void Initialize();
3839
static void Terminate();
@@ -56,19 +57,18 @@ class ProtocolServerMCP : public ProtocolServer {
5657

5758
bool m_running = false;
5859

59-
lldb_protocol::mcp::ServerInfoHandle m_server_info_handle;
6060
lldb_private::MainLoop m_loop;
6161
std::thread m_loop_thread;
6262
std::mutex m_mutex;
6363
size_t m_client_count = 0;
6464

6565
std::unique_ptr<Socket> m_listener;
66+
std::vector<ReadHandleUP> m_accept_handles;
6667

67-
std::vector<ReadHandleUP> m_listen_handlers;
68-
std::map<lldb_protocol::mcp::MCPTransport *,
69-
std::tuple<ServerUP, ReadHandleUP, TransportUP>>
70-
m_instances;
68+
ServerUP m_server;
69+
lldb_protocol::mcp::ServerInfoHandle m_server_info_handle;
7170
};
71+
7272
} // namespace lldb_private::mcp
7373

7474
#endif

0 commit comments

Comments
 (0)