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";
29
31
32ProtocolServerMCP::~ProtocolServerMCP() { llvm::consumeError(Stop()); }
33
38
44
46 return std::make_unique<ProtocolServerMCP>();
47}
48
50 return "MCP Server.";
51}
52
54 server.AddTool(
55 std::make_unique<CommandTool>("command", "Run an lldb command."));
56 server.AddTool(std::make_unique<DebuggerListTool>(
57 "debugger_list", "List debugger instances with their debugger_id."));
58 server.AddTool(std::make_unique<DebuggerCreateTool>(
59 "debugger_create", "Create a new debugger instance and return its URI."));
60 server.AddTool(std::make_unique<DebuggerDeleteTool>(
61 "debugger_delete", "Destroy a debugger instance by id or URI."));
62 server.AddResourceProvider(std::make_unique<DebuggerResourceProvider>());
63}
64
68
69void ProtocolServerMCP::AcceptCallback(std::unique_ptr<Socket> socket) {
70 Log *log = GetLog(LLDBLog::Host);
71 std::string client_name = llvm::formatv("client_{0}", ++m_client_count);
72 LLDB_LOG(log, "New MCP client connected: {0}", client_name);
73
74 lldb::IOObjectSP io_sp = std::move(socket);
75 auto transport_up = std::make_unique<lldb_protocol::mcp::Transport>(
76 m_loop, io_sp, io_sp, [client_name](llvm::StringRef message) {
77 LLDB_LOG(GetLog(LLDBLog::Host), "{0}: {1}", client_name, message);
78 });
79
80 if (auto error = m_server->Accept(std::move(transport_up)))
81 LLDB_LOG_ERROR(log, std::move(error), "{0}:");
82}
83
85 std::lock_guard<std::mutex> guard(m_mutex);
86
87 if (m_running)
88 return llvm::createStringError("the MCP server is already running");
89
90 Status status;
91 m_listener = Socket::Create(connection.protocol, status);
92 if (status.Fail())
93 return status.takeError();
94
95 status = m_listener->Listen(connection.name, /*backlog=*/5);
96 if (status.Fail())
97 return status.takeError();
98
99 auto handles =
101 this, std::placeholders::_1));
102 if (llvm::Error error = handles.takeError())
103 return error;
104
105 auto listening_uris = m_listener->GetListeningConnectionURI();
106 if (listening_uris.empty())
107 return createStringError("failed to get listening connections");
108
109 ServerInfo info{listening_uris[0]};
110 llvm::Expected<ServerInfoHandle> server_info_handle = ServerInfo::Write(info);
111 if (!server_info_handle)
112 return server_info_handle.takeError();
113
114 m_client_count = 0;
115 m_server = std::make_unique<lldb_protocol::mcp::Server>(
116 std::string(kName), std::string(lldb_protocol::mcp::GetServerVersion()),
117 [](StringRef message) {
118 LLDB_LOG(GetLog(LLDBLog::Host), "MCP Server: {0}", message);
119 });
121
122 m_running = true;
123 m_server_info_handle = std::move(*server_info_handle);
124 m_accept_handles = std::move(*handles);
125 m_loop_thread = std::thread([this] {
126 llvm::set_thread_name("protocol-server.mcp");
127 m_loop.Run();
128 });
129
130 return llvm::Error::success();
131}
132
134 {
135 std::lock_guard<std::mutex> guard(m_mutex);
136 if (!m_running)
137 return createStringError("the MCP sever is not running");
138 m_running = false;
139 }
140
141 // Stop the main loop.
142 bool addition_succeeded = m_loop.AddPendingCallback(
143 [](lldb_private::MainLoopBase &loop) { loop.RequestTermination(); });
144
145 // Wait for the main loop to exit, but not if we didn't succeed in inserting
146 // our pending callback or we'll wait forever.
147 if (addition_succeeded && m_loop_thread.joinable())
148 m_loop_thread.join();
149
150 m_accept_handles.clear();
151
152 m_server.reset(nullptr);
153 m_server_info_handle.Remove();
154 m_listener.reset();
155
156 return llvm::Error::success();
157}
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:375
#define LLDB_LOG_ERROR(log, error,...)
Definition Log.h:405
#define LLDB_PLUGIN_DEFINE(PluginName)
static constexpr llvm::StringLiteral kName
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:293
std::unique_ptr< Socket > m_listener
virtual void Extend(lldb_protocol::mcp::Server &server) const
llvm::Error Start(ProtocolServer::Connection connection) override
std::vector< ReadHandleUP > m_accept_handles
static lldb::ProtocolServerUP CreateInstance()
void AcceptCallback(std::unique_ptr< Socket > socket)
static llvm::StringRef GetPluginNameStatic()
lldb_protocol::mcp::ServerInfoHandle m_server_info_handle
static llvm::StringRef GetPluginDescriptionStatic()
void AddTool(std::unique_ptr< Tool > tool)
Definition Server.cpp:137
void AddResourceProvider(std::unique_ptr< ResourceProvider > resource_provider)
Definition Server.cpp:143
void PopulateServer(lldb_protocol::mcp::Server &server)
Installs the standard LLDB tool and resource-provider set on server.
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:338
llvm::StringLiteral GetServerVersion()
Version reported by LLDB's MCP server.
Definition Protocol.cpp:18
std::shared_ptr< lldb_private::IOObject > IOObjectSP
std::unique_ptr< lldb_private::ProtocolServer > ProtocolServerUP
Information about this instance of lldb's MCP server for lldb-mcp to use to coordinate connecting an ...
Definition Server.h:89
static llvm::Expected< ServerInfoHandle > Write(const ServerInfo &)
Writes the server info into a unique file in ~/.lldb.
Definition Server.cpp:69