cxxmcp 1.3.0
C++ MCP SDK
Loading...
Searching...
No Matches
peer.hpp
Go to the documentation of this file.
1// Copyright (c) 2025 [caomengxuan666]
2
3#pragma once
4
13
14#include <atomic>
15#include <chrono>
16#include <condition_variable>
17#include <cstdint>
18#include <exception>
19#include <iostream>
20#include <memory>
21#include <mutex>
22#include <optional>
23#include <string>
24#include <string_view>
25#include <thread>
26#include <unordered_map>
27#include <utility>
28#include <variant>
29#include <vector>
30
35#include "cxxmcp/config.hpp"
36#include "cxxmcp/error.hpp"
37#include "cxxmcp/handler.hpp"
39#include "cxxmcp/roles.hpp"
43#if defined(CXXMCP_ENABLE_HTTP)
45#endif
46#if defined(CXXMCP_ENABLE_WEBSOCKET)
48#endif
52
53namespace mcp {
54
55namespace detail {
56
57inline core::Error peer_dispatch_error(std::string_view message) {
58 return errors::make(protocol::ErrorCode::InvalidRequest,
59 std::string(message));
60}
61
62inline core::Error peer_transport_error(std::string_view message) {
63 return errors::make(protocol::ErrorCode::InvalidRequest, std::string(message),
64 {}, "transport");
65}
66
67inline protocol::ErrorObject peer_error_object_from_core_error(
68 const core::Error& error) {
69 return errors::to_json_rpc_error(error);
70}
71
72inline protocol::JsonRpcResponse peer_error_response(
73 const protocol::JsonRpcRequest& request, const core::Error& error) {
74 return protocol::make_error_response(
75 request.id, peer_error_object_from_core_error(error));
76}
77
78inline protocol::JsonRpcResponse peer_auth_error_response(
79 const protocol::JsonRpcRequest& request, const core::Error& error) {
80 protocol::Json data = protocol::Json::object();
81 data["category"] = std::string(server::AuthErrorCategory);
82 if (!error.detail.empty()) {
83 data["detail"] = error.detail;
84 } else if (!error.message.empty()) {
85 data["detail"] = error.message;
86 }
87 return protocol::make_error_response(
88 request.id,
89 protocol::make_error(protocol::ErrorCode::PermissionDenied,
90 "authentication failed", std::move(data)));
91}
92
93inline core::Error peer_params_error(core::Error error) {
94 if (error.code == static_cast<int>(protocol::ErrorCode::InvalidRequest)) {
95 error.code = static_cast<int>(protocol::ErrorCode::InvalidParams);
96 }
97 return error;
98}
99
100inline protocol::JsonRpcResponse peer_params_error_response(
101 const protocol::JsonRpcRequest& request, core::Error error) {
102 return peer_error_response(request, peer_params_error(std::move(error)));
103}
104
105inline std::string peer_request_cancellation_key(
106 const protocol::RequestId& request_id) {
107 return protocol::request_id_to_json(request_id).dump();
108}
109
110inline core::Result<protocol::Json> peer_require_result_payload(
111 const protocol::JsonRpcResponse& response) {
112 if (response.error.has_value()) {
113 return mcp::core::unexpected(core::Error{
114 response.error->code,
115 response.error->message,
116 response.error->data.has_value() ? response.error->data->dump()
117 : std::string{},
118 "protocol",
119 });
120 }
121 if (!response.result.has_value()) {
123 errors::make(protocol::ErrorCode::InvalidRequest,
124 "response did not contain a result", {}, "protocol"));
125 }
126 return *response.result;
127}
128
129template <class Transport, class Dispatch>
130inline core::Result<core::Unit> serve_transport_loop(
131 Transport& transport, CancellationToken cancellation, Dispatch dispatch) {
132 while (!cancellation.cancelled()) {
133 auto received = transport.receive();
134 if (!received) {
135 return mcp::core::unexpected(received.error());
136 }
137 if (!received->has_value()) {
138 return core::Unit{};
139 }
140
141 auto dispatched = dispatch(received->value());
142 if (!dispatched) {
143 return mcp::core::unexpected(dispatched.error());
144 }
145 if (dispatched->has_value()) {
146 auto sent = transport.send(std::move(dispatched->value()));
147 if (!sent) {
148 return mcp::core::unexpected(sent.error());
149 }
150 }
151 }
152 return core::Unit{};
153}
154
155inline void keep_first_service_error(std::optional<core::Error>& first_error,
156 std::mutex& mutex,
157 core::Error error) noexcept {
158 std::lock_guard lock(mutex);
159 if (!first_error.has_value()) {
160 first_error = std::move(error);
161 }
162}
163
164inline server::SessionContext context_for_received_server_message(
165 transport::ServerTransport& transport,
166 const server::SessionContext& fallback) {
167 server::SessionContext context = fallback;
168#if defined(CXXMCP_ENABLE_HTTP)
169 auto* http_transport =
170 dynamic_cast<transport::StreamableHttpServerTransport*>(&transport);
171 if (http_transport == nullptr) {
172 return context;
173 }
174
175 const auto http_context = http_transport->last_received_context();
176 if (!http_context.has_value()) {
177 return context;
178 }
179
180 context.session_id = http_context->session_id;
181 if (!http_context->remote_address.empty()) {
182 context.remote_address = http_context->remote_address;
183 }
184 context.headers = http_context->headers;
185 context.http_method = http_context->http_method;
186 context.http_url = http_context->http_url;
187#endif
188 return context;
189}
190
192 std::mutex mutex;
193 std::condition_variable cv;
194 std::unordered_map<std::string, protocol::JsonRpcResponse> responses;
195 std::optional<core::Error> failure;
196 bool loop_active = false;
197 bool closed = false;
198};
199
200inline std::unique_ptr<client::Transport> make_peer_client_transport_adapter(
201 std::unique_ptr<transport::ClientTransport>& transport) {
202 if (!transport) {
203 return client::make_contract_transport_adapter(
204 std::unique_ptr<transport::ClientTransport>{});
205 }
206 return client::make_contract_transport_adapter(*transport);
207}
208
209inline protocol::ClientCapabilities default_peer_client_capabilities(
210 const std::optional<protocol::ClientCapabilities>& capabilities) {
211 if (capabilities.has_value()) {
212 return *capabilities;
213 }
214
215 protocol::ClientCapabilities defaults;
216 defaults.roots.enabled = true;
217 defaults.roots.list_changed = true;
218 defaults.sampling.enabled = true;
219 defaults.sampling.tools = true;
220 defaults.sampling.context = true;
221 defaults.elicitation.form = true;
222 defaults.elicitation.form_schema_validation = true;
223 defaults.elicitation.url = true;
224 return defaults;
225}
226
227inline protocol::Json make_peer_initialize_params(
228 std::string client_name, std::string client_version,
229 const std::optional<protocol::ClientCapabilities>& capabilities) {
230 protocol::Json params = protocol::Json::object();
231 params["protocolVersion"] = std::string(protocol::McpProtocolVersion);
232 params["capabilities"] = protocol::client_capabilities_to_json(
233 default_peer_client_capabilities(capabilities));
234 params["clientInfo"] = protocol::Json{
235 {"name", std::move(client_name)},
236 {"version", std::move(client_version)},
237 };
238 return params;
239}
240
241inline core::Result<protocol::Json> require_peer_initialize_payload(
242 const protocol::Json& payload) {
243 // Accept minimal responses (e.g. from conformance harness) that don't have
244 // all required fields. This allows version negotiation to succeed even when
245 // the server returns a simplified response.
246 if (payload.is_object() && !payload.contains("protocolVersion")) {
247 return payload;
248 }
249 const auto parsed = protocol::initialize_result_from_json(payload);
250 if (!parsed) {
251 return mcp::core::unexpected(errors::make(
252 protocol::ErrorCode::InvalidRequest, parsed.error().message,
253 parsed.error().detail, "protocol"));
254 }
255 if (!protocol::is_supported_protocol_version(parsed->protocol_version)) {
257 errors::make(protocol::ErrorCode::InvalidRequest,
258 "unsupported MCP protocol version(\"" +
259 parsed->protocol_version + "\")",
260 {}, "protocol"));
261 }
262 return payload;
263}
264
265inline core::Result<core::Unit> validate_peer_server_initialize_params(
266 const protocol::Json& params) {
267 if (!params.is_object()) {
269 errors::make(protocol::ErrorCode::InvalidParams,
270 "initialize params must be an object"));
271 }
272 if (!params.contains("protocolVersion") ||
273 !params.at("protocolVersion").is_string()) {
275 errors::make(protocol::ErrorCode::InvalidParams,
276 "initialize requires a string protocolVersion"));
277 }
278 const auto requested_version =
279 params.at("protocolVersion").get<std::string>();
280 if (!protocol::is_supported_protocol_version(requested_version)) {
281 return mcp::core::unexpected(errors::make(
282 protocol::ErrorCode::InvalidParams,
283 "unsupported MCP protocol version(\"" + requested_version + "\")"));
284 }
285
286 return core::Unit{};
287}
288
289inline protocol::Json make_peer_server_initialize_result(
290 const server::ServerInfo& info,
291 const protocol::ServerCapabilities& capabilities,
292 std::string_view protocol_version = protocol::McpProtocolVersion) {
293 protocol::Json result = protocol::Json::object();
294 result["protocolVersion"] = std::string(protocol_version);
295 result["capabilities"] = protocol::server_capabilities_to_json(capabilities);
296 result["serverInfo"] = protocol::Json{
297 {"name", info.name},
298 {"version", info.version},
299 };
300 if (!info.instructions.empty()) {
301 result["instructions"] = info.instructions;
302 }
303 return result;
304}
305
306} // namespace detail
307
309template <class Role>
310class Peer;
311
318template <>
320 public:
321 class Builder;
322 class TaskHandle {
323 public:
324 TaskHandle() = default;
325
326 TaskHandle(Peer& peer, protocol::Task task)
327 : peer_(&peer), task_(std::move(task)) {}
328
329 const std::string& id() const noexcept { return task_.task_id; }
330
331 protocol::TaskStatus status() const noexcept { return task_.status; }
332
333 const protocol::Task& snapshot() const noexcept { return task_; }
334
336 if (peer_ == nullptr) {
337 return mcp::core::unexpected(detached_error());
338 }
339 auto task = peer_->get_task(task_.task_id);
340 if (!task) {
341 return mcp::core::unexpected(task.error());
342 }
343 task_ = *task;
344 return task_;
345 }
346
348 if (peer_ == nullptr) {
349 return mcp::core::unexpected(detached_error());
350 }
351 auto task = peer_->cancel_task(task_.task_id);
352 if (!task) {
353 return mcp::core::unexpected(task.error());
354 }
355 task_ = *task;
356 return task_;
357 }
358
360 if (peer_ == nullptr) {
361 return mcp::core::unexpected(detached_error());
362 }
363 return peer_->task_result(task_.task_id);
364 }
365
366 template <class T>
367 core::Result<T> result_as() {
368 auto payload = result();
369 if (!payload) {
370 return mcp::core::unexpected(payload.error());
371 }
372 try {
373 if (payload->is_object() && payload->contains("value")) {
374 return payload->at("value").template get<T>();
375 }
376 return payload->template get<T>();
377 } catch (const std::exception& exception) {
378 return mcp::core::unexpected(errors::make(
379 protocol::ErrorCode::InvalidRequest, "failed to decode task result",
380 exception.what(), "protocol"));
381 } catch (...) {
382 return mcp::core::unexpected(
383 errors::make(protocol::ErrorCode::InvalidRequest,
384 "failed to decode task result", {}, "protocol"));
385 }
386 }
387
388 core::Result<protocol::Task> wait(std::chrono::milliseconds timeout,
389 std::chrono::milliseconds poll_interval =
390 std::chrono::milliseconds(100)) {
391 if (peer_ == nullptr) {
392 return mcp::core::unexpected(detached_error());
393 }
394 if (poll_interval <= std::chrono::milliseconds::zero()) {
395 poll_interval = std::chrono::milliseconds(1);
396 }
397
398 const auto started_at = std::chrono::steady_clock::now();
399 while (true) {
400 if (is_terminal(task_.status)) {
401 return task_;
402 }
403 if (std::chrono::steady_clock::now() - started_at >= timeout) {
404 return mcp::core::unexpected(errors::request_timed_out(timeout));
405 }
406
407 auto task = poll();
408 if (!task) {
409 return mcp::core::unexpected(task.error());
410 }
411 if (is_terminal(task->status)) {
412 return *task;
413 }
414
415 const auto elapsed = std::chrono::steady_clock::now() - started_at;
416 if (elapsed >= timeout) {
417 return mcp::core::unexpected(errors::request_timed_out(timeout));
418 }
419 auto sleep_for = poll_interval;
420 const auto remaining =
421 std::chrono::duration_cast<std::chrono::milliseconds>(timeout -
422 elapsed);
423 if (remaining > std::chrono::milliseconds::zero() &&
424 remaining < sleep_for) {
425 sleep_for = remaining;
426 }
427 std::this_thread::sleep_for(sleep_for);
428 }
429 }
430
431 private:
432 static bool is_terminal(protocol::TaskStatus status) noexcept {
433 return status == protocol::TaskStatus::Completed ||
434 status == protocol::TaskStatus::Failed ||
435 status == protocol::TaskStatus::Cancelled;
436 }
437
438 static core::Error detached_error() {
439 return errors::make(protocol::ErrorCode::InvalidRequest,
440 "task handle is not attached to a client peer", {},
441 "request");
442 }
443
444 Peer* peer_ = nullptr;
445 protocol::Task task_;
446 };
447
449 explicit Peer(std::unique_ptr<client::Transport> transport)
450 : client_(std::move(transport)) {}
451
453 explicit Peer(std::unique_ptr<transport::ClientTransport> transport)
454 : native_transport_(std::move(transport)),
455 client_(detail::make_peer_client_transport_adapter(native_transport_)) {
456 }
457
459 explicit Peer(client::Client client) : client_(std::move(client)) {}
460
461#if defined(CXXMCP_ENABLE_HTTP)
462 static Peer connect_streamable_http(
463 client::Client::StreamableHttpEndpoint endpoint) {
464 return Peer(client::Client::connect_streamable_http(std::move(endpoint)));
465 }
466
467 static Peer connect_legacy_sse(
468 client::Client::StreamableHttpEndpoint endpoint) {
469 return Peer(client::Client::connect_legacy_sse(std::move(endpoint)));
470 }
471#endif
472
473 static Peer connect_stdio(client::Client::StdioEndpoint endpoint) {
474 return Peer(client::Client::connect_stdio(std::move(endpoint)));
475 }
476
478 static Builder builder();
479
480 CXXMCP_DEPRECATED(
481 "client() is a compatibility escape hatch; prefer ClientPeer methods")
482 client::Client& client() noexcept { return client_; }
483
484 CXXMCP_DEPRECATED(
485 "client() is a compatibility escape hatch; prefer ClientPeer methods")
486 const client::Client& client() const noexcept { return client_; }
487
488 bool uses_native_transport() const noexcept {
489 return native_transport_ != nullptr;
490 }
491
492 core::Result<core::Unit> start(
493 CancellationToken cancellation = CancellationToken::none()) {
494 if (native_transport_) {
495 return start_native_receive_loop(cancellation);
496 }
497 return client_.start();
498 }
499
500 void stop() noexcept {
501 if (native_transport_) {
502 (void)native_transport_->close();
503 return;
504 }
505 client_.stop();
506 }
507
508 core::Result<protocol::Json> initialize(std::string client_name = "cxxmcp",
509 std::string client_version = "0") {
510 if (native_transport_) {
511 auto payload =
512 request_json(std::string(protocol::InitializeMethod),
513 detail::make_peer_initialize_params(
514 std::move(client_name), std::move(client_version),
515 client_capabilities_));
516 if (!payload) {
517 return mcp::core::unexpected(payload.error());
518 }
519 auto checked = detail::require_peer_initialize_payload(*payload);
520 if (!checked) {
521 return mcp::core::unexpected(checked.error());
522 }
523 auto recorded = record_server_capabilities(*checked);
524 if (!recorded) {
525 return mcp::core::unexpected(recorded.error());
526 }
527 return *checked;
528 }
529 return client_.initialize(std::move(client_name),
530 std::move(client_version));
531 }
532
533 core::Result<protocol::Json> initialize(std::string client_name,
534 std::string client_version,
535 RequestOptions options) {
536 if (native_transport_) {
537 auto params = detail::make_peer_initialize_params(
538 std::move(client_name), std::move(client_version),
539 client_capabilities_);
540 if (options.protocol_version.has_value()) {
541 params["protocolVersion"] = *options.protocol_version;
542 }
543 if (options.meta.has_value()) {
544 params["_meta"] = *options.meta;
545 }
546
547 auto do_init =
548 [&](const protocol::Json& p) -> core::Result<protocol::Json> {
549 protocol::JsonRpcRequest request = protocol::make_request(
550 std::string(protocol::InitializeMethod), next_peer_request_id(), p);
551 request.transport_headers = options.headers;
552 request.protocol_version_override = options.protocol_version;
553 auto payload = raw_request(request);
554 if (!payload) {
555 return mcp::core::unexpected(payload.error());
556 }
557 return *payload;
558 };
559
560 auto payload = do_init(params);
561 if (!payload) {
562 // Version negotiation retry.
563 auto& err = payload.error();
564 if (!err.detail.empty()) {
565 auto detail_json = protocol::Json::parse(err.detail, nullptr, false);
566 std::string retry_version;
567 if (detail_json.is_object() &&
568 detail_json.contains("supportedVersion")) {
569 retry_version = detail_json["supportedVersion"].get<std::string>();
570 } else if (detail_json.is_object() &&
571 detail_json.contains("supported") &&
572 detail_json["supported"].is_array() &&
573 !detail_json["supported"].empty()) {
574 // Server returns supported versions as array
575 // (conformance harness format).
576 retry_version = detail_json["supported"][0].get<std::string>();
577 } else if (detail_json.is_string()) {
578 // Server returns unsupported version as string.
579 // Fall back to the default protocol version.
580 retry_version = std::string(protocol::McpProtocolVersion);
581 }
582 if (!retry_version.empty()) {
583 params["protocolVersion"] = retry_version;
584 if (options.meta.has_value()) {
585 params["_meta"] = *options.meta;
586 }
587 payload = do_init(params);
588 if (!payload) {
589 return mcp::core::unexpected(payload.error());
590 }
591 } else {
592 return mcp::core::unexpected(err);
593 }
594 } else {
595 return mcp::core::unexpected(err);
596 }
597 }
598 auto checked = detail::require_peer_initialize_payload(*payload);
599 if (!checked) {
600 return mcp::core::unexpected(checked.error());
601 }
602 auto recorded = record_server_capabilities(*checked);
603 if (!recorded) {
604 return mcp::core::unexpected(recorded.error());
605 }
606 return *checked;
607 }
608 return client_.initialize(std::move(client_name), std::move(client_version),
609 std::move(options));
610 }
611
612 std::optional<protocol::ServerCapabilities> server_capabilities() const {
613 return native_transport_ ? server_capabilities_
614 : client_.server_capabilities();
615 }
616
617 core::Result<core::Unit> notify_initialized() {
618 return raw_notification(protocol::make_notification(
619 std::string(protocol::InitializedMethod), protocol::Json::object()));
620 }
621
622 core::Result<core::Unit> notify_initialized(RequestOptions options) {
623 protocol::Json params = protocol::Json::object();
624 if (options.meta.has_value()) {
625 params["_meta"] = *options.meta;
626 }
627 protocol::JsonRpcNotification notification;
628 notification.method = std::string(protocol::InitializedMethod);
629 notification.params = std::move(params);
630 return raw_notification(std::move(notification));
631 }
632
633 core::Result<core::Unit> notify_cancelled(protocol::RequestId request_id,
634 std::string reason = {}) {
635 protocol::CancelledNotificationParams params;
636 params.request_id = std::move(request_id);
637 if (!reason.empty()) {
638 params.reason = std::move(reason);
639 }
640 return raw_notification(protocol::make_notification(
641 std::string(protocol::CancelledNotificationMethod),
642 protocol::cancelled_notification_params_to_json(params)));
643 }
644
645 core::Result<core::Unit> notify_progress(
646 protocol::ProgressToken progress_token, double progress,
647 std::optional<double> total = std::nullopt, std::string message = {}) {
648 protocol::ProgressNotificationParams params;
649 params.progress_token = std::move(progress_token);
650 params.progress = progress;
651 params.total = total;
652 if (!message.empty()) {
653 params.message = std::move(message);
654 }
655 return raw_notification(protocol::make_notification(
656 std::string(protocol::ProgressNotificationMethod),
657 protocol::progress_notification_params_to_json(params)));
658 }
659
660 core::Result<core::Unit> notify_roots_list_changed() {
661 return raw_notification(protocol::make_notification(
662 std::string(protocol::RootsListChangedNotificationMethod),
663 protocol::Json::object()));
664 }
665
666 core::Result<core::Unit> ping() {
667 return request_unit(std::string(protocol::PingMethod),
668 protocol::Json::object());
669 }
670
671 std::vector<protocol::Root> list_roots() const {
672 if (native_transport_) {
673 return roots_;
674 }
675 return client_.list_roots();
676 }
677
678 Peer& set_roots(std::vector<protocol::Root> roots) {
679 roots_ = roots;
680 client_.set_roots(std::move(roots));
681 return *this;
682 }
683
684 Peer& set_capabilities(protocol::ClientCapabilities capabilities) {
685 client_capabilities_ = capabilities;
686 client_.set_capabilities(std::move(capabilities));
687 return *this;
688 }
689
690 Peer& on_initialized(client::Client::InitializedHandler handler) {
691 initialized_handler_ = handler;
692 client_.on_initialized(std::move(handler));
693 return *this;
694 }
695
696 Peer& on_cancelled(client::Client::CancelledHandler handler) {
697 cancelled_handler_ = handler;
698 client_.on_cancelled(std::move(handler));
699 return *this;
700 }
701
702 Peer& on_logging_message(client::Client::LoggingMessageHandler handler) {
703 logging_message_handler_ = handler;
704 client_.on_logging_message(std::move(handler));
705 return *this;
706 }
707
708 Peer& on_tool_list_changed(client::Client::ListChangedHandler handler) {
709 tool_list_changed_handler_ = handler;
710 client_.on_tool_list_changed(std::move(handler));
711 return *this;
712 }
713
714 Peer& on_prompt_list_changed(client::Client::ListChangedHandler handler) {
715 prompt_list_changed_handler_ = handler;
716 client_.on_prompt_list_changed(std::move(handler));
717 return *this;
718 }
719
720 Peer& on_resource_list_changed(client::Client::ListChangedHandler handler) {
721 resource_list_changed_handler_ = handler;
722 client_.on_resource_list_changed(std::move(handler));
723 return *this;
724 }
725
726 Peer& on_resource_updated(client::Client::ResourceUpdatedHandler handler) {
727 resource_updated_handler_ = handler;
728 client_.on_resource_updated(std::move(handler));
729 return *this;
730 }
731
732 Peer& on_progress(client::Client::ProgressHandler handler) {
733 progress_handler_ = handler;
734 client_.on_progress(std::move(handler));
735 return *this;
736 }
737
738 Peer& on_elicitation_complete(
739 client::Client::ElicitationCompleteHandler handler) {
740 elicitation_complete_handler_ = handler;
741 client_.on_elicitation_complete(std::move(handler));
742 return *this;
743 }
744
745 Peer& on_task_status(client::Client::TaskStatusHandler handler) {
746 task_status_handler_ = handler;
747 client_.on_task_status(std::move(handler));
748 return *this;
749 }
750
751 Peer& on_roots_list_changed(client::Client::ListChangedHandler handler) {
752 roots_list_changed_handler_ = handler;
753 client_.on_roots_list_changed(std::move(handler));
754 return *this;
755 }
756
757 Peer& on_list_roots_request(client::Client::ListRootsRequestHandler handler) {
758 roots_list_request_handler_ = handler;
759 client_.on_list_roots_request(std::move(handler));
760 return *this;
761 }
762
763 Peer& on_list_roots_request(
764 client::Client::RootsListRequestCancellationHandler handler) {
765 roots_list_request_cancellation_handler_ = handler;
766 client_.on_list_roots_request(std::move(handler));
767 return *this;
768 }
769
770 Peer& on_create_message_request(
771 client::Client::CreateMessageRequestHandler handler) {
772 sampling_request_handler_ = handler;
773 client_.on_create_message_request(std::move(handler));
774 return *this;
775 }
776
777 Peer& on_create_message_request(
778 client::Client::SamplingRequestCancellationHandler handler) {
779 sampling_request_cancellation_handler_ = handler;
780 client_.on_create_message_request(std::move(handler));
781 return *this;
782 }
783
784 Peer& on_create_elicitation_request(
785 client::Client::CreateElicitationRequestHandler handler) {
786 elicitation_request_handler_ = handler;
787 client_.on_create_elicitation_request(std::move(handler));
788 return *this;
789 }
790
791 Peer& on_create_elicitation_request(
792 client::Client::ElicitationRequestCancellationHandler handler) {
793 elicitation_request_cancellation_handler_ = handler;
794 client_.on_create_elicitation_request(std::move(handler));
795 return *this;
796 }
797
798 Peer& on_custom_request(client::Client::CustomRequestHandler handler) {
799 custom_request_handler_ = handler;
800 client_.on_custom_request(std::move(handler));
801 return *this;
802 }
803
804 Peer& on_custom_request(
805 client::Client::CustomRequestCancellationHandler handler) {
806 custom_request_cancellation_handler_ = handler;
807 client_.on_custom_request(std::move(handler));
808 return *this;
809 }
810
811 Peer& on_raw_notification(client::Client::RawNotificationHandler handler) {
812 raw_notification_handler_ = handler;
813 client_.on_raw_notification(std::move(handler));
814 return *this;
815 }
816
817 Peer& set_handler(const client::ClientHandler& handler) {
818 if (handler.on_initialized) {
819 on_initialized(handler.on_initialized);
820 }
821 if (handler.on_cancelled) {
822 on_cancelled(handler.on_cancelled);
823 }
824 if (handler.on_logging_message) {
825 on_logging_message(handler.on_logging_message);
826 }
827 if (handler.on_tool_list_changed) {
828 on_tool_list_changed(handler.on_tool_list_changed);
829 }
830 if (handler.on_prompt_list_changed) {
831 on_prompt_list_changed(handler.on_prompt_list_changed);
832 }
833 if (handler.on_resource_list_changed) {
834 on_resource_list_changed(handler.on_resource_list_changed);
835 }
836 if (handler.on_resource_updated) {
837 on_resource_updated(handler.on_resource_updated);
838 }
839 if (handler.on_progress) {
840 on_progress(handler.on_progress);
841 }
842 if (handler.on_elicitation_complete) {
843 on_elicitation_complete(handler.on_elicitation_complete);
844 }
845 if (handler.on_task_status) {
846 on_task_status(handler.on_task_status);
847 }
848 if (handler.on_roots_list_changed) {
849 on_roots_list_changed(handler.on_roots_list_changed);
850 }
851 if (handler.on_list_roots_request) {
852 on_list_roots_request(handler.on_list_roots_request);
853 }
854 if (handler.on_list_roots_request_with_cancellation) {
855 on_list_roots_request(handler.on_list_roots_request_with_cancellation);
856 }
857 if (handler.on_create_message_request) {
858 on_create_message_request(handler.on_create_message_request);
859 }
860 if (handler.on_create_message_request_with_cancellation) {
861 on_create_message_request(
862 handler.on_create_message_request_with_cancellation);
863 }
864 if (handler.on_create_elicitation_request) {
865 on_create_elicitation_request(handler.on_create_elicitation_request);
866 }
867 if (handler.on_create_elicitation_request_with_cancellation) {
868 on_create_elicitation_request(
869 handler.on_create_elicitation_request_with_cancellation);
870 }
871 if (handler.on_custom_request) {
872 on_custom_request(handler.on_custom_request);
873 }
874 if (handler.on_custom_request_with_cancellation) {
875 on_custom_request(handler.on_custom_request_with_cancellation);
876 }
877 if (handler.on_roots_list_request) {
878 on_list_roots_request(handler.on_roots_list_request);
879 }
880 if (handler.on_roots_list_request_with_cancellation) {
881 on_list_roots_request(handler.on_roots_list_request_with_cancellation);
882 }
883 if (handler.on_sampling_request) {
884 on_create_message_request(handler.on_sampling_request);
885 }
886 if (handler.on_sampling_request_with_cancellation) {
887 on_create_message_request(handler.on_sampling_request_with_cancellation);
888 }
889 if (handler.on_elicitation_request) {
890 on_create_elicitation_request(handler.on_elicitation_request);
891 }
892 if (handler.on_elicitation_request_with_cancellation) {
893 on_create_elicitation_request(
894 handler.on_elicitation_request_with_cancellation);
895 }
896 if (handler.on_raw_notification) {
897 on_raw_notification(handler.on_raw_notification);
898 }
899 if (handler.on_custom_notification) {
900 on_raw_notification(handler.on_custom_notification);
901 }
902 return *this;
903 }
904
905 Peer& set_handler(const client::ClientHandlerInterface& handler) {
906 on_initialized([&handler]() { handler.on_initialized(); });
907 on_cancelled([&handler](const protocol::RequestId& request_id,
908 std::string_view reason) {
909 handler.on_cancelled(request_id, reason);
910 });
911 on_logging_message(
912 [&handler](std::string_view level, std::string_view message) {
913 handler.on_logging_message(level, message);
914 });
915 on_tool_list_changed([&handler]() { handler.on_tool_list_changed(); });
916 on_prompt_list_changed([&handler]() { handler.on_prompt_list_changed(); });
917 on_resource_list_changed(
918 [&handler]() { handler.on_resource_list_changed(); });
919 on_resource_updated([&handler](const std::string& uri) {
920 handler.on_resource_updated(uri);
921 });
922 on_progress([&handler](const protocol::ProgressNotificationParams& params) {
923 handler.on_progress(params);
924 });
925 on_elicitation_complete([&handler](std::string_view elicitation_id) {
926 handler.on_elicitation_complete(elicitation_id);
927 });
928 on_task_status([&handler](const protocol::Task& task) {
929 handler.on_task_status(task);
930 });
931 on_roots_list_changed([&handler]() { handler.on_roots_list_changed(); });
932 on_list_roots_request([&handler](CancellationToken cancellation)
933 -> core::Result<protocol::RootsListResult> {
934 const auto response = handler.on_list_roots_request(cancellation);
935 if (response.has_value()) {
936 return std::move(*response);
937 }
938 return mcp::core::unexpected(client::handler_method_not_found(
939 "client handler does not handle list_roots"));
940 });
941 on_create_message_request(
942 [&handler](const protocol::CreateMessageParams& params,
943 CancellationToken cancellation)
944 -> core::Result<protocol::CreateMessageResult> {
945 const auto response =
946 handler.on_create_message_request(params, cancellation);
947 if (response.has_value()) {
948 return std::move(*response);
949 }
950 return mcp::core::unexpected(client::handler_method_not_found(
951 "client handler does not handle create_message"));
952 });
953 on_create_elicitation_request(
954 [&handler](const protocol::CreateElicitationRequestParam& params,
955 CancellationToken cancellation)
956 -> core::Result<protocol::CreateElicitationResult> {
957 const auto response =
958 handler.on_create_elicitation_request(params, cancellation);
959 if (response.has_value()) {
960 return std::move(*response);
961 }
962 return mcp::core::unexpected(client::handler_method_not_found(
963 "client handler does not handle elicitation"));
964 });
965 on_custom_request([&handler](const protocol::JsonRpcRequest& request,
966 CancellationToken cancellation)
967 -> core::Result<protocol::Json> {
968 const auto response = handler.on_custom_request(request, cancellation);
969 if (response.has_value()) {
970 return std::move(*response);
971 }
972 return mcp::core::unexpected(client::handler_method_not_found(
973 "client handler does not handle custom request"));
974 });
975 on_raw_notification(
976 [&handler](const protocol::JsonRpcNotification& notification) {
977 handler.on_raw_notification(notification);
978 });
979 return *this;
980 }
981
982 core::Result<std::vector<protocol::ToolDefinition>> list_tools() {
983 const auto page = list_tools_page();
984 if (!page) {
985 return mcp::core::unexpected(page.error());
986 }
987 cache_tool_schemas(page->tools);
988 return page->tools;
989 }
990
991 core::Result<protocol::ToolsListResult> list_tools_page(
992 const protocol::PaginatedRequestParams& params = {}) {
993 if (!native_transport_) {
994 return client_.list_tools_page(params);
995 }
996 auto payload =
997 request_json(std::string(protocol::ToolsListMethod),
998 protocol::paginated_request_params_to_json(params));
999 if (!payload) {
1000 return mcp::core::unexpected(payload.error());
1001 }
1002 const auto result = protocol::tools_list_result_from_json(*payload);
1003 if (!result) {
1004 return mcp::core::unexpected(result.error());
1005 }
1006 return *result;
1007 }
1008
1009 core::Result<std::vector<protocol::ToolDefinition>> list_all_tools() {
1010 auto result = list_all_pages<protocol::ToolDefinition>(
1011 std::string(protocol::ToolsListMethod),
1012 [](const protocol::Json& payload) {
1013 return protocol::tools_list_result_from_json(payload);
1014 },
1015 [](const protocol::ToolsListResult& page,
1016 std::vector<protocol::ToolDefinition>& all) {
1017 all.insert(all.end(), page.tools.begin(), page.tools.end());
1018 });
1019 if (result) {
1020 cache_tool_schemas(*result);
1021 }
1022 return result;
1023 }
1024
1025 core::Result<protocol::ToolResult> call_tool(const protocol::ToolCall& call) {
1026 auto request = protocol::make_request(
1027 std::string(protocol::ToolsCallMethod), next_peer_request_id(),
1028 protocol::tool_call_to_json(call));
1029 apply_x_mcp_headers(request, call);
1030 auto payload = raw_request(request);
1031 if (!payload) {
1032 return mcp::core::unexpected(payload.error());
1033 }
1034 return protocol::tool_result_from_json(*payload);
1035 }
1036
1037 core::Result<protocol::CreateTaskResult> call_tool_task(
1038 const protocol::ToolCall& call) {
1039 if (!call.task.has_value()) {
1040 return mcp::core::unexpected(errors::make(
1041 protocol::ErrorCode::InvalidRequest,
1042 "task-aware tool call requires task parameters", {}, "protocol"));
1043 }
1044 if (!supports_server_task_tool_call()) {
1045 return mcp::core::unexpected(unsupported_server_capability(
1046 "server does not support task-aware tool calls"));
1047 }
1048 auto payload = request_json(std::string(protocol::ToolsCallMethod),
1049 protocol::tool_call_to_json(call));
1050 if (!payload) {
1051 return mcp::core::unexpected(payload.error());
1052 }
1053 return protocol::create_task_result_from_json(*payload);
1054 }
1055
1056 core::Result<TaskHandle> call_tool_task_handle(
1057 const protocol::ToolCall& call) {
1058 auto task = call_tool_task(call);
1059 if (!task) {
1060 return mcp::core::unexpected(task.error());
1061 }
1062 return TaskHandle(*this, task->task);
1063 }
1064
1065 core::Result<protocol::ToolResult> call_tool(
1066 std::string_view name,
1067 const protocol::Json& arguments = protocol::Json::object()) {
1068 protocol::ToolCall call;
1069 call.name = std::string(name);
1070 call.arguments = arguments;
1071 return call_tool(call);
1072 }
1073
1074 core::Result<std::vector<protocol::Prompt>> list_prompts() {
1075 const auto page = list_prompts_page();
1076 if (!page) {
1077 return mcp::core::unexpected(page.error());
1078 }
1079 return page->prompts;
1080 }
1081
1082 core::Result<protocol::PromptsListResult> list_prompts_page(
1083 const protocol::PaginatedRequestParams& params = {}) {
1084 if (!native_transport_) {
1085 return client_.list_prompts_page(params);
1086 }
1087 auto payload =
1088 request_json(std::string(protocol::PromptsListMethod),
1089 protocol::paginated_request_params_to_json(params));
1090 if (!payload) {
1091 return mcp::core::unexpected(payload.error());
1092 }
1093 const auto result = protocol::prompts_list_result_from_json(*payload);
1094 if (!result) {
1095 return mcp::core::unexpected(result.error());
1096 }
1097 return *result;
1098 }
1099
1100 core::Result<std::vector<protocol::Prompt>> list_all_prompts() {
1101 return list_all_pages<protocol::Prompt>(
1102 std::string(protocol::PromptsListMethod),
1103 [](const protocol::Json& payload) {
1104 return protocol::prompts_list_result_from_json(payload);
1105 },
1106 [](const protocol::PromptsListResult& page,
1107 std::vector<protocol::Prompt>& all) {
1108 all.insert(all.end(), page.prompts.begin(), page.prompts.end());
1109 });
1110 }
1111
1112 core::Result<protocol::PromptsGetResult> get_prompt(
1113 const protocol::PromptsGetParams& params) {
1114 auto payload = request_json(std::string(protocol::PromptsGetMethod),
1115 protocol::prompts_get_params_to_json(params));
1116 if (!payload) {
1117 return mcp::core::unexpected(payload.error());
1118 }
1119 return protocol::prompts_get_result_from_json(*payload);
1120 }
1121
1122 core::Result<protocol::PromptsGetResult> get_prompt(
1123 std::string_view name,
1124 const protocol::Json& arguments = protocol::Json::object()) {
1125 protocol::PromptsGetParams params;
1126 params.name = std::string(name);
1127 params.arguments = arguments;
1128 return get_prompt(params);
1129 }
1130
1131 core::Result<std::vector<protocol::Resource>> list_resources() {
1132 const auto page = list_resources_page();
1133 if (!page) {
1134 return mcp::core::unexpected(page.error());
1135 }
1136 return page->resources;
1137 }
1138
1139 core::Result<protocol::ResourcesListResult> list_resources_page(
1140 const protocol::PaginatedRequestParams& params = {}) {
1141 if (!native_transport_) {
1142 return client_.list_resources_page(params);
1143 }
1144 auto payload =
1145 request_json(std::string(protocol::ResourcesListMethod),
1146 protocol::paginated_request_params_to_json(params));
1147 if (!payload) {
1148 return mcp::core::unexpected(payload.error());
1149 }
1150 const auto result = protocol::resources_list_result_from_json(*payload);
1151 if (!result) {
1152 return mcp::core::unexpected(result.error());
1153 }
1154 return *result;
1155 }
1156
1157 core::Result<std::vector<protocol::Resource>> list_all_resources() {
1158 return list_all_pages<protocol::Resource>(
1159 std::string(protocol::ResourcesListMethod),
1160 [](const protocol::Json& payload) {
1161 return protocol::resources_list_result_from_json(payload);
1162 },
1163 [](const protocol::ResourcesListResult& page,
1164 std::vector<protocol::Resource>& all) {
1165 all.insert(all.end(), page.resources.begin(), page.resources.end());
1166 });
1167 }
1168
1169 core::Result<std::vector<protocol::ResourceTemplate>>
1170 list_resource_templates() {
1171 const auto page = list_resource_templates_page();
1172 if (!page) {
1173 return mcp::core::unexpected(page.error());
1174 }
1175 return page->resource_templates;
1176 }
1177
1178 core::Result<protocol::ResourceTemplatesListResult>
1179 list_resource_templates_page(
1180 const protocol::PaginatedRequestParams& params = {}) {
1181 if (!native_transport_) {
1182 return client_.list_resource_templates_page(params);
1183 }
1184 auto payload =
1185 request_json(std::string(protocol::ResourcesTemplatesListMethod),
1186 protocol::paginated_request_params_to_json(params));
1187 if (!payload) {
1188 return mcp::core::unexpected(payload.error());
1189 }
1190 const auto result =
1191 protocol::resource_templates_list_result_from_json(*payload);
1192 if (!result) {
1193 return mcp::core::unexpected(result.error());
1194 }
1195 return *result;
1196 }
1197
1198 core::Result<std::vector<protocol::ResourceTemplate>>
1199 list_all_resource_templates() {
1200 return list_all_pages<protocol::ResourceTemplate>(
1201 std::string(protocol::ResourcesTemplatesListMethod),
1202 [](const protocol::Json& payload) {
1203 return protocol::resource_templates_list_result_from_json(payload);
1204 },
1205 [](const protocol::ResourceTemplatesListResult& page,
1206 std::vector<protocol::ResourceTemplate>& all) {
1207 all.insert(all.end(), page.resource_templates.begin(),
1208 page.resource_templates.end());
1209 });
1210 }
1211
1212 core::Result<protocol::ResourcesReadResult> read_resource(
1213 const protocol::ResourcesReadParams& params) {
1214 auto payload =
1215 request_json(std::string(protocol::ResourcesReadMethod),
1216 protocol::resources_read_params_to_json(params));
1217 if (!payload) {
1218 return mcp::core::unexpected(payload.error());
1219 }
1220 return protocol::resources_read_result_from_json(*payload);
1221 }
1222
1223 core::Result<protocol::ResourcesReadResult> read_resource(
1224 std::string_view uri) {
1225 return read_resource(protocol::ResourcesReadParams{std::string(uri)});
1226 }
1227
1228 core::Result<protocol::CompleteResult> complete(
1229 const protocol::CompleteParams& request) {
1230 if (!supports_server_completion()) {
1231 return mcp::core::unexpected(
1232 unsupported_server_capability("server does not support completion"));
1233 }
1234 auto payload = request_json(std::string(protocol::CompletionCompleteMethod),
1235 protocol::complete_params_to_json(request));
1236 if (!payload) {
1237 return mcp::core::unexpected(payload.error());
1238 }
1239 return protocol::complete_result_from_json(*payload);
1240 }
1241
1242 core::Result<protocol::Json> complete(const protocol::Json& request) {
1243 if (!supports_server_completion()) {
1244 return mcp::core::unexpected(
1245 unsupported_server_capability("server does not support completion"));
1246 }
1247 return request_json(std::string(protocol::CompletionCompleteMethod),
1248 request);
1249 }
1250
1251 core::Result<protocol::CompletionResult> complete_prompt_argument(
1252 std::string_view prompt_name, std::string_view argument_name,
1253 std::string current_value,
1254 protocol::Json context = protocol::Json::object()) {
1255 protocol::CompletionArgument argument;
1256 argument.name = std::string(argument_name);
1257 argument.value = std::move(current_value);
1258 protocol::CompleteParams params;
1259 params.ref =
1260 protocol::prompt_completion_reference(std::string(prompt_name));
1261 params.argument = std::move(argument);
1262 params.context = std::move(context);
1263 const auto result = complete(params);
1264 if (!result) {
1265 return mcp::core::unexpected(result.error());
1266 }
1267 return result->completion;
1268 }
1269
1270 core::Result<protocol::CompletionResult> complete_resource_argument(
1271 std::string_view uri_template, std::string_view argument_name,
1272 std::string current_value,
1273 protocol::Json context = protocol::Json::object()) {
1274 protocol::CompletionArgument argument;
1275 argument.name = std::string(argument_name);
1276 argument.value = std::move(current_value);
1277 protocol::CompleteParams params;
1278 params.ref =
1279 protocol::resource_completion_reference(std::string(uri_template));
1280 params.argument = std::move(argument);
1281 params.context = std::move(context);
1282 const auto result = complete(params);
1283 if (!result) {
1284 return mcp::core::unexpected(result.error());
1285 }
1286 return result->completion;
1287 }
1288
1289 core::Result<std::vector<std::string>> complete_prompt_simple(
1290 std::string_view prompt_name, std::string_view argument_name,
1291 std::string current_value,
1292 protocol::Json context = protocol::Json::object()) {
1293 const auto completion =
1294 complete_prompt_argument(prompt_name, argument_name,
1295 std::move(current_value), std::move(context));
1296 if (!completion) {
1297 return mcp::core::unexpected(completion.error());
1298 }
1299 return completion->values;
1300 }
1301
1302 core::Result<std::vector<std::string>> complete_resource_simple(
1303 std::string_view uri_template, std::string_view argument_name,
1304 std::string current_value,
1305 protocol::Json context = protocol::Json::object()) {
1306 const auto completion = complete_resource_argument(
1307 uri_template, argument_name, std::move(current_value),
1308 std::move(context));
1309 if (!completion) {
1310 return mcp::core::unexpected(completion.error());
1311 }
1312 return completion->values;
1313 }
1314
1315 core::Result<protocol::CreateMessageResult> create_message(
1316 const protocol::CreateMessageParams& request) {
1317 auto payload =
1318 request_json(std::string(protocol::SamplingCreateMessageMethod),
1319 protocol::create_message_params_to_json(request));
1320 if (!payload) {
1321 return mcp::core::unexpected(payload.error());
1322 }
1323 return protocol::create_message_result_from_json(*payload);
1324 }
1325
1326 core::Result<protocol::Json> create_message(const protocol::Json& request) {
1327 return request_json(std::string(protocol::SamplingCreateMessageMethod),
1328 request);
1329 }
1330
1331 core::Result<protocol::CreateElicitationResult> create_elicitation(
1332 const protocol::CreateElicitationRequestParam& request) {
1333 auto payload = request_json(
1334 std::string(protocol::ElicitationCreateMethod),
1335 protocol::create_elicitation_request_param_to_json(request));
1336 if (!payload) {
1337 return mcp::core::unexpected(payload.error());
1338 }
1339 return protocol::create_elicitation_result_from_json(*payload);
1340 }
1341
1342 core::Result<protocol::Json> create_elicitation(
1343 const protocol::Json& request) {
1344 return request_json(std::string(protocol::ElicitationCreateMethod),
1345 request);
1346 }
1347
1348 core::Result<std::vector<protocol::Task>> list_tasks() {
1349 const auto page = list_tasks_page();
1350 if (!page) {
1351 return mcp::core::unexpected(page.error());
1352 }
1353 return page->tasks;
1354 }
1355
1356 core::Result<protocol::TaskListResult> list_tasks_page(
1357 const protocol::TaskListParams& params = {}) {
1358 if (!supports_server_task_list()) {
1359 return mcp::core::unexpected(unsupported_server_capability(
1360 "server does not support task listing"));
1361 }
1362 if (!native_transport_) {
1363 return client_.list_tasks_page(params);
1364 }
1365 auto payload = request_json(std::string(protocol::TasksListMethod),
1366 protocol::task_list_params_to_json(params));
1367 if (!payload) {
1368 return mcp::core::unexpected(payload.error());
1369 }
1370 const auto result = protocol::task_list_result_from_json(*payload);
1371 if (!result) {
1372 return mcp::core::unexpected(result.error());
1373 }
1374 return *result;
1375 }
1376
1377 core::Result<std::vector<protocol::Task>> list_all_tasks() {
1378 if (!supports_server_task_list()) {
1379 return mcp::core::unexpected(unsupported_server_capability(
1380 "server does not support task listing"));
1381 }
1382 return list_all_pages<protocol::Task>(
1383 std::string(protocol::TasksListMethod),
1384 [](const protocol::Json& payload) {
1385 return protocol::task_list_result_from_json(payload);
1386 },
1387 [](const protocol::TaskListResult& page,
1388 std::vector<protocol::Task>& all) {
1389 all.insert(all.end(), page.tasks.begin(), page.tasks.end());
1390 });
1391 }
1392
1393 core::Result<protocol::Task> get_task(std::string_view task_id) {
1394 if (!supports_server_tasks()) {
1395 return mcp::core::unexpected(
1396 unsupported_server_capability("server does not support tasks"));
1397 }
1398 protocol::TaskGetParams params;
1399 params.task_id = std::string(task_id);
1400 auto payload = request_json(std::string(protocol::TasksGetMethod),
1401 protocol::task_get_params_to_json(params));
1402 if (!payload) {
1403 return mcp::core::unexpected(payload.error());
1404 }
1405 return protocol::task_from_json(*payload);
1406 }
1407
1408 core::Result<protocol::Task> cancel_task(std::string_view task_id) {
1409 if (!supports_server_task_cancel()) {
1410 return mcp::core::unexpected(unsupported_server_capability(
1411 "server does not support task cancellation"));
1412 }
1413 protocol::TaskCancelParams params;
1414 params.task_id = std::string(task_id);
1415 auto payload = request_json(std::string(protocol::TasksCancelMethod),
1416 protocol::task_cancel_params_to_json(params));
1417 if (!payload) {
1418 return mcp::core::unexpected(payload.error());
1419 }
1420 return protocol::task_from_json(*payload);
1421 }
1422
1423 core::Result<protocol::Json> task_result(std::string_view task_id) {
1424 if (!supports_server_tasks()) {
1425 return mcp::core::unexpected(
1426 unsupported_server_capability("server does not support tasks"));
1427 }
1428 protocol::TaskResultParams params;
1429 params.task_id = std::string(task_id);
1430 return request_json(std::string(protocol::TasksResultMethod),
1431 protocol::task_result_params_to_json(params));
1432 }
1433
1434 core::Result<core::Unit> set_level(std::string_view level) {
1435 const auto parsed = protocol::logging_level_from_string(std::string(level));
1436 if (!parsed.has_value()) {
1437 return mcp::core::unexpected(
1438 errors::make(protocol::ErrorCode::InvalidRequest,
1439 "logging/setLevel level is invalid", {}, "protocol"));
1440 }
1441 if (!supports_server_logging()) {
1442 return mcp::core::unexpected(
1443 unsupported_server_capability("server does not support logging"));
1444 }
1445 protocol::LoggingSetLevelParams params;
1446 params.level = *parsed;
1447 return request_unit(std::string(protocol::LoggingSetLevelMethod),
1448 protocol::logging_set_level_params_to_json(params));
1449 }
1450
1451 core::Result<core::Unit> subscribe(std::string_view uri) {
1452 if (!supports_server_resource_subscribe()) {
1453 return mcp::core::unexpected(unsupported_server_capability(
1454 "server does not support resource subscriptions"));
1455 }
1456 protocol::ResourcesSubscribeParams params;
1457 params.uri = std::string(uri);
1458 return request_unit(std::string(protocol::ResourcesSubscribeMethod),
1459 protocol::resources_subscribe_params_to_json(params));
1460 }
1461
1462 core::Result<core::Unit> unsubscribe(std::string_view uri) {
1463 if (!supports_server_resource_subscribe()) {
1464 return mcp::core::unexpected(unsupported_server_capability(
1465 "server does not support resource subscriptions"));
1466 }
1467 protocol::ResourcesUnsubscribeParams params;
1468 params.uri = std::string(uri);
1469 return request_unit(std::string(protocol::ResourcesUnsubscribeMethod),
1470 protocol::resources_unsubscribe_params_to_json(params));
1471 }
1472
1473 core::Result<protocol::Json> raw_request(
1474 const protocol::JsonRpcRequest& request) {
1475 if (native_transport_) {
1476 const auto response = send_native_request(request);
1477 if (!response) {
1478 return mcp::core::unexpected(response.error());
1479 }
1480 return detail::peer_require_result_payload(*response);
1481 }
1482 return client_.raw_request(request);
1483 }
1484
1485 RequestHandle<protocol::Json> request_async(
1486 std::string method, protocol::Json params = protocol::Json::object(),
1487 RequestOptions options = {}) {
1488 if (native_transport_) {
1489 protocol::JsonRpcRequest request = protocol::make_request(
1490 std::move(method), next_peer_request_id(), std::move(params));
1491 if (options.meta.has_value()) {
1492 request.meta = std::move(options.meta);
1493 }
1494 request.transport_headers = std::move(options.headers);
1495 request.protocol_version_override = std::move(options.protocol_version);
1496
1497 const auto request_id = request.id;
1498 return RequestHandle<protocol::Json>::spawn(
1499 request_id, options.timeout, options.cancellation_token,
1500 [this, request_id](std::string reason) mutable {
1501 return notify_cancelled(std::move(request_id), std::move(reason));
1502 },
1503 [this, request = std::move(request)]() mutable {
1504 return raw_request(request);
1505 });
1506 }
1507 return client_.request_async(std::move(method), std::move(params),
1508 std::move(options));
1509 }
1510
1511 template <class T, class Parser>
1512 RequestHandle<T> request_async(std::string method, protocol::Json params,
1513 Parser parser, RequestOptions options = {}) {
1514 if (native_transport_) {
1515 protocol::JsonRpcRequest request = protocol::make_request(
1516 std::move(method), next_peer_request_id(), std::move(params));
1517 if (options.meta.has_value()) {
1518 request.meta = std::move(options.meta);
1519 }
1520 request.transport_headers = std::move(options.headers);
1521 request.protocol_version_override = std::move(options.protocol_version);
1522
1523 const auto request_id = request.id;
1524 return RequestHandle<T>::spawn(
1525 request_id, options.timeout, options.cancellation_token,
1526 [this, request_id](std::string reason) mutable {
1527 return notify_cancelled(std::move(request_id), std::move(reason));
1528 },
1529 [this, request = std::move(request),
1530 parser = std::move(parser)]() mutable -> core::Result<T> {
1531 auto payload = raw_request(request);
1532 if (!payload) {
1533 return mcp::core::unexpected(payload.error());
1534 }
1535 return parser(*payload);
1536 });
1537 }
1538 return client_.request_async<T>(std::move(method), std::move(params),
1539 std::move(parser), std::move(options));
1540 }
1541
1542 RequestHandle<std::vector<protocol::ToolDefinition>> list_tools_async(
1543 RequestOptions options = {}) {
1544 return request_async<std::vector<protocol::ToolDefinition>>(
1545 std::string(protocol::ToolsListMethod), protocol::Json::object(),
1546 [](const protocol::Json& payload)
1547 -> core::Result<std::vector<protocol::ToolDefinition>> {
1548 const auto result = protocol::tools_list_result_from_json(payload);
1549 if (!result) {
1550 return mcp::core::unexpected(result.error());
1551 }
1552 return result->tools;
1553 },
1554 std::move(options));
1555 }
1556
1557 RequestHandle<std::vector<protocol::Prompt>> list_prompts_async(
1558 RequestOptions options = {}) {
1559 return request_async<std::vector<protocol::Prompt>>(
1560 std::string(protocol::PromptsListMethod), protocol::Json::object(),
1561 [](const protocol::Json& payload)
1562 -> core::Result<std::vector<protocol::Prompt>> {
1563 const auto result = protocol::prompts_list_result_from_json(payload);
1564 if (!result) {
1565 return mcp::core::unexpected(result.error());
1566 }
1567 return result->prompts;
1568 },
1569 std::move(options));
1570 }
1571
1572 RequestHandle<std::vector<protocol::Resource>> list_resources_async(
1573 RequestOptions options = {}) {
1574 return request_async<std::vector<protocol::Resource>>(
1575 std::string(protocol::ResourcesListMethod), protocol::Json::object(),
1576 [](const protocol::Json& payload)
1577 -> core::Result<std::vector<protocol::Resource>> {
1578 const auto result =
1579 protocol::resources_list_result_from_json(payload);
1580 if (!result) {
1581 return mcp::core::unexpected(result.error());
1582 }
1583 return result->resources;
1584 },
1585 std::move(options));
1586 }
1587
1588 RequestHandle<std::vector<protocol::ResourceTemplate>>
1589 list_resource_templates_async(RequestOptions options = {}) {
1590 return request_async<std::vector<protocol::ResourceTemplate>>(
1591 std::string(protocol::ResourcesTemplatesListMethod),
1592 protocol::Json::object(),
1593 [](const protocol::Json& payload)
1594 -> core::Result<std::vector<protocol::ResourceTemplate>> {
1595 const auto result =
1596 protocol::resource_templates_list_result_from_json(payload);
1597 if (!result) {
1598 return mcp::core::unexpected(result.error());
1599 }
1600 return result->resource_templates;
1601 },
1602 std::move(options));
1603 }
1604
1605 RequestHandle<protocol::ToolResult> call_tool_async(
1606 const protocol::ToolCall& call, RequestOptions options = {}) {
1607 return request_async<protocol::ToolResult>(
1608 std::string(protocol::ToolsCallMethod),
1609 protocol::tool_call_to_json(call),
1610 [](const protocol::Json& payload) {
1611 return protocol::tool_result_from_json(payload);
1612 },
1613 std::move(options));
1614 }
1615
1616 RequestHandle<protocol::ToolResult> call_tool_async(
1617 std::string_view name,
1618 const protocol::Json& arguments = protocol::Json::object(),
1619 RequestOptions options = {}) {
1620 protocol::ToolCall call;
1621 call.name = std::string(name);
1622 call.arguments = arguments;
1623 return call_tool_async(call, std::move(options));
1624 }
1625
1626 RequestHandle<protocol::CreateTaskResult> call_tool_task_async(
1627 const protocol::ToolCall& call, RequestOptions options = {}) {
1628 if (!call.task.has_value()) {
1630 next_peer_request_id(),
1631 mcp::core::unexpected(
1632 errors::make(protocol::ErrorCode::InvalidRequest,
1633 "task-aware tool call requires task parameters", {},
1634 "protocol")));
1635 }
1636 if (!supports_server_task_tool_call()) {
1638 next_peer_request_id(),
1639 mcp::core::unexpected(unsupported_server_capability(
1640 "server does not support task-aware tool calls")));
1641 }
1642
1643 return request_async<protocol::CreateTaskResult>(
1644 std::string(protocol::ToolsCallMethod),
1645 protocol::tool_call_to_json(call),
1646 [](const protocol::Json& payload) {
1647 return protocol::create_task_result_from_json(payload);
1648 },
1649 std::move(options));
1650 }
1651
1652 RequestHandle<protocol::PromptsGetResult> get_prompt_async(
1653 const protocol::PromptsGetParams& params, RequestOptions options = {}) {
1654 return request_async<protocol::PromptsGetResult>(
1655 std::string(protocol::PromptsGetMethod),
1656 protocol::prompts_get_params_to_json(params),
1657 [](const protocol::Json& payload) {
1658 return protocol::prompts_get_result_from_json(payload);
1659 },
1660 std::move(options));
1661 }
1662
1663 RequestHandle<protocol::PromptsGetResult> get_prompt_async(
1664 std::string_view name,
1665 const protocol::Json& arguments = protocol::Json::object(),
1666 RequestOptions options = {}) {
1667 protocol::PromptsGetParams params;
1668 params.name = std::string(name);
1669 params.arguments = arguments;
1670 return get_prompt_async(params, std::move(options));
1671 }
1672
1673 RequestHandle<protocol::ResourcesReadResult> read_resource_async(
1674 const protocol::ResourcesReadParams& params,
1675 RequestOptions options = {}) {
1676 return request_async<protocol::ResourcesReadResult>(
1677 std::string(protocol::ResourcesReadMethod),
1678 protocol::resources_read_params_to_json(params),
1679 [](const protocol::Json& payload) {
1680 return protocol::resources_read_result_from_json(payload);
1681 },
1682 std::move(options));
1683 }
1684
1685 RequestHandle<protocol::ResourcesReadResult> read_resource_async(
1686 std::string_view uri, RequestOptions options = {}) {
1687 return read_resource_async(protocol::ResourcesReadParams{std::string(uri)},
1688 std::move(options));
1689 }
1690
1691 RequestHandle<protocol::CompleteResult> complete_async(
1692 const protocol::CompleteParams& request, RequestOptions options = {}) {
1693 if (!supports_server_completion()) {
1695 next_peer_request_id(),
1696 mcp::core::unexpected(unsupported_server_capability(
1697 "server does not support completion")));
1698 }
1699 return request_async<protocol::CompleteResult>(
1700 std::string(protocol::CompletionCompleteMethod),
1701 protocol::complete_params_to_json(request),
1702 [](const protocol::Json& payload) {
1703 return protocol::complete_result_from_json(payload);
1704 },
1705 std::move(options));
1706 }
1707
1708 RequestHandle<protocol::Json> complete_async(const protocol::Json& request,
1709 RequestOptions options = {}) {
1710 if (!supports_server_completion()) {
1712 next_peer_request_id(),
1713 mcp::core::unexpected(unsupported_server_capability(
1714 "server does not support completion")));
1715 }
1716 return request_async(std::string(protocol::CompletionCompleteMethod),
1717 request, std::move(options));
1718 }
1719
1720 RequestHandle<protocol::CreateMessageResult> create_message_async(
1721 const protocol::CreateMessageParams& request,
1722 RequestOptions options = {}) {
1723 return request_async<protocol::CreateMessageResult>(
1724 std::string(protocol::SamplingCreateMessageMethod),
1725 protocol::create_message_params_to_json(request),
1726 [](const protocol::Json& payload) {
1727 return protocol::create_message_result_from_json(payload);
1728 },
1729 std::move(options));
1730 }
1731
1732 RequestHandle<protocol::Json> create_message_async(
1733 const protocol::Json& request, RequestOptions options = {}) {
1734 return request_async(std::string(protocol::SamplingCreateMessageMethod),
1735 request, std::move(options));
1736 }
1737
1738 RequestHandle<protocol::CreateElicitationResult> create_elicitation_async(
1739 const protocol::CreateElicitationRequestParam& request,
1740 RequestOptions options = {}) {
1741 return request_async<protocol::CreateElicitationResult>(
1742 std::string(protocol::ElicitationCreateMethod),
1743 protocol::create_elicitation_request_param_to_json(request),
1744 [](const protocol::Json& payload) {
1745 return protocol::create_elicitation_result_from_json(payload);
1746 },
1747 std::move(options));
1748 }
1749
1750 RequestHandle<protocol::Json> create_elicitation_async(
1751 const protocol::Json& request, RequestOptions options = {}) {
1752 return request_async(std::string(protocol::ElicitationCreateMethod),
1753 request, std::move(options));
1754 }
1755
1756 RequestHandle<std::vector<protocol::Task>> list_tasks_async(
1757 RequestOptions options = {}) {
1758 if (!supports_server_task_list()) {
1759 return RequestHandle<std::vector<protocol::Task>>::ready(
1760 next_peer_request_id(),
1761 mcp::core::unexpected(unsupported_server_capability(
1762 "server does not support task listing")));
1763 }
1764 return request_async<std::vector<protocol::Task>>(
1765 std::string(protocol::TasksListMethod), protocol::Json::object(),
1766 [](const protocol::Json& payload)
1767 -> core::Result<std::vector<protocol::Task>> {
1768 const auto result = protocol::task_list_result_from_json(payload);
1769 if (!result) {
1770 return mcp::core::unexpected(result.error());
1771 }
1772 return result->tasks;
1773 },
1774 std::move(options));
1775 }
1776
1777 RequestHandle<protocol::Task> get_task_async(
1778 const protocol::TaskGetParams& request, RequestOptions options = {}) {
1779 if (!supports_server_tasks()) {
1781 next_peer_request_id(),
1782 mcp::core::unexpected(
1783 unsupported_server_capability("server does not support tasks")));
1784 }
1785 return request_async<protocol::Task>(
1786 std::string(protocol::TasksGetMethod),
1787 protocol::task_get_params_to_json(request),
1788 [](const protocol::Json& payload) {
1789 return protocol::task_from_json(payload);
1790 },
1791 std::move(options));
1792 }
1793
1794 RequestHandle<protocol::Task> get_task_async(std::string_view task_id,
1795 RequestOptions options = {}) {
1796 protocol::TaskGetParams params;
1797 params.task_id = std::string(task_id);
1798 return get_task_async(params, std::move(options));
1799 }
1800
1801 RequestHandle<protocol::Task> cancel_task_async(
1802 const protocol::TaskCancelParams& request, RequestOptions options = {}) {
1803 if (!supports_server_task_cancel()) {
1805 next_peer_request_id(),
1806 mcp::core::unexpected(unsupported_server_capability(
1807 "server does not support task cancellation")));
1808 }
1809 return request_async<protocol::Task>(
1810 std::string(protocol::TasksCancelMethod),
1811 protocol::task_cancel_params_to_json(request),
1812 [](const protocol::Json& payload) {
1813 return protocol::task_from_json(payload);
1814 },
1815 std::move(options));
1816 }
1817
1818 RequestHandle<protocol::Task> cancel_task_async(std::string_view task_id,
1819 RequestOptions options = {}) {
1820 protocol::TaskCancelParams params;
1821 params.task_id = std::string(task_id);
1822 return cancel_task_async(params, std::move(options));
1823 }
1824
1825 RequestHandle<protocol::Json> task_result_async(
1826 const protocol::TaskResultParams& request, RequestOptions options = {}) {
1827 if (!supports_server_tasks()) {
1829 next_peer_request_id(),
1830 mcp::core::unexpected(
1831 unsupported_server_capability("server does not support tasks")));
1832 }
1833 return request_async(std::string(protocol::TasksResultMethod),
1834 protocol::task_result_params_to_json(request),
1835 std::move(options));
1836 }
1837
1838 RequestHandle<protocol::Json> task_result_async(std::string_view task_id,
1839 RequestOptions options = {}) {
1840 protocol::TaskResultParams params;
1841 params.task_id = std::string(task_id);
1842 return task_result_async(params, std::move(options));
1843 }
1844
1845 core::Result<core::Unit> raw_notification(
1846 const protocol::JsonRpcNotification& notification) {
1847 if (native_transport_) {
1848 return native_transport_->send(protocol::JsonRpcMessage{notification});
1849 }
1850 return client_.raw_notification(notification);
1851 }
1852
1859 const protocol::JsonRpcMessage& message) {
1860 if (const auto* request = std::get_if<protocol::JsonRpcRequest>(&message)) {
1861 auto handled = native_transport_ ? handle_native_request(*request)
1862 : client_.handle_request(*request);
1863 if (!handled) {
1864 return protocol::JsonRpcMessage{protocol::make_error_response(
1865 request->id,
1866 detail::peer_error_object_from_core_error(handled.error()))};
1867 }
1868 return protocol::JsonRpcMessage{std::move(*handled)};
1869 }
1870
1871 if (const auto* notification =
1872 std::get_if<protocol::JsonRpcNotification>(&message)) {
1873 const auto handled = native_transport_
1874 ? handle_native_notification(*notification)
1875 : client_.handle_notification(*notification);
1876 if (!handled) {
1877 return mcp::core::unexpected(handled.error());
1878 }
1879 return std::nullopt;
1880 }
1881
1882 if (const auto* response =
1883 std::get_if<protocol::JsonRpcResponse>(&message)) {
1884 if (native_transport_) {
1885 auto stored = store_native_response(*response);
1886 if (!stored) {
1887 return mcp::core::unexpected(stored.error());
1888 }
1889 return std::nullopt;
1890 }
1891 }
1892
1893 return mcp::core::unexpected(detail::peer_dispatch_error(
1894 "client peer cannot dispatch an uncorrelated response"));
1895 }
1896
1900 transport::ClientTransport& transport,
1901 CancellationToken cancellation = CancellationToken::none()) {
1902 return detail::serve_transport_loop(
1903 transport, cancellation,
1904 [this](const protocol::JsonRpcMessage& message) {
1905 return dispatch_message(message);
1906 });
1907 }
1908
1909 private:
1910 std::int64_t next_peer_request_id() noexcept {
1911 return next_request_id_->fetch_add(1, std::memory_order_relaxed);
1912 }
1913
1914 void cache_tool_schemas(const std::vector<protocol::ToolDefinition>& tools) {
1915 for (const auto& tool : tools) {
1916 tool_schema_cache_[tool.name] = tool.input_schema;
1917 }
1918 }
1919
1920 void apply_x_mcp_headers(protocol::JsonRpcRequest& request,
1921 const protocol::ToolCall& call) {
1922 auto it = tool_schema_cache_.find(call.name);
1923 if (it == tool_schema_cache_.end()) return;
1924 auto entries = protocol::extract_x_mcp_headers(it->second);
1925 if (entries.empty()) return;
1926 auto param_headers =
1927 protocol::build_tool_param_headers(call.arguments, entries);
1928 for (auto& [key, value] : param_headers) {
1929 request.transport_headers.emplace(std::move(key), std::move(value));
1930 }
1931 }
1932
1933 bool native_receive_loop_active() const {
1934 std::lock_guard<std::mutex> lock(native_receive_state_->mutex);
1935 return native_receive_state_->loop_active;
1936 }
1937
1938 core::Result<core::Unit> start_native_receive_loop(
1939 CancellationToken cancellation) {
1940 {
1941 std::lock_guard<std::mutex> lock(native_receive_state_->mutex);
1942 if (native_receive_state_->loop_active) {
1943 return mcp::core::unexpected(detail::peer_transport_error(
1944 "client peer receive loop is already running"));
1945 }
1946 native_receive_state_->loop_active = true;
1947 native_receive_state_->closed = false;
1948 native_receive_state_->failure.reset();
1949 native_receive_state_->responses.clear();
1950 }
1951
1952 auto finished = detail::serve_transport_loop(
1953 *native_transport_, cancellation,
1954 [this](const protocol::JsonRpcMessage& message) {
1955 return dispatch_message(message);
1956 });
1957
1958 {
1959 std::lock_guard<std::mutex> lock(native_receive_state_->mutex);
1960 native_receive_state_->loop_active = false;
1961 native_receive_state_->closed = true;
1962 if (!finished) {
1963 native_receive_state_->failure = finished.error();
1964 }
1965 }
1966 native_receive_state_->cv.notify_all();
1967 return finished;
1968 }
1969
1970 core::Result<core::Unit> store_native_response(
1971 const protocol::JsonRpcResponse& response) {
1972 if (!response.id.has_value()) {
1973 return mcp::core::unexpected(detail::peer_transport_error(
1974 "client peer received a response without an id"));
1975 }
1976 std::lock_guard<std::mutex> lock(native_receive_state_->mutex);
1977 native_receive_state_
1978 ->responses[detail::peer_request_cancellation_key(*response.id)] =
1979 response;
1980 native_receive_state_->cv.notify_all();
1981 return core::Unit{};
1982 }
1983
1984 core::Result<protocol::JsonRpcResponse> wait_native_response(
1985 const protocol::RequestId& request_id) {
1986 const auto key = detail::peer_request_cancellation_key(request_id);
1987 std::unique_lock<std::mutex> lock(native_receive_state_->mutex);
1988 native_receive_state_->cv.wait(lock, [&] {
1989 return native_receive_state_->responses.find(key) !=
1990 native_receive_state_->responses.end() ||
1991 native_receive_state_->failure.has_value() ||
1992 native_receive_state_->closed;
1993 });
1994
1995 const auto found = native_receive_state_->responses.find(key);
1996 if (found != native_receive_state_->responses.end()) {
1997 auto response = std::move(found->second);
1998 native_receive_state_->responses.erase(found);
1999 return response;
2000 }
2001 if (native_receive_state_->failure.has_value()) {
2002 return mcp::core::unexpected(*native_receive_state_->failure);
2003 }
2004 return mcp::core::unexpected(detail::peer_transport_error(
2005 "client peer transport closed before response"));
2006 }
2007
2008 std::optional<protocol::JsonRpcResponse> take_native_response(
2009 const protocol::RequestId& request_id) {
2010 const auto key = detail::peer_request_cancellation_key(request_id);
2011 std::lock_guard<std::mutex> lock(native_receive_state_->mutex);
2012 const auto found = native_receive_state_->responses.find(key);
2013 if (found == native_receive_state_->responses.end()) {
2014 return std::nullopt;
2015 }
2016 auto response = std::move(found->second);
2017 native_receive_state_->responses.erase(found);
2018 return response;
2019 }
2020
2021 static protocol::JsonRpcResponse native_error_response(
2022 const protocol::JsonRpcRequest& request, const core::Error& error) {
2023 return protocol::make_error_response(
2024 std::optional<protocol::RequestId>{request.id},
2025 protocol::make_error(
2026 error.code, error.message,
2027 error.detail.empty()
2028 ? std::nullopt
2029 : std::optional<protocol::Json>{error.detail}));
2030 }
2031
2032 static protocol::JsonRpcResponse native_error_response(
2033 const protocol::JsonRpcRequest& request, protocol::ErrorCode code,
2034 std::string message, std::string detail = {}) {
2035 return protocol::make_error_response(
2036 std::optional<protocol::RequestId>{request.id},
2037 protocol::make_error(
2038 code, std::move(message),
2039 detail.empty() ? std::nullopt
2040 : std::optional<protocol::Json>{std::move(detail)}));
2041 }
2042
2043 CancellationToken begin_client_request_cancellation(
2044 const protocol::RequestId& request_id) {
2045 CancellationSource source;
2046 auto token = source.token();
2047 std::lock_guard lock(*client_request_cancellations_mutex_);
2048 (*client_request_cancellations_)[detail::peer_request_cancellation_key(
2049 request_id)] = std::move(source);
2050 return token;
2051 }
2052
2053 void end_client_request_cancellation(
2054 const protocol::RequestId& request_id) noexcept {
2055 std::lock_guard lock(*client_request_cancellations_mutex_);
2056 client_request_cancellations_->erase(
2057 detail::peer_request_cancellation_key(request_id));
2058 }
2059
2060 void cancel_client_request(const protocol::RequestId& request_id) noexcept {
2061 std::lock_guard lock(*client_request_cancellations_mutex_);
2062 const auto it = client_request_cancellations_->find(
2063 detail::peer_request_cancellation_key(request_id));
2064 if (it != client_request_cancellations_->end()) {
2065 it->second.cancel();
2066 }
2067 }
2068
2069 core::Result<core::Unit> handle_native_notification(
2070 const protocol::JsonRpcNotification& notification) try {
2071 if (notification.method == std::string(protocol::InitializedMethod) &&
2072 initialized_handler_) {
2073 initialized_handler_();
2074 } else if (notification.method ==
2075 std::string(protocol::CancelledNotificationMethod)) {
2076 const auto params = protocol::cancelled_notification_params_from_json(
2077 notification.params);
2078 if (!params) {
2079 return mcp::core::unexpected(
2080 errors::make(protocol::ErrorCode::InvalidParams,
2081 "cancelled notification requires a requestId"));
2082 }
2083 cancel_client_request(params->request_id);
2084 if (cancelled_handler_) {
2085 cancelled_handler_(params->request_id,
2086 params->reason.value_or(std::string{}));
2087 }
2088 } else if (notification.method ==
2089 std::string(protocol::LoggingMessageNotificationMethod) &&
2090 logging_message_handler_) {
2091 std::string level;
2092 std::string message;
2093 if (notification.params.is_object()) {
2094 if (notification.params.contains("level") &&
2095 notification.params.at("level").is_string()) {
2096 level = notification.params.at("level").get<std::string>();
2097 }
2098 if (notification.params.contains("data")) {
2099 if (notification.params.at("data").is_string()) {
2100 message = notification.params.at("data").get<std::string>();
2101 } else {
2102 message = notification.params.at("data").dump();
2103 }
2104 }
2105 }
2106 logging_message_handler_(level, message);
2107 } else if (notification.method ==
2108 std::string(protocol::ToolsListChangedNotificationMethod) &&
2109 tool_list_changed_handler_) {
2110 tool_list_changed_handler_();
2111 } else if (notification.method ==
2112 std::string(
2113 protocol::PromptsListChangedNotificationMethod) &&
2114 prompt_list_changed_handler_) {
2115 prompt_list_changed_handler_();
2116 } else if (notification.method ==
2117 std::string(
2118 protocol::ResourcesListChangedNotificationMethod) &&
2119 resource_list_changed_handler_) {
2120 resource_list_changed_handler_();
2121 } else if (notification.method ==
2122 std::string(protocol::ResourcesUpdatedNotificationMethod) &&
2123 resource_updated_handler_) {
2124 if (!notification.params.is_object() ||
2125 !notification.params.contains("uri") ||
2126 !notification.params.at("uri").is_string()) {
2127 return mcp::core::unexpected(errors::make(
2128 protocol::ErrorCode::InvalidParams,
2129 "resource updated notification requires a string uri"));
2130 }
2131 resource_updated_handler_(
2132 notification.params.at("uri").get<std::string>());
2133 } else if (notification.method ==
2134 std::string(protocol::ProgressNotificationMethod) &&
2135 progress_handler_) {
2136 const auto params =
2137 protocol::progress_notification_params_from_json(notification.params);
2138 if (!params) {
2139 return mcp::core::unexpected(
2140 errors::make(protocol::ErrorCode::InvalidParams,
2141 "progress notification requires valid params"));
2142 }
2143 progress_handler_(*params);
2144 } else if (notification.method ==
2145 std::string(
2146 protocol::ElicitationCompleteNotificationMethod) &&
2147 elicitation_complete_handler_) {
2148 const auto params =
2149 protocol::elicitation_complete_notification_params_from_json(
2150 notification.params);
2151 if (!params) {
2152 return mcp::core::unexpected(errors::make(
2153 protocol::ErrorCode::InvalidParams,
2154 "elicitation completion notification requires valid params"));
2155 }
2156 elicitation_complete_handler_(params->elicitation_id);
2157 } else if (notification.method ==
2158 std::string(protocol::TasksStatusNotificationMethod) &&
2159 task_status_handler_) {
2160 const auto task = protocol::task_from_json(notification.params);
2161 if (!task) {
2162 return mcp::core::unexpected(
2163 errors::make(protocol::ErrorCode::InvalidParams,
2164 "task status notification requires valid task data"));
2165 }
2166 task_status_handler_(*task);
2167 } else if (notification.method ==
2168 std::string(protocol::RootsListChangedNotificationMethod) &&
2169 roots_list_changed_handler_) {
2170 roots_list_changed_handler_();
2171 }
2172
2173 if (raw_notification_handler_) {
2174 raw_notification_handler_(notification);
2175 }
2176 return core::Unit{};
2177 } catch (const std::exception& ex) {
2178 return mcp::core::unexpected(errors::handler_failed(ex.what()));
2179 } catch (...) {
2180 return mcp::core::unexpected(errors::handler_unknown_exception());
2181 }
2182
2183 core::Result<protocol::JsonRpcResponse> handle_native_request(
2184 const protocol::JsonRpcRequest& request) try {
2185 if (request.method == std::string(protocol::PingMethod)) {
2186 return protocol::make_response(request.id, protocol::Json::object());
2187 }
2188
2189 const auto request_cancellation =
2190 begin_client_request_cancellation(request.id);
2191 const std::shared_ptr<void> request_cancellation_cleanup(
2192 nullptr, [this, request_id = request.id](void*) noexcept {
2193 end_client_request_cancellation(request_id);
2194 });
2195
2196 if (request.method == std::string(protocol::RootsListMethod)) {
2197 if (roots_list_request_cancellation_handler_) {
2198 const auto roots =
2199 roots_list_request_cancellation_handler_(request_cancellation);
2200 if (!roots) {
2201 return native_error_response(request, roots.error());
2202 }
2203 return protocol::make_response(
2204 request.id, protocol::roots_list_result_to_json(*roots));
2205 }
2206 if (roots_list_request_handler_) {
2207 const auto roots = roots_list_request_handler_();
2208 if (!roots) {
2209 return native_error_response(request, roots.error());
2210 }
2211 return protocol::make_response(
2212 request.id, protocol::roots_list_result_to_json(*roots));
2213 }
2214
2215 protocol::RootsListResult result;
2216 result.roots = roots_;
2217 return protocol::make_response(
2218 request.id, protocol::roots_list_result_to_json(result));
2219 }
2220
2221 if (request.method == std::string(protocol::SamplingCreateMessageMethod)) {
2222 if (!sampling_request_handler_ &&
2223 !sampling_request_cancellation_handler_) {
2224 return native_error_response(
2225 request, protocol::ErrorCode::MethodNotFound,
2226 "sampling request handler is not configured");
2227 }
2228 const auto params =
2229 protocol::create_message_params_from_json(request.params);
2230 if (!params) {
2231 return detail::peer_params_error_response(request, params.error());
2232 }
2233 const auto result = sampling_request_cancellation_handler_
2234 ? sampling_request_cancellation_handler_(
2235 *params, request_cancellation)
2236 : sampling_request_handler_(*params);
2237 if (!result) {
2238 return native_error_response(request, result.error());
2239 }
2240 return protocol::make_response(
2241 request.id, protocol::create_message_result_to_json(*result));
2242 }
2243
2244 if (request.method == std::string(protocol::ElicitationCreateMethod)) {
2245 const auto params =
2246 protocol::create_elicitation_request_param_from_json(request.params);
2247 if (!params) {
2248 return detail::peer_params_error_response(request, params.error());
2249 }
2250 if (!elicitation_request_handler_ &&
2251 !elicitation_request_cancellation_handler_) {
2252 protocol::CreateElicitationResult decline_result;
2253 decline_result.action = protocol::ElicitationAction::Decline;
2254 return protocol::make_response(
2255 request.id,
2256 protocol::create_elicitation_result_to_json(decline_result));
2257 }
2258 const auto result = elicitation_request_cancellation_handler_
2259 ? elicitation_request_cancellation_handler_(
2260 *params, request_cancellation)
2261 : elicitation_request_handler_(*params);
2262 if (!result) {
2263 return native_error_response(request, result.error());
2264 }
2265 const auto capabilities =
2266 detail::default_peer_client_capabilities(client_capabilities_);
2267 if (params->mode == protocol::ElicitationMode::Form &&
2268 capabilities.elicitation.form_schema_validation) {
2269 const auto valid = protocol::validate_elicitation_result_content(
2270 params->requested_schema, *result);
2271 if (!valid) {
2272 return native_error_response(
2273 request,
2274 errors::make(protocol::ErrorCode::InternalError,
2275 "elicitation result failed schema validation",
2276 valid.error().message));
2277 }
2278 }
2279 return protocol::make_response(
2280 request.id, protocol::create_elicitation_result_to_json(*result));
2281 }
2282
2283 if (custom_request_cancellation_handler_ || custom_request_handler_) {
2284 const auto result = custom_request_cancellation_handler_
2285 ? custom_request_cancellation_handler_(
2286 request, request_cancellation)
2287 : custom_request_handler_(request);
2288 if (!result) {
2289 return native_error_response(request, result.error());
2290 }
2291 return protocol::make_response(request.id, std::move(*result));
2292 }
2293
2294 return native_error_response(request, protocol::ErrorCode::MethodNotFound,
2295 "method not found", request.method);
2296 } catch (const std::exception& ex) {
2297 return native_error_response(request, errors::handler_failed(ex.what()));
2298 } catch (...) {
2299 return native_error_response(request, errors::handler_unknown_exception());
2300 }
2301
2302 static core::Error unsupported_server_capability(std::string message) {
2303 return errors::make(protocol::ErrorCode::MethodNotFound, std::move(message),
2304 {}, "protocol");
2305 }
2306
2307 core::Result<core::Unit> record_server_capabilities(
2308 const protocol::Json& initialize_payload) {
2309 // Accept minimal responses (e.g. from conformance harness) that don't have
2310 // all required fields. This allows version negotiation to succeed even when
2311 // the server returns a simplified response.
2312 if (initialize_payload.is_object() &&
2313 !initialize_payload.contains("protocolVersion")) {
2314 return core::Unit{};
2315 }
2316
2317 const auto parsed =
2318 protocol::initialize_result_from_json(initialize_payload);
2319 if (!parsed.has_value()) {
2320 return mcp::core::unexpected(errors::make(
2321 protocol::ErrorCode::InvalidRequest, parsed.error().message,
2322 parsed.error().detail, "protocol"));
2323 }
2324 server_capabilities_ = parsed->capabilities;
2325 return core::Unit{};
2326 }
2327
2328 bool supports_server_completion() const noexcept {
2329 const auto& capabilities = server_capabilities();
2330 return !capabilities.has_value() || capabilities->completions.enabled;
2331 }
2332
2333 bool supports_server_logging() const noexcept {
2334 const auto& capabilities = server_capabilities();
2335 return !capabilities.has_value() || capabilities->logging.enabled;
2336 }
2337
2338 bool supports_server_resource_subscribe() const noexcept {
2339 const auto& capabilities = server_capabilities();
2340 return !capabilities.has_value() || capabilities->resources.subscribe;
2341 }
2342
2343 bool supports_server_task_list() const noexcept {
2344 const auto& capabilities = server_capabilities();
2345 return !capabilities.has_value() ||
2346 (capabilities->tasks.has_value() && capabilities->tasks->list);
2347 }
2348
2349 bool supports_server_task_cancel() const noexcept {
2350 const auto& capabilities = server_capabilities();
2351 return !capabilities.has_value() ||
2352 (capabilities->tasks.has_value() && capabilities->tasks->cancel);
2353 }
2354
2355 bool supports_server_tasks() const noexcept {
2356 const auto& capabilities = server_capabilities();
2357 return !capabilities.has_value() || capabilities->tasks.has_value();
2358 }
2359
2360 bool supports_server_task_tool_call() const noexcept {
2361 const auto& capabilities = server_capabilities();
2362 return !capabilities.has_value() ||
2363 (capabilities->tasks.has_value() && capabilities->tasks->tools_call);
2364 }
2365
2366 core::Result<protocol::Json> request_json(std::string method,
2367 protocol::Json params) {
2368 return raw_request(protocol::make_request(
2369 std::move(method), next_peer_request_id(), std::move(params)));
2370 }
2371
2372 core::Result<core::Unit> request_unit(std::string method,
2373 protocol::Json params) {
2374 auto payload = request_json(std::move(method), std::move(params));
2375 if (!payload) {
2376 return mcp::core::unexpected(payload.error());
2377 }
2378 return core::Unit{};
2379 }
2380
2381 static protocol::Json cursor_params(
2382 const std::optional<std::string>& cursor) {
2383 protocol::Json params = protocol::Json::object();
2384 if (cursor.has_value()) {
2385 params["cursor"] = *cursor;
2386 }
2387 return params;
2388 }
2389
2390 template <class Item, class Parser, class Append>
2391 core::Result<std::vector<Item>> list_all_pages(std::string method,
2392 Parser parser, Append append) {
2393 std::vector<Item> all;
2394 std::optional<std::string> cursor;
2395 do {
2396 auto payload = request_json(method, cursor_params(cursor));
2397 if (!payload) {
2398 return mcp::core::unexpected(payload.error());
2399 }
2400 auto page = parser(*payload);
2401 if (!page) {
2402 return mcp::core::unexpected(page.error());
2403 }
2404 append(*page, all);
2405 cursor = page->next_cursor;
2406 } while (cursor.has_value() && !cursor->empty());
2407 return all;
2408 }
2409
2410 core::Result<protocol::JsonRpcResponse> send_native_request(
2411 const protocol::JsonRpcRequest& request) {
2412 std::lock_guard<std::mutex> request_lock(*native_request_mutex_);
2413
2414 auto sent = native_transport_->send(protocol::JsonRpcMessage{request});
2415 if (!sent) {
2416 return mcp::core::unexpected(sent.error());
2417 }
2418
2419 if (native_receive_loop_active()) {
2420 return wait_native_response(request.id);
2421 }
2422
2423 if (auto buffered = take_native_response(request.id)) {
2424 return *buffered;
2425 }
2426
2427 while (true) {
2428 auto received = native_transport_->receive();
2429 if (!received) {
2430 return mcp::core::unexpected(received.error());
2431 }
2432 if (!received->has_value()) {
2433 return mcp::core::unexpected(detail::peer_transport_error(
2434 "client peer transport closed before response"));
2435 }
2436
2437 if (auto* response =
2438 std::get_if<protocol::JsonRpcResponse>(&received->value())) {
2439 if (response->id.has_value() && *response->id == request.id) {
2440 return *response;
2441 }
2442 auto stored = store_native_response(*response);
2443 if (!stored) {
2444 return mcp::core::unexpected(stored.error());
2445 }
2446 continue;
2447 }
2448
2449 auto dispatched = dispatch_message(received->value());
2450 if (!dispatched) {
2451 return mcp::core::unexpected(dispatched.error());
2452 }
2453 if (dispatched->has_value()) {
2454 sent = native_transport_->send(std::move(dispatched->value()));
2455 if (!sent) {
2456 return mcp::core::unexpected(sent.error());
2457 }
2458 }
2459 }
2460 }
2461
2462 std::unique_ptr<transport::ClientTransport> native_transport_;
2463 std::shared_ptr<std::mutex> native_request_mutex_ =
2464 std::make_shared<std::mutex>();
2465 std::shared_ptr<detail::ClientNativeReceiveState> native_receive_state_ =
2466 std::make_shared<detail::ClientNativeReceiveState>();
2467 std::shared_ptr<std::atomic<std::int64_t>> next_request_id_ =
2468 std::make_shared<std::atomic<std::int64_t>>(1);
2469 std::optional<protocol::ClientCapabilities> client_capabilities_;
2470 std::optional<protocol::ServerCapabilities> server_capabilities_;
2471 std::vector<protocol::Root> roots_;
2472 client::Client::InitializedHandler initialized_handler_;
2473 client::Client::CancelledHandler cancelled_handler_;
2474 client::Client::LoggingMessageHandler logging_message_handler_;
2475 client::Client::ListChangedHandler tool_list_changed_handler_;
2476 client::Client::ListChangedHandler prompt_list_changed_handler_;
2477 client::Client::ListChangedHandler resource_list_changed_handler_;
2478 client::Client::ResourceUpdatedHandler resource_updated_handler_;
2479 client::Client::ProgressHandler progress_handler_;
2480 client::Client::ElicitationCompleteHandler elicitation_complete_handler_;
2481 client::Client::TaskStatusHandler task_status_handler_;
2482 client::Client::ListChangedHandler roots_list_changed_handler_;
2483 client::Client::RootsListRequestHandler roots_list_request_handler_;
2484 client::Client::RootsListRequestCancellationHandler
2485 roots_list_request_cancellation_handler_;
2486 client::Client::SamplingRequestHandler sampling_request_handler_;
2487 client::Client::SamplingRequestCancellationHandler
2488 sampling_request_cancellation_handler_;
2489 client::Client::ElicitationRequestHandler elicitation_request_handler_;
2490 client::Client::ElicitationRequestCancellationHandler
2491 elicitation_request_cancellation_handler_;
2492 client::Client::CustomRequestHandler custom_request_handler_;
2493 client::Client::CustomRequestCancellationHandler
2494 custom_request_cancellation_handler_;
2495 client::Client::RawNotificationHandler raw_notification_handler_;
2496 std::shared_ptr<std::mutex> client_request_cancellations_mutex_ =
2497 std::make_shared<std::mutex>();
2498 std::shared_ptr<std::unordered_map<std::string, CancellationSource>>
2499 client_request_cancellations_ = std::make_shared<
2500 std::unordered_map<std::string, CancellationSource>>();
2501 client::Client client_;
2502 std::unordered_map<std::string, protocol::Json> tool_schema_cache_;
2503};
2504
2506class Peer<RoleClient>::Builder {
2507 public:
2508 Builder() = default;
2509 Builder(const Builder&) = delete;
2510 Builder& operator=(const Builder&) = delete;
2511 Builder(Builder&&) noexcept = default;
2512 Builder& operator=(Builder&&) noexcept = default;
2513
2514 Builder& transport(std::unique_ptr<client::Transport> value) {
2515 reset_transport();
2516 concrete_transport_ = std::move(value);
2517 transport_kind_ = TransportKind::Concrete;
2518 return *this;
2519 }
2520
2521 Builder& transport(std::unique_ptr<transport::ClientTransport> value) {
2522 reset_transport();
2523 native_transport_ = std::move(value);
2524 transport_kind_ = TransportKind::Native;
2525 return *this;
2526 }
2527
2528#if defined(CXXMCP_ENABLE_HTTP)
2529 Builder& streamable_http(client::Client::StreamableHttpEndpoint endpoint) {
2530 reset_transport();
2531 http_endpoint_ = std::move(endpoint);
2532 transport_kind_ = TransportKind::StreamableHttp;
2533 return *this;
2534 }
2535
2536 Builder& streamable_http(std::string uri) {
2537 client::Client::StreamableHttpEndpoint endpoint{};
2538 endpoint.uri = std::move(uri);
2539 return streamable_http(std::move(endpoint));
2540 }
2541
2542 Builder& legacy_sse(client::Client::StreamableHttpEndpoint endpoint) {
2543 reset_transport();
2544 http_endpoint_ = std::move(endpoint);
2545 transport_kind_ = TransportKind::LegacySse;
2546 return *this;
2547 }
2548
2549 Builder& legacy_sse(std::string uri) {
2550 client::Client::StreamableHttpEndpoint endpoint{};
2551 endpoint.uri = std::move(uri);
2552 return legacy_sse(std::move(endpoint));
2553 }
2554#endif
2555
2556#if defined(CXXMCP_ENABLE_WEBSOCKET)
2557 Builder& websocket(transport::WebSocketClientTransportOptions options) {
2558 reset_transport();
2559 ws_options_ = std::move(options);
2560 transport_kind_ = TransportKind::WebSocket;
2561 return *this;
2562 }
2563
2564 Builder& websocket(std::string uri) {
2566 options.uri = std::move(uri);
2567 return websocket(std::move(options));
2568 }
2569#endif
2570
2571 Builder& stdio(client::Client::StdioEndpoint endpoint) {
2572 reset_transport();
2573 stdio_endpoint_ = std::move(endpoint);
2574 transport_kind_ = TransportKind::Stdio;
2575 return *this;
2576 }
2577
2578 Builder& process_stdio(client::Client::StdioEndpoint endpoint) {
2579 return stdio(std::move(endpoint));
2580 }
2581
2584 Builder& process_stdio(std::string command) {
2585 reset_transport();
2587 options.command = std::move(command);
2588 native_transport_ =
2589 std::make_unique<transport::ProcessStdioClientTransport>(
2590 std::move(options));
2591 transport_kind_ = TransportKind::Native;
2592 return *this;
2593 }
2594
2595#if defined(CXXMCP_ENABLE_HTTP) || defined(CXXMCP_ENABLE_WEBSOCKET)
2596 Builder& header(std::string name, std::string value) {
2597 http_headers_[std::move(name)] = std::move(value);
2598 return *this;
2599 }
2600
2601 Builder& bearer_token(std::string token) {
2602 auth_header_ = std::move(token);
2603 return *this;
2604 }
2605#endif
2606
2607#if defined(CXXMCP_ENABLE_HTTP)
2608 Builder& auth_refresh_handler(client::HttpAuthRefreshHandler handler) {
2609 auth_refresh_handler_ = std::move(handler);
2610 return *this;
2611 }
2612
2613 Builder& dpop_request_signer(client::HttpDpopRequestSigner signer) {
2614 dpop_request_signer_ = std::move(signer);
2615 return *this;
2616 }
2617
2619 Builder& stateless_http(bool enabled = true) {
2620 http_endpoint_.stateless = enabled;
2621 return *this;
2622 }
2623#endif
2624
2625 Builder& timeout(std::chrono::milliseconds value) {
2626 timeout_ = value;
2627 return *this;
2628 }
2629
2630 Builder& capabilities(protocol::ClientCapabilities value) {
2631 capabilities_ = std::move(value);
2632 return *this;
2633 }
2634
2635 Builder& roots(std::vector<protocol::Root> value) {
2636 roots_ = std::move(value);
2637 return *this;
2638 }
2639
2640 Builder& on_initialized(client::Client::InitializedHandler handler) {
2641 initialized_handler_ = std::move(handler);
2642 return *this;
2643 }
2644
2645 Builder& on_cancelled(client::Client::CancelledHandler handler) {
2646 cancelled_handler_ = std::move(handler);
2647 return *this;
2648 }
2649
2650 Builder& on_logging_message(client::Client::LoggingMessageHandler handler) {
2651 logging_message_handler_ = std::move(handler);
2652 return *this;
2653 }
2654
2655 Builder& on_tool_list_changed(client::Client::ListChangedHandler handler) {
2656 tool_list_changed_handler_ = std::move(handler);
2657 return *this;
2658 }
2659
2660 Builder& on_prompt_list_changed(client::Client::ListChangedHandler handler) {
2661 prompt_list_changed_handler_ = std::move(handler);
2662 return *this;
2663 }
2664
2665 Builder& on_resource_list_changed(
2667 resource_list_changed_handler_ = std::move(handler);
2668 return *this;
2669 }
2670
2671 Builder& on_resource_updated(client::Client::ResourceUpdatedHandler handler) {
2672 resource_updated_handler_ = std::move(handler);
2673 return *this;
2674 }
2675
2676 Builder& on_progress(client::Client::ProgressHandler handler) {
2677 progress_handler_ = std::move(handler);
2678 return *this;
2679 }
2680
2681 Builder& on_elicitation_complete(
2683 elicitation_complete_handler_ = std::move(handler);
2684 return *this;
2685 }
2686
2687 Builder& on_task_status(client::Client::TaskStatusHandler handler) {
2688 task_status_handler_ = std::move(handler);
2689 return *this;
2690 }
2691
2692 Builder& on_roots_list_changed(client::Client::ListChangedHandler handler) {
2693 roots_list_changed_handler_ = std::move(handler);
2694 return *this;
2695 }
2696
2697 Builder& on_list_roots_request(
2698 client::Client::ListRootsRequestHandler handler) {
2699 roots_list_request_handler_ = std::move(handler);
2700 return *this;
2701 }
2702
2703 Builder& on_list_roots_request(
2704 client::Client::RootsListRequestCancellationHandler handler) {
2705 roots_list_request_cancellation_handler_ = std::move(handler);
2706 return *this;
2707 }
2708
2709 Builder& on_create_message_request(
2710 client::Client::CreateMessageRequestHandler handler) {
2711 sampling_request_handler_ = std::move(handler);
2712 return *this;
2713 }
2714
2715 Builder& on_create_message_request(
2716 client::Client::SamplingRequestCancellationHandler handler) {
2717 sampling_request_cancellation_handler_ = std::move(handler);
2718 return *this;
2719 }
2720
2721 Builder& on_create_elicitation_request(
2722 client::Client::CreateElicitationRequestHandler handler) {
2723 elicitation_request_handler_ = std::move(handler);
2724 return *this;
2725 }
2726
2727 Builder& on_create_elicitation_request(
2728 client::Client::ElicitationRequestCancellationHandler handler) {
2729 elicitation_request_cancellation_handler_ = std::move(handler);
2730 return *this;
2731 }
2732
2733 Builder& on_custom_request(client::Client::CustomRequestHandler handler) {
2734 custom_request_handler_ = std::move(handler);
2735 return *this;
2736 }
2737
2738 Builder& on_custom_request(
2739 client::Client::CustomRequestCancellationHandler handler) {
2740 custom_request_cancellation_handler_ = std::move(handler);
2741 return *this;
2742 }
2743
2744 Builder& on_raw_notification(client::Client::RawNotificationHandler handler) {
2745 raw_notification_handler_ = std::move(handler);
2746 return *this;
2747 }
2748
2749 Builder& handler(const client::ClientHandler& handler) {
2750 handler_ = handler;
2751 return *this;
2752 }
2753
2754 core::Result<Peer> build() {
2755 auto peer = make_peer();
2756 if (!peer) {
2757 return mcp::core::unexpected(peer.error());
2758 }
2759 apply_to(*peer);
2760 return std::move(*peer);
2761 }
2762
2777 template <class Fn>
2778 int run(Fn&& fn);
2779
2780 private:
2781 enum class TransportKind {
2782 None,
2783 Concrete,
2784 Native,
2785#if defined(CXXMCP_ENABLE_HTTP)
2786 StreamableHttp,
2787 LegacySse,
2788#endif
2789#if defined(CXXMCP_ENABLE_WEBSOCKET)
2790 WebSocket,
2791#endif
2792 Stdio,
2793 };
2794
2795 void reset_transport() {
2796 concrete_transport_.reset();
2797 native_transport_.reset();
2798#if defined(CXXMCP_ENABLE_HTTP)
2799 http_endpoint_ = client::Client::StreamableHttpEndpoint{};
2800#endif
2801#if defined(CXXMCP_ENABLE_WEBSOCKET)
2802 ws_options_ = {};
2803#endif
2804 stdio_endpoint_ = client::Client::StdioEndpoint{};
2805 transport_kind_ = TransportKind::None;
2806 }
2807
2808#if defined(CXXMCP_ENABLE_HTTP)
2809 client::Client::StreamableHttpEndpoint configured_http_endpoint() {
2810 auto endpoint = std::move(http_endpoint_);
2811 for (auto& header : http_headers_) {
2812 endpoint.headers[std::move(header.first)] = std::move(header.second);
2813 }
2814 if (auth_header_.has_value()) {
2815 endpoint.auth_header = std::move(auth_header_);
2816 }
2817 if (auth_refresh_handler_) {
2818 endpoint.auth_refresh_handler = std::move(auth_refresh_handler_);
2819 }
2820 if (dpop_request_signer_) {
2821 endpoint.dpop_request_signer = dpop_request_signer_;
2822 }
2823 if (timeout_.has_value()) {
2824 endpoint.timeout = *timeout_;
2825 }
2826 return endpoint;
2827 }
2828#endif
2829
2830 core::Result<Peer> make_peer() {
2831 switch (transport_kind_) {
2832 case TransportKind::Concrete:
2833 if (!concrete_transport_) {
2834 return missing_transport("client transport is null");
2835 }
2836 return Peer(std::move(concrete_transport_));
2837 case TransportKind::Native:
2838 if (!native_transport_) {
2839 return missing_transport("client transport is null");
2840 }
2841 return Peer(std::move(native_transport_));
2842#if defined(CXXMCP_ENABLE_HTTP)
2843 case TransportKind::StreamableHttp:
2844 return Peer::connect_streamable_http(configured_http_endpoint());
2845 case TransportKind::LegacySse:
2846 return Peer::connect_legacy_sse(configured_http_endpoint());
2847#endif
2848#if defined(CXXMCP_ENABLE_WEBSOCKET)
2849 case TransportKind::WebSocket: {
2850 auto options = std::move(ws_options_);
2851 for (auto& [name, value] : http_headers_) {
2852 options.headers[std::move(name)] = std::move(value);
2853 }
2854 if (auth_header_.has_value()) {
2855 options.auth_header = std::string("Bearer ") + *auth_header_;
2856 auth_header_.reset();
2857 }
2858 if (timeout_.has_value()) {
2859 options.timeout = *timeout_;
2860 }
2861 return Peer(std::make_unique<transport::WebSocketClientTransport>(
2862 std::move(options)));
2863 }
2864#endif
2865 case TransportKind::Stdio:
2866 return Peer::connect_stdio(std::move(stdio_endpoint_));
2867 case TransportKind::None:
2868 return missing_transport("client peer transport is not configured");
2869 }
2870 return missing_transport("client peer transport is not configured");
2871 }
2872
2873 static core::Result<Peer> missing_transport(std::string message) {
2874 return mcp::core::unexpected(core::Error{
2875 static_cast<int>(protocol::ErrorCode::InvalidParams),
2876 std::move(message),
2877 {},
2878 "configuration",
2879 });
2880 }
2881
2882 void apply_to(Peer& peer) {
2883 if (capabilities_.has_value()) {
2884 peer.set_capabilities(std::move(*capabilities_));
2885 }
2886 if (roots_.has_value()) {
2887 peer.set_roots(std::move(*roots_));
2888 }
2889 if (handler_.has_value()) {
2890 peer.set_handler(*handler_);
2891 }
2892 if (initialized_handler_) {
2893 peer.on_initialized(std::move(initialized_handler_));
2894 }
2895 if (cancelled_handler_) {
2896 peer.on_cancelled(std::move(cancelled_handler_));
2897 }
2898 if (logging_message_handler_) {
2899 peer.on_logging_message(std::move(logging_message_handler_));
2900 }
2901 if (tool_list_changed_handler_) {
2902 peer.on_tool_list_changed(std::move(tool_list_changed_handler_));
2903 }
2904 if (prompt_list_changed_handler_) {
2905 peer.on_prompt_list_changed(std::move(prompt_list_changed_handler_));
2906 }
2907 if (resource_list_changed_handler_) {
2908 peer.on_resource_list_changed(std::move(resource_list_changed_handler_));
2909 }
2910 if (resource_updated_handler_) {
2911 peer.on_resource_updated(std::move(resource_updated_handler_));
2912 }
2913 if (progress_handler_) {
2914 peer.on_progress(std::move(progress_handler_));
2915 }
2916 if (elicitation_complete_handler_) {
2917 peer.on_elicitation_complete(std::move(elicitation_complete_handler_));
2918 }
2919 if (task_status_handler_) {
2920 peer.on_task_status(std::move(task_status_handler_));
2921 }
2922 if (roots_list_changed_handler_) {
2923 peer.on_roots_list_changed(std::move(roots_list_changed_handler_));
2924 }
2925 if (roots_list_request_handler_) {
2926 peer.on_list_roots_request(std::move(roots_list_request_handler_));
2927 }
2928 if (roots_list_request_cancellation_handler_) {
2929 peer.on_list_roots_request(
2930 std::move(roots_list_request_cancellation_handler_));
2931 }
2932 if (sampling_request_handler_) {
2933 peer.on_create_message_request(std::move(sampling_request_handler_));
2934 }
2935 if (sampling_request_cancellation_handler_) {
2936 peer.on_create_message_request(
2937 std::move(sampling_request_cancellation_handler_));
2938 }
2939 if (elicitation_request_handler_) {
2940 peer.on_create_elicitation_request(
2941 std::move(elicitation_request_handler_));
2942 }
2943 if (elicitation_request_cancellation_handler_) {
2944 peer.on_create_elicitation_request(
2945 std::move(elicitation_request_cancellation_handler_));
2946 }
2947 if (custom_request_handler_) {
2948 peer.on_custom_request(std::move(custom_request_handler_));
2949 }
2950 if (custom_request_cancellation_handler_) {
2951 peer.on_custom_request(std::move(custom_request_cancellation_handler_));
2952 }
2953 if (raw_notification_handler_) {
2954 peer.on_raw_notification(std::move(raw_notification_handler_));
2955 }
2956 }
2957
2958 TransportKind transport_kind_ = TransportKind::None;
2959 std::unique_ptr<client::Transport> concrete_transport_;
2960 std::unique_ptr<transport::ClientTransport> native_transport_;
2961#if defined(CXXMCP_ENABLE_HTTP) || defined(CXXMCP_ENABLE_WEBSOCKET)
2962 std::unordered_map<std::string, std::string> http_headers_;
2963 std::optional<std::string> auth_header_;
2964#endif
2965#if defined(CXXMCP_ENABLE_HTTP)
2966 client::Client::StreamableHttpEndpoint http_endpoint_;
2967 client::HttpAuthRefreshHandler auth_refresh_handler_;
2968 client::HttpDpopRequestSigner dpop_request_signer_;
2969#endif
2970#if defined(CXXMCP_ENABLE_WEBSOCKET)
2971 transport::WebSocketClientTransportOptions ws_options_;
2972#endif
2973 client::Client::StdioEndpoint stdio_endpoint_;
2974 std::optional<std::chrono::milliseconds> timeout_;
2975 std::optional<protocol::ClientCapabilities> capabilities_;
2976 std::optional<std::vector<protocol::Root>> roots_;
2977 std::optional<client::ClientHandler> handler_;
2978 client::Client::InitializedHandler initialized_handler_;
2979 client::Client::CancelledHandler cancelled_handler_;
2980 client::Client::LoggingMessageHandler logging_message_handler_;
2981 client::Client::ListChangedHandler tool_list_changed_handler_;
2982 client::Client::ListChangedHandler prompt_list_changed_handler_;
2983 client::Client::ListChangedHandler resource_list_changed_handler_;
2984 client::Client::ResourceUpdatedHandler resource_updated_handler_;
2985 client::Client::ProgressHandler progress_handler_;
2986 client::Client::ElicitationCompleteHandler elicitation_complete_handler_;
2987 client::Client::TaskStatusHandler task_status_handler_;
2988 client::Client::ListChangedHandler roots_list_changed_handler_;
2989 client::Client::RootsListRequestHandler roots_list_request_handler_;
2990 client::Client::RootsListRequestCancellationHandler
2991 roots_list_request_cancellation_handler_;
2992 client::Client::SamplingRequestHandler sampling_request_handler_;
2993 client::Client::SamplingRequestCancellationHandler
2994 sampling_request_cancellation_handler_;
2995 client::Client::ElicitationRequestHandler elicitation_request_handler_;
2996 client::Client::ElicitationRequestCancellationHandler
2997 elicitation_request_cancellation_handler_;
2998 client::Client::CustomRequestHandler custom_request_handler_;
2999 client::Client::CustomRequestCancellationHandler
3000 custom_request_cancellation_handler_;
3001 client::Client::RawNotificationHandler raw_notification_handler_;
3002};
3003
3005 return Builder{};
3006}
3007
3009template <>
3011 public:
3012 class Builder;
3013
3015 explicit Peer(server::ServerOptions options = {})
3016 : server_(std::make_unique<server::Server>(std::move(options))) {}
3017
3019 explicit Peer(std::unique_ptr<server::Server> server)
3020 : server_(std::move(server)) {}
3021
3022 static core::Result<Peer> build(server::ServerBuilder builder) {
3023 auto built = builder.build();
3024 if (!built) {
3025 return mcp::core::unexpected(built.error());
3026 }
3027 return Peer(std::move(*built));
3028 }
3029
3031 static Builder builder();
3032
3033 CXXMCP_DEPRECATED(
3034 "server() is a compatibility escape hatch; prefer ServerPeer methods")
3035 server::Server& server() noexcept { return *server_; }
3036
3037 CXXMCP_DEPRECATED(
3038 "server() is a compatibility escape hatch; prefer ServerPeer methods")
3039 const server::Server& server() const noexcept { return *server_; }
3040
3041 server::ServerInfo get_info() const { return server_->get_info(); }
3042
3043 const protocol::ServerCapabilities& capabilities() const noexcept {
3044 return server_->capabilities();
3045 }
3046
3047 Peer& set_completion_handler(server::Server::JsonHandler handler) {
3048 if (handler) {
3049 completion_handler_ = [handler](const protocol::Json& params,
3050 const server::SessionContext&) mutable {
3051 return handler(params);
3052 };
3053 } else {
3054 completion_handler_ = {};
3055 }
3056 server_->set_completion_handler(std::move(handler));
3057 return *this;
3058 }
3059
3060 Peer& set_completion_handler(server::Server::JsonContextHandler handler) {
3061 completion_handler_ = handler;
3062 server_->set_completion_handler(std::move(handler));
3063 return *this;
3064 }
3065
3066 Peer& set_sampling_handler(server::Server::JsonHandler handler) {
3067 if (handler) {
3068 sampling_handler_ = [handler](const protocol::Json& params,
3069 const server::SessionContext&) mutable {
3070 return handler(params);
3071 };
3072 } else {
3073 sampling_handler_ = {};
3074 }
3075 server_->set_sampling_handler(std::move(handler));
3076 return *this;
3077 }
3078
3079 Peer& set_sampling_handler(server::Server::JsonContextHandler handler) {
3080 sampling_handler_ = handler;
3081 server_->set_sampling_handler(std::move(handler));
3082 return *this;
3083 }
3084
3085 Peer& set_logging_handler(server::Server::LoggingHandler handler) {
3086 logging_handler_ = handler;
3087 server_->set_logging_handler(std::move(handler));
3088 return *this;
3089 }
3090
3091 Peer& set_raw_request_handler(server::Server::RawRequestHandler handler) {
3092 raw_request_handler_ = handler;
3093 server_->set_raw_request_handler(std::move(handler));
3094 return *this;
3095 }
3096
3097 Peer& set_raw_request_handler(
3098 server::Server::RawRequestContextHandler handler) {
3099 raw_request_context_handler_ = handler;
3100 server_->set_raw_request_handler(std::move(handler));
3101 return *this;
3102 }
3103
3104 Peer& set_raw_notification_handler(
3106 native_notification_state_ = true;
3107 raw_notification_handler_ = handler;
3108 server_->set_raw_notification_handler(std::move(handler));
3109 return *this;
3110 }
3111
3112 Peer& set_custom_request_handler(server::Server::RawRequestHandler handler) {
3113 raw_request_handler_ = handler;
3114 server_->set_custom_request_handler(std::move(handler));
3115 return *this;
3116 }
3117
3118 Peer& set_custom_request_handler(
3119 server::Server::RawRequestContextHandler handler) {
3120 raw_request_context_handler_ = handler;
3121 server_->set_custom_request_handler(std::move(handler));
3122 return *this;
3123 }
3124
3125 Peer& set_custom_notification_handler(
3127 native_notification_state_ = true;
3128 raw_notification_handler_ = handler;
3129 server_->set_custom_notification_handler(std::move(handler));
3130 return *this;
3131 }
3132
3133 Peer& set_tools_list_handler(server::Server::ToolsListHandler handler) {
3134 server_->set_tools_list_handler(std::move(handler));
3135 return *this;
3136 }
3137
3138 Peer& set_prompts_list_handler(server::Server::PromptsListHandler handler) {
3139 server_->set_prompts_list_handler(std::move(handler));
3140 return *this;
3141 }
3142
3143 Peer& set_resources_list_handler(
3145 server_->set_resources_list_handler(std::move(handler));
3146 return *this;
3147 }
3148
3149 Peer& set_resource_templates_list_handler(
3151 server_->set_resource_templates_list_handler(std::move(handler));
3152 return *this;
3153 }
3154
3155 Peer& set_task_list_handler(server::Server::TaskListHandler handler) {
3156 task_list_handler_ = handler;
3157 server_->set_task_list_handler(std::move(handler));
3158 return *this;
3159 }
3160
3161 Peer& set_task_get_handler(server::Server::TaskGetHandler handler) {
3162 task_get_handler_ = handler;
3163 server_->set_task_get_handler(std::move(handler));
3164 return *this;
3165 }
3166
3167 Peer& set_task_cancel_handler(server::Server::TaskCancelHandler handler) {
3168 task_cancel_handler_ = handler;
3169 server_->set_task_cancel_handler(std::move(handler));
3170 return *this;
3171 }
3172
3173 Peer& set_task_result_handler(server::Server::TaskResultHandler handler) {
3174 task_result_handler_ = handler;
3175 server_->set_task_result_handler(std::move(handler));
3176 return *this;
3177 }
3178
3179 Peer& set_progress_handler(server::Server::ProgressHandler handler) {
3180 native_notification_state_ = true;
3181 progress_handler_ = handler;
3182 server_->set_progress_handler(std::move(handler));
3183 return *this;
3184 }
3185
3186 Peer& set_roots_list_changed_handler(
3188 native_notification_state_ = true;
3189 roots_list_changed_handler_ = handler;
3190 server_->set_roots_list_changed_handler(std::move(handler));
3191 return *this;
3192 }
3193
3194 Peer& set_tool_list_changed_handler(
3196 native_notification_state_ = true;
3197 tool_list_changed_handler_ = handler;
3198 server_->set_tool_list_changed_handler(std::move(handler));
3199 return *this;
3200 }
3201
3202 Peer& set_prompt_list_changed_handler(
3204 native_notification_state_ = true;
3205 prompt_list_changed_handler_ = handler;
3206 server_->set_prompt_list_changed_handler(std::move(handler));
3207 return *this;
3208 }
3209
3210 Peer& set_resource_list_changed_handler(
3212 native_notification_state_ = true;
3213 resource_list_changed_handler_ = handler;
3214 server_->set_resource_list_changed_handler(std::move(handler));
3215 return *this;
3216 }
3217
3218 Peer& set_resource_updated_handler(
3220 native_notification_state_ = true;
3221 resource_updated_handler_ = handler;
3222 server_->set_resource_updated_handler(std::move(handler));
3223 return *this;
3224 }
3225
3226 Peer& set_handler(const server::ServerHandler& handler) {
3227 if (handler.on_completion) {
3228 set_completion_handler(handler.on_completion);
3229 }
3230 if (handler.on_sampling) {
3231 set_sampling_handler(handler.on_sampling);
3232 }
3233 if (handler.on_logging) {
3234 set_logging_handler(handler.on_logging);
3235 }
3236 if (handler.on_raw_request) {
3237 set_raw_request_handler(handler.on_raw_request);
3238 }
3239 if (handler.on_raw_notification) {
3240 set_raw_notification_handler(handler.on_raw_notification);
3241 }
3242 if (handler.on_custom_request) {
3243 set_custom_request_handler(handler.on_custom_request);
3244 }
3245 if (handler.on_custom_notification) {
3246 set_custom_notification_handler(handler.on_custom_notification);
3247 }
3248 if (handler.on_tools_list) {
3249 set_tools_list_handler(handler.on_tools_list);
3250 }
3251 if (handler.on_prompts_list) {
3252 set_prompts_list_handler(handler.on_prompts_list);
3253 }
3254 if (handler.on_resources_list) {
3255 set_resources_list_handler(handler.on_resources_list);
3256 }
3257 if (handler.on_resource_templates_list) {
3258 set_resource_templates_list_handler(handler.on_resource_templates_list);
3259 }
3260 if (handler.on_task_list) {
3261 set_task_list_handler(handler.on_task_list);
3262 }
3263 if (handler.on_task_get) {
3264 set_task_get_handler(handler.on_task_get);
3265 }
3266 if (handler.on_task_cancel) {
3267 set_task_cancel_handler(handler.on_task_cancel);
3268 }
3269 if (handler.on_task_result) {
3270 set_task_result_handler(handler.on_task_result);
3271 }
3272 if (handler.on_progress) {
3273 set_progress_handler(handler.on_progress);
3274 }
3275 if (handler.on_roots_list_changed) {
3276 set_roots_list_changed_handler(handler.on_roots_list_changed);
3277 }
3278 if (handler.on_tool_list_changed) {
3279 set_tool_list_changed_handler(handler.on_tool_list_changed);
3280 }
3281 if (handler.on_prompt_list_changed) {
3282 set_prompt_list_changed_handler(handler.on_prompt_list_changed);
3283 }
3284 if (handler.on_resource_list_changed) {
3285 set_resource_list_changed_handler(handler.on_resource_list_changed);
3286 }
3287 if (handler.on_resource_updated) {
3288 set_resource_updated_handler(handler.on_resource_updated);
3289 }
3290 if (handler.on_completion_with_context) {
3291 set_completion_handler(handler.on_completion_with_context);
3292 }
3293 if (handler.on_sampling_with_context) {
3294 set_sampling_handler(handler.on_sampling_with_context);
3295 }
3296 return *this;
3297 }
3298
3299 Peer& set_handler(const server::ServerHandlerInterface& handler) {
3300 server_->set_handler(handler);
3301 set_raw_request_handler([&handler](const protocol::JsonRpcRequest& request,
3302 const server::SessionContext& context,
3303 CancellationToken cancellation)
3304 -> std::optional<protocol::JsonRpcResponse> {
3305 const auto handler_response = server::dispatch_server_handler_request(
3306 handler, request, context, cancellation);
3307 if (handler_response.has_value()) {
3308 return handler_response;
3309 }
3310 return handler.on_custom_request(request, context);
3311 });
3312 return *this;
3313 }
3314
3315 std::vector<protocol::ToolDefinition> list_tools() const {
3316 return server_->list_tools();
3317 }
3318
3319 core::Result<protocol::ToolDefinition> get_tool(std::string_view name) const {
3320 return server_->get_tool(name);
3321 }
3322
3323 core::Result<protocol::ToolResult> call_tool(
3324 std::string_view name,
3325 protocol::Json arguments = protocol::Json::object(),
3326 const std::string& session_id = {}) const {
3327 return server_->call_tool(name, std::move(arguments), session_id);
3328 }
3329
3330 core::Result<protocol::ToolResult> call_tool(
3331 std::string_view name, protocol::Json arguments,
3332 const server::SessionContext& context,
3333 CancellationToken cancellation = CancellationToken::none()) const {
3334 return server_->call_tool(name, std::move(arguments), context,
3335 cancellation);
3336 }
3337
3338 std::vector<protocol::Prompt> list_prompts() const {
3339 return server_->list_prompts();
3340 }
3341
3342 core::Result<protocol::PromptsGetResult> get_prompt(
3343 std::string_view name,
3344 protocol::Json arguments = protocol::Json::object(),
3345 const std::string& session_id = {}) const {
3346 return server_->get_prompt(name, std::move(arguments), session_id);
3347 }
3348
3349 core::Result<protocol::PromptsGetResult> get_prompt(
3350 std::string_view name, protocol::Json arguments,
3351 const server::SessionContext& context,
3352 CancellationToken cancellation = CancellationToken::none()) const {
3353 return server_->get_prompt(name, std::move(arguments), context,
3354 cancellation);
3355 }
3356
3357 std::vector<protocol::Resource> list_resources() const {
3358 return server_->list_resources();
3359 }
3360
3361 core::Result<protocol::ResourcesReadResult> read_resource(
3362 std::string_view uri, protocol::Json params = protocol::Json::object(),
3363 const std::string& session_id = {}) const {
3364 return server_->read_resource(uri, std::move(params), session_id);
3365 }
3366
3367 core::Result<protocol::ResourcesReadResult> read_resource(
3368 std::string_view uri, protocol::Json params,
3369 const server::SessionContext& context,
3370 CancellationToken cancellation = CancellationToken::none()) const {
3371 return server_->read_resource(uri, std::move(params), context,
3372 cancellation);
3373 }
3374
3375 std::vector<protocol::ResourceTemplate> list_resource_templates() const {
3376 return server_->list_resource_templates();
3377 }
3378
3379 core::Result<protocol::Json> initialize() { return server_->initialize(); }
3380
3381 core::Result<protocol::Json> ping(
3382 const server::SessionContext& context = {}) {
3383 return server_->ping(context);
3384 }
3385
3386 core::Result<protocol::JsonRpcResponse> handle_request(
3387 const protocol::JsonRpcRequest& request,
3388 const server::SessionContext& input_context = {},
3389 transport::ServerTransport* native_transport = nullptr) try {
3390 auto authenticated_context = server_->authenticate_context(input_context);
3391 if (!authenticated_context) {
3392 return detail::peer_auth_error_response(request,
3393 authenticated_context.error());
3394 }
3395 server::SessionContext context = std::move(*authenticated_context);
3396
3397 if (request.method == protocol::InitializeMethod) {
3398 const auto valid =
3399 detail::validate_peer_server_initialize_params(request.params);
3400 if (!valid) {
3401 return detail::peer_error_response(request, valid.error());
3402 }
3403 const auto requested_version =
3404 request.params.at("protocolVersion").get<std::string>();
3405 const auto negotiated_version =
3406 protocol::negotiate_protocol_version(requested_version);
3407 if (!negotiated_version.has_value()) {
3408 return detail::peer_error_response(
3409 request, core::Error{
3410 static_cast<int>(protocol::ErrorCode::InvalidParams),
3411 "unsupported MCP protocol version(\"" +
3412 requested_version + "\")",
3413 requested_version,
3414 "protocol",
3415 });
3416 }
3417 return protocol::make_response(
3418 request.id, detail::make_peer_server_initialize_result(
3419 get_info(), capabilities(), *negotiated_version));
3420 }
3421 if (request.method == protocol::PingMethod) {
3422 return protocol::make_response(request.id, protocol::Json::object());
3423 }
3424
3425 if (raw_request_context_handler_) {
3426 const auto request_cancellation =
3427 begin_peer_request_cancellation(request.id);
3428 const std::shared_ptr<void> request_cancellation_cleanup(
3429 nullptr, [this, request_id = request.id](void*) noexcept {
3430 end_peer_request_cancellation(request_id);
3431 });
3432 const auto raw_response =
3433 raw_request_context_handler_(request, context, request_cancellation);
3434 if (raw_response.has_value()) {
3435 return *raw_response;
3436 }
3437 }
3438
3439 if (raw_request_handler_) {
3440 const auto raw_response = raw_request_handler_(request, context);
3441 if (raw_response.has_value()) {
3442 return *raw_response;
3443 }
3444 }
3445
3446 if (request.method == protocol::ToolsListMethod) {
3447 const auto params =
3448 protocol::paginated_request_params_from_json(request.params);
3449 if (!params) {
3450 return detail::peer_error_response(
3451 request, errors::make(protocol::ErrorCode::InvalidParams,
3452 "tools/list params must be an object with an "
3453 "optional string cursor"));
3454 }
3455 const auto result = server_->list_tools(*params, context);
3456 if (!result) {
3457 return detail::peer_error_response(request, result.error());
3458 }
3459 return protocol::make_response(
3460 request.id, protocol::tools_list_result_to_json(*result));
3461 }
3462
3463 if (request.method == protocol::ToolsGetMethod) {
3464 if (!request.params.is_object() || !request.params.contains("name") ||
3465 !request.params.at("name").is_string()) {
3466 return detail::peer_error_response(
3467 request, errors::make(protocol::ErrorCode::InvalidParams,
3468 "tools/get requires a string name"));
3469 }
3470
3471 const auto tool = get_tool(request.params.at("name").get<std::string>());
3472 if (!tool) {
3473 return detail::peer_error_response(request, tool.error());
3474 }
3475 return protocol::make_response(request.id,
3476 protocol::tool_definition_to_json(*tool));
3477 }
3478
3479 if (request.method == protocol::ToolsCallMethod) {
3480 auto call = protocol::tool_call_from_json(request.params);
3481 if (!call) {
3482 return detail::peer_params_error_response(request, call.error());
3483 }
3484 // SEP-2663: on the 2026-07-28 wire the v1 `task` request field is a
3485 // tolerated hint only — it never promotes a task-forbidden tool, so
3486 // strip it before validation instead of rejecting the call.
3487 if (call->task.has_value() &&
3488 server::wire_version_requires_result_type(
3489 server::request_wire_version(request, context))) {
3490 const auto tool = server_->tools().get(call->name);
3491 if (tool.has_value() &&
3492 tool->task_support() == protocol::TaskSupport::Forbidden) {
3493 call->task.reset();
3494 }
3495 }
3496 if (call->task.has_value()) {
3497 const auto valid = server_->tools().validate(*call);
3498 if (!valid) {
3499 return detail::peer_params_error_response(request, valid.error());
3500 }
3501 const auto task_manager = server_->task_manager();
3502 if (!task_manager) {
3503 return detail::peer_error_response(
3504 request, errors::make(protocol::ErrorCode::MethodNotFound,
3505 "task processor is not configured"));
3506 }
3507 const auto task =
3508 task_manager->submit_tool_call(server_->tools(), *call, context,
3509 server_->schema_validator().get());
3510 if (!task) {
3511 return detail::peer_params_error_response(request, task.error());
3512 }
3513 return protocol::make_response(
3514 request.id, protocol::create_task_result_to_json(*task));
3515 }
3516
3517 const auto request_cancellation =
3518 begin_peer_request_cancellation(request.id);
3519 const std::shared_ptr<void> request_cancellation_cleanup(
3520 nullptr, [this, request_id = request.id](void*) noexcept {
3521 end_peer_request_cancellation(request_id);
3522 });
3523 const auto result =
3524 server_->tools().call(*call, context, request_cancellation,
3525 server_->schema_validator().get());
3526 if (!result) {
3527 return detail::peer_error_response(request, result.error());
3528 }
3529
3530 return protocol::make_response(request.id,
3531 protocol::tool_result_to_json(*result));
3532 }
3533
3534 if (request.method == protocol::PromptsListMethod) {
3535 const auto params =
3536 protocol::paginated_request_params_from_json(request.params);
3537 if (!params) {
3538 return detail::peer_error_response(
3539 request,
3540 errors::make(protocol::ErrorCode::InvalidParams,
3541 "prompts/list params must be an object with an "
3542 "optional string cursor"));
3543 }
3544 const auto result = server_->list_prompts(*params, context);
3545 if (!result) {
3546 return detail::peer_error_response(request, result.error());
3547 }
3548 return protocol::make_response(
3549 request.id, protocol::prompts_list_result_to_json(*result));
3550 }
3551
3552 if (request.method == protocol::PromptsGetMethod) {
3553 const auto params =
3554 protocol::prompts_get_params_from_json(request.params);
3555 if (!params) {
3556 return detail::peer_params_error_response(request, params.error());
3557 }
3558
3559 const auto request_cancellation =
3560 begin_peer_request_cancellation(request.id);
3561 const std::shared_ptr<void> request_cancellation_cleanup(
3562 nullptr, [this, request_id = request.id](void*) noexcept {
3563 end_peer_request_cancellation(request_id);
3564 });
3565 const auto result = server_->prompts().get(
3566 params->name, params->arguments, context, request_cancellation);
3567 if (!result) {
3568 return detail::peer_error_response(request, result.error());
3569 }
3570
3571 return protocol::make_response(
3572 request.id, protocol::prompts_get_result_to_json(*result));
3573 }
3574
3575 if (request.method == protocol::ResourcesListMethod) {
3576 const auto params =
3577 protocol::paginated_request_params_from_json(request.params);
3578 if (!params) {
3579 return detail::peer_error_response(
3580 request,
3581 errors::make(protocol::ErrorCode::InvalidParams,
3582 "resources/list params must be an object with an "
3583 "optional string cursor"));
3584 }
3585 const auto result = server_->list_resources(*params, context);
3586 if (!result) {
3587 return detail::peer_error_response(request, result.error());
3588 }
3589 return protocol::make_response(
3590 request.id, protocol::resources_list_result_to_json(*result));
3591 }
3592
3593 if (request.method == protocol::ResourcesReadMethod) {
3594 const auto params =
3595 protocol::resources_read_params_from_json(request.params);
3596 if (!params) {
3597 return detail::peer_params_error_response(request, params.error());
3598 }
3599
3600 const auto request_cancellation =
3601 begin_peer_request_cancellation(request.id);
3602 const std::shared_ptr<void> request_cancellation_cleanup(
3603 nullptr, [this, request_id = request.id](void*) noexcept {
3604 end_peer_request_cancellation(request_id);
3605 });
3606 auto result = server_->resources().read(params->uri, request.params,
3607 context, request_cancellation);
3608 if (!result) {
3609 return detail::peer_error_response(request, result.error());
3610 }
3611 result->ttl_ms = 300000;
3612 result->cache_scope = "public";
3613
3614 return protocol::make_response(
3615 request.id, protocol::resources_read_result_to_json(*result));
3616 }
3617
3618 if (request.method == protocol::ResourcesTemplatesListMethod) {
3619 const auto params =
3620 protocol::paginated_request_params_from_json(request.params);
3621 if (!params) {
3622 return detail::peer_error_response(
3623 request,
3624 errors::make(protocol::ErrorCode::InvalidParams,
3625 "resources/templates/list params must be an object "
3626 "with an optional string cursor"));
3627 }
3628 const auto result = server_->list_resource_templates(*params, context);
3629 if (!result) {
3630 return detail::peer_error_response(request, result.error());
3631 }
3632 return protocol::make_response(
3633 request.id,
3634 protocol::resource_templates_list_result_to_json(*result));
3635 }
3636
3637 if (request.method == protocol::ResourcesSubscribeMethod ||
3638 request.method == protocol::ResourcesUnsubscribeMethod) {
3639 if (!capabilities().resources.subscribe) {
3640 return detail::peer_error_response(
3641 request, errors::make(protocol::ErrorCode::MethodNotFound,
3642 "resource subscriptions are not enabled"));
3643 }
3644 if (!request.params.is_object() || !request.params.contains("uri") ||
3645 !request.params.at("uri").is_string()) {
3646 return detail::peer_error_response(
3647 request,
3648 errors::make(protocol::ErrorCode::InvalidParams,
3649 "resource subscription requires a string uri"));
3650 }
3651 const auto subscription = server_->set_resource_subscription(
3652 subscription_context_for(context, native_transport),
3653 request.params.at("uri").get<std::string>(),
3654 request.method == protocol::ResourcesSubscribeMethod);
3655 if (!subscription) {
3656 return detail::peer_error_response(request, subscription.error());
3657 }
3658 return protocol::make_response(request.id, protocol::Json::object());
3659 }
3660
3661 if (request.method == protocol::CompletionCompleteMethod &&
3662 completion_handler_) {
3663 const auto result = completion_handler_(request.params, context);
3664 if (!result) {
3665 return detail::peer_error_response(request, result.error());
3666 }
3667 return protocol::make_response(request.id, *result);
3668 }
3669
3670 if (request.method == protocol::SamplingCreateMessageMethod &&
3671 sampling_handler_) {
3672 const auto result = sampling_handler_(request.params, context);
3673 if (!result) {
3674 return detail::peer_error_response(request, result.error());
3675 }
3676 return protocol::make_response(request.id, *result);
3677 }
3678
3679 if (request.method == protocol::LoggingSetLevelMethod && logging_handler_) {
3680 if (!request.params.is_object() || !request.params.contains("level") ||
3681 !request.params.at("level").is_string()) {
3682 return detail::peer_error_response(
3683 request, errors::make(protocol::ErrorCode::InvalidParams,
3684 "logging/setLevel requires a string level"));
3685 }
3686 logging_handler_(request.params.at("level").get<std::string>(),
3687 "logging level changed");
3688 return protocol::make_response(request.id, protocol::Json::object());
3689 }
3690
3691 if (request.method == protocol::TasksListMethod && task_list_handler_) {
3692 const auto params = protocol::task_list_params_from_json(request.params);
3693 if (!params) {
3694 return detail::peer_params_error_response(request, params.error());
3695 }
3696 const auto result = task_list_handler_(*params, context);
3697 if (!result) {
3698 return detail::peer_error_response(request, result.error());
3699 }
3700 return protocol::make_response(
3701 request.id, protocol::task_list_result_to_json(*result));
3702 }
3703
3704 if (request.method == protocol::TasksGetMethod && task_get_handler_) {
3705 const auto params = protocol::task_get_params_from_json(request.params);
3706 if (!params) {
3707 return detail::peer_params_error_response(request, params.error());
3708 }
3709 const auto result = task_get_handler_(*params, context);
3710 if (!result) {
3711 return detail::peer_error_response(request, result.error());
3712 }
3713 protocol::TaskGetResult response_result;
3714 response_result.task = *result;
3715 return protocol::make_response(
3716 request.id, protocol::task_get_result_to_json(response_result));
3717 }
3718
3719 if (request.method == protocol::TasksCancelMethod && task_cancel_handler_) {
3720 const auto params =
3721 protocol::task_cancel_params_from_json(request.params);
3722 if (!params) {
3723 return detail::peer_params_error_response(request, params.error());
3724 }
3725 const auto result = task_cancel_handler_(*params, context);
3726 if (!result) {
3727 return detail::peer_error_response(request, result.error());
3728 }
3729 protocol::TaskCancelResult response_result;
3730 response_result.task = *result;
3731 return protocol::make_response(
3732 request.id, protocol::task_cancel_result_to_json(response_result));
3733 }
3734
3735 if (request.method == protocol::TasksResultMethod && task_result_handler_) {
3736 const auto params =
3737 protocol::task_result_params_from_json(request.params);
3738 if (!params) {
3739 return detail::peer_params_error_response(request, params.error());
3740 }
3741 const auto result = task_result_handler_(*params, context);
3742 if (!result) {
3743 return detail::peer_error_response(request, result.error());
3744 }
3745 return protocol::make_response(request.id, *result);
3746 }
3747
3748 return server_->handle_request(request, context);
3749 } catch (const std::exception& ex) {
3750 return detail::peer_error_response(request,
3751 errors::handler_failed(ex.what()));
3752 } catch (...) {
3753 return detail::peer_error_response(request,
3754 errors::handler_unknown_exception());
3755 }
3756
3757 core::Result<core::Unit> handle_notification(
3758 const protocol::JsonRpcNotification& notification,
3759 const server::SessionContext& context = {}) {
3760 if (notification.method == protocol::CancelledNotificationMethod ||
3761 native_notification_state_) {
3762 return handle_native_notification(notification, context);
3763 }
3764 return server_->handle_notification(notification, context);
3765 }
3766
3767 core::Result<core::Unit> add_transport(
3768 std::unique_ptr<server::Transport> transport) {
3769 return server_->add_transport(std::move(transport));
3770 }
3771
3774 std::unique_ptr<transport::ServerTransport> transport) {
3775 if (!transport) {
3776 return mcp::core::unexpected(detail::peer_dispatch_error(
3777 "server peer transport must not be null"));
3778 }
3779 native_transports_.push_back(std::move(transport));
3780 native_context_transports_.push_back(
3781 server::make_contract_transport_adapter(*native_transports_.back()));
3782 const auto attached =
3783 server_->add_session_transport(*native_context_transports_.back());
3784 if (!attached) {
3785 native_context_transports_.pop_back();
3786 native_transports_.pop_back();
3787 return mcp::core::unexpected(attached.error());
3788 }
3789 return core::Unit{};
3790 }
3791
3793 CancellationToken cancellation = CancellationToken::none()) {
3794 if (native_transports_.empty()) {
3795 return server_->start();
3796 }
3797
3798 std::vector<std::thread> workers;
3799 workers.reserve(native_transports_.size());
3800 std::mutex error_mutex;
3801 std::optional<core::Error> first_error;
3802
3803 for (std::size_t index = 0; index < native_transports_.size(); ++index) {
3804 auto* transport_ptr = native_transports_[index].get();
3805 auto* context_transport_ptr = native_context_transports_[index].get();
3806 workers.emplace_back([this, transport_ptr, context_transport_ptr,
3807 cancellation, &error_mutex,
3808 &first_error]() noexcept {
3809 server::SessionContext context;
3810 context.remote_address = std::string(transport_ptr->name());
3811 context.transport = context_transport_ptr;
3812 if (context_transport_ptr != nullptr) {
3813 context.transport_lifetime = context_transport_ptr->lifetime_token();
3814 }
3815 const auto served =
3816 serve_transport(*transport_ptr, context, cancellation);
3817 if (!served) {
3818 detail::keep_first_service_error(first_error, error_mutex,
3819 served.error());
3820 }
3821 });
3822 }
3823
3824 for (auto& worker : workers) {
3825 if (worker.joinable()) {
3826 worker.join();
3827 }
3828 }
3829
3830 if (first_error.has_value()) {
3831 return mcp::core::unexpected(*first_error);
3832 }
3833 return core::Unit{};
3834 }
3835
3836 void stop() noexcept {
3837 for (auto& transport : native_transports_) {
3838 if (transport) {
3839 (void)transport->close();
3840 }
3841 }
3842 server_->stop();
3843 }
3844
3847 for (auto& transport : native_transports_) {
3848 transport->wait_until_ready();
3849 }
3850 }
3851
3852 core::Result<core::Unit> notify_roots_list_changed() {
3853 return server_->notify_roots_list_changed();
3854 }
3855
3856 core::Result<core::Unit> notify_tool_list_changed() {
3857 return server_->notify_tool_list_changed();
3858 }
3859
3860 core::Result<core::Unit> notify_prompt_list_changed() {
3861 return server_->notify_prompt_list_changed();
3862 }
3863
3864 core::Result<core::Unit> notify_resource_list_changed() {
3865 return server_->notify_resource_list_changed();
3866 }
3867
3868 core::Result<core::Unit> notify_resource_updated(std::string_view uri) {
3869 return server_->notify_resource_updated(uri);
3870 }
3871
3872 core::Result<core::Unit> notify_progress(
3873 const protocol::ProgressNotificationParams& params) {
3874 return server_->notify_progress(params);
3875 }
3876
3877 core::Result<core::Unit> notify_logging_message(
3878 const protocol::LoggingMessageNotificationParams& params) {
3879 return server_->notify_logging_message(params);
3880 }
3881
3882 core::Result<core::Unit> notify_elicitation_complete(
3883 std::string elicitation_id) {
3884 return server_->notify_elicitation_complete(std::move(elicitation_id));
3885 }
3886
3887 core::Result<core::Unit> notify_task_status(const protocol::Task& task) {
3888 return server_->notify_task_status(task);
3889 }
3890
3897 const protocol::JsonRpcMessage& message,
3898 const server::SessionContext& context = {},
3899 transport::ServerTransport* native_transport = nullptr) {
3900 if (const auto* request = std::get_if<protocol::JsonRpcRequest>(&message)) {
3901 auto handled = handle_request(*request, context, native_transport);
3902 if (!handled) {
3903 return protocol::JsonRpcMessage{protocol::make_error_response(
3904 request->id,
3905 detail::peer_error_object_from_core_error(handled.error()))};
3906 }
3907 // SEP-2322 (MRTR): the 2026-07-28 wire format requires `resultType` on
3908 // every result object. Stamp the implicit "complete" default when the
3909 // method payload did not set a more specific type. SEP-2549 cacheable
3910 // operations also carry the `ttlMs`/`cacheScope` hints.
3911 if (handled->result.has_value() && handled->result->is_object() &&
3912 server::wire_version_requires_result_type(
3913 server::request_wire_version(*request, context))) {
3914 auto& result = *handled->result;
3915 if (!result.contains("resultType")) {
3916 result["resultType"] = "complete";
3917 }
3918 if (server::method_requires_cache_hints(request->method)) {
3919 if (!result.contains("ttlMs")) {
3920 result["ttlMs"] = 0;
3921 }
3922 if (!result.contains("cacheScope")) {
3923 result["cacheScope"] = "private";
3924 }
3925 }
3926 }
3927 return protocol::JsonRpcMessage{std::move(*handled)};
3928 }
3929
3930 if (const auto* notification =
3931 std::get_if<protocol::JsonRpcNotification>(&message)) {
3932 const auto handled = handle_notification(*notification, context);
3933 if (!handled) {
3934 return mcp::core::unexpected(handled.error());
3935 }
3936 return std::nullopt;
3937 }
3938
3939 return mcp::core::unexpected(detail::peer_dispatch_error(
3940 "server peer cannot dispatch an uncorrelated response"));
3941 }
3942
3950 transport::ServerTransport& transport,
3951 const server::SessionContext& context = {},
3952 CancellationToken cancellation = CancellationToken::none()) {
3953 struct WorkerState {
3954 std::mutex mutex;
3955 std::mutex send_mutex;
3956 std::condition_variable cv;
3957 std::size_t active = 0;
3958 std::optional<core::Error> first_error;
3959 };
3960
3961 auto worker_state = std::make_shared<WorkerState>();
3962 bool initialized = false;
3963
3964 auto record_worker_error = [worker_state,
3965 &transport](core::Error error) noexcept {
3966 {
3967 std::lock_guard lock(worker_state->mutex);
3968 if (!worker_state->first_error.has_value()) {
3969 worker_state->first_error = std::move(error);
3970 }
3971 }
3972 (void)transport.close();
3973 worker_state->cv.notify_all();
3974 };
3975
3976 auto finish_worker = [worker_state]() noexcept {
3977 {
3978 std::lock_guard lock(worker_state->mutex);
3979 if (worker_state->active > 0) {
3980 --worker_state->active;
3981 }
3982 }
3983 worker_state->cv.notify_all();
3984 };
3985
3986 auto wait_for_workers = [worker_state]() -> std::optional<core::Error> {
3987 std::unique_lock lock(worker_state->mutex);
3988 worker_state->cv.wait(lock, [&] { return worker_state->active == 0; });
3989 return worker_state->first_error;
3990 };
3991
3992 auto send_message = [worker_state, &transport](
3993 protocol::JsonRpcMessage message,
3994 const server::SessionContext& message_context)
3995 -> core::Result<core::Unit> {
3996 std::lock_guard lock(worker_state->send_mutex);
3997#if defined(CXXMCP_ENABLE_HTTP)
3998 auto* http_transport =
3999 dynamic_cast<transport::StreamableHttpServerTransport*>(&transport);
4000 if (http_transport != nullptr && !message_context.session_id.empty()) {
4001 return http_transport->send_to_session(message_context.session_id,
4002 std::move(message));
4003 }
4004#endif
4005 return transport.send(std::move(message));
4006 };
4007
4008 auto dispatch_and_send = [this, &transport, &send_message](
4009 const protocol::JsonRpcMessage& message,
4010 const server::SessionContext& message_context)
4011 -> core::Result<core::Unit> {
4012 auto dispatched = dispatch_message(message, message_context, &transport);
4013 if (!dispatched) {
4014 return mcp::core::unexpected(dispatched.error());
4015 }
4016 if (dispatched->has_value()) {
4017 auto sent =
4018 send_message(std::move(dispatched->value()), message_context);
4019 if (!sent) {
4020 return mcp::core::unexpected(sent.error());
4021 }
4022 }
4023 return core::Unit{};
4024 };
4025
4026 auto start_request_worker = [worker_state, &record_worker_error,
4027 &finish_worker, &dispatch_and_send](
4028 protocol::JsonRpcMessage message,
4029 server::SessionContext message_context)
4030 -> core::Result<core::Unit> {
4031 {
4032 std::lock_guard lock(worker_state->mutex);
4033 ++worker_state->active;
4034 }
4035
4036 try {
4037 std::thread([message = std::move(message),
4038 message_context = std::move(message_context),
4039 &dispatch_and_send, record_worker_error,
4040 finish_worker]() mutable {
4041 try {
4042 auto dispatched = dispatch_and_send(message, message_context);
4043 if (!dispatched) {
4044 record_worker_error(dispatched.error());
4045 }
4046 } catch (const std::exception& ex) {
4047 record_worker_error(errors::make(
4048 protocol::ErrorCode::InternalError,
4049 "server peer request worker failed", ex.what(), "transport"));
4050 } catch (...) {
4051 record_worker_error(
4052 errors::make(protocol::ErrorCode::InternalError,
4053 "server peer request worker failed",
4054 "unknown exception", "transport"));
4055 }
4056 finish_worker();
4057 }).detach();
4058 } catch (const std::system_error& ex) {
4059 finish_worker();
4060 return mcp::core::unexpected(
4061 errors::make(protocol::ErrorCode::InternalError,
4062 "failed to start server peer request worker",
4063 ex.what(), "transport"));
4064 }
4065 return core::Unit{};
4066 };
4067
4068 auto is_stateless_request = [](const protocol::Json& params) -> bool {
4069 return params.is_object() && params.contains("_meta") &&
4070 params.at("_meta").is_object() &&
4071 params.at("_meta").contains(
4072 "io.modelcontextprotocol/protocolVersion");
4073 };
4074
4075 while (!cancellation.cancelled()) {
4076 auto received = transport.receive();
4077 if (!received) {
4078 if (const auto worker_error = wait_for_workers()) {
4079 return mcp::core::unexpected(*worker_error);
4080 }
4081 return mcp::core::unexpected(received.error());
4082 }
4083 if (!received->has_value()) {
4084 if (const auto worker_error = wait_for_workers()) {
4085 return mcp::core::unexpected(*worker_error);
4086 }
4087 return core::Unit{};
4088 }
4089
4090 auto message = std::move(received->value());
4091 if (const auto* request =
4092 std::get_if<protocol::JsonRpcRequest>(&message)) {
4093 const bool stateless = is_stateless_request(request->params);
4094 const bool allowed_before_initialized =
4095 stateless || request->method == protocol::InitializeMethod ||
4096 request->method == protocol::PingMethod;
4097 if (!initialized && !allowed_before_initialized) {
4098 auto sent = send_message(
4099 protocol::JsonRpcMessage{protocol::make_error_response(
4100 request->id,
4101 protocol::make_error(protocol::ErrorCode::InvalidRequest,
4102 "server peer transport session is "
4103 "not initialized"))},
4104 detail::context_for_received_server_message(transport, context));
4105 if (!sent) {
4106 if (const auto worker_error = wait_for_workers()) {
4107 return mcp::core::unexpected(*worker_error);
4108 }
4109 return mcp::core::unexpected(sent.error());
4110 }
4111 continue;
4112 }
4113
4114 auto message_context =
4115 detail::context_for_received_server_message(transport, context);
4116 const bool run_inline = request->method == protocol::InitializeMethod ||
4117 (!initialized && !stateless);
4118 if (run_inline) {
4119 auto dispatched = dispatch_and_send(message, message_context);
4120 if (!dispatched) {
4121 if (const auto worker_error = wait_for_workers()) {
4122 return mcp::core::unexpected(*worker_error);
4123 }
4124 return mcp::core::unexpected(dispatched.error());
4125 }
4126 continue;
4127 }
4128
4129 auto started = start_request_worker(std::move(message),
4130 std::move(message_context));
4131 if (!started) {
4132 if (const auto worker_error = wait_for_workers()) {
4133 return mcp::core::unexpected(*worker_error);
4134 }
4135 return mcp::core::unexpected(started.error());
4136 }
4137 continue;
4138 }
4139
4140 if (const auto* notification =
4141 std::get_if<protocol::JsonRpcNotification>(&message)) {
4142 const bool stateless = is_stateless_request(notification->params);
4143 if (!initialized && !stateless &&
4144 notification->method != protocol::InitializedMethod) {
4145 if (const auto worker_error = wait_for_workers()) {
4146 return mcp::core::unexpected(*worker_error);
4147 }
4148 return mcp::core::unexpected(detail::peer_dispatch_error(
4149 "server peer transport session is not initialized"));
4150 }
4151 auto message_context =
4152 detail::context_for_received_server_message(transport, context);
4153 auto dispatched = dispatch_and_send(message, message_context);
4154 if (!dispatched) {
4155 if (const auto worker_error = wait_for_workers()) {
4156 return mcp::core::unexpected(*worker_error);
4157 }
4158 return mcp::core::unexpected(dispatched.error());
4159 }
4160 if (notification->method == protocol::InitializedMethod) {
4161 initialized = true;
4162 }
4163 continue;
4164 }
4165
4166 if (const auto worker_error = wait_for_workers()) {
4167 return mcp::core::unexpected(*worker_error);
4168 }
4169 return mcp::core::unexpected(detail::peer_dispatch_error(
4170 "server peer cannot dispatch an uncorrelated response"));
4171 }
4172
4173 if (const auto worker_error = wait_for_workers()) {
4174 return mcp::core::unexpected(*worker_error);
4175 }
4176 return core::Unit{};
4177 }
4178
4179 private:
4180 server::SessionContext subscription_context_for(
4181 const server::SessionContext& context,
4182 const transport::ServerTransport* native_transport) const {
4183 server::SessionContext subscription_context = context;
4184 if (subscription_context.transport || native_transport == nullptr) {
4185 return subscription_context;
4186 }
4187
4188 for (std::size_t index = 0; index < native_transports_.size(); ++index) {
4189 if (native_transports_[index].get() == native_transport &&
4190 index < native_context_transports_.size()) {
4191 subscription_context.transport =
4192 native_context_transports_[index].get();
4193 subscription_context.transport_lifetime =
4194 native_context_transports_[index]->lifetime_token();
4195 break;
4196 }
4197 }
4198 return subscription_context;
4199 }
4200
4201 CancellationToken begin_peer_request_cancellation(
4202 const protocol::RequestId& request_id) {
4203 CancellationSource source;
4204 auto token = source.token();
4205 std::lock_guard lock(*peer_request_cancellations_mutex_);
4206 (*peer_request_cancellations_)[detail::peer_request_cancellation_key(
4207 request_id)] = std::move(source);
4208 return token;
4209 }
4210
4211 void end_peer_request_cancellation(
4212 const protocol::RequestId& request_id) noexcept {
4213 std::lock_guard lock(*peer_request_cancellations_mutex_);
4214 peer_request_cancellations_->erase(
4215 detail::peer_request_cancellation_key(request_id));
4216 }
4217
4218 void cancel_peer_request(const protocol::RequestId& request_id) noexcept {
4219 std::lock_guard lock(*peer_request_cancellations_mutex_);
4220 const auto it = peer_request_cancellations_->find(
4221 detail::peer_request_cancellation_key(request_id));
4222 if (it != peer_request_cancellations_->end()) {
4223 it->second.cancel();
4224 }
4225 }
4226
4227 core::Result<core::Unit> handle_native_notification(
4228 const protocol::JsonRpcNotification& notification,
4229 const server::SessionContext& context) try {
4230 if (notification.method == protocol::CancelledNotificationMethod) {
4231 const auto cancelled = protocol::cancelled_notification_params_from_json(
4232 notification.params);
4233 if (!cancelled) {
4234 return mcp::core::unexpected(
4235 errors::make(protocol::ErrorCode::InvalidParams,
4236 "cancelled notification requires valid params"));
4237 }
4238 cancel_peer_request(cancelled->request_id);
4239 return server_->handle_notification(notification, context);
4240 }
4241
4242 if (raw_notification_handler_) {
4243 const auto raw_result = raw_notification_handler_(notification, context);
4244 if (!raw_result) {
4245 return mcp::core::unexpected(raw_result.error());
4246 }
4247 }
4248
4249 if (notification.method == protocol::RootsListChangedNotificationMethod &&
4250 roots_list_changed_handler_) {
4251 const auto result = roots_list_changed_handler_(context);
4252 if (!result) {
4253 return mcp::core::unexpected(result.error());
4254 }
4255 } else if (notification.method == protocol::ProgressNotificationMethod &&
4256 progress_handler_) {
4257 const auto params =
4258 protocol::progress_notification_params_from_json(notification.params);
4259 if (!params) {
4260 return mcp::core::unexpected(
4261 errors::make(protocol::ErrorCode::InvalidParams,
4262 "progress notification requires valid params"));
4263 }
4264 const auto result = progress_handler_(*params, context);
4265 if (!result) {
4266 return mcp::core::unexpected(result.error());
4267 }
4268 } else if (notification.method ==
4269 protocol::ToolsListChangedNotificationMethod &&
4270 tool_list_changed_handler_) {
4271 const auto result = tool_list_changed_handler_(context);
4272 if (!result) {
4273 return mcp::core::unexpected(result.error());
4274 }
4275 } else if (notification.method ==
4276 protocol::PromptsListChangedNotificationMethod &&
4277 prompt_list_changed_handler_) {
4278 const auto result = prompt_list_changed_handler_(context);
4279 if (!result) {
4280 return mcp::core::unexpected(result.error());
4281 }
4282 } else if (notification.method ==
4283 protocol::ResourcesListChangedNotificationMethod &&
4284 resource_list_changed_handler_) {
4285 const auto result = resource_list_changed_handler_(context);
4286 if (!result) {
4287 return mcp::core::unexpected(result.error());
4288 }
4289 } else if (notification.method ==
4290 protocol::ResourcesUpdatedNotificationMethod &&
4291 resource_updated_handler_) {
4292 if (!notification.params.is_object() ||
4293 !notification.params.contains("uri") ||
4294 !notification.params.at("uri").is_string()) {
4295 return mcp::core::unexpected(errors::make(
4296 protocol::ErrorCode::InvalidParams,
4297 "resource updated notification requires a string uri"));
4298 }
4299 const auto result = resource_updated_handler_(
4300 notification.params.at("uri").get<std::string>(), context);
4301 if (!result) {
4302 return mcp::core::unexpected(result.error());
4303 }
4304 }
4305
4306 return core::Unit{};
4307 } catch (const std::exception& ex) {
4308 return mcp::core::unexpected(errors::handler_failed(ex.what()));
4309 } catch (...) {
4310 return mcp::core::unexpected(errors::handler_unknown_exception());
4311 }
4312
4313 std::unique_ptr<server::Server> server_;
4314 std::vector<std::unique_ptr<transport::ServerTransport>> native_transports_;
4315 std::vector<std::unique_ptr<server::Transport>> native_context_transports_;
4316 std::shared_ptr<std::mutex> peer_request_cancellations_mutex_ =
4317 std::make_shared<std::mutex>();
4318 std::shared_ptr<std::unordered_map<std::string, CancellationSource>>
4319 peer_request_cancellations_ = std::make_shared<
4320 std::unordered_map<std::string, CancellationSource>>();
4321 bool native_notification_state_ = false;
4322 server::Server::RawRequestHandler raw_request_handler_;
4323 server::Server::RawRequestContextHandler raw_request_context_handler_;
4324 server::Server::JsonContextHandler completion_handler_;
4325 server::Server::JsonContextHandler sampling_handler_;
4326 server::Server::LoggingHandler logging_handler_;
4327 server::Server::TaskListHandler task_list_handler_;
4328 server::Server::TaskGetHandler task_get_handler_;
4329 server::Server::TaskCancelHandler task_cancel_handler_;
4330 server::Server::TaskResultHandler task_result_handler_;
4331 server::Server::RawNotificationHandler raw_notification_handler_;
4332 server::Server::ProgressHandler progress_handler_;
4333 server::Server::RootsListChangedHandler roots_list_changed_handler_;
4334 server::Server::ListChangedHandler tool_list_changed_handler_;
4335 server::Server::ListChangedHandler prompt_list_changed_handler_;
4336 server::Server::ListChangedHandler resource_list_changed_handler_;
4337 server::Server::ResourceUpdatedHandler resource_updated_handler_;
4338};
4339
4341class Peer<RoleServer>::Builder {
4342 public:
4343 Builder() = default;
4344 Builder(const Builder&) = delete;
4345 Builder& operator=(const Builder&) = delete;
4346 Builder(Builder&&) noexcept = default;
4347 Builder& operator=(Builder&&) noexcept = default;
4348
4349 Builder& name(std::string value) {
4350 builder_.name(std::move(value));
4351 return *this;
4352 }
4353
4354 Builder& version(std::string value) {
4355 builder_.version(std::move(value));
4356 return *this;
4357 }
4358
4359 Builder& instructions(std::string value) {
4360 builder_.instructions(std::move(value));
4361 return *this;
4362 }
4363
4364 Builder& capabilities(protocol::ServerCapabilities value) {
4365 builder_.with_capabilities(std::move(value));
4366 return *this;
4367 }
4368
4369 Builder& transport(std::unique_ptr<server::Transport> value) {
4370 builder_.with_transport(std::move(value));
4371 return *this;
4372 }
4373
4374 Builder& transport(std::unique_ptr<transport::ServerTransport> value) {
4375 native_transports_.push_back(std::move(value));
4376 return *this;
4377 }
4378
4379 Builder& stdio(std::istream& input, std::ostream& output) {
4380 return transport(
4381 std::make_unique<transport::ServerStdioTransport>(input, output));
4382 }
4383
4384 Builder& stdio() { return stdio(std::cin, std::cout); }
4385
4386#if defined(CXXMCP_ENABLE_HTTP)
4387 Builder& streamable_http(
4389 return transport(std::make_unique<transport::StreamableHttpServerTransport>(
4390 std::move(options)));
4391 }
4392
4393 Builder& streamable_http(int listen_port) {
4395 options.listen_port = listen_port;
4396 return streamable_http(std::move(options));
4397 }
4398
4399 Builder& streamable_http(std::string listen_host, int listen_port,
4400 std::string path = "/mcp") {
4402 options.listen_host = std::move(listen_host);
4403 options.listen_port = listen_port;
4404 options.path = std::move(path);
4405 return streamable_http(std::move(options));
4406 }
4407#endif
4408
4409#if defined(CXXMCP_ENABLE_WEBSOCKET)
4410 Builder& websocket(transport::WebSocketServerTransportOptions options) {
4411 return transport(std::make_unique<transport::WebSocketServerTransport>(
4412 std::move(options)));
4413 }
4414
4415 Builder& websocket(int listen_port) {
4417 options.listen_port = listen_port;
4418 return websocket(std::move(options));
4419 }
4420
4421 Builder& websocket(std::string listen_host, int listen_port,
4422 std::string path = "/mcp") {
4424 options.listen_host = std::move(listen_host);
4425 options.listen_port = listen_port;
4426 options.path = std::move(path);
4427 return websocket(std::move(options));
4428 }
4429#endif
4430
4431 Builder& auth_provider(std::unique_ptr<server::AuthProvider> value) {
4432 builder_.with_auth_provider(std::move(value));
4433 return *this;
4434 }
4435
4436 Builder& rate_limiter(std::unique_ptr<server::RateLimiter> value) {
4437 builder_.with_rate_limiter(std::move(value));
4438 return *this;
4439 }
4440
4441 Builder& schema_validator(
4442 std::shared_ptr<const server::JsonSchemaValidator> value) {
4443 builder_.with_schema_validator(std::move(value));
4444 return *this;
4445 }
4446
4447 Builder& task_manager(server::TaskOperationProcessorOptions options = {}) {
4448 builder_.with_task_manager(std::move(options));
4449 return *this;
4450 }
4451
4452 Builder& task_manager(
4453 std::shared_ptr<server::TaskOperationProcessor> processor) {
4454 builder_.with_task_manager(std::move(processor));
4455 return *this;
4456 }
4457
4458 Builder& add_tool(protocol::ToolDefinition definition,
4459 server::ToolHandler handler) {
4460 builder_.add_tool(std::move(definition), std::move(handler));
4461 return *this;
4462 }
4463
4464 Builder& tool(protocol::ToolDefinition definition,
4465 server::ToolHandler handler) {
4466 return add_tool(std::move(definition), std::move(handler));
4467 }
4468
4469 Builder& add_prompt(protocol::Prompt prompt, server::PromptHandler handler) {
4470 builder_.add_prompt(std::move(prompt), std::move(handler));
4471 return *this;
4472 }
4473
4474 Builder& prompt(protocol::Prompt prompt, server::PromptHandler handler) {
4475 return add_prompt(std::move(prompt), std::move(handler));
4476 }
4477
4478 template <class Args, class Handler>
4479 Builder& prompt(server::TypedPromptRegistration<Args, Handler> registration) {
4480 return add_prompt(
4481 std::move(registration.prompt),
4482 [handler = std::move(registration.handler)](
4483 const server::PromptContext& context)
4485 try {
4486 auto args = context.arguments.get<Args>();
4487 auto handled = server::detail::invoke_typed_context_handler(
4488 handler, std::move(args), context);
4489 if constexpr (server::detail::is_result<decltype(handled)>::value) {
4490 if (!handled) {
4491 return mcp::core::unexpected(handled.error());
4492 }
4493 return server::detail::value_to_prompt_result(*handled);
4494 } else {
4495 return server::detail::value_to_prompt_result(std::move(handled));
4496 }
4497 } catch (const std::exception& exception) {
4498 return mcp::core::unexpected(core::Error{
4499 static_cast<int>(protocol::ErrorCode::InvalidParams),
4500 "failed to decode prompt arguments",
4501 exception.what(),
4502 });
4503 }
4504 });
4505 }
4506
4507 Builder& add_resource(protocol::Resource resource,
4509 builder_.add_resource(std::move(resource), std::move(handler));
4510 return *this;
4511 }
4512
4513 Builder& resource(protocol::Resource resource,
4515 return add_resource(std::move(resource), std::move(handler));
4516 }
4517
4518 template <class Args, class Handler>
4519 Builder& resource(
4520 server::TypedResourceRegistration<Args, Handler> registration) {
4521 return add_resource(
4522 std::move(registration.resource),
4523 [handler = std::move(registration.handler)](
4524 const server::ResourceContext& context)
4525 -> core::Result<protocol::ResourcesReadResult> {
4526 try {
4527 auto args = context.params.get<Args>();
4528 auto handled = server::detail::invoke_typed_context_handler(
4529 handler, std::move(args), context);
4530 if constexpr (server::detail::is_result<decltype(handled)>::value) {
4531 if (!handled) {
4532 return mcp::core::unexpected(handled.error());
4533 }
4534 return server::detail::value_to_resource_read_result(*handled,
4535 context.uri);
4536 } else {
4537 return server::detail::value_to_resource_read_result(
4538 std::move(handled), context.uri);
4539 }
4540 } catch (const std::exception& exception) {
4541 return mcp::core::unexpected(core::Error{
4542 static_cast<int>(protocol::ErrorCode::InvalidParams),
4543 "failed to decode resource parameters",
4544 exception.what(),
4545 });
4546 }
4547 });
4548 }
4549
4550 Builder& add_resource_template(protocol::ResourceTemplate resource_template) {
4551 builder_.add_resource_template(std::move(resource_template));
4552 return *this;
4553 }
4554
4555 Builder& resource_template(protocol::ResourceTemplate resource_template) {
4556 return add_resource_template(std::move(resource_template));
4557 }
4558
4565 template <class Args, class Result, class Handler>
4566 Builder& tool(std::string name, Handler handler) {
4567 auto definition = protocol::tool_definition(std::move(name))
4568 .input_schema(protocol::tool_input_schema_for<Args>())
4569 .build();
4570 server::detail::apply_default_output_schema<Result>(definition);
4571 return tool<Args, Result>(std::move(definition), std::move(handler));
4572 }
4573
4575 template <class Args, class Result, class Handler>
4576 Builder& tool(protocol::ToolDefinition definition, Handler handler) {
4577 return add_tool(
4578 std::move(definition),
4579 [handler = std::move(handler)](const server::ToolContext& context)
4581 try {
4582 auto args =
4583 server::detail::argument_from_json<Args>(context.arguments);
4584 auto handled = server::detail::invoke_tool_handler(
4585 handler, std::move(args), context);
4586 if constexpr (server::detail::is_result<decltype(handled)>::value) {
4587 if (!handled) {
4588 return mcp::core::unexpected(handled.error());
4589 }
4590 return server::detail::value_to_tool_result(*handled);
4591 } else {
4592 return server::detail::value_to_tool_result(std::move(handled));
4593 }
4594 } catch (const std::exception& exception) {
4595 return mcp::core::unexpected(core::Error{
4596 static_cast<int>(protocol::ErrorCode::InvalidParams),
4597 "failed to decode tool arguments",
4598 exception.what(),
4599 });
4600 }
4601 });
4602 }
4603
4605 template <class Args, class Handler>
4606 Builder& prompt(std::string name, Handler handler) {
4607 server::detail::require_unambiguous_typed_context_handler<
4608 Handler, Args, server::PromptContext>("prompt");
4609 protocol::Prompt prompt;
4610 prompt.name = std::move(name);
4611 return add_prompt(
4612 std::move(prompt),
4613 [handler = std::move(handler)](const server::PromptContext& context)
4615 try {
4616 auto args = context.arguments.get<Args>();
4617 auto handled = server::detail::invoke_typed_context_handler(
4618 handler, std::move(args), context);
4619 if constexpr (server::detail::is_result<decltype(handled)>::value) {
4620 if (!handled) {
4621 return mcp::core::unexpected(handled.error());
4622 }
4623 return server::detail::value_to_prompt_result(*handled);
4624 } else {
4625 return server::detail::value_to_prompt_result(std::move(handled));
4626 }
4627 } catch (const std::exception& exception) {
4628 return mcp::core::unexpected(core::Error{
4629 static_cast<int>(protocol::ErrorCode::InvalidParams),
4630 "failed to decode prompt arguments",
4631 exception.what(),
4632 });
4633 }
4634 });
4635 }
4636
4638 template <class Handler>
4639 Builder& prompt(std::string name, Handler handler) {
4640 server::detail::require_unambiguous_prompt_handler<Handler>();
4641 protocol::Prompt prompt;
4642 prompt.name = std::move(name);
4643 return add_prompt(
4644 std::move(prompt),
4645 [handler = std::move(handler)](const server::PromptContext& context)
4647 try {
4648 auto handled =
4649 server::detail::invoke_prompt_handler(handler, context);
4650 if constexpr (server::detail::is_result<decltype(handled)>::value) {
4651 if (!handled) {
4652 return mcp::core::unexpected(handled.error());
4653 }
4654 return server::detail::value_to_prompt_result(*handled);
4655 } else {
4656 return server::detail::value_to_prompt_result(std::move(handled));
4657 }
4658 } catch (const std::exception& exception) {
4659 return mcp::core::unexpected(core::Error{
4660 static_cast<int>(protocol::ErrorCode::InvalidParams),
4661 "failed to run prompt handler",
4662 exception.what(),
4663 });
4664 }
4665 });
4666 }
4667
4669 template <class Args, class Handler>
4670 Builder& resource(std::string name, Handler handler) {
4671 server::detail::require_unambiguous_typed_context_handler<
4672 Handler, Args, server::ResourceContext>("resource");
4673 protocol::Resource resource;
4674 resource.uri = std::move(name);
4675 resource.name = resource.uri;
4676 return add_resource(
4677 std::move(resource),
4678 [handler = std::move(handler)](const server::ResourceContext& context)
4680 try {
4681 auto args = context.params.get<Args>();
4682 auto handled = server::detail::invoke_typed_context_handler(
4683 handler, std::move(args), context);
4684 if constexpr (server::detail::is_result<decltype(handled)>::value) {
4685 if (!handled) {
4686 return mcp::core::unexpected(handled.error());
4687 }
4688 return server::detail::value_to_resource_read_result(*handled,
4689 context.uri);
4690 } else {
4691 return server::detail::value_to_resource_read_result(
4692 std::move(handled), context.uri);
4693 }
4694 } catch (const std::exception& exception) {
4695 return mcp::core::unexpected(core::Error{
4696 static_cast<int>(protocol::ErrorCode::InvalidParams),
4697 "failed to decode resource parameters",
4698 exception.what(),
4699 });
4700 }
4701 });
4702 }
4703
4708 template <class Handler>
4709 Builder& resource(std::string name, Handler handler) {
4710 server::detail::require_callable(handler, "resource");
4711 server::detail::require_unambiguous_resource_handler<Handler>();
4712 auto uri = name;
4713 protocol::Resource resource;
4714 resource.uri = uri;
4715 resource.name = uri;
4716 if constexpr (std::is_invocable_v<Handler>) {
4717 using Handled = decltype(handler());
4718 if constexpr (std::is_same_v<std::decay_t<Handled>, protocol::Resource>) {
4719 resource = handler();
4720 }
4721 }
4722 return add_resource(
4723 std::move(resource),
4724 [handler = std::move(handler),
4725 uri = std::move(uri)](const server::ResourceContext& context)
4727 try {
4728 auto handled =
4729 server::detail::invoke_resource_handler(handler, context);
4730 if constexpr (std::is_same_v<std::decay_t<decltype(handled)>,
4733 } else if constexpr (server::detail::is_result<
4734 decltype(handled)>::value) {
4735 if (!handled) {
4736 return mcp::core::unexpected(handled.error());
4737 }
4738 auto result = server::detail::value_to_resource_read_result(
4739 *handled, context.uri);
4740 for (auto& c : result.contents) {
4741 if (c.uri.empty()) {
4742 c.uri = uri;
4743 }
4744 }
4745 return result;
4746 } else {
4747 auto result = server::detail::value_to_resource_read_result(
4748 std::move(handled), context.uri);
4749 for (auto& c : result.contents) {
4750 if (c.uri.empty()) {
4751 c.uri = uri;
4752 }
4753 }
4754 return result;
4755 }
4756 } catch (const std::exception& exception) {
4757 return mcp::core::unexpected(core::Error{
4758 static_cast<int>(protocol::ErrorCode::InvalidParams),
4759 "failed to run resource handler",
4760 exception.what(),
4761 });
4762 }
4763 });
4764 }
4765
4767 template <class Handler>
4768 Builder& resource_template(std::string name, Handler handler) {
4769 server::detail::require_callable(handler, "resource_template");
4771 if constexpr (std::is_invocable_v<Handler>) {
4772 auto handled = handler();
4773 if constexpr (server::detail::is_result<decltype(handled)>::value) {
4774 if (!handled) {
4775 throw std::runtime_error(handled.error().message);
4776 }
4777 tmpl = *handled;
4778 } else {
4779 tmpl = std::move(handled);
4780 }
4781 } else if constexpr (std::is_invocable_v<Handler, std::string>) {
4782 tmpl = handler({});
4783 } else {
4784 static_assert(
4785 std::is_invocable_v<Handler>,
4786 "resource_template handler must accept no arguments or string");
4787 }
4788 if (tmpl.name.empty()) {
4789 tmpl.name = name;
4790 }
4791 if (tmpl.uri_template.empty()) {
4792 tmpl.uri_template = std::move(name);
4793 }
4794 return add_resource_template(std::move(tmpl));
4795 }
4796
4798 template <class Handler>
4799 Builder& completion(Handler handler) {
4800 server::detail::require_callable(handler, "completion");
4801 if constexpr (server::detail::is_typed_completion_handler_v<Handler>) {
4802 server::detail::require_unambiguous_completion_handler<Handler>();
4803 } else {
4804 server::detail::require_unambiguous_json_extension_handler<Handler>();
4805 }
4806 builder_.on_completion([handler = std::move(handler)](
4807 const protocol::Json& request,
4808 const server::SessionContext& context,
4809 CancellationToken cancellation) mutable
4811 if constexpr (server::detail::is_typed_completion_handler_v<Handler>) {
4812 const auto params = protocol::complete_params_from_json(request);
4813 if (!params) {
4814 return mcp::core::unexpected(core::Error{
4815 static_cast<int>(protocol::ErrorCode::InvalidParams),
4816 params.error().message, params.error().detail, "protocol"});
4817 }
4818 server::CompletionContext completion_context;
4819 static_cast<server::SessionContext&>(completion_context) = context;
4820 completion_context.params = *params;
4821 completion_context.cancellation = cancellation;
4822 auto handled = server::detail::invoke_completion_handler(
4823 handler, completion_context);
4824 return server::detail::completion_response_to_json(std::move(handled));
4825 } else {
4826 auto handled = server::detail::invoke_json_extension_handler(
4827 handler, request, context, cancellation);
4828 if constexpr (server::detail::is_result<decltype(handled)>::value) {
4829 return handled;
4830 } else {
4831 return server::detail::value_to_json(std::move(handled));
4832 }
4833 }
4834 });
4835 return *this;
4836 }
4837
4839 template <class Handler>
4840 Builder& sampling(Handler handler) {
4841 server::detail::require_callable(handler, "sampling");
4842 server::detail::require_unambiguous_json_extension_handler<Handler>();
4843 builder_.on_sampling(
4844 [handler = std::move(handler)](const protocol::Json& request,
4845 const server::SessionContext& context,
4846 CancellationToken cancellation) mutable
4848 auto handled = server::detail::invoke_json_extension_handler(
4849 handler, request, context, cancellation);
4850 if constexpr (server::detail::is_result<decltype(handled)>::value) {
4851 return handled;
4852 } else {
4853 return server::detail::value_to_json(std::move(handled));
4854 }
4855 });
4856 return *this;
4857 }
4858
4860 template <class Handler>
4861 Builder& logging(Handler handler) {
4862 server::detail::require_callable(handler, "logging");
4863 builder_.on_logging([handler = std::move(handler)](
4864 std::string_view level, std::string_view message) {
4865 handler(level, message);
4866 });
4867 return *this;
4868 }
4869
4871 template <class Handler>
4872 Builder& raw_request(Handler handler) {
4873 server::detail::require_callable(handler, "raw_request");
4874 builder_.on_raw_request([handler = std::move(handler)](
4875 const protocol::JsonRpcRequest& request,
4876 const server::SessionContext& /*context*/)
4877 -> std::optional<protocol::JsonRpcResponse> {
4878 if constexpr (std::is_same_v<std::decay_t<decltype(handler(request))>,
4879 std::optional<protocol::JsonRpcResponse>>) {
4880 return handler(request);
4881 } else if constexpr (std::is_same_v<
4882 std::decay_t<decltype(handler(request))>,
4884 return handler(request);
4885 } else {
4886 handler(request);
4887 return std::nullopt;
4888 }
4889 });
4890 return *this;
4891 }
4892
4893 template <class Router>
4894 Builder& router(const Router& router) {
4895 router.apply_to(builder_);
4896 return *this;
4897 }
4898
4899 template <class Args, class Result, class Handler>
4900 Builder& tool(
4902 return add_tool(
4903 std::move(registration.definition),
4904 [handler = std::move(registration.handler)](
4905 const server::ToolContext& context)
4907 try {
4908 auto args =
4909 server::detail::argument_from_json<Args>(context.arguments);
4910 auto handled = server::detail::invoke_tool_handler(
4911 handler, std::move(args), context);
4912 if constexpr (server::detail::is_result<decltype(handled)>::value) {
4913 if (!handled) {
4914 return mcp::core::unexpected(handled.error());
4915 }
4916 return server::detail::value_to_tool_result(*handled);
4917 } else {
4918 return server::detail::value_to_tool_result(std::move(handled));
4919 }
4920 } catch (const std::exception& exception) {
4921 return mcp::core::unexpected(core::Error{
4922 static_cast<int>(protocol::ErrorCode::InvalidParams),
4923 "failed to decode tool arguments",
4924 exception.what(),
4925 });
4926 }
4927 });
4928 }
4929
4930 Builder& on_completion(server::Server::JsonHandler handler) {
4931 builder_.on_completion(std::move(handler));
4932 return *this;
4933 }
4934
4935 Builder& on_completion(server::Server::JsonContextHandler handler) {
4936 builder_.on_completion(std::move(handler));
4937 return *this;
4938 }
4939
4940 Builder& on_completion(server::Server::JsonRequestContextHandler handler) {
4941 builder_.on_completion(std::move(handler));
4942 return *this;
4943 }
4944
4945 Builder& on_sampling(server::Server::JsonHandler handler) {
4946 builder_.on_sampling(std::move(handler));
4947 return *this;
4948 }
4949
4950 Builder& on_sampling(server::Server::JsonContextHandler handler) {
4951 builder_.on_sampling(std::move(handler));
4952 return *this;
4953 }
4954
4955 Builder& on_sampling(server::Server::JsonRequestContextHandler handler) {
4956 builder_.on_sampling(std::move(handler));
4957 return *this;
4958 }
4959
4960 Builder& on_logging(server::Server::LoggingHandler handler) {
4961 builder_.on_logging(std::move(handler));
4962 return *this;
4963 }
4964
4965 Builder& on_raw_request(server::Server::RawRequestHandler handler) {
4966 builder_.on_raw_request(std::move(handler));
4967 return *this;
4968 }
4969
4970 Builder& on_raw_notification(server::Server::RawNotificationHandler handler) {
4971 builder_.on_raw_notification(std::move(handler));
4972 return *this;
4973 }
4974
4975 Builder& on_task_list(server::Server::TaskListHandler handler) {
4976 builder_.on_task_list(std::move(handler));
4977 return *this;
4978 }
4979
4980 Builder& on_tools_list(server::Server::ToolsListHandler handler) {
4981 builder_.on_tools_list(std::move(handler));
4982 return *this;
4983 }
4984
4985 Builder& on_prompts_list(server::Server::PromptsListHandler handler) {
4986 builder_.on_prompts_list(std::move(handler));
4987 return *this;
4988 }
4989
4990 Builder& on_resources_list(server::Server::ResourcesListHandler handler) {
4991 builder_.on_resources_list(std::move(handler));
4992 return *this;
4993 }
4994
4995 Builder& on_resource_templates_list(
4996 server::Server::ResourceTemplatesListHandler handler) {
4997 builder_.on_resource_templates_list(std::move(handler));
4998 return *this;
4999 }
5000
5001 Builder& on_task_get(server::Server::TaskGetHandler handler) {
5002 builder_.on_task_get(std::move(handler));
5003 return *this;
5004 }
5005
5006 Builder& on_task_cancel(server::Server::TaskCancelHandler handler) {
5007 builder_.on_task_cancel(std::move(handler));
5008 return *this;
5009 }
5010
5011 Builder& on_task_result(server::Server::TaskResultHandler handler) {
5012 builder_.on_task_result(std::move(handler));
5013 return *this;
5014 }
5015
5016 Builder& on_progress(server::Server::ProgressHandler handler) {
5017 builder_.on_progress(std::move(handler));
5018 return *this;
5019 }
5020
5021 Builder& on_roots_list_changed(
5022 server::Server::RootsListChangedHandler handler) {
5023 builder_.on_roots_list_changed(std::move(handler));
5024 return *this;
5025 }
5026
5027 Builder& on_tool_list_changed(server::Server::ListChangedHandler handler) {
5028 builder_.on_tool_list_changed(std::move(handler));
5029 return *this;
5030 }
5031
5032 Builder& on_prompt_list_changed(server::Server::ListChangedHandler handler) {
5033 builder_.on_prompt_list_changed(std::move(handler));
5034 return *this;
5035 }
5036
5037 Builder& on_resource_list_changed(
5038 server::Server::ListChangedHandler handler) {
5039 builder_.on_resource_list_changed(std::move(handler));
5040 return *this;
5041 }
5042
5043 Builder& on_resource_updated(server::Server::ResourceUpdatedHandler handler) {
5044 builder_.on_resource_updated(std::move(handler));
5045 return *this;
5046 }
5047
5048 Builder& handler(server::ServerHandler handler) {
5049 builder_.with_handler(std::move(handler));
5050 return *this;
5051 }
5052
5053 Builder& handler(const server::ServerHandlerInterface& handler) {
5054 builder_.with_handler(handler);
5055 return *this;
5056 }
5057
5058 core::Result<Peer> build() {
5059 auto server = builder_.build();
5060 if (!server) {
5061 return mcp::core::unexpected(server.error());
5062 }
5063
5064 // Capture handlers from the Server before moving it into the Peer.
5065 // The Peer's own handler fields must be populated so that
5066 // ServerPeer::handle_request() dispatches through them.
5067 auto raw_handler = (*server)->raw_request_handler();
5068 auto raw_context_handler = (*server)->raw_request_context_handler();
5069
5070 Peer peer(std::move(*server));
5071 if (raw_handler) {
5072 peer.raw_request_handler_ = std::move(raw_handler);
5073 }
5074 if (raw_context_handler) {
5075 peer.raw_request_context_handler_ = std::move(raw_context_handler);
5076 }
5077 for (auto& transport : native_transports_) {
5078 auto added = peer.add_transport(std::move(transport));
5079 if (!added) {
5080 return mcp::core::unexpected(added.error());
5081 }
5082 }
5083 native_transports_.clear();
5084 return peer;
5085 }
5086
5089 int run();
5090
5091 private:
5092 server::ServerBuilder builder_;
5093 std::vector<std::unique_ptr<transport::ServerTransport>> native_transports_;
5094};
5095
5097 return Builder{};
5098}
5099
5102
5103} // namespace mcp
High-level server authoring helpers and typed App builder adapters.
TypedToolBuilder< Args, Result > tool(std::string name)
Starts a typed tool registration builder.
Definition authoring.hpp:155
Cooperative cancellation primitives shared by SDK lifecycle APIs.
ServerCapabilitiesBuilder server_capabilities()
Starts a fluent server capability builder.
Definition capabilities.hpp:568
Copyable token observed by cancellation-aware SDK operations.
Definition cancellation.hpp:104
static CancellationToken none()
Constructs a detached token that is never cancelled.
Definition cancellation.hpp:110
bool cancelled() const noexcept
Returns true after the associated source has requested cancellation.
Definition cancellation.hpp:117
Builder & process_stdio(std::string command)
Convenience: launch a child process as the MCP server.
Definition peer.hpp:2584
Client-side peer boundary for talking to an MCP server.
Definition peer.hpp:319
Peer(std::unique_ptr< client::Transport > transport)
Creates a client peer from an owned transport.
Definition peer.hpp:449
Peer(client::Client client)
Creates a client peer from an existing client implementation.
Definition peer.hpp:459
core::Result< core::Unit > serve_transport(transport::ClientTransport &transport, CancellationToken cancellation=CancellationToken::none())
Runs a sequential receive loop over a role-generic client transport.
Definition peer.hpp:1899
core::Result< std::optional< protocol::JsonRpcMessage > > dispatch_message(const protocol::JsonRpcMessage &message)
Dispatches one inbound role-generic transport message.
Definition peer.hpp:1858
Peer(std::unique_ptr< transport::ClientTransport > transport)
Creates a client peer from an owned role-generic transport.
Definition peer.hpp:453
Builder & tool(std::string name, Handler handler)
Registers a tool with a typed handler.
Definition peer.hpp:4566
Builder & logging(Handler handler)
Registers a logging handler.
Definition peer.hpp:4861
Builder & resource(std::string name, Handler handler)
Registers a resource with auto-detected handler signature.
Definition peer.hpp:4709
Builder & prompt(std::string name, Handler handler)
Registers a typed prompt with argument decoding.
Definition peer.hpp:4606
Builder & prompt(std::string name, Handler handler)
Registers a prompt with auto-detected handler signature.
Definition peer.hpp:4639
Builder & raw_request(Handler handler)
Registers a raw request handler.
Definition peer.hpp:4872
Builder & tool(protocol::ToolDefinition definition, Handler handler)
Registers a tool with an explicit definition and typed handler.
Definition peer.hpp:4576
Builder & sampling(Handler handler)
Registers a sampling handler.
Definition peer.hpp:4840
Builder & resource_template(std::string name, Handler handler)
Registers a resource template with a callable adapter.
Definition peer.hpp:4768
Builder & completion(Handler handler)
Registers a completion handler.
Definition peer.hpp:4799
Builder & resource(std::string name, Handler handler)
Registers a typed resource with parameter decoding.
Definition peer.hpp:4670
Server-side peer boundary for exposing MCP capabilities.
Definition peer.hpp:3010
Peer(std::unique_ptr< server::Server > server)
Creates a server peer from an owned server implementation.
Definition peer.hpp:3019
core::Result< std::optional< protocol::JsonRpcMessage > > dispatch_message(const protocol::JsonRpcMessage &message, const server::SessionContext &context={}, transport::ServerTransport *native_transport=nullptr)
Dispatches one inbound role-generic transport message.
Definition peer.hpp:3896
Peer(server::ServerOptions options={})
Creates a server peer from options.
Definition peer.hpp:3015
core::Result< core::Unit > add_transport(std::unique_ptr< transport::ServerTransport > transport)
Adds an owned role-generic server transport.
Definition peer.hpp:3773
core::Result< core::Unit > serve_transport(transport::ServerTransport &transport, const server::SessionContext &context={}, CancellationToken cancellation=CancellationToken::none())
Runs a receive loop over a role-generic server transport.
Definition peer.hpp:3949
void wait_until_ready()
Blocks until all native transports are ready to process messages.
Definition peer.hpp:3846
Role-specialized MCP peer boundary.
Definition peer.hpp:310
static RequestHandle ready(protocol::RequestId request_id, ResultType result)
Creates a handle whose result is already available.
Definition request.hpp:132
High-level MCP client compatibility API.
Definition client.hpp:160
std::function< void(std::string_view, std::string_view)> LoggingMessageHandler
Receives logging messages from the server.
Definition client.hpp:222
std::function< void(const protocol::ProgressNotificationParams &)> ProgressHandler
Receives progress notifications associated with a progress token.
Definition client.hpp:243
std::function< void(const std::string &)> ResourceUpdatedHandler
Receives resource update notifications.
Definition client.hpp:239
std::function< core::Result< protocol::RootsListResult >()> RootsListRequestHandler
Handles a server request for the client's current roots.
Definition client.hpp:255
std::function< void(const protocol::RequestId &, std::string_view)> CancelledHandler
Receives cancellation notifications for in-flight requests.
Definition client.hpp:231
std::function< core::Result< protocol::Json >(const protocol::JsonRpcRequest &)> CustomRequestHandler
Handles non-built-in server requests.
Definition client.hpp:283
std::function< core::Result< protocol::CreateMessageResult >(const protocol::CreateMessageParams &)> SamplingRequestHandler
Handles a server sampling request.
Definition client.hpp:264
std::function< void(std::string_view)> ElicitationCompleteHandler
Receives completion notifications for elicitation flows.
Definition client.hpp:247
std::function< void()> ListChangedHandler
Receives list-change notifications for prompts, resources, tools, or roots.
Definition client.hpp:235
std::function< void()> InitializedHandler
Receives notifications that the peer completed initialization.
Definition client.hpp:225
std::function< void(const protocol::JsonRpcNotification &)> RawNotificationHandler
Observes every inbound notification after built-in dispatch.
Definition client.hpp:293
std::function< core::Result< protocol::CreateElicitationResult >(const protocol::CreateElicitationRequestParam &)> ElicitationRequestHandler
Handles a server elicitation request.
Definition client.hpp:274
std::function< void(const protocol::Task &)> TaskStatusHandler
Receives task status notifications.
Definition client.hpp:250
Fluent builder for constructing a configured Server.
Definition server.hpp:576
core::Result< std::unique_ptr< Server > > build()
Builds a configured server.
std::function< core::Result< core::Unit >(const std::string &uri, const SessionContext &)> ResourceUpdatedHandler
Handles client resource-updated notifications.
Definition server.hpp:402
std::function< core::Result< protocol::PromptsListResult >(const protocol::PaginatedRequestParams &, const SessionContext &)> PromptsListHandler
Handles prompts/list requests.
Definition server.hpp:364
std::function< core::Result< protocol::ResourceTemplatesListResult >(const protocol::PaginatedRequestParams &, const SessionContext &)> ResourceTemplatesListHandler
Handles resources/templates/list requests.
Definition server.hpp:374
std::function< core::Result< protocol::Task >(const protocol::TaskGetParams &, const SessionContext &)> TaskGetHandler
Handles task get requests.
Definition server.hpp:378
std::function< core::Result< protocol::Json >(const protocol::TaskResultParams &, const SessionContext &)> TaskResultHandler
Handles task result requests.
Definition server.hpp:386
std::function< core::Result< protocol::Task >(const protocol::TaskCancelParams &, const SessionContext &)> TaskCancelHandler
Handles task cancel requests.
Definition server.hpp:382
std::function< std::optional< protocol::JsonRpcResponse >(const protocol::JsonRpcRequest &, const SessionContext &)> RawRequestHandler
Optionally handles raw or custom requests before built-in fallback.
Definition server.hpp:342
std::function< core::Result< protocol::Json >(const protocol::Json &)> JsonHandler
Handles JSON-shaped extension requests such as completion or sampling.
Definition server.hpp:327
std::function< core::Result< core::Unit >(const protocol::JsonRpcNotification &, const SessionContext &)> RawNotificationHandler
Handles raw or custom notifications.
Definition server.hpp:350
std::function< void(std::string_view, std::string_view)> LoggingHandler
Handles logging messages from clients.
Definition server.hpp:335
std::function< core::Result< core::Unit >(const SessionContext &)> RootsListChangedHandler
Handles client roots-list-changed notifications.
Definition server.hpp:390
std::function< core::Result< protocol::ResourcesListResult >(const protocol::PaginatedRequestParams &, const SessionContext &)> ResourcesListHandler
Handles resources/list requests.
Definition server.hpp:369
std::function< core::Result< core::Unit >(const SessionContext &)> ListChangedHandler
Handles client list-changed notifications.
Definition server.hpp:398
std::function< core::Result< core::Unit >(const protocol::ProgressNotificationParams &, const SessionContext &)> ProgressHandler
Handles client progress notifications.
Definition server.hpp:394
std::function< core::Result< protocol::TaskListResult >(const protocol::TaskListParams &, const SessionContext &)> TaskListHandler
Handles task list requests.
Definition server.hpp:354
std::function< core::Result< protocol::ToolsListResult >(const protocol::PaginatedRequestParams &, const SessionContext &)> ToolsListHandler
Handles tools/list requests.
Definition server.hpp:359
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.
Internal factory declarations for client transport compatibility adapters.
Public SDK configuration and compatibility markers.
mcp::CancellationToken CancellationToken
Copyable cooperative cancellation token for server handlers.
Definition context.hpp:22
Stable public error helpers for SDK request and dispatch paths.
std::function< core::Result< protocol::ToolResult >(const ToolContext &)> ToolHandler
Application callback that executes a tool.
Definition handler_types.hpp:21
std::function< core::Result< protocol::ResourcesReadResult >(const ResourceContext &)> ResourceReadHandler
Application callback that reads a resource.
Definition handler_types.hpp:32
std::function< core::Result< protocol::PromptsGetResult >(const PromptContext &)> PromptHandler
Application callback that renders a prompt.
Definition handler_types.hpp:26
Umbrella include for client and server handler aggregate structs.
Typed initialize request and result payloads.
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
Role markers shared by peer, service, and transport SDK boundaries.
@ None
Do not include any server context.
Server-side handle for sending requests and notifications to a client peer.
Internal factory declarations for server transport compatibility adapters.
Convenience session wrapper around Client initialization and discovery.
Marker for a client-side MCP role.
Definition roles.hpp:11
Marker for a server-side MCP role.
Definition roles.hpp:14
Endpoint options for launching a child process over stdio.
Definition client.hpp:205
Structured error returned by fallible SDK operations.
Definition result.hpp:35
std::string message
Short human-readable explanation of the failure.
Definition result.hpp:40
int code
Numeric protocol, transport, or component-specific error code.
Definition result.hpp:37
RootCapabilities roots
Roots feature support.
Definition capabilities.hpp:298
JSON-RPC request envelope carrying an MCP method invocation.
Definition types.hpp:99
JSON-RPC response envelope for either success or failure.
Definition types.hpp:115
Prompt descriptor returned by prompts/list.
Definition prompt.hpp:83
URI template advertised by resources/templates/list.
Definition resource.hpp:127
std::string uri_template
URI template string that can be expanded into concrete resource URIs.
Definition resource.hpp:131
std::string name
Stable template name.
Definition resource.hpp:133
Concrete resource advertised by resources/list.
Definition resource.hpp:28
Result object for resources/read.
Definition resource.hpp:285
bool enabled
Whether the client supports roots/list.
Definition capabilities.hpp:94
Capabilities advertised by an MCP server in the initialize result.
Definition capabilities.hpp:458
Snapshot of an asynchronous task.
Definition task.hpp:144
TaskStatus status
Current lifecycle status.
Definition task.hpp:148
Metadata describing a callable MCP tool.
Definition tool.hpp:213
Completion request context passed to typed completion handlers.
Definition context.hpp:86
protocol::CompleteParams params
Parsed completion/complete request parameters.
Definition context.hpp:88
CancellationToken cancellation
Cooperative cancellation token for this request.
Definition context.hpp:90
Invocation context passed to prompt handlers.
Definition context.hpp:56
protocol::Json arguments
JSON arguments supplied with the prompt request.
Definition context.hpp:61
Invocation context passed to resource read handlers.
Definition context.hpp:70
std::string uri
Requested resource URI.
Definition context.hpp:75
protocol::Json params
Raw resource read parameters supplied by the client.
Definition context.hpp:77
Configuration used to construct a Server.
Definition server.hpp:44
Per-message connection metadata supplied to server handlers.
Definition transport.hpp:43
std::string session_id
Logical MCP session id when the transport has one.
Definition transport.hpp:45
Options for the SDK server task processor.
Definition task_manager.hpp:35
Invocation context passed to tool handlers.
Definition context.hpp:30
protocol::Json arguments
JSON arguments supplied with the tool call.
Definition context.hpp:35
Typed prompt registration produced by mcp::server::prompt().
Definition authoring.hpp:202
Typed tool registration produced by mcp::server::tool().
Definition authoring.hpp:58
Definition handler_dispatch.hpp:22
Configuration for a child-process stdio client transport.
Definition process_stdio_transport.hpp:22
std::string command
Executable to launch.
Definition process_stdio_transport.hpp:24
Configuration for a Streamable HTTP server transport.
Definition http_transport.hpp:104
int listen_port
TCP port to listen on. Must be in the range 1..65535.
Definition http_transport.hpp:109
std::string path
HTTP path for POST, GET/SSE, and DELETE session requests.
Definition http_transport.hpp:112
std::string listen_host
Interface address passed to the underlying HTTP server.
Definition http_transport.hpp:106
Configuration for the WebSocket client transport.
Definition websocket_transport.hpp:32
std::optional< std::string > auth_header
Optional Authorization header value (e.g. "Bearer <token>").
Definition websocket_transport.hpp:50
std::unordered_map< std::string, std::string > headers
Extra HTTP headers sent during the WebSocket upgrade handshake.
Definition websocket_transport.hpp:47
std::string uri
Full ws:// or wss:// URI for the MCP endpoint.
Definition websocket_transport.hpp:35
std::chrono::milliseconds timeout
Per-operation timeout for the WebSocket connection.
Definition websocket_transport.hpp:53
Configuration for the WebSocket server transport.
Definition websocket_transport.hpp:78
std::string path
WebSocket endpoint path pattern (e.g. "/mcp").
Definition websocket_transport.hpp:86
std::string listen_host
Interface address to bind.
Definition websocket_transport.hpp:80
int listen_port
TCP port to listen on.
Definition websocket_transport.hpp:83
TaskStatus
Lifecycle status for an asynchronous MCP task.
Definition task.hpp:28
Role-generic client transport for Streamable HTTP MCP servers.
Role-generic client transport for child-process stdio MCP servers.
Role-generic newline-delimited stdio transport.
Role-generic MCP transport contract for SDK peer/service layers.
WebSocket transport implementations for MCP client and server peers.