cxxmcp 1.2.4
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#endif
2613
2614 Builder& timeout(std::chrono::milliseconds value) {
2615 timeout_ = value;
2616 return *this;
2617 }
2618
2619 Builder& capabilities(protocol::ClientCapabilities value) {
2620 capabilities_ = std::move(value);
2621 return *this;
2622 }
2623
2624 Builder& roots(std::vector<protocol::Root> value) {
2625 roots_ = std::move(value);
2626 return *this;
2627 }
2628
2629 Builder& on_initialized(client::Client::InitializedHandler handler) {
2630 initialized_handler_ = std::move(handler);
2631 return *this;
2632 }
2633
2634 Builder& on_cancelled(client::Client::CancelledHandler handler) {
2635 cancelled_handler_ = std::move(handler);
2636 return *this;
2637 }
2638
2639 Builder& on_logging_message(client::Client::LoggingMessageHandler handler) {
2640 logging_message_handler_ = std::move(handler);
2641 return *this;
2642 }
2643
2644 Builder& on_tool_list_changed(client::Client::ListChangedHandler handler) {
2645 tool_list_changed_handler_ = std::move(handler);
2646 return *this;
2647 }
2648
2649 Builder& on_prompt_list_changed(client::Client::ListChangedHandler handler) {
2650 prompt_list_changed_handler_ = std::move(handler);
2651 return *this;
2652 }
2653
2654 Builder& on_resource_list_changed(
2656 resource_list_changed_handler_ = std::move(handler);
2657 return *this;
2658 }
2659
2660 Builder& on_resource_updated(client::Client::ResourceUpdatedHandler handler) {
2661 resource_updated_handler_ = std::move(handler);
2662 return *this;
2663 }
2664
2665 Builder& on_progress(client::Client::ProgressHandler handler) {
2666 progress_handler_ = std::move(handler);
2667 return *this;
2668 }
2669
2670 Builder& on_elicitation_complete(
2672 elicitation_complete_handler_ = std::move(handler);
2673 return *this;
2674 }
2675
2676 Builder& on_task_status(client::Client::TaskStatusHandler handler) {
2677 task_status_handler_ = std::move(handler);
2678 return *this;
2679 }
2680
2681 Builder& on_roots_list_changed(client::Client::ListChangedHandler handler) {
2682 roots_list_changed_handler_ = std::move(handler);
2683 return *this;
2684 }
2685
2686 Builder& on_list_roots_request(
2687 client::Client::ListRootsRequestHandler handler) {
2688 roots_list_request_handler_ = std::move(handler);
2689 return *this;
2690 }
2691
2692 Builder& on_list_roots_request(
2693 client::Client::RootsListRequestCancellationHandler handler) {
2694 roots_list_request_cancellation_handler_ = std::move(handler);
2695 return *this;
2696 }
2697
2698 Builder& on_create_message_request(
2699 client::Client::CreateMessageRequestHandler handler) {
2700 sampling_request_handler_ = std::move(handler);
2701 return *this;
2702 }
2703
2704 Builder& on_create_message_request(
2705 client::Client::SamplingRequestCancellationHandler handler) {
2706 sampling_request_cancellation_handler_ = std::move(handler);
2707 return *this;
2708 }
2709
2710 Builder& on_create_elicitation_request(
2711 client::Client::CreateElicitationRequestHandler handler) {
2712 elicitation_request_handler_ = std::move(handler);
2713 return *this;
2714 }
2715
2716 Builder& on_create_elicitation_request(
2717 client::Client::ElicitationRequestCancellationHandler handler) {
2718 elicitation_request_cancellation_handler_ = std::move(handler);
2719 return *this;
2720 }
2721
2722 Builder& on_custom_request(client::Client::CustomRequestHandler handler) {
2723 custom_request_handler_ = std::move(handler);
2724 return *this;
2725 }
2726
2727 Builder& on_custom_request(
2728 client::Client::CustomRequestCancellationHandler handler) {
2729 custom_request_cancellation_handler_ = std::move(handler);
2730 return *this;
2731 }
2732
2733 Builder& on_raw_notification(client::Client::RawNotificationHandler handler) {
2734 raw_notification_handler_ = std::move(handler);
2735 return *this;
2736 }
2737
2738 Builder& handler(const client::ClientHandler& handler) {
2739 handler_ = handler;
2740 return *this;
2741 }
2742
2743 core::Result<Peer> build() {
2744 auto peer = make_peer();
2745 if (!peer) {
2746 return mcp::core::unexpected(peer.error());
2747 }
2748 apply_to(*peer);
2749 return std::move(*peer);
2750 }
2751
2766 template <class Fn>
2767 int run(Fn&& fn);
2768
2769 private:
2770 enum class TransportKind {
2771 None,
2772 Concrete,
2773 Native,
2774#if defined(CXXMCP_ENABLE_HTTP)
2775 StreamableHttp,
2776 LegacySse,
2777#endif
2778#if defined(CXXMCP_ENABLE_WEBSOCKET)
2779 WebSocket,
2780#endif
2781 Stdio,
2782 };
2783
2784 void reset_transport() {
2785 concrete_transport_.reset();
2786 native_transport_.reset();
2787#if defined(CXXMCP_ENABLE_HTTP)
2788 http_endpoint_ = client::Client::StreamableHttpEndpoint{};
2789#endif
2790#if defined(CXXMCP_ENABLE_WEBSOCKET)
2791 ws_options_ = {};
2792#endif
2793 stdio_endpoint_ = client::Client::StdioEndpoint{};
2794 transport_kind_ = TransportKind::None;
2795 }
2796
2797#if defined(CXXMCP_ENABLE_HTTP)
2798 client::Client::StreamableHttpEndpoint configured_http_endpoint() {
2799 auto endpoint = std::move(http_endpoint_);
2800 for (auto& header : http_headers_) {
2801 endpoint.headers[std::move(header.first)] = std::move(header.second);
2802 }
2803 if (auth_header_.has_value()) {
2804 endpoint.auth_header = std::move(auth_header_);
2805 }
2806 if (auth_refresh_handler_) {
2807 endpoint.auth_refresh_handler = std::move(auth_refresh_handler_);
2808 }
2809 if (timeout_.has_value()) {
2810 endpoint.timeout = *timeout_;
2811 }
2812 return endpoint;
2813 }
2814#endif
2815
2816 core::Result<Peer> make_peer() {
2817 switch (transport_kind_) {
2818 case TransportKind::Concrete:
2819 if (!concrete_transport_) {
2820 return missing_transport("client transport is null");
2821 }
2822 return Peer(std::move(concrete_transport_));
2823 case TransportKind::Native:
2824 if (!native_transport_) {
2825 return missing_transport("client transport is null");
2826 }
2827 return Peer(std::move(native_transport_));
2828#if defined(CXXMCP_ENABLE_HTTP)
2829 case TransportKind::StreamableHttp:
2830 return Peer::connect_streamable_http(configured_http_endpoint());
2831 case TransportKind::LegacySse:
2832 return Peer::connect_legacy_sse(configured_http_endpoint());
2833#endif
2834#if defined(CXXMCP_ENABLE_WEBSOCKET)
2835 case TransportKind::WebSocket: {
2836 auto options = std::move(ws_options_);
2837 for (auto& [name, value] : http_headers_) {
2838 options.headers[std::move(name)] = std::move(value);
2839 }
2840 if (auth_header_.has_value()) {
2841 options.auth_header = std::string("Bearer ") + *auth_header_;
2842 auth_header_.reset();
2843 }
2844 if (timeout_.has_value()) {
2845 options.timeout = *timeout_;
2846 }
2847 return Peer(std::make_unique<transport::WebSocketClientTransport>(
2848 std::move(options)));
2849 }
2850#endif
2851 case TransportKind::Stdio:
2852 return Peer::connect_stdio(std::move(stdio_endpoint_));
2853 case TransportKind::None:
2854 return missing_transport("client peer transport is not configured");
2855 }
2856 return missing_transport("client peer transport is not configured");
2857 }
2858
2859 static core::Result<Peer> missing_transport(std::string message) {
2860 return mcp::core::unexpected(core::Error{
2861 static_cast<int>(protocol::ErrorCode::InvalidParams),
2862 std::move(message),
2863 {},
2864 "configuration",
2865 });
2866 }
2867
2868 void apply_to(Peer& peer) {
2869 if (capabilities_.has_value()) {
2870 peer.set_capabilities(std::move(*capabilities_));
2871 }
2872 if (roots_.has_value()) {
2873 peer.set_roots(std::move(*roots_));
2874 }
2875 if (handler_.has_value()) {
2876 peer.set_handler(*handler_);
2877 }
2878 if (initialized_handler_) {
2879 peer.on_initialized(std::move(initialized_handler_));
2880 }
2881 if (cancelled_handler_) {
2882 peer.on_cancelled(std::move(cancelled_handler_));
2883 }
2884 if (logging_message_handler_) {
2885 peer.on_logging_message(std::move(logging_message_handler_));
2886 }
2887 if (tool_list_changed_handler_) {
2888 peer.on_tool_list_changed(std::move(tool_list_changed_handler_));
2889 }
2890 if (prompt_list_changed_handler_) {
2891 peer.on_prompt_list_changed(std::move(prompt_list_changed_handler_));
2892 }
2893 if (resource_list_changed_handler_) {
2894 peer.on_resource_list_changed(std::move(resource_list_changed_handler_));
2895 }
2896 if (resource_updated_handler_) {
2897 peer.on_resource_updated(std::move(resource_updated_handler_));
2898 }
2899 if (progress_handler_) {
2900 peer.on_progress(std::move(progress_handler_));
2901 }
2902 if (elicitation_complete_handler_) {
2903 peer.on_elicitation_complete(std::move(elicitation_complete_handler_));
2904 }
2905 if (task_status_handler_) {
2906 peer.on_task_status(std::move(task_status_handler_));
2907 }
2908 if (roots_list_changed_handler_) {
2909 peer.on_roots_list_changed(std::move(roots_list_changed_handler_));
2910 }
2911 if (roots_list_request_handler_) {
2912 peer.on_list_roots_request(std::move(roots_list_request_handler_));
2913 }
2914 if (roots_list_request_cancellation_handler_) {
2915 peer.on_list_roots_request(
2916 std::move(roots_list_request_cancellation_handler_));
2917 }
2918 if (sampling_request_handler_) {
2919 peer.on_create_message_request(std::move(sampling_request_handler_));
2920 }
2921 if (sampling_request_cancellation_handler_) {
2922 peer.on_create_message_request(
2923 std::move(sampling_request_cancellation_handler_));
2924 }
2925 if (elicitation_request_handler_) {
2926 peer.on_create_elicitation_request(
2927 std::move(elicitation_request_handler_));
2928 }
2929 if (elicitation_request_cancellation_handler_) {
2930 peer.on_create_elicitation_request(
2931 std::move(elicitation_request_cancellation_handler_));
2932 }
2933 if (custom_request_handler_) {
2934 peer.on_custom_request(std::move(custom_request_handler_));
2935 }
2936 if (custom_request_cancellation_handler_) {
2937 peer.on_custom_request(std::move(custom_request_cancellation_handler_));
2938 }
2939 if (raw_notification_handler_) {
2940 peer.on_raw_notification(std::move(raw_notification_handler_));
2941 }
2942 }
2943
2944 TransportKind transport_kind_ = TransportKind::None;
2945 std::unique_ptr<client::Transport> concrete_transport_;
2946 std::unique_ptr<transport::ClientTransport> native_transport_;
2947#if defined(CXXMCP_ENABLE_HTTP) || defined(CXXMCP_ENABLE_WEBSOCKET)
2948 std::unordered_map<std::string, std::string> http_headers_;
2949 std::optional<std::string> auth_header_;
2950#endif
2951#if defined(CXXMCP_ENABLE_HTTP)
2952 client::Client::StreamableHttpEndpoint http_endpoint_;
2953 client::HttpAuthRefreshHandler auth_refresh_handler_;
2954#endif
2955#if defined(CXXMCP_ENABLE_WEBSOCKET)
2956 transport::WebSocketClientTransportOptions ws_options_;
2957#endif
2958 client::Client::StdioEndpoint stdio_endpoint_;
2959 std::optional<std::chrono::milliseconds> timeout_;
2960 std::optional<protocol::ClientCapabilities> capabilities_;
2961 std::optional<std::vector<protocol::Root>> roots_;
2962 std::optional<client::ClientHandler> handler_;
2963 client::Client::InitializedHandler initialized_handler_;
2964 client::Client::CancelledHandler cancelled_handler_;
2965 client::Client::LoggingMessageHandler logging_message_handler_;
2966 client::Client::ListChangedHandler tool_list_changed_handler_;
2967 client::Client::ListChangedHandler prompt_list_changed_handler_;
2968 client::Client::ListChangedHandler resource_list_changed_handler_;
2969 client::Client::ResourceUpdatedHandler resource_updated_handler_;
2970 client::Client::ProgressHandler progress_handler_;
2971 client::Client::ElicitationCompleteHandler elicitation_complete_handler_;
2972 client::Client::TaskStatusHandler task_status_handler_;
2973 client::Client::ListChangedHandler roots_list_changed_handler_;
2974 client::Client::RootsListRequestHandler roots_list_request_handler_;
2975 client::Client::RootsListRequestCancellationHandler
2976 roots_list_request_cancellation_handler_;
2977 client::Client::SamplingRequestHandler sampling_request_handler_;
2978 client::Client::SamplingRequestCancellationHandler
2979 sampling_request_cancellation_handler_;
2980 client::Client::ElicitationRequestHandler elicitation_request_handler_;
2981 client::Client::ElicitationRequestCancellationHandler
2982 elicitation_request_cancellation_handler_;
2983 client::Client::CustomRequestHandler custom_request_handler_;
2984 client::Client::CustomRequestCancellationHandler
2985 custom_request_cancellation_handler_;
2986 client::Client::RawNotificationHandler raw_notification_handler_;
2987};
2988
2990 return Builder{};
2991}
2992
2994template <>
2996 public:
2997 class Builder;
2998
3000 explicit Peer(server::ServerOptions options = {})
3001 : server_(std::make_unique<server::Server>(std::move(options))) {}
3002
3004 explicit Peer(std::unique_ptr<server::Server> server)
3005 : server_(std::move(server)) {}
3006
3007 static core::Result<Peer> build(server::ServerBuilder builder) {
3008 auto built = builder.build();
3009 if (!built) {
3010 return mcp::core::unexpected(built.error());
3011 }
3012 return Peer(std::move(*built));
3013 }
3014
3016 static Builder builder();
3017
3018 CXXMCP_DEPRECATED(
3019 "server() is a compatibility escape hatch; prefer ServerPeer methods")
3020 server::Server& server() noexcept { return *server_; }
3021
3022 CXXMCP_DEPRECATED(
3023 "server() is a compatibility escape hatch; prefer ServerPeer methods")
3024 const server::Server& server() const noexcept { return *server_; }
3025
3026 server::ServerInfo get_info() const { return server_->get_info(); }
3027
3028 const protocol::ServerCapabilities& capabilities() const noexcept {
3029 return server_->capabilities();
3030 }
3031
3032 Peer& set_completion_handler(server::Server::JsonHandler handler) {
3033 if (handler) {
3034 completion_handler_ = [handler](const protocol::Json& params,
3035 const server::SessionContext&) mutable {
3036 return handler(params);
3037 };
3038 } else {
3039 completion_handler_ = {};
3040 }
3041 server_->set_completion_handler(std::move(handler));
3042 return *this;
3043 }
3044
3045 Peer& set_completion_handler(server::Server::JsonContextHandler handler) {
3046 completion_handler_ = handler;
3047 server_->set_completion_handler(std::move(handler));
3048 return *this;
3049 }
3050
3051 Peer& set_sampling_handler(server::Server::JsonHandler handler) {
3052 if (handler) {
3053 sampling_handler_ = [handler](const protocol::Json& params,
3054 const server::SessionContext&) mutable {
3055 return handler(params);
3056 };
3057 } else {
3058 sampling_handler_ = {};
3059 }
3060 server_->set_sampling_handler(std::move(handler));
3061 return *this;
3062 }
3063
3064 Peer& set_sampling_handler(server::Server::JsonContextHandler handler) {
3065 sampling_handler_ = handler;
3066 server_->set_sampling_handler(std::move(handler));
3067 return *this;
3068 }
3069
3070 Peer& set_logging_handler(server::Server::LoggingHandler handler) {
3071 logging_handler_ = handler;
3072 server_->set_logging_handler(std::move(handler));
3073 return *this;
3074 }
3075
3076 Peer& set_raw_request_handler(server::Server::RawRequestHandler handler) {
3077 raw_request_handler_ = handler;
3078 server_->set_raw_request_handler(std::move(handler));
3079 return *this;
3080 }
3081
3082 Peer& set_raw_request_handler(
3083 server::Server::RawRequestContextHandler handler) {
3084 raw_request_context_handler_ = handler;
3085 server_->set_raw_request_handler(std::move(handler));
3086 return *this;
3087 }
3088
3089 Peer& set_raw_notification_handler(
3091 native_notification_state_ = true;
3092 raw_notification_handler_ = handler;
3093 server_->set_raw_notification_handler(std::move(handler));
3094 return *this;
3095 }
3096
3097 Peer& set_custom_request_handler(server::Server::RawRequestHandler handler) {
3098 raw_request_handler_ = handler;
3099 server_->set_custom_request_handler(std::move(handler));
3100 return *this;
3101 }
3102
3103 Peer& set_custom_request_handler(
3104 server::Server::RawRequestContextHandler handler) {
3105 raw_request_context_handler_ = handler;
3106 server_->set_custom_request_handler(std::move(handler));
3107 return *this;
3108 }
3109
3110 Peer& set_custom_notification_handler(
3112 native_notification_state_ = true;
3113 raw_notification_handler_ = handler;
3114 server_->set_custom_notification_handler(std::move(handler));
3115 return *this;
3116 }
3117
3118 Peer& set_tools_list_handler(server::Server::ToolsListHandler handler) {
3119 server_->set_tools_list_handler(std::move(handler));
3120 return *this;
3121 }
3122
3123 Peer& set_prompts_list_handler(server::Server::PromptsListHandler handler) {
3124 server_->set_prompts_list_handler(std::move(handler));
3125 return *this;
3126 }
3127
3128 Peer& set_resources_list_handler(
3130 server_->set_resources_list_handler(std::move(handler));
3131 return *this;
3132 }
3133
3134 Peer& set_resource_templates_list_handler(
3136 server_->set_resource_templates_list_handler(std::move(handler));
3137 return *this;
3138 }
3139
3140 Peer& set_task_list_handler(server::Server::TaskListHandler handler) {
3141 task_list_handler_ = handler;
3142 server_->set_task_list_handler(std::move(handler));
3143 return *this;
3144 }
3145
3146 Peer& set_task_get_handler(server::Server::TaskGetHandler handler) {
3147 task_get_handler_ = handler;
3148 server_->set_task_get_handler(std::move(handler));
3149 return *this;
3150 }
3151
3152 Peer& set_task_cancel_handler(server::Server::TaskCancelHandler handler) {
3153 task_cancel_handler_ = handler;
3154 server_->set_task_cancel_handler(std::move(handler));
3155 return *this;
3156 }
3157
3158 Peer& set_task_result_handler(server::Server::TaskResultHandler handler) {
3159 task_result_handler_ = handler;
3160 server_->set_task_result_handler(std::move(handler));
3161 return *this;
3162 }
3163
3164 Peer& set_progress_handler(server::Server::ProgressHandler handler) {
3165 native_notification_state_ = true;
3166 progress_handler_ = handler;
3167 server_->set_progress_handler(std::move(handler));
3168 return *this;
3169 }
3170
3171 Peer& set_roots_list_changed_handler(
3173 native_notification_state_ = true;
3174 roots_list_changed_handler_ = handler;
3175 server_->set_roots_list_changed_handler(std::move(handler));
3176 return *this;
3177 }
3178
3179 Peer& set_tool_list_changed_handler(
3181 native_notification_state_ = true;
3182 tool_list_changed_handler_ = handler;
3183 server_->set_tool_list_changed_handler(std::move(handler));
3184 return *this;
3185 }
3186
3187 Peer& set_prompt_list_changed_handler(
3189 native_notification_state_ = true;
3190 prompt_list_changed_handler_ = handler;
3191 server_->set_prompt_list_changed_handler(std::move(handler));
3192 return *this;
3193 }
3194
3195 Peer& set_resource_list_changed_handler(
3197 native_notification_state_ = true;
3198 resource_list_changed_handler_ = handler;
3199 server_->set_resource_list_changed_handler(std::move(handler));
3200 return *this;
3201 }
3202
3203 Peer& set_resource_updated_handler(
3205 native_notification_state_ = true;
3206 resource_updated_handler_ = handler;
3207 server_->set_resource_updated_handler(std::move(handler));
3208 return *this;
3209 }
3210
3211 Peer& set_handler(const server::ServerHandler& handler) {
3212 if (handler.on_completion) {
3213 set_completion_handler(handler.on_completion);
3214 }
3215 if (handler.on_sampling) {
3216 set_sampling_handler(handler.on_sampling);
3217 }
3218 if (handler.on_logging) {
3219 set_logging_handler(handler.on_logging);
3220 }
3221 if (handler.on_raw_request) {
3222 set_raw_request_handler(handler.on_raw_request);
3223 }
3224 if (handler.on_raw_notification) {
3225 set_raw_notification_handler(handler.on_raw_notification);
3226 }
3227 if (handler.on_custom_request) {
3228 set_custom_request_handler(handler.on_custom_request);
3229 }
3230 if (handler.on_custom_notification) {
3231 set_custom_notification_handler(handler.on_custom_notification);
3232 }
3233 if (handler.on_tools_list) {
3234 set_tools_list_handler(handler.on_tools_list);
3235 }
3236 if (handler.on_prompts_list) {
3237 set_prompts_list_handler(handler.on_prompts_list);
3238 }
3239 if (handler.on_resources_list) {
3240 set_resources_list_handler(handler.on_resources_list);
3241 }
3242 if (handler.on_resource_templates_list) {
3243 set_resource_templates_list_handler(handler.on_resource_templates_list);
3244 }
3245 if (handler.on_task_list) {
3246 set_task_list_handler(handler.on_task_list);
3247 }
3248 if (handler.on_task_get) {
3249 set_task_get_handler(handler.on_task_get);
3250 }
3251 if (handler.on_task_cancel) {
3252 set_task_cancel_handler(handler.on_task_cancel);
3253 }
3254 if (handler.on_task_result) {
3255 set_task_result_handler(handler.on_task_result);
3256 }
3257 if (handler.on_progress) {
3258 set_progress_handler(handler.on_progress);
3259 }
3260 if (handler.on_roots_list_changed) {
3261 set_roots_list_changed_handler(handler.on_roots_list_changed);
3262 }
3263 if (handler.on_tool_list_changed) {
3264 set_tool_list_changed_handler(handler.on_tool_list_changed);
3265 }
3266 if (handler.on_prompt_list_changed) {
3267 set_prompt_list_changed_handler(handler.on_prompt_list_changed);
3268 }
3269 if (handler.on_resource_list_changed) {
3270 set_resource_list_changed_handler(handler.on_resource_list_changed);
3271 }
3272 if (handler.on_resource_updated) {
3273 set_resource_updated_handler(handler.on_resource_updated);
3274 }
3275 if (handler.on_completion_with_context) {
3276 set_completion_handler(handler.on_completion_with_context);
3277 }
3278 if (handler.on_sampling_with_context) {
3279 set_sampling_handler(handler.on_sampling_with_context);
3280 }
3281 return *this;
3282 }
3283
3284 Peer& set_handler(const server::ServerHandlerInterface& handler) {
3285 server_->set_handler(handler);
3286 set_raw_request_handler([&handler](const protocol::JsonRpcRequest& request,
3287 const server::SessionContext& context,
3288 CancellationToken cancellation)
3289 -> std::optional<protocol::JsonRpcResponse> {
3290 const auto handler_response = server::dispatch_server_handler_request(
3291 handler, request, context, cancellation);
3292 if (handler_response.has_value()) {
3293 return handler_response;
3294 }
3295 return handler.on_custom_request(request, context);
3296 });
3297 return *this;
3298 }
3299
3300 std::vector<protocol::ToolDefinition> list_tools() const {
3301 return server_->list_tools();
3302 }
3303
3304 core::Result<protocol::ToolDefinition> get_tool(std::string_view name) const {
3305 return server_->get_tool(name);
3306 }
3307
3308 core::Result<protocol::ToolResult> call_tool(
3309 std::string_view name,
3310 protocol::Json arguments = protocol::Json::object(),
3311 const std::string& session_id = {}) const {
3312 return server_->call_tool(name, std::move(arguments), session_id);
3313 }
3314
3315 core::Result<protocol::ToolResult> call_tool(
3316 std::string_view name, protocol::Json arguments,
3317 const server::SessionContext& context,
3318 CancellationToken cancellation = CancellationToken::none()) const {
3319 return server_->call_tool(name, std::move(arguments), context,
3320 cancellation);
3321 }
3322
3323 std::vector<protocol::Prompt> list_prompts() const {
3324 return server_->list_prompts();
3325 }
3326
3327 core::Result<protocol::PromptsGetResult> get_prompt(
3328 std::string_view name,
3329 protocol::Json arguments = protocol::Json::object(),
3330 const std::string& session_id = {}) const {
3331 return server_->get_prompt(name, std::move(arguments), session_id);
3332 }
3333
3334 core::Result<protocol::PromptsGetResult> get_prompt(
3335 std::string_view name, protocol::Json arguments,
3336 const server::SessionContext& context,
3337 CancellationToken cancellation = CancellationToken::none()) const {
3338 return server_->get_prompt(name, std::move(arguments), context,
3339 cancellation);
3340 }
3341
3342 std::vector<protocol::Resource> list_resources() const {
3343 return server_->list_resources();
3344 }
3345
3346 core::Result<protocol::ResourcesReadResult> read_resource(
3347 std::string_view uri, protocol::Json params = protocol::Json::object(),
3348 const std::string& session_id = {}) const {
3349 return server_->read_resource(uri, std::move(params), session_id);
3350 }
3351
3352 core::Result<protocol::ResourcesReadResult> read_resource(
3353 std::string_view uri, protocol::Json params,
3354 const server::SessionContext& context,
3355 CancellationToken cancellation = CancellationToken::none()) const {
3356 return server_->read_resource(uri, std::move(params), context,
3357 cancellation);
3358 }
3359
3360 std::vector<protocol::ResourceTemplate> list_resource_templates() const {
3361 return server_->list_resource_templates();
3362 }
3363
3364 core::Result<protocol::Json> initialize() { return server_->initialize(); }
3365
3366 core::Result<protocol::Json> ping(
3367 const server::SessionContext& context = {}) {
3368 return server_->ping(context);
3369 }
3370
3371 core::Result<protocol::JsonRpcResponse> handle_request(
3372 const protocol::JsonRpcRequest& request,
3373 const server::SessionContext& input_context = {},
3374 transport::ServerTransport* native_transport = nullptr) try {
3375 auto authenticated_context = server_->authenticate_context(input_context);
3376 if (!authenticated_context) {
3377 return detail::peer_auth_error_response(request,
3378 authenticated_context.error());
3379 }
3380 server::SessionContext context = std::move(*authenticated_context);
3381
3382 if (request.method == protocol::InitializeMethod) {
3383 const auto valid =
3384 detail::validate_peer_server_initialize_params(request.params);
3385 if (!valid) {
3386 return detail::peer_error_response(request, valid.error());
3387 }
3388 const auto requested_version =
3389 request.params.at("protocolVersion").get<std::string>();
3390 const auto negotiated_version =
3391 protocol::negotiate_protocol_version(requested_version);
3392 if (!negotiated_version.has_value()) {
3393 return detail::peer_error_response(
3394 request, core::Error{
3395 static_cast<int>(protocol::ErrorCode::InvalidParams),
3396 "unsupported MCP protocol version(\"" +
3397 requested_version + "\")",
3398 requested_version,
3399 "protocol",
3400 });
3401 }
3402 return protocol::make_response(
3403 request.id, detail::make_peer_server_initialize_result(
3404 get_info(), capabilities(), *negotiated_version));
3405 }
3406 if (request.method == protocol::PingMethod) {
3407 return protocol::make_response(request.id, protocol::Json::object());
3408 }
3409
3410 if (raw_request_context_handler_) {
3411 const auto request_cancellation =
3412 begin_peer_request_cancellation(request.id);
3413 const std::shared_ptr<void> request_cancellation_cleanup(
3414 nullptr, [this, request_id = request.id](void*) noexcept {
3415 end_peer_request_cancellation(request_id);
3416 });
3417 const auto raw_response =
3418 raw_request_context_handler_(request, context, request_cancellation);
3419 if (raw_response.has_value()) {
3420 return *raw_response;
3421 }
3422 }
3423
3424 if (raw_request_handler_) {
3425 const auto raw_response = raw_request_handler_(request, context);
3426 if (raw_response.has_value()) {
3427 return *raw_response;
3428 }
3429 }
3430
3431 if (request.method == protocol::ToolsListMethod) {
3432 const auto params =
3433 protocol::paginated_request_params_from_json(request.params);
3434 if (!params) {
3435 return detail::peer_error_response(
3436 request, errors::make(protocol::ErrorCode::InvalidParams,
3437 "tools/list params must be an object with an "
3438 "optional string cursor"));
3439 }
3440 const auto result = server_->list_tools(*params, context);
3441 if (!result) {
3442 return detail::peer_error_response(request, result.error());
3443 }
3444 return protocol::make_response(
3445 request.id, protocol::tools_list_result_to_json(*result));
3446 }
3447
3448 if (request.method == protocol::ToolsGetMethod) {
3449 if (!request.params.is_object() || !request.params.contains("name") ||
3450 !request.params.at("name").is_string()) {
3451 return detail::peer_error_response(
3452 request, errors::make(protocol::ErrorCode::InvalidParams,
3453 "tools/get requires a string name"));
3454 }
3455
3456 const auto tool = get_tool(request.params.at("name").get<std::string>());
3457 if (!tool) {
3458 return detail::peer_error_response(request, tool.error());
3459 }
3460 return protocol::make_response(request.id,
3461 protocol::tool_definition_to_json(*tool));
3462 }
3463
3464 if (request.method == protocol::ToolsCallMethod) {
3465 const auto call = protocol::tool_call_from_json(request.params);
3466 if (!call) {
3467 return detail::peer_params_error_response(request, call.error());
3468 }
3469 if (call->task.has_value()) {
3470 const auto valid = server_->tools().validate(*call);
3471 if (!valid) {
3472 return detail::peer_params_error_response(request, valid.error());
3473 }
3474 const auto task_manager = server_->task_manager();
3475 if (!task_manager) {
3476 return detail::peer_error_response(
3477 request, errors::make(protocol::ErrorCode::MethodNotFound,
3478 "task processor is not configured"));
3479 }
3480 const auto task =
3481 task_manager->submit_tool_call(server_->tools(), *call, context,
3482 server_->schema_validator().get());
3483 if (!task) {
3484 return detail::peer_params_error_response(request, task.error());
3485 }
3486 return protocol::make_response(
3487 request.id, protocol::create_task_result_to_json(*task));
3488 }
3489
3490 const auto request_cancellation =
3491 begin_peer_request_cancellation(request.id);
3492 const std::shared_ptr<void> request_cancellation_cleanup(
3493 nullptr, [this, request_id = request.id](void*) noexcept {
3494 end_peer_request_cancellation(request_id);
3495 });
3496 const auto result =
3497 server_->tools().call(*call, context, request_cancellation,
3498 server_->schema_validator().get());
3499 if (!result) {
3500 return detail::peer_error_response(request, result.error());
3501 }
3502
3503 return protocol::make_response(request.id,
3504 protocol::tool_result_to_json(*result));
3505 }
3506
3507 if (request.method == protocol::PromptsListMethod) {
3508 const auto params =
3509 protocol::paginated_request_params_from_json(request.params);
3510 if (!params) {
3511 return detail::peer_error_response(
3512 request,
3513 errors::make(protocol::ErrorCode::InvalidParams,
3514 "prompts/list params must be an object with an "
3515 "optional string cursor"));
3516 }
3517 const auto result = server_->list_prompts(*params, context);
3518 if (!result) {
3519 return detail::peer_error_response(request, result.error());
3520 }
3521 return protocol::make_response(
3522 request.id, protocol::prompts_list_result_to_json(*result));
3523 }
3524
3525 if (request.method == protocol::PromptsGetMethod) {
3526 const auto params =
3527 protocol::prompts_get_params_from_json(request.params);
3528 if (!params) {
3529 return detail::peer_params_error_response(request, params.error());
3530 }
3531
3532 const auto request_cancellation =
3533 begin_peer_request_cancellation(request.id);
3534 const std::shared_ptr<void> request_cancellation_cleanup(
3535 nullptr, [this, request_id = request.id](void*) noexcept {
3536 end_peer_request_cancellation(request_id);
3537 });
3538 const auto result = server_->prompts().get(
3539 params->name, params->arguments, context, request_cancellation);
3540 if (!result) {
3541 return detail::peer_error_response(request, result.error());
3542 }
3543
3544 return protocol::make_response(
3545 request.id, protocol::prompts_get_result_to_json(*result));
3546 }
3547
3548 if (request.method == protocol::ResourcesListMethod) {
3549 const auto params =
3550 protocol::paginated_request_params_from_json(request.params);
3551 if (!params) {
3552 return detail::peer_error_response(
3553 request,
3554 errors::make(protocol::ErrorCode::InvalidParams,
3555 "resources/list params must be an object with an "
3556 "optional string cursor"));
3557 }
3558 const auto result = server_->list_resources(*params, context);
3559 if (!result) {
3560 return detail::peer_error_response(request, result.error());
3561 }
3562 return protocol::make_response(
3563 request.id, protocol::resources_list_result_to_json(*result));
3564 }
3565
3566 if (request.method == protocol::ResourcesReadMethod) {
3567 const auto params =
3568 protocol::resources_read_params_from_json(request.params);
3569 if (!params) {
3570 return detail::peer_params_error_response(request, params.error());
3571 }
3572
3573 const auto request_cancellation =
3574 begin_peer_request_cancellation(request.id);
3575 const std::shared_ptr<void> request_cancellation_cleanup(
3576 nullptr, [this, request_id = request.id](void*) noexcept {
3577 end_peer_request_cancellation(request_id);
3578 });
3579 auto result = server_->resources().read(params->uri, request.params,
3580 context, request_cancellation);
3581 if (!result) {
3582 return detail::peer_error_response(request, result.error());
3583 }
3584 result->ttl_ms = 300000;
3585 result->cache_scope = "public";
3586
3587 return protocol::make_response(
3588 request.id, protocol::resources_read_result_to_json(*result));
3589 }
3590
3591 if (request.method == protocol::ResourcesTemplatesListMethod) {
3592 const auto params =
3593 protocol::paginated_request_params_from_json(request.params);
3594 if (!params) {
3595 return detail::peer_error_response(
3596 request,
3597 errors::make(protocol::ErrorCode::InvalidParams,
3598 "resources/templates/list params must be an object "
3599 "with an optional string cursor"));
3600 }
3601 const auto result = server_->list_resource_templates(*params, context);
3602 if (!result) {
3603 return detail::peer_error_response(request, result.error());
3604 }
3605 return protocol::make_response(
3606 request.id,
3607 protocol::resource_templates_list_result_to_json(*result));
3608 }
3609
3610 if (request.method == protocol::ResourcesSubscribeMethod ||
3611 request.method == protocol::ResourcesUnsubscribeMethod) {
3612 if (!capabilities().resources.subscribe) {
3613 return detail::peer_error_response(
3614 request, errors::make(protocol::ErrorCode::MethodNotFound,
3615 "resource subscriptions are not enabled"));
3616 }
3617 if (!request.params.is_object() || !request.params.contains("uri") ||
3618 !request.params.at("uri").is_string()) {
3619 return detail::peer_error_response(
3620 request,
3621 errors::make(protocol::ErrorCode::InvalidParams,
3622 "resource subscription requires a string uri"));
3623 }
3624 const auto subscription = server_->set_resource_subscription(
3625 subscription_context_for(context, native_transport),
3626 request.params.at("uri").get<std::string>(),
3627 request.method == protocol::ResourcesSubscribeMethod);
3628 if (!subscription) {
3629 return detail::peer_error_response(request, subscription.error());
3630 }
3631 return protocol::make_response(request.id, protocol::Json::object());
3632 }
3633
3634 if (request.method == protocol::CompletionCompleteMethod &&
3635 completion_handler_) {
3636 const auto result = completion_handler_(request.params, context);
3637 if (!result) {
3638 return detail::peer_error_response(request, result.error());
3639 }
3640 return protocol::make_response(request.id, *result);
3641 }
3642
3643 if (request.method == protocol::SamplingCreateMessageMethod &&
3644 sampling_handler_) {
3645 const auto result = sampling_handler_(request.params, context);
3646 if (!result) {
3647 return detail::peer_error_response(request, result.error());
3648 }
3649 return protocol::make_response(request.id, *result);
3650 }
3651
3652 if (request.method == protocol::LoggingSetLevelMethod && logging_handler_) {
3653 if (!request.params.is_object() || !request.params.contains("level") ||
3654 !request.params.at("level").is_string()) {
3655 return detail::peer_error_response(
3656 request, errors::make(protocol::ErrorCode::InvalidParams,
3657 "logging/setLevel requires a string level"));
3658 }
3659 logging_handler_(request.params.at("level").get<std::string>(),
3660 "logging level changed");
3661 return protocol::make_response(request.id, protocol::Json::object());
3662 }
3663
3664 if (request.method == protocol::TasksListMethod && task_list_handler_) {
3665 const auto params = protocol::task_list_params_from_json(request.params);
3666 if (!params) {
3667 return detail::peer_params_error_response(request, params.error());
3668 }
3669 const auto result = task_list_handler_(*params, context);
3670 if (!result) {
3671 return detail::peer_error_response(request, result.error());
3672 }
3673 return protocol::make_response(
3674 request.id, protocol::task_list_result_to_json(*result));
3675 }
3676
3677 if (request.method == protocol::TasksGetMethod && task_get_handler_) {
3678 const auto params = protocol::task_get_params_from_json(request.params);
3679 if (!params) {
3680 return detail::peer_params_error_response(request, params.error());
3681 }
3682 const auto result = task_get_handler_(*params, context);
3683 if (!result) {
3684 return detail::peer_error_response(request, result.error());
3685 }
3686 protocol::TaskGetResult response_result;
3687 response_result.task = *result;
3688 return protocol::make_response(
3689 request.id, protocol::task_get_result_to_json(response_result));
3690 }
3691
3692 if (request.method == protocol::TasksCancelMethod && task_cancel_handler_) {
3693 const auto params =
3694 protocol::task_cancel_params_from_json(request.params);
3695 if (!params) {
3696 return detail::peer_params_error_response(request, params.error());
3697 }
3698 const auto result = task_cancel_handler_(*params, context);
3699 if (!result) {
3700 return detail::peer_error_response(request, result.error());
3701 }
3702 protocol::TaskCancelResult response_result;
3703 response_result.task = *result;
3704 return protocol::make_response(
3705 request.id, protocol::task_cancel_result_to_json(response_result));
3706 }
3707
3708 if (request.method == protocol::TasksResultMethod && task_result_handler_) {
3709 const auto params =
3710 protocol::task_result_params_from_json(request.params);
3711 if (!params) {
3712 return detail::peer_params_error_response(request, params.error());
3713 }
3714 const auto result = task_result_handler_(*params, context);
3715 if (!result) {
3716 return detail::peer_error_response(request, result.error());
3717 }
3718 return protocol::make_response(request.id, *result);
3719 }
3720
3721 return server_->handle_request(request, context);
3722 } catch (const std::exception& ex) {
3723 return detail::peer_error_response(request,
3724 errors::handler_failed(ex.what()));
3725 } catch (...) {
3726 return detail::peer_error_response(request,
3727 errors::handler_unknown_exception());
3728 }
3729
3730 core::Result<core::Unit> handle_notification(
3731 const protocol::JsonRpcNotification& notification,
3732 const server::SessionContext& context = {}) {
3733 if (notification.method == protocol::CancelledNotificationMethod ||
3734 native_notification_state_) {
3735 return handle_native_notification(notification, context);
3736 }
3737 return server_->handle_notification(notification, context);
3738 }
3739
3740 core::Result<core::Unit> add_transport(
3741 std::unique_ptr<server::Transport> transport) {
3742 return server_->add_transport(std::move(transport));
3743 }
3744
3747 std::unique_ptr<transport::ServerTransport> transport) {
3748 if (!transport) {
3749 return mcp::core::unexpected(detail::peer_dispatch_error(
3750 "server peer transport must not be null"));
3751 }
3752 native_transports_.push_back(std::move(transport));
3753 native_context_transports_.push_back(
3754 server::make_contract_transport_adapter(*native_transports_.back()));
3755 const auto attached =
3756 server_->add_session_transport(*native_context_transports_.back());
3757 if (!attached) {
3758 native_context_transports_.pop_back();
3759 native_transports_.pop_back();
3760 return mcp::core::unexpected(attached.error());
3761 }
3762 return core::Unit{};
3763 }
3764
3766 CancellationToken cancellation = CancellationToken::none()) {
3767 if (native_transports_.empty()) {
3768 return server_->start();
3769 }
3770
3771 std::vector<std::thread> workers;
3772 workers.reserve(native_transports_.size());
3773 std::mutex error_mutex;
3774 std::optional<core::Error> first_error;
3775
3776 for (std::size_t index = 0; index < native_transports_.size(); ++index) {
3777 auto* transport_ptr = native_transports_[index].get();
3778 auto* context_transport_ptr = native_context_transports_[index].get();
3779 workers.emplace_back([this, transport_ptr, context_transport_ptr,
3780 cancellation, &error_mutex,
3781 &first_error]() noexcept {
3782 server::SessionContext context;
3783 context.remote_address = std::string(transport_ptr->name());
3784 context.transport = context_transport_ptr;
3785 if (context_transport_ptr != nullptr) {
3786 context.transport_lifetime = context_transport_ptr->lifetime_token();
3787 }
3788 const auto served =
3789 serve_transport(*transport_ptr, context, cancellation);
3790 if (!served) {
3791 detail::keep_first_service_error(first_error, error_mutex,
3792 served.error());
3793 }
3794 });
3795 }
3796
3797 for (auto& worker : workers) {
3798 if (worker.joinable()) {
3799 worker.join();
3800 }
3801 }
3802
3803 if (first_error.has_value()) {
3804 return mcp::core::unexpected(*first_error);
3805 }
3806 return core::Unit{};
3807 }
3808
3809 void stop() noexcept {
3810 for (auto& transport : native_transports_) {
3811 if (transport) {
3812 (void)transport->close();
3813 }
3814 }
3815 server_->stop();
3816 }
3817
3820 for (auto& transport : native_transports_) {
3821 transport->wait_until_ready();
3822 }
3823 }
3824
3825 core::Result<core::Unit> notify_roots_list_changed() {
3826 return server_->notify_roots_list_changed();
3827 }
3828
3829 core::Result<core::Unit> notify_tool_list_changed() {
3830 return server_->notify_tool_list_changed();
3831 }
3832
3833 core::Result<core::Unit> notify_prompt_list_changed() {
3834 return server_->notify_prompt_list_changed();
3835 }
3836
3837 core::Result<core::Unit> notify_resource_list_changed() {
3838 return server_->notify_resource_list_changed();
3839 }
3840
3841 core::Result<core::Unit> notify_resource_updated(std::string_view uri) {
3842 return server_->notify_resource_updated(uri);
3843 }
3844
3845 core::Result<core::Unit> notify_progress(
3846 const protocol::ProgressNotificationParams& params) {
3847 return server_->notify_progress(params);
3848 }
3849
3850 core::Result<core::Unit> notify_logging_message(
3851 const protocol::LoggingMessageNotificationParams& params) {
3852 return server_->notify_logging_message(params);
3853 }
3854
3855 core::Result<core::Unit> notify_elicitation_complete(
3856 std::string elicitation_id) {
3857 return server_->notify_elicitation_complete(std::move(elicitation_id));
3858 }
3859
3860 core::Result<core::Unit> notify_task_status(const protocol::Task& task) {
3861 return server_->notify_task_status(task);
3862 }
3863
3870 const protocol::JsonRpcMessage& message,
3871 const server::SessionContext& context = {},
3872 transport::ServerTransport* native_transport = nullptr) {
3873 if (const auto* request = std::get_if<protocol::JsonRpcRequest>(&message)) {
3874 auto handled = handle_request(*request, context, native_transport);
3875 if (!handled) {
3876 return protocol::JsonRpcMessage{protocol::make_error_response(
3877 request->id,
3878 detail::peer_error_object_from_core_error(handled.error()))};
3879 }
3880 return protocol::JsonRpcMessage{std::move(*handled)};
3881 }
3882
3883 if (const auto* notification =
3884 std::get_if<protocol::JsonRpcNotification>(&message)) {
3885 const auto handled = handle_notification(*notification, context);
3886 if (!handled) {
3887 return mcp::core::unexpected(handled.error());
3888 }
3889 return std::nullopt;
3890 }
3891
3892 return mcp::core::unexpected(detail::peer_dispatch_error(
3893 "server peer cannot dispatch an uncorrelated response"));
3894 }
3895
3903 transport::ServerTransport& transport,
3904 const server::SessionContext& context = {},
3905 CancellationToken cancellation = CancellationToken::none()) {
3906 struct WorkerState {
3907 std::mutex mutex;
3908 std::mutex send_mutex;
3909 std::condition_variable cv;
3910 std::size_t active = 0;
3911 std::optional<core::Error> first_error;
3912 };
3913
3914 auto worker_state = std::make_shared<WorkerState>();
3915 bool initialized = false;
3916
3917 auto record_worker_error = [worker_state,
3918 &transport](core::Error error) noexcept {
3919 {
3920 std::lock_guard lock(worker_state->mutex);
3921 if (!worker_state->first_error.has_value()) {
3922 worker_state->first_error = std::move(error);
3923 }
3924 }
3925 (void)transport.close();
3926 worker_state->cv.notify_all();
3927 };
3928
3929 auto finish_worker = [worker_state]() noexcept {
3930 {
3931 std::lock_guard lock(worker_state->mutex);
3932 if (worker_state->active > 0) {
3933 --worker_state->active;
3934 }
3935 }
3936 worker_state->cv.notify_all();
3937 };
3938
3939 auto wait_for_workers = [worker_state]() -> std::optional<core::Error> {
3940 std::unique_lock lock(worker_state->mutex);
3941 worker_state->cv.wait(lock, [&] { return worker_state->active == 0; });
3942 return worker_state->first_error;
3943 };
3944
3945 auto send_message =
3946 [worker_state, &transport](
3947 protocol::JsonRpcMessage message) -> core::Result<core::Unit> {
3948 std::lock_guard lock(worker_state->send_mutex);
3949 return transport.send(std::move(message));
3950 };
3951
3952 auto dispatch_and_send = [this, &transport, &send_message](
3953 const protocol::JsonRpcMessage& message,
3954 const server::SessionContext& message_context)
3955 -> core::Result<core::Unit> {
3956 auto dispatched = dispatch_message(message, message_context, &transport);
3957 if (!dispatched) {
3958 return mcp::core::unexpected(dispatched.error());
3959 }
3960 if (dispatched->has_value()) {
3961 auto sent = send_message(std::move(dispatched->value()));
3962 if (!sent) {
3963 return mcp::core::unexpected(sent.error());
3964 }
3965 }
3966 return core::Unit{};
3967 };
3968
3969 auto start_request_worker = [worker_state, &record_worker_error,
3970 &finish_worker, &dispatch_and_send](
3971 protocol::JsonRpcMessage message,
3972 server::SessionContext message_context)
3973 -> core::Result<core::Unit> {
3974 {
3975 std::lock_guard lock(worker_state->mutex);
3976 ++worker_state->active;
3977 }
3978
3979 try {
3980 std::thread([message = std::move(message),
3981 message_context = std::move(message_context),
3982 &dispatch_and_send, record_worker_error,
3983 finish_worker]() mutable {
3984 try {
3985 auto dispatched = dispatch_and_send(message, message_context);
3986 if (!dispatched) {
3987 record_worker_error(dispatched.error());
3988 }
3989 } catch (const std::exception& ex) {
3990 record_worker_error(errors::make(
3991 protocol::ErrorCode::InternalError,
3992 "server peer request worker failed", ex.what(), "transport"));
3993 } catch (...) {
3994 record_worker_error(
3995 errors::make(protocol::ErrorCode::InternalError,
3996 "server peer request worker failed",
3997 "unknown exception", "transport"));
3998 }
3999 finish_worker();
4000 }).detach();
4001 } catch (const std::system_error& ex) {
4002 finish_worker();
4003 return mcp::core::unexpected(
4004 errors::make(protocol::ErrorCode::InternalError,
4005 "failed to start server peer request worker",
4006 ex.what(), "transport"));
4007 }
4008 return core::Unit{};
4009 };
4010
4011 auto is_stateless_request = [](const protocol::Json& params) -> bool {
4012 return params.is_object() && params.contains("_meta") &&
4013 params.at("_meta").is_object() &&
4014 params.at("_meta").contains(
4015 "io.modelcontextprotocol/protocolVersion");
4016 };
4017
4018 while (!cancellation.cancelled()) {
4019 auto received = transport.receive();
4020 if (!received) {
4021 if (const auto worker_error = wait_for_workers()) {
4022 return mcp::core::unexpected(*worker_error);
4023 }
4024 return mcp::core::unexpected(received.error());
4025 }
4026 if (!received->has_value()) {
4027 if (const auto worker_error = wait_for_workers()) {
4028 return mcp::core::unexpected(*worker_error);
4029 }
4030 return core::Unit{};
4031 }
4032
4033 auto message = std::move(received->value());
4034 if (const auto* request =
4035 std::get_if<protocol::JsonRpcRequest>(&message)) {
4036 const bool stateless = is_stateless_request(request->params);
4037 const bool allowed_before_initialized =
4038 stateless || request->method == protocol::InitializeMethod ||
4039 request->method == protocol::PingMethod;
4040 if (!initialized && !allowed_before_initialized) {
4041 auto sent = send_message(
4042 protocol::JsonRpcMessage{protocol::make_error_response(
4043 request->id,
4044 protocol::make_error(protocol::ErrorCode::InvalidRequest,
4045 "server peer transport session is "
4046 "not initialized"))});
4047 if (!sent) {
4048 if (const auto worker_error = wait_for_workers()) {
4049 return mcp::core::unexpected(*worker_error);
4050 }
4051 return mcp::core::unexpected(sent.error());
4052 }
4053 continue;
4054 }
4055
4056 auto message_context =
4057 detail::context_for_received_server_message(transport, context);
4058 const bool run_inline = request->method == protocol::InitializeMethod ||
4059 (!initialized && !stateless);
4060 if (run_inline) {
4061 auto dispatched = dispatch_and_send(message, message_context);
4062 if (!dispatched) {
4063 if (const auto worker_error = wait_for_workers()) {
4064 return mcp::core::unexpected(*worker_error);
4065 }
4066 return mcp::core::unexpected(dispatched.error());
4067 }
4068 continue;
4069 }
4070
4071 auto started = start_request_worker(std::move(message),
4072 std::move(message_context));
4073 if (!started) {
4074 if (const auto worker_error = wait_for_workers()) {
4075 return mcp::core::unexpected(*worker_error);
4076 }
4077 return mcp::core::unexpected(started.error());
4078 }
4079 continue;
4080 }
4081
4082 if (const auto* notification =
4083 std::get_if<protocol::JsonRpcNotification>(&message)) {
4084 const bool stateless = is_stateless_request(notification->params);
4085 if (!initialized && !stateless &&
4086 notification->method != protocol::InitializedMethod) {
4087 if (const auto worker_error = wait_for_workers()) {
4088 return mcp::core::unexpected(*worker_error);
4089 }
4090 return mcp::core::unexpected(detail::peer_dispatch_error(
4091 "server peer transport session is not initialized"));
4092 }
4093 auto message_context =
4094 detail::context_for_received_server_message(transport, context);
4095 auto dispatched = dispatch_and_send(message, message_context);
4096 if (!dispatched) {
4097 if (const auto worker_error = wait_for_workers()) {
4098 return mcp::core::unexpected(*worker_error);
4099 }
4100 return mcp::core::unexpected(dispatched.error());
4101 }
4102 if (notification->method == protocol::InitializedMethod) {
4103 initialized = true;
4104 }
4105 continue;
4106 }
4107
4108 if (const auto worker_error = wait_for_workers()) {
4109 return mcp::core::unexpected(*worker_error);
4110 }
4111 return mcp::core::unexpected(detail::peer_dispatch_error(
4112 "server peer cannot dispatch an uncorrelated response"));
4113 }
4114
4115 if (const auto worker_error = wait_for_workers()) {
4116 return mcp::core::unexpected(*worker_error);
4117 }
4118 return core::Unit{};
4119 }
4120
4121 private:
4122 server::SessionContext subscription_context_for(
4123 const server::SessionContext& context,
4124 const transport::ServerTransport* native_transport) const {
4125 server::SessionContext subscription_context = context;
4126 if (subscription_context.transport || native_transport == nullptr) {
4127 return subscription_context;
4128 }
4129
4130 for (std::size_t index = 0; index < native_transports_.size(); ++index) {
4131 if (native_transports_[index].get() == native_transport &&
4132 index < native_context_transports_.size()) {
4133 subscription_context.transport =
4134 native_context_transports_[index].get();
4135 subscription_context.transport_lifetime =
4136 native_context_transports_[index]->lifetime_token();
4137 break;
4138 }
4139 }
4140 return subscription_context;
4141 }
4142
4143 CancellationToken begin_peer_request_cancellation(
4144 const protocol::RequestId& request_id) {
4145 CancellationSource source;
4146 auto token = source.token();
4147 std::lock_guard lock(*peer_request_cancellations_mutex_);
4148 (*peer_request_cancellations_)[detail::peer_request_cancellation_key(
4149 request_id)] = std::move(source);
4150 return token;
4151 }
4152
4153 void end_peer_request_cancellation(
4154 const protocol::RequestId& request_id) noexcept {
4155 std::lock_guard lock(*peer_request_cancellations_mutex_);
4156 peer_request_cancellations_->erase(
4157 detail::peer_request_cancellation_key(request_id));
4158 }
4159
4160 void cancel_peer_request(const protocol::RequestId& request_id) noexcept {
4161 std::lock_guard lock(*peer_request_cancellations_mutex_);
4162 const auto it = peer_request_cancellations_->find(
4163 detail::peer_request_cancellation_key(request_id));
4164 if (it != peer_request_cancellations_->end()) {
4165 it->second.cancel();
4166 }
4167 }
4168
4169 core::Result<core::Unit> handle_native_notification(
4170 const protocol::JsonRpcNotification& notification,
4171 const server::SessionContext& context) try {
4172 if (notification.method == protocol::CancelledNotificationMethod) {
4173 const auto cancelled = protocol::cancelled_notification_params_from_json(
4174 notification.params);
4175 if (!cancelled) {
4176 return mcp::core::unexpected(
4177 errors::make(protocol::ErrorCode::InvalidParams,
4178 "cancelled notification requires valid params"));
4179 }
4180 cancel_peer_request(cancelled->request_id);
4181 return server_->handle_notification(notification, context);
4182 }
4183
4184 if (raw_notification_handler_) {
4185 const auto raw_result = raw_notification_handler_(notification, context);
4186 if (!raw_result) {
4187 return mcp::core::unexpected(raw_result.error());
4188 }
4189 }
4190
4191 if (notification.method == protocol::RootsListChangedNotificationMethod &&
4192 roots_list_changed_handler_) {
4193 const auto result = roots_list_changed_handler_(context);
4194 if (!result) {
4195 return mcp::core::unexpected(result.error());
4196 }
4197 } else if (notification.method == protocol::ProgressNotificationMethod &&
4198 progress_handler_) {
4199 const auto params =
4200 protocol::progress_notification_params_from_json(notification.params);
4201 if (!params) {
4202 return mcp::core::unexpected(
4203 errors::make(protocol::ErrorCode::InvalidParams,
4204 "progress notification requires valid params"));
4205 }
4206 const auto result = progress_handler_(*params, context);
4207 if (!result) {
4208 return mcp::core::unexpected(result.error());
4209 }
4210 } else if (notification.method ==
4211 protocol::ToolsListChangedNotificationMethod &&
4212 tool_list_changed_handler_) {
4213 const auto result = tool_list_changed_handler_(context);
4214 if (!result) {
4215 return mcp::core::unexpected(result.error());
4216 }
4217 } else if (notification.method ==
4218 protocol::PromptsListChangedNotificationMethod &&
4219 prompt_list_changed_handler_) {
4220 const auto result = prompt_list_changed_handler_(context);
4221 if (!result) {
4222 return mcp::core::unexpected(result.error());
4223 }
4224 } else if (notification.method ==
4225 protocol::ResourcesListChangedNotificationMethod &&
4226 resource_list_changed_handler_) {
4227 const auto result = resource_list_changed_handler_(context);
4228 if (!result) {
4229 return mcp::core::unexpected(result.error());
4230 }
4231 } else if (notification.method ==
4232 protocol::ResourcesUpdatedNotificationMethod &&
4233 resource_updated_handler_) {
4234 if (!notification.params.is_object() ||
4235 !notification.params.contains("uri") ||
4236 !notification.params.at("uri").is_string()) {
4237 return mcp::core::unexpected(errors::make(
4238 protocol::ErrorCode::InvalidParams,
4239 "resource updated notification requires a string uri"));
4240 }
4241 const auto result = resource_updated_handler_(
4242 notification.params.at("uri").get<std::string>(), context);
4243 if (!result) {
4244 return mcp::core::unexpected(result.error());
4245 }
4246 }
4247
4248 return core::Unit{};
4249 } catch (const std::exception& ex) {
4250 return mcp::core::unexpected(errors::handler_failed(ex.what()));
4251 } catch (...) {
4252 return mcp::core::unexpected(errors::handler_unknown_exception());
4253 }
4254
4255 std::unique_ptr<server::Server> server_;
4256 std::vector<std::unique_ptr<transport::ServerTransport>> native_transports_;
4257 std::vector<std::unique_ptr<server::Transport>> native_context_transports_;
4258 std::shared_ptr<std::mutex> peer_request_cancellations_mutex_ =
4259 std::make_shared<std::mutex>();
4260 std::shared_ptr<std::unordered_map<std::string, CancellationSource>>
4261 peer_request_cancellations_ = std::make_shared<
4262 std::unordered_map<std::string, CancellationSource>>();
4263 bool native_notification_state_ = false;
4264 server::Server::RawRequestHandler raw_request_handler_;
4265 server::Server::RawRequestContextHandler raw_request_context_handler_;
4266 server::Server::JsonContextHandler completion_handler_;
4267 server::Server::JsonContextHandler sampling_handler_;
4268 server::Server::LoggingHandler logging_handler_;
4269 server::Server::TaskListHandler task_list_handler_;
4270 server::Server::TaskGetHandler task_get_handler_;
4271 server::Server::TaskCancelHandler task_cancel_handler_;
4272 server::Server::TaskResultHandler task_result_handler_;
4273 server::Server::RawNotificationHandler raw_notification_handler_;
4274 server::Server::ProgressHandler progress_handler_;
4275 server::Server::RootsListChangedHandler roots_list_changed_handler_;
4276 server::Server::ListChangedHandler tool_list_changed_handler_;
4277 server::Server::ListChangedHandler prompt_list_changed_handler_;
4278 server::Server::ListChangedHandler resource_list_changed_handler_;
4279 server::Server::ResourceUpdatedHandler resource_updated_handler_;
4280};
4281
4283class Peer<RoleServer>::Builder {
4284 public:
4285 Builder() = default;
4286 Builder(const Builder&) = delete;
4287 Builder& operator=(const Builder&) = delete;
4288 Builder(Builder&&) noexcept = default;
4289 Builder& operator=(Builder&&) noexcept = default;
4290
4291 Builder& name(std::string value) {
4292 builder_.name(std::move(value));
4293 return *this;
4294 }
4295
4296 Builder& version(std::string value) {
4297 builder_.version(std::move(value));
4298 return *this;
4299 }
4300
4301 Builder& instructions(std::string value) {
4302 builder_.instructions(std::move(value));
4303 return *this;
4304 }
4305
4306 Builder& capabilities(protocol::ServerCapabilities value) {
4307 builder_.with_capabilities(std::move(value));
4308 return *this;
4309 }
4310
4311 Builder& transport(std::unique_ptr<server::Transport> value) {
4312 builder_.with_transport(std::move(value));
4313 return *this;
4314 }
4315
4316 Builder& transport(std::unique_ptr<transport::ServerTransport> value) {
4317 native_transports_.push_back(std::move(value));
4318 return *this;
4319 }
4320
4321 Builder& stdio(std::istream& input, std::ostream& output) {
4322 return transport(
4323 std::make_unique<transport::ServerStdioTransport>(input, output));
4324 }
4325
4326 Builder& stdio() { return stdio(std::cin, std::cout); }
4327
4328#if defined(CXXMCP_ENABLE_HTTP)
4329 Builder& streamable_http(
4331 return transport(std::make_unique<transport::StreamableHttpServerTransport>(
4332 std::move(options)));
4333 }
4334
4335 Builder& streamable_http(int listen_port) {
4337 options.listen_port = listen_port;
4338 return streamable_http(std::move(options));
4339 }
4340
4341 Builder& streamable_http(std::string listen_host, int listen_port,
4342 std::string path = "/mcp") {
4344 options.listen_host = std::move(listen_host);
4345 options.listen_port = listen_port;
4346 options.path = std::move(path);
4347 return streamable_http(std::move(options));
4348 }
4349#endif
4350
4351#if defined(CXXMCP_ENABLE_WEBSOCKET)
4352 Builder& websocket(transport::WebSocketServerTransportOptions options) {
4353 return transport(std::make_unique<transport::WebSocketServerTransport>(
4354 std::move(options)));
4355 }
4356
4357 Builder& websocket(int listen_port) {
4359 options.listen_port = listen_port;
4360 return websocket(std::move(options));
4361 }
4362
4363 Builder& websocket(std::string listen_host, int listen_port,
4364 std::string path = "/mcp") {
4366 options.listen_host = std::move(listen_host);
4367 options.listen_port = listen_port;
4368 options.path = std::move(path);
4369 return websocket(std::move(options));
4370 }
4371#endif
4372
4373 Builder& auth_provider(std::unique_ptr<server::AuthProvider> value) {
4374 builder_.with_auth_provider(std::move(value));
4375 return *this;
4376 }
4377
4378 Builder& rate_limiter(std::unique_ptr<server::RateLimiter> value) {
4379 builder_.with_rate_limiter(std::move(value));
4380 return *this;
4381 }
4382
4383 Builder& schema_validator(
4384 std::shared_ptr<const server::JsonSchemaValidator> value) {
4385 builder_.with_schema_validator(std::move(value));
4386 return *this;
4387 }
4388
4389 Builder& task_manager(server::TaskOperationProcessorOptions options = {}) {
4390 builder_.with_task_manager(std::move(options));
4391 return *this;
4392 }
4393
4394 Builder& task_manager(
4395 std::shared_ptr<server::TaskOperationProcessor> processor) {
4396 builder_.with_task_manager(std::move(processor));
4397 return *this;
4398 }
4399
4400 Builder& add_tool(protocol::ToolDefinition definition,
4401 server::ToolHandler handler) {
4402 builder_.add_tool(std::move(definition), std::move(handler));
4403 return *this;
4404 }
4405
4406 Builder& tool(protocol::ToolDefinition definition,
4407 server::ToolHandler handler) {
4408 return add_tool(std::move(definition), std::move(handler));
4409 }
4410
4411 Builder& add_prompt(protocol::Prompt prompt, server::PromptHandler handler) {
4412 builder_.add_prompt(std::move(prompt), std::move(handler));
4413 return *this;
4414 }
4415
4416 Builder& prompt(protocol::Prompt prompt, server::PromptHandler handler) {
4417 return add_prompt(std::move(prompt), std::move(handler));
4418 }
4419
4420 template <class Args, class Handler>
4421 Builder& prompt(server::TypedPromptRegistration<Args, Handler> registration) {
4422 return add_prompt(
4423 std::move(registration.prompt),
4424 [handler = std::move(registration.handler)](
4425 const server::PromptContext& context)
4427 try {
4428 auto args = context.arguments.get<Args>();
4429 auto handled = server::detail::invoke_typed_context_handler(
4430 handler, std::move(args), context);
4431 if constexpr (server::detail::is_result<decltype(handled)>::value) {
4432 if (!handled) {
4433 return mcp::core::unexpected(handled.error());
4434 }
4435 return server::detail::value_to_prompt_result(*handled);
4436 } else {
4437 return server::detail::value_to_prompt_result(std::move(handled));
4438 }
4439 } catch (const std::exception& exception) {
4440 return mcp::core::unexpected(core::Error{
4441 static_cast<int>(protocol::ErrorCode::InvalidParams),
4442 "failed to decode prompt arguments",
4443 exception.what(),
4444 });
4445 }
4446 });
4447 }
4448
4449 Builder& add_resource(protocol::Resource resource,
4451 builder_.add_resource(std::move(resource), std::move(handler));
4452 return *this;
4453 }
4454
4455 Builder& resource(protocol::Resource resource,
4457 return add_resource(std::move(resource), std::move(handler));
4458 }
4459
4460 template <class Args, class Handler>
4461 Builder& resource(
4462 server::TypedResourceRegistration<Args, Handler> registration) {
4463 return add_resource(
4464 std::move(registration.resource),
4465 [handler = std::move(registration.handler)](
4466 const server::ResourceContext& context)
4467 -> core::Result<protocol::ResourcesReadResult> {
4468 try {
4469 auto args = context.params.get<Args>();
4470 auto handled = server::detail::invoke_typed_context_handler(
4471 handler, std::move(args), context);
4472 if constexpr (server::detail::is_result<decltype(handled)>::value) {
4473 if (!handled) {
4474 return mcp::core::unexpected(handled.error());
4475 }
4476 return server::detail::value_to_resource_read_result(*handled,
4477 context.uri);
4478 } else {
4479 return server::detail::value_to_resource_read_result(
4480 std::move(handled), context.uri);
4481 }
4482 } catch (const std::exception& exception) {
4483 return mcp::core::unexpected(core::Error{
4484 static_cast<int>(protocol::ErrorCode::InvalidParams),
4485 "failed to decode resource parameters",
4486 exception.what(),
4487 });
4488 }
4489 });
4490 }
4491
4492 Builder& add_resource_template(protocol::ResourceTemplate resource_template) {
4493 builder_.add_resource_template(std::move(resource_template));
4494 return *this;
4495 }
4496
4497 Builder& resource_template(protocol::ResourceTemplate resource_template) {
4498 return add_resource_template(std::move(resource_template));
4499 }
4500
4507 template <class Args, class Result, class Handler>
4508 Builder& tool(std::string name, Handler handler) {
4509 auto definition = protocol::tool_definition(std::move(name))
4510 .input_schema(protocol::tool_input_schema_for<Args>())
4511 .build();
4512 server::detail::apply_default_output_schema<Result>(definition);
4513 return tool<Args, Result>(std::move(definition), std::move(handler));
4514 }
4515
4517 template <class Args, class Result, class Handler>
4518 Builder& tool(protocol::ToolDefinition definition, Handler handler) {
4519 return add_tool(
4520 std::move(definition),
4521 [handler = std::move(handler)](const server::ToolContext& context)
4523 try {
4524 auto args =
4525 server::detail::argument_from_json<Args>(context.arguments);
4526 auto handled = server::detail::invoke_tool_handler(
4527 handler, std::move(args), context);
4528 if constexpr (server::detail::is_result<decltype(handled)>::value) {
4529 if (!handled) {
4530 return mcp::core::unexpected(handled.error());
4531 }
4532 return server::detail::value_to_tool_result(*handled);
4533 } else {
4534 return server::detail::value_to_tool_result(std::move(handled));
4535 }
4536 } catch (const std::exception& exception) {
4537 return mcp::core::unexpected(core::Error{
4538 static_cast<int>(protocol::ErrorCode::InvalidParams),
4539 "failed to decode tool arguments",
4540 exception.what(),
4541 });
4542 }
4543 });
4544 }
4545
4547 template <class Args, class Handler>
4548 Builder& prompt(std::string name, Handler handler) {
4549 server::detail::require_unambiguous_typed_context_handler<
4550 Handler, Args, server::PromptContext>("prompt");
4551 protocol::Prompt prompt;
4552 prompt.name = std::move(name);
4553 return add_prompt(
4554 std::move(prompt),
4555 [handler = std::move(handler)](const server::PromptContext& context)
4557 try {
4558 auto args = context.arguments.get<Args>();
4559 auto handled = server::detail::invoke_typed_context_handler(
4560 handler, std::move(args), context);
4561 if constexpr (server::detail::is_result<decltype(handled)>::value) {
4562 if (!handled) {
4563 return mcp::core::unexpected(handled.error());
4564 }
4565 return server::detail::value_to_prompt_result(*handled);
4566 } else {
4567 return server::detail::value_to_prompt_result(std::move(handled));
4568 }
4569 } catch (const std::exception& exception) {
4570 return mcp::core::unexpected(core::Error{
4571 static_cast<int>(protocol::ErrorCode::InvalidParams),
4572 "failed to decode prompt arguments",
4573 exception.what(),
4574 });
4575 }
4576 });
4577 }
4578
4580 template <class Handler>
4581 Builder& prompt(std::string name, Handler handler) {
4582 server::detail::require_unambiguous_prompt_handler<Handler>();
4583 protocol::Prompt prompt;
4584 prompt.name = std::move(name);
4585 return add_prompt(
4586 std::move(prompt),
4587 [handler = std::move(handler)](const server::PromptContext& context)
4589 try {
4590 auto handled =
4591 server::detail::invoke_prompt_handler(handler, context);
4592 if constexpr (server::detail::is_result<decltype(handled)>::value) {
4593 if (!handled) {
4594 return mcp::core::unexpected(handled.error());
4595 }
4596 return server::detail::value_to_prompt_result(*handled);
4597 } else {
4598 return server::detail::value_to_prompt_result(std::move(handled));
4599 }
4600 } catch (const std::exception& exception) {
4601 return mcp::core::unexpected(core::Error{
4602 static_cast<int>(protocol::ErrorCode::InvalidParams),
4603 "failed to run prompt handler",
4604 exception.what(),
4605 });
4606 }
4607 });
4608 }
4609
4611 template <class Args, class Handler>
4612 Builder& resource(std::string name, Handler handler) {
4613 server::detail::require_unambiguous_typed_context_handler<
4614 Handler, Args, server::ResourceContext>("resource");
4615 protocol::Resource resource;
4616 resource.uri = std::move(name);
4617 resource.name = resource.uri;
4618 return add_resource(
4619 std::move(resource),
4620 [handler = std::move(handler)](const server::ResourceContext& context)
4622 try {
4623 auto args = context.params.get<Args>();
4624 auto handled = server::detail::invoke_typed_context_handler(
4625 handler, std::move(args), context);
4626 if constexpr (server::detail::is_result<decltype(handled)>::value) {
4627 if (!handled) {
4628 return mcp::core::unexpected(handled.error());
4629 }
4630 return server::detail::value_to_resource_read_result(*handled,
4631 context.uri);
4632 } else {
4633 return server::detail::value_to_resource_read_result(
4634 std::move(handled), context.uri);
4635 }
4636 } catch (const std::exception& exception) {
4637 return mcp::core::unexpected(core::Error{
4638 static_cast<int>(protocol::ErrorCode::InvalidParams),
4639 "failed to decode resource parameters",
4640 exception.what(),
4641 });
4642 }
4643 });
4644 }
4645
4650 template <class Handler>
4651 Builder& resource(std::string name, Handler handler) {
4652 server::detail::require_callable(handler, "resource");
4653 server::detail::require_unambiguous_resource_handler<Handler>();
4654 auto uri = name;
4655 protocol::Resource resource;
4656 resource.uri = uri;
4657 resource.name = uri;
4658 if constexpr (std::is_invocable_v<Handler>) {
4659 using Handled = decltype(handler());
4660 if constexpr (std::is_same_v<std::decay_t<Handled>, protocol::Resource>) {
4661 resource = handler();
4662 }
4663 }
4664 return add_resource(
4665 std::move(resource),
4666 [handler = std::move(handler),
4667 uri = std::move(uri)](const server::ResourceContext& context)
4669 try {
4670 auto handled =
4671 server::detail::invoke_resource_handler(handler, context);
4672 if constexpr (std::is_same_v<std::decay_t<decltype(handled)>,
4675 } else if constexpr (server::detail::is_result<
4676 decltype(handled)>::value) {
4677 if (!handled) {
4678 return mcp::core::unexpected(handled.error());
4679 }
4680 auto result = server::detail::value_to_resource_read_result(
4681 *handled, context.uri);
4682 for (auto& c : result.contents) {
4683 if (c.uri.empty()) {
4684 c.uri = uri;
4685 }
4686 }
4687 return result;
4688 } else {
4689 auto result = server::detail::value_to_resource_read_result(
4690 std::move(handled), context.uri);
4691 for (auto& c : result.contents) {
4692 if (c.uri.empty()) {
4693 c.uri = uri;
4694 }
4695 }
4696 return result;
4697 }
4698 } catch (const std::exception& exception) {
4699 return mcp::core::unexpected(core::Error{
4700 static_cast<int>(protocol::ErrorCode::InvalidParams),
4701 "failed to run resource handler",
4702 exception.what(),
4703 });
4704 }
4705 });
4706 }
4707
4709 template <class Handler>
4710 Builder& resource_template(std::string name, Handler handler) {
4711 server::detail::require_callable(handler, "resource_template");
4713 if constexpr (std::is_invocable_v<Handler>) {
4714 auto handled = handler();
4715 if constexpr (server::detail::is_result<decltype(handled)>::value) {
4716 if (!handled) {
4717 throw std::runtime_error(handled.error().message);
4718 }
4719 tmpl = *handled;
4720 } else {
4721 tmpl = std::move(handled);
4722 }
4723 } else if constexpr (std::is_invocable_v<Handler, std::string>) {
4724 tmpl = handler({});
4725 } else {
4726 static_assert(
4727 std::is_invocable_v<Handler>,
4728 "resource_template handler must accept no arguments or string");
4729 }
4730 if (tmpl.name.empty()) {
4731 tmpl.name = name;
4732 }
4733 if (tmpl.uri_template.empty()) {
4734 tmpl.uri_template = std::move(name);
4735 }
4736 return add_resource_template(std::move(tmpl));
4737 }
4738
4740 template <class Handler>
4741 Builder& completion(Handler handler) {
4742 server::detail::require_callable(handler, "completion");
4743 if constexpr (server::detail::is_typed_completion_handler_v<Handler>) {
4744 server::detail::require_unambiguous_completion_handler<Handler>();
4745 } else {
4746 server::detail::require_unambiguous_json_extension_handler<Handler>();
4747 }
4748 builder_.on_completion([handler = std::move(handler)](
4749 const protocol::Json& request,
4750 const server::SessionContext& context,
4751 CancellationToken cancellation) mutable
4753 if constexpr (server::detail::is_typed_completion_handler_v<Handler>) {
4754 const auto params = protocol::complete_params_from_json(request);
4755 if (!params) {
4756 return mcp::core::unexpected(core::Error{
4757 static_cast<int>(protocol::ErrorCode::InvalidParams),
4758 params.error().message, params.error().detail, "protocol"});
4759 }
4760 server::CompletionContext completion_context;
4761 static_cast<server::SessionContext&>(completion_context) = context;
4762 completion_context.params = *params;
4763 completion_context.cancellation = cancellation;
4764 auto handled = server::detail::invoke_completion_handler(
4765 handler, completion_context);
4766 return server::detail::completion_response_to_json(std::move(handled));
4767 } else {
4768 auto handled = server::detail::invoke_json_extension_handler(
4769 handler, request, context, cancellation);
4770 if constexpr (server::detail::is_result<decltype(handled)>::value) {
4771 return handled;
4772 } else {
4773 return server::detail::value_to_json(std::move(handled));
4774 }
4775 }
4776 });
4777 return *this;
4778 }
4779
4781 template <class Handler>
4782 Builder& sampling(Handler handler) {
4783 server::detail::require_callable(handler, "sampling");
4784 server::detail::require_unambiguous_json_extension_handler<Handler>();
4785 builder_.on_sampling(
4786 [handler = std::move(handler)](const protocol::Json& request,
4787 const server::SessionContext& context,
4788 CancellationToken cancellation) mutable
4790 auto handled = server::detail::invoke_json_extension_handler(
4791 handler, request, context, cancellation);
4792 if constexpr (server::detail::is_result<decltype(handled)>::value) {
4793 return handled;
4794 } else {
4795 return server::detail::value_to_json(std::move(handled));
4796 }
4797 });
4798 return *this;
4799 }
4800
4802 template <class Handler>
4803 Builder& logging(Handler handler) {
4804 server::detail::require_callable(handler, "logging");
4805 builder_.on_logging([handler = std::move(handler)](
4806 std::string_view level, std::string_view message) {
4807 handler(level, message);
4808 });
4809 return *this;
4810 }
4811
4813 template <class Handler>
4814 Builder& raw_request(Handler handler) {
4815 server::detail::require_callable(handler, "raw_request");
4816 builder_.on_raw_request([handler = std::move(handler)](
4817 const protocol::JsonRpcRequest& request,
4818 const server::SessionContext& /*context*/)
4819 -> std::optional<protocol::JsonRpcResponse> {
4820 if constexpr (std::is_same_v<std::decay_t<decltype(handler(request))>,
4821 std::optional<protocol::JsonRpcResponse>>) {
4822 return handler(request);
4823 } else if constexpr (std::is_same_v<
4824 std::decay_t<decltype(handler(request))>,
4826 return handler(request);
4827 } else {
4828 handler(request);
4829 return std::nullopt;
4830 }
4831 });
4832 return *this;
4833 }
4834
4835 template <class Router>
4836 Builder& router(const Router& router) {
4837 router.apply_to(builder_);
4838 return *this;
4839 }
4840
4841 template <class Args, class Result, class Handler>
4842 Builder& tool(
4844 return add_tool(
4845 std::move(registration.definition),
4846 [handler = std::move(registration.handler)](
4847 const server::ToolContext& context)
4849 try {
4850 auto args =
4851 server::detail::argument_from_json<Args>(context.arguments);
4852 auto handled = server::detail::invoke_tool_handler(
4853 handler, std::move(args), context);
4854 if constexpr (server::detail::is_result<decltype(handled)>::value) {
4855 if (!handled) {
4856 return mcp::core::unexpected(handled.error());
4857 }
4858 return server::detail::value_to_tool_result(*handled);
4859 } else {
4860 return server::detail::value_to_tool_result(std::move(handled));
4861 }
4862 } catch (const std::exception& exception) {
4863 return mcp::core::unexpected(core::Error{
4864 static_cast<int>(protocol::ErrorCode::InvalidParams),
4865 "failed to decode tool arguments",
4866 exception.what(),
4867 });
4868 }
4869 });
4870 }
4871
4872 Builder& on_completion(server::Server::JsonHandler handler) {
4873 builder_.on_completion(std::move(handler));
4874 return *this;
4875 }
4876
4877 Builder& on_completion(server::Server::JsonContextHandler handler) {
4878 builder_.on_completion(std::move(handler));
4879 return *this;
4880 }
4881
4882 Builder& on_completion(server::Server::JsonRequestContextHandler handler) {
4883 builder_.on_completion(std::move(handler));
4884 return *this;
4885 }
4886
4887 Builder& on_sampling(server::Server::JsonHandler handler) {
4888 builder_.on_sampling(std::move(handler));
4889 return *this;
4890 }
4891
4892 Builder& on_sampling(server::Server::JsonContextHandler handler) {
4893 builder_.on_sampling(std::move(handler));
4894 return *this;
4895 }
4896
4897 Builder& on_sampling(server::Server::JsonRequestContextHandler handler) {
4898 builder_.on_sampling(std::move(handler));
4899 return *this;
4900 }
4901
4902 Builder& on_logging(server::Server::LoggingHandler handler) {
4903 builder_.on_logging(std::move(handler));
4904 return *this;
4905 }
4906
4907 Builder& on_raw_request(server::Server::RawRequestHandler handler) {
4908 builder_.on_raw_request(std::move(handler));
4909 return *this;
4910 }
4911
4912 Builder& on_raw_notification(server::Server::RawNotificationHandler handler) {
4913 builder_.on_raw_notification(std::move(handler));
4914 return *this;
4915 }
4916
4917 Builder& on_task_list(server::Server::TaskListHandler handler) {
4918 builder_.on_task_list(std::move(handler));
4919 return *this;
4920 }
4921
4922 Builder& on_tools_list(server::Server::ToolsListHandler handler) {
4923 builder_.on_tools_list(std::move(handler));
4924 return *this;
4925 }
4926
4927 Builder& on_prompts_list(server::Server::PromptsListHandler handler) {
4928 builder_.on_prompts_list(std::move(handler));
4929 return *this;
4930 }
4931
4932 Builder& on_resources_list(server::Server::ResourcesListHandler handler) {
4933 builder_.on_resources_list(std::move(handler));
4934 return *this;
4935 }
4936
4937 Builder& on_resource_templates_list(
4938 server::Server::ResourceTemplatesListHandler handler) {
4939 builder_.on_resource_templates_list(std::move(handler));
4940 return *this;
4941 }
4942
4943 Builder& on_task_get(server::Server::TaskGetHandler handler) {
4944 builder_.on_task_get(std::move(handler));
4945 return *this;
4946 }
4947
4948 Builder& on_task_cancel(server::Server::TaskCancelHandler handler) {
4949 builder_.on_task_cancel(std::move(handler));
4950 return *this;
4951 }
4952
4953 Builder& on_task_result(server::Server::TaskResultHandler handler) {
4954 builder_.on_task_result(std::move(handler));
4955 return *this;
4956 }
4957
4958 Builder& on_progress(server::Server::ProgressHandler handler) {
4959 builder_.on_progress(std::move(handler));
4960 return *this;
4961 }
4962
4963 Builder& on_roots_list_changed(
4964 server::Server::RootsListChangedHandler handler) {
4965 builder_.on_roots_list_changed(std::move(handler));
4966 return *this;
4967 }
4968
4969 Builder& on_tool_list_changed(server::Server::ListChangedHandler handler) {
4970 builder_.on_tool_list_changed(std::move(handler));
4971 return *this;
4972 }
4973
4974 Builder& on_prompt_list_changed(server::Server::ListChangedHandler handler) {
4975 builder_.on_prompt_list_changed(std::move(handler));
4976 return *this;
4977 }
4978
4979 Builder& on_resource_list_changed(
4980 server::Server::ListChangedHandler handler) {
4981 builder_.on_resource_list_changed(std::move(handler));
4982 return *this;
4983 }
4984
4985 Builder& on_resource_updated(server::Server::ResourceUpdatedHandler handler) {
4986 builder_.on_resource_updated(std::move(handler));
4987 return *this;
4988 }
4989
4990 Builder& handler(server::ServerHandler handler) {
4991 builder_.with_handler(std::move(handler));
4992 return *this;
4993 }
4994
4995 Builder& handler(const server::ServerHandlerInterface& handler) {
4996 builder_.with_handler(handler);
4997 return *this;
4998 }
4999
5000 core::Result<Peer> build() {
5001 auto server = builder_.build();
5002 if (!server) {
5003 return mcp::core::unexpected(server.error());
5004 }
5005
5006 // Capture handlers from the Server before moving it into the Peer.
5007 // The Peer's own handler fields must be populated so that
5008 // ServerPeer::handle_request() dispatches through them.
5009 auto raw_handler = (*server)->raw_request_handler();
5010 auto raw_context_handler = (*server)->raw_request_context_handler();
5011
5012 Peer peer(std::move(*server));
5013 if (raw_handler) {
5014 peer.raw_request_handler_ = std::move(raw_handler);
5015 }
5016 if (raw_context_handler) {
5017 peer.raw_request_context_handler_ = std::move(raw_context_handler);
5018 }
5019 for (auto& transport : native_transports_) {
5020 auto added = peer.add_transport(std::move(transport));
5021 if (!added) {
5022 return mcp::core::unexpected(added.error());
5023 }
5024 }
5025 native_transports_.clear();
5026 return peer;
5027 }
5028
5031 int run();
5032
5033 private:
5034 server::ServerBuilder builder_;
5035 std::vector<std::unique_ptr<transport::ServerTransport>> native_transports_;
5036};
5037
5039 return Builder{};
5040}
5041
5044
5045} // 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:4508
Builder & logging(Handler handler)
Registers a logging handler.
Definition peer.hpp:4803
Builder & resource(std::string name, Handler handler)
Registers a resource with auto-detected handler signature.
Definition peer.hpp:4651
Builder & prompt(std::string name, Handler handler)
Registers a typed prompt with argument decoding.
Definition peer.hpp:4548
Builder & prompt(std::string name, Handler handler)
Registers a prompt with auto-detected handler signature.
Definition peer.hpp:4581
Builder & raw_request(Handler handler)
Registers a raw request handler.
Definition peer.hpp:4814
Builder & tool(protocol::ToolDefinition definition, Handler handler)
Registers a tool with an explicit definition and typed handler.
Definition peer.hpp:4518
Builder & sampling(Handler handler)
Registers a sampling handler.
Definition peer.hpp:4782
Builder & resource_template(std::string name, Handler handler)
Registers a resource template with a callable adapter.
Definition peer.hpp:4710
Builder & completion(Handler handler)
Registers a completion handler.
Definition peer.hpp:4741
Builder & resource(std::string name, Handler handler)
Registers a typed resource with parameter decoding.
Definition peer.hpp:4612
Server-side peer boundary for exposing MCP capabilities.
Definition peer.hpp:2995
Peer(std::unique_ptr< server::Server > server)
Creates a server peer from an owned server implementation.
Definition peer.hpp:3004
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:3869
Peer(server::ServerOptions options={})
Creates a server peer from options.
Definition peer.hpp:3000
core::Result< core::Unit > add_transport(std::unique_ptr< transport::ServerTransport > transport)
Adds an owned role-generic server transport.
Definition peer.hpp:3746
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:3902
void wait_until_ready()
Blocks until all native transports are ready to process messages.
Definition peer.hpp:3819
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:132
std::function< void(std::string_view, std::string_view)> LoggingMessageHandler
Receives logging messages from the server.
Definition client.hpp:184
std::function< void(const protocol::ProgressNotificationParams &)> ProgressHandler
Receives progress notifications associated with a progress token.
Definition client.hpp:205
std::function< void(const std::string &)> ResourceUpdatedHandler
Receives resource update notifications.
Definition client.hpp:201
std::function< core::Result< protocol::RootsListResult >()> RootsListRequestHandler
Handles a server request for the client's current roots.
Definition client.hpp:217
std::function< void(const protocol::RequestId &, std::string_view)> CancelledHandler
Receives cancellation notifications for in-flight requests.
Definition client.hpp:193
std::function< core::Result< protocol::Json >(const protocol::JsonRpcRequest &)> CustomRequestHandler
Handles non-built-in server requests.
Definition client.hpp:245
std::function< core::Result< protocol::CreateMessageResult >(const protocol::CreateMessageParams &)> SamplingRequestHandler
Handles a server sampling request.
Definition client.hpp:226
std::function< void(std::string_view)> ElicitationCompleteHandler
Receives completion notifications for elicitation flows.
Definition client.hpp:209
std::function< void()> ListChangedHandler
Receives list-change notifications for prompts, resources, tools, or roots.
Definition client.hpp:197
std::function< void()> InitializedHandler
Receives notifications that the peer completed initialization.
Definition client.hpp:187
std::function< void(const protocol::JsonRpcNotification &)> RawNotificationHandler
Observes every inbound notification after built-in dispatch.
Definition client.hpp:255
std::function< core::Result< protocol::CreateElicitationResult >(const protocol::CreateElicitationRequestParam &)> ElicitationRequestHandler
Handles a server elicitation request.
Definition client.hpp:236
std::function< void(const protocol::Task &)> TaskStatusHandler
Receives task status notifications.
Definition client.hpp:212
Fluent builder for constructing a configured Server.
Definition server.hpp:571
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:397
std::function< core::Result< protocol::PromptsListResult >(const protocol::PaginatedRequestParams &, const SessionContext &)> PromptsListHandler
Handles prompts/list requests.
Definition server.hpp:359
std::function< core::Result< protocol::ResourceTemplatesListResult >(const protocol::PaginatedRequestParams &, const SessionContext &)> ResourceTemplatesListHandler
Handles resources/templates/list requests.
Definition server.hpp:369
std::function< core::Result< protocol::Task >(const protocol::TaskGetParams &, const SessionContext &)> TaskGetHandler
Handles task get requests.
Definition server.hpp:373
std::function< core::Result< protocol::Json >(const protocol::TaskResultParams &, const SessionContext &)> TaskResultHandler
Handles task result requests.
Definition server.hpp:381
std::function< core::Result< protocol::Task >(const protocol::TaskCancelParams &, const SessionContext &)> TaskCancelHandler
Handles task cancel requests.
Definition server.hpp:377
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:337
std::function< core::Result< protocol::Json >(const protocol::Json &)> JsonHandler
Handles JSON-shaped extension requests such as completion or sampling.
Definition server.hpp:322
std::function< core::Result< core::Unit >(const protocol::JsonRpcNotification &, const SessionContext &)> RawNotificationHandler
Handles raw or custom notifications.
Definition server.hpp:345
std::function< void(std::string_view, std::string_view)> LoggingHandler
Handles logging messages from clients.
Definition server.hpp:330
std::function< core::Result< core::Unit >(const SessionContext &)> RootsListChangedHandler
Handles client roots-list-changed notifications.
Definition server.hpp:385
std::function< core::Result< protocol::ResourcesListResult >(const protocol::PaginatedRequestParams &, const SessionContext &)> ResourcesListHandler
Handles resources/list requests.
Definition server.hpp:364
std::function< core::Result< core::Unit >(const SessionContext &)> ListChangedHandler
Handles client list-changed notifications.
Definition server.hpp:393
std::function< core::Result< core::Unit >(const protocol::ProgressNotificationParams &, const SessionContext &)> ProgressHandler
Handles client progress notifications.
Definition server.hpp:389
std::function< core::Result< protocol::TaskListResult >(const protocol::TaskListParams &, const SessionContext &)> TaskListHandler
Handles task list requests.
Definition server.hpp:349
std::function< core::Result< protocol::ToolsListResult >(const protocol::PaginatedRequestParams &, const SessionContext &)> ToolsListHandler
Handles tools/list requests.
Definition server.hpp:354
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:167
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:42
std::string session_id
Logical MCP session id when the transport has one.
Definition transport.hpp:44
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.