Skip to content

Conversation

ashgti
Copy link
Contributor

@ashgti ashgti commented Aug 26, 2025

This adds or renames existing types to match the names of the types on https://modelcontextprotocol.io/specification/2025-06-18/schema for the existing calls.

The new types are used in the unit tests and server implementation to remove the need for crafting various llvm::json::Object values by hand.

This adds or renames existing types to match the names of the types on
https://modelcontextprotocol.io/specification/2025-06-18/schema for the existing calls.

The new types are used in the unit tests and server implementation to remove the need for crafting various `llvm::json::Object` values by hand.
@ashgti ashgti requested a review from JDevlieghere as a code owner August 26, 2025 17:59
@llvmbot llvmbot added the lldb label Aug 26, 2025
@llvmbot
Copy link
Member

llvmbot commented Aug 26, 2025

@llvm/pr-subscribers-lldb

Author: John Harrison (ashgti)

Changes

This adds or renames existing types to match the names of the types on https://modelcontextprotocol.io/specification/2025-06-18/schema for the existing calls.

The new types are used in the unit tests and server implementation to remove the need for crafting various llvm::json::Object values by hand.


Patch is 52.89 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/155460.diff

12 Files Affected:

  • (modified) lldb/include/lldb/Protocol/MCP/Protocol.h (+227-61)
  • (modified) lldb/include/lldb/Protocol/MCP/Resource.h (+1-1)
  • (modified) lldb/include/lldb/Protocol/MCP/Server.h (+1-1)
  • (modified) lldb/include/lldb/Protocol/MCP/Tool.h (+2-1)
  • (modified) lldb/source/Plugins/Protocol/MCP/Resource.cpp (+7-8)
  • (modified) lldb/source/Plugins/Protocol/MCP/Resource.h (+9-6)
  • (modified) lldb/source/Plugins/Protocol/MCP/Tool.cpp (+6-6)
  • (modified) lldb/source/Plugins/Protocol/MCP/Tool.h (+4-4)
  • (modified) lldb/source/Protocol/MCP/Protocol.cpp (+172-40)
  • (modified) lldb/source/Protocol/MCP/Server.cpp (+34-39)
  • (modified) lldb/unittests/Protocol/ProtocolMCPServerTest.cpp (+60-52)
  • (modified) lldb/unittests/Protocol/ProtocolMCPTest.cpp (+25-38)
diff --git a/lldb/include/lldb/Protocol/MCP/Protocol.h b/lldb/include/lldb/Protocol/MCP/Protocol.h
index 49f9490221755..cdbdd67263e69 100644
--- a/lldb/include/lldb/Protocol/MCP/Protocol.h
+++ b/lldb/include/lldb/Protocol/MCP/Protocol.h
@@ -18,6 +18,7 @@
 #include <optional>
 #include <string>
 #include <variant>
+#include <vector>
 
 namespace lldb_protocol::mcp {
 
@@ -30,6 +31,9 @@ static llvm::StringLiteral kProtocolVersion = "2024-11-05";
 using Id = std::variant<int64_t, std::string>;
 
 /// A request that expects a response.
+///
+/// See
+/// https://modelcontextprotocol.io/specification/2025-06-18/schema#jsonrpcrequest
 struct Request {
   /// The request id.
   Id id = 0;
@@ -38,11 +42,27 @@ struct Request {
   /// The method's params.
   std::optional<llvm::json::Value> params;
 };
-
 llvm::json::Value toJSON(const Request &);
 bool fromJSON(const llvm::json::Value &, Request &, llvm::json::Path);
 bool operator==(const Request &, const Request &);
+void PrintTo(const Request &req, std::ostream *os);
+
+enum ErrorCode : signed {
+  /// Invalid JSON was received by the server. An error occurred on the server
+  /// while parsing the JSON text.
+  eErrorCodeParseError = -32700,
+  /// The JSON sent is not a valid Request object.
+  eErrorCodeInvalidRequest = -32600,
+  /// The method does not exist / is not available.
+  eErrorCodeMethodNotFound = -32601,
+  /// Invalid method parameter(s).
+  eErrorCodeInvalidParams = -32602,
+  /// Internal JSON-RPC error.
+  eErrorCodeInternalError = -32603,
+};
 
+/// See
+/// https://modelcontextprotocol.io/specification/2025-06-18/schema#jsonrpcerror
 struct Error {
   /// The error type that occurred.
   int64_t code = 0;
@@ -52,14 +72,16 @@ struct Error {
   /// Additional information about the error. The value of this member is
   /// defined by the sender (e.g. detailed error information, nested errors
   /// etc.).
-  std::optional<llvm::json::Value> data;
+  std::optional<llvm::json::Value> data = std::nullopt;
 };
-
 llvm::json::Value toJSON(const Error &);
 bool fromJSON(const llvm::json::Value &, Error &, llvm::json::Path);
 bool operator==(const Error &, const Error &);
 
 /// A response to a request, either an error or a result.
+///
+/// See
+/// https://modelcontextprotocol.io/specification/2025-06-18/schema#jsonrpcrequest
 struct Response {
   /// The request id.
   Id id = 0;
@@ -67,22 +89,24 @@ struct Response {
   /// response.
   std::variant<Error, llvm::json::Value> result;
 };
-
 llvm::json::Value toJSON(const Response &);
 bool fromJSON(const llvm::json::Value &, Response &, llvm::json::Path);
 bool operator==(const Response &, const Response &);
+void PrintTo(const Response &resp, std::ostream *os);
 
 /// A notification which does not expect a response.
+/// See
+/// https://modelcontextprotocol.io/specification/2025-06-18/schema#jsonrpcnotification
 struct Notification {
   /// The method to be invoked.
   std::string method;
   /// The notification's params.
   std::optional<llvm::json::Value> params;
 };
-
 llvm::json::Value toJSON(const Notification &);
 bool fromJSON(const llvm::json::Value &, Notification &, llvm::json::Path);
 bool operator==(const Notification &, const Notification &);
+void PrintTo(const Notification &note, std::ostream *os);
 
 /// A general message as defined by the JSON-RPC 2.0 spec.
 using Message = std::variant<Request, Response, Notification>;
@@ -90,46 +114,13 @@ using Message = std::variant<Request, Response, Notification>;
 // not force it to be checked early here.
 static_assert(std::is_convertible_v<Message, Message>,
               "Message is not convertible to itself");
-
 bool fromJSON(const llvm::json::Value &, Message &, llvm::json::Path);
 llvm::json::Value toJSON(const Message &);
-
-struct ToolCapability {
-  /// Whether this server supports notifications for changes to the tool list.
-  bool listChanged = false;
-};
-
-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 {
-  /// 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);
+void PrintTo(const Message &message, std::ostream *os);
 
 /// A known resource that the server is capable of reading.
+///
+/// See https://modelcontextprotocol.io/specification/2025-06-18/schema#resource
 struct Resource {
   /// The URI of this resource.
   std::string uri;
@@ -138,17 +129,28 @@ struct Resource {
   std::string name;
 
   /// A description of what this resource represents.
-  std::string description;
+  std::string description = "";
 
   /// The MIME type of this resource, if known.
-  std::string mimeType;
+  std::string mimeType = "";
 };
 
 llvm::json::Value toJSON(const Resource &);
 bool fromJSON(const llvm::json::Value &, Resource &, llvm::json::Path);
 
+/// The server’s response to a resources/list request from the client.
+///
+/// See
+/// https://modelcontextprotocol.io/specification/2025-06-18/schema#listresourcesresult
+struct ListResourcesResult {
+  std::vector<Resource> resources;
+};
+llvm::json::Value toJSON(const ListResourcesResult &);
+bool fromJSON(const llvm::json::Value &, ListResourcesResult &,
+              llvm::json::Path);
+
 /// The contents of a specific resource or sub-resource.
-struct ResourceContents {
+struct TextResourceContents {
   /// The URI of this resource.
   std::string uri;
 
@@ -160,34 +162,45 @@ struct ResourceContents {
   std::string mimeType;
 };
 
-llvm::json::Value toJSON(const ResourceContents &);
-bool fromJSON(const llvm::json::Value &, ResourceContents &, llvm::json::Path);
+llvm::json::Value toJSON(const TextResourceContents &);
+bool fromJSON(const llvm::json::Value &, TextResourceContents &,
+              llvm::json::Path);
 
-/// The server's response to a resources/read request from the client.
-struct ResourceResult {
-  std::vector<ResourceContents> contents;
+/// Sent from the client to the server, to read a specific resource URI.
+///
+/// See
+/// https://modelcontextprotocol.io/specification/2025-06-18/schema#readresourcerequest
+struct ReadResourceParams {
+  /// The URI of the resource to read. The URI can use any protocol; it is up to
+  /// the server how to interpret it.
+  std::string uri;
 };
+llvm::json::Value toJSON(const ReadResourceParams &);
+bool fromJSON(const llvm::json::Value &, ReadResourceParams &,
+              llvm::json::Path);
 
-llvm::json::Value toJSON(const ResourceResult &);
-bool fromJSON(const llvm::json::Value &, ResourceResult &, llvm::json::Path);
+/// The server's response to a resources/read request from the client.
+struct ReadResourceResult {
+  std::vector<TextResourceContents> contents;
+};
+llvm::json::Value toJSON(const ReadResourceResult &);
+bool fromJSON(const llvm::json::Value &, ReadResourceResult &,
+              llvm::json::Path);
 
 /// Text provided to or from an LLM.
+///
+/// See
+/// https://modelcontextprotocol.io/specification/2025-06-18/schema#textcontent
 struct TextContent {
   /// The text content of the message.
   std::string text;
 };
-
 llvm::json::Value toJSON(const TextContent &);
 bool fromJSON(const llvm::json::Value &, TextContent &, llvm::json::Path);
 
-struct TextResult {
-  std::vector<TextContent> content;
-  bool isError = false;
-};
-
-llvm::json::Value toJSON(const TextResult &);
-bool fromJSON(const llvm::json::Value &, TextResult &, llvm::json::Path);
-
+/// Definition for a tool the client can call.
+///
+/// See https://modelcontextprotocol.io/specification/2025-06-18/schema#tool
 struct ToolDefinition {
   /// Unique identifier for the tool.
   std::string name;
@@ -198,12 +211,165 @@ struct ToolDefinition {
   // JSON Schema for the tool's parameters.
   std::optional<llvm::json::Value> inputSchema;
 };
-
 llvm::json::Value toJSON(const ToolDefinition &);
 bool fromJSON(const llvm::json::Value &, ToolDefinition &, llvm::json::Path);
 
 using ToolArguments = std::variant<std::monostate, llvm::json::Value>;
 
+/// Describes the name and version of an MCP implementation, with an optional
+/// title for UI representation.
+///
+/// see
+/// https://modelcontextprotocol.io/specification/2025-06-18/schema#implementation
+struct Implementation {
+  /// Intended for programmatic or logical use, but used as a display name in
+  /// past specs or fallback (if title isn’t present).
+  std::string name;
+
+  std::string version;
+
+  /// Intended for UI and end-user contexts — optimized to be human-readable and
+  /// easily understood, even by those unfamiliar with domain-specific
+  /// terminology.
+  ///
+  /// If not provided, the name should be used for display (except for Tool,
+  /// where annotations.title should be given precedence over using name, if
+  /// present).
+  std::string title = "";
+};
+llvm::json::Value toJSON(const Implementation &);
+bool fromJSON(const llvm::json::Value &, Implementation &, llvm::json::Path);
+
+/// Capabilities a client may support. Known capabilities are defined here, in
+/// this schema, but this is not a closed set: any client can define its own,
+/// additional capabilities.
+///
+/// See
+/// https://modelcontextprotocol.io/specification/2025-06-18/schema#clientcapabilities
+struct ClientCapabilities {};
+llvm::json::Value toJSON(const ClientCapabilities &);
+bool fromJSON(const llvm::json::Value &, ClientCapabilities &,
+              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.
+///
+/// See
+/// https://modelcontextprotocol.io/specification/2025-06-18/schema#servercapabilities
+struct ServerCapabilities {
+  bool supportsToolsList = false;
+  bool supportsResourcesList = false;
+  bool supportsResourcesSubscribe = false;
+
+  /// Utilities.
+  bool supportsCompletions = false;
+  bool supportsLogging = false;
+};
+llvm::json::Value toJSON(const ServerCapabilities &);
+bool fromJSON(const llvm::json::Value &, ServerCapabilities &,
+              llvm::json::Path);
+
+/// Initialization
+
+/// This request is sent from the client to the server when it first connects,
+/// asking it to begin initialization.
+///
+/// See
+/// https://modelcontextprotocol.io/specification/2025-06-18/schema#initializerequest
+struct InitializeParams {
+  /// The latest version of the Model Context Protocol that the client supports.
+  /// The client MAY decide to support older versions as well.
+  std::string protocolVersion;
+
+  ClientCapabilities capabilities;
+
+  Implementation clientInfo;
+};
+llvm::json::Value toJSON(const InitializeParams &);
+bool fromJSON(const llvm::json::Value &, InitializeParams &, llvm::json::Path);
+
+/// After receiving an initialize request from the client, the server sends this
+/// response.
+///
+/// See
+/// https://modelcontextprotocol.io/specification/2025-06-18/schema#initializeresult
+struct InitializeResult {
+  /// The version of the Model Context Protocol that the server wants to use.
+  /// This may not match the version that the client requested. If the client
+  /// cannot support this version, it MUST disconnect.
+  std::string protocolVersion;
+
+  ServerCapabilities capabilities;
+  Implementation serverInfo;
+
+  /// Instructions describing how to use the server and its features.
+  ///
+  /// This can be used by clients to improve the LLM's understanding of
+  /// available tools, resources, etc. It can be thought of like a "hint" to the
+  /// model. For example, this information MAY be added to the system prompt.
+  std::string instructions = "";
+};
+llvm::json::Value toJSON(const InitializeResult &);
+bool fromJSON(const llvm::json::Value &, InitializeResult &, llvm::json::Path);
+
+/// Special case parameter or result that has no value.
+using Void = std::monostate;
+llvm::json::Value toJSON(const Void &);
+bool fromJSON(const llvm::json::Value &, Void &, llvm::json::Path);
+
+/// The server's response to a `tools/list` request from the client.
+///
+/// See
+/// https://modelcontextprotocol.io/specification/2025-06-18/schema#listtoolsresult
+struct ListToolsResult {
+  std::vector<ToolDefinition> tools;
+};
+llvm::json::Value toJSON(const ListToolsResult &);
+bool fromJSON(const llvm::json::Value &, ListToolsResult &, llvm::json::Path);
+
+/// Supported content types, currently only TextContent, but the spec includes
+/// additional content types.
+using ContentBlock = TextContent;
+
+/// Used by the client to invoke a tool provided by the server.
+struct CallToolParams {
+  std::string name;
+  std::optional<llvm::json::Value> arguments;
+};
+llvm::json::Value toJSON(const CallToolParams &);
+bool fromJSON(const llvm::json::Value &, CallToolParams &, llvm::json::Path);
+
+/// The server’s response to a tool call.
+///
+/// See
+/// https://modelcontextprotocol.io/specification/2025-06-18/schema#calltoolresult
+struct CallToolResult {
+  /// A list of content objects that represent the unstructured result of the
+  /// tool call.
+  std::vector<ContentBlock> content;
+
+  /// Whether the tool call ended in an error.
+  ///
+  /// If not set, this is assumed to be false (the call was successful).
+  ///
+  /// Any errors that originate from the tool SHOULD be reported inside the
+  /// result object, with `isError` set to true, not as an MCP protocol-level
+  /// error response. Otherwise, the LLM would not be able to see that an error
+  /// occurred and self-correct.
+  ///
+  /// However, any errors in finding the tool, an error indicating that the
+  /// server does not support tool calls, or any other exceptional conditions,
+  /// should be reported as an MCP error response.
+  bool isError = false;
+
+  /// An optional JSON object that represents the structured result of the tool
+  /// call.
+  std::optional<llvm::json::Value> structuredContent = std::nullopt;
+};
+llvm::json::Value toJSON(const CallToolResult &);
+bool fromJSON(const llvm::json::Value &, CallToolResult &, llvm::json::Path);
+
 } // namespace lldb_protocol::mcp
 
 #endif
diff --git a/lldb/include/lldb/Protocol/MCP/Resource.h b/lldb/include/lldb/Protocol/MCP/Resource.h
index 4835d340cd4c6..158cffc71ea10 100644
--- a/lldb/include/lldb/Protocol/MCP/Resource.h
+++ b/lldb/include/lldb/Protocol/MCP/Resource.h
@@ -20,7 +20,7 @@ class ResourceProvider {
   virtual ~ResourceProvider() = default;
 
   virtual std::vector<lldb_protocol::mcp::Resource> GetResources() const = 0;
-  virtual llvm::Expected<lldb_protocol::mcp::ResourceResult>
+  virtual llvm::Expected<lldb_protocol::mcp::ReadResourceResult>
   ReadResource(llvm::StringRef uri) const = 0;
 };
 
diff --git a/lldb/include/lldb/Protocol/MCP/Server.h b/lldb/include/lldb/Protocol/MCP/Server.h
index 2b9e919329752..aa5714e45755e 100644
--- a/lldb/include/lldb/Protocol/MCP/Server.h
+++ b/lldb/include/lldb/Protocol/MCP/Server.h
@@ -58,7 +58,7 @@ class Server : public MCPTransport::MessageHandler {
   llvm::Error Run();
 
 protected:
-  Capabilities GetCapabilities();
+  ServerCapabilities GetCapabilities();
 
   using RequestHandler =
       std::function<llvm::Expected<Response>(const Request &)>;
diff --git a/lldb/include/lldb/Protocol/MCP/Tool.h b/lldb/include/lldb/Protocol/MCP/Tool.h
index 96669d1357166..6c9f05161f8e7 100644
--- a/lldb/include/lldb/Protocol/MCP/Tool.h
+++ b/lldb/include/lldb/Protocol/MCP/Tool.h
@@ -10,6 +10,7 @@
 #define LLDB_PROTOCOL_MCP_TOOL_H
 
 #include "lldb/Protocol/MCP/Protocol.h"
+#include "llvm/Support/Error.h"
 #include "llvm/Support/JSON.h"
 #include <string>
 
@@ -20,7 +21,7 @@ class Tool {
   Tool(std::string name, std::string description);
   virtual ~Tool() = default;
 
-  virtual llvm::Expected<lldb_protocol::mcp::TextResult>
+  virtual llvm::Expected<lldb_protocol::mcp::CallToolResult>
   Call(const lldb_protocol::mcp::ToolArguments &args) = 0;
 
   virtual std::optional<llvm::json::Value> GetSchema() const {
diff --git a/lldb/source/Plugins/Protocol/MCP/Resource.cpp b/lldb/source/Plugins/Protocol/MCP/Resource.cpp
index e94d2cdd65e07..581424510d4cf 100644
--- a/lldb/source/Plugins/Protocol/MCP/Resource.cpp
+++ b/lldb/source/Plugins/Protocol/MCP/Resource.cpp
@@ -8,7 +8,6 @@
 #include "lldb/Core/Debugger.h"
 #include "lldb/Core/Module.h"
 #include "lldb/Protocol/MCP/MCPError.h"
-#include "lldb/Target/Platform.h"
 
 using namespace lldb_private;
 using namespace lldb_private::mcp;
@@ -124,7 +123,7 @@ DebuggerResourceProvider::GetResources() const {
   return resources;
 }
 
-llvm::Expected<lldb_protocol::mcp::ResourceResult>
+llvm::Expected<lldb_protocol::mcp::ReadResourceResult>
 DebuggerResourceProvider::ReadResource(llvm::StringRef uri) const {
 
   auto [protocol, path] = uri.split("://");
@@ -161,7 +160,7 @@ DebuggerResourceProvider::ReadResource(llvm::StringRef uri) const {
   return ReadDebuggerResource(uri, debugger_idx);
 }
 
-llvm::Expected<lldb_protocol::mcp::ResourceResult>
+llvm::Expected<lldb_protocol::mcp::ReadResourceResult>
 DebuggerResourceProvider::ReadDebuggerResource(llvm::StringRef uri,
                                                lldb::user_id_t debugger_id) {
   lldb::DebuggerSP debugger_sp = Debugger::FindDebuggerWithID(debugger_id);
@@ -173,17 +172,17 @@ DebuggerResourceProvider::ReadDebuggerResource(llvm::StringRef uri,
   debugger_resource.name = debugger_sp->GetInstanceName();
   debugger_resource.num_targets = debugger_sp->GetTargetList().GetNumTargets();
 
-  lldb_protocol::mcp::ResourceContents contents;
+  lldb_protocol::mcp::TextResourceContents contents;
   contents.uri = uri;
   contents.mimeType = kMimeTypeJSON;
   contents.text = llvm::formatv("{0}", toJSON(debugger_resource));
 
-  lldb_protocol::mcp::ResourceResult result;
+  lldb_protocol::mcp::ReadResourceResult result;
   result.contents.push_back(contents);
   return result;
 }
 
-llvm::Expected<lldb_protocol::mcp::ResourceResult>
+llvm::Expected<lldb_protocol::mcp::ReadResourceResult>
 DebuggerResourceProvider::ReadTargetResource(llvm::StringRef uri,
                                              lldb::user_id_t debugger_id,
                                              size_t target_idx) {
@@ -209,12 +208,12 @@ DebuggerResourceProvider::ReadTargetResource(llvm::StringRef uri,
   if (lldb::PlatformSP platform_sp = target_sp->GetPlatform())
     target_resource.platform = platform_sp->GetName();
 
-  lldb_protocol::mcp::ResourceContents contents;
+  lldb_protocol::mcp::TextResourceContents contents;
   contents.uri = uri;
   contents.mimeType = kMimeTypeJSON;
   contents.text = llvm::formatv("{0}", toJSON(target_resource));
 
-  lldb_protocol::mcp::ResourceResult result;
+  lldb_protocol::mcp::ReadResourceResult 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
index e2382a74f796b..0c6576602905e 100644
--- a/lldb/source/Plugins/Protocol/MCP/Resource.h
+++ b/lldb/source/Plugins/Protocol/MCP/Resource.h
@@ -11,7 +11,11 @@
 
 #include "lldb/Protocol/MCP/Protocol.h"
 #include "lldb/Protocol/MCP/Resource.h"
-#include "lldb/lldb-private.h"
+#include "lldb/lldb-forward.h"
+#include "lldb/lldb-types.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/Error.h"
+#include <cstddef>
 #include <vector>
 
 namespace lldb_private::mcp {
@@ -21,9 +25,8 @@ class DebuggerResourceProvider : public lldb_protocol::mcp::ResourceProvider {
   using ResourceProvider::ResourceProvider;
   virtual ~DebuggerResourceProvider() = default;
 
-  virtual std::vector<lldb_protocol::mcp::Resource>
-  GetResources() const override;
-  virtual llvm::Expected<lldb_protocol::mcp::ResourceResult>
+  std::vector<lldb_...
[truncated]

@JDevlieghere
Copy link
Member

🚢

@ashgti ashgti merged commit a67257b into llvm:main Aug 26, 2025
9 checks passed
@ashgti ashgti deleted the lldb-mcp-protocl branch August 26, 2025 22:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants