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

source code of lldb/source/Plugins/Protocol/MCP/Protocol.cpp