cxxmcp 1.1.6
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
3899 transport::ServerTransport& transport,
3900 const server::SessionContext& context = {},
3901 CancellationToken cancellation = CancellationToken::none()) {
3902 bool initialized = false;
3903 return detail::serve_transport_loop(
3904 transport, cancellation,
3905 [this, &context, &transport,
3906 initialized](const protocol::JsonRpcMessage& message) mutable
3907 -> core::Result<std::optional<protocol::JsonRpcMessage>> {
3908 // Detect stateless mode: _meta contains protocolVersion
3909 auto is_stateless_request = [](const protocol::Json& params) -> bool {
3910 return params.is_object() && params.contains("_meta") &&
3911 params.at("_meta").is_object() &&
3912 params.at("_meta").contains(
3913 "io.modelcontextprotocol/protocolVersion");
3914 };
3915 if (const auto* request =
3916 std::get_if<protocol::JsonRpcRequest>(&message)) {
3917 const bool stateless = is_stateless_request(request->params);
3918 const bool allowed_before_initialized =
3919 stateless || request->method == protocol::InitializeMethod ||
3920 request->method == protocol::PingMethod;
3921 if (!initialized && !allowed_before_initialized) {
3922 return core::Result<std::optional<protocol::JsonRpcMessage>>{
3923 protocol::JsonRpcMessage{protocol::make_error_response(
3924 request->id,
3925 protocol::make_error(protocol::ErrorCode::InvalidRequest,
3926 "server peer transport session is "
3927 "not initialized"))}};
3928 }
3929 }
3930 if (const auto* notification =
3931 std::get_if<protocol::JsonRpcNotification>(&message)) {
3932 const bool stateless = is_stateless_request(notification->params);
3933 if (!initialized && !stateless &&
3934 notification->method != protocol::InitializedMethod) {
3935 return mcp::core::unexpected(detail::peer_dispatch_error(
3936 "server peer transport session is not initialized"));
3937 }
3938 auto message_context =
3939 detail::context_for_received_server_message(transport, context);
3940 auto dispatched =
3941 dispatch_message(message, message_context, &transport);
3942 if (!dispatched) {
3943 return dispatched;
3944 }
3945 if (notification->method == protocol::InitializedMethod) {
3946 initialized = true;
3947 }
3948 return dispatched;
3949 }
3950 auto message_context =
3951 detail::context_for_received_server_message(transport, context);
3952 return dispatch_message(message, message_context, &transport);
3953 });
3954 }
3955
3956 private:
3957 server::SessionContext subscription_context_for(
3958 const server::SessionContext& context,
3959 const transport::ServerTransport* native_transport) const {
3960 server::SessionContext subscription_context = context;
3961 if (subscription_context.transport || native_transport == nullptr) {
3962 return subscription_context;
3963 }
3964
3965 for (std::size_t index = 0; index < native_transports_.size(); ++index) {
3966 if (native_transports_[index].get() == native_transport &&
3967 index < native_context_transports_.size()) {
3968 subscription_context.transport =
3969 native_context_transports_[index].get();
3970 subscription_context.transport_lifetime =
3971 native_context_transports_[index]->lifetime_token();
3972 break;
3973 }
3974 }
3975 return subscription_context;
3976 }
3977
3978 CancellationToken begin_peer_request_cancellation(
3979 const protocol::RequestId& request_id) {
3980 CancellationSource source;
3981 auto token = source.token();
3982 std::lock_guard lock(*peer_request_cancellations_mutex_);
3983 (*peer_request_cancellations_)[detail::peer_request_cancellation_key(
3984 request_id)] = std::move(source);
3985 return token;
3986 }
3987
3988 void end_peer_request_cancellation(
3989 const protocol::RequestId& request_id) noexcept {
3990 std::lock_guard lock(*peer_request_cancellations_mutex_);
3991 peer_request_cancellations_->erase(
3992 detail::peer_request_cancellation_key(request_id));
3993 }
3994
3995 void cancel_peer_request(const protocol::RequestId& request_id) noexcept {
3996 std::lock_guard lock(*peer_request_cancellations_mutex_);
3997 const auto it = peer_request_cancellations_->find(
3998 detail::peer_request_cancellation_key(request_id));
3999 if (it != peer_request_cancellations_->end()) {
4000 it->second.cancel();
4001 }
4002 }
4003
4004 core::Result<core::Unit> handle_native_notification(
4005 const protocol::JsonRpcNotification& notification,
4006 const server::SessionContext& context) try {
4007 if (notification.method == protocol::CancelledNotificationMethod) {
4008 const auto cancelled = protocol::cancelled_notification_params_from_json(
4009 notification.params);
4010 if (!cancelled) {
4011 return mcp::core::unexpected(
4012 errors::make(protocol::ErrorCode::InvalidParams,
4013 "cancelled notification requires valid params"));
4014 }
4015 cancel_peer_request(cancelled->request_id);
4016 return server_->handle_notification(notification, context);
4017 }
4018
4019 if (raw_notification_handler_) {
4020 const auto raw_result = raw_notification_handler_(notification, context);
4021 if (!raw_result) {
4022 return mcp::core::unexpected(raw_result.error());
4023 }
4024 }
4025
4026 if (notification.method == protocol::RootsListChangedNotificationMethod &&
4027 roots_list_changed_handler_) {
4028 const auto result = roots_list_changed_handler_(context);
4029 if (!result) {
4030 return mcp::core::unexpected(result.error());
4031 }
4032 } else if (notification.method == protocol::ProgressNotificationMethod &&
4033 progress_handler_) {
4034 const auto params =
4035 protocol::progress_notification_params_from_json(notification.params);
4036 if (!params) {
4037 return mcp::core::unexpected(
4038 errors::make(protocol::ErrorCode::InvalidParams,
4039 "progress notification requires valid params"));
4040 }
4041 const auto result = progress_handler_(*params, context);
4042 if (!result) {
4043 return mcp::core::unexpected(result.error());
4044 }
4045 } else if (notification.method ==
4046 protocol::ToolsListChangedNotificationMethod &&
4047 tool_list_changed_handler_) {
4048 const auto result = tool_list_changed_handler_(context);
4049 if (!result) {
4050 return mcp::core::unexpected(result.error());
4051 }
4052 } else if (notification.method ==
4053 protocol::PromptsListChangedNotificationMethod &&
4054 prompt_list_changed_handler_) {
4055 const auto result = prompt_list_changed_handler_(context);
4056 if (!result) {
4057 return mcp::core::unexpected(result.error());
4058 }
4059 } else if (notification.method ==
4060 protocol::ResourcesListChangedNotificationMethod &&
4061 resource_list_changed_handler_) {
4062 const auto result = resource_list_changed_handler_(context);
4063 if (!result) {
4064 return mcp::core::unexpected(result.error());
4065 }
4066 } else if (notification.method ==
4067 protocol::ResourcesUpdatedNotificationMethod &&
4068 resource_updated_handler_) {
4069 if (!notification.params.is_object() ||
4070 !notification.params.contains("uri") ||
4071 !notification.params.at("uri").is_string()) {
4072 return mcp::core::unexpected(errors::make(
4073 protocol::ErrorCode::InvalidParams,
4074 "resource updated notification requires a string uri"));
4075 }
4076 const auto result = resource_updated_handler_(
4077 notification.params.at("uri").get<std::string>(), context);
4078 if (!result) {
4079 return mcp::core::unexpected(result.error());
4080 }
4081 }
4082
4083 return core::Unit{};
4084 } catch (const std::exception& ex) {
4085 return mcp::core::unexpected(errors::handler_failed(ex.what()));
4086 } catch (...) {
4087 return mcp::core::unexpected(errors::handler_unknown_exception());
4088 }
4089
4090 std::unique_ptr<server::Server> server_;
4091 std::vector<std::unique_ptr<transport::ServerTransport>> native_transports_;
4092 std::vector<std::unique_ptr<server::Transport>> native_context_transports_;
4093 std::shared_ptr<std::mutex> peer_request_cancellations_mutex_ =
4094 std::make_shared<std::mutex>();
4095 std::shared_ptr<std::unordered_map<std::string, CancellationSource>>
4096 peer_request_cancellations_ = std::make_shared<
4097 std::unordered_map<std::string, CancellationSource>>();
4098 bool native_notification_state_ = false;
4099 server::Server::RawRequestHandler raw_request_handler_;
4100 server::Server::RawRequestContextHandler raw_request_context_handler_;
4101 server::Server::JsonContextHandler completion_handler_;
4102 server::Server::JsonContextHandler sampling_handler_;
4103 server::Server::LoggingHandler logging_handler_;
4104 server::Server::TaskListHandler task_list_handler_;
4105 server::Server::TaskGetHandler task_get_handler_;
4106 server::Server::TaskCancelHandler task_cancel_handler_;
4107 server::Server::TaskResultHandler task_result_handler_;
4108 server::Server::RawNotificationHandler raw_notification_handler_;
4109 server::Server::ProgressHandler progress_handler_;
4110 server::Server::RootsListChangedHandler roots_list_changed_handler_;
4111 server::Server::ListChangedHandler tool_list_changed_handler_;
4112 server::Server::ListChangedHandler prompt_list_changed_handler_;
4113 server::Server::ListChangedHandler resource_list_changed_handler_;
4114 server::Server::ResourceUpdatedHandler resource_updated_handler_;
4115};
4116
4118class Peer<RoleServer>::Builder {
4119 public:
4120 Builder() = default;
4121 Builder(const Builder&) = delete;
4122 Builder& operator=(const Builder&) = delete;
4123 Builder(Builder&&) noexcept = default;
4124 Builder& operator=(Builder&&) noexcept = default;
4125
4126 Builder& name(std::string value) {
4127 builder_.name(std::move(value));
4128 return *this;
4129 }
4130
4131 Builder& version(std::string value) {
4132 builder_.version(std::move(value));
4133 return *this;
4134 }
4135
4136 Builder& instructions(std::string value) {
4137 builder_.instructions(std::move(value));
4138 return *this;
4139 }
4140
4141 Builder& capabilities(protocol::ServerCapabilities value) {
4142 builder_.with_capabilities(std::move(value));
4143 return *this;
4144 }
4145
4146 Builder& transport(std::unique_ptr<server::Transport> value) {
4147 builder_.with_transport(std::move(value));
4148 return *this;
4149 }
4150
4151 Builder& transport(std::unique_ptr<transport::ServerTransport> value) {
4152 native_transports_.push_back(std::move(value));
4153 return *this;
4154 }
4155
4156 Builder& stdio(std::istream& input, std::ostream& output) {
4157 return transport(
4158 std::make_unique<transport::ServerStdioTransport>(input, output));
4159 }
4160
4161 Builder& stdio() { return stdio(std::cin, std::cout); }
4162
4163#if defined(CXXMCP_ENABLE_HTTP)
4164 Builder& streamable_http(
4166 return transport(std::make_unique<transport::StreamableHttpServerTransport>(
4167 std::move(options)));
4168 }
4169
4170 Builder& streamable_http(int listen_port) {
4172 options.listen_port = listen_port;
4173 return streamable_http(std::move(options));
4174 }
4175
4176 Builder& streamable_http(std::string listen_host, int listen_port,
4177 std::string path = "/mcp") {
4179 options.listen_host = std::move(listen_host);
4180 options.listen_port = listen_port;
4181 options.path = std::move(path);
4182 return streamable_http(std::move(options));
4183 }
4184#endif
4185
4186#if defined(CXXMCP_ENABLE_WEBSOCKET)
4187 Builder& websocket(transport::WebSocketServerTransportOptions options) {
4188 return transport(std::make_unique<transport::WebSocketServerTransport>(
4189 std::move(options)));
4190 }
4191
4192 Builder& websocket(int listen_port) {
4194 options.listen_port = listen_port;
4195 return websocket(std::move(options));
4196 }
4197
4198 Builder& websocket(std::string listen_host, int listen_port,
4199 std::string path = "/mcp") {
4201 options.listen_host = std::move(listen_host);
4202 options.listen_port = listen_port;
4203 options.path = std::move(path);
4204 return websocket(std::move(options));
4205 }
4206#endif
4207
4208 Builder& auth_provider(std::unique_ptr<server::AuthProvider> value) {
4209 builder_.with_auth_provider(std::move(value));
4210 return *this;
4211 }
4212
4213 Builder& rate_limiter(std::unique_ptr<server::RateLimiter> value) {
4214 builder_.with_rate_limiter(std::move(value));
4215 return *this;
4216 }
4217
4218 Builder& schema_validator(
4219 std::shared_ptr<const server::JsonSchemaValidator> value) {
4220 builder_.with_schema_validator(std::move(value));
4221 return *this;
4222 }
4223
4224 Builder& task_manager(server::TaskOperationProcessorOptions options = {}) {
4225 builder_.with_task_manager(std::move(options));
4226 return *this;
4227 }
4228
4229 Builder& task_manager(
4230 std::shared_ptr<server::TaskOperationProcessor> processor) {
4231 builder_.with_task_manager(std::move(processor));
4232 return *this;
4233 }
4234
4235 Builder& add_tool(protocol::ToolDefinition definition,
4236 server::ToolHandler handler) {
4237 builder_.add_tool(std::move(definition), std::move(handler));
4238 return *this;
4239 }
4240
4241 Builder& tool(protocol::ToolDefinition definition,
4242 server::ToolHandler handler) {
4243 return add_tool(std::move(definition), std::move(handler));
4244 }
4245
4246 Builder& add_prompt(protocol::Prompt prompt, server::PromptHandler handler) {
4247 builder_.add_prompt(std::move(prompt), std::move(handler));
4248 return *this;
4249 }
4250
4251 Builder& prompt(protocol::Prompt prompt, server::PromptHandler handler) {
4252 return add_prompt(std::move(prompt), std::move(handler));
4253 }
4254
4255 template <class Args, class Handler>
4256 Builder& prompt(server::TypedPromptRegistration<Args, Handler> registration) {
4257 return add_prompt(
4258 std::move(registration.prompt),
4259 [handler = std::move(registration.handler)](
4260 const server::PromptContext& context)
4262 try {
4263 auto args = context.arguments.get<Args>();
4264 auto handled = server::detail::invoke_typed_context_handler(
4265 handler, std::move(args), context);
4266 if constexpr (server::detail::is_result<decltype(handled)>::value) {
4267 if (!handled) {
4268 return mcp::core::unexpected(handled.error());
4269 }
4270 return server::detail::value_to_prompt_result(*handled);
4271 } else {
4272 return server::detail::value_to_prompt_result(std::move(handled));
4273 }
4274 } catch (const std::exception& exception) {
4275 return mcp::core::unexpected(core::Error{
4276 static_cast<int>(protocol::ErrorCode::InvalidParams),
4277 "failed to decode prompt arguments",
4278 exception.what(),
4279 });
4280 }
4281 });
4282 }
4283
4284 Builder& add_resource(protocol::Resource resource,
4286 builder_.add_resource(std::move(resource), std::move(handler));
4287 return *this;
4288 }
4289
4290 Builder& resource(protocol::Resource resource,
4292 return add_resource(std::move(resource), std::move(handler));
4293 }
4294
4295 template <class Args, class Handler>
4296 Builder& resource(
4297 server::TypedResourceRegistration<Args, Handler> registration) {
4298 return add_resource(
4299 std::move(registration.resource),
4300 [handler = std::move(registration.handler)](
4301 const server::ResourceContext& context)
4302 -> core::Result<protocol::ResourcesReadResult> {
4303 try {
4304 auto args = context.params.get<Args>();
4305 auto handled = server::detail::invoke_typed_context_handler(
4306 handler, std::move(args), context);
4307 if constexpr (server::detail::is_result<decltype(handled)>::value) {
4308 if (!handled) {
4309 return mcp::core::unexpected(handled.error());
4310 }
4311 return server::detail::value_to_resource_read_result(*handled,
4312 context.uri);
4313 } else {
4314 return server::detail::value_to_resource_read_result(
4315 std::move(handled), context.uri);
4316 }
4317 } catch (const std::exception& exception) {
4318 return mcp::core::unexpected(core::Error{
4319 static_cast<int>(protocol::ErrorCode::InvalidParams),
4320 "failed to decode resource parameters",
4321 exception.what(),
4322 });
4323 }
4324 });
4325 }
4326
4327 Builder& add_resource_template(protocol::ResourceTemplate resource_template) {
4328 builder_.add_resource_template(std::move(resource_template));
4329 return *this;
4330 }
4331
4332 Builder& resource_template(protocol::ResourceTemplate resource_template) {
4333 return add_resource_template(std::move(resource_template));
4334 }
4335
4342 template <class Args, class Result, class Handler>
4343 Builder& tool(std::string name, Handler handler) {
4344 auto definition =
4345 protocol::tool_definition(std::move(name)).input<Args>().build();
4346 server::detail::apply_default_output_schema<Result>(definition);
4347 return tool<Args, Result>(std::move(definition), std::move(handler));
4348 }
4349
4351 template <class Args, class Result, class Handler>
4352 Builder& tool(protocol::ToolDefinition definition, Handler handler) {
4353 return add_tool(
4354 std::move(definition),
4355 [handler = std::move(handler)](const server::ToolContext& context)
4357 try {
4358 auto args =
4359 server::detail::argument_from_json<Args>(context.arguments);
4360 auto handled = server::detail::invoke_tool_handler(
4361 handler, std::move(args), context);
4362 if constexpr (server::detail::is_result<decltype(handled)>::value) {
4363 if (!handled) {
4364 return mcp::core::unexpected(handled.error());
4365 }
4366 return server::detail::value_to_tool_result(*handled);
4367 } else {
4368 return server::detail::value_to_tool_result(std::move(handled));
4369 }
4370 } catch (const std::exception& exception) {
4371 return mcp::core::unexpected(core::Error{
4372 static_cast<int>(protocol::ErrorCode::InvalidParams),
4373 "failed to decode tool arguments",
4374 exception.what(),
4375 });
4376 }
4377 });
4378 }
4379
4381 template <class Args, class Handler>
4382 Builder& prompt(std::string name, Handler handler) {
4383 server::detail::require_unambiguous_typed_context_handler<
4384 Handler, Args, server::PromptContext>("prompt");
4385 protocol::Prompt prompt;
4386 prompt.name = std::move(name);
4387 return add_prompt(
4388 std::move(prompt),
4389 [handler = std::move(handler)](const server::PromptContext& context)
4391 try {
4392 auto args = context.arguments.get<Args>();
4393 auto handled = server::detail::invoke_typed_context_handler(
4394 handler, std::move(args), context);
4395 if constexpr (server::detail::is_result<decltype(handled)>::value) {
4396 if (!handled) {
4397 return mcp::core::unexpected(handled.error());
4398 }
4399 return server::detail::value_to_prompt_result(*handled);
4400 } else {
4401 return server::detail::value_to_prompt_result(std::move(handled));
4402 }
4403 } catch (const std::exception& exception) {
4404 return mcp::core::unexpected(core::Error{
4405 static_cast<int>(protocol::ErrorCode::InvalidParams),
4406 "failed to decode prompt arguments",
4407 exception.what(),
4408 });
4409 }
4410 });
4411 }
4412
4414 template <class Handler>
4415 Builder& prompt(std::string name, Handler handler) {
4416 server::detail::require_unambiguous_prompt_handler<Handler>();
4417 protocol::Prompt prompt;
4418 prompt.name = std::move(name);
4419 return add_prompt(
4420 std::move(prompt),
4421 [handler = std::move(handler)](const server::PromptContext& context)
4423 try {
4424 auto handled =
4425 server::detail::invoke_prompt_handler(handler, context);
4426 if constexpr (server::detail::is_result<decltype(handled)>::value) {
4427 if (!handled) {
4428 return mcp::core::unexpected(handled.error());
4429 }
4430 return server::detail::value_to_prompt_result(*handled);
4431 } else {
4432 return server::detail::value_to_prompt_result(std::move(handled));
4433 }
4434 } catch (const std::exception& exception) {
4435 return mcp::core::unexpected(core::Error{
4436 static_cast<int>(protocol::ErrorCode::InvalidParams),
4437 "failed to run prompt handler",
4438 exception.what(),
4439 });
4440 }
4441 });
4442 }
4443
4445 template <class Args, class Handler>
4446 Builder& resource(std::string name, Handler handler) {
4447 server::detail::require_unambiguous_typed_context_handler<
4448 Handler, Args, server::ResourceContext>("resource");
4449 protocol::Resource resource;
4450 resource.uri = std::move(name);
4451 resource.name = resource.uri;
4452 return add_resource(
4453 std::move(resource),
4454 [handler = std::move(handler)](const server::ResourceContext& context)
4456 try {
4457 auto args = context.params.get<Args>();
4458 auto handled = server::detail::invoke_typed_context_handler(
4459 handler, std::move(args), context);
4460 if constexpr (server::detail::is_result<decltype(handled)>::value) {
4461 if (!handled) {
4462 return mcp::core::unexpected(handled.error());
4463 }
4464 return server::detail::value_to_resource_read_result(*handled,
4465 context.uri);
4466 } else {
4467 return server::detail::value_to_resource_read_result(
4468 std::move(handled), context.uri);
4469 }
4470 } catch (const std::exception& exception) {
4471 return mcp::core::unexpected(core::Error{
4472 static_cast<int>(protocol::ErrorCode::InvalidParams),
4473 "failed to decode resource parameters",
4474 exception.what(),
4475 });
4476 }
4477 });
4478 }
4479
4484 template <class Handler>
4485 Builder& resource(std::string name, Handler handler) {
4486 server::detail::require_callable(handler, "resource");
4487 server::detail::require_unambiguous_resource_handler<Handler>();
4488 auto uri = name;
4489 protocol::Resource resource;
4490 resource.uri = uri;
4491 resource.name = uri;
4492 if constexpr (std::is_invocable_v<Handler>) {
4493 using Handled = decltype(handler());
4494 if constexpr (std::is_same_v<std::decay_t<Handled>, protocol::Resource>) {
4495 resource = handler();
4496 }
4497 }
4498 return add_resource(
4499 std::move(resource),
4500 [handler = std::move(handler),
4501 uri = std::move(uri)](const server::ResourceContext& context)
4503 try {
4504 auto handled =
4505 server::detail::invoke_resource_handler(handler, context);
4506 if constexpr (std::is_same_v<std::decay_t<decltype(handled)>,
4509 } else if constexpr (server::detail::is_result<
4510 decltype(handled)>::value) {
4511 if (!handled) {
4512 return mcp::core::unexpected(handled.error());
4513 }
4514 auto result = server::detail::value_to_resource_read_result(
4515 *handled, context.uri);
4516 for (auto& c : result.contents) {
4517 if (c.uri.empty()) {
4518 c.uri = uri;
4519 }
4520 }
4521 return result;
4522 } else {
4523 auto result = server::detail::value_to_resource_read_result(
4524 std::move(handled), context.uri);
4525 for (auto& c : result.contents) {
4526 if (c.uri.empty()) {
4527 c.uri = uri;
4528 }
4529 }
4530 return result;
4531 }
4532 } catch (const std::exception& exception) {
4533 return mcp::core::unexpected(core::Error{
4534 static_cast<int>(protocol::ErrorCode::InvalidParams),
4535 "failed to run resource handler",
4536 exception.what(),
4537 });
4538 }
4539 });
4540 }
4541
4543 template <class Handler>
4544 Builder& resource_template(std::string name, Handler handler) {
4545 server::detail::require_callable(handler, "resource_template");
4547 if constexpr (std::is_invocable_v<Handler>) {
4548 auto handled = handler();
4549 if constexpr (server::detail::is_result<decltype(handled)>::value) {
4550 if (!handled) {
4551 throw std::runtime_error(handled.error().message);
4552 }
4553 tmpl = *handled;
4554 } else {
4555 tmpl = std::move(handled);
4556 }
4557 } else if constexpr (std::is_invocable_v<Handler, std::string>) {
4558 tmpl = handler({});
4559 } else {
4560 static_assert(
4561 std::is_invocable_v<Handler>,
4562 "resource_template handler must accept no arguments or string");
4563 }
4564 if (tmpl.name.empty()) {
4565 tmpl.name = name;
4566 }
4567 if (tmpl.uri_template.empty()) {
4568 tmpl.uri_template = std::move(name);
4569 }
4570 return add_resource_template(std::move(tmpl));
4571 }
4572
4574 template <class Handler>
4575 Builder& completion(Handler handler) {
4576 server::detail::require_callable(handler, "completion");
4577 if constexpr (server::detail::is_typed_completion_handler_v<Handler>) {
4578 server::detail::require_unambiguous_completion_handler<Handler>();
4579 } else {
4580 server::detail::require_unambiguous_json_extension_handler<Handler>();
4581 }
4582 builder_.on_completion([handler = std::move(handler)](
4583 const protocol::Json& request,
4584 const server::SessionContext& context,
4585 CancellationToken cancellation) mutable
4587 if constexpr (server::detail::is_typed_completion_handler_v<Handler>) {
4588 const auto params = protocol::complete_params_from_json(request);
4589 if (!params) {
4590 return mcp::core::unexpected(core::Error{
4591 static_cast<int>(protocol::ErrorCode::InvalidParams),
4592 params.error().message, params.error().detail, "protocol"});
4593 }
4594 server::CompletionContext completion_context;
4595 static_cast<server::SessionContext&>(completion_context) = context;
4596 completion_context.params = *params;
4597 completion_context.cancellation = cancellation;
4598 auto handled = server::detail::invoke_completion_handler(
4599 handler, completion_context);
4600 return server::detail::completion_response_to_json(std::move(handled));
4601 } else {
4602 auto handled = server::detail::invoke_json_extension_handler(
4603 handler, request, context, cancellation);
4604 if constexpr (server::detail::is_result<decltype(handled)>::value) {
4605 return handled;
4606 } else {
4607 return server::detail::value_to_json(std::move(handled));
4608 }
4609 }
4610 });
4611 return *this;
4612 }
4613
4615 template <class Handler>
4616 Builder& sampling(Handler handler) {
4617 server::detail::require_callable(handler, "sampling");
4618 server::detail::require_unambiguous_json_extension_handler<Handler>();
4619 builder_.on_sampling(
4620 [handler = std::move(handler)](const protocol::Json& request,
4621 const server::SessionContext& context,
4622 CancellationToken cancellation) mutable
4624 auto handled = server::detail::invoke_json_extension_handler(
4625 handler, request, context, cancellation);
4626 if constexpr (server::detail::is_result<decltype(handled)>::value) {
4627 return handled;
4628 } else {
4629 return server::detail::value_to_json(std::move(handled));
4630 }
4631 });
4632 return *this;
4633 }
4634
4636 template <class Handler>
4637 Builder& logging(Handler handler) {
4638 server::detail::require_callable(handler, "logging");
4639 builder_.on_logging([handler = std::move(handler)](
4640 std::string_view level, std::string_view message) {
4641 handler(level, message);
4642 });
4643 return *this;
4644 }
4645
4647 template <class Handler>
4648 Builder& raw_request(Handler handler) {
4649 server::detail::require_callable(handler, "raw_request");
4650 builder_.on_raw_request([handler = std::move(handler)](
4651 const protocol::JsonRpcRequest& request,
4652 const server::SessionContext& /*context*/)
4653 -> std::optional<protocol::JsonRpcResponse> {
4654 if constexpr (std::is_same_v<std::decay_t<decltype(handler(request))>,
4655 std::optional<protocol::JsonRpcResponse>>) {
4656 return handler(request);
4657 } else if constexpr (std::is_same_v<
4658 std::decay_t<decltype(handler(request))>,
4660 return handler(request);
4661 } else {
4662 handler(request);
4663 return std::nullopt;
4664 }
4665 });
4666 return *this;
4667 }
4668
4669 template <class Router>
4670 Builder& router(const Router& router) {
4671 router.apply_to(builder_);
4672 return *this;
4673 }
4674
4675 template <class Args, class Result, class Handler>
4676 Builder& tool(
4678 return add_tool(
4679 std::move(registration.definition),
4680 [handler = std::move(registration.handler)](
4681 const server::ToolContext& context)
4683 try {
4684 auto args =
4685 server::detail::argument_from_json<Args>(context.arguments);
4686 auto handled = server::detail::invoke_tool_handler(
4687 handler, std::move(args), context);
4688 if constexpr (server::detail::is_result<decltype(handled)>::value) {
4689 if (!handled) {
4690 return mcp::core::unexpected(handled.error());
4691 }
4692 return server::detail::value_to_tool_result(*handled);
4693 } else {
4694 return server::detail::value_to_tool_result(std::move(handled));
4695 }
4696 } catch (const std::exception& exception) {
4697 return mcp::core::unexpected(core::Error{
4698 static_cast<int>(protocol::ErrorCode::InvalidParams),
4699 "failed to decode tool arguments",
4700 exception.what(),
4701 });
4702 }
4703 });
4704 }
4705
4706 Builder& on_completion(server::Server::JsonHandler handler) {
4707 builder_.on_completion(std::move(handler));
4708 return *this;
4709 }
4710
4711 Builder& on_completion(server::Server::JsonContextHandler handler) {
4712 builder_.on_completion(std::move(handler));
4713 return *this;
4714 }
4715
4716 Builder& on_completion(server::Server::JsonRequestContextHandler handler) {
4717 builder_.on_completion(std::move(handler));
4718 return *this;
4719 }
4720
4721 Builder& on_sampling(server::Server::JsonHandler handler) {
4722 builder_.on_sampling(std::move(handler));
4723 return *this;
4724 }
4725
4726 Builder& on_sampling(server::Server::JsonContextHandler handler) {
4727 builder_.on_sampling(std::move(handler));
4728 return *this;
4729 }
4730
4731 Builder& on_sampling(server::Server::JsonRequestContextHandler handler) {
4732 builder_.on_sampling(std::move(handler));
4733 return *this;
4734 }
4735
4736 Builder& on_logging(server::Server::LoggingHandler handler) {
4737 builder_.on_logging(std::move(handler));
4738 return *this;
4739 }
4740
4741 Builder& on_raw_request(server::Server::RawRequestHandler handler) {
4742 builder_.on_raw_request(std::move(handler));
4743 return *this;
4744 }
4745
4746 Builder& on_raw_notification(server::Server::RawNotificationHandler handler) {
4747 builder_.on_raw_notification(std::move(handler));
4748 return *this;
4749 }
4750
4751 Builder& on_task_list(server::Server::TaskListHandler handler) {
4752 builder_.on_task_list(std::move(handler));
4753 return *this;
4754 }
4755
4756 Builder& on_tools_list(server::Server::ToolsListHandler handler) {
4757 builder_.on_tools_list(std::move(handler));
4758 return *this;
4759 }
4760
4761 Builder& on_prompts_list(server::Server::PromptsListHandler handler) {
4762 builder_.on_prompts_list(std::move(handler));
4763 return *this;
4764 }
4765
4766 Builder& on_resources_list(server::Server::ResourcesListHandler handler) {
4767 builder_.on_resources_list(std::move(handler));
4768 return *this;
4769 }
4770
4771 Builder& on_resource_templates_list(
4772 server::Server::ResourceTemplatesListHandler handler) {
4773 builder_.on_resource_templates_list(std::move(handler));
4774 return *this;
4775 }
4776
4777 Builder& on_task_get(server::Server::TaskGetHandler handler) {
4778 builder_.on_task_get(std::move(handler));
4779 return *this;
4780 }
4781
4782 Builder& on_task_cancel(server::Server::TaskCancelHandler handler) {
4783 builder_.on_task_cancel(std::move(handler));
4784 return *this;
4785 }
4786
4787 Builder& on_task_result(server::Server::TaskResultHandler handler) {
4788 builder_.on_task_result(std::move(handler));
4789 return *this;
4790 }
4791
4792 Builder& on_progress(server::Server::ProgressHandler handler) {
4793 builder_.on_progress(std::move(handler));
4794 return *this;
4795 }
4796
4797 Builder& on_roots_list_changed(
4798 server::Server::RootsListChangedHandler handler) {
4799 builder_.on_roots_list_changed(std::move(handler));
4800 return *this;
4801 }
4802
4803 Builder& on_tool_list_changed(server::Server::ListChangedHandler handler) {
4804 builder_.on_tool_list_changed(std::move(handler));
4805 return *this;
4806 }
4807
4808 Builder& on_prompt_list_changed(server::Server::ListChangedHandler handler) {
4809 builder_.on_prompt_list_changed(std::move(handler));
4810 return *this;
4811 }
4812
4813 Builder& on_resource_list_changed(
4814 server::Server::ListChangedHandler handler) {
4815 builder_.on_resource_list_changed(std::move(handler));
4816 return *this;
4817 }
4818
4819 Builder& on_resource_updated(server::Server::ResourceUpdatedHandler handler) {
4820 builder_.on_resource_updated(std::move(handler));
4821 return *this;
4822 }
4823
4824 Builder& handler(server::ServerHandler handler) {
4825 builder_.with_handler(std::move(handler));
4826 return *this;
4827 }
4828
4829 Builder& handler(const server::ServerHandlerInterface& handler) {
4830 builder_.with_handler(handler);
4831 return *this;
4832 }
4833
4834 core::Result<Peer> build() {
4835 auto server = builder_.build();
4836 if (!server) {
4837 return mcp::core::unexpected(server.error());
4838 }
4839
4840 // Capture handlers from the Server before moving it into the Peer.
4841 // The Peer's own handler fields must be populated so that
4842 // ServerPeer::handle_request() dispatches through them.
4843 auto raw_handler = (*server)->raw_request_handler();
4844 auto raw_context_handler = (*server)->raw_request_context_handler();
4845
4846 Peer peer(std::move(*server));
4847 if (raw_handler) {
4848 peer.raw_request_handler_ = std::move(raw_handler);
4849 }
4850 if (raw_context_handler) {
4851 peer.raw_request_context_handler_ = std::move(raw_context_handler);
4852 }
4853 for (auto& transport : native_transports_) {
4854 auto added = peer.add_transport(std::move(transport));
4855 if (!added) {
4856 return mcp::core::unexpected(added.error());
4857 }
4858 }
4859 native_transports_.clear();
4860 return peer;
4861 }
4862
4865 int run();
4866
4867 private:
4868 server::ServerBuilder builder_;
4869 std::vector<std::unique_ptr<transport::ServerTransport>> native_transports_;
4870};
4871
4873 return Builder{};
4874}
4875
4878
4879} // 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:116
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
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:4343
Builder & logging(Handler handler)
Registers a logging handler.
Definition peer.hpp:4637
Builder & resource(std::string name, Handler handler)
Registers a resource with auto-detected handler signature.
Definition peer.hpp:4485
Builder & prompt(std::string name, Handler handler)
Registers a typed prompt with argument decoding.
Definition peer.hpp:4382
Builder & prompt(std::string name, Handler handler)
Registers a prompt with auto-detected handler signature.
Definition peer.hpp:4415
Builder & raw_request(Handler handler)
Registers a raw request handler.
Definition peer.hpp:4648
Builder & tool(protocol::ToolDefinition definition, Handler handler)
Registers a tool with an explicit definition and typed handler.
Definition peer.hpp:4352
Builder & sampling(Handler handler)
Registers a sampling handler.
Definition peer.hpp:4616
Builder & resource_template(std::string name, Handler handler)
Registers a resource template with a callable adapter.
Definition peer.hpp:4544
Builder & completion(Handler handler)
Registers a completion handler.
Definition peer.hpp:4575
Builder & resource(std::string name, Handler handler)
Registers a typed resource with parameter decoding.
Definition peer.hpp:4446
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 sequential receive loop over a role-generic server transport.
Definition peer.hpp:3898
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
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:76
protocol::CompleteParams params
Parsed completion/complete request parameters.
Definition context.hpp:78
CancellationToken cancellation
Cooperative cancellation token for this request.
Definition context.hpp:80
Invocation context passed to prompt handlers.
Definition context.hpp:46
protocol::Json arguments
JSON arguments supplied with the prompt request.
Definition context.hpp:51
Invocation context passed to resource read handlers.
Definition context.hpp:60
std::string uri
Requested resource URI.
Definition context.hpp:65
protocol::Json params
Raw resource read parameters supplied by the client.
Definition context.hpp:67
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:122
Typed tool registration produced by mcp::server::tool().
Definition authoring.hpp:23
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:99
int listen_port
TCP port to listen on. Must be in the range 1..65535.
Definition http_transport.hpp:104
std::string path
HTTP path for POST, GET/SSE, and DELETE session requests.
Definition http_transport.hpp:107
std::string listen_host
Interface address passed to the underlying HTTP server.
Definition http_transport.hpp:101
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.