7
7
// ===----------------------------------------------------------------------===//
8
8
9
9
#include " Tool.h"
10
+ #include " lldb/Core/Debugger.h"
10
11
#include " lldb/Interpreter/CommandInterpreter.h"
11
12
#include " lldb/Interpreter/CommandReturnObject.h"
12
13
#include " lldb/Protocol/MCP/Protocol.h"
14
+ #include " lldb/Utility/UriParser.h"
15
+ #include " llvm/ADT/StringRef.h"
16
+ #include " llvm/Support/Error.h"
17
+ #include < cstdint>
18
+ #include < optional>
13
19
14
20
using namespace lldb_private ;
15
21
using namespace lldb_protocol ;
16
22
using namespace lldb_private ::mcp;
23
+ using namespace lldb ;
17
24
using namespace llvm ;
18
25
19
26
namespace {
27
+
28
+ static constexpr StringLiteral kSchemeAndHost = " lldb-mcp://debugger/" ;
29
+
20
30
struct CommandToolArguments {
21
- uint64_t debugger_id;
22
- std::string arguments;
31
+ // / Either an id like '1' or a uri like 'lldb-mcp://debugger/1'.
32
+ std::string debugger;
33
+ std::string command;
23
34
};
24
35
25
- bool fromJSON (const llvm::json::Value &V, CommandToolArguments &A,
26
- llvm::json::Path P) {
27
- llvm::json::ObjectMapper O (V, P);
28
- return O && O.map (" debugger_id" , A.debugger_id ) &&
29
- O.mapOptional (" arguments" , A.arguments );
36
+ bool fromJSON (const json::Value &V, CommandToolArguments &A, json::Path P) {
37
+ json::ObjectMapper O (V, P);
38
+ return O && O.mapOptional (" debugger" , A.debugger ) &&
39
+ O.mapOptional (" command" , A.command );
30
40
}
31
41
32
42
// / Helper function to create a CallToolResult from a string output.
@@ -39,9 +49,13 @@ createTextResult(std::string output, bool is_error = false) {
39
49
return text_result;
40
50
}
41
51
52
+ std::string to_uri (DebuggerSP debugger) {
53
+ return (kSchemeAndHost + std::to_string (debugger->GetID ())).str ();
54
+ }
55
+
42
56
} // namespace
43
57
44
- llvm:: Expected<lldb_protocol::mcp::CallToolResult>
58
+ Expected<lldb_protocol::mcp::CallToolResult>
45
59
CommandTool::Call (const lldb_protocol::mcp::ToolArguments &args) {
46
60
if (!std::holds_alternative<json::Value>(args))
47
61
return createStringError (" CommandTool requires arguments" );
@@ -52,19 +66,35 @@ CommandTool::Call(const lldb_protocol::mcp::ToolArguments &args) {
52
66
if (!fromJSON (std::get<json::Value>(args), arguments, root))
53
67
return root.getError ();
54
68
55
- lldb::DebuggerSP debugger_sp =
56
- Debugger::FindDebuggerWithID (arguments.debugger_id );
69
+ lldb::DebuggerSP debugger_sp;
70
+
71
+ if (!arguments.debugger .empty ()) {
72
+ llvm::StringRef debugger_specifier = arguments.debugger ;
73
+ debugger_specifier.consume_front (kSchemeAndHost );
74
+ uint32_t debugger_id = 0 ;
75
+ if (debugger_specifier.consumeInteger (10 , debugger_id))
76
+ return createStringError (
77
+ formatv (" malformed debugger specifier {0}" , arguments.debugger ));
78
+
79
+ debugger_sp = Debugger::FindDebuggerWithID (debugger_id);
80
+ } else {
81
+ for (size_t i = 0 ; i < Debugger::GetNumDebuggers (); i++) {
82
+ debugger_sp = Debugger::GetDebuggerAtIndex (i);
83
+ if (debugger_sp)
84
+ break ;
85
+ }
86
+ }
87
+
57
88
if (!debugger_sp)
58
- return createStringError (
59
- llvm::formatv (" no debugger with id {0}" , arguments.debugger_id ));
89
+ return createStringError (" no debugger found" );
60
90
61
91
// FIXME: Disallow certain commands and their aliases.
62
92
CommandReturnObject result (/* colors=*/ false );
63
- debugger_sp->GetCommandInterpreter ().HandleCommand (
64
- arguments. arguments . c_str (), eLazyBoolYes, result);
93
+ debugger_sp->GetCommandInterpreter ().HandleCommand (arguments. command . c_str (),
94
+ eLazyBoolYes, result);
65
95
66
96
std::string output;
67
- llvm:: StringRef output_str = result.GetOutputString ();
97
+ StringRef output_str = result.GetOutputString ();
68
98
if (!output_str.empty ())
69
99
output += output_str.str ();
70
100
@@ -78,14 +108,42 @@ CommandTool::Call(const lldb_protocol::mcp::ToolArguments &args) {
78
108
return createTextResult (output, !result.Succeeded ());
79
109
}
80
110
81
- std::optional<llvm::json::Value> CommandTool::GetSchema () const {
82
- llvm::json::Object id_type{{" type" , " number" }};
83
- llvm::json::Object str_type{{" type" , " string" }};
84
- llvm::json::Object properties{{" debugger_id" , std::move (id_type)},
85
- {" arguments" , std::move (str_type)}};
86
- llvm::json::Array required{" debugger_id" };
87
- llvm::json::Object schema{{" type" , " object" },
88
- {" properties" , std::move (properties)},
89
- {" required" , std::move (required)}};
111
+ std::optional<json::Value> CommandTool::GetSchema () const {
112
+ using namespace llvm ::json;
113
+ Object properties{
114
+ {" debugger" ,
115
+ Object{{" type" , " string" },
116
+ {" description" ,
117
+ " The debugger ID or URI to a specific debug session. If not "
118
+ " specified, the first debugger will be used." }}},
119
+ {" command" ,
120
+ Object{{" type" , " string" }, {" description" , " An lldb command to run." }}}};
121
+ Object schema{{" type" , " object" }, {" properties" , std::move (properties)}};
90
122
return schema;
91
123
}
124
+
125
+ Expected<lldb_protocol::mcp::CallToolResult>
126
+ DebuggerListTool::Call (const lldb_protocol::mcp::ToolArguments &args) {
127
+ llvm::json::Path::Root root;
128
+
129
+ // Return a nested Markdown list with debuggers and target.
130
+ // Example output:
131
+ //
132
+ // - lldb-mcp://debugger/1
133
+ // - lldb-mcp://debugger/2
134
+ //
135
+ // FIXME: Use Structured Content when we adopt protocol version 2025-06-18.
136
+ std::string output;
137
+ llvm::raw_string_ostream os (output);
138
+
139
+ const size_t num_debuggers = Debugger::GetNumDebuggers ();
140
+ for (size_t i = 0 ; i < num_debuggers; ++i) {
141
+ lldb::DebuggerSP debugger_sp = Debugger::GetDebuggerAtIndex (i);
142
+ if (!debugger_sp)
143
+ continue ;
144
+
145
+ os << " - " << to_uri (debugger_sp) << ' \n ' ;
146
+ }
147
+
148
+ return createTextResult (output);
149
+ }
0 commit comments