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