[Lldb-commits] [lldb] [lldb] Add Model Context Protocol (MCP) support to LLDB (PR #143628)

Jonas Devlieghere via lldb-commits lldb-commits at lists.llvm.org
Mon Jun 16 12:21:03 PDT 2025


================
@@ -0,0 +1,280 @@
+//===- ProtocolServerMCP.cpp ----------------------------------------------===//
+//
+// 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 "ProtocolServerMCP.h"
+#include "MCPError.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Utility/LLDBLog.h"
+#include "lldb/Utility/Log.h"
+#include "llvm/ADT/StringExtras.h"
+#include "llvm/Support/Threading.h"
+#include <thread>
+
+using namespace lldb_private;
+using namespace lldb_private::mcp;
+using namespace llvm;
+
+LLDB_PLUGIN_DEFINE(ProtocolServerMCP)
+
+ProtocolServerMCP::ProtocolServerMCP(Debugger &debugger)
+    : ProtocolServer(), m_debugger(debugger) {
+  AddHandler("initialize", std::bind(&ProtocolServerMCP::InitializeHandler,
+                                     this, std::placeholders::_1));
+  AddHandler("tools/list", std::bind(&ProtocolServerMCP::ToolsListHandler, this,
+                                     std::placeholders::_1));
+  AddHandler("tools/call", std::bind(&ProtocolServerMCP::ToolsCallHandler, this,
+                                     std::placeholders::_1));
+  AddTool(std::make_unique<LLDBCommandTool>(
+      "lldb_command", "Run an lldb command.", m_debugger));
+}
+
+ProtocolServerMCP::~ProtocolServerMCP() { llvm::consumeError(Stop()); }
+
+void ProtocolServerMCP::Initialize() {
+  PluginManager::RegisterPlugin(GetPluginNameStatic(),
+                                GetPluginDescriptionStatic(), CreateInstance);
+}
+
+void ProtocolServerMCP::Terminate() {
+  PluginManager::UnregisterPlugin(CreateInstance);
+}
+
+lldb::ProtocolServerSP ProtocolServerMCP::CreateInstance(Debugger &debugger) {
+  return std::make_shared<ProtocolServerMCP>(debugger);
+}
+
+llvm::StringRef ProtocolServerMCP::GetPluginDescriptionStatic() {
+  return "MCP Server.";
+}
+
+llvm::Expected<protocol::Response>
+ProtocolServerMCP::Handle(protocol::Request request) {
+  auto it = m_handlers.find(request.method);
+  if (it != m_handlers.end())
+    return it->second(request);
+
+  return make_error<MCPError>(
+      llvm::formatv("no handler for request: {0}", request.method).str(), 1);
+}
+
+llvm::Error ProtocolServerMCP::Start(ProtocolServer::Connection connection) {
+  std::lock_guard<std::mutex> guard(m_server_mutex);
+
+  if (m_running)
+    return llvm::createStringError("server already running");
+
+  Status status;
+  m_listener = Socket::Create(connection.protocol, status);
+  if (status.Fail())
+    return status.takeError();
+
+  status = m_listener->Listen(connection.name, /*backlog=*/5);
+  if (status.Fail())
+    return status.takeError();
+
+  std::string address =
+      llvm::join(m_listener->GetListeningConnectionURI(), ", ");
+  Log *log = GetLog(LLDBLog::Host);
+  LLDB_LOG(log, "MCP server started with connection listeners: {0}", address);
+
+  auto handles = m_listener->Accept(m_loop, [=](std::unique_ptr<Socket> sock) {
+    std::lock_guard<std::mutex> guard(m_server_mutex);
+
+    const std::string client_name =
+        llvm::formatv("client-{0}", m_clients.size() + 1).str();
+    LLDB_LOG(log, "client {0} connected", client_name);
+
+    lldb::IOObjectSP io(std::move(sock));
+
+    m_clients.emplace_back(io, [=]() {
+      llvm::set_thread_name(client_name + "-runloop");
+      if (auto Err = Run(std::make_unique<JSONRPCTransport>(io, io)))
+        LLDB_LOG_ERROR(GetLog(LLDBLog::Host), std::move(Err), "MCP Error: {0}");
+    });
+  });
+  if (llvm::Error error = handles.takeError())
+    return error;
+
+  m_read_handles = std::move(*handles);
+  m_loop_thread = std::thread([=] {
+    llvm::set_thread_name("mcp-runloop");
+    m_loop.Run();
+  });
+
+  return llvm::Error::success();
+}
+
+llvm::Error ProtocolServerMCP::Stop() {
+  {
+    std::lock_guard<std::mutex> guard(m_server_mutex);
+    m_running = false;
+  }
+
+  // Stop accepting new connections.
+  m_loop.AddPendingCallback(
+      [](MainLoopBase &loop) { loop.RequestTermination(); });
+
+  // Wait for the main loop to exit.
+  if (m_loop_thread.joinable())
+    m_loop_thread.join();
+
+  // Wait for all our clients to exit.
+  for (auto &client : m_clients) {
+    client.first->Close();
----------------
JDevlieghere wrote:

Thanks Pavel, I knew about all those things in isolation but I didn't put the pieces together. There's no reason we can't do this fully in the MainLoop. 

https://github.com/llvm/llvm-project/pull/143628


More information about the lldb-commits mailing list