https://github.com/JDevlieghere updated 
https://github.com/llvm/llvm-project/pull/148075

>From 74cc4598eccdb43b3a36e3276d16de84a901f095 Mon Sep 17 00:00:00 2001
From: Jonas Devlieghere <jo...@devlieghere.com>
Date: Thu, 10 Jul 2025 15:51:44 -0700
Subject: [PATCH 1/3] [lldb] Expose debuggers and target as resources through
 MCP

Expose debuggers and target as resources through MCP. This has two
advantages:

 1. Enables returning data in a structured way. Although tools can
    return structured data with the latest revision of the protocol, we
    might not be able to update before the majority of clients has
    adopted it.
 2. Enables the user to specify a resource themselves, rather than
    letting the model guess which debugger instance it should use.

This PR exposes a resource for debuggers and targets.

The following URI returns information about a given debugger instance:

```
lldb://debugger/<debugger id>
```

For example:

```
{
  uri: "lldb://debugger/0"
  mimeType: "application/json"
  text: "{"debugger_id":0,"num_targets":2}"
}
```

The following URI returns information about a given target:

```
lldb://debugger/<debugger id>/target/<target id>
```

For example:

```
{
  uri: "lldb://debugger/0/target/0"
  mimeType: "application/json"
  text: 
"{"arch":"arm64-apple-macosx26.0.0","debugger_id":0,"path":"/Users/jonas/llvm/build-ra/bin/count","target_id":0}"
}
```
---
 .../Plugins/Protocol/MCP/CMakeLists.txt       |   1 +
 lldb/source/Plugins/Protocol/MCP/MCPError.cpp |  11 ++
 lldb/source/Plugins/Protocol/MCP/MCPError.h   |  14 ++
 lldb/source/Plugins/Protocol/MCP/Protocol.cpp |  54 +++++-
 lldb/source/Plugins/Protocol/MCP/Protocol.h   |  60 ++++++-
 .../Protocol/MCP/ProtocolServerMCP.cpp        |  82 ++++++++-
 .../Plugins/Protocol/MCP/ProtocolServerMCP.h  |  10 ++
 lldb/source/Plugins/Protocol/MCP/Resource.cpp | 166 ++++++++++++++++++
 lldb/source/Plugins/Protocol/MCP/Resource.h   |  51 ++++++
 lldb/source/Plugins/Protocol/MCP/Tool.cpp     |  47 -----
 lldb/source/Plugins/Protocol/MCP/Tool.h       |   9 -
 .../Protocol/ProtocolMCPServerTest.cpp        |  41 ++++-
 lldb/unittests/Protocol/ProtocolMCPTest.cpp   |  98 +++++++++++
 13 files changed, 581 insertions(+), 63 deletions(-)
 create mode 100644 lldb/source/Plugins/Protocol/MCP/Resource.cpp
 create mode 100644 lldb/source/Plugins/Protocol/MCP/Resource.h

diff --git a/lldb/source/Plugins/Protocol/MCP/CMakeLists.txt 
b/lldb/source/Plugins/Protocol/MCP/CMakeLists.txt
index db31a7a69cb33..e104fb527e57a 100644
--- a/lldb/source/Plugins/Protocol/MCP/CMakeLists.txt
+++ b/lldb/source/Plugins/Protocol/MCP/CMakeLists.txt
@@ -2,6 +2,7 @@ add_lldb_library(lldbPluginProtocolServerMCP PLUGIN
   MCPError.cpp
   Protocol.cpp
   ProtocolServerMCP.cpp
+  Resource.cpp
   Tool.cpp
 
   LINK_COMPONENTS
diff --git a/lldb/source/Plugins/Protocol/MCP/MCPError.cpp 
b/lldb/source/Plugins/Protocol/MCP/MCPError.cpp
index 5ed850066b659..659b53a14fe23 100644
--- a/lldb/source/Plugins/Protocol/MCP/MCPError.cpp
+++ b/lldb/source/Plugins/Protocol/MCP/MCPError.cpp
@@ -14,6 +14,7 @@
 namespace lldb_private::mcp {
 
 char MCPError::ID;
+char UnsupportedURI::ID;
 
 MCPError::MCPError(std::string message, int64_t error_code)
     : m_message(message), m_error_code(error_code) {}
@@ -31,4 +32,14 @@ protocol::Error MCPError::toProtcolError() const {
   return error;
 }
 
+UnsupportedURI::UnsupportedURI(std::string uri) : m_uri(uri) {}
+
+void UnsupportedURI::log(llvm::raw_ostream &OS) const {
+  OS << "unsupported uri: " << m_uri;
+}
+
+std::error_code UnsupportedURI::convertToErrorCode() const {
+  return llvm::inconvertibleErrorCode();
+}
+
 } // namespace lldb_private::mcp
diff --git a/lldb/source/Plugins/Protocol/MCP/MCPError.h 
b/lldb/source/Plugins/Protocol/MCP/MCPError.h
index 2a76a7b087e20..05a047ec881a9 100644
--- a/lldb/source/Plugins/Protocol/MCP/MCPError.h
+++ b/lldb/source/Plugins/Protocol/MCP/MCPError.h
@@ -8,6 +8,7 @@
 
 #include "Protocol.h"
 #include "llvm/Support/Error.h"
+#include "llvm/Support/FormatVariadic.h"
 #include <string>
 
 namespace lldb_private::mcp {
@@ -30,4 +31,17 @@ class MCPError : public llvm::ErrorInfo<MCPError> {
   int64_t m_error_code;
 };
 
+class UnsupportedURI : public llvm::ErrorInfo<UnsupportedURI> {
+public:
+  static char ID;
+
+  UnsupportedURI(std::string uri);
+
+  void log(llvm::raw_ostream &OS) const override;
+  std::error_code convertToErrorCode() const override;
+
+private:
+  std::string m_uri;
+};
+
 } // namespace lldb_private::mcp
diff --git a/lldb/source/Plugins/Protocol/MCP/Protocol.cpp 
b/lldb/source/Plugins/Protocol/MCP/Protocol.cpp
index d66c931a0b284..e42e1bf1118cf 100644
--- a/lldb/source/Plugins/Protocol/MCP/Protocol.cpp
+++ b/lldb/source/Plugins/Protocol/MCP/Protocol.cpp
@@ -107,8 +107,36 @@ bool fromJSON(const llvm::json::Value &V, ToolCapability 
&TC,
   return O && O.map("listChanged", TC.listChanged);
 }
 
+llvm::json::Value toJSON(const ResourceCapability &RC) {
+  return llvm::json::Object{{"listChanged", RC.listChanged},
+                            {"subscribe", RC.subscribe}};
+}
+
+bool fromJSON(const llvm::json::Value &V, ResourceCapability &RC,
+              llvm::json::Path P) {
+  llvm::json::ObjectMapper O(V, P);
+  return O && O.map("listChanged", RC.listChanged) &&
+         O.map("subscribe", RC.subscribe);
+}
+
 llvm::json::Value toJSON(const Capabilities &C) {
-  return llvm::json::Object{{"tools", C.tools}};
+  return llvm::json::Object{{"tools", C.tools}, {"resources", C.resources}};
+}
+
+bool fromJSON(const llvm::json::Value &V, Resource &R, llvm::json::Path P) {
+  llvm::json::ObjectMapper O(V, P);
+  return O && O.map("uri", R.uri) && O.map("name", R.name) &&
+         O.mapOptional("description", R.description) &&
+         O.mapOptional("mimeType", R.mimeType);
+}
+
+llvm::json::Value toJSON(const Resource &R) {
+  llvm::json::Object Result{{"uri", R.uri}, {"name", R.name}};
+  if (R.description)
+    Result.insert({"description", R.description});
+  if (R.mimeType)
+    Result.insert({"mimeType", R.mimeType});
+  return Result;
 }
 
 bool fromJSON(const llvm::json::Value &V, Capabilities &C, llvm::json::Path P) 
{
@@ -116,6 +144,30 @@ bool fromJSON(const llvm::json::Value &V, Capabilities &C, 
llvm::json::Path P) {
   return O && O.map("tools", C.tools);
 }
 
+llvm::json::Value toJSON(const ResourceContents &RC) {
+  llvm::json::Object Result{{"uri", RC.uri}, {"text", RC.text}};
+  if (RC.mimeType)
+    Result.insert({"mimeType", RC.mimeType});
+  return Result;
+}
+
+bool fromJSON(const llvm::json::Value &V, ResourceContents &RC,
+              llvm::json::Path P) {
+  llvm::json::ObjectMapper O(V, P);
+  return O && O.map("uri", RC.uri) && O.map("text", RC.text) &&
+         O.mapOptional("mimeType", RC.mimeType);
+}
+
+llvm::json::Value toJSON(const ResourceResult &RR) {
+  return llvm::json::Object{{"contents", RR.contents}};
+}
+
+bool fromJSON(const llvm::json::Value &V, ResourceResult &RR,
+              llvm::json::Path P) {
+  llvm::json::ObjectMapper O(V, P);
+  return O && O.map("contents", RR.contents);
+}
+
 llvm::json::Value toJSON(const TextContent &TC) {
   return llvm::json::Object{{"type", "text"}, {"text", TC.text}};
 }
diff --git a/lldb/source/Plugins/Protocol/MCP/Protocol.h 
b/lldb/source/Plugins/Protocol/MCP/Protocol.h
index cb790dc4e5596..ffe621bee1c2a 100644
--- a/lldb/source/Plugins/Protocol/MCP/Protocol.h
+++ b/lldb/source/Plugins/Protocol/MCP/Protocol.h
@@ -76,17 +76,75 @@ struct ToolCapability {
 llvm::json::Value toJSON(const ToolCapability &);
 bool fromJSON(const llvm::json::Value &, ToolCapability &, llvm::json::Path);
 
+struct ResourceCapability {
+  /// Whether this server supports notifications for changes to the resources
+  /// list.
+  bool listChanged = false;
+
+  ///  Whether subscriptions are supported.
+  bool subscribe = false;
+};
+
+llvm::json::Value toJSON(const ResourceCapability &);
+bool fromJSON(const llvm::json::Value &, ResourceCapability &,
+              llvm::json::Path);
+
 /// Capabilities that a server may support. Known capabilities are defined 
here,
 /// in this schema, but this is not a closed set: any server can define its 
own,
 /// additional capabilities.
 struct Capabilities {
-  /// Present if the server offers any tools to call.
+  /// Tool capabilities of the server.
   ToolCapability tools;
+
+  /// Resource capabilities of the server.
+  ResourceCapability resources;
 };
 
 llvm::json::Value toJSON(const Capabilities &);
 bool fromJSON(const llvm::json::Value &, Capabilities &, llvm::json::Path);
 
+/// A known resource that the server is capable of reading.
+struct Resource {
+  /// The URI of this resource.
+  std::string uri;
+
+  /// A human-readable name for this resource.
+  std::string name;
+
+  /// A description of what this resource represents.
+  std::optional<std::string> description;
+
+  /// The MIME type of this resource, if known.
+  std::optional<std::string> mimeType;
+};
+
+llvm::json::Value toJSON(const Resource &);
+bool fromJSON(const llvm::json::Value &, Resource &, llvm::json::Path);
+
+/// The contents of a specific resource or sub-resource.
+struct ResourceContents {
+  /// The URI of this resource.
+  std::string uri;
+
+  /// The text of the item. This must only be set if the item can actually be
+  /// represented as text (not binary data).
+  std::string text;
+
+  /// The MIME type of this resource, if known.
+  std::optional<std::string> mimeType;
+};
+
+llvm::json::Value toJSON(const ResourceContents &);
+bool fromJSON(const llvm::json::Value &, ResourceContents &, llvm::json::Path);
+
+/// The server's response to a resources/read request from the client.
+struct ResourceResult {
+  std::vector<ResourceContents> contents;
+};
+
+llvm::json::Value toJSON(const ResourceResult &);
+bool fromJSON(const llvm::json::Value &, ResourceResult &, llvm::json::Path);
+
 /// Text provided to or from an LLM.
 struct TextContent {
   /// The text content of the message.
diff --git a/lldb/source/Plugins/Protocol/MCP/ProtocolServerMCP.cpp 
b/lldb/source/Plugins/Protocol/MCP/ProtocolServerMCP.cpp
index 3180341b50b91..6099d429672bb 100644
--- a/lldb/source/Plugins/Protocol/MCP/ProtocolServerMCP.cpp
+++ b/lldb/source/Plugins/Protocol/MCP/ProtocolServerMCP.cpp
@@ -28,20 +28,29 @@ ProtocolServerMCP::ProtocolServerMCP() : ProtocolServer() {
   AddRequestHandler("initialize",
                     std::bind(&ProtocolServerMCP::InitializeHandler, this,
                               std::placeholders::_1));
+
   AddRequestHandler("tools/list",
                     std::bind(&ProtocolServerMCP::ToolsListHandler, this,
                               std::placeholders::_1));
   AddRequestHandler("tools/call",
                     std::bind(&ProtocolServerMCP::ToolsCallHandler, this,
                               std::placeholders::_1));
+
+  AddRequestHandler("resources/list",
+                    std::bind(&ProtocolServerMCP::ResourcesListHandler, this,
+                              std::placeholders::_1));
+  AddRequestHandler("resources/read",
+                    std::bind(&ProtocolServerMCP::ResourcesReadHandler, this,
+                              std::placeholders::_1));
   AddNotificationHandler(
       "notifications/initialized", [](const protocol::Notification &) {
         LLDB_LOG(GetLog(LLDBLog::Host), "MCP initialization complete");
       });
+
   AddTool(
       std::make_unique<CommandTool>("lldb_command", "Run an lldb command."));
-  AddTool(std::make_unique<DebuggerListTool>(
-      "lldb_debugger_list", "List debugger instances with their 
debugger_id."));
+
+  AddResourceProvider(std::make_unique<DebuggerResourceProvider>());
 }
 
 ProtocolServerMCP::~ProtocolServerMCP() { llvm::consumeError(Stop()); }
@@ -244,6 +253,7 @@ ProtocolServerMCP::HandleData(llvm::StringRef data) {
 protocol::Capabilities ProtocolServerMCP::GetCapabilities() {
   protocol::Capabilities capabilities;
   capabilities.tools.listChanged = true;
+  capabilities.resources.listChanged = false;
   return capabilities;
 }
 
@@ -255,6 +265,15 @@ void ProtocolServerMCP::AddTool(std::unique_ptr<Tool> 
tool) {
   m_tools[tool->GetName()] = std::move(tool);
 }
 
+void ProtocolServerMCP::AddResourceProvider(
+    std::unique_ptr<ResourceProvider> resource_provider) {
+  std::lock_guard<std::mutex> guard(m_server_mutex);
+
+  if (!resource_provider)
+    return;
+  m_resource_providers.push_back(std::move(resource_provider));
+}
+
 void ProtocolServerMCP::AddRequestHandler(llvm::StringRef method,
                                           RequestHandler handler) {
   std::lock_guard<std::mutex> guard(m_server_mutex);
@@ -327,3 +346,62 @@ ProtocolServerMCP::ToolsCallHandler(const 
protocol::Request &request) {
 
   return response;
 }
+
+llvm::Expected<protocol::Response>
+ProtocolServerMCP::ResourcesListHandler(const protocol::Request &request) {
+  protocol::Response response;
+
+  llvm::json::Array resources;
+
+  std::lock_guard<std::mutex> guard(m_server_mutex);
+  for (std::unique_ptr<ResourceProvider> &resource_provider_up :
+       m_resource_providers) {
+    for (const protocol::Resource &resource :
+         resource_provider_up->GetResources())
+      resources.push_back(resource);
+  }
+  response.result.emplace(
+      llvm::json::Object{{"resources", std::move(resources)}});
+
+  return response;
+}
+
+llvm::Expected<protocol::Response>
+ProtocolServerMCP::ResourcesReadHandler(const protocol::Request &request) {
+  protocol::Response response;
+
+  if (!request.params)
+    return llvm::createStringError("no resource parameters");
+
+  const json::Object *param_obj = request.params->getAsObject();
+  if (!param_obj)
+    return llvm::createStringError("no resource parameters");
+
+  const json::Value *uri = param_obj->get("uri");
+  if (!uri)
+    return llvm::createStringError("no resource uri");
+
+  llvm::StringRef uri_str = uri->getAsString().value_or("");
+  if (uri_str.empty())
+    return llvm::createStringError("no resource uri");
+
+  std::lock_guard<std::mutex> guard(m_server_mutex);
+  for (std::unique_ptr<ResourceProvider> &resource_provider_up :
+       m_resource_providers) {
+    llvm::Expected<protocol::ResourceResult> result =
+        resource_provider_up->ReadResource(uri_str);
+    if (result.errorIsA<UnsupportedURI>()) {
+      llvm::consumeError(result.takeError());
+      continue;
+    }
+    if (!result)
+      return result.takeError();
+
+    protocol::Response response;
+    response.result.emplace(std::move(*result));
+    return response;
+  }
+
+  return make_error<MCPError>(
+      llvm::formatv("no resource handler for uri: {0}", uri_str).str(), 1);
+}
diff --git a/lldb/source/Plugins/Protocol/MCP/ProtocolServerMCP.h 
b/lldb/source/Plugins/Protocol/MCP/ProtocolServerMCP.h
index d55882cc8ab09..e273f6e2a8d37 100644
--- a/lldb/source/Plugins/Protocol/MCP/ProtocolServerMCP.h
+++ b/lldb/source/Plugins/Protocol/MCP/ProtocolServerMCP.h
@@ -10,6 +10,7 @@
 #define LLDB_PLUGINS_PROTOCOL_MCP_PROTOCOLSERVERMCP_H
 
 #include "Protocol.h"
+#include "Resource.h"
 #include "Tool.h"
 #include "lldb/Core/ProtocolServer.h"
 #include "lldb/Host/MainLoop.h"
@@ -46,6 +47,8 @@ class ProtocolServerMCP : public ProtocolServer {
       std::function<void(const protocol::Notification &)>;
 
   void AddTool(std::unique_ptr<Tool> tool);
+  void AddResourceProvider(std::unique_ptr<ResourceProvider> 
resource_provider);
+
   void AddRequestHandler(llvm::StringRef method, RequestHandler handler);
   void AddNotificationHandler(llvm::StringRef method,
                               NotificationHandler handler);
@@ -61,11 +64,17 @@ class ProtocolServerMCP : public ProtocolServer {
 
   llvm::Expected<protocol::Response>
   InitializeHandler(const protocol::Request &);
+
   llvm::Expected<protocol::Response>
   ToolsListHandler(const protocol::Request &);
   llvm::Expected<protocol::Response>
   ToolsCallHandler(const protocol::Request &);
 
+  llvm::Expected<protocol::Response>
+  ResourcesListHandler(const protocol::Request &);
+  llvm::Expected<protocol::Response>
+  ResourcesReadHandler(const protocol::Request &);
+
   protocol::Capabilities GetCapabilities();
 
   llvm::StringLiteral kName = "lldb-mcp";
@@ -89,6 +98,7 @@ class ProtocolServerMCP : public ProtocolServer {
 
   std::mutex m_server_mutex;
   llvm::StringMap<std::unique_ptr<Tool>> m_tools;
+  std::vector<std::unique_ptr<ResourceProvider>> m_resource_providers;
 
   llvm::StringMap<RequestHandler> m_request_handlers;
   llvm::StringMap<NotificationHandler> m_notification_handlers;
diff --git a/lldb/source/Plugins/Protocol/MCP/Resource.cpp 
b/lldb/source/Plugins/Protocol/MCP/Resource.cpp
new file mode 100644
index 0000000000000..8cb4e3916b1b1
--- /dev/null
+++ b/lldb/source/Plugins/Protocol/MCP/Resource.cpp
@@ -0,0 +1,166 @@
+// 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 "Resource.h"
+#include "MCPError.h"
+#include "lldb/Core/Debugger.h"
+#include "lldb/Core/Module.h"
+
+using namespace lldb_private::mcp;
+
+template <typename... Args>
+static llvm::Error createStringError(const char *format, Args &&...args) {
+  return llvm::createStringError(
+      llvm::formatv(format, std::forward<Args>(args)...).str());
+}
+
+static llvm::Error createUnsupportedURIError(llvm::StringRef uri) {
+  return llvm::make_error<UnsupportedURI>(uri.str());
+}
+
+protocol::Resource
+DebuggerResourceProvider::GetDebuggerResource(lldb::user_id_t debugger_id) {
+  protocol::Resource resource;
+  resource.uri = llvm::formatv("lldb://debugger/{0}", debugger_id);
+  resource.name = llvm::formatv("debugger {0}", debugger_id);
+  resource.description =
+      llvm::formatv("Information about debugger instance {0}", debugger_id);
+  resource.mimeType = "application/json";
+  return resource;
+}
+
+protocol::Resource
+DebuggerResourceProvider::GetTargetResource(lldb::user_id_t debugger_id,
+                                            lldb::user_id_t target_id) {
+  protocol::Resource resource;
+  resource.uri =
+      llvm::formatv("lldb://debugger/{0}/target/{1}", debugger_id, target_id);
+  resource.name = llvm::formatv("target {0}", target_id);
+  resource.description =
+      llvm::formatv("Information about target {0} in debugger instance {1}",
+                    target_id, debugger_id);
+  resource.mimeType = "application/json";
+  return resource;
+}
+
+std::vector<protocol::Resource> DebuggerResourceProvider::GetResources() const 
{
+  std::vector<protocol::Resource> resources;
+
+  const size_t num_debuggers = Debugger::GetNumDebuggers();
+  for (size_t i = 0; i < num_debuggers; ++i) {
+    lldb::DebuggerSP debugger_sp = Debugger::GetDebuggerAtIndex(i);
+    if (!debugger_sp)
+      continue;
+    resources.emplace_back(GetDebuggerResource(i));
+
+    TargetList &target_list = debugger_sp->GetTargetList();
+    const size_t num_targets = target_list.GetNumTargets();
+    for (size_t j = 0; j < num_targets; ++j) {
+      lldb::TargetSP target_sp = target_list.GetTargetAtIndex(j);
+      if (!target_sp)
+        continue;
+      resources.emplace_back(GetTargetResource(i, j));
+    }
+  }
+
+  return resources;
+}
+
+llvm::Expected<protocol::ResourceResult>
+DebuggerResourceProvider::ReadResource(llvm::StringRef uri) const {
+  auto [protocol, path] = uri.split("://");
+
+  if (protocol != "lldb")
+    return createUnsupportedURIError(uri);
+
+  llvm::SmallVector<llvm::StringRef, 4> components;
+  path.split(components, '/');
+
+  if (components.size() < 2)
+    return createUnsupportedURIError(uri);
+
+  if (components[0] != "debugger")
+    return createUnsupportedURIError(uri);
+
+  lldb::user_id_t debugger_id;
+  if (components[1].getAsInteger(0, debugger_id))
+    return createStringError("invalid debugger id '{0}': {1}", components[1],
+                             path);
+
+  if (components.size() > 3) {
+    if (components[2] != "target")
+      return createUnsupportedURIError(uri);
+
+    lldb::user_id_t target_id;
+    if (components[3].getAsInteger(0, target_id))
+      return createStringError("invalid target id '{0}': {1}", components[3],
+                               path);
+
+    return ReadTargetResource(uri, debugger_id, target_id);
+  }
+
+  return ReadDebuggerResource(uri, debugger_id);
+}
+
+llvm::Expected<protocol::ResourceResult>
+DebuggerResourceProvider::ReadDebuggerResource(llvm::StringRef uri,
+                                               lldb::user_id_t debugger_id) {
+  lldb::DebuggerSP debugger_sp = Debugger::GetDebuggerAtIndex(debugger_id);
+  if (!debugger_sp)
+    return createStringError("invalid debugger id: {0}", debugger_id);
+
+  TargetList &target_list = debugger_sp->GetTargetList();
+  const size_t num_targets = target_list.GetNumTargets();
+
+  llvm::json::Value value = llvm::json::Object{{"debugger_id", debugger_id},
+                                               {"num_targets", num_targets}};
+
+  std::string json = llvm::formatv("{0}", value);
+
+  protocol::ResourceContents contents;
+  contents.uri = uri;
+  contents.mimeType = "application/json";
+  contents.text = json;
+
+  protocol::ResourceResult result;
+  result.contents.push_back(contents);
+  return result;
+}
+
+llvm::Expected<protocol::ResourceResult>
+DebuggerResourceProvider::ReadTargetResource(llvm::StringRef uri,
+                                             lldb::user_id_t debugger_id,
+                                             lldb::user_id_t target_id) {
+
+  lldb::DebuggerSP debugger_sp = Debugger::GetDebuggerAtIndex(debugger_id);
+  if (!debugger_sp)
+    return createStringError("invalid debugger id: {0}", debugger_id);
+
+  TargetList &target_list = debugger_sp->GetTargetList();
+  lldb::TargetSP target_sp = target_list.GetTargetAtIndex(target_id);
+  if (!target_sp)
+    return createStringError("invalid target id: {0}", target_id);
+
+  llvm::json::Object object{
+      {"debugger_id", debugger_id},
+      {"target_id", target_id},
+      {"arch", target_sp->GetArchitecture().GetTriple().str()}};
+
+  if (Module *exe_module = target_sp->GetExecutableModulePointer())
+    object.insert({"path", exe_module->GetFileSpec().GetPath()});
+
+  llvm::json::Value value = std::move(object);
+  std::string json = llvm::formatv("{0}", value);
+
+  protocol::ResourceContents contents;
+  contents.uri = uri;
+  contents.mimeType = "application/json";
+  contents.text = json;
+
+  protocol::ResourceResult result;
+  result.contents.push_back(contents);
+  return result;
+}
diff --git a/lldb/source/Plugins/Protocol/MCP/Resource.h 
b/lldb/source/Plugins/Protocol/MCP/Resource.h
new file mode 100644
index 0000000000000..45c39673445da
--- /dev/null
+++ b/lldb/source/Plugins/Protocol/MCP/Resource.h
@@ -0,0 +1,51 @@
+//===----------------------------------------------------------------------===//
+//
+// 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_PLUGINS_PROTOCOL_MCP_RESOURCE_H
+#define LLDB_PLUGINS_PROTOCOL_MCP_RESOURCE_H
+
+#include "Protocol.h"
+#include "lldb/lldb-private.h"
+#include <vector>
+
+namespace lldb_private::mcp {
+
+class ResourceProvider {
+public:
+  ResourceProvider() = default;
+  virtual ~ResourceProvider() = default;
+
+  virtual std::vector<protocol::Resource> GetResources() const = 0;
+  virtual llvm::Expected<protocol::ResourceResult>
+  ReadResource(llvm::StringRef uri) const = 0;
+};
+
+class DebuggerResourceProvider : public ResourceProvider {
+public:
+  using ResourceProvider::ResourceProvider;
+  virtual ~DebuggerResourceProvider() = default;
+
+  virtual std::vector<protocol::Resource> GetResources() const override;
+  virtual llvm::Expected<protocol::ResourceResult>
+  ReadResource(llvm::StringRef uri) const override;
+
+private:
+  static protocol::Resource GetDebuggerResource(lldb::user_id_t target_id);
+  static protocol::Resource GetTargetResource(lldb::user_id_t debugger_id,
+                                              lldb::user_id_t target_id);
+
+  static llvm::Expected<protocol::ResourceResult>
+  ReadDebuggerResource(llvm::StringRef uri, lldb::user_id_t target_id);
+  static llvm::Expected<protocol::ResourceResult>
+  ReadTargetResource(llvm::StringRef uri, lldb::user_id_t debugger_id,
+                     lldb::user_id_t target_id);
+};
+
+} // namespace lldb_private::mcp
+
+#endif
diff --git a/lldb/source/Plugins/Protocol/MCP/Tool.cpp 
b/lldb/source/Plugins/Protocol/MCP/Tool.cpp
index 5c4626cf66b32..a814f830dd5c0 100644
--- a/lldb/source/Plugins/Protocol/MCP/Tool.cpp
+++ b/lldb/source/Plugins/Protocol/MCP/Tool.cpp
@@ -101,50 +101,3 @@ std::optional<llvm::json::Value> CommandTool::GetSchema() 
const {
                             {"required", std::move(required)}};
   return schema;
 }
-
-llvm::Expected<protocol::TextResult>
-DebuggerListTool::Call(const protocol::ToolArguments &args) {
-  if (!std::holds_alternative<std::monostate>(args))
-    return createStringError("DebuggerListTool takes no arguments");
-
-  llvm::json::Path::Root root;
-
-  // Return a nested Markdown list with debuggers and target.
-  // Example output:
-  //
-  // - debugger 0
-  //     - target 0 /path/to/foo
-  //     - target 1
-  // - debugger 1
-  //     - target 0 /path/to/bar
-  //
-  // FIXME: Use Structured Content when we adopt protocol version 2025-06-18.
-  std::string output;
-  llvm::raw_string_ostream os(output);
-
-  const size_t num_debuggers = Debugger::GetNumDebuggers();
-  for (size_t i = 0; i < num_debuggers; ++i) {
-    lldb::DebuggerSP debugger_sp = Debugger::GetDebuggerAtIndex(i);
-    if (!debugger_sp)
-      continue;
-
-    os << "- debugger " << i << '\n';
-
-    TargetList &target_list = debugger_sp->GetTargetList();
-    const size_t num_targets = target_list.GetNumTargets();
-    for (size_t j = 0; j < num_targets; ++j) {
-      lldb::TargetSP target_sp = target_list.GetTargetAtIndex(j);
-      if (!target_sp)
-        continue;
-      os << "    - target " << j;
-      if (target_sp == target_list.GetSelectedTarget())
-        os << " (selected)";
-      // Append the module path if we have one.
-      if (Module *exe_module = target_sp->GetExecutableModulePointer())
-        os << " " << exe_module->GetFileSpec().GetPath();
-      os << '\n';
-    }
-  }
-
-  return createTextResult(output);
-}
diff --git a/lldb/source/Plugins/Protocol/MCP/Tool.h 
b/lldb/source/Plugins/Protocol/MCP/Tool.h
index 74ab04b472522..d0f639adad24e 100644
--- a/lldb/source/Plugins/Protocol/MCP/Tool.h
+++ b/lldb/source/Plugins/Protocol/MCP/Tool.h
@@ -48,15 +48,6 @@ class CommandTool : public mcp::Tool {
   virtual std::optional<llvm::json::Value> GetSchema() const override;
 };
 
-class DebuggerListTool : public mcp::Tool {
-public:
-  using mcp::Tool::Tool;
-  ~DebuggerListTool() = default;
-
-  virtual llvm::Expected<protocol::TextResult>
-  Call(const protocol::ToolArguments &args) override;
-};
-
 } // namespace lldb_private::mcp
 
 #endif
diff --git a/lldb/unittests/Protocol/ProtocolMCPServerTest.cpp 
b/lldb/unittests/Protocol/ProtocolMCPServerTest.cpp
index 8e61379b5c731..2832716704a07 100644
--- a/lldb/unittests/Protocol/ProtocolMCPServerTest.cpp
+++ b/lldb/unittests/Protocol/ProtocolMCPServerTest.cpp
@@ -7,6 +7,7 @@
 
//===----------------------------------------------------------------------===//
 
 #include "Plugins/Platform/MacOSX/PlatformRemoteMacOSX.h"
+#include "Plugins/Protocol/MCP/MCPError.h"
 #include "Plugins/Protocol/MCP/ProtocolServerMCP.h"
 #include "TestingSupport/Host/SocketTestUtilities.h"
 #include "TestingSupport/SubsystemRAII.h"
@@ -28,6 +29,7 @@ class TestProtocolServerMCP : public 
lldb_private::mcp::ProtocolServerMCP {
 public:
   using ProtocolServerMCP::AddNotificationHandler;
   using ProtocolServerMCP::AddRequestHandler;
+  using ProtocolServerMCP::AddResourceProvider;
   using ProtocolServerMCP::AddTool;
   using ProtocolServerMCP::GetSocket;
   using ProtocolServerMCP::ProtocolServerMCP;
@@ -61,6 +63,38 @@ class TestTool : public mcp::Tool {
   }
 };
 
+class TestResourceProvider : public mcp::ResourceProvider {
+  using mcp::ResourceProvider::ResourceProvider;
+
+  virtual std::vector<Resource> GetResources() const override {
+    std::vector<Resource> resources;
+
+    Resource resource;
+    resource.uri = "lldb://foo/bar";
+    resource.name = "name";
+    resource.description = "description";
+    resource.mimeType = "application/json";
+
+    resources.push_back(resource);
+    return resources;
+  }
+
+  virtual llvm::Expected<ResourceResult>
+  ReadResource(llvm::StringRef uri) const override {
+    if (uri != "lldb://foo/bar")
+      return llvm::make_error<mcp::UnsupportedURI>(uri.str());
+
+    ResourceContents contents;
+    contents.uri = "lldb://foo/bar";
+    contents.mimeType = "application/json";
+    contents.text = "foobar";
+
+    ResourceResult result;
+    result.contents.push_back(contents);
+    return result;
+  }
+};
+
 /// Test tool that returns an error.
 class ErrorTool : public mcp::Tool {
 public:
@@ -118,6 +152,7 @@ class ProtocolServerMCPTest : public ::testing::Test {
     connection.name = llvm::formatv("{0}:0", k_localhost).str();
     m_server_up = std::make_unique<TestProtocolServerMCP>();
     m_server_up->AddTool(std::make_unique<TestTool>("test", "test tool"));
+    m_server_up->AddResourceProvider(std::make_unique<TestResourceProvider>());
     ASSERT_THAT_ERROR(m_server_up->Start(connection), llvm::Succeeded());
 
     // Connect to the server over a TCP socket.
@@ -148,7 +183,7 @@ TEST_F(ProtocolServerMCPTest, Intialization) {
   llvm::StringLiteral request =
       
R"json({"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"lldb-unit","version":"0.1.0"}},"jsonrpc":"2.0","id":0})json";
   llvm::StringLiteral response =
-      
R"json({"jsonrpc":"2.0","id":0,"result":{"capabilities":{"tools":{"listChanged":true}},"protocolVersion":"2024-11-05","serverInfo":{"name":"lldb-mcp","version":"0.1.0"}}})json";
+      R"json( 
{"id":0,"jsonrpc":"2.0","result":{"capabilities":{"resources":{"listChanged":false,"subscribe":false},"tools":{"listChanged":true}},"protocolVersion":"2024-11-05","serverInfo":{"name":"lldb-mcp","version":"0.1.0"}}})json";
 
   ASSERT_THAT_ERROR(Write(request), llvm::Succeeded());
 
@@ -168,7 +203,7 @@ TEST_F(ProtocolServerMCPTest, ToolsList) {
   llvm::StringLiteral request =
       R"json({"method":"tools/list","params":{},"jsonrpc":"2.0","id":1})json";
   llvm::StringLiteral response =
-      R"json( {"id":1,"jsonrpc":"2.0","result":{"tools":[{"description":"test 
tool","inputSchema":{"type":"object"},"name":"test"},{"description":"List 
debugger instances with their 
debugger_id.","inputSchema":{"type":"object"},"name":"lldb_debugger_list"},{"description":"Run
 an lldb 
command.","inputSchema":{"properties":{"arguments":{"type":"string"},"debugger_id":{"type":"number"}},"required":["debugger_id"],"type":"object"},"name":"lldb_command"}]}})json";
+      R"json({"id":1,"jsonrpc":"2.0","result":{"tools":[{"description":"test 
tool","inputSchema":{"type":"object"},"name":"test"},{"description":"Run an 
lldb 
command.","inputSchema":{"properties":{"arguments":{"type":"string"},"debugger_id":{"type":"number"}},"required":["debugger_id"],"type":"object"},"name":"lldb_command"}]}})json";
 
   ASSERT_THAT_ERROR(Write(request), llvm::Succeeded());
 
@@ -188,7 +223,7 @@ TEST_F(ProtocolServerMCPTest, ResourcesList) {
   llvm::StringLiteral request =
       
R"json({"method":"resources/list","params":{},"jsonrpc":"2.0","id":2})json";
   llvm::StringLiteral response =
-      R"json({"error":{"code":1,"message":"no handler for request: 
resources/list"},"id":2,"jsonrpc":"2.0"})json";
+      
R"json({"id":2,"jsonrpc":"2.0","result":{"resources":[{"description":"description","mimeType":"application/json","name":"name","uri":"lldb://foo/bar"}]}})json";
 
   ASSERT_THAT_ERROR(Write(request), llvm::Succeeded());
 
diff --git a/lldb/unittests/Protocol/ProtocolMCPTest.cpp 
b/lldb/unittests/Protocol/ProtocolMCPTest.cpp
index 14cc240dd3628..ddc5a411a5c31 100644
--- a/lldb/unittests/Protocol/ProtocolMCPTest.cpp
+++ b/lldb/unittests/Protocol/ProtocolMCPTest.cpp
@@ -230,3 +230,101 @@ TEST(ProtocolMCPTest, ResponseWithError) {
   EXPECT_EQ(response.error->code, deserialized_response->error->code);
   EXPECT_EQ(response.error->message, deserialized_response->error->message);
 }
+
+TEST(ProtocolMCPTest, Resource) {
+  Resource resource;
+  resource.uri = "resource://example/test";
+  resource.name = "Test Resource";
+  resource.description = "A test resource for unit testing";
+  resource.mimeType = "text/plain";
+
+  llvm::Expected<Resource> deserialized_resource = roundtripJSON(resource);
+  ASSERT_THAT_EXPECTED(deserialized_resource, llvm::Succeeded());
+
+  EXPECT_EQ(resource.uri, deserialized_resource->uri);
+  EXPECT_EQ(resource.name, deserialized_resource->name);
+  EXPECT_EQ(resource.description, deserialized_resource->description);
+  EXPECT_EQ(resource.mimeType, deserialized_resource->mimeType);
+}
+
+TEST(ProtocolMCPTest, ResourceWithoutOptionals) {
+  Resource resource;
+  resource.uri = "resource://example/minimal";
+  resource.name = "Minimal Resource";
+
+  llvm::Expected<Resource> deserialized_resource = roundtripJSON(resource);
+  ASSERT_THAT_EXPECTED(deserialized_resource, llvm::Succeeded());
+
+  EXPECT_EQ(resource.uri, deserialized_resource->uri);
+  EXPECT_EQ(resource.name, deserialized_resource->name);
+  EXPECT_FALSE(deserialized_resource->description.has_value());
+  EXPECT_FALSE(deserialized_resource->mimeType.has_value());
+}
+
+TEST(ProtocolMCPTest, ResourceContents) {
+  ResourceContents contents;
+  contents.uri = "resource://example/content";
+  contents.text = "This is the content of the resource";
+  contents.mimeType = "text/plain";
+
+  llvm::Expected<ResourceContents> deserialized_contents =
+      roundtripJSON(contents);
+  ASSERT_THAT_EXPECTED(deserialized_contents, llvm::Succeeded());
+
+  EXPECT_EQ(contents.uri, deserialized_contents->uri);
+  EXPECT_EQ(contents.text, deserialized_contents->text);
+  EXPECT_EQ(contents.mimeType, deserialized_contents->mimeType);
+}
+
+TEST(ProtocolMCPTest, ResourceContentsWithoutMimeType) {
+  ResourceContents contents;
+  contents.uri = "resource://example/content-no-mime";
+  contents.text = "Content without mime type specified";
+
+  llvm::Expected<ResourceContents> deserialized_contents =
+      roundtripJSON(contents);
+  ASSERT_THAT_EXPECTED(deserialized_contents, llvm::Succeeded());
+
+  EXPECT_EQ(contents.uri, deserialized_contents->uri);
+  EXPECT_EQ(contents.text, deserialized_contents->text);
+  EXPECT_FALSE(deserialized_contents->mimeType.has_value());
+}
+
+TEST(ProtocolMCPTest, ResourceResult) {
+  ResourceContents contents1;
+  contents1.uri = "resource://example/content1";
+  contents1.text = "First resource content";
+  contents1.mimeType = "text/plain";
+
+  ResourceContents contents2;
+  contents2.uri = "resource://example/content2";
+  contents2.text = "Second resource content";
+  contents2.mimeType = "application/json";
+
+  ResourceResult result;
+  result.contents = {contents1, contents2};
+
+  llvm::Expected<ResourceResult> deserialized_result = roundtripJSON(result);
+  ASSERT_THAT_EXPECTED(deserialized_result, llvm::Succeeded());
+
+  ASSERT_EQ(result.contents.size(), deserialized_result->contents.size());
+
+  EXPECT_EQ(result.contents[0].uri, deserialized_result->contents[0].uri);
+  EXPECT_EQ(result.contents[0].text, deserialized_result->contents[0].text);
+  EXPECT_EQ(result.contents[0].mimeType,
+            deserialized_result->contents[0].mimeType);
+
+  EXPECT_EQ(result.contents[1].uri, deserialized_result->contents[1].uri);
+  EXPECT_EQ(result.contents[1].text, deserialized_result->contents[1].text);
+  EXPECT_EQ(result.contents[1].mimeType,
+            deserialized_result->contents[1].mimeType);
+}
+
+TEST(ProtocolMCPTest, ResourceResultEmpty) {
+  ResourceResult result;
+
+  llvm::Expected<ResourceResult> deserialized_result = roundtripJSON(result);
+  ASSERT_THAT_EXPECTED(deserialized_result, llvm::Succeeded());
+
+  EXPECT_TRUE(deserialized_result->contents.empty());
+}

>From c9f3a687ec2ca6b7e2a670cfdee23d9d2560e714 Mon Sep 17 00:00:00 2001
From: Jonas Devlieghere <jo...@devlieghere.com>
Date: Fri, 11 Jul 2025 10:46:36 -0700
Subject: [PATCH 2/3] Address review feedback

---
 lldb/include/lldb/Core/Debugger.h             |   2 +-
 lldb/include/lldb/Target/Target.h             |   2 +-
 lldb/source/Plugins/Protocol/MCP/MCPError.h   |   5 +-
 .../Protocol/MCP/ProtocolServerMCP.cpp        |   9 +-
 lldb/source/Plugins/Protocol/MCP/Resource.cpp | 129 ++++++++++++------
 lldb/source/Plugins/Protocol/MCP/Resource.h   |  10 +-
 lldb/source/Plugins/Protocol/MCP/Tool.cpp     |   2 +-
 .../Protocol/ProtocolMCPServerTest.cpp        |   2 +-
 8 files changed, 106 insertions(+), 55 deletions(-)

diff --git a/lldb/include/lldb/Core/Debugger.h 
b/lldb/include/lldb/Core/Debugger.h
index 504f936fe317a..250ad64b76d9a 100644
--- a/lldb/include/lldb/Core/Debugger.h
+++ b/lldb/include/lldb/Core/Debugger.h
@@ -367,7 +367,7 @@ class Debugger : public 
std::enable_shared_from_this<Debugger>,
 
   bool GetNotifyVoid() const;
 
-  const std::string &GetInstanceName() { return m_instance_name; }
+  const std::string &GetInstanceName() const { return m_instance_name; }
 
   bool GetShowInlineDiagnostics() const;
 
diff --git a/lldb/include/lldb/Target/Target.h 
b/lldb/include/lldb/Target/Target.h
index 0d4e11b65339e..a1d881375b08b 100644
--- a/lldb/include/lldb/Target/Target.h
+++ b/lldb/include/lldb/Target/Target.h
@@ -1093,7 +1093,7 @@ class Target : public 
std::enable_shared_from_this<Target>,
 
   Architecture *GetArchitecturePlugin() const { return m_arch.GetPlugin(); }
 
-  Debugger &GetDebugger() { return m_debugger; }
+  Debugger &GetDebugger() const { return m_debugger; }
 
   size_t ReadMemoryFromFileCache(const Address &addr, void *dst, size_t 
dst_len,
                                  Status &error);
diff --git a/lldb/source/Plugins/Protocol/MCP/MCPError.h 
b/lldb/source/Plugins/Protocol/MCP/MCPError.h
index 05a047ec881a9..f4db13d6deade 100644
--- a/lldb/source/Plugins/Protocol/MCP/MCPError.h
+++ b/lldb/source/Plugins/Protocol/MCP/MCPError.h
@@ -17,7 +17,7 @@ class MCPError : public llvm::ErrorInfo<MCPError> {
 public:
   static char ID;
 
-  MCPError(std::string message, int64_t error_code);
+  MCPError(std::string message, int64_t error_code = kInternalError);
 
   void log(llvm::raw_ostream &OS) const override;
   std::error_code convertToErrorCode() const override;
@@ -26,6 +26,9 @@ class MCPError : public llvm::ErrorInfo<MCPError> {
 
   protocol::Error toProtcolError() 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/source/Plugins/Protocol/MCP/ProtocolServerMCP.cpp 
b/lldb/source/Plugins/Protocol/MCP/ProtocolServerMCP.cpp
index 6099d429672bb..0e5a3631e6387 100644
--- a/lldb/source/Plugins/Protocol/MCP/ProtocolServerMCP.cpp
+++ b/lldb/source/Plugins/Protocol/MCP/ProtocolServerMCP.cpp
@@ -84,7 +84,7 @@ ProtocolServerMCP::Handle(protocol::Request request) {
   }
 
   return make_error<MCPError>(
-      llvm::formatv("no handler for request: {0}", request.method).str(), 1);
+      llvm::formatv("no handler for request: {0}", request.method).str());
 }
 
 void ProtocolServerMCP::Handle(protocol::Notification notification) {
@@ -225,7 +225,7 @@ ProtocolServerMCP::HandleData(llvm::StringRef data) {
           response.takeError(),
           [&](const MCPError &err) { protocol_error = err.toProtcolError(); },
           [&](const llvm::ErrorInfoBase &err) {
-            protocol_error.error.code = -1;
+            protocol_error.error.code = MCPError::kInternalError;
             protocol_error.error.message = err.message();
           });
       protocol_error.id = request->id;
@@ -253,6 +253,8 @@ ProtocolServerMCP::HandleData(llvm::StringRef data) {
 protocol::Capabilities ProtocolServerMCP::GetCapabilities() {
   protocol::Capabilities capabilities;
   capabilities.tools.listChanged = true;
+  // FIXME: Support sending notifications when a debugger/target are
+  // added/removed.
   capabilities.resources.listChanged = false;
   return capabilities;
 }
@@ -403,5 +405,6 @@ ProtocolServerMCP::ResourcesReadHandler(const 
protocol::Request &request) {
   }
 
   return make_error<MCPError>(
-      llvm::formatv("no resource handler for uri: {0}", uri_str).str(), 1);
+      llvm::formatv("no resource handler for uri: {0}", uri_str).str(),
+      MCPError::kResourceNotFound);
 }
diff --git a/lldb/source/Plugins/Protocol/MCP/Resource.cpp 
b/lldb/source/Plugins/Protocol/MCP/Resource.cpp
index 8cb4e3916b1b1..2a5a2d48ce48b 100644
--- a/lldb/source/Plugins/Protocol/MCP/Resource.cpp
+++ b/lldb/source/Plugins/Protocol/MCP/Resource.cpp
@@ -8,9 +8,48 @@
 #include "MCPError.h"
 #include "lldb/Core/Debugger.h"
 #include "lldb/Core/Module.h"
+#include "lldb/Target/Platform.h"
 
 using namespace lldb_private::mcp;
 
+namespace {
+struct DebuggerResource {
+  uint64_t debugger_id;
+  std::string name;
+  uint64_t num_targets;
+};
+
+llvm::json::Value toJSON(const DebuggerResource &DR) {
+  llvm::json::Object Result{{"debugger_id", DR.debugger_id},
+                            {"num_targets", DR.num_targets}};
+  if (!DR.name.empty())
+    Result.insert({"name", DR.name});
+  return Result;
+}
+
+struct TargetResource {
+  size_t debugger_id;
+  size_t target_idx;
+  std::string arch;
+  std::string path;
+  std::string platform;
+};
+
+llvm::json::Value toJSON(const TargetResource &TR) {
+  llvm::json::Object Result{{"debugger_id", TR.debugger_id},
+                            {"target_idx", TR.target_idx}};
+  if (!TR.arch.empty())
+    Result.insert({"arch", TR.arch});
+  if (!TR.path.empty())
+    Result.insert({"path", TR.path});
+  if (!TR.platform.empty())
+    Result.insert({"platform", TR.platform});
+  return Result;
+}
+} // namespace
+
+static constexpr llvm::StringLiteral kMimeTypeJSON = "application/json";
+
 template <typename... Args>
 static llvm::Error createStringError(const char *format, Args &&...args) {
   return llvm::createStringError(
@@ -22,27 +61,36 @@ static llvm::Error 
createUnsupportedURIError(llvm::StringRef uri) {
 }
 
 protocol::Resource
-DebuggerResourceProvider::GetDebuggerResource(lldb::user_id_t debugger_id) {
+DebuggerResourceProvider::GetDebuggerResource(Debugger &debugger) {
+  const lldb::user_id_t debugger_id = debugger.GetID();
+
   protocol::Resource resource;
   resource.uri = llvm::formatv("lldb://debugger/{0}", debugger_id);
-  resource.name = llvm::formatv("debugger {0}", debugger_id);
+  resource.name = debugger.GetInstanceName();
   resource.description =
-      llvm::formatv("Information about debugger instance {0}", debugger_id);
-  resource.mimeType = "application/json";
+      llvm::formatv("Information about debugger instance {0}: {1}", 
debugger_id,
+                    debugger.GetInstanceName());
+  resource.mimeType = kMimeTypeJSON;
   return resource;
 }
 
 protocol::Resource
-DebuggerResourceProvider::GetTargetResource(lldb::user_id_t debugger_id,
-                                            lldb::user_id_t target_id) {
+DebuggerResourceProvider::GetTargetResource(size_t target_idx, Target &target) 
{
+  const size_t debugger_id = target.GetDebugger().GetID();
+
+  std::string target_name = llvm::formatv("target {0}", target_idx);
+
+  if (Module *exe_module = target.GetExecutableModulePointer())
+    target_name = exe_module->GetFileSpec().GetFilename().GetString();
+
   protocol::Resource resource;
   resource.uri =
-      llvm::formatv("lldb://debugger/{0}/target/{1}", debugger_id, target_id);
-  resource.name = llvm::formatv("target {0}", target_id);
+      llvm::formatv("lldb://debugger/{0}/target/{1}", debugger_id, target_idx);
+  resource.name = target_name;
   resource.description =
       llvm::formatv("Information about target {0} in debugger instance {1}",
-                    target_id, debugger_id);
-  resource.mimeType = "application/json";
+                    target_idx, debugger_id);
+  resource.mimeType = kMimeTypeJSON;
   return resource;
 }
 
@@ -54,7 +102,7 @@ std::vector<protocol::Resource> 
DebuggerResourceProvider::GetResources() const {
     lldb::DebuggerSP debugger_sp = Debugger::GetDebuggerAtIndex(i);
     if (!debugger_sp)
       continue;
-    resources.emplace_back(GetDebuggerResource(i));
+    resources.emplace_back(GetDebuggerResource(*debugger_sp));
 
     TargetList &target_list = debugger_sp->GetTargetList();
     const size_t num_targets = target_list.GetNumTargets();
@@ -62,7 +110,7 @@ std::vector<protocol::Resource> 
DebuggerResourceProvider::GetResources() const {
       lldb::TargetSP target_sp = target_list.GetTargetAtIndex(j);
       if (!target_sp)
         continue;
-      resources.emplace_back(GetTargetResource(i, j));
+      resources.emplace_back(GetTargetResource(j, *target_sp));
     }
   }
 
@@ -71,6 +119,7 @@ std::vector<protocol::Resource> 
DebuggerResourceProvider::GetResources() const {
 
 llvm::Expected<protocol::ResourceResult>
 DebuggerResourceProvider::ReadResource(llvm::StringRef uri) const {
+
   auto [protocol, path] = uri.split("://");
 
   if (protocol != "lldb")
@@ -85,8 +134,8 @@ DebuggerResourceProvider::ReadResource(llvm::StringRef uri) 
const {
   if (components[0] != "debugger")
     return createUnsupportedURIError(uri);
 
-  lldb::user_id_t debugger_id;
-  if (components[1].getAsInteger(0, debugger_id))
+  size_t debugger_idx;
+  if (components[1].getAsInteger(0, debugger_idx))
     return createStringError("invalid debugger id '{0}': {1}", components[1],
                              path);
 
@@ -94,36 +143,33 @@ DebuggerResourceProvider::ReadResource(llvm::StringRef 
uri) const {
     if (components[2] != "target")
       return createUnsupportedURIError(uri);
 
-    lldb::user_id_t target_id;
-    if (components[3].getAsInteger(0, target_id))
+    size_t target_idx;
+    if (components[3].getAsInteger(0, target_idx))
       return createStringError("invalid target id '{0}': {1}", components[3],
                                path);
 
-    return ReadTargetResource(uri, debugger_id, target_id);
+    return ReadTargetResource(uri, debugger_idx, target_idx);
   }
 
-  return ReadDebuggerResource(uri, debugger_id);
+  return ReadDebuggerResource(uri, debugger_idx);
 }
 
 llvm::Expected<protocol::ResourceResult>
 DebuggerResourceProvider::ReadDebuggerResource(llvm::StringRef uri,
                                                lldb::user_id_t debugger_id) {
-  lldb::DebuggerSP debugger_sp = Debugger::GetDebuggerAtIndex(debugger_id);
+  lldb::DebuggerSP debugger_sp = Debugger::FindDebuggerWithID(debugger_id);
   if (!debugger_sp)
     return createStringError("invalid debugger id: {0}", debugger_id);
 
-  TargetList &target_list = debugger_sp->GetTargetList();
-  const size_t num_targets = target_list.GetNumTargets();
-
-  llvm::json::Value value = llvm::json::Object{{"debugger_id", debugger_id},
-                                               {"num_targets", num_targets}};
-
-  std::string json = llvm::formatv("{0}", value);
+  DebuggerResource debugger_resource;
+  debugger_resource.debugger_id = debugger_id;
+  debugger_resource.name = debugger_sp->GetInstanceName();
+  debugger_resource.num_targets = debugger_sp->GetTargetList().GetNumTargets();
 
   protocol::ResourceContents contents;
   contents.uri = uri;
-  contents.mimeType = "application/json";
-  contents.text = json;
+  contents.mimeType = kMimeTypeJSON;
+  contents.text = llvm::formatv("{0}", toJSON(debugger_resource));
 
   protocol::ResourceResult result;
   result.contents.push_back(contents);
@@ -133,32 +179,31 @@ 
DebuggerResourceProvider::ReadDebuggerResource(llvm::StringRef uri,
 llvm::Expected<protocol::ResourceResult>
 DebuggerResourceProvider::ReadTargetResource(llvm::StringRef uri,
                                              lldb::user_id_t debugger_id,
-                                             lldb::user_id_t target_id) {
+                                             size_t target_idx) {
 
-  lldb::DebuggerSP debugger_sp = Debugger::GetDebuggerAtIndex(debugger_id);
+  lldb::DebuggerSP debugger_sp = Debugger::FindDebuggerWithID(debugger_id);
   if (!debugger_sp)
     return createStringError("invalid debugger id: {0}", debugger_id);
 
   TargetList &target_list = debugger_sp->GetTargetList();
-  lldb::TargetSP target_sp = target_list.GetTargetAtIndex(target_id);
+  lldb::TargetSP target_sp = target_list.GetTargetAtIndex(target_idx);
   if (!target_sp)
-    return createStringError("invalid target id: {0}", target_id);
+    return createStringError("invalid target idx: {0}", target_idx);
 
-  llvm::json::Object object{
-      {"debugger_id", debugger_id},
-      {"target_id", target_id},
-      {"arch", target_sp->GetArchitecture().GetTriple().str()}};
+  TargetResource target_resource;
+  target_resource.debugger_id = debugger_id;
+  target_resource.target_idx = target_idx;
+  target_resource.arch = target_sp->GetArchitecture().GetTriple().str();
 
   if (Module *exe_module = target_sp->GetExecutableModulePointer())
-    object.insert({"path", exe_module->GetFileSpec().GetPath()});
-
-  llvm::json::Value value = std::move(object);
-  std::string json = llvm::formatv("{0}", value);
+    target_resource.path = exe_module->GetFileSpec().GetPath();
+  if (lldb::PlatformSP platform_sp = target_sp->GetPlatform())
+    target_resource.platform = platform_sp->GetName();
 
   protocol::ResourceContents contents;
   contents.uri = uri;
-  contents.mimeType = "application/json";
-  contents.text = json;
+  contents.mimeType = kMimeTypeJSON;
+  contents.text = llvm::formatv("{0}", toJSON(target_resource));
 
   protocol::ResourceResult result;
   result.contents.push_back(contents);
diff --git a/lldb/source/Plugins/Protocol/MCP/Resource.h 
b/lldb/source/Plugins/Protocol/MCP/Resource.h
index 45c39673445da..5ac38e7e878ff 100644
--- a/lldb/source/Plugins/Protocol/MCP/Resource.h
+++ b/lldb/source/Plugins/Protocol/MCP/Resource.h
@@ -35,15 +35,15 @@ class DebuggerResourceProvider : public ResourceProvider {
   ReadResource(llvm::StringRef uri) const override;
 
 private:
-  static protocol::Resource GetDebuggerResource(lldb::user_id_t target_id);
-  static protocol::Resource GetTargetResource(lldb::user_id_t debugger_id,
-                                              lldb::user_id_t target_id);
+  static protocol::Resource GetDebuggerResource(Debugger &debugger);
+  static protocol::Resource GetTargetResource(size_t target_idx,
+                                              Target &target);
 
   static llvm::Expected<protocol::ResourceResult>
-  ReadDebuggerResource(llvm::StringRef uri, lldb::user_id_t target_id);
+  ReadDebuggerResource(llvm::StringRef uri, lldb::user_id_t debugger_id);
   static llvm::Expected<protocol::ResourceResult>
   ReadTargetResource(llvm::StringRef uri, lldb::user_id_t debugger_id,
-                     lldb::user_id_t target_id);
+                     size_t target_idx);
 };
 
 } // namespace lldb_private::mcp
diff --git a/lldb/source/Plugins/Protocol/MCP/Tool.cpp 
b/lldb/source/Plugins/Protocol/MCP/Tool.cpp
index a814f830dd5c0..eecd56141d2ed 100644
--- a/lldb/source/Plugins/Protocol/MCP/Tool.cpp
+++ b/lldb/source/Plugins/Protocol/MCP/Tool.cpp
@@ -65,7 +65,7 @@ CommandTool::Call(const protocol::ToolArguments &args) {
     return root.getError();
 
   lldb::DebuggerSP debugger_sp =
-      Debugger::GetDebuggerAtIndex(arguments.debugger_id);
+      Debugger::FindDebuggerWithID(arguments.debugger_id);
   if (!debugger_sp)
     return createStringError(
         llvm::formatv("no debugger with id {0}", arguments.debugger_id));
diff --git a/lldb/unittests/Protocol/ProtocolMCPServerTest.cpp 
b/lldb/unittests/Protocol/ProtocolMCPServerTest.cpp
index 2832716704a07..51eb6275e811a 100644
--- a/lldb/unittests/Protocol/ProtocolMCPServerTest.cpp
+++ b/lldb/unittests/Protocol/ProtocolMCPServerTest.cpp
@@ -265,7 +265,7 @@ TEST_F(ProtocolServerMCPTest, ToolsCallError) {
   llvm::StringLiteral request =
       
R"json({"method":"tools/call","params":{"name":"error","arguments":{"arguments":"foo","debugger_id":0}},"jsonrpc":"2.0","id":11})json";
   llvm::StringLiteral response =
-      
R"json({"error":{"code":-1,"message":"error"},"id":11,"jsonrpc":"2.0"})json";
+      
R"json({"error":{"code":-32603,"message":"error"},"id":11,"jsonrpc":"2.0"})json";
 
   ASSERT_THAT_ERROR(Write(request), llvm::Succeeded());
 

>From 2d1cf5e3457ac2ef39b13225401c3c99e0ace213 Mon Sep 17 00:00:00 2001
From: Jonas Devlieghere <jo...@devlieghere.com>
Date: Fri, 11 Jul 2025 10:51:40 -0700
Subject: [PATCH 3/3] Add 'selected' and 'dummy' for target

---
 lldb/source/Plugins/Protocol/MCP/Resource.cpp | 16 +++++++++++-----
 1 file changed, 11 insertions(+), 5 deletions(-)

diff --git a/lldb/source/Plugins/Protocol/MCP/Resource.cpp 
b/lldb/source/Plugins/Protocol/MCP/Resource.cpp
index 2a5a2d48ce48b..d75d5b6dd6a41 100644
--- a/lldb/source/Plugins/Protocol/MCP/Resource.cpp
+++ b/lldb/source/Plugins/Protocol/MCP/Resource.cpp
@@ -14,9 +14,9 @@ using namespace lldb_private::mcp;
 
 namespace {
 struct DebuggerResource {
-  uint64_t debugger_id;
+  uint64_t debugger_id = 0;
   std::string name;
-  uint64_t num_targets;
+  uint64_t num_targets = 0;
 };
 
 llvm::json::Value toJSON(const DebuggerResource &DR) {
@@ -28,8 +28,10 @@ llvm::json::Value toJSON(const DebuggerResource &DR) {
 }
 
 struct TargetResource {
-  size_t debugger_id;
-  size_t target_idx;
+  size_t debugger_id = 0;
+  size_t target_idx = 0;
+  bool selected = false;
+  bool dummy = false;
   std::string arch;
   std::string path;
   std::string platform;
@@ -37,7 +39,9 @@ struct TargetResource {
 
 llvm::json::Value toJSON(const TargetResource &TR) {
   llvm::json::Object Result{{"debugger_id", TR.debugger_id},
-                            {"target_idx", TR.target_idx}};
+                            {"target_idx", TR.target_idx},
+                            {"selected", TR.selected},
+                            {"dummy", TR.dummy}};
   if (!TR.arch.empty())
     Result.insert({"arch", TR.arch});
   if (!TR.path.empty())
@@ -194,6 +198,8 @@ 
DebuggerResourceProvider::ReadTargetResource(llvm::StringRef uri,
   target_resource.debugger_id = debugger_id;
   target_resource.target_idx = target_idx;
   target_resource.arch = target_sp->GetArchitecture().GetTriple().str();
+  target_resource.dummy = target_sp->IsDummyTarget();
+  target_resource.selected = target_sp == debugger_sp->GetSelectedTarget();
 
   if (Module *exe_module = target_sp->GetExecutableModulePointer())
     target_resource.path = exe_module->GetFileSpec().GetPath();

_______________________________________________
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to