llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT--> @llvm/pr-subscribers-lldb Author: John Harrison (ashgti) <details> <summary>Changes</summary> 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 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 ¬e, 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] `````````` </details> https://github.com/llvm/llvm-project/pull/155460 _______________________________________________ lldb-commits mailing list lldb-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits