cxxmcp 1.3.0
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
110 void publish_subscription_notification(std::string_view method,
111 protocol::Json params) override {
112 if (transport_ != nullptr) {
113 transport_->publish_subscription_notification(method, std::move(params));
114 }
115 }
116
118 if (transport_ != nullptr) {
119 transport_->stop();
120 }
121 return core::Unit{};
122 }
123
124 private:
125 std::unique_ptr<mcp::server::Transport> owned_;
126 mcp::server::Transport* transport_ = nullptr;
127 std::deque<RxMessage> received_;
128};
129
139 public:
141 : transport_(&transport) {}
142
144 std::unique_ptr<transport::ServerTransport> transport)
145 : owned_(std::move(transport)), transport_(owned_.get()) {}
146
148 ContractTransportAdapter& operator=(const ContractTransportAdapter&) = delete;
149
151 : owned_(std::move(other.owned_)),
152 transport_(owned_ ? owned_.get() : other.transport_),
153 request_handler_(std::move(other.request_handler_)),
154 notification_handler_(std::move(other.notification_handler_)),
155 client_capabilities_(std::move(other.client_capabilities_)),
156 stopped_(other.stopped_) {
157 other.transport_ = nullptr;
158 other.stopped_ = true;
159 }
160
161 ContractTransportAdapter& operator=(
162 ContractTransportAdapter&& other) noexcept {
163 if (this != &other) {
164 owned_ = std::move(other.owned_);
165 transport_ = owned_ ? owned_.get() : other.transport_;
166 request_handler_ = std::move(other.request_handler_);
167 notification_handler_ = std::move(other.notification_handler_);
168 client_capabilities_ = std::move(other.client_capabilities_);
169 stopped_ = other.stopped_;
170 other.transport_ = nullptr;
171 other.stopped_ = true;
172 }
173 return *this;
174 }
175
177 RequestHandler handler,
178 NotificationHandler notification_handler = {}) override {
179 request_handler_ = std::move(handler);
180 notification_handler_ = std::move(notification_handler);
181 if (transport_ == nullptr) {
182 return mcp::core::unexpected(
183 detail::adapter_error("server contract transport is null"));
184 }
185
186 stopped_ = false;
187 while (!stopped_) {
188 auto received = transport_->receive();
189 if (!received) {
190 return mcp::core::unexpected(received.error());
191 }
192 if (!received->has_value()) {
193 return core::Unit{};
194 }
195
196 const auto handled = handle_inbound(received->value());
197 if (!handled) {
198 return mcp::core::unexpected(handled.error());
199 }
200 }
201 return core::Unit{};
202 }
203
205 const protocol::JsonRpcRequest& request) override {
206 if (transport_ == nullptr) {
207 return mcp::core::unexpected(
208 detail::adapter_error("server contract transport is null"));
209 }
210
211 const auto sent = transport_->send(protocol::JsonRpcMessage{request});
212 if (!sent) {
213 return mcp::core::unexpected(sent.error());
214 }
215
216 // Prefer the transport's dedicated reverse-response routing when it is
217 // available: waiting on the shared receive() queue would let the main
218 // receive loop steal the response (single-consumer contract).
219 while (true) {
220 auto routed = transport_->receive_response(request.id);
221 if (!routed) {
222 break; // transport does not support direct routing; fall back below
223 }
224 if (!routed->has_value()) {
225 return mcp::core::unexpected(detail::adapter_error(
226 "server contract transport closed before response"));
227 }
228 if (auto* response =
229 std::get_if<protocol::JsonRpcResponse>(&routed->value())) {
230 if (response->id.has_value() && *response->id == request.id) {
231 return *response;
232 }
233 continue;
234 }
235 continue;
236 }
237
238 while (true) {
239 auto received = transport_->receive();
240 if (!received) {
241 return mcp::core::unexpected(received.error());
242 }
243 if (!received->has_value()) {
244 return mcp::core::unexpected(detail::adapter_error(
245 "server contract transport closed before response"));
246 }
247
248 if (auto* response =
249 std::get_if<protocol::JsonRpcResponse>(&received->value())) {
250 if (response->id.has_value() && *response->id == request.id) {
251 return *response;
252 }
253 return mcp::core::unexpected(detail::adapter_error(
254 "server contract transport received unexpected response id"));
255 }
256
257 const auto handled = handle_inbound(received->value());
258 if (!handled) {
259 return mcp::core::unexpected(handled.error());
260 }
261 }
262 }
263
265 const protocol::JsonRpcNotification& notification) override {
266 if (transport_ == nullptr) {
267 return mcp::core::unexpected(
268 detail::adapter_error("server contract transport is null"));
269 }
270 return transport_->send(protocol::JsonRpcMessage{notification});
271 }
272
273 std::optional<protocol::ClientCapabilities> client_capabilities()
274 const override {
275 return client_capabilities_;
276 }
277
278 void publish_subscription_notification(std::string_view method,
279 protocol::Json params) override {
280 if (transport_ != nullptr) {
281 transport_->publish_subscription_notification(method, std::move(params));
282 }
283 }
284
285 void stop() noexcept override {
286 stopped_ = true;
287 if (transport_ != nullptr) {
288 (void)transport_->close();
289 }
290 }
291
292 std::string_view name() const noexcept override {
293 return transport_ == nullptr ? "server-contract-transport-adapter"
294 : transport_->name();
295 }
296
297 private:
298 core::Result<core::Unit> handle_inbound(
299 const protocol::JsonRpcMessage& message) {
300 if (auto* request = std::get_if<protocol::JsonRpcRequest>(&message)) {
301 return handle_request(*request);
302 }
303 if (auto* notification =
304 std::get_if<protocol::JsonRpcNotification>(&message)) {
305 return handle_notification(*notification);
306 }
307 return core::Unit{};
308 }
309
310 core::Result<core::Unit> handle_request(
311 const protocol::JsonRpcRequest& request) {
312 protocol::JsonRpcResponse response;
313 if (request_handler_) {
314 try {
315 auto handled = request_handler_(request, session_context());
316 if (!handled) {
317 response = protocol::make_error_response(
318 request.id, error_object_from_core_error(handled.error()));
319 } else {
320 response = std::move(*handled);
321 }
322 } catch (const std::exception& ex) {
323 response = protocol::make_error_response(
324 request.id,
325 error_object_from_core_error(errors::handler_failed(ex.what())));
326 } catch (...) {
327 response = protocol::make_error_response(
328 request.id,
329 error_object_from_core_error(errors::handler_unknown_exception()));
330 }
331 } else {
332 response = protocol::make_error_response(
333 request.id,
334 protocol::make_error(protocol::ErrorCode::MethodNotFound,
335 "server request handler is not set"));
336 }
337
338 if (request.method == protocol::InitializeMethod) {
339 if (request.params.is_object() &&
340 request.params.contains("capabilities")) {
341 client_capabilities_ = protocol::client_capabilities_from_json(
342 request.params.at("capabilities"));
343 } else {
344 client_capabilities_.reset();
345 }
346 }
347
348 auto sent = transport_->send(protocol::JsonRpcMessage{std::move(response)});
349 if (!sent) {
350 return mcp::core::unexpected(sent.error());
351 }
352 return core::Unit{};
353 }
354
355 core::Result<core::Unit> handle_notification(
356 const protocol::JsonRpcNotification& notification) {
357 if (!notification_handler_) {
358 return core::Unit{};
359 }
360 try {
361 return notification_handler_(notification, session_context());
362 } catch (const std::exception& ex) {
363 return mcp::core::unexpected(errors::handler_failed(ex.what()));
364 } catch (...) {
365 return mcp::core::unexpected(errors::handler_unknown_exception());
366 }
367 }
368
369 SessionContext session_context() noexcept {
370 SessionContext context;
371 context.remote_address = std::string(name());
372 context.transport = this;
373 context.transport_lifetime = lifetime_token();
374 return context;
375 }
376
377 static protocol::ErrorObject error_object_from_core_error(
378 const core::Error& error) {
379 return errors::to_json_rpc_error(error);
380 }
381
382 std::unique_ptr<transport::ServerTransport> owned_;
383 transport::ServerTransport* transport_ = nullptr;
384 RequestHandler request_handler_;
385 NotificationHandler notification_handler_;
386 std::optional<protocol::ClientCapabilities> client_capabilities_;
387 bool stopped_ = false;
388};
389
390} // namespace mcp::server
Adapts a transport::ServerTransport to the existing server::Transport API.
Definition transport_adapter.hpp:138
std::string_view name() const noexcept override
Human-readable transport name for diagnostics.
Definition transport_adapter.hpp:292
core::Result< core::Unit > start(RequestHandler handler, NotificationHandler notification_handler={}) override
Start accepting inbound messages and dispatch them to handlers.
Definition transport_adapter.hpp:176
void stop() noexcept override
Request transport shutdown.
Definition transport_adapter.hpp:285
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:204
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:264
void publish_subscription_notification(std::string_view method, protocol::Json params) override
Publish a notification to open subscriptions/listen streams (SEP-2575).
Definition transport_adapter.hpp:278
std::optional< protocol::ClientCapabilities > client_capabilities() const override
Return capabilities learned from the client's initialize request.
Definition transport_adapter.hpp:273
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:117
void publish_subscription_notification(std::string_view method, protocol::Json params) override
Publishes a notification to active subscription listen streams.
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:154
std::weak_ptr< void > lifetime_token() const noexcept
Weak lifetime token for SessionContext and ClientPeer guards.
Definition transport.hpp:159
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 void publish_subscription_notification(std::string_view method, protocol::Json params)
Publish a notification to open subscriptions/listen streams (SEP-2575).
Definition transport.hpp:249
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:179
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< std::optional< RxMessage > > receive_response(const protocol::RequestId &id)
Waits for the response to one specific outbound request.
Definition transport.hpp:84
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 void publish_subscription_notification(std::string_view method, protocol::Json params)
Publishes a notification to active subscription listen streams.
Definition transport.hpp:98
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.
nlohmann::json Json
JSON value type used by all protocol DTOs.
Definition types.hpp:28
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:75
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:139
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.