1//===- Tool.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 "Tool.h"
10#include "lldb/Core/Module.h"
11#include "lldb/Interpreter/CommandInterpreter.h"
12#include "lldb/Interpreter/CommandReturnObject.h"
13
14using namespace lldb_private::mcp;
15using namespace llvm;
16
17namespace {
18struct CommandToolArguments {
19 uint64_t debugger_id;
20 std::string arguments;
21};
22
23bool fromJSON(const llvm::json::Value &V, CommandToolArguments &A,
24 llvm::json::Path P) {
25 llvm::json::ObjectMapper O(V, P);
26 return O && O.map(Prop: "debugger_id", Out&: A.debugger_id) &&
27 O.mapOptional(Prop: "arguments", Out&: A.arguments);
28}
29
30/// Helper function to create a TextResult from a string output.
31static lldb_private::mcp::protocol::TextResult
32createTextResult(std::string output, bool is_error = false) {
33 lldb_private::mcp::protocol::TextResult text_result;
34 text_result.content.emplace_back(
35 args: lldb_private::mcp::protocol::TextContent{.text: {std::move(output)}});
36 text_result.isError = is_error;
37 return text_result;
38}
39
40} // namespace
41
42Tool::Tool(std::string name, std::string description)
43 : m_name(std::move(name)), m_description(std::move(description)) {}
44
45protocol::ToolDefinition Tool::GetDefinition() const {
46 protocol::ToolDefinition definition;
47 definition.name = m_name;
48 definition.description = m_description;
49
50 if (std::optional<llvm::json::Value> input_schema = GetSchema())
51 definition.inputSchema = *input_schema;
52
53 return definition;
54}
55
56llvm::Expected<protocol::TextResult>
57CommandTool::Call(const protocol::ToolArguments &args) {
58 if (!std::holds_alternative<json::Value>(v: args))
59 return createStringError(Fmt: "CommandTool requires arguments");
60
61 json::Path::Root root;
62
63 CommandToolArguments arguments;
64 if (!fromJSON(V: std::get<json::Value>(v: args), A&: arguments, P: root))
65 return root.getError();
66
67 lldb::DebuggerSP debugger_sp =
68 Debugger::FindDebuggerWithID(id: arguments.debugger_id);
69 if (!debugger_sp)
70 return createStringError(
71 S: llvm::formatv(Fmt: "no debugger with id {0}", Vals&: arguments.debugger_id));
72
73 // FIXME: Disallow certain commands and their aliases.
74 CommandReturnObject result(/*colors=*/false);
75 debugger_sp->GetCommandInterpreter().HandleCommand(
76 command_line: arguments.arguments.c_str(), add_to_history: eLazyBoolYes, result);
77
78 std::string output;
79 llvm::StringRef output_str = result.GetOutputString();
80 if (!output_str.empty())
81 output += output_str.str();
82
83 std::string err_str = result.GetErrorString();
84 if (!err_str.empty()) {
85 if (!output.empty())
86 output += '\n';
87 output += err_str;
88 }
89
90 return createTextResult(output, is_error: !result.Succeeded());
91}
92
93std::optional<llvm::json::Value> CommandTool::GetSchema() const {
94 llvm::json::Object id_type{{.K: "type", .V: "number"}};
95 llvm::json::Object str_type{{.K: "type", .V: "string"}};
96 llvm::json::Object properties{{.K: "debugger_id", .V: std::move(id_type)},
97 {.K: "arguments", .V: std::move(str_type)}};
98 llvm::json::Array required{"debugger_id"};
99 llvm::json::Object schema{{.K: "type", .V: "object"},
100 {.K: "properties", .V: std::move(properties)},
101 {.K: "required", .V: std::move(required)}};
102 return schema;
103}
104

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