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
18#include "cxxmcp/error.hpp"
22
23namespace mcp::server {
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::server::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 transport_ == nullptr ? "server-transport-adapter"
74 : transport_->name();
75 }
76
77 core::Result<core::Unit> send(TxMessage message) override {
78 if (transport_ == nullptr) {
79 return mcp::core::unexpected(
80 detail::adapter_error("server transport is null"));
81 }
82
83 if (const auto* request = std::get_if<protocol::JsonRpcRequest>(&message)) {
84 auto response = transport_->send_request(*request);
85 if (!response) {
86 return mcp::core::unexpected(response.error());
87 }
88 received_.push_back(std::move(*response));
89 return core::Unit{};
90 }
91
92 if (const auto* notification =
93 std::get_if<protocol::JsonRpcNotification>(&message)) {
94 return transport_->send_notification(*notification);
95 }
96
97 return mcp::core::unexpected(detail::adapter_error(
98 "server transport adapter cannot send responses"));
99 }
100
102 if (received_.empty()) {
103 return std::nullopt;
104 }
105 auto message = std::move(received_.front());
106 received_.pop_front();
107 return message;
108 }
109
111 if (transport_ != nullptr) {
112 transport_->stop();
113 }
114 return core::Unit{};
115 }
116
117 private:
118 std::unique_ptr<mcp::server::Transport> owned_;
119 mcp::server::Transport* transport_ = nullptr;
120 std::deque<RxMessage> received_;
121};
122
132 public:
134 : transport_(&transport) {}
135
137 std::unique_ptr<transport::ServerTransport> transport)
138 : owned_(std::move(transport)), transport_(owned_.get()) {}
139
141 ContractTransportAdapter& operator=(const ContractTransportAdapter&) = delete;
142
144 : owned_(std::move(other.owned_)),
145 transport_(owned_ ? owned_.get() : other.transport_),
146 request_handler_(std::move(other.request_handler_)),
147 notification_handler_(std::move(other.notification_handler_)),
148 client_capabilities_(std::move(other.client_capabilities_)),
149 stopped_(other.stopped_) {
150 other.transport_ = nullptr;
151 other.stopped_ = true;
152 }
153
154 ContractTransportAdapter& operator=(
155 ContractTransportAdapter&& other) noexcept {
156 if (this != &other) {
157 owned_ = std::move(other.owned_);
158 transport_ = owned_ ? owned_.get() : other.transport_;
159 request_handler_ = std::move(other.request_handler_);
160 notification_handler_ = std::move(other.notification_handler_);
161 client_capabilities_ = std::move(other.client_capabilities_);
162 stopped_ = other.stopped_;
163 other.transport_ = nullptr;
164 other.stopped_ = true;
165 }
166 return *this;
167 }
168
170 RequestHandler handler,
171 NotificationHandler notification_handler = {}) override {
172 request_handler_ = std::move(handler);
173 notification_handler_ = std::move(notification_handler);
174 if (transport_ == nullptr) {
175 return mcp::core::unexpected(
176 detail::adapter_error("server contract transport is null"));
177 }
178
179 stopped_ = false;
180 while (!stopped_) {
181 auto received = transport_->receive();
182 if (!received) {
183 return mcp::core::unexpected(received.error());
184 }
185 if (!received->has_value()) {
186 return core::Unit{};
187 }
188
189 const auto handled = handle_inbound(received->value());
190 if (!handled) {
191 return mcp::core::unexpected(handled.error());
192 }
193 }
194 return core::Unit{};
195 }
196
198 const protocol::JsonRpcRequest& request) override {
199 if (transport_ == nullptr) {
200 return mcp::core::unexpected(
201 detail::adapter_error("server contract transport is null"));
202 }
203
204 const auto sent = transport_->send(protocol::JsonRpcMessage{request});
205 if (!sent) {
206 return mcp::core::unexpected(sent.error());
207 }
208
209 while (true) {
210 auto received = transport_->receive();
211 if (!received) {
212 return mcp::core::unexpected(received.error());
213 }
214 if (!received->has_value()) {
215 return mcp::core::unexpected(detail::adapter_error(
216 "server contract transport closed before response"));
217 }
218
219 if (auto* response =
220 std::get_if<protocol::JsonRpcResponse>(&received->value())) {
221 if (response->id.has_value() && *response->id == request.id) {
222 return *response;
223 }
224 return mcp::core::unexpected(detail::adapter_error(
225 "server contract transport received unexpected response id"));
226 }
227
228 const auto handled = handle_inbound(received->value());
229 if (!handled) {
230 return mcp::core::unexpected(handled.error());
231 }
232 }
233 }
234
236 const protocol::JsonRpcNotification& notification) override {
237 if (transport_ == nullptr) {
238 return mcp::core::unexpected(
239 detail::adapter_error("server contract transport is null"));
240 }
241 return transport_->send(protocol::JsonRpcMessage{notification});
242 }
243
244 std::optional<protocol::ClientCapabilities> client_capabilities()
245 const override {
246 return client_capabilities_;
247 }
248
249 void stop() noexcept override {
250 stopped_ = true;
251 if (transport_ != nullptr) {
252 (void)transport_->close();
253 }
254 }
255
256 std::string_view name() const noexcept override {
257 return transport_ == nullptr ? "server-contract-transport-adapter"
258 : transport_->name();
259 }
260
261 private:
262 core::Result<core::Unit> handle_inbound(
263 const protocol::JsonRpcMessage& message) {
264 if (auto* request = std::get_if<protocol::JsonRpcRequest>(&message)) {
265 return handle_request(*request);
266 }
267 if (auto* notification =
268 std::get_if<protocol::JsonRpcNotification>(&message)) {
269 return handle_notification(*notification);
270 }
271 return core::Unit{};
272 }
273
274 core::Result<core::Unit> handle_request(
275 const protocol::JsonRpcRequest& request) {
276 protocol::JsonRpcResponse response;
277 if (request_handler_) {
278 try {
279 auto handled = request_handler_(request, session_context());
280 if (!handled) {
281 response = protocol::make_error_response(
282 request.id, error_object_from_core_error(handled.error()));
283 } else {
284 response = std::move(*handled);
285 }
286 } catch (const std::exception& ex) {
287 response = protocol::make_error_response(
288 request.id,
289 error_object_from_core_error(errors::handler_failed(ex.what())));
290 } catch (...) {
291 response = protocol::make_error_response(
292 request.id,
293 error_object_from_core_error(errors::handler_unknown_exception()));
294 }
295 } else {
296 response = protocol::make_error_response(
297 request.id,
298 protocol::make_error(protocol::ErrorCode::MethodNotFound,
299 "server request handler is not set"));
300 }
301
302 if (request.method == protocol::InitializeMethod) {
303 if (request.params.is_object() &&
304 request.params.contains("capabilities")) {
305 client_capabilities_ = protocol::client_capabilities_from_json(
306 request.params.at("capabilities"));
307 } else {
308 client_capabilities_.reset();
309 }
310 }
311
312 auto sent = transport_->send(protocol::JsonRpcMessage{std::move(response)});
313 if (!sent) {
314 return mcp::core::unexpected(sent.error());
315 }
316 return core::Unit{};
317 }
318
319 core::Result<core::Unit> handle_notification(
320 const protocol::JsonRpcNotification& notification) {
321 if (!notification_handler_) {
322 return core::Unit{};
323 }
324 try {
325 return notification_handler_(notification, session_context());
326 } catch (const std::exception& ex) {
327 return mcp::core::unexpected(errors::handler_failed(ex.what()));
328 } catch (...) {
329 return mcp::core::unexpected(errors::handler_unknown_exception());
330 }
331 }
332
333 SessionContext session_context() noexcept {
334 SessionContext context;
335 context.remote_address = std::string(name());
336 context.transport = this;
337 context.transport_lifetime = lifetime_token();
338 return context;
339 }
340
341 static protocol::ErrorObject error_object_from_core_error(
342 const core::Error& error) {
343 return errors::to_json_rpc_error(error);
344 }
345
346 std::unique_ptr<transport::ServerTransport> owned_;
347 transport::ServerTransport* transport_ = nullptr;
348 RequestHandler request_handler_;
349 NotificationHandler notification_handler_;
350 std::optional<protocol::ClientCapabilities> client_capabilities_;
351 bool stopped_ = false;
352};
353
354} // namespace mcp::server
Adapts a transport::ServerTransport to the existing server::Transport API.
Definition transport_adapter.hpp:131
std::string_view name() const noexcept override
Human-readable transport name for diagnostics.
Definition transport_adapter.hpp:256
core::Result< core::Unit > start(RequestHandler handler, NotificationHandler notification_handler={}) override
Start accepting inbound messages and dispatch them to handlers.
Definition transport_adapter.hpp:169
void stop() noexcept override
Request transport shutdown.
Definition transport_adapter.hpp:249
core::Result< protocol::JsonRpcResponse > send_request(const protocol::JsonRpcRequest &request) override
Send a JSON-RPC request from the server to the connected client.
Definition transport_adapter.hpp:197
core::Result< core::Unit > send_notification(const protocol::JsonRpcNotification &notification) override
Send a JSON-RPC notification from the server to the client.
Definition transport_adapter.hpp:235
std::optional< protocol::ClientCapabilities > client_capabilities() const override
Return capabilities learned from the client's initialize request.
Definition transport_adapter.hpp:244
Adapts an existing server::Transport to transport::ServerTransport.
Definition transport_adapter.hpp:42
core::Result< core::Unit > close() override
Closes the transport and unblocks receive() where possible.
Definition transport_adapter.hpp:110
std::string_view name() const noexcept override
Human-readable transport name for diagnostics.
Definition transport_adapter.hpp:72
core::Result< std::optional< RxMessage > > receive() override
Receives the next JSON-RPC message from the peer.
Definition transport_adapter.hpp:101
Abstract server transport for receiving client JSON-RPC messages.
Definition transport.hpp:99
std::weak_ptr< void > lifetime_token() const noexcept
Weak lifetime token for SessionContext and ClientPeer guards.
Definition transport.hpp:104
virtual void stop() noexcept=0
Request transport shutdown.
virtual core::Result< core::Unit > send_notification(const protocol::JsonRpcNotification &notification)=0
Send a JSON-RPC notification from the server to the client.
virtual std::string_view name() const noexcept=0
Human-readable transport name for diagnostics.
virtual core::Result< protocol::JsonRpcResponse > send_request(const protocol::JsonRpcRequest &request)
Send a JSON-RPC request from the server to the connected client.
Definition transport.hpp:124
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 std::string_view name() const noexcept=0
Human-readable transport name for diagnostics.
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.
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.
Server-side transport abstraction for MCP JSON-RPC traffic.
std::function< core::Result< protocol::JsonRpcResponse >(const protocol::JsonRpcRequest &, const SessionContext &)> RequestHandler
Callback used by transports to dispatch inbound JSON-RPC requests.
Definition transport.hpp:74
std::function< core::Result< core::Unit >(const protocol::JsonRpcNotification &, const SessionContext &)> NotificationHandler
Callback used by transports to dispatch inbound JSON-RPC notifications.
Definition transport.hpp:84
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.