xref: /freebsd/contrib/llvm-project/lldb/source/Plugins/Protocol/MCP/Protocol.cpp (revision 700637cbb5e582861067a11aaca4d053546871d2)
1*700637cbSDimitry Andric //===- Protocol.cpp -------------------------------------------------------===//
2*700637cbSDimitry Andric //
3*700637cbSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4*700637cbSDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
5*700637cbSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6*700637cbSDimitry Andric //
7*700637cbSDimitry Andric //===----------------------------------------------------------------------===//
8*700637cbSDimitry Andric 
9*700637cbSDimitry Andric #include "Protocol.h"
10*700637cbSDimitry Andric #include "llvm/Support/JSON.h"
11*700637cbSDimitry Andric 
12*700637cbSDimitry Andric using namespace llvm;
13*700637cbSDimitry Andric 
14*700637cbSDimitry Andric namespace lldb_private::mcp::protocol {
15*700637cbSDimitry Andric 
mapRaw(const json::Value & Params,StringLiteral Prop,std::optional<json::Value> & V,json::Path P)16*700637cbSDimitry Andric static bool mapRaw(const json::Value &Params, StringLiteral Prop,
17*700637cbSDimitry Andric                    std::optional<json::Value> &V, json::Path P) {
18*700637cbSDimitry Andric   const auto *O = Params.getAsObject();
19*700637cbSDimitry Andric   if (!O) {
20*700637cbSDimitry Andric     P.report("expected object");
21*700637cbSDimitry Andric     return false;
22*700637cbSDimitry Andric   }
23*700637cbSDimitry Andric   const json::Value *E = O->get(Prop);
24*700637cbSDimitry Andric   if (E)
25*700637cbSDimitry Andric     V = std::move(*E);
26*700637cbSDimitry Andric   return true;
27*700637cbSDimitry Andric }
28*700637cbSDimitry Andric 
toJSON(const Request & R)29*700637cbSDimitry Andric llvm::json::Value toJSON(const Request &R) {
30*700637cbSDimitry Andric   json::Object Result{{"jsonrpc", "2.0"}, {"id", R.id}, {"method", R.method}};
31*700637cbSDimitry Andric   if (R.params)
32*700637cbSDimitry Andric     Result.insert({"params", R.params});
33*700637cbSDimitry Andric   return Result;
34*700637cbSDimitry Andric }
35*700637cbSDimitry Andric 
fromJSON(const llvm::json::Value & V,Request & R,llvm::json::Path P)36*700637cbSDimitry Andric bool fromJSON(const llvm::json::Value &V, Request &R, llvm::json::Path P) {
37*700637cbSDimitry Andric   llvm::json::ObjectMapper O(V, P);
38*700637cbSDimitry Andric   if (!O || !O.map("id", R.id) || !O.map("method", R.method))
39*700637cbSDimitry Andric     return false;
40*700637cbSDimitry Andric   return mapRaw(V, "params", R.params, P);
41*700637cbSDimitry Andric }
42*700637cbSDimitry Andric 
toJSON(const ErrorInfo & EI)43*700637cbSDimitry Andric llvm::json::Value toJSON(const ErrorInfo &EI) {
44*700637cbSDimitry Andric   llvm::json::Object Result{{"code", EI.code}, {"message", EI.message}};
45*700637cbSDimitry Andric   if (!EI.data.empty())
46*700637cbSDimitry Andric     Result.insert({"data", EI.data});
47*700637cbSDimitry Andric   return Result;
48*700637cbSDimitry Andric }
49*700637cbSDimitry Andric 
fromJSON(const llvm::json::Value & V,ErrorInfo & EI,llvm::json::Path P)50*700637cbSDimitry Andric bool fromJSON(const llvm::json::Value &V, ErrorInfo &EI, llvm::json::Path P) {
51*700637cbSDimitry Andric   llvm::json::ObjectMapper O(V, P);
52*700637cbSDimitry Andric   return O && O.map("code", EI.code) && O.map("message", EI.message) &&
53*700637cbSDimitry Andric          O.mapOptional("data", EI.data);
54*700637cbSDimitry Andric }
55*700637cbSDimitry Andric 
toJSON(const Error & E)56*700637cbSDimitry Andric llvm::json::Value toJSON(const Error &E) {
57*700637cbSDimitry Andric   return json::Object{{"jsonrpc", "2.0"}, {"id", E.id}, {"error", E.error}};
58*700637cbSDimitry Andric }
59*700637cbSDimitry Andric 
fromJSON(const llvm::json::Value & V,Error & E,llvm::json::Path P)60*700637cbSDimitry Andric bool fromJSON(const llvm::json::Value &V, Error &E, llvm::json::Path P) {
61*700637cbSDimitry Andric   llvm::json::ObjectMapper O(V, P);
62*700637cbSDimitry Andric   return O && O.map("id", E.id) && O.map("error", E.error);
63*700637cbSDimitry Andric }
64*700637cbSDimitry Andric 
toJSON(const Response & R)65*700637cbSDimitry Andric llvm::json::Value toJSON(const Response &R) {
66*700637cbSDimitry Andric   llvm::json::Object Result{{"jsonrpc", "2.0"}, {"id", R.id}};
67*700637cbSDimitry Andric   if (R.result)
68*700637cbSDimitry Andric     Result.insert({"result", R.result});
69*700637cbSDimitry Andric   if (R.error)
70*700637cbSDimitry Andric     Result.insert({"error", R.error});
71*700637cbSDimitry Andric   return Result;
72*700637cbSDimitry Andric }
73*700637cbSDimitry Andric 
fromJSON(const llvm::json::Value & V,Response & R,llvm::json::Path P)74*700637cbSDimitry Andric bool fromJSON(const llvm::json::Value &V, Response &R, llvm::json::Path P) {
75*700637cbSDimitry Andric   llvm::json::ObjectMapper O(V, P);
76*700637cbSDimitry Andric   if (!O || !O.map("id", R.id) || !O.map("error", R.error))
77*700637cbSDimitry Andric     return false;
78*700637cbSDimitry Andric   return mapRaw(V, "result", R.result, P);
79*700637cbSDimitry Andric }
80*700637cbSDimitry Andric 
toJSON(const Notification & N)81*700637cbSDimitry Andric llvm::json::Value toJSON(const Notification &N) {
82*700637cbSDimitry Andric   llvm::json::Object Result{{"jsonrpc", "2.0"}, {"method", N.method}};
83*700637cbSDimitry Andric   if (N.params)
84*700637cbSDimitry Andric     Result.insert({"params", N.params});
85*700637cbSDimitry Andric   return Result;
86*700637cbSDimitry Andric }
87*700637cbSDimitry Andric 
fromJSON(const llvm::json::Value & V,Notification & N,llvm::json::Path P)88*700637cbSDimitry Andric bool fromJSON(const llvm::json::Value &V, Notification &N, llvm::json::Path P) {
89*700637cbSDimitry Andric   llvm::json::ObjectMapper O(V, P);
90*700637cbSDimitry Andric   if (!O || !O.map("method", N.method))
91*700637cbSDimitry Andric     return false;
92*700637cbSDimitry Andric   auto *Obj = V.getAsObject();
93*700637cbSDimitry Andric   if (!Obj)
94*700637cbSDimitry Andric     return false;
95*700637cbSDimitry Andric   if (auto *Params = Obj->get("params"))
96*700637cbSDimitry Andric     N.params = *Params;
97*700637cbSDimitry Andric   return true;
98*700637cbSDimitry Andric }
99*700637cbSDimitry Andric 
toJSON(const ToolCapability & TC)100*700637cbSDimitry Andric llvm::json::Value toJSON(const ToolCapability &TC) {
101*700637cbSDimitry Andric   return llvm::json::Object{{"listChanged", TC.listChanged}};
102*700637cbSDimitry Andric }
103*700637cbSDimitry Andric 
fromJSON(const llvm::json::Value & V,ToolCapability & TC,llvm::json::Path P)104*700637cbSDimitry Andric bool fromJSON(const llvm::json::Value &V, ToolCapability &TC,
105*700637cbSDimitry Andric               llvm::json::Path P) {
106*700637cbSDimitry Andric   llvm::json::ObjectMapper O(V, P);
107*700637cbSDimitry Andric   return O && O.map("listChanged", TC.listChanged);
108*700637cbSDimitry Andric }
109*700637cbSDimitry Andric 
toJSON(const ResourceCapability & RC)110*700637cbSDimitry Andric llvm::json::Value toJSON(const ResourceCapability &RC) {
111*700637cbSDimitry Andric   return llvm::json::Object{{"listChanged", RC.listChanged},
112*700637cbSDimitry Andric                             {"subscribe", RC.subscribe}};
113*700637cbSDimitry Andric }
114*700637cbSDimitry Andric 
fromJSON(const llvm::json::Value & V,ResourceCapability & RC,llvm::json::Path P)115*700637cbSDimitry Andric bool fromJSON(const llvm::json::Value &V, ResourceCapability &RC,
116*700637cbSDimitry Andric               llvm::json::Path P) {
117*700637cbSDimitry Andric   llvm::json::ObjectMapper O(V, P);
118*700637cbSDimitry Andric   return O && O.map("listChanged", RC.listChanged) &&
119*700637cbSDimitry Andric          O.map("subscribe", RC.subscribe);
120*700637cbSDimitry Andric }
121*700637cbSDimitry Andric 
toJSON(const Capabilities & C)122*700637cbSDimitry Andric llvm::json::Value toJSON(const Capabilities &C) {
123*700637cbSDimitry Andric   return llvm::json::Object{{"tools", C.tools}, {"resources", C.resources}};
124*700637cbSDimitry Andric }
125*700637cbSDimitry Andric 
fromJSON(const llvm::json::Value & V,Resource & R,llvm::json::Path P)126*700637cbSDimitry Andric bool fromJSON(const llvm::json::Value &V, Resource &R, llvm::json::Path P) {
127*700637cbSDimitry Andric   llvm::json::ObjectMapper O(V, P);
128*700637cbSDimitry Andric   return O && O.map("uri", R.uri) && O.map("name", R.name) &&
129*700637cbSDimitry Andric          O.mapOptional("description", R.description) &&
130*700637cbSDimitry Andric          O.mapOptional("mimeType", R.mimeType);
131*700637cbSDimitry Andric }
132*700637cbSDimitry Andric 
toJSON(const Resource & R)133*700637cbSDimitry Andric llvm::json::Value toJSON(const Resource &R) {
134*700637cbSDimitry Andric   llvm::json::Object Result{{"uri", R.uri}, {"name", R.name}};
135*700637cbSDimitry Andric   if (!R.description.empty())
136*700637cbSDimitry Andric     Result.insert({"description", R.description});
137*700637cbSDimitry Andric   if (!R.mimeType.empty())
138*700637cbSDimitry Andric     Result.insert({"mimeType", R.mimeType});
139*700637cbSDimitry Andric   return Result;
140*700637cbSDimitry Andric }
141*700637cbSDimitry Andric 
fromJSON(const llvm::json::Value & V,Capabilities & C,llvm::json::Path P)142*700637cbSDimitry Andric bool fromJSON(const llvm::json::Value &V, Capabilities &C, llvm::json::Path P) {
143*700637cbSDimitry Andric   llvm::json::ObjectMapper O(V, P);
144*700637cbSDimitry Andric   return O && O.map("tools", C.tools);
145*700637cbSDimitry Andric }
146*700637cbSDimitry Andric 
toJSON(const ResourceContents & RC)147*700637cbSDimitry Andric llvm::json::Value toJSON(const ResourceContents &RC) {
148*700637cbSDimitry Andric   llvm::json::Object Result{{"uri", RC.uri}, {"text", RC.text}};
149*700637cbSDimitry Andric   if (!RC.mimeType.empty())
150*700637cbSDimitry Andric     Result.insert({"mimeType", RC.mimeType});
151*700637cbSDimitry Andric   return Result;
152*700637cbSDimitry Andric }
153*700637cbSDimitry Andric 
fromJSON(const llvm::json::Value & V,ResourceContents & RC,llvm::json::Path P)154*700637cbSDimitry Andric bool fromJSON(const llvm::json::Value &V, ResourceContents &RC,
155*700637cbSDimitry Andric               llvm::json::Path P) {
156*700637cbSDimitry Andric   llvm::json::ObjectMapper O(V, P);
157*700637cbSDimitry Andric   return O && O.map("uri", RC.uri) && O.map("text", RC.text) &&
158*700637cbSDimitry Andric          O.mapOptional("mimeType", RC.mimeType);
159*700637cbSDimitry Andric }
160*700637cbSDimitry Andric 
toJSON(const ResourceResult & RR)161*700637cbSDimitry Andric llvm::json::Value toJSON(const ResourceResult &RR) {
162*700637cbSDimitry Andric   return llvm::json::Object{{"contents", RR.contents}};
163*700637cbSDimitry Andric }
164*700637cbSDimitry Andric 
fromJSON(const llvm::json::Value & V,ResourceResult & RR,llvm::json::Path P)165*700637cbSDimitry Andric bool fromJSON(const llvm::json::Value &V, ResourceResult &RR,
166*700637cbSDimitry Andric               llvm::json::Path P) {
167*700637cbSDimitry Andric   llvm::json::ObjectMapper O(V, P);
168*700637cbSDimitry Andric   return O && O.map("contents", RR.contents);
169*700637cbSDimitry Andric }
170*700637cbSDimitry Andric 
toJSON(const TextContent & TC)171*700637cbSDimitry Andric llvm::json::Value toJSON(const TextContent &TC) {
172*700637cbSDimitry Andric   return llvm::json::Object{{"type", "text"}, {"text", TC.text}};
173*700637cbSDimitry Andric }
174*700637cbSDimitry Andric 
fromJSON(const llvm::json::Value & V,TextContent & TC,llvm::json::Path P)175*700637cbSDimitry Andric bool fromJSON(const llvm::json::Value &V, TextContent &TC, llvm::json::Path P) {
176*700637cbSDimitry Andric   llvm::json::ObjectMapper O(V, P);
177*700637cbSDimitry Andric   return O && O.map("text", TC.text);
178*700637cbSDimitry Andric }
179*700637cbSDimitry Andric 
toJSON(const TextResult & TR)180*700637cbSDimitry Andric llvm::json::Value toJSON(const TextResult &TR) {
181*700637cbSDimitry Andric   return llvm::json::Object{{"content", TR.content}, {"isError", TR.isError}};
182*700637cbSDimitry Andric }
183*700637cbSDimitry Andric 
fromJSON(const llvm::json::Value & V,TextResult & TR,llvm::json::Path P)184*700637cbSDimitry Andric bool fromJSON(const llvm::json::Value &V, TextResult &TR, llvm::json::Path P) {
185*700637cbSDimitry Andric   llvm::json::ObjectMapper O(V, P);
186*700637cbSDimitry Andric   return O && O.map("content", TR.content) && O.map("isError", TR.isError);
187*700637cbSDimitry Andric }
188*700637cbSDimitry Andric 
toJSON(const ToolDefinition & TD)189*700637cbSDimitry Andric llvm::json::Value toJSON(const ToolDefinition &TD) {
190*700637cbSDimitry Andric   llvm::json::Object Result{{"name", TD.name}};
191*700637cbSDimitry Andric   if (!TD.description.empty())
192*700637cbSDimitry Andric     Result.insert({"description", TD.description});
193*700637cbSDimitry Andric   if (TD.inputSchema)
194*700637cbSDimitry Andric     Result.insert({"inputSchema", TD.inputSchema});
195*700637cbSDimitry Andric   return Result;
196*700637cbSDimitry Andric }
197*700637cbSDimitry Andric 
fromJSON(const llvm::json::Value & V,ToolDefinition & TD,llvm::json::Path P)198*700637cbSDimitry Andric bool fromJSON(const llvm::json::Value &V, ToolDefinition &TD,
199*700637cbSDimitry Andric               llvm::json::Path P) {
200*700637cbSDimitry Andric 
201*700637cbSDimitry Andric   llvm::json::ObjectMapper O(V, P);
202*700637cbSDimitry Andric   if (!O || !O.map("name", TD.name) ||
203*700637cbSDimitry Andric       !O.mapOptional("description", TD.description))
204*700637cbSDimitry Andric     return false;
205*700637cbSDimitry Andric   return mapRaw(V, "inputSchema", TD.inputSchema, P);
206*700637cbSDimitry Andric }
207*700637cbSDimitry Andric 
toJSON(const Message & M)208*700637cbSDimitry Andric llvm::json::Value toJSON(const Message &M) {
209*700637cbSDimitry Andric   return std::visit([](auto &M) { return toJSON(M); }, M);
210*700637cbSDimitry Andric }
211*700637cbSDimitry Andric 
fromJSON(const llvm::json::Value & V,Message & M,llvm::json::Path P)212*700637cbSDimitry Andric bool fromJSON(const llvm::json::Value &V, Message &M, llvm::json::Path P) {
213*700637cbSDimitry Andric   const auto *O = V.getAsObject();
214*700637cbSDimitry Andric   if (!O) {
215*700637cbSDimitry Andric     P.report("expected object");
216*700637cbSDimitry Andric     return false;
217*700637cbSDimitry Andric   }
218*700637cbSDimitry Andric 
219*700637cbSDimitry Andric   if (const json::Value *V = O->get("jsonrpc")) {
220*700637cbSDimitry Andric     if (V->getAsString().value_or("") != "2.0") {
221*700637cbSDimitry Andric       P.report("unsupported JSON RPC version");
222*700637cbSDimitry Andric       return false;
223*700637cbSDimitry Andric     }
224*700637cbSDimitry Andric   } else {
225*700637cbSDimitry Andric     P.report("not a valid JSON RPC message");
226*700637cbSDimitry Andric     return false;
227*700637cbSDimitry Andric   }
228*700637cbSDimitry Andric 
229*700637cbSDimitry Andric   // A message without an ID is a Notification.
230*700637cbSDimitry Andric   if (!O->get("id")) {
231*700637cbSDimitry Andric     protocol::Notification N;
232*700637cbSDimitry Andric     if (!fromJSON(V, N, P))
233*700637cbSDimitry Andric       return false;
234*700637cbSDimitry Andric     M = std::move(N);
235*700637cbSDimitry Andric     return true;
236*700637cbSDimitry Andric   }
237*700637cbSDimitry Andric 
238*700637cbSDimitry Andric   if (O->get("error")) {
239*700637cbSDimitry Andric     protocol::Error E;
240*700637cbSDimitry Andric     if (!fromJSON(V, E, P))
241*700637cbSDimitry Andric       return false;
242*700637cbSDimitry Andric     M = std::move(E);
243*700637cbSDimitry Andric     return true;
244*700637cbSDimitry Andric   }
245*700637cbSDimitry Andric 
246*700637cbSDimitry Andric   if (O->get("result")) {
247*700637cbSDimitry Andric     protocol::Response R;
248*700637cbSDimitry Andric     if (!fromJSON(V, R, P))
249*700637cbSDimitry Andric       return false;
250*700637cbSDimitry Andric     M = std::move(R);
251*700637cbSDimitry Andric     return true;
252*700637cbSDimitry Andric   }
253*700637cbSDimitry Andric 
254*700637cbSDimitry Andric   if (O->get("method")) {
255*700637cbSDimitry Andric     protocol::Request R;
256*700637cbSDimitry Andric     if (!fromJSON(V, R, P))
257*700637cbSDimitry Andric       return false;
258*700637cbSDimitry Andric     M = std::move(R);
259*700637cbSDimitry Andric     return true;
260*700637cbSDimitry Andric   }
261*700637cbSDimitry Andric 
262*700637cbSDimitry Andric   P.report("unrecognized message type");
263*700637cbSDimitry Andric   return false;
264*700637cbSDimitry Andric }
265*700637cbSDimitry Andric 
266*700637cbSDimitry Andric } // namespace lldb_private::mcp::protocol
267