cxxmcp 1.1.6
C++ MCP SDK
Loading...
Searching...
No Matches
elicitation.hpp
Go to the documentation of this file.
1// Copyright (c) 2025 [caomengxuan666]
2
3#pragma once
4
12
13#include <algorithm>
14#include <cstddef>
15#include <cstdint>
16#include <map>
17#include <optional>
18#include <string>
19#include <string_view>
20#include <type_traits>
21#include <utility>
22#include <variant>
23#include <vector>
24
28
29namespace mcp::protocol {
30
34 Accept,
36 Decline,
38 Cancel,
39};
40
42enum class ElicitationMode {
44 Form,
46 Url,
47};
48
52 std::optional<std::string> title;
54 std::optional<std::string> description;
56 std::optional<std::string> format;
58 std::optional<std::int64_t> min_length;
60 std::optional<std::int64_t> max_length;
62 std::optional<std::string> default_value;
64 Json extensions = Json::object();
65};
66
70 std::optional<std::string> title;
72 std::optional<std::string> description;
74 std::optional<double> minimum;
76 std::optional<double> maximum;
78 std::optional<double> default_value;
80 Json extensions = Json::object();
81};
82
86 std::optional<std::string> title;
88 std::optional<std::string> description;
90 std::optional<std::int64_t> minimum;
92 std::optional<std::int64_t> maximum;
94 std::optional<std::int64_t> default_value;
96 Json extensions = Json::object();
97};
98
102 std::optional<std::string> title;
104 std::optional<std::string> description;
106 std::optional<bool> default_value;
108 Json extensions = Json::object();
109};
110
114 std::optional<std::string> title;
116 std::optional<std::string> description;
118 std::vector<std::string> values;
120 std::vector<std::string> enum_names;
122 std::vector<std::string> value_titles;
126 bool multi_select = false;
128 std::optional<std::int64_t> min_items;
130 std::optional<std::int64_t> max_items;
132 std::optional<std::string> default_value;
134 std::vector<std::string> default_values;
136 Json extensions = Json::object();
137};
138
142
146 std::optional<std::string> title;
148 std::optional<std::string> description;
150 std::map<std::string, PrimitiveSchema> properties;
152 std::vector<std::string> required;
154 Json extensions = Json::object();
155
158 std::optional<std::string> title = std::nullopt,
159 std::optional<std::string> description = std::nullopt,
160 std::optional<std::string> format = std::nullopt,
161 std::optional<std::int64_t> min_length = std::nullopt,
162 std::optional<std::int64_t> max_length = std::nullopt,
163 std::optional<std::string> default_value = std::nullopt) {
164 StringSchema schema;
165 schema.title = std::move(title);
166 schema.description = std::move(description);
167 schema.format = std::move(format);
168 schema.min_length = min_length;
169 schema.max_length = max_length;
170 schema.default_value = std::move(default_value);
171 return schema;
172 }
173
176 std::optional<std::string> title = std::nullopt,
177 std::optional<std::string> description = std::nullopt,
178 std::optional<double> minimum = std::nullopt,
179 std::optional<double> maximum = std::nullopt,
180 std::optional<double> default_value = std::nullopt) {
181 NumberSchema schema;
182 schema.title = std::move(title);
183 schema.description = std::move(description);
184 schema.minimum = minimum;
185 schema.maximum = maximum;
186 schema.default_value = default_value;
187 return schema;
188 }
189
192 std::optional<std::string> title = std::nullopt,
193 std::optional<std::string> description = std::nullopt,
194 std::optional<std::int64_t> minimum = std::nullopt,
195 std::optional<std::int64_t> maximum = std::nullopt,
196 std::optional<std::int64_t> default_value = std::nullopt) {
197 IntegerSchema schema;
198 schema.title = std::move(title);
199 schema.description = std::move(description);
200 schema.minimum = minimum;
201 schema.maximum = maximum;
202 schema.default_value = default_value;
203 return schema;
204 }
205
208 std::optional<std::string> title = std::nullopt,
209 std::optional<std::string> description = std::nullopt,
210 std::optional<bool> default_value = std::nullopt) {
211 BooleanSchema schema;
212 schema.title = std::move(title);
213 schema.description = std::move(description);
214 schema.default_value = default_value;
215 return schema;
216 }
217
220 std::vector<std::string> values,
221 std::optional<std::string> title = std::nullopt,
222 std::optional<std::string> description = std::nullopt,
223 std::optional<std::string> default_value = std::nullopt) {
224 EnumSchema schema;
225 schema.values = std::move(values);
226 schema.title = std::move(title);
227 schema.description = std::move(description);
228 schema.default_value = std::move(default_value);
229 return schema;
230 }
231
237 class Builder {
238 public:
240 Builder& title(std::string value) {
241 title_ = std::move(value);
242 return *this;
243 }
244
246 Builder& description(std::string value) {
247 description_ = std::move(value);
248 return *this;
249 }
250
253 std::string name,
254 std::optional<std::string> default_value = std::nullopt,
255 std::optional<std::string> format = std::nullopt);
258 std::string name,
259 std::optional<std::string> default_value = std::nullopt,
260 std::optional<std::string> format = std::nullopt);
263 std::string name, std::optional<double> minimum = std::nullopt,
264 std::optional<double> maximum = std::nullopt,
265 std::optional<double> default_value = std::nullopt);
268 std::string name, std::optional<double> minimum = std::nullopt,
269 std::optional<double> maximum = std::nullopt,
270 std::optional<double> default_value = std::nullopt);
273 std::string name, std::optional<std::int64_t> minimum = std::nullopt,
274 std::optional<std::int64_t> maximum = std::nullopt,
275 std::optional<std::int64_t> default_value = std::nullopt);
278 std::string name, std::optional<std::int64_t> minimum = std::nullopt,
279 std::optional<std::int64_t> maximum = std::nullopt,
280 std::optional<std::int64_t> default_value = std::nullopt);
282 Builder& required_bool(std::string name,
283 std::optional<bool> default_value = std::nullopt);
285 Builder& optional_bool(std::string name,
286 std::optional<bool> default_value = std::nullopt);
289 std::string name,
290 std::optional<std::string> default_value = std::nullopt);
293 std::string name,
294 std::optional<std::string> default_value = std::nullopt);
297 std::string name, std::vector<std::string> values,
298 std::optional<std::string> default_value = std::nullopt);
301 std::string name, std::vector<std::string> values,
302 std::optional<std::string> default_value = std::nullopt);
303
305 Builder& required_property(std::string name, PrimitiveSchema schema) {
306 return add_required(std::move(name), std::move(schema));
307 }
308
310 Builder& optional_property(std::string name, PrimitiveSchema schema) {
311 return add_optional(std::move(name), std::move(schema));
312 }
313
317
318 private:
319 static bool validate_name(const std::string& name);
320
321 Builder& add_required(std::string name, PrimitiveSchema schema);
322 Builder& add_optional(std::string name, PrimitiveSchema schema);
323
324 std::optional<std::string> title_;
325 std::optional<std::string> description_;
326 std::map<std::string, PrimitiveSchema> properties_;
327 std::vector<std::string> required_;
328 };
329
331 static Builder builder() { return Builder{}; }
332};
333
337 std::string message;
340 ElicitationMode mode = ElicitationMode::Form;
342 std::optional<std::string> elicitation_id;
344 std::optional<std::string> url;
348 std::optional<Json> request_state;
350 std::optional<TaskRequestParameters> task;
352 std::optional<Json> meta;
354 Json extensions = Json::object();
355};
356
360 ElicitationAction action = ElicitationAction::Cancel;
362 std::optional<Json> content;
364 std::optional<Json> meta;
366 Json extensions = Json::object();
367};
368
372 std::string elicitation_id;
374 std::optional<Json> meta;
376 Json extensions = Json::object();
377};
378
379template <>
381 static constexpr bool defined = true;
382 static auto fields() {
383 return std::make_tuple(
384 field("elicitationId",
388 {"elicitationId", "_meta"}));
389 }
390 static std::vector<std::string> known_keys() {
391 return {"elicitationId", "_meta"};
392 }
393};
394
397 switch (action) {
398 case ElicitationAction::Accept:
399 return "accept";
400 case ElicitationAction::Decline:
401 return "decline";
402 case ElicitationAction::Cancel:
403 return "cancel";
404 }
405 return "cancel";
406}
407
410inline std::optional<ElicitationAction> elicitation_action_from_string(
411 const std::string& value) {
412 if (value == "accept") {
413 return ElicitationAction::Accept;
414 }
415 if (value == "decline") {
416 return ElicitationAction::Decline;
417 }
418 if (value == "cancel") {
419 return ElicitationAction::Cancel;
420 }
421 return std::nullopt;
422}
423
424template <>
426 static void serialize(Json& json, const char* key, ElicitationAction value) {
427 json[key] = elicitation_action_to_string(value);
428 }
429 static bool deserialize(const Json& json, const char* key,
430 ElicitationAction& target) {
431 if (!json.contains(key) || !json.at(key).is_string()) {
432 return false;
433 }
434 auto val = elicitation_action_from_string(json.at(key).get<std::string>());
435 if (!val.has_value()) {
436 return false;
437 }
438 target = *val;
439 return true;
440 }
441};
442
445 switch (mode) {
446 case ElicitationMode::Form:
447 return "form";
448 case ElicitationMode::Url:
449 return "url";
450 }
451 return "form";
452}
453
456inline std::optional<ElicitationMode> elicitation_mode_from_string(
457 const std::string& value) {
458 if (value == "form") {
459 return ElicitationMode::Form;
460 }
461 if (value == "url") {
462 return ElicitationMode::Url;
463 }
464 return std::nullopt;
465}
466
470 const std::string& value) noexcept {
471 return value == "email" || value == "uri" || value == "date" ||
472 value == "date-time";
473}
474
477 Json json = Json::object();
478 json["type"] = "string";
479 if (schema.title.has_value()) {
480 json["title"] = *schema.title;
481 }
482 if (schema.description.has_value()) {
483 json["description"] = *schema.description;
484 }
485 if (schema.format.has_value()) {
486 json["format"] = *schema.format;
487 }
488 if (schema.min_length.has_value()) {
489 json["minLength"] = *schema.min_length;
490 }
491 if (schema.max_length.has_value()) {
492 json["maxLength"] = *schema.max_length;
493 }
494 if (schema.default_value.has_value()) {
495 json["default"] = *schema.default_value;
496 }
498 return json;
499}
500
503 Json json = Json::object();
504 json["type"] = "number";
505 if (schema.title.has_value()) {
506 json["title"] = *schema.title;
507 }
508 if (schema.description.has_value()) {
509 json["description"] = *schema.description;
510 }
511 if (schema.minimum.has_value()) {
512 json["minimum"] = *schema.minimum;
513 }
514 if (schema.maximum.has_value()) {
515 json["maximum"] = *schema.maximum;
516 }
517 if (schema.default_value.has_value()) {
518 json["default"] = *schema.default_value;
519 }
521 return json;
522}
523
526 Json json = Json::object();
527 json["type"] = "integer";
528 if (schema.title.has_value()) {
529 json["title"] = *schema.title;
530 }
531 if (schema.description.has_value()) {
532 json["description"] = *schema.description;
533 }
534 if (schema.minimum.has_value()) {
535 json["minimum"] = *schema.minimum;
536 }
537 if (schema.maximum.has_value()) {
538 json["maximum"] = *schema.maximum;
539 }
540 if (schema.default_value.has_value()) {
541 json["default"] = *schema.default_value;
542 }
544 return json;
545}
546
549 Json json = Json::object();
550 json["type"] = "boolean";
551 if (schema.title.has_value()) {
552 json["title"] = *schema.title;
553 }
554 if (schema.description.has_value()) {
555 json["description"] = *schema.description;
556 }
557 if (schema.default_value.has_value()) {
558 json["default"] = *schema.default_value;
559 }
561 return json;
562}
563
565inline Json enum_schema_to_json(const EnumSchema& schema) {
566 Json json = Json::object();
567 json["type"] = schema.multi_select ? "array" : "string";
568 if (schema.multi_select) {
569 if (!schema.value_titles.empty() &&
570 schema.value_titles.size() == schema.values.size()) {
571 json["items"] = Json::object();
572 json["items"]["anyOf"] = Json::array();
573 for (std::size_t index = 0; index < schema.values.size(); ++index) {
574 json["items"]["anyOf"].push_back(
575 Json{{"const", schema.values[index]},
576 {"title", schema.value_titles[index]}});
577 }
578 } else {
579 json["items"] = Json{{"type", "string"}, {"enum", Json::array()}};
580 for (const auto& value : schema.values) {
581 json["items"]["enum"].push_back(value);
582 }
583 }
584 if (schema.min_items.has_value()) {
585 json["minItems"] = *schema.min_items;
586 }
587 if (schema.max_items.has_value()) {
588 json["maxItems"] = *schema.max_items;
589 }
590 if (!schema.default_values.empty()) {
591 json["default"] = schema.default_values;
592 }
593 } else if (schema.titled_single_select && !schema.value_titles.empty() &&
594 schema.value_titles.size() == schema.values.size()) {
595 json["oneOf"] = Json::array();
596 for (std::size_t index = 0; index < schema.values.size(); ++index) {
597 json["oneOf"].push_back(Json{{"const", schema.values[index]},
598 {"title", schema.value_titles[index]}});
599 }
600 } else {
601 json["enum"] = Json::array();
602 for (const auto& value : schema.values) {
603 json["enum"].push_back(value);
604 }
605 if (!schema.enum_names.empty()) {
606 json["enumNames"] = schema.enum_names;
607 }
608 }
609 if (schema.title.has_value()) {
610 json["title"] = *schema.title;
611 }
612 if (schema.description.has_value()) {
613 json["description"] = *schema.description;
614 }
615 if (!schema.multi_select && schema.default_value.has_value()) {
616 json["default"] = *schema.default_value;
617 }
619 return json;
620}
621
623inline Json primitive_schema_to_json(const PrimitiveSchema& schema);
624
627 Json json = Json::object();
628 json["type"] = "object";
629 if (schema.title.has_value()) {
630 json["title"] = *schema.title;
631 }
632 if (schema.description.has_value()) {
633 json["description"] = *schema.description;
634 }
635 json["properties"] = Json::object();
636 for (const auto& [name, property] : schema.properties) {
637 json["properties"][name] = primitive_schema_to_json(property);
638 }
639 if (!schema.required.empty()) {
640 json["required"] = schema.required;
641 }
643 return json;
644}
645
648inline core::Error elicitation_json_error(std::string message) {
649 return core::Error{
650 static_cast<int>(ErrorCode::InvalidRequest), std::move(message), {}};
651}
652
655 const Json& choices, std::vector<std::string>& values,
656 std::vector<std::string>& titles, std::string_view context) {
657 if (!choices.is_array()) {
658 return mcp::core::unexpected(
659 elicitation_json_error(std::string(context) + " must be an array"));
660 }
661 for (const auto& choice : choices) {
662 if (!choice.is_object() || !choice.contains("const") ||
663 !choice.at("const").is_string() || !choice.contains("title") ||
664 !choice.at("title").is_string()) {
665 return mcp::core::unexpected(elicitation_json_error(
666 std::string(context) + " entries require string const and title"));
667 }
668 values.push_back(choice.at("const").get<std::string>());
669 titles.push_back(choice.at("title").get<std::string>());
670 }
671 return core::Unit{};
672}
673
675inline bool enum_values_are_allowed(const std::vector<std::string>& allowed,
676 const std::vector<std::string>& values) {
677 for (const auto& value : values) {
678 if (std::find(allowed.begin(), allowed.end(), value) == allowed.end()) {
679 return false;
680 }
681 }
682 return true;
683}
684
687inline core::Result<PrimitiveSchema> primitive_schema_from_json(
688 const Json& json);
689
693 const Json& json) {
694 if (!json.is_object()) {
695 return mcp::core::unexpected(
696 elicitation_json_error("elicitation schema must be an object"));
697 }
698 if (!json.contains("type") || !json.at("type").is_string() ||
699 json.at("type").get<std::string>() != "object") {
700 return mcp::core::unexpected(
701 elicitation_json_error("elicitation schema requires type object"));
702 }
703 if (!json.contains("properties") || !json.at("properties").is_object()) {
704 return mcp::core::unexpected(
705 elicitation_json_error("elicitation schema requires properties"));
706 }
707
708 ElicitationSchema schema;
709 if (json.contains("title")) {
710 if (!json.at("title").is_string()) {
711 return mcp::core::unexpected(
712 elicitation_json_error("elicitation schema title must be a string"));
713 }
714 schema.title = json.at("title").get<std::string>();
715 }
716 if (json.contains("description")) {
717 if (!json.at("description").is_string()) {
718 return mcp::core::unexpected(elicitation_json_error(
719 "elicitation schema description must be a string"));
720 }
721 schema.description = json.at("description").get<std::string>();
722 }
723 for (const auto& [name, property_json] : json.at("properties").items()) {
724 const auto property = primitive_schema_from_json(property_json);
725 if (!property) {
726 return mcp::core::unexpected(property.error());
727 }
728 schema.properties.emplace(name, *property);
729 }
730 if (json.contains("required")) {
731 if (!json.at("required").is_array()) {
732 return mcp::core::unexpected(elicitation_json_error(
733 "elicitation schema required must be an array"));
734 }
735 for (const auto& item : json.at("required")) {
736 if (!item.is_string()) {
737 return mcp::core::unexpected(elicitation_json_error(
738 "elicitation schema required entries must be strings"));
739 }
740 schema.required.push_back(item.get<std::string>());
741 }
742 }
744 json, {"type", "title", "description", "properties", "required"});
745 return schema;
746}
747
752 const CreateElicitationRequestParam& request) {
753 Json json = Json::object();
754 json["message"] = request.message;
755 json["mode"] = elicitation_mode_to_string(request.mode);
756 if (request.request_state.has_value()) {
757 json["requestState"] = *request.request_state;
758 }
759 if (request.task.has_value()) {
760 json["task"] = task_request_parameters_to_json(*request.task);
761 }
762 if (request.meta.has_value()) {
763 json["_meta"] = *request.meta;
764 }
765 append_json_extensions(json, request.extensions);
766
767 if (request.mode == ElicitationMode::Url) {
768 if (request.elicitation_id.has_value()) {
769 json["elicitationId"] = *request.elicitation_id;
770 }
771 if (request.url.has_value()) {
772 json["url"] = *request.url;
773 }
774 return json;
775 }
776
777 json["requestedSchema"] =
779 return json;
780}
781
786 if (!json.is_object()) {
787 return mcp::core::unexpected(
788 elicitation_json_error("elicitation request must be an object"));
789 }
790 if (!json.contains("message") || !json.at("message").is_string()) {
791 return mcp::core::unexpected(elicitation_json_error(
792 "elicitation request requires a string message"));
793 }
794 ElicitationMode mode = ElicitationMode::Form;
795 if (json.contains("mode")) {
796 if (!json.at("mode").is_string()) {
797 return mcp::core::unexpected(
798 elicitation_json_error("elicitation request mode must be a string"));
799 }
800 const auto parsed_mode =
801 elicitation_mode_from_string(json.at("mode").get<std::string>());
802 if (!parsed_mode) {
803 return mcp::core::unexpected(
804 elicitation_json_error("elicitation request mode is not supported"));
805 }
806 mode = *parsed_mode;
807 }
808
810 request.message = json.at("message").get<std::string>();
811 request.mode = mode;
812
813 if (json.contains("requestState")) {
814 request.request_state = json.at("requestState");
815 }
816 if (json.contains("task")) {
817 const auto task = task_request_parameters_from_json(json.at("task"));
818 if (!task) {
819 return mcp::core::unexpected(task.error());
820 }
821 request.task = *task;
822 }
823 if (json.contains("_meta")) {
824 if (!json.at("_meta").is_object()) {
825 return mcp::core::unexpected(elicitation_json_error(
826 "elicitation request _meta must be an object"));
827 }
828 request.meta = json.at("_meta");
829 }
831 json, {"message", "mode", "elicitationId", "url", "requestedSchema",
832 "requested_schema", "requestState", "task", "_meta"});
833
834 if (mode == ElicitationMode::Url) {
835 if (!json.contains("elicitationId") ||
836 !json.at("elicitationId").is_string()) {
837 return mcp::core::unexpected(
838 elicitation_json_error("elicitation request requires elicitationId"));
839 }
840 if (!json.contains("url") || !json.at("url").is_string()) {
841 return mcp::core::unexpected(
842 elicitation_json_error("elicitation request requires url"));
843 }
844 request.elicitation_id = json.at("elicitationId").get<std::string>();
845 request.url = json.at("url").get<std::string>();
846 return request;
847 }
848
849 const auto schema_key =
850 json.contains("requestedSchema") ? "requestedSchema" : "requested_schema";
851 if (!json.contains(schema_key)) {
852 return mcp::core::unexpected(
853 elicitation_json_error("elicitation request requires requestedSchema"));
854 }
855 const auto schema = elicitation_schema_from_json(json.at(schema_key));
856 if (!schema) {
857 return mcp::core::unexpected(schema.error());
858 }
859 request.requested_schema = *schema;
860 return request;
861}
862
865 const CreateElicitationResult& result) {
866 Json json = Json::object();
867 json["action"] = elicitation_action_to_string(result.action);
868 if (result.content.has_value()) {
869 json["content"] = *result.content;
870 }
871 if (result.meta.has_value()) {
872 json["_meta"] = *result.meta;
873 }
875 return json;
876}
877
882 if (!json.is_object()) {
883 return mcp::core::unexpected(
884 elicitation_json_error("elicitation result must be an object"));
885 }
886 if (!json.contains("action") || !json.at("action").is_string()) {
887 return mcp::core::unexpected(
888 elicitation_json_error("elicitation result requires a string action"));
889 }
890
891 const auto action =
892 elicitation_action_from_string(json.at("action").get<std::string>());
893 if (!action.has_value()) {
894 return mcp::core::unexpected(
895 elicitation_json_error("elicitation action is not supported"));
896 }
897
899 result.action = *action;
900 if (json.contains("content")) {
901 result.content = json.at("content");
902 }
903 if (json.contains("_meta")) {
904 if (!json.at("_meta").is_object()) {
905 return mcp::core::unexpected(
906 elicitation_json_error("elicitation result _meta must be an object"));
907 }
908 result.meta = json.at("_meta");
909 }
910 result.extensions =
911 collect_json_extensions(json, {"action", "content", "_meta"});
912 return result;
913}
914
918 const std::string& name, const PrimitiveSchema& schema, const Json& value) {
919 return std::visit(
920 [&](const auto& property) -> core::Result<core::Unit> {
921 using Property = std::decay_t<decltype(property)>;
922 if constexpr (std::is_same_v<Property, StringSchema>) {
923 if (!value.is_string()) {
924 return mcp::core::unexpected(elicitation_json_error(
925 "elicitation content field '" + name + "' must be a string"));
926 }
927 const auto string_value = value.get<std::string>();
928 if (property.min_length.has_value() &&
929 string_value.size() <
930 static_cast<std::size_t>(*property.min_length)) {
931 return mcp::core::unexpected(
932 elicitation_json_error("elicitation content field '" + name +
933 "' is shorter than minLength"));
934 }
935 if (property.max_length.has_value() &&
936 string_value.size() >
937 static_cast<std::size_t>(*property.max_length)) {
938 return mcp::core::unexpected(
939 elicitation_json_error("elicitation content field '" + name +
940 "' is longer than maxLength"));
941 }
942 } else if constexpr (std::is_same_v<Property, NumberSchema>) {
943 if (!value.is_number()) {
944 return mcp::core::unexpected(elicitation_json_error(
945 "elicitation content field '" + name + "' must be numeric"));
946 }
947 const auto number = value.get<double>();
948 if (!protocol_number_is_finite(number)) {
949 return mcp::core::unexpected(elicitation_json_error(
950 "elicitation content field '" + name + "' must be finite"));
951 }
952 if (property.minimum.has_value() && number < *property.minimum) {
953 return mcp::core::unexpected(elicitation_json_error(
954 "elicitation content field '" + name + "' is below minimum"));
955 }
956 if (property.maximum.has_value() && number > *property.maximum) {
957 return mcp::core::unexpected(elicitation_json_error(
958 "elicitation content field '" + name + "' is above maximum"));
959 }
960 } else if constexpr (std::is_same_v<Property, IntegerSchema>) {
961 if (!value.is_number_integer()) {
962 return mcp::core::unexpected(elicitation_json_error(
963 "elicitation content field '" + name + "' must be an integer"));
964 }
965 const auto integer = value.get<std::int64_t>();
966 if (property.minimum.has_value() && integer < *property.minimum) {
967 return mcp::core::unexpected(elicitation_json_error(
968 "elicitation content field '" + name + "' is below minimum"));
969 }
970 if (property.maximum.has_value() && integer > *property.maximum) {
971 return mcp::core::unexpected(elicitation_json_error(
972 "elicitation content field '" + name + "' is above maximum"));
973 }
974 } else if constexpr (std::is_same_v<Property, BooleanSchema>) {
975 if (!value.is_boolean()) {
976 return mcp::core::unexpected(elicitation_json_error(
977 "elicitation content field '" + name + "' must be a boolean"));
978 }
979 } else {
980 if (property.multi_select) {
981 if (!value.is_array()) {
982 return mcp::core::unexpected(elicitation_json_error(
983 "elicitation content field '" + name + "' must be an array"));
984 }
985 if (property.min_items.has_value() &&
986 value.size() < static_cast<std::size_t>(*property.min_items)) {
987 return mcp::core::unexpected(
988 elicitation_json_error("elicitation content field '" + name +
989 "' has too few selections"));
990 }
991 if (property.max_items.has_value() &&
992 value.size() > static_cast<std::size_t>(*property.max_items)) {
993 return mcp::core::unexpected(
994 elicitation_json_error("elicitation content field '" + name +
995 "' has too many selections"));
996 }
997 for (const auto& item : value) {
998 if (!item.is_string()) {
999 return mcp::core::unexpected(elicitation_json_error(
1000 "elicitation content field '" + name +
1001 "' selections must be strings"));
1002 }
1003 const auto enum_value = item.get<std::string>();
1004 if (std::find(property.values.begin(), property.values.end(),
1005 enum_value) == property.values.end()) {
1006 return mcp::core::unexpected(
1007 elicitation_json_error("elicitation content field '" +
1008 name + "' must match enum values"));
1009 }
1010 }
1011 } else if (!value.is_string()) {
1012 return mcp::core::unexpected(elicitation_json_error(
1013 "elicitation content field '" + name + "' must be a string"));
1014 } else {
1015 const auto enum_value = value.get<std::string>();
1016 if (std::find(property.values.begin(), property.values.end(),
1017 enum_value) == property.values.end()) {
1018 return mcp::core::unexpected(
1019 elicitation_json_error("elicitation content field '" + name +
1020 "' must match an enum value"));
1021 }
1022 }
1023 }
1024 return core::Unit{};
1025 },
1026 schema);
1027}
1028
1037 const ElicitationSchema& schema, const Json& content) {
1038 if (!content.is_object()) {
1039 return mcp::core::unexpected(
1040 elicitation_json_error("elicitation content must be an object"));
1041 }
1042
1043 for (const auto& required : schema.required) {
1044 if (!content.contains(required)) {
1045 return mcp::core::unexpected(elicitation_json_error(
1046 "elicitation content requires field '" + required + "'"));
1047 }
1048 }
1049
1050 for (const auto& [name, property] : schema.properties) {
1051 if (!content.contains(name)) {
1052 continue;
1053 }
1054 const auto valid =
1055 validate_elicitation_content_property(name, property, content.at(name));
1056 if (!valid) {
1057 return valid;
1058 }
1059 }
1060
1061 return core::Unit{};
1062}
1063
1071 const ElicitationSchema& schema, const CreateElicitationResult& result) {
1072 if (result.action != ElicitationAction::Accept) {
1073 return core::Unit{};
1074 }
1075 if (!result.content.has_value()) {
1076 if (schema.required.empty()) {
1077 return core::Unit{};
1078 }
1079 return mcp::core::unexpected(
1080 elicitation_json_error("accepted elicitation result requires content"));
1081 }
1082 return validate_elicitation_content(schema, *result.content);
1083}
1084
1090
1095 return reflect_from_json<ElicitationCompleteNotificationParams>(json);
1096}
1097
1099inline bool ElicitationSchema::Builder::validate_name(const std::string& name) {
1100 return !name.empty();
1101}
1102
1104inline ElicitationSchema::Builder& ElicitationSchema::Builder::add_required(
1105 std::string name, PrimitiveSchema schema) {
1106 required_.push_back(name);
1107 properties_.insert_or_assign(std::move(name), std::move(schema));
1108 return *this;
1109}
1110
1112inline ElicitationSchema::Builder& ElicitationSchema::Builder::add_optional(
1113 std::string name, PrimitiveSchema schema) {
1114 properties_.insert_or_assign(std::move(name), std::move(schema));
1115 return *this;
1116}
1117
1119 std::string name, std::optional<std::string> default_value,
1120 std::optional<std::string> format) {
1121 if (!validate_name(name)) {
1122 return *this;
1123 }
1124 StringSchema schema;
1125 schema.default_value = std::move(default_value);
1126 schema.format = std::move(format);
1127 return add_required(std::move(name), std::move(schema));
1128}
1129
1131 std::string name, std::optional<std::string> default_value,
1132 std::optional<std::string> format) {
1133 if (!validate_name(name)) {
1134 return *this;
1135 }
1136 StringSchema schema;
1137 schema.default_value = std::move(default_value);
1138 schema.format = std::move(format);
1139 return add_optional(std::move(name), std::move(schema));
1140}
1141
1143 std::string name, std::optional<double> minimum,
1144 std::optional<double> maximum, std::optional<double> default_value) {
1145 if (!validate_name(name)) {
1146 return *this;
1147 }
1148 NumberSchema schema;
1149 schema.minimum = minimum;
1150 schema.maximum = maximum;
1151 schema.default_value = default_value;
1152 return add_required(std::move(name), std::move(schema));
1153}
1154
1156 std::string name, std::optional<double> minimum,
1157 std::optional<double> maximum, std::optional<double> default_value) {
1158 if (!validate_name(name)) {
1159 return *this;
1160 }
1161 NumberSchema schema;
1162 schema.minimum = minimum;
1163 schema.maximum = maximum;
1164 schema.default_value = default_value;
1165 return add_optional(std::move(name), std::move(schema));
1166}
1167
1169 std::string name, std::optional<std::int64_t> minimum,
1170 std::optional<std::int64_t> maximum,
1171 std::optional<std::int64_t> default_value) {
1172 if (!validate_name(name)) {
1173 return *this;
1174 }
1175 IntegerSchema schema;
1176 schema.minimum = minimum;
1177 schema.maximum = maximum;
1178 schema.default_value = default_value;
1179 return add_required(std::move(name), std::move(schema));
1180}
1181
1183 std::string name, std::optional<std::int64_t> minimum,
1184 std::optional<std::int64_t> maximum,
1185 std::optional<std::int64_t> default_value) {
1186 if (!validate_name(name)) {
1187 return *this;
1188 }
1189 IntegerSchema schema;
1190 schema.minimum = minimum;
1191 schema.maximum = maximum;
1192 schema.default_value = default_value;
1193 return add_optional(std::move(name), std::move(schema));
1194}
1195
1197 std::string name, std::optional<bool> default_value) {
1198 if (!validate_name(name)) {
1199 return *this;
1200 }
1201 BooleanSchema schema;
1202 schema.default_value = default_value;
1203 return add_required(std::move(name), std::move(schema));
1204}
1205
1207 std::string name, std::optional<bool> default_value) {
1208 if (!validate_name(name)) {
1209 return *this;
1210 }
1211 BooleanSchema schema;
1212 schema.default_value = default_value;
1213 return add_optional(std::move(name), std::move(schema));
1214}
1215
1217 std::string name, std::optional<std::string> default_value) {
1218 return required_string(std::move(name), std::move(default_value),
1219 std::string("email"));
1220}
1221
1223 std::string name, std::optional<std::string> default_value) {
1224 return optional_string(std::move(name), std::move(default_value),
1225 std::string("email"));
1226}
1227
1229 std::string name, std::vector<std::string> values,
1230 std::optional<std::string> default_value) {
1231 if (!validate_name(name)) {
1232 return *this;
1233 }
1234 EnumSchema schema;
1235 schema.values = std::move(values);
1236 schema.default_value = std::move(default_value);
1237 return add_required(std::move(name), std::move(schema));
1238}
1239
1241 std::string name, std::vector<std::string> values,
1242 std::optional<std::string> default_value) {
1243 if (!validate_name(name)) {
1244 return *this;
1245 }
1246 EnumSchema schema;
1247 schema.values = std::move(values);
1248 schema.default_value = std::move(default_value);
1249 return add_optional(std::move(name), std::move(schema));
1250}
1251
1254 const {
1255 if (properties_.empty()) {
1256 return mcp::core::unexpected(elicitation_json_error(
1257 "elicitation schema requires at least one property"));
1258 }
1259 ElicitationSchema schema;
1260 schema.title = title_;
1261 schema.description = description_;
1262 schema.properties = properties_;
1263 schema.required = required_;
1264 return schema;
1265}
1266
1268inline Json primitive_schema_to_json(const PrimitiveSchema& schema);
1269
1272 return std::visit(
1273 [](const auto& value) -> Json {
1274 using Value = std::decay_t<decltype(value)>;
1275 if constexpr (std::is_same_v<Value, StringSchema>) {
1276 return string_schema_to_json(value);
1277 } else if constexpr (std::is_same_v<Value, NumberSchema>) {
1278 return number_schema_to_json(value);
1279 } else if constexpr (std::is_same_v<Value, IntegerSchema>) {
1280 return integer_schema_to_json(value);
1281 } else if constexpr (std::is_same_v<Value, BooleanSchema>) {
1282 return boolean_schema_to_json(value);
1283 } else {
1284 return enum_schema_to_json(value);
1285 }
1286 },
1287 schema);
1288}
1289
1292 const Json& json) {
1293 if (!json.is_object()) {
1294 return mcp::core::unexpected(elicitation_json_error(
1295 "elicitation property schema must be an object"));
1296 }
1297 if (!json.contains("type") || !json.at("type").is_string()) {
1298 return mcp::core::unexpected(elicitation_json_error(
1299 "elicitation property schema requires a string type"));
1300 }
1301
1302 const auto type = json.at("type").get<std::string>();
1303 if (type == "array") {
1304 EnumSchema schema;
1305 schema.multi_select = true;
1306 if (json.contains("title")) {
1307 if (!json.at("title").is_string()) {
1308 return mcp::core::unexpected(elicitation_json_error(
1309 "elicitation multi-select title must be a string"));
1310 }
1311 schema.title = json.at("title").get<std::string>();
1312 }
1313 if (json.contains("description")) {
1314 if (!json.at("description").is_string()) {
1315 return mcp::core::unexpected(elicitation_json_error(
1316 "elicitation multi-select description must be a string"));
1317 }
1318 schema.description = json.at("description").get<std::string>();
1319 }
1320 if (!json.contains("items") || !json.at("items").is_object()) {
1321 return mcp::core::unexpected(
1322 elicitation_json_error("elicitation multi-select requires items"));
1323 }
1324 const auto& items = json.at("items");
1325 if (items.contains("type") &&
1326 (!items.at("type").is_string() ||
1327 items.at("type").get<std::string>() != "string")) {
1328 return mcp::core::unexpected(elicitation_json_error(
1329 "elicitation multi-select items type must be string"));
1330 }
1331 if (items.contains("enum")) {
1332 if (!items.at("enum").is_array()) {
1333 return mcp::core::unexpected(elicitation_json_error(
1334 "elicitation multi-select enum must be an array"));
1335 }
1336 for (const auto& item : items.at("enum")) {
1337 if (!item.is_string()) {
1338 return mcp::core::unexpected(elicitation_json_error(
1339 "elicitation multi-select enum values must be strings"));
1340 }
1341 schema.values.push_back(item.get<std::string>());
1342 }
1343 } else if (items.contains("anyOf") || items.contains("oneOf")) {
1344 const auto choices_key = items.contains("anyOf") ? "anyOf" : "oneOf";
1345 const auto parsed_choices = parse_titled_enum_choices(
1346 items.at(choices_key), schema.values, schema.value_titles,
1347 "elicitation multi-select choices");
1348 if (!parsed_choices) {
1349 return mcp::core::unexpected(parsed_choices.error());
1350 }
1351 } else {
1352 return mcp::core::unexpected(elicitation_json_error(
1353 "elicitation multi-select requires enum, anyOf, or oneOf items"));
1354 }
1355 if (json.contains("minItems")) {
1356 if (!json.at("minItems").is_number_integer()) {
1357 return mcp::core::unexpected(elicitation_json_error(
1358 "elicitation multi-select minItems must be an integer"));
1359 }
1360 schema.min_items = json.at("minItems").get<std::int64_t>();
1361 if (*schema.min_items < 0) {
1362 return mcp::core::unexpected(elicitation_json_error(
1363 "elicitation multi-select minItems must be non-negative"));
1364 }
1365 }
1366 if (json.contains("maxItems")) {
1367 if (!json.at("maxItems").is_number_integer()) {
1368 return mcp::core::unexpected(elicitation_json_error(
1369 "elicitation multi-select maxItems must be an integer"));
1370 }
1371 schema.max_items = json.at("maxItems").get<std::int64_t>();
1372 if (*schema.max_items < 0) {
1373 return mcp::core::unexpected(elicitation_json_error(
1374 "elicitation multi-select maxItems must be non-negative"));
1375 }
1376 }
1377 if (schema.min_items.has_value() && schema.max_items.has_value() &&
1378 *schema.min_items > *schema.max_items) {
1379 return mcp::core::unexpected(elicitation_json_error(
1380 "elicitation multi-select minItems must be <= maxItems"));
1381 }
1382 if (json.contains("default")) {
1383 if (!json.at("default").is_array()) {
1384 return mcp::core::unexpected(elicitation_json_error(
1385 "elicitation multi-select default must be an array"));
1386 }
1387 for (const auto& item : json.at("default")) {
1388 if (!item.is_string()) {
1389 return mcp::core::unexpected(elicitation_json_error(
1390 "elicitation multi-select default values must be strings"));
1391 }
1392 schema.default_values.push_back(item.get<std::string>());
1393 }
1394 if (!enum_values_are_allowed(schema.values, schema.default_values)) {
1395 return mcp::core::unexpected(elicitation_json_error(
1396 "elicitation multi-select default must match enum values"));
1397 }
1398 }
1400 json, {"type", "title", "description", "minItems", "maxItems", "items",
1401 "default"});
1402 return schema;
1403 }
1404 if (json.contains("oneOf")) {
1405 if (type != "string") {
1406 return mcp::core::unexpected(elicitation_json_error(
1407 "elicitation titled enum type must be string"));
1408 }
1409 EnumSchema schema;
1410 schema.titled_single_select = true;
1411 if (json.contains("title")) {
1412 if (!json.at("title").is_string()) {
1413 return mcp::core::unexpected(elicitation_json_error(
1414 "elicitation titled enum title must be a string"));
1415 }
1416 schema.title = json.at("title").get<std::string>();
1417 }
1418 if (json.contains("description")) {
1419 if (!json.at("description").is_string()) {
1420 return mcp::core::unexpected(elicitation_json_error(
1421 "elicitation titled enum description must be a string"));
1422 }
1423 schema.description = json.at("description").get<std::string>();
1424 }
1425 const auto parsed_choices = parse_titled_enum_choices(
1426 json.at("oneOf"), schema.values, schema.value_titles,
1427 "elicitation titled enum oneOf");
1428 if (!parsed_choices) {
1429 return mcp::core::unexpected(parsed_choices.error());
1430 }
1431 if (json.contains("default")) {
1432 if (!json.at("default").is_string()) {
1433 return mcp::core::unexpected(elicitation_json_error(
1434 "elicitation titled enum default must be a string"));
1435 }
1436 schema.default_value = json.at("default").get<std::string>();
1437 if (std::find(schema.values.begin(), schema.values.end(),
1438 *schema.default_value) == schema.values.end()) {
1439 return mcp::core::unexpected(elicitation_json_error(
1440 "elicitation titled enum default must match an enum value"));
1441 }
1442 }
1444 json, {"type", "title", "description", "oneOf", "default"});
1445 return schema;
1446 }
1447 if (json.contains("enum")) {
1448 if (type != "string") {
1449 return mcp::core::unexpected(
1450 elicitation_json_error("elicitation enum type must be string"));
1451 }
1452 if (!json.at("enum").is_array()) {
1453 return mcp::core::unexpected(
1454 elicitation_json_error("elicitation enum must be an array"));
1455 }
1456 EnumSchema schema;
1457 if (json.contains("title")) {
1458 if (!json.at("title").is_string()) {
1459 return mcp::core::unexpected(
1460 elicitation_json_error("elicitation enum title must be a string"));
1461 }
1462 schema.title = json.at("title").get<std::string>();
1463 }
1464 if (json.contains("description")) {
1465 if (!json.at("description").is_string()) {
1466 return mcp::core::unexpected(elicitation_json_error(
1467 "elicitation enum description must be a string"));
1468 }
1469 schema.description = json.at("description").get<std::string>();
1470 }
1471 for (const auto& item : json.at("enum")) {
1472 if (!item.is_string()) {
1473 return mcp::core::unexpected(
1474 elicitation_json_error("elicitation enum values must be strings"));
1475 }
1476 schema.values.push_back(item.get<std::string>());
1477 }
1478 if (json.contains("enumNames")) {
1479 if (!json.at("enumNames").is_array()) {
1480 return mcp::core::unexpected(
1481 elicitation_json_error("elicitation enumNames must be an array"));
1482 }
1483 for (const auto& item : json.at("enumNames")) {
1484 if (!item.is_string()) {
1485 return mcp::core::unexpected(elicitation_json_error(
1486 "elicitation enumNames entries must be strings"));
1487 }
1488 schema.enum_names.push_back(item.get<std::string>());
1489 }
1490 if (schema.enum_names.size() != schema.values.size()) {
1491 return mcp::core::unexpected(elicitation_json_error(
1492 "elicitation enumNames size must match enum values"));
1493 }
1494 }
1495 if (json.contains("default")) {
1496 if (!json.at("default").is_string()) {
1497 return mcp::core::unexpected(elicitation_json_error(
1498 "elicitation enum default must be a string"));
1499 }
1500 schema.default_value = json.at("default").get<std::string>();
1501 if (std::find(schema.values.begin(), schema.values.end(),
1502 *schema.default_value) == schema.values.end()) {
1503 return mcp::core::unexpected(elicitation_json_error(
1504 "elicitation enum default must match an enum value"));
1505 }
1506 }
1508 json, {"type", "title", "description", "enum", "enumNames", "default"});
1509 return schema;
1510 }
1511
1512 if (type == "string") {
1513 StringSchema schema;
1514 if (json.contains("title")) {
1515 if (!json.at("title").is_string()) {
1516 return mcp::core::unexpected(elicitation_json_error(
1517 "elicitation string title must be a string"));
1518 }
1519 schema.title = json.at("title").get<std::string>();
1520 }
1521 if (json.contains("description")) {
1522 if (!json.at("description").is_string()) {
1523 return mcp::core::unexpected(elicitation_json_error(
1524 "elicitation string description must be a string"));
1525 }
1526 schema.description = json.at("description").get<std::string>();
1527 }
1528 if (json.contains("format")) {
1529 if (!json.at("format").is_string()) {
1530 return mcp::core::unexpected(elicitation_json_error(
1531 "elicitation string format must be a string"));
1532 }
1533 schema.format = json.at("format").get<std::string>();
1535 return mcp::core::unexpected(elicitation_json_error(
1536 "elicitation string format is not supported"));
1537 }
1538 }
1539 if (json.contains("minLength")) {
1540 if (!json.at("minLength").is_number_integer()) {
1541 return mcp::core::unexpected(elicitation_json_error(
1542 "elicitation string minLength must be an integer"));
1543 }
1544 schema.min_length = json.at("minLength").get<std::int64_t>();
1545 if (*schema.min_length < 0) {
1546 return mcp::core::unexpected(elicitation_json_error(
1547 "elicitation string minLength must be non-negative"));
1548 }
1549 }
1550 if (json.contains("maxLength")) {
1551 if (!json.at("maxLength").is_number_integer()) {
1552 return mcp::core::unexpected(elicitation_json_error(
1553 "elicitation string maxLength must be an integer"));
1554 }
1555 schema.max_length = json.at("maxLength").get<std::int64_t>();
1556 if (*schema.max_length < 0) {
1557 return mcp::core::unexpected(elicitation_json_error(
1558 "elicitation string maxLength must be non-negative"));
1559 }
1560 }
1561 if (schema.min_length.has_value() && schema.max_length.has_value() &&
1562 *schema.min_length > *schema.max_length) {
1563 return mcp::core::unexpected(elicitation_json_error(
1564 "elicitation string minLength must be <= maxLength"));
1565 }
1566 if (json.contains("default")) {
1567 if (!json.at("default").is_string()) {
1568 return mcp::core::unexpected(elicitation_json_error(
1569 "elicitation string default must be a string"));
1570 }
1571 schema.default_value = json.at("default").get<std::string>();
1572 }
1573 schema.extensions =
1574 collect_json_extensions(json, {"type", "title", "description", "format",
1575 "minLength", "maxLength", "default"});
1576 return schema;
1577 }
1578 if (type == "number") {
1579 NumberSchema schema;
1580 if (json.contains("title")) {
1581 if (!json.at("title").is_string()) {
1582 return mcp::core::unexpected(elicitation_json_error(
1583 "elicitation number title must be a string"));
1584 }
1585 schema.title = json.at("title").get<std::string>();
1586 }
1587 if (json.contains("description")) {
1588 if (!json.at("description").is_string()) {
1589 return mcp::core::unexpected(elicitation_json_error(
1590 "elicitation number description must be a string"));
1591 }
1592 schema.description = json.at("description").get<std::string>();
1593 }
1594 if (json.contains("minimum")) {
1595 if (!json.at("minimum").is_number()) {
1596 return mcp::core::unexpected(elicitation_json_error(
1597 "elicitation number minimum must be numeric"));
1598 }
1599 const auto value = json.at("minimum").get<double>();
1600 if (!protocol_number_is_finite(value)) {
1601 return mcp::core::unexpected(elicitation_json_error(
1602 "elicitation number minimum must be finite"));
1603 }
1604 schema.minimum = value;
1605 }
1606 if (json.contains("maximum")) {
1607 if (!json.at("maximum").is_number()) {
1608 return mcp::core::unexpected(elicitation_json_error(
1609 "elicitation number maximum must be numeric"));
1610 }
1611 const auto value = json.at("maximum").get<double>();
1612 if (!protocol_number_is_finite(value)) {
1613 return mcp::core::unexpected(elicitation_json_error(
1614 "elicitation number maximum must be finite"));
1615 }
1616 schema.maximum = value;
1617 }
1618 if (json.contains("default")) {
1619 if (!json.at("default").is_number()) {
1620 return mcp::core::unexpected(elicitation_json_error(
1621 "elicitation number default must be numeric"));
1622 }
1623 const auto value = json.at("default").get<double>();
1624 if (!protocol_number_is_finite(value)) {
1625 return mcp::core::unexpected(elicitation_json_error(
1626 "elicitation number default must be finite"));
1627 }
1628 schema.default_value = value;
1629 }
1631 json,
1632 {"type", "title", "description", "minimum", "maximum", "default"});
1633 return schema;
1634 }
1635 if (type == "integer") {
1636 IntegerSchema schema;
1637 if (json.contains("title")) {
1638 if (!json.at("title").is_string()) {
1639 return mcp::core::unexpected(elicitation_json_error(
1640 "elicitation integer title must be a string"));
1641 }
1642 schema.title = json.at("title").get<std::string>();
1643 }
1644 if (json.contains("description")) {
1645 if (!json.at("description").is_string()) {
1646 return mcp::core::unexpected(elicitation_json_error(
1647 "elicitation integer description must be a string"));
1648 }
1649 schema.description = json.at("description").get<std::string>();
1650 }
1651 if (json.contains("minimum")) {
1652 if (!json.at("minimum").is_number_integer()) {
1653 return mcp::core::unexpected(elicitation_json_error(
1654 "elicitation integer minimum must be an integer"));
1655 }
1656 schema.minimum = json.at("minimum").get<std::int64_t>();
1657 }
1658 if (json.contains("maximum")) {
1659 if (!json.at("maximum").is_number_integer()) {
1660 return mcp::core::unexpected(elicitation_json_error(
1661 "elicitation integer maximum must be an integer"));
1662 }
1663 schema.maximum = json.at("maximum").get<std::int64_t>();
1664 }
1665 if (json.contains("default")) {
1666 if (!json.at("default").is_number_integer()) {
1667 return mcp::core::unexpected(elicitation_json_error(
1668 "elicitation integer default must be an integer"));
1669 }
1670 schema.default_value = json.at("default").get<std::int64_t>();
1671 }
1673 json,
1674 {"type", "title", "description", "minimum", "maximum", "default"});
1675 return schema;
1676 }
1677 if (type == "boolean") {
1678 BooleanSchema schema;
1679 if (json.contains("title")) {
1680 if (!json.at("title").is_string()) {
1681 return mcp::core::unexpected(elicitation_json_error(
1682 "elicitation boolean title must be a string"));
1683 }
1684 schema.title = json.at("title").get<std::string>();
1685 }
1686 if (json.contains("description")) {
1687 if (!json.at("description").is_string()) {
1688 return mcp::core::unexpected(elicitation_json_error(
1689 "elicitation boolean description must be a string"));
1690 }
1691 schema.description = json.at("description").get<std::string>();
1692 }
1693 if (json.contains("default")) {
1694 if (!json.at("default").is_boolean()) {
1695 return mcp::core::unexpected(elicitation_json_error(
1696 "elicitation boolean default must be a boolean"));
1697 }
1698 schema.default_value = json.at("default").get<bool>();
1699 }
1701 json, {"type", "title", "description", "default"});
1702 return schema;
1703 }
1704
1705 return mcp::core::unexpected(
1706 elicitation_json_error("elicitation property type is not supported"));
1707}
1708
1709} // namespace mcp::protocol
Fluent builder for valid elicitation object schemas.
Definition elicitation.hpp:237
Builder & optional_bool(std::string name, std::optional< bool > default_value=std::nullopt)
Adds an optional boolean field.
Definition elicitation.hpp:1206
Builder & optional_enum(std::string name, std::vector< std::string > values, std::optional< std::string > default_value=std::nullopt)
Adds an optional string enum field.
Definition elicitation.hpp:1240
Builder & required_enum(std::string name, std::vector< std::string > values, std::optional< std::string > default_value=std::nullopt)
Adds a required string enum field.
Definition elicitation.hpp:1228
Builder & optional_number(std::string name, std::optional< double > minimum=std::nullopt, std::optional< double > maximum=std::nullopt, std::optional< double > default_value=std::nullopt)
Adds an optional number field.
Definition elicitation.hpp:1155
Builder & required_integer(std::string name, std::optional< std::int64_t > minimum=std::nullopt, std::optional< std::int64_t > maximum=std::nullopt, std::optional< std::int64_t > default_value=std::nullopt)
Adds a required integer field.
Definition elicitation.hpp:1168
Builder & required_number(std::string name, std::optional< double > minimum=std::nullopt, std::optional< double > maximum=std::nullopt, std::optional< double > default_value=std::nullopt)
Adds a required number field.
Definition elicitation.hpp:1142
Builder & required_bool(std::string name, std::optional< bool > default_value=std::nullopt)
Adds a required boolean field.
Definition elicitation.hpp:1196
Builder & required_string(std::string name, std::optional< std::string > default_value=std::nullopt, std::optional< std::string > format=std::nullopt)
Adds a required string field.
Definition elicitation.hpp:1118
Builder & optional_integer(std::string name, std::optional< std::int64_t > minimum=std::nullopt, std::optional< std::int64_t > maximum=std::nullopt, std::optional< std::int64_t > default_value=std::nullopt)
Adds an optional integer field.
Definition elicitation.hpp:1182
Builder & required_property(std::string name, PrimitiveSchema schema)
Adds a required property with a pre-built primitive schema.
Definition elicitation.hpp:305
Builder & required_email(std::string name, std::optional< std::string > default_value=std::nullopt)
Adds a required string field with email format.
Definition elicitation.hpp:1216
Builder & title(std::string value)
Sets the optional form title.
Definition elicitation.hpp:240
Builder & optional_property(std::string name, PrimitiveSchema schema)
Adds an optional property with a pre-built primitive schema.
Definition elicitation.hpp:310
core::Result< ElicitationSchema > build() const
Builds the schema after validation.
Definition elicitation.hpp:1253
Builder & optional_string(std::string name, std::optional< std::string > default_value=std::nullopt, std::optional< std::string > format=std::nullopt)
Adds an optional string field.
Definition elicitation.hpp:1130
Builder & optional_email(std::string name, std::optional< std::string > default_value=std::nullopt)
Adds an optional string field with email format.
Definition elicitation.hpp:1222
Builder & description(std::string value)
Sets the optional form description.
Definition elicitation.hpp:246
std::string elicitation_action_to_string(ElicitationAction action)
Converts an elicitation action to its MCP string value.
Definition elicitation.hpp:396
Json primitive_schema_to_json(const PrimitiveSchema &schema)
Serializes any primitive elicitation property schema.
Definition elicitation.hpp:1271
Json number_schema_to_json(const NumberSchema &schema)
Serializes a number property schema.
Definition elicitation.hpp:502
bool elicitation_string_format_is_supported(const std::string &value) noexcept
Returns true for string formats allowed by the MCP elicitation schema.
Definition elicitation.hpp:469
Json elicitation_complete_notification_params_to_json(const ElicitationCompleteNotificationParams &params)
Serializes URL-mode completion notification params.
Definition elicitation.hpp:1086
ElicitationMode
Elicitation interaction mode.
Definition elicitation.hpp:42
@ Url
External URL flow completed by notification.
@ Form
Inline form described by an ElicitationSchema.
core::Error elicitation_json_error(std::string message)
Builds an InvalidRequest error for elicitation JSON validation failures.
Definition elicitation.hpp:648
core::Result< core::Unit > parse_titled_enum_choices(const Json &choices, std::vector< std::string > &values, std::vector< std::string > &titles, std::string_view context)
Parses {"const": "...", "title": "..."} enum choices.
Definition elicitation.hpp:654
core::Result< core::Unit > validate_elicitation_content(const ElicitationSchema &schema, const Json &content)
Validates a form elicitation content object against the SDK's constrained ElicitationSchema model.
Definition elicitation.hpp:1036
Json elicitation_schema_to_json(const ElicitationSchema &schema)
Serializes a form elicitation object schema.
Definition elicitation.hpp:626
core::Result< PrimitiveSchema > primitive_schema_from_json(const Json &json)
Parses any primitive elicitation property schema.
Definition elicitation.hpp:1291
std::optional< ElicitationMode > elicitation_mode_from_string(const std::string &value)
Parses an elicitation mode string.
Definition elicitation.hpp:456
core::Result< core::Unit > validate_elicitation_content_property(const std::string &name, const PrimitiveSchema &schema, const Json &value)
Validates a form elicitation content object against one primitive property schema.
Definition elicitation.hpp:917
core::Result< CreateElicitationRequestParam > create_elicitation_request_param_from_json(const Json &json)
Parses elicitation/create params.
Definition elicitation.hpp:785
std::optional< ElicitationAction > elicitation_action_from_string(const std::string &value)
Parses an elicitation action string.
Definition elicitation.hpp:410
Json enum_schema_to_json(const EnumSchema &schema)
Serializes a string enum property schema.
Definition elicitation.hpp:565
core::Result< ElicitationSchema > elicitation_schema_from_json(const Json &json)
Parses a form elicitation object schema.
Definition elicitation.hpp:692
core::Result< CreateElicitationResult > create_elicitation_result_from_json(const Json &json)
Parses an elicitation/create result.
Definition elicitation.hpp:881
core::Result< ElicitationCompleteNotificationParams > elicitation_complete_notification_params_from_json(const Json &json)
Parses URL-mode completion notification params.
Definition elicitation.hpp:1094
bool enum_values_are_allowed(const std::vector< std::string > &allowed, const std::vector< std::string > &values)
Validates all selected enum values are allowed.
Definition elicitation.hpp:675
ElicitationAction
User action returned by an elicitation request.
Definition elicitation.hpp:32
@ Decline
The user declined the request.
@ Accept
The user accepted and may have supplied content.
@ Cancel
The elicitation was cancelled.
Json boolean_schema_to_json(const BooleanSchema &schema)
Serializes a boolean property schema.
Definition elicitation.hpp:548
std::string elicitation_mode_to_string(ElicitationMode mode)
Converts an elicitation mode to its MCP string value.
Definition elicitation.hpp:444
Json integer_schema_to_json(const IntegerSchema &schema)
Serializes an integer property schema.
Definition elicitation.hpp:525
Json string_schema_to_json(const StringSchema &schema)
Serializes a string property schema.
Definition elicitation.hpp:476
std::variant< StringSchema, NumberSchema, IntegerSchema, BooleanSchema, EnumSchema > PrimitiveSchema
Variant over primitive schema shapes supported by elicitation forms.
Definition elicitation.hpp:141
Json create_elicitation_result_to_json(const CreateElicitationResult &result)
Serializes an elicitation/create result.
Definition elicitation.hpp:864
Json create_elicitation_request_param_to_json(const CreateElicitationRequestParam &request)
Serializes elicitation/create params.
Definition elicitation.hpp:751
core::Result< core::Unit > validate_elicitation_result_content(const ElicitationSchema &schema, const CreateElicitationResult &result)
Validates an accepted elicitation result's content against the requested form schema.
Definition elicitation.hpp:1070
Shared JSON, JSON-RPC, error, cancellation, and progress model types.
void append_json_extensions(Json &json, const Json &extensions)
Flattens extension members into a JSON object without overwriting typed fields.
Definition types.hpp:358
nlohmann::json Json
JSON value type used by all protocol DTOs.
Definition types.hpp:28
Json collect_json_extensions(const Json &json, std::initializer_list< std::string_view > known_keys)
Collects unknown object members so typed DTOs can preserve future protocol fields and vendor extensio...
Definition types.hpp:320
bool protocol_number_is_finite(double value) noexcept
Returns true for finite JSON floating-point values accepted by MCP.
Definition types.hpp:31
constexpr FieldDescriptor< Struct, Field > field(const char *wire_name, Field Struct::*pointer)
Creates a FieldDescriptor with wire name and pointer-to-member.
Definition reflect.hpp:75
ExtensionsField< Struct > extensions_field(Json Struct::*pointer, std::vector< std::string > own_keys)
Creates an ExtensionsField descriptor.
Definition reflect.hpp:90
Json reflect_to_json(const T &obj)
Serializes a DTO to JSON using its Reflect<T> trait.
Definition reflect.hpp:701
Shared result and error primitives used by the public cxxmcp SDK.
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
Structured error returned by fallible SDK operations.
Definition result.hpp:35
Primitive boolean property schema for form elicitation.
Definition elicitation.hpp:100
std::optional< bool > default_value
Optional default boolean value.
Definition elicitation.hpp:106
std::optional< std::string > description
Optional display description.
Definition elicitation.hpp:104
Json extensions
Unknown JSON members preserved for forward-compatible round trips.
Definition elicitation.hpp:108
std::optional< std::string > title
Optional display title.
Definition elicitation.hpp:102
Parameters for elicitation/create.
Definition elicitation.hpp:335
std::optional< TaskRequestParameters > task
Optional task request parameters for asynchronous elicitation.
Definition elicitation.hpp:350
std::optional< Json > request_state
Optional opaque state echoed through the elicitation request lifecycle.
Definition elicitation.hpp:348
ElicitationSchema requested_schema
Form-mode schema describing requested values.
Definition elicitation.hpp:346
std::optional< std::string > url
Required in URL mode; target URL for the external interaction.
Definition elicitation.hpp:344
std::optional< Json > meta
Optional _meta extension object preserved on the wire.
Definition elicitation.hpp:352
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
Json extensions
Unknown JSON members preserved for forward-compatible round trips.
Definition elicitation.hpp:354
Result object for elicitation/create.
Definition elicitation.hpp:358
std::optional< Json > meta
Optional _meta extension object preserved on the wire.
Definition elicitation.hpp:364
std::optional< Json > content
Optional content object supplied when the action is Accept.
Definition elicitation.hpp:362
Json extensions
Unknown JSON members preserved for forward-compatible round trips.
Definition elicitation.hpp:366
ElicitationAction action
User action taken in response to the request.
Definition elicitation.hpp:360
Parameters for notifications/elicitation/complete.
Definition elicitation.hpp:370
std::optional< Json > meta
Optional _meta extension object preserved on the wire.
Definition elicitation.hpp:374
Json extensions
Unknown JSON members preserved for forward-compatible round trips.
Definition elicitation.hpp:376
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
static StringSchema new_string(std::optional< std::string > title=std::nullopt, std::optional< std::string > description=std::nullopt, std::optional< std::string > format=std::nullopt, std::optional< std::int64_t > min_length=std::nullopt, std::optional< std::int64_t > max_length=std::nullopt, std::optional< std::string > default_value=std::nullopt)
Creates a new string primitive schema.
Definition elicitation.hpp:157
std::map< std::string, PrimitiveSchema > properties
Properties keyed by field name.
Definition elicitation.hpp:150
static BooleanSchema new_boolean(std::optional< std::string > title=std::nullopt, std::optional< std::string > description=std::nullopt, std::optional< bool > default_value=std::nullopt)
Creates a new boolean primitive schema.
Definition elicitation.hpp:207
std::optional< std::string > title
Optional form title.
Definition elicitation.hpp:146
static Builder builder()
Creates a builder for an elicitation object schema.
Definition elicitation.hpp:331
static EnumSchema new_enum(std::vector< std::string > values, std::optional< std::string > title=std::nullopt, std::optional< std::string > description=std::nullopt, std::optional< std::string > default_value=std::nullopt)
Creates a new enum primitive schema.
Definition elicitation.hpp:219
std::vector< std::string > required
Names of required properties.
Definition elicitation.hpp:152
static NumberSchema new_number(std::optional< std::string > title=std::nullopt, std::optional< std::string > description=std::nullopt, std::optional< double > minimum=std::nullopt, std::optional< double > maximum=std::nullopt, std::optional< double > default_value=std::nullopt)
Creates a new number primitive schema.
Definition elicitation.hpp:175
static IntegerSchema new_integer(std::optional< std::string > title=std::nullopt, std::optional< std::string > description=std::nullopt, std::optional< std::int64_t > minimum=std::nullopt, std::optional< std::int64_t > maximum=std::nullopt, std::optional< std::int64_t > default_value=std::nullopt)
Creates a new integer primitive schema.
Definition elicitation.hpp:191
Json extensions
Unknown JSON members preserved for forward-compatible round trips.
Definition elicitation.hpp:154
std::optional< std::string > description
Optional form description.
Definition elicitation.hpp:148
Primitive string enum property schema for form elicitation.
Definition elicitation.hpp:112
bool multi_select
Whether this enum is represented as an array multi-select.
Definition elicitation.hpp:126
std::optional< std::int64_t > max_items
Optional maximum number of selected items for multi-select.
Definition elicitation.hpp:130
std::optional< std::string > default_value
Optional default enum value.
Definition elicitation.hpp:132
bool titled_single_select
Whether to serialize a single-select enum as titled oneOf.
Definition elicitation.hpp:124
std::optional< std::string > description
Optional display description.
Definition elicitation.hpp:116
std::vector< std::string > default_values
Optional default enum values for multi-select.
Definition elicitation.hpp:134
std::optional< std::string > title
Optional display title.
Definition elicitation.hpp:114
std::optional< std::int64_t > min_items
Optional minimum number of selected items for multi-select.
Definition elicitation.hpp:128
std::vector< std::string > enum_names
Optional display names for legacy enumNames.
Definition elicitation.hpp:120
std::vector< std::string > values
Allowed string values.
Definition elicitation.hpp:118
Json extensions
Unknown JSON members preserved for forward-compatible round trips.
Definition elicitation.hpp:136
std::vector< std::string > value_titles
Optional titles for RMCP/spec oneOf/anyOf enum entries.
Definition elicitation.hpp:122
Primitive integer property schema for form elicitation.
Definition elicitation.hpp:84
std::optional< std::string > description
Optional display description.
Definition elicitation.hpp:88
std::optional< std::int64_t > minimum
Optional inclusive minimum.
Definition elicitation.hpp:90
Json extensions
Unknown JSON members preserved for forward-compatible round trips.
Definition elicitation.hpp:96
std::optional< std::string > title
Optional display title.
Definition elicitation.hpp:86
std::optional< std::int64_t > default_value
Optional default integer value.
Definition elicitation.hpp:94
std::optional< std::int64_t > maximum
Optional inclusive maximum.
Definition elicitation.hpp:92
Type-specific serialization and deserialization logic.
Definition reflect.hpp:246
Primitive number property schema for form elicitation.
Definition elicitation.hpp:68
std::optional< std::string > description
Optional display description.
Definition elicitation.hpp:72
std::optional< std::string > title
Optional display title.
Definition elicitation.hpp:70
Json extensions
Unknown JSON members preserved for forward-compatible round trips.
Definition elicitation.hpp:80
std::optional< double > minimum
Optional inclusive minimum.
Definition elicitation.hpp:74
std::optional< double > default_value
Optional default numeric value.
Definition elicitation.hpp:78
std::optional< double > maximum
Optional inclusive maximum.
Definition elicitation.hpp:76
Primary template.
Definition reflect.hpp:199
Primitive string property schema for form elicitation.
Definition elicitation.hpp:50
std::optional< std::string > title
Optional display title.
Definition elicitation.hpp:52
std::optional< std::string > description
Optional display description.
Definition elicitation.hpp:54
std::optional< std::string > default_value
Optional default string value.
Definition elicitation.hpp:62
std::optional< std::string > format
Optional JSON Schema string format, such as email.
Definition elicitation.hpp:56
std::optional< std::int64_t > min_length
Optional minimum string length.
Definition elicitation.hpp:58
Json extensions
Unknown JSON members preserved for forward-compatible round trips.
Definition elicitation.hpp:64
std::optional< std::int64_t > max_length
Optional maximum string length.
Definition elicitation.hpp:60
Asynchronous task status and task-management payloads.
Json task_request_parameters_to_json(const TaskRequestParameters &parameters)
Serializes optional task request parameters.
Definition task.hpp:319
core::Result< TaskRequestParameters > task_request_parameters_from_json(const Json &json)
Parses optional task request parameters.
Definition task.hpp:326