LLDB mainline
Server.cpp
Go to the documentation of this file.
1//===----------------------------------------------------------------------===//
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/Host/File.h"
12#include "lldb/Host/HostInfo.h"
16#include "llvm/ADT/SmallString.h"
17#include "llvm/Support/ErrorExtras.h"
18#include "llvm/Support/FileSystem.h"
19#include "llvm/Support/JSON.h"
20#include "llvm/Support/Signals.h"
21
22using namespace llvm;
23using namespace lldb_private;
24using namespace lldb_protocol::mcp;
25
26ServerInfoHandle::ServerInfoHandle(StringRef filename) : m_filename(filename) {
27 if (!m_filename.empty())
28 sys::RemoveFileOnSignal(m_filename);
29}
30
32
34 *this = std::move(other);
35}
36
39 m_filename = std::move(other.m_filename);
40 return *this;
41}
42
44 if (m_filename.empty())
45 return;
46
47 sys::fs::remove(m_filename);
48 sys::DontRemoveFileOnSignal(m_filename);
49 m_filename.clear();
50}
51
52json::Value lldb_protocol::mcp::toJSON(const ServerInfo &SM) {
53 return json::Object{{"connection_uri", SM.connection_uri}};
54}
55
56bool lldb_protocol::mcp::fromJSON(const json::Value &V, ServerInfo &SM,
57 json::Path P) {
58 json::ObjectMapper O(V, P);
59 return O && O.map("connection_uri", SM.connection_uri);
60}
61
62Expected<ServerInfoHandle> ServerInfo::Write(const ServerInfo &info) {
63 std::string buf = formatv("{0}", toJSON(info)).str();
64 size_t num_bytes = buf.size();
65
66 FileSpec user_lldb_dir = HostInfo::GetUserLLDBDir();
67
68 Status error(sys::fs::create_directory(user_lldb_dir.GetPath()));
69 if (error.Fail())
70 return error.takeError();
71
72 FileSpec mcp_registry_entry_path = user_lldb_dir.CopyByAppendingPathComponent(
73 formatv("lldb-mcp-{0}.json", getpid()).str());
74
78 Expected<lldb::FileUP> file =
79 FileSystem::Instance().Open(mcp_registry_entry_path, flags);
80 if (!file)
81 return file.takeError();
82 if (llvm::Error error = (*file)->Write(buf.data(), num_bytes).takeError())
83 return error;
84 return ServerInfoHandle{mcp_registry_entry_path.GetPath()};
85}
86
87Expected<std::vector<ServerInfo>> ServerInfo::Load() {
88 namespace path = llvm::sys::path;
89 FileSpec user_lldb_dir = HostInfo::GetUserLLDBDir();
91 std::error_code EC;
92 vfs::directory_iterator it = fs.DirBegin(user_lldb_dir, EC);
93 vfs::directory_iterator end;
94 std::vector<ServerInfo> infos;
95 for (; it != end && !EC; it.increment(EC)) {
96 auto &entry = *it;
97 auto path = entry.path();
98 auto name = path::filename(path);
99 if (!name.starts_with("lldb-mcp-") || !name.ends_with(".json"))
100 continue;
101
102 auto buffer = fs.CreateDataBuffer(path);
103 auto info = json::parse<ServerInfo>(toStringRef(buffer->GetData()));
104 if (!info)
105 return info.takeError();
106
107 infos.emplace_back(std::move(*info));
108 }
109
110 return infos;
111}
112
113Server::Server(std::string name, std::string version, LogCallback log_callback)
114 : m_name(std::move(name)), m_version(std::move(version)),
115 m_log_callback(std::move(log_callback)) {}
116
117void Server::AddTool(std::unique_ptr<Tool> tool) {
118 if (!tool)
119 return;
120 m_tools[tool->GetName()] = std::move(tool);
121}
122
124 std::unique_ptr<ResourceProvider> resource_provider) {
125 if (!resource_provider)
126 return;
127 m_resource_providers.push_back(std::move(resource_provider));
128}
129
131 MCPBinderUP binder_up = std::make_unique<MCPBinder>(transport);
132 binder_up->Bind<InitializeResult, InitializeParams>(
133 "initialize", &Server::InitializeHandler, this);
134 binder_up->Bind<ListToolsResult, void>("tools/list",
136 binder_up->Bind<CallToolResult, CallToolParams>(
137 "tools/call", &Server::ToolsCallHandler, this);
138 binder_up->Bind<ListResourcesResult, void>(
139 "resources/list", &Server::ResourcesListHandler, this);
140 binder_up->Bind<ReadResourceResult, ReadResourceParams>(
141 "resources/read", &Server::ResourcesReadHandler, this);
142 binder_up->Bind<void>("notifications/initialized",
143 [this]() { Log("MCP initialization complete"); });
144 return binder_up;
145}
146
148 MCPBinderUP binder = Bind(*transport);
149 MCPTransport *transport_ptr = transport.get();
150 binder->OnDisconnect([this, transport_ptr]() {
151 assert(m_instances.find(transport_ptr) != m_instances.end() &&
152 "Client not found in m_instances");
153 m_instances.erase(transport_ptr);
154 });
155 binder->OnError([this](llvm::Error err) {
156 Logv("Transport error: {0}", llvm::toString(std::move(err)));
157 });
158
159 auto handle = transport->RegisterMessageHandler(loop, *binder);
160 if (!handle)
161 return handle.takeError();
162
163 m_instances[transport_ptr] =
164 Client{std::move(*handle), std::move(transport), std::move(binder)};
165 return llvm::Error::success();
166}
167
168Expected<InitializeResult>
170 InitializeResult result;
172 result.capabilities = GetCapabilities();
173 result.serverInfo.name = m_name;
175 return result;
176}
177
178llvm::Expected<ListToolsResult> Server::ToolsListHandler() {
179 ListToolsResult result;
180 for (const auto &tool : m_tools)
181 result.tools.emplace_back(tool.second->GetDefinition());
182
183 return result;
184}
185
186llvm::Expected<CallToolResult>
188 llvm::StringRef tool_name = params.name;
189 if (tool_name.empty())
190 return llvm::createStringError("no tool name");
191
192 auto it = m_tools.find(tool_name);
193 if (it == m_tools.end())
194 return llvm::createStringErrorV("no tool \"{0}\"", tool_name);
195
196 ToolArguments tool_args;
197 if (params.arguments)
198 tool_args = *params.arguments;
199
200 llvm::Expected<CallToolResult> text_result = it->second->Call(tool_args);
201 if (!text_result)
202 return text_result.takeError();
203
204 return text_result;
205}
206
207llvm::Expected<ListResourcesResult> Server::ResourcesListHandler() {
208 ListResourcesResult result;
209 for (std::unique_ptr<ResourceProvider> &resource_provider_up :
211 for (const Resource &resource : resource_provider_up->GetResources())
212 result.resources.push_back(resource);
213
214 return result;
215}
216
217Expected<ReadResourceResult>
219 StringRef uri_str = params.uri;
220 if (uri_str.empty())
221 return createStringError("no resource uri");
222
223 for (std::unique_ptr<ResourceProvider> &resource_provider_up :
225 Expected<ReadResourceResult> result =
226 resource_provider_up->ReadResource(uri_str);
227 if (result.errorIsA<UnsupportedURI>()) {
228 consumeError(result.takeError());
229 continue;
230 }
231 if (!result)
232 return result.takeError();
233
234 return *result;
235 }
236
237 return make_error<MCPError>(
238 formatv("no resource handler for uri: {0}", uri_str).str(),
240}
241
244 capabilities.supportsToolsList = true;
245 capabilities.supportsResourcesList = true;
246 // FIXME: Support sending notifications when a debugger/target are
247 // added/removed.
248 capabilities.supportsResourcesSubscribe = false;
249 return capabilities;
250}
static llvm::raw_ostream & error(Stream &strm)
static llvm::Error createStringError(const char *format, Args &&...args)
Definition Resource.cpp:59
A file utility class.
Definition FileSpec.h:57
FileSpec CopyByAppendingPathComponent(llvm::StringRef component) const
Definition FileSpec.cpp:425
size_t GetPath(char *path, size_t max_path_length, bool denormalize=true) const
Extract the full path to the file.
Definition FileSpec.cpp:374
int Open(const char *path, int flags, int mode=0600)
Wraps open in a platform-independent way.
llvm::vfs::directory_iterator DirBegin(const FileSpec &file_spec, std::error_code &ec)
Get a directory iterator.
static FileSystem & Instance()
std::shared_ptr< DataBuffer > CreateDataBuffer(const llvm::Twine &path, uint64_t size=0, uint64_t offset=0)
Create memory buffer from path.
@ eOpenOptionWriteOnly
Definition File.h:52
@ eOpenOptionCanCreate
Definition File.h:56
@ eOpenOptionTruncate
Definition File.h:57
An error handling class.
Definition Status.h:118
static constexpr int64_t kResourceNotFound
Definition MCPError.h:28
A handle that tracks the server info on disk and cleans up the disk record once it is no longer refer...
Definition Server.h:100
llvm::SmallString< 128 > m_filename
Definition Server.h:118
void Remove()
Remove the file on disk, if one is tracked.
Definition Server.cpp:43
ServerInfoHandle & operator=(ServerInfoHandle &&other) noexcept
Definition Server.cpp:38
ServerInfoHandle(llvm::StringRef filename="")
const std::string m_version
Definition Server.h:69
const std::string m_name
Definition Server.h:68
void AddTool(std::unique_ptr< Tool > tool)
Definition Server.cpp:117
void AddResourceProvider(std::unique_ptr< ResourceProvider > resource_provider)
Definition Server.cpp:123
llvm::StringMap< std::unique_ptr< Tool > > m_tools
Definition Server.h:79
MCPBinderUP Bind(MCPTransport &)
Definition Server.cpp:130
std::vector< std::unique_ptr< ResourceProvider > > m_resource_providers
Definition Server.h:80
llvm::Expected< ReadResourceResult > ResourcesReadHandler(const ReadResourceParams &)
Definition Server.cpp:218
LogCallback m_log_callback
Definition Server.h:71
Server(std::string name, std::string version, LogCallback log_callback={})
Definition Server.cpp:113
llvm::Expected< ListToolsResult > ToolsListHandler()
Definition Server.cpp:178
void Log(llvm::StringRef message)
Definition Server.h:62
llvm::Expected< InitializeResult > InitializeHandler(const InitializeParams &)
Definition Server.cpp:169
llvm::Expected< CallToolResult > ToolsCallHandler(const CallToolParams &)
Definition Server.cpp:187
llvm::Error Accept(lldb_private::MainLoop &, MCPTransportUP)
Definition Server.cpp:147
std::unique_ptr< lldb_protocol::mcp::MCPTransport > MCPTransportUP
Definition Server.h:32
llvm::Expected< ListResourcesResult > ResourcesListHandler()
Definition Server.cpp:207
auto Logv(const char *Fmt, Ts &&...Vals)
Definition Server.h:59
std::map< MCPTransport *, Client > m_instances
Definition Server.h:77
ServerCapabilities GetCapabilities()
Definition Server.cpp:242
A class that represents a running process on the host machine.
MainLoopPosix MainLoop
Definition MainLoop.h:20
std::unique_ptr< MCPBinder > MCPBinderUP
Definition Transport.h:77
std::variant< std::monostate, llvm::json::Value > ToolArguments
Definition Protocol.h:191
lldb_private::transport::JSONTransport< ProtocolDescriptor > MCPTransport
Generic transport that uses the MCP protocol.
Definition Transport.h:75
llvm::json::Value toJSON(const Request &)
Definition Protocol.cpp:66
llvm::unique_function< void(llvm::StringRef message)> LogCallback
Generic logging callback, to allow the MCP server / client / transport layer to be independent of the...
Definition Transport.h:81
static llvm::StringLiteral kProtocolVersion
Definition Protocol.h:26
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:292
std::optional< llvm::json::Value > arguments
Definition Protocol.h:294
The server’s response to a tool call.
Definition Protocol.h:300
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:198
After receiving an initialize request from the client, the server sends this response.
Definition Protocol.h:256
std::string protocolVersion
The version of the Model Context Protocol that the server wants to use.
Definition Protocol.h:260
The server’s response to a resources/list request from the client.
Definition Protocol.h:127
std::vector< Resource > resources
Definition Protocol.h:128
The server's response to a tools/list request from the client.
Definition Protocol.h:281
std::vector< ToolDefinition > tools
Definition Protocol.h:282
Sent from the client to the server, to read a specific resource URI.
Definition Protocol.h:152
std::string uri
The URI of the resource to read.
Definition Protocol.h:155
The server's response to a resources/read request from the client.
Definition Protocol.h:162
A known resource that the server is capable of reading.
Definition Protocol.h:109
Capabilities that a server may support.
Definition Protocol.h:225
Information about this instance of lldb's MCP server for lldb-mcp to use to coordinate connecting an ...
Definition Server.h:87
static llvm::Expected< std::vector< ServerInfo > > Load()
Loads any server info saved in ~/.lldb.
Definition Server.cpp:87
static llvm::Expected< ServerInfoHandle > Write(const ServerInfo &)
Writes the server info into a unique file in ~/.lldb.
Definition Server.cpp:62