David Spickett dece902a52 [lldb][MCP] Fix compiler error in Windows on Arm build
Caused by https://github.com/llvm/llvm-project/pull/153297.

This is likely not Windows on Arm specific but the other Windows bots
don't have this problem.

json::parse tries to construct llvm::Expected<Message> by moving another
instance of Message into it. This would normally use this constructor:
```
  /// Create an Expected<T> success value from the given OtherT value, which
  /// must be convertible to T.
  template <typename OtherT>
  Expected(OtherT &&Val,
           std::enable_if_t<std::is_convertible_v<OtherT, T>> * = nullptr)
<...>
```
Note that llvm::Expected does not have a T&& constructor. Presumably
the authors thought the converting one would be used.

Except that in our build, using clang-cl 19.1.7, Visual Studio 2022,
MSVC STL 202208, somehow is_convertible_v is false for Message.

If you add a static_assert to check that this is the case, it suddenly
becomes convertible. As best I can understand, this is because evaluation
of the properties of Message are delayed. Delayed so much that by the time
the constructor is called, it's still false.

So we can "fix" this by asserting that it is convertible some time before
it is checked by the constructor.

I'm not sure if that's a compiler problem or the way MSVC STL's variant is
written. I couldn't reproduce this behaviour in smaller examples or on Linux
systems. This is the least invasive fix and only touches the new lldb code,
so I'm going with it.

Including the whole error here because it's a strange one and maybe later
someone who has a clue about this can fix it in a better way.

```
C:\Users\tcwg\llvm-worker\lldb-aarch64-windows\build>ninja
[5/15] Building CXX object tools\lldb\source\Pro...CP\CMakeFiles\lldbProtocolMCP.dir\Server.cpp.obj
FAILED: tools/lldb/source/Protocol/MCP/CMakeFiles/lldbProtocolMCP.dir/Server.cpp.obj
C:\Users\tcwg\scoop\shims\ccache.exe C:\Users\tcwg\scoop\apps\llvm-arm64\current\bin\clang-cl.exe  /nologo -TP -DGTEST_HAS_RTTI=0 -DUNICODE -D_CRT_NONSTDC_NO_DEPRECATE -D_CRT_NONSTDC_NO_WARNINGS -D_CRT_SECURE_NO_DEPRECATE -D_CRT_SECURE_NO_WARNINGS -D_ENABLE_EXTENDED_ALIGNED_STORAGE -D_HAS_EXCEPTIONS=0 -D_SCL_SECURE_NO_DEPRECATE -D_SCL_SECURE_NO_WARNINGS -D_UNICODE -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS -IC:\Users\tcwg\llvm-worker\lldb-aarch64-windows\build\tools\lldb\source\Protocol\MCP -IC:\Users\tcwg\llvm-worker\lldb-aarch64-windows\llvm-project\lldb\source\Protocol\MCP -IC:\Users\tcwg\llvm-worker\lldb-aarch64-windows\llvm-project\lldb\include -IC:\Users\tcwg\llvm-worker\lldb-aarch64-windows\build\tools\lldb\include -IC:\Users\tcwg\llvm-worker\lldb-aarch64-windows\build\include -IC:\Users\tcwg\llvm-worker\lldb-aarch64-windows\llvm-project\llvm\include -IC:\Users\tcwg\scoop\apps\python\current\include -IC:\Users\tcwg\llvm-worker\lldb-aarch64-windows\llvm-project\llvm\..\clang\include -IC:\Users\tcwg\llvm-worker\lldb-aarch64-windows\build\tools\lldb\..\clang\include -IC:\Users\tcwg\llvm-worker\lldb-aarch64-windows\llvm-project\lldb\source -IC:\Users\tcwg\llvm-worker\lldb-aarch64-windows\build\tools\lldb\source /DWIN32 /D_WINDOWS   /Zc:inline /Zc:__cplusplus /Oi /Brepro /bigobj /permissive- -Werror=unguarded-availability-new /W4  -Wextra -Wno-unused-parameter -Wwrite-strings -Wcast-qual -Wmissing-field-initializers -Wimplicit-fallthrough -Wcovered-switch-default -Wno-noexcept-type -Wnon-virtual-dtor -Wdelete-non-virtual-dtor -Wsuggest-override -Wstring-conversion -Wmisleading-indentation -Wctad-maybe-unsupported /Gw -Wno-vla-extension /O2 /Ob2 /DNDEBUG -std:c++17 -MD   -wd4018 -wd4068 -wd4150 -wd4201 -wd4251 -wd4521 -wd4530 -wd4589  /EHs-c- /GR- /showIncludes /Fotools\lldb\source\Protocol\MCP\CMakeFiles\lldbProtocolMCP.dir\Server.cpp.obj /Fdtools\lldb\source\Protocol\MCP\CMakeFiles\lldbProtocolMCP.dir\lldbProtocolMCP.pdb -c -- C:\Users\tcwg\llvm-worker\lldb-aarch64-windows\llvm-project\lldb\source\Protocol\MCP\Server.cpp
In file included from C:\Users\tcwg\llvm-worker\lldb-aarch64-windows\llvm-project\lldb\source\Protocol\MCP\Server.cpp:9:
In file included from C:\Users\tcwg\llvm-worker\lldb-aarch64-windows\llvm-project\lldb\include\lldb/Protocol/MCP/Server.h:12:
In file included from C:\Users\tcwg\llvm-worker\lldb-aarch64-windows\llvm-project\lldb\include\lldb/Protocol/MCP/Protocol.h:17:
C:\Users\tcwg\llvm-worker\lldb-aarch64-windows\llvm-project\llvm\include\llvm/Support/JSON.h(938,12): error: no viable conversion from returned value of type 'remove_reference_t<variant<Request, Response, Notification> &>' (aka 'std::variant<lldb_protocol::mcp::Request, lldb_protocol::mcp::Response, lldb_protocol::mcp::Notification>') to function return type 'Expected<variant<Request, Response, Notification>>'
  938 |     return std::move(Result);
      |            ^~~~~~~~~~~~~~~~~
C:\Users\tcwg\llvm-worker\lldb-aarch64-windows\llvm-project\lldb\source\Protocol\MCP\Server.cpp(57,30): note: in instantiation of function template specialization 'llvm::json::parse<std::variant<lldb_protocol::mcp::Request, lldb_protocol::mcp::Response, lldb_protocol::mcp::Notification>>' requested here
   57 |   auto message = llvm::json::parse<Message>(/*JSON=*/data);
      |                              ^
C:\Users\tcwg\llvm-worker\lldb-aarch64-windows\llvm-project\llvm\include\llvm/Support/Error.h(485,40): note: candidate constructor (the implicit copy constructor) not viable: no known conversion from 'remove_reference_t<variant<Request, Response, Notification> &>' (aka 'std::variant<lldb_protocol::mcp::Request, lldb_protocol::mcp::Response, lldb_protocol::mcp::Notification>') to 'const Expected<variant<Request, Response, Notification>> &' for 1st argument
  485 | template <class T> class [[nodiscard]] Expected {
      |                                        ^~~~~~~~
C:\Users\tcwg\llvm-worker\lldb-aarch64-windows\llvm-project\llvm\include\llvm/Support/Error.h(507,3): note: candidate constructor not viable: no known conversion from 'remove_reference_t<variant<Request, Response, Notification> &>' (aka 'std::variant<lldb_protocol::mcp::Request, lldb_protocol::mcp::Response, lldb_protocol::mcp::Notification>') to 'Error &&' for 1st argument
  507 |   Expected(Error &&Err)
      |   ^        ~~~~~~~~~~~
C:\Users\tcwg\llvm-worker\lldb-aarch64-windows\llvm-project\llvm\include\llvm/Support/Error.h(521,3): note: candidate constructor not viable: no known conversion from 'remove_reference_t<variant<Request, Response, Notification> &>' (aka 'std::variant<lldb_protocol::mcp::Request, lldb_protocol::mcp::Response, lldb_protocol::mcp::Notification>') to 'ErrorSuccess' for 1st argument
  521 |   Expected(ErrorSuccess) = delete;
      |   ^        ~~~~~~~~~~~~
C:\Users\tcwg\llvm-worker\lldb-aarch64-windows\llvm-project\llvm\include\llvm/Support/Error.h(539,3): note: candidate constructor not viable: no known conversion from 'remove_reference_t<variant<Request, Response, Notification> &>' (aka 'std::variant<lldb_protocol::mcp::Request, lldb_protocol::mcp::Response, lldb_protocol::mcp::Notification>') to 'Expected<variant<Request, Response, Notification>> &&' for 1st argument
  539 |   Expected(Expected &&Other) { moveConstruct(std::move(Other)); }
      |   ^        ~~~~~~~~~~~~~~~~
C:\Users\tcwg\llvm-worker\lldb-aarch64-windows\llvm-project\llvm\include\llvm/Support/Error.h(526,3): note: candidate template ignored: requirement 'std::is_convertible_v<std::variant<lldb_protocol::mcp::Request, lldb_protocol::mcp::Response, lldb_protocol::mcp::Notification>, std::variant<lldb_protocol::mcp::Request, lldb_protocol::mcp::Response, lldb_protocol::mcp::Notification>>' was not satisfied [with OtherT = remove_reference_t<variant<Request, Response, Notification> &>]
  526 |   Expected(OtherT &&Val,
      |   ^
C:\Users\tcwg\llvm-worker\lldb-aarch64-windows\llvm-project\llvm\include\llvm/Support/Error.h(544,3): note: candidate template ignored: could not match 'Expected' against 'variant'
  544 |   Expected(Expected<OtherT> &&Other,
      |   ^
C:\Users\tcwg\llvm-worker\lldb-aarch64-windows\llvm-project\llvm\include\llvm/Support/Error.h(552,12): note: explicit constructor is not a candidate
  552 |   explicit Expected(
      |            ^
1 error generated.
[8/15] Building CXX object tools\lldb\source\Plu...nProtocolServerMCP.dir\ProtocolServerMCP.cpp.obj
ninja: build stopped: subcommand failed.
```
2025-08-13 12:55:40 +00:00

210 lines
6.5 KiB
C++

//===- Protocol.h ---------------------------------------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//
//
// This file contains POD structs based on the MCP specification at
// https://github.com/modelcontextprotocol/modelcontextprotocol/blob/main/schema/2024-11-05/schema.json
//
//===----------------------------------------------------------------------===//
#ifndef LLDB_PROTOCOL_MCP_PROTOCOL_H
#define LLDB_PROTOCOL_MCP_PROTOCOL_H
#include "llvm/Support/JSON.h"
#include <optional>
#include <string>
#include <variant>
namespace lldb_protocol::mcp {
static llvm::StringLiteral kProtocolVersion = "2024-11-05";
/// A Request or Response 'id'.
///
/// NOTE: This differs from the JSON-RPC 2.0 spec. The MCP spec says this must
/// be a string or number, excluding a json 'null' as a valid id.
using Id = std::variant<int64_t, std::string>;
/// A request that expects a response.
struct Request {
/// The request id.
Id id = 0;
/// The method to be invoked.
std::string method;
/// 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 &);
struct Error {
/// The error type that occurred.
int64_t code = 0;
/// A short description of the error. The message SHOULD be limited to a
/// concise single sentence.
std::string message;
/// 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;
};
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.
struct Response {
/// The request id.
Id id = 0;
/// The result of the request, either an Error or the JSON value of the
/// 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 &);
/// A notification which does not expect a response.
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 &);
/// A general message as defined by the JSON-RPC 2.0 spec.
using Message = std::variant<Request, Response, Notification>;
// With clang-cl and MSVC STL 202208, convertible can be false later if we do
// 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);
/// 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::string description;
/// The MIME type of this resource, if known.
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::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.
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);
struct ToolDefinition {
/// Unique identifier for the tool.
std::string name;
/// Human-readable description.
std::string description;
// 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>;
} // namespace lldb_protocol::mcp
#endif