Path: blob/main/contrib/llvm-project/lldb/source/Plugins/Protocol/MCP/Protocol.cpp
213845 views
//===- Protocol.cpp -------------------------------------------------------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//78#include "Protocol.h"9#include "llvm/Support/JSON.h"1011using namespace llvm;1213namespace lldb_private::mcp::protocol {1415static bool mapRaw(const json::Value &Params, StringLiteral Prop,16std::optional<json::Value> &V, json::Path P) {17const auto *O = Params.getAsObject();18if (!O) {19P.report("expected object");20return false;21}22const json::Value *E = O->get(Prop);23if (E)24V = std::move(*E);25return true;26}2728llvm::json::Value toJSON(const Request &R) {29json::Object Result{{"jsonrpc", "2.0"}, {"id", R.id}, {"method", R.method}};30if (R.params)31Result.insert({"params", R.params});32return Result;33}3435bool fromJSON(const llvm::json::Value &V, Request &R, llvm::json::Path P) {36llvm::json::ObjectMapper O(V, P);37if (!O || !O.map("id", R.id) || !O.map("method", R.method))38return false;39return mapRaw(V, "params", R.params, P);40}4142llvm::json::Value toJSON(const ErrorInfo &EI) {43llvm::json::Object Result{{"code", EI.code}, {"message", EI.message}};44if (!EI.data.empty())45Result.insert({"data", EI.data});46return Result;47}4849bool fromJSON(const llvm::json::Value &V, ErrorInfo &EI, llvm::json::Path P) {50llvm::json::ObjectMapper O(V, P);51return O && O.map("code", EI.code) && O.map("message", EI.message) &&52O.mapOptional("data", EI.data);53}5455llvm::json::Value toJSON(const Error &E) {56return json::Object{{"jsonrpc", "2.0"}, {"id", E.id}, {"error", E.error}};57}5859bool fromJSON(const llvm::json::Value &V, Error &E, llvm::json::Path P) {60llvm::json::ObjectMapper O(V, P);61return O && O.map("id", E.id) && O.map("error", E.error);62}6364llvm::json::Value toJSON(const Response &R) {65llvm::json::Object Result{{"jsonrpc", "2.0"}, {"id", R.id}};66if (R.result)67Result.insert({"result", R.result});68if (R.error)69Result.insert({"error", R.error});70return Result;71}7273bool fromJSON(const llvm::json::Value &V, Response &R, llvm::json::Path P) {74llvm::json::ObjectMapper O(V, P);75if (!O || !O.map("id", R.id) || !O.map("error", R.error))76return false;77return mapRaw(V, "result", R.result, P);78}7980llvm::json::Value toJSON(const Notification &N) {81llvm::json::Object Result{{"jsonrpc", "2.0"}, {"method", N.method}};82if (N.params)83Result.insert({"params", N.params});84return Result;85}8687bool fromJSON(const llvm::json::Value &V, Notification &N, llvm::json::Path P) {88llvm::json::ObjectMapper O(V, P);89if (!O || !O.map("method", N.method))90return false;91auto *Obj = V.getAsObject();92if (!Obj)93return false;94if (auto *Params = Obj->get("params"))95N.params = *Params;96return true;97}9899llvm::json::Value toJSON(const ToolCapability &TC) {100return llvm::json::Object{{"listChanged", TC.listChanged}};101}102103bool fromJSON(const llvm::json::Value &V, ToolCapability &TC,104llvm::json::Path P) {105llvm::json::ObjectMapper O(V, P);106return O && O.map("listChanged", TC.listChanged);107}108109llvm::json::Value toJSON(const ResourceCapability &RC) {110return llvm::json::Object{{"listChanged", RC.listChanged},111{"subscribe", RC.subscribe}};112}113114bool fromJSON(const llvm::json::Value &V, ResourceCapability &RC,115llvm::json::Path P) {116llvm::json::ObjectMapper O(V, P);117return O && O.map("listChanged", RC.listChanged) &&118O.map("subscribe", RC.subscribe);119}120121llvm::json::Value toJSON(const Capabilities &C) {122return llvm::json::Object{{"tools", C.tools}, {"resources", C.resources}};123}124125bool fromJSON(const llvm::json::Value &V, Resource &R, llvm::json::Path P) {126llvm::json::ObjectMapper O(V, P);127return O && O.map("uri", R.uri) && O.map("name", R.name) &&128O.mapOptional("description", R.description) &&129O.mapOptional("mimeType", R.mimeType);130}131132llvm::json::Value toJSON(const Resource &R) {133llvm::json::Object Result{{"uri", R.uri}, {"name", R.name}};134if (!R.description.empty())135Result.insert({"description", R.description});136if (!R.mimeType.empty())137Result.insert({"mimeType", R.mimeType});138return Result;139}140141bool fromJSON(const llvm::json::Value &V, Capabilities &C, llvm::json::Path P) {142llvm::json::ObjectMapper O(V, P);143return O && O.map("tools", C.tools);144}145146llvm::json::Value toJSON(const ResourceContents &RC) {147llvm::json::Object Result{{"uri", RC.uri}, {"text", RC.text}};148if (!RC.mimeType.empty())149Result.insert({"mimeType", RC.mimeType});150return Result;151}152153bool fromJSON(const llvm::json::Value &V, ResourceContents &RC,154llvm::json::Path P) {155llvm::json::ObjectMapper O(V, P);156return O && O.map("uri", RC.uri) && O.map("text", RC.text) &&157O.mapOptional("mimeType", RC.mimeType);158}159160llvm::json::Value toJSON(const ResourceResult &RR) {161return llvm::json::Object{{"contents", RR.contents}};162}163164bool fromJSON(const llvm::json::Value &V, ResourceResult &RR,165llvm::json::Path P) {166llvm::json::ObjectMapper O(V, P);167return O && O.map("contents", RR.contents);168}169170llvm::json::Value toJSON(const TextContent &TC) {171return llvm::json::Object{{"type", "text"}, {"text", TC.text}};172}173174bool fromJSON(const llvm::json::Value &V, TextContent &TC, llvm::json::Path P) {175llvm::json::ObjectMapper O(V, P);176return O && O.map("text", TC.text);177}178179llvm::json::Value toJSON(const TextResult &TR) {180return llvm::json::Object{{"content", TR.content}, {"isError", TR.isError}};181}182183bool fromJSON(const llvm::json::Value &V, TextResult &TR, llvm::json::Path P) {184llvm::json::ObjectMapper O(V, P);185return O && O.map("content", TR.content) && O.map("isError", TR.isError);186}187188llvm::json::Value toJSON(const ToolDefinition &TD) {189llvm::json::Object Result{{"name", TD.name}};190if (!TD.description.empty())191Result.insert({"description", TD.description});192if (TD.inputSchema)193Result.insert({"inputSchema", TD.inputSchema});194return Result;195}196197bool fromJSON(const llvm::json::Value &V, ToolDefinition &TD,198llvm::json::Path P) {199200llvm::json::ObjectMapper O(V, P);201if (!O || !O.map("name", TD.name) ||202!O.mapOptional("description", TD.description))203return false;204return mapRaw(V, "inputSchema", TD.inputSchema, P);205}206207llvm::json::Value toJSON(const Message &M) {208return std::visit([](auto &M) { return toJSON(M); }, M);209}210211bool fromJSON(const llvm::json::Value &V, Message &M, llvm::json::Path P) {212const auto *O = V.getAsObject();213if (!O) {214P.report("expected object");215return false;216}217218if (const json::Value *V = O->get("jsonrpc")) {219if (V->getAsString().value_or("") != "2.0") {220P.report("unsupported JSON RPC version");221return false;222}223} else {224P.report("not a valid JSON RPC message");225return false;226}227228// A message without an ID is a Notification.229if (!O->get("id")) {230protocol::Notification N;231if (!fromJSON(V, N, P))232return false;233M = std::move(N);234return true;235}236237if (O->get("error")) {238protocol::Error E;239if (!fromJSON(V, E, P))240return false;241M = std::move(E);242return true;243}244245if (O->get("result")) {246protocol::Response R;247if (!fromJSON(V, R, P))248return false;249M = std::move(R);250return true;251}252253if (O->get("method")) {254protocol::Request R;255if (!fromJSON(V, R, P))256return false;257M = std::move(R);258return true;259}260261P.report("unrecognized message type");262return false;263}264265} // namespace lldb_private::mcp::protocol266267268