LLDB mainline
ProtocolServerMCP.cpp
Go to the documentation of this file.
1//===- ProtocolServerMCP.cpp ----------------------------------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
9#include "ProtocolServerMCP.h"
10#include "Resource.h"
11#include "Tool.h"
15#include "lldb/Utility/Log.h"
16#include "llvm/ADT/StringExtras.h"
17#include "llvm/Support/Error.h"
18#include "llvm/Support/Threading.h"
19#include <thread>
20
21using namespace lldb_private;
22using namespace lldb_private::mcp;
23using namespace lldb_protocol::mcp;
24using namespace llvm;
25
27
28static constexpr llvm::StringLiteral kName = "lldb-mcp";
29static constexpr llvm::StringLiteral kVersion = "0.1.0";
30
32
33ProtocolServerMCP::~ProtocolServerMCP() { llvm::consumeError(Stop()); }
34
39
45
47 return std::make_unique<ProtocolServerMCP>();
48}
49
51 return "MCP Server.";
52}
53
55 server.AddNotificationHandler("notifications/initialized",
58 "MCP initialization complete");
59 });
60 server.AddTool(
61 std::make_unique<CommandTool>("command", "Run an lldb command."));
62 server.AddTool(std::make_unique<DebuggerListTool>(
63 "debugger_list", "List debugger instances with their debugger_id."));
64 server.AddResourceProvider(std::make_unique<DebuggerResourceProvider>());
65}
66
67void ProtocolServerMCP::AcceptCallback(std::unique_ptr<Socket> socket) {
68 Log *log = GetLog(LLDBLog::Host);
69 std::string client_name = llvm::formatv("client_{0}", ++m_client_count);
70 LLDB_LOG(log, "New MCP client connected: {0}", client_name);
71
72 lldb::IOObjectSP io_sp = std::move(socket);
73 auto transport_up = std::make_unique<lldb_protocol::mcp::Transport>(
74 io_sp, io_sp, [client_name](llvm::StringRef message) {
75 LLDB_LOG(GetLog(LLDBLog::Host), "{0}: {1}", client_name, message);
76 });
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));
97}
98
100 std::lock_guard<std::mutex> guard(m_mutex);
101
102 if (m_running)
103 return llvm::createStringError("the MCP server is already running");
104
105 Status status;
106 m_listener = Socket::Create(connection.protocol, status);
107 if (status.Fail())
108 return status.takeError();
109
110 status = m_listener->Listen(connection.name, /*backlog=*/5);
111 if (status.Fail())
112 return status.takeError();
113
114 auto handles =
116 this, std::placeholders::_1));
117 if (llvm::Error error = handles.takeError())
118 return error;
119
120 auto listening_uris = m_listener->GetListeningConnectionURI();
121 if (listening_uris.empty())
122 return createStringError("failed to get listening connections");
123 std::string address =
124 llvm::join(m_listener->GetListeningConnectionURI(), ", ");
125
126 ServerInfo info{listening_uris[0]};
127 llvm::Expected<ServerInfoHandle> handle = ServerInfo::Write(info);
128 if (!handle)
129 return handle.takeError();
130
131 m_running = true;
132 m_server_info_handle = std::move(*handle);
133 m_listen_handlers = std::move(*handles);
134 m_loop_thread = std::thread([=] {
135 llvm::set_thread_name("protocol-server.mcp");
136 m_loop.Run();
137 });
138
139 return llvm::Error::success();
140}
141
143 {
144 std::lock_guard<std::mutex> guard(m_mutex);
145 if (!m_running)
146 return createStringError("the MCP sever is not running");
147 m_running = false;
148 }
149
150 // Stop the main loop.
151 m_loop.AddPendingCallback(
152 [](lldb_private::MainLoopBase &loop) { loop.RequestTermination(); });
153
154 // Wait for the main loop to exit.
155 if (m_loop_thread.joinable())
156 m_loop_thread.join();
157
158 m_server_info_handle.Remove();
159 m_listen_handlers.clear();
160 m_instances.clear();
161
162 return llvm::Error::success();
163}
static llvm::raw_ostream & error(Stream &strm)
#define LLDB_LOG(log,...)
The LLDB_LOG* macros defined below are the way to emit log messages.
Definition Log.h:369
#define LLDB_LOG_ERROR(log, error,...)
Definition Log.h:392
#define LLDB_PLUGIN_DEFINE(PluginName)
static constexpr llvm::StringLiteral kName
static constexpr llvm::StringLiteral kVersion
static llvm::Error createStringError(const char *format, Args &&...args)
Definition Resource.cpp:59
virtual void RequestTermination()
static bool RegisterPlugin(llvm::StringRef name, llvm::StringRef description, ABICreateInstance create_callback)
static bool UnregisterPlugin(ABICreateInstance create_callback)
static llvm::Error Terminate()
static std::unique_ptr< Socket > Create(const SocketProtocol protocol, Status &error)
Definition Socket.cpp:200
An error handling class.
Definition Status.h:118
llvm::Error takeError()
Definition Status.h:170
bool Fail() const
Test for error condition.
Definition Status.cpp:294
std::unique_ptr< Socket > m_listener
virtual void Extend(lldb_protocol::mcp::Server &server) const
virtual llvm::Error Start(ProtocolServer::Connection connection) override
static lldb::ProtocolServerUP CreateInstance()
virtual llvm::Error Stop() override
void AcceptCallback(std::unique_ptr< Socket > socket)
static llvm::StringRef GetPluginNameStatic()
lldb_protocol::mcp::ServerInfoHandle m_server_info_handle
std::map< lldb_protocol::mcp::MCPTransport *, std::tuple< ServerUP, ReadHandleUP, TransportUP > > m_instances
static llvm::StringRef GetPluginDescriptionStatic()
std::vector< ReadHandleUP > m_listen_handlers
void AddTool(std::unique_ptr< Tool > tool)
Definition Server.cpp:154
void AddResourceProvider(std::unique_ptr< ResourceProvider > resource_provider)
Definition Server.cpp:160
void AddNotificationHandler(llvm::StringRef method, NotificationHandler handler)
Definition Server.cpp:171
A class that represents a running process on the host machine.
Log * GetLog(Cat mask)
Retrieve the Log object for the channel associated with the given log enum.
Definition Log.h:332
lldb_private::Transport< Request, Response, Notification > MCPTransport
Generic transport that uses the MCP protocol.
Definition Transport.h:21
std::shared_ptr< lldb_private::IOObject > IOObjectSP
std::unique_ptr< lldb_private::ProtocolServer > ProtocolServerUP
A notification which does not expect a response.
Definition Protocol.h:88
Information about this instance of lldb's MCP server for lldb-mcp to use to coordinate connecting an ...
Definition Server.h:97
static llvm::Expected< ServerInfoHandle > Write(const ServerInfo &)
Writes the server info into a unique file in ~/.lldb.
Definition Server.cpp:60