[Lldb-commits] [lldb] [lldb] Adjust ProtocolServer connection defaults. (PR #155714)
John Harrison via lldb-commits
lldb-commits at lists.llvm.org
Wed Aug 27 16:03:13 PDT 2025
https://github.com/ashgti created https://github.com/llvm/llvm-project/pull/155714
This adjusts the ProtocolServer command to default to create a new connection listening on `localhost:0` and adds a new `ServerMetadata` details to `~/.lldb/mcp/lldb-<pid>.json` to record information about the current MCP server.
This can be consumed by the lldb-mcp binary to establish a connection from an LLM client.
>From b10f3ffca74e08234960fc0a3d4dfb5431d901ee Mon Sep 17 00:00:00 2001
From: John Harrison <harjohn at google.com>
Date: Wed, 27 Aug 2025 15:22:40 -0700
Subject: [PATCH 1/2] [lldb] NFC Moving mcp::Transport into its own file.
Moving `lldb_protocol::mcp::MCPTransport` out of Server.h and into its own file and simplifying the name to `Transport`.
---
lldb/include/lldb/Protocol/MCP/MCPError.h | 5 +--
lldb/include/lldb/Protocol/MCP/Protocol.h | 5 +++
lldb/include/lldb/Protocol/MCP/Server.h | 31 ++-------------
lldb/include/lldb/Protocol/MCP/Transport.h | 39 +++++++++++++++++++
.../Protocol/MCP/ProtocolServerMCP.cpp | 2 +-
lldb/source/Protocol/MCP/CMakeLists.txt | 1 +
lldb/source/Protocol/MCP/Server.cpp | 6 +--
lldb/source/Protocol/MCP/Transport.cpp | 32 +++++++++++++++
.../Protocol/ProtocolMCPServerTest.cpp | 7 ++--
9 files changed, 90 insertions(+), 38 deletions(-)
create mode 100644 lldb/include/lldb/Protocol/MCP/Transport.h
create mode 100644 lldb/source/Protocol/MCP/Transport.cpp
diff --git a/lldb/include/lldb/Protocol/MCP/MCPError.h b/lldb/include/lldb/Protocol/MCP/MCPError.h
index 55dd40f124a15..52c5a78fa23c0 100644
--- a/lldb/include/lldb/Protocol/MCP/MCPError.h
+++ b/lldb/include/lldb/Protocol/MCP/MCPError.h
@@ -19,7 +19,7 @@ class MCPError : public llvm::ErrorInfo<MCPError> {
public:
static char ID;
- MCPError(std::string message, int64_t error_code = kInternalError);
+ MCPError(std::string message, int64_t error_code = eErrorCodeInternalError);
void log(llvm::raw_ostream &OS) const override;
std::error_code convertToErrorCode() const override;
@@ -28,9 +28,6 @@ class MCPError : public llvm::ErrorInfo<MCPError> {
lldb_protocol::mcp::Error toProtocolError() const;
- static constexpr int64_t kResourceNotFound = -32002;
- static constexpr int64_t kInternalError = -32603;
-
private:
std::string m_message;
int64_t m_error_code;
diff --git a/lldb/include/lldb/Protocol/MCP/Protocol.h b/lldb/include/lldb/Protocol/MCP/Protocol.h
index 6e1ffcbe1f3e3..295f1f6e1b037 100644
--- a/lldb/include/lldb/Protocol/MCP/Protocol.h
+++ b/lldb/include/lldb/Protocol/MCP/Protocol.h
@@ -55,6 +55,11 @@ enum ErrorCode : signed {
eErrorCodeInvalidParams = -32602,
/// Internal JSON-RPC error.
eErrorCodeInternalError = -32603,
+
+ /// Additional MCP error codes.
+
+ /// Resource related uri not found.
+ eErrorCodeResourceNotFound = -32002,
};
struct Error {
diff --git a/lldb/include/lldb/Protocol/MCP/Server.h b/lldb/include/lldb/Protocol/MCP/Server.h
index aa5714e45755e..009c574fde92f 100644
--- a/lldb/include/lldb/Protocol/MCP/Server.h
+++ b/lldb/include/lldb/Protocol/MCP/Server.h
@@ -9,43 +9,20 @@
#ifndef LLDB_PROTOCOL_MCP_SERVER_H
#define LLDB_PROTOCOL_MCP_SERVER_H
-#include "lldb/Host/JSONTransport.h"
#include "lldb/Host/MainLoop.h"
#include "lldb/Protocol/MCP/Protocol.h"
#include "lldb/Protocol/MCP/Resource.h"
#include "lldb/Protocol/MCP/Tool.h"
+#include "lldb/Protocol/MCP/Transport.h"
#include "llvm/ADT/StringMap.h"
#include "llvm/Support/Error.h"
-#include <mutex>
namespace lldb_protocol::mcp {
-class MCPTransport
- : public lldb_private::JSONRPCTransport<Request, Response, Notification> {
-public:
- using LogCallback = std::function<void(llvm::StringRef message)>;
-
- MCPTransport(lldb::IOObjectSP in, lldb::IOObjectSP out,
- std::string client_name, LogCallback log_callback = {})
- : JSONRPCTransport(in, out), m_client_name(std::move(client_name)),
- m_log_callback(log_callback) {}
- virtual ~MCPTransport() = default;
-
- void Log(llvm::StringRef message) override {
- if (m_log_callback)
- m_log_callback(llvm::formatv("{0}: {1}", m_client_name, message).str());
- }
-
-private:
- std::string m_client_name;
- LogCallback m_log_callback;
-};
-
-class Server : public MCPTransport::MessageHandler {
+class Server : public Transport::MessageHandler {
public:
Server(std::string name, std::string version,
- std::unique_ptr<MCPTransport> transport_up,
- lldb_private::MainLoop &loop);
+ std::unique_ptr<Transport> transport_up, lldb_private::MainLoop &loop);
~Server() = default;
using NotificationHandler = std::function<void(const Notification &)>;
@@ -92,7 +69,7 @@ class Server : public MCPTransport::MessageHandler {
const std::string m_name;
const std::string m_version;
- std::unique_ptr<MCPTransport> m_transport_up;
+ std::unique_ptr<Transport> m_transport_up;
lldb_private::MainLoop &m_loop;
llvm::StringMap<std::unique_ptr<Tool>> m_tools;
diff --git a/lldb/include/lldb/Protocol/MCP/Transport.h b/lldb/include/lldb/Protocol/MCP/Transport.h
new file mode 100644
index 0000000000000..3cb725131f3b0
--- /dev/null
+++ b/lldb/include/lldb/Protocol/MCP/Transport.h
@@ -0,0 +1,39 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDB_PROTOCOL_MCP_TRANSPORT_H
+#define LLDB_PROTOCOL_MCP_TRANSPORT_H
+
+#include "lldb/Host/JSONTransport.h"
+#include "lldb/Protocol/MCP/Protocol.h"
+#include "lldb/lldb-forward.h"
+#include "llvm/ADT/StringRef.h"
+#include <functional>
+#include <string>
+
+namespace lldb_protocol::mcp {
+
+class Transport
+ : public lldb_private::JSONRPCTransport<Request, Response, Notification> {
+public:
+ using LogCallback = std::function<void(llvm::StringRef message)>;
+
+ Transport(lldb::IOObjectSP in, lldb::IOObjectSP out, std::string client_name,
+ LogCallback log_callback = {});
+ virtual ~Transport() = default;
+
+ void Log(llvm::StringRef message) override;
+
+private:
+ std::string m_client_name;
+ LogCallback m_log_callback;
+};
+
+} // namespace lldb_protocol::mcp
+
+#endif
diff --git a/lldb/source/Plugins/Protocol/MCP/ProtocolServerMCP.cpp b/lldb/source/Plugins/Protocol/MCP/ProtocolServerMCP.cpp
index 57132534cf680..a9c4164313a6d 100644
--- a/lldb/source/Plugins/Protocol/MCP/ProtocolServerMCP.cpp
+++ b/lldb/source/Plugins/Protocol/MCP/ProtocolServerMCP.cpp
@@ -67,7 +67,7 @@ void ProtocolServerMCP::AcceptCallback(std::unique_ptr<Socket> socket) {
LLDB_LOG(log, "New MCP client connected: {0}", client_name);
lldb::IOObjectSP io_sp = std::move(socket);
- auto transport_up = std::make_unique<lldb_protocol::mcp::MCPTransport>(
+ auto transport_up = std::make_unique<lldb_protocol::mcp::Transport>(
io_sp, io_sp, std::move(client_name), [&](llvm::StringRef message) {
LLDB_LOG(GetLog(LLDBLog::Host), "{0}", message);
});
diff --git a/lldb/source/Protocol/MCP/CMakeLists.txt b/lldb/source/Protocol/MCP/CMakeLists.txt
index a73e7e6a7cab1..23da62085537e 100644
--- a/lldb/source/Protocol/MCP/CMakeLists.txt
+++ b/lldb/source/Protocol/MCP/CMakeLists.txt
@@ -3,6 +3,7 @@ add_lldb_library(lldbProtocolMCP NO_PLUGIN_DEPENDENCIES
Protocol.cpp
Server.cpp
Tool.cpp
+ Transport.cpp
LINK_COMPONENTS
Support
diff --git a/lldb/source/Protocol/MCP/Server.cpp b/lldb/source/Protocol/MCP/Server.cpp
index 63c2d01d17922..fb317083b015e 100644
--- a/lldb/source/Protocol/MCP/Server.cpp
+++ b/lldb/source/Protocol/MCP/Server.cpp
@@ -15,7 +15,7 @@ using namespace lldb_protocol::mcp;
using namespace llvm;
Server::Server(std::string name, std::string version,
- std::unique_ptr<MCPTransport> transport_up,
+ std::unique_ptr<Transport> transport_up,
lldb_private::MainLoop &loop)
: m_name(std::move(name)), m_version(std::move(version)),
m_transport_up(std::move(transport_up)), m_loop(loop) {
@@ -180,7 +180,7 @@ llvm::Expected<Response> Server::ResourcesReadHandler(const Request &request) {
return make_error<MCPError>(
llvm::formatv("no resource handler for uri: {0}", uri_str).str(),
- MCPError::kResourceNotFound);
+ eErrorCodeResourceNotFound);
}
ServerCapabilities Server::GetCapabilities() {
@@ -219,7 +219,7 @@ void Server::Received(const Request &request) {
response.takeError(),
[&](const MCPError &err) { protocol_error = err.toProtocolError(); },
[&](const llvm::ErrorInfoBase &err) {
- protocol_error.code = MCPError::kInternalError;
+ protocol_error.code = eErrorCodeInternalError;
protocol_error.message = err.message();
});
Response error_response;
diff --git a/lldb/source/Protocol/MCP/Transport.cpp b/lldb/source/Protocol/MCP/Transport.cpp
new file mode 100644
index 0000000000000..58c66af7320ed
--- /dev/null
+++ b/lldb/source/Protocol/MCP/Transport.cpp
@@ -0,0 +1,32 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Protocol/MCP/Transport.h"
+#include "lldb/Host/JSONTransport.h"
+#include "lldb/lldb-forward.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/FormatVariadic.h"
+#include <string>
+#include <utility>
+
+using namespace llvm;
+using namespace lldb;
+
+namespace lldb_protocol::mcp {
+
+Transport::Transport(IOObjectSP in, IOObjectSP out, std::string client_name,
+ LogCallback log_callback)
+ : JSONRPCTransport(in, out), m_client_name(std::move(client_name)),
+ m_log_callback(log_callback) {}
+
+void Transport::Log(StringRef message) {
+ if (m_log_callback)
+ m_log_callback(formatv("{0}: {1}", m_client_name, message).str());
+}
+
+} // namespace lldb_protocol::mcp
diff --git a/lldb/unittests/Protocol/ProtocolMCPServerTest.cpp b/lldb/unittests/Protocol/ProtocolMCPServerTest.cpp
index 9fa446133d46f..846582ab9a74e 100644
--- a/lldb/unittests/Protocol/ProtocolMCPServerTest.cpp
+++ b/lldb/unittests/Protocol/ProtocolMCPServerTest.cpp
@@ -21,6 +21,7 @@
#include "lldb/Protocol/MCP/Resource.h"
#include "lldb/Protocol/MCP/Server.h"
#include "lldb/Protocol/MCP/Tool.h"
+#include "lldb/Protocol/MCP/Transport.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/Error.h"
#include "llvm/Support/JSON.h"
@@ -36,12 +37,12 @@ using namespace lldb_private;
using namespace lldb_protocol::mcp;
namespace {
-class TestMCPTransport final : public MCPTransport {
+class TestMCPTransport final : public lldb_protocol::mcp::Transport {
public:
TestMCPTransport(lldb::IOObjectSP in, lldb::IOObjectSP out)
- : lldb_protocol::mcp::MCPTransport(in, out, "unittest") {}
+ : lldb_protocol::mcp::Transport(in, out, "unittest") {}
- using MCPTransport::Write;
+ using Transport::Write;
void Log(llvm::StringRef message) override {
log_messages.emplace_back(message);
>From 5dc722adf2a4e8b6cc20c88ea514e6eb195de0f3 Mon Sep 17 00:00:00 2001
From: John Harrison <harjohn at google.com>
Date: Wed, 27 Aug 2025 15:58:04 -0700
Subject: [PATCH 2/2] [lldb] Adjust ProtocolServer connection defaults.
This adjusts the ProtocolServer command to default to create a new connection listening on `localhost:0` and adds a new `ServerMetadata` details to `~/.lldb/mcp/lldb-<pid>.json` to record information about the current MCP server.
This can be consumed by the lldb-mcp binary to establish a connection from an LLM client.
---
lldb/include/lldb/Protocol/MCP/Server.h | 12 +++++
.../Commands/CommandObjectProtocolServer.cpp | 15 +++---
.../Protocol/MCP/ProtocolServerMCP.cpp | 47 +++++++++++++++++--
.../Plugins/Protocol/MCP/ProtocolServerMCP.h | 1 +
lldb/source/Protocol/MCP/Server.cpp | 12 +++++
5 files changed, 76 insertions(+), 11 deletions(-)
diff --git a/lldb/include/lldb/Protocol/MCP/Server.h b/lldb/include/lldb/Protocol/MCP/Server.h
index 009c574fde92f..54f004b17546e 100644
--- a/lldb/include/lldb/Protocol/MCP/Server.h
+++ b/lldb/include/lldb/Protocol/MCP/Server.h
@@ -14,11 +14,23 @@
#include "lldb/Protocol/MCP/Resource.h"
#include "lldb/Protocol/MCP/Tool.h"
#include "lldb/Protocol/MCP/Transport.h"
+#include "lldb/lldb-types.h"
#include "llvm/ADT/StringMap.h"
#include "llvm/Support/Error.h"
+#include "llvm/Support/JSON.h"
namespace lldb_protocol::mcp {
+/// Metadata about this instance of lldb's MCP server for lldb-mcp to use to
+/// coordinate connecting an lldb-mcp client.
+struct ServerMetadata {
+ std::string connection_uri;
+ lldb::pid_t pid;
+};
+llvm::json::Value toJSON(const ServerMetadata &SM);
+bool fromJSON(const llvm::json::Value &V, ServerMetadata &SM,
+ llvm::json::Path P);
+
class Server : public Transport::MessageHandler {
public:
Server(std::string name, std::string version,
diff --git a/lldb/source/Commands/CommandObjectProtocolServer.cpp b/lldb/source/Commands/CommandObjectProtocolServer.cpp
index f11e27f01c8a8..81c97614b8fe3 100644
--- a/lldb/source/Commands/CommandObjectProtocolServer.cpp
+++ b/lldb/source/Commands/CommandObjectProtocolServer.cpp
@@ -15,6 +15,7 @@
#include "lldb/Utility/UriParser.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/Support/FormatAdapters.h"
+#include <string>
using namespace llvm;
using namespace lldb;
@@ -28,7 +29,7 @@ class CommandObjectProtocolServerStart : public CommandObjectParsed {
CommandObjectProtocolServerStart(CommandInterpreter &interpreter)
: CommandObjectParsed(interpreter, "protocol-server start",
"start protocol server",
- "protocol-server start <protocol> <connection>") {
+ "protocol-server start <protocol> [<connection>]") {
AddSimpleArgumentList(lldb::eArgTypeProtocol, eArgRepeatPlain);
AddSimpleArgumentList(lldb::eArgTypeConnectURL, eArgRepeatPlain);
}
@@ -51,15 +52,13 @@ class CommandObjectProtocolServerStart : public CommandObjectParsed {
return;
}
- if (args.GetArgumentCount() < 2) {
- result.AppendError("no connection specified");
- return;
- }
- llvm::StringRef connection_uri = args.GetArgumentAtIndex(1);
+ std::string connection_uri = "listen://[localhost]:0";
+ if (args.GetArgumentCount() > 2)
+ connection_uri = args.GetArgumentAtIndex(1);
const char *connection_error =
- "unsupported connection specifier, expected 'accept:///path' or "
- "'listen://[host]:port', got '{0}'.";
+ "unsupported connection specifier, expected 'accept:///path' "
+ "or 'listen://[host]:port', got '{0}'.";
auto uri = lldb_private::URI::Parse(connection_uri);
if (!uri) {
result.AppendErrorWithFormatv(connection_error, connection_uri);
diff --git a/lldb/source/Plugins/Protocol/MCP/ProtocolServerMCP.cpp b/lldb/source/Plugins/Protocol/MCP/ProtocolServerMCP.cpp
index a9c4164313a6d..9068ff92a9aab 100644
--- a/lldb/source/Plugins/Protocol/MCP/ProtocolServerMCP.cpp
+++ b/lldb/source/Plugins/Protocol/MCP/ProtocolServerMCP.cpp
@@ -10,14 +10,14 @@
#include "Resource.h"
#include "Tool.h"
#include "lldb/Core/PluginManager.h"
-#include "lldb/Protocol/MCP/MCPError.h"
-#include "lldb/Protocol/MCP/Tool.h"
+#include "lldb/Host/FileSystem.h"
+#include "lldb/Protocol/MCP/Server.h"
#include "lldb/Utility/LLDBLog.h"
#include "lldb/Utility/Log.h"
#include "llvm/ADT/StringExtras.h"
+#include "llvm/Support/Error.h"
#include "llvm/Support/Threading.h"
#include <thread>
-#include <variant>
using namespace lldb_private;
using namespace lldb_private::mcp;
@@ -104,6 +104,43 @@ llvm::Error ProtocolServerMCP::Start(ProtocolServer::Connection connection) {
if (llvm::Error error = handles.takeError())
return error;
+ auto listening_uris = m_listener->GetListeningConnectionURI();
+ if (listening_uris.empty())
+ return createStringError("Failed to list listening connections");
+ std::string address =
+ llvm::join(m_listener->GetListeningConnectionURI(), ", ");
+
+ llvm::SmallString<128> user_home_dir;
+ FileSystem::Instance().GetHomeDirectory(user_home_dir);
+ FileSpec mcp_registry_dir = FileSpec(user_home_dir.c_str());
+ mcp_registry_dir.AppendPathComponent(".lldb");
+ mcp_registry_dir.AppendPathComponent("mcp");
+
+ Status error(llvm::sys::fs::create_directory(mcp_registry_dir.GetPath()));
+ if (error.Fail())
+ return error.takeError();
+
+ m_mcp_registry_entry_path = mcp_registry_dir.CopyByAppendingPathComponent(
+ formatv("lldb-{0}.json", getpid()).str());
+
+ const File::OpenOptions flags = File::eOpenOptionWriteOnly |
+ File::eOpenOptionCanCreate |
+ File::eOpenOptionTruncate;
+ llvm::Expected<lldb::FileUP> file =
+ FileSystem::Instance().Open(m_mcp_registry_entry_path, flags,
+ lldb::eFilePermissionsFileDefault, false);
+ if (!file)
+ return file.takeError();
+
+ ServerMetadata metadata;
+ metadata.connection_uri = listening_uris[0];
+ metadata.pid = getpid();
+
+ std::string buf = formatv("{0}", toJSON(metadata)).str();
+ size_t num_bytes = buf.size();
+ if (llvm::Error error = (*file)->Write(buf.data(), num_bytes).takeError())
+ return error;
+
m_running = true;
m_listen_handlers = std::move(*handles);
m_loop_thread = std::thread([=] {
@@ -122,6 +159,10 @@ llvm::Error ProtocolServerMCP::Stop() {
m_running = false;
}
+ if (!m_mcp_registry_entry_path.GetPath().empty())
+ FileSystem::Instance().RemoveFile(m_mcp_registry_entry_path);
+ m_mcp_registry_entry_path.Clear();
+
// Stop the main loop.
m_loop.AddPendingCallback(
[](lldb_private::MainLoopBase &loop) { loop.RequestTermination(); });
diff --git a/lldb/source/Plugins/Protocol/MCP/ProtocolServerMCP.h b/lldb/source/Plugins/Protocol/MCP/ProtocolServerMCP.h
index fc650ffe0dfa7..004fa3c2d05a8 100644
--- a/lldb/source/Plugins/Protocol/MCP/ProtocolServerMCP.h
+++ b/lldb/source/Plugins/Protocol/MCP/ProtocolServerMCP.h
@@ -48,6 +48,7 @@ class ProtocolServerMCP : public ProtocolServer {
bool m_running = false;
+ FileSpec m_mcp_registry_entry_path;
lldb_private::MainLoop m_loop;
std::thread m_loop_thread;
std::mutex m_mutex;
diff --git a/lldb/source/Protocol/MCP/Server.cpp b/lldb/source/Protocol/MCP/Server.cpp
index fb317083b015e..47cfb1a6114b4 100644
--- a/lldb/source/Protocol/MCP/Server.cpp
+++ b/lldb/source/Protocol/MCP/Server.cpp
@@ -14,6 +14,18 @@
using namespace lldb_protocol::mcp;
using namespace llvm;
+llvm::json::Value toJSON(const ServerMetadata &SM) {
+ return llvm::json::Object{{"connection_uri", SM.connection_uri},
+ {"pid", SM.pid}};
+}
+
+bool fromJSON(const llvm::json::Value &V, ServerMetadata &SM,
+ llvm::json::Path P) {
+ llvm::json::ObjectMapper O(V, P);
+ return O && O.map("connection_uri", SM.connection_uri) &&
+ O.map("pid", SM.pid);
+}
+
Server::Server(std::string name, std::string version,
std::unique_ptr<Transport> transport_up,
lldb_private::MainLoop &loop)
More information about the lldb-commits
mailing list