cxxmcp 1.1.6
C++ MCP SDK
Loading...
Searching...
No Matches
transport_adapter.hpp
Go to the documentation of this file.
1// Copyright (c) 2025 [caomengxuan666]
2
3#pragma once
4
8
9#include <deque>
10#include <exception>
11#include <memory>
12#include <optional>
13#include <string>
14#include <string_view>
15#include <utility>
16#include <variant>
17
19#include "cxxmcp/error.hpp"
22
23namespace mcp::client {
24
25namespace detail {
26
27inline core::Error adapter_error(std::string_view message) {
28 return errors::make(protocol::ErrorCode::InvalidRequest, std::string(message),
29 {}, "transport");
30}
31
32} // namespace detail
33
43 public:
45 : transport_(&transport) {}
46
48 std::unique_ptr<mcp::client::Transport> transport)
49 : owned_(std::move(transport)), transport_(owned_.get()) {}
50
52 TransportContractAdapter& operator=(const TransportContractAdapter&) = delete;
53
55 : owned_(std::move(other.owned_)),
56 transport_(owned_ ? owned_.get() : other.transport_),
57 received_(std::move(other.received_)) {
58 other.transport_ = nullptr;
59 }
60
61 TransportContractAdapter& operator=(
62 TransportContractAdapter&& other) noexcept {
63 if (this != &other) {
64 owned_ = std::move(other.owned_);
65 transport_ = owned_ ? owned_.get() : other.transport_;
66 received_ = std::move(other.received_);
67 other.transport_ = nullptr;
68 }
69 return *this;
70 }
71
72 std::string_view name() const noexcept override {
73 return "client-transport-adapter";
74 }
75
76 core::Result<core::Unit> send(TxMessage message) override {
77 if (transport_ == nullptr) {
78 return mcp::core::unexpected(
79 detail::adapter_error("client transport is null"));
80 }
81
82 if (const auto* request = std::get_if<protocol::JsonRpcRequest>(&message)) {
83 auto response = transport_->send(*request);
84 if (!response) {
85 return mcp::core::unexpected(response.error());
86 }
87 received_.push_back(std::move(*response));
88 return core::Unit{};
89 }
90
91 if (const auto* notification =
92 std::get_if<protocol::JsonRpcNotification>(&message)) {
93 return transport_->send_notification(*notification);
94 }
95
96 return mcp::core::unexpected(detail::adapter_error(
97 "client transport adapter cannot send responses"));
98 }
99
101 if (received_.empty()) {
102 return std::nullopt;
103 }
104 auto message = std::move(received_.front());
105 received_.pop_front();
106 return message;
107 }
108
110 if (transport_ != nullptr) {
111 transport_->stop();
112 }
113 return core::Unit{};
114 }
115
116 private:
117 std::unique_ptr<mcp::client::Transport> owned_;
118 mcp::client::Transport* transport_ = nullptr;
119 std::deque<RxMessage> received_;
120};
121
131 public:
133 : transport_(&transport) {}
134
136 std::unique_ptr<transport::ClientTransport> transport)
137 : owned_(std::move(transport)), transport_(owned_.get()) {}
138
140 ContractTransportAdapter& operator=(const ContractTransportAdapter&) = delete;
141
143 : owned_(std::move(other.owned_)),
144 transport_(owned_ ? owned_.get() : other.transport_),
145 request_handler_(std::move(other.request_handler_)),
146 notification_handler_(std::move(other.notification_handler_)) {
147 other.transport_ = nullptr;
148 }
149
150 ContractTransportAdapter& operator=(
151 ContractTransportAdapter&& other) noexcept {
152 if (this != &other) {
153 owned_ = std::move(other.owned_);
154 transport_ = owned_ ? owned_.get() : other.transport_;
155 request_handler_ = std::move(other.request_handler_);
156 notification_handler_ = std::move(other.notification_handler_);
157 other.transport_ = nullptr;
158 }
159 return *this;
160 }
161
163 const protocol::JsonRpcRequest& request) override {
164 if (transport_ == nullptr) {
165 return mcp::core::unexpected(
166 detail::adapter_error("client contract transport is null"));
167 }
168
169 const auto sent = transport_->send(protocol::JsonRpcMessage{request});
170 if (!sent) {
171 return mcp::core::unexpected(sent.error());
172 }
173
174 while (true) {
175 auto received = transport_->receive();
176 if (!received) {
177 return mcp::core::unexpected(received.error());
178 }
179 if (!received->has_value()) {
180 return mcp::core::unexpected(detail::adapter_error(
181 "client contract transport closed before response"));
182 }
183
184 if (auto* response =
185 std::get_if<protocol::JsonRpcResponse>(&received->value())) {
186 if (response->id.has_value() && *response->id == request.id) {
187 return *response;
188 }
189 return mcp::core::unexpected(detail::adapter_error(
190 "client contract transport received unexpected response id"));
191 }
192
193 if (auto* notification =
194 std::get_if<protocol::JsonRpcNotification>(&received->value())) {
195 const auto handled = handle_notification(*notification);
196 if (!handled) {
197 return mcp::core::unexpected(handled.error());
198 }
199 continue;
200 }
201
202 if (auto* inbound_request =
203 std::get_if<protocol::JsonRpcRequest>(&received->value())) {
204 const auto handled = handle_request(*inbound_request);
205 if (!handled) {
206 return mcp::core::unexpected(handled.error());
207 }
208 continue;
209 }
210 }
211 }
212
214 const protocol::JsonRpcNotification& notification) override {
215 if (transport_ == nullptr) {
216 return mcp::core::unexpected(
217 detail::adapter_error("client contract transport is null"));
218 }
219 return transport_->send(protocol::JsonRpcMessage{notification});
220 }
221
223 TransportRequestHandler request_handler,
224 TransportNotificationHandler notification_handler = {}) override {
225 request_handler_ = std::move(request_handler);
226 notification_handler_ = std::move(notification_handler);
227 return core::Unit{};
228 }
229
230 void stop() noexcept override {
231 if (transport_ != nullptr) {
232 (void)transport_->close();
233 }
234 }
235
236 private:
237 core::Result<core::Unit> handle_notification(
238 const protocol::JsonRpcNotification& notification) {
239 if (!notification_handler_) {
240 return core::Unit{};
241 }
242 try {
243 return notification_handler_(notification);
244 } catch (const std::exception& ex) {
245 return mcp::core::unexpected(errors::handler_failed(ex.what()));
246 } catch (...) {
247 return mcp::core::unexpected(errors::handler_unknown_exception());
248 }
249 }
250
251 core::Result<core::Unit> handle_request(
252 const protocol::JsonRpcRequest& request) {
253 protocol::JsonRpcResponse response;
254 if (request_handler_) {
255 try {
256 auto handled = request_handler_(request);
257 if (!handled) {
258 response = protocol::make_error_response(
259 request.id, error_object_from_core_error(handled.error()));
260 } else {
261 response = std::move(*handled);
262 }
263 } catch (const std::exception& ex) {
264 response = protocol::make_error_response(
265 request.id,
266 error_object_from_core_error(errors::handler_failed(ex.what())));
267 } catch (...) {
268 response = protocol::make_error_response(
269 request.id,
270 error_object_from_core_error(errors::handler_unknown_exception()));
271 }
272 } else {
273 response = protocol::make_error_response(
274 request.id,
275 protocol::make_error(protocol::ErrorCode::MethodNotFound,
276 "client request handler is not set"));
277 }
278
279 auto sent = transport_->send(protocol::JsonRpcMessage{std::move(response)});
280 if (!sent) {
281 return mcp::core::unexpected(sent.error());
282 }
283 return core::Unit{};
284 }
285
286 static protocol::ErrorObject error_object_from_core_error(
287 const core::Error& error) {
288 return errors::to_json_rpc_error(error);
289 }
290
291 std::unique_ptr<transport::ClientTransport> owned_;
292 transport::ClientTransport* transport_ = nullptr;
293 TransportRequestHandler request_handler_;
294 TransportNotificationHandler notification_handler_;
295};
296
297} // namespace mcp::client
Adapts a transport::ClientTransport to the existing client::Transport API.
Definition transport_adapter.hpp:130
void stop() noexcept override
Requests transport shutdown.
Definition transport_adapter.hpp:230
core::Result< core::Unit > send_notification(const protocol::JsonRpcNotification &notification) override
Sends a JSON-RPC notification without waiting for a response.
Definition transport_adapter.hpp:213
core::Result< protocol::JsonRpcResponse > send(const protocol::JsonRpcRequest &request) override
Sends a JSON-RPC request and waits for the corresponding response.
Definition transport_adapter.hpp:162
core::Result< core::Unit > start(TransportRequestHandler request_handler, TransportNotificationHandler notification_handler={}) override
Starts receiving inbound messages for transports that need an active loop.
Definition transport_adapter.hpp:222
Adapts an existing client::Transport to transport::ClientTransport.
Definition transport_adapter.hpp:42
core::Result< core::Unit > close() override
Closes the transport and unblocks receive() where possible.
Definition transport_adapter.hpp:109
core::Result< std::optional< RxMessage > > receive() override
Receives the next JSON-RPC message from the peer.
Definition transport_adapter.hpp:100
std::string_view name() const noexcept override
Human-readable transport name for diagnostics.
Definition transport_adapter.hpp:72
Abstract client transport used by Client to exchange JSON-RPC messages.
Definition client.hpp:83
virtual core::Result< core::Unit > send_notification(const protocol::JsonRpcNotification &notification)
Sends a JSON-RPC notification without waiting for a response.
virtual core::Result< protocol::JsonRpcResponse > send(const protocol::JsonRpcRequest &request)=0
Sends a JSON-RPC request and waits for the corresponding response.
virtual void stop() noexcept
Requests transport shutdown.
Definition client.hpp:120
Minimal message-level transport contract shared by MCP roles.
Definition transport.hpp:38
virtual core::Result< std::optional< RxMessage > > receive()=0
Receives the next JSON-RPC message from the peer.
virtual core::Result< core::Unit > send(TxMessage message)=0
Sends one JSON-RPC message to the peer.
virtual core::Result< core::Unit > close()=0
Closes the transport and unblocks receive() where possible.
Core client compatibility API and transport interface for MCP clients.
std::function< core::Result< core::Unit >(const protocol::JsonRpcNotification &)> TransportNotificationHandler
Handles JSON-RPC notifications sent by the server to this client.
Definition client.hpp:75
std::function< core::Result< protocol::JsonRpcResponse >(const protocol::JsonRpcRequest &)> TransportRequestHandler
Handles JSON-RPC requests sent by the server to this client.
Definition client.hpp:68
Stable public error helpers for SDK request and dispatch paths.
std::variant< JsonRpcRequest, JsonRpcResponse, JsonRpcNotification > JsonRpcMessage
Variant over the JSON-RPC message shapes accepted by MCP transports.
Definition types.hpp:148
std::monostate Unit
Success value for operations that only need to report failure.
Definition result.hpp:55
tl::expected< T, Error > Result
Alias for the SDK result type.
Definition result.hpp:64
constexpr auto unexpected(E &&value)
Creates an unexpected result value for the active expected backend.
Definition result.hpp:24
JSON-RPC method names and message construction/parsing helpers.
JSON-RPC notification envelope for one-way MCP messages.
Definition types.hpp:137
JSON-RPC request envelope carrying an MCP method invocation.
Definition types.hpp:99
RequestId id
Request id that must be echoed by the response.
Definition types.hpp:105
Role-generic MCP transport contract for SDK peer/service layers.