LLDB mainline
Protocol.cpp
Go to the documentation of this file.
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
10#include "lldb/Version/Version.inc"
11#include "llvm/Support/ErrorHandling.h"
12#include "llvm/Support/JSON.h"
13
14using namespace llvm;
15
16namespace lldb_protocol::mcp {
17
18llvm::StringLiteral GetServerVersion() { return LLDB_VERSION_STRING; }
19
20static bool mapRaw(const json::Value &Params, StringLiteral Prop,
21 std::optional<json::Value> &V, json::Path P) {
22 const auto *O = Params.getAsObject();
23 if (!O) {
24 P.report("expected object");
25 return false;
26 }
27 const json::Value *E = O->get(Prop);
28 if (E)
29 V = std::move(*E);
30 return true;
31}
32
33static llvm::json::Value toJSON(const Id &Id) {
34 if (const int64_t *I = std::get_if<int64_t>(&Id))
35 return json::Value(*I);
36 if (const std::string *S = std::get_if<std::string>(&Id))
37 return json::Value(*S);
38 llvm_unreachable("unexpected type in protocol::Id");
39}
40
41static bool mapId(const llvm::json::Value &V, StringLiteral Prop, Id &Id,
42 llvm::json::Path P) {
43 const auto *O = V.getAsObject();
44 if (!O) {
45 P.report("expected object");
46 return false;
47 }
48
49 const auto *E = O->get(Prop);
50 if (!E) {
51 P.field(Prop).report("not found");
52 return false;
53 }
54
55 if (auto S = E->getAsString()) {
56 Id = S->str();
57 return true;
58 }
59
60 if (auto I = E->getAsInteger()) {
61 Id = *I;
62 return true;
63 }
64
65 P.report("expected string or number");
66 return false;
67}
68
69llvm::json::Value toJSON(const Request &R) {
70 json::Object Result{
71 {"jsonrpc", "2.0"}, {"id", toJSON(R.id)}, {"method", R.method}};
72 if (R.params)
73 Result.insert({"params", R.params});
74 return Result;
75}
76
77bool fromJSON(const llvm::json::Value &V, Request &R, llvm::json::Path P) {
78 llvm::json::ObjectMapper O(V, P);
79 return O && mapId(V, "id", R.id, P) && O.map("method", R.method) &&
80 mapRaw(V, "params", R.params, P);
81}
82
83bool operator==(const Request &a, const Request &b) {
84 return a.id == b.id && a.method == b.method && a.params == b.params;
85}
86
87llvm::json::Value toJSON(const Error &E) {
88 llvm::json::Object Result{{"code", E.code}, {"message", E.message}};
89 if (E.data)
90 Result.insert({"data", *E.data});
91 return Result;
92}
93
94bool fromJSON(const llvm::json::Value &V, Error &E, llvm::json::Path P) {
95 llvm::json::ObjectMapper O(V, P);
96 return O && O.map("code", E.code) && O.map("message", E.message) &&
97 mapRaw(V, "data", E.data, P);
98}
99
100bool operator==(const Error &a, const Error &b) {
101 return a.code == b.code && a.message == b.message && a.data == b.data;
102}
103
104llvm::json::Value toJSON(const Response &R) {
105 llvm::json::Object Result{{"jsonrpc", "2.0"}, {"id", toJSON(R.id)}};
106
107 if (const Error *error = std::get_if<Error>(&R.result))
108 Result.insert({"error", *error});
109 if (const json::Value *result = std::get_if<json::Value>(&R.result))
110 Result.insert({"result", *result});
111 return Result;
112}
113
114bool fromJSON(const llvm::json::Value &V, Response &R, llvm::json::Path P) {
115 const json::Object *E = V.getAsObject();
116 if (!E) {
117 P.report("expected object");
118 return false;
119 }
120
121 const json::Value *result = E->get("result");
122 const json::Value *raw_error = E->get("error");
123
124 if (result && raw_error) {
125 P.report("'result' and 'error' fields are mutually exclusive");
126 return false;
127 }
128
129 if (!result && !raw_error) {
130 P.report("'result' or 'error' fields are required'");
131 return false;
132 }
133
134 if (result) {
135 R.result = std::move(*result);
136 } else {
137 Error error;
138 if (!fromJSON(*raw_error, error, P))
139 return false;
140 R.result = std::move(error);
141 }
142
143 return mapId(V, "id", R.id, P);
144}
145
146bool operator==(const Response &a, const Response &b) {
147 return a.id == b.id && a.result == b.result;
148}
149
150llvm::json::Value toJSON(const Notification &N) {
151 llvm::json::Object Result{{"jsonrpc", "2.0"}, {"method", N.method}};
152 if (N.params)
153 Result.insert({"params", N.params});
154 return Result;
155}
156
157bool fromJSON(const llvm::json::Value &V, Notification &N, llvm::json::Path P) {
158 llvm::json::ObjectMapper O(V, P);
159 if (!O || !O.map("method", N.method))
160 return false;
161 auto *Obj = V.getAsObject();
162 if (!Obj)
163 return false;
164 if (auto *Params = Obj->get("params"))
165 N.params = *Params;
166 return true;
167}
168
169bool operator==(const Notification &a, const Notification &b) {
170 return a.method == b.method && a.params == b.params;
171}
172
173bool fromJSON(const llvm::json::Value &V, Resource &R, llvm::json::Path P) {
174 llvm::json::ObjectMapper O(V, P);
175 return O && O.map("uri", R.uri) && O.map("name", R.name) &&
176 O.mapOptional("description", R.description) &&
177 O.mapOptional("mimeType", R.mimeType);
178}
179
180llvm::json::Value toJSON(const Resource &R) {
181 llvm::json::Object Result{{"uri", R.uri}, {"name", R.name}};
182 if (!R.description.empty())
183 Result.insert({"description", R.description});
184 if (!R.mimeType.empty())
185 Result.insert({"mimeType", R.mimeType});
186 return Result;
187}
188
189llvm::json::Value toJSON(const TextResourceContents &RC) {
190 llvm::json::Object Result{{"uri", RC.uri}, {"text", RC.text}};
191 if (!RC.mimeType.empty())
192 Result.insert({"mimeType", RC.mimeType});
193 return Result;
194}
195
196bool fromJSON(const llvm::json::Value &V, TextResourceContents &RC,
197 llvm::json::Path P) {
198 llvm::json::ObjectMapper O(V, P);
199 return O && O.map("uri", RC.uri) && O.map("text", RC.text) &&
200 O.mapOptional("mimeType", RC.mimeType);
201}
202
203llvm::json::Value toJSON(const ReadResourceResult &RR) {
204 return llvm::json::Object{{"contents", RR.contents}};
205}
206
207bool fromJSON(const llvm::json::Value &V, ReadResourceResult &RR,
208 llvm::json::Path P) {
209 llvm::json::ObjectMapper O(V, P);
210 return O && O.map("contents", RR.contents);
211}
212
213llvm::json::Value toJSON(const TextContent &TC) {
214 return llvm::json::Object{{"type", "text"}, {"text", TC.text}};
215}
216
217bool fromJSON(const llvm::json::Value &V, TextContent &TC, llvm::json::Path P) {
218 llvm::json::ObjectMapper O(V, P);
219 return O && O.map("text", TC.text);
220}
221
222llvm::json::Value toJSON(const ToolDefinition &TD) {
223 llvm::json::Object Result{{"name", TD.name}};
224 if (!TD.description.empty())
225 Result.insert({"description", TD.description});
226 if (TD.inputSchema)
227 Result.insert({"inputSchema", TD.inputSchema});
228 return Result;
229}
230
231bool fromJSON(const llvm::json::Value &V, ToolDefinition &TD,
232 llvm::json::Path P) {
233
234 llvm::json::ObjectMapper O(V, P);
235 if (!O || !O.map("name", TD.name) ||
236 !O.mapOptional("description", TD.description))
237 return false;
238 return mapRaw(V, "inputSchema", TD.inputSchema, P);
239}
240
241llvm::json::Value toJSON(const Message &M) {
242 return std::visit([](auto &M) { return toJSON(M); }, M);
243}
244
245bool fromJSON(const llvm::json::Value &V, Message &M, llvm::json::Path P) {
246 const auto *O = V.getAsObject();
247 if (!O) {
248 P.report("expected object");
249 return false;
250 }
251
252 if (const json::Value *V = O->get("jsonrpc")) {
253 if (V->getAsString().value_or("") != "2.0") {
254 P.report("unsupported JSON RPC version");
255 return false;
256 }
257 } else {
258 P.report("not a valid JSON RPC message");
259 return false;
260 }
261
262 // A message without an ID is a Notification.
263 if (!O->get("id")) {
264 Notification N;
265 if (!fromJSON(V, N, P))
266 return false;
267 M = std::move(N);
268 return true;
269 }
270
271 if (O->get("method")) {
272 Request R;
273 if (!fromJSON(V, R, P))
274 return false;
275 M = std::move(R);
276 return true;
277 }
278
279 if (O->get("result") || O->get("error")) {
280 Response R;
281 if (!fromJSON(V, R, P))
282 return false;
283 M = std::move(R);
284 return true;
285 }
286
287 P.report("unrecognized message type");
288 return false;
289}
290
291json::Value toJSON(const Implementation &I) {
292 json::Object result{{"name", I.name}, {"version", I.version}};
293
294 if (!I.title.empty())
295 result.insert({"title", I.title});
296
297 return result;
298}
299
300bool fromJSON(const json::Value &V, Implementation &I, json::Path P) {
301 json::ObjectMapper O(V, P);
302 return O && O.map("name", I.name) && O.mapOptional("title", I.title) &&
303 O.mapOptional("version", I.version);
304}
305
306json::Value toJSON(const ClientCapabilities &C) { return json::Object{}; }
307
308bool fromJSON(const json::Value &, ClientCapabilities &, json::Path) {
309 return true;
310}
311
312json::Value toJSON(const ServerCapabilities &C) {
313 json::Object result{};
314
315 if (C.supportsToolsList)
316 result.insert({"tools", json::Object{{"listChanged", true}}});
317
319 json::Object resources;
321 resources.insert({"listChanged", true});
323 resources.insert({"subscribe", true});
324 result.insert({"resources", std::move(resources)});
325 }
326
328 result.insert({"completions", json::Object{}});
329
330 if (C.supportsLogging)
331 result.insert({"logging", json::Object{}});
332
333 return result;
334}
335
336bool fromJSON(const json::Value &V, ServerCapabilities &C, json::Path P) {
337 const json::Object *O = V.getAsObject();
338 if (!O) {
339 P.report("expected object");
340 return false;
341 }
342
343 if (O->find("tools") != O->end())
344 C.supportsToolsList = true;
345
346 if (const json::Object *resources = O->getObject("resources")) {
348 resources->getBoolean("listChanged").value_or(false);
350 resources->getBoolean("subscribe").value_or(false);
351 }
352
353 if (O->find("completions") != O->end())
354 C.supportsCompletions = true;
355
356 if (O->find("logging") != O->end())
357 C.supportsLogging = true;
358
359 return true;
360}
361
362json::Value toJSON(const InitializeParams &P) {
363 return json::Object{
364 {"protocolVersion", P.protocolVersion},
365 {"capabilities", P.capabilities},
366 {"clientInfo", P.clientInfo},
367 };
368}
369
370bool fromJSON(const json::Value &V, InitializeParams &I, json::Path P) {
371 json::ObjectMapper O(V, P);
372 return O && O.map("protocolVersion", I.protocolVersion) &&
373 O.map("capabilities", I.capabilities) &&
374 O.map("clientInfo", I.clientInfo);
375}
376
377json::Value toJSON(const InitializeResult &R) {
378 json::Object result{{"protocolVersion", R.protocolVersion},
379 {"capabilities", R.capabilities},
380 {"serverInfo", R.serverInfo}};
381
382 if (!R.instructions.empty())
383 result.insert({"instructions", R.instructions});
384
385 return result;
386}
387
388bool fromJSON(const json::Value &V, InitializeResult &R, json::Path P) {
389 json::ObjectMapper O(V, P);
390 return O && O.map("protocolVersion", R.protocolVersion) &&
391 O.map("capabilities", R.capabilities) &&
392 O.map("serverInfo", R.serverInfo) &&
393 O.mapOptional("instructions", R.instructions);
394}
395
396json::Value toJSON(const ListToolsResult &R) {
397 return json::Object{{"tools", R.tools}};
398}
399
400bool fromJSON(const json::Value &V, ListToolsResult &R, json::Path P) {
401 json::ObjectMapper O(V, P);
402 return O && O.map("tools", R.tools);
403}
404
405json::Value toJSON(const CallToolResult &R) {
406 json::Object result{{"content", R.content}};
407
408 if (R.isError)
409 result.insert({"isError", R.isError});
410 if (R.structuredContent)
411 result.insert({"structuredContent", *R.structuredContent});
412
413 return result;
414}
415
416bool fromJSON(const json::Value &V, CallToolResult &R, json::Path P) {
417 json::ObjectMapper O(V, P);
418 return O && O.map("content", R.content) &&
419 O.mapOptional("isError", R.isError) &&
420 mapRaw(V, "structuredContent", R.structuredContent, P);
421}
422
423json::Value toJSON(const CallToolParams &R) {
424 json::Object result{{"name", R.name}};
425
426 if (R.arguments)
427 result.insert({"arguments", *R.arguments});
428
429 return result;
430}
431
432bool fromJSON(const json::Value &V, CallToolParams &R, json::Path P) {
433 json::ObjectMapper O(V, P);
434 return O && O.map("name", R.name) && mapRaw(V, "arguments", R.arguments, P);
435}
436
437json::Value toJSON(const ReadResourceParams &R) {
438 return json::Object{{"uri", R.uri}};
439}
440
441bool fromJSON(const json::Value &V, ReadResourceParams &R, json::Path P) {
442 json::ObjectMapper O(V, P);
443 return O && O.map("uri", R.uri);
444}
445
446json::Value toJSON(const ListResourcesResult &R) {
447 return json::Object{{"resources", R.resources}};
448}
449
450bool fromJSON(const json::Value &V, ListResourcesResult &R, json::Path P) {
451 json::ObjectMapper O(V, P);
452 return O && O.map("resources", R.resources);
453}
454
455json::Value toJSON(const Void &R) { return json::Object{}; }
456
457bool fromJSON(const json::Value &V, Void &R, json::Path P) { return true; }
458
459} // namespace lldb_protocol::mcp
static llvm::raw_ostream & error(Stream &strm)
std::variant< Request, Response, Notification > Message
A general message as defined by the JSON-RPC 2.0 spec.
Definition Protocol.h:104
std::monostate Void
Special case parameter or result that has no value.
Definition Protocol.h:280
std::variant< int64_t, std::string > Id
A Request or Response 'id'.
Definition Protocol.h:36
llvm::json::Value toJSON(const Request &)
Definition Protocol.cpp:69
llvm::StringLiteral GetServerVersion()
Version reported by LLDB's MCP server.
Definition Protocol.cpp:18
bool operator==(const Request &, const Request &)
Definition Protocol.cpp:83
static bool mapRaw(const json::Value &Params, StringLiteral Prop, std::optional< json::Value > &V, json::Path P)
Definition Protocol.cpp:20
static bool mapId(const llvm::json::Value &V, StringLiteral Prop, Id &Id, llvm::json::Path P)
Definition Protocol.cpp:41
bool fromJSON(const llvm::json::Value &, Request &, llvm::json::Path)
Definition Protocol.cpp:77
Used by the client to invoke a tool provided by the server.
Definition Protocol.h:296
std::optional< llvm::json::Value > arguments
Definition Protocol.h:298
The server’s response to a tool call.
Definition Protocol.h:304
std::vector< ContentBlock > content
A list of content objects that represent the unstructured result of the tool call.
Definition Protocol.h:307
std::optional< llvm::json::Value > structuredContent
An optional JSON object that represents the structured result of the tool call.
Definition Protocol.h:325
bool isError
Whether the tool call ended in an error.
Definition Protocol.h:321
Capabilities a client may support.
Definition Protocol.h:221
std::string message
A short description of the error.
Definition Protocol.h:70
std::optional< llvm::json::Value > data
Additional information about the error.
Definition Protocol.h:74
int64_t code
The error type that occurred.
Definition Protocol.h:67
Describes the name and version of an MCP implementation, with an optional title for UI representation...
Definition Protocol.h:199
std::string title
Intended for UI and end-user contexts — optimized to be human-readable and easily understood,...
Definition Protocol.h:213
std::string name
Intended for programmatic or logical use, but used as a display name in past specs or fallback (if ti...
Definition Protocol.h:202
std::string protocolVersion
The latest version of the Model Context Protocol that the client supports.
Definition Protocol.h:249
After receiving an initialize request from the client, the server sends this response.
Definition Protocol.h:260
std::string protocolVersion
The version of the Model Context Protocol that the server wants to use.
Definition Protocol.h:264
std::string instructions
Instructions describing how to use the server and its features.
Definition Protocol.h:274
The server’s response to a resources/list request from the client.
Definition Protocol.h:131
std::vector< Resource > resources
Definition Protocol.h:132
The server's response to a tools/list request from the client.
Definition Protocol.h:285
std::vector< ToolDefinition > tools
Definition Protocol.h:286
A notification which does not expect a response.
Definition Protocol.h:93
std::optional< llvm::json::Value > params
The notification's params.
Definition Protocol.h:97
std::string method
The method to be invoked.
Definition Protocol.h:95
Sent from the client to the server, to read a specific resource URI.
Definition Protocol.h:156
std::string uri
The URI of the resource to read.
Definition Protocol.h:159
The server's response to a resources/read request from the client.
Definition Protocol.h:166
std::vector< TextResourceContents > contents
Definition Protocol.h:167
A request that expects a response.
Definition Protocol.h:39
std::optional< llvm::json::Value > params
The method's params.
Definition Protocol.h:45
std::string method
The method to be invoked.
Definition Protocol.h:43
Id id
The request id.
Definition Protocol.h:41
A known resource that the server is capable of reading.
Definition Protocol.h:113
std::string description
A description of what this resource represents.
Definition Protocol.h:121
std::string uri
The URI of this resource.
Definition Protocol.h:115
std::string mimeType
The MIME type of this resource, if known.
Definition Protocol.h:124
std::string name
A human-readable name for this resource.
Definition Protocol.h:118
A response to a request, either an error or a result.
Definition Protocol.h:81
Id id
The request id.
Definition Protocol.h:83
std::variant< Error, llvm::json::Value > result
The result of the request, either an Error or the JSON value of the response.
Definition Protocol.h:86
Capabilities that a server may support.
Definition Protocol.h:229
Text provided to or from an LLM.
Definition Protocol.h:174
std::string text
The text content of the message.
Definition Protocol.h:176
The contents of a specific resource or sub-resource.
Definition Protocol.h:139
std::string text
The text of the item.
Definition Protocol.h:145
std::string uri
The URI of this resource.
Definition Protocol.h:141
std::string mimeType
The MIME type of this resource, if known.
Definition Protocol.h:148
Definition for a tool the client can call.
Definition Protocol.h:182
std::string description
Human-readable description.
Definition Protocol.h:187
std::string name
Unique identifier for the tool.
Definition Protocol.h:184
std::optional< llvm::json::Value > inputSchema
Definition Protocol.h:190