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
5#include <atomic>
6#include <cstdint>
7#include <exception>
8#include <memory>
9#include <optional>
10#include <string>
11#include <string_view>
12#include <type_traits>
13#include <utility>
14#include <vector>
15
17#include "cxxmcp/error.hpp"
24#include "cxxmcp/request.hpp"
26
30
31namespace mcp::server {
32
47 public:
53 explicit ClientPeer(Transport* transport = nullptr,
54 std::string session_id = {},
55 std::weak_ptr<void> transport_lifetime = {}) noexcept
56 : transport_(transport),
57 session_id_(std::move(session_id)),
58 transport_lifetime_(std::move(transport_lifetime)),
59 has_lifetime_guard_(!transport_lifetime_.expired()) {}
60
62 bool available() const noexcept { return transport() != nullptr; }
63
65 bool supports_roots() const noexcept {
66 const auto* peer_transport = transport();
67 const auto capabilities =
68 peer_transport
69 ? peer_transport->client_capabilities_for_session(session_id_)
70 : std::optional<protocol::ClientCapabilities>{};
71 return capabilities.has_value() && capabilities->roots.enabled;
72 }
73
75 bool supports_sampling_tools() const noexcept {
76 const auto* peer_transport = transport();
77 const auto capabilities =
78 peer_transport
79 ? peer_transport->client_capabilities_for_session(session_id_)
80 : std::optional<protocol::ClientCapabilities>{};
81 return capabilities.has_value() && capabilities->sampling.enabled;
82 }
83
85 bool supports_elicitation_form() const noexcept {
86 const auto* peer_transport = transport();
87 const auto capabilities =
88 peer_transport
89 ? peer_transport->client_capabilities_for_session(session_id_)
90 : std::optional<protocol::ClientCapabilities>{};
91 return capabilities.has_value() && capabilities->elicitation.form;
92 }
93
95 bool supports_elicitation_url() const noexcept {
96 const auto* peer_transport = transport();
97 const auto capabilities =
98 peer_transport
99 ? peer_transport->client_capabilities_for_session(session_id_)
100 : std::optional<protocol::ClientCapabilities>{};
101 return capabilities.has_value() && capabilities->elicitation.url;
102 }
103
105 bool supports_elicitation() const noexcept {
107 }
108
110 bool supports_task_list() const noexcept {
111 const auto* peer_transport = transport();
112 const auto capabilities =
113 peer_transport
114 ? peer_transport->client_capabilities_for_session(session_id_)
115 : std::optional<protocol::ClientCapabilities>{};
116 return capabilities.has_value() && capabilities->tasks.has_value() &&
117 capabilities->tasks->list;
118 }
119
121 bool supports_task_cancel() const noexcept {
122 const auto* peer_transport = transport();
123 const auto capabilities =
124 peer_transport
125 ? peer_transport->client_capabilities_for_session(session_id_)
126 : std::optional<protocol::ClientCapabilities>{};
127 return capabilities.has_value() && capabilities->tasks.has_value() &&
128 capabilities->tasks->cancel;
129 }
130
132 bool supports_tasks() const noexcept {
133 const auto* peer_transport = transport();
134 const auto capabilities =
135 peer_transport
136 ? peer_transport->client_capabilities_for_session(session_id_)
137 : std::optional<protocol::ClientCapabilities>{};
138 return capabilities.has_value() && capabilities->tasks.has_value();
139 }
140
143 std::string reason = {}) const {
144 auto* peer_transport = transport();
145 if (!peer_transport) {
146 return mcp::core::unexpected(
147 core::Error{static_cast<int>(protocol::ErrorCode::InternalError),
148 "client peer is not available",
149 {}});
150 }
151 protocol::CancelledNotificationParams params;
152 params.request_id = std::move(request_id);
153 if (!reason.empty()) {
154 params.reason = std::move(reason);
155 }
156 protocol::JsonRpcNotification notification;
157 notification.method = std::string(protocol::CancelledNotificationMethod);
158 notification.params =
159 protocol::cancelled_notification_params_to_json(params);
160 return peer_transport->send_notification_to_session(
161 session_id_, std::move(notification));
162 }
163
170 std::string method, protocol::Json params = protocol::Json::object(),
171 RequestOptions options = {}) const {
172 const auto request_id = next_request_id();
173 ClientPeer peer = *this;
175 request_id, options.timeout, options.cancellation_token,
176 [peer, request_id](std::string reason) mutable {
177 return peer.notify_cancelled(std::move(request_id),
178 std::move(reason));
179 },
180 [peer, method = std::move(method), params = std::move(params),
181 request_id, options = std::move(options)]() mutable {
182 return peer.request_with_id(std::move(method), std::move(params),
183 std::move(request_id),
184 std::move(options));
185 });
186 }
187
197 std::string method,
198 protocol::Json params = protocol::Json::object()) const {
199 return request_with_id(std::move(method), std::move(params),
200 next_request_id());
201 }
202
207 if (!supports_roots()) {
208 return mcp::core::unexpected(core::Error{
209 static_cast<int>(protocol::ErrorCode::MethodNotFound),
210 "client does not support roots",
211 {},
212 });
213 }
214 auto payload = request(std::string(protocol::RootsListMethod),
215 protocol::Json::object());
216 if (!payload) {
217 return mcp::core::unexpected(payload.error());
218 }
219 auto result = protocol::roots_list_result_from_json(*payload);
220 if (!result) {
221 return mcp::core::unexpected(result.error());
222 }
223 return *result;
224 }
225
228 RequestOptions options = {}) const {
229 const auto request_id = next_request_id();
230 if (!supports_roots()) {
232 request_id, mcp::core::unexpected(core::Error{
233 static_cast<int>(protocol::ErrorCode::MethodNotFound),
234 "client does not support roots",
235 {},
236 }));
237 }
238 ClientPeer peer = *this;
239 return RequestHandle<protocol::RootsListResult>::spawn(
240 request_id, options.timeout, options.cancellation_token,
241 [peer, request_id](std::string reason) mutable {
242 return peer.notify_cancelled(std::move(request_id),
243 std::move(reason));
244 },
245 [peer, request_id,
246 options]() mutable -> core::Result<protocol::RootsListResult> {
247 auto payload = peer.request_with_id(
248 std::string(protocol::RootsListMethod), protocol::Json::object(),
249 request_id, options);
250 if (!payload) {
251 return mcp::core::unexpected(payload.error());
252 }
253 auto result = protocol::roots_list_result_from_json(*payload);
254 if (!result) {
255 return mcp::core::unexpected(result.error());
256 }
257 return *result;
258 });
259 }
260
266 const protocol::CreateMessageParams& params) const {
267 if (!supports_sampling_tools()) {
268 return mcp::core::unexpected(core::Error{
269 static_cast<int>(protocol::ErrorCode::MethodNotFound),
270 "client does not support sampling",
271 {},
272 });
273 }
274 auto payload = request(std::string(protocol::SamplingCreateMessageMethod),
275 protocol::create_message_params_to_json(params));
276 if (!payload) {
277 return mcp::core::unexpected(payload.error());
278 }
279 auto result = protocol::create_message_result_from_json(*payload);
280 if (!result) {
281 return mcp::core::unexpected(result.error());
282 }
283 return *result;
284 }
285
288 const protocol::CreateMessageParams& params,
289 RequestOptions options = {}) const {
290 const auto request_id = next_request_id();
291 if (!supports_sampling_tools()) {
293 request_id, mcp::core::unexpected(core::Error{
294 static_cast<int>(protocol::ErrorCode::MethodNotFound),
295 "client does not support sampling",
296 {},
297 }));
298 }
299 ClientPeer peer = *this;
300 return RequestHandle<protocol::CreateMessageResult>::spawn(
301 request_id, options.timeout, options.cancellation_token,
302 [peer, request_id](std::string reason) mutable {
303 return peer.notify_cancelled(std::move(request_id),
304 std::move(reason));
305 },
306 [peer, params, request_id,
307 options]() mutable -> core::Result<protocol::CreateMessageResult> {
308 auto payload = peer.request_with_id(
309 std::string(protocol::SamplingCreateMessageMethod),
310 protocol::create_message_params_to_json(params), request_id,
311 options);
312 if (!payload) {
313 return mcp::core::unexpected(payload.error());
314 }
315 auto result = protocol::create_message_result_from_json(*payload);
316 if (!result) {
317 return mcp::core::unexpected(result.error());
318 }
319 return *result;
320 });
321 }
322
329 const protocol::CreateElicitationRequestParam& params) const {
330 if (params.mode == protocol::ElicitationMode::Url &&
331 !supports_elicitation_url()) {
332 return mcp::core::unexpected(core::Error{
333 static_cast<int>(protocol::ErrorCode::UrlElicitationRequired),
334 "client does not support url elicitation",
335 {},
336 });
337 }
338 if (params.mode == protocol::ElicitationMode::Form &&
339 !supports_elicitation_form()) {
340 return mcp::core::unexpected(core::Error{
341 static_cast<int>(protocol::ErrorCode::MethodNotFound),
342 "client does not support elicitation",
343 {},
344 });
345 }
346 auto payload =
347 request(std::string(protocol::ElicitationCreateMethod),
348 protocol::create_elicitation_request_param_to_json(params));
349 if (!payload) {
350 return mcp::core::unexpected(payload.error());
351 }
352 auto result = protocol::create_elicitation_result_from_json(*payload);
353 if (!result) {
354 return mcp::core::unexpected(result.error());
355 }
356 return *result;
357 }
358
362 RequestOptions options = {}) const {
363 const auto request_id = next_request_id();
364 if (params.mode == protocol::ElicitationMode::Url &&
365 !supports_elicitation_url()) {
367 request_id,
368 mcp::core::unexpected(core::Error{
369 static_cast<int>(protocol::ErrorCode::UrlElicitationRequired),
370 "client does not support url elicitation",
371 {},
372 }));
373 }
374 if (params.mode == protocol::ElicitationMode::Form &&
375 !supports_elicitation_form()) {
376 return RequestHandle<protocol::CreateElicitationResult>::ready(
377 request_id, mcp::core::unexpected(core::Error{
378 static_cast<int>(protocol::ErrorCode::MethodNotFound),
379 "client does not support elicitation",
380 {},
381 }));
382 }
383 ClientPeer peer = *this;
384 return RequestHandle<protocol::CreateElicitationResult>::spawn(
385 request_id, options.timeout, options.cancellation_token,
386 [peer, request_id](std::string reason) mutable {
387 return peer.notify_cancelled(std::move(request_id),
388 std::move(reason));
389 },
390 [peer, params, request_id,
391 options]() mutable -> core::Result<protocol::CreateElicitationResult> {
392 auto payload = peer.request_with_id(
393 std::string(protocol::ElicitationCreateMethod),
394 protocol::create_elicitation_request_param_to_json(params),
395 request_id, options);
396 if (!payload) {
397 return mcp::core::unexpected(payload.error());
398 }
399 auto result = protocol::create_elicitation_result_from_json(*payload);
400 if (!result) {
401 return mcp::core::unexpected(result.error());
402 }
403 return *result;
404 });
405 }
406
412 template <class T>
413 core::Result<T> elicit(std::string message,
414 RequestOptions options = {}) const {
415 auto handle = elicit_async<T>(std::move(message), std::move(options));
416 return handle.await_response();
417 }
418
420 template <class T>
421 core::Result<T> elicit(std::string message,
423 RequestOptions options = {}) const {
424 auto handle = elicit_async<T>(std::move(message), std::move(schema),
425 std::move(options));
426 return handle.await_response();
427 }
428
430 template <class T>
431 RequestHandle<T> elicit_async(std::string message,
432 RequestOptions options = {}) const {
433 auto schema = elicitation_schema_for_value<T>();
434 if (!schema) {
435 return RequestHandle<T>::ready(next_request_id(),
436 mcp::core::unexpected(schema.error()));
437 }
438 return elicit_async<T>(std::move(message), std::move(schema->schema),
439 std::move(options), schema->wrapped_value);
440 }
441
443 template <class T>
444 RequestHandle<T> elicit_async(std::string message,
446 RequestOptions options = {}) const {
447 return elicit_async<T>(std::move(message), std::move(schema),
448 std::move(options), false);
449 }
450
453 std::string elicitation_id,
454 std::string url,
455 RequestOptions options = {}) const {
456 auto handle =
457 elicit_url_async(std::move(message), std::move(elicitation_id),
458 std::move(url), std::move(options));
459 return handle.await_response();
460 }
461
464 std::string message, std::string elicitation_id, std::string url,
465 RequestOptions options = {}) const {
467 params.message = std::move(message);
468 params.mode = protocol::ElicitationMode::Url;
469 params.elicitation_id = std::move(elicitation_id);
470 params.url = std::move(url);
471 return elicit_url_async(std::move(params), std::move(options));
472 }
473
478 if (!supports_task_list()) {
479 return mcp::core::unexpected(core::Error{
480 static_cast<int>(protocol::ErrorCode::MethodNotFound),
481 "client does not support task listing",
482 {},
483 });
484 }
485 auto payload = request(std::string(protocol::TasksListMethod),
486 protocol::Json::object());
487 if (!payload) {
488 return mcp::core::unexpected(payload.error());
489 }
490 const auto tasks = protocol::task_list_result_from_json(*payload);
491 if (!tasks) {
492 return mcp::core::unexpected(tasks.error());
493 }
494 return tasks->tasks;
495 }
496
501 if (!supports_task_list()) {
502 return mcp::core::unexpected(core::Error{
503 static_cast<int>(protocol::ErrorCode::MethodNotFound),
504 "client does not support task listing",
505 {},
506 });
507 }
508 std::vector<protocol::Task> all;
509 std::optional<std::string> cursor;
510 do {
511 auto payload =
512 request(std::string(protocol::TasksListMethod),
513 cursor.has_value() ? protocol::Json{{"cursor", *cursor}}
514 : protocol::Json::object());
515 if (!payload) {
516 return mcp::core::unexpected(payload.error());
517 }
518 const auto page = protocol::task_list_result_from_json(*payload);
519 if (!page) {
520 return mcp::core::unexpected(page.error());
521 }
522 all.insert(all.end(), page->tasks.begin(), page->tasks.end());
523 cursor = page->next_cursor;
524 } while (cursor.has_value() && !cursor->empty());
525 return all;
526 }
527
532 core::Result<protocol::Task> get_task(std::string_view task_id) const {
533 if (!supports_tasks()) {
534 return mcp::core::unexpected(core::Error{
535 static_cast<int>(protocol::ErrorCode::MethodNotFound),
536 "client does not support tasks",
537 {},
538 });
539 }
541 params.task_id = std::string(task_id);
542 auto payload = request(std::string(protocol::TasksGetMethod),
543 protocol::task_get_params_to_json(params));
544 if (!payload) {
545 return mcp::core::unexpected(payload.error());
546 }
547 const auto task = protocol::task_from_json(*payload);
548 if (!task) {
549 return mcp::core::unexpected(task.error());
550 }
551 return *task;
552 }
553
557 core::Result<protocol::Task> cancel_task(std::string_view task_id) const {
558 if (!supports_task_cancel()) {
559 return mcp::core::unexpected(core::Error{
560 static_cast<int>(protocol::ErrorCode::MethodNotFound),
561 "client does not support task cancellation",
562 {},
563 });
564 }
566 params.task_id = std::string(task_id);
567 auto payload = request(std::string(protocol::TasksCancelMethod),
568 protocol::task_cancel_params_to_json(params));
569 if (!payload) {
570 return mcp::core::unexpected(payload.error());
571 }
572 const auto task = protocol::task_from_json(*payload);
573 if (!task) {
574 return mcp::core::unexpected(task.error());
575 }
576 return *task;
577 }
578
582 core::Result<protocol::Json> task_result(std::string_view task_id) const {
583 if (!supports_tasks()) {
584 return mcp::core::unexpected(core::Error{
585 static_cast<int>(protocol::ErrorCode::MethodNotFound),
586 "client does not support tasks",
587 {},
588 });
589 }
591 params.task_id = std::string(task_id);
592 return request(std::string(protocol::TasksResultMethod),
593 protocol::task_result_params_to_json(params));
594 }
595
601 std::string elicitation_id) const {
602 auto* peer_transport = transport();
603 if (!peer_transport) {
604 return mcp::core::unexpected(
605 core::Error{static_cast<int>(protocol::ErrorCode::InternalError),
606 "client peer is not available",
607 {}});
608 }
610 params.elicitation_id = std::move(elicitation_id);
612 notification.method =
613 std::string(protocol::ElicitationCompleteNotificationMethod);
614 notification.params =
615 protocol::elicitation_complete_notification_params_to_json(params);
616 return peer_transport->send_notification_to_session(
617 session_id_, std::move(notification));
618 }
619
620 private:
621 struct ElicitationSchemaBinding {
623 bool wrapped_value = false;
624 };
625
626 static core::Error elicitation_declined_error() {
627 return errors::make(protocol::ErrorCode::InvalidRequest,
628 "elicitation declined", {}, "elicitation");
629 }
630
631 static core::Error elicitation_cancelled_error() {
632 return errors::make(protocol::ErrorCode::InternalError,
633 "elicitation cancelled", {}, "elicitation");
634 }
635
636 static core::Error elicitation_decode_error(std::string detail = {}) {
637 return errors::make(protocol::ErrorCode::InvalidParams,
638 "elicitation content could not be decoded",
639 std::move(detail), "elicitation");
640 }
641
642 static core::Error elicitation_missing_content_error() {
643 return errors::make(protocol::ErrorCode::InvalidParams,
644 "elicitation accepted without content", {},
645 "elicitation");
646 }
647
648 template <class T>
649 static core::Result<ElicitationSchemaBinding> elicitation_schema_for_value() {
650 protocol::Json schema_json = protocol::schema_for<T>();
651 bool wrapped_value = true;
652 if (schema_json.is_object() && schema_json.contains("type") &&
653 schema_json.at("type").is_string() &&
654 schema_json.at("type").get<std::string>() == "object" &&
655 schema_json.contains("properties") &&
656 schema_json.at("properties").is_object()) {
657 wrapped_value = false;
658 } else {
659 schema_json = protocol::object_schema()
660 .required_property("value", std::move(schema_json))
661 .additional_properties(false)
662 .build();
663 }
664
665 auto parsed = protocol::elicitation_schema_from_json(schema_json);
666 if (!parsed) {
667 return mcp::core::unexpected(errors::make(
668 protocol::ErrorCode::InvalidParams, parsed.error().message,
669 parsed.error().detail, "elicitation"));
670 }
671 return ElicitationSchemaBinding{*parsed, wrapped_value};
672 }
673
674 template <class T>
675 static core::Result<T> decode_elicitation_value(
676 const protocol::CreateElicitationResult& result,
677 const protocol::ElicitationSchema& schema, bool wrapped_value) {
678 if (result.action == protocol::ElicitationAction::Decline) {
679 return mcp::core::unexpected(elicitation_declined_error());
680 }
681 if (result.action == protocol::ElicitationAction::Cancel) {
682 return mcp::core::unexpected(elicitation_cancelled_error());
683 }
684 if (!result.content.has_value()) {
685 return mcp::core::unexpected(elicitation_missing_content_error());
686 }
687
688 const auto valid =
689 protocol::validate_elicitation_result_content(schema, result);
690 if (!valid) {
692 errors::make(protocol::ErrorCode::InvalidParams,
693 "elicitation content failed schema validation",
694 valid.error().message, "elicitation"));
695 }
696
697 try {
698 if (wrapped_value) {
699 return result.content->at("value").template get<T>();
700 }
701 return result.content->template get<T>();
702 } catch (const std::exception& ex) {
703 return mcp::core::unexpected(elicitation_decode_error(ex.what()));
704 } catch (...) {
705 return mcp::core::unexpected(elicitation_decode_error());
706 }
707 }
708
709 template <class T>
710 RequestHandle<T> elicit_async(std::string message,
711 protocol::ElicitationSchema schema,
712 RequestOptions options,
713 bool wrapped_value) const {
714 const auto request_id = next_request_id();
715 if (!supports_elicitation_form()) {
716 return RequestHandle<T>::ready(
717 request_id,
718 mcp::core::unexpected(errors::make(
719 protocol::ErrorCode::MethodNotFound,
720 "client does not support elicitation", {}, "elicitation")));
721 }
722
723 protocol::CreateElicitationRequestParam params;
724 params.message = std::move(message);
725 params.requested_schema = std::move(schema);
726
727 ClientPeer peer = *this;
728 auto validation_schema = params.requested_schema;
729 return RequestHandle<T>::spawn(
730 request_id, options.timeout, options.cancellation_token,
731 [peer, request_id](std::string reason) mutable {
732 return peer.notify_cancelled(std::move(request_id),
733 std::move(reason));
734 },
735 [peer, params = std::move(params), request_id,
736 options = std::move(options),
737 validation_schema = std::move(validation_schema),
738 wrapped_value]() mutable -> core::Result<T> {
739 auto payload = peer.request_with_id(
740 std::string(protocol::ElicitationCreateMethod),
741 protocol::create_elicitation_request_param_to_json(params),
742 request_id, options);
743 if (!payload) {
744 return mcp::core::unexpected(payload.error());
745 }
746 auto result = protocol::create_elicitation_result_from_json(*payload);
747 if (!result) {
748 return mcp::core::unexpected(result.error());
749 }
750 return decode_elicitation_value<T>(*result, validation_schema,
751 wrapped_value);
752 });
753 }
754
755 RequestHandle<core::Unit> elicit_url_async(
756 protocol::CreateElicitationRequestParam params,
757 RequestOptions options = {}) const {
758 const auto request_id = next_request_id();
759 if (!supports_elicitation_url()) {
760 return RequestHandle<core::Unit>::ready(
761 request_id,
762 mcp::core::unexpected(errors::make(
763 protocol::ErrorCode::UrlElicitationRequired,
764 "client does not support url elicitation", {}, "elicitation")));
765 }
766
767 ClientPeer peer = *this;
768 return RequestHandle<core::Unit>::spawn(
769 request_id, options.timeout, options.cancellation_token,
770 [peer, request_id](std::string reason) mutable {
771 return peer.notify_cancelled(std::move(request_id),
772 std::move(reason));
773 },
774 [peer, params = std::move(params), request_id,
775 options = std::move(options)]() mutable -> core::Result<core::Unit> {
776 auto payload = peer.request_with_id(
777 std::string(protocol::ElicitationCreateMethod),
778 protocol::create_elicitation_request_param_to_json(params),
779 request_id, options);
780 if (!payload) {
781 return mcp::core::unexpected(payload.error());
782 }
783 auto result = protocol::create_elicitation_result_from_json(*payload);
784 if (!result) {
785 return mcp::core::unexpected(result.error());
786 }
787 if (result->action == protocol::ElicitationAction::Decline) {
788 return mcp::core::unexpected(elicitation_declined_error());
789 }
790 if (result->action == protocol::ElicitationAction::Cancel) {
791 return mcp::core::unexpected(elicitation_cancelled_error());
792 }
793 return core::Unit{};
794 });
795 }
796
797 core::Result<protocol::Json> request_with_id(
798 std::string method, protocol::Json params, protocol::RequestId request_id,
799 RequestOptions options = {}) const {
800 auto* peer_transport = transport();
801 if (!peer_transport) {
802 return mcp::core::unexpected(core::Error{
803 static_cast<int>(protocol::ErrorCode::InternalError),
804 "client peer is not available",
805 {},
806 });
807 }
808
809 protocol::JsonRpcRequest request;
810 request.method = std::move(method);
811 request.params = std::move(params);
812 request.id = std::move(request_id);
813 if (options.meta.has_value()) {
814 request.meta = std::move(options.meta);
815 }
816
817 auto response = peer_transport->send_request_to_session(session_id_,
818 std::move(request));
819 if (!response) {
820 return mcp::core::unexpected(response.error());
821 }
822 if (response->error.has_value()) {
823 return mcp::core::unexpected(core::Error{
824 response->error->code,
825 response->error->message,
826 response->error->data.has_value() ? response->error->data->dump()
827 : std::string{},
828 });
829 }
830 if (!response->result.has_value()) {
831 return mcp::core::unexpected(core::Error{
832 static_cast<int>(protocol::ErrorCode::InvalidRequest),
833 "client peer response did not contain a result",
834 {},
835 });
836 }
837 return *response->result;
838 }
839
840 static protocol::RequestId next_request_id() {
841 static std::atomic<std::int64_t> next{1};
842 return next.fetch_add(1);
843 }
844
845 Transport* transport() const noexcept {
846 if (!transport_) {
847 return nullptr;
848 }
849 if (has_lifetime_guard_ && transport_lifetime_.expired()) {
850 return nullptr;
851 }
852 return transport_;
853 }
854
855 Transport* transport_;
856 std::string session_id_;
857 std::weak_ptr<void> transport_lifetime_;
858 bool has_lifetime_guard_ = false;
859};
860
864inline ClientPeer client_peer(const SessionContext& context) noexcept {
865 return ClientPeer(context.transport, context.session_id,
866 context.transport_lifetime);
867}
868
870inline SessionClient session_client(const SessionContext& context) noexcept {
871 return client_peer(context);
872}
873
874inline SessionClient SessionContext::client() const noexcept {
875 return session_client(*this);
876}
877
878} // namespace mcp::server
Definition request.hpp:123
static RequestHandle ready(protocol::RequestId request_id, ResultType result)
Creates a handle whose result is already available.
Definition request.hpp:132
Non-owning handle for the client associated with a server session.
Definition peer.hpp:46
bool available() const noexcept
Report whether this peer has a transport to send through.
Definition peer.hpp:62
core::Result< core::Unit > elicit_url(std::string message, std::string elicitation_id, std::string url, RequestOptions options={}) const
Ask the client to complete a URL-based elicitation flow.
Definition peer.hpp:452
RequestHandle< protocol::RootsListResult > list_roots_async(RequestOptions options={}) const
Request the client's root list asynchronously.
Definition peer.hpp:227
RequestHandle< T > elicit_async(std::string message, protocol::ElicitationSchema schema, RequestOptions options={}) const
Async typed form elicitation using an explicit schema.
Definition peer.hpp:444
RequestHandle< T > elicit_async(std::string message, RequestOptions options={}) const
Async typed form elicitation using SchemaTraits<T>.
Definition peer.hpp:431
bool supports_elicitation_form() const noexcept
Report whether the client advertised form elicitation support.
Definition peer.hpp:85
RequestHandle< protocol::CreateElicitationResult > create_elicitation_async(const protocol::CreateElicitationRequestParam &params, RequestOptions options={}) const
Ask the client to perform an elicitation flow asynchronously.
Definition peer.hpp:360
RequestHandle< protocol::CreateMessageResult > create_message_async(const protocol::CreateMessageParams &params, RequestOptions options={}) const
Ask the client to create a sampled message asynchronously.
Definition peer.hpp:287
bool supports_roots() const noexcept
Report whether the client advertised roots/list support.
Definition peer.hpp:65
core::Result< T > elicit(std::string message, protocol::ElicitationSchema schema, RequestOptions options={}) const
Ask the client for typed form input using an explicit schema.
Definition peer.hpp:421
core::Result< protocol::CreateElicitationResult > create_elicitation(const protocol::CreateElicitationRequestParam &params) const
Ask the client to perform an elicitation flow.
Definition peer.hpp:328
bool supports_elicitation() const noexcept
Report whether the client supports any elicitation mode.
Definition peer.hpp:105
ClientPeer(Transport *transport=nullptr, std::string session_id={}, std::weak_ptr< void > transport_lifetime={}) noexcept
Construct a peer from a borrowed transport pointer.
Definition peer.hpp:53
core::Result< protocol::Task > get_task(std::string_view task_id) const
Request a single task by id.
Definition peer.hpp:532
bool supports_sampling_tools() const noexcept
Report whether the client advertised sampling support.
Definition peer.hpp:75
core::Result< protocol::CreateMessageResult > create_message(const protocol::CreateMessageParams &params) const
Ask the client to create a sampled message.
Definition peer.hpp:265
core::Result< protocol::Json > task_result(std::string_view task_id) const
Request the result payload for a completed client task.
Definition peer.hpp:582
bool supports_elicitation_url() const noexcept
Report whether the client advertised URL elicitation support.
Definition peer.hpp:95
core::Result< std::vector< protocol::Task > > list_tasks() const
Request one page of tasks from the client.
Definition peer.hpp:477
core::Result< std::vector< protocol::Task > > list_all_tasks() const
Request all client task pages by following cursors.
Definition peer.hpp:500
bool supports_tasks() const noexcept
Report whether the client advertised any task support.
Definition peer.hpp:132
core::Result< protocol::Task > cancel_task(std::string_view task_id) const
Request cancellation of a client task.
Definition peer.hpp:557
bool supports_task_list() const noexcept
Report whether the client advertised task listing support.
Definition peer.hpp:110
core::Result< core::Unit > notify_cancelled(protocol::RequestId request_id, std::string reason={}) const
Notify the client that a request was cancelled.
Definition peer.hpp:142
core::Result< T > elicit(std::string message, RequestOptions options={}) const
Ask the client for typed form input using SchemaTraits<T>.
Definition peer.hpp:413
core::Result< protocol::Json > request(std::string method, protocol::Json params=protocol::Json::object()) const
Send a raw JSON-RPC request to the client.
Definition peer.hpp:196
bool supports_task_cancel() const noexcept
Report whether the client advertised task cancellation support.
Definition peer.hpp:121
core::Result< core::Unit > notify_elicitation_complete(std::string elicitation_id) const
Notify the client that an elicitation flow has completed.
Definition peer.hpp:600
RequestHandle< protocol::Json > request_async(std::string method, protocol::Json params=protocol::Json::object(), RequestOptions options={}) const
Send a raw JSON-RPC request to the client and return a handle.
Definition peer.hpp:169
RequestHandle< core::Unit > elicit_url_async(std::string message, std::string elicitation_id, std::string url, RequestOptions options={}) const
Async URL elicitation helper.
Definition peer.hpp:463
core::Result< protocol::RootsListResult > list_roots() const
Request the client's root list.
Definition peer.hpp:206
Abstract server transport for receiving client JSON-RPC messages.
Definition transport.hpp:99
Elicitation request, result, and schema payloads.
Stable public error helpers for SDK request and dispatch paths.
nlohmann::json Json
JSON value type used by all protocol DTOs.
Definition types.hpp:28
std::variant< std::int64_t, std::string > RequestId
JSON-RPC request or response identifier.
Definition types.hpp:56
Request lifecycle helpers for cancellable SDK calls.
Shared result and error primitives used by the public cxxmcp SDK.
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
Client root discovery payloads.
Client-side model sampling request and response payloads.
Small JSON Schema builders for MCP tool and elicitation metadata.
JSON-RPC method names and message construction/parsing helpers.
SessionClient session_client(const SessionContext &context) noexcept
Create a SessionClient handle from a session context.
Definition peer.hpp:870
ClientPeer client_peer(const SessionContext &context) noexcept
Create a ClientPeer handle from a session context.
Definition peer.hpp:864
Server-side transport abstraction for MCP JSON-RPC traffic.
Options for an outbound SDK request.
Definition request.hpp:103
Structured error returned by fallible SDK operations.
Definition result.hpp:35
Parameters for elicitation/create.
Definition elicitation.hpp:335
std::optional< std::string > url
Required in URL mode; target URL for the external interaction.
Definition elicitation.hpp:344
std::optional< std::string > elicitation_id
Required in URL mode to correlate the later completion notification.
Definition elicitation.hpp:342
std::string message
User-facing message explaining what input is requested.
Definition elicitation.hpp:337
ElicitationMode mode
Interaction mode; form mode serializes requested_schema, URL mode serializes elicitation_id and url.
Definition elicitation.hpp:340
Parameters for sampling/createMessage.
Definition sampling.hpp:231
Parameters for notifications/elicitation/complete.
Definition elicitation.hpp:370
std::string elicitation_id
URL-mode elicitation id that has completed.
Definition elicitation.hpp:372
Object schema requested from a user in form elicitation.
Definition elicitation.hpp:144
JSON-RPC notification envelope for one-way MCP messages.
Definition types.hpp:137
Json params
Method-specific notification params object.
Definition types.hpp:141
std::string method
Notification method name such as notifications/progress.
Definition types.hpp:139
Parameters for tasks/cancel.
Definition task.hpp:234
std::string task_id
Task identifier to cancel.
Definition task.hpp:236
Parameters for tasks/get.
Definition task.hpp:209
std::string task_id
Task identifier to retrieve.
Definition task.hpp:211
Per-message connection metadata supplied to server handlers.
Definition transport.hpp:42
Asynchronous task status and task-management payloads.