Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,17 +1,19 @@
//===-- MCPError.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
//
//===----------------------------------------------------------------------===//

#include "Protocol.h"
#ifndef LLDB_PROTOCOL_MCP_MCPERROR_H
#define LLDB_PROTOCOL_MCP_MCPERROR_H

#include "lldb/Protocol/MCP/Protocol.h"
#include "llvm/Support/Error.h"
#include "llvm/Support/FormatVariadic.h"
#include <string>

namespace lldb_private::mcp {
namespace lldb_protocol::mcp {

class MCPError : public llvm::ErrorInfo<MCPError> {
public:
Expand All @@ -24,7 +26,7 @@ class MCPError : public llvm::ErrorInfo<MCPError> {

const std::string &getMessage() const { return m_message; }

protocol::Error toProtcolError() const;
lldb_protocol::mcp::Error toProtocolError() const;

static constexpr int64_t kResourceNotFound = -32002;
static constexpr int64_t kInternalError = -32603;
Expand All @@ -47,4 +49,6 @@ class UnsupportedURI : public llvm::ErrorInfo<UnsupportedURI> {
std::string m_uri;
};

} // namespace lldb_private::mcp
} // namespace lldb_protocol::mcp

#endif
Original file line number Diff line number Diff line change
Expand Up @@ -11,62 +11,88 @@
//
//===----------------------------------------------------------------------===//

#ifndef LLDB_PLUGINS_PROTOCOL_MCP_PROTOCOL_H
#define LLDB_PLUGINS_PROTOCOL_MCP_PROTOCOL_H
#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_private::mcp::protocol {
namespace lldb_protocol::mcp {

static llvm::StringLiteral kVersion = "2024-11-05";
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 {
uint64_t id = 0;
/// 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 ErrorInfo {
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;
std::string data;
};

llvm::json::Value toJSON(const ErrorInfo &);
bool fromJSON(const llvm::json::Value &, ErrorInfo &, llvm::json::Path);

struct Error {
uint64_t id = 0;
ErrorInfo 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;
};

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 {
uint64_t id = 0;
std::optional<llvm::json::Value> result;
std::optional<ErrorInfo> error;
/// 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.
Expand Down Expand Up @@ -176,13 +202,8 @@ struct ToolDefinition {
llvm::json::Value toJSON(const ToolDefinition &);
bool fromJSON(const llvm::json::Value &, ToolDefinition &, llvm::json::Path);

using Message = std::variant<Request, Response, Notification, Error>;

bool fromJSON(const llvm::json::Value &, Message &, llvm::json::Path);
llvm::json::Value toJSON(const Message &);

using ToolArguments = std::variant<std::monostate, llvm::json::Value>;

} // namespace lldb_private::mcp::protocol
} // namespace lldb_protocol::mcp

#endif
29 changes: 29 additions & 0 deletions lldb/include/lldb/Protocol/MCP/Resource.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
//===----------------------------------------------------------------------===//
//
// 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_PROTOCOL_MCP_RESOURCE_H
#define LLDB_PROTOCOL_MCP_RESOURCE_H

#include "lldb/Protocol/MCP/Protocol.h"
#include <vector>

namespace lldb_protocol::mcp {

class ResourceProvider {
public:
ResourceProvider() = default;
virtual ~ResourceProvider() = default;

virtual std::vector<lldb_protocol::mcp::Resource> GetResources() const = 0;
virtual llvm::Expected<lldb_protocol::mcp::ResourceResult>
ReadResource(llvm::StringRef uri) const = 0;
};

} // namespace lldb_protocol::mcp

#endif
70 changes: 70 additions & 0 deletions lldb/include/lldb/Protocol/MCP/Server.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
//===----------------------------------------------------------------------===//
//
// 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_PROTOCOL_MCP_SERVER_H
#define LLDB_PROTOCOL_MCP_SERVER_H

#include "lldb/Protocol/MCP/Protocol.h"
#include "lldb/Protocol/MCP/Resource.h"
#include "lldb/Protocol/MCP/Tool.h"
#include "llvm/ADT/StringMap.h"
#include "llvm/Support/Error.h"
#include <mutex>

namespace lldb_protocol::mcp {

class Server {
public:
Server(std::string name, std::string version);
virtual ~Server() = default;

void AddTool(std::unique_ptr<Tool> tool);
void AddResourceProvider(std::unique_ptr<ResourceProvider> resource_provider);

protected:
virtual Capabilities GetCapabilities() = 0;

using RequestHandler =
std::function<llvm::Expected<Response>(const Request &)>;
using NotificationHandler = std::function<void(const Notification &)>;

void AddRequestHandlers();

void AddRequestHandler(llvm::StringRef method, RequestHandler handler);
void AddNotificationHandler(llvm::StringRef method,
NotificationHandler handler);

llvm::Expected<std::optional<Message>> HandleData(llvm::StringRef data);

llvm::Expected<Response> Handle(Request request);
void Handle(Notification notification);

llvm::Expected<Response> InitializeHandler(const Request &);

llvm::Expected<Response> ToolsListHandler(const Request &);
llvm::Expected<Response> ToolsCallHandler(const Request &);

llvm::Expected<Response> ResourcesListHandler(const Request &);
llvm::Expected<Response> ResourcesReadHandler(const Request &);

std::mutex m_mutex;

private:
const std::string m_name;
const std::string m_version;

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;
};

} // namespace lldb_protocol::mcp

#endif
41 changes: 41 additions & 0 deletions lldb/include/lldb/Protocol/MCP/Tool.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
//===----------------------------------------------------------------------===//
//
// 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_PROTOCOL_MCP_TOOL_H
#define LLDB_PROTOCOL_MCP_TOOL_H

#include "lldb/Protocol/MCP/Protocol.h"
#include "llvm/Support/JSON.h"
#include <string>

namespace lldb_protocol::mcp {

class Tool {
public:
Tool(std::string name, std::string description);
virtual ~Tool() = default;

virtual llvm::Expected<lldb_protocol::mcp::TextResult>
Call(const lldb_protocol::mcp::ToolArguments &args) = 0;

virtual std::optional<llvm::json::Value> GetSchema() const {
return llvm::json::Object{{"type", "object"}};
}

lldb_protocol::mcp::ToolDefinition GetDefinition() const;

const std::string &GetName() { return m_name; }

private:
std::string m_name;
std::string m_description;
};

} // namespace lldb_protocol::mcp

#endif
1 change: 1 addition & 0 deletions lldb/source/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ add_subdirectory(Host)
add_subdirectory(Initialization)
add_subdirectory(Interpreter)
add_subdirectory(Plugins)
add_subdirectory(Protocol)
add_subdirectory(Symbol)
add_subdirectory(Target)
add_subdirectory(Utility)
Expand Down
3 changes: 1 addition & 2 deletions lldb/source/Plugins/Protocol/MCP/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
add_lldb_library(lldbPluginProtocolServerMCP PLUGIN
MCPError.cpp
Protocol.cpp
ProtocolServerMCP.cpp
Resource.cpp
Tool.cpp
Expand All @@ -10,5 +8,6 @@ add_lldb_library(lldbPluginProtocolServerMCP PLUGIN

LINK_LIBS
lldbHost
lldbProtocolMCP
lldbUtility
)
Loading